RouteTest.php 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970
  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(): void
  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(): void
  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->getMockForAbstractClass('Route',
  149. [],
  150. '',
  151. true,
  152. true,
  153. true,
  154. ['_compile']
  155. );
  156. $route
  157. ->expects($this->never())
  158. ->method('_compile');
  159. $route->__construct(NULL,NULL);
  160. $this->assertAttributeSame('', '_uri', $route);
  161. $this->assertAttributeSame([], '_regex', $route);
  162. $this->assertAttributeSame(['action' => 'index', 'host' => FALSE], '_defaults', $route);
  163. $this->assertAttributeSame(NULL, '_route_regex', $route);
  164. }
  165. /**
  166. * Provider for test_constructor_only_changes_custom_regex_if_passed
  167. *
  168. * @return array
  169. */
  170. public function provider_constructor_only_changes_custom_regex_if_passed()
  171. {
  172. return [
  173. ['<controller>/<action>', '<controller>/<action>'],
  174. ];
  175. }
  176. /**
  177. * The constructor should only use custom regex if passed a non-empty array
  178. *
  179. * Technically we can't "test" this as the default regex is an empty array, this
  180. * is purely for improving test coverage
  181. *
  182. * @dataProvider provider_constructor_only_changes_custom_regex_if_passed
  183. *
  184. * @test
  185. * @covers Route::__construct
  186. */
  187. public function test_constructor_only_changes_custom_regex_if_passed($uri, $uri2)
  188. {
  189. $route = new Route($uri, []);
  190. $this->assertAttributeSame([], '_regex', $route);
  191. $route = new Route($uri2, NULL);
  192. $this->assertAttributeSame([], '_regex', $route);
  193. }
  194. /**
  195. * When we pass custom regex to the route's constructor it should it
  196. * in leu of the default. This does not apply to callback/lambda routes
  197. *
  198. * @test
  199. * @covers Route::__construct
  200. * @covers Route::compile
  201. */
  202. public function test_route_uses_custom_regex_passed_to_constructor()
  203. {
  204. $regex = ['id' => '[0-9]{1,2}'];
  205. $route = new Route('<controller>(/<action>(/<id>))', $regex);
  206. $this->assertAttributeSame($regex, '_regex', $route);
  207. $this->assertAttributeContains(
  208. $regex['id'],
  209. '_route_regex',
  210. $route
  211. );
  212. }
  213. /**
  214. * Provider for test_matches_returns_false_on_failure
  215. *
  216. * @return array
  217. */
  218. public function provider_matches_returns_false_on_failure()
  219. {
  220. return [
  221. ['projects/(<project_id>/(<controller>(/<action>(/<id>))))', 'apple/pie'],
  222. ];
  223. }
  224. /**
  225. * Route::matches() should return false if the route doesn't match against a uri
  226. *
  227. * @dataProvider provider_matches_returns_false_on_failure
  228. *
  229. * @test
  230. * @covers Route::matches
  231. */
  232. public function test_matches_returns_false_on_failure($uri, $match)
  233. {
  234. $route = new Route($uri);
  235. // Mock a request class with the $match uri
  236. $stub = $this->get_request_mock($match);
  237. $this->assertSame(FALSE, $route->matches($stub));
  238. }
  239. /**
  240. * Provider for test_matches_returns_array_of_parameters_on_successful_match
  241. *
  242. * @return array
  243. */
  244. public function provider_matches_returns_array_of_parameters_on_successful_match()
  245. {
  246. return [
  247. [
  248. '(<controller>(/<action>(/<id>)))',
  249. 'welcome/index',
  250. 'Welcome',
  251. 'index',
  252. ],
  253. ];
  254. }
  255. /**
  256. * Route::matches() should return an array of parameters when a match is made
  257. * An parameters that are not matched should not be present in the array of matches
  258. *
  259. * @dataProvider provider_matches_returns_array_of_parameters_on_successful_match
  260. *
  261. * @test
  262. * @covers Route::matches
  263. */
  264. public function test_matches_returns_array_of_parameters_on_successful_match($uri, $m, $c, $a)
  265. {
  266. $route = new Route($uri);
  267. // Mock a request class with the $m uri
  268. $request = $this->get_request_mock($m);
  269. $matches = $route->matches($request);
  270. $this->assertInternalType('array', $matches);
  271. $this->assertArrayHasKey('controller', $matches);
  272. $this->assertArrayHasKey('action', $matches);
  273. $this->assertArrayNotHasKey('id', $matches);
  274. // $this->assertSame(5, count($matches));
  275. $this->assertSame($c, $matches['controller']);
  276. $this->assertSame($a, $matches['action']);
  277. }
  278. /**
  279. * Provider for test_matches_returns_array_of_parameters_on_successful_match
  280. *
  281. * @return array
  282. */
  283. public function provider_defaults_are_used_if_params_arent_specified()
  284. {
  285. return [
  286. [
  287. '<controller>(/<action>(/<id>))',
  288. NULL,
  289. ['controller' => 'Welcome', 'action' => 'index'],
  290. 'Welcome',
  291. 'index',
  292. 'unit/test/1',
  293. [
  294. 'controller' => 'unit',
  295. 'action' => 'test',
  296. 'id' => '1'
  297. ],
  298. 'Welcome',
  299. ],
  300. [
  301. '(<controller>(/<action>(/<id>)))',
  302. NULL,
  303. ['controller' => 'welcome', 'action' => 'index'],
  304. 'Welcome',
  305. 'index',
  306. 'unit/test/1',
  307. [
  308. 'controller' => 'unit',
  309. 'action' => 'test',
  310. 'id' => '1'
  311. ],
  312. '',
  313. ],
  314. ];
  315. }
  316. /**
  317. * Defaults specified with defaults() should be used if their values aren't
  318. * present in the uri
  319. *
  320. * @dataProvider provider_defaults_are_used_if_params_arent_specified
  321. *
  322. * @test
  323. * @covers Route::matches
  324. */
  325. public function test_defaults_are_used_if_params_arent_specified($uri, $regex, $defaults, $c, $a, $test_uri, $test_uri_array, $default_uri)
  326. {
  327. $route = new Route($uri, $regex);
  328. $route->defaults($defaults);
  329. $this->assertSame($defaults, $route->defaults());
  330. // Mock a request class
  331. $request = $this->get_request_mock($default_uri);
  332. $matches = $route->matches($request);
  333. $this->assertInternalType('array', $matches);
  334. $this->assertArrayHasKey('controller', $matches);
  335. $this->assertArrayHasKey('action', $matches);
  336. $this->assertArrayNotHasKey('id', $matches);
  337. // $this->assertSame(4, count($matches));
  338. $this->assertSame($c, $matches['controller']);
  339. $this->assertSame($a, $matches['action']);
  340. $this->assertSame($test_uri, $route->uri($test_uri_array));
  341. $this->assertSame($default_uri, $route->uri());
  342. }
  343. /**
  344. * Provider for test_optional_groups_containing_specified_params
  345. *
  346. * @return array
  347. */
  348. public function provider_optional_groups_containing_specified_params()
  349. {
  350. return [
  351. /**
  352. * Specifying this should cause controller and action to show up
  353. * refs #4113
  354. */
  355. [
  356. '(<controller>(/<action>(/<id>)))',
  357. ['controller' => 'welcome', 'action' => 'index'],
  358. ['id' => '1'],
  359. 'welcome/index/1',
  360. ],
  361. [
  362. '<controller>(/<action>(/<id>))',
  363. ['controller' => 'welcome', 'action' => 'index'],
  364. ['action' => 'foo'],
  365. 'welcome/foo',
  366. ],
  367. [
  368. '<controller>(/<action>(/<id>))',
  369. ['controller' => 'welcome', 'action' => 'index'],
  370. ['action' => 'index'],
  371. 'welcome',
  372. ],
  373. /**
  374. * refs #4630
  375. */
  376. [
  377. 'api(/<version>)/const(/<id>)(/<custom>)',
  378. ['version' => 1],
  379. NULL,
  380. 'api/const',
  381. ],
  382. [
  383. 'api(/<version>)/const(/<id>)(/<custom>)',
  384. ['version' => 1],
  385. ['version' => 9],
  386. 'api/9/const',
  387. ],
  388. [
  389. 'api(/<version>)/const(/<id>)(/<custom>)',
  390. ['version' => 1],
  391. ['id' => 2],
  392. 'api/const/2',
  393. ],
  394. [
  395. 'api(/<version>)/const(/<id>)(/<custom>)',
  396. ['version' => 1],
  397. ['custom' => 'x'],
  398. 'api/const/x',
  399. ],
  400. [
  401. '(<controller>(/<action>(/<id>)(/<type>)))',
  402. ['controller' => 'test', 'action' => 'index', 'type' => 'html'],
  403. ['type' => 'json'],
  404. 'test/index/json',
  405. ],
  406. [
  407. '(<controller>(/<action>(/<id>)(/<type>)))',
  408. ['controller' => 'test', 'action' => 'index', 'type' => 'html'],
  409. ['id' => 123],
  410. 'test/index/123',
  411. ],
  412. [
  413. '(<controller>(/<action>(/<id>)(/<type>)))',
  414. ['controller' => 'test', 'action' => 'index', 'type' => 'html'],
  415. ['id' => 123, 'type' => 'html'],
  416. 'test/index/123',
  417. ],
  418. [
  419. '(<controller>(/<action>(/<id>)(/<type>)))',
  420. ['controller' => 'test', 'action' => 'index', 'type' => 'html'],
  421. ['id' => 123, 'type' => 'json'],
  422. 'test/index/123/json',
  423. ],
  424. ];
  425. }
  426. /**
  427. * When an optional param is specified, the optional params leading up to it
  428. * must be in the URI.
  429. *
  430. * @dataProvider provider_optional_groups_containing_specified_params
  431. *
  432. * @ticket 4113
  433. * @ticket 4630
  434. */
  435. public function test_optional_groups_containing_specified_params($uri, $defaults, $params, $expected)
  436. {
  437. $route = new Route($uri, NULL);
  438. $route->defaults($defaults);
  439. $this->assertSame($expected, $route->uri($params));
  440. }
  441. /**
  442. * Optional params should not be used if what is passed in is identical
  443. * to the default.
  444. *
  445. * refs #4116
  446. *
  447. * @test
  448. * @covers Route::uri
  449. */
  450. public function test_defaults_are_not_used_if_param_is_identical()
  451. {
  452. $route = new Route('(<controller>(/<action>(/<id>)))');
  453. $route->defaults([
  454. 'controller' => 'welcome',
  455. 'action' => 'index'
  456. ]);
  457. $this->assertSame('', $route->uri(['controller' => 'welcome']));
  458. $this->assertSame('welcome2', $route->uri(['controller' => 'welcome2']));
  459. }
  460. /**
  461. * Provider for test_required_parameters_are_needed
  462. *
  463. * @return array
  464. */
  465. public function provider_required_parameters_are_needed()
  466. {
  467. return [
  468. [
  469. 'admin(/<controller>(/<action>(/<id>)))',
  470. 'admin',
  471. 'admin/users/add',
  472. ],
  473. ];
  474. }
  475. /**
  476. * This tests that routes with required parameters will not match uris without them present
  477. *
  478. * @dataProvider provider_required_parameters_are_needed
  479. *
  480. * @test
  481. * @covers Route::matches
  482. */
  483. public function test_required_parameters_are_needed($uri, $matches_route1, $matches_route2)
  484. {
  485. $route = new Route($uri);
  486. // Mock a request class that will return empty uri
  487. $request = $this->get_request_mock('');
  488. $this->assertFalse($route->matches($request));
  489. // Mock a request class that will return route1
  490. $request = $this->get_request_mock($matches_route1);
  491. $matches = $route->matches($request);
  492. $this->assertInternalType('array', $matches);
  493. // Mock a request class that will return route2 uri
  494. $request = $this->get_request_mock($matches_route2);
  495. $matches = $route->matches($request);
  496. $this->assertInternalType('array', $matches);
  497. // $this->assertSame(5, count($matches));
  498. $this->assertArrayHasKey('controller', $matches);
  499. $this->assertArrayHasKey('action', $matches);
  500. }
  501. /**
  502. * Provider for test_required_parameters_are_needed
  503. *
  504. * @return array
  505. */
  506. public function provider_reverse_routing_returns_routes_uri_if_route_is_static()
  507. {
  508. return [
  509. [
  510. 'info/about_us',
  511. NULL,
  512. 'info/about_us',
  513. ['some' => 'random', 'params' => 'to confuse'],
  514. ],
  515. ];
  516. }
  517. /**
  518. * This tests the reverse routing returns the uri specified in the route
  519. * if it's a static route
  520. *
  521. * A static route is a route without any parameters
  522. *
  523. * @dataProvider provider_reverse_routing_returns_routes_uri_if_route_is_static
  524. *
  525. * @test
  526. * @covers Route::uri
  527. */
  528. public function test_reverse_routing_returns_routes_uri_if_route_is_static($uri, $regex, $target_uri, $uri_params)
  529. {
  530. $route = new Route($uri, $regex);
  531. $this->assertSame($target_uri, $route->uri($uri_params));
  532. }
  533. /**
  534. * Provider for test_uri_throws_exception_if_required_params_are_missing
  535. *
  536. * @return array
  537. */
  538. public function provider_uri_throws_exception_if_required_params_are_missing()
  539. {
  540. return [
  541. [
  542. '<controller>(/<action)',
  543. NULL,
  544. ['action' => 'awesome-action'],
  545. ],
  546. /**
  547. * Optional params are required when they lead to a specified param
  548. * refs #4113
  549. */
  550. [
  551. '(<controller>(/<action>))',
  552. NULL,
  553. ['action' => 'awesome-action'],
  554. ],
  555. ];
  556. }
  557. /**
  558. * When Route::uri is working on a uri that requires certain parameters to be present
  559. * (i.e. <controller> in '<controller(/<action)') then it should throw an exception
  560. * if the param was not provided
  561. *
  562. * @dataProvider provider_uri_throws_exception_if_required_params_are_missing
  563. *
  564. * @test
  565. * @covers Route::uri
  566. */
  567. public function test_uri_throws_exception_if_required_params_are_missing($uri, $regex, $uri_array)
  568. {
  569. $route = new Route($uri, $regex);
  570. $this->expectException('Kohana_Exception');
  571. $route->uri($uri_array);
  572. }
  573. /**
  574. * Provider for test_uri_fills_required_uri_segments_from_params
  575. *
  576. * @return array
  577. */
  578. public function provider_uri_fills_required_uri_segments_from_params()
  579. {
  580. return [
  581. [
  582. '<controller>/<action>(/<id>)',
  583. NULL,
  584. 'users/edit',
  585. [
  586. 'controller' => 'users',
  587. 'action' => 'edit',
  588. ],
  589. 'users/edit/god',
  590. [
  591. 'controller' => 'users',
  592. 'action' => 'edit',
  593. 'id' => 'god',
  594. ],
  595. ],
  596. ];
  597. }
  598. /**
  599. * The logic for replacing required segments is separate (but similar) to that for
  600. * replacing optional segments.
  601. *
  602. * This test asserts that Route::uri will replace required segments with provided
  603. * params
  604. *
  605. * @dataProvider provider_uri_fills_required_uri_segments_from_params
  606. *
  607. * @test
  608. * @covers Route::uri
  609. */
  610. public function test_uri_fills_required_uri_segments_from_params($uri, $regex, $uri_string1, $uri_array1, $uri_string2, $uri_array2)
  611. {
  612. $route = new Route($uri, $regex);
  613. $this->assertSame(
  614. $uri_string1,
  615. $route->uri($uri_array1)
  616. );
  617. $this->assertSame(
  618. $uri_string2,
  619. $route->uri($uri_array2)
  620. );
  621. }
  622. /**
  623. * Provides test data for test_composing_url_from_route()
  624. * @return array
  625. */
  626. public function provider_composing_url_from_route()
  627. {
  628. return [
  629. ['/'],
  630. ['/news/view/42', ['controller' => 'news', 'action' => 'view', 'id' => 42]],
  631. ['http://kohanaframework.org/news', ['controller' => 'news'], 'http']
  632. ];
  633. }
  634. /**
  635. * Tests Route::url()
  636. *
  637. * Checks the url composing from specific route via Route::url() shortcut
  638. *
  639. * @test
  640. * @dataProvider provider_composing_url_from_route
  641. * @param string $expected
  642. * @param array $params
  643. * @param boolean $protocol
  644. */
  645. public function test_composing_url_from_route($expected, $params = NULL, $protocol = NULL)
  646. {
  647. Route::set('foobar', '(<controller>(/<action>(/<id>)))')
  648. ->defaults([
  649. 'controller' => 'welcome',
  650. ]
  651. );
  652. $this->setEnvironment([
  653. '_SERVER' => ['HTTP_HOST' => 'kohanaframework.org'],
  654. 'Kohana::$base_url' => '/',
  655. 'Kohana::$index_file' => '',
  656. ]);
  657. $this->assertSame($expected, Route::url('foobar', $params, $protocol));
  658. }
  659. /**
  660. * Tests Route::compile()
  661. *
  662. * Makes sure that compile will use custom regex if specified
  663. *
  664. * @test
  665. * @covers Route::compile
  666. */
  667. public function test_compile_uses_custom_regex_if_specificed()
  668. {
  669. $compiled = Route::compile(
  670. '<controller>(/<action>(/<id>))',
  671. [
  672. 'controller' => '[a-z]+',
  673. 'id' => '\d+',
  674. ]
  675. );
  676. $this->assertSame('#^(?P<controller>[a-z]+)(?:/(?P<action>[^/.,;?\n]++)(?:/(?P<id>\d+))?)?$#uD', $compiled);
  677. }
  678. /**
  679. * Tests Route::is_external(), ensuring the host can return
  680. * whether internal or external host
  681. */
  682. public function test_is_external_route_from_host()
  683. {
  684. // Setup local route
  685. Route::set('internal', 'local/test/route')
  686. ->defaults([
  687. 'controller' => 'foo',
  688. 'action' => 'bar'
  689. ]
  690. );
  691. // Setup external route
  692. Route::set('external', 'local/test/route')
  693. ->defaults([
  694. 'controller' => 'foo',
  695. 'action' => 'bar',
  696. 'host' => 'http://kohanaframework.org'
  697. ]
  698. );
  699. // Test internal route
  700. $this->assertFalse(Route::get('internal')->is_external());
  701. // Test external route
  702. $this->assertTrue(Route::get('external')->is_external());
  703. }
  704. /**
  705. * Provider for test_external_route_includes_params_in_uri
  706. *
  707. * @return array
  708. */
  709. public function provider_external_route_includes_params_in_uri()
  710. {
  711. return [
  712. [
  713. '<controller>/<action>',
  714. [
  715. 'controller' => 'foo',
  716. 'action' => 'bar',
  717. 'host' => 'kohanaframework.org'
  718. ],
  719. 'http://kohanaframework.org/foo/bar'
  720. ],
  721. [
  722. '<controller>/<action>',
  723. [
  724. 'controller' => 'foo',
  725. 'action' => 'bar',
  726. 'host' => 'http://kohanaframework.org'
  727. ],
  728. 'http://kohanaframework.org/foo/bar'
  729. ],
  730. [
  731. 'foo/bar',
  732. [
  733. 'controller' => 'foo',
  734. 'host' => 'http://kohanaframework.org'
  735. ],
  736. 'http://kohanaframework.org/foo/bar'
  737. ],
  738. ];
  739. }
  740. /**
  741. * Tests the external route include route parameters
  742. *
  743. * @dataProvider provider_external_route_includes_params_in_uri
  744. */
  745. public function test_external_route_includes_params_in_uri($route, $defaults, $expected_uri)
  746. {
  747. Route::set('test', $route)
  748. ->defaults($defaults);
  749. $this->assertSame($expected_uri, Route::get('test')->uri());
  750. }
  751. /**
  752. * Provider for test_route_filter_modify_params
  753. *
  754. * @return array
  755. */
  756. public function provider_route_filter_modify_params()
  757. {
  758. return [
  759. [
  760. '<controller>/<action>',
  761. [
  762. 'controller' => 'Test',
  763. 'action' => 'same',
  764. ],
  765. ['Route_Holder', 'route_filter_modify_params_array'],
  766. 'test/different',
  767. [
  768. 'controller' => 'Test',
  769. 'action' => 'modified',
  770. ],
  771. ],
  772. [
  773. '<controller>/<action>',
  774. [
  775. 'controller' => 'test',
  776. 'action' => 'same',
  777. ],
  778. ['Route_Holder', 'route_filter_modify_params_false'],
  779. 'test/fail',
  780. FALSE,
  781. ],
  782. ];
  783. }
  784. /**
  785. * Tests that route filters can modify parameters
  786. *
  787. * @covers Route::filter
  788. * @dataProvider provider_route_filter_modify_params
  789. */
  790. public function test_route_filter_modify_params($route, $defaults, $filter, $uri, $expected_params)
  791. {
  792. $route = new Route($route);
  793. // Mock a request class
  794. $request = $this->get_request_mock($uri);
  795. $params = $route->defaults($defaults)->filter($filter)->matches($request);
  796. $this->assertSame($expected_params, $params);
  797. }
  798. /**
  799. * Provides test data for test_route_uri_encode_parameters
  800. *
  801. * @return array
  802. */
  803. public function provider_route_uri_encode_parameters()
  804. {
  805. return [
  806. [
  807. 'article',
  808. 'blog/article/<article_name>',
  809. [
  810. 'controller' => 'home',
  811. 'action' => 'index'
  812. ],
  813. 'article_name',
  814. 'Article name with special chars \\ ##',
  815. 'blog/article/Article%20name%20with%20special%20chars%20\\%20%23%23'
  816. ]
  817. ];
  818. }
  819. /**
  820. * http://dev.kohanaframework.org/issues/4079
  821. *
  822. * @test
  823. * @covers Route::get
  824. * @ticket 4079
  825. * @dataProvider provider_route_uri_encode_parameters
  826. */
  827. public function test_route_uri_encode_parameters($name, $uri_callback, $defaults, $uri_key, $uri_value, $expected)
  828. {
  829. Route::set($name, $uri_callback)->defaults($defaults);
  830. $get_route_uri = Route::get($name)->uri([$uri_key => $uri_value]);
  831. $this->assertSame($expected, $get_route_uri);
  832. }
  833. /**
  834. * Get a mock of the Request class with a mocked `uri` method
  835. *
  836. * We are also mocking `method` method as it conflicts with newer PHPUnit,
  837. * in order to avoid the fatal errors
  838. *
  839. * @param string $uri
  840. * @return type
  841. */
  842. public function get_request_mock($uri)
  843. {
  844. // Mock a request class with the $uri uri
  845. $request = $this->getMockForAbstractClass('Request',
  846. [$uri],
  847. '',
  848. true,
  849. true,
  850. true,
  851. ['uri', 'method']
  852. );
  853. // mock `uri` method
  854. $request->expects($this->any())
  855. ->method('uri')
  856. // Request::uri() called by Route::matches() in the tests will return $uri
  857. ->will($this->returnValue($uri));
  858. // also mock `method` method
  859. $request->expects($this->any())
  860. ->method('method')
  861. ->withAnyParameters();
  862. return $request;
  863. }
  864. }