Failed Conditions
Push — master ( 4e43a2...d70c8e )
by Vladimir
15:58 queued 04:47
created

BuildSchemaTest::testSimpleTypeWithMutation()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 20
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

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

750
        $rootFields = $schema->getType('Query')->/** @scrutinizer ignore-call */ getFields();
Loading history...
751
        $this->assertEquals($rootFields['field1']->isDeprecated(), true);
752
        $this->assertEquals($rootFields['field1']->deprecationReason, 'No longer supported');
753
754
        $this->assertEquals($rootFields['field2']->isDeprecated(), true);
755
        $this->assertEquals($rootFields['field2']->deprecationReason, 'Because I said so');
756
    }
757
758
    /**
759
     * @see it('Correctly assign AST nodes')
760
     */
761
    public function testCorrectlyAssignASTNodes() : void
762
    {
763
        $schemaAST = Parser::parse('
764
      schema {
765
        query: Query
766
      }
767
768
      type Query {
769
        testField(testArg: TestInput): TestUnion
770
      }
771
772
      input TestInput {
773
        testInputField: TestEnum
774
      }
775
776
      enum TestEnum {
777
        TEST_VALUE
778
      }
779
780
      union TestUnion = TestType
781
782
      interface TestInterface {
783
        interfaceField: String
784
      }
785
786
      type TestType implements TestInterface {
787
        interfaceField: String
788
      }
789
      
790
      scalar TestScalar
791
792
      directive @test(arg: TestScalar) on FIELD
793
    ');
794
        $schema    = BuildSchema::buildAST($schemaAST);
795
        /** @var ObjectType $query */
796
        $query         = $schema->getType('Query');
797
        $testInput     = $schema->getType('TestInput');
798
        $testEnum      = $schema->getType('TestEnum');
799
        $testUnion     = $schema->getType('TestUnion');
800
        $testInterface = $schema->getType('TestInterface');
801
        $testType      = $schema->getType('TestType');
802
        $testScalar    = $schema->getType('TestScalar');
803
        $testDirective = $schema->getDirective('test');
804
805
        $restoredIDL = SchemaPrinter::doPrint(BuildSchema::build(
806
            Printer::doPrint($schema->getAstNode()) . "\n" .
807
            Printer::doPrint($query->astNode) . "\n" .
808
            Printer::doPrint($testInput->astNode) . "\n" .
809
            Printer::doPrint($testEnum->astNode) . "\n" .
810
            Printer::doPrint($testUnion->astNode) . "\n" .
811
            Printer::doPrint($testInterface->astNode) . "\n" .
812
            Printer::doPrint($testType->astNode) . "\n" .
813
            Printer::doPrint($testScalar->astNode) . "\n" .
814
            Printer::doPrint($testDirective->astNode)
815
        ));
816
817
        $this->assertEquals($restoredIDL, SchemaPrinter::doPrint($schema));
818
819
        $testField = $query->getField('testField');
820
        $this->assertEquals('testField(testArg: TestInput): TestUnion', Printer::doPrint($testField->astNode));
821
        $this->assertEquals('testArg: TestInput', Printer::doPrint($testField->args[0]->astNode));
822
        $this->assertEquals(
823
            'testInputField: TestEnum',
824
            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

824
            Printer::doPrint($testInput->/** @scrutinizer ignore-call */ getField('testInputField')->astNode)
Loading history...
825
        );
826
        $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

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

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