Completed
Pull Request — master (#13)
by
unknown
05:06
created

ProcessorTest::testNestedVariables()   B

Complexity

Conditions 1
Paths 1

Size

Total Lines 27
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
c 2
b 0
f 0
dl 0
loc 27
rs 8.8571
cc 1
eloc 14
nc 1
nop 0
1
<?php
2
/*
3
 * This file is a part of GraphQL project.
4
 *
5
 * @author Alexandr Viniychuk <[email protected]>
6
 * created: 11:02 PM 5/13/16
7
 */
8
9
namespace Youshido\Tests\Schema;
10
11
12
use Youshido\GraphQL\Execution\Processor;
13
use Youshido\GraphQL\Execution\ResolveInfo;
14
use Youshido\GraphQL\Field\Field;
15
use Youshido\GraphQL\Schema\Schema;
16
use Youshido\GraphQL\Type\ListType\ListType;
17
use Youshido\GraphQL\Type\NonNullType;
18
use Youshido\GraphQL\Type\Object\ObjectType;
19
use Youshido\GraphQL\Type\Scalar\BooleanType;
20
use Youshido\GraphQL\Type\Scalar\IntType;
21
use Youshido\GraphQL\Type\Scalar\StringType;
22
use Youshido\GraphQL\Type\Union\UnionType;
23
use Youshido\Tests\DataProvider\TestEmptySchema;
24
use Youshido\Tests\DataProvider\TestEnumType;
25
use Youshido\Tests\DataProvider\TestInterfaceType;
26
use Youshido\Tests\DataProvider\TestObjectType;
27
use Youshido\Tests\DataProvider\TestSchema;
28
29
class ProcessorTest extends \PHPUnit_Framework_TestCase
30
{
31
32
    private $_counter = 0;
33
34
    /**
35
     * @expectedException \Youshido\GraphQL\Validator\Exception\ConfigurationException
36
     * @expectedExceptionMessage Schema has to have fields
37
     */
38
    public function testInit()
39
    {
40
        new Processor(new TestEmptySchema());
41
    }
42
43
    public function testEmptyQueries()
44
    {
45
        $processor = new Processor(new TestSchema());
46
        $processor->processPayload('');
47
        $this->assertEquals(['errors' => [
48
            ['message' => 'Must provide an operation.']
49
        ]], $processor->getResponseData());
50
51
        $processor->processPayload('{ me { name } }');
52
        $this->assertEquals(['data' => [
53
            'me' => ['name' => 'John']
54
        ]], $processor->getResponseData());
55
56
    }
57
58
  public function testNestedVariables() {
59
    $processor = new Processor(new TestSchema());
60
    $noArgsQuery = '{ me { echo(value:"foo") } }';
61
    $expectedData = ['data' => ['me' => ['echo' => 'foo']]];
62
    $processor->processPayload($noArgsQuery, ['value' => 'foo']);
63
    $this->assertEquals($expectedData, $processor->getResponseData());
64
65
    $parameterizedFieldQuery =
66
        'query nestedFieldQuery($value:String!){
67
          me {
68
            echo(value:$value)
69
          }
70
        }';
71
    $processor->processPayload($parameterizedFieldQuery, ['value' => 'foo']);
72
    $this->assertEquals($expectedData, $processor->getResponseData());
73
74
    $parameterizedQueryQuery =
75
        'query nestedQueryQuery($value:Int){
76
          me {
77
            location(noop:$value) {
78
              address
79
            }
80
          }
81
        }';
82
    $processor->processPayload($parameterizedQueryQuery, ['value' => 1]);
83
    $this->assertArrayNotHasKey('errors', $processor->getResponseData());
84
  }
85
86 View Code Duplication
    public function testListNullResponse()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
87
    {
88
        $processor = new Processor(new Schema([
89
            'query' => new ObjectType([
90
                'name' => 'RootQuery',
91
                'fields' => [
92
                    'list' => [
93
                        'type' => new ListType(new StringType()),
94
                        'resolve' => function() {
95
                            return null;
96
                        }
97
                    ]
98
                ]
99
            ])
100
        ]));
101
        $data = $processor->processPayload(' { list }')->getResponseData();
102
        $this->assertEquals(['data' => ['list' => null]], $data);
103
    }
104
105
106 View Code Duplication
    public function testSubscriptionNullResponse()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
