types:
  - name: cannot be redeclared
    input: |
      type A {
        name: String
      }
      type A {
        name: String
      }
    error:
      message: "Cannot redeclare type A."
      locations: [{line: 4, column: 6}]
  - name: cannot be duplicated field at same definition 1
    input: |
      type A {
        name: String
        name: String
      }
    error:
      message: "Field A.name can only be defined once."
      locations: [{line: 3, column: 3}]
  - name: cannot be duplicated field at same definition 2
    input: |
      type A {
        name: String
      }
      extend type A {
        name: String
      }
    error:
      message: "Field A.name can only be defined once."
      locations: [{line: 5, column: 3}]
  - name: cannot be duplicated field at same definition 3
    input: |
      type A {
        name: String
      }
      extend type A {
        age: Int
        age: Int
      }
    error:
      message: "Field A.age can only be defined once."
      locations: [{line: 6, column: 3}]

object types:
  - name: must define one or more fields
    input: |
      directive @D on OBJECT

      # This pattern rejected by parser
      # type InvalidObject1 {}

      type InvalidObject2 @D

      type ValidObject {
        id: ID
      }
      extend type ValidObject @D
      extend type ValidObject {
        b: Int
      }
    error:
      message: 'OBJECT InvalidObject2: must define one or more fields.'
      locations: [{line: 6, column: 6}]
  - name: check reserved names on type name
    input: |
      type __FooBar {
        id: ID
      }
    error:
      message: 'Name "__FooBar" must not begin with "__", which is reserved by GraphQL introspection.'
      locations: [{line: 1, column: 6}]
  - name: check reserved names on type field
    input: |
      type FooBar {
        __id: ID
      }
    error:
      message: 'Name "__id" must not begin with "__", which is reserved by GraphQL introspection.'
      locations: [{line: 2, column: 3}]

  - name: field argument list must not be empty
    input: |
      type FooBar {
        foo(): ID
      }
    error:
      message: 'expected at least one definition, found )'
      locations: [{line: 2, column: 7}]

  - name: check reserved names on type field argument
    input: |
      type FooBar {
        foo(__bar: ID): ID
      }
    error:
      message: 'Name "__bar" must not begin with "__", which is reserved by GraphQL introspection.'
      locations: [{line: 2, column: 7}]

  - name: must not allow input object as field type
    input: |
      input Input {
        id: ID
      }
      type Query {
        input: Input!
      }
    error:
      message: 'OBJECT Query: field must be one of SCALAR, OBJECT, INTERFACE, UNION, ENUM.'
      locations: [{line: 5, column: 3}]

