Completed
Push — master ( 047bc0...3590bf )
by Chad
15s queued 11s
created

FiltererTest::executeValidatesReturnOnNull()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 15

Duplication

Lines 15
Ratio 100 %

Importance

Changes 0
Metric Value
dl 15
loc 15
rs 9.7666
c 0
b 0
f 0
cc 1
nc 1
nop 0
1
<?php
2
3
namespace TraderInteractive;
4
5
use Exception;
6
use InvalidArgumentException;
7
use PHPUnit\Framework\TestCase;
8
use RuntimeException;
9
use stdClass;
10
use Throwable;
11
use TraderInteractive\Exceptions\FilterException;
12
use TypeError;
13
14
/**
15
 * @coversDefaultClass \TraderInteractive\Filterer
16
 * @covers ::__construct
17
 * @covers ::<private>
18
 */
19
final class FiltererTest extends TestCase
20
{
21
    public function setUp()
22
    {
23
        Filterer::setFilterAliases(Filterer::DEFAULT_FILTER_ALIASES);
24
    }
25
26
    /**
27
     * @test
28
     * @covers ::filter
29
     * @covers ::execute
30
     * @dataProvider provideValidFilterData
31
     *
32
     * @param array $spec  The filter specification to be use.
33
     * @param array $input The input to be filtered.
34
     * @param array $options The filterer options
35
     * @param array $expectedResult The expected filterer result.
36
     */
37
    public function filter(array $spec, array $input, array $options, array $expectedResult)
38
    {
39
        $this->assertSame($expectedResult, Filterer::filter($spec, $input, $options));
40
    }
41
42
    public function provideValidFilterData() : array
43
    {
44
        return [
45
            'not required field not present' => [
46
                'spec' => ['fieldOne' => ['required' => false]],
47
                'input' => [],
48
                'options' => [],
49
                'result' => [true, [], null, []],
50
            ],
51
            'Required With A Default Without Input' => [
52
                'spec' => ['fieldOne' => ['required' => true, 'default' => 'theDefault']],
53
                'input' => [],
54
                'options' => [],
55
                'result' => [true, ['fieldOne' => 'theDefault'], null, []],
56
            ],
57
            'Required With A Null Default Without Input' => [
58
                'spec' => ['fieldOne' => ['required' => true, 'default' => null]],
59
                'input' => [],
60
                'options' => [],
61
                'result' => [true, ['fieldOne' => null], null, []],
62
            ],
63
            'Required With A Default With Input' => [
64
                'spec' => ['fieldOne' => ['required' => true, 'default' => 'theDefault']],
65
                'input' => ['fieldOne' => 'notTheDefault'],
66
                'options' => [],
67
                'result' => [true, ['fieldOne' => 'notTheDefault'], null, []],
68
            ],
69
            'Not Required With A Default Without Input' => [
70
                'spec' => ['fieldOne' => ['default' => 'theDefault']],
71
                'input' => [],
72
                'options' => [],
73
                'result' => [true, ['fieldOne' => 'theDefault'], null, []],
74
            ],
75
            'Not Required With A Default With Input' => [
76
                'spec' => ['fieldOne' => ['default' => 'theDefault']],
77
                'input' => ['fieldOne' => 'notTheDefault'],
78
                'options' => [],
79
                'result' => [true, ['fieldOne' => 'notTheDefault'], null, []],
80
            ],
81
            'Required Fail' => [
82
                'spec' => ['fieldOne' => ['required' => true]],
83
                'input' => [],
84
                'options' => [],
85
                'result' => [false, null, "Field 'fieldOne' was required and not present", []],
86
            ],
87
            'Required Default Pass' => [
88
                'spec' => ['fieldOne' => []],
89
                'input' => [],
90
                'options' => [],
91
                'result' => [true, [], null, []],
92
            ],
93
            'requiredDefaultFail' => [
94
                'spec' => ['fieldOne' => []],
95
                'input' => [],
96
                'options' => ['defaultRequired' => true],
97
                'result' => [false, null, "Field 'fieldOne' was required and not present", []],
98
            ],
99
            'filterPass' => [
100
                'spec' => ['fieldOne' => [['floatval']]],
101
                'input' => ['fieldOne' => '3.14'],
102
                'options' => [],
103
                'result' => [true, ['fieldOne' => 3.14], null, []],
104
            ],
105
            'filterDefaultShortNamePass' => [
106
                'spec' => ['fieldOne' => [['float']]],
107
                'input' => ['fieldOne' => '3.14'],
108
                'options' => [],
109
                'result' => [true, ['fieldOne' => 3.14], null, []],
110
            ],
111
            'filterFail' => [
112
                'spec' => ['fieldOne' => [['\TraderInteractive\FiltererTest::failingFilter']]],
113
                'input' => ['fieldOne' => 'valueOne'],
114
                'options' => [],
115
                'result' => [
116
                    false,
117
                    null,
118
                    "Field 'fieldOne' with value 'valueOne' failed filtering, message 'i failed'",
119
                    [],
120
                ],
121
            ],
122
            'chainPass' => [
123
                'spec' => ['fieldOne' => [['trim', 'a'], ['floatval']]],
124
                'input' => ['fieldOne' => 'a3.14'],
125
                'options' => [],
126
                'result' => [true, ['fieldOne' => 3.14], null, []],
127
            ],
128
            'chainFail' => [
129
                'spec' => ['fieldOne' => [['trim'], ['\TraderInteractive\FiltererTest::failingFilter']]],
130
                'input' => ['fieldOne' => 'the value'],
131
                'options' => [],
132
                'result' => [
133
                    false,
134
                    null,
135
                    "Field 'fieldOne' with value 'the value' failed filtering, message 'i failed'",
136
                    [],
137
                ],
138
            ],
139
            'multiInputPass' => [
140
                'spec' => ['fieldOne' => [['trim']], 'fieldTwo' => [['strtoupper']]],
141
                'input' => ['fieldOne' => ' value', 'fieldTwo' => 'bob'],
142
                'options' => [],
143
                'result' => [true, ['fieldOne' => 'value', 'fieldTwo' => 'BOB'], null, []],
144
            ],
145
            'multiInputFail' => [
146
                'spec' => [
147
                    'fieldOne' => [['\TraderInteractive\FiltererTest::failingFilter']],
148
                    'fieldTwo' => [['\TraderInteractive\FiltererTest::failingFilter']],
149
                ],
150
                'input' => ['fieldOne' => 'value one', 'fieldTwo' => 'value two'],
151
                'options' => [],
152
                'result' => [
153
                    false,
154
                    null,
155
                    "Field 'fieldOne' with value 'value one' failed filtering, message 'i failed'\n"
156
                    . "Field 'fieldTwo' with value 'value two' failed filtering, message 'i failed'",
157
                    [],
158
                ],
159
            ],
160
            'emptyFilter' => [
161
                'spec' => ['fieldOne' => [[]]],
162
                'input' => ['fieldOne' => 0],
163
                'options' => [],
164
                'result' => [true, ['fieldOne' => 0], null, []],
165
            ],
166
            'unknownsAllowed' => [
167
                'spec' => [],
168
                'input'=> ['fieldTwo' => 0],
169
                'options' => ['allowUnknowns' => true],
170
                'result' => [true, [], null, ['fieldTwo' => 0]],
171
            ],
172
            'unknownsNotAllowed' => [
173
                'spec' => [],
174
                'input' => ['fieldTwo' => 0],
175
                'options' => [],
176
                'result' => [false, null, "Field 'fieldTwo' with value '0' is unknown", ['fieldTwo' => 0]],
177
            ],
178
            'objectFilter' => [
179
                'spec' => ['fieldOne' => [[[$this, 'passingFilter']]]],
180
                'input' => ['fieldOne' => 'foo'],
181
                'options' => [],
182
                'result' => [true, ['fieldOne' => 'fooboo'], null, []],
183
            ],
184
            'filterWithCustomError' => [
185
                'spec' => [
186
                    'fieldOne' => [
187
                        'error' => 'My custom error message',
188
                        ['\TraderInteractive\FiltererTest::failingFilter'],
189
                    ],
190
                ],
191
                'input' => ['fieldOne' => 'valueOne'],
192
                'options' => [],
193
                'result' => [false, null, 'My custom error message', []],
194
            ],
195
            'filterWithCustomErrorContainingValuePlaceholder' => [
196
                'spec' => [
197
                    'fieldOne' => [
198
                        'error' => "The value '{value}' is invalid.",
199
                        ['uint'],
200
                    ],
201
                ],
202
                'input' => ['fieldOne' => 'abc'],
203
                'options' => [],
204
                'result' => [false, null, "The value 'abc' is invalid.", []],
205
            ],
206
            'arrayizeAliasIsCalledProperly' => [
207
                'spec' => ['field' => [['arrayize']]],
208
                'input' => ['field' => 'a string value'],
209
                'options' => [],
210
                'result' => [true, ['field' => ['a string value']], null, []],
211
            ],
212
            'concatAliasIsCalledProperly' => [
213
                'spec' => ['field' => [['concat', '%', '%']]],
214
                'input' => ['field' => 'value'],
215
                'options' => [],
216
                'result' => [true, ['field' => '%value%'], null, []],
217
            ],
218
            'translate alias' => [
219
                'spec' => ['field' => [['translate', ['active' => 'A', 'inactive' => 'I']]]],
220
                'input' => ['field' => 'inactive'],
221
                'options' => [],
222
                'result' => [true, ['field' => 'I'], null, []],
223
            ],
224
            'redact alias' => [
225
                'spec' => ['field' => [['redact', ['other'], '*']]],
226
                'input' => ['field' => 'one or other'],
227
                'options' => [],
228
                'result' => [true, ['field' => 'one or *****'], null, []],
229
            ],
230
            'compress-string alias' => [
231
                'spec' => ['field' => [['compress-string']]],
232
                'input' => ['field' => ' a  string    with    extra spaces '],
233
                'options' => [],
234
                'result' => [true, ['field' => 'a string with extra spaces'], null, []],
235
            ],
236
            'compress-string alias include newlines' => [
237
                'spec' => ['field' => [['compress-string', true]]],
238
                'input' => ['field' => " a  string\n    with\nnewlines  and    extra spaces\n "],
239
                'options' => [],
240
                'result' => [true, ['field' => 'a string with newlines and extra spaces'], null, []],
241
            ],
242
            'conflicts with single' => [
243
                'spec' => [
244
                    'fieldOne' => [FilterOptions::CONFLICTS_WITH => 'fieldThree', ['string']],
245
                    'fieldTwo' => [['string']],
246
                    'fieldThree' => [FilterOptions::CONFLICTS_WITH => 'fieldOne', ['string']],
247
                ],
248
                'input' => [
249
                    'fieldOne' => 'abc',
250
                    'fieldTwo' => '123',
251
                    'fieldThree' => 'xyz',
252
                ],
253
                'options' => [],
254
                'result' => [
255
                    false,
256
                    null,
257
                    "Field 'fieldOne' cannot be given if field 'fieldThree' is present.\n"
258
                    . "Field 'fieldThree' cannot be given if field 'fieldOne' is present.",
259
                    [],
260
                ],
261
            ],
262
            'conflicts with multiple' => [
263
                'spec' => [
264
                    'fieldOne' => [FilterOptions::CONFLICTS_WITH => ['fieldTwo', 'fieldThree'], ['string']],
265
                    'fieldTwo' => [['string']],
266
                    'fieldThree' => [['string']],
267
                ],
268
                'input' => [
269
                    'fieldOne' => 'abc',
270
                    'fieldTwo' => '123',
271
                ],
272
                'options' => [],
273
                'result' => [
274
                    false,
275
                    null,
276
                    "Field 'fieldOne' cannot be given if field 'fieldTwo' is present.",
277
                    [],
278
                ],
279
            ],
280
            'conflicts with not present' => [
281
                'spec' => [
282
                    'fieldOne' => [FilterOptions::CONFLICTS_WITH => 'fieldThree', ['string']],
283
                    'fieldTwo' => [['string']],
284
                ],
285
                'input' => [
286
                    'fieldOne' => 'abc',
287
                    'fieldTwo' => '123',
288
                ],
289
                'options' => [],
290
                'result' => [
291
                    true,
292
                    [
293
                        'fieldOne' => 'abc',
294
                        'fieldTwo' => '123',
295
                    ],
296
                    null,
297
                    [],
298
                ],
299
            ],
300
            'uses' => [
301
                'spec' => [
302
                    'fieldOne' => [['uint']],
303
                    'fieldTwo' => [
304
                        ['uint'],
305
                        [
306
                            FilterOptions::USES => ['fieldOne'],
307
                            function (int $input, int $fieldOneValue) : int {
308
                                return $input * $fieldOneValue;
309
                            },
310
                        ],
311
                    ],
312
                ],
313
                'input' => [
314
                    'fieldOne' => '5',
315
                    'fieldTwo' => '2',
316
                ],
317
                'options' => [],
318
                'result' => [
319
                    true,
320
                    [
321
                        'fieldOne' => 5,
322
                        'fieldTwo' => 10,
323
                    ],
324
                    null,
325
                    [],
326
                ],
327
            ],
328
            'input order does not matter for uses' => [
329
                'spec' => [
330
                    'fieldOne' => [['uint']],
331
                    'fieldTwo' => [
332
                        ['uint'],
333
                        [
334
                            FilterOptions::USES => ['fieldOne'],
335
                            function (int $input, int $fieldOneValue) : int {
336
                                return $input * $fieldOneValue;
337
                            },
338
                        ],
339
                    ],
340
                ],
341
                'input' => [
342
                    'fieldTwo' => '2',
343
                    'fieldOne' => '5',
344
                ],
345
                'options' => [],
346
                'result' => [
347
                    true,
348
                    [
349
                        'fieldOne' => 5,
350
                        'fieldTwo' => 10,
351
                    ],
352
                    null,
353
                    [],
354
                ],
355
            ],
356
            'uses missing field' => [
357
                'spec' => [
358
                    'fieldOne' => [['uint']],
359
                    'fieldTwo' => [
360
                        ['uint'],
361
                        [
362
                            FilterOptions::USES => ['fieldOne'],
363
                            function (int $input, int $fieldOneValue) : int {
364
                                return $input * $fieldOneValue;
365
                            },
366
                        ],
367
                    ],
368
                ],
369
                'input' => [
370
                    'fieldTwo' => '2',
371
                ],
372
                'options' => [],
373
                'result' => [
374
                    false,
375
                    null,
376
                    "Field 'fieldTwo' with value '2' failed filtering, message 'fieldTwo uses fieldOne but fieldOne was"
377
                    . " not given.'",
378
                    [],
379
                ],
380
            ],
381
            'returnOnNull filter option' => [
382
                'spec' => ['field' => [FilterOptions::RETURN_ON_NULL => true, ['string', true], ['string']]],
383
                'input' => ['field' => null],
384
                'options' => [],
385
                'result' => [true, ['field' => null], null, []],
386
            ],
387
        ];
388
    }
389
390
    /**
391
     * @test
392
     * @covers ::execute
393
     */
394
    public function executeThrowsOnError()
395
    {
396
        $exception = new RuntimeException('the error');
397
        $this->expectException(RuntimeException::class);
398
        $this->expectExceptionMessage($exception->getMessage());
399
        $filter = function () use ($exception) {
400
            throw $exception;
401
        };
402
403
        $specification = [
404
            'id' => [
405
                FilterOptions::THROW_ON_ERROR => true,
406
                [$filter],
407
            ],
408
        ];
409
        $filterer = new Filterer($specification);
410
        $filterer->execute(['id' => 1]);
411
    }
412
413
    /**
414
     * @test
415
     * @covers ::execute
416
     */
417 View Code Duplication
    public function executeValidatesThrowsOnError()
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...
418
    {
419
        $this->expectException(InvalidArgumentException::class);
420
        $this->expectExceptionMessage(
421
            sprintf(Filterer::INVALID_BOOLEAN_FILTER_OPTION, FilterOptions::THROW_ON_ERROR, 'id')
422
        );
423
        $specification = [
424
            'id' => [
425
                FilterOptions::THROW_ON_ERROR => 'abc',
426
                ['uint'],
427
            ],
428
        ];
429
        $filterer = new Filterer($specification);
430
        $filterer->execute(['id' => 1]);
431
    }
432
433
    /**
434
     * @test
435
     * @covers ::execute
436
     */
437 View Code Duplication
    public function executeValidatesReturnOnNull()
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...
438
    {
439
        $this->expectException(InvalidArgumentException::class);
440
        $this->expectExceptionMessage(
441
            sprintf(Filterer::INVALID_BOOLEAN_FILTER_OPTION, FilterOptions::RETURN_ON_NULL, 'id')
442
        );
443
        $specification = [
444
            'id' => [
445
                FilterOptions::RETURN_ON_NULL => 'abc',
446
                ['uint'],
447
            ],
448
        ];
449
        $filterer = new Filterer($specification);
450
        $filterer->execute(['id' => 1]);
451
    }
452
453
    /**
454
     * @test
455
     * @covers ::filter
456
     * @covers ::execute
457
     */
458
    public function filterReturnsResponseType()
459
    {
460
        $specification = ['id' => [['uint']]];
461
        $input = ['id' => 1];
462
        $options = ['responseType' => Filterer::RESPONSE_TYPE_FILTER];
463
464
        $result = Filterer::filter($specification, $input, $options);
465
466
        $this->assertInstanceOf(FilterResponse::class, $result);
467
        $this->assertSame(true, $result->success);
468
        $this->assertSame($input, $result->filteredValue);
469
        $this->assertSame([], $result->errors);
470
        $this->assertSame(null, $result->errorMessage);
471
        $this->assertSame([], $result->unknowns);
472
    }
473
474
    /**
475
     * @test
476
     * @covers ::filter
477
     * @covers ::execute
478
     */
479
    public function filterReturnsResponseTypeWithErrors()
480
    {
481
        $specification = ['name' => [['string']]];
482
        $input = ['name' => 'foo', 'id' => 1];
483
        $options = ['responseType' => Filterer::RESPONSE_TYPE_FILTER];
484
485
        $result = Filterer::filter($specification, $input, $options);
486
487
        $this->assertInstanceOf(FilterResponse::class, $result);
488
        $this->assertSame(false, $result->success);
489
        $this->assertSame(['name' => 'foo'], $result->filteredValue);
490
        $this->assertSame(['id' => "Field 'id' with value '1' is unknown"], $result->errors);
491
        $this->assertSame("Field 'id' with value '1' is unknown", $result->errorMessage);
492
        $this->assertSame(['id' => 1], $result->unknowns);
493
    }
494
495
    /**
496
     * @test
497
     * @covers ::filter
498
     * @covers ::execute
499
     * @covers ::setFilterAliases
500
     */
501
    public function filterCustomShortNamePass()
502
    {
503
        Filterer::setFilterAliases(['fval' => 'floatval']);
504
        $result = Filterer::filter(['fieldOne' => [['fval']]], ['fieldOne' => '3.14']);
505
        $this->assertSame([true, ['fieldOne' => 3.14], null, []], $result);
506
    }
507
508
    /**
509
     * @test
510
     * @covers ::filter
511
     * @covers ::execute
512
     * @covers ::setFilterAliases
513
     * @covers ::getFilterAliases
514
     */
515
    public function filterGetSetKnownFilters()
516
    {
517
        $knownFilters = ['lower' => 'strtolower', 'upper' => 'strtoupper'];
518
        Filterer::setFilterAliases($knownFilters);
519
        $this->assertSame($knownFilters, Filterer::getFilterAliases());
520
    }
521
522
    /**
523
     * @test
524
     * @covers ::setFilterAliases
525
     */
526
    public function setBadFilterAliases()
527
    {
528
        $originalAliases = Filterer::getFilterAliases();
529
530
        $actualThrowable = null;
531
        try {
532
            Filterer::setFilterAliases(['foo' => 'not callable']);
533
        } catch (Throwable $throwable) {
534
            $actualThrowable = $throwable;
535
        }
536
537
        $this->assertSame($originalAliases, Filterer::getFilterAliases());
538
        $this->assertInstanceOf(TypeError::class, $actualThrowable);
539
    }
540
541
    /**
542
     * @test
543
     * @covers ::filter
544
     * @covers ::execute
545
     * @expectedException Exception
546
     * @expectedExceptionMessage Function 'boo' for field 'foo' is not callable
547
     */
548
    public function notCallable()
549
    {
550
        Filterer::filter(['foo' => [['boo']]], ['foo' => 0]);
551
    }
552
553
    /**
554
     * @test
555
     * @covers ::filter
556
     * @covers ::execute
557
     * @expectedException InvalidArgumentException
558
     * @expectedExceptionMessage 'allowUnknowns' option was not a bool
559
     */
560
    public function allowUnknownsNotBool()
561
    {
562
        Filterer::filter([], [], ['allowUnknowns' => 1]);
563
    }
564
565
    /**
566
     * @test
567
     * @covers ::filter
568
     * @covers ::execute
569
     * @expectedException InvalidArgumentException
570
     * @expectedExceptionMessage 'defaultRequired' option was not a bool
571
     */
572
    public function defaultRequiredNotBool()
573
    {
574
        Filterer::filter([], [], ['defaultRequired' => 1]);
575
    }
576
577
    /**
578
     * @test
579
     * @covers ::filter
580
     * @covers ::execute
581
     */
582
    public function filterThrowsExceptionOnInvalidResponseType()
583
    {
584
        $this->expectException(InvalidArgumentException::class);
585
        $this->expectExceptionMessage("'responseType' was not a recognized value");
586
587
        Filterer::filter([], [], ['responseType' => 'invalid']);
588
    }
589
590
    /**
591
     * @test
592
     * @covers ::filter
593
     * @covers ::execute
594
     * @expectedException InvalidArgumentException
595
     * @expectedExceptionMessage filters for field 'boo' was not a array
596
     */
597
    public function filtersNotArrayInLeftOverSpec()
598
    {
599
        Filterer::filter(['boo' => 1], []);
600
    }
601
602
    /**
603
     * @test
604
     * @covers ::filter
605
     * @covers ::execute
606
     * @expectedException InvalidArgumentException
607
     * @expectedExceptionMessage filters for field 'boo' was not a array
608
     */
609
    public function filtersNotArrayWithInput()
610
    {
611
        Filterer::filter(['boo' => 1], ['boo' => 'notUnderTest']);
612
    }
613
614
    /**
615
     * @test
616
     * @covers ::filter
617
     * @covers ::execute
618
     * @expectedException InvalidArgumentException
619
     * @expectedExceptionMessage filter for field 'boo' was not a array
620
     */
621
    public function filterNotArray()
622
    {
623
        Filterer::filter(['boo' => [1]], ['boo' => 'notUnderTest']);
624
    }
625
626
    /**
627
     * @test
628
     * @covers ::filter
629
     * @covers ::execute
630
     * @expectedException InvalidArgumentException
631
     * @expectedExceptionMessage 'required' for field 'boo' was not a bool
632
     */
633
    public function requiredNotBool()
634
    {
635
        Filterer::filter(['boo' => ['required' => 1]], []);
636
    }
637
638
    /**
639
     * @test
640
     * @covers ::registerAlias
641
     * @expectedException InvalidArgumentException
642
     * @expectedExceptionMessage $alias was not a string or int
643
     */
644
    public function registerAliasAliasNotString()
645
    {
646
        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...
647
    }
648
649
    /**
650
     * @test
651
     * @covers ::registerAlias
652
     * @expectedException Exception
653
     * @expectedExceptionMessage Alias 'upper' exists
654
     */
655
    public function registerExistingAliasOverwriteFalse()
656
    {
657
        Filterer::setFilterAliases([]);
658
        Filterer::registerAlias('upper', 'strtoupper');
659
        Filterer::registerAlias('upper', 'strtoupper', false);
660
    }
661
662
    /**
663
     * @test
664
     * @covers ::registerAlias
665
     */
666
    public function registerExistingAliasOverwriteTrue()
667
    {
668
        Filterer::setFilterAliases(['upper' => 'strtoupper', 'lower' => 'strtolower']);
669
        Filterer::registerAlias('upper', 'ucfirst', true);
670
        $this->assertSame(['upper' => 'ucfirst', 'lower' => 'strtolower'], Filterer::getFilterAliases());
671
    }
672
673
    public static function failingFilter()
674
    {
675
        throw new Exception('i failed');
676
    }
677
678
    public static function passingFilter($value)
679
    {
680
        return $value . 'boo';
681
    }
682
683
    /**
684
     * Verify behavior of filter() when 'error' is not a string value.
685
     *
686
     * @test
687
     * @covers ::filter
688
     * @covers ::execute
689
     * @expectedException InvalidArgumentException
690
     * @expectedExceptionMessage error for field 'fieldOne' was not a non-empty string
691
     *
692
     * @return void
693
     */
694 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...
695
    {
696
        Filterer::filter(
697
            ['fieldOne' => [['strtoupper'], 'error' => new stdClass()]],
698
            ['fieldOne' => 'valueOne']
699
        );
700
    }
701
702
    /**
703
     * Verify behavior of filter() when 'error' is an empty string.
704
     *
705
     * @test
706
     * @covers ::filter
707
     * @covers ::execute
708
     * @expectedException InvalidArgumentException
709
     * @expectedExceptionMessage error for field 'fieldOne' was not a non-empty string
710
     *
711
     * @return void
712
     */
713 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...
714
    {
715
        Filterer::filter(
716
            ['fieldOne' => [['strtoupper'], 'error' => "\n   \t"]],
717
            ['fieldOne' => 'valueOne']
718
        );
719
    }
720
721
    /**
722
     * @test
723
     * @covers ::ofScalars
724
     */
725
    public function ofScalars()
726
    {
727
        $this->assertSame([1, 2], Filterer::ofScalars(['1', '2'], [['uint']]));
728
    }
729
730
    /**
731
     * @test
732
     * @covers ::ofScalars
733
     */
734
    public function ofScalarsChained()
735
    {
736
        $this->assertSame([3.3, 5.5], Filterer::ofScalars(['a3.3', 'a5.5'], [['trim', 'a'], ['floatval']]));
737
    }
738
739
    /**
740
     * @test
741
     * @covers ::ofScalars
742
     */
743
    public function ofScalarsWithMeaninglessKeys()
744
    {
745
        $this->assertSame(['key1' => 1, 'key2' => 2], Filterer::ofScalars(['key1' => '1', 'key2' => '2'], [['uint']]));
746
    }
747
748
    /**
749
     * @test
750
     * @covers ::ofScalars
751
     */
752 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...
753
    {
754
        try {
755
            Filterer::ofScalars(['1', [], new stdClass], [['string']]);
756
            $this->fail();
757
        } catch (FilterException $e) {
758
            $expected = <<<TXT
759
Field '1' with value 'array (
760
)' failed filtering, message 'Value 'array (
761
)' is not a string'
762
Field '2' with value 'stdClass::__set_state(array(
763
))' failed filtering, message 'Value 'stdClass::__set_state(array(
764
))' is not a string'
765
TXT;
766
            $this->assertSame($expected, $e->getMessage());
