use std::fmt::Debug;
use crate::compat::graphql_parser_v0_4::to_graphql_parser_query_ast;
use crate::compat::graphql_parser_v0_4::to_graphql_parser_schema_ast;
use crate::GraphQLParser;
fn assert_ast_eq<T: Debug + PartialEq>(
actual: &T,
expected: &T,
source: &str,
kind: &str,
) {
if actual == expected {
return;
}
let actual_lines: Vec<&str> =
format!("{actual:#?}").leak().lines().collect();
let expected_lines: Vec<&str> =
format!("{expected:#?}").leak().lines().collect();
let mut diff = String::new();
let max_lines = actual_lines.len().max(expected_lines.len());
for i in 0..max_lines {
let a = actual_lines.get(i).copied().unwrap_or("");
let e = expected_lines.get(i).copied().unwrap_or("");
if a != e {
diff.push_str(&format!(
" line {i}:\n\
\x20 ours: {a}\n\
\x20 expected: {e}\n",
));
}
}
panic!(
"\n\nGround-truth {kind} mismatch.\n\n\
Source:\n{source}\n\n\
Differing lines:\n{diff}",
);
}
fn assert_schema_ground_truth(source: &str) {
let expected =
graphql_parser::schema::parse_schema::<String>(source)
.unwrap_or_else(|e| {
panic!(
"graphql_parser failed to parse \
schema:\n{e}\n\nSource:\n{source}",
)
})
.into_static();
let our_ast = GraphQLParser::new(source)
.parse_schema_document();
assert!(
!our_ast.has_errors(),
"Our parser reported errors:\n{}\n\nSource:\n{source}",
our_ast.formatted_errors(),
);
let (our_doc, _) = our_ast.into_valid().expect(
"valid_ast should be Some when no errors",
);
let sm = crate::SourceMap::new_with_source(
source, None,
);
let actual = to_graphql_parser_schema_ast(
&our_doc, &sm,
)
.into_ast();
assert_ast_eq(&actual, &expected, source, "schema");
}
fn assert_query_ground_truth(source: &str) {
let expected =
graphql_parser::query::parse_query::<String>(source)
.unwrap_or_else(|e| {
panic!(
"graphql_parser failed to parse \
query:\n{e}\n\nSource:\n{source}",
)
})
.into_static();
let our_ast = GraphQLParser::new(source)
.parse_executable_document();
assert!(
!our_ast.has_errors(),
"Our parser reported errors:\n{}\n\nSource:\n{source}",
our_ast.formatted_errors(),
);
let (our_doc, _) = our_ast.into_valid().expect(
"valid_ast should be Some when no errors",
);
let sm = crate::SourceMap::new_with_source(
source, None,
);
let actual = to_graphql_parser_query_ast(
&our_doc, &sm,
)
.into_ast();
assert_ast_eq(&actual, &expected, source, "query");
}
#[test]
fn test_schema_ground_truth_simple_object() {
assert_schema_ground_truth(
"\
type User {
id: ID!
name: String
age: Int
}
",
);
}
#[test]
fn test_schema_ground_truth_described_fields() {
assert_schema_ground_truth(
"\
type User {
\"The unique identifier\"
id: ID!
\"The user's display name\"
name: String
}
",
);
}
#[test]
fn test_schema_ground_truth_scalar_with_directive() {
assert_schema_ground_truth(
"\
scalar DateTime @specifiedBy(url: \"https://scalars.graphql.org/andimarek/date-time\")
",
);
}
#[test]
fn test_schema_ground_truth_enum_with_descriptions() {
assert_schema_ground_truth(
"\
enum Status {
\"Currently active\"
ACTIVE
\"No longer active\"
INACTIVE
PENDING
}
",
);
}
#[test]
fn test_schema_ground_truth_union() {
assert_schema_ground_truth(
"\
union SearchResult = User | Post | Comment
",
);
}
#[test]
fn test_schema_ground_truth_input_with_descriptions() {
assert_schema_ground_truth(
"\
input CreateUserInput {
\"The user's name\"
name: String!
\"Optional email\"
email: String
age: Int = 0
}
",
);
}
#[test]
fn test_schema_ground_truth_interface() {
assert_schema_ground_truth(
"\
interface Node {
id: ID!
}
interface NamedNode implements Node {
id: ID!
name: String
}
",
);
}
#[test]
fn test_schema_ground_truth_directive_definition() {
assert_schema_ground_truth(
"\
directive @cacheControl(maxAge: Int, scope: CacheControlScope) repeatable on FIELD_DEFINITION | OBJECT | INTERFACE
",
);
}
#[test]
fn test_schema_ground_truth_schema_definition() {
assert_schema_ground_truth(
"\
schema {
query: Query
mutation: Mutation
subscription: Subscription
}
",
);
}
#[test]
fn test_schema_ground_truth_type_extensions() {
assert_schema_ground_truth(
"\
extend type User {
email: String
}
extend enum Status {
ARCHIVED
}
extend union SearchResult = Comment
extend interface Node {
createdAt: DateTime
}
extend scalar DateTime @specifiedBy(url: \"https://scalars.graphql.org/andimarek/date-time\")
extend input CreateUserInput {
role: String
}
",
);
}
#[test]
fn test_schema_ground_truth_complex_document() {
assert_schema_ground_truth(
"\
\"The root query type\"
type Query {
\"Fetch a user by ID\"
user(id: ID!): User
users(first: Int = 10, after: String): [User!]!
}
type User implements Node {
id: ID!
name: String!
email: String
role: Role!
}
interface Node {
id: ID!
}
enum Role {
ADMIN
USER
GUEST
}
union SearchResult = User | Post
input CreateUserInput {
name: String!
email: String
role: Role = USER
}
scalar DateTime
directive @deprecated(reason: String = \"No longer supported\") on FIELD_DEFINITION | ENUM_VALUE
",
);
}
#[test]
fn test_query_ground_truth_simple_query() {
assert_query_ground_truth(
"\
query GetUser {
user {
id
name
}
}
",
);
}
#[test]
fn test_query_ground_truth_shorthand() {
assert_query_ground_truth(
"\
{
viewer {
name
}
}
",
);
}
#[test]
fn test_query_ground_truth_mutation() {
assert_query_ground_truth(
"\
mutation CreateUser {
createUser(name: \"Alice\") {
id
name
}
}
",
);
}
#[test]
fn test_query_ground_truth_subscription() {
assert_query_ground_truth(
"\
subscription OnMessageAdded {
messageAdded {
id
content
author {
name
}
}
}
",
);
}
#[test]
fn test_query_ground_truth_variables() {
assert_query_ground_truth(
"\
query GetUsers($first: Int = 10, $after: String, $includeEmail: Boolean!) {
users(first: $first, after: $after) {
id
name
}
}
",
);
}
#[test]
fn test_query_ground_truth_fragments() {
assert_query_ground_truth(
"\
query GetUser {
user(id: 1) {
...UserFields
}
}
fragment UserFields on User {
id
name
email
}
",
);
}
#[test]
fn test_query_ground_truth_inline_fragments() {
assert_query_ground_truth(
"\
query Search {
search(query: \"test\") {
... on User {
name
email
}
... on Post {
title
body
}
}
}
",
);
}
#[test]
fn test_query_ground_truth_aliases_and_value_types() {
assert_query_ground_truth(
"\
query ValueTypes {
intVal: field(arg: 42)
floatVal: field(arg: 3.14)
stringVal: field(arg: \"hello\")
boolTrue: field(arg: true)
boolFalse: field(arg: false)
nullVal: field(arg: null)
enumVal: field(arg: ACTIVE)
listVal: field(arg: [1, 2, 3])
objectVal: field(arg: {key: \"value\", nested: {a: 1}})
}
",
);
}
#[test]
fn test_query_ground_truth_deep_nesting() {
assert_query_ground_truth(
"\
query DeepQuery {
viewer {
organization {
teams {
members {
name
}
}
}
}
}
",
);
}
#[test]
fn test_schema_ground_truth_type_annotation_variety() {
assert_schema_ground_truth(
"\
type TypeAnnotations {
nullableList: [String]
nonNullList: [String!]!
nestedList: [[Int]]
complexNested: [[Int!]!]!
}
",
);
}
#[test]
fn test_schema_ground_truth_block_string_descriptions() {
assert_schema_ground_truth(
r#"
"""
A user in the system.
Represents an authenticated account with profile data.
"""
type User {
"""The unique identifier."""
id: ID!
"""
The user's display name.
May contain unicode characters.
"""
name: String
}
"#,
);
}
#[test]
fn test_query_ground_truth_variable_refs_and_negative_numbers() {
assert_query_ground_truth(
"\
query Fetch($id: ID!, $limit: Int) {
user(id: $id) {
posts(limit: $limit, offset: -5) {
score(threshold: -1.5)
}
}
}
",
);
}
#[test]
fn test_query_ground_truth_directives() {
assert_query_ground_truth(
"\
query GetUser($withEmail: Boolean!) @cacheControl(maxAge: 60) {
user(id: 1) {
id
name
email @include(if: $withEmail)
...UserExtra @skip(if: true)
}
}
fragment UserExtra on User {
age
role
}
",
);
}