2022-09-13 15:13:11 +02:00
< ? php
// RouterTest.php
// Created: 2022-01-20
2025-03-07 00:25:00 +00:00
// Updated: 2025-03-07
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-03-07 00:25:00 +00:00
use Index\Http\ { HttpHeaders , HttpResponseBuilder , HttpRequest , HttpUri , NullStream };
use Index\Http\Routing\ {
ExactRoute ,
FilterInfo ,
PatternRoute , PrefixFilter ,
RouteInfo , RouteHandler , RouteHandlerCommon , Router
};
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-02-28 22:44:56 +00:00
#[CoversClass(Router::class)]
2025-03-07 00:25:00 +00:00
#[CoversClass(RouteInfo::class)]
#[CoversClass(PrefixFilter::class)]
#[CoversClass(ExactRoute::class)]
2024-07-31 18:12:46 +00:00
#[CoversClass(RouteHandler::class)]
2025-01-18 21:57:27 +00:00
#[CoversClass(RouteHandlerCommon::class)]
2025-03-07 00:25:00 +00:00
#[CoversClass(PatternRoute::class)]
#[CoversClass(FilterInfo::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-07 00:25:00 +00:00
$router1 -> route ( RouteInfo :: exact ( 'GET' , '/' , fn () => 'get' ));
$router1 -> route ( RouteInfo :: exact ( 'POST' , '/' , fn () => 'post' ));
$router1 -> route ( RouteInfo :: exact ( 'DELETE' , '/' , fn () => 'delete' ));
$router1 -> route ( RouteInfo :: exact ( 'PATCH' , '/' , fn () => 'patch' ));
$router1 -> route ( RouteInfo :: exact ( 'PUT' , '/' , fn () => 'put' ));
$router1 -> route ( RouteInfo :: exact ( 'CUSTOM' , '/' , fn () => 'wacky' ));
2024-03-30 16:24:34 +00:00
2025-03-07 00:25:00 +00:00
$this -> assertEquals ( 'get' , ( string ) $router1 -> handle ( new HttpRequest ( '1.1' , [], NullStream :: instance (), [], 'GET' , HttpUri :: createUri ( '/' ), [], [])) -> getBody ());
$this -> assertEquals ( 'wacky' , ( string ) $router1 -> handle ( new HttpRequest ( '1.1' , [], NullStream :: instance (), [], 'CUSTOM' , HttpUri :: createUri ( '/' ), [], [])) -> getBody ());
2024-03-30 16:24:34 +00:00
2025-03-07 00:25:00 +00:00
$router1 -> filter ( FilterInfo :: prefix ( '/' , function () { /* this one intentionally does nothing */ }));
2024-03-30 16:24:34 +00:00
// registration order should matter
2025-03-07 00:25:00 +00:00
$router1 -> filter ( FilterInfo :: prefix ( '/deep' , fn () => 'deep' ));
2024-03-30 16:24:34 +00:00
2025-03-07 00:25:00 +00:00
$router1 -> filter ( FilterInfo :: pattern ( '#^/user/([A-Za-z0-9]+)/below#u' , fn ( string $user ) => 'warioware below ' . $user ));
2024-03-30 16:24:34 +00:00
2025-03-07 00:25:00 +00:00
$router1 -> route ( RouteInfo :: exact ( 'GET' , '/user/static' , fn () => 'the static one' ));
$router1 -> route ( RouteInfo :: exact ( 'GET' , '/user/static/below' , fn () => 'below the static one' ));
$router1 -> route ( RouteInfo :: pattern ( 'GET' , '#^/user/([A-Za-z0-9]+)$#uD' , fn ( string $user ) => $user ));
$router1 -> route ( RouteInfo :: pattern ( 'GET' , '#^/user/([A-Za-z0-9]+)/below$#uD' , fn ( string $user ) => 'below ' . $user ));
2024-03-30 16:24:34 +00:00
2025-03-07 00:25:00 +00:00
$this -> assertEquals ( 'warioware below static' , ( string ) $router1 -> handle ( new HttpRequest ( '1.1' , [], NullStream :: instance (), [], 'GET' , HttpUri :: createUri ( '/user/static/below' ), [], [])) -> getBody ());
2024-03-30 16:24:34 +00:00
2025-02-28 22:44:56 +00:00
$router2 = new Router ;
2025-03-07 00:25:00 +00:00
$router2 -> filter ( FilterInfo :: prefix ( '/' , fn () => 'meow' ));
$router2 -> route ( RouteInfo :: exact ( 'GET' , '/rules' , fn () => 'rules page' ));
$router2 -> route ( RouteInfo :: exact ( 'GET' , '/contact' , fn () => 'contact page' ));
$router2 -> route ( RouteInfo :: exact ( 'GET' , '/25252' , fn () => 'numeric test' ));
$this -> assertEquals ( 'meow' , ( string ) $router2 -> handle ( new HttpRequest ( '1.1' , [], NullStream :: instance (), [], 'GET' , HttpUri :: createUri ( '/rules' ), [], [])) -> getBody ());
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-07 00:25:00 +00:00
#[ExactRoute('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-07 00:25:00 +00:00
#[ExactRoute('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-07 00:25:00 +00:00
#[ExactRoute('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-07 00:25:00 +00:00
#[ExactRoute('GET', '/meow')]
#[ExactRoute('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-07 00:25:00 +00:00
#[PrefixFilter('/filter')]
2025-03-02 02:08:45 +00:00
public function useFilter () : string {
2023-09-08 00:09:23 +00:00
return 'this intercepts' ;
}
2025-03-07 00:25:00 +00:00
#[ExactRoute('GET', '/filter')]
2025-03-02 02:08:45 +00:00
public function getFilter () : string {
2023-09-08 00:09:23 +00:00
return 'this is intercepted' ;
}
2025-03-07 00:25:00 +00:00
#[PatternRoute('GET', '/profile/([A-Za-z0-9]+)')]
public function getPattern ( string $beans ) : string {
return sprintf ( 'profile of %s' , $beans );
}
#[PatternRoute('GET', '#^/profile-but-raw/([A-Za-z0-9]+)$#uD', raw: true)]
public function getPatternRaw ( string $beans ) : string {
return sprintf ( 'still the profile of %s' , $beans );
}
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 );
2025-03-07 00:25:00 +00:00
$this -> assertEquals ( 'index' , ( string ) $router -> handle ( new HttpRequest ( '1.1' , [], NullStream :: instance (), [], 'GET' , HttpUri :: createUri ( '/' ), [], [])) -> getBody ());
$this -> assertEquals ( 'avatar' , ( string ) $router -> handle ( new HttpRequest ( '1.1' , [], NullStream :: instance (), [], 'POST' , HttpUri :: createUri ( '/avatar' ), [], [])) -> getBody ());
$this -> assertEquals ( 'static' , ( string ) $router -> handle ( new HttpRequest ( '1.1' , [], NullStream :: instance (), [], 'PUT' , HttpUri :: createUri ( '/static' ), [], [])) -> getBody ());
$this -> assertEquals ( 'meow' , ( string ) $router -> handle ( new HttpRequest ( '1.1' , [], NullStream :: instance (), [], 'GET' , HttpUri :: createUri ( '/meow' ), [], [])) -> getBody ());
$this -> assertEquals ( 'meow' , ( string ) $router -> handle ( new HttpRequest ( '1.1' , [], NullStream :: instance (), [], 'POST' , HttpUri :: createUri ( '/meow' ), [], [])) -> getBody ());
$this -> assertEquals ( 'profile of Cool134' , ( string ) $router -> handle ( new HttpRequest ( '1.1' , [], NullStream :: instance (), [], 'GET' , HttpUri :: createUri ( '/profile/Cool134' ), [], [])) -> getBody ());
$this -> assertEquals ( 'still the profile of Cool134' , ( string ) $router -> handle ( new HttpRequest ( '1.1' , [], NullStream :: instance (), [], 'GET' , HttpUri :: createUri ( '/profile-but-raw/Cool134' ), [], [])) -> getBody ());
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 ;
2025-03-07 00:25:00 +00:00
$router -> route ( RouteInfo :: pattern ( 'GET' , '#^/uploads/([A-Za-z0-9\-_]+)(?:\.(t|json))?$#uD' , fn ( string $id ) => " Get { $id } " ));
$router -> route ( RouteInfo :: pattern ( 'DELETE' , '#^/uploads/([A-Za-z0-9\-_]+)$#uD' , fn ( string $id ) => " Delete { $id } " ));
2024-04-02 17:27:06 +00:00
2025-03-07 00:25:00 +00:00
// make sure both GET and DELETE are able to execute with a different pattern
$this -> assertEquals ( 'Get AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' , ( string ) $router -> handle ( new HttpRequest ( '1.1' , [], NullStream :: instance (), [], 'GET' , HttpUri :: createUri ( '/uploads/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' ), [], [])) -> getBody ());
$this -> assertEquals ( 'Delete BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB' , ( string ) $router -> handle ( new HttpRequest ( '1.1' , [], NullStream :: instance (), [], 'DELETE' , HttpUri :: createUri ( '/uploads/BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB' ), [], [])) -> getBody ());
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-07 00:25:00 +00:00
$router -> filter ( FilterInfo :: prefix ( '/' , fn () => 'expected' ));
$router -> route ( RouteInfo :: exact ( 'GET' , '/' , fn () => 'unexpected' ));
$router -> route ( RouteInfo :: exact ( 'GET' , '/test' , fn () => 'also unexpected' ));
2024-08-18 16:41:02 +00:00
2025-03-07 00:25:00 +00:00
$this -> assertEquals ( 'expected' , ( string ) $router -> handle ( new HttpRequest ( '1.1' , [], NullStream :: instance (), [], 'GET' , HttpUri :: createUri ( '/' ), [], [])) -> getBody ());
$this -> assertEquals ( 'expected' , ( string ) $router -> handle ( new HttpRequest ( '1.1' , [], NullStream :: instance (), [], 'GET' , HttpUri :: createUri ( '/test' ), [], [])) -> getBody ());
$this -> assertEquals ( 'expected' , ( string ) $router -> handle ( new HttpRequest ( '1.1' , [], NullStream :: instance (), [], 'GET' , HttpUri :: createUri ( '/error' ), [], [])) -> getBody ());
}
2024-08-18 16:41:02 +00:00
2025-03-07 00:25:00 +00:00
public function testDefaultOptionsImplementation () : void {
$router = new Router ;
$router -> route ( RouteInfo :: exact ( 'GET' , '/test' , fn () => 'get' ));
$router -> route ( RouteInfo :: exact ( 'POST' , '/test' , fn () => 'post' ));
$router -> route ( RouteInfo :: exact ( 'PATCH' , '/test' , fn () => 'patch' ));
2024-08-18 16:41:02 +00:00
2025-03-07 00:25:00 +00:00
$response = $router -> handle ( new HttpRequest ( '1.1' , [], NullStream :: instance (), [], 'OPTIONS' , HttpUri :: createUri ( '/test' ), [], []));
$this -> assertEquals ( 204 , $response -> getStatusCode ());
2025-03-07 00:32:06 +00:00
$this -> assertEquals ( 'No Content' , $response -> getReasonPhrase ());
2025-03-07 00:25:00 +00:00
$this -> assertEquals ( 'GET, HEAD, OPTIONS, PATCH, POST' , $response -> getHeaderLine ( 'Allow' ));
2024-08-18 16:41:02 +00:00
}
2022-09-13 15:13:11 +02:00
}