Failed Conditions
Push — master ( ea13c9...49ec89 )
by Vladimir
07:25
created

testDoesntAllowLegacySDLImplementsInterfacesByDefault()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 2
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
1
<?php
2
namespace GraphQL\Tests\Language;
3
4
use GraphQL\Error\SyntaxError;
5
use GraphQL\Language\AST\NodeKind;
6
use GraphQL\Language\Parser;
7
use GraphQL\Language\SourceLocation;
8
use PHPUnit\Framework\TestCase;
9
10
class SchemaParserTest extends TestCase
11
{
12
    // Describe: Schema Parser
13
14
    /**
15
     * @it Simple type
16
     */
17
    public function testSimpleType()
18
    {
19
        $body = '
20
type Hello {
21
  world: String
22
}';
23
        $doc = Parser::parse($body);
24
        $loc = function($start, $end) {return TestUtils::locArray($start, $end);};
25
26
        $expected = [
27
            'kind' => NodeKind::DOCUMENT,
28
            'definitions' => [
29
                [
30
                    'kind' => NodeKind::OBJECT_TYPE_DEFINITION,
31
                    'name' => $this->nameNode('Hello', $loc(6, 11)),
32
                    'interfaces' => [],
33
                    'directives' => [],
34
                    'fields' => [
35
                        $this->fieldNode(
36
                            $this->nameNode('world', $loc(16, 21)),
37
                            $this->typeNode('String', $loc(23, 29)),
38
                            $loc(16, 29)
39
                        )
40
                    ],
41
                    'loc' => $loc(1, 31),
42
                    'description' => null
43
                ]
44
            ],
45
            'loc' => $loc(0, 31)
46
        ];
47
        $this->assertEquals($expected, TestUtils::nodeToArray($doc));
48
    }
49
50
    /**
51
     * @it parses type with description string
52
     */
53
    public function testParsesTypeWithDescriptionString()
54
    {
55
        $body = '
56
"Description"
57
type Hello {
58
  world: String
59
}';
60
        $doc = Parser::parse($body);
61
        $loc = function($start, $end) {return TestUtils::locArray($start, $end);};
62
63
        $expected = [
64
            'kind' => NodeKind::DOCUMENT,
65
            'definitions' => [
66
                [
67
                    'kind' => NodeKind::OBJECT_TYPE_DEFINITION,
68
                    'name' => $this->nameNode('Hello', $loc(20, 25)),
69
                    'interfaces' => [],
70
                    'directives' => [],
71
                    'fields' => [
72
                        $this->fieldNode(
73
                            $this->nameNode('world', $loc(30, 35)),
74
                            $this->typeNode('String', $loc(37, 43)),
75
                            $loc(30, 43)
76
                        )
77
                    ],
78
                    'loc' => $loc(1, 45),
79
                    'description' => [
80
                        'kind' => NodeKind::STRING,
81
                        'value' => 'Description',
82
                        'loc' => $loc(1, 14),
83
                        'block' => false
84
                    ]
85
                ]
86
            ],
87
            'loc' => $loc(0, 45)
88
        ];
89
        $this->assertEquals($expected, TestUtils::nodeToArray($doc));
90
    }
91
92
    /**
93
     * @it parses type with description multi-linestring
94
     */
95
    public function testParsesTypeWithDescriptionMultiLineString()
96
    {
97
        $body = '
98
"""
99
Description
100
"""
101
# Even with comments between them
102
type Hello {
103
  world: String
104
}';
105
        $doc = Parser::parse($body);
106
        $loc = function($start, $end) {return TestUtils::locArray($start, $end);};
107
108
        $expected = [
109
            'kind' => NodeKind::DOCUMENT,
110
            'definitions' => [
111
                [
112
                    'kind' => NodeKind::OBJECT_TYPE_DEFINITION,
113
                    'name' => $this->nameNode('Hello', $loc(60, 65)),
114
                    'interfaces' => [],
115
                    'directives' => [],
116
                    'fields' => [
117
                        $this->fieldNode(
118
                            $this->nameNode('world', $loc(70, 75)),
119
                            $this->typeNode('String', $loc(77, 83)),
120
                            $loc(70, 83)
121
                        )
122
                    ],
123
                    'loc' => $loc(1, 85),
124
                    'description' => [
125
                        'kind' => NodeKind::STRING,
126
                        'value' => 'Description',
127
                        'loc' => $loc(1, 20),
128
                        'block' => true
129
                    ]
130
                ]
131
            ],
132
            'loc' => $loc(0, 85)
133
        ];
134
        $this->assertEquals($expected, TestUtils::nodeToArray($doc));
135
    }
136
137
    /**
138
     * @it Simple extension
139
     */
140
    public function testSimpleExtension()
141
    {
142
        $body = '
143
extend type Hello {
144
  world: String
145
}
146
';
147
        $doc = Parser::parse($body);
148
        $loc = function($start, $end) {
149
            return TestUtils::locArray($start, $end);
150
        };
151
        $expected = [
152
            'kind' => NodeKind::DOCUMENT,
153
            'definitions' => [
154
                [
155
                    'kind' => NodeKind::OBJECT_TYPE_EXTENSION,
156
                    'name' => $this->nameNode('Hello', $loc(13, 18)),
157
                    'interfaces' => [],
158
                    'directives' => [],
159
                    'fields' => [
160
                        $this->fieldNode(
161
                            $this->nameNode('world', $loc(23, 28)),
162
                            $this->typeNode('String', $loc(30, 36)),
163
                            $loc(23, 36)
164
                        )
165
                    ],
166
                    'loc' => $loc(1, 38)
167
                ]
168
            ],
169
            'loc' => $loc(0, 39)
170
        ];
171
        $this->assertEquals($expected, TestUtils::nodeToArray($doc));
172
    }
173
174
    /**
175
     * @it Extension without fields
176
     */
177
    public function testExtensionWithoutFields()
178
    {
179
        $body = 'extend type Hello implements Greeting';
180
        $doc = Parser::parse($body);
181
        $loc = function($start, $end) {
182
            return TestUtils::locArray($start, $end);
183
        };
184
        $expected = [
185
            'kind' => NodeKind::DOCUMENT,
186
            'definitions' => [
187
                [
188
                    'kind' => NodeKind::OBJECT_TYPE_EXTENSION,
189
                    'name' => $this->nameNode('Hello', $loc(12, 17)),
190
                    'interfaces' => [
191
                        $this->typeNode('Greeting', $loc(29, 37)),
192
                    ],
193
                    'directives' => [],
194
                    'fields' => [],
195
                    'loc' => $loc(0, 37)
196
                ]
197
            ],
198
            'loc' => $loc(0, 37)
199
        ];
200
        $this->assertEquals($expected, TestUtils::nodeToArray($doc));
201
    }
202
203
    /**
204
     * @it Extension without fields followed by extension
205
     */
206
    public function testExtensionWithoutFieldsFollowedByExtension()
207
    {
208
        $body = '
209
          extend type Hello implements Greeting
210
    
211
          extend type Hello implements SecondGreeting
212
        ';
213
        $doc = Parser::parse($body);
214
        $expected = [
215
            'kind' => 'Document',
216
            'definitions' => [
217
                [
218
                    'kind' => 'ObjectTypeExtension',
219
                    'name' => $this->nameNode('Hello', ['start' => 23, 'end' => 28]),
220
                    'interfaces' => [$this->typeNode('Greeting', ['start' => 40, 'end' => 48])],
221
                    'directives' => [],
222
                    'fields' => [],
223
                    'loc' => ['start' => 11, 'end' => 48],
224
                ],
225
                [
226
                    'kind' => 'ObjectTypeExtension',
227
                    'name' => $this->nameNode('Hello', ['start' => 76, 'end' => 81]),
228
                    'interfaces' => [$this->typeNode('SecondGreeting', ['start' => 93, 'end' => 107])],
229
                    'directives' => [],
230
                    'fields' => [],
231
                    'loc' => ['start' => 64, 'end' => 107],
232
                ],
233
            ],
234
            'loc' => ['start' => 0, 'end' => 116],
235
        ];
236
        $this->assertEquals($expected, $doc->toArray(true));
237
    }
238
239
    /**
240
     * @it Extension without anything throws
241
     */
242
    public function testExtensionWithoutAnythingThrows()
243
    {
244
        $this->expectSyntaxError(
245
            'extend type Hello',
246
            'Unexpected <EOF>',
247
            $this->loc(1, 18)
248
        );
249
    }
250
251
    /**
252
     * @it Extension do not include descriptions
253
     */
254
    public function testExtensionDoNotIncludeDescriptions()
255
    {
256
        $body = '
257
      "Description"
258
      extend type Hello {
259
        world: String
260
      }';
261
        $this->expectSyntaxError(
262
            $body,
263
            'Unexpected Name "extend"',
264
            $this->loc(3, 7)
265
        );
266
    }
267
268
    /**
269
     * @it Extension do not include descriptions
270
     */
271
    public function testExtensionDoNotIncludeDescriptions2()
272
    {
273
        $body = '
274
      extend "Description" type Hello {
275
        world: String
276
      }
277
}';
278
        $this->expectSyntaxError(
279
            $body,
280
            'Unexpected String "Description"',
281
            $this->loc(2, 14)
282
        );
283
    }