107
    {
108
        $processor = new Processor(new Schema([
109
            'query' => new ObjectType([
110
                'name' => 'RootQuery',
111
                'fields' => [
112
                    'list' => [
113
                        'type' => new ListType(new StringType()),
114
                        'resolve' => function() {
115
                            return null;
116
                        }
117
                    ]
118
                ]
119
            ])
120
        ]));
121
        $data = $processor->processPayload(' { __schema { subscriptionType { name } } }')->getResponseData();
122
        $this->assertEquals(['data' => ['__schema' => ['subscriptionType' => null]]], $data);
123
    }
124
125
    public function testSchemaOperations()
126
    {
127
        $schema    = new Schema([
128
            'query' => new ObjectType([
129
                'name'   => 'RootQuery',
130
                'fields' => [
131
                    'me'                => [
132
                        'type'    => new ObjectType([
133
                            'name'   => 'User',
134
                            'fields' => [
135
                                'firstName' => [
136
                                    'type'    => new StringType(),
137
                                    'args'    => [
138
                                        'shorten' => new BooleanType()
139
                                    ],
140
                                    'resolve' => function ($value, $args) {
141
                                        return empty($args['shorten']) ? $value : $value;
142
                                    }
143
                                ],
144
                                'lastName'  => new StringType(),
145
                                'code'      => new StringType(),
146
                            ]
147
                        ]),
148
                        'resolve' => function ($value, $args) {
149
                            $data = ['firstName' => 'John', 'code' => '007'];
150
                            if (!empty($args['upper'])) {
151
                                foreach ($data as $key => $value) {
152
                                    $data[$key] = strtoupper($value);
153
                                }
154
                            }
155
156
                            return $data;
157
                        },
158
                        'args'    => [
159
                            'upper' => [
160
                                'type'    => new BooleanType(),
161
                                'default' => false
162
                            ]
163
                        ]
164
                    ],
165
                    'randomUser'        => [
166
                        'type'    => new TestObjectType(),
167
                        'resolve' => function () {
168
                            return ['invalidField' => 'John'];
169
                        }
170
                    ],
171
                    'invalidValueQuery' => [
172
                        'type'    => new TestObjectType(),
173
                        'resolve' => function () {
174
                            return 'stringValue';
175
                        }
176
                    ],
177
                ],
178
            ])
179
        ]);
180
        $processor = new Processor($schema);
181
182
        $processor->processPayload('{ me { firstName } }');
183
        $this->assertEquals(['data' => ['me' => ['firstName' => 'John']]], $processor->getResponseData());
184
185
        $processor->processPayload('{ me { firstName, lastName } }');
186
        $this->assertEquals(['data' => ['me' => ['firstName' => 'John', 'lastName' => null]]], $processor->getResponseData());
187
188
        $processor->processPayload('{ me { code } }');
189
        $this->assertEquals(['data' => ['me' => ['code' => 7]]], $processor->getResponseData());
190
191
        $processor->processPayload('{ me(upper:true) { firstName } }');
192
        $this->assertEquals(['data' => ['me' => ['firstName' => 'JOHN']]], $processor->getResponseData());
193
194
        $schema->getMutationType()
195
               ->addField(new Field([
196
                   'name'    => 'increaseCounter',
197
                   'type'    => new IntType(),
198
                   'resolve' => function ($value, $args, ResolveInfo $info) {
0 ignored issues
show
Unused Code introduced by
The parameter $info is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
199
                       return $this->_counter += $args['amount'];
200
                   },
201
                   'args'    => [
202
                       'amount' => [
203
                           'type'    => new IntType(),
204
                           'default' => 1
205
                       ]
206
                   ]
207
               ]))->addField(new Field([
208
                'name'    => 'invalidResolveTypeMutation',
209
                'type'    => new NonNullType(new IntType()),
210
                'resolve' => function () {
211
                    return null;
212
                }
213
            ]))->addField(new Field([
214
                'name'    => 'interfacedMutation',
215
                'type'    => new TestInterfaceType(),
216
                'resolve' => function () {
217
                    return ['name' => 'John'];
218
                }
219
            ]));
220
        $processor->processPayload('mutation { increaseCounter }');
221
        $this->assertEquals(['data' => ['increaseCounter' => 1]], $processor->getResponseData());
222
223
        $processor->processPayload('mutation { invalidMutation }');
224
        $this->assertEquals(['errors' => [['message' => 'Field "invalidMutation" not found in type "RootSchemaMutation"']]], $processor->getResponseData());
225
226
        $processor->processPayload('mutation { increaseCounter(noArg: 2) }');
227
        $this->assertEquals(['errors' => [['message' => 'Unknown argument "noArg" on field "increaseCounter"']]], $processor->getResponseData());
228
229
        $processor->processPayload('mutation { increaseCounter(amount: 2) { invalidProp } }');
230
        $this->assertEquals(['errors' => [['message' => 'Field "invalidProp" not found in type "Int"']], 'data' => ['increaseCounter' => null]], $processor->getResponseData());
231
232
        $processor->processPayload('mutation { increaseCounter(amount: 2) }');
233
        $this->assertEquals(['data' => ['increaseCounter' => 5]], $processor->getResponseData());
234
235
        $processor->processPayload('{ invalidQuery }');
236
        $this->assertEquals(['errors' => [['message' => 'Field "invalidQuery" not found in type "RootQuery"']]], $processor->getResponseData());
237
238
        $processor->processPayload('{ invalidValueQuery { id } }');
239
        $this->assertEquals(['errors' => [['message' => 'Not valid value for OBJECT field invalidValueQuery']], 'data' => ['invalidValueQuery' => null]], $processor->getResponseData());
240
241
        $processor->processPayload('{ me { firstName(shorten: true), middle }}');
242
        $this->assertEquals(['errors' => [['message' => 'Field "middle" not found in type "User"']], 'data' => ['me' => null]], $processor->getResponseData());
243
244
        $processor->processPayload('{ randomUser { region }}');
245
        $this->assertEquals(['errors' => [['message' => 'Property "region" not found in resolve result']]], $processor->getResponseData());
246
247
        $processor->processPayload('mutation { invalidResolveTypeMutation }');
248
        $this->assertEquals(['errors' => [['message' => 'Cannot return null for non-nullable field invalidResolveTypeMutation']], 'data' => ['invalidResolveTypeMutation' => null]], $processor->getResponseData());
249
250
        $processor->processPayload('mutation { user:interfacedMutation { name }  }');
251
        $this->assertEquals(['data' => ['user' => ['name' => 'John']]], $processor->getResponseData());
252
    }
