Completed
Pull Request — master (#76)
by Chad
01:40
created

FiltererTest::allowUnknownsNotBool()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 0
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
 * @covers ::<private>
13
 */
14
final class FiltererTest extends TestCase
15
{
16
    public function setUp()
17
    {
18
        Filterer::setFilterAliases(Filterer::DEFAULT_FILTER_ALIASES);
19
    }
20
21
    /**
22
     * @test
23
     * @covers ::filter
24
     */
25
    public function requiredPass()
26
    {
27
        $result = Filterer::filter(['fieldOne' => ['required' => false]], []);
28
        $this->assertSame([true, [], null, []], $result);
29
    }
30
31
    /**
32
     * @test
33
     * @covers ::filter
34
     */
35
    public function requiredFail()
36
    {
37
        $result = Filterer::filter(['fieldOne' => ['required' => true]], []);
38
        $this->assertSame([false, null, "Field 'fieldOne' was required and not present", []], $result);
39
    }
40
41
    /**
42
     * @test
43
     * @covers ::filter
44
     */
45
    public function requiredWithADefaultWithoutInput()
46
    {
47
        $result = Filterer::filter(['fieldOne' => ['required' => true, 'default' => 'theDefault']], []);
48
        $this->assertSame([true, ['fieldOne' => 'theDefault'], null, []], $result);
49
    }
50
51
    /**
52
     * @test
53
     * @covers ::filter
54
     */
55
    public function requiredWithANullDefaultWithoutInput()
56
    {
57
        $result = Filterer::filter(['fieldOne' => ['required' => true, 'default' => null]], []);
58
        $this->assertSame([true, ['fieldOne' => null], null, []], $result);
59
    }
60
61
    /**
62
     * @test
63
     * @covers ::filter
64
     */
65 View Code Duplication
    public function requiredWithADefaultWithInput()
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...
66
    {
67
        $result = Filterer::filter(
68
            ['fieldOne' => ['required' => true, 'default' => 'theDefault']],
69
            ['fieldOne' => 'notTheDefault']
70
        );
71
        $this->assertSame([true, ['fieldOne' => 'notTheDefault'], null, []], $result);
72
    }
73
74
    /**
75
     * @test
76
     * @covers ::filter
77
     */
78
    public function notRequiredWithADefaultWithoutInput()
79
    {
80
        $result = Filterer::filter(['fieldOne' => ['default' => 'theDefault']], []);
81
        $this->assertSame([true, ['fieldOne' => 'theDefault'], null, []], $result);
82
    }
83
84
    /**
85
     * @test
86
     * @covers ::filter
87
     */
88
    public function notRequiredWithADefaultWithInput()
89
    {
90
        $result = Filterer::filter(['fieldOne' => ['default' => 'theDefault']], ['fieldOne' => 'notTheDefault']);
91
        $this->assertSame([true, ['fieldOne' => 'notTheDefault'], null, []], $result);
92
    }
93
94
    /**
95
     * @test
96
     * @covers ::filter
97
     */
98
    public function requiredDefaultPass()
99
    {
100
        $result = Filterer::filter(['fieldOne' => []], []);
101
        $this->assertSame([true, [], null, []], $result);
102
    }
103
104
    /**
105
     * @test
106
     * @covers ::filter
107
     */
108
    public function requiredDefaultFail()
109
    {
110
        $result = Filterer::filter(['fieldOne' => []], [], ['defaultRequired' => true]);
111
        $this->assertSame([false, null, "Field 'fieldOne' was required and not present", []], $result);
112
    }
113
114
    /**
115
     * @test
116
     * @covers ::filter
117
     */
118
    public function filterPass()
119
    {
120
        $result = Filterer::filter(['fieldOne' => [['floatval']]], ['fieldOne' => '3.14']);
121
        $this->assertSame([true, ['fieldOne' => 3.14], null, []], $result);
122
    }
123
124
    /**
125
     * @test
126
     * @covers ::filter
127
     */
128
    public function filterDefaultShortNamePass()
129
    {
130
        $result = Filterer::filter(['fieldOne' => [['float']]], ['fieldOne' => '3.14']);
131
        $this->assertSame([true, ['fieldOne' => 3.14], null, []], $result);
132
    }
133
134
    /**
135
     * @test
136
     * @covers ::filter
137
     * @covers ::setFilterAliases
138
     */
139
    public function filterCustomShortNamePass()
140
    {
141
        Filterer::setFilterAliases(['fval' => 'floatval']);
142
        $result = Filterer::filter(['fieldOne' => [['fval']]], ['fieldOne' => '3.14']);
143
        $this->assertSame([true, ['fieldOne' => 3.14], null, []], $result);
144
    }
145
146
    /**
147
     * @test
148
     * @covers ::filter
149
     * @covers ::setFilterAliases
150
     * @covers ::getFilterAliases
151
     */
152
    public function filterGetSetKnownFilters()
153
    {
154
        $knownFilters = ['lower' => 'strtolower', 'upper' => 'strtoupper'];
155
        Filterer::setFilterAliases($knownFilters);
156
        $this->assertSame($knownFilters, Filterer::getFilterAliases());
157
    }
158
159
    /**
160
     * @test
161
     * @covers ::filter
162
     */
163
    public function filterFail()
164
    {
165
        $result = Filterer::filter(
166
            ['fieldOne' => [['\TraderInteractive\FiltererTest::failingFilter']]],
167
            ['fieldOne' => 'valueOne']
168
        );
169
        $this->assertSame(
170
            [false, null, "Field 'fieldOne' with value 'valueOne' failed filtering, message 'i failed'", []],
171
            $result
172
        );
173
    }
174
175
    /**
176
     * @test
177
     * @covers ::filter
178
     */
179
    public function chainPass()
180
    {
181
        $result = Filterer::filter(['fieldOne' => [['trim', 'a'], ['floatval']]], ['fieldOne' => 'a3.14']);
182
        $this->assertSame([true, ['fieldOne' => 3.14], null, []], $result);
183
    }
184
185
    /**
186
     * @test
187
     * @covers ::filter
188
     */
189 View Code Duplication
    public function chainFail()
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...
190
    {
191
        $result = Filterer::filter(
192
            ['fieldOne' => [['trim'], ['\TraderInteractive\FiltererTest::failingFilter']]],
193
            ['fieldOne' => 'the value']
194
        );
195
        $this->assertSame(
196
            [false, null, "Field 'fieldOne' with value 'the value' failed filtering, message 'i failed'", []],
197
            $result
198
        );
199
    }
200
201
    /**
202
     * @test
203
     * @covers ::filter
204
     */
205
    public function multiInputPass()
206
    {
207
        $result = Filterer::filter(
208
            ['fieldOne' => [['trim']], 'fieldTwo' => [['strtoupper']]],
209
            ['fieldOne' => ' value', 'fieldTwo' => 'bob']
210
        );
211
        $this->assertSame([true, ['fieldOne' => 'value', 'fieldTwo' => 'BOB'], null, []], $result);
212
    }
213
214
    /**
215
     * @test
216
     * @covers ::filter
217
     */
218
    public function multiInputFail()
219
    {
220
        $result = Filterer::filter(
221
            [
222
                'fieldOne' => [['\TraderInteractive\FiltererTest::failingFilter']],
223
                'fieldTwo' => [['\TraderInteractive\FiltererTest::failingFilter']],
224
            ],
225
            ['fieldOne' => 'value one', 'fieldTwo' => 'value two']
226
        );
227
        $expectedMessage = "Field 'fieldOne' with value 'value one' failed filtering, message 'i failed'\n";
228
        $expectedMessage .= "Field 'fieldTwo' with value 'value two' failed filtering, message 'i failed'";
229
        $this->assertSame([false, null, $expectedMessage, []], $result);
230
    }
231
232
    /**
233
     * @test
234
     * @covers ::filter
235
     */
236
    public function emptyFilter()
237
    {
238
        $result = Filterer::filter(['fieldOne' => [[]]], ['fieldOne' => 0]);
239
        $this->assertSame([true, ['fieldOne' => 0], null, []], $result);
240
    }
241
242
    /**
243
     * @test
244
     * @covers ::filter
245
     */
246
    public function unknownsAllowed()
247
    {
248
        $result = Filterer::filter([], ['fieldTwo' => 0], ['allowUnknowns' => true]);
249
        $this->assertSame([true, [], null, ['fieldTwo' => 0]], $result);
250
    }
251
252
    /**
253
     * @test
254
     * @covers ::filter
255
     */
256
    public function unknownsNotAllowed()
257
    {
258
        $result = Filterer::filter([], ['fieldTwo' => 0]);
259
        $this->assertSame([false, null, "Field 'fieldTwo' with value '0' is unknown", ['fieldTwo' => 0]], $result);
260
    }
261
262
    /**
263
     * @test
264
     * @covers ::filter
265
     */
266
    public function objectFilter()
267
    {
268
        $result = Filterer::filter(['fieldOne' => [[[$this, 'passingFilter']]]], ['fieldOne' => 'foo']);
269
        $this->assertSame([true, ['fieldOne' => 'fooboo'], null, []], $result);
270
    }
271
272
    /**
273
     * @test
274
     * @covers ::filter
275
     * @expectedException Exception
276
     * @expectedExceptionMessage Function 'boo' for field 'foo' is not callable
277
     */
278
    public function notCallable()
279
    {
280
        Filterer::filter(['foo' => [['boo']]], ['foo' => 0]);
281
    }
282
283
    /**
284
     * @test
285
     * @covers ::filter
286
     * @expectedException InvalidArgumentException
287
     * @expectedExceptionMessage 'allowUnknowns' option was not a bool
288
     */
289
    public function allowUnknownsNotBool()
290
    {
291
        Filterer::filter([], [], ['allowUnknowns' => 1]);
292
    }
293
294
    /**
295
     * @test
296
     * @covers ::filter
297
     * @expectedException InvalidArgumentException
298
     * @expectedExceptionMessage 'defaultRequired' option was not a bool
299
     */
300
    public function defaultRequiredNotBool()
301
    {
302
        Filterer::filter([], [], ['defaultRequired' => 1]);
303
    }
304
305
    /**
306
     * @test
307
     * @covers ::filter
308
     * @expectedException InvalidArgumentException
309
     * @expectedExceptionMessage filters for field 'boo' was not a array
310
     */
311
    public function filtersNotArrayInLeftOverSpec()
312
    {
313
        Filterer::filter(['boo' => 1], []);
314
    }
315
316
    /**
317
     * @test
318
     * @covers ::filter
319
     * @expectedException InvalidArgumentException
320
     * @expectedExceptionMessage filters for field 'boo' was not a array
321
     */
322
    public function filtersNotArrayWithInput()
323
    {
324
        Filterer::filter(['boo' => 1], ['boo' => 'notUnderTest']);
325
    }
326
327
    /**
328
     * @test
329
     * @covers ::filter
330
     * @expectedException InvalidArgumentException
331
     * @expectedExceptionMessage filter for field 'boo' was not a array
332
     */
333
    public function filterNotArray()
334
    {
335
        Filterer::filter(['boo' => [1]], ['boo' => 'notUnderTest']);
336
    }
337
338
    /**
339
     * @test
340
     * @covers ::filter
341
     * @expectedException InvalidArgumentException
342
     * @expectedExceptionMessage 'required' for field 'boo' was not a bool
343
     */
344
    public function requiredNotBool()
345
    {
346
        Filterer::filter(['boo' => ['required' => 1]], []);
347
    }
348
349
    /**
350
     * @test
351
     * @covers ::registerAlias
352
     * @expectedException InvalidArgumentException
353
     * @expectedExceptionMessage $alias was not a string or int
354
     */
355
    public function registerAliasAliasNotString()
356
    {
357
        Filterer::registerAlias(true, 'strtolower');
0 ignored issues
show
Documentation introduced by
true is of type boolean, but the function expects a string|integer.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
358
    }
359
360
    /**
361
     * @test
362
     * @covers ::registerAlias
363
     * @expectedException Exception
364
     * @expectedExceptionMessage Alias 'upper' exists
365
     */
366
    public function registerExistingAliasOverwriteFalse()
367
    {
368
        Filterer::setFilterAliases([]);
369
        Filterer::registerAlias('upper', 'strtoupper');
370
        Filterer::registerAlias('upper', 'strtoupper', false);
371
    }
372
373
    /**
374
     * @test
375
     * @covers ::registerAlias
376
     */
377
    public function registerExistingAliasOverwriteTrue()
378
    {
379
        Filterer::setFilterAliases(['upper' => 'strtoupper', 'lower' => 'strtolower']);
380
        Filterer::registerAlias('upper', 'ucfirst', true);
381
        $this->assertSame(['upper' => 'ucfirst', 'lower' => 'strtolower'], Filterer::getFilterAliases());
382
    }
383
384
    public static function failingFilter()
385
    {
386
        throw new Exception('i failed');
387
    }
388
389
    public static function passingFilter($value)
390
    {
391
        return $value . 'boo';
392
    }
393
394
    /**
395
     * Verify custom errors can be added to filter spec.
396
     *
397
     * @test
398
     * @covers ::filter
399
     *
400
     * @return void
401
     */
402 View Code Duplication
    public function filterWithCustomError()
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...
403
    {
404
        $result = Filterer::filter(
405
            [
406
                'fieldOne' => [
407
                    ['\TraderInteractive\FiltererTest::failingFilter'],
408
                    'error' => 'My custom error message'
409
                ],
410
            ],
411
            ['fieldOne' => 'valueOne']
412
        );
413
        $this->assertSame(
414
            [false, null, 'My custom error message', []],
415
            $result
416
        );
417
    }
418
419
    /**
420
     * Verify behavior of filter() when 'error' is not a string value.
421
     *
422
     * @test
423
     * @covers ::filter
424
     * @expectedException InvalidArgumentException
425
     * @expectedExceptionMessage error for field 'fieldOne' was not a non-empty string
426
     *
427
     * @return void
428
     */
429 View Code Duplication
    public function filterWithNonStringError()
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...
430
    {
431
        Filterer::filter(
432
            ['fieldOne' => [['strtoupper'], 'error' => new StdClass()]],
433
            ['fieldOne' => 'valueOne']
434
        );
435
    }
436
437
    /**
438
     * Verify behavior of filter() when 'error' is an empty string.
439
     *
440
     * @test
441
     * @covers ::filter
442
     * @expectedException InvalidArgumentException
443
     * @expectedExceptionMessage error for field 'fieldOne' was not a non-empty string
444
     *
445
     * @return void
446
     */
447 View Code Duplication
    public function filterWithEmptyStringError()
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...
448
    {
449
        Filterer::filter(
450
            ['fieldOne' => [['strtoupper'], 'error' => "\n   \t"]],
451
            ['fieldOne' => 'valueOne']
452
        );
453
    }
454
455
    /**
456
     * @test
457
     * @covers ::ofScalars
458
     */
459
    public function ofScalars()
460
    {
461
        $this->assertSame([1, 2], Filterer::ofScalars(['1', '2'], [['uint']]));
462
    }
463
464
    /**
465
     * @test
466
     * @covers ::ofScalars
467
     */
468
    public function ofScalarsChained()
469
    {
470
        $this->assertSame([3.3, 5.5], Filterer::ofScalars(['a3.3', 'a5.5'], [['trim', 'a'], ['floatval']]));
471
    }
472
473
    /**
474
     * @test
475
     * @covers ::ofScalars
476
     */
477
    public function ofScalarsWithMeaninglessKeys()
478
    {
479
        $this->assertSame(['key1' => 1, 'key2' => 2], Filterer::ofScalars(['key1' => '1', 'key2' => '2'], [['uint']]));
480
    }
481
482
    /**
483
     * @test
484
     * @covers ::ofScalars
485
     */
486 View Code Duplication
    public function ofScalarsFail()
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...
487
    {
488
        try {
489
            Filterer::ofScalars(['1', [], new \StdClass], [['string']]);
490
            $this->fail();
491
        } catch (Exception $e) {
492
            $expected = <<<TXT
493
Field '1' with value 'array (
494
)' failed filtering, message 'Value 'array (
495
)' is not a string'
496
Field '2' with value 'stdClass::__set_state(array(
497
))' failed filtering, message 'Value 'stdClass::__set_state(array(
498
))' is not a string'
499
TXT;
500
            $this->assertSame($expected, $e->getMessage());
