2022-09-13 15:13:11 +02:00
|
|
|
<?php
|
|
|
|
// RouterTest.php
|
|
|
|
// Created: 2022-01-20
|
2025-03-02 02:08:45 +00:00
|
|
|
// Updated: 2025-03-02
|
2022-09-13 15:13:11 +02:00
|
|
|
|
|
|
|
declare(strict_types=1);
|
|
|
|
|
|
|
|
use PHPUnit\Framework\TestCase;
|
2024-07-31 18:12:46 +00:00
|
|
|
use PHPUnit\Framework\Attributes\CoversClass;
|
2025-02-28 20:44:09 +00:00
|
|
|
use Index\Http\{HttpHeaders,HttpRequest,HttpUri,NullStream};
|
2025-03-02 02:08:45 +00:00
|
|
|
use Index\Http\Routing\{Filter,Route,Router,RouteHandler,RouteHandlerCommon};
|
2022-09-13 15:13:11 +02:00
|
|
|
|
|
|
|
/**
|
2024-03-30 16:24:34 +00:00
|
|
|
* This test isn't super representative of the current functionality
|
|
|
|
* it mostly just does the same tests that were done against the previous implementation
|
2022-09-13 15:13:11 +02:00
|
|
|
*/
|
2025-03-02 02:08:45 +00:00
|
|
|
#[CoversClass(Filter::class)]
|
|
|
|
#[CoversClass(Route::class)]
|
2025-02-28 22:44:56 +00:00
|
|
|
#[CoversClass(Router::class)]
|
2024-07-31 18:12:46 +00:00
|
|
|
#[CoversClass(RouteHandler::class)]
|
2025-01-18 21:57:27 +00:00
|
|
|
#[CoversClass(RouteHandlerCommon::class)]
|
2022-09-13 15:13:11 +02:00
|
|
|
final class RouterTest extends TestCase {
|
|
|
|
public function testRouter(): void {
|
2025-02-28 22:44:56 +00:00
|
|
|
$router1 = new Router;
|
2024-03-30 16:24:34 +00:00
|
|
|
|
2025-03-02 02:08:45 +00:00
|
|
|
$router1->route('GET', '/', fn() => 'get');
|
|
|
|
$router1->route('POST', '/', fn() => 'post');
|
|
|
|
$router1->route('DELETE', '/', fn() => 'delete');
|
|
|
|
$router1->route('PATCH', '/', fn() => 'patch');
|
|
|
|
$router1->route('PUT', '/', fn() => 'put');
|
|
|
|
$router1->route('CUSTOM', '/', fn() => 'wacky');
|
2024-03-30 16:24:34 +00:00
|
|
|
|
|
|
|
$this->assertEquals('get', $router1->resolve('GET', '/')->dispatch([]));
|
|
|
|
$this->assertEquals('wacky', $router1->resolve('CUSTOM', '/')->dispatch([]));
|
|
|
|
|
2025-03-02 02:08:45 +00:00
|
|
|
$router1->filter('/', function() { /* this one intentionally does nothing */ });
|
2024-03-30 16:24:34 +00:00
|
|
|
|
|
|
|
// registration order should matter
|
2025-03-02 02:08:45 +00:00
|
|
|
$router1->filter('/deep', fn() => 'deep');
|
2024-03-30 16:24:34 +00:00
|
|
|
|
|
|
|
$postRoot = $router1->resolve('POST', '/');
|
2025-03-02 02:08:45 +00:00
|
|
|
$this->assertNull($postRoot->runFilters([]));
|
2024-03-30 16:24:34 +00:00
|
|
|
$this->assertEquals('post', $postRoot->dispatch([]));
|
|
|
|
|
2025-03-02 02:08:45 +00:00
|
|
|
$this->assertEquals('deep', $router1->resolve('GET', '/deep/nothing')->runFilters([]));
|
2024-03-30 16:24:34 +00:00
|
|
|
|
2025-03-02 02:08:45 +00:00
|
|
|
$router1->filter('/user/([A-Za-z0-9]+)/below', fn(string $user) => 'warioware below ' . $user);
|
2024-03-30 16:24:34 +00:00
|
|
|
|
2025-03-02 02:08:45 +00:00
|
|
|
$router1->route('GET', '/user/static', fn() => 'the static one');
|
|
|
|
$router1->route('GET', '/user/static/below', fn() => 'below the static one');
|
|
|
|
$router1->route('GET', '/user/([A-Za-z0-9]+)', fn(string $user) => $user);
|
|
|
|
$router1->route('GET', '/user/([A-Za-z0-9]+)/below', fn(string $user) => 'below ' . $user);
|
2024-03-30 16:24:34 +00:00
|
|
|
|
|
|
|
$this->assertEquals('below the static one', $router1->resolve('GET', '/user/static/below')->dispatch([]));
|
|
|
|
|
|
|
|
$getWariowareBelowFlashwave = $router1->resolve('GET', '/user/flashwave/below');
|
2025-03-02 02:08:45 +00:00
|
|
|
$this->assertEquals('warioware below flashwave', $getWariowareBelowFlashwave->runFilters([]));
|
2024-03-30 16:24:34 +00:00
|
|
|
$this->assertEquals('below flashwave', $getWariowareBelowFlashwave->dispatch([]));
|
|
|
|
|
2025-02-28 22:44:56 +00:00
|
|
|
$router2 = new Router;
|
2025-03-02 02:08:45 +00:00
|
|
|
$router2->filter('/', fn() => 'meow');
|
|
|
|
$router2->route('GET', '/rules', fn() => 'rules page');
|
|
|
|
$router2->route('GET', '/contact', fn() => 'contact page');
|
|
|
|
$router2->route('GET', '/25252', fn() => 'numeric test');
|
2024-03-30 16:24:34 +00:00
|
|
|
|
|
|
|
$getRules = $router2->resolve('GET', '/rules');
|
2025-03-02 02:08:45 +00:00
|
|
|
$this->assertEquals('meow', $getRules->runFilters([]));
|
2024-03-30 16:24:34 +00:00
|
|
|
$this->assertEquals('rules page', $getRules->dispatch([]));
|
|
|
|
|
|
|
|
$get25252 = $router2->resolve('GET', '/25252');
|
2025-03-02 02:08:45 +00:00
|
|
|
$this->assertEquals('meow', $get25252->runFilters([]));
|
2024-03-30 16:24:34 +00:00
|
|
|
$this->assertEquals('numeric test', $get25252->dispatch([]));
|
2022-09-13 15:13:11 +02:00
|
|
|
}
|
2023-09-07 22:08:31 +00:00
|
|
|
|
|
|
|
public function testAttribute(): void {
|
2025-02-28 22:44:56 +00:00
|
|
|
$router = new Router;
|
2024-10-02 02:09:21 +00:00
|
|
|
$handler = new class implements RouteHandler {
|
2025-01-18 21:57:27 +00:00
|
|
|
use RouteHandlerCommon;
|
2024-10-02 02:09:21 +00:00
|
|
|
|
2025-03-02 02:08:45 +00:00
|
|
|
#[Route('GET', '/')]
|
2024-12-02 01:30:36 +00:00
|
|
|
public function getIndex(): string {
|
2023-09-07 22:08:31 +00:00
|
|
|
return 'index';
|
|
|
|
}
|
|
|
|
|
2025-03-02 02:08:45 +00:00
|
|
|
#[Route('POST', '/avatar')]
|
2024-12-02 01:30:36 +00:00
|
|
|
public function postAvatar(): string {
|
2023-09-07 22:08:31 +00:00
|
|
|
return 'avatar';
|
|
|
|
}
|
|
|
|
|
2025-03-02 02:08:45 +00:00
|
|
|
#[Route('PUT', '/static')]
|
2024-12-02 01:30:36 +00:00
|
|
|
public static function putStatic(): string {
|
2023-09-07 22:08:31 +00:00
|
|
|
return 'static';
|
|
|
|
}
|
|
|
|
|
2025-03-02 02:08:45 +00:00
|
|
|
#[Route('GET', '/meow')]
|
|
|
|
#[Route('POST', '/meow')]
|
2024-12-02 01:30:36 +00:00
|
|
|
public function multiple(): string {
|
2023-09-07 22:08:31 +00:00
|
|
|
return 'meow';
|
|
|
|
}
|
2023-09-07 22:37:04 +00:00
|
|
|
|
2025-03-02 02:08:45 +00:00
|
|
|
#[Filter('/filter')]
|
|
|
|
public function useFilter(): string {
|
2023-09-08 00:09:23 +00:00
|
|
|
return 'this intercepts';
|
|
|
|
}
|
|
|
|
|
2025-03-02 02:08:45 +00:00
|
|
|
#[Route('GET', '/filter')]
|
|
|
|
public function getFilter(): string {
|
2023-09-08 00:09:23 +00:00
|
|
|
return 'this is intercepted';
|
|
|
|
}
|
|
|
|
|
2024-12-02 01:30:36 +00:00
|
|
|
public function hasNoAttr(): string {
|
2023-09-07 22:37:04 +00:00
|
|
|
return 'not a route';
|
|
|
|
}
|
2023-09-07 22:08:31 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
$router->register($handler);
|
2024-03-30 16:24:34 +00:00
|
|
|
$this->assertFalse($router->resolve('GET', '/soap')->hasHandler());
|
|
|
|
|
|
|
|
$patchAvatar = $router->resolve('PATCH', '/avatar');
|
|
|
|
$this->assertFalse($patchAvatar->hasHandler());
|
2025-01-18 21:57:27 +00:00
|
|
|
$this->assertTrue(!empty($patchAvatar->supportedMethods));
|
|
|
|
$this->assertEquals(['POST'], $patchAvatar->supportedMethods);
|
2024-03-30 16:24:34 +00:00
|
|
|
|
|
|
|
$this->assertEquals('index', $router->resolve('GET', '/')->dispatch([]));
|
|
|
|
$this->assertEquals('avatar', $router->resolve('POST', '/avatar')->dispatch([]));
|
|
|
|
$this->assertEquals('static', $router->resolve('PUT', '/static')->dispatch([]));
|
|
|
|
$this->assertEquals('meow', $router->resolve('GET', '/meow')->dispatch([]));
|
|
|
|
$this->assertEquals('meow', $router->resolve('POST', '/meow')->dispatch([]));
|
|
|
|
|
2025-03-02 02:08:45 +00:00
|
|
|
// stopping on filter is the dispatcher's job
|
|
|
|
$getFilter = $router->resolve('GET', '/filter');
|
|
|
|
$this->assertEquals('this intercepts', $getFilter->runFilters([]));
|
|
|
|
$this->assertEquals('this is intercepted', $getFilter->dispatch([]));
|
2023-09-07 22:08:31 +00:00
|
|
|
}
|
2024-04-02 17:27:06 +00:00
|
|
|
|
|
|
|
public function testEEPROMSituation(): void {
|
2025-02-28 22:44:56 +00:00
|
|
|
$router = new Router;
|
2024-04-02 17:27:06 +00:00
|
|
|
|
2025-03-02 02:08:45 +00:00
|
|
|
$router->route('OPTIONS', '/uploads/([A-Za-z0-9\-_]+)(?:\.(t|json))?', function() {});
|
|
|
|
$router->route('GET', '/uploads/([A-Za-z0-9\-_]+)(?:\.(t|json))?', function() {});
|
|
|
|
$router->route('DELETE', '/uploads/([A-Za-z0-9\-_]+)', function() {});
|
2024-04-02 17:27:06 +00:00
|
|
|
|
|
|
|
$resolved = $router->resolve('DELETE', '/uploads/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA');
|
|
|
|
|
2025-01-18 21:57:27 +00:00
|
|
|
$this->assertEquals(['OPTIONS', 'GET', 'DELETE'], $resolved->supportedMethods);
|
2024-04-02 17:27:06 +00:00
|
|
|
}
|
2024-08-18 16:41:02 +00:00
|
|
|
|
2025-03-02 02:08:45 +00:00
|
|
|
public function testFilterInterceptionOnRoot(): void {
|
2025-02-28 22:44:56 +00:00
|
|
|
$router = new Router;
|
2025-03-02 02:08:45 +00:00
|
|
|
$router->filter('/', fn() => 'expected');
|
|
|
|
$router->route('GET', '/', fn() => 'unexpected');
|
|
|
|
$router->route('GET', '/test', fn() => 'also unexpected');
|
2024-08-18 16:41:02 +00:00
|
|
|
|
|
|
|
ob_start();
|
2025-02-28 20:44:09 +00:00
|
|
|
$router->dispatch(new HttpRequest('1.1', [], NullStream::instance(), [], 'GET', HttpUri::createUri('/'), [], []));
|
2024-08-18 16:41:02 +00:00
|
|
|
$this->assertEquals('expected', ob_get_clean());
|
|
|
|
|
|
|
|
ob_start();
|
2025-02-28 20:44:09 +00:00
|
|
|
$router->dispatch(new HttpRequest('1.1', [], NullStream::instance(), [], 'GET', HttpUri::createUri('/test'), [], []));
|
2024-08-18 16:41:02 +00:00
|
|
|
$this->assertEquals('expected', ob_get_clean());
|
|
|
|
|
|
|
|
ob_start();
|
2025-02-28 20:44:09 +00:00
|
|
|
$router->dispatch(new HttpRequest('1.1', [], NullStream::instance(), [], 'GET', HttpUri::createUri('/error'), [], []));
|
2024-08-18 16:41:02 +00:00
|
|
|
$this->assertEquals('expected', ob_get_clean());
|
|
|
|
}
|
2022-09-13 15:13:11 +02:00
|
|
|
}
|