interfaces:
  - name: must exist
    input: |
      type Thing implements Object {
        id: ID!
      }

      type Query {
        Things: [Thing!]!
      }
    error:
      message: 'Undefined type "Object".'
      locations: [{line: 1, column: 6}]

  - name: must be an interface
    input: |
      type Thing implements Object {
        id: ID!
      }

      type Query {
        Things: [Thing!]!
      }

      type Object {
        name: String
      }
    error:
      message: '"Object" is a non interface type OBJECT.'
      locations: [{line: 1, column: 6}]

  - name: must define one or more fields
    input: |
      directive @D on INTERFACE

      # This pattern rejected by parser
      # interface InvalidInterface1 {}

      interface InvalidInterface2 @D

      interface ValidInterface {
        id: ID
      }
      extend interface ValidInterface @D
      extend interface ValidInterface {
        b: Int
      }
    error:
      message: 'INTERFACE InvalidInterface2: must define one or more fields.'
      locations: [{line: 6, column: 11}]

  - name: check reserved names on type name
    input: |
      interface __FooBar {
        id: ID
      }
    error:
      message: 'Name "__FooBar" must not begin with "__", which is reserved by GraphQL introspection.'
      locations: [{line: 1, column: 11}]

  - name: must not allow input object as field type
    input: |
      input Input {
        id: ID
      }
      type Query {
        foo: Foo!
      }
      interface Foo {
        input: Input!
      }
    error:
      message: 'INTERFACE Foo: field must be one of SCALAR, OBJECT, INTERFACE, UNION, ENUM.'
      locations: [{line: 8, column: 3}]

  - name: must have all fields from interface
    input: |
      type Bar implements BarInterface {
          someField: Int!
      }

      interface BarInterface {
          id: ID!
      }
    error:
      message: 'For Bar to implement BarInterface it must have a field called id.'
      locations: [{line: 1, column: 6}]

  - name: must have same type of fields
    input: |
      type Bar implements BarInterface {
          id: Int!
      }

      interface BarInterface {
          id: ID!
      }
    error:
      message: 'For Bar to implement BarInterface the field id must have type ID!.'
      locations: [{line: 2, column: 5}]

  - name: must have all required arguments
    input: |
      type Bar implements BarInterface {
          id: ID!
      }

      interface BarInterface {
          id(ff: Int!): ID!
      }
    error:
      message: 'For Bar to implement BarInterface the field id must have the same arguments but it is missing ff.'
      locations: [{line: 2, column: 5}]

  - name: must have same argument types
    input: |
      type Bar implements BarInterface {
          id(ff: ID!): ID!
      }

      interface BarInterface {
          id(ff: Int!): ID!
      }
    error:
      message: 'For Bar to implement BarInterface the field id must have the same arguments but ff has the wrong type.'
      locations: [{line: 2, column: 8}]

  - name: may defined additional nullable arguments
    input: |
      type Bar implements BarInterface {
          id(opt: Int): ID!
      }

      interface BarInterface {
          id: ID!
      }

  - name: may defined additional required arguments with defaults
    input: |
      type Bar implements BarInterface {
          id(opt: Int! = 1): ID!
      }

      interface BarInterface {
          id: ID!
      }

  - name: must not define additional required arguments without defaults
    input: |
      type Bar implements BarInterface {
          id(opt: Int!): ID!
      }

      interface BarInterface {
          id: ID!
      }
    error:
      message: 'For Bar to implement BarInterface any additional arguments on id must be optional or have a default value but opt is required.'
      locations: [{line: 2, column: 8}]

  - name: can have covariant argument types
    input: |
      union U = A|B

      type A { name: String }
      type B { name: String }

      type Bar implements BarInterface {
          f: A!
      }

      interface BarInterface {
          f: U!
      }

  - name: may define intermediate interfaces
    input: |
      interface IA {
          id: ID!
      }

      interface IIA implements IA {
          id: ID!
      }

      type A implements IIA & IA {
          id: ID!
      }

  - name: Type Foo must implement Baz because it is implemented by Bar
    input: |
      interface Baz {
          baz: String
      }

      interface Bar implements Baz {
          bar: String
          baz: String
      }

      type Foo implements Bar {
          foo: String
          bar: String
          baz: String
      }
    error:
      message: 'Type Foo must implement Baz because it is implemented by Bar.'
      locations: [{line: 10, column: 6}]

  - name: circular reference error
    input: |
      interface Circular1 implements Circular2 {
        id: ID!
      }

      interface Circular2 implements Circular1 {
        id: ID!
      }
    error:
      message: 'Type Circular1 cannot implement Circular2 because it would create a circular reference.'
      locations: [{line: 1, column: 11}]

inputs:
  - name: must define one or more input fields
    input: |
      directive @D on INPUT_OBJECT

      # This pattern rejected by parser
      # input InvalidInput1 {}

      input InvalidInput2 @D

      input ValidInput {
        id: ID
      }
      extend input ValidInput @D
      extend input ValidInput {
        b: Int
      }
    error:
      message: 'INPUT_OBJECT InvalidInput2: must define one or more input fields.'
      locations: [{line: 6, column: 7}]
  - name: check reserved names on type name
    input: |
      input __FooBar {
        id: ID
      }
    error:
      message: 'Name "__FooBar" must not begin with "__", which is reserved by GraphQL introspection.'
      locations: [{line: 1, column: 7}]

  - name: fields cannot be Objects
    input: |
      type Object { id: ID }
      input Foo { a: Object! }
    error:
      message: 'OBJECT a: field must be one of SCALAR, ENUM, INPUT_OBJECT.'
      locations: [{line: 2, column: 13}]

  - name: fields cannot be Interfaces
    input: |
      interface Interface { id: ID! }
      input Foo { a: Interface! }
    error:
      message: 'INTERFACE a: field must be one of SCALAR, ENUM, INPUT_OBJECT.'
      locations: [{line: 2, column: 13}]

  - name: fields cannot be Unions
    input: |
      type Object { id: ID }
      union Union = Object
      input Foo { a: Union! }
    error:
      message: 'UNION a: field must be one of SCALAR, ENUM, INPUT_OBJECT.'
      locations: [{line: 3, column: 13}]

