1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace TraderInteractive; |
4
|
|
|
|
5
|
|
|
use Exception; |
6
|
|
|
use InvalidArgumentException; |
7
|
|
|
use PHPUnit\Framework\TestCase; |
8
|
|
|
use StdClass; |
9
|
|
|
|
10
|
|
|
/** |
11
|
|
|
* @coversDefaultClass \TraderInteractive\Filterer |
12
|
|
|
*/ |
13
|
|
|
final class FiltererTest extends TestCase |
14
|
|
|
{ |
15
|
|
|
/** |
16
|
|
|
* @test |
17
|
|
|
* @covers ::filter |
18
|
|
|
*/ |
19
|
|
|
public function requiredPass() |
20
|
|
|
{ |
21
|
|
|
$result = Filterer::filter(['fieldOne' => ['required' => false]], []); |
22
|
|
|
$this->assertSame([true, [], null, []], $result); |
23
|
|
|
} |
24
|
|
|
|
25
|
|
|
/** |
26
|
|
|
* @test |
27
|
|
|
* @covers ::filter |
28
|
|
|
*/ |
29
|
|
|
public function requiredFail() |
30
|
|
|
{ |
31
|
|
|
$result = Filterer::filter(['fieldOne' => ['required' => true]], []); |
32
|
|
|
$this->assertSame([false, null, "Field 'fieldOne' was required and not present", []], $result); |
33
|
|
|
} |
34
|
|
|
|
35
|
|
|
/** |
36
|
|
|
* @test |
37
|
|
|
* @covers ::filter |
38
|
|
|
*/ |
39
|
|
|
public function requiredWithADefaultWithoutInput() |
40
|
|
|
{ |
41
|
|
|
$result = Filterer::filter(['fieldOne' => ['required' => true, 'default' => 'theDefault']], []); |
42
|
|
|
$this->assertSame([true, ['fieldOne' => 'theDefault'], null, []], $result); |
43
|
|
|
} |
44
|
|
|
|
45
|
|
|
/** |
46
|
|
|
* @test |
47
|
|
|
* @covers ::filter |
48
|
|
|
*/ |
49
|
|
|
public function requiredWithANullDefaultWithoutInput() |
50
|
|
|
{ |
51
|
|
|
$result = Filterer::filter(['fieldOne' => ['required' => true, 'default' => null]], []); |
52
|
|
|
$this->assertSame([true, ['fieldOne' => null], null, []], $result); |
53
|
|
|
} |
54
|
|
|
|
55
|
|
|
/** |
56
|
|
|
* @test |
57
|
|
|
* @covers ::filter |
58
|
|
|
*/ |
59
|
|
View Code Duplication |
public function requiredWithADefaultWithInput() |
|
|
|
|
60
|
|
|
{ |
61
|
|
|
$result = Filterer::filter( |
62
|
|
|
['fieldOne' => ['required' => true, 'default' => 'theDefault']], |
63
|
|
|
['fieldOne' => 'notTheDefault'] |
64
|
|
|
); |
65
|
|
|
$this->assertSame([true, ['fieldOne' => 'notTheDefault'], null, []], $result); |
66
|
|
|
} |
67
|
|
|
|
68
|
|
|
/** |
69
|
|
|
* @test |
70
|
|
|
* @covers ::filter |
71
|
|
|
*/ |
72
|
|
|
public function notRequiredWithADefaultWithoutInput() |
73
|
|
|
{ |
74
|
|
|
$result = Filterer::filter(['fieldOne' => ['default' => 'theDefault']], []); |
75
|
|
|
$this->assertSame([true, ['fieldOne' => 'theDefault'], null, []], $result); |
76
|
|
|
} |
77
|
|
|
|
78
|
|
|
/** |
79
|
|
|
* @test |
80
|
|
|
* @covers ::filter |
81
|
|
|
*/ |
82
|
|
|
public function notRequiredWithADefaultWithInput() |
83
|
|
|
{ |
84
|
|
|
$result = Filterer::filter(['fieldOne' => ['default' => 'theDefault']], ['fieldOne' => 'notTheDefault']); |
85
|
|
|
$this->assertSame([true, ['fieldOne' => 'notTheDefault'], null, []], $result); |
86
|
|
|
} |
87
|
|
|
|
88
|
|
|
/** |
89
|
|
|
* @test |
90
|
|
|
* @covers ::filter |
91
|
|
|
*/ |
92
|
|
|
public function requiredDefaultPass() |
93
|
|
|
{ |
94
|
|
|
$result = Filterer::filter(['fieldOne' => []], []); |
95
|
|
|
$this->assertSame([true, [], null, []], $result); |
96
|
|
|
} |
97
|
|
|
|
98
|
|
|
/** |
99
|
|
|
* @test |
100
|
|
|
* @covers ::filter |
101
|
|
|
*/ |
102
|
|
|
public function requiredDefaultFail() |
103
|
|
|
{ |
104
|
|
|
$result = Filterer::filter(['fieldOne' => []], [], ['defaultRequired' => true]); |
105
|
|
|
$this->assertSame([false, null, "Field 'fieldOne' was required and not present", []], $result); |
106
|
|
|
} |
107
|
|
|
|
108
|
|
|
/** |
109
|
|
|
* @test |
110
|
|
|
* @covers ::filter |
111
|
|
|
*/ |
112
|
|
|
public function filterPass() |
113
|
|
|
{ |
114
|
|
|
$result = Filterer::filter(['fieldOne' => [['floatval']]], ['fieldOne' => '3.14']); |
115
|
|
|
$this->assertSame([true, ['fieldOne' => 3.14], null, []], $result); |
116
|
|
|
} |
117
|
|
|
|
118
|
|
|
/** |
119
|
|
|
* @test |
120
|
|
|
* @covers ::filter |
121
|
|
|
*/ |
122
|
|
|
public function filterDefaultShortNamePass() |
123
|
|
|
{ |
124
|
|
|
$result = Filterer::filter(['fieldOne' => [['float']]], ['fieldOne' => '3.14']); |
125
|
|
|
$this->assertSame([true, ['fieldOne' => 3.14], null, []], $result); |
126
|
|
|
} |
127
|
|
|
|
128
|
|
|
/** |
129
|
|
|
* @test |
130
|
|
|
* @covers ::filter |
131
|
|
|
* @covers ::setFilterAliases |
132
|
|
|
*/ |
133
|
|
|
public function filterCustomShortNamePass() |
134
|
|
|
{ |
135
|
|
|
Filterer::setFilterAliases(['fval' => 'floatval']); |
136
|
|
|
$result = Filterer::filter(['fieldOne' => [['fval']]], ['fieldOne' => '3.14']); |
137
|
|
|
$this->assertSame([true, ['fieldOne' => 3.14], null, []], $result); |
138
|
|
|
} |
139
|
|
|
|
140
|
|
|
/** |
141
|
|
|
* @test |
142
|
|
|
* @covers ::filter |
143
|
|
|
* @covers ::setFilterAliases |
144
|
|
|
* @covers ::getFilterAliases |
145
|
|
|
*/ |
146
|
|
|
public function filterGetSetKnownFilters() |
147
|
|
|
{ |
148
|
|
|
$knownFilters = ['lower' => 'strtolower', 'upper' => 'strtoupper']; |
149
|
|
|
Filterer::setFilterAliases($knownFilters); |
150
|
|
|
$this->assertSame($knownFilters, Filterer::getFilterAliases()); |
151
|
|
|
} |
152
|
|
|
|
153
|
|
|
/** |
154
|
|
|
* @test |
155
|
|
|
* @covers ::filter |
156
|
|
|
*/ |
157
|
|
|
public function filterFail() |
158
|
|
|
{ |
159
|
|
|
$result = Filterer::filter( |
160
|
|
|
['fieldOne' => [['\TraderInteractive\FiltererTest::failingFilter']]], |
161
|
|
|
['fieldOne' => 'valueOne'] |
162
|
|
|
); |
163
|
|
|
$this->assertSame( |
164
|
|
|
[false, null, "Field 'fieldOne' with value 'valueOne' failed filtering, message 'i failed'", []], |
165
|
|
|
$result |
166
|
|
|
); |
167
|
|
|
} |
168
|
|
|
|
169
|
|
|
/** |
170
|
|
|
* @test |
171
|
|
|
* @covers ::filter |
172
|
|
|
*/ |
173
|
|
|
public function chainPass() |
174
|
|
|
{ |
175
|
|
|
$result = Filterer::filter(['fieldOne' => [['trim', 'a'], ['floatval']]], ['fieldOne' => 'a3.14']); |
176
|
|
|
$this->assertSame([true, ['fieldOne' => 3.14], null, []], $result); |
177
|
|
|
} |
178
|
|
|
|
179
|
|
|
/** |
180
|
|
|
* @test |
181
|
|
|
* @covers ::filter |
182
|
|
|
*/ |
183
|
|
View Code Duplication |
public function chainFail() |
|
|
|
|
184
|
|
|
{ |
185
|
|
|
$result = Filterer::filter( |
186
|
|
|
['fieldOne' => [['trim'], ['\TraderInteractive\FiltererTest::failingFilter']]], |
187
|
|
|
['fieldOne' => 'the value'] |
188
|
|
|
); |
189
|
|
|
$this->assertSame( |
190
|
|
|
[false, null, "Field 'fieldOne' with value 'the value' failed filtering, message 'i failed'", []], |
191
|
|
|
$result |
192
|
|
|
); |
193
|
|
|
} |
194
|
|
|
|
195
|
|
|
/** |
196
|
|
|
* @test |
197
|
|
|
* @covers ::filter |
198
|
|
|
*/ |
199
|
|
|
public function multiInputPass() |
200
|
|
|
{ |
201
|
|
|
$result = Filterer::filter( |
202
|
|
|
['fieldOne' => [['trim']], 'fieldTwo' => [['strtoupper']]], |
203
|
|
|
['fieldOne' => ' value', 'fieldTwo' => 'bob'] |
204
|
|
|
); |
205
|
|
|
$this->assertSame([true, ['fieldOne' => 'value', 'fieldTwo' => 'BOB'], null, []], $result); |
206
|
|
|
} |
207
|
|
|
|
208
|
|
|
/** |
209
|
|
|
* @test |
210
|
|
|
* @covers ::filter |
211
|
|
|
*/ |
212
|
|
|
public function multiInputFail() |
213
|
|
|
{ |
214
|
|
|
$result = Filterer::filter( |
215
|
|
|
[ |
216
|
|
|
'fieldOne' => [['\TraderInteractive\FiltererTest::failingFilter']], |
217
|
|
|
'fieldTwo' => [['\TraderInteractive\FiltererTest::failingFilter']], |
218
|
|
|
], |
219
|
|
|
['fieldOne' => 'value one', 'fieldTwo' => 'value two'] |
220
|
|
|
); |
221
|
|
|
$expectedMessage = "Field 'fieldOne' with value 'value one' failed filtering, message 'i failed'\n"; |
222
|
|
|
$expectedMessage .= "Field 'fieldTwo' with value 'value two' failed filtering, message 'i failed'"; |
223
|
|
|
$this->assertSame([false, null, $expectedMessage, []], $result); |
224
|
|
|
} |
225
|
|
|
|
226
|
|
|
/** |
227
|
|
|
* @test |
228
|
|
|
* @covers ::filter |
229
|
|
|
*/ |
230
|
|
|
public function emptyFilter() |
231
|
|
|
{ |
232
|
|
|
$result = Filterer::filter(['fieldOne' => [[]]], ['fieldOne' => 0]); |
233
|
|
|
$this->assertSame([true, ['fieldOne' => 0], null, []], $result); |
234
|
|
|
} |
235
|
|
|
|
236
|
|
|
/** |
237
|
|
|
* @test |
238
|
|
|
* @covers ::filter |
239
|
|
|
*/ |
240
|
|
|
public function unknownsAllowed() |
241
|
|
|
{ |
242
|
|
|
$result = Filterer::filter([], ['fieldTwo' => 0], ['allowUnknowns' => true]); |
243
|
|
|
$this->assertSame([true, [], null, ['fieldTwo' => 0]], $result); |
244
|
|
|
} |
245
|
|
|
|
246
|
|
|
/** |
247
|
|
|
* @test |
248
|
|
|
* @covers ::filter |
249
|
|
|
*/ |
250
|
|
|
public function unknownsNotAllowed() |
251
|
|
|
{ |
252
|
|
|
$result = Filterer::filter([], ['fieldTwo' => 0]); |
253
|
|
|
$this->assertSame([false, null, "Field 'fieldTwo' with value '0' is unknown", ['fieldTwo' => 0]], $result); |
254
|
|
|
} |
255
|
|
|
|
256
|
|
|
/** |
257
|
|
|
* @test |
258
|
|
|
* @covers ::filter |
259
|
|
|
*/ |
260
|
|
|
public function objectFilter() |
261
|
|
|
{ |
262
|
|
|
$result = Filterer::filter(['fieldOne' => [[[$this, 'passingFilter']]]], ['fieldOne' => 'foo']); |
263
|
|
|
$this->assertSame([true, ['fieldOne' => 'fooboo'], null, []], $result); |
264
|
|
|
} |
265
|
|
|
|
266
|
|
|
/** |
267
|
|
|
* @test |
268
|
|
|
* @covers ::filter |
269
|
|
|
* @expectedException Exception |
270
|
|
|
* @expectedExceptionMessage Function 'boo' for field 'foo' is not callable |
271
|
|
|
*/ |
272
|
|
|
public function notCallable() |
273
|
|
|
{ |
274
|
|
|
Filterer::filter(['foo' => [['boo']]], ['foo' => 0]); |
275
|
|
|
} |
276
|
|
|
|
277
|
|
|
/** |
278
|
|
|
* @test |
279
|
|
|
* @covers ::filter |
280
|
|
|
* @expectedException InvalidArgumentException |
281
|
|
|
* @expectedExceptionMessage 'allowUnknowns' option was not a bool |
282
|
|
|
*/ |
283
|
|
|
public function allowUnknownsNotBool() |
284
|
|
|
{ |
285
|
|
|
Filterer::filter([], [], ['allowUnknowns' => 1]); |
286
|
|
|
} |
287
|
|
|
|
288
|
|
|
/** |
289
|
|
|
* @test |
290
|
|
|
* @covers ::filter |
291
|
|
|
* @expectedException InvalidArgumentException |
292
|
|
|
* @expectedExceptionMessage 'defaultRequired' option was not a bool |
293
|
|
|
*/ |
294
|
|
|
public function defaultRequiredNotBool() |
295
|
|
|
{ |
296
|
|
|
Filterer::filter([], [], ['defaultRequired' => 1]); |
297
|
|
|
} |
298
|
|
|
|
299
|
|
|
/** |
300
|
|
|
* @test |
301
|
|
|
* @covers ::filter |
302
|
|
|
* @expectedException InvalidArgumentException |
303
|
|
|
* @expectedExceptionMessage filters for field 'boo' was not a array |
304
|
|
|
*/ |
305
|
|
|
public function filtersNotArrayInLeftOverSpec() |
306
|
|
|
{ |
307
|
|
|
Filterer::filter(['boo' => 1], []); |
308
|
|
|
} |
309
|
|
|
|
310
|
|
|
/** |
311
|
|
|
* @test |
312
|
|
|
* @covers ::filter |
313
|
|
|
* @expectedException InvalidArgumentException |
314
|
|
|
* @expectedExceptionMessage filters for field 'boo' was not a array |
315
|
|
|
*/ |
316
|
|
|
public function filtersNotArrayWithInput() |
317
|
|
|
{ |
318
|
|
|
Filterer::filter(['boo' => 1], ['boo' => 'notUnderTest']); |
319
|
|
|
} |
320
|
|
|
|
321
|
|
|
/** |
322
|
|
|
* @test |
323
|
|
|
* @covers ::filter |
324
|
|
|
* @expectedException InvalidArgumentException |
325
|
|
|
* @expectedExceptionMessage filter for field 'boo' was not a array |
326
|
|
|
*/ |
327
|
|
|
public function filterNotArray() |
328
|
|
|
{ |
329
|
|
|
Filterer::filter(['boo' => [1]], ['boo' => 'notUnderTest']); |
330
|
|
|
} |
331
|
|
|
|
332
|
|
|
/** |
333
|
|
|
* @test |
334
|
|
|
* @covers ::filter |
335
|
|
|
* @expectedException InvalidArgumentException |
336
|
|
|
* @expectedExceptionMessage 'required' for field 'boo' was not a bool |
337
|
|
|
*/ |
338
|
|
|
public function requiredNotBool() |
339
|
|
|
{ |
340
|
|
|
Filterer::filter(['boo' => ['required' => 1]], []); |
341
|
|
|
} |
342
|
|
|
|
343
|
|
|
/** |
344
|
|
|
* @test |
345
|
|
|
* @covers ::registerAlias |
346
|
|
|
* @expectedException InvalidArgumentException |
347
|
|
|
* @expectedExceptionMessage $alias was not a string or int |
348
|
|
|
*/ |
349
|
|
|
public function registerAliasAliasNotString() |
350
|
|
|
{ |
351
|
|
|
Filterer::registerAlias(true, 'strtolower'); |
|
|
|
|
352
|
|
|
} |
353
|
|
|
|
354
|
|
|
/** |
355
|
|
|
* @test |
356
|
|
|
* @covers ::registerAlias |
357
|
|
|
* @expectedException Exception |
358
|
|
|
* @expectedExceptionMessage Alias 'upper' exists |
359
|
|
|
*/ |
360
|
|
|
public function registerExistingAliasOverwriteFalse() |
361
|
|
|
{ |
362
|
|
|
Filterer::setFilterAliases([]); |
363
|
|
|
Filterer::registerAlias('upper', 'strtoupper'); |
364
|
|
|
Filterer::registerAlias('upper', 'strtoupper', false); |
365
|
|
|
} |
366
|
|
|
|
367
|
|
|
/** |
368
|
|
|
* @test |
369
|
|
|
* @covers ::registerAlias |
370
|
|
|
*/ |
371
|
|
|
public function registerExistingAliasOverwriteTrue() |
372
|
|
|
{ |
373
|
|
|
Filterer::setFilterAliases(['upper' => 'strtoupper', 'lower' => 'strtolower']); |
374
|
|
|
Filterer::registerAlias('upper', 'ucfirst', true); |
375
|
|
|
$this->assertSame(['upper' => 'ucfirst', 'lower' => 'strtolower'], Filterer::getFilterAliases()); |
376
|
|
|
} |
377
|
|
|
|
378
|
|
|
public static function failingFilter() |
379
|
|
|
{ |
380
|
|
|
throw new Exception('i failed'); |
381
|
|
|
} |
382
|
|
|
|
383
|
|
|
public static function passingFilter($value) |
384
|
|
|
{ |
385
|
|
|
return $value . 'boo'; |
386
|
|
|
} |
387
|
|
|
|
388
|
|
|
/** |
389
|
|
|
* Verify custom errors can be added to filter spec. |
390
|
|
|
* |
391
|
|
|
* @test |
392
|
|
|
* @covers ::filter |
393
|
|
|
* |
394
|
|
|
* @return void |
395
|
|
|
*/ |
396
|
|
View Code Duplication |
public function filterWithCustomError() |
|
|
|
|
397
|
|
|
{ |
398
|
|
|
$result = Filterer::filter( |
399
|
|
|
[ |
400
|
|
|
'fieldOne' => [ |
401
|
|
|
['\TraderInteractive\FiltererTest::failingFilter'], |
402
|
|
|
'error' => 'My custom error message' |
403
|
|
|
], |
404
|
|
|
], |
405
|
|
|
['fieldOne' => 'valueOne'] |
406
|
|
|
); |
407
|
|
|
$this->assertSame( |
408
|
|
|
[false, null, 'My custom error message', []], |
409
|
|
|
$result |
410
|
|
|
); |
411
|
|
|
} |
412
|
|
|
|
413
|
|
|
/** |
414
|
|
|
* Verify behavior of filter() when 'error' is not a string value. |
415
|
|
|
* |
416
|
|
|
* @test |
417
|
|
|
* @covers ::filter |
418
|
|
|
* @expectedException InvalidArgumentException |
419
|
|
|
* @expectedExceptionMessage error for field 'fieldOne' was not a non-empty string |
420
|
|
|
* |
421
|
|
|
* @return void |
422
|
|
|
*/ |
423
|
|
View Code Duplication |
public function filterWithNonStringError() |
|
|
|
|
424
|
|
|
{ |
425
|
|
|
Filterer::filter( |
426
|
|
|
['fieldOne' => [['strtoupper'], 'error' => new StdClass()]], |
427
|
|
|
['fieldOne' => 'valueOne'] |
428
|
|
|
); |
429
|
|
|
} |
430
|
|
|
|
431
|
|
|
/** |
432
|
|
|
* Verify behavior of filter() when 'error' is an empty string. |
433
|
|
|
* |
434
|
|
|
* @test |
435
|
|
|
* @covers ::filter |
436
|
|
|
* @expectedException InvalidArgumentException |
437
|
|
|
* @expectedExceptionMessage error for field 'fieldOne' was not a non-empty string |
438
|
|
|
* |
439
|
|
|
* @return void |
440
|
|
|
*/ |
441
|
|
View Code Duplication |
public function filterWithEmptyStringError() |
|
|
|
|
442
|
|
|
{ |
443
|
|
|
Filterer::filter( |
444
|
|
|
['fieldOne' => [['strtoupper'], 'error' => "\n \t"]], |
445
|
|
|
['fieldOne' => 'valueOne'] |
446
|
|
|
); |
447
|
|
|
} |
448
|
|
|
} |
449
|
|
|
|
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.