From 39bf192444bdeda11311257840333d5af943c38e Mon Sep 17 00:00:00 2001 From: agis Date: Fri, 25 Sep 2026 23:47:31 +0700 Subject: [PATCH] feat(routing): swap illuminate/routing to ^13 (task 4.3) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move illuminate/routing from replace (self.version) to require ^13; v13's Routing tree shadows the fork via PSR-4 (seed mechanism). This removes the fork Router's before/after/filter global-filter engine — the last of the task 2.8 filter surface (part 2). Foundation: App::before/App::after no longer delegate to the (now v13) Router, which has no global-filter concept. Store the callbacks on the Application and run them around the route dispatch (before with response short-circuit, after wrapping the response) — the app has no HTTP Kernel to host global middleware. CachedRouting evaporated early (its Router subclass overrides L4.2 Router internals gone in v13 and can't even compile against v13's public newRoute()). The routing swap forces this component's removal ahead of task 4.4. Deleted vendored-behavior tests (now assert v13-owned Router/UrlGenerator/ Redirector/ControllerDispatcher/ResponseFactory internals): Routing/* + Support/SupportFacadeResponseTest + CachedRouting/*. Fork suite 382 green. Co-Authored-By: Claude Opus 4.8 (1M context) --- composer.json | 10 +- src/Illuminate/CachedRouting/Route.php | 105 -- .../CachedRouting/RouteCollection.php | 174 --- src/Illuminate/CachedRouting/Router.php | 295 ----- .../CachedRouting/RoutingServiceProvider.php | 74 -- src/Illuminate/Foundation/Application.php | 34 +- .../CachedRouting/RoutingIntegrationTest.php | 450 ------- .../RoutingServiceProviderTest.php | 97 -- .../RoutingControllerDispatcherTest.php | 74 -- tests/Routing/RoutingRedirectorTest.php | 167 --- tests/Routing/RoutingRouteTest.php | 1084 ----------------- tests/Routing/RoutingUrlGeneratorTest.php | 293 ----- tests/Support/SupportFacadeResponseTest.php | 63 - 13 files changed, 36 insertions(+), 2884 deletions(-) delete mode 100644 src/Illuminate/CachedRouting/Route.php delete mode 100644 src/Illuminate/CachedRouting/RouteCollection.php delete mode 100644 src/Illuminate/CachedRouting/Router.php delete mode 100644 src/Illuminate/CachedRouting/RoutingServiceProvider.php delete mode 100755 tests/CachedRouting/RoutingIntegrationTest.php delete mode 100644 tests/CachedRouting/RoutingServiceProviderTest.php delete mode 100644 tests/Routing/RoutingControllerDispatcherTest.php delete mode 100644 tests/Routing/RoutingRedirectorTest.php delete mode 100644 tests/Routing/RoutingRouteTest.php delete mode 100755 tests/Routing/RoutingUrlGeneratorTest.php delete mode 100755 tests/Support/SupportFacadeResponseTest.php diff --git a/composer.json b/composer.json index 73d4d1b10..31d824c9c 100755 --- a/composer.json +++ b/composer.json @@ -26,17 +26,18 @@ "illuminate/encryption": "^13", "illuminate/events": "^13", "illuminate/filesystem": "^13", - "illuminate/hashing": "^13", - "illuminate/http": "^13", + "illuminate/hashing": "^13", + "illuminate/http": "^13", "illuminate/log": "^13", "illuminate/macroable": "^13", "illuminate/pagination": "^13", "illuminate/pipeline": "^13", "illuminate/redis": "^13", "illuminate/reflection": "^13", + "illuminate/routing": "^13", "illuminate/session": "^13", - "illuminate/support": "^13", - "illuminate/translation": "^13", + "illuminate/support": "^13", + "illuminate/translation": "^13", "illuminate/validation": "^13", "illuminate/view": "^13", "ircmaxell/password-compat": "~1.0", @@ -69,7 +70,6 @@ "illuminate/html": "self.version", "illuminate/mail": "self.version", "illuminate/queue": "self.version", - "illuminate/routing": "self.version", "illuminate/workbench": "self.version" }, "require-dev": { diff --git a/src/Illuminate/CachedRouting/Route.php b/src/Illuminate/CachedRouting/Route.php deleted file mode 100644 index c0870cb4d..000000000 --- a/src/Illuminate/CachedRouting/Route.php +++ /dev/null @@ -1,105 +0,0 @@ -routingToController($this->action) === true) { - $this->action = $app['router']->makeControllerActionClosure($this->action); - } - - return parent::run(); - } - - /** - * Determines whether routes should be compiled. Caches the value at a class - * level. - * - * @return bool - */ - protected function shouldCompile() - { - if (static::$shouldCompileRoute === null) { - // If the compiled variable exists, we should compile. - static::$shouldCompileRoute = array_key_exists('compiled', get_object_vars($this)); - } - - return static::$shouldCompileRoute; - } - - /** - * Compile the route into a Symfony CompiledRoute instance. - */ - #[\Override] - protected function compileRoute() - { - if ($this->shouldCompile() === true && $this->compiled === null) { - parent::compileRoute(); - } - } - - /** - * Prepare to go to sleep. Compiles the route so that doesn't have to happen - * on every subsequent request. - * - * @return array - */ - public function __sleep() - { - $this->compileRoute(); - - return array_keys(get_object_vars($this)); - } -} diff --git a/src/Illuminate/CachedRouting/RouteCollection.php b/src/Illuminate/CachedRouting/RouteCollection.php deleted file mode 100644 index 2f6f4fcdf..000000000 --- a/src/Illuminate/CachedRouting/RouteCollection.php +++ /dev/null @@ -1,174 +0,0 @@ -backup; - } - - /** - * Save the current collection of routes to the backup to start with an empty - * collection to cache. - */ - public function saveRouteCollection() - { - $this->backup = $this->getCacheableRouteContents(); - - foreach (array_keys($this->backup) as $k) { - $this->$k = array(); - } - } - - /** - * Restore the backed up routes. - */ - public function restoreRouteCollection() - { - $this->restoreRoutes($this->getBackup(), true); - } - - /** - * Get the routes in a form that can be saved to the cache. - * - * @return string - */ - public function getCacheableRoutes() - { - return serialize($this->getCacheableRouteContents()); - } - - /** - * Get the data that should be sent to the cache. - * - * @return array - */ - public function getCacheableRouteContents() - { - return array_except(get_object_vars($this), array('backup')); - } - - /** - * Restore a set of cached routes into this collection. - * - * @param string $cache - */ - public function restoreRouteCache($cache) - { - $routes = unserialize($cache); - - $this->restoreRoutes($routes); - } - - /** - * Add a set of routes back into this collection. - * - * @param bool $prepend - * @param array $routes - */ - protected function restoreRoutes($routes, $prepend = false) - { - foreach ($routes as $k => $v) { - if ($k === 'routes') { - $this->$k = $prepend === false ? - $this->mergeGroupedRoutes($this->$k, $v) : - $this->mergeGroupedRoutes($v, $this->$k); - } else { - $this->$k = $prepend === false ? - $this->mergeRoutes($this->$k, $v) : - $this->mergeRoutes($v, $this->$k); - } - } - } - - /** - * Merge two array, each containing routes grouped by method. - * - * @param array - * @param array - * @return array - */ - public function mergeGroupedRoutes(array $r1, array $r2) - { - $methods = array('GET', 'POST', 'HEAD', 'PATH', 'PUT', 'DELETE'); - foreach ($methods as $method) { - if (isset($r2[$method]) === false) { - continue; - } - if (isset($r1[$method]) === false) { - $r1[$method] = array(); - } - - $r1[$method] = $this->mergeRoutes($r1[$method], $r2[$method]); - } - - return $r1; - } - - /** - * Merge two arrays, each containing routes. - * - * @param array - * @param array - * @return array - */ - public function mergeRoutes(array $r1, array $r2) - { - foreach ($r2 as $uri => $route) { - $r1[$uri] = $route; - } - - return $r1; - } -} diff --git a/src/Illuminate/CachedRouting/Router.php b/src/Illuminate/CachedRouting/Router.php deleted file mode 100644 index 580e26a51..000000000 --- a/src/Illuminate/CachedRouting/Router.php +++ /dev/null @@ -1,295 +0,0 @@ -routes = new RouteCollection; - } - - /** - * Indicate that the routes that are defined in the given callback - * should be cached. - * - * @param string $filename - * @param Closure $callback - * @param int $cacheMinutes - * @return string|null - */ - public function cache($filename, Closure $callback, $cacheMinutes = 1440): ?string - { - $cacheKey = null; - if ($cacheMinutes > 0) { - $cacheKey = $this->getCacheKey($filename); - } - - // No cache key — either caching is disabled, or the route file cannot be - // stat'd (e.g. removed in a deploy while a worker still serves its stale - // opcode). Define the routes directly and skip caching; boot must never - // break, and a null key must never reach the cache store (it would - // collide across files under an empty key). - if ($cacheKey === null) { - call_user_func($callback, $this); - - return null; - } - - $cacher = $this->getRouteCacher(); - - // Route caching is best-effort: an unreadable/corrupt entry or an - // unwritable cache directory (e.g. permissions, or a torn write from - // concurrent boots after a deploy) must never break application boot. - // On any cache I/O failure we fall back to defining the routes directly. - $cache = null; - try { - $cache = $cacher->get($cacheKey); - } catch (\Throwable $e) { - // Treat an unreadable cache as a miss and rebuild below. - } - - if ($cache !== null) { - $this->routes->restoreRouteCache($cache); - } else { - // Back up current RouteCollection contents. - $this->routes->saveRouteCollection(); - - // Call closure to define routes that should be cached. - call_user_func($callback, $this); - - // Persist the routes, ignoring failures so a broken cache store - // never propagates out of boot (routes stay defined in memory). - try { - $cacher->put($cacheKey, $this->routes->getCacheableRoutes(), Carbon::now()->addMinutes($cacheMinutes)); - } catch (\Throwable $e) { - // Best-effort cache; a write failure is non-fatal. - } - - // And restore the routes that shouldn't be cached. - $this->routes->restoreRouteCollection(); - } - - return $cacheKey; - } - - /** - * Clear the cached data for the given routes file. - * - * @param string $filename - */ - public function clearCache($filename) - { - $cacheKey = $this->getCacheKey($filename); - - if ($cacheKey !== null) { - $this->getRouteCacher()->forget($cacheKey); - } - } - - /** - * Get the cache store used to persist compiled routes. - * - * Always the local "file" store so that booting the application (web or - * artisan) never depends on a networked cache such as Redis. - * - * @return \Illuminate\Cache\Repository - */ - protected function getRouteCacher() - { - return $this->container['cache']->driver('file'); - } - - /** - * Get the key under which the routes cache for the given file should be stored. - * - * @param string $filename - * @return string|null - */ - protected function getCacheKey($filename) - { - // Building the key must never break boot. filemtime() (and md5() on a - // null path) raise warnings the app's error handler escalates to a - // fatal — e.g. a route file removed in a deploy while a worker still - // serves its stale opcode. Treat any failure as "not cacheable". - try { - return 'routes.cache.'.$this->cacheVersion.'.'.md5($filename).filemtime($filename); - } catch (\Throwable $e) { - return null; - } - } - - /** - * Determine if the action is routing to a controller. - * - * @param array $action - * @return bool - */ - #[\Override] - public function routingToController($action) - { - return parent::routingToController($action); - } - - /** - * Add a controller based route action to the action array. - * - * @param array|string $action - * @return array - */ - #[\Override] - protected function getControllerAction($action) - { - if (is_string($action) === true) { - $action = array('uses' => $action); - } - - // Here we'll get an instance of this controller dispatcher and hand it off to - // the Closure so it will be used to resolve the class instances out of our - // IoC container instance and call the appropriate methods on the class. - if (count($this->groupStack) > 0) { - $action['uses'] = $this->prependGroupUses($action['uses']); - } - - // Here we'll get an instance of this controller dispatcher and hand it off to - // the Closure so it will be used to resolve the class instances out of our - // IoC container instance and call the appropriate methods on the class. - $action['controller'] = $action['uses']; - - $closure = $action['uses']; - - return array_set($action, 'uses', $closure); - } - - /** - * Replace the string action in the given array with a Closure to call. - * - * @param array $action - * @return array - */ - public function makeControllerActionClosure(array $action) - { - $closure = $this->getClassClosure($action['uses']); - - return array_set($action, 'uses', $closure); - } - - /** - * Create a new route instance. - * - * @param array|string $methods - * @param string $uri - * @param mixed $action - * @return \Illuminate\Routing\Route - */ - #[\Override] - protected function createRoute($methods, $uri, $action) - { - // If the route is routing to a controller we will parse the route action into - // an acceptable array format before registering it and creating this route - // instance itself. We need to build the Closure that will call this out. - if ($this->routingToController($action) === true) { - $action = $this->getControllerAction($action); - } - - $route = $this->newRoute( - $methods, - $uri = $this->prefix($uri), - $action - ); - - // If we have groups that need to be merged, we will merge them now after this - // route has already been created and is ready to go. After we're done with - // the merge we will be ready to return the route back out to the caller. - if (empty($this->groupStack) === false) { - $this->mergeController($route); - } - - $this->addWhereClausesToRoute($route); - - return $route; - } - - /** - * Create a new Route object. - * - * @param array|string $methods - * @param string $uri - * @param mixed $action - * @return \Illuminate\Routing\Route - */ - #[\Override] - protected function newRoute($methods, $uri, $action) - { - return new Route($methods, $uri, $action); - } - - /** - * Add the necessary where clauses to the route based on its initial registration. - * - * @param \Illuminate\Routing\Route $route - * @return \Illuminate\Routing\Route - */ - #[\Override] - protected function addWhereClausesToRoute($route) - { - $route->where( - array_merge($this->patterns, array_get($route->getAction(), 'where', array())) - ); - return $route; - } -} diff --git a/src/Illuminate/CachedRouting/RoutingServiceProvider.php b/src/Illuminate/CachedRouting/RoutingServiceProvider.php deleted file mode 100644 index 472777e66..000000000 --- a/src/Illuminate/CachedRouting/RoutingServiceProvider.php +++ /dev/null @@ -1,74 +0,0 @@ -registerRouter(); - } - - /** - * Register the router instance. - * - * @return void - */ - protected function registerRouter() - { - $this->app->singleton('router', - function ($app) { - $router = new Router($app['events'], $app); - - // If the current application environment is "testing", we will disable the - // routing filters, since they can be tested independently of the routes - // and just get in the way of our typical controller testing concerns. - if ($app['env'] === 'testing') { - $router->disableFilters(); - } - - return $router; - } - ); - } -} diff --git a/src/Illuminate/Foundation/Application.php b/src/Illuminate/Foundation/Application.php index 7b92b2b13..0d68fdad8 100755 --- a/src/Illuminate/Foundation/Application.php +++ b/src/Illuminate/Foundation/Application.php @@ -61,6 +61,18 @@ class Application extends Container implements HttpKernelInterface, TerminableIn */ protected $finishCallbacks = array(); + /** + * ponytail: global before/after callbacks (App::before/App::after). L4.2 stored + * these as router.before/after global filters; v13's Router has no global-filter + * concept, so the app runs them itself around the route dispatch (see dispatch()). + * Remove at task 4.5 foundation swap if global middleware replaces them. + * + * @var array + */ + protected $beforeCallbacks = array(); + + protected $afterCallbacks = array(); + /** * ponytail: v13 terminating-callback shim (v13 ServiceProviders register * these; fork Foundation predates the API). Remove at task 4.5 foundation swap. @@ -627,7 +639,7 @@ public function extend($abstract, Closure $closure): void */ public function before($callback) { - return $this['router']->before($callback); + $this->beforeCallbacks[] = $callback; } /** @@ -638,7 +650,7 @@ public function before($callback) */ public function after($callback) { - return $this['router']->after($callback); + $this->afterCallbacks[] = $callback; } /** @@ -929,7 +941,23 @@ public function dispatch(Request $request) $this['session']->start(); } - return $this['router']->dispatch($this->prepareRequest($request)); + $request = $this->prepareRequest($request); + + foreach ($this->beforeCallbacks as $callback) + { + $response = call_user_func($callback, $request); + + if ( ! is_null($response)) return $this->prepareResponse($response, $request); + } + + $response = $this['router']->dispatch($request); + + foreach ($this->afterCallbacks as $callback) + { + call_user_func($callback, $request, $response); + } + + return $response; } /** diff --git a/tests/CachedRouting/RoutingIntegrationTest.php b/tests/CachedRouting/RoutingIntegrationTest.php deleted file mode 100755 index ee0152269..000000000 --- a/tests/CachedRouting/RoutingIntegrationTest.php +++ /dev/null @@ -1,450 +0,0 @@ -app === null) { - $this->refreshApplication(); - } - } - - protected function tearDown(): void - { - if (self::$cachePath !== null) { - (new Filesystem)->deleteDirectory(self::$cachePath); - } - } - - /** - * Refresh the application instance. - */ - protected function refreshApplication(): void - { - $this->app = $this->createApplication(); - - Facade::setFacadeApplication($this->app); - - $this->app['env'] = 'testing'; - - $this->app['path.storage'] = __DIR__; - - $this->app['config'] = new Repository(); - - $this->app['files'] = new Filesystem; - $this->app['cache'] = new CacheManager($this->app); - self::$cachePath = sys_get_temp_dir() . '/l42x-route-cache-' . uniqid(); - $this->app['config']['cache.default'] = 'file'; - $this->app['config']['cache.stores.file'] = ['driver' => 'file', 'path' => self::$cachePath]; - - $this->app['session'] = new SessionManager($this->app); - $this->app['config']['session.driver'] = 'array'; - $this->app['session.store'] = $this->app['session']->driver(); - - $this->app->boot(); - } - - /** - * Creates the application. - */ - protected function createApplication(): Application - { - return new Application(); - } - - /** - * Create a router. - */ - protected function getRouter(): Router - { - return new Router($this->app['events'], $this->app); - } - - /** - * Create a new HttpKernel client instance. - */ - protected function createClient(array $server = array()): HttpKernelBrowser - { - return new HttpKernelBrowser($this->app, $server); - } - - public function testCacheRoutes(): void - { - $router = $this->getRouter(); - - $key = $router->cache(__FILE__, function () use ($router) { - $router->get('/', 'HomeController@actionIndex'); - }); - - static::assertTrue($this->app->cache->has($key), 'Routes must be in cache'); - static::assertEquals(1, $router->getRoutes()->count(), 'Routes must be in collection'); - - $cachedRoutes = unserialize($this->app->cache->get($key)); - static::assertArrayHasKey('routes', $cachedRoutes); - static::assertArrayHasKey('GET', $cachedRoutes['routes']); - static::assertCount(1, $cachedRoutes['routes']['GET']); - - // Next request should not call the callback. - $router = $this->getRouter(); - $router->cache(__FILE__, function () use ($router) { - throw new RuntimeException('This should not be called'); - }); - static::assertEquals(1, $router->getRoutes()->count(), 'Routes must be obtained from cache'); - } - - public function testCacheRoutesNoTtl(): void - { - $router = $this->getRouter(); - - $key = $router->cache(__FILE__, function () use ($router) { - $router->get('/', 'HomeController@actionIndex'); - }, 0); - - static::assertNull($key, 'Cache key should be null with TTL=0'); - static::assertEquals(1, $router->getRoutes()->count(), 'Route must be added to router'); - } - - public function testAllMethodsWorks(): void - { - $methods = ['get', 'post', 'put', 'patch', 'delete']; - - $router = $this->getRouter(); - - $key = $router->cache(__FILE__, function () use ($router, $methods) { - foreach ($methods as $method) { - $router->$method('/', 'HomeController@action' . ucfirst($method)); - } - }); - - static::assertTrue($this->app->cache->has($key), 'Routes must be in cache'); - static::assertEquals(count($methods), $router->getRoutes()->count(), 'Routes must be in collection'); - - $cachedRoutes = unserialize($this->app->cache->get($key)); - static::assertArrayHasKey('routes', $cachedRoutes); - - foreach ($methods as $method) { - static::assertArrayHasKey(strtoupper($method), $cachedRoutes['routes']); - static::assertCount(1, $cachedRoutes['routes'][strtoupper($method)]); - } - - // Next request should not call the callback. - $router = $this->getRouter(); - $router->cache(__FILE__, function () use ($router) { - throw new RuntimeException('This should not be called'); - }); - static::assertEquals(count($methods), $router->getRoutes()->count(), 'Routes must be obtained from cache'); - } - - public function testControllerRouting(): void - { - $router = $this->getRouter(); - - $controllerName = str_shuffle('abcdefghijklmnopqrstuvwxyz'); - - // Create a controller class. - eval('class ' . $controllerName . ' extends Illuminate\Routing\Controller { public function getHomePage() {} }'); - - $key = $router->cache(__FILE__, function () use ($router, $controllerName) { - $router->controller('/', $controllerName); - }); - - static::assertTrue($this->app->cache->has($key), 'Routes must be in cache'); - // 2 because controller adds missingMethod - static::assertEquals(2, $router->getRoutes()->count(), 'Routes must be in collection'); - - // Next request should not call the callback. - $router = $this->getRouter(); - $router->cache(__FILE__, function () use ($router) { - throw new Exception('This should not be called'); - }); - static::assertEquals(2, $router->getRoutes()->count(), 'Routes must be obtained from cache'); - } - - public function testCanDispatchRequest(): void - { - // Create a controller class. - $controllerName = str_shuffle('abcdefghijklmnopqrstuvwxyz'); - eval('class ' . $controllerName . ' extends Illuminate\Routing\Controller { - public function getIndex() { - return Illuminate\Support\Facades\Response::make(1); - } - }'); - - // First, define a route. - $router = $this->getRouter(); - $router->cache(__FILE__, function () use ($router, $controllerName) { - $router->get('/', $controllerName . '@getIndex'); - }); - - // Create a new router, set it on the app, and simulate a request. - $this->app['router'] = $this->getRouter(); - $this->app['router']->cache(__FILE__, function () use ($router) { - throw new RuntimeException('This should not be called'); - }); - - $client = $this->createClient(); - $client->request('get', '/'); - - $response = $client->getResponse(); - static::assertInstanceOf('Symfony\Component\HttpFoundation\Response', $response); - static::assertEquals(1, $response->getContent()); - } - - public function testCanRouteToClosure(): void - { - // Create a new router, set it on the app, and simulate a request. - $this->app['router'] = $this->getRouter(); - $this->app['router']->get('/', fn() => 1); - - $client = $this->createClient(); - $client->request('get', '/'); - - $response = $client->getResponse(); - static::assertInstanceOf('Symfony\Component\HttpFoundation\Response', $response); - static::assertEquals(1, $response->getContent()); - } - - public function testCanGroupRoutes(): void - { - $router = $this->getRouter(); - - $controllerName = str_shuffle('abcdefghijklmnopqrstuvwxyz'); - - // Create a controller class. - eval('class ' . $controllerName . ' extends Illuminate\Routing\Controller { - public function getHomePage() { - return Illuminate\Support\Facades\Response::make(1); - } - }'); - - $router->cache(__FILE__, function () use ($controllerName, $router) { - $router->group( - ['prefix' => 'grouped'], - function () use ($router, $controllerName) { - $router->get('/', $controllerName . '@getHomePage'); - $router->get('/dashboard', $controllerName . '@getHomePage'); - } - ); - }); - - // 2 routes originating from group closure - static::assertEquals(2, $router->getRoutes()->count(), 'Routes must be in collection'); - - // Create a new router, set it on the app, and simulate a request. - $this->app['router'] = $this->getRouter(); - $this->app['router']->cache(__FILE__, callback: function () use ($router) { - throw new RuntimeException('This should not be called'); - }); - - $client = $this->createClient(); - $client->request('GET', '/grouped/dashboard'); - - $response = $client->getResponse(); - static::assertInstanceOf('Symfony\Component\HttpFoundation\Response', $response); - static::assertEquals(1, $response->getContent()); - } - - public function testCanChainWheres(): void - { - $this->expectException(NotFoundHttpException::class); - - $this->app['router'] = $this->getRouter(); - $this->app['router']->get('/{foo}/{bar}', fn() => 'baz') - ->where('foo', '\w+') - ->where('bar', '\d+'); - - // /herp/derp should not match above route. - $client = $this->createClient(); - $client->catchExceptions(false); - $client->request('GET', '/herp/derp'); - } - - public function testWheresRetainedInCache(): void - { - $this->expectException(NotFoundHttpException::class); - - // Create a controller class. - $controllerName = str_shuffle('abcdefghijklmnopqrstuvwxyz'); - eval('class ' . $controllerName . ' extends Illuminate\Routing\Controller { - public function getIndex() { - return Illuminate\Support\Facades\Response::make(1); - } - }'); - - // First, define a route. - $router = $this->getRouter(); - $router->cache(__FILE__, function () use ($router, $controllerName) { - $router->get('/{foo}/{bar}', $controllerName . '@getIndex')->where('foo', '\w+')->where('bar', '\d+'); - }); - - // Create a new router, set it on the app, and simulate a request. - $this->app['router'] = $this->getRouter(); - $this->app['router']->cache(__FILE__, callback: function () use ($router) { - throw new RuntimeException('This should not be called'); - }); - - // /herp/derp should not match above route. - $client = $this->createClient(); - $client->catchExceptions(false); - $client->request('GET', '/herp/derp'); - } - - public function testCanUseResource(): void - { - // Create a controller class. - $controllerName = str_shuffle('abcdefghijklmnopqrstuvwxyz'); - eval('class ' . $controllerName . ' extends Illuminate\Routing\Controller { }'); - - // First, define a resource. - $router = $this->getRouter(); - $key = $router->cache(__FILE__, function () use ($router, $controllerName) { - $router->resource('item', $controllerName); - }); - - static::assertTrue($this->app->cache->has($key), 'Routes must be in cache'); - static::assertEquals(8, $router->getRoutes()->count(), 'Routes must be in collection'); - - // Next request should not call the callback. - $router = $this->getRouter(); - $router->cache(__FILE__, function () use ($router) { - throw new RuntimeException('This should not be called'); - }); - static::assertEquals(8, $router->getRoutes()->count(), 'Routes must be obtained from cache'); - } - - public function testCanClearCache(): void - { - // Create a controller class. - $controllerName = str_shuffle('abcdefghijklmnopqrstuvwxyz'); - eval('class ' . $controllerName . ' extends Illuminate\Routing\Controller { }'); - - // First, define a route. - $router = $this->getRouter(); - $key = $router->cache(__FILE__, function () use ($router, $controllerName) { - $router->get('/{foo}/{bar}', $controllerName . '@getIndex')->where('foo', '\w+')->where('bar', '\d+'); - }); - static::assertTrue($this->app->cache->has($key), 'Routes must be in cache'); - - // Next, clear it. - $router->clearCache(__FILE__); - static::assertFalse($this->app->cache->has($key), 'Routes must no longer be cached'); - } - - public function testRebuildsWhenCachedFileIsCorrupt(): void - { - $router = $this->getRouter(); - $router->cache(__FILE__, function () use ($router) { - $router->get('/', 'HomeController@actionIndex'); - }); - - // Simulate a torn/partial write: keep a far-future expiry prefix so the - // entry is not treated as expired, but leave an unserializable body. - foreach (glob(self::$cachePath . '/*/*/*') as $file) { - file_put_contents($file, '9999999999corrupt-payload'); - } - - $rebuilt = false; - $router = $this->getRouter(); - $router->cache(__FILE__, function () use ($router, &$rebuilt) { - $rebuilt = true; - $router->get('/', 'HomeController@actionIndex'); - }); - - static::assertTrue($rebuilt, 'Corrupt cache must trigger a rebuild, not a failure'); - static::assertEquals(1, $router->getRoutes()->count(), 'Routes must be rebuilt from the callback'); - } - - public function testBootStillWorksWhenCacheIsUnwritable(): void - { - // Point the cache at a path that cannot be created (a file where a - // directory is expected), so the underlying write fails — mirroring an - // unwritable cache directory on a production node. - $files = new Filesystem; - $files->makeDirectory(self::$cachePath, 0777, true, true); - $blocker = self::$cachePath . '/blocker'; - file_put_contents($blocker, 'x'); - $this->app['config']['cache.path'] = $blocker . '/nested'; - - $router = $this->getRouter(); - $key = $router->cache(__FILE__, function () use ($router) { - $router->get('/', 'HomeController@actionIndex'); - }); - - static::assertNotNull($key, 'cache() must return normally despite the write failure'); - static::assertEquals(1, $router->getRoutes()->count(), 'Routes must still be defined when caching fails'); - } - - public function testBootStillWorksWhenRouteFileIsMissing(): void - { - // A route file removed in a deploy (while a worker still serves its - // stale opcode) makes filemtime() fail on the cache-key path. That must - // fall back to a rebuild, not escalate to a fatal boot error. - $missing = sys_get_temp_dir() . '/route-deleted-mid-deploy-' . uniqid() . '.php'; - - $defined = false; - $router = $this->getRouter(); - $key = $router->cache($missing, function () use ($router, &$defined) { - $defined = true; - $router->get('/', 'HomeController@actionIndex'); - }); - - static::assertNull($key, 'An unstattable route file must not be cached'); - static::assertTrue($defined, 'A missing route file must still define its routes directly'); - static::assertEquals(1, $router->getRoutes()->count(), 'Routes must still be defined'); - } -} diff --git a/tests/CachedRouting/RoutingServiceProviderTest.php b/tests/CachedRouting/RoutingServiceProviderTest.php deleted file mode 100644 index 05588ca40..000000000 --- a/tests/CachedRouting/RoutingServiceProviderTest.php +++ /dev/null @@ -1,97 +0,0 @@ -app === null) { - $this->refreshApplication(); - } - } - - /** - * Refresh the application instance. - * - * @return void - */ - protected function refreshApplication(): void - { - $this->app = $this->createApplication(); - - $this->app['env'] = 'testing'; - - $this->app->boot(); - } - - /** - * Creates the application. - */ - public function createApplication(): Application - { - return new Application(); - } - - /** - * @covers ::register - * @covers ::registerRouter - */ - public function testRegister(): void - { - $provider = new RoutingServiceProvider($this->app); - $provider->register(); - - static::assertInstanceOf(Router::class, $this->app->router); - } -} diff --git a/tests/Routing/RoutingControllerDispatcherTest.php b/tests/Routing/RoutingControllerDispatcherTest.php deleted file mode 100644 index 3e238c592..000000000 --- a/tests/Routing/RoutingControllerDispatcherTest.php +++ /dev/null @@ -1,74 +0,0 @@ - function () { - } - ] - ); - $route->bind($request); - $dispatcher = new ControllerDispatcher(m::mock(RouteFiltererInterface::class), new Container); - - $this->assertNull($_SERVER['ControllerDispatcherTestControllerStub']); - - $response = $dispatcher->dispatch($route, $request, 'ControllerDispatcherTestControllerStub', 'getIndex'); - $this->assertEquals('getIndex', $response); - $this->assertEquals('setupLayout', $_SERVER['ControllerDispatcherTestControllerStub']); - } - -} - - -class ControllerDispatcherTestControllerStub extends Controller { - - public function __construct() - { - // construct shouldn't affect setupLayout. - } - - protected function setupLayout() - { - $_SERVER['ControllerDispatcherTestControllerStub'] = __FUNCTION__; - } - - - public function getIndex() - { - return __FUNCTION__; - } - - - public function getFoo() - { - return __FUNCTION__; - } - -} diff --git a/tests/Routing/RoutingRedirectorTest.php b/tests/Routing/RoutingRedirectorTest.php deleted file mode 100644 index 449c92f03..000000000 --- a/tests/Routing/RoutingRedirectorTest.php +++ /dev/null @@ -1,167 +0,0 @@ -headers = m::mock(HeaderBag::class); - - $this->request = m::mock(Request::class); - $this->request->headers = $this->headers; - - $this->url = m::mock(UrlGenerator::class); - $this->url->shouldReceive('getRequest')->andReturn($this->request); - $this->url->shouldReceive('to')->with('bar', [], null)->andReturn('http://foo.com/bar'); - $this->url->shouldReceive('to')->with('bar', [], true)->andReturn('https://foo.com/bar'); - $this->url->shouldReceive('to')->with('login', [], null)->andReturn('http://foo.com/login'); - $this->url->shouldReceive('to')->with('http://foo.com/bar', [], null)->andReturn('http://foo.com/bar'); - $this->url->shouldReceive('to')->with('/', [], null)->andReturn('http://foo.com/'); - - $this->session = m::mock(Store::class); - - $this->redirect = new Redirector($this->url); - $this->redirect->setSession($this->session); - } - - - protected function tearDown(): void - { - m::close(); - } - - - public function testBasicRedirectTo() - { - $response = $this->redirect->to('bar'); - - $this->assertInstanceOf(RedirectResponse::class, $response); - $this->assertEquals('http://foo.com/bar', $response->getTargetUrl()); - $this->assertEquals(302, $response->getStatusCode()); - $this->assertEquals($this->session, $response->getSession()); - } - - - public function testComplexRedirectTo() - { - $response = $this->redirect->to('bar', 303, ['X-RateLimit-Limit' => 60, 'X-RateLimit-Remaining' => 59], true); - - $this->assertEquals('https://foo.com/bar', $response->getTargetUrl()); - $this->assertEquals(303, $response->getStatusCode()); - $this->assertEquals(60, $response->headers->get('X-RateLimit-Limit')); - $this->assertEquals(59, $response->headers->get('X-RateLimit-Remaining')); - } - - - public function testGuestPutCurrentUrlInSession() - { - $this->url->shouldReceive('full')->andReturn('http://foo.com/bar'); - $this->session->shouldReceive('put')->once()->with('url.intended', 'http://foo.com/bar'); - - $response = $this->redirect->guest('login'); - - $this->assertEquals('http://foo.com/login', $response->getTargetUrl()); - } - - - public function testIntendedRedirectToIntendedUrlInSession() - { - $this->session->shouldReceive('pull')->with('url.intended', '/')->andReturn('http://foo.com/bar'); - - $response = $this->redirect->intended(); - - $this->assertEquals('http://foo.com/bar', $response->getTargetUrl()); - } - - - public function testIntendedWithoutIntendedUrlInSession() - { - $this->session->shouldReceive('forget')->with('url.intended'); - - // without fallback url - $this->session->shouldReceive('pull')->with('url.intended', '/')->andReturn('/'); - $response = $this->redirect->intended(); - $this->assertEquals('http://foo.com/', $response->getTargetUrl()); - - // with a fallback url - $this->session->shouldReceive('pull')->with('url.intended', 'bar')->andReturn('bar'); - $response = $this->redirect->intended('bar'); - $this->assertEquals('http://foo.com/bar', $response->getTargetUrl()); - } - - - public function testRefreshRedirectToCurrentUrl() - { - $this->request->shouldReceive('path')->andReturn('http://foo.com/bar'); - $response = $this->redirect->refresh(); - $this->assertEquals('http://foo.com/bar', $response->getTargetUrl()); - } - - - public function testBackRedirectToHttpReferer() - { - $this->url->shouldReceive('previous')->andReturn('http://foo.com/bar'); - $response = $this->redirect->back(); - $this->assertEquals('http://foo.com/bar', $response->getTargetUrl()); - } - - - public function testBackRedirectToFallbackUrl() - { - $this->url->shouldReceive('previous')->with('/fallback')->andReturn('http://foo.com/fallback'); - - $response = $this->redirect->back(fallback: '/fallback'); - $this->assertEquals('http://foo.com/fallback', $response->getTargetUrl()); - } - - - public function testAwayDoesntValidateTheUrl() - { - $response = $this->redirect->away('bar'); - $this->assertEquals('bar', $response->getTargetUrl()); - } - - - public function testSecureRedirectToHttpsUrl() - { - $response = $this->redirect->secure('bar'); - $this->assertEquals('https://foo.com/bar', $response->getTargetUrl()); - } - - - public function testAction() - { - $this->url->shouldReceive('action')->with('bar@index', [])->andReturn('http://foo.com/bar'); - $response = $this->redirect->action('bar@index'); - $this->assertEquals('http://foo.com/bar', $response->getTargetUrl()); - } - - - public function testRoute() - { - $this->url->shouldReceive('route')->with('home')->andReturn('http://foo.com/bar'); - $this->url->shouldReceive('route')->with('home', [])->andReturn('http://foo.com/bar'); - - $response = $this->redirect->route('home'); - $this->assertEquals('http://foo.com/bar', $response->getTargetUrl()); - - $response = $this->redirect->home(); - $this->assertEquals('http://foo.com/bar', $response->getTargetUrl()); - } - -} diff --git a/tests/Routing/RoutingRouteTest.php b/tests/Routing/RoutingRouteTest.php deleted file mode 100644 index 64dec601b..000000000 --- a/tests/Routing/RoutingRouteTest.php +++ /dev/null @@ -1,1084 +0,0 @@ -getRouter(); - $router->get( - 'foo/bar', - fn() => 'hello' - ); - $this->assertEquals('hello', $router->dispatch(Request::create('foo/bar', 'GET'))->getContent()); - - $router = $this->getRouter(); - $route = $router->get( - 'foo/bar', - [ - 'domain' => 'api.{name}.bar', - fn($name) => $name - ] - ); - $route = $router->get( - 'foo/bar', - [ - 'domain' => 'api.{name}.baz', - fn($name) => $name - ] - ); - $this->assertEquals('taylor', $router->dispatch(Request::create('http://api.taylor.bar/foo/bar', 'GET'))->getContent()); - $this->assertEquals('dayle', $router->dispatch(Request::create('http://api.dayle.baz/foo/bar', 'GET'))->getContent()); - - $router = $this->getRouter(); - $route = $router->get('foo/{age}', ['domain' => 'api.{name}.bar', fn($name, $age) => $name.$age]); - $this->assertEquals('taylor25', $router->dispatch(Request::create('http://api.taylor.bar/foo/25', 'GET'))->getContent()); - - $router = $this->getRouter(); - $router->get('foo/bar', fn() => 'hello'); - $router->post('foo/bar', fn() => 'post hello'); - $this->assertEquals('hello', $router->dispatch(Request::create('foo/bar', 'GET'))->getContent()); - $this->assertEquals('post hello', $router->dispatch(Request::create('foo/bar', 'POST'))->getContent()); - - $router = $this->getRouter(); - $router->get('foo/{bar}', fn($name) => $name); - $this->assertEquals('taylor', $router->dispatch(Request::create('foo/taylor', 'GET'))->getContent()); - - $router = $this->getRouter(); - $router->get('foo/{bar}/{baz?}', fn($name, $age = 25) => $name.$age); - $this->assertEquals('taylor25', $router->dispatch(Request::create('foo/taylor', 'GET'))->getContent()); - - $router = $this->getRouter(); - $router->get('foo/{name}/boom/{age?}/{location?}', fn($name, $age = 25, $location = 'AR') => $name.$age.$location); - $this->assertEquals('taylor30AR', $router->dispatch(Request::create('foo/taylor/boom/30', 'GET'))->getContent()); - - $router = $this->getRouter(); - $router->get('{bar}/{baz?}', fn($name, $age = 25) => $name.$age); - $this->assertEquals('taylor25', $router->dispatch(Request::create('taylor', 'GET'))->getContent()); - - $router = $this->getRouter(); - $router->get('{baz?}', fn($age = 25) => $age); - $this->assertEquals('25', $router->dispatch(Request::create('/', 'GET'))->getContent()); - $this->assertEquals('30', $router->dispatch(Request::create('30', 'GET'))->getContent()); - - $router = $this->getRouter(); - $router->get('{foo?}/{baz?}', ['as' => 'foo', fn($name = 'taylor', $age = 25) => $name.$age]); - $this->assertEquals('taylor25', $router->dispatch(Request::create('/', 'GET'))->getContent()); - $this->assertEquals('fred25', $router->dispatch(Request::create('fred', 'GET'))->getContent()); - $this->assertEquals('fred30', $router->dispatch(Request::create('fred/30', 'GET'))->getContent()); - $this->assertTrue($router->currentRouteNamed('foo')); - $this->assertTrue($router->is('foo')); - $this->assertFalse($router->is('bar')); - - $router = $this->getRouter(); - $router->get('foo/bar', fn() => 'hello'); - $this->assertEquals('', $router->dispatch(Request::create('foo/bar', 'HEAD'))->getContent()); - - $router = $this->getRouter(); - $router->any('foo/bar', fn() => 'hello'); - $this->assertEquals('', $router->dispatch(Request::create('foo/bar', 'HEAD'))->getContent()); - - $router = $this->getRouter(); - $router->get('foo/bar', fn() => 'first'); - $router->get('foo/bar', fn() => 'second'); - $this->assertEquals('second', $router->dispatch(Request::create('foo/bar', 'GET'))->getContent()); - - $router = $this->getRouter(); - $router->get('foo/bar/åαф', function() { return 'hello'; }); - $this->assertEquals('hello', $router->dispatch(Request::create('foo/bar/%C3%A5%CE%B1%D1%84', 'GET'))->getContent()); - } - - - public function testOptionsResponsesAreGeneratedByDefault(): void - { - $router = $this->getRouter(); - $router->get('foo/bar', fn() => 'hello'); - $router->post('foo/bar', fn() => 'hello'); - $response = $router->dispatch(Request::create('foo/bar', 'OPTIONS')); - - $this->assertEquals(200, $response->getStatusCode()); - $this->assertEquals('GET,HEAD,POST', $response->headers->get('Allow')); - } - - - public function testHeadDispatcher(): void - { - $router = $this->getRouter(); - $router->match(['GET', 'POST'], 'foo', fn() => 'bar'); - - $response = $router->dispatch(Request::create('foo', 'OPTIONS')); - $this->assertEquals(200, $response->getStatusCode()); - $this->assertEquals('GET,HEAD,POST', $response->headers->get('Allow')); - - $response = $router->dispatch(Request::create('foo', 'HEAD')); - $this->assertEquals(200, $response->getStatusCode()); - $this->assertEquals('', $response->getContent()); - - $router = $this->getRouter(); - $router->match(['GET'], 'foo', fn() => 'bar'); - - $response = $router->dispatch(Request::create('foo', 'OPTIONS')); - $this->assertEquals(200, $response->getStatusCode()); - $this->assertEquals('GET,HEAD', $response->headers->get('Allow')); - - $router = $this->getRouter(); - $router->match(['POST'], 'foo', fn() => 'bar'); - - $response = $router->dispatch(Request::create('foo', 'OPTIONS')); - $this->assertEquals(200, $response->getStatusCode()); - $this->assertEquals('POST', $response->headers->get('Allow')); - } - - - public function testNonGreedyMatches(): void - { - $route = new Route('GET', 'images/{id}.{ext}', function() {}); - - $request1 = Request::create('images/1.png', 'GET'); - $this->assertTrue($route->matches($request1)); - $route->bind($request1); - $this->assertEquals('1', $route->parameter('id')); - $this->assertEquals('png', $route->parameter('ext')); - - $request2 = Request::create('images/12.png', 'GET'); - $this->assertTrue($route->matches($request2)); - $route->bind($request2); - $this->assertEquals('12', $route->parameter('id')); - $this->assertEquals('png', $route->parameter('ext')); - - // Test parameter() default value - $route = new Route('GET', 'foo/{foo?}', function() {}); - - $request3 = Request::create('foo', 'GET'); - $this->assertTrue($route->matches($request3)); - $route->bind($request3); - $this->assertEquals('bar', $route->parameter('foo', 'bar')); - } - - - public function testRoutesDontMatchNonMatchingPathsWithLeadingOptionals(): void - { - $this->expectException(Symfony\Component\HttpKernel\Exception\NotFoundHttpException::class); - $router = $this->getRouter(); - $router->get( - '{baz?}', - fn($age = 25) => $age - ); - $this->assertEquals('25', $router->dispatch(Request::create('foo/bar', 'GET'))->getContent()); - } - - - public function testRoutesDontMatchNonMatchingDomain(): void - { - $this->expectException(Symfony\Component\HttpKernel\Exception\NotFoundHttpException::class); - $router = $this->getRouter(); - $route = $router->get( - 'foo/bar', - [ - 'domain' => 'api.foo.bar', - fn() => 'hello' - ] - ); - $this->assertEquals( - 'hello', - $router->dispatch(Request::create('http://api.baz.boom/foo/bar', 'GET'))->getContent() - ); - } - - - public function testDispatchingOfControllers(): void - { - $router = $this->getRouter(); - $router->get('foo', 'RouteTestControllerDispatchStub@foo'); - $this->assertEquals('bar', $router->dispatch(Request::create('foo', 'GET'))->getContent()); - - $router = $this->getRouter(); - $router->filter('foo', fn() => 'filter'); - $router->get('bar', 'RouteTestControllerDispatchStub@bar'); - $this->assertEquals('filter', $router->dispatch(Request::create('bar', 'GET'))->getContent()); - - $router = $this->getRouter(); - $router->get('baz', 'RouteTestControllerDispatchStub@baz'); - $this->assertEquals('filtered', $router->dispatch(Request::create('baz', 'GET'))->getContent()); - - - unset($_SERVER['__test.after.filter']); - $router = $this->getRouter(); - $router->filter('qux', function() - { - $_SERVER['__test.after.filter'] = true; - }); - $router->get('qux', 'RouteTestControllerDispatchStub@qux'); - $this->assertEquals('qux', $router->dispatch(Request::create('qux', 'GET'))->getContent()); - $this->assertTrue($_SERVER['__test.after.filter']); - - /** - * Test filter removal. - */ - $router = $this->getRouter(); - $router->filter('removeBefore', function() { - $_SERVER['__test.before.removeBeforeFilter'] = true; - }); - $router->get('beforeRoute', 'RouteTestControllerRemoveFilterStub@beforeRoute'); - $this->assertEquals('beforeRoute', $router->dispatch(Request::create('beforeRoute', 'GET'))->getContent()); - $this->assertTrue(!isset($_SERVER['__test.after.removeBeforeFilter']) || is_null(isset($_SERVER['__test.after.removeBeforeFilter']))); - - $router = $this->getRouter(); - $router->filter('removeAfter', function() { - $_SERVER['__test.after.removeAfterFilter'] = true; - }); - $router->get('afterRoute', 'RouteTestControllerRemoveFilterStub@afterRoute'); - $this->assertEquals('afterRoute', $router->dispatch(Request::create('afterRoute', 'GET'))->getContent()); - $this->assertTrue(!isset($_SERVER['__test.after.removeAfterFilter']) || is_null(isset($_SERVER['__test.after.removeAfterFilter']))); - - /** - * Test filters disabled... - */ - $router = $this->getRouter(); - $router->filter('foo', fn() => 'filter'); - $router->disableFilters(); - $router->get('bar', 'RouteTestControllerDispatchStub@bar'); - $this->assertEquals('baz', $router->dispatch(Request::create('bar', 'GET'))->getContent()); - - $this->assertTrue($router->currentRouteUses('RouteTestControllerDispatchStub@bar')); - $this->assertTrue($router->uses('RouteTestControllerDispatchStub@bar')); - $this->assertFalse($router->uses('RouteTestControllerDispatchStub@baz')); - } - - - public function testBasicBeforeFilters(): void - { - $router = $this->getRouter(); - $router->get('foo/bar', fn() => 'hello'); - $router->before(fn() => 'foo!'); - $this->assertEquals('foo!', $router->dispatch(Request::create('foo/bar', 'GET'))->getContent()); - - $router = $this->getRouter(); - $router->get('foo/bar', fn() => 'hello'); - $router->before('RouteTestFilterStub'); - $this->assertEquals('foo!', $router->dispatch(Request::create('foo/bar', 'GET'))->getContent()); - - $router = $this->getRouter(); - $router->get('foo/bar', fn() => 'hello'); - $router->before('RouteTestFilterStub@handle'); - $this->assertEquals('handling!', $router->dispatch(Request::create('foo/bar', 'GET'))->getContent()); - - $router = $this->getRouter(); - $router->get('foo/bar', ['before' => 'foo', fn() => 'hello']); - $router->filter('foo', fn() => 'foo!'); - $this->assertEquals('foo!', $router->dispatch(Request::create('foo/bar', 'GET'))->getContent()); - - $router = $this->getRouter(); - $router->get('foo/bar', ['before' => 'foo:25', fn() => 'hello']); - $router->filter('foo', fn($route, $request, $age) => $age); - $this->assertEquals('25', $router->dispatch(Request::create('foo/bar', 'GET'))->getContent()); - - $router = $this->getRouter(); - $router->get('foo/bar', ['before' => 'foo:0,taylor', fn() => 'hello']); - $router->filter('foo', fn($route, $request, $age, $name) => $age.$name); - $this->assertEquals('0taylor', $router->dispatch(Request::create('foo/bar', 'GET'))->getContent()); - - $router = $this->getRouter(); - $router->get('foo/bar', ['before' => 'foo:bar,baz', fn() => 'hello']); - $router->filter('foo', fn($route, $request, $bar, $baz) => $bar.$baz); - $this->assertEquals('barbaz', $router->dispatch(Request::create('foo/bar', 'GET'))->getContent()); - - $router = $this->getRouter(); - $router->get('foo/bar', ['before' => 'foo:bar,baz|bar:boom', fn() => 'hello']); - $router->filter('foo', fn($route, $request, $bar, $baz) => null); - $router->filter('bar', fn($route, $request, $boom) => $boom); - $this->assertEquals('boom', $router->dispatch(Request::create('foo/bar', 'GET'))->getContent()); - - /** - * Basic filter parameter - */ - unset($_SERVER['__route.filter']); - $router = $this->getRouter(); - $router->get('foo/bar', ['before' => 'foo:bar', fn() => 'hello']); - $router->filter('foo', function($route, $request, $value = null) { $_SERVER['__route.filter'] = $value; }); - $router->dispatch(Request::create('foo/bar', 'GET')); - $this->assertEquals('bar', $_SERVER['__route.filter']); - - /** - * Optional filter parameter - */ - unset($_SERVER['__route.filter']); - $router = $this->getRouter(); - $router->get('foo/bar', ['before' => 'foo', fn() => 'hello']); - $router->filter('foo', function($route, $request, $value = null) { $_SERVER['__route.filter'] = $value; }); - $router->dispatch(Request::create('foo/bar', 'GET')); - $this->assertNull($_SERVER['__route.filter']); - } - - - public function testFiltersCanBeDisabled(): void - { - $router = $this->getRouter(); - $router->disableFilters(); - $router->get('foo/bar', fn() => 'hello'); - $router->before(fn() => 'foo!'); - $this->assertEquals('hello', $router->dispatch(Request::create('foo/bar', 'GET'))->getContent()); - - $router = $this->getRouter(); - $router->disableFilters(); - $router->get('foo/bar', ['before' => 'foo', fn() => 'hello']); - $router->filter('foo', fn() => 'foo!'); - $this->assertEquals('hello', $router->dispatch(Request::create('foo/bar', 'GET'))->getContent()); - } - - - public function testGlobalAfterFilters(): void - { - unset($_SERVER['__filter.after']); - $router = $this->getRouter(); - $router->get('foo/bar', fn() => 'hello'); - $router->after(function() { $_SERVER['__filter.after'] = true; return 'foo!'; }); - - $this->assertEquals('hello', $router->dispatch(Request::create('foo/bar', 'GET'))->getContent()); - $this->assertTrue($_SERVER['__filter.after']); - } - - - public function testBasicAfterFilters(): void - { - unset($_SERVER['__filter.after']); - $router = $this->getRouter(); - $router->get('foo/bar', ['after' => 'foo', fn() => 'hello']); - $router->filter('foo', function() { $_SERVER['__filter.after'] = true; return 'foo!'; }); - - $this->assertEquals('hello', $router->dispatch(Request::create('foo/bar', 'GET'))->getContent()); - $this->assertTrue($_SERVER['__filter.after']); - } - - - public function testPatternBasedFilters(): void - { - $router = $this->getRouter(); - $router->get('foo/bar', fn() => 'hello'); - $router->filter('foo', fn($route, $request, $bar) => 'foo'.$bar); - $router->when('foo/*', 'foo:bar'); - $this->assertEquals('foobar', $router->dispatch(Request::create('foo/bar', 'GET'))->getContent()); - - $router = $this->getRouter(); - $router->get('foo/bar', fn() => 'hello'); - $router->filter('foo', fn($route, $request, $bar) => 'foo'.$bar); - $router->when('bar/*', 'foo:bar'); - $this->assertEquals('hello', $router->dispatch(Request::create('foo/bar', 'GET'))->getContent()); - - $router = $this->getRouter(); - $router->get('foo/bar', fn() => 'hello'); - $router->filter('foo', fn($route, $request, $bar) => 'foo'.$bar); - $router->when('foo/*', 'foo:bar', ['post']); - $this->assertEquals('hello', $router->dispatch(Request::create('foo/bar', 'GET'))->getContent()); - - $router = $this->getRouter(); - $router->get('foo/bar', fn() => 'hello'); - $router->filter('foo', fn($route, $request, $bar) => 'foo'.$bar); - $router->when('foo/*', 'foo:bar', ['get']); - $this->assertEquals('foobar', $router->dispatch(Request::create('foo/bar', 'GET'))->getContent()); - - $router = $this->getRouter(); - $router->get('foo/bar', fn() => 'hello'); - $router->filter('foo', function($route, $request) {}); - $router->filter('bar', fn($route, $request) => 'bar'); - $router->when('foo/*', 'foo|bar', ['get']); - $this->assertEquals('bar', $router->dispatch(Request::create('foo/bar', 'GET'))->getContent()); - } - - - public function testRegexBasedFilters(): void - { - $router = $this->getRouter(); - $router->get('foo/bar', fn() => 'hello'); - $router->get('bar/foo', fn() => 'hello'); - $router->get('baz/foo', fn() => 'hello'); - $router->filter('foo', fn($route, $request, $bar) => 'foo'.$bar); - $router->whenRegex('/^(foo|bar).*/', 'foo:bar'); - $this->assertEquals('foobar', $router->dispatch(Request::create('foo/bar', 'GET'))->getContent()); - $this->assertEquals('foobar', $router->dispatch(Request::create('bar/foo', 'GET'))->getContent()); - $this->assertEquals('hello', $router->dispatch(Request::create('baz/foo', 'GET'))->getContent()); - } - - - public function testRegexBasedFiltersWithVariables(): void - { - $router = $this->getRouter(); - $router->get('{var}/bar', fn($var) => 'hello'); - $router->filter('foo', fn($route, $request, $bar) => 'foo'.$bar); - $router->whenRegex('/^(foo|bar).*/', 'foo:bar'); - $this->assertEquals('foobar', $router->dispatch(Request::create('foo/bar', 'GET'))->getContent()); - $this->assertEquals('foobar', $router->dispatch(Request::create('bar/bar', 'GET'))->getContent()); - $this->assertEquals('hello', $router->dispatch(Request::create('baz/bar', 'GET'))->getContent()); - } - - - public function testGroupFiltersAndRouteFilters(): void - { - $router = $this->getRouter(); - $router->group(['before' => ['foo']], function() use ($router) - { - $router->get('foo/bar', fn() => 'hello')->before('bar'); - }); - $router->filter('foo', fn($route, $request) => 'foo'); - $router->filter('bar', fn($route, $request) => 'bar'); - $this->assertEquals('foo', $router->dispatch(Request::create('foo/bar', 'GET'))->getContent()); - } - - - public function testMatchesMethodAgainstRequests(): void - { - /** - * Basic - */ - $request = Request::create('foo/bar', 'GET'); - $route = new Route('GET', 'foo/{bar}', function() {}); - $this->assertTrue($route->matches($request)); - - $request = Request::create('foo/bar', 'GET'); - $route = new Route('GET', 'foo', function() {}); - $this->assertFalse($route->matches($request)); - - /** - * Method checks - */ - $request = Request::create('foo/bar', 'GET'); - $route = new Route('GET', 'foo/{bar}', function() {}); - $this->assertTrue($route->matches($request)); - - $request = Request::create('foo/bar', 'POST'); - $route = new Route('GET', 'foo', function() {}); - $this->assertFalse($route->matches($request)); - - /** - * Domain checks - */ - $request = Request::create('http://something.foo.com/foo/bar', 'GET'); - $route = new Route('GET', 'foo/{bar}', ['domain' => '{foo}.foo.com', function() {}]); - $this->assertTrue($route->matches($request)); - - $request = Request::create('http://something.bar.com/foo/bar', 'GET'); - $route = new Route('GET', 'foo/{bar}', ['domain' => '{foo}.foo.com', function() {}]); - $this->assertFalse($route->matches($request)); - - /** - * HTTPS checks - */ - $request = Request::create('https://foo.com/foo/bar', 'GET'); - $route = new Route('GET', 'foo/{bar}', ['https', function() {}]); - $this->assertTrue($route->matches($request)); - - $request = Request::create('https://foo.com/foo/bar', 'GET'); - $route = new Route('GET', 'foo/{bar}', ['https', 'baz' => true, function() {}]); - $this->assertTrue($route->matches($request)); - - $request = Request::create('http://foo.com/foo/bar', 'GET'); - $route = new Route('GET', 'foo/{bar}', ['https', function() {}]); - $this->assertFalse($route->matches($request)); - - /** - * HTTP checks - */ - $request = Request::create('https://foo.com/foo/bar', 'GET'); - $route = new Route('GET', 'foo/{bar}', ['http', function() {}]); - $this->assertFalse($route->matches($request)); - - $request = Request::create('http://foo.com/foo/bar', 'GET'); - $route = new Route('GET', 'foo/{bar}', ['http', function() {}]); - $this->assertTrue($route->matches($request)); - - $request = Request::create('http://foo.com/foo/bar', 'GET'); - $route = new Route('GET', 'foo/{bar}', ['baz' => true, function() {}]); - $this->assertTrue($route->matches($request)); - } - - - public function testWherePatternsProperlyFilter(): void - { - $request = Request::create('foo/123', 'GET'); - $route = new Route('GET', 'foo/{bar}', function() {}); - $route->where('bar', '[0-9]+'); - $this->assertTrue($route->matches($request)); - - $request = Request::create('foo/123abc', 'GET'); - $route = new Route('GET', 'foo/{bar}', function() {}); - $route->where('bar', '[0-9]+'); - $this->assertFalse($route->matches($request)); - - $request = Request::create('foo/123abc', 'GET'); - $route = new Route('GET', 'foo/{bar}', ['where' => ['bar' => '[0-9]+'], function() {}]); - $route->where('bar', '[0-9]+'); - $this->assertFalse($route->matches($request)); - - /** - * Optional - */ - $request = Request::create('foo/123', 'GET'); - $route = new Route('GET', 'foo/{bar?}', function() {}); - $route->where('bar', '[0-9]+'); - $this->assertTrue($route->matches($request)); - - $request = Request::create('foo/123', 'GET'); - $route = new Route('GET', 'foo/{bar?}', ['where' => ['bar' => '[0-9]+'], function() {}]); - $route->where('bar', '[0-9]+'); - $this->assertTrue($route->matches($request)); - - $request = Request::create('foo/123', 'GET'); - $route = new Route('GET', 'foo/{bar?}/{baz?}', function() {}); - $route->where('bar', '[0-9]+'); - $this->assertTrue($route->matches($request)); - - $request = Request::create('foo/123/foo', 'GET'); - $route = new Route('GET', 'foo/{bar?}/{baz?}', function() {}); - $route->where('bar', '[0-9]+'); - $this->assertTrue($route->matches($request)); - - $request = Request::create('foo/123abc', 'GET'); - $route = new Route('GET', 'foo/{bar?}', function() {}); - $route->where('bar', '[0-9]+'); - $this->assertFalse($route->matches($request)); - } - - - public function testDotDoesNotMatchEverything(): void - { - $route = new Route('GET', 'images/{id}.{ext}', function() {}); - - $request1 = Request::create('images/1.png', 'GET'); - $this->assertTrue($route->matches($request1)); - $route->bind($request1); - $this->assertEquals('1', $route->parameter('id')); - $this->assertEquals('png', $route->parameter('ext')); - - $request2 = Request::create('images/12.png', 'GET'); - $this->assertTrue($route->matches($request2)); - $route->bind($request2); - $this->assertEquals('12', $route->parameter('id')); - $this->assertEquals('png', $route->parameter('ext')); - - } - - - public function testRouteBinding(): void - { - $router = $this->getRouter(); - $router->get('foo/{bar}', fn($name) => $name); - $router->bind('bar', fn($value) => strtoupper($value)); - $this->assertEquals('TAYLOR', $router->dispatch(Request::create('foo/taylor', 'GET'))->getContent()); - } - - - public function testRouteClassBinding(): void - { - $router = $this->getRouter(); - $router->get('foo/{bar}', fn($name) => $name); - $router->bind('bar', 'RouteBindingStub'); - $this->assertEquals('TAYLOR', $router->dispatch(Request::create('foo/taylor', 'GET'))->getContent()); - } - - - public function testRouteClassMethodBinding(): void - { - $router = $this->getRouter(); - $router->get('foo/{bar}', fn($name) => $name); - $router->bind('bar', 'RouteBindingStub@find'); - $this->assertEquals('dragon', $router->dispatch(Request::create('foo/Dragon', 'GET'))->getContent()); - } - - - public function testModelBinding(): void - { - $router = $this->getRouter(); - $router->get('foo/{bar}', fn($name) => $name); - $router->model('bar', 'RouteModelBindingStub'); - $this->assertEquals('TAYLOR', $router->dispatch(Request::create('foo/taylor', 'GET'))->getContent()); - } - - - public function testModelBindingWithNullReturn(): void - { - $this->expectException(Symfony\Component\HttpKernel\Exception\NotFoundHttpException::class); - $router = $this->getRouter(); - $router->get( - 'foo/{bar}', - fn($name) => $name - ); - $router->model('bar', 'RouteModelBindingNullStub'); - $router->dispatch(Request::create('foo/taylor', 'GET'))->getContent(); - } - - - public function testModelBindingWithCustomNullReturn(): void - { - $router = $this->getRouter(); - $router->get('foo/{bar}', fn($name) => $name); - $router->model('bar', 'RouteModelBindingNullStub', fn() => 'missing'); - $this->assertEquals('missing', $router->dispatch(Request::create('foo/taylor', 'GET'))->getContent()); - } - - - public function testGroupMerging(): void - { - $old = ['prefix' => 'foo/bar/']; - $this->assertEquals( - ['prefix' => 'foo/bar/baz', 'namespace' => null, 'where' => []], Router::mergeGroup( - ['prefix' => 'baz'], $old)); - - $old = ['domain' => 'foo']; - $this->assertEquals( - ['domain' => 'baz', 'prefix' => null, 'namespace' => null, 'where' => []], Router::mergeGroup( - ['domain' => 'baz'], $old)); - - $old = ['where' => ['var1' => 'foo', 'var2' => 'bar']]; - $this->assertEquals([ - 'prefix' => null, 'namespace' => null, 'where' => [ - 'var1' => 'foo', 'var2' => 'baz', 'var3' => 'qux', - ] - ], Router::mergeGroup(['where' => ['var2' => 'baz', 'var3' => 'qux']], $old)); - - $old = []; - $this->assertEquals([ - 'prefix' => null, 'namespace' => null, 'where' => [ - 'var1' => 'foo', 'var2' => 'bar', - ] - ], Router::mergeGroup(['where' => ['var1' => 'foo', 'var2' => 'bar']], $old)); - } - - - public function testRouteGrouping(): void - { - /** - * Inhereting Filters - */ - $router = $this->getRouter(); - $router->group(['before' => 'foo'], function() use ($router) - { - $router->get('foo/bar', fn() => 'hello'); - }); - $router->filter('foo', fn() => 'foo!'); - $this->assertEquals('foo!', $router->dispatch(Request::create('foo/bar', 'GET'))->getContent()); - - - /** - * Merging Filters - */ - $router = $this->getRouter(); - $router->group(['before' => 'foo'], function() use ($router) - { - $router->get('foo/bar', ['before' => 'bar', fn() => 'hello']); - }); - $router->filter('foo', function() {}); - $router->filter('bar', fn() => 'foo!'); - $this->assertEquals('foo!', $router->dispatch(Request::create('foo/bar', 'GET'))->getContent()); - - - /** - * Merging Filters - */ - $router = $this->getRouter(); - $router->group(['before' => 'foo|bar'], function() use ($router) - { - $router->get('foo/bar', ['before' => 'baz', fn() => 'hello']); - }); - $router->filter('foo', function() {}); - $router->filter('bar', function() {}); - $router->filter('baz', fn() => 'foo!'); - $this->assertEquals('foo!', $router->dispatch(Request::create('foo/bar', 'GET'))->getContent()); - - /** - * getPrefix() method - */ - $router = $this->getRouter(); - $router->group(['prefix' => 'foo'], function() use ($router) - { - $router->get('bar', fn() => 'hello'); - }); - $routes = $router->getRoutes(); - $routes = $routes->getRoutes(); - $this->assertEquals('foo', $routes[0]->getPrefix()); - } - - - public function testMergingControllerUses(): void - { - $router = $this->getRouter(); - $router->group(['namespace' => 'Namespace'], function() use ($router) - { - $router->get('foo/bar', 'Controller'); - }); - $routes = $router->getRoutes()->getRoutes(); - $action = $routes[0]->getAction(); - - $this->assertEquals('Namespace\\Controller', $action['controller']); - - - $router = $this->getRouter(); - $router->group(['namespace' => 'Namespace'], function() use ($router) - { - $router->group(['namespace' => 'Nested'], function() use ($router) - { - $router->get('foo/bar', 'Controller'); - }); - }); - $routes = $router->getRoutes()->getRoutes(); - $action = $routes[0]->getAction(); - - $this->assertEquals('Namespace\\Nested\\Controller', $action['controller']); - - - $router = $this->getRouter(); - $router->group(['prefix' => 'baz'], function() use ($router) - { - $router->group(['namespace' => 'Namespace'], function() use ($router) - { - $router->get('foo/bar', 'Controller'); - }); - }); - $routes = $router->getRoutes()->getRoutes(); - $action = $routes[0]->getAction(); - - $this->assertEquals('Namespace\\Controller', $action['controller']); - } - - - public function testResourceRouting(): void - { - $router = $this->getRouter(); - $router->resource('foo', 'FooController'); - $routes = $router->getRoutes(); - $this->assertCount(8, $routes); - - $router = $this->getRouter(); - $router->resource('foo', 'FooController', ['only' => ['show', 'destroy']]); - $routes = $router->getRoutes(); - - $this->assertCount(2, $routes); - - $router = $this->getRouter(); - $router->resource('foo', 'FooController', ['except' => ['show', 'destroy']]); - $routes = $router->getRoutes(); - - $this->assertCount(6, $routes); - - $router = $this->getRouter(); - $router->resource('foo-bars', 'FooController', ['only' => ['show']]); - $routes = $router->getRoutes(); - $routes = $routes->getRoutes(); - - $this->assertEquals('foo-bars/{foo_bars}', $routes[0]->getUri()); - - $router = $this->getRouter(); - $router->resource('foo-bars.foo-bazs', 'FooController', ['only' => ['show']]); - $routes = $router->getRoutes(); - $routes = $routes->getRoutes(); - - $this->assertEquals('foo-bars/{foo_bars}/foo-bazs/{foo_bazs}', $routes[0]->getUri()); - - $router = $this->getRouter(); - $router->resource('foo-bars', 'FooController', ['only' => ['show'], 'as' => 'prefix']); - $routes = $router->getRoutes(); - $routes = $routes->getRoutes(); - - $this->assertEquals('foo-bars/{foo_bars}', $routes[0]->getUri()); - $this->assertEquals('prefix.foo-bars.show', $routes[0]->getName()); - } - - - public function testResourceRouteNaming(): void - { - $router = $this->getRouter(); - $router->resource('foo', 'FooController'); - - $this->assertTrue($router->getRoutes()->hasNamedRoute('foo.index')); - $this->assertTrue($router->getRoutes()->hasNamedRoute('foo.show')); - $this->assertTrue($router->getRoutes()->hasNamedRoute('foo.create')); - $this->assertTrue($router->getRoutes()->hasNamedRoute('foo.store')); - $this->assertTrue($router->getRoutes()->hasNamedRoute('foo.edit')); - $this->assertTrue($router->getRoutes()->hasNamedRoute('foo.update')); - $this->assertTrue($router->getRoutes()->hasNamedRoute('foo.destroy')); - - $router = $this->getRouter(); - $router->resource('foo.bar', 'FooController'); - - $this->assertTrue($router->getRoutes()->hasNamedRoute('foo.bar.index')); - $this->assertTrue($router->getRoutes()->hasNamedRoute('foo.bar.show')); - $this->assertTrue($router->getRoutes()->hasNamedRoute('foo.bar.create')); - $this->assertTrue($router->getRoutes()->hasNamedRoute('foo.bar.store')); - $this->assertTrue($router->getRoutes()->hasNamedRoute('foo.bar.edit')); - $this->assertTrue($router->getRoutes()->hasNamedRoute('foo.bar.update')); - $this->assertTrue($router->getRoutes()->hasNamedRoute('foo.bar.destroy')); - - $router = $this->getRouter(); - $router->resource('foo', 'FooController', [ - 'names' => [ - 'index' => 'foo', - 'show' => 'bar', - ] - ]); - - $this->assertTrue($router->getRoutes()->hasNamedRoute('foo')); - $this->assertTrue($router->getRoutes()->hasNamedRoute('bar')); - } - - - public function testRouterFiresRoutedEvent(): void - { - $events = new Illuminate\Events\Dispatcher(); - $router = new Router($events); - $router->get('foo/bar', fn() => ''); - - $request = Request::create('http://foo.com/foo/bar', 'GET'); - $route = new Route('GET', 'foo/bar', ['http', function() {}]); - - $_SERVER['__router.request'] = null; - $_SERVER['__router.route'] = null; - - $router->matched(function($route, $request){ - $_SERVER['__router.request'] = $request; - $_SERVER['__router.route'] = $route; - }); - - $router->dispatchToRoute($request); - - $this->assertInstanceOf(Request::class, $_SERVER['__router.request']); - $this->assertEquals($_SERVER['__router.request'], $request); - unset($_SERVER['__router.request']); - - $this->assertInstanceOf(Route::class, $_SERVER['__router.route']); - $this->assertEquals($_SERVER['__router.route']->getUri(), $route->getUri()); - unset($_SERVER['__router.route']); - } - - - public function testRouterPatternSetting(): void - { - $router = $this->getRouter(); - $router->pattern('test', 'pattern'); - $this->assertEquals(['test' => 'pattern'], $router->getPatterns()); - - $router = $this->getRouter(); - $router->patterns(['test' => 'pattern', 'test2' => 'pattern2']); - $this->assertEquals(['test' => 'pattern', 'test2' => 'pattern2'], $router->getPatterns()); - } - - - public function testRouteParametersDefaultValue() - { - $router = $this->getRouter(); - - $router->get('foo/{bar?}', function ($bar = '') { - return $bar; - })->defaults('bar', 'foo'); - $this->assertEquals('foo', $router->dispatch(Request::create('foo', 'GET'))->getContent()); - - - $router->get('foo/{bar?}', function ($bar = '') { - return $bar; - })->defaults('bar', 'foo'); - $this->assertEquals('bar', $router->dispatch(Request::create('foo/bar', 'GET'))->getContent()); - } - - - public function testRouteRedirect() - { - $router = $this->getRouter(); - $router->get('contact_us', function () { - throw new \Exception('Route should not be reachable.'); - }); - $router->redirect('contact_us', 'contact', 302); - - $response = $router->dispatch(Request::create('contact_us', 'GET')); - $this->assertTrue($response->isRedirect('contact')); - $this->assertEquals(302, $response->getStatusCode()); - } - - - public function testDispatchingCallableActionClasses() - { - $router = $this->getRouter(); - $router->get('foo/bar', 'ActionStub'); - - $this->assertEquals('hello', $router->dispatch(Request::create('foo/bar', 'GET'))->getContent()); - - $router->get('foo/bar2', [ - 'uses' => 'ActionStub', - ]); - - $this->assertEquals('hello', $router->dispatch(Request::create('foo/bar2', 'GET'))->getContent()); - } - - - public function testRouteMiddlewareRunsAndWraps(): void - { - // Closure middleware wraps the route response. - $router = $this->getRouter(); - $router->get('foo/bar', fn() => 'hello')->middleware(fn($request, $next) => $next($request).'!'); - $this->assertEquals('hello!', $router->dispatch(Request::create('foo/bar', 'GET'))->getContent()); - - // Middleware that never calls $next short-circuits the route. - $router = $this->getRouter(); - $router->get('foo/bar', fn() => 'hello')->middleware(fn($request, $next) => 'blocked'); - $this->assertEquals('blocked', $router->dispatch(Request::create('foo/bar', 'GET'))->getContent()); - - // Alias resolves to a class middleware. - $router = $this->getRouter(); - $router->aliasMiddleware('stub', 'RouteMiddlewareStub'); - $router->get('foo/bar', fn() => 'hello')->middleware('stub'); - $this->assertEquals('hello-stubbed', $router->dispatch(Request::create('foo/bar', 'GET'))->getContent()); - - // Parameter after ':' is passed to the middleware. - $router = $this->getRouter(); - $router->aliasMiddleware('append', 'RouteAppendMiddlewareStub'); - $router->get('foo/bar', fn() => 'hello')->middleware('append:X'); - $this->assertEquals('helloX', $router->dispatch(Request::create('foo/bar', 'GET'))->getContent()); - - // A group expands to its members, nesting outer->inner. - $router = $this->getRouter(); - $router->aliasMiddleware('append', 'RouteAppendMiddlewareStub'); - $router->middlewareGroup('grp', ['append:-a', 'append:-b']); - $router->get('foo/bar', fn() => 'x')->middleware('grp'); - $this->assertEquals('x-b-a', $router->dispatch(Request::create('foo/bar', 'GET'))->getContent()); - } - - - public function testRouteMiddlewareCoexistsWithFilters(): void - { - // A before filter that short-circuits still bypasses middleware (filters wrap middleware). - $router = $this->getRouter(); - $router->filter('block', fn() => 'blocked-by-filter'); - $router->get('foo/bar', ['before' => 'block', fn() => 'hello']) - ->middleware(fn($request, $next) => $next($request).'+mw'); - $this->assertEquals('blocked-by-filter', $router->dispatch(Request::create('foo/bar', 'GET'))->getContent()); - - // A pass-through before filter lets the route run; middleware still wraps it. - $router = $this->getRouter(); - $router->filter('pass', fn() => null); - $router->get('foo/bar', ['before' => 'pass', fn() => 'hello']) - ->middleware(fn($request, $next) => $next($request).'+mw'); - $this->assertEquals('hello+mw', $router->dispatch(Request::create('foo/bar', 'GET'))->getContent()); - } - - - protected function getRouter(): Router - { - return new Router(new Illuminate\Events\Dispatcher); - } - -} - - -class RouteTestControllerDispatchStub extends Illuminate\Routing\Controller { - public function __construct() - { - $this->beforeFilter('foo', ['only' => 'bar']); - $this->beforeFilter('@filter', ['only' => 'baz']); - $this->afterFilter('qux', ['only' => 'qux']); - } - public function foo(): string - { - return 'bar'; - } - public function bar(): string - { - return 'baz'; - } - public function filter(): string - { - return 'filtered'; - } - public function baz(): string - { - return 'baz'; - } - public function qux(): string - { - return 'qux'; - } -} - -class RouteTestControllerRemoveFilterStub extends Controller -{ - public function __construct() - { - $this->beforeFilter('removeBefore', ['only' => 'beforeRoute']); - $this->beforeFilter('@inlineBeforeFilter', ['only' => 'beforeRoute']); - $this->afterFilter('removeAfter', ['only' => 'afterRoute']); - $this->afterFilter('@inlineAfterFilter', ['only' => 'afterRoute']); - - $this->forgetBeforeFilter('removeBefore'); - $this->forgetBeforeFilter('@inlineBeforeFilter'); - $this->forgetAfterFilter('removeAfter'); - $this->forgetAfterFilter('@inlineAfterFilter'); - } - public function beforeRoute(): string - { - return __FUNCTION__; - } - public function afterRoute(): string - { - return __FUNCTION__; - } - public function inlineBeforeFilter(): string - { - return __FUNCTION__; - } - public function inlineAfterFilter(): string - { - return __FUNCTION__; - } -} - -class RouteBindingStub { - public function bind($value, $route): string - { return strtoupper((string) $value); } - public function find($value, $route): string - { return strtolower((string) $value); } -} - -class RouteModelBindingStub { - public function find($value): string - { return strtoupper((string) $value); } -} - -class RouteModelBindingNullStub { - public function find($value): void - {} -} - -class RouteTestFilterStub { - public function filter(): string - { - return 'foo!'; - } - public function handle(): string - { - return 'handling!'; - } -} - -class ActionStub extends Controller -{ - public function __invoke(): string - { - return 'hello'; - } -} - -class RouteMiddlewareStub { - public function handle($request, $next) - { - return $next($request).'-stubbed'; - } -} - -class RouteAppendMiddlewareStub { - public function handle($request, $next, $suffix) - { - return $next($request).$suffix; - } -} diff --git a/tests/Routing/RoutingUrlGeneratorTest.php b/tests/Routing/RoutingUrlGeneratorTest.php deleted file mode 100755 index 700aee701..000000000 --- a/tests/Routing/RoutingUrlGeneratorTest.php +++ /dev/null @@ -1,293 +0,0 @@ -assertEquals('http://www.foo.com/foo/bar', $url->to('foo/bar')); - $this->assertEquals('https://www.foo.com/foo/bar', $url->to('foo/bar', [], true)); - $this->assertEquals('https://www.foo.com/foo/bar/baz/boom', $url->to('foo/bar', ['baz', 'boom'], true)); - - /** - * Test HTTPS request URL generation... - */ - $url = new UrlGenerator( - $routes = new Illuminate\Routing\RouteCollection, - $request = Illuminate\Http\Request::create('https://www.foo.com/') - ); - - $this->assertEquals('https://www.foo.com/foo/bar', $url->to('foo/bar')); - - /** - * Test asset URL generation... - */ - $url = new UrlGenerator( - $routes = new Illuminate\Routing\RouteCollection, - $request = Illuminate\Http\Request::create('http://www.foo.com/index.php/') - ); - - $this->assertEquals('http://www.foo.com/foo/bar', $url->asset('foo/bar')); - $this->assertEquals('https://www.foo.com/foo/bar', $url->asset('foo/bar', true)); - } - - - public function testBasicRouteGeneration(): void - { - $url = new UrlGenerator( - $routes = new Illuminate\Routing\RouteCollection, - $request = Illuminate\Http\Request::create('http://www.foo.com/') - ); - - /** - * Empty Named Route - */ - $route = new Illuminate\Routing\Route(['GET'], '/', ['as' => 'plain']); - $routes->add($route); - - /** - * Named Routes - */ - $route = new Illuminate\Routing\Route(['GET'], 'foo/bar', ['as' => 'foo']); - $routes->add($route); - - /** - * Parameters... - */ - $route = new Illuminate\Routing\Route(['GET'], 'foo/bar/{baz}/breeze/{boom}', ['as' => 'bar']); - $routes->add($route); - - /** - * HTTPS... - */ - $route = new Illuminate\Routing\Route(['GET'], 'foo/bar', ['as' => 'baz', 'https']); - $routes->add($route); - - /** - * Controller Route Route - */ - $route = new Illuminate\Routing\Route(['GET'], 'foo/bar', ['controller' => 'foo@bar']); - $routes->add($route); - - /** - * Non ASCII routes - */ - $route = new Illuminate\Routing\Route(['GET'], 'foo/bar/åαф/{baz}', ['as' => 'foobarbaz']); - $routes->add($route); - - $this->assertEquals('/', $url->route('plain', [], false)); - $this->assertEquals('/?foo=bar', $url->route('plain', ['foo' => 'bar'], false)); - $this->assertEquals('http://www.foo.com/foo/bar', $url->route('foo')); - $this->assertEquals('/foo/bar', $url->route('foo', [], false)); - $this->assertEquals('/foo/bar?foo=bar', $url->route('foo', ['foo' => 'bar'], false)); - $this->assertEquals('http://www.foo.com/foo/bar/taylor/breeze/otwell?fly=wall', $url->route('bar', ['taylor', 'otwell', 'fly' => 'wall'] - )); - $this->assertEquals('http://www.foo.com/foo/bar/otwell/breeze/taylor?fly=wall', $url->route('bar', ['boom' => 'taylor', 'baz' => 'otwell', 'fly' => 'wall'] - )); - $this->assertEquals('/foo/bar/taylor/breeze/otwell?fly=wall', $url->route('bar', ['taylor', 'otwell', 'fly' => 'wall'], false)); - $this->assertEquals('https://www.foo.com/foo/bar', $url->route('baz')); - $this->assertEquals('http://www.foo.com/foo/bar', $url->action('foo@bar')); - $this->assertEquals('http://www.foo.com/foo/bar/taylor/breeze/otwell?wall&woz', $url->route('bar', ['wall', 'woz', 'boom' => 'otwell', 'baz' => 'taylor'] - )); - $this->assertEquals('http://www.foo.com/foo/bar/taylor/breeze/otwell?wall&woz', $url->route('bar', ['taylor', 'otwell', 'wall', 'woz'] - )); - $this->assertEquals('http://www.foo.com/foo/bar/%C3%A5%CE%B1%D1%84/%C3%A5%CE%B1%D1%84', $url->route('foobarbaz', ['baz' => 'åαф'] - )); - - } - - - public function testRoutesMaintainRequestScheme(): void - { - $url = new UrlGenerator( - $routes = new Illuminate\Routing\RouteCollection, - $request = Illuminate\Http\Request::create('https://www.foo.com/') - ); - - /** - * Named Routes - */ - $route = new Illuminate\Routing\Route(['GET'], 'foo/bar', ['as' => 'foo']); - $routes->add($route); - - $this->assertEquals('https://www.foo.com/foo/bar', $url->route('foo')); - } - - - public function testHttpOnlyRoutes(): void - { - $url = new UrlGenerator( - $routes = new Illuminate\Routing\RouteCollection, - $request = Illuminate\Http\Request::create('https://www.foo.com/') - ); - - /** - * Named Routes - */ - $route = new Illuminate\Routing\Route(['GET'], 'foo/bar', ['as' => 'foo', 'http']); - $routes->add($route); - - $this->assertEquals('http://www.foo.com/foo/bar', $url->route('foo')); - } - - - public function testRoutesWithDomains(): void - { - $url = new UrlGenerator( - $routes = new Illuminate\Routing\RouteCollection, - $request = Illuminate\Http\Request::create('http://www.foo.com/') - ); - - $route = new Illuminate\Routing\Route(['GET'], 'foo/bar', ['as' => 'foo', 'domain' => 'sub.foo.com']); - $routes->add($route); - - /** - * Wildcards & Domains... - */ - $route = new Illuminate\Routing\Route(['GET'], 'foo/bar/{baz}', ['as' => 'bar', 'domain' => 'sub.{foo}.com']); - $routes->add($route); - - $this->assertEquals('http://sub.foo.com/foo/bar', $url->route('foo')); - $this->assertEquals('http://sub.taylor.com/foo/bar/otwell', $url->route('bar', ['taylor', 'otwell'])); - $this->assertEquals('/foo/bar/otwell', $url->route('bar', ['taylor', 'otwell'], false)); - } - - - public function testRoutesWithDomainsAndPorts(): void - { - $url = new UrlGenerator( - $routes = new Illuminate\Routing\RouteCollection, - $request = Illuminate\Http\Request::create('http://www.foo.com:8080/') - ); - - $route = new Illuminate\Routing\Route(['GET'], 'foo/bar', ['as' => 'foo', 'domain' => 'sub.foo.com']); - $routes->add($route); - - /** - * Wildcards & Domains... - */ - $route = new Illuminate\Routing\Route(['GET'], 'foo/bar/{baz}', ['as' => 'bar', 'domain' => 'sub.{foo}.com']); - $routes->add($route); - - $this->assertEquals('http://sub.foo.com:8080/foo/bar', $url->route('foo')); - $this->assertEquals('http://sub.taylor.com:8080/foo/bar/otwell', $url->route('bar', ['taylor', 'otwell'])); - } - - - public function testHttpsRoutesWithDomains(): void - { - $url = new UrlGenerator( - $routes = new Illuminate\Routing\RouteCollection, - $request = Illuminate\Http\Request::create('https://foo.com/') - ); - - /** - * When on HTTPS, no need to specify 443 - */ - $route = new Illuminate\Routing\Route(['GET'], 'foo/bar', ['as' => 'baz', 'domain' => 'sub.foo.com']); - $routes->add($route); - - $this->assertEquals('https://sub.foo.com/foo/bar', $url->route('baz')); - } - - - public function testUrlGenerationForControllers(): void - { - $url = new UrlGenerator( - $routes = new Illuminate\Routing\RouteCollection, - $request = Illuminate\Http\Request::create('http://www.foo.com:8080/') - ); - - $route = new Illuminate\Routing\Route(['GET'], 'foo/{one}/{two?}/{three?}', ['as' => 'foo', function() {}]); - $routes->add($route); - - $this->assertEquals('http://www.foo.com:8080/foo', $url->route('foo')); - } - - - public function testForceRootUrl(): void - { - $url = new UrlGenerator( - $routes = new Illuminate\Routing\RouteCollection, - $request = Illuminate\Http\Request::create('http://www.foo.com/') - ); - - $url->forceRootUrl('https://www.bar.com'); - $this->assertEquals('http://www.bar.com/foo/bar', $url->to('foo/bar')); - - - /** - * Route Based... - */ - $url = new UrlGenerator( - $routes = new Illuminate\Routing\RouteCollection, - $request = Illuminate\Http\Request::create('http://www.foo.com/') - ); - - $url->forceSchema('https'); - $route = new Illuminate\Routing\Route(['GET'], '/foo', ['as' => 'plain']); - $routes->add($route); - - $this->assertEquals('https://www.foo.com/foo', $url->route('plain')); - - $url->forceRootUrl('https://www.bar.com'); - $this->assertEquals('https://www.bar.com/foo', $url->route('plain')); - } - - - public function testPrevious(): void - { - $url = new UrlGenerator( - $routes = new Illuminate\Routing\RouteCollection, - $request = Illuminate\Http\Request::create('http://www.foo.com/') - ); - - $url->getRequest()->headers->set('referer', 'http://www.bar.com/'); - $this->assertEquals('http://www.bar.com/', $url->previous()); - - $url->getRequest()->headers->remove('referer'); - $this->assertEquals($url->to('/'), $url->previous()); - } - - - public function testPreviousUrlFromSession(): void - { - $session = m::mock(Store::class); - $request = Illuminate\Http\Request::create('http://www.foo.com/some'); - - $session->shouldReceive('previousUrl')->andReturn('http://www.foo.com/previous-page'); - $request->setLaravelSession($session); - - $url = new UrlGenerator( - new Illuminate\Routing\RouteCollection, - $request - ); - - $this->assertEquals('http://www.foo.com/previous-page', $url->previous()); - } - - - public function testPreviousWithFallback(): void - { - $url = new UrlGenerator( - $routes = new Illuminate\Routing\RouteCollection, - $request = Illuminate\Http\Request::create('http://www.foo.com/') - ); - - $url->getRequest()->headers->set('referer', 'http://www.bar.com/'); - $this->assertEquals('http://www.bar.com/', $url->previous('/some-page')); - - $url->getRequest()->headers->remove('referer'); - $this->assertEquals($url->to('/some-page'), $url->previous('/some-page')); - } - -} diff --git a/tests/Support/SupportFacadeResponseTest.php b/tests/Support/SupportFacadeResponseTest.php deleted file mode 100755 index 8365a9c1d..000000000 --- a/tests/Support/SupportFacadeResponseTest.php +++ /dev/null @@ -1,63 +0,0 @@ -instance(ResponseFactoryContract::class, new ResponseFactory(null)); - - Response::clearResolvedInstances(); - Response::setFacadeApplication($app); - } - - protected function tearDown(): void - { - Response::setFacadeApplication(null); - m::close(); - } - - - public function testArrayableSendAsJson() - { - $data = m::mock(ArrayableInterface::class); - $data->shouldReceive('toArray')->andReturn(['foo' => 'bar']); - - $response = Response::json($data); - $this->assertEquals('{"foo":"bar"}', $response->getContent()); - } - - - public function testMakeWorksWithoutViewBinding() - { - $response = Response::make('hello', 201); - - $this->assertEquals('hello', $response->getContent()); - $this->assertEquals(201, $response->getStatusCode()); - } - - - public function testViewResolvesViewFactoryLazilyFromContainer() - { - $viewFactory = m::mock('StdClass'); - $viewFactory->shouldReceive('make')->once()->with('welcome', ['a' => 1])->andReturn('rendered'); - - $container = new Container; - $container->instance('view', $viewFactory); - $factory = new ResponseFactory($container); - - $this->assertEquals('rendered', $factory->view('welcome', ['a' => 1])->getContent()); - } - -}