1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
declare(strict_types=1); |
4
|
|
|
|
5
|
|
|
namespace GraphQL\Tests\Executor; |
6
|
|
|
|
7
|
|
|
use GraphQL\Deferred; |
8
|
|
|
use GraphQL\Executor\Executor; |
9
|
|
|
use GraphQL\Language\Parser; |
10
|
|
|
use GraphQL\Type\Definition\ObjectType; |
11
|
|
|
use GraphQL\Type\Definition\ResolveInfo; |
12
|
|
|
use GraphQL\Type\Definition\Type; |
13
|
|
|
use GraphQL\Type\Schema; |
14
|
|
|
use GraphQL\Utils\Utils; |
15
|
|
|
use PHPUnit\Framework\TestCase; |
16
|
|
|
use function count; |
17
|
|
|
use function in_array; |
18
|
|
|
use function json_encode; |
19
|
|
|
|
20
|
|
|
class DeferredFieldsTest extends TestCase |
21
|
|
|
{ |
22
|
|
|
/** @var ObjectType */ |
23
|
|
|
private $userType; |
24
|
|
|
|
25
|
|
|
/** @var ObjectType */ |
26
|
|
|
private $storyType; |
27
|
|
|
|
28
|
|
|
/** @var ObjectType */ |
29
|
|
|
private $categoryType; |
30
|
|
|
|
31
|
|
|
/** @var mixed */ |
32
|
|
|
private $paths; |
33
|
|
|
|
34
|
|
|
/** @var mixed[][] */ |
35
|
|
|
private $storyDataSource; |
36
|
|
|
|
37
|
|
|
/** @var mixed[][] */ |
38
|
|
|
private $userDataSource; |
39
|
|
|
|
40
|
|
|
/** @var mixed[][] */ |
41
|
|
|
private $categoryDataSource; |
42
|
|
|
|
43
|
|
|
/** @var ObjectType */ |
44
|
|
|
private $queryType; |
45
|
|
|
|
46
|
|
|
public function setUp() |
47
|
|
|
{ |
48
|
|
|
$this->storyDataSource = [ |
49
|
|
|
['id' => 1, 'authorId' => 1, 'title' => 'Story #1', 'categoryIds' => [2, 3]], |
50
|
|
|
['id' => 2, 'authorId' => 2, 'title' => 'Story #2', 'categoryIds' => [1, 2]], |
51
|
|
|
['id' => 3, 'authorId' => 3, 'title' => 'Story #3', 'categoryIds' => [2]], |
52
|
|
|
['id' => 4, 'authorId' => 3, 'title' => 'Story #4', 'categoryIds' => [1]], |
53
|
|
|
['id' => 5, 'authorId' => 1, 'title' => 'Story #5', 'categoryIds' => [3]], |
54
|
|
|
['id' => 6, 'authorId' => 2, 'title' => 'Story #6', 'categoryIds' => [1]], |
55
|
|
|
['id' => 7, 'authorId' => 3, 'title' => 'Story #7', 'categoryIds' => [2]], |
56
|
|
|
['id' => 8, 'authorId' => 1, 'title' => 'Story #8', 'categoryIds' => [1, 2, 3]], |
57
|
|
|
['id' => 9, 'authorId' => 2, 'title' => 'Story #9', 'categoryIds' => [2, 3]], |
58
|
|
|
]; |
59
|
|
|
|
60
|
|
|
$this->userDataSource = [ |
61
|
|
|
['id' => 1, 'name' => 'John', 'bestFriendId' => 4], |
62
|
|
|
['id' => 2, 'name' => 'Jane', 'bestFriendId' => 3], |
63
|
|
|
['id' => 3, 'name' => 'Joe', 'bestFriendId' => 2], |
64
|
|
|
['id' => 4, 'name' => 'Dirk', 'bestFriend' => 1], |
65
|
|
|
]; |
66
|
|
|
|
67
|
|
|
$this->categoryDataSource = [ |
68
|
|
|
['id' => 1, 'name' => 'Category #1', 'topStoryId' => 8], |
69
|
|
|
['id' => 2, 'name' => 'Category #2', 'topStoryId' => 3], |
70
|
|
|
['id' => 3, 'name' => 'Category #3', 'topStoryId' => 9], |
71
|
|
|
]; |
72
|
|
|
|
73
|
|
|
$this->paths = []; |
74
|
|
|
$this->userType = new ObjectType([ |
75
|
|
|
'name' => 'User', |
76
|
|
|
'fields' => function () { |
77
|
|
|
return [ |
78
|
|
|
'name' => [ |
79
|
|
|
'type' => Type::string(), |
80
|
|
|
'resolve' => function ($user, $args, $context, ResolveInfo $info) { |
81
|
|
|
$this->paths[] = $info->path; |
82
|
|
|
|
83
|
|
|
return $user['name']; |
84
|
|
|
}, |
85
|
|
|
], |
86
|
|
|
'bestFriend' => [ |
87
|
|
|
'type' => $this->userType, |
88
|
|
|
'resolve' => function ($user, $args, $context, ResolveInfo $info) { |
89
|
|
|
$this->paths[] = $info->path; |
90
|
|
|
|
91
|
|
|
return new Deferred(function () use ($user) { |
92
|
|
|
$this->paths[] = 'deferred-for-best-friend-of-' . $user['id']; |
93
|
|
|
|
94
|
|
|
return Utils::find( |
95
|
|
|
$this->userDataSource, |
96
|
|
|
static function ($entry) use ($user) { |
97
|
|
|
return $entry['id'] === $user['bestFriendId']; |
98
|
|
|
} |
99
|
|
|
); |
100
|
|
|
}); |
101
|
|
|
}, |
102
|
|
|
], |
103
|
|
|
]; |
104
|
|
|
}, |
105
|
|
|
]); |
106
|
|
|
|
107
|
|
|
$this->storyType = new ObjectType([ |
108
|
|
|
'name' => 'Story', |
109
|
|
|
'fields' => [ |
110
|
|
|
'title' => [ |
111
|
|
|
'type' => Type::string(), |
112
|
|
|
'resolve' => function ($story, $args, $context, ResolveInfo $info) { |
113
|
|
|
$this->paths[] = $info->path; |
114
|
|
|
|
115
|
|
|
return $story['title']; |
116
|
|
|
}, |
117
|
|
|
], |
118
|
|
|
'author' => [ |
119
|
|
|
'type' => $this->userType, |
120
|
|
|
'resolve' => function ($story, $args, $context, ResolveInfo $info) { |
121
|
|
|
$this->paths[] = $info->path; |
122
|
|
|
|
123
|
|
|
return new Deferred(function () use ($story) { |
124
|
|
|
$this->paths[] = 'deferred-for-story-' . $story['id'] . '-author'; |
125
|
|
|
|
126
|
|
|
return $this->findUserById($story['authorId']); |
127
|
|
|
}); |
128
|
|
|
}, |
129
|
|
|
], |
130
|
|
|
], |
131
|
|
|
]); |
132
|
|
|
|
133
|
|
|
$this->categoryType = new ObjectType([ |
134
|
|
|
'name' => 'Category', |
135
|
|
|
'fields' => [ |
136
|
|
|
'name' => [ |
137
|
|
|
'type' => Type::string(), |
138
|
|
|
'resolve' => function ($category, $args, $context, ResolveInfo $info) { |
139
|
|
|
$this->paths[] = $info->path; |
140
|
|
|
|
141
|
|
|
return $category['name']; |
142
|
|
|
}, |
143
|
|
|
], |
144
|
|
|
|
145
|
|
|
'stories' => [ |
146
|
|
|
'type' => Type::listOf($this->storyType), |
147
|
|
|
'resolve' => function ($category, $args, $context, ResolveInfo $info) { |
148
|
|
|
$this->paths[] = $info->path; |
149
|
|
|
|
150
|
|
|
return Utils::filter( |
151
|
|
|
$this->storyDataSource, |
152
|
|
|
static function ($story) use ($category) { |
153
|
|
|
return in_array($category['id'], $story['categoryIds'], true); |
154
|
|
|
} |
155
|
|
|
); |
156
|
|
|
}, |
157
|
|
|
], |
158
|
|
|
'topStory' => [ |
159
|
|
|
'type' => $this->storyType, |
160
|
|
|
'resolve' => function ($category, $args, $context, ResolveInfo $info) { |
161
|
|
|
$this->paths[] = $info->path; |
162
|
|
|
|
163
|
|
|
return new Deferred(function () use ($category) { |
164
|
|
|
$this->paths[] = 'deferred-for-category-' . $category['id'] . '-topStory'; |
165
|
|
|
|
166
|
|
|
return $this->findStoryById($category['topStoryId']); |
167
|
|
|
}); |
168
|
|
|
}, |
169
|
|
|
], |
170
|
|
|
'topStoryAuthor' => [ |
171
|
|
|
'type' => $this->userType, |
172
|
|
|
'resolve' => function ($category, $args, $context, ResolveInfo $info) { |
173
|
|
|
$this->paths[] = $info->path; |
174
|
|
|
|
175
|
|
|
return new Deferred(function () use ($category) { |
176
|
|
|
$this->paths[] = 'deferred-for-category-' . $category['id'] . '-topStoryAuthor1'; |
177
|
|
|
$story = $this->findStoryById($category['topStoryId']); |
178
|
|
|
|
179
|
|
|
return new Deferred(function () use ($category, $story) { |
180
|
|
|
$this->paths[] = 'deferred-for-category-' . $category['id'] . '-topStoryAuthor2'; |
181
|
|
|
|
182
|
|
|
return $this->findUserById($story['authorId']); |
183
|
|
|
}); |
184
|
|
|
}); |
185
|
|
|
}, |
186
|
|
|
], |
187
|
|
|
], |
188
|
|
|
]); |
189
|
|
|
|
190
|
|
|
$this->queryType = new ObjectType([ |
191
|
|
|
'name' => 'Query', |
192
|
|
|
'fields' => [ |
193
|
|
|
'topStories' => [ |
194
|
|
|
'type' => Type::listOf($this->storyType), |
195
|
|
|
'resolve' => function ($rootValue, $args, $context, ResolveInfo $info) { |
196
|
|
|
$this->paths[] = $info->path; |
197
|
|
|
|
198
|
|
|
return Utils::filter( |
199
|
|
|
$this->storyDataSource, |
200
|
|
|
static function ($story) { |
201
|
|
|
return $story['id'] % 2 === 1; |
202
|
|
|
} |
203
|
|
|
); |
204
|
|
|
}, |
205
|
|
|
], |
206
|
|
|
'featuredCategory' => [ |
207
|
|
|
'type' => $this->categoryType, |
208
|
|
|
'resolve' => function ($rootValue, $args, $context, ResolveInfo $info) { |
209
|
|
|
$this->paths[] = $info->path; |
210
|
|
|
|
211
|
|
|
return $this->categoryDataSource[0]; |
212
|
|
|
}, |
213
|
|
|
], |
214
|
|
|
'categories' => [ |
215
|
|
|
'type' => Type::listOf($this->categoryType), |
216
|
|
|
'resolve' => function ($rootValue, $args, $context, ResolveInfo $info) { |
217
|
|
|
$this->paths[] = $info->path; |
218
|
|
|
|
219
|
|
|
return $this->categoryDataSource; |
220
|
|
|
}, |
221
|
|
|
], |
222
|
|
|
], |
223
|
|
|
]); |
224
|
|
|
|
225
|
|
|
parent::setUp(); |
226
|
|
|
} |
227
|
|
|
|
228
|
|
|
public function testDeferredFields() : void |
229
|
|
|
{ |
230
|
|
|
$query = Parser::parse(' |
231
|
|
|
{ |
232
|
|
|
topStories { |
233
|
|
|
title |
234
|
|
|
author { |
235
|
|
|
name |
236
|
|
|
} |
237
|
|
|
} |
238
|
|
|
featuredCategory { |
239
|
|
|
stories { |
240
|
|
|
title |
241
|
|
|
author { |
242
|
|
|
name |
243
|
|
|
} |
244
|
|
|
} |
245
|
|
|
} |
246
|
|
|
} |
247
|
|
|
'); |
248
|
|
|
|
249
|
|
|
$schema = new Schema([ |
250
|
|
|
'query' => $this->queryType, |
251
|
|
|
]); |
252
|
|
|
|
253
|
|
|
$expected = [ |
254
|
|
|
'data' => [ |
255
|
|
|
'topStories' => [ |
256
|
|
|
['title' => 'Story #1', 'author' => ['name' => 'John']], |
257
|
|
|
['title' => 'Story #3', 'author' => ['name' => 'Joe']], |
258
|
|
|
['title' => 'Story #5', 'author' => ['name' => 'John']], |
259
|
|
|
['title' => 'Story #7', 'author' => ['name' => 'Joe']], |
260
|
|
|
['title' => 'Story #9', 'author' => ['name' => 'Jane']], |
261
|
|
|
], |
262
|
|
|
'featuredCategory' => [ |
263
|
|
|
'stories' => [ |
264
|
|
|
['title' => 'Story #2', 'author' => ['name' => 'Jane']], |
265
|
|
|
['title' => 'Story #4', 'author' => ['name' => 'Joe']], |
266
|
|
|
['title' => 'Story #6', 'author' => ['name' => 'Jane']], |
267
|
|
|
['title' => 'Story #8', 'author' => ['name' => 'John']], |
268
|
|
|
], |
269
|
|
|
], |
270
|
|
|
], |
271
|
|
|
]; |
272
|
|
|
|
273
|
|
|
$result = Executor::execute($schema, $query); |
274
|
|
|
self::assertEquals($expected, $result->toArray()); |
|
|
|
|
275
|
|
|
|
276
|
|
|
$expectedPaths = [ |
277
|
|
|
['topStories'], |
278
|
|
|
['topStories', 0, 'title'], |
279
|
|
|
['topStories', 0, 'author'], |
280
|
|
|
['topStories', 1, 'title'], |
281
|
|
|
['topStories', 1, 'author'], |
282
|
|
|
['topStories', 2, 'title'], |
283
|
|
|
['topStories', 2, 'author'], |
284
|
|
|
['topStories', 3, 'title'], |
285
|
|
|
['topStories', 3, 'author'], |
286
|
|
|
['topStories', 4, 'title'], |
287
|
|
|
['topStories', 4, 'author'], |
288
|
|
|
['featuredCategory'], |
289
|
|
|
['featuredCategory', 'stories'], |
290
|
|
|
['featuredCategory', 'stories', 0, 'title'], |
291
|
|
|
['featuredCategory', 'stories', 0, 'author'], |
292
|
|
|
['featuredCategory', 'stories', 1, 'title'], |
293
|
|
|
['featuredCategory', 'stories', 1, 'author'], |
294
|
|
|
['featuredCategory', 'stories', 2, 'title'], |
295
|
|
|
['featuredCategory', 'stories', 2, 'author'], |
296
|
|
|
['featuredCategory', 'stories', 3, 'title'], |
297
|
|
|
['featuredCategory', 'stories', 3, 'author'], |
298
|
|
|
'deferred-for-story-1-author', |
299
|
|
|
'deferred-for-story-3-author', |
300
|
|
|
'deferred-for-story-5-author', |
301
|
|
|
'deferred-for-story-7-author', |
302
|
|
|
'deferred-for-story-9-author', |
303
|
|
|
'deferred-for-story-2-author', |
304
|
|
|
'deferred-for-story-4-author', |
305
|
|
|
'deferred-for-story-6-author', |
306
|
|
|
'deferred-for-story-8-author', |
307
|
|
|
['topStories', 0, 'author', 'name'], |
308
|
|
|
['topStories', 1, 'author', 'name'], |
309
|
|
|
['topStories', 2, 'author', 'name'], |
310
|
|
|
['topStories', 3, 'author', 'name'], |
311
|
|
|
['topStories', 4, 'author', 'name'], |
312
|
|
|
['featuredCategory', 'stories', 0, 'author', 'name'], |
313
|
|
|
['featuredCategory', 'stories', 1, 'author', 'name'], |
314
|
|
|
['featuredCategory', 'stories', 2, 'author', 'name'], |
315
|
|
|
['featuredCategory', 'stories', 3, 'author', 'name'], |
316
|
|
|
]; |
317
|
|
|
self::assertCount(count($expectedPaths), $this->paths); |
318
|
|
|
foreach ($expectedPaths as $expectedPath) { |
319
|
|
|
self::assertTrue(in_array($expectedPath, $this->paths, true), 'Missing path: ' . json_encode($expectedPath)); |
320
|
|
|
} |
321
|
|
|
} |
322
|
|
|
|
323
|
|
|
public function testNestedDeferredFields() : void |
324
|
|
|
{ |
325
|
|
|
$query = Parser::parse(' |
326
|
|
|
{ |
327
|
|
|
categories { |
328
|
|
|
name |
329
|
|
|
topStory { |
330
|
|
|
title |
331
|
|
|
author { |
332
|
|
|
name |
333
|
|
|
bestFriend { |
334
|
|
|
name |
335
|
|
|
} |
336
|
|
|
} |
337
|
|
|
} |
338
|
|
|
} |
339
|
|
|
} |
340
|
|
|
'); |
341
|
|
|
|
342
|
|
|
$schema = new Schema([ |
343
|
|
|
'query' => $this->queryType, |
344
|
|
|
]); |
345
|
|
|
|
346
|
|
|
$author1 = ['name' => 'John', 'bestFriend' => ['name' => 'Dirk']]; |
347
|
|
|
$author2 = ['name' => 'Jane', 'bestFriend' => ['name' => 'Joe']]; |
348
|
|
|
$author3 = ['name' => 'Joe', 'bestFriend' => ['name' => 'Jane']]; |
349
|
|
|
$author4 = ['name' => 'Dirk', 'bestFriend' => ['name' => 'John']]; |
|
|
|
|
350
|
|
|
|
351
|
|
|
$expected = [ |
352
|
|
|
'data' => [ |
353
|
|
|
'categories' => [ |
354
|
|
|
['name' => 'Category #1', 'topStory' => ['title' => 'Story #8', 'author' => $author1]], |
355
|
|
|
['name' => 'Category #2', 'topStory' => ['title' => 'Story #3', 'author' => $author3]], |
356
|
|
|
['name' => 'Category #3', 'topStory' => ['title' => 'Story #9', 'author' => $author2]], |
357
|
|
|
], |
358
|
|
|
], |
359
|
|
|
]; |
360
|
|
|
|
361
|
|
|
$result = Executor::execute($schema, $query); |
362
|
|
|
self::assertEquals($expected, $result->toArray()); |
363
|
|
|
|
364
|
|
|
$expectedPaths = [ |
365
|
|
|
['categories'], |
366
|
|
|
['categories', 0, 'name'], |
367
|
|
|
['categories', 0, 'topStory'], |
368
|
|
|
['categories', 1, 'name'], |
369
|
|
|
['categories', 1, 'topStory'], |
370
|
|
|
['categories', 2, 'name'], |
371
|
|
|
['categories', 2, 'topStory'], |
372
|
|
|
'deferred-for-category-1-topStory', |
373
|
|
|
'deferred-for-category-2-topStory', |
374
|
|
|
'deferred-for-category-3-topStory', |
375
|
|
|
['categories', 0, 'topStory', 'title'], |
376
|
|
|
['categories', 0, 'topStory', 'author'], |
377
|
|
|
['categories', 1, 'topStory', 'title'], |
378
|
|
|
['categories', 1, 'topStory', 'author'], |
379
|
|
|
['categories', 2, 'topStory', 'title'], |
380
|
|
|
['categories', 2, 'topStory', 'author'], |
381
|
|
|
'deferred-for-story-8-author', |
382
|
|
|
'deferred-for-story-3-author', |
383
|
|
|
'deferred-for-story-9-author', |
384
|
|
|
['categories', 0, 'topStory', 'author', 'name'], |
385
|
|
|
['categories', 0, 'topStory', 'author', 'bestFriend'], |
386
|
|
|
['categories', 1, 'topStory', 'author', 'name'], |
387
|
|
|
['categories', 1, 'topStory', 'author', 'bestFriend'], |
388
|
|
|
['categories', 2, 'topStory', 'author', 'name'], |
389
|
|
|
['categories', 2, 'topStory', 'author', 'bestFriend'], |
390
|
|
|
'deferred-for-best-friend-of-1', |
391
|
|
|
'deferred-for-best-friend-of-3', |
392
|
|
|
'deferred-for-best-friend-of-2', |
393
|
|
|
['categories', 0, 'topStory', 'author', 'bestFriend', 'name'], |
394
|
|
|
['categories', 1, 'topStory', 'author', 'bestFriend', 'name'], |
395
|
|
|
['categories', 2, 'topStory', 'author', 'bestFriend', 'name'], |
396
|
|
|
]; |
397
|
|
|
self::assertCount(count($expectedPaths), $this->paths); |
398
|
|
|
foreach ($expectedPaths as $expectedPath) { |
399
|
|
|
self::assertTrue(in_array($expectedPath, $this->paths, true), 'Missing path: ' . json_encode($expectedPath)); |
400
|
|
|
} |
401
|
|
|
} |
402
|
|
|
|
403
|
|
|
public function testComplexRecursiveDeferredFields() : void |
404
|
|
|
{ |
405
|
|
|
$complexType = new ObjectType([ |
406
|
|
|
'name' => 'ComplexType', |
407
|
|
|
'fields' => function () use (&$complexType) { |
408
|
|
|
return [ |
409
|
|
|
'sync' => [ |
410
|
|
|
'type' => Type::string(), |
411
|
|
|
'resolve' => function ($complexType, $args, $context, ResolveInfo $info) { |
412
|
|
|
$this->paths[] = $info->path; |
413
|
|
|
|
414
|
|
|
return 'sync'; |
415
|
|
|
}, |
416
|
|
|
], |
417
|
|
|
'deferred' => [ |
418
|
|
|
'type' => Type::string(), |
419
|
|
|
'resolve' => function ($complexType, $args, $context, ResolveInfo $info) { |
420
|
|
|
$this->paths[] = $info->path; |
421
|
|
|
|
422
|
|
|
return new Deferred(function () use ($info) { |
423
|
|
|
$this->paths[] = ['!dfd for: ', $info->path]; |
424
|
|
|
|
425
|
|
|
return 'deferred'; |
426
|
|
|
}); |
427
|
|
|
}, |
428
|
|
|
], |
429
|
|
|
'nest' => [ |
430
|
|
|
'type' => $complexType, |
431
|
|
|
'resolve' => function ($complexType, $args, $context, ResolveInfo $info) { |
432
|
|
|
$this->paths[] = $info->path; |
433
|
|
|
|
434
|
|
|
return []; |
435
|
|
|
}, |
436
|
|
|
], |
437
|
|
|
'deferredNest' => [ |
438
|
|
|
'type' => $complexType, |
439
|
|
|
'resolve' => function ($complexType, $args, $context, ResolveInfo $info) { |
440
|
|
|
$this->paths[] = $info->path; |
441
|
|
|
|
442
|
|
|
return new Deferred(function () use ($info) { |
443
|
|
|
$this->paths[] = ['!dfd nest for: ', $info->path]; |
444
|
|
|
|
445
|
|
|
return []; |
446
|
|
|
}); |
447
|
|
|
}, |
448
|
|
|
], |
449
|
|
|
]; |
450
|
|
|
}, |
451
|
|
|
]); |
452
|
|
|
|
453
|
|
|
$schema = new Schema(['query' => $complexType]); |
454
|
|
|
|
455
|
|
|
$query = Parser::parse(' |
456
|
|
|
{ |
457
|
|
|
nest { |
458
|
|
|
sync |
459
|
|
|
deferred |
460
|
|
|
nest { |
461
|
|
|
sync |
462
|
|
|
deferred |
463
|
|
|
} |
464
|
|
|
deferredNest { |
465
|
|
|
sync |
466
|
|
|
deferred |
467
|
|
|
} |
468
|
|
|
} |
469
|
|
|
deferredNest { |
470
|
|
|
sync |
471
|
|
|
deferred |
472
|
|
|
nest { |
473
|
|
|
sync |
474
|
|
|
deferred |
475
|
|
|
} |
476
|
|
|
deferredNest { |
477
|
|
|
sync |
478
|
|
|
deferred |
479
|
|
|
} |
480
|
|
|
} |
481
|
|
|
} |
482
|
|
|
'); |
483
|
|
|
$result = Executor::execute($schema, $query); |
484
|
|
|
$expected = [ |
485
|
|
|
'data' => [ |
486
|
|
|
'nest' => [ |
487
|
|
|
'sync' => 'sync', |
488
|
|
|
'deferred' => 'deferred', |
489
|
|
|
'nest' => [ |
490
|
|
|
'sync' => 'sync', |
491
|
|
|
'deferred' => 'deferred', |
492
|
|
|
], |
493
|
|
|
'deferredNest' => [ |
494
|
|
|
'sync' => 'sync', |
495
|
|
|
'deferred' => 'deferred', |
496
|
|
|
], |
497
|
|
|
], |
498
|
|
|
'deferredNest' => [ |
499
|
|
|
'sync' => 'sync', |
500
|
|
|
'deferred' => 'deferred', |
501
|
|
|
'nest' => [ |
502
|
|
|
'sync' => 'sync', |
503
|
|
|
'deferred' => 'deferred', |
504
|
|
|
], |
505
|
|
|
'deferredNest' => [ |
506
|
|
|
'sync' => 'sync', |
507
|
|
|
'deferred' => 'deferred', |
508
|
|
|
], |
509
|
|
|
], |
510
|
|
|
], |
511
|
|
|
]; |
512
|
|
|
|
513
|
|
|
self::assertEquals($expected, $result->toArray()); |
514
|
|
|
|
515
|
|
|
$expectedPaths = [ |
516
|
|
|
['nest'], |
517
|
|
|
['nest', 'sync'], |
518
|
|
|
['nest', 'deferred'], |
519
|
|
|
['nest', 'nest'], |
520
|
|
|
['nest', 'nest', 'sync'], |
521
|
|
|
['nest', 'nest', 'deferred'], |
522
|
|
|
['nest', 'deferredNest'], |
523
|
|
|
['deferredNest'], |
524
|
|
|
|
525
|
|
|
['!dfd for: ', ['nest', 'deferred']], |
526
|
|
|
['!dfd for: ', ['nest', 'nest', 'deferred']], |
527
|
|
|
['!dfd nest for: ', ['nest', 'deferredNest']], |
528
|
|
|
['!dfd nest for: ', ['deferredNest']], |
529
|
|
|
|
530
|
|
|
['nest', 'deferredNest', 'sync'], |
531
|
|
|
['nest', 'deferredNest', 'deferred'], |
532
|
|
|
['deferredNest', 'sync'], |
533
|
|
|
['deferredNest', 'deferred'], |
534
|
|
|
['deferredNest', 'nest'], |
535
|
|
|
['deferredNest', 'nest', 'sync'], |
536
|
|
|
['deferredNest', 'nest', 'deferred'], |
537
|
|
|
['deferredNest', 'deferredNest'], |
538
|
|
|
|
539
|
|
|
['!dfd for: ', ['nest', 'deferredNest', 'deferred']], |
540
|
|
|
['!dfd for: ', ['deferredNest', 'deferred']], |
541
|
|
|
['!dfd for: ', ['deferredNest', 'nest', 'deferred']], |
542
|
|
|
['!dfd nest for: ', ['deferredNest', 'deferredNest']], |
543
|
|
|
|
544
|
|
|
['deferredNest', 'deferredNest', 'sync'], |
545
|
|
|
['deferredNest', 'deferredNest', 'deferred'], |
546
|
|
|
['!dfd for: ', ['deferredNest', 'deferredNest', 'deferred']], |
547
|
|
|
]; |
548
|
|
|
|
549
|
|
|
// Note: not using self::assertEquals() because coroutineExecutor has a different sequence of calls |
550
|
|
|
self::assertCount(count($expectedPaths), $this->paths); |
551
|
|
|
foreach ($expectedPaths as $expectedPath) { |
552
|
|
|
self::assertTrue(in_array($expectedPath, $this->paths, true), 'Missing path: ' . json_encode($expectedPath)); |
553
|
|
|
} |
554
|
|
|
} |
555
|
|
|
|
556
|
|
|
public function testDeferredChaining() |
557
|
|
|
{ |
558
|
|
|
$schema = new Schema([ |
559
|
|
|
'query' => $this->queryType, |
560
|
|
|
]); |
561
|
|
|
|
562
|
|
|
$query = Parser::parse(' |
563
|
|
|
{ |
564
|
|
|
categories { |
565
|
|
|
name |
566
|
|
|
topStory { |
567
|
|
|
title |
568
|
|
|
author { |
569
|
|
|
name |
570
|
|
|
} |
571
|
|
|
} |
572
|
|
|
topStoryAuthor { |
573
|
|
|
name |
574
|
|
|
} |
575
|
|
|
} |
576
|
|
|
} |
577
|
|
|
'); |
578
|
|
|
|
579
|
|
|
$author1 = ['name' => 'John'/*, 'bestFriend' => ['name' => 'Dirk']*/]; |
580
|
|
|
$author2 = ['name' => 'Jane'/*, 'bestFriend' => ['name' => 'Joe']*/]; |
581
|
|
|
$author3 = ['name' => 'Joe'/*, 'bestFriend' => ['name' => 'Jane']*/]; |
582
|
|
|
$author4 = ['name' => 'Dirk'/*, 'bestFriend' => ['name' => 'John']*/]; |
|
|
|
|
583
|
|
|
|
584
|
|
|
$story1 = ['title' => 'Story #8', 'author' => $author1]; |
585
|
|
|
$story2 = ['title' => 'Story #3', 'author' => $author3]; |
586
|
|
|
$story3 = ['title' => 'Story #9', 'author' => $author2]; |
587
|
|
|
|
588
|
|
|
$result = Executor::execute($schema, $query); |
589
|
|
|
$expected = [ |
590
|
|
|
'data' => [ |
591
|
|
|
'categories' => [ |
592
|
|
|
['name' => 'Category #1', 'topStory' => $story1, 'topStoryAuthor' => $author1], |
593
|
|
|
['name' => 'Category #2', 'topStory' => $story2, 'topStoryAuthor' => $author3], |
594
|
|
|
['name' => 'Category #3', 'topStory' => $story3, 'topStoryAuthor' => $author2], |
595
|
|
|
], |
596
|
|
|
], |
597
|
|
|
]; |
598
|
|
|
self::assertEquals($expected, $result->toArray()); |
599
|
|
|
|
600
|
|
|
$expectedPaths = [ |
601
|
|
|
['categories'], |
602
|
|
|
['categories', 0, 'name'], |
603
|
|
|
['categories', 0, 'topStory'], |
604
|
|
|
['categories', 0, 'topStoryAuthor'], |
605
|
|
|
['categories', 1, 'name'], |
606
|
|
|
['categories', 1, 'topStory'], |
607
|
|
|
['categories', 1, 'topStoryAuthor'], |
608
|
|
|
['categories', 2, 'name'], |
609
|
|
|
['categories', 2, 'topStory'], |
610
|
|
|
['categories', 2, 'topStoryAuthor'], |
611
|
|
|
'deferred-for-category-1-topStory', |
612
|
|
|
'deferred-for-category-1-topStoryAuthor1', |
613
|
|
|
'deferred-for-category-2-topStory', |
614
|
|
|
'deferred-for-category-2-topStoryAuthor1', |
615
|
|
|
'deferred-for-category-3-topStory', |
616
|
|
|
'deferred-for-category-3-topStoryAuthor1', |
617
|
|
|
'deferred-for-category-1-topStoryAuthor2', |
618
|
|
|
'deferred-for-category-2-topStoryAuthor2', |
619
|
|
|
'deferred-for-category-3-topStoryAuthor2', |
620
|
|
|
['categories', 0, 'topStory', 'title'], |
621
|
|
|
['categories', 0, 'topStory', 'author'], |
622
|
|
|
['categories', 1, 'topStory', 'title'], |
623
|
|
|
['categories', 1, 'topStory', 'author'], |
624
|
|
|
['categories', 2, 'topStory', 'title'], |
625
|
|
|
['categories', 2, 'topStory', 'author'], |
626
|
|
|
['categories', 0, 'topStoryAuthor', 'name'], |
627
|
|
|
['categories', 1, 'topStoryAuthor', 'name'], |
628
|
|
|
['categories', 2, 'topStoryAuthor', 'name'], |
629
|
|
|
'deferred-for-story-8-author', |
630
|
|
|
'deferred-for-story-3-author', |
631
|
|
|
'deferred-for-story-9-author', |
632
|
|
|
['categories', 0, 'topStory', 'author', 'name'], |
633
|
|
|
['categories', 1, 'topStory', 'author', 'name'], |
634
|
|
|
['categories', 2, 'topStory', 'author', 'name'], |
635
|
|
|
]; |
636
|
|
|
self::assertEquals($expectedPaths, $this->paths); |
637
|
|
|
} |
638
|
|
|
|
639
|
|
|
private function findStoryById($id) |
640
|
|
|
{ |
641
|
|
|
return Utils::find( |
642
|
|
|
$this->storyDataSource, |
643
|
|
|
static function ($story) use ($id) { |
644
|
|
|
return $story['id'] === $id; |
645
|
|
|
} |
646
|
|
|
); |
647
|
|
|
} |
648
|
|
|
|
649
|
|
|
private function findUserById($id) |
650
|
|
|
{ |
651
|
|
|
return Utils::find( |
652
|
|
|
$this->userDataSource, |
653
|
|
|
static function ($user) use ($id) { |
654
|
|
|
return $user['id'] === $id; |
655
|
|
|
} |
656
|
|
|
); |
657
|
|
|
} |
658
|
|
|
} |
659
|
|
|
|
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.