Passed
Pull Request — master (#22)
by
unknown
01:40
created

RouterTest::testMatchAttributes()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 23
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 15
nc 1
nop 0
dl 0
loc 23
rs 9.7666
c 0
b 0
f 0
1
<?php
2
3
namespace Sunrise\Http\Router\Tests;
4
5
use PHPUnit\Framework\TestCase;
6
use Psr\Http\Server\RequestHandlerInterface;
7
use Sunrise\Http\Router\Exception\HttpExceptionInterface;
8
use Sunrise\Http\Router\Exception\MethodNotAllowedException;
9
use Sunrise\Http\Router\Exception\RouteNotFoundException;
10
use Sunrise\Http\Router\Route;
11
use Sunrise\Http\Router\RouteCollection;
12
use Sunrise\Http\Router\RouteCollectionInterface;
13
use Sunrise\Http\Router\RouteInterface;
14
use Sunrise\Http\Router\Router;
15
use Sunrise\Http\Router\RouterInterface;
16
use Sunrise\Http\ServerRequest\ServerRequestFactory;
17
18
// fake middlewares
19
use Sunrise\Http\Router\Tests\Middleware\FooMiddlewareTest;
20
use Sunrise\Http\Router\Tests\Middleware\BarMiddlewareTest;
21
use Sunrise\Http\Router\Tests\Middleware\BazMiddlewareTest;
22
use Sunrise\Http\Router\Tests\Middleware\QuxMiddlewareTest;
23
use Sunrise\Http\Router\Tests\Middleware\SetRequestAttributesWithoutRouteIdToResponseHeaderMiddlewareTest;
24
use Sunrise\Http\Router\Tests\Middleware\SetRouteIdFromRequestAttributesToResponseHeaderMiddlewareTest;
25
26
class RouterTest extends TestCase
27
{
28
	public function testConstructor()
29
	{
30
		$router = new Router();
31
32
		$this->assertInstanceOf(RouterInterface::class, $router);
33
		$this->assertInstanceOf(RequestHandlerInterface::class, $router);
34
	}
35
36
	public function testGetRoutes()
37
	{
38
		$router = new Router();
39
40
		$this->assertEquals([], $router->getRoutes());
41
	}
42
43
	public function testGetMiddlewareStack()
44
	{
45
		$router = new Router();
46
47
		$this->assertEquals([], $router->getMiddlewareStack());
48
	}
49
50
	public function testAddRoute()
51
	{
52
		$foo = new Route('foo', '/foo', []);
53
		$router = new Router();
54
55
		$this->assertInstanceOf(RouterInterface::class, $router->addRoute($foo));
56
		$this->assertEquals([$foo], $router->getRoutes());
57
	}
58
59
	public function testAddSeveralRoutes()
60
	{
61
		$foo = new Route('foo', '/foo', []);
62
		$bar = new Route('bar', '/bar', []);
63
64
		$router = new Router();
65
		$router->addRoute($foo);
66
		$router->addRoute($bar);
67
68
		$this->assertEquals([
69
			$foo,
70
			$bar,
71
		], $router->getRoutes());
72
	}
73
74
	public function testAddRoutesFromRouteCollection()
75
	{
76
		$router = new Router();
77
		$routes = new RouteCollection();
78
79
		$foo = $routes->get('foo', '/foo');
80
81
		$this->assertInstanceOf(RouterInterface::class, $router->addRoutes($routes));
82
		$this->assertEquals([$foo], $router->getRoutes());
83
	}
84
85
	public function testAddRoutesFromSeveralRouteCollections()
86
	{
87
		$router = new Router();
88
89
		$routes = new RouteCollection();
90
		$foo = $routes->get('foo', '/foo');
91
		$bar = $routes->get('bar', '/bar');
92
		$router->addRoutes($routes);
93
94
		$routes = new RouteCollection();
95
		$baz = $routes->get('baz', '/baz');
96
		$qux = $routes->get('qux', '/qux');
97
		$router->addRoutes($routes);
98
99
		$this->assertEquals([
100
			$foo,
101
			$bar,
102
			$baz,
103
			$qux,
104
		], $router->getRoutes());
105
	}
106
107
	public function testAddMiddleware()
108
	{
109
		$foo = new FooMiddlewareTest();
110
		$router = new Router();
111
112
		$this->assertInstanceOf(RouterInterface::class, $router->addMiddleware($foo));
113
		$this->assertEquals([$foo], $router->getMiddlewareStack());
114
	}
115
116
	public function testAddSeveralMiddlewares()
117
	{
118
		$foo = new FooMiddlewareTest();
119
		$bar = new BarMiddlewareTest();
120
121
		$router = new Router();
122
		$router->addMiddleware($foo);
123
		$router->addMiddleware($bar);
124
125
		$this->assertEquals([
126
			$foo,
127
			$bar,
128
		], $router->getMiddlewareStack());
129
	}
130
131
	public function testMatchWithSeveralRoutes()
132
	{
133
		$routes = new RouteCollection();
134
		$routes->get('foo', '/foo');
135
		$routes->get('bar', '/bar');
136
		$routes->get('baz', '/baz');
137
138
		$expected = $routes->get('qux', '/qux');
139
		$actual = $this->discoverRoute($routes, 'GET', '/qux');
140
141
		$this->assertEquals($expected, $actual);
142
	}
143
144
	public function testMatchWithSeveralHttpMethods()
145
	{
146
		$routes = new RouteCollection();
147
148
		$foo = $routes->head('foo', '/foo');
149
		$bar = $routes->get('bar', '/bar');
150
		$baz = $routes->post('baz', '/baz');
151
152
		$route = $this->discoverRoute($routes, 'HEAD', '/foo');
153
		$this->assertEquals($foo, $route);
154
155
		$route = $this->discoverRoute($routes, 'GET', '/foo');
156
		$this->assertNull($route);
157
158
		$route = $this->discoverRoute($routes, 'POST', '/foo');
159
		$this->assertNull($route);
160
161
		$route = $this->discoverRoute($routes, 'GET', '/bar');
162
		$this->assertEquals($bar, $route);
163
164
		$route = $this->discoverRoute($routes, 'HEAD', '/bar');
165
		$this->assertNull($route);
166
167
		$route = $this->discoverRoute($routes, 'POST', '/bar');
168
		$this->assertNull($route);
169
170
		$route = $this->discoverRoute($routes, 'POST', '/baz');
171
		$this->assertEquals($baz, $route);
172
173
		$route = $this->discoverRoute($routes, 'HEAD', '/baz');
174
		$this->assertNull($route);
175
176
		$route = $this->discoverRoute($routes, 'GET', '/baz');
177
		$this->assertNull($route);
178
	}
179
180
	public function testMatchAttributes()
181
	{
182
		$routes = new RouteCollection();
183
		$routes->get('test', '/{foo}/{bar}/{baz}');
184
185
		$route = $this->discoverRoute($routes, 'GET', '/first/second/third');
186
		$this->assertEquals($route->getAttributes(), [
187
			'foo' => 'first',
188
			'bar' => 'second',
189
			'baz' => 'third',
190
		]);
191
192
		$route = $this->discoverRoute($routes, 'GET', '/');
193
		$this->assertNull($route);
194
195
		$route = $this->discoverRoute($routes, 'GET', '/first');
196
		$this->assertNull($route);
197
198
		$route = $this->discoverRoute($routes, 'GET', '/first/second');
199
		$this->assertNull($route);
200
201
		$route = $this->discoverRoute($routes, 'GET', '/first/second/third/fourth');
202
		$this->assertNull($route);
203
	}
204
205
	public function testMatchOptionalAttributes()
206
	{
207
		$routes = new RouteCollection();
208
		$routes->get('test', '/{foo}(/{bar}/{baz})/{qux}');
209
210
		$route = $this->discoverRoute($routes, 'GET', '/first/second');
211
		$this->assertEquals($route->getAttributes(), [
212
			'foo' => 'first',
213
			'qux' => 'second',
214
		]);
215
216
		$route = $this->discoverRoute($routes, 'GET', '/first/second/third/fourth');
217
		$this->assertEquals($route->getAttributes(), [
218
			'foo' => 'first',
219
			'bar' => 'second',
220
			'baz' => 'third',
221
			'qux' => 'fourth',
222
		]);
223
224
		$route = $this->discoverRoute($routes, 'GET', '/first');
225
		$this->assertNull($route);
226
227
		$route = $this->discoverRoute($routes, 'GET', '/first/second/third/fourth/fifth');
228
		$this->assertNull($route);
229
	}
230
231
	public function testMatchNestedOptionalAttributes()
232
	{
233
		$routes = new RouteCollection();
234
		$routes->get('test', '/{foo}(/{bar}(/{baz})/{qux})/{quux}');
235
236
		$route = $this->discoverRoute($routes, 'GET', '/first/second');
237
		$this->assertEquals($route->getAttributes(), [
238
			'foo' => 'first',
239
			'quux' => 'second',
240
		]);
241
242
		$route = $this->discoverRoute($routes, 'GET', '/first/second/third/fourth');
243
		$this->assertEquals($route->getAttributes(), [
244
			'foo' => 'first',
245
			'bar' => 'second',
246
			'qux' => 'third',
247
			'quux' => 'fourth',
248
		]);
249
250
		$route = $this->discoverRoute($routes, 'GET', '/first/second/third/fourth/fifth');
251
		$this->assertEquals($route->getAttributes(), [
252
			'foo' => 'first',
253
			'bar' => 'second',
254
			'baz' => 'third',
255
			'qux' => 'fourth',
256
			'quux' => 'fifth',
257
		]);
258
259
		$route = $this->discoverRoute($routes, 'GET', '/first');
260
		$this->assertNull($route);
261
262
		$route = $this->discoverRoute($routes, 'GET', '/first/second/third');
263
		$this->assertNull($route);
264
	}
265
266
	public function testMatchPatterns()
267
	{
268
		$routes = new RouteCollection();
269
270
		$routes->get('test', '/{foo}/{bar}(/{baz})')
271
			->addPattern('foo', '[0-9]+')
272
			->addPattern('bar', '[a-z]+')
273
			->addPattern('baz', '.*?');
274
275
		$route = $this->discoverRoute($routes, 'GET', '/1990/Surgut/Tyumen');
276
		$this->assertEquals($route->getAttributes(), [
277
			'foo' => '1990',
278
			'bar' => 'Surgut',
279
			'baz' => 'Tyumen',
280
		]);
281
282
		$route = $this->discoverRoute($routes, 'GET', '/1990/Surgut/Tyumen/Moscow');
283
		$this->assertEquals($route->getAttributes(), [
284
			'foo' => '1990',
285
			'bar' => 'Surgut',
286
			'baz' => 'Tyumen/Moscow',
287
		]);
288
289
		$route = $this->discoverRoute($routes, 'GET', '/Oops/Surgut/Tyumen/Moscow');
290
		$this->assertNull($route);
291
292
		$route = $this->discoverRoute($routes, 'GET', '/1990/2018/Moscow');
293
		$this->assertNull($route);
294
	}
295
296
	public function testMatchRouteNotFoundException()
297
	{
298
		$request = (new ServerRequestFactory)
299
		->createServerRequest('GET', '/oops');
300
301
		$routes = new RouteCollection();
302
		$routes->get('test', '/');
303
304
		$router = new Router();
305
		$router->addRoutes($routes);
306
307
		$this->expectException(RouteNotFoundException::class);
308
		$this->expectExceptionMessage('Unable to find a route for the request');
309
		$router->match($request);
310
	}
311
312
	public function testMatchMethodNotAllowedException()
313
	{
314
		$request = (new ServerRequestFactory)
315
		->createServerRequest('POST', '/');
316
317
		$routes = new RouteCollection();
318
		$routes->route('test', '/', ['HEAD', 'GET', 'OPTIONS']);
319
320
		$router = new Router();
321
		$router->addRoutes($routes);
322
323
		$this->expectException(MethodNotAllowedException::class);
324
		$this->expectExceptionMessage('The requested resource is not available for the HTTP method');
325
326
		try {
327
			$router->match($request);
328
		} catch (MethodNotAllowedException $e) {
329
			$this->assertEquals(['HEAD', 'GET', 'OPTIONS'], $e->getAllowedMethods());
330
			throw $e;
331
		}
332
	}
333
334
	public function testHandle()
335
	{
336
		$routes = new RouteCollection();
337
338
		$routes->get('test', '/{foo}/{bar}/{baz}')
339
		->addMiddleware(new SetRequestAttributesWithoutRouteIdToResponseHeaderMiddlewareTest())
340
		->addMiddleware(new SetRouteIdFromRequestAttributesToResponseHeaderMiddlewareTest())
341
		->addMiddleware(new BazMiddlewareTest())
342
		->addMiddleware(new QuxMiddlewareTest());
343
344
		$router = new Router();
345
		$router->addRoutes($routes);
346
		$router->addMiddleware(new FooMiddlewareTest());
347
		$router->addMiddleware(new BarMiddlewareTest());
348
349
		$request = (new ServerRequestFactory)
350
		->createServerRequest('GET', '/first/second/third');
351
352
		$response = $router->handle($request);
353
354
		$this->assertEquals(['test'], $response->getHeader('x-route-id'));
355
356
		$this->assertEquals(['first, second, third'], $response->getHeader('x-request-attributes'));
357
358
		$this->assertEquals([
359
			'qux',
360
			'baz',
361
			'bar',
362
			'foo',
363
		], $response->getHeader('x-middleware'));
364
	}
365
366
	public function testExceptions()
367
	{
368
		$request = (new ServerRequestFactory)
369
		->createServerRequest('GET', '/');
370
371
		$routeNotFoundException = new RouteNotFoundException($request);
372
		$this->assertInstanceOf(HttpExceptionInterface::class, $routeNotFoundException);
373
		$this->assertInstanceOf(\RuntimeException::class, $routeNotFoundException);
374
		$this->assertEquals($request, $routeNotFoundException->getRequest());
375
376
		$methodNotAllowedException = new MethodNotAllowedException($request, ['HEAD', 'GET']);
377
		$this->assertInstanceOf(HttpExceptionInterface::class, $methodNotAllowedException);
378
		$this->assertInstanceOf(\RuntimeException::class, $methodNotAllowedException);
379
		$this->assertEquals($request, $methodNotAllowedException->getRequest());
380
		$this->assertEquals(['HEAD', 'GET'], $methodNotAllowedException->getAllowedMethods());
381
		$this->assertEquals('HEAD,GET', $methodNotAllowedException->getAllowedMethodsAsString());
382
	}
383
384
	private function discoverRoute(RouteCollectionInterface $routes, string $method, string $uri) : ?RouteInterface
385
	{
386
		$router = new Router();
387
		$router->addRoutes($routes);
388
389
		$request = (new ServerRequestFactory)
390
		->createServerRequest($method, $uri);
391
392
		try {
393
			return $router->match($request);
394
		} catch (HttpExceptionInterface $error) {
395
			return null;
396
		}
397
	}
398
}
399