PaginatorTestTrait.php 44 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436
  1. <?php
  2. declare(strict_types=1);
  3. /**
  4. * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
  5. * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
  6. *
  7. * Licensed under The MIT License
  8. * For full copyright and license information, please see the LICENSE.txt
  9. * Redistributions of files must retain the above copyright notice
  10. *
  11. * @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
  12. * @link http://cakephp.org CakePHP(tm) Project
  13. * @since 3.9.0
  14. * @license http://www.opensource.org/licenses/mit-license.php MIT License
  15. */
  16. namespace Cake\Test\TestCase\Datasource;
  17. use Cake\Core\Configure;
  18. use Cake\Datasource\ConnectionManager;
  19. use Cake\Datasource\EntityInterface;
  20. use Cake\Datasource\Exception\PageOutOfBoundsException;
  21. use Cake\Datasource\Paginator;
  22. use Cake\Datasource\RepositoryInterface;
  23. trait PaginatorTestTrait
  24. {
  25. /**
  26. * @var \Cake\Datasource\Paginator
  27. */
  28. protected $Paginator;
  29. /**
  30. * @var \Cake\Datasource\RepositoryInterface|\PHPUnit\Framework\MockObject\MockObject
  31. */
  32. protected $Post;
  33. /**
  34. * setup
  35. *
  36. * @return void
  37. */
  38. public function setUp(): void
  39. {
  40. parent::setUp();
  41. Configure::write('App.namespace', 'TestApp');
  42. $this->Paginator = new Paginator();
  43. $this->Post = $this->getMockRepository();
  44. }
  45. /**
  46. * tearDown
  47. *
  48. * @return void
  49. */
  50. public function tearDown(): void
  51. {
  52. parent::tearDown();
  53. $this->getTableLocator()->clear();
  54. }
  55. /**
  56. * Test that non-numeric values are rejected for page, and limit
  57. *
  58. * @return void
  59. */
  60. public function testPageParamCasting()
  61. {
  62. $this->Post->expects($this->any())
  63. ->method('getAlias')
  64. ->will($this->returnValue('Posts'));
  65. $query = $this->_getMockFindQuery();
  66. $this->Post->expects($this->any())
  67. ->method('find')
  68. ->will($this->returnValue($query));
  69. $params = ['page' => '1 " onclick="alert(\'xss\');">'];
  70. $settings = ['limit' => 1, 'maxLimit' => 10];
  71. $this->Paginator->paginate($this->Post, $params, $settings);
  72. $pagingParams = $this->Paginator->getPagingParams();
  73. $this->assertSame(1, $pagingParams['Posts']['page'], 'XSS exploit opened');
  74. }
  75. /**
  76. * test that unknown keys in the default settings are
  77. * passed to the find operations.
  78. *
  79. * @return void
  80. */
  81. public function testPaginateExtraParams()
  82. {
  83. $params = ['page' => '-1'];
  84. $settings = [
  85. 'PaginatorPosts' => [
  86. 'contain' => ['PaginatorAuthor'],
  87. 'maxLimit' => 10,
  88. 'group' => 'PaginatorPosts.published',
  89. 'order' => ['PaginatorPosts.id' => 'ASC'],
  90. ],
  91. ];
  92. $table = $this->_getMockPosts(['query']);
  93. $query = $this->_getMockFindQuery();
  94. $table->expects($this->once())
  95. ->method('query')
  96. ->will($this->returnValue($query));
  97. $query->expects($this->once())
  98. ->method('applyOptions')
  99. ->with([
  100. 'contain' => ['PaginatorAuthor'],
  101. 'group' => 'PaginatorPosts.published',
  102. 'limit' => 10,
  103. 'order' => ['PaginatorPosts.id' => 'ASC'],
  104. 'page' => 1,
  105. 'whitelist' => ['limit', 'sort', 'page', 'direction'],
  106. 'allowedParameters' => ['limit', 'sort', 'page', 'direction'],
  107. 'scope' => null,
  108. 'sort' => 'PaginatorPosts.id',
  109. ]);
  110. $this->Paginator->paginate($table, $params, $settings);
  111. }
  112. /**
  113. * Test to make sure options get sent to custom finder methods via paginate
  114. *
  115. * @return void
  116. */
  117. public function testPaginateCustomFinderOptions()
  118. {
  119. $this->loadFixtures('Posts');
  120. $settings = [
  121. 'PaginatorPosts' => [
  122. 'finder' => ['author' => ['author_id' => 1]],
  123. ],
  124. ];
  125. $table = $this->getTableLocator()->get('PaginatorPosts');
  126. $expected = $table
  127. ->find('author', [
  128. 'conditions' => [
  129. 'PaginatorPosts.author_id' => 1,
  130. ],
  131. ])
  132. ->count();
  133. $result = $this->Paginator->paginate($table, [], $settings)->count();
  134. $this->assertEquals($expected, $result);
  135. }
  136. /**
  137. * Test that nested eager loaders don't trigger invalid SQL errors.
  138. *
  139. * @return void
  140. */
  141. public function testPaginateNestedEagerLoader()
  142. {
  143. $this->loadFixtures('Articles', 'Tags', 'Authors', 'ArticlesTags', 'AuthorsTags');
  144. $articles = $this->getTableLocator()->get('Articles');
  145. $articles->belongsToMany('Tags');
  146. $tags = $this->getTableLocator()->get('Tags');
  147. $tags->belongsToMany('Authors');
  148. $articles->getEventManager()->on('Model.beforeFind', function ($event, $query) {
  149. $query ->matching('Tags', function ($q) {
  150. return $q->matching('Authors', function ($q) {
  151. return $q->where(['Authors.name' => 'larry']);
  152. });
  153. });
  154. });
  155. $results = $this->Paginator->paginate($articles);
  156. $result = $results->first();
  157. $this->assertInstanceOf(EntityInterface::class, $result);
  158. $this->assertInstanceOf(EntityInterface::class, $result->_matchingData['Tags']);
  159. $this->assertInstanceOf(EntityInterface::class, $result->_matchingData['Authors']);
  160. }
  161. /**
  162. * test that flat default pagination parameters work.
  163. *
  164. * @return void
  165. */
  166. public function testDefaultPaginateParams()
  167. {
  168. $settings = [
  169. 'order' => ['PaginatorPosts.id' => 'DESC'],
  170. 'maxLimit' => 10,
  171. ];
  172. $table = $this->_getMockPosts(['query']);
  173. $query = $this->_getMockFindQuery();
  174. $table->expects($this->once())
  175. ->method('query')
  176. ->will($this->returnValue($query));
  177. $query->expects($this->once())
  178. ->method('applyOptions')
  179. ->with([
  180. 'limit' => 10,
  181. 'page' => 1,
  182. 'order' => ['PaginatorPosts.id' => 'DESC'],
  183. 'whitelist' => ['limit', 'sort', 'page', 'direction'],
  184. 'allowedParameters' => ['limit', 'sort', 'page', 'direction'],
  185. 'scope' => null,
  186. 'sort' => 'PaginatorPosts.id',
  187. ]);
  188. $this->Paginator->paginate($table, [], $settings);
  189. }
  190. /**
  191. * Tests that flat default pagination parameters work for multi order.
  192. *
  193. * @return void
  194. */
  195. public function testDefaultPaginateParamsMultiOrder()
  196. {
  197. $settings = [
  198. 'order' => ['PaginatorPosts.id' => 'DESC', 'PaginatorPosts.title' => 'ASC'],
  199. ];
  200. $table = $this->_getMockPosts(['query']);
  201. $query = $this->_getMockFindQuery();
  202. $table->expects($this->once())
  203. ->method('query')
  204. ->will($this->returnValue($query));
  205. $query->expects($this->once())
  206. ->method('applyOptions')
  207. ->with([
  208. 'limit' => 20,
  209. 'page' => 1,
  210. 'order' => $settings['order'],
  211. 'whitelist' => ['limit', 'sort', 'page', 'direction'],
  212. 'allowedParameters' => ['limit', 'sort', 'page', 'direction'],
  213. 'scope' => null,
  214. 'sort' => null,
  215. ]);
  216. $this->Paginator->paginate($table, [], $settings);
  217. $pagingParams = $this->Paginator->getPagingParams();
  218. $this->assertNull($pagingParams['PaginatorPosts']['direction']);
  219. $this->assertFalse($pagingParams['PaginatorPosts']['sortDefault']);
  220. $this->assertFalse($pagingParams['PaginatorPosts']['directionDefault']);
  221. }
  222. /**
  223. * test that default sort and default direction are injected into request
  224. *
  225. * @return void
  226. */
  227. public function testDefaultPaginateParamsIntoRequest()
  228. {
  229. $settings = [
  230. 'order' => ['PaginatorPosts.id' => 'DESC'],
  231. 'maxLimit' => 10,
  232. ];
  233. $table = $this->_getMockPosts(['query']);
  234. $query = $this->_getMockFindQuery();
  235. $table->expects($this->once())
  236. ->method('query')
  237. ->will($this->returnValue($query));
  238. $query->expects($this->once())
  239. ->method('applyOptions')
  240. ->with([
  241. 'limit' => 10,
  242. 'page' => 1,
  243. 'order' => ['PaginatorPosts.id' => 'DESC'],
  244. 'whitelist' => ['limit', 'sort', 'page', 'direction'],
  245. 'allowedParameters' => ['limit', 'sort', 'page', 'direction'],
  246. 'scope' => null,
  247. 'sort' => 'PaginatorPosts.id',
  248. ]);
  249. $this->Paginator->paginate($table, [], $settings);
  250. $pagingParams = $this->Paginator->getPagingParams();
  251. $this->assertEquals('PaginatorPosts.id', $pagingParams['PaginatorPosts']['sortDefault']);
  252. $this->assertEquals('DESC', $pagingParams['PaginatorPosts']['directionDefault']);
  253. }
  254. /**
  255. * test that option merging prefers specific models
  256. *
  257. * @return void
  258. */
  259. public function testMergeOptionsModelSpecific()
  260. {
  261. $settings = [
  262. 'page' => 1,
  263. 'limit' => 20,
  264. 'maxLimit' => 100,
  265. 'Posts' => [
  266. 'page' => 1,
  267. 'limit' => 10,
  268. 'maxLimit' => 50,
  269. ],
  270. 'allowedParameters' => ['limit', 'sort', 'page', 'direction'],
  271. 'whitelist' => ['limit', 'sort', 'page', 'direction'],
  272. ];
  273. $defaults = $this->Paginator->getDefaults('Silly', $settings);
  274. $result = $this->Paginator->mergeOptions([], $defaults);
  275. $this->assertEquals($settings, $result);
  276. $defaults = $this->Paginator->getDefaults('Posts', $settings);
  277. $result = $this->Paginator->mergeOptions([], $defaults);
  278. $expected = [
  279. 'page' => 1,
  280. 'limit' => 10,
  281. 'maxLimit' => 50,
  282. 'allowedParameters' => ['limit', 'sort', 'page', 'direction'],
  283. 'whitelist' => ['limit', 'sort', 'page', 'direction'],
  284. ];
  285. $this->assertEquals($expected, $result);
  286. }
  287. /**
  288. * test mergeOptions with custom scope
  289. *
  290. * @return void
  291. */
  292. public function testMergeOptionsCustomScope()
  293. {
  294. $params = [
  295. 'page' => 10,
  296. 'limit' => 10,
  297. 'scope' => [
  298. 'page' => 2,
  299. 'limit' => 5,
  300. ],
  301. ];
  302. $settings = [
  303. 'page' => 1,
  304. 'limit' => 20,
  305. 'maxLimit' => 100,
  306. 'finder' => 'myCustomFind',
  307. ];
  308. $defaults = $this->Paginator->getDefaults('Post', $settings);
  309. $result = $this->Paginator->mergeOptions($params, $defaults);
  310. $expected = [
  311. 'page' => 10,
  312. 'limit' => 10,
  313. 'maxLimit' => 100,
  314. 'finder' => 'myCustomFind',
  315. 'whitelist' => ['limit', 'sort', 'page', 'direction'],
  316. 'allowedParameters' => ['limit', 'sort', 'page', 'direction'],
  317. ];
  318. $this->assertEquals($expected, $result);
  319. $settings = [
  320. 'page' => 1,
  321. 'limit' => 20,
  322. 'maxLimit' => 100,
  323. 'finder' => 'myCustomFind',
  324. 'scope' => 'nonexistent',
  325. ];
  326. $defaults = $this->Paginator->getDefaults('Post', $settings);
  327. $result = $this->Paginator->mergeOptions($params, $defaults);
  328. $expected = [
  329. 'page' => 1,
  330. 'limit' => 20,
  331. 'maxLimit' => 100,
  332. 'finder' => 'myCustomFind',
  333. 'whitelist' => ['limit', 'sort', 'page', 'direction'],
  334. 'allowedParameters' => ['limit', 'sort', 'page', 'direction'],
  335. 'scope' => 'nonexistent',
  336. ];
  337. $this->assertEquals($expected, $result);
  338. $settings = [
  339. 'page' => 1,
  340. 'limit' => 20,
  341. 'maxLimit' => 100,
  342. 'finder' => 'myCustomFind',
  343. 'scope' => 'scope',
  344. ];
  345. $defaults = $this->Paginator->getDefaults('Post', $settings);
  346. $result = $this->Paginator->mergeOptions($params, $defaults);
  347. $expected = [
  348. 'page' => 2,
  349. 'limit' => 5,
  350. 'maxLimit' => 100,
  351. 'finder' => 'myCustomFind',
  352. 'whitelist' => ['limit', 'sort', 'page', 'direction'],
  353. 'allowedParameters' => ['limit', 'sort', 'page', 'direction'],
  354. 'scope' => 'scope',
  355. ];
  356. $this->assertEquals($expected, $result);
  357. }
  358. /**
  359. * test mergeOptions with customFind key
  360. *
  361. * @return void
  362. */
  363. public function testMergeOptionsCustomFindKey()
  364. {
  365. $params = [
  366. 'page' => 10,
  367. 'limit' => 10,
  368. ];
  369. $settings = [
  370. 'page' => 1,
  371. 'limit' => 20,
  372. 'maxLimit' => 100,
  373. 'finder' => 'myCustomFind',
  374. ];
  375. $defaults = $this->Paginator->getDefaults('Post', $settings);
  376. $result = $this->Paginator->mergeOptions($params, $defaults);
  377. $expected = [
  378. 'page' => 10,
  379. 'limit' => 10,
  380. 'maxLimit' => 100,
  381. 'finder' => 'myCustomFind',
  382. 'whitelist' => ['limit', 'sort', 'page', 'direction'],
  383. 'allowedParameters' => ['limit', 'sort', 'page', 'direction'],
  384. ];
  385. $this->assertEquals($expected, $result);
  386. }
  387. /**
  388. * test merging options from the querystring.
  389. *
  390. * @return void
  391. */
  392. public function testMergeOptionsQueryString()
  393. {
  394. $params = [
  395. 'page' => 99,
  396. 'limit' => 75,
  397. ];
  398. $settings = [
  399. 'page' => 1,
  400. 'limit' => 20,
  401. 'maxLimit' => 100,
  402. ];
  403. $defaults = $this->Paginator->getDefaults('Post', $settings);
  404. $result = $this->Paginator->mergeOptions($params, $defaults);
  405. $expected = [
  406. 'page' => 99,
  407. 'limit' => 75,
  408. 'maxLimit' => 100,
  409. 'whitelist' => ['limit', 'sort', 'page', 'direction'],
  410. 'allowedParameters' => ['limit', 'sort', 'page', 'direction'],
  411. ];
  412. $this->assertEquals($expected, $result);
  413. }
  414. /**
  415. * test that the default allowedParameters doesn't let people screw with things they should not be allowed to.
  416. *
  417. * @return void
  418. */
  419. public function testMergeOptionsDefaultAllowedParameters()
  420. {
  421. $params = [
  422. 'page' => 10,
  423. 'limit' => 10,
  424. 'fields' => ['bad.stuff'],
  425. 'recursive' => 1000,
  426. 'conditions' => ['bad.stuff'],
  427. 'contain' => ['bad'],
  428. ];
  429. $settings = [
  430. 'page' => 1,
  431. 'limit' => 20,
  432. 'maxLimit' => 100,
  433. ];
  434. $defaults = $this->Paginator->getDefaults('Post', $settings);
  435. $result = $this->Paginator->mergeOptions($params, $defaults);
  436. $expected = [
  437. 'page' => 10,
  438. 'limit' => 10,
  439. 'maxLimit' => 100,
  440. 'whitelist' => ['limit', 'sort', 'page', 'direction'],
  441. 'allowedParameters' => ['limit', 'sort', 'page', 'direction'],
  442. ];
  443. $this->assertEquals($expected, $result);
  444. }
  445. /**
  446. * test that modifying the deprecated whitelist works.
  447. *
  448. * @return void
  449. */
  450. public function testMergeOptionsExtraWhitelist()
  451. {
  452. $params = [
  453. 'page' => 10,
  454. 'limit' => 10,
  455. 'fields' => ['bad.stuff'],
  456. 'recursive' => 1000,
  457. 'conditions' => ['bad.stuff'],
  458. 'contain' => ['bad'],
  459. ];
  460. $settings = [
  461. 'page' => 1,
  462. 'limit' => 20,
  463. 'maxLimit' => 100,
  464. ];
  465. $this->deprecated(function () use ($settings, $params) {
  466. $this->Paginator->setConfig('whitelist', ['fields']);
  467. $defaults = $this->Paginator->getDefaults('Post', $settings);
  468. $result = $this->Paginator->mergeOptions($params, $defaults);
  469. $expected = [
  470. 'page' => 10,
  471. 'limit' => 10,
  472. 'maxLimit' => 100,
  473. 'fields' => ['bad.stuff'],
  474. 'whitelist' => ['limit', 'sort', 'page', 'direction', 'fields'],
  475. 'allowedParameters' => ['limit', 'sort', 'page', 'direction', 'fields'],
  476. ];
  477. $this->assertEquals($expected, $result);
  478. });
  479. }
  480. /**
  481. * test mergeOptions with limit > maxLimit in code.
  482. *
  483. * @return void
  484. */
  485. public function testMergeOptionsMaxLimit()
  486. {
  487. $settings = [
  488. 'limit' => 200,
  489. 'paramType' => 'named',
  490. ];
  491. $defaults = $this->Paginator->getDefaults('Post', $settings);
  492. $result = $this->Paginator->mergeOptions([], $defaults);
  493. $expected = [
  494. 'page' => 1,
  495. 'limit' => 100,
  496. 'maxLimit' => 100,
  497. 'paramType' => 'named',
  498. 'whitelist' => ['limit', 'sort', 'page', 'direction'],
  499. 'allowedParameters' => ['limit', 'sort', 'page', 'direction'],
  500. ];
  501. $this->assertEquals($expected, $result);
  502. $settings = [
  503. 'maxLimit' => 10,
  504. 'paramType' => 'named',
  505. ];
  506. $defaults = $this->Paginator->getDefaults('Post', $settings);
  507. $result = $this->Paginator->mergeOptions([], $defaults);
  508. $expected = [
  509. 'page' => 1,
  510. 'limit' => 10,
  511. 'maxLimit' => 10,
  512. 'paramType' => 'named',
  513. 'whitelist' => ['limit', 'sort', 'page', 'direction'],
  514. 'allowedParameters' => ['limit', 'sort', 'page', 'direction'],
  515. ];
  516. $this->assertEquals($expected, $result);
  517. }
  518. /**
  519. * test getDefaults with limit > maxLimit in code.
  520. *
  521. * @return void
  522. */
  523. public function testGetDefaultMaxLimit()
  524. {
  525. $settings = [
  526. 'page' => 1,
  527. 'limit' => 2,
  528. 'maxLimit' => 10,
  529. 'order' => [
  530. 'Users.username' => 'asc',
  531. ],
  532. ];
  533. $defaults = $this->Paginator->getDefaults('Post', $settings);
  534. $result = $this->Paginator->mergeOptions([], $defaults);
  535. $expected = [
  536. 'page' => 1,
  537. 'limit' => 2,
  538. 'maxLimit' => 10,
  539. 'order' => [
  540. 'Users.username' => 'asc',
  541. ],
  542. 'whitelist' => ['limit', 'sort', 'page', 'direction'],
  543. 'allowedParameters' => ['limit', 'sort', 'page', 'direction'],
  544. ];
  545. $this->assertEquals($expected, $result);
  546. $settings = [
  547. 'page' => 1,
  548. 'limit' => 100,
  549. 'maxLimit' => 10,
  550. 'order' => [
  551. 'Users.username' => 'asc',
  552. ],
  553. ];
  554. $defaults = $this->Paginator->getDefaults('Post', $settings);
  555. $result = $this->Paginator->mergeOptions([], $defaults);
  556. $expected = [
  557. 'page' => 1,
  558. 'limit' => 10,
  559. 'maxLimit' => 10,
  560. 'order' => [
  561. 'Users.username' => 'asc',
  562. ],
  563. 'whitelist' => ['limit', 'sort', 'page', 'direction'],
  564. 'allowedParameters' => ['limit', 'sort', 'page', 'direction'],
  565. ];
  566. $this->assertEquals($expected, $result);
  567. }
  568. /**
  569. * Integration test to ensure that validateSort is being used by paginate()
  570. *
  571. * @return void
  572. */
  573. public function testValidateSortInvalid()
  574. {
  575. $table = $this->_getMockPosts(['query']);
  576. $query = $this->_getMockFindQuery();
  577. $table->expects($this->once())
  578. ->method('query')
  579. ->will($this->returnValue($query));
  580. $query->expects($this->once())->method('applyOptions')
  581. ->with([
  582. 'limit' => 20,
  583. 'page' => 1,
  584. 'order' => ['PaginatorPosts.id' => 'asc'],
  585. 'whitelist' => ['limit', 'sort', 'page', 'direction'],
  586. 'allowedParameters' => ['limit', 'sort', 'page', 'direction'],
  587. 'scope' => null,
  588. 'sort' => 'id',
  589. ]);
  590. $params = [
  591. 'page' => 1,
  592. 'sort' => 'id',
  593. 'direction' => 'herp',
  594. ];
  595. $this->Paginator->paginate($table, $params);
  596. $pagingParams = $this->Paginator->getPagingParams();
  597. $this->assertEquals('id', $pagingParams['PaginatorPosts']['sort']);
  598. $this->assertEquals('asc', $pagingParams['PaginatorPosts']['direction']);
  599. }
  600. /**
  601. * test that invalid directions are ignored.
  602. *
  603. * @return void
  604. */
  605. public function testValidateSortInvalidDirection()
  606. {
  607. $model = $this->getMockRepository();
  608. $model->expects($this->any())
  609. ->method('getAlias')
  610. ->will($this->returnValue('model'));
  611. $model->expects($this->any())
  612. ->method('hasField')
  613. ->will($this->returnValue(true));
  614. $options = ['sort' => 'something', 'direction' => 'boogers'];
  615. $result = $this->Paginator->validateSort($model, $options);
  616. $this->assertEquals('asc', $result['order']['model.something']);
  617. }
  618. /**
  619. * Test that "sort" and "direction" in paging params is properly set based
  620. * on initial value of "order" in paging settings.
  621. *
  622. * @return void
  623. */
  624. public function testValidaSortInitialSortAndDirection()
  625. {
  626. $table = $this->_getMockPosts(['query']);
  627. $query = $this->_getMockFindQuery();
  628. $table->expects($this->once())
  629. ->method('query')
  630. ->will($this->returnValue($query));
  631. $query->expects($this->once())->method('applyOptions')
  632. ->with([
  633. 'limit' => 20,
  634. 'page' => 1,
  635. 'order' => ['PaginatorPosts.id' => 'asc'],
  636. 'whitelist' => ['limit', 'sort', 'page', 'direction'],
  637. 'allowedParameters' => ['limit', 'sort', 'page', 'direction'],
  638. 'sort' => 'id',
  639. 'scope' => null,
  640. 'sortWhitelist' => ['id'],
  641. 'sortableFields' => ['id'],
  642. ]);
  643. $options = [
  644. 'order' => [
  645. 'id' => 'asc',
  646. ],
  647. 'sortableFields' => ['id'],
  648. ];
  649. $this->Paginator->paginate($table, [], $options);
  650. $pagingParams = $this->Paginator->getPagingParams();
  651. $this->assertEquals('id', $pagingParams['PaginatorPosts']['sort']);
  652. $this->assertEquals('asc', $pagingParams['PaginatorPosts']['direction']);
  653. }
  654. /**
  655. * Test that "sort" and "direction" in paging params is properly set based
  656. * on initial value of "order" in paging settings.
  657. *
  658. * @return void
  659. */
  660. public function testValidateSortAndDirectionAliased()
  661. {
  662. $table = $this->_getMockPosts(['query']);
  663. $query = $this->_getMockFindQuery();
  664. $table->expects($this->once())
  665. ->method('query')
  666. ->will($this->returnValue($query));
  667. $query->expects($this->once())->method('applyOptions')
  668. ->with([
  669. 'limit' => 20,
  670. 'page' => 1,
  671. 'order' => ['PaginatorPosts.title' => 'asc'],
  672. 'whitelist' => ['limit', 'sort', 'page', 'direction'],
  673. 'allowedParameters' => ['limit', 'sort', 'page', 'direction'],
  674. 'sort' => 'title',
  675. 'scope' => null,
  676. ]);
  677. $options = [
  678. 'order' => [
  679. 'Articles.title' => 'desc',
  680. ],
  681. ];
  682. $queryParams = [
  683. 'page' => 1,
  684. 'sort' => 'title',
  685. 'direction' => 'asc',
  686. ];
  687. $this->Paginator->paginate($table, $queryParams, $options);
  688. $pagingParams = $this->Paginator->getPagingParams();
  689. $this->assertEquals('title', $pagingParams['PaginatorPosts']['sort']);
  690. $this->assertEquals('asc', $pagingParams['PaginatorPosts']['direction']);
  691. $this->assertEquals('Articles.title', $pagingParams['PaginatorPosts']['sortDefault']);
  692. $this->assertEquals('desc', $pagingParams['PaginatorPosts']['directionDefault']);
  693. }
  694. /**
  695. * testValidateSortRetainsOriginalSortValue
  696. *
  697. * @return void
  698. * @see https://github.com/cakephp/cakephp/issues/11740
  699. */
  700. public function testValidateSortRetainsOriginalSortValue()
  701. {
  702. $table = $this->_getMockPosts(['query']);
  703. $query = $this->_getMockFindQuery();
  704. $table->expects($this->once())
  705. ->method('query')
  706. ->will($this->returnValue($query));
  707. $query->expects($this->once())->method('applyOptions')
  708. ->with([
  709. 'limit' => 20,
  710. 'page' => 1,
  711. 'order' => ['PaginatorPosts.id' => 'asc'],
  712. 'whitelist' => ['limit', 'sort', 'page', 'direction'],
  713. 'allowedParameters' => ['limit', 'sort', 'page', 'direction'],
  714. 'scope' => null,
  715. 'sortWhitelist' => ['id'],
  716. 'sortableFields' => ['id'],
  717. 'sort' => 'id',
  718. ]);
  719. $params = [
  720. 'page' => 1,
  721. 'sort' => 'id',
  722. 'direction' => 'herp',
  723. ];
  724. $options = [
  725. 'sortableFields' => ['id'],
  726. ];
  727. $this->Paginator->paginate($table, $params, $options);
  728. $pagingParams = $this->Paginator->getPagingParams();
  729. $this->assertEquals('id', $pagingParams['PaginatorPosts']['sort']);
  730. }
  731. /**
  732. * Test that a really large page number gets clamped to the max page size.
  733. *
  734. * @return void
  735. */
  736. public function testOutOfRangePageNumberGetsClamped()
  737. {
  738. $this->loadFixtures('Posts');
  739. $params['page'] = 3000;
  740. $table = $this->getTableLocator()->get('PaginatorPosts');
  741. try {
  742. $this->Paginator->paginate($table, $params);
  743. $this->fail('No exception raised');
  744. } catch (PageOutOfBoundsException $exception) {
  745. $this->assertEquals(
  746. 'Page number 3000 could not be found.',
  747. $exception->getMessage()
  748. );
  749. $this->assertSame(
  750. [
  751. 'requestedPage' => 3000,
  752. 'pagingParams' => $this->Paginator->getPagingParams(),
  753. ],
  754. $exception->getAttributes()
  755. );
  756. }
  757. }
  758. /**
  759. * Test that a really REALLY large page number gets clamped to the max page size.
  760. *
  761. * @return void
  762. */
  763. public function testOutOfVeryBigPageNumberGetsClamped()
  764. {
  765. $this->expectException(\Cake\Datasource\Exception\PageOutOfBoundsException::class);
  766. $this->loadFixtures('Posts');
  767. $params = [
  768. 'page' => '3000000000000000000000000',
  769. ];
  770. $table = $this->getTableLocator()->get('PaginatorPosts');
  771. $this->Paginator->paginate($table, $params);
  772. }
  773. /**
  774. * test that fields not in sortableFields won't be part of order conditions.
  775. *
  776. * @return void
  777. */
  778. public function testValidateAllowedSortFailure()
  779. {
  780. $model = $this->mockAliasHasFieldModel();
  781. $options = [
  782. 'sort' => 'body',
  783. 'direction' => 'asc',
  784. 'sortableFields' => ['title', 'id'],
  785. ];
  786. $result = $this->Paginator->validateSort($model, $options);
  787. $this->assertEquals([], $result['order']);
  788. }
  789. /**
  790. * test that fields not in whitelist won't be part of order conditions.
  791. *
  792. * @return void
  793. */
  794. public function testValidateSortWhitelistFailure()
  795. {
  796. $this->deprecated(function () {
  797. $model = $this->mockAliasHasFieldModel();
  798. $options = [
  799. 'sort' => 'body',
  800. 'direction' => 'asc',
  801. 'sortWhitelist' => ['title', 'id'],
  802. ];
  803. $result = $this->Paginator->validateSort($model, $options);
  804. $this->assertEquals([], $result['order']);
  805. });
  806. }
  807. /**
  808. * test that fields in the sortableFields are not validated
  809. *
  810. * @return void
  811. */
  812. public function testValidateAllowedSortTrusted()
  813. {
  814. $model = $this->mockAliasHasFieldModel();
  815. $options = [
  816. 'sort' => 'body',
  817. 'direction' => 'asc',
  818. 'allowedsort' => ['body'],
  819. ];
  820. $result = $this->Paginator->validateSort($model, $options);
  821. $expected = ['model.body' => 'asc'];
  822. $this->assertEquals(
  823. $expected,
  824. $result['order'],
  825. 'Trusted fields in schema should be prefixed'
  826. );
  827. }
  828. /**
  829. * test that sortableFields as empty array does not allow any sorting
  830. *
  831. * @return void
  832. */
  833. public function testValidateAllowedSortEmpty()
  834. {
  835. $model = $this->mockAliasHasFieldModel();
  836. $options = [
  837. 'order' => [
  838. 'body' => 'asc',
  839. 'foo.bar' => 'asc',
  840. ],
  841. 'sort' => 'body',
  842. 'direction' => 'asc',
  843. 'sortableFields' => [],
  844. ];
  845. $result = $this->Paginator->validateSort($model, $options);
  846. $this->assertSame([], $result['order'], 'No sort should be applied');
  847. }
  848. /**
  849. * test that fields in the sortableFields are not validated
  850. *
  851. * @return void
  852. */
  853. public function testValidateAllowedSortNotInSchema()
  854. {
  855. $model = $this->getMockRepository();
  856. $model->expects($this->any())
  857. ->method('getAlias')
  858. ->will($this->returnValue('model'));
  859. $model->expects($this->once())->method('hasField')
  860. ->will($this->returnValue(false));
  861. $options = [
  862. 'sort' => 'score',
  863. 'direction' => 'asc',
  864. 'sortableFields' => ['score'],
  865. ];
  866. $result = $this->Paginator->validateSort($model, $options);
  867. $expected = ['score' => 'asc'];
  868. $this->assertEquals(
  869. $expected,
  870. $result['order'],
  871. 'Trusted fields not in schema should not be altered'
  872. );
  873. }
  874. /**
  875. * test that multiple fields in the sortableFields are not validated and properly aliased.
  876. *
  877. * @return void
  878. */
  879. public function testValidateAllowedSortMultiple()
  880. {
  881. $model = $this->mockAliasHasFieldModel();
  882. $options = [
  883. 'order' => [
  884. 'body' => 'asc',
  885. 'foo.bar' => 'asc',
  886. ],
  887. 'sortableFields' => ['body', 'foo.bar'],
  888. ];
  889. $result = $this->Paginator->validateSort($model, $options);
  890. $expected = [
  891. 'model.body' => 'asc',
  892. 'foo.bar' => 'asc',
  893. ];
  894. $this->assertEquals($expected, $result['order']);
  895. }
  896. /**
  897. * @return \Cake\Datasource\RepositoryInterface|\PHPUnit\Framework\MockObject\MockObject
  898. */
  899. protected function getMockRepository()
  900. {
  901. $model = $this->getMockBuilder(RepositoryInterface::class)
  902. ->getMock();
  903. return $model;
  904. }
  905. /**
  906. * @param string $modelAlias Model alias to use.
  907. * @return \Cake\Datasource\RepositoryInterface|\PHPUnit\Framework\MockObject\MockObject
  908. */
  909. protected function mockAliasHasFieldModel($modelAlias = 'model')
  910. {
  911. $model = $this->getMockRepository();
  912. $model->expects($this->any())
  913. ->method('getAlias')
  914. ->will($this->returnValue($modelAlias));
  915. $model->expects($this->any())
  916. ->method('hasField')
  917. ->will($this->returnValue(true));
  918. return $model;
  919. }
  920. /**
  921. * test that multiple sort works.
  922. *
  923. * @return void
  924. */
  925. public function testValidateSortMultiple()
  926. {
  927. $model = $this->mockAliasHasFieldModel();
  928. $options = [
  929. 'order' => [
  930. 'author_id' => 'asc',
  931. 'title' => 'asc',
  932. ],
  933. ];
  934. $result = $this->Paginator->validateSort($model, $options);
  935. $expected = [
  936. 'model.author_id' => 'asc',
  937. 'model.title' => 'asc',
  938. ];
  939. $this->assertEquals($expected, $result['order']);
  940. }
  941. /**
  942. * test that multiple sort adds in query data.
  943. *
  944. * @return void
  945. */
  946. public function testValidateSortMultipleWithQuery()
  947. {
  948. $model = $this->mockAliasHasFieldModel();
  949. $options = [
  950. 'sort' => 'created',
  951. 'direction' => 'desc',
  952. 'order' => [
  953. 'author_id' => 'asc',
  954. 'title' => 'asc',
  955. ],
  956. ];
  957. $result = $this->Paginator->validateSort($model, $options);
  958. $expected = [
  959. 'model.created' => 'desc',
  960. 'model.author_id' => 'asc',
  961. 'model.title' => 'asc',
  962. ];
  963. $this->assertEquals($expected, $result['order']);
  964. $options = [
  965. 'sort' => 'title',
  966. 'direction' => 'desc',
  967. 'order' => [
  968. 'author_id' => 'asc',
  969. 'title' => 'asc',
  970. ],
  971. ];
  972. $result = $this->Paginator->validateSort($model, $options);
  973. $expected = [
  974. 'model.title' => 'desc',
  975. 'model.author_id' => 'asc',
  976. ];
  977. $this->assertEquals($expected, $result['order']);
  978. }
  979. /**
  980. * Tests that sort query string and model prefixes default match on assoc merging.
  981. *
  982. * @return void
  983. */
  984. public function testValidateSortMultipleWithQueryAndAliasedDefault()
  985. {
  986. $model = $this->mockAliasHasFieldModel();
  987. $options = [
  988. 'sort' => 'created',
  989. 'direction' => 'desc',
  990. 'order' => [
  991. 'model.created' => 'asc',
  992. ],
  993. ];
  994. $result = $this->Paginator->validateSort($model, $options);
  995. $expected = [
  996. 'sort' => 'created',
  997. 'order' => [
  998. 'model.created' => 'desc',
  999. ],
  1000. ];
  1001. $this->assertEquals($expected, $result);
  1002. }
  1003. /**
  1004. * Tests that order strings can used by Paginator
  1005. *
  1006. * @return void
  1007. */
  1008. public function testValidateSortWithString()
  1009. {
  1010. $model = $this->mockAliasHasFieldModel();
  1011. $options = [
  1012. 'order' => 'model.author_id DESC',
  1013. ];
  1014. $result = $this->Paginator->validateSort($model, $options);
  1015. $expected = 'model.author_id DESC';
  1016. $this->assertEquals($expected, $result['order']);
  1017. }
  1018. /**
  1019. * Test that no sort doesn't trigger an error.
  1020. *
  1021. * @return void
  1022. */
  1023. public function testValidateSortNoSort()
  1024. {
  1025. $model = $this->mockAliasHasFieldModel();
  1026. $options = [
  1027. 'direction' => 'asc',
  1028. 'sortableFields' => ['title', 'id'],
  1029. ];
  1030. $result = $this->Paginator->validateSort($model, $options);
  1031. $this->assertEquals([], $result['order']);
  1032. }
  1033. /**
  1034. * Test sorting with incorrect aliases on valid fields.
  1035. *
  1036. * @return void
  1037. */
  1038. public function testValidateSortInvalidAlias()
  1039. {
  1040. $model = $this->mockAliasHasFieldModel();
  1041. $options = ['sort' => 'Derp.id'];
  1042. $result = $this->Paginator->validateSort($model, $options);
  1043. $this->assertEquals([], $result['order']);
  1044. }
  1045. /**
  1046. * @return array
  1047. */
  1048. public function checkLimitProvider()
  1049. {
  1050. return [
  1051. 'out of bounds' => [
  1052. ['limit' => 1000000, 'maxLimit' => 100],
  1053. 100,
  1054. ],
  1055. 'limit is nan' => [
  1056. ['limit' => 'sheep!', 'maxLimit' => 100],
  1057. 1,
  1058. ],
  1059. 'negative limit' => [
  1060. ['limit' => '-1', 'maxLimit' => 100],
  1061. 1,
  1062. ],
  1063. 'unset limit' => [
  1064. ['limit' => null, 'maxLimit' => 100],
  1065. 1,
  1066. ],
  1067. 'limit = 0' => [
  1068. ['limit' => 0, 'maxLimit' => 100],
  1069. 1,
  1070. ],
  1071. 'limit = 0 v2' => [
  1072. ['limit' => 0, 'maxLimit' => 0],
  1073. 1,
  1074. ],
  1075. 'limit = null' => [
  1076. ['limit' => null, 'maxLimit' => 0],
  1077. 1,
  1078. ],
  1079. 'bad input, results in 1' => [
  1080. ['limit' => null, 'maxLimit' => null],
  1081. 1,
  1082. ],
  1083. 'bad input, results in 1 v2' => [
  1084. ['limit' => false, 'maxLimit' => false],
  1085. 1,
  1086. ],
  1087. ];
  1088. }
  1089. /**
  1090. * test that maxLimit is respected
  1091. *
  1092. * @dataProvider checkLimitProvider
  1093. * @return void
  1094. */
  1095. public function testCheckLimit(array $input, int $expected)
  1096. {
  1097. $result = $this->Paginator->checkLimit($input);
  1098. $this->assertSame($expected, $result['limit']);
  1099. }
  1100. /**
  1101. * Integration test for checkLimit() being applied inside paginate()
  1102. *
  1103. * @return void
  1104. */
  1105. public function testPaginateMaxLimit()
  1106. {
  1107. $this->loadFixtures('Posts');
  1108. $table = $this->getTableLocator()->get('PaginatorPosts');
  1109. $settings = [
  1110. 'maxLimit' => 100,
  1111. ];
  1112. $params = [
  1113. 'limit' => '1000',
  1114. ];
  1115. $this->Paginator->paginate($table, $params, $settings);
  1116. $pagingParams = $this->Paginator->getPagingParams();
  1117. $this->assertEquals(100, $pagingParams['PaginatorPosts']['limit']);
  1118. $this->assertEquals(100, $pagingParams['PaginatorPosts']['perPage']);
  1119. $params = [
  1120. 'limit' => '10',
  1121. ];
  1122. $this->Paginator->paginate($table, $params, $settings);
  1123. $pagingParams = $this->Paginator->getPagingParams();
  1124. $this->assertEquals(10, $pagingParams['PaginatorPosts']['limit']);
  1125. $this->assertEquals(10, $pagingParams['PaginatorPosts']['perPage']);
  1126. }
  1127. /**
  1128. * test paginate() and custom finders to ensure the count + find
  1129. * use the custom type.
  1130. *
  1131. * @return void
  1132. */
  1133. public function testPaginateCustomFindCount()
  1134. {
  1135. $settings = [
  1136. 'finder' => 'published',
  1137. 'limit' => 2,
  1138. ];
  1139. $table = $this->_getMockPosts(['query']);
  1140. $query = $this->_getMockFindQuery();
  1141. $table->expects($this->once())
  1142. ->method('query')
  1143. ->will($this->returnValue($query));
  1144. $query->expects($this->once())->method('applyOptions')
  1145. ->with([
  1146. 'limit' => 2,
  1147. 'page' => 1,
  1148. 'order' => [],
  1149. 'whitelist' => ['limit', 'sort', 'page', 'direction'],
  1150. 'allowedParameters' => ['limit', 'sort', 'page', 'direction'],
  1151. 'scope' => null,
  1152. 'sort' => null,
  1153. ]);
  1154. $this->Paginator->paginate($table, [], $settings);
  1155. }
  1156. /**
  1157. * Tests that it is possible to pass an already made query object to
  1158. * paginate()
  1159. *
  1160. * @return void
  1161. */
  1162. public function testPaginateQuery()
  1163. {
  1164. $params = ['page' => '-1'];
  1165. $settings = [
  1166. 'PaginatorPosts' => [
  1167. 'contain' => ['PaginatorAuthor'],
  1168. 'maxLimit' => 10,
  1169. 'group' => 'PaginatorPosts.published',
  1170. 'order' => ['PaginatorPosts.id' => 'ASC'],
  1171. ],
  1172. ];
  1173. $table = $this->_getMockPosts(['find']);
  1174. $query = $this->_getMockFindQuery($table);
  1175. $table->expects($this->never())->method('find');
  1176. $query->expects($this->once())
  1177. ->method('applyOptions')
  1178. ->with([
  1179. 'contain' => ['PaginatorAuthor'],
  1180. 'group' => 'PaginatorPosts.published',
  1181. 'limit' => 10,
  1182. 'order' => ['PaginatorPosts.id' => 'ASC'],
  1183. 'page' => 1,
  1184. 'whitelist' => ['limit', 'sort', 'page', 'direction'],
  1185. 'allowedParameters' => ['limit', 'sort', 'page', 'direction'],
  1186. 'scope' => null,
  1187. 'sort' => 'PaginatorPosts.id',
  1188. ]);
  1189. $this->Paginator->paginate($query, $params, $settings);
  1190. }
  1191. /**
  1192. * test paginate() with bind()
  1193. *
  1194. * @return void
  1195. */
  1196. public function testPaginateQueryWithBindValue()
  1197. {
  1198. $config = ConnectionManager::getConfig('test');
  1199. $this->skipIf(strpos($config['driver'], 'Sqlserver') !== false, 'Test temporarily broken in SQLServer');
  1200. $this->loadFixtures('Posts');
  1201. $table = $this->getTableLocator()->get('PaginatorPosts');
  1202. $query = $table->find()
  1203. ->where(['PaginatorPosts.author_id BETWEEN :start AND :end'])
  1204. ->bind(':start', 1)
  1205. ->bind(':end', 2);
  1206. $results = $this->Paginator->paginate($query, []);
  1207. $result = $results->toArray();
  1208. $this->assertCount(2, $result);
  1209. $this->assertEquals('First Post', $result[0]->title);
  1210. $this->assertEquals('Third Post', $result[1]->title);
  1211. }
  1212. /**
  1213. * Tests that passing a query object with a limit clause set will
  1214. * overwrite it with the passed defaults.
  1215. *
  1216. * @return void
  1217. */
  1218. public function testPaginateQueryWithLimit()
  1219. {
  1220. $params = ['page' => '-1'];
  1221. $settings = [
  1222. 'PaginatorPosts' => [
  1223. 'contain' => ['PaginatorAuthor'],
  1224. 'maxLimit' => 10,
  1225. 'limit' => 5,
  1226. 'group' => 'PaginatorPosts.published',
  1227. 'order' => ['PaginatorPosts.id' => 'ASC'],
  1228. ],
  1229. ];
  1230. $table = $this->_getMockPosts(['find']);
  1231. $query = $this->_getMockFindQuery($table);
  1232. $query->limit(2);
  1233. $table->expects($this->never())->method('find');
  1234. $query->expects($this->once())
  1235. ->method('applyOptions')
  1236. ->with([
  1237. 'contain' => ['PaginatorAuthor'],
  1238. 'group' => 'PaginatorPosts.published',
  1239. 'limit' => 5,
  1240. 'order' => ['PaginatorPosts.id' => 'ASC'],
  1241. 'page' => 1,
  1242. 'whitelist' => ['limit', 'sort', 'page', 'direction'],
  1243. 'allowedParameters' => ['limit', 'sort', 'page', 'direction'],
  1244. 'scope' => null,
  1245. 'sort' => 'PaginatorPosts.id',
  1246. ]);
  1247. $this->Paginator->paginate($query, $params, $settings);
  1248. }
  1249. /**
  1250. * Helper method for making mocks.
  1251. *
  1252. * @param array $methods
  1253. * @return \Cake\ORM\Table|\PHPUnit\Framework\MockObject\MockObject
  1254. */
  1255. protected function _getMockPosts($methods = [])
  1256. {
  1257. return $this->getMockBuilder('TestApp\Model\Table\PaginatorPostsTable')
  1258. ->onlyMethods($methods)
  1259. ->setConstructorArgs([[
  1260. 'connection' => ConnectionManager::get('test'),
  1261. 'alias' => 'PaginatorPosts',
  1262. 'schema' => [
  1263. 'id' => ['type' => 'integer'],
  1264. 'author_id' => ['type' => 'integer', 'null' => false],
  1265. 'title' => ['type' => 'string', 'null' => false],
  1266. 'body' => 'text',
  1267. 'published' => ['type' => 'string', 'length' => 1, 'default' => 'N'],
  1268. '_constraints' => ['primary' => ['type' => 'primary', 'columns' => ['id']]],
  1269. ],
  1270. ]])
  1271. ->getMock();
  1272. }
  1273. /**
  1274. * Helper method for mocking queries.
  1275. *
  1276. * @param string|null $table
  1277. * @return \Cake\ORM\Query|\PHPUnit\Framework\MockObject\MockObject
  1278. */
  1279. protected function _getMockFindQuery($table = null)
  1280. {
  1281. /** @var \Cake\ORM\Query|\PHPUnit\Framework\MockObject\MockObject $query */
  1282. $query = $this->getMockBuilder('Cake\ORM\Query')
  1283. ->onlyMethods(['all', 'count', 'applyOptions'])
  1284. ->addMethods(['total'])
  1285. ->disableOriginalConstructor()
  1286. ->getMock();
  1287. $results = $this->getMockBuilder('Cake\ORM\ResultSet')
  1288. ->disableOriginalConstructor()
  1289. ->getMock();
  1290. $query->expects($this->any())
  1291. ->method('count')
  1292. ->will($this->returnValue(2));
  1293. $query->expects($this->any())
  1294. ->method('all')
  1295. ->will($this->returnValue($results));
  1296. if ($table) {
  1297. $query->repository($table);
  1298. }
  1299. return $query;
  1300. }
  1301. }