284
285
    /**
286
     * @it Simple non-null type
287
     */
288
    public function testSimpleNonNullType()
289
    {
290
        $body = '
291
type Hello {
292
  world: String!
293
}';
294
        $loc = function($start, $end) {
295
            return TestUtils::locArray($start, $end);
296
        };
297
        $doc = Parser::parse($body);
298
299
        $expected = [
300
            'kind' => NodeKind::DOCUMENT,
301
            'definitions' => [
302
                [
303
                    'kind' => NodeKind::OBJECT_TYPE_DEFINITION,
304
                    'name' => $this->nameNode('Hello', $loc(6,11)),
305
                    'interfaces' => [],
306
                    'directives' => [],
307
                    'fields' => [
308
                        $this->fieldNode(
309
                            $this->nameNode('world', $loc(16, 21)),
310
                            [
311
                                'kind' => NodeKind::NON_NULL_TYPE,
312
                                'type' => $this->typeNode('String', $loc(23, 29)),
313
                                'loc' => $loc(23, 30)
314
                            ],
315
                            $loc(16,30)
316
                        )
317
                    ],
318
                    'loc' => $loc(1,32),
319
                    'description' => null
320
                ]
321
            ],
322
            'loc' => $loc(0,32)
323
        ];
324
325
        $this->assertEquals($expected, TestUtils::nodeToArray($doc));
326
    }