501
        }
502
    }
503
504
    /**
505
     * @test
506
     * @covers ::ofArrays
507
     */
508
    public function ofArrays()
509
    {
510
        $expected = [['key' => 1], ['key' => 2]];
511
        $this->assertSame($expected, Filterer::ofArrays([['key' => '1'], ['key' => '2']], ['key' => [['uint']]]));
512
    }
513
514
    /**
515
     * @test
516
     * @covers ::ofArrays
517
     */
518
    public function ofArraysChained()
519
    {
520
        $expected = [['key' => 3.3], ['key' => 5.5]];
521
        $spec = ['key' => [['trim', 'a'], ['floatval']]];
522
        $this->assertSame($expected, Filterer::ofArrays([['key' => 'a3.3'], ['key' => 'a5.5']], $spec));
523
    }
524
525
    /**
526
     * @test
527
     * @covers ::ofArrays
528
     */
529 View Code Duplication
    public function ofArraysRequiredAndUnknown()
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...
530
    {
531
        try {
532
            Filterer::ofArrays([['key' => '1'], ['key2' => '2']], ['key' => ['required' => true, ['uint']]]);
533
            $this->fail();
534
        } catch (Exception $e) {
535
            $expected = "Field 'key' was required and not present\nField 'key2' with value '2' is unknown";
536
            $this->assertSame($expected, $e->getMessage());
537
        }
538
    }