args:
  - name: Valid arg types
    input: |
        input Input { id: ID }
        enum Enum { A }
        scalar Scalar

        type Query {
          f(a: Input, b: Scalar, c: Enum): Boolean!
        }

  - name: Objects not allowed
    input: |
      type Object { id: ID }
      type Query { f(a: Object): Boolean! }

    error:
      message: 'cannot use Object as argument a because OBJECT is not a valid input type'
      locations: [{line: 2, column: 16}]

  - name: Union not allowed
    input: |
      type Object { id: ID }
      union Union = Object
      type Query { f(a: Union): Boolean! }

    error:
      message: 'cannot use Union as argument a because UNION is not a valid input type'
      locations: [{line: 3, column: 16}]

  - name: Interface not allowed
    input: |
      interface Interface { id: ID }
      type Query { f(a: Interface): Boolean! }

    error:
      message: 'cannot use Interface as argument a because INTERFACE is not a valid input type'
      locations: [{line: 2, column: 16}]

enums:
  - name: must define one or more unique enum values
    input: |
      directive @D on ENUM

      # This pattern rejected by parser
      # enum InvalidEmum1 {}

      enum InvalidEnum2 @D

      enum ValidEnum {
        FOO
      }
      extend enum ValidEnum @D
      extend enum ValidEnum {
        BAR
      }
    error:
      message: 'ENUM InvalidEnum2: must define one or more unique enum values.'
      locations: [{line: 6, column: 6}]
  - name: check reserved names on type name
    input: |
      enum __FooBar {
        A
        B
      }
    error:
      message: 'Name "__FooBar" must not begin with "__", which is reserved by GraphQL introspection.'
      locations: [{line: 1, column: 6}]

unions:
  - name: union types must be defined
    input: |
      union Foo = Bar | Baz
      type Bar {
        id: ID
      }
    error:
      message: "Undefined type \"Baz\"."
      locations: [{line: 1, column: 7}]
  - name: union types must be objects
    input: |
      union Foo = Baz
      interface Baz {
        id: ID
      }
    error:
      message: "UNION type \"Baz\" must be OBJECT."
      locations: [{line: 1, column: 7}]

  - name: unions of pure type extensions are valid
    input: |

      type Review {
          body: String!
          author: User! @provides(fields: "username")
          product: Product!
      }

      extend type User @key(fields: "id") {
        id: ID! @external
        reviews: [Review]
      }

      extend type Product @key(fields: "upc") {
        upc: String! @external
        reviews: [Review]
      }

      union Foo = User | Product
      scalar _Any
      scalar _FieldSet
      directive @external on FIELD_DEFINITION
      directive @requires(fields: _FieldSet!) on FIELD_DEFINITION
      directive @provides(fields: _FieldSet!) on FIELD_DEFINITION
      directive @key(fields: _FieldSet!) on OBJECT | INTERFACE
      directive @extends on OBJECT



type extensions:
  - name: can extend non existant types
    input: |
      extend type A {
        name: String
      }


  - name: cannot extend incorret type existant types
    input: |
      scalar A
      extend type A {
        name: String
      }
    error:
      message: "Cannot extend type A because the base type is a SCALAR, not OBJECT."
      locations: [{line: 2, column: 13}]