327
328
    /**
329
     * @it Simple type inheriting interface
330
     */
331
    public function testSimpleTypeInheritingInterface()
332
    {
333
        $body = 'type Hello implements World { field: String }';
334
        $loc = function($start, $end) { return TestUtils::locArray($start, $end); };
335
        $doc = Parser::parse($body);
336
337
        $expected = [
338
            'kind' => NodeKind::DOCUMENT,
339
            'definitions' => [
340
                [
341
                    'kind' => NodeKind::OBJECT_TYPE_DEFINITION,
342
                    'name' => $this->nameNode('Hello', $loc(5, 10)),
343
                    'interfaces' => [
344
                        $this->typeNode('World', $loc(22, 27))
345
                    ],
346
                    'directives' => [],
347
                    'fields' => [
348
                        $this->fieldNode(
349
                            $this->nameNode('field', $loc(30, 35)),
350
                            $this->typeNode('String', $loc(37, 43)),
351
                            $loc(30, 43)
352
                        )
353
                    ],
354
                    'loc' => $loc(0, 45),
355
                    'description' => null
356
                ]
357
            ],
358
            'loc' => $loc(0, 45)
359
        ];
360
361
        $this->assertEquals($expected, TestUtils::nodeToArray($doc));
362
    }
363
364
    /**
365
     * @it Simple type inheriting multiple interfaces
366
     */
367
    public function testSimpleTypeInheritingMultipleInterfaces()
368
    {
369
        $body = 'type Hello implements Wo & rld { field: String }';
370
        $loc = function($start, $end) {return TestUtils::locArray($start, $end);};
371
        $doc = Parser::parse($body);
372
373
        $expected = [
374
            'kind' => NodeKind::DOCUMENT,
375
            'definitions' => [
376
                [
377
                    'kind' => NodeKind::OBJECT_TYPE_DEFINITION,
378
                    'name' => $this->nameNode('Hello', $loc(5, 10)),
379
                    'interfaces' => [
380
                        $this->typeNode('Wo', $loc(22, 24)),
381
                        $this->typeNode('rld', $loc(27, 30))
382
                    ],
383
                    'directives' => [],
384
                    'fields' => [
385
                        $this->fieldNode(
386
                            $this->nameNode('field', $loc(33, 38)),
387
                            $this->typeNode('String', $loc(40, 46)),
388
                            $loc(33, 46)
389
                        )
390
                    ],
391
                    'loc' => $loc(0, 48),
392
                    'description' => null
393
                ]
394
            ],
395
            'loc' => $loc(0, 48)
396
        ];
397
398
        $this->assertEquals($expected, TestUtils::nodeToArray($doc));
399
    }
400
401
    /**
402
     * @it Simple type inheriting multiple interfaces with leading ampersand
403
     */
