test_middleware_can_short_circuit_request()
last analyzed

Size

Total Lines 25
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
dl 0
loc 25
c 1
b 0
f 0
eloc 12
nc 1
nop 0

1 Method

Rating   Name   Duplication   Size   Complexity  
A MiddlewareTest.php$0 ➔ handle() 0 5 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace GacelaTest\Feature\Router;
6
7
use Closure;
8
use Gacela\Router\Configure\Middlewares;
9
use Gacela\Router\Configure\Routes;
10
use Gacela\Router\Entities\Request;
11
use Gacela\Router\Middleware\MiddlewareInterface;
12
use Gacela\Router\Router;
13
use GacelaTest\Feature\HeaderTestCase;
14
use GacelaTest\Feature\Router\Fixtures\FakeMiddleware;
15
use Override;
16
17
final class MiddlewareTest extends HeaderTestCase
18
{
19
    public function test_middleware_can_modify_response(): void
20
    {
21
        $router = new Router(static function (Routes $routes, Middlewares $middlewares): void {
22
            $middlewares->add(self::createAnonMiddleware('WRAPPED'));
23
24
            $routes->get('/', static fn () => 'CONTENT');
0 ignored issues
show
Bug introduced by
function(...) { /* ... */ } of type callable is incompatible with the type object|string expected by parameter $controller of Gacela\Router\Configure\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

24
            $routes->get('/', /** @scrutinizer ignore-type */ static fn () => 'CONTENT');
Loading history...
25
        });
26
27
        $_SERVER['REQUEST_METHOD'] = 'GET';
28
        $_SERVER['REQUEST_URI'] = '/';
29
30
        ob_start();
31
        $router->run();
32
        $output = ob_get_clean();
33
34
        self::assertSame('[WRAPPED]CONTENT[/WRAPPED]', $output);
35
    }
36
37
    public function test_multiple_middlewares_execute_in_onion_pattern(): void
38
    {
39
        $router = new Router(static function (Routes $routes, Middlewares $middlewares): void {
40
            $middlewares->add(self::createAnonMiddleware('FIRST'));
41
            $middlewares->add(self::createAnonMiddleware('SECOND'));
42
            $middlewares->add(self::createAnonMiddleware('THIRD'));
43
44
            $routes->get('/', static fn () => 'CORE');
0 ignored issues
show
Bug introduced by
function(...) { /* ... */ } of type callable is incompatible with the type object|string expected by parameter $controller of Gacela\Router\Configure\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

44
            $routes->get('/', /** @scrutinizer ignore-type */ static fn () => 'CORE');
Loading history...
45
        });
46
47
        $_SERVER['REQUEST_METHOD'] = 'GET';
48
        $_SERVER['REQUEST_URI'] = '/';
49
50
        ob_start();
51
        $router->run();
52
        $output = ob_get_clean();
53
54
        // Onion pattern: first middleware added wraps outermost
55
        self::assertSame('[FIRST][SECOND][THIRD]CORE[/THIRD][/SECOND][/FIRST]', $output);
56
    }
57
58
    public function test_middleware_can_short_circuit_request(): void
59
    {
60
        $router = new Router(static function (Routes $routes, Middlewares $middlewares): void {
61
            $middlewares->add(
62
                new class() implements MiddlewareInterface {
63
                    #[Override]
64
                    public function handle(Request $request, Closure $next): string
65
                    {
66
                        // Short-circuit: don't call $next
67
                        return 'SHORT-CIRCUITED';
68
                    }
69
                },
70
            );
71
72
            $routes->get('/', static fn () => 'NEVER REACHED');
0 ignored issues
show
Bug introduced by
function(...) { /* ... */ } of type callable is incompatible with the type object|string expected by parameter $controller of Gacela\Router\Configure\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

72
            $routes->get('/', /** @scrutinizer ignore-type */ static fn () => 'NEVER REACHED');
Loading history...
73
        });
74
75
        $_SERVER['REQUEST_METHOD'] = 'GET';
76
        $_SERVER['REQUEST_URI'] = '/';
77
78
        ob_start();
79
        $router->run();
80
        $output = ob_get_clean();
81
82
        self::assertSame('SHORT-CIRCUITED', $output);
83
    }
84
85
    public function test_route_specific_middleware_only_applies_to_that_route(): void
86
    {
87
        $router = new Router(static function (Routes $routes): void {
88
            $routes
89
                ->get('protected', static fn () => 'PROTECTED')
0 ignored issues
show
Bug introduced by
function(...) { /* ... */ } of type callable is incompatible with the type object|string expected by parameter $controller of Gacela\Router\Configure\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

89
                ->get('protected', /** @scrutinizer ignore-type */ static fn () => 'PROTECTED')
Loading history...
90
                ->middleware(self::createAnonMiddleware('AUTH'));
91
92
            $routes->get('public', static fn () => 'PUBLIC');
0 ignored issues
show
Bug introduced by
function(...) { /* ... */ } of type callable is incompatible with the type object|string expected by parameter $controller of Gacela\Router\Configure\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

92
            $routes->get('public', /** @scrutinizer ignore-type */ static fn () => 'PUBLIC');
Loading history...
93
        });
94
95
        // Test protected route
96
        $_SERVER['REQUEST_METHOD'] = 'GET';
97
        $_SERVER['REQUEST_URI'] = '/protected';
98
99
        ob_start();
100
        $router->run();
101
        $output = ob_get_clean();
102
103
        self::assertSame('[AUTH]PROTECTED[/AUTH]', $output);
104
105
        // Test public route
106
        $_SERVER['REQUEST_URI'] = '/public';
107
108
        ob_start();
109
        $router->run();
110
        $output = ob_get_clean();
111
112
        self::assertSame('PUBLIC', $output);
113
    }
114
115
    public function test_route_middleware_combines_with_global_middleware(): void
116
    {
117
        $router = new Router(static function (Routes $routes, Middlewares $middlewares): void {
118
            $middlewares->add(self::createAnonMiddleware('GLOBAL'));
119
120
            $routes
121
                ->get('/', static fn () => 'CONTENT')
0 ignored issues
show
Bug introduced by
function(...) { /* ... */ } of type callable is incompatible with the type object|string expected by parameter $controller of Gacela\Router\Configure\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

121
                ->get('/', /** @scrutinizer ignore-type */ static fn () => 'CONTENT')
Loading history...
122
                ->middleware(self::createAnonMiddleware('ROUTE'));
123
        });
124
125
        $_SERVER['REQUEST_METHOD'] = 'GET';
126
        $_SERVER['REQUEST_URI'] = '/';
127
128
        ob_start();
129
        $router->run();
130
        $output = ob_get_clean();
131
132
        // Global middleware wraps outside route middleware
133
        self::assertSame('[GLOBAL][ROUTE]CONTENT[/ROUTE][/GLOBAL]', $output);
134
    }
135
136
    public function test_middleware_can_access_request(): void
137
    {
138
        $router = new Router(static function (Routes $routes, Middlewares $middlewares): void {
139
            $middlewares->add(
140
                new class() implements MiddlewareInterface {
141
                    #[Override]
142
                    public function handle(Request $request, Closure $next): string
143
                    {
144
                        $path = $request->path();
145
                        return "[PATH:{$path}]" . $next($request);
146
                    }
147
                },
148
            );
149
150
            $routes->get('test-path', static fn () => 'CONTENT');
0 ignored issues
show
Bug introduced by
function(...) { /* ... */ } of type callable is incompatible with the type object|string expected by parameter $controller of Gacela\Router\Configure\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

150
            $routes->get('test-path', /** @scrutinizer ignore-type */ static fn () => 'CONTENT');
Loading history...
151
        });
152
153
        $_SERVER['REQUEST_METHOD'] = 'GET';
154
        $_SERVER['REQUEST_URI'] = '/test-path';
155
156
        ob_start();
157
        $router->run();
158
        $output = ob_get_clean();
159
160
        self::assertSame('[PATH:/test-path]CONTENT', $output);
161
    }
162
163
    public function test_middleware_can_be_class_string(): void
164
    {
165
        $router = new Router(static function (Routes $routes, Middlewares $middlewares): void {
166
            $middlewares->add(FakeMiddleware::class);
167
            $routes->get('/', static fn () => 'CONTENT');
0 ignored issues
show
Bug introduced by
function(...) { /* ... */ } of type callable is incompatible with the type object|string expected by parameter $controller of Gacela\Router\Configure\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

167
            $routes->get('/', /** @scrutinizer ignore-type */ static fn () => 'CONTENT');
Loading history...
168
        });
169
170
        $_SERVER['REQUEST_METHOD'] = 'GET';
171
        $_SERVER['REQUEST_URI'] = '/';
172
173
        ob_start();
174
        $router->run();
175
        $output = ob_get_clean();
176
177
        self::assertSame('[TEST]CONTENT[/TEST]', $output);
178
    }
179
180
    public function test_route_can_have_multiple_middlewares(): void
181
    {
182
        $router = new Router(static function (Routes $routes): void {
183
            $routes
184
                ->get('/', static fn () => 'CONTENT')
0 ignored issues
show
Bug introduced by
function(...) { /* ... */ } of type callable is incompatible with the type object|string expected by parameter $controller of Gacela\Router\Configure\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

184
                ->get('/', /** @scrutinizer ignore-type */ static fn () => 'CONTENT')
Loading history...
185
                ->middleware(self::createAnonMiddleware('ONE'))
186
                ->middleware(self::createAnonMiddleware('TWO'));
187
        });
188
189
        $_SERVER['REQUEST_METHOD'] = 'GET';
190
        $_SERVER['REQUEST_URI'] = '/';
191
192
        ob_start();
193
        $router->run();
194
        $output = ob_get_clean();
195
196
        self::assertSame('[ONE][TWO]CONTENT[/TWO][/ONE]', $output);
197
    }
198
199
    private static function createAnonMiddleware(string $tag): MiddlewareInterface
200
    {
201
        return new class($tag) implements MiddlewareInterface {
202
            public function __construct(
203
                private readonly string $tag,
204
            ) {
205
            }
206
207
            #[Override]
208
            public function handle(Request $request, Closure $next): string
209
            {
210
                return "[{$this->tag}]" . $next($request) . "[/{$this->tag}]";
211
            }
212
        };
213
    }
214
}
215