Passed
Push — main ( 50aede...a9f24b )
by Chema
01:07 queued 13s
created

php$0 ➔ test_throws_exception_when_param_has_no_type()   A

Complexity

Conditions 1

Size

Total Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
c 0
b 0
f 0
dl 0
loc 16
rs 9.7333
1
<?php
2
3
declare(strict_types=1);
4
5
namespace GacelaTest\Feature\Router;
6
7
use Exception;
8
use Gacela\Router\Entities\Request;
9
use Gacela\Router\Exceptions\NotFound404Exception;
10
use Gacela\Router\Exceptions\UnsupportedParamTypeException;
11
use Gacela\Router\Exceptions\UnsupportedResponseTypeException;
12
use Gacela\Router\Handlers;
13
use Gacela\Router\Router;
14
use Gacela\Router\Routes;
15
use GacelaTest\Feature\HeaderTestCase;
16
use GacelaTest\Feature\Router\Fixtures\FakeController;
17
use GacelaTest\Feature\Router\Fixtures\FakeControllerWithUnhandledException;
18
use GacelaTest\Feature\Router\Fixtures\UnhandledException;
19
use Generator;
20
use stdClass;
21
22
final class ErrorHandlingTest extends HeaderTestCase
23
{
24
    public function test_respond_404_status_when_uri_does_not_match(): void
25
    {
26
        $_SERVER['REQUEST_URI'] = 'https://example.org/optional/uri';
27
        $_SERVER['REQUEST_METHOD'] = Request::METHOD_OPTIONS;
28
29
        $router = new Router(static function (): void {
30
        });
31
        $router->run();
32
33
        self::assertSame([
34
            [
35
                'header' => 'HTTP/1.0 404 Not Found',
36
                'replace' => true,
37
                'response_code' => 0,
38
            ],
39
        ], $this->headers());
40
    }
41
42
    public function test_respond_404_status_when_method_does_not_match(): void
43
    {
44
        $_SERVER['REQUEST_URI'] = 'https://example.org/expected/uri';
45
        $_SERVER['REQUEST_METHOD'] = Request::METHOD_GET;
46
47
        $router = new Router(static function (Routes $routes): void {
48
            $routes->post('expected/uri', FakeController::class, 'basicAction');
49
        });
50
        $router->run();
51
52
        self::assertSame([
53
            [
54
                'header' => 'HTTP/1.0 404 Not Found',
55
                'replace' => true,
56
                'response_code' => 0,
57
            ],
58
        ], $this->headers());
59
    }
60
61
    /**
62
     * @dataProvider notMatchesMethodsProvider
63
     */
64
    public function test_respond_404_status_when_not_matches_match_methods(string $testMethod, array $givenMethods): void
65
    {
66
        $_SERVER['REQUEST_URI'] = 'https://example.org/expected/uri';
67
        $_SERVER['REQUEST_METHOD'] = $testMethod;
68
69
        $router = new Router(static function (Routes $routes) use ($givenMethods): void {
70
            $routes->match($givenMethods, 'expected/uri', FakeController::class, 'basicAction');
71
        });
72
        $router->run();
73
74
        self::assertSame([
75
            [
76
                'header' => 'HTTP/1.0 404 Not Found',
77
                'replace' => true,
78
                'response_code' => 0,
79
            ],
80
        ], $this->headers());
81
    }
82
83
    public function notMatchesMethodsProvider(): Generator
84
    {
85
        yield [Request::METHOD_PUT, [Request::METHOD_GET, Request::METHOD_POST]];
86
        yield [Request::METHOD_OPTIONS, [Request::METHOD_GET, Request::METHOD_POST]];
87
        yield [Request::METHOD_GET, [Request::METHOD_PATCH, Request::METHOD_PUT, Request::METHOD_DELETE, Request::METHOD_POST]];
88
        yield [Request::METHOD_CONNECT, [
89
            Request::METHOD_GET, Request::METHOD_DELETE, Request::METHOD_HEAD, Request::METHOD_OPTIONS,
90
            Request::METHOD_PATCH, Request::METHOD_POST, Request::METHOD_PUT, Request::METHOD_TRACE,
91
        ]];
92
    }
93
94
    public function test_respond_500_status_when_unhandled_exception(): void
95
    {
96
        $_SERVER['REQUEST_URI'] = 'https://example.org/expected/uri';
97
        $_SERVER['REQUEST_METHOD'] = Request::METHOD_GET;
98
99
        $router = new Router(static function (Routes $routes): void {
100
            $routes->get('expected/uri', FakeControllerWithUnhandledException::class);
101
        });
102
        $router->run();
103
104
        self::assertSame([
105
            [
106
                'header' => 'HTTP/1.1 500 Internal Server Error',
107
                'replace' => true,
108
                'response_code' => 0,
109
            ],
110
        ], $this->headers());
111
    }
112
113
    public function test_handle_handled_exception_with_anonymous_function(): void
114
    {
115
        $_SERVER['REQUEST_URI'] = 'https://example.org/expected/uri';
116
        $_SERVER['REQUEST_METHOD'] = Request::METHOD_GET;
117
118
        $router = new Router(static function (Routes $routes, Handlers $handlers): void {
119
            $routes->get('expected/uri', FakeControllerWithUnhandledException::class);
120
121
            $handlers->handle(UnhandledException::class, static function (): string {
122
                \Gacela\Router\header('HTTP/1.1 418 I\'m a teapot');
123
                return 'Handled!';
124
            });
125
        });
126
        $router->run();
127
128
        $this->expectOutputString('Handled!');
129
        self::assertSame([
130
            [
131
                'header' => 'HTTP/1.1 418 I\'m a teapot',
132
                'replace' => true,
133
                'response_code' => 0,
134
            ],
135
        ], $this->headers());
136
    }
137
138
    public function test_custom_404_handler(): void
139
    {
140
        $_SERVER['REQUEST_URI'] = 'https://example.org/expected/uri';
141
        $_SERVER['REQUEST_METHOD'] = Request::METHOD_GET;
142
143
        $router = new Router(static function (Handlers $handlers): void {
144
            $handlers->handle(NotFound404Exception::class, static function (NotFound404Exception $exception): string {
145
                \Gacela\Router\header('HTTP/1.1 418 I\'m a teapot');
146
                return "'{$exception->getMessage()}' Handled!";
147
            });
148
        });
149
        $router->run();
150
151
        $this->expectOutputString("'Error 404 - Not Found' Handled!");
152
        self::assertSame([
153
            [
154
                'header' => 'HTTP/1.1 418 I\'m a teapot',
155
                'replace' => true,
156
                'response_code' => 0,
157
            ],
158
        ], $this->headers());
159
    }
160
161
    public function test_custom_fallback_handler(): void
162
    {
163
        $_SERVER['REQUEST_URI'] = 'https://example.org/expected/uri';
164
        $_SERVER['REQUEST_METHOD'] = Request::METHOD_GET;
165
166
        $router = new Router(static function (Handlers $handlers, Routes $routes): void {
167
            $routes->get('expected/uri', FakeControllerWithUnhandledException::class);
168
169
            $handlers->handle(Exception::class, static function (): string {
170
                \Gacela\Router\header('HTTP/1.1 418 I\'m a teapot');
171
                return 'Handled!';
172
            });
173
        });
174
        $router->run();
175
176
        $this->expectOutputString('Handled!');
177
        self::assertSame([
178
            [
179
                'header' => 'HTTP/1.1 418 I\'m a teapot',
180
                'replace' => true,
181
                'response_code' => 0,
182
            ],
183
        ], $this->headers());
184
    }
185
186
    public function test_handle_handled_exception_with_anonymous_class(): void
187
    {
188
        $_SERVER['REQUEST_URI'] = 'https://example.org/expected/uri';
189
        $_SERVER['REQUEST_METHOD'] = Request::METHOD_GET;
190
191
        $router = new Router(static function (Handlers $handlers, Routes $routes): void {
192
            $routes->get('expected/uri', FakeControllerWithUnhandledException::class);
193
194
            $handlers->handle(UnhandledException::class, new class() {
195
                public function __invoke(): string
196
                {
197
                    \Gacela\Router\header('HTTP/1.1 418 I\'m a teapot');
198
                    return 'Handled!';
199
                }
200
            });
201
        });
202
        $router->run();
203
204
        $this->expectOutputString('Handled!');
205
        self::assertSame([
206
            [
207
                'header' => 'HTTP/1.1 418 I\'m a teapot',
208
                'replace' => true,
209
                'response_code' => 0,
210
            ],
211
        ], $this->headers());
212
    }
213
214
    /**
215
     * @dataProvider nonStringProvider
216
     *
217
     * @param mixed $given
218
     * @param mixed $type
219
     */
220
    public function test_throws_exception_if_response_is_not_a_string_or_stringable($given, $type): void
221
    {
222
        $_SERVER['REQUEST_URI'] = 'https://example.org/expected/uri';
223
        $_SERVER['REQUEST_METHOD'] = Request::METHOD_GET;
224
225
        $router = new Router(static function (Handlers $handlers, Routes $routes) use ($given): void {
226
            $routes->get('expected/uri', static fn () => $given);
227
228
            $handlers->handle(
229
                UnsupportedResponseTypeException::class,
230
                static fn (UnsupportedResponseTypeException $exception): string => $exception->getMessage(),
231
            );
232
        });
233
        $router->run();
234
235
        $this->expectOutputString("Unsupported response type '{$type}'. Must be a string or implement Stringable interface.");
236
    }
237
238
    public function nonStringProvider(): Generator
239
    {
240
        yield [42, 'integer'];
241
        yield [false, 'boolean'];
242
        yield [[], 'array'];
243
        yield [new stdClass(), 'stdClass'];
244
    }
245
246
    public function test_throws_exception_when_param_has_no_type(): void
247
    {
248
        $_SERVER['REQUEST_URI'] = 'https://example.org/expected/param/is/any';
249
        $_SERVER['REQUEST_METHOD'] = Request::METHOD_GET;
250
251
        $router = new Router(static function (Routes $routes, Handlers $handlers): void {
252
            $routes->get('expected/param/is/{param}', FakeController::class, 'nonTypedParam');
253
254
            $handlers->handle(
255
                UnsupportedParamTypeException::class,
256
                static fn (UnsupportedParamTypeException $exception): string => $exception->getMessage(),
257
            );
258
        });
259
        $router->run();
260
261
        $this->expectOutputString('Unsupported non-typed param. Must be a scalar.');
262
    }
263
264
    public function test_throws_exception_when_param_is_no_scalar(): void
265
    {
266
        $_SERVER['REQUEST_URI'] = 'https://example.org/expected/param/is/array';
267
        $_SERVER['REQUEST_METHOD'] = Request::METHOD_GET;
268
269
        $router = new Router(static function (Routes $routes, Handlers $handlers): void {
270
            $routes->get('expected/param/is/{param}', FakeController::class, 'nonScalarParam');
271
272
            $handlers->handle(
273
                UnsupportedParamTypeException::class,
274
                static fn (UnsupportedParamTypeException $exception): string => $exception->getMessage(),
275
            );
276
        });
277
        $router->run();
278
279
        $this->expectOutputString("Unsupported param type 'array'. Must be a scalar.");
280
    }
281
282
    public function test_configure_throws_unsupported_closure_param(): void
283
    {
284
        $this->expectExceptionMessage("'unrecognised' parameter in configuration Closure for Router must be from types Routes, Bindings or Handlers.");
285
286
        new Router(static function ($unrecognised): void {});
0 ignored issues
show
Unused Code introduced by
The parameter $unrecognised is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

286
        new Router(static function (/** @scrutinizer ignore-unused */ $unrecognised): void {});

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
287
    }
288
289
    public function test_configure_non_callable_handler(): void
290
    {
291
        $_SERVER['REQUEST_URI'] = 'https://example.org/expected/uri';
292
        $_SERVER['REQUEST_METHOD'] = Request::METHOD_GET;
293
294
        $this->expectExceptionMessage('Handler assigned to \'GacelaTest\Feature\Router\Fixtures\UnhandledException\' exception cannot be called.');
295
296
        $router = new Router(static function (Handlers $handlers, Routes $routes): void {
297
            $routes->get('expected/uri', FakeControllerWithUnhandledException::class);
298
299
            $handlers->handle(UnhandledException::class, 'non-callable');
300
        });
301
        $router->run();
302
    }
303
}
304