253
254
    public function testListEnumsSchemaOperations()
255
    {
256
        $processor = new Processor(new Schema([
257
            'query' => new ObjectType([
258
                'name'   => 'RootQuery',
259
                'fields' => [
260
                    'listQuery'                 => [
261
                        'type'    => new ListType(new TestEnumType()),
262
                        'resolve' => function () {
263
                            return 'invalid list';
264
                        }
265
                    ],
266
                    'listEnumQuery'             => [
267
                        'type'    => new ListType(new TestEnumType()),
268
                        'resolve' => function () {
269
                            return ['invalid enum'];
270
                        }
271
                    ],
272
                    'invalidEnumQuery'          => [
273
                        'type'    => new TestEnumType(),
274
                        'resolve' => function () {
275
                            return 'invalid enum';
276
                        }
277
                    ],
278
                    'enumQuery'                 => [
279
                        'type'    => new TestEnumType(),
280
                        'resolve' => function () {
281
                            return 1;
282
                        }
283
                    ],
284
                    'invalidNonNullQuery'       => [
285
                        'type'    => new NonNullType(new IntType()),
286
                        'resolve' => function () {
287
                            return null;
288
                        }
289
                    ],
290
                    'invalidNonNullInsideQuery' => [
291
                        'type'    => new NonNullType(new IntType()),
292
                        'resolve' => function () {
293
                            return 'hello';
294
                        }
295
                    ],
296
                    'objectQuery'               => [
297
                        'type'    => new TestObjectType(),
298
                        'resolve' => function () {
299
                            return ['name' => 'John'];
300
                        }
301
                    ],
302
                    'deepObjectQuery'           => [
303
                        'type'    => new ObjectType([
304
                            'name'   => 'deepObject',
305
                            'fields' => [
306
                                'object' => new TestObjectType(),
307
                                'enum'   => new TestEnumType(),
308
                            ],
309
                        ]),
310
                        'resolve' => function () {
311
                            return [
312
                                'object' => [
313
                                    'name' => 'John'
314
                                ],
315
                                'enum'   => 1
316
                            ];
317
                        },
318
                    ],
319
                ]
320
            ])
321
        ]));
322
323
        $processor->processPayload('{ listQuery }');
324
        $this->assertEquals(['errors' => [
325
            ['message' => 'Not valid value for LIST field listQuery']
326
        ], 'data'                     => ['listQuery' => null]], $processor->getResponseData());
327
328
        $processor->processPayload('{ listEnumQuery }');
329
        $this->assertEquals(['errors' => [
330
            ['message' => 'Not valid resolve value in listEnumQuery field']
331
        ], 'data'                     => ['listEnumQuery' => [null]]], $processor->getResponseData());
332
333
        $processor->processPayload('{ invalidEnumQuery }');
334
        $this->assertEquals(['errors' => [
335
            ['message' => 'Not valid value for ENUM field invalidEnumQuery']
336
        ], 'data'                     => ['invalidEnumQuery' => null]], $processor->getResponseData());
337
338
        $processor->processPayload('{ enumQuery }');
339
        $this->assertEquals(['data' => ['enumQuery' => 'FINISHED']], $processor->getResponseData());
340
341
        $processor->processPayload('{ invalidNonNullQuery }');
342
        $this->assertEquals(['errors' => [
343
            ['message' => 'Cannot return null for non-nullable field invalidNonNullQuery']
344
        ], 'data'                     => ['invalidNonNullQuery' => null]], $processor->getResponseData());
345
346
        $processor->processPayload('{ invalidNonNullInsideQuery }');
347
        $this->assertEquals(['errors' => [
348
            ['message' => 'Not valid value for SCALAR field invalidNonNullInsideQuery']
349
        ], 'data'                     => ['invalidNonNullInsideQuery' => null]], $processor->getResponseData());
350
351
        $processor->processPayload('{ test:deepObjectQuery { object { name } } }');
352
        $this->assertEquals(['data' => ['test' => ['object' => ['name' => 'John']]]], $processor->getResponseData());
353
    }
