Completed
Push — master ( 408c7c...047bc0 )
by Chad
42s queued 30s
created

FiltererTest::executeThrowsOnError()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 18
rs 9.6666
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
        ];
382
    }
383
384
    /**
385
     * @test
386
     * @covers ::execute
387
     */
388
    public function executeThrowsOnError()
389
    {
390
        $exception = new RuntimeException('the error');
391
        $this->expectException(RuntimeException::class);
392
        $this->expectExceptionMessage($exception->getMessage());
393
        $filter = function () use ($exception) {
394
            throw $exception;
395
        };
396
397
        $specification = [
398
            'id' => [
399
                FilterOptions::THROW_ON_ERROR => true,
400
                [$filter],
401
            ],
402
        ];
403
        $filterer = new Filterer($specification);
404
        $filterer->execute(['id' => 1]);
405
    }
406
407
    /**
408
     * @test
409
     * @covers ::execute
410
     */
411
    public function executeValidatesThrowsOnError()
412
    {
413
        $this->expectException(InvalidArgumentException::class);
414
        $this->expectExceptionMessage(sprintf(Filterer::INVALID_THROW_ON_ERROR_VALUE_ERROR_FORMAT, 'id'));
415
        $specification = [
416
            'id' => [
417
                FilterOptions::THROW_ON_ERROR => 'abc',
418
                ['uint'],
419
            ],
420
        ];
421
        $filterer = new Filterer($specification);
422
        $filterer->execute(['id' => 1]);
423
    }
424
425
    /**
426
     * @test
427
     * @covers ::filter
428
     * @covers ::execute
429
     */
430
    public function filterReturnsResponseType()
431
    {
432
        $specification = ['id' => [['uint']]];
433
        $input = ['id' => 1];
434
        $options = ['responseType' => Filterer::RESPONSE_TYPE_FILTER];
435
436
        $result = Filterer::filter($specification, $input, $options);
437
438
        $this->assertInstanceOf(FilterResponse::class, $result);
439
        $this->assertSame(true, $result->success);
440
        $this->assertSame($input, $result->filteredValue);
441
        $this->assertSame([], $result->errors);
442
        $this->assertSame(null, $result->errorMessage);
443
        $this->assertSame([], $result->unknowns);
444
    }
445
446
    /**
447
     * @test
448
     * @covers ::filter
449
     * @covers ::execute
450
     */
451
    public function filterReturnsResponseTypeWithErrors()
452
    {
453
        $specification = ['name' => [['string']]];
454
        $input = ['name' => 'foo', 'id' => 1];
455
        $options = ['responseType' => Filterer::RESPONSE_TYPE_FILTER];
456
457
        $result = Filterer::filter($specification, $input, $options);
458
459
        $this->assertInstanceOf(FilterResponse::class, $result);
460
        $this->assertSame(false, $result->success);
461
        $this->assertSame(['name' => 'foo'], $result->filteredValue);
462
        $this->assertSame(['id' => "Field 'id' with value '1' is unknown"], $result->errors);
463
        $this->assertSame("Field 'id' with value '1' is unknown", $result->errorMessage);
464
        $this->assertSame(['id' => 1], $result->unknowns);
465
    }
466
467
    /**
468
     * @test
469
     * @covers ::filter
470
     * @covers ::execute
471
     * @covers ::setFilterAliases
472
     */
473
    public function filterCustomShortNamePass()
474
    {
475
        Filterer::setFilterAliases(['fval' => 'floatval']);
476
        $result = Filterer::filter(['fieldOne' => [['fval']]], ['fieldOne' => '3.14']);
477
        $this->assertSame([true, ['fieldOne' => 3.14], null, []], $result);
478
    }
479
480
    /**
481
     * @test
482
     * @covers ::filter
483
     * @covers ::execute
484
     * @covers ::setFilterAliases
485
     * @covers ::getFilterAliases
486
     */
487
    public function filterGetSetKnownFilters()
488
    {
489
        $knownFilters = ['lower' => 'strtolower', 'upper' => 'strtoupper'];
490
        Filterer::setFilterAliases($knownFilters);
491
        $this->assertSame($knownFilters, Filterer::getFilterAliases());
492
    }
493
494
    /**
495
     * @test
496
     * @covers ::setFilterAliases
497
     */
498
    public function setBadFilterAliases()
499
    {
500
        $originalAliases = Filterer::getFilterAliases();
501
502
        $actualThrowable = null;
503
        try {
504
            Filterer::setFilterAliases(['foo' => 'not callable']);
505
        } catch (Throwable $throwable) {
506
            $actualThrowable = $throwable;
507
        }
508
509
        $this->assertSame($originalAliases, Filterer::getFilterAliases());
510
        $this->assertInstanceOf(TypeError::class, $actualThrowable);
511
    }