539
540
    /**
541
     * @test
542
     * @covers ::ofArrays
543
     */
544
    public function ofArraysFail()
545
    {
546
        try {
547
            Filterer::ofArrays(
548
                [['key' => new \StdClass], ['key' => []], ['key' => null], 'key'],
549
                ['key' => [['string']]]
550
            );
551
            $this->fail();
552
        } catch (Exception $e) {
553
            $expected = <<<TXT
554
Field 'key' with value 'stdClass::__set_state(array(
555
))' failed filtering, message 'Value 'stdClass::__set_state(array(
556
))' is not a string'
557
Field 'key' with value 'array (
558
)' failed filtering, message 'Value 'array (
559
)' is not a string'
560
Field 'key' with value 'NULL' failed filtering, message 'Value failed filtering, \$allowNull is set to false'
561
Value at position '3' was not an array
562
TXT;
563
            $this->assertSame($expected, $e->getMessage());
564
        }
565
    }
566
567
    /**
568
     * @test
569
     * @covers ::ofArray
570
     */
571
    public function ofArray()
572
    {
573
        $expected = ['key1' => 1, 'key2' => 2];
574
        $spec = ['key1' => [['uint']], 'key2' => [['uint']]];
575
        $this->assertSame($expected, Filterer::ofArray(['key1' => '1', 'key2' => '2'], $spec));
576
    }
