Completed
Pull Request — master (#76)
by Chad
02:55
created

FiltererTest::passingFilter()   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 1
1
<?php
2
3
namespace TraderInteractive;
4
5
use Exception;
6
use InvalidArgumentException;
7
use PHPUnit\Framework\TestCase;
8
use StdClass;
9
use TraderInteractive\Exceptions\FilterException;
10
11
/**
12
 * @coversDefaultClass \TraderInteractive\Filterer
13
 * @covers ::<private>
14
 */
15
final class FiltererTest extends TestCase
16
{
17
    public function setUp()
18
    {
19
        Filterer::setFilterAliases(Filterer::DEFAULT_FILTER_ALIASES);
20
    }
21
22
    /**
23
     * @test
24
     * @covers ::filter
25
     */
26
    public function requiredPass()
27
    {
28
        $result = Filterer::filter(['fieldOne' => ['required' => false]], []);
29
        $this->assertSame([true, [], null, []], $result);
30
    }
31
32
    /**
33
     * @test
34
     * @covers ::filter
35
     */
36
    public function requiredFail()
37
    {
38
        $result = Filterer::filter(['fieldOne' => ['required' => true]], []);
39
        $this->assertSame([false, null, "Field 'fieldOne' was required and not present", []], $result);
40
    }
41
42
    /**
43
     * @test
44
     * @covers ::filter
45
     */
46
    public function requiredWithADefaultWithoutInput()
47
    {
48
        $result = Filterer::filter(['fieldOne' => ['required' => true, 'default' => 'theDefault']], []);
49
        $this->assertSame([true, ['fieldOne' => 'theDefault'], null, []], $result);
50
    }
51
52
    /**
53
     * @test
54
     * @covers ::filter
55
     */
56
    public function requiredWithANullDefaultWithoutInput()
57
    {
58
        $result = Filterer::filter(['fieldOne' => ['required' => true, 'default' => null]], []);
59
        $this->assertSame([true, ['fieldOne' => null], null, []], $result);
60
    }
61
62
    /**
63
     * @test
64
     * @covers ::filter
65
     */
66 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...
67
    {
68
        $result = Filterer::filter(
69
            ['fieldOne' => ['required' => true, 'default' => 'theDefault']],
70
            ['fieldOne' => 'notTheDefault']
71
        );
72
        $this->assertSame([true, ['fieldOne' => 'notTheDefault'], null, []], $result);
73
    }
74
75
    /**
76
     * @test
77
     * @covers ::filter
78
     */
79
    public function notRequiredWithADefaultWithoutInput()
80
    {
81
        $result = Filterer::filter(['fieldOne' => ['default' => 'theDefault']], []);
82
        $this->assertSame([true, ['fieldOne' => 'theDefault'], null, []], $result);
83
    }
84
85
    /**
86
     * @test
87
     * @covers ::filter
88
     */
89
    public function notRequiredWithADefaultWithInput()
90
    {
91
        $result = Filterer::filter(['fieldOne' => ['default' => 'theDefault']], ['fieldOne' => 'notTheDefault']);
92
        $this->assertSame([true, ['fieldOne' => 'notTheDefault'], null, []], $result);
93
    }
94
95
    /**
96
     * @test
97
     * @covers ::filter
98
     */
99
    public function requiredDefaultPass()
100
    {
101
        $result = Filterer::filter(['fieldOne' => []], []);
102
        $this->assertSame([true, [], null, []], $result);
103
    }
104
105
    /**
106
     * @test
107
     * @covers ::filter
108
     */
109
    public function requiredDefaultFail()
110
    {
111
        $result = Filterer::filter(['fieldOne' => []], [], ['defaultRequired' => true]);
112
        $this->assertSame([false, null, "Field 'fieldOne' was required and not present", []], $result);
113
    }
114
115
    /**
116
     * @test
117
     * @covers ::filter
118
     */
119
    public function filterPass()
120
    {
121
        $result = Filterer::filter(['fieldOne' => [['floatval']]], ['fieldOne' => '3.14']);
122
        $this->assertSame([true, ['fieldOne' => 3.14], null, []], $result);
123
    }
124
125
    /**
126
     * @test
127
     * @covers ::filter
128
     */
129
    public function filterDefaultShortNamePass()
130
    {
131
        $result = Filterer::filter(['fieldOne' => [['float']]], ['fieldOne' => '3.14']);
132
        $this->assertSame([true, ['fieldOne' => 3.14], null, []], $result);
133
    }
134
135
    /**
136
     * @test
137
     * @covers ::filter
138
     * @covers ::setFilterAliases
139
     */
140
    public function filterCustomShortNamePass()
141
    {
142
        Filterer::setFilterAliases(['fval' => 'floatval']);
143
        $result = Filterer::filter(['fieldOne' => [['fval']]], ['fieldOne' => '3.14']);
144
        $this->assertSame([true, ['fieldOne' => 3.14], null, []], $result);
145
    }
146
147
    /**
148
     * @test
149
     * @covers ::filter
150
     * @covers ::setFilterAliases
151
     * @covers ::getFilterAliases
152
     */
153
    public function filterGetSetKnownFilters()
154
    {
155
        $knownFilters = ['lower' => 'strtolower', 'upper' => 'strtoupper'];
156
        Filterer::setFilterAliases($knownFilters);
157
        $this->assertSame($knownFilters, Filterer::getFilterAliases());
158
    }
159
160
    /**
161
     * @test
162
     * @covers ::filter
163
     */
164
    public function filterFail()
165
    {
166
        $result = Filterer::filter(
167
            ['fieldOne' => [['\TraderInteractive\FiltererTest::failingFilter']]],
168
            ['fieldOne' => 'valueOne']
169
        );
170
        $this->assertSame(
171
            [false, null, "Field 'fieldOne' with value 'valueOne' failed filtering, message 'i failed'", []],
172
            $result
173
        );
174
    }
175
176
    /**
177
     * @test
178
     * @covers ::filter
179
     */
180
    public function chainPass()
181
    {
182
        $result = Filterer::filter(['fieldOne' => [['trim', 'a'], ['floatval']]], ['fieldOne' => 'a3.14']);
183
        $this->assertSame([true, ['fieldOne' => 3.14], null, []], $result);
184
    }
185
186
    /**
187
     * @test
188
     * @covers ::filter
189
     */
190 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...
191
    {
192
        $result = Filterer::filter(
193
            ['fieldOne' => [['trim'], ['\TraderInteractive\FiltererTest::failingFilter']]],
194
            ['fieldOne' => 'the value']
195
        );
196
        $this->assertSame(
197
            [false, null, "Field 'fieldOne' with value 'the value' failed filtering, message 'i failed'", []],
198
            $result
199
        );
200
    }
201
202
    /**
203
     * @test
204
     * @covers ::filter
205
     */
206
    public function multiInputPass()
207
    {
208
        $result = Filterer::filter(
209
            ['fieldOne' => [['trim']], 'fieldTwo' => [['strtoupper']]],
210
            ['fieldOne' => ' value', 'fieldTwo' => 'bob']
211
        );
212
        $this->assertSame([true, ['fieldOne' => 'value', 'fieldTwo' => 'BOB'], null, []], $result);
213
    }
214
215
    /**
216
     * @test
217
     * @covers ::filter
218
     */
219
    public function multiInputFail()
220
    {
221
        $result = Filterer::filter(
222
            [
223
                'fieldOne' => [['\TraderInteractive\FiltererTest::failingFilter']],
224
                'fieldTwo' => [['\TraderInteractive\FiltererTest::failingFilter']],
225
            ],
226
            ['fieldOne' => 'value one', 'fieldTwo' => 'value two']
227
        );
228
        $expectedMessage = "Field 'fieldOne' with value 'value one' failed filtering, message 'i failed'\n";
229
        $expectedMessage .= "Field 'fieldTwo' with value 'value two' failed filtering, message 'i failed'";
230
        $this->assertSame([false, null, $expectedMessage, []], $result);
231
    }
232
233
    /**
234
     * @test
235
     * @covers ::filter
236
     */
237
    public function emptyFilter()
238
    {
239
        $result = Filterer::filter(['fieldOne' => [[]]], ['fieldOne' => 0]);
240
        $this->assertSame([true, ['fieldOne' => 0], null, []], $result);
241
    }
242
243
    /**
244
     * @test
245
     * @covers ::filter
246
     */
247
    public function unknownsAllowed()
248
    {
249
        $result = Filterer::filter([], ['fieldTwo' => 0], ['allowUnknowns' => true]);
250
        $this->assertSame([true, [], null, ['fieldTwo' => 0]], $result);
251
    }
252
253
    /**
254
     * @test
255
     * @covers ::filter
256
     */
257
    public function unknownsNotAllowed()
258
    {
259
        $result = Filterer::filter([], ['fieldTwo' => 0]);
260
        $this->assertSame([false, null, "Field 'fieldTwo' with value '0' is unknown", ['fieldTwo' => 0]], $result);
261
    }
262
263
    /**
264
     * @test
265
     * @covers ::filter
266
     */
267
    public function objectFilter()
268
    {
269
        $result = Filterer::filter(['fieldOne' => [[[$this, 'passingFilter']]]], ['fieldOne' => 'foo']);
270
        $this->assertSame([true, ['fieldOne' => 'fooboo'], null, []], $result);
271
    }
272
273
    /**
274
     * @test
275
     * @covers ::filter
276
     * @expectedException Exception
277
     * @expectedExceptionMessage Function 'boo' for field 'foo' is not callable
278
     */
279
    public function notCallable()
280
    {
281
        Filterer::filter(['foo' => [['boo']]], ['foo' => 0]);
282
    }
283
284
    /**
285
     * @test
286
     * @covers ::filter
287
     * @expectedException InvalidArgumentException
288
     * @expectedExceptionMessage 'allowUnknowns' option was not a bool
289
     */
290
    public function allowUnknownsNotBool()
291
    {
292
        Filterer::filter([], [], ['allowUnknowns' => 1]);
293
    }
294
295
    /**
296
     * @test
297
     * @covers ::filter
298
     * @expectedException InvalidArgumentException
299
     * @expectedExceptionMessage 'defaultRequired' option was not a bool
300
     */
301
    public function defaultRequiredNotBool()
302
    {
303
        Filterer::filter([], [], ['defaultRequired' => 1]);
304
    }
305
306
    /**
307
     * @test
308
     * @covers ::filter
309
     * @expectedException InvalidArgumentException
310
     * @expectedExceptionMessage filters for field 'boo' was not a array
311
     */
312
    public function filtersNotArrayInLeftOverSpec()
313
    {
314
        Filterer::filter(['boo' => 1], []);
315
    }
316
317
    /**
318
     * @test
319
     * @covers ::filter
320
     * @expectedException InvalidArgumentException
321
     * @expectedExceptionMessage filters for field 'boo' was not a array
322
     */
323
    public function filtersNotArrayWithInput()
324
    {
325
        Filterer::filter(['boo' => 1], ['boo' => 'notUnderTest']);
326
    }
327
328
    /**
329
     * @test
330
     * @covers ::filter
331
     * @expectedException InvalidArgumentException
332
     * @expectedExceptionMessage filter for field 'boo' was not a array
333
     */
334
    public function filterNotArray()
335
    {
336
        Filterer::filter(['boo' => [1]], ['boo' => 'notUnderTest']);
337
    }
338
339
    /**
340
     * @test
341
     * @covers ::filter
342
     * @expectedException InvalidArgumentException
343
     * @expectedExceptionMessage 'required' for field 'boo' was not a bool
344
     */
345
    public function requiredNotBool()
346
    {
347
        Filterer::filter(['boo' => ['required' => 1]], []);
348
    }
349
350
    /**
351
     * @test
352
     * @covers ::registerAlias
353
     * @expectedException InvalidArgumentException
354
     * @expectedExceptionMessage $alias was not a string or int
355
     */
356
    public function registerAliasAliasNotString()
357
    {
358
        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...
359
    }
360
361
    /**
362
     * @test
363
     * @covers ::registerAlias
364
     * @expectedException Exception
365
     * @expectedExceptionMessage Alias 'upper' exists
366
     */
367
    public function registerExistingAliasOverwriteFalse()
368
    {
369
        Filterer::setFilterAliases([]);
370
        Filterer::registerAlias('upper', 'strtoupper');
371
        Filterer::registerAlias('upper', 'strtoupper', false);
372
    }
373
374
    /**
375
     * @test
376
     * @covers ::registerAlias
377
     */
378
    public function registerExistingAliasOverwriteTrue()
379
    {
380
        Filterer::setFilterAliases(['upper' => 'strtoupper', 'lower' => 'strtolower']);
381
        Filterer::registerAlias('upper', 'ucfirst', true);
382
        $this->assertSame(['upper' => 'ucfirst', 'lower' => 'strtolower'], Filterer::getFilterAliases());
383
    }
384
385
    public static function failingFilter()
386
    {
387
        throw new Exception('i failed');
388
    }
389
390
    public static function passingFilter($value)
391
    {
392
        return $value . 'boo';
393
    }
394
395
    /**
396
     * Verify custom errors can be added to filter spec.
397
     *
398
     * @test
399
     * @covers ::filter
400
     *
401
     * @return void
402
     */
403 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...
404
    {
405
        $result = Filterer::filter(
406
            [
407
                'fieldOne' => [
408
                    ['\TraderInteractive\FiltererTest::failingFilter'],
409
                    'error' => 'My custom error message'
410
                ],
411
            ],
412
            ['fieldOne' => 'valueOne']
413
        );
414
        $this->assertSame(
415
            [false, null, 'My custom error message', []],
416
            $result
417
        );
418
    }
419
420
    /**
421
     * Verify behavior of filter() when 'error' is not a string value.
422
     *
423
     * @test
424
     * @covers ::filter
425
     * @expectedException InvalidArgumentException
426
     * @expectedExceptionMessage error for field 'fieldOne' was not a non-empty string
427
     *
428
     * @return void
429
     */
430 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...
431
    {
432
        Filterer::filter(
433
            ['fieldOne' => [['strtoupper'], 'error' => new StdClass()]],
434
            ['fieldOne' => 'valueOne']
435
        );
436
    }
437
438
    /**
439
     * Verify behavior of filter() when 'error' is an empty string.
440
     *
441
     * @test
442
     * @covers ::filter
443
     * @expectedException InvalidArgumentException
444
     * @expectedExceptionMessage error for field 'fieldOne' was not a non-empty string
445
     *
446
     * @return void
447
     */
448 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...
449
    {
450
        Filterer::filter(
451
            ['fieldOne' => [['strtoupper'], 'error' => "\n   \t"]],
452
            ['fieldOne' => 'valueOne']
453
        );
454
    }
455
456
    /**
457
     * @test
458
     * @covers ::ofScalars
459
     */
460
    public function ofScalars()
461
    {
462
        $this->assertSame([1, 2], Filterer::ofScalars(['1', '2'], [['uint']]));
463
    }
464
465
    /**
466
     * @test
467
     * @covers ::ofScalars
468
     */
469
    public function ofScalarsChained()
470
    {
471
        $this->assertSame([3.3, 5.5], Filterer::ofScalars(['a3.3', 'a5.5'], [['trim', 'a'], ['floatval']]));
472
    }
473
474
    /**
475
     * @test
476
     * @covers ::ofScalars
477
     */
478
    public function ofScalarsWithMeaninglessKeys()
479
    {
480
        $this->assertSame(['key1' => 1, 'key2' => 2], Filterer::ofScalars(['key1' => '1', 'key2' => '2'], [['uint']]));
481
    }
482
483
    /**
484
     * @test
485
     * @covers ::ofScalars
486
     */
487 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...
488
    {
489
        try {
490
            Filterer::ofScalars(['1', [], new \StdClass], [['string']]);
491
            $this->fail();
492
        } catch (FilterException $e) {
493
            $expected = <<<TXT
494
Field '1' with value 'array (
495
)' failed filtering, message 'Value 'array (
496
)' is not a string'
497
Field '2' with value 'stdClass::__set_state(array(
498
))' failed filtering, message 'Value 'stdClass::__set_state(array(
499
))' is not a string'
500
TXT;
501
            $this->assertSame($expected, $e->getMessage());
