Completed
Pull Request — master (#85)
by
unknown
01:18
created

FiltererTest::provideValidFilterData()   B

Complexity

Conditions 1
Paths 1

Size

Total Lines 190

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 190
rs 8
c 0
b 0
f 0
cc 1
nc 1
nop 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace TraderInteractive;
4
5
use Exception;
6
use InvalidArgumentException;
7
use MongoDB\BSON\Type;
8
use PHPUnit\Framework\TestCase;
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
        ];
231
    }
232
233
    /**
234
     * @test
235
     * @covers ::filter
236
     * @covers ::execute
237
     */
238
    public function filterReturnsResponseType()
239
    {
240
        $specification = ['id' => [['uint']]];
241
        $input = ['id' => 1];
242
        $options = ['responseType' => Filterer::RESPONSE_TYPE_FILTER];
243
244
        $result = Filterer::filter($specification, $input, $options);
245
246
        $this->assertInstanceOf(FilterResponse::class, $result);
247
        $this->assertSame(true, $result->success);
248
        $this->assertSame($input, $result->filteredValue);
249
        $this->assertSame([], $result->errors);
250
        $this->assertSame(null, $result->errorMessage);
251
        $this->assertSame([], $result->unknowns);
252
    }
253
254
    /**
255
     * @test
256
     * @covers ::filter
257
     * @covers ::execute
258
     */
259
    public function filterReturnsResponseTypeWithErrors()
260
    {
261
        $specification = ['name' => [['string']]];
262
        $input = ['name' => 'foo', 'id' => 1];
263
        $options = ['responseType' => Filterer::RESPONSE_TYPE_FILTER];
264
265
        $result = Filterer::filter($specification, $input, $options);
266
267
        $this->assertInstanceOf(FilterResponse::class, $result);
268
        $this->assertSame(false, $result->success);
269
        $this->assertSame(['name' => 'foo'], $result->filteredValue);
270
        $this->assertSame(['id' => "Field 'id' with value '1' is unknown"], $result->errors);
271
        $this->assertSame("Field 'id' with value '1' is unknown", $result->errorMessage);
272
        $this->assertSame(['id' => 1], $result->unknowns);
273
    }
274
275
    /**
276
     * @test
277
     * @covers ::filter
278
     * @covers ::execute
279
     * @covers ::setFilterAliases
280
     */
281
    public function filterCustomShortNamePass()
282
    {
283
        Filterer::setFilterAliases(['fval' => 'floatval']);
284
        $result = Filterer::filter(['fieldOne' => [['fval']]], ['fieldOne' => '3.14']);
285
        $this->assertSame([true, ['fieldOne' => 3.14], null, []], $result);
286
    }
287
288
    /**
289
     * @test
290
     * @covers ::filter
291
     * @covers ::execute
292
     * @covers ::setFilterAliases
293
     * @covers ::getFilterAliases
294
     */
295
    public function filterGetSetKnownFilters()
296
    {
297
        $knownFilters = ['lower' => 'strtolower', 'upper' => 'strtoupper'];
298
        Filterer::setFilterAliases($knownFilters);
299
        $this->assertSame($knownFilters, Filterer::getFilterAliases());
300
    }
301
302
    /**
303
     * @test
304
     * @covers ::setFilterAliases
305
     */
306
    public function setBadFilterAliases()
307
    {
308
        $originalAliases = Filterer::getFilterAliases();
309
310
        $actualThrowable = null;
311
        try {
312
            Filterer::setFilterAliases(['foo' => 'not callable']);
313
        } catch (Throwable $throwable) {
314
            $actualThrowable = $throwable;
315
        }
316
317
        $this->assertSame($originalAliases, Filterer::getFilterAliases());
318
        $this->assertInstanceOf(TypeError::class, $actualThrowable);
319
    }
320
321
    /**
322
     * @test
323
     * @covers ::filter
324
     * @covers ::execute
325
     * @expectedException Exception
326
     * @expectedExceptionMessage Function 'boo' for field 'foo' is not callable
327
     */
328
    public function notCallable()
329
    {
330
        Filterer::filter(['foo' => [['boo']]], ['foo' => 0]);
331
    }
332
333
    /**
334
     * @test
335
     * @covers ::filter
336
     * @covers ::execute
337
     * @expectedException InvalidArgumentException
338
     * @expectedExceptionMessage 'allowUnknowns' option was not a bool
339
     */