577
578
    /**
579
     * @test
580
     * @covers ::ofArray
581
     */
582
    public function ofArrayChained()
583
    {
584
        $expected = ['key' => 3.3];
585
        $spec = ['key' => [['trim', 'a'], ['floatval']]];
586
        $this->assertSame($expected, Filterer::ofArray(['key' => 'a3.3'], $spec));
587
    }
588
589
    /**
590
     * @test
591
     * @covers ::ofArray
592
     */
593
    public function ofArrayRequiredSuccess()
594
    {
595
        $expected = ['key2' => 2];
596
        $spec = ['key1' => [['uint']], 'key2' => ['required' => true, ['uint']]];
597
        $this->assertSame($expected, Filterer::ofArray(['key2' => '2'], $spec));
598
    }
599
600
    /**
601
     * @test
602
     * @covers ::ofArray
603
     */
604 View Code Duplication
    public function ofArrayRequiredFail()
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...
605
    {
606
        try {
607
            Filterer::ofArray(['key1' => '1'], ['key1' => [['uint']], 'key2' => ['required' => true, ['uint']]]);
608
            $this->fail();
609
        } catch (Exception $e) {
610
            $expected = "Field 'key2' was required and not present";
611
            $this->assertSame($expected, $e->getMessage());
612
        }
613
    }
614
615
    /**
616
     * @test
617
     * @covers ::ofArray
618
     */
619 View Code Duplication
    public function ofArrayUnknown()
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...
620
    {
621
        try {
622
            Filterer::ofArray(['key' => '1'], ['key2' => [['uint']]]);
623
            $this->fail();
624
        } catch (Exception $e) {
625
            $expected = "Field 'key' with value '1' is unknown";
626
            $this->assertSame($expected, $e->getMessage());
627
        }
628
    }
629
}
630