diff options
Diffstat (limited to 'tests/Regression/KnownIssuesTest.php')
| -rw-r--r-- | tests/Regression/KnownIssuesTest.php | 158 |
1 files changed, 158 insertions, 0 deletions
diff --git a/tests/Regression/KnownIssuesTest.php b/tests/Regression/KnownIssuesTest.php new file mode 100644 index 0000000..5a6c6f7 --- /dev/null +++ b/tests/Regression/KnownIssuesTest.php @@ -0,0 +1,158 @@ +<?php + +declare(strict_types=1); + +namespace BrettParson\MiniRoute\Tests\Regression; + +use BrettParson\MiniRoute\Routing\RouteLoader; +use BrettParson\MiniRoute\Routing\RouteNotFoundException; +use BrettParson\MiniRoute\Routing\Router; +use BrettParson\MiniRoute\Tests\Fixtures\DuplicateParamController; +use BrettParson\MiniRoute\Tests\Fixtures\FakeController; +use BrettParson\MiniRoute\Tests\Fixtures\FakeResolver; +use BrettParson\MiniRoute\Tests\Fixtures\MixedVisibilityController; +use BrettParson\MiniRoute\Tests\Fixtures\OnlyGetController; +use BrettParson\MiniRoute\Tests\Fixtures\StatefulController; +use BrettParson\MiniRoute\Tests\Fixtures\StatefulMiddleware; +use BrettParson\MiniRoute\Tests\Fixtures\TestRequest; +use PHPUnit\Framework\TestCase; + +/** + * Known-issue baselines: pin v0.1.0 behavior for every confirmed stress + * finding so the suite is green today and each case flips to the desired + * behavior when its v0.2.0 fix lands. + * + * Bead: miniroute-4dh.2. Fixes tracked in the v0.2.0 epic (miniroute-uhs). + */ +final class KnownIssuesTest extends TestCase +{ + private function routerWith(object $controller, string $class): Router + { + $resolver = new FakeResolver(); + $resolver->bind($class, $controller); + + return new Router($resolver); + } + + /** + * Baseline: duplicated parameter names produce a route that can never + * match, and emit a PHP warning on every match attempt. + * + * Desired (miniroute-uhs.1): registration throws + * RouteRegistrationException. Flip this test when the fix lands. + */ + public function testDuplicateParamNamesAreADeadRoute(): void + { + $router = $this->routerWith(new DuplicateParamController(), DuplicateParamController::class); + $router->registerController(DuplicateParamController::class); + + $warnings = []; + set_error_handler(static function (int $errno, string $errstr) use (&$warnings): bool { + $warnings[] = $errstr; + + return true; + }); + + try { + $router->dispatch(new TestRequest('GET', '/dup/1/x/2')); + $matched = true; + } catch (RouteNotFoundException) { + $matched = false; + } finally { + restore_error_handler(); + } + + $this->assertFalse($matched, 'duplicate-param route must not match'); + $this->assertNotEmpty($warnings, 'matching a duplicate-param route emits a preg warning'); + } + + /** + * Baseline: a wrong method on an existing path is a 404, with no way to + * distinguish method-not-allowed. + * + * Desired (miniroute-uhs.2): MethodNotAllowedException carrying the + * allowed methods, so the app can render 405 + Allow. Flip this test. + */ + public function testWrongMethodOnExistingPathIsNotFound(): void + { + $router = $this->routerWith(new OnlyGetController(), OnlyGetController::class); + $router->registerController(OnlyGetController::class); + + $this->expectException(RouteNotFoundException::class); + + $router->dispatch(new TestRequest('POST', '/only')); + } + + /** + * Baseline: HEAD requests never map to GET handlers. + * + * Desired (miniroute-uhs.3): HEAD dispatches to the GET handler and the + * app's response layer suppresses the body. Flip this test. + */ + public function testHeadDoesNotMapToGet(): void + { + $router = $this->routerWith(new OnlyGetController(), OnlyGetController::class); + $router->registerController(OnlyGetController::class); + + $this->expectException(RouteNotFoundException::class); + + $router->dispatch(new TestRequest('HEAD', '/only')); + } + + /** + * Baseline: route attributes on non-public methods are silently skipped + * by the loader. + * + * Desired (miniroute-uhs.4): loading a controller with a route attribute + * on a non-public method throws RouteRegistrationException. Flip this test. + */ + public function testNonPublicMethodsWithRouteAttributesAreSkipped(): void + { + $routes = (new RouteLoader())->load(MixedVisibilityController::class); + + $this->assertCount(1, $routes); + $this->assertSame('/pub', $routes[0]->path); + } + + /** + * Baseline: a resolver returning the same middleware instance across + * requests accumulates state (singleton-container leak). This is a usage + * contract, not a kernel bug: middleware must be stateless or + * per-request scoped. ADR work tracked in miniroute-k4i.3. + */ + public function testSingletonMiddlewareAccumulatesStateAcrossRequests(): void + { + $stateful = new StatefulMiddleware(); + + $resolver = new FakeResolver(); + $resolver->bind(StatefulController::class, new StatefulController()); + $resolver->bind(StatefulMiddleware::class, $stateful); + + $router = new Router($resolver); + $router->registerController(StatefulController::class); + + $router->dispatch(new TestRequest('GET', '/stateful')); + $router->dispatch(new TestRequest('GET', '/stateful')); + + $this->assertSame(2, $stateful->count, 'state leaks across dispatches with a shared instance'); + } + + /** + * Baseline: {param} segments are greedy — any single segment matches, so + * a typo'd URL gets a 200 from the parameterized route, not a 404. + * + * Documented behavior, no kernel fix planned: controllers validate the + * captured value (the blog controller 404s on an unknown slug). Decision + * recorded in miniroute-k4i.3 (param constraints rejected). + */ + public function testParamRoutesAreGreedy(): void + { + $router = $this->routerWith(new FakeController(), FakeController::class); + $router->registerController(FakeController::class); + + $response = $router->dispatch(new TestRequest('GET', '/entries/typo-of-known-path')); + + $this->assertSame(200, $response->status()); + $this->assertSame('show:typo-of-known-path', $response->body()); + } +} |
