Passed
Push — master ( a27dd3...975c9f )
by Vladimir
11:24
created

testAllowsNullableInputsToBeOmittedInAVariable()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 10
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 3
c 2
b 0
f 0
dl 0
loc 10
rs 10
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\EnumType;
12
use GraphQL\Type\Definition\InputObjectType;
13
use GraphQL\Type\Definition\ObjectType;
14
use GraphQL\Type\Definition\Type;
15
use GraphQL\Type\Schema;
16
use GraphQL\Utils\Utils;
17
use PHPUnit\Framework\TestCase;
18
use function acos;
19
use function array_key_exists;
20
use function json_encode;
21
22
/**
23
 * Execute: Handles inputs
24
 * Handles objects and nullability
25
 */
26
class VariablesTest extends TestCase
27
{
28
    public function testUsingInlineStructs() : void
29
    {
30
        // executes with complex input:
31
        $result = $this->executeQuery('
32
        {
33
          fieldWithObjectInput(input: {a: "foo", b: ["bar"], c: "baz"})
34
        }
35
        ');
36
37
        $expected = [
38
            'data' => ['fieldWithObjectInput' => '{"a":"foo","b":["bar"],"c":"baz"}'],
39
        ];
40
        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

40
        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...
41
42
        // properly parses single value to list:
43
        $result   = $this->executeQuery('
44
        {
45
          fieldWithObjectInput(input: {a: "foo", b: "bar", c: "baz"})
46
        }
47
        ');
48
        $expected = ['data' => ['fieldWithObjectInput' => '{"a":"foo","b":["bar"],"c":"baz"}']];
49
50
        self::assertEquals($expected, $result->toArray());
51
52
        $result   = $this->executeQuery(
53
            '
54
            query ($input: TestInputObject) {
55
              fieldWithObjectInput(input: $input)
56
            }
57
            ',
58
            ['input' => ['a' => 'foo', 'b' => 'bar', 'c' => 'baz']]
59
        );
60
        $expected = ['data' => ['fieldWithObjectInput' => '{"a":"foo","b":["bar"],"c":"baz"}']];
61
62
        self::assertEquals($expected, $result->toArray());
63
64
        // properly parses null value to null
65
        $result   = $this->executeQuery('
66
        {
67
          fieldWithObjectInput(input: {a: null, b: null, c: "C", d: null})
68
        }
69
        ');
70
        $expected = ['data' => ['fieldWithObjectInput' => '{"a":null,"b":null,"c":"C","d":null}']];
71
72
        self::assertEquals($expected, $result->toArray());
73
74
        // properly parses null value in list
75
        $result   = $this->executeQuery('
76
        {
77
          fieldWithObjectInput(input: {b: ["A",null,"C"], c: "C"})
78
        }
79
        ');
80
        $expected = ['data' => ['fieldWithObjectInput' => '{"b":["A",null,"C"],"c":"C"}']];
81
82
        self::assertEquals($expected, $result->toArray());
83
84
        // does not use incorrect value
85
        $result = $this->executeQuery('
86
        {
87
          fieldWithObjectInput(input: ["foo", "bar", "baz"])
88
        }
89
        ');
90
91
        $expected = [
92
            'data'   => ['fieldWithObjectInput' => null],
93
            'errors' => [[
94
                'message'   => 'Argument "input" has invalid value ["foo", "bar", "baz"].',
95
                'path'      => ['fieldWithObjectInput'],
96
                'locations' => [['line' => 3, 'column' => 39]],
97
            ],
98
            ],
99
        ];
100
        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

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