use cynic::QueryFragment; use serde::Serialize; use serde_json::json; mod schema { cynic::use_schema!("tests/test-schema.graphql"); } mod recursive_lists { use super::*; #[derive(QueryFragment, Serialize)] #[cynic(graphql_type = "Query", schema_path = "tests/test-schema.graphql")] struct AllPostsQuery { all_posts: Vec, } #[derive(QueryFragment, Serialize)] #[cynic(graphql_type = "BlogPost", schema_path = "tests/test-schema.graphql")] struct Post { comments: Vec, } #[derive(QueryFragment, Serialize)] #[cynic(schema_path = "tests/test-schema.graphql")] struct Comment { author: Author, } #[derive(QueryFragment, Serialize)] #[cynic(schema_path = "tests/test-schema.graphql")] struct Author { #[cynic(recurse = "2")] posts: Option>, } #[test] fn test_all_posts_query_output() { use cynic::QueryBuilder; let operation = AllPostsQuery::build(()); insta::assert_display_snapshot!(operation.query); } #[test] fn test_decoding_with_matching_depth() { let data = json!({ "allPosts": posts(posts(posts(json!(null)))) }); insta::assert_yaml_snapshot!(serde_json::from_value::(data).unwrap()); } fn posts(inner_posts: serde_json::Value) -> serde_json::Value { json!([ { "comments": [{ "author": { "posts": inner_posts } }] } ]) } } mod optional_recursive_types { use super::*; #[derive(QueryFragment, Serialize)] #[cynic(graphql_type = "Query", schema_path = "tests/test-schema.graphql")] struct FriendsQuery { all_authors: Vec, } #[derive(QueryFragment, Serialize)] #[cynic(schema_path = "tests/test-schema.graphql")] struct Author { #[cynic(recurse = "2")] friends: Option>, #[cynic(recurse = "2")] referrer: Box>, } #[test] fn test_friends_query_output() { use cynic::QueryBuilder; let operation = FriendsQuery::build(()); insta::assert_display_snapshot!(operation.query); } macro_rules! null { () => { json!(null) }; } macro_rules! empty_object { () => { json!(null) }; } #[test] fn test_friends_decoding_with_matching_depth() { let data = json!({ "allAuthors": authors( authors(authors(null!(), null!()), author(empty_object!(), empty_object!())), author(authors(null!(), null!()), author(empty_object!(), empty_object!())), ) }); insta::assert_yaml_snapshot!(serde_json::from_value::(data).unwrap()); } #[test] fn test_friends_decoding_with_less_depth() { // This is only a valid test for optional fields. let data = json!({ "allAuthors": authors(authors(null!(), null!()), author(null!(), null!())) }); insta::assert_yaml_snapshot!(serde_json::from_value::(data).unwrap()); } fn authors( inner_friends: serde_json::Value, inner_referrer: serde_json::Value, ) -> serde_json::Value { let a = author(inner_friends, inner_referrer); json!([a]) } fn author( inner_friends: serde_json::Value, inner_referrer: serde_json::Value, ) -> serde_json::Value { json!({ "friends": inner_friends, "referrer": inner_referrer }) } } mod required_recursive_types { use super::*; #[derive(QueryFragment, Serialize)] #[cynic(graphql_type = "Query", schema_path = "tests/test-schema.graphql")] struct FriendsQuery { all_authors: Vec, } #[derive(QueryFragment, Serialize)] #[cynic(schema_path = "tests/test-schema.graphql")] struct Author { #[cynic(recurse = "2")] me: Option>, } #[test] fn test_friends_query_output() { use cynic::QueryBuilder; let operation = FriendsQuery::build(()); insta::assert_display_snapshot!(operation.query, @r###" query FriendsQuery { allAuthors { me { me } } } "###); } #[test] fn test_friends_decoding_with_matching_depth() { let data = json!({ "allAuthors": [{"me": {"me": {}}}]}); insta::assert_yaml_snapshot!(serde_json::from_value::(data).unwrap()); } #[test] fn test_friends_decoding_with_less_depth() { let data = json!({ "allAuthors": [{"me": null}]}); insta::assert_yaml_snapshot!(serde_json::from_value::(data).unwrap()); } } mod recursing_through_inline_fragments { use cynic_proc_macros::InlineFragments; #[test] fn test_inline_fragment_preserves_recurse_behaviour() { use super::*; #[derive(QueryFragment, Serialize)] #[cynic(graphql_type = "Query", schema_path = "tests/test-schema.graphql")] struct AllDataQuery { all_data: Vec, } #[derive(InlineFragments, Serialize)] #[cynic(schema_path = "tests/test-schema.graphql")] enum PostOrAuthor { Author(Author), #[cynic(fallback)] Other, } #[derive(QueryFragment, Serialize)] #[cynic(schema_path = "tests/test-schema.graphql")] struct Author { #[cynic(recurse = "2")] silly_me: Option>, } use cynic::QueryBuilder; let operation = AllDataQuery::build(()); insta::assert_display_snapshot!(operation.query, @r###" query AllDataQuery { allData { __typename ... on Author { sillyMe { __typename ... on Author { sillyMe { __typename } } } } } } "###); } } mod recursing_without_recursse { use cynic_proc_macros::InlineFragments; #[test] #[should_panic( expected = "Maximum query depth exceeded. Have you forgotten to mark a query as recursive?" )] fn test_recursion_without_recurse_panics_correctly() { // This example _should_ hit a panic I've added rather than just overflowing // the stack. This test makes sure it does. use super::*; #[derive(QueryFragment, Serialize)] #[cynic(graphql_type = "Query", schema_path = "tests/test-schema.graphql")] struct AllDataQuery { all_data: Vec, } #[derive(InlineFragments, Serialize)] #[cynic(schema_path = "tests/test-schema.graphql")] enum PostOrAuthor { Author(Author), Post(Post), #[cynic(fallback)] Other, } #[derive(QueryFragment, Serialize)] #[cynic(graphql_type = "BlogPost", schema_path = "tests/test-schema.graphql")] struct Post { __typename: String, } #[derive(QueryFragment, Serialize)] #[cynic(schema_path = "tests/test-schema.graphql")] struct Author { // #[cynic(recurse = "2")] #[cynic(flatten)] friends: Vec, } use cynic::QueryBuilder; AllDataQuery::build(()); } }