anonymous//tests/Filter/StringsTest.php$1   A
last analyzed

Complexity

Total Complexity 1

Size/Duplication

Total Lines 6
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
dl 0
loc 6
c 0
b 0
f 0
rs 10
wmc 1
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
     * @covers ::filter
61
     */
62
    public function filterNullFail()
63
    {
64
        $this->expectException(\TraderInteractive\Exceptions\FilterException::class);
65
        $this->expectExceptionMessage('Value failed filtering, $allowNull is set to false');
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
     * @covers ::filter
81
     */
82
    public function filterMinLengthFail()
83
    {
84
        $this->expectException(\TraderInteractive\Exceptions\FilterException::class);
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
     * @covers ::filter
100
     */
101
    public function filterMaxLengthFail()
102
    {
103
        $this->expectException(\TraderInteractive\Exceptions\FilterException::class);
104
        $this->expectExceptionMessage("Value 'a' with length '1' is less than '0' or greater than '0'");
105
        Strings::filter('a', false, 0, 0);
106
    }
107
108
    /**
109
     * @test
110
     * @covers ::filter
111
     */
112
    public function filterMinLengthNotInteger()
113
    {
114
        $this->expectException(InvalidArgumentException::class);
115
        $this->expectExceptionMessage('$minLength was not a positive integer value');
116
        Strings::filter('a', false, -1);
117
    }
118
119
    /**
120
     * @test
121
     * @covers ::filter
122
     */
123
    public function filterMaxLengthNotInteger()
124
    {
125
        $this->expectException(InvalidArgumentException::class);
126
        $this->expectExceptionMessage('$maxLength was not a positive integer value');
127
        Strings::filter('a', false, 1, -1);
128
    }
129
130
    /**
131
     * @test
132
     * @covers ::filter
133
     */
134
    public function filterMinLengthNegative()
135
    {
136
        $this->expectException(InvalidArgumentException::class);
137
        $this->expectExceptionMessage('$minLength was not a positive integer value');
138
        Strings::filter('a', false, -1);
139
    }
140
141
    /**
142
     * @test
143
     * @covers ::filter
144
     */
145
    public function filterMaxLengthNegative()
146
    {
147
        $this->expectException(InvalidArgumentException::class);
148
        $this->expectExceptionMessage('$maxLength was not a positive integer value');
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
    public function filterWithObjectNoToStringMethod()
189
    {
190
        $this->expectException(\TraderInteractive\Exceptions\FilterException::class);
191
        $this->expectExceptionMessageMatches("/Value '\\\\?class\@anonymous/");
192
        $testObject = new class() {
193
            private $data;
194
195
            public function __construct()
196
            {
197
                $this->data = [1, 2, 3, 4, 5];
198
            }
199
        };
200
201
        Strings::filter(new $testObject);
202
    }
203
204
    /**
205
     * @test
206
     * @covers ::translate
207
     */
208
    public function translateValue()
209
    {
210
        $map = ['foo' => '100', 'bar' => '200'];
211
        $this->assertSame('100', Strings::translate('foo', $map));
212
    }
213
214
    /**
215
     * @test
216
     * @covers ::translate
217
     */
218
    public function translateValueNotFoundInMap()
219
    {
220
        $this->expectException(\TraderInteractive\Exceptions\FilterException::class);
221
        $this->expectExceptionMessage("The value 'baz' was not found in the translation map array.");
222
        $map = ['foo' => '100', 'bar' => '200'];
223
        Strings::translate('baz', $map);
224
    }
225
226
    /**
227
     * Verifies basic explode functionality.
228
     *
229
     * @test
230
     * @covers ::explode
231
     */
232
    public function explode()
233
    {
234
        $this->assertSame(['a', 'bcd', 'e'], Strings::explode('a,bcd,e'));
235
    }
236
237
    /**
238
     * Verifies explode with a custom delimiter.
239
     *
240
     * @test
241
     * @covers ::explode
242
     */
243
    public function explodeCustomDelimiter()
244
    {
245
        $this->assertSame(['a', 'b', 'c', 'd,e'], Strings::explode('a b c d,e', ' '));
246
    }
247
248
    /**
249
     * @test
250
     * @covers ::explode
251
     */
252
    public function explodeNonString()
253
    {
254
        $this->expectException(\TraderInteractive\Exceptions\FilterException::class);
255
        $this->expectExceptionMessage("Value '1234' is not a string");
256
        Strings::explode(1234, '');
257
    }
258
259
    /**
260
     * Verifies explode filter with an empty delimiter.
261
     *
262
     * @test
263
     * @covers ::explode
264
     */
265
    public function explodeEmptyDelimiter()
266
    {
267
        $this->expectException(\InvalidArgumentException::class);
268
        $this->expectExceptionMessage("Delimiter '''' is not a non-empty string");
269
        Strings::explode('test', '');
270
    }
271
272
    /**
273
     * @test
274
     * @covers ::stripTags
275
     * @dataProvider provideStripTags
276
     *
277
     * @param string|null $value
278
     * @param string      $replacement
279
     * @param string|null $expected
280
     */
281
    public function stripTags($value, string $replacement, $expected)
282
    {
283
        $actual = Strings::stripTags($value, $replacement);
284
        $this->assertSame($expected, $actual);
285
    }
286
287
    /**
288
     * @return array
289
     */
290
    public function provideStripTags()
291
    {
292
        return [
293
            'null returns null' => [
294
                'value' => null,
295
                'replacement' => '',
296
                'expected' => null,
297
            ],
298
            'remove html from string' => [
299
                'value' => 'A string with <p>paragraph</p> tags',
300
                'replacement' => '',
301
                'expected' => 'A string with paragraph tags',
302
            ],
303
            'remove xml and replace with space' => [
304
                'value' => '<something>inner value</something>',
305
                'replacement' => ' ',
306
                'expected' => ' inner value ',
307
            ],
308
            'remove multiline html from string' => [
309
                'value' => "<p\nclass='something'\nstyle='display:none'></p>",
310
                'replacement' => ' ',
311
                'expected' => '  ',
312
            ],
313
            'remove php tags' => [
314
                'value' => '<?php some php code',
315
                'replacement' => ' ',
316
                'expected' => '',
317
            ],
318
            'remove shorthand php tags' => [
319
                'value' => '<?= some php code ?> something else',
320
                'replacement' => ' ',
321
                'expected' => '  something else',
322
            ],
323
            'do not remove unmatched <' => [
324
                'value' => '1 < 3',
325
                'replacement' => ' ',
326
                'expected' => '1 < 3',
327
            ],
328
            'do not remove unmatched >' => [
329
                'value' => '3 > 1',
330
                'replacement' => ' ',
331
                'expected' => '3 > 1',
332
            ],
333
        ];
334
    }
335
336
    /**
337
     * @test
338
     * @covers ::concat
339
     */
340
    public function concat()
341
    {
342
        $this->assertSame('prefixstringsuffix', Strings::concat('string', 'prefix', 'suffix'));
343
    }
344
345
    /**
346
     * Verify behavior of concat() when $value is not filterable
347
     *
348
     * @test
349
     * @covers ::concat
350
     *
351
     * @return void
352
     */
353
    public function concatValueNotFilterable()
354
    {
355
        $this->expectException(\TraderInteractive\Exceptions\FilterException::class);
356
        Strings::concat(new \StdClass(), 'prefix', 'suffix');
357
    }
358
359
    /**
360
     * @test
361
     * @covers ::concat
362
     */
363
    public function concatScalarValue()
364
    {
365
        $this->assertSame('prefix123suffix', Strings::concat(123, 'prefix', 'suffix'));
366
    }
367
368
    /**
369
     * @test
370
     * @covers ::concat
371
     */
372
    public function concatObjectValue()
373
    {
374
        $this->assertSame(
375
            'prefix' . __FILE__ . 'suffix',
376
            Strings::concat(new \SplFileInfo(__FILE__), 'prefix', 'suffix')
377
        );
378
    }
379
380
    /**
381
     * @test
382
     * @covers ::compress
383
     */
384
    public function compressRemovesSuperfluousWhitespace()
385
    {
386
        $this->assertSame('a compressed string', Strings::compress('  a   compressed     string    '));
387
    }
388
389
    /**
390
     * @test
391
     * @covers ::compress
392
     */
393
    public function compressReturnsNullIfValueIsNull()
394
    {
395
        $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...
396
    }
397
398
    /**
399
     * @test
400
     * @covers ::compress
401
     */
402
    public function compressRemovesNewLines()
403
    {
404
        $input = " This string\nhas     superfluous   whitespace  and  \nnewlines\n";
405
        $this->assertSame(
406
            'This string has superfluous whitespace and newlines',
407
            Strings::compress($input, true)
408
        );
409
    }
410
411
    /**
412
     * @test
413
     * @covers ::compress
414
     */
415
    public function compressIgnoresNewLinesByDefault()
416
    {
417
        $input = " This string\nhas     superfluous   whitespace  and  \nnewlines\n";
418
        $this->assertSame(
419
            "This string\nhas superfluous whitespace and \nnewlines",
420
            Strings::compress($input)
421
        );
422
    }
423
424
    /**
425
     * @test
426
     * @covers ::redact
427
     * @dataProvider provideRedact
428
     *
429
     * @param string|null    $value       The value to pass to the filter.
430
     * @param array|callable $words       The words to pass to the filter.
431
     * @param string         $replacement The replacement to pass to the filter.
432
     * @param string|null    $expected    The expected result.
433
     */
434
    public function redact($value, $words, string $replacement, $expected)
435
    {
436
        $actual = Strings::redact($value, $words, $replacement);
437
438
        $this->assertSame($expected, $actual);
439
    }
440
441
    /**
442
     * @return array
443
     */
444
    public function provideRedact() : array
445
    {
446
        return [
447
            'null value' => [
448
                'value' => null,
449
                'words' => [],
450
                'replacement' => '',
451
                'expected' => null,
452
            ],
453
            'empty string' => [
454
                'value' => '',
455
                'words' => [],
456
                'replacement' => '',
457
                'expected' => '',
458
            ],
459
            'replace with empty' => [
460
                'value' => 'this message contains something that you want removed',
461
                'words' => ['something that you want removed'],
462
                'replacement' => '',
463
                'expected' => 'this message contains ',
464
            ],
465
            'replace with *' => [
466
                'value' => 'replace certain words that you might want to remove',
467
                'words' => ['might', 'certain'],
468
                'replacement' => '*',
469
                'expected' => 'replace ******* words that you ***** want to remove',
470
            ],
471
            'replace with █' => [
472
                'value' => 'redact specific dates and secret locations',
473
                'words' => ['secret locations', 'specific dates'],
474
                'replacement' => '█',
475
                'expected' => 'redact ██████████████ and ████████████████',
476
            ],
477
            'replace with multi-character string uses first character' => [
478
                'value' => 'replace some particular words',
479
                'words' => ['particular', 'words', 'some'],
480
                'replacement' => ' *** ',
481
                'expected' => 'replace                      ',
482
            ],
483
            'no replacements' => [
484
                'value' => 'some perfectly normal string',
485
                'words' => ['undesired', 'words'],
486
                'replacement' => '*',
487
                'expected' => 'some perfectly normal string',
488
            ],
489
            'closure provides words' => [
490
                'value' => 'doe a deer, a female deer',
491
                'words' => function () {
492
                    return ['doe', 'deer'];
493
                },
494
                'replacement' => '-',
495
                'expected' => '--- a ----, a female ----',
496
            ],
497
        ];
498
    }
499
500
    /**
501
     * @test
502
     * @covers ::redact
503
     * @dataProvider provideRedactFailsOnBadInput
504
     *
505
     * @param mixed  $value       The value to pass to the filter.
506
     * @param mixed  $words       The words to pass to the filter.
507
     * @param string $replacement The replacement to pass to the filter.
508
     * @param string $exception   The exception to expect.
509
     * @param string $message     The exception message to expect.
510
     */
511
    public function redactFailsOnBadInput($value, $words, string $replacement, string $exception, string $message)
512
    {
513
        $this->expectException($exception);
514
        $this->expectExceptionMessage($message);
515
516
        Strings::redact($value, $words, $replacement);
517
    }
518
519
    /**
520
     * @return array
521
     */
522
    public function provideRedactFailsOnBadInput() : array
523
    {
524
        return [
525
            'non-string value' => [
526
                'value' => ['bad', 'input'],
527
                'words' => [],
528
                'replacement' => '',
529
                'exception' => FilterException::class,
530
                'message' => "Value '" . var_export(['bad', 'input'], true) . "' is not a string",
531
            ],
532
            'invalid words argument' => [
533
                'value' => 'some string',
534
                'words' => 'this is not valid',
535
                'replacement' => '',
536
                'exception' => FilterException::class,
537
                'message' => 'Words was not an array or a callable that returns an array',
538
            ],
539
            'invalid return from callable words argument' => [
540
                'value' => 'some string',
541
                'words' => function () {
542
                    return 'this is also not valid';
543
                },
544
                'replacement' => '',
545
                'exception' => FilterException::class,
546
                'message' => 'Words was not an array or a callable that returns an array',
547
            ],
548
        ];
549
    }
550
}
551