PaginatorComponentTest.php 36 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159
  1. <?php
  2. /**
  3. * PaginatorComponentTest file
  4. *
  5. * Series of tests for paginator component.
  6. *
  7. * PHP 5
  8. *
  9. * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
  10. * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
  11. *
  12. * Licensed under The MIT License
  13. * Redistributions of files must retain the above copyright notice
  14. *
  15. * @copyright Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
  16. * @link http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
  17. * @package Cake.Test.Case.Controller.Component
  18. * @since CakePHP(tm) v 2.0
  19. * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
  20. */
  21. App::uses('Controller', 'Controller');
  22. App::uses('PaginatorComponent', 'Controller/Component');
  23. App::uses('CakeRequest', 'Network');
  24. App::uses('CakeResponse', 'Network');
  25. /**
  26. * PaginatorTestController class
  27. *
  28. * @package Cake.Test.Case.Controller.Component
  29. */
  30. class PaginatorTestController extends Controller {
  31. /**
  32. * name property
  33. *
  34. * @var string 'PaginatorTest'
  35. */
  36. public $name = 'PaginatorTest';
  37. /**
  38. * components property
  39. *
  40. * @var array
  41. */
  42. public $components = array('Paginator');
  43. }
  44. /**
  45. * PaginatorControllerPost class
  46. *
  47. * @package Cake.Test.Case.Controller.Component
  48. */
  49. class PaginatorControllerPost extends CakeTestModel {
  50. /**
  51. * name property
  52. *
  53. * @var string 'PaginatorControllerPost'
  54. */
  55. public $name = 'PaginatorControllerPost';
  56. /**
  57. * useTable property
  58. *
  59. * @var string 'posts'
  60. */
  61. public $useTable = 'posts';
  62. /**
  63. * invalidFields property
  64. *
  65. * @var array
  66. */
  67. public $invalidFields = array('name' => 'error_msg');
  68. /**
  69. * lastQueries property
  70. *
  71. * @var array
  72. */
  73. public $lastQueries = array();
  74. /**
  75. * belongsTo property
  76. *
  77. * @var array
  78. */
  79. public $belongsTo = array('PaginatorAuthor' => array('foreignKey' => 'author_id'));
  80. /**
  81. * beforeFind method
  82. *
  83. * @param mixed $query
  84. * @return void
  85. */
  86. public function beforeFind($query) {
  87. array_unshift($this->lastQueries, $query);
  88. }
  89. /**
  90. * find method
  91. *
  92. * @param mixed $type
  93. * @param array $options
  94. * @return void
  95. */
  96. public function find($conditions = null, $fields = array(), $order = null, $recursive = null) {
  97. if ($conditions == 'popular') {
  98. $conditions = array($this->name . '.' . $this->primaryKey . ' > ' => '1');
  99. $options = Hash::merge($fields, compact('conditions'));
  100. return parent::find('all', $options);
  101. }
  102. return parent::find($conditions, $fields);
  103. }
  104. }
  105. /**
  106. * ControllerPaginateModel class
  107. *
  108. * @package Cake.Test.Case.Controller.Component
  109. */
  110. class ControllerPaginateModel extends CakeTestModel {
  111. /**
  112. * name property
  113. *
  114. * @var string 'ControllerPaginateModel'
  115. */
  116. public $name = 'ControllerPaginateModel';
  117. /**
  118. * useTable property
  119. *
  120. * @var string 'comments'
  121. */
  122. public $useTable = 'comments';
  123. /**
  124. * paginate method
  125. *
  126. * @return void
  127. */
  128. public function paginate($conditions, $fields, $order, $limit, $page, $recursive, $extra) {
  129. $this->extra = $extra;
  130. }
  131. /**
  132. * paginateCount
  133. *
  134. * @return void
  135. */
  136. public function paginateCount($conditions, $recursive, $extra) {
  137. $this->extraCount = $extra;
  138. }
  139. }
  140. /**
  141. * PaginatorControllerComment class
  142. *
  143. * @package Cake.Test.Case.Controller.Component
  144. */
  145. class PaginatorControllerComment extends CakeTestModel {
  146. /**
  147. * name property
  148. *
  149. * @var string 'Comment'
  150. */
  151. public $name = 'Comment';
  152. /**
  153. * useTable property
  154. *
  155. * @var string 'comments'
  156. */
  157. public $useTable = 'comments';
  158. /**
  159. * alias property
  160. *
  161. * @var string 'PaginatorControllerComment'
  162. */
  163. public $alias = 'PaginatorControllerComment';
  164. }
  165. /**
  166. * PaginatorAuthor class
  167. *
  168. * @package Cake.Test.Case.Controller.Component
  169. */
  170. class PaginatorAuthor extends CakeTestModel {
  171. /**
  172. * name property
  173. *
  174. * @var string 'PaginatorAuthor'
  175. */
  176. public $name = 'PaginatorAuthor';
  177. /**
  178. * useTable property
  179. *
  180. * @var string 'authors'
  181. */
  182. public $useTable = 'authors';
  183. /**
  184. * alias property
  185. *
  186. * @var string 'PaginatorAuthor'
  187. */
  188. public $alias = 'PaginatorAuthor';
  189. /**
  190. * alias property
  191. *
  192. * @var string 'PaginatorAuthor'
  193. */
  194. public $virtualFields = array(
  195. 'joined_offset' => 'PaginatorAuthor.id + 1'
  196. );
  197. }
  198. /**
  199. * PaginatorCustomPost class
  200. *
  201. * @package Cake.Test.Case.Controller.Component
  202. */
  203. class PaginatorCustomPost extends CakeTestModel {
  204. /**
  205. * useTable property
  206. *
  207. * @var string
  208. */
  209. public $useTable = 'posts';
  210. /**
  211. * belongsTo property
  212. *
  213. * @var string
  214. */
  215. public $belongsTo = array('Author');
  216. /**
  217. * findMethods property
  218. *
  219. * @var array
  220. */
  221. public $findMethods = array(
  222. 'published' => true,
  223. 'totals' => true,
  224. 'totalsOperation' => true
  225. );
  226. /**
  227. * _findPublished custom find
  228. *
  229. * @return array
  230. */
  231. public function _findPublished($state, $query, $results = array()) {
  232. if ($state === 'before') {
  233. $query['conditions']['published'] = 'Y';
  234. return $query;
  235. }
  236. return $results;
  237. }
  238. /**
  239. * _findTotals custom find
  240. *
  241. * @return array
  242. */
  243. public function _findTotals($state, $query, $results = array()) {
  244. if ($state == 'before') {
  245. $query['fields'] = array('author_id');
  246. $this->virtualFields['total_posts'] = "COUNT({$this->alias}.id)";
  247. $query['fields'][] = 'total_posts';
  248. $query['group'] = array('author_id');
  249. $query['order'] = array('author_id' => 'ASC');
  250. return $query;
  251. }
  252. $this->virtualFields = array();
  253. return $results;
  254. }
  255. /**
  256. * _findTotalsOperation custom find
  257. *
  258. * @return array
  259. */
  260. public function _findTotalsOperation($state, $query, $results = array()) {
  261. if ($state == 'before') {
  262. if (!empty($query['operation']) && $query['operation'] === 'count') {
  263. unset($query['limit']);
  264. $query['recursive'] = -1;
  265. $query['fields'] = array('COUNT(DISTINCT author_id) AS count');
  266. return $query;
  267. }
  268. $query['recursive'] = 0;
  269. $query['callbacks'] = 'before';
  270. $query['fields'] = array('author_id', 'Author.user');
  271. $this->virtualFields['total_posts'] = "COUNT({$this->alias}.id)";
  272. $query['fields'][] = 'total_posts';
  273. $query['group'] = array('author_id', 'Author.user');
  274. $query['order'] = array('author_id' => 'ASC');
  275. return $query;
  276. }
  277. $this->virtualFields = array();
  278. return $results;
  279. }
  280. }
  281. class PaginatorComponentTest extends CakeTestCase {
  282. /**
  283. * fixtures property
  284. *
  285. * @var array
  286. */
  287. public $fixtures = array('core.post', 'core.comment', 'core.author');
  288. /**
  289. * setup
  290. *
  291. * @return void
  292. */
  293. public function setUp() {
  294. parent::setUp();
  295. $this->request = new CakeRequest('controller_posts/index');
  296. $this->request->params['pass'] = $this->request->params['named'] = array();
  297. $this->Controller = new Controller($this->request);
  298. $this->Paginator = new PaginatorComponent($this->getMock('ComponentCollection'), array());
  299. $this->Paginator->Controller = $this->Controller;
  300. $this->Controller->Post = $this->getMock('Model');
  301. $this->Controller->Post->alias = 'Post';
  302. }
  303. /**
  304. * testPaginate method
  305. *
  306. * @return void
  307. */
  308. public function testPaginate() {
  309. $Controller = new PaginatorTestController($this->request);
  310. $Controller->uses = array('PaginatorControllerPost', 'PaginatorControllerComment');
  311. $Controller->request->params['pass'] = array('1');
  312. $Controller->request->query = array();
  313. $Controller->constructClasses();
  314. $results = Hash::extract($Controller->Paginator->paginate('PaginatorControllerPost'), '{n}.PaginatorControllerPost.id');
  315. $this->assertEquals(array(1, 2, 3), $results);
  316. $results = Hash::extract($Controller->Paginator->paginate('PaginatorControllerComment'), '{n}.PaginatorControllerComment.id');
  317. $this->assertEquals(array(1, 2, 3, 4, 5, 6), $results);
  318. $Controller->modelClass = null;
  319. $Controller->uses[0] = 'Plugin.PaginatorControllerPost';
  320. $results = Hash::extract($Controller->Paginator->paginate(), '{n}.PaginatorControllerPost.id');
  321. $this->assertEquals(array(1, 2, 3), $results);
  322. $Controller->request->params['named'] = array('page' => '-1');
  323. $results = Hash::extract($Controller->Paginator->paginate('PaginatorControllerPost'), '{n}.PaginatorControllerPost.id');
  324. $this->assertEquals(1, $Controller->params['paging']['PaginatorControllerPost']['page']);
  325. $this->assertEquals(array(1, 2, 3), $results);
  326. $Controller->request->params['named'] = array('sort' => 'PaginatorControllerPost.id', 'direction' => 'asc');
  327. $results = Hash::extract($Controller->Paginator->paginate('PaginatorControllerPost'), '{n}.PaginatorControllerPost.id');
  328. $this->assertEquals(1, $Controller->params['paging']['PaginatorControllerPost']['page']);
  329. $this->assertEquals(array(1, 2, 3), $results);
  330. $Controller->request->params['named'] = array('sort' => 'PaginatorControllerPost.id', 'direction' => 'desc');
  331. $results = Hash::extract($Controller->Paginator->paginate('PaginatorControllerPost'), '{n}.PaginatorControllerPost.id');
  332. $this->assertEquals(1, $Controller->params['paging']['PaginatorControllerPost']['page']);
  333. $this->assertEquals(array(3, 2, 1), $results);
  334. $Controller->request->params['named'] = array('sort' => 'id', 'direction' => 'desc');
  335. $results = Hash::extract($Controller->Paginator->paginate('PaginatorControllerPost'), '{n}.PaginatorControllerPost.id');
  336. $this->assertEquals(1, $Controller->params['paging']['PaginatorControllerPost']['page']);
  337. $this->assertEquals(array(3, 2, 1), $results);
  338. $Controller->request->params['named'] = array('sort' => 'NotExisting.field', 'direction' => 'desc');
  339. $results = Hash::extract($Controller->Paginator->paginate('PaginatorControllerPost'), '{n}.PaginatorControllerPost.id');
  340. $this->assertEquals(1, $Controller->params['paging']['PaginatorControllerPost']['page'], 'Invalid field in query %s');
  341. $this->assertEquals(array(1, 2, 3), $results);
  342. $Controller->request->params['named'] = array(
  343. 'sort' => 'PaginatorControllerPost.author_id', 'direction' => 'allYourBase'
  344. );
  345. $results = Hash::extract($Controller->Paginator->paginate('PaginatorControllerPost'), '{n}.PaginatorControllerPost.id');
  346. $this->assertEquals(array('PaginatorControllerPost.author_id' => 'asc'), $Controller->PaginatorControllerPost->lastQueries[1]['order'][0]);
  347. $this->assertEquals(array(1, 3, 2), $results);
  348. $Controller->request->params['named'] = array();
  349. $Controller->Paginator->settings = array('limit' => 0, 'maxLimit' => 10, 'paramType' => 'named');
  350. $Controller->Paginator->paginate('PaginatorControllerPost');
  351. $this->assertSame(1, $Controller->params['paging']['PaginatorControllerPost']['page']);
  352. $this->assertSame($Controller->params['paging']['PaginatorControllerPost']['pageCount'], 3);
  353. $this->assertSame($Controller->params['paging']['PaginatorControllerPost']['prevPage'], false);
  354. $this->assertSame($Controller->params['paging']['PaginatorControllerPost']['nextPage'], true);
  355. $Controller->request->params['named'] = array();
  356. $Controller->Paginator->settings = array('limit' => 'garbage!', 'maxLimit' => 10, 'paramType' => 'named');
  357. $Controller->Paginator->paginate('PaginatorControllerPost');
  358. $this->assertSame(1, $Controller->params['paging']['PaginatorControllerPost']['page']);
  359. $this->assertSame($Controller->params['paging']['PaginatorControllerPost']['pageCount'], 3);
  360. $this->assertSame($Controller->params['paging']['PaginatorControllerPost']['prevPage'], false);
  361. $this->assertSame($Controller->params['paging']['PaginatorControllerPost']['nextPage'], true);
  362. $Controller->request->params['named'] = array();
  363. $Controller->Paginator->settings = array('limit' => '-1', 'maxLimit' => 10, 'paramType' => 'named');
  364. $Controller->Paginator->paginate('PaginatorControllerPost');
  365. $this->assertSame($Controller->params['paging']['PaginatorControllerPost']['limit'], 1);
  366. $this->assertSame(1, $Controller->params['paging']['PaginatorControllerPost']['page']);
  367. $this->assertSame($Controller->params['paging']['PaginatorControllerPost']['pageCount'], 3);
  368. $this->assertSame($Controller->params['paging']['PaginatorControllerPost']['prevPage'], false);
  369. $this->assertSame($Controller->params['paging']['PaginatorControllerPost']['nextPage'], true);
  370. }
  371. /**
  372. * Test that non-numeric values are rejected for page, and limit
  373. *
  374. * @return void
  375. */
  376. public function testPageParamCasting() {
  377. $this->Controller->Post->expects($this->at(0))
  378. ->method('hasMethod')
  379. ->with('paginate')
  380. ->will($this->returnValue(false));
  381. $this->Controller->Post->expects($this->at(1))
  382. ->method('find')
  383. ->will($this->returnValue(array('stuff')));
  384. $this->Controller->Post->expects($this->at(2))
  385. ->method('hasMethod')
  386. ->with('paginateCount')
  387. ->will($this->returnValue(false));
  388. $this->Controller->Post->expects($this->at(3))
  389. ->method('find')
  390. ->will($this->returnValue(2));
  391. $this->request->params['named'] = array('page' => '1 " onclick="alert(\'xss\');">');
  392. $this->Paginator->settings = array('limit' => 1, 'maxLimit' => 10, 'paramType' => 'named');
  393. $this->Paginator->paginate('Post');
  394. $this->assertSame(1, $this->request->params['paging']['Post']['page'], 'XSS exploit opened');
  395. }
  396. /**
  397. * testPaginateExtraParams method
  398. *
  399. * @return void
  400. */
  401. public function testPaginateExtraParams() {
  402. $Controller = new PaginatorTestController($this->request);
  403. $Controller->uses = array('PaginatorControllerPost', 'PaginatorControllerComment');
  404. $Controller->request->params['pass'] = array('1');
  405. $Controller->params['url'] = array();
  406. $Controller->constructClasses();
  407. $Controller->request->params['named'] = array('page' => '-1', 'contain' => array('PaginatorControllerComment'));
  408. $result = $Controller->Paginator->paginate('PaginatorControllerPost');
  409. $this->assertEquals(1, $Controller->params['paging']['PaginatorControllerPost']['page']);
  410. $this->assertEquals(array(1, 2, 3), Hash::extract($result, '{n}.PaginatorControllerPost.id'));
  411. $this->assertTrue(!isset($Controller->PaginatorControllerPost->lastQueries[1]['contain']));
  412. $Controller->request->params['named'] = array('page' => '-1');
  413. $Controller->Paginator->settings = array(
  414. 'PaginatorControllerPost' => array(
  415. 'contain' => array('PaginatorControllerComment'),
  416. 'maxLimit' => 10,
  417. 'paramType' => 'named'
  418. ),
  419. );
  420. $result = $Controller->Paginator->paginate('PaginatorControllerPost');
  421. $this->assertEquals(1, $Controller->params['paging']['PaginatorControllerPost']['page']);
  422. $this->assertEquals(array(1, 2, 3), Hash::extract($result, '{n}.PaginatorControllerPost.id'));
  423. $this->assertTrue(isset($Controller->PaginatorControllerPost->lastQueries[1]['contain']));
  424. $Controller->Paginator->settings = array(
  425. 'PaginatorControllerPost' => array(
  426. 'popular', 'fields' => array('id', 'title'), 'maxLimit' => 10, 'paramType' => 'named'
  427. ),
  428. );
  429. $result = $Controller->Paginator->paginate('PaginatorControllerPost');
  430. $this->assertEquals(array(2, 3), Hash::extract($result, '{n}.PaginatorControllerPost.id'));
  431. $this->assertEquals(array('PaginatorControllerPost.id > ' => '1'), $Controller->PaginatorControllerPost->lastQueries[1]['conditions']);
  432. $Controller->request->params['named'] = array('limit' => 12);
  433. $Controller->Paginator->settings = array('limit' => 30, 'maxLimit' => 100, 'paramType' => 'named');
  434. $result = $Controller->Paginator->paginate('PaginatorControllerPost');
  435. $paging = $Controller->params['paging']['PaginatorControllerPost'];
  436. $this->assertEquals(12, $Controller->PaginatorControllerPost->lastQueries[1]['limit']);
  437. $this->assertEquals(12, $paging['options']['limit']);
  438. $Controller = new PaginatorTestController($this->request);
  439. $Controller->uses = array('ControllerPaginateModel');
  440. $Controller->request->query = array();
  441. $Controller->constructClasses();
  442. $Controller->Paginator->settings = array(
  443. 'ControllerPaginateModel' => array(
  444. 'contain' => array('ControllerPaginateModel'),
  445. 'group' => 'Comment.author_id',
  446. 'maxLimit' => 10,
  447. 'paramType' => 'named'
  448. )
  449. );
  450. $result = $Controller->Paginator->paginate('ControllerPaginateModel');
  451. $expected = array(
  452. 'contain' => array('ControllerPaginateModel'),
  453. 'group' => 'Comment.author_id',
  454. 'maxLimit' => 10,
  455. 'paramType' => 'named'
  456. );
  457. $this->assertEquals($expected, $Controller->ControllerPaginateModel->extra);
  458. $this->assertEquals($expected, $Controller->ControllerPaginateModel->extraCount);
  459. $Controller->Paginator->settings = array(
  460. 'ControllerPaginateModel' => array(
  461. 'foo', 'contain' => array('ControllerPaginateModel'),
  462. 'group' => 'Comment.author_id',
  463. 'maxLimit' => 10,
  464. 'paramType' => 'named'
  465. )
  466. );
  467. $Controller->Paginator->paginate('ControllerPaginateModel');
  468. $expected = array(
  469. 'contain' => array('ControllerPaginateModel'),
  470. 'group' => 'Comment.author_id',
  471. 'type' => 'foo',
  472. 'maxLimit' => 10,
  473. 'paramType' => 'named'
  474. );
  475. $this->assertEquals($expected, $Controller->ControllerPaginateModel->extra);
  476. $this->assertEquals($expected, $Controller->ControllerPaginateModel->extraCount);
  477. }
  478. /**
  479. * Test that special paginate types are called and that the type param doesn't leak out into defaults or options.
  480. *
  481. * @return void
  482. */
  483. public function testPaginateSpecialType() {
  484. $Controller = new PaginatorTestController($this->request);
  485. $Controller->uses = array('PaginatorControllerPost', 'PaginatorControllerComment');
  486. $Controller->passedArgs[] = '1';
  487. $Controller->params['url'] = array();
  488. $Controller->constructClasses();
  489. $Controller->Paginator->settings = array(
  490. 'PaginatorControllerPost' => array(
  491. 'popular',
  492. 'fields' => array('id', 'title'),
  493. 'maxLimit' => 10,
  494. 'paramType' => 'named'
  495. )
  496. );
  497. $result = $Controller->Paginator->paginate('PaginatorControllerPost');
  498. $this->assertEquals(array(2, 3), Hash::extract($result, '{n}.PaginatorControllerPost.id'));
  499. $this->assertEquals(
  500. $Controller->PaginatorControllerPost->lastQueries[1]['conditions'],
  501. array('PaginatorControllerPost.id > ' => '1')
  502. );
  503. $this->assertFalse(isset($Controller->params['paging']['PaginatorControllerPost']['options'][0]));
  504. }
  505. /**
  506. * testDefaultPaginateParams method
  507. *
  508. * @return void
  509. */
  510. public function testDefaultPaginateParams() {
  511. $Controller = new PaginatorTestController($this->request);
  512. $Controller->modelClass = 'PaginatorControllerPost';
  513. $Controller->params['url'] = array();
  514. $Controller->constructClasses();
  515. $Controller->Paginator->settings = array(
  516. 'order' => 'PaginatorControllerPost.id DESC',
  517. 'maxLimit' => 10,
  518. 'paramType' => 'named'
  519. );
  520. $results = Hash::extract($Controller->Paginator->paginate('PaginatorControllerPost'), '{n}.PaginatorControllerPost.id');
  521. $this->assertEquals('PaginatorControllerPost.id DESC', $Controller->params['paging']['PaginatorControllerPost']['order']);
  522. $this->assertEquals(array(3, 2, 1), $results);
  523. }
  524. /**
  525. * test paginate() and virtualField interactions
  526. *
  527. * @return void
  528. */
  529. public function testPaginateOrderVirtualField() {
  530. $Controller = new PaginatorTestController($this->request);
  531. $Controller->uses = array('PaginatorControllerPost', 'PaginatorControllerComment');
  532. $Controller->params['url'] = array();
  533. $Controller->constructClasses();
  534. $Controller->PaginatorControllerPost->virtualFields = array(
  535. 'offset_test' => 'PaginatorControllerPost.id + 1'
  536. );
  537. $Controller->Paginator->settings = array(
  538. 'fields' => array('id', 'title', 'offset_test'),
  539. 'order' => array('offset_test' => 'DESC'),
  540. 'maxLimit' => 10,
  541. 'paramType' => 'named'
  542. );
  543. $result = $Controller->Paginator->paginate('PaginatorControllerPost');
  544. $this->assertEquals(array(4, 3, 2), Hash::extract($result, '{n}.PaginatorControllerPost.offset_test'));
  545. $Controller->request->params['named'] = array('sort' => 'offset_test', 'direction' => 'asc');
  546. $result = $Controller->Paginator->paginate('PaginatorControllerPost');
  547. $this->assertEquals(array(2, 3, 4), Hash::extract($result, '{n}.PaginatorControllerPost.offset_test'));
  548. }
  549. /**
  550. * test paginate() and virtualField on joined model
  551. *
  552. * @return void
  553. */
  554. public function testPaginateOrderVirtualFieldJoinedModel() {
  555. $Controller = new PaginatorTestController($this->request);
  556. $Controller->uses = array('PaginatorControllerPost');
  557. $Controller->params['url'] = array();
  558. $Controller->constructClasses();
  559. $Controller->PaginatorControllerPost->recursive = 0;
  560. $Controller->Paginator->settings = array(
  561. 'order' => array('PaginatorAuthor.joined_offset' => 'DESC'),
  562. 'maxLimit' => 10,
  563. 'paramType' => 'named'
  564. );
  565. $result = $Controller->Paginator->paginate('PaginatorControllerPost');
  566. $this->assertEquals(array(4, 2, 2), Hash::extract($result, '{n}.PaginatorAuthor.joined_offset'));
  567. $Controller->request->params['named'] = array('sort' => 'PaginatorAuthor.joined_offset', 'direction' => 'asc');
  568. $result = $Controller->Paginator->paginate('PaginatorControllerPost');
  569. $this->assertEquals(array(2, 2, 4), Hash::extract($result, '{n}.PaginatorAuthor.joined_offset'));
  570. }
  571. /**
  572. * Tests for missing models
  573. *
  574. * @expectedException MissingModelException
  575. */
  576. public function testPaginateMissingModel() {
  577. $Controller = new PaginatorTestController($this->request);
  578. $Controller->constructClasses();
  579. $Controller->Paginator->paginate('MissingModel');
  580. }
  581. /**
  582. * test that option merging prefers specific models
  583. *
  584. * @return void
  585. */
  586. public function testMergeOptionsModelSpecific() {
  587. $this->Paginator->settings = array(
  588. 'page' => 1,
  589. 'limit' => 20,
  590. 'maxLimit' => 100,
  591. 'paramType' => 'named',
  592. 'Post' => array(
  593. 'page' => 1,
  594. 'limit' => 10,
  595. 'maxLimit' => 50,
  596. 'paramType' => 'named',
  597. )
  598. );
  599. $result = $this->Paginator->mergeOptions('Silly');
  600. $this->assertEquals($this->Paginator->settings, $result);
  601. $result = $this->Paginator->mergeOptions('Post');
  602. $expected = array('page' => 1, 'limit' => 10, 'paramType' => 'named', 'maxLimit' => 50);
  603. $this->assertEquals($expected, $result);
  604. }
  605. /**
  606. * test mergeOptions with named params.
  607. *
  608. * @return void
  609. */
  610. public function testMergeOptionsNamedParams() {
  611. $this->request->params['named'] = array(
  612. 'page' => 10,
  613. 'limit' => 10
  614. );
  615. $this->Paginator->settings = array(
  616. 'page' => 1,
  617. 'limit' => 20,
  618. 'maxLimit' => 100,
  619. 'paramType' => 'named',
  620. );
  621. $result = $this->Paginator->mergeOptions('Post');
  622. $expected = array('page' => 10, 'limit' => 10, 'maxLimit' => 100, 'paramType' => 'named');
  623. $this->assertEquals($expected, $result);
  624. }
  625. /**
  626. * test merging options from the querystring.
  627. *
  628. * @return void
  629. */
  630. public function testMergeOptionsQueryString() {
  631. $this->request->params['named'] = array(
  632. 'page' => 10,
  633. 'limit' => 10
  634. );
  635. $this->request->query = array(
  636. 'page' => 99,
  637. 'limit' => 75
  638. );
  639. $this->Paginator->settings = array(
  640. 'page' => 1,
  641. 'limit' => 20,
  642. 'maxLimit' => 100,
  643. 'paramType' => 'querystring',
  644. );
  645. $result = $this->Paginator->mergeOptions('Post');
  646. $expected = array('page' => 99, 'limit' => 75, 'maxLimit' => 100, 'paramType' => 'querystring');
  647. $this->assertEquals($expected, $result);
  648. }
  649. /**
  650. * test that the default whitelist doesn't let people screw with things they should not be allowed to.
  651. *
  652. * @return void
  653. */
  654. public function testMergeOptionsDefaultWhiteList() {
  655. $this->request->params['named'] = array(
  656. 'page' => 10,
  657. 'limit' => 10,
  658. 'fields' => array('bad.stuff'),
  659. 'recursive' => 1000,
  660. 'conditions' => array('bad.stuff'),
  661. 'contain' => array('bad')
  662. );
  663. $this->Paginator->settings = array(
  664. 'page' => 1,
  665. 'limit' => 20,
  666. 'maxLimit' => 100,
  667. 'paramType' => 'named',
  668. );
  669. $result = $this->Paginator->mergeOptions('Post');
  670. $expected = array('page' => 10, 'limit' => 10, 'maxLimit' => 100, 'paramType' => 'named');
  671. $this->assertEquals($expected, $result);
  672. }
  673. /**
  674. * test that modifying the whitelist works.
  675. *
  676. * @return void
  677. */
  678. public function testMergeOptionsExtraWhitelist() {
  679. $this->request->params['named'] = array(
  680. 'page' => 10,
  681. 'limit' => 10,
  682. 'fields' => array('bad.stuff'),
  683. 'recursive' => 1000,
  684. 'conditions' => array('bad.stuff'),
  685. 'contain' => array('bad')
  686. );
  687. $this->Paginator->settings = array(
  688. 'page' => 1,
  689. 'limit' => 20,
  690. 'maxLimit' => 100,
  691. 'paramType' => 'named',
  692. );
  693. $this->Paginator->whitelist[] = 'fields';
  694. $result = $this->Paginator->mergeOptions('Post');
  695. $expected = array(
  696. 'page' => 10, 'limit' => 10, 'maxLimit' => 100, 'paramType' => 'named', 'fields' => array('bad.stuff')
  697. );
  698. $this->assertEquals($expected, $result);
  699. }
  700. /**
  701. * test that invalid directions are ignored.
  702. *
  703. * @return void
  704. */
  705. public function testValidateSortInvalidDirection() {
  706. $model = $this->getMock('Model');
  707. $model->alias = 'model';
  708. $model->expects($this->any())->method('hasField')->will($this->returnValue(true));
  709. $options = array('sort' => 'something', 'direction' => 'boogers');
  710. $result = $this->Paginator->validateSort($model, $options);
  711. $this->assertEquals('asc', $result['order']['model.something']);
  712. }
  713. /**
  714. * test that fields not in whitelist won't be part of order conditions.
  715. *
  716. * @return void
  717. */
  718. public function testValidateSortWhitelistFailure() {
  719. $model = $this->getMock('Model');
  720. $model->alias = 'model';
  721. $model->expects($this->any())->method('hasField')->will($this->returnValue(true));
  722. $options = array('sort' => 'body', 'direction' => 'asc');
  723. $result = $this->Paginator->validateSort($model, $options, array('title', 'id'));
  724. $this->assertNull($result['order']);
  725. }
  726. /**
  727. * test that virtual fields work.
  728. *
  729. * @return void
  730. */
  731. public function testValidateSortVirtualField() {
  732. $model = $this->getMock('Model');
  733. $model->alias = 'model';
  734. $model->expects($this->at(0))
  735. ->method('hasField')
  736. ->with('something')
  737. ->will($this->returnValue(false));
  738. $model->expects($this->at(1))
  739. ->method('hasField')
  740. ->with('something', true)
  741. ->will($this->returnValue(true));
  742. $options = array('sort' => 'something', 'direction' => 'desc');
  743. $result = $this->Paginator->validateSort($model, $options);
  744. $this->assertEquals('desc', $result['order']['something']);
  745. }
  746. /**
  747. * test that multiple sort works.
  748. *
  749. * @return void
  750. */
  751. public function testValidateSortMultiple() {
  752. $model = $this->getMock('Model');
  753. $model->alias = 'model';
  754. $model->expects($this->any())->method('hasField')->will($this->returnValue(true));
  755. $options = array('order' => array(
  756. 'author_id' => 'asc',
  757. 'title' => 'asc'
  758. ));
  759. $result = $this->Paginator->validateSort($model, $options);
  760. $expected = array(
  761. 'model.author_id' => 'asc',
  762. 'model.title' => 'asc'
  763. );
  764. $this->assertEquals($expected, $result['order']);
  765. }
  766. /**
  767. * Test that no sort doesn't trigger an error.
  768. *
  769. * @return void
  770. */
  771. public function testValidateSortNoSort() {
  772. $model = $this->getMock('Model');
  773. $model->alias = 'model';
  774. $model->expects($this->any())->method('hasField')->will($this->returnValue(true));
  775. $options = array('direction' => 'asc');
  776. $result = $this->Paginator->validateSort($model, $options, array('title', 'id'));
  777. $this->assertFalse(isset($result['order']));
  778. $options = array('order' => 'invalid desc');
  779. $result = $this->Paginator->validateSort($model, $options, array('title', 'id'));
  780. $this->assertEquals($options['order'], $result['order']);
  781. }
  782. /**
  783. * test that maxLimit is respected
  784. *
  785. * @return void
  786. */
  787. public function testCheckLimit() {
  788. $result = $this->Paginator->checkLimit(array('limit' => 1000000, 'maxLimit' => 100));
  789. $this->assertEquals(100, $result['limit']);
  790. $result = $this->Paginator->checkLimit(array('limit' => 'sheep!', 'maxLimit' => 100));
  791. $this->assertEquals(1, $result['limit']);
  792. $result = $this->Paginator->checkLimit(array('limit' => '-1', 'maxLimit' => 100));
  793. $this->assertEquals(1, $result['limit']);
  794. $result = $this->Paginator->checkLimit(array('limit' => null, 'maxLimit' => 100));
  795. $this->assertEquals(1, $result['limit']);
  796. $result = $this->Paginator->checkLimit(array('limit' => 0, 'maxLimit' => 100));
  797. $this->assertEquals(1, $result['limit']);
  798. }
  799. /**
  800. * testPaginateMaxLimit
  801. *
  802. * @return void
  803. */
  804. public function testPaginateMaxLimit() {
  805. $Controller = new Controller($this->request);
  806. $Controller->uses = array('PaginatorControllerPost', 'ControllerComment');
  807. $Controller->passedArgs[] = '1';
  808. $Controller->constructClasses();
  809. $Controller->request->params['named'] = array(
  810. 'contain' => array('ControllerComment'), 'limit' => '1000'
  811. );
  812. $result = $Controller->paginate('PaginatorControllerPost');
  813. $this->assertEquals(100, $Controller->params['paging']['PaginatorControllerPost']['options']['limit']);
  814. $Controller->request->params['named'] = array(
  815. 'contain' => array('ControllerComment'), 'limit' => '1000', 'maxLimit' => 1000
  816. );
  817. $result = $Controller->paginate('PaginatorControllerPost');
  818. $this->assertEquals(100, $Controller->params['paging']['PaginatorControllerPost']['options']['limit']);
  819. $Controller->request->params['named'] = array('contain' => array('ControllerComment'), 'limit' => '10');
  820. $result = $Controller->paginate('PaginatorControllerPost');
  821. $this->assertEquals(10, $Controller->params['paging']['PaginatorControllerPost']['options']['limit']);
  822. $Controller->request->params['named'] = array('contain' => array('ControllerComment'), 'limit' => '1000');
  823. $Controller->paginate = array('maxLimit' => 2000, 'paramType' => 'named');
  824. $result = $Controller->paginate('PaginatorControllerPost');
  825. $this->assertEquals(1000, $Controller->params['paging']['PaginatorControllerPost']['options']['limit']);
  826. $Controller->request->params['named'] = array('contain' => array('ControllerComment'), 'limit' => '5000');
  827. $result = $Controller->paginate('PaginatorControllerPost');
  828. $this->assertEquals(2000, $Controller->params['paging']['PaginatorControllerPost']['options']['limit']);
  829. }
  830. /**
  831. * test paginate() and virtualField overlapping with real fields.
  832. *
  833. * @return void
  834. */
  835. public function testPaginateOrderVirtualFieldSharedWithRealField() {
  836. $Controller = new Controller($this->request);
  837. $Controller->uses = array('PaginatorControllerPost', 'PaginatorControllerComment');
  838. $Controller->constructClasses();
  839. $Controller->PaginatorControllerComment->virtualFields = array(
  840. 'title' => 'PaginatorControllerComment.comment'
  841. );
  842. $Controller->PaginatorControllerComment->bindModel(array(
  843. 'belongsTo' => array(
  844. 'PaginatorControllerPost' => array(
  845. 'className' => 'PaginatorControllerPost',
  846. 'foreignKey' => 'article_id'
  847. )
  848. )
  849. ), false);
  850. $Controller->paginate = array(
  851. 'fields' => array('PaginatorControllerComment.id', 'title', 'PaginatorControllerPost.title'),
  852. );
  853. $Controller->passedArgs = array('sort' => 'PaginatorControllerPost.title', 'dir' => 'asc');
  854. $result = $Controller->paginate('PaginatorControllerComment');
  855. $this->assertEquals(array(1, 2, 3, 4, 5, 6), Hash::extract($result, '{n}.PaginatorControllerComment.id'));
  856. }
  857. /**
  858. * test paginate() and custom find, to make sure the correct count is returned.
  859. *
  860. * @return void
  861. */
  862. public function testPaginateCustomFind() {
  863. $Controller =& new Controller($this->request);
  864. $Controller->uses = array('PaginatorCustomPost');
  865. $Controller->constructClasses();
  866. $data = array('author_id' => 3, 'title' => 'Fourth Article', 'body' => 'Article Body, unpublished', 'published' => 'N');
  867. $Controller->PaginatorCustomPost->create($data);
  868. $result = $Controller->PaginatorCustomPost->save();
  869. $this->assertTrue(!empty($result));
  870. $result = $Controller->paginate();
  871. $this->assertEquals(array(1, 2, 3, 4), Hash::extract($result, '{n}.PaginatorCustomPost.id'));
  872. $result = $Controller->params['paging']['PaginatorCustomPost'];
  873. $this->assertEquals(4, $result['current']);
  874. $this->assertEquals(4, $result['count']);
  875. $Controller->paginate = array('published');
  876. $result = $Controller->paginate();
  877. $this->assertEquals(array(1, 2, 3), Hash::extract($result, '{n}.PaginatorCustomPost.id'));
  878. $result = $Controller->params['paging']['PaginatorCustomPost'];
  879. $this->assertEquals(3, $result['current']);
  880. $this->assertEquals(3, $result['count']);
  881. $Controller->paginate = array('published', 'limit' => 2);
  882. $result = $Controller->paginate();
  883. $this->assertEquals(array(1, 2), Hash::extract($result, '{n}.PaginatorCustomPost.id'));
  884. $result = $Controller->params['paging']['PaginatorCustomPost'];
  885. $this->assertEquals(2, $result['current']);
  886. $this->assertEquals(3, $result['count']);
  887. $this->assertEquals(2, $result['pageCount']);
  888. $this->assertTrue($result['nextPage']);
  889. $this->assertFalse($result['prevPage']);
  890. }
  891. /**
  892. * test paginate() and custom find with fields array, to make sure the correct count is returned.
  893. *
  894. * @return void
  895. */
  896. public function testPaginateCustomFindFieldsArray() {
  897. $Controller =& new Controller($this->request);
  898. $Controller->uses = array('PaginatorCustomPost');
  899. $Controller->constructClasses();
  900. $data = array('author_id' => 3, 'title' => 'Fourth Article', 'body' => 'Article Body, unpublished', 'published' => 'N');
  901. $Controller->PaginatorCustomPost->create($data);
  902. $result = $Controller->PaginatorCustomPost->save();
  903. $this->assertTrue(!empty($result));
  904. $Controller->paginate = array(
  905. 'list',
  906. 'conditions' => array('PaginatorCustomPost.published' => 'Y'),
  907. 'limit' => 2
  908. );
  909. $result = $Controller->paginate();
  910. $expected = array(
  911. 1 => 'First Post',
  912. 2 => 'Second Post',
  913. );
  914. $this->assertEquals($expected, $result);
  915. $result = $Controller->params['paging']['PaginatorCustomPost'];
  916. $this->assertEquals(2, $result['current']);
  917. $this->assertEquals(3, $result['count']);
  918. $this->assertEquals(2, $result['pageCount']);
  919. $this->assertTrue($result['nextPage']);
  920. $this->assertFalse($result['prevPage']);
  921. }
  922. /**
  923. * test paginate() and custom find with fields array, to make sure the correct count is returned.
  924. *
  925. * @return void
  926. */
  927. public function testPaginateCustomFindGroupBy() {
  928. $Controller =& new Controller($this->request);
  929. $Controller->uses = array('PaginatorCustomPost');
  930. $Controller->constructClasses();
  931. $data = array('author_id' => 2, 'title' => 'Fourth Article', 'body' => 'Article Body, unpublished', 'published' => 'N');
  932. $Controller->PaginatorCustomPost->create($data);
  933. $result = $Controller->PaginatorCustomPost->save();
  934. $this->assertTrue(!empty($result));
  935. $Controller->paginate = array(
  936. 'totals',
  937. 'limit' => 2
  938. );
  939. $result = $Controller->paginate();
  940. $expected = array(
  941. array(
  942. 'PaginatorCustomPost' => array(
  943. 'author_id' => '1',
  944. 'total_posts' => '2'
  945. )
  946. ),
  947. array(
  948. 'PaginatorCustomPost' => array(
  949. 'author_id' => '2',
  950. 'total_posts' => '1'
  951. )
  952. )
  953. );
  954. $this->assertEquals($expected, $result);
  955. $result = $Controller->params['paging']['PaginatorCustomPost'];
  956. $this->assertEquals(2, $result['current']);
  957. $this->assertEquals(3, $result['count']);
  958. $this->assertEquals(2, $result['pageCount']);
  959. $this->assertTrue($result['nextPage']);
  960. $this->assertFalse($result['prevPage']);
  961. $Controller->paginate = array(
  962. 'totals',
  963. 'limit' => 2,
  964. 'page' => 2
  965. );
  966. $result = $Controller->paginate();
  967. $expected = array(
  968. array(
  969. 'PaginatorCustomPost' => array(
  970. 'author_id' => '3',
  971. 'total_posts' => '1'
  972. )
  973. ),
  974. );
  975. $this->assertEquals($expected, $result);
  976. $result = $Controller->params['paging']['PaginatorCustomPost'];
  977. $this->assertEquals(1, $result['current']);
  978. $this->assertEquals(3, $result['count']);
  979. $this->assertEquals(2, $result['pageCount']);
  980. $this->assertFalse($result['nextPage']);
  981. $this->assertTrue($result['prevPage']);
  982. }
  983. /**
  984. * test paginate() and custom find with returning other query on count operation,
  985. * to make sure the correct count is returned.
  986. *
  987. * @return void
  988. */
  989. public function testPaginateCustomFindCount() {
  990. $Controller =& new Controller($this->request);
  991. $Controller->uses = array('PaginatorCustomPost');
  992. $Controller->constructClasses();
  993. $data = array('author_id' => 2, 'title' => 'Fourth Article', 'body' => 'Article Body, unpublished', 'published' => 'N');
  994. $Controller->PaginatorCustomPost->create($data);
  995. $result = $Controller->PaginatorCustomPost->save();
  996. $this->assertTrue(!empty($result));
  997. $Controller->paginate = array(
  998. 'totalsOperation',
  999. 'limit' => 2
  1000. );
  1001. $result = $Controller->paginate();
  1002. $expected = array(
  1003. array(
  1004. 'PaginatorCustomPost' => array(
  1005. 'author_id' => '1',
  1006. 'total_posts' => '2'
  1007. ),
  1008. 'Author' => array(
  1009. 'user' => 'mariano',
  1010. )
  1011. ),
  1012. array(
  1013. 'PaginatorCustomPost' => array(
  1014. 'author_id' => '2',
  1015. 'total_posts' => '1'
  1016. ),
  1017. 'Author' => array(
  1018. 'user' => 'nate'
  1019. )
  1020. )
  1021. );
  1022. $this->assertEquals($expected, $result);
  1023. $result = $Controller->params['paging']['PaginatorCustomPost'];
  1024. $this->assertEquals(2, $result['current']);
  1025. $this->assertEquals(3, $result['count']);
  1026. $this->assertEquals(2, $result['pageCount']);
  1027. $this->assertTrue($result['nextPage']);
  1028. $this->assertFalse($result['prevPage']);
  1029. }
  1030. }