Failed Conditions
Push — master ( 177394...bf4e7d )
by Vladimir
09:19
created

testWhenNoRuntimeValueIsProvidedToANonNullArgument()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 12
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 4
dl 0
loc 12
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace GraphQL\Tests\Executor;
6
7
use GraphQL\Error\Error;
8
use GraphQL\Executor\Executor;
9
use GraphQL\Language\Parser;
10
use GraphQL\Tests\Executor\TestClasses\ComplexScalar;
11
use GraphQL\Type\Definition\InputObjectType;
12
use GraphQL\Type\Definition\ObjectType;
13
use GraphQL\Type\Definition\Type;
14
use GraphQL\Type\Schema;
15
use PHPUnit\Framework\TestCase;
16
use function array_key_exists;
17
use function json_encode;
18
19
/**
20
 * Execute: Handles inputs
21
 * Handles objects and nullability
22
 */
23
class VariablesTest extends TestCase
24
{
25
    public function testUsingInlineStructs() : void
26
    {
27
        // executes with complex input:
28
        $result = $this->executeQuery('
29
        {
30
          fieldWithObjectInput(input: {a: "foo", b: ["bar"], c: "baz"})
31
        }
32
        ');
33
34
        $expected = [
35
            'data' => ['fieldWithObjectInput' => '{"a":"foo","b":["bar"],"c":"baz"}'],
36
        ];
37
        self::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
        self::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
        self::assertEquals($expected, $result->toArray());
48
49
        $result   = $this->executeQuery(
50
            '
51
            query ($input: TestInputObject) {
52
              fieldWithObjectInput(input: $input)
53
            }
54
            ',
55
            ['input' => ['a' => 'foo', 'b' => 'bar', 'c' => 'baz']]
56
        );
57
        $expected = ['data' => ['fieldWithObjectInput' => '{"a":"foo","b":["bar"],"c":"baz"}']];
58
59
        self::assertEquals($expected, $result->toArray());
60
61
        // properly parses null value to null
62
        $result   = $this->executeQuery('
63
        {
64
          fieldWithObjectInput(input: {a: null, b: null, c: "C", d: null})
65
        }
66
        ');
67
        $expected = ['data' => ['fieldWithObjectInput' => '{"a":null,"b":null,"c":"C","d":null}']];
68
69
        self::assertEquals($expected, $result->toArray());
70
71
        // properly parses null value in list
72
        $result   = $this->executeQuery('
73
        {
74
          fieldWithObjectInput(input: {b: ["A",null,"C"], c: "C"})
75
        }
76
        ');
77
        $expected = ['data' => ['fieldWithObjectInput' => '{"b":["A",null,"C"],"c":"C"}']];
78
79
        self::assertEquals($expected, $result->toArray());
80
81
        // does not use incorrect value
82
        $result = $this->executeQuery('
83
        {
84
          fieldWithObjectInput(input: ["foo", "bar", "baz"])
85
        }
86
        ');
87
88
        $expected = [
89
            'data'   => ['fieldWithObjectInput' => null],
90
            'errors' => [[
91
                'message'   => 'Argument "input" has invalid value ["foo", "bar", "baz"].',
92
                'path'      => ['fieldWithObjectInput'],
93
                'locations' => [['line' => 3, 'column' => 39]],
94
            ],
95
            ],
96
        ];
97
        self::assertArraySubset($expected, $result->toArray());
0 ignored issues
show
Deprecated Code introduced by
The function PHPUnit\Framework\Assert::assertArraySubset() has been deprecated: https://github.com/sebastianbergmann/phpunit/issues/3494 ( Ignorable by Annotation )

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

97
        /** @scrutinizer ignore-deprecated */ self::assertArraySubset($expected, $result->toArray());

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

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