RouteCollectionTest.php 23 KB

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