340
    public function allowUnknownsNotBool()
341
    {
342
        Filterer::filter([], [], ['allowUnknowns' => 1]);
343
    }
344
345
    /**
346
     * @test
347
     * @covers ::filter
348
     * @covers ::execute
349
     * @expectedException InvalidArgumentException
350
     * @expectedExceptionMessage 'defaultRequired' option was not a bool
351
     */
352
    public function defaultRequiredNotBool()
353
    {
354
        Filterer::filter([], [], ['defaultRequired' => 1]);
355
    }
356
357
    /**
358
     * @test
359
     * @covers ::filter
360
     * @covers ::execute
361
     */
362
    public function filterThrowsExceptionOnInvalidResponseType()
363
    {
364
        $this->expectException(InvalidArgumentException::class);
365
        $this->expectExceptionMessage("'responseType' was not a recognized value");
366
367
        Filterer::filter([], [], ['responseType' => 'invalid']);
368
    }
369
370
    /**
371
     * @test
372
     * @covers ::filter
373
     * @covers ::execute
374
     * @expectedException InvalidArgumentException
375
     * @expectedExceptionMessage filters for field 'boo' was not a array
376
     */
377
    public function filtersNotArrayInLeftOverSpec()
378
    {
379
        Filterer::filter(['boo' => 1], []);
380
    }
381
382
    /**
383
     * @test
384
     * @covers ::filter
385
     * @covers ::execute
386
     * @expectedException InvalidArgumentException
387
     * @expectedExceptionMessage filters for field 'boo' was not a array
388
     */
389
    public function filtersNotArrayWithInput()
390
    {
391
        Filterer::filter(['boo' => 1], ['boo' => 'notUnderTest']);
392
    }
393
394
    /**
395
     * @test
396
     * @covers ::filter
397
     * @covers ::execute
398
     * @expectedException InvalidArgumentException
399
     * @expectedExceptionMessage filter for field 'boo' was not a array
400
     */
401
    public function filterNotArray()
402
    {
403
        Filterer::filter(['boo' => [1]], ['boo' => 'notUnderTest']);
404
    }
405
406
    /**
407
     * @test
408
     * @covers ::filter
409
     * @covers ::execute
410
     * @expectedException InvalidArgumentException
411
     * @expectedExceptionMessage 'required' for field 'boo' was not a bool
412
     */
413
    public function requiredNotBool()
414
    {
415
        Filterer::filter(['boo' => ['required' => 1]], []);
416
    }
417
418
    /**
419
     * @test
420
     * @covers ::registerAlias
421
     * @expectedException InvalidArgumentException
422
     * @expectedExceptionMessage $alias was not a string or int
423
     */
424
    public function registerAliasAliasNotString()
425
    {
426
        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...
427
    }
428
429
    /**
430
     * @test
431
     * @covers ::registerAlias
432
     * @expectedException Exception
433
     * @expectedExceptionMessage Alias 'upper' exists
434
     */
435
    public function registerExistingAliasOverwriteFalse()
436
    {
437
        Filterer::setFilterAliases([]);
438
        Filterer::registerAlias('upper', 'strtoupper');
439
        Filterer::registerAlias('upper', 'strtoupper', false);
440
    }
441
442
    /**
443
     * @test
444
     * @covers ::registerAlias
445
     */
446
    public function registerExistingAliasOverwriteTrue()
447
    {
448
        Filterer::setFilterAliases(['upper' => 'strtoupper', 'lower' => 'strtolower']);
449
        Filterer::registerAlias('upper', 'ucfirst', true);
450
        $this->assertSame(['upper' => 'ucfirst', 'lower' => 'strtolower'], Filterer::getFilterAliases());
451
    }
452
453
    public static function failingFilter()
454
    {
455
        throw new Exception('i failed');
456
    }
457
458
    public static function passingFilter($value)
459
    {
460
        return $value . 'boo';
461
    }
462
463
    /**
464
     * Verify behavior of filter() when 'error' is not a string value.
465
     *
466
     * @test
467
     * @covers ::filter
468
     * @covers ::execute
469
     * @expectedException InvalidArgumentException
470
     * @expectedExceptionMessage error for field 'fieldOne' was not a non-empty string
471
     *
472
     * @return void
473
     */
