2022-09-13 15:13:11 +02:00
< ? php
// RouterTest.php
// Created: 2022-01-20
2025-03-15 02:10:16 +00:00
// Updated: 2025-03-15
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-12 20:56:48 +00:00
use Index\Http\ { HttpHeaders , HttpResponseBuilder , HttpRequest , HttpUri };
2025-03-15 02:10:16 +00:00
use Index\Http\Content\ { FormContent , MultipartFormContent , UrlEncodedFormContent };
2025-03-07 23:39:15 +00:00
use Index\Http\Routing\ { HandlerContext , RouteHandler , RouteHandlerCommon , Router };
use Index\Http\Routing\Filters\ { FilterInfo , PrefixFilter };
use Index\Http\Routing\Processors\ { After , Before , Postprocessor , Preprocessor , ProcessorInfo };
use Index\Http\Routing\Routes\ { ExactRoute , PatternRoute , RouteInfo };
2025-03-15 02:10:16 +00:00
use Index\Http\Streams\Stream ;
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-15 02:10:16 +00:00
$this -> assertEquals ( 'get' , ( string ) $router1 -> handle ( HttpRequest :: createRequestWithoutBody ( 'GET' , '/' )) -> getBody ());
$this -> assertEquals ( 'wacky' , ( string ) $router1 -> handle ( HttpRequest :: createRequestWithoutBody ( 'CUSTOM' , '/' )) -> 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-15 02:10:16 +00:00
$this -> assertEquals ( 'warioware below static' , ( string ) $router1 -> handle ( HttpRequest :: createRequestWithoutBody ( 'GET' , '/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' ));
2025-03-15 02:10:16 +00:00
$this -> assertEquals ( 'meow' , ( string ) $router2 -> handle ( HttpRequest :: createRequestWithoutBody ( 'GET' , '/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
2025-03-15 02:10:16 +00:00
$this -> assertEquals ( 'index' , ( string ) $router -> handle ( HttpRequest :: createRequestWithoutBody ( 'GET' , '/' )) -> getBody ());
$this -> assertEquals ( 'avatar' , ( string ) $router -> handle ( HttpRequest :: createRequestWithoutBody ( 'POST' , '/avatar' )) -> getBody ());
$this -> assertEquals ( 'static' , ( string ) $router -> handle ( HttpRequest :: createRequestWithoutBody ( 'PUT' , '/static' )) -> getBody ());
$this -> assertEquals ( 'meow' , ( string ) $router -> handle ( HttpRequest :: createRequestWithoutBody ( 'GET' , '/meow' )) -> getBody ());
$this -> assertEquals ( 'meow' , ( string ) $router -> handle ( HttpRequest :: createRequestWithoutBody ( 'POST' , '/meow' )) -> getBody ());
$this -> assertEquals ( 'profile of Cool134' , ( string ) $router -> handle ( HttpRequest :: createRequestWithoutBody ( 'GET' , '/profile/Cool134' )) -> getBody ());
$this -> assertEquals ( 'still the profile of Cool134' , ( string ) $router -> handle ( HttpRequest :: createRequestWithoutBody ( 'GET' , '/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
2025-03-15 02:10:16 +00:00
$this -> assertEquals ( 'Get AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' , ( string ) $router -> handle ( HttpRequest :: createRequestWithoutBody ( 'GET' , '/uploads/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' )) -> getBody ());
$this -> assertEquals ( 'Delete BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB' , ( string ) $router -> handle ( HttpRequest :: createRequestWithoutBody ( 'DELETE' , '/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-15 02:10:16 +00:00
$this -> assertEquals ( 'expected' , ( string ) $router -> handle ( HttpRequest :: createRequestWithoutBody ( 'GET' , '/' )) -> getBody ());
$this -> assertEquals ( 'expected' , ( string ) $router -> handle ( HttpRequest :: createRequestWithoutBody ( 'GET' , '/test' )) -> getBody ());
$this -> assertEquals ( 'expected' , ( string ) $router -> handle ( HttpRequest :: createRequestWithoutBody ( 'GET' , '/error' )) -> getBody ());
2025-03-07 00:25:00 +00:00
}
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-15 02:10:16 +00:00
$response = $router -> handle ( HttpRequest :: createRequestWithoutBody ( 'OPTIONS' , '/test' ));
2025-03-07 00:25:00 +00:00
$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
}
2025-03-07 23:39:15 +00:00
2025-03-15 02:10:16 +00:00
public function testDefaultProcessorsNoRegister () : void {
$this -> expectException ( RuntimeException :: class );
$this -> expectExceptionMessage ( sprintf ( 'preprocessor "%s" was not found' , 'input:urlencoded:required' ));
$router = new Router ( registerDefaultProcessors : false );
$router -> route ( RouteInfo :: exact (
'GET' , '/soap' ,
#[Before('input:urlencoded:required')]
function () {
return 'test' ;
},
));
$router -> handle ( HttpRequest :: createRequestWithoutBody ( 'GET' , '/soap' ));
}
public function testDefaultProcessors () : void {
$router = new Router ;
$router -> register ( new class implements RouteHandler {
use RouteHandlerCommon ;
#[Before('input:urlencoded', required: false)]
#[Before('input:multipart', required: false)]
#[ExactRoute('POST', '/optional-form')]
public function formOptional ( ? FormContent $content = null ) : string {
if ( $content instanceof MultipartFormContent )
return 'multipart:' . ( string ) $content -> getParam ( 'test' );
if ( $content instanceof UrlEncodedFormContent )
return 'urlencoded:' . ( string ) $content -> getParam ( 'test' );
return 'none' ;
}
#[Before('input:multipart')]
#[ExactRoute('POST', '/required-multipart')]
public function multipartRequired ( MultipartFormContent $content ) : string {
return ( string ) $content -> getParam ( 'test' );
}
#[Before('input:urlencoded')]
#[ExactRoute('POST', '/required-urlencoded')]
public function urlencodedRequired ( UrlEncodedFormContent $content ) : string {
return ( string ) $content -> getParam ( 'test' );
}
2025-03-15 02:41:14 +00:00
#[After('output:stream')]
#[ExactRoute('GET', '/output/stream')]
public function testOutputStream () : string {
return '<!doctype html><h1>ensuring autodetect gets skipped</h1>' ;
}
#[After('output:plain', charset: 'utf-8')]
#[ExactRoute('GET', '/output/plain')]
public function testOutputPlain () : string {
return '<!doctype html><h1>ensuring autodetect gets skipped</h1>' ;
}
#[After('output:html', charset: 'us-ascii')]
#[ExactRoute('GET', '/output/html')]
public function testOutputHtml () : string {
return '<?xml idk how xml opens the prefix is enough><beans></beans>' ;
}
#[After('output:xml')]
#[ExactRoute('GET', '/output/xml')]
public function testOutputXml () : string {
return 'soup' ;
}
#[After('output:css')]
#[ExactRoute('GET', '/output/css')]
public function testOutputCss () : string {
return '<!doctype html><h1>ensuring autodetect gets skipped</h1>' ;
}
#[After('output:js')]
#[ExactRoute('GET', '/output/js')]
public function testOutputJs () : string {
return '<!doctype html><h1>ensuring autodetect gets skipped</h1>' ;
}
#[After('output:json', flags: 0)]
#[ExactRoute('GET', '/output/json')]
/** @return array{wow: string} */
public function testOutputJson () : array {
return [ 'wow' => 'objects?? / epic!!' ];
}
#[After('output:bencode')]
#[ExactRoute('GET', '/output/bencode')]
/** @return array{benben: int} */
public function testOutputBencode () : array {
return [ 'benben' => 12345 ];
}
2025-03-15 02:10:16 +00:00
});
2025-03-15 02:41:14 +00:00
$response = $router -> handle ( HttpRequest :: createRequestWithoutBody ( 'GET' , '/output/stream' ));
$this -> assertEquals ( 'application/octet-stream' , $response -> getHeaderLine ( 'Content-Type' ));
$this -> assertEquals ( '<!doctype html><h1>ensuring autodetect gets skipped</h1>' , ( string ) $response -> getBody ());
$response = $router -> handle ( HttpRequest :: createRequestWithoutBody ( 'GET' , '/output/plain' ));
$this -> assertEquals ( 'text/plain;charset=utf-8' , $response -> getHeaderLine ( 'Content-Type' ));
$this -> assertEquals ( '<!doctype html><h1>ensuring autodetect gets skipped</h1>' , ( string ) $response -> getBody ());
$response = $router -> handle ( HttpRequest :: createRequestWithoutBody ( 'GET' , '/output/html' ));
$this -> assertEquals ( 'text/html;charset=us-ascii' , $response -> getHeaderLine ( 'Content-Type' ));
$this -> assertEquals ( '<?xml idk how xml opens the prefix is enough><beans></beans>' , ( string ) $response -> getBody ());
$response = $router -> handle ( HttpRequest :: createRequestWithoutBody ( 'GET' , '/output/xml' ));
$this -> assertEquals ( 'application/xml' , $response -> getHeaderLine ( 'Content-Type' ));
$this -> assertEquals ( 'soup' , ( string ) $response -> getBody ());
$response = $router -> handle ( HttpRequest :: createRequestWithoutBody ( 'GET' , '/output/css' ));
$this -> assertEquals ( 'text/css' , $response -> getHeaderLine ( 'Content-Type' ));
$this -> assertEquals ( '<!doctype html><h1>ensuring autodetect gets skipped</h1>' , ( string ) $response -> getBody ());
$response = $router -> handle ( HttpRequest :: createRequestWithoutBody ( 'GET' , '/output/js' ));
$this -> assertEquals ( 'application/javascript' , $response -> getHeaderLine ( 'Content-Type' ));
$this -> assertEquals ( '<!doctype html><h1>ensuring autodetect gets skipped</h1>' , ( string ) $response -> getBody ());
$response = $router -> handle ( HttpRequest :: createRequestWithoutBody ( 'GET' , '/output/json' ));
$this -> assertEquals ( 'application/json' , $response -> getHeaderLine ( 'Content-Type' ));
$this -> assertEquals ( '{"wow":"objects?? \/ epic!!"}' , ( string ) $response -> getBody ());
$response = $router -> handle ( HttpRequest :: createRequestWithoutBody ( 'GET' , '/output/bencode' ));
$this -> assertEquals ( 'application/x-bittorrent' , $response -> getHeaderLine ( 'Content-Type' ));
$this -> assertEquals ( 'd6:benbeni12345ee' , ( string ) $response -> getBody ());
2025-03-15 02:10:16 +00:00
$response = $router -> handle ( HttpRequest :: createRequestWithoutBody ( 'POST' , '/optional-form' ));
$this -> assertEquals ( 200 , $response -> getStatusCode ());
$this -> assertEquals ( 'none' , ( string ) $response -> getBody ());
$response = $router -> handle ( HttpRequest :: createRequestWithBody ( 'POST' , '/optional-form' , [], Stream :: createStream ( 'test=mewow' )));
$this -> assertEquals ( 200 , $response -> getStatusCode ());
$this -> assertEquals ( 'none' , ( string ) $response -> getBody ());
$response = $router -> handle ( HttpRequest :: createRequestWithBody ( 'POST' , '/optional-form' , [ 'Content-Type' => [ 'application/x-www-form-urlencoded' ]], Stream :: createStream ( 'test=mewow' )));
$this -> assertEquals ( 200 , $response -> getStatusCode ());
$this -> assertEquals ( 'urlencoded:mewow' , ( string ) $response -> getBody ());
2025-03-15 02:48:59 +00:00
// an empty string is valid too
$response = $router -> handle ( HttpRequest :: createRequestWithBody ( 'POST' , '/optional-form' , [ 'Content-Type' => [ 'application/x-www-form-urlencoded' ]], Stream :: createStream ( '' )));
$this -> assertEquals ( 200 , $response -> getStatusCode ());
$this -> assertEquals ( 'urlencoded:' , ( string ) $response -> getBody ());
2025-03-15 02:10:16 +00:00
$response = $router -> handle ( HttpRequest :: createRequestWithBody ( 'POST' , '/optional-form' , [ 'Content-Type' => [ 'multipart/form-data; boundary="--soap12345"' ]], Stream :: createStream ( implode ( " \r \n " , [
'----soap12345' ,
'Content-Disposition: form-data; name="test"' ,
'' ,
'wowof' ,
'----soap12345--' ,
'' ,
]))));
$this -> assertEquals ( 200 , $response -> getStatusCode ());
$this -> assertEquals ( 'multipart:wowof' , ( string ) $response -> getBody ());
2025-03-15 02:48:59 +00:00
$response = $router -> handle ( HttpRequest :: createRequestWithBody ( 'POST' , '/optional-form' , [ 'Content-Type' => [ 'multipart/form-data; boundary="--soap12345"' ]], Stream :: createStream ( implode ( " \r \n " , [
'----soap12345--' ,
'' ,
]))));
$this -> assertEquals ( 200 , $response -> getStatusCode ());
$this -> assertEquals ( 'multipart:' , ( string ) $response -> getBody ());
2025-03-15 02:10:16 +00:00
$response = $router -> handle ( HttpRequest :: createRequestWithoutBody ( 'POST' , '/required-urlencoded' ));
$this -> assertEquals ( 400 , $response -> getStatusCode ());
$response = $router -> handle ( HttpRequest :: createRequestWithoutBody ( 'POST' , '/required-multipart' ));
$this -> assertEquals ( 400 , $response -> getStatusCode ());
$response = $router -> handle ( HttpRequest :: createRequestWithBody ( 'POST' , '/required-urlencoded' , [ 'Content-Type' => [ 'application/x-www-form-urlencoded' ]], Stream :: createStream ( 'test=meow' )));
$this -> assertEquals ( 200 , $response -> getStatusCode ());
$this -> assertEquals ( 'meow' , ( string ) $response -> getBody ());
$response = $router -> handle ( HttpRequest :: createRequestWithBody ( 'POST' , '/required-multipart' , [ 'Content-Type' => [ 'multipart/form-data; boundary="--soap56789"' ]], Stream :: createStream ( implode ( " \r \n " , [
'----soap56789' ,
'Content-Disposition: form-data; name="test"' ,
'' ,
'woof' ,
'----soap56789--' ,
'' ,
]))));
$this -> assertEquals ( 200 , $response -> getStatusCode ());
$this -> assertEquals ( 'woof' , ( string ) $response -> getBody ());
$response = $router -> handle ( HttpRequest :: createRequestWithBody ( 'POST' , '/required-urlencoded' , [ 'Content-Type' => [ 'multipart/form-data; boundary="--soap56789"' ]], Stream :: createStream ( 'test=meow' )));
$this -> assertEquals ( 400 , $response -> getStatusCode ());
$response = $router -> handle ( HttpRequest :: createRequestWithBody ( 'POST' , '/required-multipart' , [ 'Content-Type' => [ 'application/x-www-form-urlencoded' ]], Stream :: createStream ( implode ( " \r \n " , [
'----soap56789' ,
'Content-Disposition: form-data; name="test"' ,
'' ,
'woof' ,
'----soap56789--' ,
'' ,
]))));
$this -> assertEquals ( 400 , $response -> getStatusCode ());
$response = $router -> handle ( HttpRequest :: createRequestWithBody ( 'POST' , '/required-multipart' , [ 'Content-Type' => [ 'multipart/form-data' ]], Stream :: createStream ( implode ( " \r \n " , [
'----aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' ,
'Content-Disposition: form-data; name="test"' ,
'' ,
'the' ,
'----aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa--' ,
'' ,
]))));
$this -> assertEquals ( 400 , $response -> getStatusCode ());
}
2025-03-07 23:39:15 +00:00
public function testProcessors () : void {
$router = new Router ;
$router -> register ( new class implements RouteHandler {
use RouteHandlerCommon ;
#[Preprocessor('base64-decode-body')]
public function decodePre ( HandlerContext $context , HttpRequest $request ) : void {
$context -> addArgument ( base64_decode (( string ) $request -> getBody ()));
}
#[Postprocessor('crash')]
public function crashPost () : void {
throw new RuntimeException ( 'this shouldnt run!' );
}
#[Postprocessor('json-encode')]
public function encodePost ( HandlerContext $context , int $flags ) : void {
$context -> halt ();
$context -> response -> setTypeJson ();
2025-03-12 20:56:48 +00:00
$context -> response -> body = Stream :: createStream (
2025-03-07 23:39:15 +00:00
json_encode ( $context -> result , JSON_UNESCAPED_SLASHES | JSON_THROW_ON_ERROR | $flags )
);
}
/** @return array{data: string} */
#[ExactRoute('POST', '/test')]
#[Before('base64-decode-body')]
#[After('json-encode', flags: JSON_PRETTY_PRINT)]
#[After('crash')]
public function route ( string $decoded ) : array {
return [ 'data' => $decoded ];
}
});
$router -> preprocessor ( ProcessorInfo :: pre ( 'intercept' , function ( HttpResponseBuilder $response ) {
$response -> statusCode = 403 ;
return 'it can work like a filter too' ;
}));
$router -> route ( RouteInfo :: exact (
'PUT' , '/alternate' ,
#[Before('base64-decode-body')]
#[After('json-encode', flags: JSON_PRETTY_PRINT)]
function ( HttpRequest $request , string $decoded ) : array {
return [ 'path' => $request -> uri -> path , 'data' => $decoded ];
}
));
$router -> route ( RouteInfo :: exact (
'GET' , '/filtered' ,
#[Before('intercept')]
#[Before('base64-decode-body')]
function () : string {
return 'should not get here' ;
}
));
2025-03-15 02:10:16 +00:00
$response = $router -> handle ( HttpRequest :: createRequestWithBody ( 'POST' , '/test' , [], Stream :: createStream ( base64_encode ( 'mewow' ))));
2025-03-07 23:39:15 +00:00
$this -> assertEquals ( 200 , $response -> getStatusCode ());
$this -> assertEquals ( " { \n \" data \" : \" mewow \" \n } " , ( string ) $response -> getBody ());
2025-03-15 02:10:16 +00:00
$response = $router -> handle ( HttpRequest :: createRequestWithBody ( 'PUT' , '/alternate' , [], Stream :: createStream ( base64_encode ( 'soap' ))));
2025-03-07 23:39:15 +00:00
$this -> assertEquals ( 200 , $response -> getStatusCode ());
$this -> assertEquals ( " { \n \" path \" : \" /alternate \" , \n \" data \" : \" soap \" \n } " , ( string ) $response -> getBody ());
2025-03-15 02:10:16 +00:00
$response = $router -> handle ( HttpRequest :: createRequestWithoutBody ( 'GET' , '/filtered' ));
2025-03-07 23:39:15 +00:00
$this -> assertEquals ( 403 , $response -> getStatusCode ());
$this -> assertEquals ( 'it can work like a filter too' , ( string ) $response -> getBody ());
}
2022-09-13 15:13:11 +02:00
}