512
513
    /**
514
     * @test
515
     * @covers ::filter
516
     * @covers ::execute
517
     * @expectedException Exception
518
     * @expectedExceptionMessage Function 'boo' for field 'foo' is not callable
519
     */
520
    public function notCallable()
521
    {
522
        Filterer::filter(['foo' => [['boo']]], ['foo' => 0]);
523
    }
524
525
    /**
526
     * @test
527
     * @covers ::filter
528
     * @covers ::execute
529
     * @expectedException InvalidArgumentException
530
     * @expectedExceptionMessage 'allowUnknowns' option was not a bool
531
     */
532
    public function allowUnknownsNotBool()
533
    {
534
        Filterer::filter([], [], ['allowUnknowns' => 1]);
535
    }
536
537
    /**
538
     * @test
539
     * @covers ::filter
540
     * @covers ::execute
541
     * @expectedException InvalidArgumentException
542
     * @expectedExceptionMessage 'defaultRequired' option was not a bool
543
     */
544
    public function defaultRequiredNotBool()
545
    {
546
        Filterer::filter([], [], ['defaultRequired' => 1]);
547
    }
548
549
    /**
550
     * @test
551
     * @covers ::filter
552
     * @covers ::execute
553
     */
554
    public function filterThrowsExceptionOnInvalidResponseType()
555
    {
556
        $this->expectException(InvalidArgumentException::class);
557
        $this->expectExceptionMessage("'responseType' was not a recognized value");
558
559
        Filterer::filter([], [], ['responseType' => 'invalid']);
560
    }
561
562
    /**
563
     * @test
564
     * @covers ::filter
565
     * @covers ::execute
566
     * @expectedException InvalidArgumentException
567
     * @expectedExceptionMessage filters for field 'boo' was not a array
568
     */
569
    public function filtersNotArrayInLeftOverSpec()
570
    {
571
        Filterer::filter(['boo' => 1], []);
572
    }
573
574
    /**
575
     * @test
576
     * @covers ::filter
577
     * @covers ::execute
578
     * @expectedException InvalidArgumentException
579
     * @expectedExceptionMessage filters for field 'boo' was not a array
580
     */
581
    public function filtersNotArrayWithInput()
582
    {
583
        Filterer::filter(['boo' => 1], ['boo' => 'notUnderTest']);
584
    }
585
586
    /**
587
     * @test
588
     * @covers ::filter
589
     * @covers ::execute
590
     * @expectedException InvalidArgumentException
591
     * @expectedExceptionMessage filter for field 'boo' was not a array
592
     */
593
    public function filterNotArray()
594
    {
595
        Filterer::filter(['boo' => [1]], ['boo' => 'notUnderTest']);
596
    }
597
598
    /**
599
     * @test
600
     * @covers ::filter
601
     * @covers ::execute
602
     * @expectedException InvalidArgumentException
603
     * @expectedExceptionMessage 'required' for field 'boo' was not a bool
604
     */
605
    public function requiredNotBool()
606
    {
607
        Filterer::filter(['boo' => ['required' => 1]], []);
608
    }
609
610
    /**
611
     * @test
612
     * @covers ::registerAlias
613
     * @expectedException InvalidArgumentException
614
     * @expectedExceptionMessage $alias was not a string or int
615
     */
616
    public function registerAliasAliasNotString()
617
    {
618
        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...
619
    }
620
621
    /**
622
     * @test
623
     * @covers ::registerAlias
624
     * @expectedException Exception
625
     * @expectedExceptionMessage Alias 'upper' exists
626
     */
627
    public function registerExistingAliasOverwriteFalse()
628
    {
629
        Filterer::setFilterAliases([]);
630
        Filterer::registerAlias('upper', 'strtoupper');
631
        Filterer::registerAlias('upper', 'strtoupper', false);
632
    }
633
634
    /**
635
     * @test
636
     * @covers ::registerAlias
637
     */
638
    public function registerExistingAliasOverwriteTrue()
639
    {
640
        Filterer::setFilterAliases(['upper' => 'strtoupper', 'lower' => 'strtolower']);
641
        Filterer::registerAlias('upper', 'ucfirst', true);
642
        $this->assertSame(['upper' => 'ucfirst', 'lower' => 'strtolower'], Filterer::getFilterAliases());
643
    }
644
645
    public static function failingFilter()