354
355
    public function testTypedFragment()
356
    {
357
358
        $object1 = new ObjectType([
359
            'name'   => 'Object1',
360
            'fields' => [
361
                'id' => ['type' => 'int']
362
            ]
363
        ]);
364
365
        $object2 = new ObjectType([
366
            'name'   => 'Object2',
367
            'fields' => [
368
                'name' => ['type' => 'string']
369
            ]
370
        ]);
371
372
        $object3 = new ObjectType([
373
            'name'   => 'Object3',
374
            'fields' => [
375
                'name' => ['type' => 'string']
376
            ]
377
        ]);
378
379
        $union        = new UnionType([
380
            'name'        => 'TestUnion',
381
            'types'       => [$object1, $object2],
382
            'resolveType' => function ($object) use ($object1, $object2) {
383
                if (isset($object['id'])) {
384
                    return $object1;
385
                }
386
387
                return $object2;
388
            }
389
        ]);
390
        $invalidUnion = new UnionType([
391
            'name'        => 'TestUnion',
392
            'types'       => [$object1, $object2],
393
            'resolveType' => function ($object) use ($object3) {
0 ignored issues
show
Unused Code introduced by
The parameter $object is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
394
                return $object3;
395
            }
396
        ]);
397
        $processor    = new Processor(new Schema([
398
            'query' => new ObjectType([
399
                'name'   => 'RootQuery',
400
                'fields' => [
401
                    'union'        => [
402
                        'type'    => $union,
403
                        'args'    => [
404
                            'type' => ['type' => 'string']
405
                        ],
406
                        'resolve' => function ($value, $args) {
407
                            if ($args['type'] == 'object1') {
408
                                return [
409
                                    'id' => 43
410
                                ];
411
                            } else {
412
                                return [
413
                                    'name' => 'name resolved'
414
                                ];
415
                            }
416
                        }
417
                    ],
418
                    'invalidUnion' => [
419
                        'type'    => $invalidUnion,
420
                        'resolve' => function () {
421
                            return ['name' => 'name resolved'];
422
                        }
423
                    ],
424
                ]
425
            ])
426
        ]));
427
        $processor->processPayload('{ union(type: "object1") { ... on Object2 { id } } }');
428
        $this->assertEquals(['data' => ['union' => []]], $processor->getResponseData());
429
430
        $processor->processPayload('{ union(type: "object1") { ... on Object1 { name } } }');
431
        $this->assertEquals([
432
            'data'   => [
433
                'union' => []
434
            ],
435
            'errors' => [
436
                ['message' => 'Field "name" not found in type "Object1"']
437
            ]
438
        ], $processor->getResponseData());
439
440
        $processor->processPayload('{ union(type: "object1") { ... on Object1 { id } } }');
441
        $this->assertEquals(['data' => ['union' => ['id' => 43]]], $processor->getResponseData());
442
443
        $processor->processPayload('{ union(type: "asd") { ... on Object2 { name } } }');
444
        $this->assertEquals(['data' => ['union' => ['name' => 'name resolved']]], $processor->getResponseData());
445
446
        $processor->processPayload('{ invalidUnion { ... on Object2 { name } } }');
447
        $this->assertEquals(['errors' => [['message' => 'Type Object3 not exist in types of Object2']]], $processor->getResponseData());
448
449
    }
450
451
452
}
453