404
    public function testSimpleTypeInheritingMultipleInterfacesWithLeadingAmpersand()
405
    {
406
        $body = 'type Hello implements & Wo & rld { field: String }';
407
        $loc = function($start, $end) {return TestUtils::locArray($start, $end);};
408
        $doc = Parser::parse($body);
409
        $expected = [
410
            'kind' => 'Document',
411
            'definitions' => [
412
                [
413
                    'kind' => 'ObjectTypeDefinition',
414
                    'name' => $this->nameNode('Hello', $loc(5, 10)),
415
                    'interfaces' => [
416
                        $this->typeNode('Wo', $loc(24, 26)),
417
                        $this->typeNode('rld', $loc(29, 32)),
418
                    ],
419
                    'directives' => [],
420
                    'fields' => [
421
                        $this->fieldNode(
422
                            $this->nameNode('field', $loc(35, 40)),
423
                            $this->typeNode('String', $loc(42, 48)),
424
                            $loc(35, 48)
425
                        ),
426
                    ],
427
                    'loc' => $loc(0, 50),
428
                    'description' => null,
429
                ],
430
            ],
431
            'loc' => $loc(0, 50),
432
        ];
433
        $this->assertEquals($expected, TestUtils::nodeToArray($doc));
434
    }
435
436
    /**
437
     * @it Single value enum
438
     */
439
    public function testSingleValueEnum()
440
    {
441
        $body = 'enum Hello { WORLD }';
442
        $loc = function($start, $end) {return TestUtils::locArray($start, $end);};
443
        $doc = Parser::parse($body);
444
445
        $expected = [
446
            'kind' => NodeKind::DOCUMENT,
447
            'definitions' => [
448
                [
449
                    'kind' => NodeKind::ENUM_TYPE_DEFINITION,
450
                    'name' => $this->nameNode('Hello', $loc(5, 10)),
451
                    'directives' => [],
452
                    'values' => [$this->enumValueNode('WORLD', $loc(13, 18))],
453
                    'loc' => $loc(0, 20),
454
                    'description' => null
455
                ]
456
            ],
457
            'loc' => $loc(0, 20)
458
        ];
459
460
        $this->assertEquals($expected, TestUtils::nodeToArray($doc));
461
    }
462
463
    /**
464
     * @it Double value enum
465
     */
466
    public function testDoubleValueEnum()
467
    {
468
        $body = 'enum Hello { WO, RLD }';
469
        $loc = function($start, $end) {return TestUtils::locArray($start, $end);};
470
        $doc = Parser::parse($body);
471
472
        $expected = [
473
            'kind' => NodeKind::DOCUMENT,
474
            'definitions' => [
475
                [
476
                    'kind' => NodeKind::ENUM_TYPE_DEFINITION,
477
                    'name' => $this->nameNode('Hello', $loc(5, 10)),
478
                    'directives' => [],
479
                    'values' => [
480
                        $this->enumValueNode('WO', $loc(13, 15)),
481
                        $this->enumValueNode('RLD', $loc(17, 20))
482
                    ],
483
                    'loc' => $loc(0, 22),
484
                    'description' => null
485
                ]
486
            ],
487
            'loc' => $loc(0, 22)
488
        ];
489
490
        $this->assertEquals($expected, TestUtils::nodeToArray($doc));
491
    }
492
493
    /**
494
     * @it Simple interface
495
     */
496
    public function testSimpleInterface()
497
    {
498
        $body = '
499
interface Hello {
500
  world: String
501
}';
502
        $doc = Parser::parse($body);
503
        $loc = function($start, $end) {return TestUtils::locArray($start, $end);};
504
505
        $expected = [
506
            'kind' => NodeKind::DOCUMENT,
507
            'definitions' => [
508
                [
509
                    'kind' => NodeKind::INTERFACE_TYPE_DEFINITION,
510
                    'name' => $this->nameNode('Hello', $loc(11, 16)),
511
                    'directives' => [],
512
                    'fields' => [
513
                        $this->fieldNode(
514
                            $this->nameNode('world', $loc(21, 26)),
515
                            $this->typeNode('String', $loc(28, 34)),
516
                            $loc(21, 34)
517
                        )
518
                    ],
519
                    'loc' => $loc(1, 36),
520
                    'description' => null
521
                ]
522
            ],
523
            'loc' => $loc(0,36)
524
        ];
525
        $this->assertEquals($expected, TestUtils::nodeToArray($doc));
526
    }
527
528
    /**
529
     * @it Simple field with arg
530
     */
