RoutingTest.php 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465
  1. <?php
  2. namespace Test\AppFramework\Routing;
  3. use OC\AppFramework\DependencyInjection\DIContainer;
  4. use OC\AppFramework\Routing\RouteConfig;
  5. use OC\Route\Route;
  6. use OC\Route\Router;
  7. use OCP\Diagnostics\IEventLogger;
  8. use OCP\IConfig;
  9. use OCP\IRequest;
  10. use OCP\Route\IRouter;
  11. use PHPUnit\Framework\MockObject\MockObject;
  12. use Psr\Container\ContainerInterface;
  13. use Psr\Log\LoggerInterface;
  14. class RoutingTest extends \Test\TestCase {
  15. public function testSimpleRoute() {
  16. $routes = ['routes' => [
  17. ['name' => 'folders#open', 'url' => '/folders/{folderId}/open', 'verb' => 'GET']
  18. ]];
  19. $this->assertSimpleRoute($routes, 'folders.open', 'GET', '/apps/app1/folders/{folderId}/open', 'FoldersController', 'open');
  20. }
  21. public function testSimpleRouteWithUnderScoreNames() {
  22. $routes = ['routes' => [
  23. ['name' => 'admin_folders#open_current', 'url' => '/folders/{folderId}/open', 'verb' => 'delete', 'root' => '']
  24. ]];
  25. $this->assertSimpleRoute($routes, 'admin_folders.open_current', 'DELETE', '/folders/{folderId}/open', 'AdminFoldersController', 'openCurrent', [], [], '', true);
  26. }
  27. public function testSimpleOCSRoute() {
  28. $routes = ['ocs' => [
  29. ['name' => 'folders#open', 'url' => '/folders/{folderId}/open', 'verb' => 'GET']
  30. ]
  31. ];
  32. $this->assertSimpleOCSRoute($routes, 'folders.open', 'GET', '/apps/app1/folders/{folderId}/open', 'FoldersController', 'open');
  33. }
  34. public function testSimpleRouteWithMissingVerb() {
  35. $routes = ['routes' => [
  36. ['name' => 'folders#open', 'url' => '/folders/{folderId}/open']
  37. ]];
  38. $this->assertSimpleRoute($routes, 'folders.open', 'GET', '/apps/app1/folders/{folderId}/open', 'FoldersController', 'open');
  39. }
  40. public function testSimpleOCSRouteWithMissingVerb() {
  41. $routes = ['ocs' => [
  42. ['name' => 'folders#open', 'url' => '/folders/{folderId}/open']
  43. ]
  44. ];
  45. $this->assertSimpleOCSRoute($routes, 'folders.open', 'GET', '/apps/app1/folders/{folderId}/open', 'FoldersController', 'open');
  46. }
  47. public function testSimpleRouteWithLowercaseVerb() {
  48. $routes = ['routes' => [
  49. ['name' => 'folders#open', 'url' => '/folders/{folderId}/open', 'verb' => 'delete']
  50. ]];
  51. $this->assertSimpleRoute($routes, 'folders.open', 'DELETE', '/apps/app1/folders/{folderId}/open', 'FoldersController', 'open');
  52. }
  53. public function testSimpleOCSRouteWithLowercaseVerb() {
  54. $routes = ['ocs' => [
  55. ['name' => 'folders#open', 'url' => '/folders/{folderId}/open', 'verb' => 'delete']
  56. ]
  57. ];
  58. $this->assertSimpleOCSRoute($routes, 'folders.open', 'DELETE', '/apps/app1/folders/{folderId}/open', 'FoldersController', 'open');
  59. }
  60. public function testSimpleRouteWithRequirements() {
  61. $routes = ['routes' => [
  62. ['name' => 'folders#open', 'url' => '/folders/{folderId}/open', 'verb' => 'delete', 'requirements' => ['something']]
  63. ]];
  64. $this->assertSimpleRoute($routes, 'folders.open', 'DELETE', '/apps/app1/folders/{folderId}/open', 'FoldersController', 'open', ['something']);
  65. }
  66. public function testSimpleOCSRouteWithRequirements() {
  67. $routes = ['ocs' => [
  68. ['name' => 'folders#open', 'url' => '/folders/{folderId}/open', 'verb' => 'delete', 'requirements' => ['something']]
  69. ]
  70. ];
  71. $this->assertSimpleOCSRoute($routes, 'folders.open', 'DELETE', '/apps/app1/folders/{folderId}/open', 'FoldersController', 'open', ['something']);
  72. }
  73. public function testSimpleRouteWithDefaults() {
  74. $routes = ['routes' => [
  75. ['name' => 'folders#open', 'url' => '/folders/{folderId}/open', 'verb' => 'delete', [], 'defaults' => ['param' => 'foobar']]
  76. ]];
  77. $this->assertSimpleRoute($routes, 'folders.open', 'DELETE', '/apps/app1/folders/{folderId}/open', 'FoldersController', 'open', [], ['param' => 'foobar']);
  78. }
  79. public function testSimpleOCSRouteWithDefaults() {
  80. $routes = ['ocs' => [
  81. ['name' => 'folders#open', 'url' => '/folders/{folderId}/open', 'verb' => 'delete', 'defaults' => ['param' => 'foobar']]
  82. ]
  83. ];
  84. $this->assertSimpleOCSRoute($routes, 'folders.open', 'DELETE', '/apps/app1/folders/{folderId}/open', 'FoldersController', 'open', [], ['param' => 'foobar']);
  85. }
  86. public function testSimpleRouteWithPostfix() {
  87. $routes = ['routes' => [
  88. ['name' => 'folders#open', 'url' => '/folders/{folderId}/open', 'verb' => 'delete', 'postfix' => '_something']
  89. ]];
  90. $this->assertSimpleRoute($routes, 'folders.open', 'DELETE', '/apps/app1/folders/{folderId}/open', 'FoldersController', 'open', [], [], '_something');
  91. }
  92. public function testSimpleOCSRouteWithPostfix() {
  93. $routes = ['ocs' => [
  94. ['name' => 'folders#open', 'url' => '/folders/{folderId}/open', 'verb' => 'delete', 'postfix' => '_something']
  95. ]
  96. ];
  97. $this->assertSimpleOCSRoute($routes, 'folders.open', 'DELETE', '/apps/app1/folders/{folderId}/open', 'FoldersController', 'open', [], [], '_something');
  98. }
  99. public function testSimpleRouteWithBrokenName() {
  100. $this->expectException(\UnexpectedValueException::class);
  101. $routes = ['routes' => [
  102. ['name' => 'folders_open', 'url' => '/folders/{folderId}/open', 'verb' => 'delete']
  103. ]];
  104. /** @var IRouter|MockObject $router */
  105. $router = $this->getMockBuilder(Router::class)
  106. ->onlyMethods(['create'])
  107. ->setConstructorArgs([
  108. $this->createMock(LoggerInterface::class),
  109. $this->createMock(IRequest::class),
  110. $this->createMock(IConfig::class),
  111. $this->createMock(IEventLogger::class),
  112. $this->createMock(ContainerInterface::class)
  113. ])
  114. ->getMock();
  115. // load route configuration
  116. $container = new DIContainer('app1');
  117. $config = new RouteConfig($container, $router, $routes);
  118. $config->register();
  119. }
  120. public function testSimpleOCSRouteWithBrokenName() {
  121. $this->expectException(\UnexpectedValueException::class);
  122. $routes = ['ocs' => [
  123. ['name' => 'folders_open', 'url' => '/folders/{folderId}/open', 'verb' => 'delete']
  124. ]];
  125. /** @var IRouter|MockObject $router */
  126. $router = $this->getMockBuilder(Router::class)
  127. ->onlyMethods(['create'])
  128. ->setConstructorArgs([
  129. $this->createMock(LoggerInterface::class),
  130. $this->createMock(IRequest::class),
  131. $this->createMock(IConfig::class),
  132. $this->createMock(IEventLogger::class),
  133. $this->createMock(ContainerInterface::class)
  134. ])
  135. ->getMock();
  136. // load route configuration
  137. $container = new DIContainer('app1');
  138. $config = new RouteConfig($container, $router, $routes);
  139. $config->register();
  140. }
  141. public function testSimpleOCSRouteWithUnderScoreNames() {
  142. $routes = ['ocs' => [
  143. ['name' => 'admin_folders#open_current', 'url' => '/folders/{folderId}/open', 'verb' => 'delete']
  144. ]];
  145. $this->assertSimpleOCSRoute($routes, 'admin_folders.open_current', 'DELETE', '/apps/app1/folders/{folderId}/open', 'AdminFoldersController', 'openCurrent');
  146. }
  147. public function testOCSResource() {
  148. $routes = ['ocs-resources' => ['account' => ['url' => '/accounts']]];
  149. $this->assertOCSResource($routes, 'account', '/apps/app1/accounts', 'AccountController', 'id');
  150. }
  151. public function testOCSResourceWithUnderScoreName() {
  152. $routes = ['ocs-resources' => ['admin_accounts' => ['url' => '/admin/accounts']]];
  153. $this->assertOCSResource($routes, 'admin_accounts', '/apps/app1/admin/accounts', 'AdminAccountsController', 'id');
  154. }
  155. public function testOCSResourceWithRoot() {
  156. $routes = ['ocs-resources' => ['admin_accounts' => ['url' => '/admin/accounts', 'root' => '/core/endpoint']]];
  157. $this->assertOCSResource($routes, 'admin_accounts', '/core/endpoint/admin/accounts', 'AdminAccountsController', 'id');
  158. }
  159. public function testResource() {
  160. $routes = ['resources' => ['account' => ['url' => '/accounts']]];
  161. $this->assertResource($routes, 'account', '/apps/app1/accounts', 'AccountController', 'id');
  162. }
  163. public function testResourceWithUnderScoreName() {
  164. $routes = ['resources' => ['admin_accounts' => ['url' => '/admin/accounts']]];
  165. $this->assertResource($routes, 'admin_accounts', '/apps/app1/admin/accounts', 'AdminAccountsController', 'id');
  166. }
  167. private function assertSimpleRoute($routes, $name, $verb, $url, $controllerName, $actionName, array $requirements = [], array $defaults = [], $postfix = '', $allowRootUrl = false): void {
  168. if ($postfix) {
  169. $name .= $postfix;
  170. }
  171. // route mocks
  172. $container = new DIContainer('app1');
  173. $route = $this->mockRoute($container, $verb, $controllerName, $actionName, $requirements, $defaults);
  174. /** @var IRouter|MockObject $router */
  175. $router = $this->getMockBuilder(Router::class)
  176. ->onlyMethods(['create'])
  177. ->setConstructorArgs([
  178. $this->createMock(LoggerInterface::class),
  179. $this->createMock(IRequest::class),
  180. $this->createMock(IConfig::class),
  181. $this->createMock(IEventLogger::class),
  182. $this->createMock(ContainerInterface::class)
  183. ])
  184. ->getMock();
  185. // we expect create to be called once:
  186. $router
  187. ->expects($this->once())
  188. ->method('create')
  189. ->with($this->equalTo('app1.' . $name), $this->equalTo($url))
  190. ->willReturn($route);
  191. // load route configuration
  192. $config = new RouteConfig($container, $router, $routes);
  193. if ($allowRootUrl) {
  194. self::invokePrivate($config, 'rootUrlApps', [['app1']]);
  195. }
  196. $config->register();
  197. }
  198. /**
  199. * @param $routes
  200. * @param string $name
  201. * @param string $verb
  202. * @param string $url
  203. * @param string $controllerName
  204. * @param string $actionName
  205. * @param array $requirements
  206. * @param array $defaults
  207. * @param string $postfix
  208. */
  209. private function assertSimpleOCSRoute($routes,
  210. $name,
  211. $verb,
  212. $url,
  213. $controllerName,
  214. $actionName,
  215. array $requirements = [],
  216. array $defaults = [],
  217. $postfix = '') {
  218. if ($postfix) {
  219. $name .= $postfix;
  220. }
  221. // route mocks
  222. $container = new DIContainer('app1');
  223. $route = $this->mockRoute($container, $verb, $controllerName, $actionName, $requirements, $defaults);
  224. /** @var IRouter|MockObject $router */
  225. $router = $this->getMockBuilder(Router::class)
  226. ->onlyMethods(['create'])
  227. ->setConstructorArgs([
  228. $this->createMock(LoggerInterface::class),
  229. $this->createMock(IRequest::class),
  230. $this->createMock(IConfig::class),
  231. $this->createMock(IEventLogger::class),
  232. $this->createMock(ContainerInterface::class)
  233. ])
  234. ->getMock();
  235. // we expect create to be called once:
  236. $router
  237. ->expects($this->once())
  238. ->method('create')
  239. ->with($this->equalTo('ocs.app1.' . $name), $this->equalTo($url))
  240. ->willReturn($route);
  241. // load route configuration
  242. $config = new RouteConfig($container, $router, $routes);
  243. $config->register();
  244. }
  245. /**
  246. * @param array $yaml
  247. * @param string $resourceName
  248. * @param string $url
  249. * @param string $controllerName
  250. * @param string $paramName
  251. */
  252. private function assertOCSResource($yaml, $resourceName, $url, $controllerName, $paramName): void {
  253. /** @var IRouter|MockObject $router */
  254. $router = $this->getMockBuilder(Router::class)
  255. ->onlyMethods(['create'])
  256. ->setConstructorArgs([
  257. $this->createMock(LoggerInterface::class),
  258. $this->createMock(IRequest::class),
  259. $this->createMock(IConfig::class),
  260. $this->createMock(IEventLogger::class),
  261. $this->createMock(ContainerInterface::class)
  262. ])
  263. ->getMock();
  264. // route mocks
  265. $container = new DIContainer('app1');
  266. $indexRoute = $this->mockRoute($container, 'GET', $controllerName, 'index');
  267. $showRoute = $this->mockRoute($container, 'GET', $controllerName, 'show');
  268. $createRoute = $this->mockRoute($container, 'POST', $controllerName, 'create');
  269. $updateRoute = $this->mockRoute($container, 'PUT', $controllerName, 'update');
  270. $destroyRoute = $this->mockRoute($container, 'DELETE', $controllerName, 'destroy');
  271. $urlWithParam = $url . '/{' . $paramName . '}';
  272. // we expect create to be called five times:
  273. $router
  274. ->expects($this->exactly(5))
  275. ->method('create')
  276. ->withConsecutive(
  277. [$this->equalTo('ocs.app1.' . $resourceName . '.index'), $this->equalTo($url)],
  278. [$this->equalTo('ocs.app1.' . $resourceName . '.show'), $this->equalTo($urlWithParam)],
  279. [$this->equalTo('ocs.app1.' . $resourceName . '.create'), $this->equalTo($url)],
  280. [$this->equalTo('ocs.app1.' . $resourceName . '.update'), $this->equalTo($urlWithParam)],
  281. [$this->equalTo('ocs.app1.' . $resourceName . '.destroy'), $this->equalTo($urlWithParam)],
  282. )->willReturnOnConsecutiveCalls(
  283. $indexRoute,
  284. $showRoute,
  285. $createRoute,
  286. $updateRoute,
  287. $destroyRoute,
  288. );
  289. // load route configuration
  290. $config = new RouteConfig($container, $router, $yaml);
  291. $config->register();
  292. }
  293. /**
  294. * @param string $resourceName
  295. * @param string $url
  296. * @param string $controllerName
  297. * @param string $paramName
  298. */
  299. private function assertResource($yaml, $resourceName, $url, $controllerName, $paramName) {
  300. /** @var IRouter|MockObject $router */
  301. $router = $this->getMockBuilder(Router::class)
  302. ->onlyMethods(['create'])
  303. ->setConstructorArgs([
  304. $this->createMock(LoggerInterface::class),
  305. $this->createMock(IRequest::class),
  306. $this->createMock(IConfig::class),
  307. $this->createMock(IEventLogger::class),
  308. $this->createMock(ContainerInterface::class)
  309. ])
  310. ->getMock();
  311. // route mocks
  312. $container = new DIContainer('app1');
  313. $indexRoute = $this->mockRoute($container, 'GET', $controllerName, 'index');
  314. $showRoute = $this->mockRoute($container, 'GET', $controllerName, 'show');
  315. $createRoute = $this->mockRoute($container, 'POST', $controllerName, 'create');
  316. $updateRoute = $this->mockRoute($container, 'PUT', $controllerName, 'update');
  317. $destroyRoute = $this->mockRoute($container, 'DELETE', $controllerName, 'destroy');
  318. $urlWithParam = $url . '/{' . $paramName . '}';
  319. // we expect create to be called five times:
  320. $router
  321. ->expects($this->exactly(5))
  322. ->method('create')
  323. ->withConsecutive(
  324. [$this->equalTo('app1.' . $resourceName . '.index'), $this->equalTo($url)],
  325. [$this->equalTo('app1.' . $resourceName . '.show'), $this->equalTo($urlWithParam)],
  326. [$this->equalTo('app1.' . $resourceName . '.create'), $this->equalTo($url)],
  327. [$this->equalTo('app1.' . $resourceName . '.update'), $this->equalTo($urlWithParam)],
  328. [$this->equalTo('app1.' . $resourceName . '.destroy'), $this->equalTo($urlWithParam)],
  329. )->willReturnOnConsecutiveCalls(
  330. $indexRoute,
  331. $showRoute,
  332. $createRoute,
  333. $updateRoute,
  334. $destroyRoute,
  335. );
  336. // load route configuration
  337. $config = new RouteConfig($container, $router, $yaml);
  338. $config->register();
  339. }
  340. /**
  341. * @param DIContainer $container
  342. * @param string $verb
  343. * @param string $controllerName
  344. * @param string $actionName
  345. * @param array $requirements
  346. * @param array $defaults
  347. * @return MockObject
  348. */
  349. private function mockRoute(
  350. DIContainer $container,
  351. $verb,
  352. $controllerName,
  353. $actionName,
  354. array $requirements = [],
  355. array $defaults = []
  356. ) {
  357. $route = $this->getMockBuilder(Route::class)
  358. ->onlyMethods(['method', 'requirements', 'defaults'])
  359. ->disableOriginalConstructor()
  360. ->getMock();
  361. $route
  362. ->expects($this->once())
  363. ->method('method')
  364. ->with($this->equalTo($verb))
  365. ->willReturn($route);
  366. if (count($requirements) > 0) {
  367. $route
  368. ->expects($this->once())
  369. ->method('requirements')
  370. ->with($this->equalTo($requirements))
  371. ->willReturn($route);
  372. }
  373. $route->expects($this->once())
  374. ->method('defaults')
  375. ->with($this->callback(function (array $def) use ($defaults, $controllerName, $actionName) {
  376. $defaults['caller'] = ['app1', $controllerName, $actionName];
  377. $this->assertEquals($defaults, $def);
  378. return true;
  379. }))
  380. ->willReturn($route);
  381. return $route;
  382. }
  383. }