RouteTest.php 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958
  1. <?php
  2. /**
  3. * Description of RouteTest
  4. *
  5. * @group kohana
  6. * @group kohana.core
  7. * @group kohana.core.route
  8. *
  9. * @package Kohana
  10. * @category Tests
  11. * @author Kohana Team
  12. * @author BRMatt <matthew@sigswitch.com>
  13. * @copyright (c) Kohana Team
  14. * @license https://koseven.ga/LICENSE.md
  15. */
  16. include Kohana::find_file('tests', 'test_data/callback_routes');
  17. class Kohana_RouteTest extends Unittest_TestCase
  18. {
  19. /**
  20. * Remove all caches
  21. */
  22. // @codingStandardsIgnoreStart
  23. public function setUp()
  24. // @codingStandardsIgnoreEnd
  25. {
  26. parent::setUp();
  27. Kohana::$config->load('url')->set('trusted_hosts', ['kohanaframework\.org']);
  28. $this->cleanCacheDir();
  29. }
  30. /**
  31. * Removes cache files created during tests
  32. */
  33. // @codingStandardsIgnoreStart
  34. public function tearDown()
  35. // @codingStandardsIgnoreEnd
  36. {
  37. parent::tearDown();
  38. $this->cleanCacheDir();
  39. }
  40. /**
  41. * If Route::get() is asked for a route that does not exist then
  42. * it should throw a Kohana_Exception
  43. *
  44. * Note use of @expectedException
  45. *
  46. * @test
  47. * @covers Route::get
  48. * @expectedException Kohana_Exception
  49. */
  50. public function test_get_throws_exception_if_route_dnx()
  51. {
  52. Route::get('HAHAHAHAHAHAHAHAHA');
  53. }
  54. /**
  55. * Route::all() should return all routes defined via Route::set()
  56. * and not through new Route()
  57. *
  58. * @test
  59. * @covers Route::all
  60. */
  61. public function test_all_returns_all_defined_routes()
  62. {
  63. $defined_routes = self::readAttribute('Route', '_routes');
  64. $this->assertSame($defined_routes, Route::all());
  65. }
  66. /**
  67. * Route::name() should fetch the name of a passed route
  68. * If route is not found then it should return FALSE
  69. *
  70. * @TODO: This test needs to segregate the Route::$_routes singleton
  71. * @test
  72. * @covers Route::name
  73. */
  74. public function test_name_returns_routes_name_or_false_if_dnx()
  75. {
  76. $route = Route::set('flamingo_people', 'flamingo/dance');
  77. $this->assertSame('flamingo_people', Route::name($route));
  78. $route = new Route('dance/dance');
  79. $this->assertFalse(Route::name($route));
  80. }
  81. /**
  82. * If Route::cache() was able to restore routes from the cache then
  83. * it should return TRUE and load the cached routes
  84. *
  85. * @test
  86. * @covers Route::cache
  87. */
  88. public function test_cache_stores_route_objects()
  89. {
  90. $routes = Route::all();
  91. // First we create the cache
  92. Route::cache(TRUE);
  93. // Now lets modify the "current" routes
  94. Route::set('nonsensical_route', 'flabbadaga/ding_dong');
  95. // Then try and load said cache
  96. $this->assertTrue(Route::cache());
  97. // Check the route cache flag
  98. $this->assertTrue(Route::$cache);
  99. // And if all went ok the nonsensical route should be gone...
  100. $this->assertEquals($routes, Route::all());
  101. }
  102. /**
  103. * Check appending cached routes. See http://dev.kohanaframework.org/issues/4347
  104. *
  105. * @test
  106. * @covers Route::cache
  107. */
  108. public function test_cache_append_routes()
  109. {
  110. $cached = Route::all();
  111. // First we create the cache
  112. Route::cache(TRUE);
  113. // Now lets modify the "current" routes
  114. Route::set('nonsensical_route', 'flabbadaga/ding_dong');
  115. $modified = Route::all();
  116. // Then try and load said cache
  117. $this->assertTrue(Route::cache(NULL, TRUE));
  118. // Check the route cache flag
  119. $this->assertTrue(Route::$cache);
  120. // And if all went ok the nonsensical route should exist with the other routes...
  121. $this->assertEquals(Route::all(), $cached + $modified);
  122. }
  123. /**
  124. * Route::cache() should return FALSE if cached routes could not be found
  125. *
  126. * The cache is cleared before and after each test in setUp tearDown
  127. * by cleanCacheDir()
  128. *
  129. * @test
  130. * @covers Route::cache
  131. */
  132. public function test_cache_returns_false_if_cache_dnx()
  133. {
  134. $this->assertSame(FALSE, Route::cache(), 'Route cache was not empty');
  135. // Check the route cache flag
  136. $this->assertFalse(Route::$cache);
  137. }
  138. /**
  139. * If the constructor is passed a NULL uri then it should assume it's
  140. * being loaded from the cache & therefore shouldn't override the cached attributes
  141. *
  142. * @test
  143. * @covers Route::__construct
  144. */
  145. public function test_constructor_returns_if_uri_is_null()
  146. {
  147. // We use a mock object to make sure that the route wasn't recompiled
  148. $route = $this->createMock('Route', ['_compile'], [], '', FALSE);
  149. $route
  150. ->expects($this->never())
  151. ->method('compile');
  152. $route->__construct(NULL,NULL);
  153. $this->assertAttributeSame('', '_uri', $route);
  154. $this->assertAttributeSame([], '_regex', $route);
  155. $this->assertAttributeSame(['action' => 'index', 'host' => FALSE], '_defaults', $route);
  156. $this->assertAttributeSame(NULL, '_route_regex', $route);
  157. }
  158. /**
  159. * Provider for test_constructor_only_changes_custom_regex_if_passed
  160. *
  161. * @return array
  162. */
  163. public function provider_constructor_only_changes_custom_regex_if_passed()
  164. {
  165. return [
  166. ['<controller>/<action>', '<controller>/<action>'],
  167. ];
  168. }
  169. /**
  170. * The constructor should only use custom regex if passed a non-empty array
  171. *
  172. * Technically we can't "test" this as the default regex is an empty array, this
  173. * is purely for improving test coverage
  174. *
  175. * @dataProvider provider_constructor_only_changes_custom_regex_if_passed
  176. *
  177. * @test
  178. * @covers Route::__construct
  179. */
  180. public function test_constructor_only_changes_custom_regex_if_passed($uri, $uri2)
  181. {
  182. $route = new Route($uri, []);
  183. $this->assertAttributeSame([], '_regex', $route);
  184. $route = new Route($uri2, NULL);
  185. $this->assertAttributeSame([], '_regex', $route);
  186. }
  187. /**
  188. * When we pass custom regex to the route's constructor it should it
  189. * in leu of the default. This does not apply to callback/lambda routes
  190. *
  191. * @test
  192. * @covers Route::__construct
  193. * @covers Route::compile
  194. */
  195. public function test_route_uses_custom_regex_passed_to_constructor()
  196. {
  197. $regex = ['id' => '[0-9]{1,2}'];
  198. $route = new Route('<controller>(/<action>(/<id>))', $regex);
  199. $this->assertAttributeSame($regex, '_regex', $route);
  200. $this->assertAttributeContains(
  201. $regex['id'],
  202. '_route_regex',
  203. $route
  204. );
  205. }
  206. /**
  207. * Provider for test_matches_returns_false_on_failure
  208. *
  209. * @return array
  210. */
  211. public function provider_matches_returns_false_on_failure()
  212. {
  213. return [
  214. ['projects/(<project_id>/(<controller>(/<action>(/<id>))))', 'apple/pie'],
  215. ];
  216. }
  217. /**
  218. * Route::matches() should return false if the route doesn't match against a uri
  219. *
  220. * @dataProvider provider_matches_returns_false_on_failure
  221. *
  222. * @test
  223. * @covers Route::matches
  224. */
  225. public function test_matches_returns_false_on_failure($uri, $match)
  226. {
  227. $route = new Route($uri);
  228. // Mock a request class with the $match uri
  229. $stub = $this->get_request_mock($match);
  230. $this->assertSame(FALSE, $route->matches($stub));
  231. }
  232. /**
  233. * Provider for test_matches_returns_array_of_parameters_on_successful_match
  234. *
  235. * @return array
  236. */
  237. public function provider_matches_returns_array_of_parameters_on_successful_match()
  238. {
  239. return [
  240. [
  241. '(<controller>(/<action>(/<id>)))',
  242. 'welcome/index',
  243. 'Welcome',
  244. 'index',
  245. ],
  246. ];
  247. }
  248. /**
  249. * Route::matches() should return an array of parameters when a match is made
  250. * An parameters that are not matched should not be present in the array of matches
  251. *
  252. * @dataProvider provider_matches_returns_array_of_parameters_on_successful_match
  253. *
  254. * @test
  255. * @covers Route::matches
  256. */
  257. public function test_matches_returns_array_of_parameters_on_successful_match($uri, $m, $c, $a)
  258. {
  259. $route = new Route($uri);
  260. // Mock a request class with the $m uri
  261. $request = $this->get_request_mock($m);
  262. $matches = $route->matches($request);
  263. $this->assertInternalType('array', $matches);
  264. $this->assertArrayHasKey('controller', $matches);
  265. $this->assertArrayHasKey('action', $matches);
  266. $this->assertArrayNotHasKey('id', $matches);
  267. // $this->assertSame(5, count($matches));
  268. $this->assertSame($c, $matches['controller']);
  269. $this->assertSame($a, $matches['action']);
  270. }
  271. /**
  272. * Provider for test_matches_returns_array_of_parameters_on_successful_match
  273. *
  274. * @return array
  275. */
  276. public function provider_defaults_are_used_if_params_arent_specified()
  277. {
  278. return [
  279. [
  280. '<controller>(/<action>(/<id>))',
  281. NULL,
  282. ['controller' => 'Welcome', 'action' => 'index'],
  283. 'Welcome',
  284. 'index',
  285. 'unit/test/1',
  286. [
  287. 'controller' => 'unit',
  288. 'action' => 'test',
  289. 'id' => '1'
  290. ],
  291. 'Welcome',
  292. ],
  293. [
  294. '(<controller>(/<action>(/<id>)))',
  295. NULL,
  296. ['controller' => 'welcome', 'action' => 'index'],
  297. 'Welcome',
  298. 'index',
  299. 'unit/test/1',
  300. [
  301. 'controller' => 'unit',
  302. 'action' => 'test',
  303. 'id' => '1'
  304. ],
  305. '',
  306. ],
  307. ];
  308. }
  309. /**
  310. * Defaults specified with defaults() should be used if their values aren't
  311. * present in the uri
  312. *
  313. * @dataProvider provider_defaults_are_used_if_params_arent_specified
  314. *
  315. * @test
  316. * @covers Route::matches
  317. */
  318. public function test_defaults_are_used_if_params_arent_specified($uri, $regex, $defaults, $c, $a, $test_uri, $test_uri_array, $default_uri)
  319. {
  320. $route = new Route($uri, $regex);
  321. $route->defaults($defaults);
  322. $this->assertSame($defaults, $route->defaults());
  323. // Mock a request class
  324. $request = $this->get_request_mock($default_uri);
  325. $matches = $route->matches($request);
  326. $this->assertInternalType('array', $matches);
  327. $this->assertArrayHasKey('controller', $matches);
  328. $this->assertArrayHasKey('action', $matches);
  329. $this->assertArrayNotHasKey('id', $matches);
  330. // $this->assertSame(4, count($matches));
  331. $this->assertSame($c, $matches['controller']);
  332. $this->assertSame($a, $matches['action']);
  333. $this->assertSame($test_uri, $route->uri($test_uri_array));
  334. $this->assertSame($default_uri, $route->uri());
  335. }
  336. /**
  337. * Provider for test_optional_groups_containing_specified_params
  338. *
  339. * @return array
  340. */
  341. public function provider_optional_groups_containing_specified_params()
  342. {
  343. return [
  344. /**
  345. * Specifying this should cause controller and action to show up
  346. * refs #4113
  347. */
  348. [
  349. '(<controller>(/<action>(/<id>)))',
  350. ['controller' => 'welcome', 'action' => 'index'],
  351. ['id' => '1'],
  352. 'welcome/index/1',
  353. ],
  354. [
  355. '<controller>(/<action>(/<id>))',
  356. ['controller' => 'welcome', 'action' => 'index'],
  357. ['action' => 'foo'],
  358. 'welcome/foo',
  359. ],
  360. [
  361. '<controller>(/<action>(/<id>))',
  362. ['controller' => 'welcome', 'action' => 'index'],
  363. ['action' => 'index'],
  364. 'welcome',
  365. ],
  366. /**
  367. * refs #4630
  368. */
  369. [
  370. 'api(/<version>)/const(/<id>)(/<custom>)',
  371. ['version' => 1],
  372. NULL,
  373. 'api/const',
  374. ],
  375. [
  376. 'api(/<version>)/const(/<id>)(/<custom>)',
  377. ['version' => 1],
  378. ['version' => 9],
  379. 'api/9/const',
  380. ],
  381. [
  382. 'api(/<version>)/const(/<id>)(/<custom>)',
  383. ['version' => 1],
  384. ['id' => 2],
  385. 'api/const/2',
  386. ],
  387. [
  388. 'api(/<version>)/const(/<id>)(/<custom>)',
  389. ['version' => 1],
  390. ['custom' => 'x'],
  391. 'api/const/x',
  392. ],
  393. [
  394. '(<controller>(/<action>(/<id>)(/<type>)))',
  395. ['controller' => 'test', 'action' => 'index', 'type' => 'html'],
  396. ['type' => 'json'],
  397. 'test/index/json',
  398. ],
  399. [
  400. '(<controller>(/<action>(/<id>)(/<type>)))',
  401. ['controller' => 'test', 'action' => 'index', 'type' => 'html'],
  402. ['id' => 123],
  403. 'test/index/123',
  404. ],
  405. [
  406. '(<controller>(/<action>(/<id>)(/<type>)))',
  407. ['controller' => 'test', 'action' => 'index', 'type' => 'html'],
  408. ['id' => 123, 'type' => 'html'],
  409. 'test/index/123',
  410. ],
  411. [
  412. '(<controller>(/<action>(/<id>)(/<type>)))',
  413. ['controller' => 'test', 'action' => 'index', 'type' => 'html'],
  414. ['id' => 123, 'type' => 'json'],
  415. 'test/index/123/json',
  416. ],
  417. ];
  418. }
  419. /**
  420. * When an optional param is specified, the optional params leading up to it
  421. * must be in the URI.
  422. *
  423. * @dataProvider provider_optional_groups_containing_specified_params
  424. *
  425. * @ticket 4113
  426. * @ticket 4630
  427. */
  428. public function test_optional_groups_containing_specified_params($uri, $defaults, $params, $expected)
  429. {
  430. $route = new Route($uri, NULL);
  431. $route->defaults($defaults);
  432. $this->assertSame($expected, $route->uri($params));
  433. }
  434. /**
  435. * Optional params should not be used if what is passed in is identical
  436. * to the default.
  437. *
  438. * refs #4116
  439. *
  440. * @test
  441. * @covers Route::uri
  442. */
  443. public function test_defaults_are_not_used_if_param_is_identical()
  444. {
  445. $route = new Route('(<controller>(/<action>(/<id>)))');
  446. $route->defaults([
  447. 'controller' => 'welcome',
  448. 'action' => 'index'
  449. ]);
  450. $this->assertSame('', $route->uri(['controller' => 'welcome']));
  451. $this->assertSame('welcome2', $route->uri(['controller' => 'welcome2']));
  452. }
  453. /**
  454. * Provider for test_required_parameters_are_needed
  455. *
  456. * @return array
  457. */
  458. public function provider_required_parameters_are_needed()
  459. {
  460. return [
  461. [
  462. 'admin(/<controller>(/<action>(/<id>)))',
  463. 'admin',
  464. 'admin/users/add',
  465. ],
  466. ];
  467. }
  468. /**
  469. * This tests that routes with required parameters will not match uris without them present
  470. *
  471. * @dataProvider provider_required_parameters_are_needed
  472. *
  473. * @test
  474. * @covers Route::matches
  475. */
  476. public function test_required_parameters_are_needed($uri, $matches_route1, $matches_route2)
  477. {
  478. $route = new Route($uri);
  479. // Mock a request class that will return empty uri
  480. $request = $this->get_request_mock('');
  481. $this->assertFalse($route->matches($request));
  482. // Mock a request class that will return route1
  483. $request = $this->get_request_mock($matches_route1);
  484. $matches = $route->matches($request);
  485. $this->assertInternalType('array', $matches);
  486. // Mock a request class that will return route2 uri
  487. $request = $this->get_request_mock($matches_route2);
  488. $matches = $route->matches($request);
  489. $this->assertInternalType('array', $matches);
  490. // $this->assertSame(5, count($matches));
  491. $this->assertArrayHasKey('controller', $matches);
  492. $this->assertArrayHasKey('action', $matches);
  493. }
  494. /**
  495. * Provider for test_required_parameters_are_needed
  496. *
  497. * @return array
  498. */
  499. public function provider_reverse_routing_returns_routes_uri_if_route_is_static()
  500. {
  501. return [
  502. [
  503. 'info/about_us',
  504. NULL,
  505. 'info/about_us',
  506. ['some' => 'random', 'params' => 'to confuse'],
  507. ],
  508. ];
  509. }
  510. /**
  511. * This tests the reverse routing returns the uri specified in the route
  512. * if it's a static route
  513. *
  514. * A static route is a route without any parameters
  515. *
  516. * @dataProvider provider_reverse_routing_returns_routes_uri_if_route_is_static
  517. *
  518. * @test
  519. * @covers Route::uri
  520. */
  521. public function test_reverse_routing_returns_routes_uri_if_route_is_static($uri, $regex, $target_uri, $uri_params)
  522. {
  523. $route = new Route($uri, $regex);
  524. $this->assertSame($target_uri, $route->uri($uri_params));
  525. }
  526. /**
  527. * Provider for test_uri_throws_exception_if_required_params_are_missing
  528. *
  529. * @return array
  530. */
  531. public function provider_uri_throws_exception_if_required_params_are_missing()
  532. {
  533. return [
  534. [
  535. '<controller>(/<action)',
  536. NULL,
  537. ['action' => 'awesome-action'],
  538. ],
  539. /**
  540. * Optional params are required when they lead to a specified param
  541. * refs #4113
  542. */
  543. [
  544. '(<controller>(/<action>))',
  545. NULL,
  546. ['action' => 'awesome-action'],
  547. ],
  548. ];
  549. }
  550. /**
  551. * When Route::uri is working on a uri that requires certain parameters to be present
  552. * (i.e. <controller> in '<controller(/<action)') then it should throw an exception
  553. * if the param was not provided
  554. *
  555. * @dataProvider provider_uri_throws_exception_if_required_params_are_missing
  556. *
  557. * @test
  558. * @covers Route::uri
  559. */
  560. public function test_uri_throws_exception_if_required_params_are_missing($uri, $regex, $uri_array)
  561. {
  562. $route = new Route($uri, $regex);
  563. $this->expectException('Kohana_Exception');
  564. $route->uri($uri_array);
  565. }
  566. /**
  567. * Provider for test_uri_fills_required_uri_segments_from_params
  568. *
  569. * @return array
  570. */
  571. public function provider_uri_fills_required_uri_segments_from_params()
  572. {
  573. return [
  574. [
  575. '<controller>/<action>(/<id>)',
  576. NULL,
  577. 'users/edit',
  578. [
  579. 'controller' => 'users',
  580. 'action' => 'edit',
  581. ],
  582. 'users/edit/god',
  583. [
  584. 'controller' => 'users',
  585. 'action' => 'edit',
  586. 'id' => 'god',
  587. ],
  588. ],
  589. ];
  590. }
  591. /**
  592. * The logic for replacing required segments is separate (but similar) to that for
  593. * replacing optional segments.
  594. *
  595. * This test asserts that Route::uri will replace required segments with provided
  596. * params
  597. *
  598. * @dataProvider provider_uri_fills_required_uri_segments_from_params
  599. *
  600. * @test
  601. * @covers Route::uri
  602. */
  603. public function test_uri_fills_required_uri_segments_from_params($uri, $regex, $uri_string1, $uri_array1, $uri_string2, $uri_array2)
  604. {
  605. $route = new Route($uri, $regex);
  606. $this->assertSame(
  607. $uri_string1,
  608. $route->uri($uri_array1)
  609. );
  610. $this->assertSame(
  611. $uri_string2,
  612. $route->uri($uri_array2)
  613. );
  614. }
  615. /**
  616. * Provides test data for test_composing_url_from_route()
  617. * @return array
  618. */
  619. public function provider_composing_url_from_route()
  620. {
  621. return [
  622. ['/'],
  623. ['/news/view/42', ['controller' => 'news', 'action' => 'view', 'id' => 42]],
  624. ['http://kohanaframework.org/news', ['controller' => 'news'], 'http']
  625. ];
  626. }
  627. /**
  628. * Tests Route::url()
  629. *
  630. * Checks the url composing from specific route via Route::url() shortcut
  631. *
  632. * @test
  633. * @dataProvider provider_composing_url_from_route
  634. * @param string $expected
  635. * @param array $params
  636. * @param boolean $protocol
  637. */
  638. public function test_composing_url_from_route($expected, $params = NULL, $protocol = NULL)
  639. {
  640. Route::set('foobar', '(<controller>(/<action>(/<id>)))')
  641. ->defaults([
  642. 'controller' => 'welcome',
  643. ]
  644. );
  645. $this->setEnvironment([
  646. '_SERVER' => ['HTTP_HOST' => 'kohanaframework.org'],
  647. 'Kohana::$base_url' => '/',
  648. 'Kohana::$index_file' => '',
  649. ]);
  650. $this->assertSame($expected, Route::url('foobar', $params, $protocol));
  651. }
  652. /**
  653. * Tests Route::compile()
  654. *
  655. * Makes sure that compile will use custom regex if specified
  656. *
  657. * @test
  658. * @covers Route::compile
  659. */
  660. public function test_compile_uses_custom_regex_if_specificed()
  661. {
  662. $compiled = Route::compile(
  663. '<controller>(/<action>(/<id>))',
  664. [
  665. 'controller' => '[a-z]+',
  666. 'id' => '\d+',
  667. ]
  668. );
  669. $this->assertSame('#^(?P<controller>[a-z]+)(?:/(?P<action>[^/.,;?\n]++)(?:/(?P<id>\d+))?)?$#uD', $compiled);
  670. }
  671. /**
  672. * Tests Route::is_external(), ensuring the host can return
  673. * whether internal or external host
  674. */
  675. public function test_is_external_route_from_host()
  676. {
  677. // Setup local route
  678. Route::set('internal', 'local/test/route')
  679. ->defaults([
  680. 'controller' => 'foo',
  681. 'action' => 'bar'
  682. ]
  683. );
  684. // Setup external route
  685. Route::set('external', 'local/test/route')
  686. ->defaults([
  687. 'controller' => 'foo',
  688. 'action' => 'bar',
  689. 'host' => 'http://kohanaframework.org'
  690. ]
  691. );
  692. // Test internal route
  693. $this->assertFalse(Route::get('internal')->is_external());
  694. // Test external route
  695. $this->assertTrue(Route::get('external')->is_external());
  696. }
  697. /**
  698. * Provider for test_external_route_includes_params_in_uri
  699. *
  700. * @return array
  701. */
  702. public function provider_external_route_includes_params_in_uri()
  703. {
  704. return [
  705. [
  706. '<controller>/<action>',
  707. [
  708. 'controller' => 'foo',
  709. 'action' => 'bar',
  710. 'host' => 'kohanaframework.org'
  711. ],
  712. 'http://kohanaframework.org/foo/bar'
  713. ],
  714. [
  715. '<controller>/<action>',
  716. [
  717. 'controller' => 'foo',
  718. 'action' => 'bar',
  719. 'host' => 'http://kohanaframework.org'
  720. ],
  721. 'http://kohanaframework.org/foo/bar'
  722. ],
  723. [
  724. 'foo/bar',
  725. [
  726. 'controller' => 'foo',
  727. 'host' => 'http://kohanaframework.org'
  728. ],
  729. 'http://kohanaframework.org/foo/bar'
  730. ],
  731. ];
  732. }
  733. /**
  734. * Tests the external route include route parameters
  735. *
  736. * @dataProvider provider_external_route_includes_params_in_uri
  737. */
  738. public function test_external_route_includes_params_in_uri($route, $defaults, $expected_uri)
  739. {
  740. Route::set('test', $route)
  741. ->defaults($defaults);
  742. $this->assertSame($expected_uri, Route::get('test')->uri());
  743. }
  744. /**
  745. * Provider for test_route_filter_modify_params
  746. *
  747. * @return array
  748. */
  749. public function provider_route_filter_modify_params()
  750. {
  751. return [
  752. [
  753. '<controller>/<action>',
  754. [
  755. 'controller' => 'Test',
  756. 'action' => 'same',
  757. ],
  758. ['Route_Holder', 'route_filter_modify_params_array'],
  759. 'test/different',
  760. [
  761. 'controller' => 'Test',
  762. 'action' => 'modified',
  763. ],
  764. ],
  765. [
  766. '<controller>/<action>',
  767. [
  768. 'controller' => 'test',
  769. 'action' => 'same',
  770. ],
  771. ['Route_Holder', 'route_filter_modify_params_false'],
  772. 'test/fail',
  773. FALSE,
  774. ],
  775. ];
  776. }
  777. /**
  778. * Tests that route filters can modify parameters
  779. *
  780. * @covers Route::filter
  781. * @dataProvider provider_route_filter_modify_params
  782. */
  783. public function test_route_filter_modify_params($route, $defaults, $filter, $uri, $expected_params)
  784. {
  785. $route = new Route($route);
  786. // Mock a request class
  787. $request = $this->get_request_mock($uri);
  788. $params = $route->defaults($defaults)->filter($filter)->matches($request);
  789. $this->assertSame($expected_params, $params);
  790. }
  791. /**
  792. * Provides test data for test_route_uri_encode_parameters
  793. *
  794. * @return array
  795. */
  796. public function provider_route_uri_encode_parameters()
  797. {
  798. return [
  799. [
  800. 'article',
  801. 'blog/article/<article_name>',
  802. [
  803. 'controller' => 'home',
  804. 'action' => 'index'
  805. ],
  806. 'article_name',
  807. 'Article name with special chars \\ ##',
  808. 'blog/article/Article%20name%20with%20special%20chars%20\\%20%23%23'
  809. ]
  810. ];
  811. }
  812. /**
  813. * http://dev.kohanaframework.org/issues/4079
  814. *
  815. * @test
  816. * @covers Route::get
  817. * @ticket 4079
  818. * @dataProvider provider_route_uri_encode_parameters
  819. */
  820. public function test_route_uri_encode_parameters($name, $uri_callback, $defaults, $uri_key, $uri_value, $expected)
  821. {
  822. Route::set($name, $uri_callback)->defaults($defaults);
  823. $get_route_uri = Route::get($name)->uri([$uri_key => $uri_value]);
  824. $this->assertSame($expected, $get_route_uri);
  825. }
  826. /**
  827. * Get a mock of the Request class with a mocked `uri` method
  828. *
  829. * We are also mocking `method` method as it conflicts with newer PHPUnit,
  830. * in order to avoid the fatal errors
  831. *
  832. * @param string $uri
  833. * @return type
  834. */
  835. public function get_request_mock($uri)
  836. {
  837. // Mock a request class with the $uri uri
  838. $request = $this->createMock('Request', ['uri', 'method'], [$uri]);
  839. // mock `uri` method
  840. $request->expects($this->any())
  841. ->method('uri')
  842. // Request::uri() called by Route::matches() in the tests will return $uri
  843. ->will($this->returnValue($uri));
  844. // also mock `method` method
  845. $request->expects($this->any())
  846. ->method('method')
  847. ->withAnyParameters();
  848. return $request;
  849. }
  850. }