Failed Conditions
Pull Request — master (#13)
by Chad
03:16
created

StringsTest.php$1 ➔ provideInvalidExtractData()   A

Complexity

Conditions 1

Size

Total Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
dl 0
loc 15
rs 9.7666
c 0
b 0
f 0
1
<?php
2
3
namespace TraderInteractive\Filter;
4
5
use InvalidArgumentException;
6
use PHPUnit\Framework\TestCase;
7
use TraderInteractive\Exceptions\FilterException;
8
9
/**
10
 * @coversDefaultClass \TraderInteractive\Filter\Strings
11
 * @covers ::<private>
12
 */
13
final class StringsTest extends TestCase
14
{
15
    /**
16
     * Verify basic use of filter
17
     *
18
     * @test
19
     * @covers ::filter
20
     * @dataProvider filterData
21
     *
22
     * @param mixed $input    The input.
23
     * @param mixed $expected The expected value(s).
24
     *
25
     * @return void
26
     * @throws FilterException
27
     */
28
    public function filter($input, $expected)
29
    {
30
        $this->assertSame($expected, Strings::filter($input));
31
    }
32
33
    /**
34
     * Data provider for basic filter tests
35
     *
36
     * @return array
37
     */
38
    public function filterData()
39
    {
40
        return [
41
            'string' => ['abc', 'abc'],
42
            'int' => [1, '1'],
43
            'float' => [1.1, '1.1'],
44
            'bool' => [true, '1'],
45
            'object' => [new \SplFileInfo(__FILE__), __FILE__],
46
        ];
47
    }
48
49
    /**
50
     * @test
51
     * @covers ::filter
52
     */
53
    public function filterNullPass()
54
    {
55
        $this->assertNull(Strings::filter(null, true));
56
    }
57
58
    /**
59
     * @test
60
     * @expectedException \TraderInteractive\Exceptions\FilterException
61
     * @expectedExceptionMessage Value failed filtering, $allowNull is set to false
62
     * @covers ::filter
63
     */
64
    public function filterNullFail()
65
    {
66
        Strings::filter(null);
67
    }
68
69
    /**
70
     * @test
71
     * @covers ::filter
72
     */
73
    public function filterMinLengthPass()
74
    {
75
        $this->assertSame('a', Strings::filter('a'));
76
    }
77
78
    /**
79
     * @test
80
     * @expectedException \TraderInteractive\Exceptions\FilterException
81
     * @covers ::filter
82
     */
83
    public function filterMinLengthFail()
84
    {
85
        Strings::filter('');
86
    }
87
88
    /**
89
     * @test
90
     * @covers ::filter
91
     */
92
    public function filterMaxLengthPass()
93
    {
94
        $this->assertSame('a', Strings::filter('a', false, 0, 1));
95
    }
96
97
    /**
98
     * @test
99
     * @expectedException \TraderInteractive\Exceptions\FilterException
100
     * @expectedExceptionMessage Value 'a' with length '1' is less than '0' or greater than '0'
101
     * @covers ::filter
102
     */
103
    public function filterMaxLengthFail()
104
    {
105
        Strings::filter('a', false, 0, 0);
106
    }
107
108
    /**
109
     * @test
110
     * @expectedException InvalidArgumentException
111
     * @expectedExceptionMessage $minLength was not a positive integer value
112
     * @covers ::filter
113
     */
114
    public function filterMinLengthNotInteger()
115
    {
116
        Strings::filter('a', false, -1);
117
    }
118
119
    /**
120
     * @test
121
     * @expectedException InvalidArgumentException
122
     * @expectedExceptionMessage $maxLength was not a positive integer value
123
     * @covers ::filter
124
     */
125
    public function filterMaxLengthNotInteger()
126
    {
127
        Strings::filter('a', false, 1, -1);
128
    }
129
130
    /**
131
     * @test
132
     * @expectedException InvalidArgumentException
133
     * @expectedExceptionMessage $minLength was not a positive integer value
134
     * @covers ::filter
135
     */
136
    public function filterMinLengthNegative()
137
    {
138
        Strings::filter('a', false, -1);
139
    }
140
141
    /**
142
     * @test
143
     * @expectedException InvalidArgumentException
144
     * @expectedExceptionMessage $maxLength was not a positive integer value
145
     * @covers ::filter
146
     */
147
    public function filterMaxLengthNegative()
148
    {
149
        Strings::filter('a', false, 1, -1);
150
    }
151
152
    /**
153
     * @test
154
     * @covers ::filter
155
     */
156
    public function filterWithScalar()
157
    {
158
        $this->assertSame('24141', Strings::filter(24141));
159
    }
160
161
    /**
162
     * @test
163
     * @covers ::filter
164
     */
165
    public function filterWithObject()
166
    {
167
        $testObject = new class() {
168
            private $data;
169
170
            public function __construct()
171
            {
172
                $this->data = [1,2,3,4,5];
173
            }
174
175
            public function __toString()
176
            {
177
                return implode(',', $this->data);
178
            }
179
        };
180
181
        $this->assertSame('1,2,3,4,5', Strings::filter(new $testObject));
182
    }
183
184
    /**
185
     * @test
186
     * @covers ::filter
187
     *
188
     * @expectedException \TraderInteractive\Exceptions\FilterException
189
     * @expectedExceptionMessage Value 'class@anonymous
190
     */
191
    public function filterWithObjectNoToStringMethod()
192
    {
193
        $testObject = new class() {
194
            private $data;
195
196
            public function __construct()
197
            {
198
                $this->data = [1, 2, 3, 4, 5];
199
            }
200
        };
201
202
        Strings::filter(new $testObject);
203
    }
204
205
    /**
206
     * @test
207
     * @covers ::translate
208
     */
209
    public function translateValue()
210
    {
211
        $map = ['foo' => '100', 'bar' => '200'];
212
        $this->assertSame('100', Strings::translate('foo', $map));
213
    }
214
215
    /**
216
     * @test
217
     * @covers ::translate
218
     * @expectedException \TraderInteractive\Exceptions\FilterException
219
     * @expectedExceptionMessage The value 'baz' was not found in the translation map array.
220
     */
221
    public function translateValueNotFoundInMap()
222
    {
223
        $map = ['foo' => '100', 'bar' => '200'];
224
        Strings::translate('baz', $map);
225
    }
226
227
    /**
228
     * Verifies basic explode functionality.
229
     *
230
     * @test
231
     * @covers ::explode
232
     */
233
    public function explode()
234
    {
235
        $this->assertSame(['a', 'bcd', 'e'], Strings::explode('a,bcd,e'));
236
    }
237
238
    /**
239
     * Verifies explode with a custom delimiter.
240
     *
241
     * @test
242
     * @covers ::explode
243
     */
244
    public function explodeCustomDelimiter()
245
    {
246
        $this->assertSame(['a', 'b', 'c', 'd,e'], Strings::explode('a b c d,e', ' '));
247
    }
248
249
    /**
250
     * @test
251
     * @expectedException \TraderInteractive\Exceptions\FilterException
252
     * @expectedExceptionMessage Value '1234' is not a string
253
     * @covers ::explode
254
     */
255
    public function explodeNonString()
256
    {
257
        Strings::explode(1234, '');
258
    }
259
260
    /**
261
     * Verifies explode filter with an empty delimiter.
262
     *
263
     * @test
264
     * @expectedException \InvalidArgumentException
265
     * @expectedExceptionMessage Delimiter '''' is not a non-empty string
266
     * @covers ::explode
267
     */
268
    public function explodeEmptyDelimiter()
269
    {
270
        Strings::explode('test', '');
271
    }
272
273
    /**
274
     * @test
275
     * @covers ::stripTags
276
     * @dataProvider provideStripTags
277
     *
278
     * @param string|null $value
279
     * @param string      $replacement
280
     * @param string|null $expected
281
     */
282
    public function stripTags($value, string $replacement, $expected)
283
    {
284
        $actual = Strings::stripTags($value, $replacement);
285
        $this->assertSame($expected, $actual);
286
    }
287
288
    /**
289
     * @return array
290
     */
291
    public function provideStripTags()
292
    {
293
        return [
294
            'null returns null' => [
295
                'value' => null,
296
                'replacement' => '',
297
                'expected' => null,
298
            ],
299
            'remove html from string' => [
300
                'value' => 'A string with <p>paragraph</p> tags',
301
                'replacement' => '',
302
                'expected' => 'A string with paragraph tags',
303
            ],
304
            'remove xml and replace with space' => [
305
                'value' => '<something>inner value</something>',
306
                'replacement' => ' ',
307
                'expected' => ' inner value ',
308
            ],
309
            'remove multiline html from string' => [
310
                'value' => "<p\nclass='something'\nstyle='display:none'></p>",
311
                'replacement' => ' ',
312
                'expected' => '  ',
313
            ],
314
            'remove php tags' => [
315
                'value' => '<?php some php code',
316
                'replacement' => ' ',
317
                'expected' => '',
318
            ],
319
            'remove shorthand php tags' => [
320
                'value' => '<?= some php code ?> something else',
321
                'replacement' => ' ',
322
                'expected' => '  something else',
323
            ],
324
            'do not remove unmatched <' => [
325
                'value' => '1 < 3',
326
                'replacement' => ' ',
327
                'expected' => '1 < 3',
328
            ],
329
            'do not remove unmatched >' => [
330
                'value' => '3 > 1',
331
                'replacement' => ' ',
332
                'expected' => '3 > 1',
333
            ],
334
        ];
335
    }
336
337
    /**
338
     * @test
339
     * @covers ::concat
340
     */
341
    public function concat()
342
    {
343
        $this->assertSame('prefixstringsuffix', Strings::concat('string', 'prefix', 'suffix'));
344
    }
345
346
    /**
347
     * Verify behavior of concat() when $value is not filterable
348
     *
349
     * @test
350
     * @covers ::concat
351
     * @expectedException \TraderInteractive\Exceptions\FilterException
352
     *
353
     * @return void
354
     */
355
    public function concatValueNotFilterable()
356
    {
357
        Strings::concat(new \StdClass(), 'prefix', 'suffix');
358
    }
359
360
    /**
361
     * @test
362
     * @covers ::concat
363
     */
364
    public function concatScalarValue()
365
    {
366
        $this->assertSame('prefix123suffix', Strings::concat(123, 'prefix', 'suffix'));
367
    }
368
369
    /**
370
     * @test
371
     * @covers ::concat
372
     */
373
    public function concatObjectValue()
374
    {
375
        $this->assertSame(
376
            'prefix' . __FILE__ . 'suffix',
377
            Strings::concat(new \SplFileInfo(__FILE__), 'prefix', 'suffix')
378
        );
379
    }
380
381
    /**
382
     * @test
383
     * @covers ::compress
384
     */
385
    public function compressRemovesSuperfluousWhitespace()
386
    {
387
        $this->assertSame('a compressed string', Strings::compress('  a   compressed     string    '));
388
    }
389
390
    /**
391
     * @test
392
     * @covers ::compress
393
     */
394
    public function compressReturnsNullIfValueIsNull()
395
    {
396
        $this->assertNull(Strings::compress(null));
0 ignored issues
show
Bug introduced by
Are you sure the usage of TraderInteractive\Filter\Strings::compress(null) targeting TraderInteractive\Filter\Strings::compress() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
397
    }
398
399
    /**
400
     * @test
401
     * @covers ::compress
402
     */
403
    public function compressRemovesNewLines()
404
    {
405
        $input = " This string\nhas     superfluous   whitespace  and  \nnewlines\n";
406
        $this->assertSame(
407
            'This string has superfluous whitespace and newlines',
408
            Strings::compress($input, true)
409
        );
410
    }
411
412
    /**
413
     * @test
414
     * @covers ::compress
415
     */
416
    public function compressIgnoresNewLinesByDefault()
417
    {
418
        $input = " This string\nhas     superfluous   whitespace  and  \nnewlines\n";
419
        $this->assertSame(
420
            "This string\nhas superfluous whitespace and \nnewlines",
421
            Strings::compress($input)
422
        );
423
    }
424
425
    /**
426
     * @test
427
     * @covers ::redact
428
     * @dataProvider provideRedact
429
     *
430
     * @param string|null    $value       The value to pass to the filter.
431
     * @param array|callable $words       The words to pass to the filter.
432
     * @param string         $replacement The replacement to pass to the filter.
433
     * @param string|null    $expected    The expected result.
434
     */
435
    public function redact($value, $words, string $replacement, $expected)
436
    {
437
        $actual = Strings::redact($value, $words, $replacement);
438
439
        $this->assertSame($expected, $actual);
440
    }
441
442
    /**
443
     * @return array
444
     */
445
    public function provideRedact() : array
446
    {
447
        return [
448
            'null value' => [
449
                'value' => null,
450
                'words' => [],
451
                'replacement' => '',
452
                'expected' => null,
453
            ],
454
            'empty string' => [
455
                'value' => '',
456
                'words' => [],
457
                'replacement' => '',
458
                'expected' => '',
459
            ],
460
            'replace with empty' => [
461
                'value' => 'this message contains something that you want removed',
462
                'words' => ['something that you want removed'],
463
                'replacement' => '',
464
                'expected' => 'this message contains ',
465
            ],
466
            'replace with *' => [
467
                'value' => 'replace certain words that you might want to remove',
468
                'words' => ['might', 'certain'],
469
                'replacement' => '*',
470
                'expected' => 'replace ******* words that you ***** want to remove',
471
            ],
472
            'replace with █' => [
473
                'value' => 'redact specific dates and secret locations',
474
                'words' => ['secret locations', 'specific dates'],
475
                'replacement' => '█',
476
                'expected' => 'redact ██████████████ and ████████████████',
477
            ],
478
            'replace with multi-character string uses first character' => [
479
                'value' => 'replace some particular words',
480
                'words' => ['particular', 'words', 'some'],
481
                'replacement' => ' *** ',
482
                'expected' => 'replace                      ',
483
            ],
484
            'no replacements' => [
485
                'value' => 'some perfectly normal string',
486
                'words' => ['undesired', 'words'],
487
                'replacement' => '*',
488
                'expected' => 'some perfectly normal string',
489
            ],
490
            'closure provides words' => [
491
                'value' => 'doe a deer, a female deer',
492
                'words' => function () {
493
                    return ['doe', 'deer'];
494
                },
495
                'replacement' => '-',
496
                'expected' => '--- a ----, a female ----',
497
            ],
498
        ];
499
    }
500
501
    /**
502
     * @test
503
     * @covers ::redact
504
     * @dataProvider provideRedactFailsOnBadInput
505
     *
506
     * @param mixed  $value       The value to pass to the filter.
507
     * @param mixed  $words       The words to pass to the filter.
508
     * @param string $replacement The replacement to pass to the filter.
509
     * @param string $exception   The exception to expect.
510
     * @param string $message     The exception message to expect.
511
     */
512
    public function redactFailsOnBadInput($value, $words, string $replacement, string $exception, string $message)
513
    {
514
        $this->expectException($exception);
515
        $this->expectExceptionMessage($message);
516
517
        Strings::redact($value, $words, $replacement);
518
    }
519
520
    /**
521
     * @return array
522
     */
523
    public function provideRedactFailsOnBadInput() : array
524
    {
525
        return [
526
            'non-string value' => [
527
                'value' => ['bad', 'input'],
528
                'words' => [],
529
                'replacement' => '',
530
                'exception' => FilterException::class,
531
                'message' => "Value '" . var_export(['bad', 'input'], true) . "' is not a string",
532
            ],
533
            'invalid words argument' => [
534
                'value' => 'some string',
535
                'words' => 'this is not valid',
536
                'replacement' => '',
537
                'exception' => FilterException::class,
538
                'message' => 'Words was not an array or a callable that returns an array',
539
            ],
540
            'invalid return from callable words argument' => [
541
                'value' => 'some string',
542
                'words' => function () {
543
                    return 'this is also not valid';
544
                },
545
                'replacement' => '',
546
                'exception' => FilterException::class,
547
                'message' => 'Words was not an array or a callable that returns an array',
548
            ],
549
        ];
550
    }
551
552
    /**
553
     * @test
554
     * @covers ::match
555
     * @expectedException \TraderInteractive\Exceptions\FilterException
556
     * @expectedExceptionMessage The given string '123d' did not match the expression /^\d+$/
557
     */
558
    public function matchThrowsExceptionWhenInputDoesNotMatchExpression()
559
    {
560
        Strings::match('123d', '/^\d+$/');
561
    }
562
563
    /**
564
     * @test
565
     * @covers ::match
566
     */
567
    public function matchReturnsInputIfInputMatchesPattern()
568
    {
569
        $this->assertSame('123', Strings::match('123', '/^\d+$/'));
570
    }
571
572
    /**
573
     * @test
574
     * @covers ::extract
575
     */
576
    public function extractReturnsTheNamedSubpattern()
577
    {
578
        $pattern = '/(?P<name>\w+): (?P<year>\d+)/';
579
        $input = 'foobar: 2008';
580
        $this->assertSame('2008', Strings::extract($input, $pattern, 'year'));
581
    }
582
583
    /**
584
     * @param string $input   The value to be filtered.
585
     * @param string $pattern The pattern to use in the filter.
586
     * @param string $name    The named subpattern to extract.
587
     * @param string $message The expected exception message.
588
     *
589
     * @test
590
     * @covers ::extract
591
     * @dataProvider provideInvalidExtractData
592
     */
593
    public function extractFailures(string $input, string $pattern, string $name, string $message)
594
    {
595
        $this->expectException(FilterException::class);
596
        $this->expectExceptionMessage($message);
597
        Strings::extract($input, $pattern, $name);
598
    }
599
600
    /**
601
     * @return array
602
     */
603
    public function provideInvalidExtractData() : array
604
    {
605
        $pattern = '/(?P<name>\w+): (?P<year>\d+)/';
606
        return [
607
            'invalid sub pattern name' => [
608
                'input' => 'Ford: 2019',
609
                'pattern' => $pattern,
610
                'name' => 'digit',
611
                'message' => "The given named sub pattern 'digit' was not found in the expression {$pattern}",
612
            ],
613
            'input does not match pattern' => [
614
                'input' => 'Ford: abc',
615
                'pattern' => $pattern,
616
                'name' => 'year',
617
                'message' => "The given string 'Ford: abc' did not match the expression {$pattern}",
618
            ],
619
        ];
620
    }
621
}
622