474 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...
475
    {
476
        Filterer::filter(
477
            ['fieldOne' => [['strtoupper'], 'error' => new stdClass()]],
478
            ['fieldOne' => 'valueOne']
479
        );
480
    }
481
482
    /**
483
     * Verify behavior of filter() when 'error' is an empty string.
484
     *
485
     * @test
486
     * @covers ::filter
487
     * @covers ::execute
488
     * @expectedException InvalidArgumentException
489
     * @expectedExceptionMessage error for field 'fieldOne' was not a non-empty string
490
     *
491
     * @return void
492
     */
493 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...
494
    {
495
        Filterer::filter(
496
            ['fieldOne' => [['strtoupper'], 'error' => "\n   \t"]],
497
            ['fieldOne' => 'valueOne']
498
        );
499
    }
500
501
    /**
502
     * @test
503
     * @covers ::ofScalars
504
     */
505
    public function ofScalars()
506
    {
507
        $this->assertSame([1, 2], Filterer::ofScalars(['1', '2'], [['uint']]));
508
    }
509
510
    /**
511
     * @test
512
     * @covers ::ofScalars
513
     */
514
    public function ofScalarsChained()
515
    {
516
        $this->assertSame([3.3, 5.5], Filterer::ofScalars(['a3.3', 'a5.5'], [['trim', 'a'], ['floatval']]));
517
    }
518
519
    /**
520
     * @test
521
     * @covers ::ofScalars
522
     */
523
    public function ofScalarsWithMeaninglessKeys()
524
    {
525
        $this->assertSame(['key1' => 1, 'key2' => 2], Filterer::ofScalars(['key1' => '1', 'key2' => '2'], [['uint']]));
526
    }
527
528
    /**
529
     * @test
530
     * @covers ::ofScalars
531
     */
532 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...
533
    {
534
        try {
535
            Filterer::ofScalars(['1', [], new stdClass], [['string']]);
536
            $this->fail();
537
        } catch (FilterException $e) {
538
            $expected = <<<TXT
539
Field '1' with value 'array (
540
)' failed filtering, message 'Value 'array (
541
)' is not a string'
542
Field '2' with value 'stdClass::__set_state(array(
543
))' failed filtering, message 'Value 'stdClass::__set_state(array(
544
))' is not a string'
545
TXT;
546
            $this->assertSame($expected, $e->getMessage());
547
        }
548
    }
549
550
    /**
551
     * @test
552
     * @covers ::ofArrays
553
     */
554
    public function ofArrays()
555
    {
556
        $expected = [['key' => 1], ['key' => 2]];
557
        $this->assertSame($expected, Filterer::ofArrays([['key' => '1'], ['key' => '2']], ['key' => [['uint']]]));
558
    }
559
560
    /**
561
     * @test
562
     * @covers ::ofArrays
563
     */
564
    public function ofArraysChained()
565
    {
566
        $expected = [['key' => 3.3], ['key' => 5.5]];
567
        $spec = ['key' => [['trim', 'a'], ['floatval']]];
568
        $this->assertSame($expected, Filterer::ofArrays([['key' => 'a3.3'], ['key' => 'a5.5']], $spec));
569
    }
570
571
    /**
572
     * @test
573
     * @covers ::ofArrays
574
     */
575 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...
576
    {
577
        try {
578
            Filterer::ofArrays([['key' => '1'], ['key2' => '2']], ['key' => ['required' => true, ['uint']]]);
579
            $this->fail();
580
        } catch (Exception $e) {
581
            $expected = "Field 'key' was required and not present\nField 'key2' with value '2' is unknown";
582
            $this->assertSame($expected, $e->getMessage());
583
        }
584
    }
585
586
    /**
587
     * @test
588
     * @covers ::ofArrays
589
     */
590
    public function ofArraysFail()
591
    {
592
        try {
593
            Filterer::ofArrays(
594
                [['key' => new stdClass], ['key' => []], ['key' => null], 'key'],
595
                ['key' => [['string']]]
596
            );
597
            $this->fail();
598
        } catch (FilterException $e) {
599
            $expected = <<<TXT
600
Field 'key' with value 'stdClass::__set_state(array(
601
))' failed filtering, message 'Value 'stdClass::__set_state(array(
602
))' is not a string'
603
Field 'key' with value 'array (
604
)' failed filtering, message 'Value 'array (
605
)' is not a string'
606
Field 'key' with value 'NULL' failed filtering, message 'Value failed filtering, \$allowNull is set to false'
607
Value at position '3' was not an array
608
TXT;
609
            $this->assertSame($expected, $e->getMessage());
