Repository: graphql-rust/juniper (language: rust)

Issue:
Panic when using fragment of a implementation in a interface
**Describe the bug**
Juniper panic when using a fragment for an implementation directly in a interface.

**To Reproduce**
```rust
use juniper::*;

struct Query;

#[juniper::object]
impl Query {
    fn things() -> Vec<Thing> {
        vec![Thing::Foo(Foo { foo: 1 }), Thing::Bar(Bar { bar: 2 })]
    }
}

enum Thing {
    Foo(Foo),
    Bar(Bar),
}

juniper::graphql_interface!(Thing: () where Scalar = <S> |&self| {
    instance_resolvers: |_| {
        &Foo => match *self { Thing::Foo(ref foo) => Some(foo), _ => None },
        &Bar => match *self { Thing::Bar(ref bar) => Some(bar), _ => None },
    }
});

struct Foo {
    foo: i32,
}

#[juniper::object(
    interfaces = [&Thing],
    scalar = S
)]
impl<S: ScalarValue> Foo {
    fn foo(&self) -> i32 {
        self.foo
    }
}

struct Bar {
    bar: i32,
}

#[juniper::object(
    interfaces = [&Thing],
    scalar = S
)]
impl<S: ScalarValue> Bar {
    fn bar(&self) -> i32 {
        self.bar
    }
}

type Schema = juniper::RootNode<'static, Query, EmptyMutation<()>>;

#[test]
fn test_fragment_in_interface_does_not_work() {
    let _ = juniper::execute(
        r#"
            query Query {
                things {
                    ...fooFields
                    ...barFields
                }
            }

            fragment fooFields on Foo {
                foo
            }

            fragment barFields on Bar {
                bar
            }
        "#,
        None,
        &Schema::new(Query, EmptyMutation::new()),
        &Variables::new(),
        &(),
    )
    .unwrap();
}

#[test]
fn test_fragment_in_interface_works() {
    let _ = juniper::execute(
        r#"
            query Query {
                things {
                    ...on Foo {
                        ...fooFields
                    }
                    ...on Bar {
                        ...barFields
                    }
                }
            }

            fragment fooFields on Foo {
                foo
            }

            fragment barFields on Bar {
                bar
            }
        "#,
        None,
        &Schema::new(Query, EmptyMutation::new()),
        &Variables::new(),
        &(),
    )
    .unwrap();
}
```

Error:
`thread 'fragment_in_interface::test_fragment_in_interface_does_not_work' panicked at 'Field foo not found on type Some("Thing")', juniper\src\types\base.rs:385:21`

**Expected behavior**
Both queries should be equivalent.