767
        }
768
    }
769
770
    /**
771
     * @test
772
     * @covers ::ofArrays
773
     */
774
    public function ofArrays()
775
    {
776
        $expected = [['key' => 1], ['key' => 2]];
777
        $this->assertSame($expected, Filterer::ofArrays([['key' => '1'], ['key' => '2']], ['key' => [['uint']]]));
778
    }
779
780
    /**
781
     * @test
782
     * @covers ::ofArrays
783
     */
784
    public function ofArraysChained()
785
    {
786
        $expected = [['key' => 3.3], ['key' => 5.5]];
787
        $spec = ['key' => [['trim', 'a'], ['floatval']]];
788
        $this->assertSame($expected, Filterer::ofArrays([['key' => 'a3.3'], ['key' => 'a5.5']], $spec));
789
    }
790
791
    /**
792
     * @test
793
     * @covers ::ofArrays
794
     */
795 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...
796
    {
797
        try {
798
            Filterer::ofArrays([['key' => '1'], ['key2' => '2']], ['key' => ['required' => true, ['uint']]]);
799
            $this->fail();
800
        } catch (Exception $e) {
801
            $expected = "Field 'key' was required and not present\nField 'key2' with value '2' is unknown";
802
            $this->assertSame($expected, $e->getMessage());
803
        }
804
    }
