testAppendfilterThrowsAnExceptionIfTheFilterIsNotACallable()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 3
nc 1
nop 1
dl 0
loc 6
c 0
b 0
f 0
cc 1
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
namespace DanBettles\Gestalt\Tests;
6
7
use PHPUnit\Framework\TestCase;
8
use DanBettles\Gestalt\SimpleFilterChain;
9
use TypeError;
10
use stdClass;
11
12
use function array_map;
13
use function reset;
14
15
use const false;
16
use const null;
17
use const true;
18
19
class SimpleFilterChainTest extends TestCase
20
{
21
    /** @var string Because PHP 7 and 8 use different wording for the exception message */
22
    private const REGEX_MUST_BE_CALLABLE = '~( must be callable,| must be of type callable,)~';
23
24
    /**
25
     * Creates an array containing all the types of `callable` -- see
26
     * https://www.php.net/manual/en/language.types.callable.php
27
     *
28
     * @return callable[]
29
     */
30
    private function createAllTypesOfCallable(): array
31
    {
32
        return [
33
            'gettype',  //(Chosen because it'll accept any type of input.)
34
            [new TestCallables(), 'instanceMethod'],
35
            [TestCallables::class, 'staticMethod'],
36
            TestCallables::class . '::staticMethod',
37
            function () {
38
            },
39
            new TestCallables(),  //`TestCallables` implements `__invoke()`.
40
        ];
41
    }
42
43
    /**
44
     * For convenience, creates some kind of `callable`.
45
     */
46
    private function createCallable(): callable
47
    {
48
        $callables = $this->createAllTypesOfCallable();
49
        $callable = reset($callables);
50
51
        return $callable;
52
    }
53
54
    public function providesValidFilterArrays(): array
55
    {
56
        return [[
57
            [],  //None...
58
        ], [
59
            $this->createAllTypesOfCallable(),  //...Or some.
60
        ]];
61
    }
62
63
    /**
64
     * @dataProvider providesValidFilterArrays
65
     */
66
    public function testCanBeInstantiatedWithAnArrayOfFilters($filters)
67
    {
68
        $this->assertSame(
69
            $filters,
70
            (new SimpleFilterChain($filters))->getFilters()
71
        );
72
    }
73
74
    public function testConstructorAcceptsAllTypesOfCallable()
75
    {
76
        $filters = $this->createAllTypesOfCallable();
77
78
        $this->assertSame(
79
            $filters,
80
            (new SimpleFilterChain($filters))->getFilters()
81
        );
82
    }
83
84
    public function testCanBeInstantiatedWithNoArguments()
85
    {
86
        $this->assertInstanceOf(SimpleFilterChain::class, new SimpleFilterChain());
87
    }
88
89
    public function providesInvalidFilters(): array
90
    {
91
        return [[
92
            null,
93
        ], [
94
            'foo',
95
        ], [
96
            123,
97
        ], [
98
            1.23,
99
        ], [
100
            [],
101
        ], [
102
            new stdClass(),
103
        ]];
104
    }
105
106
    /**
107
     * @dataProvider providesInvalidFilters
108
     */
109
    public function testConstructorThrowsAnExceptionIfAFilterIsNotACallable($invalidFilter)
110
    {
111
        $this->expectException(TypeError::class);
112
        $this->expectExceptionMessageMatches(self::REGEX_MUST_BE_CALLABLE);
113
114
        new SimpleFilterChain([$invalidFilter]);
115
    }
116
117
    public function testAppendfilterAddsAFilterAtTheEndOfTheArray()
118
    {
119
        $callable1 = $this->createCallable();
120
        $callable2 = $this->createCallable();
121
        $callable3 = $this->createCallable();
122
123
        $chain = (new SimpleFilterChain())
124
            ->appendFilter($callable1)
125
            ->appendFilter($callable2)
126
            ->appendFilter($callable3)
127
        ;
128
129
        $this->assertSame([
130
            $callable1,
131
            $callable2,
132
            $callable3,
133
        ], $chain->getFilters());
134
    }
135
136
    public function providesValidFilters(): array
137
    {
138
        return array_map(function (callable $callable) {
139
            return [$callable];
140
        }, $this->createAllTypesOfCallable());
141
    }
142
143
    /**
144
     * @dataProvider providesValidFilters
145
     */
146
    public function testAppendfilterAcceptsAllTypesOfCallable($filter)
147
    {
148
        $chain = (new SimpleFilterChain())
149
            ->appendFilter($filter)
150
        ;
151
152
        $this->assertSame([
153
            $filter,
154
        ], $chain->getFilters());
155
    }
156
157
    /**
158
     * @dataProvider providesInvalidFilters
159
     */
160
    public function testAppendfilterThrowsAnExceptionIfTheFilterIsNotACallable($invalidFilter)
161
    {
162
        $this->expectException(TypeError::class);
163
        $this->expectExceptionMessageMatches(self::REGEX_MUST_BE_CALLABLE);
164
165
        (new SimpleFilterChain())->appendFilter($invalidFilter);
166
    }
167
168
    public function testExecuteInvokesEachFilterInTurn()
169
    {
170
        $chain = (new SimpleFilterChain([
171
            function (&$request) {
172
                $request[] = 1;
173
            },
174
            function (&$request) {
175
                $request[] = 2;
176
            },
177
            function (&$request) {
178
                $request[] = 3;
179
            },
180
        ]));
181
182
        $request = [];
183
184
        $result = $chain->execute($request);
185
186
        $this->assertSame([1, 2, 3], $request);
187
        $this->assertNull($result);
188
    }
189
190
    public function testExecuteInvokesEachFilterInTurnUntilAFilterReturnsFalse()
191
    {
192
        $chain = (new SimpleFilterChain([
193
            function (&$request) {
194
                $request[] = 1;
195
            },
196
            function (&$request) {
197
                $request[] = 2;
198
                return false;
199
            },
200
            function (&$request) {
201
                $request[] = 3;
202
            },
203
        ]));
204
205
        $request = [];
206
207
        $result = $chain->execute($request);
208
209
        $this->assertSame([1, 2], $request);
210
        $this->assertFalse($result);
211
    }
212
213
    public function testExecuteInvokesEachFilterInTurnUntilAFilterReturnsTheSpecifiedValue()
214
    {
215
        $chain = (new SimpleFilterChain([
216
            function (&$request) {
217
                $request[] = 1;
218
            },
219
            function (&$request) {
220
                $request[] = 2;
221
                return true;
222
            },
223
            function (&$request) {
224
                $request[] = 3;
225
            },
226
        ]));
227
228
        $request = [];
229
230
        $result = $chain->execute($request, true);
231
232
        $this->assertSame([1, 2], $request);
233
        $this->assertTrue($result);
234
    }
235
236
    public function testExecuteCanInvokeAnyTypeOfCallable()
237
    {
238
        $request = 'irrelevant';
239
240
        $returnValue = (new SimpleFilterChain($this->createAllTypesOfCallable()))
241
            ->execute($request)
242
        ;
243
244
        $this->assertNull($returnValue);
245
    }
246
}
247