Discussion / hints:
Just ran into the same issue while using relay 😅 
I fixed what I believe was a similar issue (https://github.com/graphql-rust/juniper/issues/500) in https://github.com/graphql-rust/juniper/pull/800/files. The fix for this may just be an extension of what I did?

Fix patch:
diff --git a/juniper/src/types/async_await.rs b/juniper/src/types/async_await.rs
index 3154db666..e1f6e6e1c 100644
--- a/juniper/src/types/async_await.rs
+++ b/juniper/src/types/async_await.rs
@@ -288,24 +288,47 @@ where
             }
 
             Selection::FragmentSpread(Spanning {
-                item: ref spread, ..
+                item: ref spread,
+                start: ref start_pos,
+                ..
             }) => {
                 if is_excluded(&spread.directives, executor.variables()) {
                     continue;
                 }
-                async_values.push(AsyncValueFuture::FragmentSpread(async move {
-                    let fragment = &executor
-                        .fragment_by_name(spread.name.item)
-                        .expect("Fragment could not be found");
-                    let value = resolve_selection_set_into_async(
-                        instance,
-                        info,
-                        &fragment.selection_set[..],
-                        executor,
-                    )
-                    .await;
-                    AsyncValue::Nested(value)
-                }));
+
+                let fragment = &executor
+                    .fragment_by_name(spread.name.item)
+                    .expect("Fragment could not be found");
+
+                let sub_exec = executor.type_sub_executor(
+                    Some(fragment.type_condition.item),
+                    Some(&fragment.selection_set[..]),
+                );
+
+                let concrete_type_name = instance.concrete_type_name(sub_exec.context(), info);
+                if fragment.type_condition.item == concrete_type_name {
+                    let sub_result = instance
+                        .resolve_into_type_async(
+                            info,
+                            fragment.type_condition.item,
+                            Some(&fragment.selection_set[..]),
+                            &sub_exec,
+                        )
+                        .await;
+
+                    if let Ok(Value::Object(obj)) = sub_result {
+                        for (k, v) in obj {
+                            async_values.push(AsyncValueFuture::FragmentSpread(async move {
+                                AsyncValue::Field(AsyncField {
+                                    name: k,
+                                    value: Some(v),
+                                })
+                            }));
+                        }
+                    } else if let Err(e) = sub_result {
+                        sub_exec.push_error_at(e, *start_pos);
+                    }
+                }
             }
 
             Selection::InlineFragment(Spanning {
diff --git a/juniper/src/types/base.rs b/juniper/src/types/base.rs
index 086722cdc..61d32ca84 100644
--- a/juniper/src/types/base.rs
+++ b/juniper/src/types/base.rs
@@ -497,7 +497,9 @@ where
                 }
             }
             Selection::FragmentSpread(Spanning {
-                item: ref spread, ..
+                item: ref spread,
+                start: ref start_pos,
+                ..
             }) => {
                 if is_excluded(&spread.directives, executor.variables()) {
                     continue;
@@ -507,14 +509,27 @@ where
                     .fragment_by_name(spread.name.item)
                     .expect("Fragment could not be found");
 
-                if !resolve_selection_set_into(
-                    instance,
-                    info,
-                    &fragment.selection_set[..],
-                    executor,
-                    result,
-                ) {
-                    return false;
+                let sub_exec = executor.type_sub_executor(
+                    Some(fragment.type_condition.item),
+                    Some(&fragment.selection_set[..]),
+                );
+
+                let concrete_type_name = instance.concrete_type_name(sub_exec.context(), info);
+                if fragment.type_condition.item == concrete_type_name {
+                    let sub_result = instance.resolve_into_type(
+                        info,
+                        fragment.type_condition.item,
+                        Some(&fragment.selection_set[..]),
+                        &sub_exec,
+                    );
+
+                    if let Ok(Value::Object(object)) = sub_result {
+                        for (k, v) in object {
+                            merge_key_into(result, &k, v);
+                        }
+                    } else if let Err(e) = sub_result {
+                        sub_exec.push_error_at(e, *start_pos);
+                    }
                 }
             }
             Selection::InlineFragment(Spanning {


Test patch:
diff --git a/integration_tests/juniper_tests/src/issue_407.rs b/integration_tests/juniper_tests/src/issue_407.rs
new file mode 100644
index 000000000..6b4e79fb2
--- /dev/null
+++ b/integration_tests/juniper_tests/src/issue_407.rs
@@ -0,0 +1,139 @@
+use juniper::*;
+
+struct Query;
+
+#[graphql_interface(for = [Human, Droid])]
+trait Character {
+    fn id(&self) -> &str;
+}
+
+#[derive(GraphQLObject)]
+#[graphql(impl = CharacterValue)]
+struct Human {
+    id: String,
+    name: String,
+}
+
+#[graphql_interface]
+impl Character for Human {
+    fn id(&self) -> &str {
+        &self.id
+    }
+}
+
+#[derive(GraphQLObject)]
+#[graphql(impl = CharacterValue)]
+struct Droid {
+    id: String,
+    serial_number: String,
+}
+
+#[graphql_interface]
+impl Character for Droid {
+    fn id(&self) -> &str {
+        &self.id
+    }
+}
+
+#[graphql_object]
+impl Query {
+    fn characters() -> Vec<CharacterValue> {
+        let human = Human {
+            id: "1".to_string(),
+            name: "Han Solo".to_string(),
+        };
+        let droid = Droid {
+            id: "2".to_string(),
+            serial_number: "234532545235".to_string(),
+        };
+        vec![Into::into(human), Into::into(droid)]
+    }
+}
+
+type Schema = juniper::RootNode<'static, Query, EmptyMutation, EmptySubscription>;
+
+#[tokio::test]
+async fn test_fragments_in_interface() {
+    let query = r#"
+        query Query {
+            characters {
+                ...HumanFragment
+                ...DroidFragment
+            }
+        }
+
+        fragment HumanFragment on Human {
+            name
+        }
+
+        fragment DroidFragment on Droid {
+            serialNumber
+        }
+    "#;
+
+    let (_, errors) = juniper::execute(
+        query,
+        None,
+        &Schema::new(Query, EmptyMutation::new(), EmptySubscription::new()),
+        &Variables::new(),
+        &(),
+    )
+    .await
+    .unwrap();
+    assert_eq!(errors.len(), 0);
+
+    let (_, errors) = juniper::execute_sync(
+        query,
+        None,
+        &Schema::new(Query, EmptyMutation::new(), EmptySubscription::new()),
+        &Variables::new(),
+        &(),
+    )
+    .unwrap();
+    assert_eq!(errors.len(), 0);
+}
+
+#[tokio::test]
+async fn test_inline_fragments_in_interface() {
+    let query = r#"
+        query Query {
+            characters {
+                ...on Human {
+                    ...HumanFragment
+                }
+                ...on Droid {
+                    ...DroidFragment
+                }
+            }
+        }
+
+        fragment HumanFragment on Human {
+            name
+        }
+
+        fragment DroidFragment on Droid {
+            serialNumber
+        }
+    "#;
+
+    let (_, errors) = juniper::execute(
+        query,
+        None,
+        &Schema::new(Query, EmptyMutation::new(), EmptySubscription::new()),
+        &Variables::new(),
+        &(),
+    )
+    .await
+    .unwrap();
+    assert_eq!(errors.len(), 0);
+
+    let (_, errors) = juniper::execute_sync(
+        query,
+        None,
+        &Schema::new(Query, EmptyMutation::new(), EmptySubscription::new()),
+        &Variables::new(),
+        &(),
+    )
+    .unwrap();
+    assert_eq!(errors.len(), 0);
+}
diff --git a/integration_tests/juniper_tests/src/lib.rs b/integration_tests/juniper_tests/src/lib.rs
index d4f0e94be..ac40e3355 100644
--- a/integration_tests/juniper_tests/src/lib.rs
+++ b/integration_tests/juniper_tests/src/lib.rs
@@ -15,4 +15,6 @@ mod issue_371;
 #[cfg(test)]
 mod issue_398;
 #[cfg(test)]
+mod issue_407;
+#[cfg(test)]
 mod issue_500;