502
        }
503
    }
504
505
    /**
506
     * @test
507
     * @covers ::ofArrays
508
     */
509
    public function ofArrays()
510
    {
511
        $expected = [['key' => 1], ['key' => 2]];
512
        $this->assertSame($expected, Filterer::ofArrays([['key' => '1'], ['key' => '2']], ['key' => [['uint']]]));
513
    }
514
515
    /**
516
     * @test
517
     * @covers ::ofArrays
518
     */
519
    public function ofArraysChained()
520
    {
521
        $expected = [['key' => 3.3], ['key' => 5.5]];
522
        $spec = ['key' => [['trim', 'a'], ['floatval']]];
523
        $this->assertSame($expected, Filterer::ofArrays([['key' => 'a3.3'], ['key' => 'a5.5']], $spec));
524
    }
525
526
    /**
527
     * @test
528
     * @covers ::ofArrays
529
     */
530 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...
531
    {
532
        try {
533
            Filterer::ofArrays([['key' => '1'], ['key2' => '2']], ['key' => ['required' => true, ['uint']]]);
534
            $this->fail();
535
        } catch (Exception $e) {
536
            $expected = "Field 'key' was required and not present\nField 'key2' with value '2' is unknown";
537
            $this->assertSame($expected, $e->getMessage());
538
        }
539
    }