646
    {
647
        throw new Exception('i failed');
648
    }
649
650
    public static function passingFilter($value)
651
    {
652
        return $value . 'boo';
653
    }
654
655
    /**
656
     * Verify behavior of filter() when 'error' is not a string value.
657
     *
658
     * @test
659
     * @covers ::filter
660
     * @covers ::execute
661
     * @expectedException InvalidArgumentException
662
     * @expectedExceptionMessage error for field 'fieldOne' was not a non-empty string
663
     *
664
     * @return void
665
     */
666 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...
667
    {
668
        Filterer::filter(
669
            ['fieldOne' => [['strtoupper'], 'error' => new stdClass()]],
670
            ['fieldOne' => 'valueOne']
671
        );
672
    }
673
674
    /**
675
     * Verify behavior of filter() when 'error' is an empty string.
676
     *
677
     * @test
678
     * @covers ::filter
679
     * @covers ::execute
680
     * @expectedException InvalidArgumentException
681
     * @expectedExceptionMessage error for field 'fieldOne' was not a non-empty string
682
     *
683
     * @return void
684
     */
685 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...
686
    {
687
        Filterer::filter(
688
            ['fieldOne' => [['strtoupper'], 'error' => "\n   \t"]],
689
            ['fieldOne' => 'valueOne']
690
        );
691
    }
692
693
    /**
694
     * @test
695
     * @covers ::ofScalars
696
     */
697
    public function ofScalars()
698
    {
699
        $this->assertSame([1, 2], Filterer::ofScalars(['1', '2'], [['uint']]));
700
    }
701
702
    /**
703
     * @test
704
     * @covers ::ofScalars
705
     */
706
    public function ofScalarsChained()
707
    {
708
        $this->assertSame([3.3, 5.5], Filterer::ofScalars(['a3.3', 'a5.5'], [['trim', 'a'], ['floatval']]));
709
    }
710
711
    /**
712
     * @test
713
     * @covers ::ofScalars
714
     */
715
    public function ofScalarsWithMeaninglessKeys()
716
    {
717
        $this->assertSame(['key1' => 1, 'key2' => 2], Filterer::ofScalars(['key1' => '1', 'key2' => '2'], [['uint']]));
718
    }
719
720
    /**
721
     * @test
722
     * @covers ::ofScalars
723
     */
724 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...
725
    {
726
        try {
727
            Filterer::ofScalars(['1', [], new stdClass], [['string']]);
728
            $this->fail();
729
        } catch (FilterException $e) {
730
            $expected = <<<TXT
731
Field '1' with value 'array (
732
)' failed filtering, message 'Value 'array (
733
)' is not a string'
734
Field '2' with value 'stdClass::__set_state(array(
735
))' failed filtering, message 'Value 'stdClass::__set_state(array(
736
))' is not a string'
737
TXT;
738
            $this->assertSame($expected, $e->getMessage());
739
        }
740
    }
741
742
    /**
743
     * @test
744
     * @covers ::ofArrays
745
     */
746
    public function ofArrays()
747
    {
748
        $expected = [['key' => 1], ['key' => 2]];
749
        $this->assertSame($expected, Filterer::ofArrays([['key' => '1'], ['key' => '2']], ['key' => [['uint']]]));
750
    }
751
752
    /**
753
     * @test
754
     * @covers ::ofArrays
755
     */
756
    public function ofArraysChained()
757
    {
758
        $expected = [['key' => 3.3], ['key' => 5.5]];
759
        $spec = ['key' => [['trim', 'a'], ['floatval']]];
760
        $this->assertSame($expected, Filterer::ofArrays([['key' => 'a3.3'], ['key' => 'a5.5']], $spec));
761
    }
762
763
    /**
764
     * @test
765
     * @covers ::ofArrays
766
     */
767 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...
768
    {
769
        try {
770
            Filterer::ofArrays([['key' => '1'], ['key2' => '2']], ['key' => ['required' => true, ['uint']]]);
771
            $this->fail();
772
        } catch (Exception $e) {
773
            $expected = "Field 'key' was required and not present\nField 'key2' with value '2' is unknown";
774
            $this->assertSame($expected, $e->getMessage());
775
        }
776
    }
777
778
    /**
779
     * @test
780
     * @covers ::ofArrays
781
     */
782
    public function ofArraysFail()
