Failed Conditions
Push — master ( 6e7cf2...804daa )
by Vladimir
04:26
created

testCustomScalarArgumentFieldWithDefault()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 11
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 3
dl 0
loc 11
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
1
<?php
2
namespace GraphQL\Tests\Utils;
3
4
use GraphQL\Error\Error;
5
use GraphQL\GraphQL;
6
use GraphQL\Language\AST\EnumTypeDefinitionNode;
7
use GraphQL\Language\AST\InterfaceTypeDefinitionNode;
8
use GraphQL\Language\AST\ObjectTypeDefinitionNode;
9
use GraphQL\Language\Parser;
10
use GraphQL\Language\Printer;
11
use GraphQL\Type\Definition\EnumType;
12
use GraphQL\Type\Definition\ObjectType;
13
use GraphQL\Utils\BuildSchema;
14
use GraphQL\Utils\SchemaPrinter;
15
use GraphQL\Type\Definition\Directive;
16
use PHPUnit\Framework\TestCase;
17
18
class BuildSchemaTest extends TestCase
19
{
20
    // Describe: Schema Builder
21
22
    private function cycleOutput($body, $options = [])
23
    {
24
        $ast = Parser::parse($body);
25
        $schema = BuildSchema::buildAST($ast, null, $options);
26
        return "\n" . SchemaPrinter::doPrint($schema, $options);
27
    }
28
29
    /**
30
     * @it can use built schema for limited execution
31
     */
32
    public function testUseBuiltSchemaForLimitedExecution()
33
    {
34
        $schema = BuildSchema::buildAST(Parser::parse('
35
            type Query {
36
                str: String
37
            }
38
        '));
39
40
        $result = GraphQL::executeQuery($schema, '{ str }', ['str' => 123]);
41
        $this->assertEquals(['str' => 123], $result->toArray(true)['data']);
42
    }
43
44
    /**
45
     * @it can build a schema directly from the source
46
     */
47
    public function testBuildSchemaDirectlyFromSource()
48
    {
49
        $schema = BuildSchema::build("
50
            type Query {
51
                add(x: Int, y: Int): Int
52
            }
53
        ");
54
55
        $root = [
56
            'add' => function ($root, $args) {
57
                return $args['x'] + $args['y'];
58
            }
59
        ];
60
61
        $result = GraphQL::executeQuery(
62
            $schema,
63
            '{ add(x: 34, y: 55) }',
64
            $root
65
        );
66
        $this->assertEquals(['data' => ['add' => 89]], $result->toArray(true));
67
    }
68
69
    /**
70
     * @it Simple Type
71
     */
72
    public function testSimpleType()
73
    {
74
        $body = '
75
type HelloScalars {
76
  str: String
77
  int: Int
78
  float: Float
79
  id: ID
80
  bool: Boolean
81
}
82
';
83
        $output = $this->cycleOutput($body);
84
        $this->assertEquals($output, $body);
85
    }
86
87
    /**
88
     * @it With directives
89
     */
90
    public function testWithDirectives()
91
    {
92
        $body = '
93
directive @foo(arg: Int) on FIELD
94
95
type Query {
96
  str: String
97
}
98
';
99
        $output = $this->cycleOutput($body);
100
        $this->assertEquals($output, $body);
101
    }
102
103
    /**
104
     * @it Supports descriptions
105
     */
106
    public function testSupportsDescriptions()
107
    {
108
      $body = '
109
"""This is a directive"""
110
directive @foo(
111
  """It has an argument"""
112
  arg: Int
113
) on FIELD
114
115
"""With an enum"""
116
enum Color {
117
  RED
118
119
  """Not a creative color"""
120
  GREEN
121
  BLUE
122
}
123
124
"""What a great type"""
125
type Query {
126
  """And a field to boot"""
127
  str: String
128
}
129
';
130
131
        $output = $this->cycleOutput($body);
132
        $this->assertEquals($body, $output);
133
    }
134
135
    /**
136
     * @it Supports option for comment descriptions
137
     */
138
    public function testSupportsOptionForCommentDescriptions()
139
    {
140
        $body = '
141
# This is a directive
142
directive @foo(
143
  # It has an argument
144
  arg: Int
145
) on FIELD
146
147
# With an enum
148
enum Color {
149
  RED
150
151
  # Not a creative color
152
  GREEN
153
  BLUE
154
}
155
156
# What a great type
157
type Query {
158
  # And a field to boot
159
  str: String
160
}
161
';
162
        $output = $this->cycleOutput($body, [ 'commentDescriptions' => true ]);
163
        $this->assertEquals($body, $output);
164
    }
165
166
    /**
167
     * @it Maintains @skip & @include
168
     */
169
    public function testMaintainsSkipAndInclude()
170
    {
171
        $body = '
172
type Query {
173
  str: String
174
}
175
';
176
        $schema = BuildSchema::buildAST(Parser::parse($body));
177
        $this->assertEquals(count($schema->getDirectives()), 3);
178
        $this->assertEquals($schema->getDirective('skip'), Directive::skipDirective());
179
        $this->assertEquals($schema->getDirective('include'), Directive::includeDirective());
180
        $this->assertEquals($schema->getDirective('deprecated'), Directive::deprecatedDirective());
181
    }
182
183
    /**
184
     * @it Overriding directives excludes specified
185
     */
186
    public function testOverridingDirectivesExcludesSpecified()
187
    {
188
        $body = '
189
directive @skip on FIELD
190
directive @include on FIELD
191
directive @deprecated on FIELD_DEFINITION
192
193
type Query {
194
  str: String
195
}
196
    ';
197
        $schema = BuildSchema::buildAST(Parser::parse($body));
198
        $this->assertEquals(count($schema->getDirectives()), 3);
199
        $this->assertNotEquals($schema->getDirective('skip'), Directive::skipDirective());
200
        $this->assertNotEquals($schema->getDirective('include'), Directive::includeDirective());
201
        $this->assertNotEquals($schema->getDirective('deprecated'), Directive::deprecatedDirective());
202
    }
203
204
    /**
205
     * @it Adding directives maintains @skip & @include
206
     */
207
    public function testAddingDirectivesMaintainsSkipAndInclude()
208
    {
209
        $body = '
210
      directive @foo(arg: Int) on FIELD
211
212
      type Query {
213
        str: String
214
      }
215
    ';
216
        $schema = BuildSchema::buildAST(Parser::parse($body));
217
        $this->assertCount(4, $schema->getDirectives());
218
        $this->assertNotEquals(null, $schema->getDirective('skip'));
219
        $this->assertNotEquals(null, $schema->getDirective('include'));
220
        $this->assertNotEquals(null, $schema->getDirective('deprecated'));
221
    }
222
223
    /**
224
     * @it Type modifiers
225
     */
226
    public function testTypeModifiers()
227
    {
228
        $body = '
229
type HelloScalars {
230
  nonNullStr: String!
231
  listOfStrs: [String]
232
  listOfNonNullStrs: [String!]
233
  nonNullListOfStrs: [String]!
234
  nonNullListOfNonNullStrs: [String!]!
235
}
236
';
237
        $output = $this->cycleOutput($body);
238
        $this->assertEquals($output, $body);
239
    }
240
241
    /**
242
     * @it Recursive type
243
     */
244
    public function testRecursiveType()
245
    {
246
        $body = '
247
type Query {
248
  str: String
249
  recurse: Query
250
}
251
';
252
        $output = $this->cycleOutput($body);
253
        $this->assertEquals($output, $body);
254
    }
255
256
    /**
257
     * @it Two types circular
258
     */
259
    public function testTwoTypesCircular()
260
    {
261
        $body = '
262
schema {
263
  query: TypeOne
264
}
265
266
type TypeOne {
267
  str: String
268
  typeTwo: TypeTwo
269
}
270
271
type TypeTwo {
272
  str: String
273
  typeOne: TypeOne
274
}
275
';
276
        $output = $this->cycleOutput($body);
277
        $this->assertEquals($output, $body);
278
    }
279
280
    /**
281
     * @it Single argument field
282
     */
283
    public function testSingleArgumentField()
284
    {
285
        $body = '
286
type Query {
287
  str(int: Int): String
288
  floatToStr(float: Float): String
289
  idToStr(id: ID): String
290
  booleanToStr(bool: Boolean): String
291
  strToStr(bool: String): String
292
}
293
';
294
        $output = $this->cycleOutput($body);
295
        $this->assertEquals($output, $body);
296
    }
297
298
    /**
299
     * @it Simple type with multiple arguments
300
     */
301
    public function testSimpleTypeWithMultipleArguments()
302
    {
303
        $body = '
304
type Query {
305
  str(int: Int, bool: Boolean): String
306
}
307
';
308
        $output = $this->cycleOutput($body);
309
        $this->assertEquals($output, $body);
310
    }
311
312
    /**
313
     * @it Simple type with interface
314
     */
315
    public function testSimpleTypeWithInterface()
316
    {
317
        $body = '
318
type Query implements WorldInterface {
319
  str: String
320
}
321
322
interface WorldInterface {
323
  str: String
324
}
325
';
326
        $output = $this->cycleOutput($body);
327
        $this->assertEquals($output, $body);
328
    }
329
330
    /**
331
     * @it Simple output enum
332
     */
333
    public function testSimpleOutputEnum()
334
    {
335
        $body = '
336
enum Hello {
337
  WORLD
338
}
339
340
type Query {
341
  hello: Hello
342
}
343
';
344
        $output = $this->cycleOutput($body);
345
        $this->assertEquals($output, $body);
346
    }
347
348
    /**
349
     * @it Simple input enum
350
     */
351
    public function testSimpleInputEnum()
352
    {
353
        $body = '
354
enum Hello {
355
  WORLD
356
}
357
358
type Query {
359
  str(hello: Hello): String
360
}
361
';
362
        $output = $this->cycleOutput($body);
363
        $this->assertEquals($body, $output);
364
    }
365
366
    /**
367
     * @it Multiple value enum
368
     */
369
    public function testMultipleValueEnum()
370
    {
371
        $body = '
372
enum Hello {
373
  WO
374
  RLD
375
}
376
377
type Query {
378
  hello: Hello
379
}
380
';
381
        $output = $this->cycleOutput($body);
382
        $this->assertEquals($output, $body);
383
    }
384
385
    /**
386
     * @it Simple Union
387
     */
388
    public function testSimpleUnion()
389
    {
390
        $body = '
391
union Hello = World
392
393
type Query {
394
  hello: Hello
395
}
396
397
type World {
398
  str: String
399
}
400
';
401
        $output = $this->cycleOutput($body);
402
        $this->assertEquals($output, $body);
403
    }
404
405
    /**
406
     * @it Multiple Union
407
     */
408
    public function testMultipleUnion()
409
    {
410
        $body = '
411
union Hello = WorldOne | WorldTwo
412
413
type Query {
414
  hello: Hello
415
}
416
417
type WorldOne {
418
  str: String
419
}
420
421
type WorldTwo {
422
  str: String
423
}
424
';
425
        $output = $this->cycleOutput($body);
426
        $this->assertEquals($output, $body);
427
    }
428
429
    /**
430
     * @it Specifying Union type using __typename
431
     */
432
    public function testSpecifyingUnionTypeUsingTypename()
433
    {
434
        $schema = BuildSchema::buildAST(Parser::parse('
435
            type Query {
436
              fruits: [Fruit]
437
            }
438
            
439
            union Fruit = Apple | Banana
440
            
441
            type Apple {
442
              color: String
443
            }
444
            
445
            type Banana {
446
              length: Int
447
            }
448
        '));
449
        $query = '
450
            {
451
              fruits {
452
                ... on Apple {
453
                  color
454
                }
455
                ... on Banana {
456
                  length
457
                }
458
              }
459
            }
460
        ';
461
        $root = [
462
            'fruits' => [
463
                [
464
                    'color' => 'green',
465
                    '__typename' => 'Apple',
466
                ],
467
                [
468
                    'length' => 5,
469
                    '__typename' => 'Banana',
470
                ]
471
            ]
472
        ];
473
        $expected = [
474
            'data' => [
475
                'fruits' => [
476
                    ['color' => 'green'],
477
                    ['length' => 5],
478
                ]
479
            ]
480
        ];
481
482
        $result = GraphQL::executeQuery($schema, $query, $root);
483
        $this->assertEquals($expected, $result->toArray(true));
484
    }
485
486
    /**
487
     * @it Specifying Interface type using __typename
488
     */
489
    public function testSpecifyingInterfaceUsingTypename()
490
    {
491
        $schema = BuildSchema::buildAST(Parser::parse('
492
            type Query {
493
              characters: [Character]
494
            }
495
            
496
            interface Character {
497
              name: String!
498
            }
499
            
500
            type Human implements Character {
501
              name: String!
502
              totalCredits: Int
503
            }
504
            
505
            type Droid implements Character {
506
              name: String!
507
              primaryFunction: String
508
            }
509
        '));
510
        $query = '
511
            {
512
              characters {
513
                name
514
                ... on Human {
515
                  totalCredits
516
                }
517
                ... on Droid {
518
                  primaryFunction
519
                }
520
              }
521
            }
522
        ';
523
        $root = [
524
            'characters' => [
525
                [
526
                    'name' => 'Han Solo',
527
                    'totalCredits' => 10,
528
                    '__typename' => 'Human',
529
                ],
530
                [
531
                    'name' => 'R2-D2',
532
                    'primaryFunction' => 'Astromech',
533
                    '__typename' => 'Droid',
534
                ]
535
            ]
536
        ];
537
        $expected = [
538
            'data' => [
539
                'characters' => [
540
                    ['name' => 'Han Solo', 'totalCredits' => 10],
541
                    ['name' => 'R2-D2', 'primaryFunction' => 'Astromech'],
542
                ]
543
            ]
544
        ];
545
546
        $result = GraphQL::executeQuery($schema, $query, $root);
547
        $this->assertEquals($expected, $result->toArray(true));
548
    }
549
550
    /**
551
     * @it Custom Scalar
552
     */
553
    public function testCustomScalar()
554
    {
555
        $body = '
556
scalar CustomScalar
557
558
type Query {
559
  customScalar: CustomScalar
560
}
561
';
562
        $output = $this->cycleOutput($body);
563
        $this->assertEquals($output, $body);
564
    }
565
566
    /**
567
     * @it Input Object
568
     */
569
    public function testInputObject()
570
    {
571
        $body = '
572
input Input {
573
  int: Int
574
}
575
576
type Query {
577
  field(in: Input): String
578
}
579
';
580
        $output = $this->cycleOutput($body);
581
        $this->assertEquals($output, $body);
582
    }
583
584
    /**
585
     * @it Simple argument field with default
586
     */
587
    public function testSimpleArgumentFieldWithDefault()
588
    {
589
        $body = '
590
type Query {
591
  str(int: Int = 2): String
592
}
593
';
594
        $output = $this->cycleOutput($body);
595
        $this->assertEquals($output, $body);
596
    }
597
598
    /**
599
     * @it Custom scalar argument field with default
600
     */
601
    public function testCustomScalarArgumentFieldWithDefault()
602
    {
603
        $body = '
604
scalar CustomScalar
605
606
type Query {
607
  str(int: CustomScalar = 2): String
608
}
609
';
610
        $output = $this->cycleOutput($body);
611
        $this->assertEquals($output, $body);
612
    }
613
614
    /**
615
     * @it Simple type with mutation
616
     */
617
    public function testSimpleTypeWithMutation()
618
    {
619
        $body = '
620
schema {
621
  query: HelloScalars
622
  mutation: Mutation
623
}
624
625
type HelloScalars {
626
  str: String
627
  int: Int
628
  bool: Boolean
629
}
630
631
type Mutation {
632
  addHelloScalars(str: String, int: Int, bool: Boolean): HelloScalars
633
}
634
';
635
        $output = $this->cycleOutput($body);
636
        $this->assertEquals($output, $body);
637
    }
638
639
    /**
640
     * @it Simple type with subscription
641
     */
642
    public function testSimpleTypeWithSubscription()
643
    {
644
        $body = '
645
schema {
646
  query: HelloScalars
647
  subscription: Subscription
648
}
649
650
type HelloScalars {
651
  str: String
652
  int: Int
653
  bool: Boolean
654
}
655
656
type Subscription {
657
  subscribeHelloScalars(str: String, int: Int, bool: Boolean): HelloScalars
658
}
659
';
660
        $output = $this->cycleOutput($body);
661
        $this->assertEquals($output, $body);
662
    }
663
664
    /**
665
     * @it Unreferenced type implementing referenced interface
666
     */
667
    public function testUnreferencedTypeImplementingReferencedInterface()
668
    {
669
        $body = '
670
type Concrete implements Iface {
671
  key: String
672
}
673
674
interface Iface {
675
  key: String
676
}
677
678
type Query {
679
  iface: Iface
680
}
681
';
682
        $output = $this->cycleOutput($body);
683
        $this->assertEquals($output, $body);
684
    }
685
686
    /**
687
     * @it Unreferenced type implementing referenced union
688
     */
689
    public function testUnreferencedTypeImplementingReferencedUnion()
690
    {
691
        $body = '
692
type Concrete {
693
  key: String
694
}
695
696
type Query {
697
  union: Union
698
}
699
700
union Union = Concrete
701
';
702
        $output = $this->cycleOutput($body);
703
        $this->assertEquals($output, $body);
704
    }
705
706
    /**
707
     * @it Supports @deprecated
708
     */
709
    public function testSupportsDeprecated()
710
    {
711
        $body = '
712
enum MyEnum {
713
  VALUE
714
  OLD_VALUE @deprecated
715
  OTHER_VALUE @deprecated(reason: "Terrible reasons")
716
}
717
718
type Query {
719
  field1: String @deprecated
720
  field2: Int @deprecated(reason: "Because I said so")
721
  enum: MyEnum
722
}
723
';
724
        $output = $this->cycleOutput($body);
725
        $this->assertEquals($output, $body);
726
727
        $ast = Parser::parse($body);
728
        $schema = BuildSchema::buildAST($ast);
729
730
        /** @var EnumType $myEnum */
731
        $myEnum = $schema->getType('MyEnum');
732
733
        $value = $myEnum->getValue('VALUE');
734
        $this->assertFalse($value->isDeprecated());
735
736
        $oldValue = $myEnum->getValue('OLD_VALUE');
737
        $this->assertTrue($oldValue->isDeprecated());
738
        $this->assertEquals('No longer supported', $oldValue->deprecationReason);
739
740
        $otherValue = $myEnum->getValue('OTHER_VALUE');
741
        $this->assertTrue($otherValue->isDeprecated());
742
        $this->assertEquals('Terrible reasons', $otherValue->deprecationReason);
743
744
        $rootFields = $schema->getType('Query')->getFields();
0 ignored issues
show
Bug introduced by
The method getFields() does not exist on GraphQL\Type\Definition\Type. It seems like you code against a sub-type of GraphQL\Type\Definition\Type such as GraphQL\Type\Definition\InterfaceType or GraphQL\Type\Definition\ObjectType or GraphQL\Type\Definition\InputObjectType. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

744
        $rootFields = $schema->getType('Query')->/** @scrutinizer ignore-call */ getFields();
Loading history...
745
        $this->assertEquals($rootFields['field1']->isDeprecated(), true);
746
        $this->assertEquals($rootFields['field1']->deprecationReason, 'No longer supported');
747
748
        $this->assertEquals($rootFields['field2']->isDeprecated(), true);
749
        $this->assertEquals($rootFields['field2']->deprecationReason, 'Because I said so');
750
    }
751
752
    /**
753
     * @it Correctly assign AST nodes
754
     */
755
    public function testCorrectlyAssignASTNodes()
756
    {
757
        $schemaAST = Parser::parse('
758
      schema {
759
        query: Query
760
      }
761
762
      type Query {
763
        testField(testArg: TestInput): TestUnion
764
      }
765
766
      input TestInput {
767
        testInputField: TestEnum
768
      }
769
770
      enum TestEnum {
771
        TEST_VALUE
772
      }
773
774
      union TestUnion = TestType
775
776
      interface TestInterface {
777
        interfaceField: String
778
      }
779
780
      type TestType implements TestInterface {
781
        interfaceField: String
782
      }
783
      
784
      scalar TestScalar
785
786
      directive @test(arg: TestScalar) on FIELD
787
    ');
788
        $schema = BuildSchema::buildAST($schemaAST);
789
        /** @var ObjectType $query */
790
        $query = $schema->getType('Query');
791
        $testInput = $schema->getType('TestInput');
792
        $testEnum = $schema->getType('TestEnum');
793
        $testUnion = $schema->getType('TestUnion');
794
        $testInterface = $schema->getType('TestInterface');
795
        $testType = $schema->getType('TestType');
796
        $testScalar = $schema->getType('TestScalar');
797
        $testDirective = $schema->getDirective('test');
798
799
        $restoredIDL = SchemaPrinter::doPrint(BuildSchema::build(
800
            Printer::doPrint($schema->getAstNode()) . "\n" .
801
            Printer::doPrint($query->astNode) . "\n" .
802
            Printer::doPrint($testInput->astNode) . "\n" .
803
            Printer::doPrint($testEnum->astNode) . "\n" .
804
            Printer::doPrint($testUnion->astNode) . "\n" .
805
            Printer::doPrint($testInterface->astNode) . "\n" .
806
            Printer::doPrint($testType->astNode) . "\n" .
807
            Printer::doPrint($testScalar->astNode) . "\n" .
808
            Printer::doPrint($testDirective->astNode)
809
        ));
810
811
        $this->assertEquals($restoredIDL, SchemaPrinter::doPrint($schema));
812
813
        $testField = $query->getField('testField');
814
        $this->assertEquals('testField(testArg: TestInput): TestUnion', Printer::doPrint($testField->astNode));
815
        $this->assertEquals('testArg: TestInput', Printer::doPrint($testField->args[0]->astNode));
816
        $this->assertEquals('testInputField: TestEnum', Printer::doPrint($testInput->getField('testInputField')->astNode));
0 ignored issues
show
Bug introduced by
The method getField() does not exist on GraphQL\Type\Definition\Type. It seems like you code against a sub-type of GraphQL\Type\Definition\Type such as GraphQL\Type\Definition\InterfaceType or GraphQL\Type\Definition\ObjectType or GraphQL\Type\Definition\InputObjectType. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

816
        $this->assertEquals('testInputField: TestEnum', Printer::doPrint($testInput->/** @scrutinizer ignore-call */ getField('testInputField')->astNode));
Loading history...
817
        $this->assertEquals('TEST_VALUE', Printer::doPrint($testEnum->getValue('TEST_VALUE')->astNode));
0 ignored issues
show
Bug introduced by
The method getValue() does not exist on GraphQL\Type\Definition\Type. It seems like you code against a sub-type of GraphQL\Type\Definition\Type such as GraphQL\Type\Definition\EnumType. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

817
        $this->assertEquals('TEST_VALUE', Printer::doPrint($testEnum->/** @scrutinizer ignore-call */ getValue('TEST_VALUE')->astNode));
Loading history...
818
        $this->assertEquals('interfaceField: String', Printer::doPrint($testInterface->getField('interfaceField')->astNode));
819
        $this->assertEquals('interfaceField: String', Printer::doPrint($testType->getField('interfaceField')->astNode));
820
        $this->assertEquals('arg: TestScalar', Printer::doPrint($testDirective->args[0]->astNode));
821
    }
822
823
    /**
824
     * @it Root operation types with custom names
825
     */
826
    public function testRootOperationTypesWithCustomNames()
827
    {
828
        $schema = BuildSchema::build('
829
          schema {
830
            query: SomeQuery
831
            mutation: SomeMutation
832
            subscription: SomeSubscription
833
          }
834
          type SomeQuery { str: String }
835
          type SomeMutation { str: String }
836
          type SomeSubscription { str: String }
837
        ');
838
839
        $this->assertEquals('SomeQuery', $schema->getQueryType()->name);
840
        $this->assertEquals('SomeMutation', $schema->getMutationType()->name);
841
        $this->assertEquals('SomeSubscription', $schema->getSubscriptionType()->name);
842
    }
843
844
    /**
845
     * @it Default root operation type names
846
     */
847
    public function testDefaultRootOperationTypeNames()
848
    {
849
        $schema = BuildSchema::build('
850
          type Query { str: String }
851
          type Mutation { str: String }
852
          type Subscription { str: String }
853
        ');
854
        $this->assertEquals('Query', $schema->getQueryType()->name);
855
        $this->assertEquals('Mutation', $schema->getMutationType()->name);
856
        $this->assertEquals('Subscription', $schema->getSubscriptionType()->name);
857
    }
858
859
    /**
860
     * @it can build invalid schema
861
     */
862
    public function testCanBuildInvalidSchema()
863
    {
864
        $schema = BuildSchema::build('
865
          # Invalid schema, because it is missing query root type
866
          type Mutation {
867
            str: String
868
          }
869
        ');
870
        $errors = $schema->validate();
871
        $this->assertGreaterThan(0, $errors);
872
    }
873
874
    // Describe: Failures
875
876
    /**
877
     * @it Allows only a single schema definition
878
     */
879
    public function testAllowsOnlySingleSchemaDefinition()
880
    {
881
        $this->expectException(Error::class);
882
        $this->expectExceptionMessage('Must provide only one schema definition.');
883
        $body = '
884
schema {
885
  query: Hello
886
}
887
888
schema {
889
  query: Hello
890
}
891
892
type Hello {
893
  bar: Bar
894
}
895
';
896
        $doc = Parser::parse($body);
897
        BuildSchema::buildAST($doc);
898
    }
899
900
    /**
901
     * @it Allows only a single query type
902
     */
903
    public function testAllowsOnlySingleQueryType()
904
    {
905
        $this->expectException(Error::class);
906
        $this->expectExceptionMessage('Must provide only one query type in schema.');
907
        $body = '
908
schema {
909
  query: Hello
910
  query: Yellow
911
}
912
913
type Hello {
914
  bar: Bar
915
}
916
917
type Yellow {
918
  isColor: Boolean
919
}
920
';
921
        $doc = Parser::parse($body);
922
        BuildSchema::buildAST($doc);
923
    }
924
925
    /**
926
     * @it Allows only a single mutation type
927
     */
928
    public function testAllowsOnlySingleMutationType()
929
    {
930
        $this->expectException(Error::class);
931
        $this->expectExceptionMessage('Must provide only one mutation type in schema.');
932
        $body = '
933
schema {
934
  query: Hello
935
  mutation: Hello
936
  mutation: Yellow
937
}
938
939
type Hello {
940
  bar: Bar
941
}
942
943
type Yellow {
944
  isColor: Boolean
945
}
946
';
947
        $doc = Parser::parse($body);
948
        BuildSchema::buildAST($doc);
949
    }
950
951
    /**
952
     * @it Allows only a single subscription type
953
     */
954
    public function testAllowsOnlySingleSubscriptionType()
955
    {
956
        $this->expectException(Error::class);
957
        $this->expectExceptionMessage('Must provide only one subscription type in schema.');
958
        $body = '
959
schema {
960
  query: Hello
961
  subscription: Hello
962
  subscription: Yellow
963
}
964
965
type Hello {
966
  bar: Bar
967
}
968
969
type Yellow {
970
  isColor: Boolean
971
}
972
';
973
        $doc = Parser::parse($body);
974
        BuildSchema::buildAST($doc);
975
    }
976
977
    /**
978
     * @it Unknown type referenced
979
     */
980
    public function testUnknownTypeReferenced()
981
    {
982
        $this->expectException(Error::class);
983
        $this->expectExceptionMessage('Type "Bar" not found in document.');
984
        $body = '
985
schema {
986
  query: Hello
987
}
988
989
type Hello {
990
  bar: Bar
991
}
992
';
993
        $doc = Parser::parse($body);
994
        $schema = BuildSchema::buildAST($doc);
995
        $schema->getTypeMap();
996
    }
997
998
    /**
999
     * @it Unknown type in interface list
1000
     */
1001
    public function testUnknownTypeInInterfaceList()
1002
    {
1003
        $this->expectException(Error::class);
1004
        $this->expectExceptionMessage('Type "Bar" not found in document.');
1005
        $body = '
1006
type Query implements Bar {
1007
  field: String
1008
}
1009
';
1010
        $doc = Parser::parse($body);
1011
        $schema = BuildSchema::buildAST($doc);
1012
        $schema->getTypeMap();
1013
    }
1014
1015
    /**
1016
     * @it Unknown type in union list
1017
     */
1018
    public function testUnknownTypeInUnionList()
1019
    {
1020
        $this->expectException(Error::class);
1021
        $this->expectExceptionMessage('Type "Bar" not found in document.');
1022
        $body = '
1023
union TestUnion = Bar
1024
type Query { testUnion: TestUnion }
1025
';
1026
        $doc = Parser::parse($body);
1027
        $schema = BuildSchema::buildAST($doc);
1028
        $schema->getTypeMap();
1029
    }
1030
1031
    /**
1032
     * @it Unknown query type
1033
     */
1034
    public function testUnknownQueryType()
1035
    {
1036
        $this->expectException(Error::class);
1037
        $this->expectExceptionMessage('Specified query type "Wat" not found in document.');
1038
        $body = '
1039
schema {
1040
  query: Wat
1041
}
1042
1043
type Hello {
1044
  str: String
1045
}
1046
';
1047
        $doc = Parser::parse($body);
1048
        BuildSchema::buildAST($doc);
1049
    }
1050
1051
    /**
1052
     * @it Unknown mutation type
1053
     */
1054
    public function testUnknownMutationType()
1055
    {
1056
        $this->expectException(Error::class);
1057
        $this->expectExceptionMessage('Specified mutation type "Wat" not found in document.');
1058
        $body = '
1059
schema {
1060
  query: Hello
1061
  mutation: Wat
1062
}
1063
1064
type Hello {
1065
  str: String
1066
}
1067
';
1068
        $doc = Parser::parse($body);
1069
        BuildSchema::buildAST($doc);
1070
    }
1071
1072
    /**
1073
     * @it Unknown subscription type
1074
     */
1075
    public function testUnknownSubscriptionType()
1076
    {
1077
        $this->expectException(Error::class);
1078
        $this->expectExceptionMessage('Specified subscription type "Awesome" not found in document.');
1079
        $body = '
1080
schema {
1081
  query: Hello
1082
  mutation: Wat
1083
  subscription: Awesome
1084
}
1085
1086
type Hello {
1087
  str: String
1088
}
1089
1090
type Wat {
1091
  str: String
1092
}
1093
';
1094
        $doc = Parser::parse($body);
1095
        BuildSchema::buildAST($doc);
1096
    }
1097
1098
    /**
1099
     * @it Does not consider operation names
1100
     */
1101
    public function testDoesNotConsiderOperationNames()
1102
    {
1103
        $this->expectException(Error::class);
1104
        $this->expectExceptionMessage('Specified query type "Foo" not found in document.');
1105
        $body = '
1106
schema {
1107
  query: Foo
1108
}
1109
1110
query Foo { field }
1111
';
1112
        $doc = Parser::parse($body);
1113
        BuildSchema::buildAST($doc);
1114
    }
1115
1116
    /**
1117
     * @it Does not consider fragment names
1118
     */
1119
    public function testDoesNotConsiderFragmentNames()
1120
    {
1121
        $this->expectException(Error::class);
1122
        $this->expectExceptionMessage('Specified query type "Foo" not found in document.');
1123
        $body = '
1124
schema {
1125
  query: Foo
1126
}
1127
1128
fragment Foo on Type { field }
1129
';
1130
        $doc = Parser::parse($body);
1131
        BuildSchema::buildAST($doc);
1132
    }
1133
1134
    /**
1135
     * @it Forbids duplicate type definitions
1136
     */
1137
    public function testForbidsDuplicateTypeDefinitions()
1138
    {
1139
        $body = '
1140
schema {
1141
  query: Repeated
1142
}
1143
1144
type Repeated {
1145
  id: Int
1146
}
1147
1148
type Repeated {
1149
  id: String
1150
}
1151
';
1152
        $doc = Parser::parse($body);
1153
1154
        $this->expectException(Error::class);
1155
        $this->expectExceptionMessage('Type "Repeated" was defined more than once.');
1156
        BuildSchema::buildAST($doc);
1157
    }
1158
1159
    public function testSupportsTypeConfigDecorator()
1160
    {
1161
        $body = '
1162
schema {
1163
  query: Query
1164
}
1165
1166
type Query {
1167
  str: String
1168
  color: Color
1169
  hello: Hello
1170
}
1171
1172
enum Color {
1173
  RED
1174
  GREEN
1175
  BLUE
1176
}
1177
1178
interface Hello {
1179
  world: String
1180
}
1181
';
1182
        $doc = Parser::parse($body);
1183
1184
        $decorated = [];
1185
        $calls = [];
1186
1187
        $typeConfigDecorator = function($defaultConfig, $node, $allNodesMap) use (&$decorated, &$calls) {
1188
            $decorated[] = $defaultConfig['name'];
1189
            $calls[] = [$defaultConfig, $node, $allNodesMap];
1190
            return ['description' => 'My description of ' . $node->name->value] + $defaultConfig;
1191
        };
1192
1193
        $schema = BuildSchema::buildAST($doc, $typeConfigDecorator);
1194
        $schema->getTypeMap();
1195
        $this->assertEquals(['Query', 'Color', 'Hello'], $decorated);
1196
1197
        list($defaultConfig, $node, $allNodesMap) = $calls[0];
1198
        $this->assertInstanceOf(ObjectTypeDefinitionNode::class, $node);
1199
        $this->assertEquals('Query', $defaultConfig['name']);
1200
        $this->assertInstanceOf(\Closure::class, $defaultConfig['fields']);
1201
        $this->assertInstanceOf(\Closure::class, $defaultConfig['interfaces']);
1202
        $this->assertArrayHasKey('description', $defaultConfig);
1203
        $this->assertCount(5, $defaultConfig);
0 ignored issues
show
Bug introduced by
It seems like $defaultConfig can also be of type ArrayAccess; however, parameter $haystack of PHPUnit\Framework\Assert::assertCount() does only seem to accept Countable|iterable, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1203
        $this->assertCount(5, /** @scrutinizer ignore-type */ $defaultConfig);
Loading history...
1204
        $this->assertEquals(array_keys($allNodesMap), ['Query', 'Color', 'Hello']);
1205
        $this->assertEquals('My description of Query', $schema->getType('Query')->description);
1206
1207
1208
        list($defaultConfig, $node, $allNodesMap) = $calls[1];
1209
        $this->assertInstanceOf(EnumTypeDefinitionNode::class, $node);
1210
        $this->assertEquals('Color', $defaultConfig['name']);
1211
        $enumValue = [
1212
            'description' => '',
1213
            'deprecationReason' => ''
1214
        ];
1215
        $this->assertArraySubset([
1216
            'RED' => $enumValue,
1217
            'GREEN' => $enumValue,
1218
            'BLUE' => $enumValue,
1219
        ], $defaultConfig['values']);
1220
        $this->assertCount(4, $defaultConfig); // 3 + astNode
1221
        $this->assertEquals(array_keys($allNodesMap), ['Query', 'Color', 'Hello']);
1222
        $this->assertEquals('My description of Color', $schema->getType('Color')->description);
1223
1224
        list($defaultConfig, $node, $allNodesMap) = $calls[2];
1225
        $this->assertInstanceOf(InterfaceTypeDefinitionNode::class, $node);
1226
        $this->assertEquals('Hello', $defaultConfig['name']);
1227
        $this->assertInstanceOf(\Closure::class, $defaultConfig['fields']);
1228
        $this->assertArrayHasKey('description', $defaultConfig);
1229
        $this->assertCount(4, $defaultConfig);
1230
        $this->assertEquals(array_keys($allNodesMap), ['Query', 'Color', 'Hello']);
1231
        $this->assertEquals('My description of Hello', $schema->getType('Hello')->description);
1232
    }
1233
1234
    public function testCreatesTypesLazily()
1235
    {
1236
        $body = '
1237
schema {
1238
  query: Query
1239
}
1240
1241
type Query {
1242
  str: String
1243
  color: Color
1244
  hello: Hello
1245
}
1246
1247
enum Color {
1248
  RED
1249
  GREEN
1250
  BLUE
1251
}
1252
1253
interface Hello {
1254
  world: String
1255
}
1256
1257
type World implements Hello {
1258
  world: String
1259
}
1260
';
1261
        $doc = Parser::parse($body);
1262
        $created = [];
1263
1264
        $typeConfigDecorator = function($config, $node) use (&$created) {
1265
            $created[] = $node->name->value;
1266
            return $config;
1267
        };
1268
1269
        $schema = BuildSchema::buildAST($doc, $typeConfigDecorator);
1270
        $this->assertEquals(['Query'], $created);
1271
1272
        $schema->getType('Color');
1273
        $this->assertEquals(['Query', 'Color'], $created);
1274
1275
        $schema->getType('Hello');
1276
        $this->assertEquals(['Query', 'Color', 'Hello'], $created);
1277
1278
        $types = $schema->getTypeMap();
1279
        $this->assertEquals(['Query', 'Color', 'Hello', 'World'], $created);
1280
        $this->assertArrayHasKey('Query', $types);
1281
        $this->assertArrayHasKey('Color', $types);
1282
        $this->assertArrayHasKey('Hello', $types);
1283
        $this->assertArrayHasKey('World', $types);
1284
    }
1285
1286
}
1287