Failed Conditions
Push — master ( 804daa...8817e8 )
by Vladimir
04:14
created

VariablesTest   A

Complexity

Total Complexity 38

Size/Duplication

Total Lines 905
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 38
eloc 341
dl 0
loc 905
rs 9.36
c 0
b 0
f 0

37 Methods

Rating   Name   Duplication   Size   Complexity  
A executeQuery() 0 4 1
A testAllowsNullableInputsToBeOmittedInAVariable() 0 10 1
A testAllowsNonNullListsToContainNull() 0 10 1
A testAllowsNonNullableInputsToBeOmittedGivenADefault() 0 11 1
A testDoesNotAllowInvalidTypesToBeUsedAsValues() 0 21 1
A testAllowsNullableInputsToBeSetToAValueInAVariable() 0 10 1
A fieldWithInputArg() 0 10 2
A testAllowsListsOfNonNullsToContainValues() 0 10 1
A testAllowsListsToContainNull() 0 10 1
A testDoesNotAllowListsOfNonNullsToContainNull() 0 20 1
A schema() 0 45 1
A testAllowsListsToContainValues() 0 10 1
A testAllowsNullableInputsToBeSetToNullInAVariable() 0 10 1
B testUsingVariables() 0 144 1
A testDoesNotAllowNonNullListsToBeNull() 0 20 1
A testDoesntAllowNonNullableInputsToBeOmittedInAVariable() 0 18 1
A testAllowsNonNullListsOfNonNullsToContainValues() 0 10 1
A testDoesNotAllowNonNullListsOfNonNullsToContainNull() 0 20 1
A testAllowsNonNullableInputsToBeSetToAValueDirectly() 0 9 1
A testDoesNotAllowNonNullListsOfNonNullsToBeNull() 0 20 1
A testUsingInlineStructs() 0 72 1
A testAllowsNullableInputsToBeOmittedInAnUnlistedVariable() 0 9 1
A testAllowsNullableInputsToBeOmitted() 0 12 1
A testWhenOmittedVariableProvided() 0 9 1
A testAllowsNonNullableInputsToBeSetToAValueInAVariable() 0 10 1
A testDoesNotAllowUnknownTypesToBeUsedAsValues() 0 22 1
A testNotWhenArgumentCannotBeCoerced() 0 18 1
A testReportsErrorForArrayPassedIntoStringInput() 0 23 1
A testAllowsListsToBeNull() 0 11 1
A testReportsErrorForNonProvidedVariablesForNonNullableInputs() 0 24 1
A testDoesNotAllowNonNullableInputsToBeSetToNullInAVariable() 0 20 1
A testSerializingAnArrayViaGraphQLStringThrowsTypeError() 0 5 1
A testWhenNoArgumentProvided() 0 9 1
A testAllowsNullableInputsToBeSetToAValueDirectly() 0 9 1
A testAllowsNonNullListsToContainValues() 0 10 1
A testReportsErrorForMissingNonNullableInputs() 0 17 1
A testAllowsListsOfNonNullsToBeNull() 0 10 1
1
<?php
2
namespace GraphQL\Tests\Executor;
3
4
require_once __DIR__ . '/TestClasses.php';
5
6
use GraphQL\Error\Error;
7
use GraphQL\Executor\Executor;
8
use GraphQL\Language\Parser;
9
use GraphQL\Type\Schema;
10
use GraphQL\Type\Definition\InputObjectType;
11
use GraphQL\Type\Definition\ObjectType;
12
use GraphQL\Type\Definition\Type;
13
use PHPUnit\Framework\TestCase;
14
15
class VariablesTest extends TestCase
16
{
17
    // Execute: Handles inputs
18
    // Handles objects and nullability
19
20
    /**
21
     * @describe using inline structs
22
     */
23
    public function testUsingInlineStructs()
24
    {
25
        // executes with complex input:
26
        $result = $this->executeQuery('
27
        {
28
          fieldWithObjectInput(input: {a: "foo", b: ["bar"], c: "baz"})
29
        }
30
        ');
31
32
        $expected = [
33
            'data' => [
34
            'fieldWithObjectInput' => '{"a":"foo","b":["bar"],"c":"baz"}'
35
          ]
36
        ];
37
        $this->assertEquals($expected, $result->toArray());
0 ignored issues
show
Bug introduced by
The method toArray() does not exist on GraphQL\Executor\Promise\Promise. ( Ignorable by Annotation )

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

37
        $this->assertEquals($expected, $result->/** @scrutinizer ignore-call */ toArray());

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
38
39
        // properly parses single value to list:
40
        $result = $this->executeQuery('
41
        {
42
          fieldWithObjectInput(input: {a: "foo", b: "bar", c: "baz"})
43
        }
44
        ');
45
        $expected = ['data' => ['fieldWithObjectInput' => '{"a":"foo","b":["bar"],"c":"baz"}']];
46
47
        $this->assertEquals($expected, $result->toArray());
48
49
        // properly parses null value to null
50
        $result = $this->executeQuery('
51
        {
52
          fieldWithObjectInput(input: {a: null, b: null, c: "C", d: null})
53
        }
54
        ');
55
        $expected = ['data' => ['fieldWithObjectInput' => '{"a":null,"b":null,"c":"C","d":null}']];
56
57
        $this->assertEquals($expected, $result->toArray());
58
59
        // properly parses null value in list
60
        $result = $this->executeQuery('
61
        {
62
          fieldWithObjectInput(input: {b: ["A",null,"C"], c: "C"})
63
        }
64
        ');
65
        $expected = ['data' => ['fieldWithObjectInput' => '{"b":["A",null,"C"],"c":"C"}']];
66
67
        $this->assertEquals($expected, $result->toArray());
68
69
        // does not use incorrect value
70
        $result = $this->executeQuery('
71
        {
72
          fieldWithObjectInput(input: ["foo", "bar", "baz"])
73
        }
74
        ');
75
76
        $expected = [
77
            'data' => ['fieldWithObjectInput' => null],
78
            'errors' => [[
79
                'message' => 'Argument "input" has invalid value ["foo", "bar", "baz"].',
80
                'path' => ['fieldWithObjectInput'],
81
                'locations' => [['line' => 3, 'column' => 39]]
82
            ]]
83
        ];
84
        $this->assertArraySubset($expected, $result->toArray());
85
86
        // properly runs parseLiteral on complex scalar types
87
        $result = $this->executeQuery('
88
        {
89
          fieldWithObjectInput(input: {c: "foo", d: "SerializedValue"})
90
        }
91
        ');
92
        $this->assertEquals(
93
            ['data' => ['fieldWithObjectInput' => '{"c":"foo","d":"DeserializedValue"}']],
94
            $result->toArray()
95
        );
96
    }
97
98
    /**
99
     * @describe using variables
100
     */
101
    public function testUsingVariables()
102
    {
103
        $doc = '
104
            query q($input:TestInputObject) {
105
              fieldWithObjectInput(input: $input)
106
            }
107
        ';
108
109
        // executes with complex input:
110
        $params = ['input' => ['a' => 'foo', 'b' => ['bar'], 'c' => 'baz']];
111
        $result = $this->executeQuery($doc, $params);
112
113
        $this->assertEquals(
114
            ['data' => ['fieldWithObjectInput' => '{"a":"foo","b":["bar"],"c":"baz"}']],
115
            $result->toArray()
116
        );
117
118
        // uses default value when not provided:
119
        $result = $this->executeQuery('
120
          query ($input: TestInputObject = {a: "foo", b: ["bar"], c: "baz"}) {
121
            fieldWithObjectInput(input: $input)
122
          }
123
        ');
124
125
        $expected = [
126
            'data' => ['fieldWithObjectInput' => '{"a":"foo","b":["bar"],"c":"baz"}']
127
        ];
128
        $this->assertEquals($expected, $result->toArray());
129
130
        // properly parses single value to list:
131
        $params = ['input' => ['a' => 'foo', 'b' => 'bar', 'c' => 'baz']];
132
        $result = $this->executeQuery($doc, $params);
133
        $this->assertEquals(
134
            ['data' => ['fieldWithObjectInput' => '{"a":"foo","b":["bar"],"c":"baz"}']],
135
            $result->toArray()
136
        );
137
138
        // executes with complex scalar input:
139
        $params = [ 'input' => [ 'c' => 'foo', 'd' => 'SerializedValue' ] ];
140
        $result = $this->executeQuery($doc, $params);
141
        $expected = [
142
          'data' => [
143
            'fieldWithObjectInput' => '{"c":"foo","d":"DeserializedValue"}'
144
          ]
145
        ];
146
        $this->assertEquals($expected, $result->toArray());
147
148
        // errors on null for nested non-null:
149
        $params = ['input' => ['a' => 'foo', 'b' => 'bar', 'c' => null]];
150
        $result = $this->executeQuery($doc, $params);
151
        $expected = [
152
            'errors' => [
153
                [
154
                    'message' =>
155
                        'Variable "$input" got invalid value ' .
156
                        '{"a":"foo","b":"bar","c":null}; ' .
157
                        'Expected non-nullable type String! not to be null at value.c.',
158
                    'locations' => [['line' => 2, 'column' => 21]],
159
                    'category' => 'graphql'
160
                ]
161
            ]
162
        ];
163
164
        $this->assertEquals($expected, $result->toArray());
165
166
        // errors on incorrect type:
167
        $params = [ 'input' => 'foo bar' ];
168
        $result = $this->executeQuery($doc, $params);
169
        $expected = [
170
            'errors' => [
171
                [
172
                    'message' =>
173
                        'Variable "$input" got invalid value "foo bar"; ' .
174
                        'Expected type TestInputObject to be an object.',
175
                    'locations' => [ [ 'line' => 2, 'column' => 21 ] ],
176
                    'category' => 'graphql',
177
                ]
178
            ]
179
        ];
180
        $this->assertEquals($expected, $result->toArray());
181
182
        // errors on omission of nested non-null:
183
        $params = ['input' => ['a' => 'foo', 'b' => 'bar']];
184
185
        $result = $this->executeQuery($doc, $params);
186
        $expected = [
187
            'errors' => [
188
                [
189
                    'message' =>
190
                        'Variable "$input" got invalid value {"a":"foo","b":"bar"}; '.
191
                        'Field value.c of required type String! was not provided.',
192
                    'locations' => [['line' => 2, 'column' => 21]],
193
                    'category' => 'graphql',
194
                ]
195
            ]
196
        ];
197
        $this->assertEquals($expected, $result->toArray());
198
199
        // errors on deep nested errors and with many errors
200
        $nestedDoc = '
201
          query q($input: TestNestedInputObject) {
202
            fieldWithNestedObjectInput(input: $input)
203
          }
204
        ';
205
        $params = [ 'input' => [ 'na' => [ 'a' => 'foo' ] ] ];
206
207
        $result = $this->executeQuery($nestedDoc, $params);
208
        $expected = [
209
            'errors' => [
210
                [
211
                    'message' =>
212
                        'Variable "$input" got invalid value {"na":{"a":"foo"}}; ' .
213
                        'Field value.na.c of required type String! was not provided.',
214
                    'locations' => [['line' => 2, 'column' => 19]],
215
                    'category' => 'graphql',
216
                ],
217
                [
218
                    'message' =>
219
                        'Variable "$input" got invalid value {"na":{"a":"foo"}}; ' .
220
                        'Field value.nb of required type String! was not provided.',
221
                    'locations' => [['line' => 2, 'column' => 19]],
222
                    'category' => 'graphql',
223
                ]
224
            ]
225
        ];
226
        $this->assertEquals($expected, $result->toArray());
227
228
229
        // errors on addition of unknown input field
230
        $params = ['input' => [ 'a' => 'foo', 'b' => 'bar', 'c' => 'baz', 'extra' => 'dog' ]];
231
        $result = $this->executeQuery($doc, $params);
232
        $expected = [
233
            'errors' => [
234
                [
235
                    'message' =>
236
                        'Variable "$input" got invalid value ' .
237
                        '{"a":"foo","b":"bar","c":"baz","extra":"dog"}; ' .
238
                        'Field "extra" is not defined by type TestInputObject.',
239
                    'locations' => [['line' => 2, 'column' => 21]],
240
                    'category' => 'graphql',
241
                ]
242
            ]
243
        ];
244
        $this->assertEquals($expected, $result->toArray());
245
    }
246
247
    // Describe: Handles nullable scalars
248
249
    /**
250
     * @it allows nullable inputs to be omitted
251
     */
252
    public function testAllowsNullableInputsToBeOmitted()
253
    {
254
        $result = $this->executeQuery('
255
      {
256
        fieldWithNullableStringInput
257
      }
258
        ');
259
        $expected = [
260
            'data' => ['fieldWithNullableStringInput' => null]
261
        ];
262
263
        $this->assertEquals($expected, $result->toArray());
264
    }
265
266
    /**
267
     * @it allows nullable inputs to be omitted in a variable
268
     */
269
    public function testAllowsNullableInputsToBeOmittedInAVariable()
270
    {
271
        $result = $this->executeQuery('
272
      query SetsNullable($value: String) {
273
        fieldWithNullableStringInput(input: $value)
274
      }
275
        ');
276
        $expected = ['data' => ['fieldWithNullableStringInput' => null]];
277
278
        $this->assertEquals($expected, $result->toArray());
279
    }
280
281
    /**
282
     * @it allows nullable inputs to be omitted in an unlisted variable
283
     */
284
    public function testAllowsNullableInputsToBeOmittedInAnUnlistedVariable()
285
    {
286
        $result = $this->executeQuery('
287
      query SetsNullable {
288
        fieldWithNullableStringInput(input: $value)
289
      }
290
      ');
291
        $expected = ['data' => ['fieldWithNullableStringInput' => null]];
292
        $this->assertEquals($expected, $result->toArray());
293
    }
294
295
    /**
296
     * @it allows nullable inputs to be set to null in a variable
297
     */
298
    public function testAllowsNullableInputsToBeSetToNullInAVariable()
299
    {
300
        $result = $this->executeQuery('
301
      query SetsNullable($value: String) {
302
        fieldWithNullableStringInput(input: $value)
303
      }
304
        ');
305
        $expected = ['data' => ['fieldWithNullableStringInput' => null]];
306
307
        $this->assertEquals($expected, $result->toArray());
308
    }
309
310
    /**
311
     * @it allows nullable inputs to be set to a value in a variable
312
     */
313
    public function testAllowsNullableInputsToBeSetToAValueInAVariable()
314
    {
315
        $doc = '
316
      query SetsNullable($value: String) {
317
        fieldWithNullableStringInput(input: $value)
318
      }
319
        ';
320
        $result = $this->executeQuery($doc, ['value' => 'a']);
321
        $expected = ['data' => ['fieldWithNullableStringInput' => '"a"']];
322
        $this->assertEquals($expected, $result->toArray());
323
    }
324
325
    /**
326
     * @it allows nullable inputs to be set to a value directly
327
     */
328
    public function testAllowsNullableInputsToBeSetToAValueDirectly()
329
    {
330
        $result = $this->executeQuery('
331
      {
332
        fieldWithNullableStringInput(input: "a")
333
      }
334
        ');
335
        $expected = ['data' => ['fieldWithNullableStringInput' => '"a"']];
336
        $this->assertEquals($expected, $result->toArray());
337
    }
338
339
340
    // Describe: Handles non-nullable scalars
341
342
    /**
343
     * @it allows non-nullable inputs to be omitted given a default
344
     */
345
    public function testAllowsNonNullableInputsToBeOmittedGivenADefault()
346
    {
347
        $result = $this->executeQuery('
348
        query SetsNonNullable($value: String = "default") {
349
          fieldWithNonNullableStringInput(input: $value)
350
        }
351
        ');
352
        $expected = [
353
            'data' => ['fieldWithNonNullableStringInput' => '"default"']
354
        ];
355
        $this->assertEquals($expected, $result->toArray());
356
    }
357
358
    /**
359
     * @it does not allow non-nullable inputs to be omitted in a variable
360
     */
361
    public function testDoesntAllowNonNullableInputsToBeOmittedInAVariable()
362
    {
363
        $result = $this->executeQuery('
364
        query SetsNonNullable($value: String!) {
365
          fieldWithNonNullableStringInput(input: $value)
366
        }
367
        ');
368
369
        $expected = [
370
            'errors' => [
371
                [
372
                    'message' => 'Variable "$value" of required type "String!" was not provided.',
373
                    'locations' => [['line' => 2, 'column' => 31]],
374
                    'category' => 'graphql'
375
                ]
376
            ]
377
        ];
378
        $this->assertEquals($expected, $result->toArray());
379
    }
380
381
    /**
382
     * @it does not allow non-nullable inputs to be set to null in a variable
383
     */
384
    public function testDoesNotAllowNonNullableInputsToBeSetToNullInAVariable()
385
    {
386
        $doc = '
387
        query SetsNonNullable($value: String!) {
388
          fieldWithNonNullableStringInput(input: $value)
389
        }
390
        ';
391
        $result = $this->executeQuery($doc, ['value' => null]);
392
        $expected = [
393
            'errors' => [
394
                [
395
                    'message' =>
396
                        'Variable "$value" got invalid value null; ' .
397
                        'Expected non-nullable type String! not to be null.',
398
                    'locations' => [['line' => 2, 'column' => 31]],
399
                    'category' => 'graphql',
400
                ]
401
            ]
402
        ];
403
        $this->assertEquals($expected, $result->toArray());
404
    }
405
406
    /**
407
     * @it allows non-nullable inputs to be set to a value in a variable
408
     */
409
    public function testAllowsNonNullableInputsToBeSetToAValueInAVariable()
410
    {
411
        $doc = '
412
        query SetsNonNullable($value: String!) {
413
          fieldWithNonNullableStringInput(input: $value)
414
        }
415
        ';
416
        $result = $this->executeQuery($doc, ['value' => 'a']);
417
        $expected = ['data' => ['fieldWithNonNullableStringInput' => '"a"']];
418
        $this->assertEquals($expected, $result->toArray());
419
    }
420
421
    /**
422
     * @it allows non-nullable inputs to be set to a value directly
423
     */
424
    public function testAllowsNonNullableInputsToBeSetToAValueDirectly()
425
    {
426
        $result = $this->executeQuery('
427
      {
428
        fieldWithNonNullableStringInput(input: "a")
429
      }
430
        ');
431
        $expected = ['data' => ['fieldWithNonNullableStringInput' => '"a"']];
432
        $this->assertEquals($expected, $result->toArray());
433
    }
434
435
    /**
436
     * @it reports error for missing non-nullable inputs
437
     */
438
    public function testReportsErrorForMissingNonNullableInputs()
439
    {
440
        $result = $this->executeQuery('
441
      {
442
        fieldWithNonNullableStringInput
443
      }
444
        ');
445
        $expected = [
446
            'data' => ['fieldWithNonNullableStringInput' => null],
447
            'errors' => [[
448
                'message' => 'Argument "input" of required type "String!" was not provided.',
449
                'locations' => [ [ 'line' => 3, 'column' => 9 ] ],
450
                'path' => [ 'fieldWithNonNullableStringInput' ],
451
                'category' => 'graphql',
452
            ]]
453
        ];
454
        $this->assertEquals($expected, $result->toArray());
455
    }
456
457
    /**
458
     * @it reports error for array passed into string input
459
     */
460
    public function testReportsErrorForArrayPassedIntoStringInput()
461
    {
462
463
        $doc = '
464
        query SetsNonNullable($value: String!) {
465
          fieldWithNonNullableStringInput(input: $value)
466
        }
467
        ';
468
        $variables = ['value' => [1, 2, 3]];
469
        $result = $this->executeQuery($doc, $variables);
470
471
        $expected = [
472
            'errors' => [[
473
                'message' =>
474
                    'Variable "$value" got invalid value [1,2,3]; Expected type ' .
475
                    'String; String cannot represent an array value: [1,2,3]',
476
                'category' => 'graphql',
477
                'locations' => [
478
                    ['line' => 2, 'column' => 31]
479
                ]
480
            ]]
481
        ];
482
        $this->assertEquals($expected, $result->toArray());
483
    }
484
485
    /**
486
     * @it serializing an array via GraphQLString throws TypeError
487
     */
488
    public function testSerializingAnArrayViaGraphQLStringThrowsTypeError()
489
    {
490
        $this->expectException(Error::class);
491
        $this->expectExceptionMessage('String cannot represent non scalar value: [1,2,3]');
492
        Type::string()->serialize([1, 2, 3]);
493
    }
494
495
    /**
496
     * @it reports error for non-provided variables for non-nullable inputs
497
     */
498
    public function testReportsErrorForNonProvidedVariablesForNonNullableInputs()
499
    {
500
        // Note: this test would typically fail validation before encountering
501
        // this execution error, however for queries which previously validated
502
        // and are being run against a new schema which have introduced a breaking
503
        // change to make a formerly non-required argument required, this asserts
504
        // failure before allowing the underlying code to receive a non-null value.
505
        $result = $this->executeQuery('
506
      {
507
        fieldWithNonNullableStringInput(input: $foo)
508
      }
509
        ');
510
        $expected = [
511
            'data' => ['fieldWithNonNullableStringInput' => null],
512
            'errors' => [[
513
                'message' =>
514
                    'Argument "input" of required type "String!" was provided the ' .
515
                    'variable "$foo" which was not provided a runtime value.',
516
                'locations' => [['line' => 3, 'column' => 48]],
517
                'path' => ['fieldWithNonNullableStringInput'],
518
                'category' => 'graphql',
519
            ]]
520
        ];
521
        $this->assertEquals($expected, $result->toArray());
522
    }
523
524
    // Describe: Handles lists and nullability
525
526
    /**
527
     * @it allows lists to be null
528
     */
529
    public function testAllowsListsToBeNull()
530
    {
531
        $doc = '
532
        query q($input:[String]) {
533
          list(input: $input)
534
        }
535
        ';
536
        $result = $this->executeQuery($doc, ['input' => null]);
537
        $expected = ['data' => ['list' => null]];
538
539
        $this->assertEquals($expected, $result->toArray());
540
    }
541
542
    /**
543
     * @it allows lists to contain values
544
     */
545
    public function testAllowsListsToContainValues()
546
    {
547
        $doc = '
548
        query q($input:[String]) {
549
          list(input: $input)
550
        }
551
        ';
552
        $result = $this->executeQuery($doc, ['input' => ['A']]);
553
        $expected = ['data' => ['list' => '["A"]']];
554
        $this->assertEquals($expected, $result->toArray());
555
    }
556
557
    /**
558
     * @it allows lists to contain null
559
     */
560
    public function testAllowsListsToContainNull()
561
    {
562
        $doc = '
563
        query q($input:[String]) {
564
          list(input: $input)
565
        }
566
        ';
567
        $result = $this->executeQuery($doc, ['input' => ['A',null,'B']]);
568
        $expected = ['data' => ['list' => '["A",null,"B"]']];
569
        $this->assertEquals($expected, $result->toArray());
570
    }
571
572
    /**
573
     * @it does not allow non-null lists to be null
574
     */
575
    public function testDoesNotAllowNonNullListsToBeNull()
576
    {
577
        $doc = '
578
        query q($input:[String]!) {
579
          nnList(input: $input)
580
        }
581
        ';
582
        $result = $this->executeQuery($doc, ['input' => null]);
583
        $expected = [
584
            'errors' => [
585
                [
586
                    'message' =>
587
                        'Variable "$input" got invalid value null; ' .
588
                        'Expected non-nullable type [String]! not to be null.',
589
                    'locations' => [['line' => 2, 'column' => 17]],
590
                    'category' => 'graphql',
591
                ]
592
            ]
593
        ];
594
        $this->assertEquals($expected, $result->toArray());
595
    }
596
597
    /**
598
     * @it allows non-null lists to contain values
599
     */
600
    public function testAllowsNonNullListsToContainValues()
601
    {
602
        $doc = '
603
        query q($input:[String]!) {
604
          nnList(input: $input)
605
        }
606
        ';
607
        $result = $this->executeQuery($doc, ['input' => ['A']]);
608
        $expected = ['data' => ['nnList' => '["A"]']];
609
        $this->assertEquals($expected, $result->toArray());
610
    }
611
612
    /**
613
     * @it allows non-null lists to contain null
614
     */
615
    public function testAllowsNonNullListsToContainNull()
616
    {
617
        $doc = '
618
        query q($input:[String]!) {
619
          nnList(input: $input)
620
        }
621
        ';
622
        $result = $this->executeQuery($doc, ['input' => ['A',null,'B']]);
623
        $expected = ['data' => ['nnList' => '["A",null,"B"]']];
624
        $this->assertEquals($expected, $result->toArray());
625
    }
626
627
    /**
628
     * @it allows lists of non-nulls to be null
629
     */
630
    public function testAllowsListsOfNonNullsToBeNull()
631
    {
632
        $doc = '
633
        query q($input:[String!]) {
634
          listNN(input: $input)
635
        }
636
        ';
637
        $result = $this->executeQuery($doc, ['input' => null]);
638
        $expected = ['data' => ['listNN' => null]];
639
        $this->assertEquals($expected, $result->toArray());
640
    }
641
642
    /**
643
     * @it allows lists of non-nulls to contain values
644
     */
645
    public function testAllowsListsOfNonNullsToContainValues()
646
    {
647
        $doc = '
648
        query q($input:[String!]) {
649
          listNN(input: $input)
650
        }
651
        ';
652
        $result = $this->executeQuery($doc, ['input' => ['A']]);
653
        $expected = ['data' => ['listNN' => '["A"]']];
654
        $this->assertEquals($expected, $result->toArray());
655
    }
656
657
    /**
658
     * @it does not allow lists of non-nulls to contain null
659
     */
660
    public function testDoesNotAllowListsOfNonNullsToContainNull()
661
    {
662
        $doc = '
663
        query q($input:[String!]) {
664
          listNN(input: $input)
665
        }
666
        ';
667
        $result = $this->executeQuery($doc, ['input' => ['A', null, 'B']]);
668
        $expected = [
669
            'errors' => [
670
                [
671
                    'message' =>
672
                        'Variable "$input" got invalid value ["A",null,"B"]; ' .
673
                        'Expected non-nullable type String! not to be null at value[1].',
674
                    'locations' => [ ['line' => 2, 'column' => 17] ],
675
                    'category' => 'graphql',
676
                ]
677
            ]
678
        ];
679
        $this->assertEquals($expected, $result->toArray());
680
    }
681
682
    /**
683
     * @it does not allow non-null lists of non-nulls to be null
684
     */
685
    public function testDoesNotAllowNonNullListsOfNonNullsToBeNull()
686
    {
687
        $doc = '
688
        query q($input:[String!]!) {
689
          nnListNN(input: $input)
690
        }
691
        ';
692
        $result = $this->executeQuery($doc, ['input' => null]);
693
        $expected = [
694
            'errors' => [
695
                [
696
                    'message' =>
697
                        'Variable "$input" got invalid value null; ' .
698
                        'Expected non-nullable type [String!]! not to be null.',
699
                    'locations' => [ ['line' => 2, 'column' => 17] ],
700
                    'category' => 'graphql',
701
                ]
702
            ]
703
        ];
704
        $this->assertEquals($expected, $result->toArray());
705
    }
706
707
    /**
708
     * @it allows non-null lists of non-nulls to contain values
709
     */
710
    public function testAllowsNonNullListsOfNonNullsToContainValues()
711
    {
712
        $doc = '
713
        query q($input:[String!]!) {
714
          nnListNN(input: $input)
715
        }
716
        ';
717
        $result = $this->executeQuery($doc, ['input' => ['A']]);
718
        $expected = ['data' => ['nnListNN' => '["A"]']];
719
        $this->assertEquals($expected, $result->toArray());
720
    }
721
722
    /**
723
     * @it does not allow non-null lists of non-nulls to contain null
724
     */
725
    public function testDoesNotAllowNonNullListsOfNonNullsToContainNull()
726
    {
727
        $doc = '
728
        query q($input:[String!]!) {
729
          nnListNN(input: $input)
730
        }
731
        ';
732
        $result = $this->executeQuery($doc, ['input' => ['A', null, 'B']]);
733
        $expected = [
734
            'errors' => [
735
                [
736
                    'message' =>
737
                        'Variable "$input" got invalid value ["A",null,"B"]; ' .
738
                        'Expected non-nullable type String! not to be null at value[1].',
739
                    'locations' => [ ['line' => 2, 'column' => 17] ],
740
                    'category' => 'graphql',
741
                ]
742
            ]
743
        ];
744
        $this->assertEquals($expected, $result->toArray());
745
    }
746
747
    /**
748
     * @it does not allow invalid types to be used as values
749
     */
750
    public function testDoesNotAllowInvalidTypesToBeUsedAsValues()
751
    {
752
        $doc = '
753
        query q($input: TestType!) {
754
          fieldWithObjectInput(input: $input)
755
        }
756
        ';
757
        $vars = [ 'input' => [ 'list' => [ 'A', 'B' ] ] ];
758
        $result = $this->executeQuery($doc, $vars);
759
        $expected = [
760
            'errors' => [
761
                [
762
                    'message' =>
763
                        'Variable "$input" expected value of type "TestType!" which cannot ' .
764
                        'be used as an input type.',
765
                    'locations' => [['line' => 2, 'column' => 25]],
766
                    'category' => 'graphql',
767
                ]
768
            ]
769
        ];
770
        $this->assertEquals($expected, $result->toArray());
771
    }
772
773
    /**
774
     * @it does not allow unknown types to be used as values
775
     */
776
    public function testDoesNotAllowUnknownTypesToBeUsedAsValues()
777
    {
778
        $doc = '
779
        query q($input: UnknownType!) {
780
          fieldWithObjectInput(input: $input)
781
        }
782
        ';
783
        $vars = ['input' => 'whoknows'];
784
785
        $result = $this->executeQuery($doc, $vars);
786
        $expected = [
787
            'errors' => [
788
                [
789
                    'message' =>
790
                        'Variable "$input" expected value of type "UnknownType!" which ' .
791
                        'cannot be used as an input type.',
792
                    'locations' => [['line' => 2, 'column' => 25]],
793
                    'category' => 'graphql',
794
                ]
795
            ]
796
        ];
797
        $this->assertEquals($expected, $result->toArray());
798
    }
799
800
    // Describe: Execute: Uses argument default values
801
    /**
802
     * @it when no argument provided
803
     */
804
    public function testWhenNoArgumentProvided()
805
    {
806
        $result = $this->executeQuery('{
807
        fieldWithDefaultArgumentValue
808
        }');
809
810
        $this->assertEquals(
811
            ['data' => ['fieldWithDefaultArgumentValue' => '"Hello World"']],
812
            $result->toArray()
813
        );
814
    }
815
816
    /**
817
     * @it when omitted variable provided
818
     */
819
    public function testWhenOmittedVariableProvided()
820
    {
821
        $result = $this->executeQuery('query optionalVariable($optional: String) {
822
            fieldWithDefaultArgumentValue(input: $optional)
823
        }');
824
825
        $this->assertEquals(
826
            ['data' => ['fieldWithDefaultArgumentValue' => '"Hello World"']],
827
            $result->toArray()
828
        );
829
    }
830
831
    /**
832
     * @it not when argument cannot be coerced
833
     */
834
    public function testNotWhenArgumentCannotBeCoerced()
835
    {
836
        $result = $this->executeQuery('{
837
            fieldWithDefaultArgumentValue(input: WRONG_TYPE)
838
        }');
839
840
        $expected = [
841
            'data' => ['fieldWithDefaultArgumentValue' => null],
842
            'errors' => [[
843
                'message' =>
844
                    'Argument "input" has invalid value WRONG_TYPE.',
845
                'locations' => [ [ 'line' => 2, 'column' => 50 ] ],
846
                'path' => [ 'fieldWithDefaultArgumentValue' ],
847
                'category' => 'graphql',
848
            ]]
849
        ];
850
851
        $this->assertEquals($expected, $result->toArray());
852
    }
853
854
855
    public function schema()
856
    {
857
        $ComplexScalarType = ComplexScalar::create();
858
859
        $TestInputObject = new InputObjectType([
860
            'name' => 'TestInputObject',
861
            'fields' => [
862
                'a' => ['type' => Type::string()],
863
                'b' => ['type' => Type::listOf(Type::string())],
864
                'c' => ['type' => Type::nonNull(Type::string())],
865
                'd' => ['type' => $ComplexScalarType],
866
            ]
867
        ]);
868
869
        $TestNestedInputObject = new InputObjectType([
870
            'name' => 'TestNestedInputObject',
871
            'fields' => [
872
                'na' => [ 'type' => Type::nonNull($TestInputObject) ],
873
                'nb' => [ 'type' => Type::nonNull(Type::string()) ],
874
            ],
875
        ]);
876
877
        $TestType = new ObjectType([
878
            'name' => 'TestType',
879
            'fields' => [
880
                'fieldWithObjectInput' => $this->fieldWithInputArg(['type' => $TestInputObject]),
881
                'fieldWithNullableStringInput' => $this->fieldWithInputArg(['type' => Type::string()]),
882
                'fieldWithNonNullableStringInput' => $this->fieldWithInputArg(['type' => Type::nonNull(Type::string())]),
883
                'fieldWithDefaultArgumentValue' => $this->fieldWithInputArg([
884
                    'type' => Type::string(),
885
                    'defaultValue' => 'Hello World',
886
                ]),
887
                'fieldWithNestedInputObject' => $this->fieldWithInputArg([
888
                    'type' => $TestNestedInputObject,
889
                    'defaultValue' => 'Hello World'
890
                ]),
891
                'list' => $this->fieldWithInputArg(['type' => Type::listOf(Type::string())]),
892
                'nnList' => $this->fieldWithInputArg(['type' => Type::nonNull(Type::listOf(Type::string()))]),
893
                'listNN' => $this->fieldWithInputArg(['type' => Type::listOf(Type::nonNull(Type::string()))]),
894
                'nnListNN' => $this->fieldWithInputArg(['type' => Type::nonNull(Type::listOf(Type::nonNull(Type::string())))]),
895
            ]
896
        ]);
897
898
        $schema = new Schema(['query' => $TestType]);
899
        return $schema;
900
    }
901
902
    private function fieldWithInputArg($inputArg)
903
    {
904
        return [
905
            'type' => Type::string(),
906
            'args' => ['input' => $inputArg],
907
            'resolve' => function ($_, $args) {
908
                if (isset($args['input'])) {
909
                    return json_encode($args['input']);
910
                }
911
                return null;
912
            },
913
        ];
914
    }
915
916
    private function executeQuery($query, $variableValues = null)
917
    {
918
        $document = Parser::parse($query);
919
        return Executor::execute($this->schema(), $document, null, null, $variableValues);
920
    }
921
}
922