783
    {
784
        try {
785
            Filterer::ofArrays(
786
                [['key' => new stdClass], ['key' => []], ['key' => null], 'key'],
787
                ['key' => [['string']]]
788
            );
789
            $this->fail();
790
        } catch (FilterException $e) {
791
            $expected = <<<TXT
792
Field 'key' with value 'stdClass::__set_state(array(
793
))' failed filtering, message 'Value 'stdClass::__set_state(array(
794
))' is not a string'
795
Field 'key' with value 'array (
796
)' failed filtering, message 'Value 'array (
797
)' is not a string'
798
Field 'key' with value 'NULL' failed filtering, message 'Value failed filtering, \$allowNull is set to false'
799
Value at position '3' was not an array
800
TXT;
801
            $this->assertSame($expected, $e->getMessage());
802
        }
803
    }
804
805
    /**
806
     * @test
807
     * @covers ::ofArray
808
     */
809
    public function ofArray()
810
    {
811
        $expected = ['key1' => 1, 'key2' => 2];
812
        $spec = ['key1' => [['uint']], 'key2' => [['uint']]];
813
        $this->assertSame($expected, Filterer::ofArray(['key1' => '1', 'key2' => '2'], $spec));
814
    }
815
816
    /**
817
     * @test
818
     * @covers ::ofArray
819
     */
820
    public function ofArrayChained()
821
    {
822
        $expected = ['key' => 3.3];
823
        $spec = ['key' => [['trim', 'a'], ['floatval']]];
824
        $this->assertSame($expected, Filterer::ofArray(['key' => 'a3.3'], $spec));
825
    }
826
827
    /**
828
     * @test
829
     * @covers ::ofArray
830
     */
831
    public function ofArrayRequiredSuccess()
832
    {
833
        $expected = ['key2' => 2];
834
        $spec = ['key1' => [['uint']], 'key2' => ['required' => true, ['uint']]];
835
        $this->assertSame($expected, Filterer::ofArray(['key2' => '2'], $spec));
836
    }
837
838
    /**
839
     * @test
840
     * @covers ::ofArray
841
     */
842 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...
843
    {
844
        try {
845
            Filterer::ofArray(['key1' => '1'], ['key1' => [['uint']], 'key2' => ['required' => true, ['uint']]]);
846
            $this->fail();
847
        } catch (FilterException $e) {
848
            $expected = "Field 'key2' was required and not present";
849
            $this->assertSame($expected, $e->getMessage());
850
        }
851
    }
852
853
    /**
854
     * @test
855
     * @covers ::ofArray
856
     */
857 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...
858
    {
859
        try {
860
            Filterer::ofArray(['key' => '1'], ['key2' => [['uint']]]);
861
            $this->fail();
862
        } catch (FilterException $e) {
863
            $expected = "Field 'key' with value '1' is unknown";
864
            $this->assertSame($expected, $e->getMessage());
865
        }
866
    }
867
868
    /**
869
     * @test
870
     * @covers ::getAliases
871
     */
872
    public function getAliases()
873
    {
874
        $expected = ['some' => 'alias'];
875
876
        $filterer = new Filterer([], [], $expected);
877
        $actual = $filterer->getAliases();
878
879
        $this->assertSame($expected, $actual);
880
    }
881
882
    /**
883
     * @test
884
     * @covers ::getAliases
885
     */
886
    public function getAliasesReturnsStaticValueIfNull()
887
    {
888
        $filterer = new Filterer([]);
889
        $actual = $filterer->getAliases();
890
891
        $this->assertSame(Filterer::getFilterAliases(), $actual);
892
    }
893
894
    /**
895
     * @test
896
     * @covers ::getSpecification
897
     */
898
    public function getSpecification()
899
    {
900
        $expected = ['some' => 'specification'];
901
902
        $filterer = new Filterer($expected);
903
        $actual = $filterer->getSpecification();
904
905
        $this->assertSame($expected, $actual);
906
    }
907
908
    /**
909
     * @test
910
     * @covers ::withAliases
911
     */
912 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...
913
    {
914
        $expected = ['foo' => 'bar'];
915
916
        $filterer = new Filterer([]);
917
        $filtererCopy = $filterer->withAliases($expected);
918
919
        $this->assertNotSame($filterer, $filtererCopy);
920
        $this->assertSame($expected, $filtererCopy->getAliases());
921
    }
922
923
    /**
924
     * @test
925
     * @covers ::withSpecification
926
     */
927 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...
928
    {
929
        $expected = ['foo' => 'bar'];
930
931
        $filterer = new Filterer([]);
932
        $filtererCopy = $filterer->withSpecification($expected);
933
934
        $this->assertNotSame($filterer, $filtererCopy);
935
        $this->assertSame($expected, $filtererCopy->getSpecification());
936
    }
937
}
938