directives:
  - name: cannot redeclare directives
    input: |
      directive @A on FIELD_DEFINITION
      directive @A on FIELD_DEFINITION
    error:
      message: "Cannot redeclare directive A."
      locations: [{line: 2, column: 12}]

  - name: can redeclare builtin directives
    input: |
      directive @skip(if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT
      directive @skip(if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT

  - name: must be declared (type)
    input: |
      type User @foo {
        name: String
      }
    error:
      message: "Undefined directive foo."
      locations: [{line: 1, column: 12}]

  - name: must be declared (field)
    input: |
      type User {
        name: String @foo
      }
    error:
      message: "Undefined directive foo."
      locations: [{line: 2, column: 17}]

  - name: must be declared (enum)
    input: |
      enum Unit {
        METER @foo
      }
    error:
      message: "Undefined directive foo."
      locations: [{line: 2, column: 10}]

  - name: cannot be self-referential
    input: |
      directive @A(foo: Int! @A) on FIELD_DEFINITION
    error:
      message: "Directive A cannot refer to itself."
      locations: [{line: 1, column: 25}]
  - name: check reserved names on type name
    input: |
      directive @__A on FIELD_DEFINITION
    error:
      message: 'Name "__A" must not begin with "__", which is reserved by GraphQL introspection.'
      locations: [{line: 1, column: 12}]

  - name: Valid arg types
    input: |
      input Input { id: ID }
      enum Enum { A }
      scalar Scalar

      directive @A(a: Input, b: Scalar, c: Enum) on FIELD_DEFINITION

  - name: Objects not allowed
    input: |
      type Object { id: ID }
      directive @A(a: Object) on FIELD_DEFINITION

    error:
      message: 'cannot use Object as argument a because OBJECT is not a valid input type'
      locations: [{line: 2, column: 14}]

  - name: Union not allowed
    input: |
      type Object { id: ID }
      union Union = Object
      directive @A(a: Union) on FIELD_DEFINITION

    error:
      message: 'cannot use Union as argument a because UNION is not a valid input type'
      locations: [{line: 3, column: 14}]

  - name: Interface not allowed
    input: |
      interface Interface { id: ID }
      directive @A(a: Interface) on FIELD_DEFINITION

    error:
      message: 'cannot use Interface as argument a because INTERFACE is not a valid input type'
      locations: [{line: 2, column: 14}]

  - name: Invalid location usage not allowed
    input: |
      directive @test on FIELD_DEFINITION
      input I1 @test { f: String }

    error:
      message: 'Directive test is not applicable on INPUT_OBJECT.'
      locations: [{line: 2, column: 11}]

  - name: Valid location usage
    input: |
      directive @testInputField on INPUT_FIELD_DEFINITION
      directive @testField on FIELD_DEFINITION
      directive @inp on INPUT_OBJECT
      input I1 @inp { f: String @testInputField }
      type P { name: String @testField }
      interface I { id: ID @testField }

  - name: Invalid directive argument not allowed
    input: |
      directive @foo(bla: Int!) on FIELD_DEFINITION
      type P {f: Int @foo(foobla: 11)}
    
    error:
      message: 'Undefined argument foobla for directive foo.'
      locations: [{line: 2, column: 21}]

  - name: non-null argument must be provided
    input: |
      directive @foo(bla: Int!) on FIELD_DEFINITION
      type P {f: Int @foo }
    
    error:
      message: 'Argument bla for directive foo cannot be null.'
      locations: [{line: 2, column: 17}]

  - name: non-null argument must not be null
    input: |
      directive @foo(bla: Int!) on FIELD_DEFINITION
      type P {f: Int @foo(bla: null) }
    
    error:
      message: 'Argument bla for directive foo cannot be null.'
      locations: [{line: 2, column: 17}]

entry points:
  - name: multiple schema entry points
    input: |
      schema {
        query: Query
      }
      schema {
        query: Query
      }
      scalar Query
    error:
      message: "Cannot have multiple schema entry points, consider schema extensions instead."
      locations: [{line: 4, column: 8}]

  - name: Undefined schema entrypoint
    input: |
      schema {
        query: Query
      }
    error:
      message: "Schema root query refers to a type Query that does not exist."
      locations: [{line: 2, column: 3}]

entry point extensions:
  - name: Undefined schema entrypoint
    input: |
      schema {
        query: Query
      }
      scalar Query
      extend schema {
        mutation: Mutation
      }
    error:
      message: "Schema root mutation refers to a type Mutation that does not exist."
      locations: [{line: 6, column: 3}]

type references:
  - name: Field types
    input: |
      type User {
        posts: Post
      }
    error:
      message: "Undefined type Post."
      locations: [{line: 2, column: 10}]

  - name: Arg types
    input: |
      type User {
        posts(foo: FooBar): String
      }
    error:
      message: "Undefined type FooBar."
      locations: [{line: 2, column: 14}]

  - name: Directive arg types
    input: |
      directive @Foo(foo: FooBar) on FIELD_DEFINITION

    error:
      message: "Undefined type FooBar."
      locations: [{line: 1, column: 21}]

  - name: Invalid enum value
    input: |
      enum Enum { true }

    error:
      message: "ENUM Enum: non-enum value true."
      locations: [{line: 1, column: 6}]