531
    public function testSimpleFieldWithArg()
532
    {
533
        $body = '
534
type Hello {
535
  world(flag: Boolean): String
536
}';
537
        $doc = Parser::parse($body);
538
        $loc = function($start, $end) {return TestUtils::locArray($start, $end);};
539
540
        $expected = [
541
            'kind' => NodeKind::DOCUMENT,
542
            'definitions' => [
543
                [
544
                    'kind' => NodeKind::OBJECT_TYPE_DEFINITION,
545
                    'name' => $this->nameNode('Hello', $loc(6, 11)),
546
                    'interfaces' => [],
547
                    'directives' => [],
548
                    'fields' => [
549
                        $this->fieldNodeWithArgs(
550
                            $this->nameNode('world', $loc(16, 21)),
551
                            $this->typeNode('String', $loc(38, 44)),
552
                            [
553
                                $this->inputValueNode(
554
                                    $this->nameNode('flag', $loc(22, 26)),
555
                                    $this->typeNode('Boolean', $loc(28, 35)),
556
                                    null,
557
                                    $loc(22, 35)
558
                                )
559
                            ],
560
                            $loc(16, 44)
561
                        )
562
                    ],
563
                    'loc' => $loc(1, 46),
564
                    'description' => null
565
                ]
566
            ],
567
            'loc' => $loc(0, 46)
568
        ];
569
570
        $this->assertEquals($expected, TestUtils::nodeToArray($doc));
571
    }
572
573
    /**
574
     * @it Simple field with arg with default value
575
     */
576
    public function testSimpleFieldWithArgWithDefaultValue()
577
    {
578
        $body = '
579
type Hello {
580
  world(flag: Boolean = true): String
581
}';
582
        $doc = Parser::parse($body);
583
        $loc = function($start, $end) {return TestUtils::locArray($start, $end);};
584
585
        $expected = [
586
            'kind' => NodeKind::DOCUMENT,
587
            'definitions' => [
588
                [
589
                    'kind' => NodeKind::OBJECT_TYPE_DEFINITION,
590
                    'name' => $this->nameNode('Hello', $loc(6, 11)),
591
                    'interfaces' => [],
592
                    'directives' => [],
593
                    'fields' => [
594
                        $this->fieldNodeWithArgs(
595
                            $this->nameNode('world', $loc(16, 21)),
596
                            $this->typeNode('String', $loc(45, 51)),
597
                            [
598
                                $this->inputValueNode(
599
                                    $this->nameNode('flag', $loc(22, 26)),
600
                                    $this->typeNode('Boolean', $loc(28, 35)),
601
                                    ['kind' => NodeKind::BOOLEAN, 'value' => true, 'loc' => $loc(38, 42)],
602
                                    $loc(22, 42)
603
                                )
604
                            ],
605
                            $loc(16, 51)
606
                        )
607
                    ],
608
                    'loc' => $loc(1, 53),
609
                    'description' => null
610
                ]
611
            ],
612
            'loc' => $loc(0, 53)
613
        ];
614
        $this->assertEquals($expected, TestUtils::nodeToArray($doc));
615
    }
616
617
    /**
618
     * @it Simple field with list arg
619
     */
620
    public function testSimpleFieldWithListArg()
621
    {
622
        $body = '
623
type Hello {
624
  world(things: [String]): String
625
}';
626
        $doc = Parser::parse($body);
627
        $loc = function($start, $end) {return TestUtils::locArray($start, $end);};
628
629
        $expected = [
630
            'kind' => NodeKind::DOCUMENT,
631
            'definitions' => [
632
                [
633
                    'kind' => NodeKind::OBJECT_TYPE_DEFINITION,
634
                    'name' => $this->nameNode('Hello', $loc(6, 11)),
635
                    'interfaces' => [],
636
                    'directives' => [],
637
                    'fields' => [
638
                        $this->fieldNodeWithArgs(
639
                            $this->nameNode('world', $loc(16, 21)),
640
                            $this->typeNode('String', $loc(41, 47)),
641
                            [
642
                                $this->inputValueNode(
643
                                    $this->nameNode('things', $loc(22,28)),
644
                                    ['kind' => NodeKind::LIST_TYPE, 'type' => $this->typeNode('String', $loc(31, 37)), 'loc' => $loc(30, 38)],
645
                                    null,
646
                                    $loc(22, 38)
647
                                )
648
                            ],
649
                            $loc(16, 47)
650
                        )
651
                    ],
652
                    'loc' => $loc(1, 49),
653
                    'description' => null
654
                ]
655
            ],
656
            'loc' => $loc(0, 49)
657
        ];
658
659
        $this->assertEquals($expected, TestUtils::nodeToArray($doc));
660
    }
