Passed
Pull Request — master (#586)
by Šimon
12:52
created

VariablesTest   B

Complexity

Total Complexity 43

Size/Duplication

Total Lines 1064
Duplicated Lines 0 %

Importance

Changes 4
Bugs 0 Features 0
Metric Value
wmc 43
eloc 417
c 4
b 0
f 0
dl 0
loc 1064
rs 8.96

How to fix   Complexity   

Complex Class

Complex classes like VariablesTest often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use VariablesTest, and based on these observations, apply Extract Interface, too.

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