805
806
    /**
807
     * @test
808
     * @covers ::ofArrays
809
     */
810
    public function ofArraysFail()
811
    {
812
        try {
813
            Filterer::ofArrays(
814
                [['key' => new stdClass], ['key' => []], ['key' => null], 'key'],
815
                ['key' => [['string']]]
816
            );
817
            $this->fail();
818
        } catch (FilterException $e) {
819
            $expected = <<<TXT
820
Field 'key' with value 'stdClass::__set_state(array(
821
))' failed filtering, message 'Value 'stdClass::__set_state(array(
822
))' is not a string'
823
Field 'key' with value 'array (
824
)' failed filtering, message 'Value 'array (
825
)' is not a string'
826
Field 'key' with value 'NULL' failed filtering, message 'Value failed filtering, \$allowNull is set to false'
827
Value at position '3' was not an array
828
TXT;
829
            $this->assertSame($expected, $e->getMessage());
830
        }
831
    }
832
833
    /**
834
     * @test
835
     * @covers ::ofArray
836
     */
837
    public function ofArray()
838
    {
839
        $expected = ['key1' => 1, 'key2' => 2];
840
        $spec = ['key1' => [['uint']], 'key2' => [['uint']]];
841
        $this->assertSame($expected, Filterer::ofArray(['key1' => '1', 'key2' => '2'], $spec));
842
    }
