Passed
Push — master ( 10fd01...6c0fb4 )
by Anatoly
01:35
created

RouterTest::testHandle()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 22
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 16
nc 1
nop 0
dl 0
loc 22
rs 9.7333
c 0
b 0
f 0
1
<?php
2
3
namespace Sunrise\Http\ServerRequest\Tests;
4
5
use Fig\Http\Message\RequestMethodInterface;
6
use PHPUnit\Framework\TestCase;
7
use Psr\Http\Message\ResponseInterface;
8
use Psr\Http\Message\ServerRequestInterface;
9
use Psr\Http\Server\MiddlewareInterface;
10
use Psr\Http\Server\RequestHandlerInterface;
11
use Sunrise\Http\Message\ResponseFactory;
12
use Sunrise\Http\Router\RouteInterface;
13
use Sunrise\Http\Router\Router;
14
use Sunrise\Http\Router\RouterInterface;
15
use Sunrise\Http\ServerRequest\ServerRequestFactory;
16
use Sunrise\Http\Router\Exception\BadRequestException;
17
use Sunrise\Http\Router\Exception\HttpException;
18
use Sunrise\Http\Router\Exception\HttpExceptionInterface;
19
use Sunrise\Http\Router\Exception\MethodNotAllowedException;
20
use Sunrise\Http\Router\Exception\PageNotFoundException;
21
22
class RouterTest extends TestCase
23
{
24
	public function testConstructor()
25
	{
26
		$router = new Router();
27
28
		$this->assertInstanceOf(RouterInterface::class, $router);
29
		$this->assertInstanceOf(RequestHandlerInterface::class, $router);
30
	}
31
32
	public function testCreateRoute()
33
	{
34
		$router = new Router();
35
36
		$action = $this->getRouteAction();
37
		$route = $router->add('home', '/', $action);
38
39
		$this->assertInstanceOf(RouteInterface::class, $route);
40
		$this->assertEquals('home', $route->getId());
41
		$this->assertEquals('/', $route->getPath());
42
		$this->assertEquals($action, $route->getAction());
43
		$this->assertEquals([], $route->getMethods());
44
	}
45
46
	public function testCreateRouteWithMethodHead()
47
	{
48
		$router = new Router();
49
50
		$action = $this->getRouteAction();
51
		$route = $router->head('home', '/', $action);
52
53
		$this->assertInstanceOf(RouteInterface::class, $route);
54
		$this->assertEquals('home', $route->getId());
55
		$this->assertEquals('/', $route->getPath());
56
		$this->assertEquals($action, $route->getAction());
57
		$this->assertEquals([RequestMethodInterface::METHOD_HEAD], $route->getMethods());
58
	}
59
60
	public function testCreateRouteWithMethodGet()
61
	{
62
		$router = new Router();
63
64
		$action = $this->getRouteAction();
65
		$route = $router->get('home', '/', $action);
66
67
		$this->assertInstanceOf(RouteInterface::class, $route);
68
		$this->assertEquals('home', $route->getId());
69
		$this->assertEquals('/', $route->getPath());
70
		$this->assertEquals($action, $route->getAction());
71
		$this->assertEquals([RequestMethodInterface::METHOD_GET], $route->getMethods());
72
	}
73
74
	public function testCreateRouteWithMethodPost()
75
	{
76
		$router = new Router();
77
78
		$action = $this->getRouteAction();
79
		$route = $router->post('home', '/', $action);
80
81
		$this->assertInstanceOf(RouteInterface::class, $route);
82
		$this->assertEquals('home', $route->getId());
83
		$this->assertEquals('/', $route->getPath());
84
		$this->assertEquals($action, $route->getAction());
85
		$this->assertEquals([RequestMethodInterface::METHOD_POST], $route->getMethods());
86
	}
87
88
	public function testCreateRouteWithMethodPut()
89
	{
90
		$router = new Router();
91
92
		$action = $this->getRouteAction();
93
		$route = $router->put('home', '/', $action);
94
95
		$this->assertInstanceOf(RouteInterface::class, $route);
96
		$this->assertEquals('home', $route->getId());
97
		$this->assertEquals('/', $route->getPath());
98
		$this->assertEquals($action, $route->getAction());
99
		$this->assertEquals([RequestMethodInterface::METHOD_PUT], $route->getMethods());
100
	}
101
102
	public function testCreateRouteWithMethodPatch()
103
	{
104
		$router = new Router();
105
106
		$action = $this->getRouteAction();
107
		$route = $router->patch('home', '/', $action);
108
109
		$this->assertInstanceOf(RouteInterface::class, $route);
110
		$this->assertEquals('home', $route->getId());
111
		$this->assertEquals('/', $route->getPath());
112
		$this->assertEquals($action, $route->getAction());
113
		$this->assertEquals([RequestMethodInterface::METHOD_PATCH], $route->getMethods());
114
	}
115
116
	public function testCreateRouteWithMethodDelete()
117
	{
118
		$router = new Router();
119
120
		$action = $this->getRouteAction();
121
		$route = $router->delete('home', '/', $action);
122
123
		$this->assertInstanceOf(RouteInterface::class, $route);
124
		$this->assertEquals('home', $route->getId());
125
		$this->assertEquals('/', $route->getPath());
126
		$this->assertEquals($action, $route->getAction());
127
		$this->assertEquals([RequestMethodInterface::METHOD_DELETE], $route->getMethods());
128
	}
129
130
	public function testCreateRouteWithMethodPurge()
131
	{
132
		$router = new Router();
133
134
		$action = $this->getRouteAction();
135
		$route = $router->purge('home', '/', $action);
136
137
		$this->assertInstanceOf(RouteInterface::class, $route);
138
		$this->assertEquals('home', $route->getId());
139
		$this->assertEquals('/', $route->getPath());
140
		$this->assertEquals($action, $route->getAction());
141
		$this->assertEquals([RequestMethodInterface::METHOD_PURGE], $route->getMethods());
142
	}
143
144
	public function testCreateRouteWithMethodOptions()
145
	{
146
		$router = new Router();
147
148
		$action = $this->getRouteAction();
149
		$route = $router->options('home', '/', $action);
150
151
		$this->assertInstanceOf(RouteInterface::class, $route);
152
		$this->assertEquals('home', $route->getId());
153
		$this->assertEquals('/', $route->getPath());
154
		$this->assertEquals($action, $route->getAction());
155
		$this->assertEquals([RequestMethodInterface::METHOD_OPTIONS], $route->getMethods());
156
	}
157
158
	public function testCreateRouteWithMethodTrace()
159
	{
160
		$router = new Router();
161
162
		$action = $this->getRouteAction();
163
		$route = $router->trace('home', '/', $action);
164
165
		$this->assertInstanceOf(RouteInterface::class, $route);
166
		$this->assertEquals('home', $route->getId());
167
		$this->assertEquals('/', $route->getPath());
168
		$this->assertEquals($action, $route->getAction());
169
		$this->assertEquals([RequestMethodInterface::METHOD_TRACE], $route->getMethods());
170
	}
171
172
	public function testCreateRouteWithMethodConnect()
173
	{
174
		$router = new Router();
175
176
		$action = $this->getRouteAction();
177
		$route = $router->connect('home', '/', $action);
178
179
		$this->assertInstanceOf(RouteInterface::class, $route);
180
		$this->assertEquals('home', $route->getId());
181
		$this->assertEquals('/', $route->getPath());
182
		$this->assertEquals($action, $route->getAction());
183
		$this->assertEquals([RequestMethodInterface::METHOD_CONNECT], $route->getMethods());
184
	}
185
186
	public function testCreateRouteWithMethodSafe()
187
	{
188
		$router = new Router();
189
190
		$action = $this->getRouteAction();
191
		$route = $router->safe('home', '/', $action);
192
193
		$this->assertInstanceOf(RouteInterface::class, $route);
194
		$this->assertEquals('home', $route->getId());
195
		$this->assertEquals('/', $route->getPath());
196
		$this->assertEquals($action, $route->getAction());
197
		$this->assertEquals([
198
			RequestMethodInterface::METHOD_HEAD,
199
			RequestMethodInterface::METHOD_GET,
200
		], $route->getMethods());
201
	}
202
203
	public function testCreateRouteWithMethodAny()
204
	{
205
		$router = new Router();
206
207
		$action = $this->getRouteAction();
208
		$route = $router->any('home', '/', $action);
209
210
		$this->assertInstanceOf(RouteInterface::class, $route);
211
		$this->assertEquals('home', $route->getId());
212
		$this->assertEquals('/', $route->getPath());
213
		$this->assertEquals($action, $route->getAction());
214
		$this->assertEquals([
215
			RequestMethodInterface::METHOD_HEAD,
216
			RequestMethodInterface::METHOD_GET,
217
			RequestMethodInterface::METHOD_POST,
218
			RequestMethodInterface::METHOD_PUT,
219
			RequestMethodInterface::METHOD_PATCH,
220
			RequestMethodInterface::METHOD_DELETE,
221
			RequestMethodInterface::METHOD_PURGE,
222
			RequestMethodInterface::METHOD_OPTIONS,
223
			RequestMethodInterface::METHOD_TRACE,
224
			RequestMethodInterface::METHOD_CONNECT,
225
		], $route->getMethods());
226
	}
227
228
	public function testMatch()
229
	{
230
		$router = new Router();
231
232
		$router->get('home', '/', $this->getRouteAction());
233
234
		$request = (new ServerRequestFactory)
235
		->createServerRequest('GET', '/');
236
237
		$route = $router->match($request);
238
239
		$this->assertEquals('home', $route->getId());
240
	}
241
242
	public function testMatchPageNotFoundException()
243
	{
244
		$this->expectException(PageNotFoundException::class);
245
246
		$router = new Router();
247
248
		$router->get('home', '/', $this->getRouteAction());
249
250
		$request = (new ServerRequestFactory)
251
		->createServerRequest('GET', '/404');
252
253
		$router->match($request);
254
	}
255
256
	public function testMatchMethodNotAllowedException()
257
	{
258
		$this->expectException(MethodNotAllowedException::class);
259
260
		$router = new Router();
261
262
		$router->safe('home', '/', $this->getRouteAction());
263
264
		$request = (new ServerRequestFactory)
265
		->createServerRequest('POST', '/');
266
267
		try
268
		{
269
			$router->match($request);
270
		}
271
		catch (MethodNotAllowedException $e)
272
		{
273
			$allowedMethods[] = RequestMethodInterface::METHOD_HEAD;
0 ignored issues
show
Comprehensibility Best Practice introduced by
$allowedMethods was never initialized. Although not strictly required by PHP, it is generally a good practice to add $allowedMethods = array(); before regardless.
Loading history...
274
			$allowedMethods[] = RequestMethodInterface::METHOD_GET;
275
276
			$this->assertEquals($allowedMethods, $e->getAllowedMethods());
277
278
			throw $e;
279
		}
280
		catch (\Throwable $e)
281
		{
282
			throw $e;
283
		}
284
	}
285
286
	public function testMatchWithRouteAttributes()
287
	{
288
		$router = new Router();
289
290
		$router->patch('post.update', '/post/{id}', $this->getRouteAction());
291
292
		$request = (new ServerRequestFactory)
293
		->createServerRequest('PATCH', '/post/100');
294
295
		$route = $router->match($request);
296
297
		$this->assertEquals('post.update', $route->getId());
298
		$this->assertArraySubset(['id' => '100'], $route->getAttributes());
299
	}
300
301
	public function testMatchWithSeveralRouteAttributes()
302
	{
303
		$router = new Router();
304
305
		$router->patch('post.update', '/post/{section}/{post}', $this->getRouteAction());
306
307
		$request = (new ServerRequestFactory)
308
		->createServerRequest('PATCH', '/post/100/200');
309
310
		$route = $router->match($request);
311
312
		$this->assertEquals('post.update', $route->getId());
313
		$this->assertArraySubset([
314
			'section' => '100',
315
			'post' => '200',
316
		], $route->getAttributes());
317
	}
318
319
	public function testMatchWithRoutePatterns()
320
	{
321
		$router = new Router();
322
323
		$route = $router->patch('menu.item.move', '/menu/item/{id}/{direction}', $this->getRouteAction());
324
		$route->pattern('id', '\d+');
325
		$route->pattern('direction', 'up|down');
326
327
		$route = $router->match((new ServerRequestFactory)
328
		->createServerRequest('PATCH', '/menu/item/100/up'));
329
330
		$this->assertEquals('menu.item.move', $route->getId());
331
		$this->assertArraySubset([
332
			'id' => '100',
333
			'direction' => 'up',
334
		], $route->getAttributes());
335
336
		$route = $router->match((new ServerRequestFactory)
337
		->createServerRequest('PATCH', '/menu/item/100/down'));
338
339
		$this->assertEquals('menu.item.move', $route->getId());
340
		$this->assertArraySubset([
341
			'id' => '100',
342
			'direction' => 'down',
343
		], $route->getAttributes());
344
	}
345
346
	public function testMatchPageNotFoundExceptionDueInvalidAttributes()
347
	{
348
		$this->expectException(PageNotFoundException::class);
349
350
		$router = new Router();
351
352
		$route = $router->delete('post.delete', '/post/{id}', $this->getRouteAction());
353
		$route->pattern('id', '\d+');
354
355
		$request = (new ServerRequestFactory)
356
		->createServerRequest('DELETE', '/post/a');
357
358
		$router->match($request);
359
	}
360
361
	public function testHandle()
362
	{
363
		$router = new Router();
364
		$router->middleware($this->getRouteMiddlewareFoo());
365
		$router->middleware($this->getRouteMiddlewareBar());
366
367
		$route = $router->get('home', '/', $this->getRouteAction());
368
		$route->middleware($this->getRouteMiddlewareBaz());
369
		$route->middleware($this->getRouteMiddlewareQux());
370
371
		$request = (new ServerRequestFactory)
372
		->createServerRequest('GET', '/');
373
374
		$response = $router->handle($request);
375
376
		$this->assertEquals([
377
			'route',
378
			'middleware-qux',
379
			'middleware-baz',
380
			'middleware-bar',
381
			'middleware-foo',
382
		], $response->getHeader('x-queue'));
383
	}
384
385
	public function testExceptions()
386
	{
387
		$request = (new ServerRequestFactory)
388
		->createServerRequest('GET', '/');
389
390
		$response = (new ResponseFactory)
391
		->createResponse(200);
392
393
		$httpException = new HttpException($request, $response);
394
		$this->assertInstanceOf(\RuntimeException::class, $httpException);
395
		$this->assertInstanceOf(HttpExceptionInterface::class, $httpException);
396
		$this->assertEquals($request, $httpException->getRequest());
397
		$this->assertEquals($response, $httpException->getResponse());
398
399
		$badRequestException = new BadRequestException($request);
400
		$this->assertInstanceOf(HttpException::class, $badRequestException);
401
		$this->assertEquals(400, $badRequestException->getResponse()->getStatusCode());
402
403
		$pageNotFoundException = new PageNotFoundException($request);
404
		$this->assertInstanceOf(HttpException::class, $pageNotFoundException);
405
		$this->assertEquals(404, $pageNotFoundException->getResponse()->getStatusCode());
406
407
		$methodNotAllowedException = new MethodNotAllowedException($request, ['HEAD', 'GET']);
408
		$this->assertInstanceOf(HttpException::class, $methodNotAllowedException);
409
		$this->assertEquals(['HEAD', 'GET'], $methodNotAllowedException->getAllowedMethods());
410
		$this->assertEquals(405, $methodNotAllowedException->getResponse()->getStatusCode());
411
	}
412
413
	private function getRouteAction() : callable
414
	{
415
		return function(ServerRequestInterface $request, ResponseInterface $response) : ResponseInterface
416
		{
417
			return $response->withAddedHeader('x-queue', 'route');
418
		};
419
	}
420
421
	private function getRouteMiddlewareFoo() : MiddlewareInterface
422
	{
423
		return new class implements MiddlewareInterface
424
		{
425
			public function process(ServerRequestInterface $request, RequestHandlerInterface $handler) : ResponseInterface
426
			{
427
				return $handler->handle($request)->withAddedHeader('x-queue', 'middleware-foo');
428
			}
429
		};
430
	}
431
432
	private function getRouteMiddlewareBar() : MiddlewareInterface
433
	{
434
		return new class implements MiddlewareInterface
435
		{
436
			public function process(ServerRequestInterface $request, RequestHandlerInterface $handler) : ResponseInterface
437
			{
438
				return $handler->handle($request)->withAddedHeader('x-queue', 'middleware-bar');
439
			}
440
		};
441
	}
442
443
	private function getRouteMiddlewareBaz() : MiddlewareInterface
444
	{
445
		return new class implements MiddlewareInterface
446
		{
447
			public function process(ServerRequestInterface $request, RequestHandlerInterface $handler) : ResponseInterface
448
			{
449
				return $handler->handle($request)->withAddedHeader('x-queue', 'middleware-baz');
450
			}
451
		};
452
	}
453
454
	private function getRouteMiddlewareQux() : MiddlewareInterface
455
	{
456
		return new class implements MiddlewareInterface
457
		{
458
			public function process(ServerRequestInterface $request, RequestHandlerInterface $handler) : ResponseInterface
459
			{
460
				return $handler->handle($request)->withAddedHeader('x-queue', 'middleware-qux');
461
			}
462
		};
463
	}
464
}
465