540
541
    /**
542
     * @test
543
     * @covers ::ofArrays
544
     */
545
    public function ofArraysFail()
546
    {
547
        try {
548
            Filterer::ofArrays(
549
                [['key' => new \StdClass], ['key' => []], ['key' => null], 'key'],
550
                ['key' => [['string']]]
551
            );
552
            $this->fail();
553
        } catch (FilterException $e) {
554
            $expected = <<<TXT
555
Field 'key' with value 'stdClass::__set_state(array(
556
))' failed filtering, message 'Value 'stdClass::__set_state(array(
557
))' is not a string'
558
Field 'key' with value 'array (
559
)' failed filtering, message 'Value 'array (
560
)' is not a string'
561
Field 'key' with value 'NULL' failed filtering, message 'Value failed filtering, \$allowNull is set to false'
562
Value at position '3' was not an array
563
TXT;
564
            $this->assertSame($expected, $e->getMessage());
565
        }
566
    }
567
568
    /**
569
     * @test
570
     * @covers ::ofArray
571
     */
572
    public function ofArray()
573
    {
574
        $expected = ['key1' => 1, 'key2' => 2];
575
        $spec = ['key1' => [['uint']], 'key2' => [['uint']]];
576
        $this->assertSame($expected, Filterer::ofArray(['key1' => '1', 'key2' => '2'], $spec));
577
    }