843
844
    /**
845
     * @test
846
     * @covers ::ofArray
847
     */
848
    public function ofArrayChained()
849
    {
850
        $expected = ['key' => 3.3];
851
        $spec = ['key' => [['trim', 'a'], ['floatval']]];
852
        $this->assertSame($expected, Filterer::ofArray(['key' => 'a3.3'], $spec));
853
    }
854
855
    /**
856
     * @test
857
     * @covers ::ofArray
858
     */
859
    public function ofArrayRequiredSuccess()
860
    {
861
        $expected = ['key2' => 2];
862
        $spec = ['key1' => [['uint']], 'key2' => ['required' => true, ['uint']]];
863
        $this->assertSame($expected, Filterer::ofArray(['key2' => '2'], $spec));
864
    }
865
866
    /**
867
     * @test
868
     * @covers ::ofArray
869
     */
870 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...
871
    {
872
        try {
873
            Filterer::ofArray(['key1' => '1'], ['key1' => [['uint']], 'key2' => ['required' => true, ['uint']]]);
874
            $this->fail();
875
        } catch (FilterException $e) {
876
            $expected = "Field 'key2' was required and not present";
877
            $this->assertSame($expected, $e->getMessage());
878
        }
879
    }
880
881
    /**
882
     * @test
883
     * @covers ::ofArray
884
     */