661
662
    /**
663
     * @it Simple field with two args
664
     */
665
    public function testSimpleFieldWithTwoArgs()
666
    {
667
        $body = '
668
type Hello {
669
  world(argOne: Boolean, argTwo: Int): String
670
}';
671
        $doc = Parser::parse($body);
672
        $loc = function($start, $end) {return TestUtils::locArray($start, $end);};
673
674
        $expected = [
675
            'kind' => NodeKind::DOCUMENT,
676
            'definitions' => [
677
                [
678
                    'kind' => NodeKind::OBJECT_TYPE_DEFINITION,
679
                    'name' => $this->nameNode('Hello', $loc(6, 11)),
680
                    'interfaces' => [],
681
                    'directives' => [],
682
                    'fields' => [
683
                        $this->fieldNodeWithArgs(
684
                            $this->nameNode('world', $loc(16, 21)),
685
                            $this->typeNode('String', $loc(53, 59)),
686
                            [
687
                                $this->inputValueNode(
688
                                    $this->nameNode('argOne', $loc(22, 28)),
689
                                    $this->typeNode('Boolean', $loc(30, 37)),
690
                                    null,
691
                                    $loc(22, 37)
692
                                ),
693
                                $this->inputValueNode(
694
                                    $this->nameNode('argTwo', $loc(39, 45)),
695
                                    $this->typeNode('Int', $loc(47, 50)),
696
                                    null,
697
                                    $loc(39, 50)
698
                                )
699
                            ],
700
                            $loc(16, 59)
701
                        )
702
                    ],
703
                    'loc' => $loc(1, 61),
704
                    'description' => null
705
                ]
706
            ],
707
            'loc' => $loc(0, 61)
708
        ];
709
710
        $this->assertEquals($expected, TestUtils::nodeToArray($doc));
711
    }
712
713
    /**
714
     * @it Simple union
715
     */
716
    public function testSimpleUnion()
717
    {
718
        $body = 'union Hello = World';
719
        $doc = Parser::parse($body);
720
        $loc = function($start, $end) {return TestUtils::locArray($start, $end);};
721
        $expected = [
722
            'kind' => NodeKind::DOCUMENT,
723
            'definitions' => [
724
                [
725
                    'kind' => NodeKind::UNION_TYPE_DEFINITION,
726
                    'name' => $this->nameNode('Hello', $loc(6, 11)),
727
                    'directives' => [],
728
                    'types' => [$this->typeNode('World', $loc(14, 19))],
729
                    'loc' => $loc(0, 19),
730
                    'description' => null
731
                ]
732
            ],
733
            'loc' => $loc(0, 19)
734
        ];
735
736
        $this->assertEquals($expected, TestUtils::nodeToArray($doc));
737
    }
738
739
    /**
740
     * @it Union with two types
741
     */
742
    public function testUnionWithTwoTypes()
743
    {
744
        $body = 'union Hello = Wo | Rld';
745
        $doc = Parser::parse($body);
746
        $loc = function($start, $end) {return TestUtils::locArray($start, $end);};
747
748
        $expected = [
749
            'kind' => NodeKind::DOCUMENT,
750
            'definitions' => [
751
                [
752
                    'kind' => NodeKind::UNION_TYPE_DEFINITION,
753
                    'name' => $this->nameNode('Hello', $loc(6, 11)),
754
                    'directives' => [],
755
                    'types' => [
756
                        $this->typeNode('Wo', $loc(14, 16)),
757
                        $this->typeNode('Rld', $loc(19, 22))
758
                    ],
759
                    'loc' => $loc(0, 22),
760
                    'description' => null
761
                ]
762
            ],
763
            'loc' => $loc(0, 22)
764
        ];
765
        $this->assertEquals($expected, TestUtils::nodeToArray($doc));
766
    }
767
768
769
    /**
770
     * @it Union with two types and leading pipe
771
     */
772
    public function testUnionWithTwoTypesAndLeadingPipe()
