1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
declare(strict_types=1); |
4
|
|
|
|
5
|
|
|
namespace GraphQL\Tests\Utils; |
6
|
|
|
|
7
|
|
|
use GraphQL\Type\Definition\EnumType; |
8
|
|
|
use GraphQL\Type\Definition\IDType; |
9
|
|
|
use GraphQL\Type\Definition\InputObjectType; |
10
|
|
|
use GraphQL\Type\Definition\StringType; |
11
|
|
|
use GraphQL\Type\Definition\Type; |
12
|
|
|
use GraphQL\Utils\Utils; |
13
|
|
|
use GraphQL\Utils\Value; |
14
|
|
|
use PHPUnit\Framework\TestCase; |
15
|
|
|
use function acos; |
16
|
|
|
use function log; |
17
|
|
|
use function pow; |
18
|
|
|
use function sprintf; |
19
|
|
|
|
20
|
|
|
class CoerceValueTest extends TestCase |
21
|
|
|
{ |
22
|
|
|
/** @var EnumType */ |
23
|
|
|
private $testEnum; |
24
|
|
|
|
25
|
|
|
/** @var InputObjectType */ |
26
|
|
|
private $testInputObject; |
27
|
|
|
|
28
|
|
|
public function setUp() |
29
|
|
|
{ |
30
|
|
|
$this->testEnum = new EnumType([ |
31
|
|
|
'name' => 'TestEnum', |
32
|
|
|
'values' => [ |
33
|
|
|
'FOO' => 'InternalFoo', |
34
|
|
|
'BAR' => 123456789, |
35
|
|
|
], |
36
|
|
|
]); |
37
|
|
|
|
38
|
|
|
$this->testInputObject = new InputObjectType([ |
39
|
|
|
'name' => 'TestInputObject', |
40
|
|
|
'fields' => [ |
41
|
|
|
'foo' => Type::nonNull(Type::int()), |
42
|
|
|
'bar' => Type::int(), |
43
|
|
|
], |
44
|
|
|
]); |
45
|
|
|
} |
46
|
|
|
|
47
|
|
|
/** |
48
|
|
|
* Describe: coerceValue |
49
|
|
|
*/ |
50
|
|
|
|
51
|
|
|
/** |
52
|
|
|
* Describe: for GraphQLString |
53
|
|
|
* |
54
|
|
|
* @see it('returns error for array input as string') |
55
|
|
|
*/ |
56
|
|
|
public function testCoercingAnArrayToGraphQLStringProducesAnError() : void |
57
|
|
|
{ |
58
|
|
|
$result = Value::coerceValue([1, 2, 3], Type::string()); |
59
|
|
|
$this->expectError( |
60
|
|
|
$result, |
61
|
|
|
'Expected type String; String cannot represent a non string value: [1,2,3]' |
62
|
|
|
); |
63
|
|
|
|
64
|
|
|
self::assertEquals( |
65
|
|
|
'String cannot represent a non string value: [1,2,3]', |
66
|
|
|
$result['errors'][0]->getPrevious()->getMessage() |
67
|
|
|
); |
68
|
|
|
} |
69
|
|
|
|
70
|
|
|
/** |
71
|
|
|
* Describe: for GraphQLID |
72
|
|
|
* |
73
|
|
|
* @see it('returns error for array input as string') |
74
|
|
|
*/ |
75
|
|
|
public function testCoercingAnArrayToGraphQLIDProducesAnError() : void |
76
|
|
|
{ |
77
|
|
|
$result = Value::coerceValue([1, 2, 3], Type::id()); |
78
|
|
|
$this->expectError( |
79
|
|
|
$result, |
80
|
|
|
'Expected type ID; ID cannot represent value: [1,2,3]' |
81
|
|
|
); |
82
|
|
|
|
83
|
|
|
self::assertEquals( |
84
|
|
|
'ID cannot represent value: [1,2,3]', |
85
|
|
|
$result['errors'][0]->getPrevious()->getMessage() |
86
|
|
|
); |
87
|
|
|
} |
88
|
|
|
|
89
|
|
|
/** |
90
|
|
|
* Describe: for GraphQLInt |
91
|
|
|
*/ |
92
|
|
|
private function expectError($result, $expected) |
93
|
|
|
{ |
94
|
|
|
self::assertInternalType('array', $result); |
|
|
|
|
95
|
|
|
self::assertInternalType('array', $result['errors']); |
|
|
|
|
96
|
|
|
self::assertCount(1, $result['errors']); |
97
|
|
|
self::assertEquals($expected, $result['errors'][0]->getMessage()); |
98
|
|
|
self::assertEquals(Utils::undefined(), $result['value']); |
99
|
|
|
} |
100
|
|
|
|
101
|
|
|
/** |
102
|
|
|
* @see it('returns value for integer') |
103
|
|
|
*/ |
104
|
|
|
public function testIntReturnsNoErrorForIntInput() : void |
105
|
|
|
{ |
106
|
|
|
$result = Value::coerceValue(1, Type::int()); |
107
|
|
|
$this->expectValue($result, 1); |
108
|
|
|
} |
109
|
|
|
|
110
|
|
|
/** |
111
|
|
|
* @see it('returns error for numeric looking string') |
112
|
|
|
*/ |
113
|
|
|
public function testReturnsErrorForNumericLookingString() |
114
|
|
|
{ |
115
|
|
|
$result = Value::coerceValue('1', Type::int()); |
116
|
|
|
$this->expectError($result, 'Expected type Int; Int cannot represent non-integer value: 1'); |
117
|
|
|
} |
118
|
|
|
|
119
|
|
|
private function expectValue($result, $expected) |
120
|
|
|
{ |
121
|
|
|
self::assertInternalType('array', $result); |
|
|
|
|
122
|
|
|
self::assertEquals(null, $result['errors']); |
123
|
|
|
self::assertNotEquals(Utils::undefined(), $result['value']); |
124
|
|
|
self::assertEquals($expected, $result['value']); |
125
|
|
|
} |
126
|
|
|
|
127
|
|
|
/** |
128
|
|
|
* @see it('returns value for negative int input') |
129
|
|
|
*/ |
130
|
|
|
public function testIntReturnsNoErrorForNegativeIntInput() : void |
131
|
|
|
{ |
132
|
|
|
$result = Value::coerceValue(-1, Type::int()); |
133
|
|
|
$this->expectValue($result, -1); |
134
|
|
|
} |
135
|
|
|
|
136
|
|
|
/** |
137
|
|
|
* @see it('returns value for exponent input') |
138
|
|
|
*/ |
139
|
|
|
public function testIntReturnsNoErrorForExponentInput() : void |
140
|
|
|
{ |
141
|
|
|
$result = Value::coerceValue(1e3, Type::int()); |
142
|
|
|
$this->expectValue($result, 1000); |
143
|
|
|
} |
144
|
|
|
|
145
|
|
|
/** |
146
|
|
|
* @see it('returns null for null value') |
147
|
|
|
*/ |
148
|
|
|
public function testIntReturnsASingleErrorNull() : void |
149
|
|
|
{ |
150
|
|
|
$result = Value::coerceValue(null, Type::int()); |
151
|
|
|
$this->expectValue($result, null); |
152
|
|
|
} |
153
|
|
|
|
154
|
|
|
/** |
155
|
|
|
* @see it('returns a single error for empty string as value') |
156
|
|
|
*/ |
157
|
|
|
public function testIntReturnsASingleErrorForEmptyValue() : void |
158
|
|
|
{ |
159
|
|
|
$result = Value::coerceValue('', Type::int()); |
160
|
|
|
$this->expectError( |
161
|
|
|
$result, |
162
|
|
|
'Expected type Int; Int cannot represent non-integer value: (empty string)' |
163
|
|
|
); |
164
|
|
|
} |
165
|
|
|
|
166
|
|
|
/** |
167
|
|
|
* @see it('returns a single error for 2^32 input as int') |
168
|
|
|
*/ |
169
|
|
|
public function testReturnsASingleErrorFor2x32InputAsInt() |
170
|
|
|
{ |
171
|
|
|
$result = Value::coerceValue(pow(2, 32), Type::int()); |
172
|
|
|
$this->expectError( |
173
|
|
|
$result, |
174
|
|
|
'Expected type Int; Int cannot represent non 32-bit signed integer value: 4294967296' |
175
|
|
|
); |
176
|
|
|
} |
177
|
|
|
|
178
|
|
|
/** |
179
|
|
|
* @see it('returns error for float input as int') |
180
|
|
|
*/ |
181
|
|
|
public function testIntReturnsErrorForFloatInputAsInt() : void |
182
|
|
|
{ |
183
|
|
|
$result = Value::coerceValue(1.5, Type::int()); |
184
|
|
|
$this->expectError( |
185
|
|
|
$result, |
186
|
|
|
'Expected type Int; Int cannot represent non-integer value: 1.5' |
187
|
|
|
); |
188
|
|
|
} |
189
|
|
|
|
190
|
|
|
/** |
191
|
|
|
* @see it('returns a single error for Infinity input as int') |
192
|
|
|
*/ |
193
|
|
|
public function testReturnsASingleErrorForInfinityInputAsInt() |
194
|
|
|
{ |
195
|
|
|
$inf = log(0); |
196
|
|
|
$result = Value::coerceValue($inf, Type::int()); |
197
|
|
|
$this->expectError( |
198
|
|
|
$result, |
199
|
|
|
'Expected type Int; Int cannot represent non 32-bit signed integer value: -INF' |
200
|
|
|
); |
201
|
|
|
} |
202
|
|
|
|
203
|
|
|
public function testReturnsASingleErrorForNaNInputAsInt() |
204
|
|
|
{ |
205
|
|
|
$nan = acos(8); |
206
|
|
|
$result = Value::coerceValue($nan, Type::int()); |
207
|
|
|
$this->expectError( |
208
|
|
|
$result, |
209
|
|
|
'Expected type Int; Int cannot represent non-integer value: NAN' |
210
|
|
|
); |
211
|
|
|
} |
212
|
|
|
|
213
|
|
|
/** |
214
|
|
|
* @see it('returns a single error for string input') |
215
|
|
|
*/ |
216
|
|
|
public function testIntReturnsASingleErrorForCharInput() : void |
217
|
|
|
{ |
218
|
|
|
$result = Value::coerceValue('a', Type::int()); |
219
|
|
|
$this->expectError( |
220
|
|
|
$result, |
221
|
|
|
'Expected type Int; Int cannot represent non-integer value: a' |
222
|
|
|
); |
223
|
|
|
} |
224
|
|
|
|
225
|
|
|
/** |
226
|
|
|
* @see it('returns a single error for multi char input') |
227
|
|
|
*/ |
228
|
|
|
public function testIntReturnsASingleErrorForMultiCharInput() : void |
229
|
|
|
{ |
230
|
|
|
$result = Value::coerceValue('meow', Type::int()); |
231
|
|
|
$this->expectError( |
232
|
|
|
$result, |
233
|
|
|
'Expected type Int; Int cannot represent non-integer value: meow' |
234
|
|
|
); |
235
|
|
|
} |
236
|
|
|
|
237
|
|
|
// Describe: for GraphQLFloat |
238
|
|
|
|
239
|
|
|
/** |
240
|
|
|
* @see it('returns value for integer') |
241
|
|
|
*/ |
242
|
|
|
public function testFloatReturnsNoErrorForIntInput() : void |
243
|
|
|
{ |
244
|
|
|
$result = Value::coerceValue(1, Type::float()); |
245
|
|
|
$this->expectValue($result, 1); |
246
|
|
|
} |
247
|
|
|
|
248
|
|
|
/** |
249
|
|
|
* @see it('returns value for decimal') |
250
|
|
|
*/ |
251
|
|
|
public function testReturnsValueForDecimal() |
252
|
|
|
{ |
253
|
|
|
$result = Value::coerceValue(1.1, Type::float()); |
254
|
|
|
$this->expectValue($result, 1.1); |
255
|
|
|
} |
256
|
|
|
|
257
|
|
|
/** |
258
|
|
|
* @see it('returns value for exponent input') |
259
|
|
|
*/ |
260
|
|
|
public function testFloatReturnsNoErrorForExponentInput() : void |
261
|
|
|
{ |
262
|
|
|
$result = Value::coerceValue(1e3, Type::float()); |
263
|
|
|
$this->expectValue($result, 1000); |
264
|
|
|
} |
265
|
|
|
|
266
|
|
|
/** |
267
|
|
|
* @see it('returns error for numeric looking string') |
268
|
|
|
*/ |
269
|
|
|
public function testFloatReturnsErrorForNumericLookingString() |
270
|
|
|
{ |
271
|
|
|
$result = Value::coerceValue('1', Type::float()); |
272
|
|
|
$this->expectError( |
273
|
|
|
$result, |
274
|
|
|
'Expected type Float; Float cannot represent non numeric value: 1' |
275
|
|
|
); |
276
|
|
|
} |
277
|
|
|
|
278
|
|
|
/** |
279
|
|
|
* @see it('returns null for null value') |
280
|
|
|
*/ |
281
|
|
|
public function testFloatReturnsASingleErrorNull() : void |
282
|
|
|
{ |
283
|
|
|
$result = Value::coerceValue(null, Type::float()); |
284
|
|
|
$this->expectValue($result, null); |
285
|
|
|
} |
286
|
|
|
|
287
|
|
|
/** |
288
|
|
|
* @see it('returns a single error for empty string input') |
289
|
|
|
*/ |
290
|
|
|
public function testFloatReturnsASingleErrorForEmptyValue() : void |
291
|
|
|
{ |
292
|
|
|
$result = Value::coerceValue('', Type::float()); |
293
|
|
|
$this->expectError( |
294
|
|
|
$result, |
295
|
|
|
'Expected type Float; Float cannot represent non numeric value: (empty string)' |
296
|
|
|
); |
297
|
|
|
} |
298
|
|
|
|
299
|
|
|
/** |
300
|
|
|
* @see it('returns a single error for Infinity input') |
301
|
|
|
*/ |
302
|
|
|
public function testFloatReturnsASingleErrorForInfinityInput() : void |
303
|
|
|
{ |
304
|
|
|
$inf = log(0); |
305
|
|
|
$result = Value::coerceValue($inf, Type::float()); |
306
|
|
|
$this->expectError( |
307
|
|
|
$result, |
308
|
|
|
'Expected type Float; Float cannot represent non numeric value: -INF' |
309
|
|
|
); |
310
|
|
|
} |
311
|
|
|
|
312
|
|
|
public function testFloatReturnsASingleErrorForNaNInput() : void |
313
|
|
|
{ |
314
|
|
|
$nan = acos(8); |
315
|
|
|
$result = Value::coerceValue($nan, Type::float()); |
316
|
|
|
$this->expectError( |
317
|
|
|
$result, |
318
|
|
|
'Expected type Float; Float cannot represent non numeric value: NAN' |
319
|
|
|
); |
320
|
|
|
} |
321
|
|
|
|
322
|
|
|
// DESCRIBE: for GraphQLEnum |
323
|
|
|
|
324
|
|
|
/** |
325
|
|
|
* @see it('returns a single error for char input') |
326
|
|
|
*/ |
327
|
|
|
public function testFloatReturnsASingleErrorForCharInput() : void |
328
|
|
|
{ |
329
|
|
|
$result = Value::coerceValue('a', Type::float()); |
330
|
|
|
$this->expectError( |
331
|
|
|
$result, |
332
|
|
|
'Expected type Float; Float cannot represent non numeric value: a' |
333
|
|
|
); |
334
|
|
|
} |
335
|
|
|
|
336
|
|
|
/** |
337
|
|
|
* @see it('returns a single error for multi char input') |
338
|
|
|
*/ |
339
|
|
|
public function testFloatReturnsASingleErrorForMultiCharInput() : void |
340
|
|
|
{ |
341
|
|
|
$result = Value::coerceValue('meow', Type::float()); |
342
|
|
|
$this->expectError( |
343
|
|
|
$result, |
344
|
|
|
'Expected type Float; Float cannot represent non numeric value: meow' |
345
|
|
|
); |
346
|
|
|
} |
347
|
|
|
|
348
|
|
|
/** |
349
|
|
|
* @see it('returns no error for a known enum name') |
350
|
|
|
*/ |
351
|
|
|
public function testReturnsNoErrorForAKnownEnumName() : void |
352
|
|
|
{ |
353
|
|
|
$fooResult = Value::coerceValue('FOO', $this->testEnum); |
354
|
|
|
$this->expectValue($fooResult, 'InternalFoo'); |
355
|
|
|
|
356
|
|
|
$barResult = Value::coerceValue('BAR', $this->testEnum); |
357
|
|
|
$this->expectValue($barResult, 123456789); |
358
|
|
|
} |
359
|
|
|
|
360
|
|
|
// DESCRIBE: for GraphQLInputObject |
361
|
|
|
|
362
|
|
|
/** |
363
|
|
|
* @see it('results error for misspelled enum value') |
364
|
|
|
*/ |
365
|
|
|
public function testReturnsErrorForMisspelledEnumValue() : void |
366
|
|
|
{ |
367
|
|
|
$result = Value::coerceValue('foo', $this->testEnum); |
368
|
|
|
$this->expectError($result, 'Expected type TestEnum; did you mean FOO?'); |
369
|
|
|
} |
370
|
|
|
|
371
|
|
|
/** |
372
|
|
|
* @see it('results error for incorrect value type') |
373
|
|
|
*/ |
374
|
|
|
public function testReturnsErrorForIncorrectValueType() : void |
375
|
|
|
{ |
376
|
|
|
$result1 = Value::coerceValue(123, $this->testEnum); |
377
|
|
|
$this->expectError($result1, 'Expected type TestEnum.'); |
378
|
|
|
|
379
|
|
|
$result2 = Value::coerceValue(['field' => 'value'], $this->testEnum); |
380
|
|
|
$this->expectError($result2, 'Expected type TestEnum.'); |
381
|
|
|
} |
382
|
|
|
|
383
|
|
|
/** |
384
|
|
|
* @see it('returns no error for a valid input') |
385
|
|
|
*/ |
386
|
|
|
public function testReturnsNoErrorForValidInput() : void |
387
|
|
|
{ |
388
|
|
|
$result = Value::coerceValue(['foo' => 123], $this->testInputObject); |
389
|
|
|
$this->expectValue($result, ['foo' => 123]); |
390
|
|
|
} |
391
|
|
|
|
392
|
|
|
/** |
393
|
|
|
* @see it('returns no error for a non-object type') |
394
|
|
|
*/ |
395
|
|
|
public function testReturnsErrorForNonObjectType() : void |
396
|
|
|
{ |
397
|
|
|
$result = Value::coerceValue(123, $this->testInputObject); |
398
|
|
|
$this->expectError($result, 'Expected type TestInputObject to be an object.'); |
399
|
|
|
} |
400
|
|
|
|
401
|
|
|
public function testReturnsNoErrorForStdClassInput() : void |
402
|
|
|
{ |
403
|
|
|
$result = Value::coerceValue((object) ['foo' => 123], $this->testInputObject); |
404
|
|
|
$this->expectValue($result, ['foo' => 123]); |
405
|
|
|
} |
406
|
|
|
|
407
|
|
|
/** |
408
|
|
|
* @see it('returns no error for an invalid field') |
409
|
|
|
*/ |
410
|
|
|
public function testReturnErrorForAnInvalidField() : void |
411
|
|
|
{ |
412
|
|
|
$result = Value::coerceValue(['foo' => 'abc'], $this->testInputObject); |
413
|
|
|
$this->expectError( |
414
|
|
|
$result, |
415
|
|
|
'Expected type Int at value.foo; Int cannot represent non-integer value: abc' |
416
|
|
|
); |
417
|
|
|
} |
418
|
|
|
|
419
|
|
|
/** |
420
|
|
|
* @see it('returns multiple errors for multiple invalid fields') |
421
|
|
|
*/ |
422
|
|
|
public function testReturnsMultipleErrorsForMultipleInvalidFields() : void |
423
|
|
|
{ |
424
|
|
|
$result = Value::coerceValue(['foo' => 'abc', 'bar' => 'def'], $this->testInputObject); |
425
|
|
|
self::assertEquals( |
426
|
|
|
[ |
427
|
|
|
'Expected type Int at value.foo; Int cannot represent non-integer value: abc', |
428
|
|
|
'Expected type Int at value.bar; Int cannot represent non-integer value: def', |
429
|
|
|
], |
430
|
|
|
$result['errors'] |
431
|
|
|
); |
432
|
|
|
} |
433
|
|
|
|
434
|
|
|
/** |
435
|
|
|
* @see it('returns error for a missing required field') |
436
|
|
|
*/ |
437
|
|
|
public function testReturnsErrorForAMissingRequiredField() : void |
438
|
|
|
{ |
439
|
|
|
$result = Value::coerceValue(['bar' => 123], $this->testInputObject); |
440
|
|
|
$this->expectError($result, 'Field value.foo of required type Int! was not provided.'); |
441
|
|
|
} |
442
|
|
|
|
443
|
|
|
/** |
444
|
|
|
* @see it('returns error for an unknown field') |
445
|
|
|
*/ |
446
|
|
|
public function testReturnsErrorForAnUnknownField() : void |
447
|
|
|
{ |
448
|
|
|
$result = Value::coerceValue(['foo' => 123, 'unknownField' => 123], $this->testInputObject); |
449
|
|
|
$this->expectError($result, 'Field "unknownField" is not defined by type TestInputObject.'); |
450
|
|
|
} |
451
|
|
|
|
452
|
|
|
/** |
453
|
|
|
* @see it('returns error for a misspelled field') |
454
|
|
|
*/ |
455
|
|
|
public function testReturnsErrorForAMisspelledField() : void |
456
|
|
|
{ |
457
|
|
|
$result = Value::coerceValue(['foo' => 123, 'bart' => 123], $this->testInputObject); |
458
|
|
|
$this->expectError($result, 'Field "bart" is not defined by type TestInputObject; did you mean bar?'); |
459
|
|
|
} |
460
|
|
|
} |
461
|
|
|
|
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.