use gqls::example;
use gqls::load::{self, LoadOptions};
use gqls::model::SchemaRecord;
const SCHEMA: &str = "examples/schema.graphql";
fn records() -> Vec<SchemaRecord> {
let opts = LoadOptions {
refresh: true,
..Default::default()
};
load::load(SCHEMA, &opts).expect("the bundled example schema should load")
}
fn draft(path: &str) -> example::Example {
draft_deep(path, 1)
}
fn draft_deep(path: &str, depth: usize) -> example::Example {
let records = records();
let target = records
.iter()
.find(|r| r.path == path)
.unwrap_or_else(|| panic!("{path} missing from {SCHEMA}"));
example::build(target, &records, depth).expect("drafting should succeed")
}
#[test]
fn drafts_a_root_query_with_typed_variables() {
let ex = draft("Query.user");
graphql_parser::parse_query::<String>(&ex.operation).expect("drafted invalid GraphQL");
assert!(
ex.operation.starts_with("query User($id: ID!) {"),
"{}",
ex.operation
);
assert_eq!(ex.variables, serde_json::json!({ "id": "<ID!>" }));
assert!(ex.operation.contains("\n email\n"), "{}", ex.operation);
assert!(
ex.operation.contains("# posts: Post — add fields you need"),
"{}",
ex.operation
);
}
#[test]
fn expands_input_objects_and_the_enums_they_reference() {
let ex = draft("Mutation.createUser");
graphql_parser::parse_query::<String>(&ex.operation).expect("drafted invalid GraphQL");
let blocks: Vec<String> = ex.input_types.iter().map(|b| b.join("\n")).collect();
let input = blocks
.iter()
.find(|b| b.starts_with("CreateUserInput {"))
.unwrap_or_else(|| panic!("no CreateUserInput block in {blocks:?}"));
assert!(input.contains("name: String!"), "{input}");
assert!(input.contains("role: Role"), "{input}");
assert!(
blocks.iter().any(|b| b.starts_with("Role = ")),
"enum not expanded: {blocks:?}"
);
}
#[test]
fn omits_a_deprecated_fields_arguments_it_does_not_need() {
let ex = draft("Mutation.deleteUser");
graphql_parser::parse_query::<String>(&ex.operation).expect("drafted invalid GraphQL");
assert!(
ex.operation.starts_with("mutation DeleteUser($id: ID!) {"),
"{}",
ex.operation
);
assert!(
ex.operation.contains("deleteUser(id: $id)\n"),
"{}",
ex.operation
);
}
#[test]
fn wraps_a_nested_field_in_a_root_that_returns_its_type() {
let ex = draft("User.email");
graphql_parser::parse_query::<String>(&ex.operation).expect("drafted invalid GraphQL");
assert_eq!(ex.via.as_deref(), Some("Query.users"));
assert!(
ex.alternatives.iter().any(|a| a == "Query.user"),
"{:?}",
ex.alternatives
);
assert!(ex.operation.contains("users {"), "{}", ex.operation);
assert!(ex.operation.contains("email"), "{}", ex.operation);
assert_eq!(ex.variables, serde_json::json!({}));
}
#[test]
fn a_union_return_becomes_inline_fragments_over_its_members() {
let ex = draft("Query.search");
graphql_parser::parse_query::<String>(&ex.operation).expect("drafted invalid GraphQL");
assert!(ex.operation.contains("__typename"), "{}", ex.operation);
assert!(ex.operation.contains("... on User {"), "{}", ex.operation);
assert!(ex.operation.contains("... on Post {"), "{}", ex.operation);
assert!(ex.operation.contains("email"), "{}", ex.operation);
assert!(ex.operation.contains("title"), "{}", ex.operation);
}
#[test]
fn an_interface_reaches_its_implementors_extra_fields() {
let sdl = "\
type Query { node(id: ID!): Node }\n\
interface Node { id: ID! createdAt: String! }\n\
type Article implements Node { id: ID! createdAt: String! headline: String! }\n\
type Video implements Node { id: ID! createdAt: String! streamUrl: String! }\n";
let records = gqls::load::sdl::from_sdl(sdl).expect("should parse");
let target = records.iter().find(|r| r.path == "Query.node").unwrap();
let ex = example::build(target, &records, 1).expect("drafting should succeed");
graphql_parser::parse_query::<String>(&ex.operation).expect("drafted invalid GraphQL");
assert!(ex.operation.contains("\n id\n"), "{}", ex.operation);
assert!(ex.operation.contains("createdAt"), "{}", ex.operation);
assert!(
ex.operation.contains("... on Article {"),
"{}",
ex.operation
);
assert!(ex.operation.contains("headline"), "{}", ex.operation);
assert!(ex.operation.contains("streamUrl"), "{}", ex.operation);
assert_eq!(
ex.operation.matches("createdAt").count(),
1,
"{}",
ex.operation
);
}
#[test]
fn depth_expands_the_fields_that_level_one_leaves_as_markers() {
let shallow = draft_deep("Query.user", 1);
assert!(
shallow
.operation
.contains("# posts: Post — add fields you need"),
"{}",
shallow.operation
);
let deep = draft_deep("Query.user", 2);
graphql_parser::parse_query::<String>(&deep.operation).expect("drafted invalid GraphQL");
assert!(deep.operation.contains("posts {"), "{}", deep.operation);
assert!(deep.operation.contains("title"), "{}", deep.operation);
assert!(
deep.operation
.contains("# author: User — add fields you need"),
"{}",
deep.operation
);
}
#[test]
fn a_deprecated_target_is_reported_and_still_drafted() {
let ex = draft("Mutation.deleteUser");
graphql_parser::parse_query::<String>(&ex.operation).expect("drafted invalid GraphQL");
assert_eq!(ex.deprecated, ["Mutation.deleteUser (use archiveUser)"]);
assert!(
ex.operation.contains("deleteUser(id: $id)"),
"{}",
ex.operation
);
}