773
    {
774
        $body = 'union Hello = | Wo | Rld';
775
        $doc = Parser::parse($body);
776
        $expected = [
777
            'kind' => 'Document',
778
            'definitions' => [
779
                [
780
                    'kind' => 'UnionTypeDefinition',
781
                    'name' => $this->nameNode('Hello', ['start' => 6, 'end' => 11]),
782
                    'directives' => [],
783
                    'types' => [
784
                        $this->typeNode('Wo', ['start' => 16, 'end' => 18]),
785
                        $this->typeNode('Rld', ['start' => 21, 'end' => 24]),
786
                    ],
787
                    'loc' => ['start' => 0, 'end' => 24],
788
                    'description' => null
789
                ]
790
            ],
791
            'loc' => ['start' => 0, 'end' => 24],
792
        ];
793
        $this->assertEquals($expected, TestUtils::nodeToArray($doc));
794
    }
795
796
    /**
797
     * @it Union fails with no types
798
     */
799
    public function testUnionFailsWithNoTypes()
800
    {
801
        $this->expectSyntaxError(
802
            'union Hello = |',
803
            'Expected Name, found <EOF>',
804
            $this->loc(1, 16)
805
        );
806
    }
807
808
    /**
809
     * @it Union fails with leading douple pipe
810
     */
811
    public function testUnionFailsWithLeadingDoublePipe()
812
    {
813
        $this->expectSyntaxError(
814
            'union Hello = || Wo | Rld',
815
            'Expected Name, found |',
816
            $this->loc(1, 16)
817
        );
818
    }
819
820
    /**
821
     * @it Union fails with double pipe
822
     */
823
    public function testUnionFailsWithDoublePipe()
824
    {
825
        $this->expectSyntaxError(
826
            'union Hello = Wo || Rld',
827
            'Expected Name, found |',
828
            $this->loc(1, 19)
829
        );
830
    }
831
832
    /**
833
     * @it Union fails with trailing pipe
834
     */
835
    public function testUnionFailsWithTrailingPipe()
836
    {
837
        $this->expectSyntaxError(
838
            'union Hello = | Wo | Rld |',
839
            'Expected Name, found <EOF>',
840
            $this->loc(1, 27)
841
        );
842
    }
843
844
    /**
845
     * @it Scalar
846
     */
847
    public function testScalar()
848
    {
849
        $body = 'scalar Hello';
850
        $doc = Parser::parse($body);
851
        $loc = function($start, $end) {return TestUtils::locArray($start, $end);};
852
        $expected = [
853
            'kind' => NodeKind::DOCUMENT,
854
            'definitions' => [
855
                [
856
                    'kind' => NodeKind::SCALAR_TYPE_DEFINITION,
857
                    'name' => $this->nameNode('Hello', $loc(7, 12)),
858
                    'directives' => [],
859
                    'loc' => $loc(0, 12),
860
                    'description' => null
861
                ]
862
            ],
863
            'loc' => $loc(0, 12)
864
        ];
865
        $this->assertEquals($expected, TestUtils::nodeToArray($doc));
866
    }
867
868
    /**
869
     * @it Simple input object
870
     */
871
    public function testSimpleInputObject()
872
    {
873
        $body = '
874
input Hello {
875
  world: String
876
}';
877
        $doc = Parser::parse($body);
878
        $loc = function($start, $end) {return TestUtils::locArray($start, $end);};
879
880
        $expected = [
881
            'kind' => NodeKind::DOCUMENT,
882
            'definitions' => [
883
                [
884
                    'kind' => NodeKind::INPUT_OBJECT_TYPE_DEFINITION,
885
                    'name' => $this->nameNode('Hello', $loc(7, 12)),
886
                    'directives' => [],
887
                    'fields' => [
888
                        $this->inputValueNode(
889
                            $this->nameNode('world', $loc(17, 22)),
890
                            $this->typeNode('String', $loc(24, 30)),
891
                            null,
892
                            $loc(17, 30)
893
                        )
894
                    ],
895
                    'loc' => $loc(1, 32),
896
                    'description' => null
897
                ]
898
            ],
899
            'loc' => $loc(0, 32)
900
        ];
901
        $this->assertEquals($expected, TestUtils::nodeToArray($doc));
902
    }
903
904
    /**
905
     * @it Simple input object with args should fail
906
     */
907
    public function testSimpleInputObjectWithArgsShouldFail()
908
    {
909
        $body = '
910
      input Hello {
911
        world(foo: Int): String
912
      }';
913
        $this->expectSyntaxError(
914
            $body,
915
            'Expected :, found (',
916
            $this->loc(3, 14)
917
        );
918
    }
919
920
    /**
921
     * @it Directive with incorrect locations
922
     */
923
    public function testDirectiveWithIncorrectLocationShouldFail()
