1<?php
2/**
3 * CakeRequest Test case file.
4 *
5 * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
6 * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
7 *
8 * Licensed under The MIT License
9 * For full copyright and license information, please see the LICENSE.txt
10 * Redistributions of files must retain the above copyright notice.
11 *
12 * @copyright     Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
13 * @link          https://cakephp.org CakePHP(tm) Project
14 * @package       Cake.Test.Case.Routing.Route
15 * @since         CakePHP(tm) v 2.0
16 * @license       https://opensource.org/licenses/mit-license.php MIT License
17 */
18
19App::uses('CakeRoute', 'Routing/Route');
20App::uses('Router', 'Routing');
21
22/**
23 * Test case for CakeRoute
24 *
25 * @package       Cake.Test.Case.Routing.Route
26 */
27class CakeRouteTest extends CakeTestCase {
28
29/**
30 * setUp method
31 *
32 * @return void
33 */
34	public function setUp() {
35		parent::setUp();
36		Configure::write('Routing', array('admin' => null, 'prefixes' => array()));
37	}
38
39/**
40 * Test the construction of a CakeRoute
41 *
42 * @return void
43 */
44	public function testConstruction() {
45		$route = new CakeRoute('/:controller/:action/:id', array(), array('id' => '[0-9]+'));
46
47		$this->assertEquals('/:controller/:action/:id', $route->template);
48		$this->assertEquals(array(), $route->defaults);
49		$this->assertEquals(array('id' => '[0-9]+'), $route->options);
50		$this->assertFalse($route->compiled());
51	}
52
53/**
54 * test Route compiling.
55 *
56 * @return void
57 */
58	public function testBasicRouteCompiling() {
59		$route = new CakeRoute('/', array('controller' => 'pages', 'action' => 'display', 'home'));
60		$result = $route->compile();
61		$expected = '#^/*$#';
62		$this->assertEquals($expected, $result);
63		$this->assertEquals(array(), $route->keys);
64
65		$route = new CakeRoute('/:controller/:action', array('controller' => 'posts'));
66		$result = $route->compile();
67
68		$this->assertRegExp($result, '/posts/edit');
69		$this->assertRegExp($result, '/posts/super_delete');
70		$this->assertNotRegExp($result, '/posts');
71		$this->assertNotRegExp($result, '/posts/super_delete/1');
72
73		$route = new CakeRoute('/posts/foo:id', array('controller' => 'posts', 'action' => 'view'));
74		$result = $route->compile();
75
76		$this->assertRegExp($result, '/posts/foo:1');
77		$this->assertRegExp($result, '/posts/foo:param');
78		$this->assertNotRegExp($result, '/posts');
79		$this->assertNotRegExp($result, '/posts/');
80
81		$this->assertEquals(array('id'), $route->keys);
82
83		$route = new CakeRoute('/:plugin/:controller/:action/*', array('plugin' => 'test_plugin', 'action' => 'index'));
84		$result = $route->compile();
85		$this->assertRegExp($result, '/test_plugin/posts/index');
86		$this->assertRegExp($result, '/test_plugin/posts/edit/5');
87		$this->assertRegExp($result, '/test_plugin/posts/edit/5/name:value/nick:name');
88	}
89
90/**
91 * test that route parameters that overlap don't cause errors.
92 *
93 * @return void
94 */
95	public function testRouteParameterOverlap() {
96		$route = new CakeRoute('/invoices/add/:idd/:id', array('controller' => 'invoices', 'action' => 'add'));
97		$result = $route->compile();
98		$this->assertRegExp($result, '/invoices/add/1/3');
99
100		$route = new CakeRoute('/invoices/add/:id/:idd', array('controller' => 'invoices', 'action' => 'add'));
101		$result = $route->compile();
102		$this->assertRegExp($result, '/invoices/add/1/3');
103	}
104
105/**
106 * test compiling routes with keys that have patterns
107 *
108 * @return void
109 */
110	public function testRouteCompilingWithParamPatterns() {
111		$route = new CakeRoute(
112			'/:controller/:action/:id',
113			array(),
114			array('id' => Router::ID)
115		);
116		$result = $route->compile();
117		$this->assertRegExp($result, '/posts/edit/1');
118		$this->assertRegExp($result, '/posts/view/518098');
119		$this->assertNotRegExp($result, '/posts/edit/name-of-post');
120		$this->assertNotRegExp($result, '/posts/edit/4/other:param');
121		$this->assertEquals(array('id', 'controller', 'action'), $route->keys);
122
123		$route = new CakeRoute(
124			'/:lang/:controller/:action/:id',
125			array('controller' => 'testing4'),
126			array('id' => Router::ID, 'lang' => '[a-z]{3}')
127		);
128		$result = $route->compile();
129		$this->assertRegExp($result, '/eng/posts/edit/1');
130		$this->assertRegExp($result, '/cze/articles/view/1');
131		$this->assertNotRegExp($result, '/language/articles/view/2');
132		$this->assertNotRegExp($result, '/eng/articles/view/name-of-article');
133		$this->assertEquals(array('lang', 'id', 'controller', 'action'), $route->keys);
134
135		foreach (array(':', '@', ';', '$', '-') as $delim) {
136			$route = new CakeRoute('/posts/:id' . $delim . ':title');
137			$result = $route->compile();
138
139			$this->assertRegExp($result, '/posts/1' . $delim . 'name-of-article');
140			$this->assertRegExp($result, '/posts/13244' . $delim . 'name-of_Article[]');
141			$this->assertNotRegExp($result, '/posts/11!nameofarticle');
142			$this->assertNotRegExp($result, '/posts/11');
143
144			$this->assertEquals(array('title', 'id'), $route->keys);
145		}
146
147		$route = new CakeRoute(
148			'/posts/:id::title/:year',
149			array('controller' => 'posts', 'action' => 'view'),
150			array('id' => Router::ID, 'year' => Router::YEAR, 'title' => '[a-z-_]+')
151		);
152		$result = $route->compile();
153		$this->assertRegExp($result, '/posts/1:name-of-article/2009/');
154		$this->assertRegExp($result, '/posts/13244:name-of-article/1999');
155		$this->assertNotRegExp($result, '/posts/hey_now:nameofarticle');
156		$this->assertNotRegExp($result, '/posts/:nameofarticle/2009');
157		$this->assertNotRegExp($result, '/posts/:nameofarticle/01');
158		$this->assertEquals(array('year', 'title', 'id'), $route->keys);
159
160		$route = new CakeRoute(
161			'/posts/:url_title-(uuid::id)',
162			array('controller' => 'posts', 'action' => 'view'),
163			array('pass' => array('id', 'url_title'), 'id' => Router::ID)
164		);
165		$result = $route->compile();
166		$this->assertRegExp($result, '/posts/some_title_for_article-(uuid:12534)/');
167		$this->assertRegExp($result, '/posts/some_title_for_article-(uuid:12534)');
168		$this->assertNotRegExp($result, '/posts/');
169		$this->assertNotRegExp($result, '/posts/nameofarticle');
170		$this->assertNotRegExp($result, '/posts/nameofarticle-12347');
171		$this->assertEquals(array('url_title', 'id'), $route->keys);
172	}
173
174/**
175 * test more complex route compiling & parsing with mid route greedy stars
176 * and optional routing parameters
177 *
178 * @return void
179 */
180	public function testComplexRouteCompilingAndParsing() {
181		$route = new CakeRoute(
182			'/posts/:month/:day/:year/*',
183			array('controller' => 'posts', 'action' => 'view'),
184			array('year' => Router::YEAR, 'month' => Router::MONTH, 'day' => Router::DAY)
185		);
186		$result = $route->compile();
187		$this->assertRegExp($result, '/posts/08/01/2007/title-of-post');
188		$result = $route->parse('/posts/08/01/2007/title-of-post');
189
190		$this->assertEquals(7, count($result));
191		$this->assertEquals('posts', $result['controller']);
192		$this->assertEquals('view', $result['action']);
193		$this->assertEquals('2007', $result['year']);
194		$this->assertEquals('08', $result['month']);
195		$this->assertEquals('01', $result['day']);
196		$this->assertEquals('title-of-post', $result['pass'][0]);
197
198		$route = new CakeRoute(
199			"/:extra/page/:slug/*",
200			array('controller' => 'pages', 'action' => 'view', 'extra' => null),
201			array("extra" => '[a-z1-9_]*', "slug" => '[a-z1-9_]+', "action" => 'view')
202		);
203		$result = $route->compile();
204
205		$this->assertRegExp($result, '/some_extra/page/this_is_the_slug');
206		$this->assertRegExp($result, '/page/this_is_the_slug');
207		$this->assertEquals(array('slug', 'extra'), $route->keys);
208		$this->assertEquals(array('extra' => '[a-z1-9_]*', 'slug' => '[a-z1-9_]+', 'action' => 'view'), $route->options);
209		$expected = array(
210			'controller' => 'pages',
211			'action' => 'view'
212		);
213		$this->assertEquals($expected, $route->defaults);
214
215		$route = new CakeRoute(
216			'/:controller/:action/*',
217			array('project' => false),
218			array(
219				'controller' => 'source|wiki|commits|tickets|comments|view',
220				'action' => 'branches|history|branch|logs|view|start|add|edit|modify'
221			)
222		);
223		$this->assertFalse($route->parse('/chaw_test/wiki'));
224
225		$result = $route->compile();
226		$this->assertNotRegExp($result, '/some_project/source');
227		$this->assertRegExp($result, '/source/view');
228		$this->assertRegExp($result, '/source/view/other/params');
229		$this->assertNotRegExp($result, '/chaw_test/wiki');
230		$this->assertNotRegExp($result, '/source/wierd_action');
231	}
232
233/**
234 * test that routes match their pattern.
235 *
236 * @return void
237 */
238	public function testMatchBasic() {
239		$route = new CakeRoute('/:controller/:action/:id', array('plugin' => null));
240		$result = $route->match(array('controller' => 'posts', 'action' => 'view', 'plugin' => null));
241		$this->assertFalse($result);
242
243		$result = $route->match(array('plugin' => null, 'controller' => 'posts', 'action' => 'view', 0));
244		$this->assertFalse($result);
245
246		$result = $route->match(array('plugin' => null, 'controller' => 'posts', 'action' => 'view', 'id' => 1));
247		$this->assertEquals('/posts/view/1', $result);
248
249		$route = new CakeRoute('/', array('controller' => 'pages', 'action' => 'display', 'home'));
250		$result = $route->match(array('controller' => 'pages', 'action' => 'display', 'home'));
251		$this->assertEquals('/', $result);
252
253		$result = $route->match(array('controller' => 'pages', 'action' => 'display', 'about'));
254		$this->assertFalse($result);
255
256		$route = new CakeRoute('/pages/*', array('controller' => 'pages', 'action' => 'display'));
257		$result = $route->match(array('controller' => 'pages', 'action' => 'display', 'home'));
258		$this->assertEquals('/pages/home', $result);
259
260		$result = $route->match(array('controller' => 'pages', 'action' => 'display', 'about'));
261		$this->assertEquals('/pages/about', $result);
262
263		$route = new CakeRoute('/blog/:action', array('controller' => 'posts'));
264		$result = $route->match(array('controller' => 'posts', 'action' => 'view'));
265		$this->assertEquals('/blog/view', $result);
266
267		$result = $route->match(array('controller' => 'nodes', 'action' => 'view'));
268		$this->assertFalse($result);
269
270		$result = $route->match(array('controller' => 'posts', 'action' => 'view', 1));
271		$this->assertFalse($result);
272
273		$result = $route->match(array('controller' => 'posts', 'action' => 'view', 'id' => 2));
274		$this->assertFalse($result);
275
276		$route = new CakeRoute('/foo/:controller/:action', array('action' => 'index'));
277		$result = $route->match(array('controller' => 'posts', 'action' => 'view'));
278		$this->assertEquals('/foo/posts/view', $result);
279
280		$route = new CakeRoute('/:plugin/:id/*', array('controller' => 'posts', 'action' => 'view'));
281		$result = $route->match(array('plugin' => 'test', 'controller' => 'posts', 'action' => 'view', 'id' => '1'));
282		$this->assertEquals('/test/1/', $result);
283
284		$result = $route->match(array('plugin' => 'fo', 'controller' => 'posts', 'action' => 'view', 'id' => '1', '0'));
285		$this->assertEquals('/fo/1/0', $result);
286
287		$result = $route->match(array('plugin' => 'fo', 'controller' => 'nodes', 'action' => 'view', 'id' => 1));
288		$this->assertFalse($result);
289
290		$result = $route->match(array('plugin' => 'fo', 'controller' => 'posts', 'action' => 'edit', 'id' => 1));
291		$this->assertFalse($result);
292
293		$route = new CakeRoute('/admin/subscriptions/:action/*', array(
294			'controller' => 'subscribe', 'admin' => true, 'prefix' => 'admin'
295		));
296
297		$url = array('controller' => 'subscribe', 'admin' => true, 'action' => 'edit', 1);
298		$result = $route->match($url);
299		$expected = '/admin/subscriptions/edit/1';
300		$this->assertEquals($expected, $result);
301
302		$url = array(
303			'controller' => 'subscribe',
304			'admin' => true,
305			'action' => 'edit_admin_e',
306			1
307		);
308		$result = $route->match($url);
309		$expected = '/admin/subscriptions/edit_admin_e/1';
310		$this->assertEquals($expected, $result);
311
312		$url = array('controller' => 'subscribe', 'admin' => true, 'action' => 'admin_edit', 1);
313		$result = $route->match($url);
314		$expected = '/admin/subscriptions/edit/1';
315		$this->assertEquals($expected, $result);
316	}
317
318/**
319 * test that non-greedy routes fail with extra passed args
320 *
321 * @return void
322 */
323	public function testGreedyRouteFailurePassedArg() {
324		$route = new CakeRoute('/:controller/:action', array('plugin' => null));
325		$result = $route->match(array('controller' => 'posts', 'action' => 'view', '0'));
326		$this->assertFalse($result);
327
328		$route = new CakeRoute('/:controller/:action', array('plugin' => null));
329		$result = $route->match(array('controller' => 'posts', 'action' => 'view', 'test'));
330		$this->assertFalse($result);
331	}
332
333/**
334 * test that non-greedy routes fail with extra passed args
335 *
336 * @return void
337 */
338	public function testGreedyRouteFailureNamedParam() {
339		$route = new CakeRoute('/:controller/:action', array('plugin' => null));
340		$result = $route->match(array('controller' => 'posts', 'action' => 'view', 'page' => 1));
341		$this->assertFalse($result);
342	}
343
344/**
345 * test that falsey values do not interrupt a match.
346 *
347 * @return void
348 */
349	public function testMatchWithFalseyValues() {
350		$route = new CakeRoute('/:controller/:action/*', array('plugin' => null));
351		$result = $route->match(array(
352			'controller' => 'posts', 'action' => 'index', 'plugin' => null, 'admin' => false
353		));
354		$this->assertEquals('/posts/index/', $result);
355	}
356
357/**
358 * test match() with greedy routes, named parameters and passed args.
359 *
360 * @return void
361 */
362	public function testMatchWithNamedParametersAndPassedArgs() {
363		Router::connectNamed(true);
364
365		$route = new CakeRoute('/:controller/:action/*', array('plugin' => null));
366		$result = $route->match(array('controller' => 'posts', 'action' => 'index', 'plugin' => null, 'page' => 1));
367		$this->assertEquals('/posts/index/page:1', $result);
368
369		$result = $route->match(array('controller' => 'posts', 'action' => 'view', 'plugin' => null, 5));
370		$this->assertEquals('/posts/view/5', $result);
371
372		$result = $route->match(array('controller' => 'posts', 'action' => 'view', 'plugin' => null, 0));
373		$this->assertEquals('/posts/view/0', $result);
374
375		$result = $route->match(array('controller' => 'posts', 'action' => 'view', 'plugin' => null, '0'));
376		$this->assertEquals('/posts/view/0', $result);
377
378		$result = $route->match(array('controller' => 'posts', 'action' => 'view', 'plugin' => null, 5, 'page' => 1, 'limit' => 20, 'order' => 'title'));
379		$this->assertEquals('/posts/view/5/page:1/limit:20/order:title', $result);
380
381		$result = $route->match(array('controller' => 'posts', 'action' => 'view', 'plugin' => null, 'word space', 'order' => 'Θ'));
382		$this->assertEquals('/posts/view/word%20space/order:%CE%98', $result);
383
384		$route = new CakeRoute('/test2/*', array('controller' => 'pages', 'action' => 'display', 2));
385		$result = $route->match(array('controller' => 'pages', 'action' => 'display', 1));
386		$this->assertFalse($result);
387
388		$result = $route->match(array('controller' => 'pages', 'action' => 'display', 2, 'something'));
389		$this->assertEquals('/test2/something', $result);
390
391		$result = $route->match(array('controller' => 'pages', 'action' => 'display', 5, 'something'));
392		$this->assertFalse($result);
393	}
394
395/**
396 * Ensure that named parameters are urldecoded
397 *
398 * @return void
399 */
400	public function testParseNamedParametersUrlDecode() {
401		Router::connectNamed(true);
402		$route = new CakeRoute('/:controller/:action/*', array('plugin' => null));
403
404		$result = $route->parse('/posts/index/page:%CE%98');
405		$this->assertEquals('Θ', $result['named']['page']);
406
407		$result = $route->parse('/posts/index/page[]:%CE%98');
408		$this->assertEquals('Θ', $result['named']['page'][0]);
409
410		$result = $route->parse('/posts/index/something%20else/page[]:%CE%98');
411		$this->assertEquals('Θ', $result['named']['page'][0]);
412		$this->assertEquals('something else', $result['pass'][0]);
413	}
414
415/**
416 * Ensure that keys at named parameters are urldecoded
417 *
418 * @return void
419 */
420	public function testParseNamedKeyUrlDecode() {
421		Router::connectNamed(true);
422		$route = new CakeRoute('/:controller/:action/*', array('plugin' => null));
423
424		// checking /post/index/user[0]:a/user[1]:b
425		$result = $route->parse('/posts/index/user%5B0%5D:a/user%5B1%5D:b');
426		$this->assertArrayHasKey('user', $result['named']);
427		$this->assertEquals(array('a', 'b'), $result['named']['user']);
428
429		// checking /post/index/user[]:a/user[]:b
430		$result = $route->parse('/posts/index/user%5B%5D:a/user%5B%5D:b');
431		$this->assertArrayHasKey('user', $result['named']);
432		$this->assertEquals(array('a', 'b'), $result['named']['user']);
433	}
434
435/**
436 * test that named params with null/false are excluded
437 *
438 * @return void
439 */
440	public function testNamedParamsWithNullFalse() {
441		$route = new CakeRoute('/:controller/:action/*');
442		$result = $route->match(array('controller' => 'posts', 'action' => 'index', 'page' => null, 'sort' => false));
443		$this->assertEquals('/posts/index/', $result);
444	}
445
446/**
447 * test that match with patterns works.
448 *
449 * @return void
450 */
451	public function testMatchWithPatterns() {
452		$route = new CakeRoute('/:controller/:action/:id', array('plugin' => null), array('id' => '[0-9]+'));
453		$result = $route->match(array('controller' => 'posts', 'action' => 'view', 'id' => 'foo'));
454		$this->assertFalse($result);
455
456		$result = $route->match(array('plugin' => null, 'controller' => 'posts', 'action' => 'view', 'id' => '9'));
457		$this->assertEquals('/posts/view/9', $result);
458
459		$result = $route->match(array('plugin' => null, 'controller' => 'posts', 'action' => 'view', 'id' => '922'));
460		$this->assertEquals('/posts/view/922', $result);
461
462		$result = $route->match(array('plugin' => null, 'controller' => 'posts', 'action' => 'view', 'id' => 'a99'));
463		$this->assertFalse($result);
464	}
465
466/**
467 * test persistParams ability to persist parameters from $params and remove params.
468 *
469 * @return void
470 */
471	public function testPersistParams() {
472		$route = new CakeRoute(
473			'/:lang/:color/blog/:action',
474			array('controller' => 'posts'),
475			array('persist' => array('lang', 'color'))
476		);
477		$url = array('controller' => 'posts', 'action' => 'index');
478		$params = array('lang' => 'en', 'color' => 'blue');
479		$result = $route->persistParams($url, $params);
480		$this->assertEquals('en', $result['lang']);
481		$this->assertEquals('blue', $result['color']);
482
483		$url = array('controller' => 'posts', 'action' => 'index', 'color' => 'red');
484		$params = array('lang' => 'en', 'color' => 'blue');
485		$result = $route->persistParams($url, $params);
486		$this->assertEquals('en', $result['lang']);
487		$this->assertEquals('red', $result['color']);
488	}
489
490/**
491 * test persist with a non array value
492 *
493 * @return void
494 */
495	public function testPersistParamsNonArray() {
496		$url = array('controller' => 'posts', 'action' => 'index');
497		$params = array('lang' => 'en', 'color' => 'blue');
498
499		$route = new CakeRoute(
500			'/:lang/:color/blog/:action',
501			array('controller' => 'posts')
502			// No persist options
503		);
504		$result = $route->persistParams($url, $params);
505		$this->assertEquals($url, $result);
506
507		$route = new CakeRoute(
508			'/:lang/:color/blog/:action',
509			array('controller' => 'posts'),
510			array('persist' => false)
511		);
512		$result = $route->persistParams($url, $params);
513		$this->assertEquals($url, $result);
514
515		$route = new CakeRoute(
516			'/:lang/:color/blog/:action',
517			array('controller' => 'posts'),
518			array('persist' => 'derp')
519		);
520		$result = $route->persistParams($url, $params);
521		$this->assertEquals($url, $result);
522	}
523
524/**
525 * test the parse method of CakeRoute.
526 *
527 * @return void
528 */
529	public function testParse() {
530		$route = new CakeRoute(
531			'/:controller/:action/:id',
532			array('controller' => 'testing4', 'id' => null),
533			array('id' => Router::ID)
534		);
535		$route->compile();
536		$result = $route->parse('/posts/view/1');
537		$this->assertEquals('posts', $result['controller']);
538		$this->assertEquals('view', $result['action']);
539		$this->assertEquals('1', $result['id']);
540
541		$route = new Cakeroute(
542			'/admin/:controller',
543			array('prefix' => 'admin', 'admin' => 1, 'action' => 'index')
544		);
545		$route->compile();
546		$result = $route->parse('/admin/');
547		$this->assertFalse($result);
548
549		$result = $route->parse('/admin/posts');
550		$this->assertEquals('posts', $result['controller']);
551		$this->assertEquals('index', $result['action']);
552	}
553
554/**
555 * Test that :key elements are urldecoded
556 *
557 * @return void
558 */
559	public function testParseUrlDecodeElements() {
560		$route = new Cakeroute(
561			'/:controller/:slug',
562			array('action' => 'view')
563		);
564		$route->compile();
565		$result = $route->parse('/posts/%E2%88%82%E2%88%82');
566		$this->assertEquals('posts', $result['controller']);
567		$this->assertEquals('view', $result['action']);
568		$this->assertEquals('∂∂', $result['slug']);
569
570		$result = $route->parse('/posts/∂∂');
571		$this->assertEquals('posts', $result['controller']);
572		$this->assertEquals('view', $result['action']);
573		$this->assertEquals('∂∂', $result['slug']);
574	}
575
576/**
577 * test numerically indexed defaults, get appended to pass
578 *
579 * @return void
580 */
581	public function testParseWithPassDefaults() {
582		$route = new Cakeroute('/:controller', array('action' => 'display', 'home'));
583		$result = $route->parse('/posts');
584		$expected = array(
585			'controller' => 'posts',
586			'action' => 'display',
587			'pass' => array('home'),
588			'named' => array()
589		);
590		$this->assertEquals($expected, $result);
591	}
592
593/**
594 * test that http header conditions can cause route failures.
595 *
596 * @return void
597 */
598	public function testParseWithHttpHeaderConditions() {
599		$_SERVER['REQUEST_METHOD'] = 'GET';
600		$route = new CakeRoute('/sample', array('controller' => 'posts', 'action' => 'index', '[method]' => 'POST'));
601
602		$this->assertFalse($route->parse('/sample'));
603	}
604
605/**
606 * test that patterns work for :action
607 *
608 * @return void
609 */
610	public function testPatternOnAction() {
611		$route = new CakeRoute(
612			'/blog/:action/*',
613			array('controller' => 'blog_posts'),
614			array('action' => 'other|actions')
615		);
616		$result = $route->match(array('controller' => 'blog_posts', 'action' => 'foo'));
617		$this->assertFalse($result);
618
619		$result = $route->match(array('controller' => 'blog_posts', 'action' => 'actions'));
620		$this->assertNotEmpty($result);
621
622		$result = $route->parse('/blog/other');
623		$expected = array('controller' => 'blog_posts', 'action' => 'other', 'pass' => array(), 'named' => array());
624		$this->assertEquals($expected, $result);
625
626		$result = $route->parse('/blog/foobar');
627		$this->assertFalse($result);
628	}
629
630/**
631 * test the parseArgs method
632 *
633 * @return void
634 */
635	public function testParsePassedArgument() {
636		$route = new CakeRoute('/:controller/:action/*');
637		$result = $route->parse('/posts/edit/1/2/0');
638		$expected = array(
639			'controller' => 'posts',
640			'action' => 'edit',
641			'pass' => array('1', '2', '0'),
642			'named' => array()
643		);
644		$this->assertEquals($expected, $result);
645
646		$result = $route->parse('/posts/edit/a-string/page:1/sort:value');
647		$expected = array(
648			'controller' => 'posts',
649			'action' => 'edit',
650			'pass' => array('a-string'),
651			'named' => array(
652				'page' => 1,
653				'sort' => 'value'
654			)
655		);
656		$this->assertEquals($expected, $result);
657	}
658
659/**
660 * test that only named parameter rules are followed.
661 *
662 * @return void
663 */
664	public function testParseNamedParametersWithRules() {
665		$route = new CakeRoute('/:controller/:action/*', array(), array(
666			'named' => array(
667				'wibble',
668				'fish' => array('action' => 'index'),
669				'fizz' => array('controller' => array('comments', 'other')),
670				'pattern' => 'val-[\d]+'
671			)
672		));
673		$result = $route->parse('/posts/display/wibble:spin/fish:trout/fizz:buzz/unknown:value');
674		$expected = array(
675			'controller' => 'posts',
676			'action' => 'display',
677			'pass' => array('fish:trout', 'fizz:buzz', 'unknown:value'),
678			'named' => array(
679				'wibble' => 'spin'
680			)
681		);
682		$this->assertEquals($expected, $result, 'Fish should not be parsed, as action != index');
683
684		$result = $route->parse('/posts/index/wibble:spin/fish:trout/fizz:buzz');
685		$expected = array(
686			'controller' => 'posts',
687			'action' => 'index',
688			'pass' => array('fizz:buzz'),
689			'named' => array(
690				'wibble' => 'spin',
691				'fish' => 'trout'
692			)
693		);
694		$this->assertEquals($expected, $result, 'Fizz should be parsed, as controller == comments|other');
695
696		$result = $route->parse('/comments/index/wibble:spin/fish:trout/fizz:buzz');
697		$expected = array(
698			'controller' => 'comments',
699			'action' => 'index',
700			'pass' => array(),
701			'named' => array(
702				'wibble' => 'spin',
703				'fish' => 'trout',
704				'fizz' => 'buzz'
705			)
706		);
707		$this->assertEquals($expected, $result, 'All params should be parsed as conditions were met.');
708
709		$result = $route->parse('/comments/index/pattern:val--');
710		$expected = array(
711			'controller' => 'comments',
712			'action' => 'index',
713			'pass' => array('pattern:val--'),
714			'named' => array()
715		);
716		$this->assertEquals($expected, $result, 'Named parameter pattern unmet.');
717
718		$result = $route->parse('/comments/index/pattern:val-2');
719		$expected = array(
720			'controller' => 'comments',
721			'action' => 'index',
722			'pass' => array(),
723			'named' => array('pattern' => 'val-2')
724		);
725		$this->assertEquals($expected, $result, 'Named parameter pattern met.');
726	}
727
728/**
729 * test that greedyNamed ignores rules.
730 *
731 * @return void
732 */
733	public function testParseGreedyNamed() {
734		$route = new CakeRoute('/:controller/:action/*', array(), array(
735			'named' => array(
736				'fizz' => array('controller' => 'comments'),
737				'pattern' => 'val-[\d]+',
738			),
739			'greedyNamed' => true
740		));
741		$result = $route->parse('/posts/display/wibble:spin/fizz:buzz/pattern:ignored');
742		$expected = array(
743			'controller' => 'posts',
744			'action' => 'display',
745			'pass' => array('fizz:buzz', 'pattern:ignored'),
746			'named' => array(
747				'wibble' => 'spin',
748			)
749		);
750		$this->assertEquals($expected, $result, 'Greedy named grabs everything, rules are followed');
751	}
752
753/**
754 * Having greedNamed enabled should not capture routing.prefixes.
755 *
756 * @return void
757 */
758	public function testMatchGreedyNamedExcludesPrefixes() {
759		Configure::write('Routing.prefixes', array('admin'));
760		Router::reload();
761
762		$route = new CakeRoute('/sales/*', array('controller' => 'sales', 'action' => 'index'));
763		$this->assertFalse($route->match(array('controller' => 'sales', 'action' => 'index', 'admin' => 1)), 'Greedy named consume routing prefixes.');
764	}
765
766/**
767 * test that parsing array format named parameters works
768 *
769 * @return void
770 */
771	public function testParseArrayNamedParameters() {
772		$route = new CakeRoute('/:controller/:action/*');
773		$result = $route->parse('/tests/action/var[]:val1/var[]:val2');
774		$expected = array(
775			'controller' => 'tests',
776			'action' => 'action',
777			'named' => array(
778				'var' => array(
779					'val1',
780					'val2'
781				)
782			),
783			'pass' => array(),
784		);
785		$this->assertEquals($expected, $result);
786
787		$result = $route->parse('/tests/action/theanswer[is]:42/var[]:val2/var[]:val3');
788		$expected = array(
789			'controller' => 'tests',
790			'action' => 'action',
791			'named' => array(
792				'theanswer' => array(
793					'is' => 42
794				),
795				'var' => array(
796					'val2',
797					'val3'
798				)
799			),
800			'pass' => array(),
801		);
802		$this->assertEquals($expected, $result);
803
804		$result = $route->parse('/tests/action/theanswer[is][not]:42/theanswer[]:5/theanswer[is]:6');
805		$expected = array(
806			'controller' => 'tests',
807			'action' => 'action',
808			'named' => array(
809				'theanswer' => array(
810					5,
811					'is' => array(
812						6,
813						'not' => 42
814					)
815				),
816			),
817			'pass' => array(),
818		);
819		$this->assertEquals($expected, $result);
820	}
821
822/**
823 * Test that match can handle array named parameters
824 *
825 * @return void
826 */
827	public function testMatchNamedParametersArray() {
828		$route = new CakeRoute('/:controller/:action/*');
829
830		$url = array(
831			'controller' => 'posts',
832			'action' => 'index',
833			'filter' => array(
834				'one',
835				'model' => 'value'
836			)
837		);
838		$result = $route->match($url);
839		$expected = '/posts/index/filter%5B0%5D:one/filter%5Bmodel%5D:value';
840		$this->assertEquals($expected, $result);
841
842		$url = array(
843			'controller' => 'posts',
844			'action' => 'index',
845			'filter' => array(
846				'one',
847				'model' => array(
848					'two',
849					'order' => 'field'
850				)
851			)
852		);
853		$result = $route->match($url);
854		$expected = '/posts/index/filter%5B0%5D:one/filter%5Bmodel%5D%5B0%5D:two/filter%5Bmodel%5D%5Border%5D:field';
855		$this->assertEquals($expected, $result);
856	}
857
858/**
859 * Test matching of parameters where one parameter name starts with another parameter name
860 *
861 * @return void
862 */
863	public function testMatchSimilarParameters() {
864		$route = new CakeRoute('/:thisParam/:thisParamIsLonger');
865
866		$url = array(
867			'thisParamIsLonger' => 'bar',
868			'thisParam' => 'foo',
869		);
870
871		$result = $route->match($url);
872		$expected = '/foo/bar';
873		$this->assertEquals($expected, $result);
874	}
875
876/**
877 * Test match() with trailing ** style routes.
878 *
879 * @return void
880 */
881	public function testMatchTrailing() {
882		$route = new CakeRoute('/pages/**', array('controller' => 'pages', 'action' => 'display'));
883		$id = 'test/ spaces/漢字/la†în';
884		$result = $route->match(array(
885			'controller' => 'pages',
886			'action' => 'display',
887			$id
888		));
889		$expected = '/pages/test/%20spaces/%E6%BC%A2%E5%AD%97/la%E2%80%A0%C3%AEn';
890		$this->assertEquals($expected, $result);
891	}
892
893/**
894 * test restructuring args with pass key
895 *
896 * @return void
897 */
898	public function testPassArgRestructure() {
899		$route = new CakeRoute('/:controller/:action/:slug', array(), array(
900			'pass' => array('slug')
901		));
902		$result = $route->parse('/posts/view/my-title');
903		$expected = array(
904			'controller' => 'posts',
905			'action' => 'view',
906			'slug' => 'my-title',
907			'pass' => array('my-title'),
908			'named' => array()
909		);
910		$this->assertEquals($expected, $result, 'Slug should have moved');
911	}
912
913/**
914 * Test the /** special type on parsing.
915 *
916 * @return void
917 */
918	public function testParseTrailing() {
919		$route = new CakeRoute('/:controller/:action/**');
920		$result = $route->parse('/posts/index/1/2/3/foo:bar');
921		$expected = array(
922			'controller' => 'posts',
923			'action' => 'index',
924			'pass' => array('1/2/3/foo:bar'),
925			'named' => array()
926		);
927		$this->assertEquals($expected, $result);
928
929		$result = $route->parse('/posts/index/http://example.com');
930		$expected = array(
931			'controller' => 'posts',
932			'action' => 'index',
933			'pass' => array('http://example.com'),
934			'named' => array()
935		);
936		$this->assertEquals($expected, $result);
937	}
938
939/**
940 * Test the /** special type on parsing - UTF8.
941 *
942 * @return void
943 */
944	public function testParseTrailingUTF8() {
945		$route = new CakeRoute('/category/**', array('controller' => 'categories', 'action' => 'index'));
946		$result = $route->parse('/category/%D9%85%D9%88%D8%A8%D8%A7%DB%8C%D9%84');
947		$expected = array(
948			'controller' => 'categories',
949			'action' => 'index',
950			'pass' => array('موبایل'),
951			'named' => array()
952		);
953		$this->assertEquals($expected, $result);
954	}
955
956/**
957 * test that utf-8 patterns work for :section
958 *
959 * @return void
960 */
961	public function testUTF8PatternOnSection() {
962		$route = new CakeRoute(
963			'/:section',
964			array('plugin' => 'blogs', 'controller' => 'posts', 'action' => 'index'),
965			array(
966				'persist' => array('section'),
967				'section' => 'آموزش|weblog'
968			)
969		);
970
971		$result = $route->parse('/%D8%A2%D9%85%D9%88%D8%B2%D8%B4');
972		$expected = array('section' => 'آموزش', 'plugin' => 'blogs', 'controller' => 'posts', 'action' => 'index', 'pass' => array(), 'named' => array());
973		$this->assertEquals($expected, $result);
974
975		$result = $route->parse('/weblog');
976		$expected = array('section' => 'weblog', 'plugin' => 'blogs', 'controller' => 'posts', 'action' => 'index', 'pass' => array(), 'named' => array());
977		$this->assertEquals($expected, $result);
978	}
979
980/**
981 * Test for __set_state magic method on CakeRoute
982 *
983 * @return void
984 */
985	public function testSetState() {
986		$route = CakeRoute::__set_state(array(
987			'keys' => array(),
988			'options' => array(),
989			'defaults' => array(
990				'controller' => 'pages',
991				'action' => 'display',
992				'home',
993			),
994			'template' => '/',
995			'_greedy' => false,
996			'_compiledRoute' => null,
997			'_headerMap' => array (
998				'type' => 'content_type',
999				'method' => 'request_method',
1000				'server' => 'server_name',
1001			),
1002		));
1003		$this->assertInstanceOf('CakeRoute', $route);
1004		$this->assertSame('/', $route->match(array('controller' => 'pages', 'action' => 'display', 'home')));
1005		$this->assertFalse($route->match(array('controller' => 'pages', 'action' => 'display', 'about')));
1006		$expected = array('controller' => 'pages', 'action' => 'display', 'pass' => array('home'), 'named' => array());
1007		$this->assertEquals($expected, $route->parse('/'));
1008	}
1009
1010}
1011