RouteCollectionTest.php 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755
  1. <?php
  2. /**
  3. * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
  4. * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
  5. *
  6. * Licensed under The MIT License
  7. * For full copyright and license information, please see the LICENSE.txt
  8. * Redistributions of files must retain the above copyright notice.
  9. *
  10. * @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
  11. * @link https://cakephp.org CakePHP(tm) Project
  12. * @since 3.0.0
  13. * @license https://opensource.org/licenses/mit-license.php MIT License
  14. */
  15. namespace Cake\Test\TestCase\Routing;
  16. use Cake\Http\ServerRequest;
  17. use Cake\Routing\Exception\MissingRouteException;
  18. use Cake\Routing\RouteBuilder;
  19. use Cake\Routing\RouteCollection;
  20. use Cake\Routing\Route\Route;
  21. use Cake\TestSuite\TestCase;
  22. class RouteCollectionTest extends TestCase
  23. {
  24. /**
  25. * @var \Cake\Routing\RouteCollection
  26. */
  27. protected $collection;
  28. /**
  29. * Setup method
  30. *
  31. * @return void
  32. */
  33. public function setUp()
  34. {
  35. parent::setUp();
  36. $this->collection = new RouteCollection();
  37. }
  38. /**
  39. * Test parse() throws an error on unknown routes.
  40. *
  41. * @return void
  42. */
  43. public function testParseMissingRoute()
  44. {
  45. $this->expectException(\Cake\Routing\Exception\MissingRouteException::class);
  46. $this->expectExceptionMessage('A route matching "/" could not be found');
  47. $routes = new RouteBuilder($this->collection, '/b', ['key' => 'value']);
  48. $routes->connect('/', ['controller' => 'Articles']);
  49. $routes->connect('/:id', ['controller' => 'Articles', 'action' => 'view']);
  50. $result = $this->collection->parse('/');
  51. $this->assertEquals([], $result, 'Should not match, missing /b');
  52. }
  53. /**
  54. * Test parse() throws an error on known routes called with unknown methods.
  55. *
  56. * @return void
  57. */
  58. public function testParseMissingRouteMethod()
  59. {
  60. $this->expectException(\Cake\Routing\Exception\MissingRouteException::class);
  61. $this->expectExceptionMessage('A "POST" route matching "/b" could not be found');
  62. $routes = new RouteBuilder($this->collection, '/b', ['key' => 'value']);
  63. $routes->connect('/', ['controller' => 'Articles', '_method' => ['GET']]);
  64. $result = $this->collection->parse('/b', 'GET');
  65. $this->assertNotEmpty($result, 'Route should be found');
  66. $result = $this->collection->parse('/b', 'POST');
  67. $this->assertEquals([], $result, 'Should not match with missing method');
  68. }
  69. /**
  70. * Test parsing routes.
  71. *
  72. * @return void
  73. */
  74. public function testParse()
  75. {
  76. $routes = new RouteBuilder($this->collection, '/b', ['key' => 'value']);
  77. $routes->connect('/', ['controller' => 'Articles']);
  78. $routes->connect('/:id', ['controller' => 'Articles', 'action' => 'view']);
  79. $routes->connect('/media/search/*', ['controller' => 'Media', 'action' => 'search']);
  80. $result = $this->collection->parse('/b/');
  81. $expected = [
  82. 'controller' => 'Articles',
  83. 'action' => 'index',
  84. 'pass' => [],
  85. 'plugin' => null,
  86. 'key' => 'value',
  87. '_matchedRoute' => '/b',
  88. ];
  89. $this->assertEquals($expected, $result);
  90. $result = $this->collection->parse('/b/the-thing?one=two');
  91. $expected = [
  92. 'controller' => 'Articles',
  93. 'action' => 'view',
  94. 'id' => 'the-thing',
  95. 'pass' => [],
  96. 'plugin' => null,
  97. 'key' => 'value',
  98. '?' => ['one' => 'two'],
  99. '_matchedRoute' => '/b/:id',
  100. ];
  101. $this->assertEquals($expected, $result);
  102. $result = $this->collection->parse('/b/media/search');
  103. $expected = [
  104. 'key' => 'value',
  105. 'pass' => [],
  106. 'plugin' => null,
  107. 'controller' => 'Media',
  108. 'action' => 'search',
  109. '_matchedRoute' => '/b/media/search/*',
  110. ];
  111. $this->assertEquals($expected, $result);
  112. $result = $this->collection->parse('/b/media/search/thing');
  113. $expected = [
  114. 'key' => 'value',
  115. 'pass' => ['thing'],
  116. 'plugin' => null,
  117. 'controller' => 'Media',
  118. 'action' => 'search',
  119. '_matchedRoute' => '/b/media/search/*',
  120. ];
  121. $this->assertEquals($expected, $result);
  122. }
  123. /**
  124. * Test parsing routes with and without _name.
  125. *
  126. * @return void
  127. */
  128. public function testParseWithNameParameter()
  129. {
  130. $routes = new RouteBuilder($this->collection, '/b', ['key' => 'value']);
  131. $routes->connect('/', ['controller' => 'Articles']);
  132. $routes->connect('/:id', ['controller' => 'Articles', 'action' => 'view']);
  133. $routes->connect('/media/search/*', ['controller' => 'Media', 'action' => 'search'], ['_name' => 'media_search']);
  134. $result = $this->collection->parse('/b/');
  135. $expected = [
  136. 'controller' => 'Articles',
  137. 'action' => 'index',
  138. 'pass' => [],
  139. 'plugin' => null,
  140. 'key' => 'value',
  141. '_matchedRoute' => '/b',
  142. ];
  143. $this->assertEquals($expected, $result);
  144. $result = $this->collection->parse('/b/the-thing?one=two');
  145. $expected = [
  146. 'controller' => 'Articles',
  147. 'action' => 'view',
  148. 'id' => 'the-thing',
  149. 'pass' => [],
  150. 'plugin' => null,
  151. 'key' => 'value',
  152. '?' => ['one' => 'two'],
  153. '_matchedRoute' => '/b/:id',
  154. ];
  155. $this->assertEquals($expected, $result);
  156. $result = $this->collection->parse('/b/media/search');
  157. $expected = [
  158. 'key' => 'value',
  159. 'pass' => [],
  160. 'plugin' => null,
  161. 'controller' => 'Media',
  162. 'action' => 'search',
  163. '_matchedRoute' => '/b/media/search/*',
  164. '_name' => 'media_search'
  165. ];
  166. $this->assertEquals($expected, $result);
  167. $result = $this->collection->parse('/b/media/search/thing');
  168. $expected = [
  169. 'key' => 'value',
  170. 'pass' => ['thing'],
  171. 'plugin' => null,
  172. 'controller' => 'Media',
  173. 'action' => 'search',
  174. '_matchedRoute' => '/b/media/search/*',
  175. '_name' => 'media_search'
  176. ];
  177. $this->assertEquals($expected, $result);
  178. }
  179. /**
  180. * Test that parse decodes URL data before matching.
  181. * This is important for multibyte URLs that pass through URL rewriting.
  182. *
  183. * @return void
  184. */
  185. public function testParseEncodedBytesInFixedSegment()
  186. {
  187. $routes = new RouteBuilder($this->collection, '/');
  188. $routes->connect('/ден/:day-:month', ['controller' => 'Events', 'action' => 'index']);
  189. $url = '/%D0%B4%D0%B5%D0%BD/15-%D0%BE%D0%BA%D1%82%D0%BE%D0%BC%D0%B2%D1%80%D0%B8?test=foo';
  190. $result = $this->collection->parse($url);
  191. $expected = [
  192. 'pass' => [],
  193. 'plugin' => null,
  194. 'controller' => 'Events',
  195. 'action' => 'index',
  196. 'day' => '15',
  197. 'month' => 'октомври',
  198. '?' => ['test' => 'foo'],
  199. '_matchedRoute' => '/ден/:day-:month',
  200. ];
  201. $this->assertEquals($expected, $result);
  202. $request = new ServerRequest(['url' => $url]);
  203. $result = $this->collection->parseRequest($request);
  204. $this->assertEquals($expected, $result);
  205. }
  206. /**
  207. * Test that parsing checks all the related path scopes.
  208. *
  209. * @return void
  210. */
  211. public function testParseFallback()
  212. {
  213. $routes = new RouteBuilder($this->collection, '/', []);
  214. $routes->resources('Articles');
  215. $routes->connect('/:controller', ['action' => 'index'], ['routeClass' => 'InflectedRoute']);
  216. $routes->connect('/:controller/:action', [], ['routeClass' => 'InflectedRoute']);
  217. $result = $this->collection->parse('/articles/add');
  218. $expected = [
  219. 'controller' => 'Articles',
  220. 'action' => 'add',
  221. 'plugin' => null,
  222. 'pass' => [],
  223. '_matchedRoute' => '/:controller/:action',
  224. ];
  225. $this->assertEquals($expected, $result);
  226. }
  227. /**
  228. * Test parseRequest() throws an error on unknown routes.
  229. *
  230. * @return void
  231. */
  232. public function testParseRequestMissingRoute()
  233. {
  234. $this->expectException(\Cake\Routing\Exception\MissingRouteException::class);
  235. $this->expectExceptionMessage('A route matching "/" could not be found');
  236. $routes = new RouteBuilder($this->collection, '/b', ['key' => 'value']);
  237. $routes->connect('/', ['controller' => 'Articles']);
  238. $routes->connect('/:id', ['controller' => 'Articles', 'action' => 'view']);
  239. $request = new ServerRequest(['url' => '/']);
  240. $result = $this->collection->parseRequest($request);
  241. $this->assertEquals([], $result, 'Should not match, missing /b');
  242. }
  243. /**
  244. * Test parseRequest() checks host conditions
  245. *
  246. * @return void
  247. */
  248. public function testParseRequestCheckHostCondition()
  249. {
  250. $routes = new RouteBuilder($this->collection, '/');
  251. $routes->connect(
  252. '/fallback',
  253. ['controller' => 'Articles', 'action' => 'index'],
  254. ['_host' => '*.example.com']
  255. );
  256. $request = new ServerRequest([
  257. 'environment' => [
  258. 'HTTP_HOST' => 'a.example.com',
  259. 'PATH_INFO' => '/fallback'
  260. ]
  261. ]);
  262. $result = $this->collection->parseRequest($request);
  263. $expected = [
  264. 'controller' => 'Articles',
  265. 'action' => 'index',
  266. 'pass' => [],
  267. 'plugin' => null,
  268. '_matchedRoute' => '/fallback'
  269. ];
  270. $this->assertEquals($expected, $result, 'Should match, domain is correct');
  271. $request = new ServerRequest([
  272. 'environment' => [
  273. 'HTTP_HOST' => 'foo.bar.example.com',
  274. 'PATH_INFO' => '/fallback'
  275. ]
  276. ]);
  277. $result = $this->collection->parseRequest($request);
  278. $this->assertEquals($expected, $result, 'Should match, domain is a matching subdomain');
  279. $request = new ServerRequest([
  280. 'environment' => [
  281. 'HTTP_HOST' => 'example.test.com',
  282. 'PATH_INFO' => '/fallback'
  283. ]
  284. ]);
  285. try {
  286. $this->collection->parseRequest($request);
  287. $this->fail('No exception raised');
  288. } catch (MissingRouteException $e) {
  289. $this->assertContains('/fallback', $e->getMessage());
  290. }
  291. }
  292. /**
  293. * Get a list of hostnames
  294. *
  295. * @return array
  296. */
  297. public static function hostProvider()
  298. {
  299. return [
  300. ['wrong.example'],
  301. ['example.com'],
  302. ['aexample.com'],
  303. ];
  304. }
  305. /**
  306. * Test parseRequest() checks host conditions
  307. *
  308. * @dataProvider hostProvider
  309. */
  310. public function testParseRequestCheckHostConditionFail($host)
  311. {
  312. $this->expectException(\Cake\Routing\Exception\MissingRouteException::class);
  313. $this->expectExceptionMessage('A route matching "/fallback" could not be found');
  314. $routes = new RouteBuilder($this->collection, '/');
  315. $routes->connect(
  316. '/fallback',
  317. ['controller' => 'Articles', 'action' => 'index'],
  318. ['_host' => '*.example.com']
  319. );
  320. $request = new ServerRequest([
  321. 'environment' => [
  322. 'HTTP_HOST' => $host,
  323. 'PATH_INFO' => '/fallback'
  324. ]
  325. ]);
  326. $this->collection->parseRequest($request);
  327. }
  328. /**
  329. * Test parsing routes.
  330. *
  331. * @return void
  332. */
  333. public function testParseRequest()
  334. {
  335. $routes = new RouteBuilder($this->collection, '/b', ['key' => 'value']);
  336. $routes->connect('/', ['controller' => 'Articles']);
  337. $routes->connect('/:id', ['controller' => 'Articles', 'action' => 'view']);
  338. $routes->connect('/media/search/*', ['controller' => 'Media', 'action' => 'search']);
  339. $request = new ServerRequest(['url' => '/b/']);
  340. $result = $this->collection->parseRequest($request);
  341. $expected = [
  342. 'controller' => 'Articles',
  343. 'action' => 'index',
  344. 'pass' => [],
  345. 'plugin' => null,
  346. 'key' => 'value',
  347. '_matchedRoute' => '/b',
  348. ];
  349. $this->assertEquals($expected, $result);
  350. $request = new ServerRequest(['url' => '/b/media/search']);
  351. $result = $this->collection->parseRequest($request);
  352. $expected = [
  353. 'key' => 'value',
  354. 'pass' => [],
  355. 'plugin' => null,
  356. 'controller' => 'Media',
  357. 'action' => 'search',
  358. '_matchedRoute' => '/b/media/search/*',
  359. ];
  360. $this->assertEquals($expected, $result);
  361. $request = new ServerRequest(['url' => '/b/media/search/thing']);
  362. $result = $this->collection->parseRequest($request);
  363. $expected = [
  364. 'key' => 'value',
  365. 'pass' => ['thing'],
  366. 'plugin' => null,
  367. 'controller' => 'Media',
  368. 'action' => 'search',
  369. '_matchedRoute' => '/b/media/search/*',
  370. ];
  371. $this->assertEquals($expected, $result);
  372. $request = new ServerRequest(['url' => '/b/the-thing?one=two']);
  373. $result = $this->collection->parseRequest($request);
  374. $expected = [
  375. 'controller' => 'Articles',
  376. 'action' => 'view',
  377. 'id' => 'the-thing',
  378. 'pass' => [],
  379. 'plugin' => null,
  380. 'key' => 'value',
  381. '?' => ['one' => 'two'],
  382. '_matchedRoute' => '/b/:id',
  383. ];
  384. $this->assertEquals($expected, $result);
  385. }
  386. /**
  387. * Test parsing routes that match non-ascii urls
  388. *
  389. * @return void
  390. */
  391. public function testParseRequestUnicode()
  392. {
  393. $routes = new RouteBuilder($this->collection, '/b', []);
  394. $routes->connect('/alta/confirmación', ['controller' => 'Media', 'action' => 'confirm']);
  395. $request = new ServerRequest(['url' => '/b/alta/confirmaci%C3%B3n']);
  396. $result = $this->collection->parseRequest($request);
  397. $expected = [
  398. 'controller' => 'Media',
  399. 'action' => 'confirm',
  400. 'pass' => [],
  401. 'plugin' => null,
  402. '_matchedRoute' => '/b/alta/confirmación',
  403. ];
  404. $this->assertEquals($expected, $result);
  405. $request = new ServerRequest(['url' => '/b/alta/confirmación']);
  406. $result = $this->collection->parseRequest($request);
  407. $this->assertEquals($expected, $result);
  408. }
  409. /**
  410. * Test match() throws an error on unknown routes.
  411. *
  412. * @return void
  413. */
  414. public function testMatchError()
  415. {
  416. $this->expectException(\Cake\Routing\Exception\MissingRouteException::class);
  417. $this->expectExceptionMessage('A route matching "array (');
  418. $context = [
  419. '_base' => '/',
  420. '_scheme' => 'http',
  421. '_host' => 'example.org',
  422. ];
  423. $routes = new RouteBuilder($this->collection, '/b');
  424. $routes->connect('/', ['controller' => 'Articles']);
  425. $this->collection->match(['plugin' => null, 'controller' => 'Articles', 'action' => 'add'], $context);
  426. }
  427. /**
  428. * Test matching routes.
  429. *
  430. * @return void
  431. */
  432. public function testMatch()
  433. {
  434. $context = [
  435. '_base' => '/',
  436. '_scheme' => 'http',
  437. '_host' => 'example.org',
  438. ];
  439. $routes = new RouteBuilder($this->collection, '/b');
  440. $routes->connect('/', ['controller' => 'Articles']);
  441. $routes->connect('/:id', ['controller' => 'Articles', 'action' => 'view']);
  442. $result = $this->collection->match(['plugin' => null, 'controller' => 'Articles', 'action' => 'index'], $context);
  443. $this->assertEquals('b', $result);
  444. $result = $this->collection->match(
  445. ['id' => 'thing', 'plugin' => null, 'controller' => 'Articles', 'action' => 'view'],
  446. $context
  447. );
  448. $this->assertEquals('b/thing', $result);
  449. }
  450. /**
  451. * Test matching routes with names
  452. *
  453. * @return void
  454. */
  455. public function testMatchNamed()
  456. {
  457. $context = [
  458. '_base' => '/',
  459. '_scheme' => 'http',
  460. '_host' => 'example.org',
  461. ];
  462. $routes = new RouteBuilder($this->collection, '/b');
  463. $routes->connect('/', ['controller' => 'Articles']);
  464. $routes->connect('/:id', ['controller' => 'Articles', 'action' => 'view'], ['_name' => 'article:view']);
  465. $result = $this->collection->match(['_name' => 'article:view', 'id' => '2'], $context);
  466. $this->assertEquals('/b/2', $result);
  467. $result = $this->collection->match(['plugin' => null, 'controller' => 'Articles', 'action' => 'view', 'id' => '2'], $context);
  468. $this->assertEquals('b/2', $result);
  469. }
  470. /**
  471. * Test match() throws an error on named routes that fail to match
  472. *
  473. * @return void
  474. */
  475. public function testMatchNamedError()
  476. {
  477. $this->expectException(\Cake\Routing\Exception\MissingRouteException::class);
  478. $this->expectExceptionMessage('A named route was found for "fail", but matching failed');
  479. $context = [
  480. '_base' => '/',
  481. '_scheme' => 'http',
  482. '_host' => 'example.org',
  483. ];
  484. $routes = new RouteBuilder($this->collection, '/b');
  485. $routes->connect('/:lang/articles', ['controller' => 'Articles'], ['_name' => 'fail']);
  486. $this->collection->match(['_name' => 'fail'], $context);
  487. }
  488. /**
  489. * Test matching routes with names and failing
  490. *
  491. * @return void
  492. */
  493. public function testMatchNamedMissingError()
  494. {
  495. $this->expectException(\Cake\Routing\Exception\MissingRouteException::class);
  496. $context = [
  497. '_base' => '/',
  498. '_scheme' => 'http',
  499. '_host' => 'example.org',
  500. ];
  501. $routes = new RouteBuilder($this->collection, '/b');
  502. $routes->connect('/:id', ['controller' => 'Articles', 'action' => 'view'], ['_name' => 'article:view']);
  503. $this->collection->match(['_name' => 'derp'], $context);
  504. }
  505. /**
  506. * Test matching plugin routes.
  507. *
  508. * @return void
  509. */
  510. public function testMatchPlugin()
  511. {
  512. $context = [
  513. '_base' => '/',
  514. '_scheme' => 'http',
  515. '_host' => 'example.org',
  516. ];
  517. $routes = new RouteBuilder($this->collection, '/contacts', ['plugin' => 'Contacts']);
  518. $routes->connect('/', ['controller' => 'Contacts']);
  519. $result = $this->collection->match(['plugin' => 'Contacts', 'controller' => 'Contacts', 'action' => 'index'], $context);
  520. $this->assertEquals('contacts', $result);
  521. }
  522. /**
  523. * Test that prefixes increase the specificity of a route match.
  524. *
  525. * Connect the admin route after the non prefixed version, this means
  526. * the non-prefix route would have a more specific name (users:index)
  527. *
  528. * @return void
  529. */
  530. public function testMatchPrefixSpecificity()
  531. {
  532. $context = [
  533. '_base' => '/',
  534. '_scheme' => 'http',
  535. '_host' => 'example.org',
  536. ];
  537. $routes = new RouteBuilder($this->collection, '/');
  538. $routes->connect('/:action/*', ['controller' => 'Users']);
  539. $routes->connect('/admin/:controller', ['prefix' => 'admin', 'action' => 'index']);
  540. $url = [
  541. 'plugin' => null,
  542. 'prefix' => 'admin',
  543. 'controller' => 'Users',
  544. 'action' => 'index'
  545. ];
  546. $result = $this->collection->match($url, $context);
  547. $this->assertEquals('admin/Users', $result);
  548. $url = [
  549. 'plugin' => null,
  550. 'controller' => 'Users',
  551. 'action' => 'index'
  552. ];
  553. $result = $this->collection->match($url, $context);
  554. $this->assertEquals('index', $result);
  555. }
  556. /**
  557. * Test getting named routes.
  558. *
  559. * @return void
  560. */
  561. public function testNamed()
  562. {
  563. $routes = new RouteBuilder($this->collection, '/l');
  564. $routes->connect('/:controller', ['action' => 'index'], ['_name' => 'cntrl']);
  565. $routes->connect('/:controller/:action/*');
  566. $all = $this->collection->named();
  567. $this->assertCount(1, $all);
  568. $this->assertInstanceOf('Cake\Routing\Route\Route', $all['cntrl']);
  569. $this->assertEquals('/l/:controller', $all['cntrl']->template);
  570. }
  571. /**
  572. * Test the add() and routes() method.
  573. *
  574. * @return void
  575. */
  576. public function testAddingRoutes()
  577. {
  578. $one = new Route('/pages/*', ['controller' => 'Pages', 'action' => 'display']);
  579. $two = new Route('/', ['controller' => 'Dashboards', 'action' => 'display']);
  580. $this->collection->add($one);
  581. $this->collection->add($two);
  582. $routes = $this->collection->routes();
  583. $this->assertCount(2, $routes);
  584. $this->assertSame($one, $routes[0]);
  585. $this->assertSame($two, $routes[1]);
  586. }
  587. /**
  588. * Test the add() with some _name.
  589. *
  590. * @return void
  591. */
  592. public function testAddingDuplicateNamedRoutes()
  593. {
  594. $this->expectException(\Cake\Routing\Exception\DuplicateNamedRouteException::class);
  595. $one = new Route('/pages/*', ['controller' => 'Pages', 'action' => 'display']);
  596. $two = new Route('/', ['controller' => 'Dashboards', 'action' => 'display']);
  597. $this->collection->add($one, ['_name' => 'test']);
  598. $this->collection->add($two, ['_name' => 'test']);
  599. }
  600. /**
  601. * Test basic setExtension and its getter.
  602. *
  603. * @return void
  604. */
  605. public function testSetExtensions()
  606. {
  607. $this->assertEquals([], $this->collection->getExtensions());
  608. $this->collection->setExtensions(['json']);
  609. $this->assertEquals(['json'], $this->collection->getExtensions());
  610. $this->collection->setExtensions(['rss', 'xml']);
  611. $this->assertEquals(['json', 'rss', 'xml'], $this->collection->getExtensions());
  612. $this->collection->setExtensions(['csv'], false);
  613. $this->assertEquals(['csv'], $this->collection->getExtensions());
  614. }
  615. /**
  616. * Test adding middleware to the collection.
  617. *
  618. * @return void
  619. */
  620. public function testRegisterMiddleware()
  621. {
  622. $result = $this->collection->registerMiddleware('closure', function () {
  623. });
  624. $this->assertSame($result, $this->collection);
  625. $mock = $this->getMockBuilder('\stdClass')
  626. ->setMethods(['__invoke'])
  627. ->getMock();
  628. $result = $this->collection->registerMiddleware('callable', $mock);
  629. $this->assertSame($result, $this->collection);
  630. $this->assertTrue($this->collection->hasMiddleware('closure'));
  631. $this->assertTrue($this->collection->hasMiddleware('callable'));
  632. $this->collection->registerMiddleware('class', 'Dumb');
  633. }
  634. /**
  635. * Test adding a middleware group to the collection.
  636. *
  637. * @return void
  638. */
  639. public function testMiddlewareGroup()
  640. {
  641. $this->collection->registerMiddleware('closure', function () {
  642. });
  643. $mock = $this->getMockBuilder('\stdClass')
  644. ->setMethods(['__invoke'])
  645. ->getMock();
  646. $result = $this->collection->registerMiddleware('callable', $mock);
  647. $this->collection->registerMiddleware('callable', $mock);
  648. $this->collection->middlewareGroup('group', ['closure', 'callable']);
  649. $this->assertTrue($this->collection->hasMiddlewareGroup('group'));
  650. }
  651. /**
  652. * Test adding a middleware group with the same name overwrites the original list
  653. *
  654. * @return void
  655. */
  656. public function testMiddlewareGroupOverwrite()
  657. {
  658. $stub = function () {
  659. };
  660. $this->collection->registerMiddleware('closure', $stub);
  661. $result = $this->collection->registerMiddleware('callable', $stub);
  662. $this->collection->registerMiddleware('callable', $stub);
  663. $this->collection->middlewareGroup('group', ['callable']);
  664. $this->collection->middlewareGroup('group', ['closure', 'callable']);
  665. $this->assertSame([$stub, $stub], $this->collection->getMiddleware(['group']));
  666. }
  667. /**
  668. * Test adding ab unregistered middleware to a middleware group fails.
  669. *
  670. * @return void
  671. */
  672. public function testMiddlewareGroupUnregisteredMiddleware()
  673. {
  674. $this->expectException(\RuntimeException::class);
  675. $this->expectExceptionMessage('Cannot add \'bad\' middleware to group \'group\'. It has not been registered.');
  676. $this->collection->middlewareGroup('group', ['bad']);
  677. }
  678. }