924
    {
925
        $body = '
926
      directive @foo on FIELD | INCORRECT_LOCATION
927
';
928
        $this->expectSyntaxError(
929
            $body,
930
            'Unexpected Name "INCORRECT_LOCATION"',
931
            $this->loc(2, 33)
932
        );
933
    }
934
935
    public function testDoesNotAllowEmptyFields()
936
    {
937
        $body = 'type Hello { }';
938
        $this->expectSyntaxError($body, 'Syntax Error: Expected Name, found }', new SourceLocation(1, 14));
939
    }
940
941
    /**
942
     * @it Option: allowLegacySDLEmptyFields supports type with empty fields
943
     */
944
    public function testAllowLegacySDLEmptyFieldsOption()
945
    {
946
        $body = 'type Hello { }';
947
        $doc = Parser::parse($body, ['allowLegacySDLEmptyFields' => true]);
948
        $expected = [
949
            'definitions' => [
950
                [
951
                    'fields' => [],
952
                ],
953
            ],
954
        ];
955
        $this->assertArraySubset($expected, $doc->toArray(true));
956
    }
957
958
    public function testDoesntAllowLegacySDLImplementsInterfacesByDefault()
959
    {
960
        $body = 'type Hello implements Wo rld { field: String }';
961
        $this->expectSyntaxError($body, 'Syntax Error: Unexpected Name "rld"', new SourceLocation(1, 26));
962
    }
963
964
    /**
965
     * @it Option: allowLegacySDLImplementsInterfaces
966
     */
967
    public function testDefaultSDLImplementsInterfaces()
968
    {
969
        $body = 'type Hello implements Wo rld { field: String }';
970
        $doc = Parser::parse($body, ['allowLegacySDLImplementsInterfaces' => true]);
971
        $expected = [
972
            'definitions' => [
973
                [
974
                    'interfaces' => [
975
                        $this->typeNode('Wo', ['start' => 22, 'end' => 24]),
976
                        $this->typeNode('rld', ['start' => 25, 'end' => 28]),
977
                    ],
978
                ],
979
            ],
980
        ];
981
        $this->assertArraySubset($expected, $doc->toArray(true));
982
    }
983
984
    private function typeNode($name, $loc)
985
    {
986
        return [
987
            'kind' => NodeKind::NAMED_TYPE,
988
            'name' => ['kind' => NodeKind::NAME, 'value' => $name, 'loc' => $loc],
989
            'loc' => $loc
990
        ];
991
    }
992
993
    private function nameNode($name, $loc)
994
    {
995
        return [
996
            'kind' => NodeKind::NAME,
997
            'value' => $name,
998
            'loc' => $loc
999
        ];
1000
    }
1001
1002
    private function fieldNode($name, $type, $loc)
1003
    {
1004
        return $this->fieldNodeWithArgs($name, $type, [], $loc);
1005
    }
1006
1007
    private function fieldNodeWithArgs($name, $type, $args, $loc)
1008
    {
1009
        return [
1010
            'kind' => NodeKind::FIELD_DEFINITION,
1011
            'name' => $name,
1012
            'arguments' => $args,
1013
            'type' => $type,
1014
            'directives' => [],
1015
            'loc' => $loc,
1016
            'description' => null
1017
        ];
1018
    }
1019
1020
    private function enumValueNode($name, $loc)
1021
    {
1022
        return [
1023
            'kind' => NodeKind::ENUM_VALUE_DEFINITION,
1024
            'name' => $this->nameNode($name, $loc),
1025
            'directives' => [],
1026
            'loc' => $loc,
1027
            'description' => null
1028
        ];
1029
    }
1030
1031
    private function inputValueNode($name, $type, $defaultValue, $loc)
1032
    {
1033
        return [
1034
            'kind' => NodeKind::INPUT_VALUE_DEFINITION,
1035
            'name' => $name,
1036
            'type' => $type,
1037
            'defaultValue' => $defaultValue,
1038
            'directives' => [],
1039
            'loc' => $loc,
1040
            'description' => null
1041
        ];
1042
    }
1043
1044
    private function loc($line, $column)
1045
    {
1046
        return new SourceLocation($line, $column);
1047
    }
1048
1049
    private function expectSyntaxError($text, $message, $location)
1050
    {
1051
        $this->expectException(SyntaxError::class);
1052
        $this->expectExceptionMessage($message);
1053
        try {
1054
            Parser::parse($text);
1055
        } catch (SyntaxError $error) {
1056
            $this->assertEquals([$location], $error->getLocations());
1057
            throw $error;
1058
        }
1059
    }
1060
}
1061