885 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...
886
    {
887
        try {
888
            Filterer::ofArray(['key' => '1'], ['key2' => [['uint']]]);
889
            $this->fail();
890
        } catch (FilterException $e) {
891
            $expected = "Field 'key' with value '1' is unknown";
892
            $this->assertSame($expected, $e->getMessage());
893
        }
894
    }
895
896
    /**
897
     * @test
898
     * @covers ::getAliases
899
     */
900
    public function getAliases()
901
    {
902
        $expected = ['some' => 'alias'];
903
904
        $filterer = new Filterer([], [], $expected);
905
        $actual = $filterer->getAliases();
906
907
        $this->assertSame($expected, $actual);
908
    }
909
910
    /**
911
     * @test
912
     * @covers ::getAliases
913
     */
914
    public function getAliasesReturnsStaticValueIfNull()
915
    {
916
        $filterer = new Filterer([]);
917
        $actual = $filterer->getAliases();
918
919
        $this->assertSame(Filterer::getFilterAliases(), $actual);
920
    }
921
922
    /**
923
     * @test
924
     * @covers ::getSpecification
925
     */
926
    public function getSpecification()
927
    {
928
        $expected = ['some' => 'specification'];
929
930
        $filterer = new Filterer($expected);
931
        $actual = $filterer->getSpecification();
932
933
        $this->assertSame($expected, $actual);
934
    }
935
936
    /**
937
     * @test
938
     * @covers ::withAliases
939
     */
940 View Code Duplication
    public function withAliases()
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...
941
    {
942
        $expected = ['foo' => 'bar'];
943
944
        $filterer = new Filterer([]);
945
        $filtererCopy = $filterer->withAliases($expected);
946
947
        $this->assertNotSame($filterer, $filtererCopy);
948
        $this->assertSame($expected, $filtererCopy->getAliases());
949
    }
950
951
    /**
952
     * @test
953
     * @covers ::withSpecification
954
     */
955 View Code Duplication
    public function withSpecification()
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...
956
    {
957
        $expected = ['foo' => 'bar'];
958
959
        $filterer = new Filterer([]);
960
        $filtererCopy = $filterer->withSpecification($expected);
961
962
        $this->assertNotSame($filterer, $filtererCopy);
963
        $this->assertSame($expected, $filtererCopy->getSpecification());
964
    }
965
}
966