578
579
    /**
580
     * @test
581
     * @covers ::ofArray
582
     */
583
    public function ofArrayChained()
584
    {
585
        $expected = ['key' => 3.3];
586
        $spec = ['key' => [['trim', 'a'], ['floatval']]];
587
        $this->assertSame($expected, Filterer::ofArray(['key' => 'a3.3'], $spec));
588
    }
589
590
    /**
591
     * @test
592
     * @covers ::ofArray
593
     */
594
    public function ofArrayRequiredSuccess()
595
    {
596
        $expected = ['key2' => 2];
597
        $spec = ['key1' => [['uint']], 'key2' => ['required' => true, ['uint']]];
598
        $this->assertSame($expected, Filterer::ofArray(['key2' => '2'], $spec));
599
    }
600
601
    /**
602
     * @test
603
     * @covers ::ofArray
604
     */
605 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...
606
    {
607
        try {
608
            Filterer::ofArray(['key1' => '1'], ['key1' => [['uint']], 'key2' => ['required' => true, ['uint']]]);
609
            $this->fail();
610
        } catch (FilterException $e) {
611
            $expected = "Field 'key2' was required and not present";
612
            $this->assertSame($expected, $e->getMessage());
613
        }
614
    }
615
616
    /**
617
     * @test
618
     * @covers ::ofArray
619
     */
620 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...
621
    {
622
        try {
623
            Filterer::ofArray(['key' => '1'], ['key2' => [['uint']]]);
624
            $this->fail();
625
        } catch (FilterException $e) {
626
            $expected = "Field 'key' with value '1' is unknown";
627
            $this->assertSame($expected, $e->getMessage());
628
        }
629
    }
630
}
631