Passed
Push — master ( 00dd55...b390e5 )
by Anatoly
02:05
created

RouterTest::testAddRoutesFromRouteCollection()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

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