Passed
Push — main ( 56c0cb...e79186 )
by Chema
09:06 queued 14s
created

ErrorHandlingTest.php$0 ➔ nonStringProvider()   A

Complexity

Conditions 1

Size

Total Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
dl 0
loc 6
rs 10
c 0
b 0
f 0
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\UnsupportedResponseTypeException;
11
use Gacela\Router\Handlers;
12
use Gacela\Router\Router;
13
use Gacela\Router\Routes;
14
use GacelaTest\Feature\HeaderTestCase;
15
use GacelaTest\Feature\Router\Fixtures\FakeController;
16
use GacelaTest\Feature\Router\Fixtures\FakeControllerWithUnhandledException;
17
use GacelaTest\Feature\Router\Fixtures\UnhandledException;
18
use Generator;
19
use stdClass;
20
21
final class ErrorHandlingTest extends HeaderTestCase
22
{
23
    public function test_respond_404_status_when_uri_does_not_match(): void
24
    {
25
        $_SERVER['REQUEST_URI'] = 'https://example.org/optional/uri';
26
        $_SERVER['REQUEST_METHOD'] = Request::METHOD_OPTIONS;
27
28
        Router::configure(static function (): void {
29
        });
30
31
        self::assertSame([
32
            [
33
                'header' => 'HTTP/1.0 404 Not Found',
34
                'replace' => true,
35
                'response_code' => 0,
36
            ],
37
        ], $this->headers());
38
    }
39
40
    public function test_respond_404_status_when_method_does_not_match(): void
41
    {
42
        $_SERVER['REQUEST_URI'] = 'https://example.org/expected/uri';
43
        $_SERVER['REQUEST_METHOD'] = Request::METHOD_GET;
44
45
        Router::configure(static function (Routes $routes): void {
46
            $routes->post('expected/uri', FakeController::class, 'basicAction');
47
        });
48
49
        self::assertSame([
50
            [
51
                'header' => 'HTTP/1.0 404 Not Found',
52
                'replace' => true,
53
                'response_code' => 0,
54
            ],
55
        ], $this->headers());
56
    }
57
58
    /**
59
     * @dataProvider notMatchesMethodsProvider
60
     */
61
    public function test_respond_404_status_when_not_matches_match_methods(string $testMethod, array $givenMethods): void
62
    {
63
        $_SERVER['REQUEST_URI'] = 'https://example.org/expected/uri';
64
        $_SERVER['REQUEST_METHOD'] = $testMethod;
65
66
        Router::configure(static function (Routes $routes) use ($givenMethods): void {
67
            $routes->match($givenMethods, 'expected/uri', FakeController::class, 'basicAction');
68
        });
69
70
        self::assertSame([
71
            [
72
                'header' => 'HTTP/1.0 404 Not Found',
73
                'replace' => true,
74
                'response_code' => 0,
75
            ],
76
        ], $this->headers());
77
    }
78
79
    public function notMatchesMethodsProvider(): Generator
80
    {
81
        yield [Request::METHOD_PUT, [Request::METHOD_GET, Request::METHOD_POST]];
82
        yield [Request::METHOD_OPTIONS, [Request::METHOD_GET, Request::METHOD_POST]];
83
        yield [Request::METHOD_GET, [Request::METHOD_PATCH, Request::METHOD_PUT, Request::METHOD_DELETE, Request::METHOD_POST]];
84
        yield [Request::METHOD_CONNECT, [
85
            Request::METHOD_GET, Request::METHOD_DELETE, Request::METHOD_HEAD, Request::METHOD_OPTIONS,
86
            Request::METHOD_PATCH, Request::METHOD_POST, Request::METHOD_PUT, Request::METHOD_TRACE,
87
        ]];
88
    }
89
90
    public function test_respond_500_status_when_unhandled_exception(): void
91
    {
92
        $_SERVER['REQUEST_URI'] = 'https://example.org/expected/uri';
93
        $_SERVER['REQUEST_METHOD'] = Request::METHOD_GET;
94
95
        Router::configure(static function (Routes $routes): void {
96
            $routes->get('expected/uri', FakeControllerWithUnhandledException::class);
97
        });
98
99
        self::assertSame([
100
            [
101
                'header' => 'HTTP/1.1 500 Internal Server Error',
102
                'replace' => true,
103
                'response_code' => 0,
104
            ],
105
        ], $this->headers());
106
    }
107
108
    public function test_handle_handled_exception_with_anonymous_function(): void
109
    {
110
        $_SERVER['REQUEST_URI'] = 'https://example.org/expected/uri';
111
        $_SERVER['REQUEST_METHOD'] = Request::METHOD_GET;
112
113
        Router::configure(static function (Routes $routes, Handlers $handlers): void {
114
            $routes->get('expected/uri', FakeControllerWithUnhandledException::class);
115
116
            $handlers->handle(UnhandledException::class, static function (): string {
117
                \Gacela\Router\header('HTTP/1.1 418 I\'m a teapot');
118
                return 'Handled!';
119
            });
120
        });
121
122
        $this->expectOutputString('Handled!');
123
        self::assertSame([
124
            [
125
                'header' => 'HTTP/1.1 418 I\'m a teapot',
126
                'replace' => true,
127
                'response_code' => 0,
128
            ],
129
        ], $this->headers());
130
    }
131
132
    public function test_custom_404_handler(): void
133
    {
134
        $_SERVER['REQUEST_URI'] = 'https://example.org/expected/uri';
135
        $_SERVER['REQUEST_METHOD'] = Request::METHOD_GET;
136
137
        Router::configure(static function (Handlers $handlers): void {
138
            $handlers->handle(NotFound404Exception::class, static function (): string {
139
                \Gacela\Router\header('HTTP/1.1 418 I\'m a teapot');
140
                return 'Handled!';
141
            });
142
        });
143
144
        $this->expectOutputString('Handled!');
145
        self::assertSame([
146
            [
147
                'header' => 'HTTP/1.1 418 I\'m a teapot',
148
                'replace' => true,
149
                'response_code' => 0,
150
            ],
151
        ], $this->headers());
152
    }
153
154
    public function test_custom_fallback_handler(): void
155
    {
156
        $_SERVER['REQUEST_URI'] = 'https://example.org/expected/uri';
157
        $_SERVER['REQUEST_METHOD'] = Request::METHOD_GET;
158
159
        Router::configure(static function (Handlers $handlers, Routes $routes): void {
160
            $routes->get('expected/uri', FakeControllerWithUnhandledException::class);
161
162
            $handlers->handle(Exception::class, static function (): string {
163
                \Gacela\Router\header('HTTP/1.1 418 I\'m a teapot');
164
                return 'Handled!';
165
            });
166
        });
167
168
        $this->expectOutputString('Handled!');
169
        self::assertSame([
170
            [
171
                'header' => 'HTTP/1.1 418 I\'m a teapot',
172
                'replace' => true,
173
                'response_code' => 0,
174
            ],
175
        ], $this->headers());
176
    }
177
178
    public function test_handle_handled_exception_with_anonymous_class(): void
179
    {
180
        $_SERVER['REQUEST_URI'] = 'https://example.org/expected/uri';
181
        $_SERVER['REQUEST_METHOD'] = Request::METHOD_GET;
182
183
        Router::configure(static function (Handlers $handlers, Routes $routes): void {
184
            $routes->get('expected/uri', FakeControllerWithUnhandledException::class);
185
186
            $handlers->handle(UnhandledException::class, new class() {
187
                public function __invoke(): string
188
                {
189
                    \Gacela\Router\header('HTTP/1.1 418 I\'m a teapot');
190
                    return 'Handled!';
191
                }
192
            });
193
        });
194
195
        $this->expectOutputString('Handled!');
196
        self::assertSame([
197
            [
198
                'header' => 'HTTP/1.1 418 I\'m a teapot',
199
                'replace' => true,
200
                'response_code' => 0,
201
            ],
202
        ], $this->headers());
203
    }
204
205
    /**
206
     * @dataProvider nonStringProvider
207
     *
208
     * @param mixed $given
209
     * @param mixed $type
210
     */
211
    public function test_throws_exception_if_response_is_not_a_string_or_stringable($given, $type): void
212
    {
213
        $_SERVER['REQUEST_URI'] = 'https://example.org/expected/uri';
214
        $_SERVER['REQUEST_METHOD'] = Request::METHOD_GET;
215
216
        Router::configure(static function (Handlers $handlers, Routes $routes) use ($given): void {
217
            $routes->get('expected/uri', static fn () => $given);
1 ignored issue
show
Bug introduced by
function(...) { /* ... */ } of type callable is incompatible with the type object|string expected by parameter $controller of Gacela\Router\Routes::get(). ( Ignorable by Annotation )

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

217
            $routes->get('expected/uri', /** @scrutinizer ignore-type */ static fn () => $given);
Loading history...
218
219
            $handlers->handle(
220
                UnsupportedResponseTypeException::class,
221
                static fn (UnsupportedResponseTypeException $exception): string => $exception->getMessage(),
222
            );
223
        });
224
225
        $this->expectOutputString("Unsupported response type '{$type}'. Must be a string or implement Stringable interface.");
226
    }
227
228
    public function nonStringProvider(): Generator
229
    {
230
        yield [42, 'integer'];
231
        yield [false, 'boolean'];
232
        yield [[], 'array'];
233
        yield [new stdClass(), 'stdClass'];
234
    }
235
}
236