diff options
Diffstat (limited to 'tests/lib/AppFramework/Routing/RouteParserTest.php')
-rw-r--r-- | tests/lib/AppFramework/Routing/RouteParserTest.php | 347 |
1 files changed, 347 insertions, 0 deletions
diff --git a/tests/lib/AppFramework/Routing/RouteParserTest.php b/tests/lib/AppFramework/Routing/RouteParserTest.php new file mode 100644 index 00000000000..406c5f1f3a5 --- /dev/null +++ b/tests/lib/AppFramework/Routing/RouteParserTest.php @@ -0,0 +1,347 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +namespace Test\AppFramework\Routing; + +use OC\AppFramework\Routing\RouteParser; +use Symfony\Component\Routing\Route as RoutingRoute; +use Symfony\Component\Routing\RouteCollection; + +class RouteParserTest extends \Test\TestCase { + + protected RouteParser $parser; + + protected function setUp(): void { + $this->parser = new RouteParser(); + } + + public function testParseRoutes(): void { + $routes = ['routes' => [ + ['name' => 'folders#open', 'url' => '/{folderId}/open', 'verb' => 'GET'], + ['name' => 'folders#create', 'url' => '/{folderId}/create', 'verb' => 'POST'] + ]]; + + $collection = $this->parser->parseDefaultRoutes($routes, 'app1'); + $this->assertArrayHasKey('app1.folders.open', $collection->all()); + $this->assertSimpleRoute('/apps/app1/{folderId}/open', 'GET', 'FoldersController', 'open', route: $collection->get('app1.folders.open')); + $this->assertArrayHasKey('app1.folders.create', $collection->all()); + $this->assertSimpleRoute('/apps/app1/{folderId}/create', 'POST', 'FoldersController', 'create', route: $collection->get('app1.folders.create')); + } + + public function testParseRoutesRootApps(): void { + $routes = ['routes' => [ + ['name' => 'folders#open', 'url' => '/{folderId}/open', 'verb' => 'GET'], + ['name' => 'folders#create', 'url' => '/{folderId}/create', 'verb' => 'POST'] + ]]; + + $collection = $this->parser->parseDefaultRoutes($routes, 'core'); + $this->assertArrayHasKey('core.folders.open', $collection->all()); + $this->assertSimpleRoute('/{folderId}/open', 'GET', 'FoldersController', 'open', app: 'core', route: $collection->get('core.folders.open')); + $this->assertArrayHasKey('core.folders.create', $collection->all()); + $this->assertSimpleRoute('/{folderId}/create', 'POST', 'FoldersController', 'create', app: 'core', route: $collection->get('core.folders.create')); + } + + public function testParseRoutesWithResources(): void { + $routes = ['routes' => [ + ['name' => 'folders#open', 'url' => '/{folderId}/open', 'verb' => 'GET'], + ], 'resources' => [ + 'names' => ['url' => '/names'], + 'folder_names' => ['url' => '/folder/names'], + ]]; + + $collection = $this->parser->parseDefaultRoutes($routes, 'app1'); + $this->assertArrayHasKey('app1.folders.open', $collection->all()); + $this->assertSimpleResource('/apps/app1/folder/names', 'folder_names', 'FolderNamesController', 'app1', $collection); + $this->assertSimpleResource('/apps/app1/names', 'names', 'NamesController', 'app1', $collection); + } + + public function testParseRoutesWithPostfix(): void { + $routes = ['routes' => [ + ['name' => 'folders#update', 'url' => '/{folderId}/update', 'verb' => 'POST'], + ['name' => 'folders#update', 'url' => '/{folderId}/update', 'verb' => 'PUT', 'postfix' => '-edit'] + ]]; + + $collection = $this->parser->parseDefaultRoutes($routes, 'app1'); + $this->assertArrayHasKey('app1.folders.update', $collection->all()); + $this->assertSimpleRoute('/apps/app1/{folderId}/update', 'POST', 'FoldersController', 'update', route: $collection->get('app1.folders.update')); + $this->assertArrayHasKey('app1.folders.update-edit', $collection->all()); + $this->assertSimpleRoute('/apps/app1/{folderId}/update', 'PUT', 'FoldersController', 'update', route: $collection->get('app1.folders.update-edit')); + } + + public function testParseRoutesKebabCaseAction(): void { + $routes = ['routes' => [ + ['name' => 'folders#open_folder', 'url' => '/{folderId}/open', 'verb' => 'GET'] + ]]; + + $collection = $this->parser->parseDefaultRoutes($routes, 'app1'); + $this->assertArrayHasKey('app1.folders.open_folder', $collection->all()); + $this->assertSimpleRoute('/apps/app1/{folderId}/open', 'GET', 'FoldersController', 'openFolder', route: $collection->get('app1.folders.open_folder')); + } + + public function testParseRoutesKebabCaseController(): void { + $routes = ['routes' => [ + ['name' => 'my_folders#open', 'url' => '/{folderId}/open', 'verb' => 'GET'] + ]]; + + $collection = $this->parser->parseDefaultRoutes($routes, 'app1'); + $this->assertArrayHasKey('app1.my_folders.open', $collection->all()); + $this->assertSimpleRoute('/apps/app1/{folderId}/open', 'GET', 'MyFoldersController', 'open', route: $collection->get('app1.my_folders.open')); + } + + public function testParseRoutesLowercaseVerb(): void { + $routes = ['routes' => [ + ['name' => 'folders#delete', 'url' => '/{folderId}/delete', 'verb' => 'delete'] + ]]; + + $collection = $this->parser->parseDefaultRoutes($routes, 'app1'); + $this->assertArrayHasKey('app1.folders.delete', $collection->all()); + $this->assertSimpleRoute('/apps/app1/{folderId}/delete', 'DELETE', 'FoldersController', 'delete', route: $collection->get('app1.folders.delete')); + } + + public function testParseRoutesMissingVerb(): void { + $routes = ['routes' => [ + ['name' => 'folders#open', 'url' => '/{folderId}/open'] + ]]; + + $collection = $this->parser->parseDefaultRoutes($routes, 'app1'); + $this->assertArrayHasKey('app1.folders.open', $collection->all()); + $this->assertSimpleRoute('/apps/app1/{folderId}/open', 'GET', 'FoldersController', 'open', route: $collection->get('app1.folders.open')); + } + + public function testParseRoutesWithRequirements(): void { + $routes = ['routes' => [ + ['name' => 'folders#open', 'url' => '/{folderId}/open', 'verb' => 'GET', 'requirements' => ['folderId' => '\d+']] + ]]; + + $collection = $this->parser->parseDefaultRoutes($routes, 'app1'); + $this->assertArrayHasKey('app1.folders.open', $collection->all()); + $this->assertSimpleRoute('/apps/app1/{folderId}/open', 'GET', 'FoldersController', 'open', requirements: ['folderId' => '\d+'], route: $collection->get('app1.folders.open')); + } + + public function testParseRoutesWithDefaults(): void { + $routes = ['routes' => [ + ['name' => 'folders#open', 'url' => '/{folderId}/open', 'verb' => 'GET', 'defaults' => ['hello' => 'world']] + ]]; + + $collection = $this->parser->parseDefaultRoutes($routes, 'app1'); + $this->assertArrayHasKey('app1.folders.open', $collection->all()); + $this->assertSimpleRoute('/apps/app1/{folderId}/open', 'GET', 'FoldersController', 'open', defaults: ['hello' => 'world'], route: $collection->get('app1.folders.open')); + } + + public function testParseRoutesInvalidName(): void { + $routes = ['routes' => [ + ['name' => 'folders', 'url' => '/{folderId}/open', 'verb' => 'GET'] + ]]; + + $this->expectException(\UnexpectedValueException::class); + $this->parser->parseDefaultRoutes($routes, 'app1'); + } + + public function testParseRoutesInvalidName2(): void { + $routes = ['routes' => [ + ['name' => 'folders#open#action', 'url' => '/{folderId}/open', 'verb' => 'GET'] + ]]; + + $this->expectException(\UnexpectedValueException::class); + $this->parser->parseDefaultRoutes($routes, 'app1'); + } + + public function testParseRoutesEmpty(): void { + $routes = ['routes' => []]; + + $collection = $this->parser->parseDefaultRoutes($routes, 'app1'); + $this->assertEquals(0, $collection->count()); + } + + // OCS routes + + public function testParseOcsRoutes(): void { + $routes = ['ocs' => [ + ['name' => 'folders#open', 'url' => '/{folderId}/open', 'verb' => 'GET'], + ['name' => 'folders#create', 'url' => '/{folderId}/create', 'verb' => 'POST'] + ]]; + + $collection = $this->parser->parseOCSRoutes($routes, 'app1'); + $this->assertArrayHasKey('ocs.app1.folders.open', $collection->all()); + $this->assertSimpleRoute('/apps/app1/{folderId}/open', 'GET', 'FoldersController', 'open', route: $collection->get('ocs.app1.folders.open')); + $this->assertArrayHasKey('ocs.app1.folders.create', $collection->all()); + $this->assertSimpleRoute('/apps/app1/{folderId}/create', 'POST', 'FoldersController', 'create', route: $collection->get('ocs.app1.folders.create')); + } + + public function testParseOcsRoutesRootApps(): void { + $routes = ['ocs' => [ + ['name' => 'folders#open', 'url' => '/{folderId}/open', 'verb' => 'GET'], + ['name' => 'folders#create', 'url' => '/{folderId}/create', 'verb' => 'POST'] + ]]; + + $collection = $this->parser->parseOCSRoutes($routes, 'core'); + $this->assertArrayHasKey('ocs.core.folders.open', $collection->all()); + $this->assertSimpleRoute('/{folderId}/open', 'GET', 'FoldersController', 'open', app: 'core', route: $collection->get('ocs.core.folders.open')); + $this->assertArrayHasKey('ocs.core.folders.create', $collection->all()); + $this->assertSimpleRoute('/{folderId}/create', 'POST', 'FoldersController', 'create', app: 'core', route: $collection->get('ocs.core.folders.create')); + } + + public function testParseOcsRoutesWithPostfix(): void { + $routes = ['ocs' => [ + ['name' => 'folders#update', 'url' => '/{folderId}/update', 'verb' => 'POST'], + ['name' => 'folders#update', 'url' => '/{folderId}/update', 'verb' => 'PUT', 'postfix' => '-edit'] + ]]; + + $collection = $this->parser->parseOCSRoutes($routes, 'app1'); + $this->assertArrayHasKey('ocs.app1.folders.update', $collection->all()); + $this->assertSimpleRoute('/apps/app1/{folderId}/update', 'POST', 'FoldersController', 'update', route: $collection->get('ocs.app1.folders.update')); + $this->assertArrayHasKey('ocs.app1.folders.update-edit', $collection->all()); + $this->assertSimpleRoute('/apps/app1/{folderId}/update', 'PUT', 'FoldersController', 'update', route: $collection->get('ocs.app1.folders.update-edit')); + } + + public function testParseOcsRoutesKebabCaseAction(): void { + $routes = ['ocs' => [ + ['name' => 'folders#open_folder', 'url' => '/{folderId}/open', 'verb' => 'GET'] + ]]; + + $collection = $this->parser->parseOCSRoutes($routes, 'app1'); + $this->assertArrayHasKey('ocs.app1.folders.open_folder', $collection->all()); + $this->assertSimpleRoute('/apps/app1/{folderId}/open', 'GET', 'FoldersController', 'openFolder', route: $collection->get('ocs.app1.folders.open_folder')); + } + + public function testParseOcsRoutesKebabCaseController(): void { + $routes = ['ocs' => [ + ['name' => 'my_folders#open', 'url' => '/{folderId}/open', 'verb' => 'GET'] + ]]; + + $collection = $this->parser->parseOCSRoutes($routes, 'app1'); + $this->assertArrayHasKey('ocs.app1.my_folders.open', $collection->all()); + $this->assertSimpleRoute('/apps/app1/{folderId}/open', 'GET', 'MyFoldersController', 'open', route: $collection->get('ocs.app1.my_folders.open')); + } + + public function testParseOcsRoutesLowercaseVerb(): void { + $routes = ['ocs' => [ + ['name' => 'folders#delete', 'url' => '/{folderId}/delete', 'verb' => 'delete'] + ]]; + + $collection = $this->parser->parseOCSRoutes($routes, 'app1'); + $this->assertArrayHasKey('ocs.app1.folders.delete', $collection->all()); + $this->assertSimpleRoute('/apps/app1/{folderId}/delete', 'DELETE', 'FoldersController', 'delete', route: $collection->get('ocs.app1.folders.delete')); + } + + public function testParseOcsRoutesMissingVerb(): void { + $routes = ['ocs' => [ + ['name' => 'folders#open', 'url' => '/{folderId}/open'] + ]]; + + $collection = $this->parser->parseOCSRoutes($routes, 'app1'); + $this->assertArrayHasKey('ocs.app1.folders.open', $collection->all()); + $this->assertSimpleRoute('/apps/app1/{folderId}/open', 'GET', 'FoldersController', 'open', route: $collection->get('ocs.app1.folders.open')); + } + + public function testParseOcsRoutesWithRequirements(): void { + $routes = ['ocs' => [ + ['name' => 'folders#open', 'url' => '/{folderId}/open', 'verb' => 'GET', 'requirements' => ['folderId' => '\d+']] + ]]; + + $collection = $this->parser->parseOCSRoutes($routes, 'app1'); + $this->assertArrayHasKey('ocs.app1.folders.open', $collection->all()); + $this->assertSimpleRoute('/apps/app1/{folderId}/open', 'GET', 'FoldersController', 'open', requirements: ['folderId' => '\d+'], route: $collection->get('ocs.app1.folders.open')); + } + + public function testParseOcsRoutesWithDefaults(): void { + $routes = ['ocs' => [ + ['name' => 'folders#open', 'url' => '/{folderId}/open', 'verb' => 'GET', 'defaults' => ['hello' => 'world']] + ]]; + + $collection = $this->parser->parseOCSRoutes($routes, 'app1'); + $this->assertArrayHasKey('ocs.app1.folders.open', $collection->all()); + $this->assertSimpleRoute('/apps/app1/{folderId}/open', 'GET', 'FoldersController', 'open', defaults: ['hello' => 'world'], route: $collection->get('ocs.app1.folders.open')); + } + + public function testParseOcsRoutesInvalidName(): void { + $routes = ['ocs' => [ + ['name' => 'folders', 'url' => '/{folderId}/open', 'verb' => 'GET'] + ]]; + + $this->expectException(\UnexpectedValueException::class); + $this->parser->parseOCSRoutes($routes, 'app1'); + } + + public function testParseOcsRoutesEmpty(): void { + $routes = ['ocs' => []]; + + $collection = $this->parser->parseOCSRoutes($routes, 'app1'); + $this->assertEquals(0, $collection->count()); + } + + public function testParseOcsRoutesWithResources(): void { + $routes = ['ocs' => [ + ['name' => 'folders#open', 'url' => '/{folderId}/open', 'verb' => 'GET'], + ], 'ocs-resources' => [ + 'names' => ['url' => '/names', 'root' => '/core/something'], + 'folder_names' => ['url' => '/folder/names'], + ]]; + + $collection = $this->parser->parseOCSRoutes($routes, 'app1'); + $this->assertArrayHasKey('ocs.app1.folders.open', $collection->all()); + $this->assertOcsResource('/apps/app1/folder/names', 'folder_names', 'FolderNamesController', 'app1', $collection); + $this->assertOcsResource('/core/something/names', 'names', 'NamesController', 'app1', $collection); + } + + protected function assertSimpleRoute( + string $path, + string $method, + string $controller, + string $action, + string $app = 'app1', + array $requirements = [], + array $defaults = [], + ?RoutingRoute $route = null, + ): void { + self::assertEquals($path, $route->getPath()); + self::assertEqualsCanonicalizing([$method], $route->getMethods()); + self::assertEqualsCanonicalizing($requirements, $route->getRequirements()); + self::assertEquals([...$defaults, 'action' => null, 'caller' => [$app, $controller, $action]], $route->getDefaults()); + } + + protected function assertSimpleResource( + string $path, + string $resourceName, + string $controller, + string $app, + RouteCollection $collection, + ): void { + self::assertArrayHasKey("$app.$resourceName.index", $collection->all()); + self::assertArrayHasKey("$app.$resourceName.show", $collection->all()); + self::assertArrayHasKey("$app.$resourceName.create", $collection->all()); + self::assertArrayHasKey("$app.$resourceName.update", $collection->all()); + self::assertArrayHasKey("$app.$resourceName.destroy", $collection->all()); + + $this->assertSimpleRoute($path, 'GET', $controller, 'index', $app, route: $collection->get("$app.$resourceName.index")); + $this->assertSimpleRoute($path, 'POST', $controller, 'create', $app, route: $collection->get("$app.$resourceName.create")); + $this->assertSimpleRoute("$path/{id}", 'GET', $controller, 'show', $app, route: $collection->get("$app.$resourceName.show")); + $this->assertSimpleRoute("$path/{id}", 'PUT', $controller, 'update', $app, route: $collection->get("$app.$resourceName.update")); + $this->assertSimpleRoute("$path/{id}", 'DELETE', $controller, 'destroy', $app, route: $collection->get("$app.$resourceName.destroy")); + } + + protected function assertOcsResource( + string $path, + string $resourceName, + string $controller, + string $app, + RouteCollection $collection, + ): void { + self::assertArrayHasKey("ocs.$app.$resourceName.index", $collection->all()); + self::assertArrayHasKey("ocs.$app.$resourceName.show", $collection->all()); + self::assertArrayHasKey("ocs.$app.$resourceName.create", $collection->all()); + self::assertArrayHasKey("ocs.$app.$resourceName.update", $collection->all()); + self::assertArrayHasKey("ocs.$app.$resourceName.destroy", $collection->all()); + + $this->assertSimpleRoute($path, 'GET', $controller, 'index', $app, route: $collection->get("ocs.$app.$resourceName.index")); + $this->assertSimpleRoute($path, 'POST', $controller, 'create', $app, route: $collection->get("ocs.$app.$resourceName.create")); + $this->assertSimpleRoute("$path/{id}", 'GET', $controller, 'show', $app, route: $collection->get("ocs.$app.$resourceName.show")); + $this->assertSimpleRoute("$path/{id}", 'PUT', $controller, 'update', $app, route: $collection->get("ocs.$app.$resourceName.update")); + $this->assertSimpleRoute("$path/{id}", 'DELETE', $controller, 'destroy', $app, route: $collection->get("ocs.$app.$resourceName.destroy")); + } +} |