610
        }
611
    }
612
613
    /**
614
     * @test
615
     * @covers ::ofArray
616
     */
617
    public function ofArray()
618
    {
619
        $expected = ['key1' => 1, 'key2' => 2];
620
        $spec = ['key1' => [['uint']], 'key2' => [['uint']]];
621
        $this->assertSame($expected, Filterer::ofArray(['key1' => '1', 'key2' => '2'], $spec));
622
    }
623
624
    /**
625
     * @test
626
     * @covers ::ofArray
627
     */
628
    public function ofArrayChained()
629
    {
630
        $expected = ['key' => 3.3];
631
        $spec = ['key' => [['trim', 'a'], ['floatval']]];
632
        $this->assertSame($expected, Filterer::ofArray(['key' => 'a3.3'], $spec));
633
    }
634
635
    /**
636
     * @test
637
     * @covers ::ofArray
638
     */
639
    public function ofArrayRequiredSuccess()
640
    {
641
        $expected = ['key2' => 2];
642
        $spec = ['key1' => [['uint']], 'key2' => ['required' => true, ['uint']]];
643
        $this->assertSame($expected, Filterer::ofArray(['key2' => '2'], $spec));
644
    }
645
646
    /**
647
     * @test
648
     * @covers ::ofArray
649
     */
650 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...
651
    {
652
        try {
653
            Filterer::ofArray(['key1' => '1'], ['key1' => [['uint']], 'key2' => ['required' => true, ['uint']]]);
654
            $this->fail();
655
        } catch (FilterException $e) {
656
            $expected = "Field 'key2' was required and not present";
657
            $this->assertSame($expected, $e->getMessage());
658
        }
659
    }
660
661
    /**
662
     * @test
663
     * @covers ::ofArray
664
     */
665 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...
666
    {
667
        try {
668
            Filterer::ofArray(['key' => '1'], ['key2' => [['uint']]]);
669
            $this->fail();
670
        } catch (FilterException $e) {
671
            $expected = "Field 'key' with value '1' is unknown";
672
            $this->assertSame($expected, $e->getMessage());
673
        }
674
    }
675
676
    /**
677
     * @test
678
     * @covers ::__invoke
679
     * @dataProvider provideInvoke
680
     *
681
     * @param array      $filter
682
     * @param array      $options
683
     * @param array|null $filterAliases
684
     * @param mixed      $value
685
     * @param array      $expected
686
     */
687
    public function invoke(array $filter, array $options, $filterAliases, $value, array $expected)
688
    {
689
        $filterer = new Filterer($filter, $options, $filterAliases);
690
        $response = $filterer($value);
691
692
        $this->assertSame($expected, $response);
693
    }
694
695
    /**
696
     * @returns array
697
     */
698
    public function provideInvoke() : array
699
    {
700
        return [
701
            'empty' => [
702
                'filter' => [],
703
                'options' => [],
704
                'filterAliases' => null,
705
                'value' => [],
706
                'expected' => [],
707
            ],
708
            'basic use' => [
709
                'filter' => ['id' => [['uint']]],
710
                'options' => ['defaultRequired' => true],
711
                'filterAliases' => null,
712
                'value' => ['id' => '1'],
713
                'expected' => ['id' => 1],
714
            ],
715
            'with custom alias' => [
716
                'filter' => ['id' => [['hocuspocus']]],
717
                'options' => ['defaultRequired' => true],
718
                'filterAliases' => ['hocuspocus' => 'intval'],
719
                'value' => ['id' => '1'],
720
                'expected' => ['id' => 1],
721
            ],
722
        ];
723
    }
724
725
    /**
726
     * @test
727
     * @covers ::__invoke
728
     */
729
    public function invokeThrowsFilterException()
730
    {
731
        $filter = ['id' => ['required' => true]];
732
        $options = [];
733
        $value = [];
734
735
        $this->expectException(FilterException::class);
736
        $this->expectExceptionMessage("Field 'id' was required and not present");
737
738
        $filterer = new Filterer($filter, $options);
739
        $filterer($value);
740
    }
741
}
742