IntegrationTestTraitTest.php 64 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790
  1. <?php
  2. declare(strict_types=1);
  3. /**
  4. * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
  5. * Copyright (c) Cake Software Foundation, Inc. (https://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. (https://cakefoundation.org)
  12. * @link https://cakephp.org CakePHP(tm) Project
  13. * @since 3.0.0
  14. * @license https://opensource.org/licenses/mit-license.php MIT License
  15. */
  16. namespace Cake\Test\TestCase\TestSuite;
  17. use Cake\Controller\Controller;
  18. use Cake\Core\Configure;
  19. use Cake\Event\EventManager;
  20. use Cake\Http\Cookie\Cookie;
  21. use Cake\Http\Middleware\CsrfProtectionMiddleware;
  22. use Cake\Http\Middleware\EncryptedCookieMiddleware;
  23. use Cake\Http\Middleware\SessionCsrfProtectionMiddleware;
  24. use Cake\Http\Response;
  25. use Cake\Http\Session;
  26. use Cake\Routing\Route\InflectedRoute;
  27. use Cake\Routing\RouteBuilder;
  28. use Cake\Routing\Router;
  29. use Cake\Test\Fixture\AssertIntegrationTestCase;
  30. use Cake\TestSuite\IntegrationTestTrait;
  31. use Cake\TestSuite\TestCase;
  32. use Cake\Utility\Security;
  33. use Laminas\Diactoros\UploadedFile;
  34. use LogicException;
  35. use OutOfBoundsException;
  36. use PHPUnit\Framework\AssertionFailedError;
  37. use stdClass;
  38. use TestApp\ReflectionDependency;
  39. /**
  40. * Self test of the IntegrationTestTrait
  41. */
  42. class IntegrationTestTraitTest extends TestCase
  43. {
  44. use IntegrationTestTrait;
  45. /**
  46. * stub encryption key.
  47. *
  48. * @var string
  49. */
  50. protected $key = 'abcdabcdabcdabcdabcdabcdabcdabcdabcd';
  51. /**
  52. * @var \Cake\Routing\RouteBuilder
  53. */
  54. protected $builder;
  55. /**
  56. * Setup method
  57. */
  58. public function setUp(): void
  59. {
  60. parent::setUp();
  61. static::setAppNamespace();
  62. Router::reload();
  63. $this->builder = Router::createRouteBuilder('/');
  64. $this->builder->setExtensions(['json']);
  65. $this->builder->registerMiddleware('cookie', new EncryptedCookieMiddleware(['secrets'], $this->key));
  66. $this->builder->applyMiddleware('cookie');
  67. $this->builder->setRouteClass(InflectedRoute::class);
  68. $this->builder->get('/get/{controller}/{action}', []);
  69. $this->builder->head('/head/{controller}/{action}', []);
  70. $this->builder->options('/options/{controller}/{action}', []);
  71. $this->builder->connect('/{controller}/{action}/*', []);
  72. $this->builder->scope('/cookie-csrf/', ['csrf' => 'cookie'], function (RouteBuilder $routes): void {
  73. $routes->registerMiddleware('cookieCsrf', new CsrfProtectionMiddleware());
  74. $routes->applyMiddleware('cookieCsrf');
  75. $routes->connect('/posts/{action}', ['controller' => 'Posts']);
  76. });
  77. $this->builder->scope('/session-csrf/', ['csrf' => 'session'], function (RouteBuilder $routes): void {
  78. $routes->registerMiddleware('sessionCsrf', new SessionCsrfProtectionMiddleware());
  79. $routes->applyMiddleware('sessionCsrf');
  80. $routes->connect('/posts/{action}/', ['controller' => 'Posts']);
  81. });
  82. $this->configApplication(Configure::read('App.namespace') . '\Application', null);
  83. }
  84. /**
  85. * Tests that all data that used by the request is cast to strings
  86. */
  87. public function testDataCastToString(): void
  88. {
  89. $data = [
  90. 'title' => 'Blog Post',
  91. 'status' => 1,
  92. 'published' => true,
  93. 'not_published' => false,
  94. 'comments' => [
  95. [
  96. 'body' => 'Comment',
  97. 'status' => 1,
  98. ],
  99. ],
  100. 'file' => [
  101. 'tmp_name' => __FILE__,
  102. 'size' => 42,
  103. 'error' => 0,
  104. 'type' => 'text/plain',
  105. 'name' => 'Uploaded file',
  106. ],
  107. 'pictures' => [
  108. 'name' => [
  109. ['file' => 'a-file.png'],
  110. ['file' => 'a-moose.png'],
  111. ],
  112. 'type' => [
  113. ['file' => 'image/png'],
  114. ['file' => 'image/jpg'],
  115. ],
  116. 'tmp_name' => [
  117. ['file' => __FILE__],
  118. ['file' => __FILE__],
  119. ],
  120. 'error' => [
  121. ['file' => 0],
  122. ['file' => 0],
  123. ],
  124. 'size' => [
  125. ['file' => 17188],
  126. ['file' => 2010],
  127. ],
  128. ],
  129. 'upload' => new UploadedFile(__FILE__, 42, 0),
  130. ];
  131. $request = $this->_buildRequest('/posts/add', 'POST', $data);
  132. $this->assertIsString($request['post']['status']);
  133. $this->assertIsString($request['post']['published']);
  134. $this->assertSame('0', $request['post']['not_published']);
  135. $this->assertIsString($request['post']['comments'][0]['status']);
  136. $this->assertIsInt($request['post']['file']['error']);
  137. $this->assertIsInt($request['post']['file']['size']);
  138. $this->assertIsInt($request['post']['pictures']['error'][0]['file']);
  139. $this->assertIsInt($request['post']['pictures']['error'][1]['file']);
  140. $this->assertIsInt($request['post']['pictures']['size'][0]['file']);
  141. $this->assertIsInt($request['post']['pictures']['size'][1]['file']);
  142. $this->assertInstanceOf(UploadedFile::class, $request['post']['upload']);
  143. }
  144. /**
  145. * Test building a request.
  146. */
  147. public function testRequestBuilding(): void
  148. {
  149. $this->configRequest([
  150. 'headers' => [
  151. 'X-CSRF-Token' => 'abc123',
  152. 'Content-Type' => 'application/json',
  153. 'Accept' => 'application/json',
  154. ],
  155. 'base' => '',
  156. 'webroot' => '/',
  157. 'environment' => [
  158. 'PHP_AUTH_USER' => 'foo',
  159. 'PHP_AUTH_PW' => 'bar',
  160. ],
  161. ]);
  162. $this->cookie('split_token', 'def345');
  163. $this->session(['User' => ['id' => '1', 'username' => 'mark']]);
  164. $request = $this->_buildRequest('/tasks/add', 'POST', ['title' => 'First post']);
  165. $this->assertSame('abc123', $request['environment']['HTTP_X_CSRF_TOKEN']);
  166. $this->assertSame('application/json', $request['environment']['CONTENT_TYPE']);
  167. $this->assertSame('/tasks/add', $request['url']);
  168. $this->assertArrayHasKey('split_token', $request['cookies']);
  169. $this->assertSame('def345', $request['cookies']['split_token']);
  170. $this->assertSame(['id' => '1', 'username' => 'mark'], $request['session']->read('User'));
  171. $this->assertSame('foo', $request['environment']['PHP_AUTH_USER']);
  172. $this->assertSame('bar', $request['environment']['PHP_AUTH_PW']);
  173. }
  174. /**
  175. * Test request building adds csrf tokens
  176. */
  177. public function testRequestBuildingCsrfTokens(): void
  178. {
  179. $this->enableCsrfToken();
  180. $request = $this->_buildRequest('/tasks/add', 'POST', ['title' => 'First post']);
  181. $this->assertArrayHasKey('csrfToken', $request['cookies']);
  182. $this->assertArrayHasKey('_csrfToken', $request['post']);
  183. $this->assertSame($request['cookies']['csrfToken'], $request['post']['_csrfToken']);
  184. $this->assertSame($request['session']->read('csrfToken'), $request['post']['_csrfToken']);
  185. $this->cookie('csrfToken', '');
  186. $request = $this->_buildRequest('/tasks/add', 'POST', [
  187. '_csrfToken' => 'fale',
  188. 'title' => 'First post',
  189. ]);
  190. $this->assertSame('', $request['cookies']['csrfToken']);
  191. $this->assertSame('fale', $request['post']['_csrfToken']);
  192. }
  193. /**
  194. * Test multiple actions using CSRF tokens don't fail
  195. */
  196. public function testEnableCsrfMultipleRequests(): void
  197. {
  198. $this->enableCsrfToken();
  199. $first = $this->_buildRequest('/tasks/add', 'POST', ['title' => 'First post']);
  200. $second = $this->_buildRequest('/tasks/add', 'POST', ['title' => 'Second post']);
  201. $this->assertSame(
  202. $first['cookies']['csrfToken'],
  203. $second['post']['_csrfToken'],
  204. 'Csrf token should match cookie'
  205. );
  206. $this->assertSame(
  207. $first['session']->read('csrfToken'),
  208. $second['post']['_csrfToken'],
  209. 'Csrf token should match session'
  210. );
  211. $this->assertSame(
  212. $first['post']['_csrfToken'],
  213. $second['post']['_csrfToken'],
  214. 'Tokens should be consistent per test method'
  215. );
  216. }
  217. /**
  218. * Test building a request, with query parameters
  219. */
  220. public function testRequestBuildingQueryParameters(): void
  221. {
  222. $request = $this->_buildRequest('/tasks/view?archived=yes', 'GET', []);
  223. $this->assertSame('/tasks/view', $request['url']);
  224. $this->assertSame('archived=yes', $request['environment']['QUERY_STRING']);
  225. $this->assertSame('/tasks/view', $request['environment']['REQUEST_URI']);
  226. }
  227. /**
  228. * Test cookie encrypted
  229. *
  230. * @see CookieComponentControllerTest
  231. */
  232. public function testCookieEncrypted(): void
  233. {
  234. Security::setSalt($this->key);
  235. $this->cookieEncrypted('KeyOfCookie', 'Encrypted with aes by default');
  236. $request = $this->_buildRequest('/tasks/view', 'GET', []);
  237. $this->assertStringStartsWith('Q2FrZQ==.', $request['cookies']['KeyOfCookie']);
  238. }
  239. /**
  240. * Test sending get request and using default `test_app/config/routes.php`.
  241. */
  242. public function testGetUsingApplicationWithPluginRoutes(): void
  243. {
  244. // first clean routes to have Router::$initailized === false
  245. Router::reload();
  246. $this->clearPlugins();
  247. $this->configApplication(Configure::read('App.namespace') . '\ApplicationWithPluginRoutes', null);
  248. $this->get('/test_plugin');
  249. $this->assertResponseOk();
  250. }
  251. /**
  252. * Test sending get request and using default `test_app/config/routes.php`.
  253. */
  254. public function testGetUsingApplicationWithDefaultRoutes(): void
  255. {
  256. // first clean routes to have Router::$initialized === false
  257. Router::reload();
  258. $this->configApplication(Configure::read('App.namespace') . '\ApplicationWithDefaultRoutes', null);
  259. $this->get('/some_alias');
  260. $this->assertResponseOk();
  261. $this->assertSame('5', $this->_getBodyAsString());
  262. }
  263. public function testExceptionsInMiddlewareJsonView(): void
  264. {
  265. Router::reload();
  266. $this->builder->connect('/json_response/api_get_data', [
  267. 'controller' => 'JsonResponse',
  268. 'action' => 'apiGetData',
  269. ]);
  270. $this->configApplication(Configure::read('App.namespace') . '\ApplicationWithExceptionsInMiddleware', null);
  271. $this->_request['headers'] = ['Accept' => 'application/json'];
  272. $this->get('/json_response/api_get_data');
  273. $this->assertResponseCode(403);
  274. $this->assertHeader('Content-Type', 'application/json');
  275. $this->assertResponseContains('"message": "Sample Message"');
  276. $this->assertResponseContains('"code": 403');
  277. }
  278. /**
  279. * Test sending head requests.
  280. */
  281. public function testHead(): void
  282. {
  283. $this->assertNull($this->_response);
  284. $this->head('/request_action/test_request_action');
  285. $this->assertNotEmpty($this->_response);
  286. $this->assertInstanceOf('Cake\Http\Response', $this->_response);
  287. $this->assertResponseSuccess();
  288. }
  289. /**
  290. * Test sending head requests.
  291. */
  292. public function testHeadMethodRoute(): void
  293. {
  294. $this->head('/head/request_action/test_request_action');
  295. $this->assertResponseSuccess();
  296. }
  297. /**
  298. * Test sending options requests.
  299. */
  300. public function testOptions(): void
  301. {
  302. $this->assertNull($this->_response);
  303. $this->options('/request_action/test_request_action');
  304. $this->assertNotEmpty($this->_response);
  305. $this->assertInstanceOf('Cake\Http\Response', $this->_response);
  306. $this->assertResponseSuccess();
  307. }
  308. /**
  309. * Test sending options requests.
  310. */
  311. public function testOptionsMethodRoute(): void
  312. {
  313. $this->options('/options/request_action/test_request_action');
  314. $this->assertResponseSuccess();
  315. }
  316. /**
  317. * Test sending get requests sets the request method
  318. */
  319. public function testGetSpecificRouteHttpServer(): void
  320. {
  321. $this->get('/get/request_action/test_request_action');
  322. $this->assertResponseOk();
  323. $this->assertSame('This is a test', (string)$this->_response->getBody());
  324. }
  325. /**
  326. * Test customizing the app class.
  327. */
  328. public function testConfigApplication(): void
  329. {
  330. $this->expectException(LogicException::class);
  331. $this->expectExceptionMessage('Cannot load `TestApp\MissingApp` for use in integration');
  332. $this->configApplication('TestApp\MissingApp', []);
  333. $this->get('/request_action/test_request_action');
  334. }
  335. /**
  336. * Test sending get requests with Http\Server
  337. */
  338. public function testGetHttpServer(): void
  339. {
  340. $this->assertNull($this->_response);
  341. $this->get('/request_action/test_request_action');
  342. $this->assertNotEmpty($this->_response);
  343. $this->assertInstanceOf('Cake\Http\Response', $this->_response);
  344. $this->assertSame('This is a test', (string)$this->_response->getBody());
  345. $this->assertHeader('X-Middleware', 'true');
  346. }
  347. /**
  348. * Test that the PSR7 requests get query string data
  349. */
  350. public function testGetQueryStringHttpServer(): void
  351. {
  352. $this->configRequest(['headers' => ['Content-Type' => 'text/plain']]);
  353. $this->get('/request_action/params_pass?q=query');
  354. $this->assertResponseOk();
  355. $this->assertResponseContains('"q":"query"');
  356. $this->assertResponseContains('"contentType":"text\/plain"');
  357. $this->assertHeader('X-Middleware', 'true');
  358. $request = $this->_controller->getRequest();
  359. $this->assertStringContainsString('/request_action/params_pass?q=query', $request->getRequestTarget());
  360. }
  361. /**
  362. * Test that the PSR7 requests get query string data
  363. */
  364. public function testGetQueryStringSetsHere(): void
  365. {
  366. $this->configRequest(['headers' => ['Content-Type' => 'text/plain']]);
  367. $this->get('/request_action/params_pass?q=query');
  368. $this->assertResponseOk();
  369. $this->assertResponseContains('"q":"query"');
  370. $this->assertResponseContains('"contentType":"text\/plain"');
  371. $this->assertHeader('X-Middleware', 'true');
  372. $request = $this->_controller->getRequest();
  373. $this->assertStringContainsString('/request_action/params_pass?q=query', $request->getRequestTarget());
  374. $this->assertStringContainsString('/request_action/params_pass', $request->getAttribute('here'));
  375. }
  376. /**
  377. * Test that the PSR7 requests get cookies
  378. */
  379. public function testGetCookiesHttpServer(): void
  380. {
  381. $this->configRequest(['cookies' => ['split_test' => 'abc']]);
  382. $this->get('/request_action/cookie_pass');
  383. $this->assertResponseOk();
  384. $this->assertResponseContains('"split_test":"abc"');
  385. $this->assertHeader('X-Middleware', 'true');
  386. }
  387. /**
  388. * Test that the PSR7 requests receive post data
  389. */
  390. public function testPostDataHttpServer(): void
  391. {
  392. $this->post('/request_action/post_pass', ['title' => 'value']);
  393. $data = json_decode('' . $this->_response->getBody());
  394. $this->assertSame('value', $data->title);
  395. $this->assertHeader('X-Middleware', 'true');
  396. }
  397. /**
  398. * Test that the PSR7 requests receive put data
  399. */
  400. public function testPutDataFormUrlEncoded(): void
  401. {
  402. $this->configRequest([
  403. 'headers' => [
  404. 'Content-Type' => 'application/x-www-form-urlencoded',
  405. ],
  406. ]);
  407. $this->put('/request_action/post_pass', ['title' => 'value']);
  408. $this->assertResponseOk();
  409. $data = json_decode('' . $this->_response->getBody());
  410. $this->assertSame('value', $data->title);
  411. }
  412. /**
  413. * Test that the uploaded files are passed correctly to the request
  414. */
  415. public function testUploadedFiles(): void
  416. {
  417. $this->configRequest([
  418. 'files' => [
  419. 'file' => [
  420. 'tmp_name' => __FILE__,
  421. 'size' => 42,
  422. 'error' => 0,
  423. 'type' => 'text/plain',
  424. 'name' => 'Uploaded file',
  425. ],
  426. 'pictures' => [
  427. 'name' => [
  428. ['file' => 'a-file.png'],
  429. ['file' => 'a-moose.png'],
  430. ],
  431. 'type' => [
  432. ['file' => 'image/png'],
  433. ['file' => 'image/jpg'],
  434. ],
  435. 'tmp_name' => [
  436. ['file' => __FILE__],
  437. ['file' => __FILE__],
  438. ],
  439. 'error' => [
  440. ['file' => 0],
  441. ['file' => 0],
  442. ],
  443. 'size' => [
  444. ['file' => 17188],
  445. ['file' => 2010],
  446. ],
  447. ],
  448. 'upload' => new UploadedFile(__FILE__, 42, 0),
  449. ],
  450. ]);
  451. $this->post('/request_action/uploaded_files');
  452. $this->assertHeader('X-Middleware', 'true');
  453. $data = json_decode((string)$this->_response->getBody(), true);
  454. $this->assertSame([
  455. 'file' => 'Uploaded file',
  456. 'pictures.0.file' => 'a-file.png',
  457. 'pictures.1.file' => 'a-moose.png',
  458. 'upload' => null,
  459. ], $data);
  460. }
  461. /**
  462. * Test that the PSR7 requests receive encoded data.
  463. */
  464. public function testInputDataHttpServer(): void
  465. {
  466. $this->post('/request_action/input_test', '{"hello":"world"}');
  467. if ($this->_response->getBody()->isSeekable()) {
  468. $this->_response->getBody()->rewind();
  469. }
  470. $this->assertSame('world', $this->_response->getBody()->getContents());
  471. $this->assertHeader('X-Middleware', 'true');
  472. }
  473. /**
  474. * Test that the PSR7 requests receive encoded data.
  475. */
  476. public function testInputDataSecurityToken(): void
  477. {
  478. $this->enableSecurityToken();
  479. $this->post('/request_action/input_test', '{"hello":"world"}');
  480. $this->assertSame('world', '' . $this->_response->getBody());
  481. $this->assertHeader('X-Middleware', 'true');
  482. }
  483. /**
  484. * Test that the PSR7 requests get cookies
  485. */
  486. public function testSessionHttpServer(): void
  487. {
  488. $this->session(['foo' => 'session data']);
  489. $this->get('/request_action/session_test');
  490. $this->assertResponseOk();
  491. $this->assertResponseContains('session data');
  492. $this->assertHeader('X-Middleware', 'true');
  493. }
  494. /**
  495. * Test sending requests stores references to controller/view/layout.
  496. */
  497. public function testRequestSetsProperties(): void
  498. {
  499. $this->post('/posts/index');
  500. $this->assertInstanceOf('Cake\Controller\Controller', $this->_controller);
  501. $this->assertNotEmpty($this->_viewName, 'View name not set');
  502. $this->assertStringContainsString('templates' . DS . 'Posts' . DS . 'index.php', $this->_viewName);
  503. $this->assertNotEmpty($this->_layoutName, 'Layout name not set');
  504. $this->assertStringContainsString('templates' . DS . 'layout' . DS . 'default.php', $this->_layoutName);
  505. $this->assertTemplate('index');
  506. $this->assertLayout('default');
  507. $this->assertSame('value', $this->viewVariable('test'));
  508. }
  509. /**
  510. * Test PSR7 requests store references to controller/view/layout
  511. */
  512. public function testRequestSetsPropertiesHttpServer(): void
  513. {
  514. $this->post('/posts/index');
  515. $this->assertInstanceOf('Cake\Controller\Controller', $this->_controller);
  516. $this->assertNotEmpty($this->_viewName, 'View name not set');
  517. $this->assertStringContainsString('templates' . DS . 'Posts' . DS . 'index.php', $this->_viewName);
  518. $this->assertNotEmpty($this->_layoutName, 'Layout name not set');
  519. $this->assertStringContainsString('templates' . DS . 'layout' . DS . 'default.php', $this->_layoutName);
  520. $this->assertTemplate('index');
  521. $this->assertLayout('default');
  522. $this->assertSame('value', $this->viewVariable('test'));
  523. }
  524. /**
  525. * Tests URLs containing extensions.
  526. */
  527. public function testRequestWithExt(): void
  528. {
  529. $this->get(['controller' => 'Posts', 'action' => 'ajax', '_ext' => 'json']);
  530. $this->assertResponseCode(200);
  531. }
  532. /**
  533. * Assert that the stored template doesn't change when cells are rendered.
  534. */
  535. public function testAssertTemplateAfterCellRender(): void
  536. {
  537. $this->get('/posts/get');
  538. $this->assertStringContainsString('templates' . DS . 'Posts' . DS . 'get.php', $this->_viewName);
  539. $this->assertTemplate('get');
  540. $this->assertResponseContains('cellcontent');
  541. }
  542. /**
  543. * Test array URLs
  544. */
  545. public function testArrayUrls(): void
  546. {
  547. $this->post(['controller' => 'Posts', 'action' => 'index', '_method' => 'POST']);
  548. $this->assertResponseOk();
  549. $this->assertSame('value', $this->viewVariable('test'));
  550. }
  551. /**
  552. * Test array URL with host
  553. */
  554. public function testArrayUrlWithHost(): void
  555. {
  556. $this->get([
  557. 'controller' => 'Posts',
  558. 'action' => 'hostData',
  559. '_host' => 'app.example.org',
  560. '_https' => true,
  561. ]);
  562. $this->assertResponseOk();
  563. $this->assertResponseContains('"isSsl":true');
  564. $this->assertResponseContains('"host":"app.example.org"');
  565. }
  566. /**
  567. * Test array URLs with an empty router.
  568. */
  569. public function testArrayUrlsEmptyRouter(): void
  570. {
  571. Router::reload();
  572. $this->assertEmpty(Router::getRouteCollection()->routes());
  573. $this->get(['controller' => 'Posts', 'action' => 'index']);
  574. $this->assertResponseOk();
  575. $this->assertSame('value', $this->viewVariable('test'));
  576. }
  577. /**
  578. * Test flash and cookie assertions
  579. */
  580. public function testFlashSessionAndCookieAsserts(): void
  581. {
  582. $this->post('/posts/index');
  583. $this->assertSession('An error message', 'Flash.flash.0.message');
  584. $this->assertCookie(1, 'remember_me');
  585. $this->assertCookieNotSet('user_id');
  586. }
  587. /**
  588. * Test flash and cookie assertions
  589. */
  590. public function testFlashSessionAndCookieAssertsHttpServer(): void
  591. {
  592. $this->post('/posts/index');
  593. $this->assertSession('An error message', 'Flash.flash.0.message');
  594. $this->assertCookieNotSet('user_id');
  595. $this->assertCookie(1, 'remember_me');
  596. }
  597. /**
  598. * Test flash assertions stored with enableRememberFlashMessages() after a
  599. * redirect.
  600. */
  601. public function testFlashAssertionsAfterRedirect(): void
  602. {
  603. $this->get('/posts/someRedirect');
  604. $this->assertResponseCode(302);
  605. $this->assertSession('A success message', 'Flash.flash.0.message');
  606. }
  607. /**
  608. * Test flash assertions stored with enableRememberFlashMessages() after they
  609. * are rendered
  610. */
  611. public function testFlashAssertionsAfterRender(): void
  612. {
  613. $this->enableRetainFlashMessages();
  614. $this->get('/posts/index/with_flash');
  615. $this->assertResponseCode(200);
  616. $this->assertSession('An error message', 'Flash.flash.0.message');
  617. }
  618. /**
  619. * Test flash assertions stored with enableRememberFlashMessages() even if
  620. * no view is rendered
  621. */
  622. public function testFlashAssertionsWithNoRender(): void
  623. {
  624. $this->enableRetainFlashMessages();
  625. $this->get('/posts/flashNoRender');
  626. $this->assertRedirect();
  627. $this->assertFlashElement('flash/error');
  628. $this->assertFlashMessage('An error message');
  629. }
  630. /**
  631. * If multiple requests occur in the same test method
  632. * flash messages should be retained.
  633. */
  634. public function testFlashAssertionMultipleRequests(): void
  635. {
  636. $this->enableRetainFlashMessages();
  637. $this->disableErrorHandlerMiddleware();
  638. $this->get('/posts/index/with_flash');
  639. $this->assertResponseCode(200);
  640. $this->assertFlashMessage('An error message');
  641. $this->get('/posts/someRedirect');
  642. $this->assertResponseCode(302);
  643. $this->assertFlashMessage('A success message');
  644. }
  645. /**
  646. * Test flash assertions stored with enableRememberFlashMessages() even if
  647. * the controller clears flash data in `beforeRender`
  648. */
  649. public function testFlashAssertionsRemoveInBeforeRender(): void
  650. {
  651. $this->enableRetainFlashMessages();
  652. $this->get('/posts/index/with_flash/?clear=true');
  653. $this->assertResponseOk();
  654. $this->assertFlashElement('flash/error');
  655. $this->assertFlashMessage('An error message');
  656. }
  657. /**
  658. * Tests assertCookieNotSet assertion
  659. */
  660. public function testAssertCookieNotSet(): void
  661. {
  662. $this->cookie('test', 'value');
  663. $this->get('/posts/index');
  664. $this->assertCookieNotSet('test');
  665. $this->get('/posts/redirectWithCookie');
  666. $this->assertCookieNotSet('test');
  667. }
  668. /**
  669. * Tests the failure message for assertCookieNotSet
  670. */
  671. public function testCookieNotSetFailure(): void
  672. {
  673. $this->expectException(AssertionFailedError::class);
  674. $this->expectExceptionMessage('Failed asserting that \'remember_me\' cookie is not set');
  675. $this->post('/posts/index');
  676. $this->assertCookieNotSet('remember_me');
  677. }
  678. /**
  679. * Tests the failure message for assertCookieNotSet when no
  680. * response whas generated
  681. */
  682. public function testCookieNotSetFailureNoResponse(): void
  683. {
  684. $this->expectException(AssertionFailedError::class);
  685. $this->expectExceptionMessage('No response set, cannot assert content.');
  686. $this->assertCookieNotSet('remember_me');
  687. }
  688. /**
  689. * Tests assertCookieIsSet assertion
  690. */
  691. public function testAssertCookieIsSet(): void
  692. {
  693. $this->get('/posts/secretCookie');
  694. $this->assertCookieIsSet('secrets');
  695. $this->get('/posts/redirectWithCookie');
  696. $this->assertCookieIsSet('remember');
  697. }
  698. /**
  699. * Tests the failure message for assertCookieIsSet
  700. */
  701. public function testCookieIsSetFailure(): void
  702. {
  703. $this->expectException(AssertionFailedError::class);
  704. $this->expectExceptionMessage('Failed asserting that \'not-secrets\' cookie is set');
  705. $this->post('/posts/secretCookie');
  706. $this->assertCookieIsSet('not-secrets');
  707. }
  708. /**
  709. * Tests the failure message for assertCookieIsSet when no
  710. * response whas generated
  711. */
  712. public function testCookieIsSetFailureNoResponse(): void
  713. {
  714. $this->expectException(AssertionFailedError::class);
  715. $this->expectExceptionMessage('No response set, cannot assert content.');
  716. $this->assertCookieIsSet('secrets');
  717. }
  718. /**
  719. * Test error handling and error page rendering.
  720. */
  721. public function testPostAndErrorHandling(): void
  722. {
  723. $this->post('/request_action/error_method');
  724. $this->assertResponseNotEmpty();
  725. $this->assertResponseContains('Not there or here');
  726. $this->assertResponseContains('<!DOCTYPE html>');
  727. }
  728. /**
  729. * Test posting to a secured form action.
  730. */
  731. public function testPostSecuredForm(): void
  732. {
  733. $this->enableSecurityToken();
  734. $data = [
  735. 'title' => 'Some title',
  736. 'body' => 'Some text',
  737. ];
  738. $this->post('/posts/securePost', $data);
  739. $this->assertResponseOk();
  740. $this->assertResponseContains('Request was accepted');
  741. }
  742. /**
  743. * Test posting to a secured form action with nested data.
  744. */
  745. public function testPostSecuredFormNestedData(): void
  746. {
  747. $this->enableSecurityToken();
  748. $data = [
  749. 'title' => 'New post',
  750. 'comments' => [
  751. ['comment' => 'A new comment'],
  752. ],
  753. 'tags' => ['_ids' => [1, 2, 3, 4]],
  754. ];
  755. $this->post('/posts/securePost', $data);
  756. $this->assertResponseOk();
  757. $this->assertResponseContains('Request was accepted');
  758. }
  759. /**
  760. * Test posting to a secured form action with unlocked fields
  761. */
  762. public function testPostSecuredFormUnlockedFieldsFails(): void
  763. {
  764. $this->enableSecurityToken();
  765. $data = [
  766. 'title' => 'New post',
  767. 'comments' => [
  768. ['comment' => 'A new comment'],
  769. ],
  770. 'tags' => ['_ids' => [1, 2, 3, 4]],
  771. 'some_unlocked_field' => 'Unlocked data',
  772. ];
  773. $this->post('/posts/securePost', $data);
  774. $this->assertResponseCode(400);
  775. $this->assertResponseContains('Invalid form protection debug token.');
  776. }
  777. /**
  778. * Test posting to a secured form action with unlocked fields
  779. */
  780. public function testPostSecuredFormUnlockedFieldsWithSet(): void
  781. {
  782. $this->enableSecurityToken();
  783. $data = [
  784. 'title' => 'New post',
  785. 'comments' => [
  786. ['comment' => 'A new comment'],
  787. ],
  788. 'tags' => ['_ids' => [1, 2, 3, 4]],
  789. 'some_unlocked_field' => 'Unlocked data',
  790. ];
  791. $this->setUnlockedFields(['some_unlocked_field']);
  792. $this->post('/posts/securePost', $data);
  793. $this->assertResponseOk();
  794. $this->assertResponseContains('Request was accepted');
  795. }
  796. /**
  797. * Test posting to a secured form action.
  798. */
  799. public function testPostSecuredFormWithQuery(): void
  800. {
  801. $this->enableSecurityToken();
  802. $data = [
  803. 'title' => 'Some title',
  804. 'body' => 'Some text',
  805. ];
  806. $this->post('/posts/securePost?foo=bar', $data);
  807. $this->assertResponseOk();
  808. $this->assertResponseContains('Request was accepted');
  809. }
  810. /**
  811. * Test posting to a secured form action with a query that has a part that
  812. * will be encoded by the security component
  813. */
  814. public function testPostSecuredFormWithUnencodedQuery(): void
  815. {
  816. $this->enableSecurityToken();
  817. $data = [
  818. 'title' => 'Some title',
  819. 'body' => 'Some text',
  820. ];
  821. $this->post('/posts/securePost?foo=/', $data);
  822. $this->assertResponseOk();
  823. $this->assertResponseContains('Request was accepted');
  824. }
  825. /**
  826. * Test posting to a secured form action action.
  827. */
  828. public function testPostSecuredFormFailure(): void
  829. {
  830. $data = [
  831. 'title' => 'Some title',
  832. 'body' => 'Some text',
  833. ];
  834. $this->post('/posts/securePost', $data);
  835. $this->assertResponseError();
  836. }
  837. /**
  838. * Integration test for cookie based CSRF token protection success
  839. */
  840. public function testPostCookieCsrfSuccess(): void
  841. {
  842. $this->enableCsrfToken();
  843. $data = [
  844. 'title' => 'Some title',
  845. 'body' => 'Some text',
  846. ];
  847. $this->post('/cookie-csrf/posts/header', $data);
  848. $this->assertResponseSuccess();
  849. }
  850. /**
  851. * Integration test for cookie based CSRF token protection fail
  852. */
  853. public function testPostCookieCsrfFailure(): void
  854. {
  855. $this->enableCsrfToken();
  856. $data = [
  857. 'title' => 'Some title',
  858. 'body' => 'Some text',
  859. '_csrfToken' => 'failure',
  860. ];
  861. $this->post('/cookie-csrf/posts/header', $data);
  862. $this->assertResponseCode(403);
  863. }
  864. /**
  865. * Integration test for session based CSRF token protection success
  866. */
  867. public function testPostSessionCsrfSuccess(): void
  868. {
  869. $this->enableCsrfToken();
  870. $data = [
  871. 'title' => 'Some title',
  872. 'body' => 'Some text',
  873. ];
  874. $this->post('/session-csrf/posts/header', $data);
  875. $this->assertResponseSuccess();
  876. }
  877. /**
  878. * Integration test for session based CSRF token protection fail
  879. */
  880. public function testPostSessionCsrfFailure(): void
  881. {
  882. $this->enableCsrfToken();
  883. $data = [
  884. 'title' => 'Some title',
  885. 'body' => 'Some text',
  886. '_csrfToken' => 'failure',
  887. ];
  888. $this->post('/session-csrf/posts/header', $data);
  889. $this->assertResponseCode(403);
  890. }
  891. /**
  892. * Integration test for session based CSRF token protection success with specified cookie name
  893. */
  894. public function testPostSessionCsrfSuccessWithSetCookieName(): void
  895. {
  896. $this->builder->scope('/custom-cookie-csrf/', ['csrf' => 'cookie'], function (RouteBuilder $routes): void {
  897. $routes->registerMiddleware('cookieCsrf', new CsrfProtectionMiddleware(
  898. [
  899. 'cookieName' => 'customCsrfToken',
  900. ]
  901. ));
  902. $routes->applyMiddleware('cookieCsrf');
  903. $routes->connect('/posts/{action}', ['controller' => 'Posts']);
  904. });
  905. $this->enableCsrfToken('customCsrfToken');
  906. $data = [
  907. 'title' => 'Some title',
  908. 'body' => 'Some text',
  909. ];
  910. $this->post('/custom-cookie-csrf/posts/header', $data);
  911. $this->assertResponseSuccess();
  912. }
  913. /**
  914. * Integration test for session based CSRF token protection fail with specified cookie name
  915. */
  916. public function testPostSessionCsrfFailureWithSetCookieName(): void
  917. {
  918. $this->builder->scope('/custom-cookie-csrf/', ['csrf' => 'cookie'], function (RouteBuilder $routes): void {
  919. $routes->registerMiddleware('cookieCsrf', new CsrfProtectionMiddleware(
  920. [
  921. 'cookieName' => 'customCsrfToken',
  922. ]
  923. ));
  924. $routes->applyMiddleware('cookieCsrf');
  925. $routes->connect('/posts/{action}', ['controller' => 'Posts']);
  926. });
  927. $this->enableCsrfToken('customCsrfToken');
  928. $data = [
  929. 'title' => 'Some title',
  930. 'body' => 'Some text',
  931. '_csrfToken' => 'failure',
  932. ];
  933. $this->post('/custom-cookie-csrf/posts/header', $data);
  934. $this->assertResponseCode(403);
  935. }
  936. /**
  937. * Test that exceptions being thrown are handled correctly.
  938. */
  939. public function testWithExpectedException(): void
  940. {
  941. $this->get('/tests_apps/throw_exception');
  942. $this->assertResponseCode(500);
  943. }
  944. /**
  945. * Test that exceptions being thrown are handled correctly by the psr7 stack.
  946. */
  947. public function testWithExpectedExceptionHttpServer(): void
  948. {
  949. $this->get('/tests_apps/throw_exception');
  950. $this->assertResponseCode(500);
  951. }
  952. /**
  953. * Test that exceptions being thrown are handled correctly.
  954. */
  955. public function testWithUnexpectedException(): void
  956. {
  957. $this->expectException(AssertionFailedError::class);
  958. $this->get('/tests_apps/throw_exception');
  959. $this->assertResponseCode(501);
  960. }
  961. /**
  962. * Test redirecting and integration tests.
  963. */
  964. public function testRedirect(): void
  965. {
  966. $this->post('/tests_apps/redirect_to');
  967. $this->assertResponseSuccess();
  968. $this->assertResponseCode(302);
  969. }
  970. /**
  971. * Test redirecting and psr7 stack
  972. */
  973. public function testRedirectHttpServer(): void
  974. {
  975. $this->post('/tests_apps/redirect_to');
  976. $this->assertResponseCode(302);
  977. $this->assertHeader('X-Middleware', 'true');
  978. }
  979. /**
  980. * Test redirecting and integration tests.
  981. */
  982. public function testRedirectPermanent(): void
  983. {
  984. $this->post('/tests_apps/redirect_to_permanent');
  985. $this->assertResponseSuccess();
  986. $this->assertResponseCode(301);
  987. }
  988. /**
  989. * Test the responseOk status assertion
  990. */
  991. public function testAssertResponseStatusCodes(): void
  992. {
  993. $this->_response = new Response();
  994. $this->_response = $this->_response->withStatus(200);
  995. $this->assertResponseOk();
  996. $this->_response = $this->_response->withStatus(201);
  997. $this->assertResponseOk();
  998. $this->_response = $this->_response->withStatus(204);
  999. $this->assertResponseOk();
  1000. $this->_response = $this->_response->withStatus(202);
  1001. $this->assertResponseSuccess();
  1002. $this->_response = $this->_response->withStatus(302);
  1003. $this->assertResponseSuccess();
  1004. $this->_response = $this->_response->withStatus(400);
  1005. $this->assertResponseError();
  1006. $this->_response = $this->_response->withStatus(417);
  1007. $this->assertResponseError();
  1008. $this->_response = $this->_response->withStatus(500);
  1009. $this->assertResponseFailure();
  1010. $this->_response = $this->_response->withStatus(505);
  1011. $this->assertResponseFailure();
  1012. $this->_response = $this->_response->withStatus(301);
  1013. $this->assertResponseCode(301);
  1014. }
  1015. /**
  1016. * Test the location header assertion.
  1017. */
  1018. public function testAssertRedirect(): void
  1019. {
  1020. $this->_response = new Response();
  1021. $this->_response = $this->_response->withHeader('Location', 'http://localhost/get/tasks/index');
  1022. $this->assertRedirect();
  1023. $this->assertRedirect('/get/tasks/index');
  1024. $this->assertRedirect(['controller' => 'Tasks', 'action' => 'index']);
  1025. $this->assertResponseEmpty();
  1026. }
  1027. /**
  1028. * Test the location header assertion.
  1029. */
  1030. public function testAssertRedirectEquals(): void
  1031. {
  1032. $this->_response = new Response();
  1033. $this->_response = $this->_response->withHeader('Location', '/get/tasks/index');
  1034. $this->assertRedirect();
  1035. $this->assertRedirectEquals('/get/tasks/index');
  1036. $this->assertRedirectEquals(['controller' => 'Tasks', 'action' => 'index']);
  1037. $this->assertResponseEmpty();
  1038. }
  1039. /**
  1040. * Test the location header assertion string not contains
  1041. */
  1042. public function testAssertRedirectNotContains(): void
  1043. {
  1044. $this->_response = new Response();
  1045. $this->_response = $this->_response->withHeader('Location', 'http://localhost/tasks/index');
  1046. $this->assertRedirectNotContains('test');
  1047. }
  1048. /**
  1049. * Test the location header assertion.
  1050. */
  1051. public function testAssertNoRedirect(): void
  1052. {
  1053. $this->_response = new Response();
  1054. $this->assertNoRedirect();
  1055. }
  1056. /**
  1057. * Test the location header assertion.
  1058. */
  1059. public function testAssertNoRedirectFail(): void
  1060. {
  1061. $test = new AssertIntegrationTestCase('testBadAssertNoRedirect');
  1062. $result = $test->run();
  1063. $this->assertFalse($result->wasSuccessful());
  1064. $this->assertSame(1, $result->failureCount());
  1065. }
  1066. /**
  1067. * Test the location header assertion string contains
  1068. */
  1069. public function testAssertRedirectContains(): void
  1070. {
  1071. $this->_response = new Response();
  1072. $this->_response = $this->_response->withHeader('Location', 'http://localhost/tasks/index');
  1073. $this->assertRedirectContains('/tasks/index');
  1074. }
  1075. /**
  1076. * Test the header assertion.
  1077. */
  1078. public function testAssertHeader(): void
  1079. {
  1080. $this->_response = new Response();
  1081. $this->_response = $this->_response->withHeader('Etag', 'abc123');
  1082. $this->assertHeader('Etag', 'abc123');
  1083. }
  1084. /**
  1085. * Test the header contains assertion.
  1086. */
  1087. public function testAssertHeaderContains(): void
  1088. {
  1089. $this->_response = new Response();
  1090. $this->_response = $this->_response->withHeader('Etag', 'abc123');
  1091. $this->assertHeaderContains('Etag', 'abc');
  1092. }
  1093. /**
  1094. * Test the header not contains assertion.
  1095. */
  1096. public function testAssertHeaderNotContains(): void
  1097. {
  1098. $this->_response = new Response();
  1099. $this->_response = $this->_response->withHeader('Etag', 'abc123');
  1100. $this->assertHeaderNotContains('Etag', 'xyz');
  1101. }
  1102. /**
  1103. * Test the content type assertion.
  1104. */
  1105. public function testAssertContentType(): void
  1106. {
  1107. $this->_response = new Response();
  1108. $this->_response = $this->_response->withType('json');
  1109. $this->assertContentType('json');
  1110. $this->assertContentType('application/json');
  1111. }
  1112. /**
  1113. * Test that type() in an action sets the content-type header.
  1114. */
  1115. public function testContentTypeInAction(): void
  1116. {
  1117. $this->get('/tests_apps/set_type');
  1118. $this->assertHeader('Content-Type', 'application/json');
  1119. $this->assertContentType('json');
  1120. $this->assertContentType('application/json');
  1121. }
  1122. /**
  1123. * Test the content assertion.
  1124. */
  1125. public function testAssertResponseEquals(): void
  1126. {
  1127. $this->_response = new Response();
  1128. $this->_response = $this->_response->withStringBody('Some content');
  1129. $this->assertResponseEquals('Some content');
  1130. }
  1131. /**
  1132. * Test the negated content assertion.
  1133. */
  1134. public function testAssertResponseNotEquals(): void
  1135. {
  1136. $this->_response = new Response();
  1137. $this->_response = $this->_response->withStringBody('Some content');
  1138. $this->assertResponseNotEquals('Some Content');
  1139. }
  1140. /**
  1141. * Test the content assertion.
  1142. */
  1143. public function testAssertResponseContains(): void
  1144. {
  1145. $this->_response = new Response();
  1146. $this->_response = $this->_response->withStringBody('Some content');
  1147. $this->assertResponseContains('content');
  1148. }
  1149. /**
  1150. * Test the content assertion with no case sensitivity.
  1151. */
  1152. public function testAssertResponseContainsWithIgnoreCaseFlag(): void
  1153. {
  1154. $this->_response = new Response();
  1155. $this->_response = $this->_response->withStringBody('Some content');
  1156. $this->assertResponseContains('some', 'Failed asserting that the body contains given content', true);
  1157. }
  1158. /**
  1159. * Test the negated content assertion.
  1160. */
  1161. public function testAssertResponseNotContains(): void
  1162. {
  1163. $this->_response = new Response();
  1164. $this->_response = $this->_response->withStringBody('Some content');
  1165. $this->assertResponseNotContains('contents');
  1166. }
  1167. /**
  1168. * Test the content regexp assertion.
  1169. */
  1170. public function testAssertResponseRegExp(): void
  1171. {
  1172. $this->_response = new Response();
  1173. $this->_response = $this->_response->withStringBody('Some content');
  1174. $this->assertResponseRegExp('/cont/');
  1175. }
  1176. /**
  1177. * Test the content regexp assertion failing
  1178. */
  1179. public function testAssertResponseRegExpNoResponse(): void
  1180. {
  1181. $this->expectException(AssertionFailedError::class);
  1182. $this->expectExceptionMessage('No response set');
  1183. $this->assertResponseRegExp('/cont/');
  1184. }
  1185. /**
  1186. * Test the negated content regexp assertion.
  1187. */
  1188. public function testAssertResponseNotRegExp(): void
  1189. {
  1190. $this->_response = new Response();
  1191. $this->_response = $this->_response->withStringBody('Some content');
  1192. $this->assertResponseNotRegExp('/cant/');
  1193. }
  1194. /**
  1195. * Test negated content regexp assertion failing
  1196. */
  1197. public function testAssertResponseNotRegExpNoResponse(): void
  1198. {
  1199. $this->expectException(AssertionFailedError::class);
  1200. $this->expectExceptionMessage('No response set');
  1201. $this->assertResponseNotRegExp('/cont/');
  1202. }
  1203. /**
  1204. * Test that works in tandem with testEventManagerReset2 to
  1205. * test the EventManager reset.
  1206. *
  1207. * The return value is passed to testEventManagerReset2 as
  1208. * an arguments.
  1209. */
  1210. public function testEventManagerReset1(): EventManager
  1211. {
  1212. $eventManager = EventManager::instance();
  1213. $this->assertInstanceOf('Cake\Event\EventManager', $eventManager);
  1214. return $eventManager;
  1215. }
  1216. /**
  1217. * Test if the EventManager is reset between tests.
  1218. *
  1219. * @depends testEventManagerReset1
  1220. */
  1221. public function testEventManagerReset2(EventManager $prevEventManager): void
  1222. {
  1223. $this->assertInstanceOf('Cake\Event\EventManager', $prevEventManager);
  1224. $this->assertNotSame($prevEventManager, EventManager::instance());
  1225. }
  1226. /**
  1227. * Test sending file in requests.
  1228. */
  1229. public function testSendFile(): void
  1230. {
  1231. $this->get('/posts/file');
  1232. $this->assertFileResponse(TEST_APP . 'TestApp' . DS . 'Controller' . DS . 'PostsController.php');
  1233. }
  1234. /**
  1235. * Test sending file in requests.
  1236. */
  1237. public function testSendUnlinked(): void
  1238. {
  1239. $file = microtime(true) . 'txt';
  1240. $path = TMP . $file;
  1241. file_put_contents($path, 'testing unlink');
  1242. $this->get("/posts/file?file={$file}");
  1243. $this->assertResponseOk();
  1244. $this->assertFileResponse($path);
  1245. $this->assertFileExists($path);
  1246. system("rm -rf {$path}");
  1247. $this->assertFileDoesNotExist($path);
  1248. }
  1249. /**
  1250. * Test sending file with psr7 stack
  1251. */
  1252. public function testSendFileHttpServer(): void
  1253. {
  1254. $this->get('/posts/file');
  1255. $this->assertFileResponse(TEST_APP . 'TestApp' . DS . 'Controller' . DS . 'PostsController.php');
  1256. }
  1257. /**
  1258. * Test that assertFile requires a response
  1259. */
  1260. public function testAssertFileNoResponse(): void
  1261. {
  1262. $this->expectException(AssertionFailedError::class);
  1263. $this->expectExceptionMessage('No response set, cannot assert content');
  1264. $this->assertFileResponse('foo');
  1265. }
  1266. /**
  1267. * Test that assertFile requires a file
  1268. */
  1269. public function testAssertFileNoFile(): void
  1270. {
  1271. $this->expectException(AssertionFailedError::class);
  1272. $this->expectExceptionMessage('Failed asserting that file was sent.');
  1273. $this->get('/posts/get');
  1274. $this->assertFileResponse('foo');
  1275. }
  1276. /**
  1277. * Test disabling the error handler middleware with exceptions
  1278. * in controllers.
  1279. */
  1280. public function testDisableErrorHandlerMiddleware(): void
  1281. {
  1282. $this->expectException(OutOfBoundsException::class);
  1283. $this->expectExceptionMessage('oh no!');
  1284. $this->disableErrorHandlerMiddleware();
  1285. $this->get('/posts/throw_exception');
  1286. }
  1287. /**
  1288. * tests getting a secure action while passing a query string
  1289. *
  1290. * @dataProvider methodsProvider
  1291. */
  1292. public function testSecureWithQueryString(string $method): void
  1293. {
  1294. $this->enableSecurityToken();
  1295. $this->{$method}('/posts/securePost/?ids[]=1&ids[]=2');
  1296. $this->assertResponseOk();
  1297. }
  1298. /**
  1299. * Tests flash assertions
  1300. *
  1301. * @throws \PHPUnit\Exception
  1302. */
  1303. public function testAssertFlashMessage(): void
  1304. {
  1305. $this->get('/posts/stacked_flash');
  1306. $this->assertFlashElement('flash/error');
  1307. $this->assertFlashElement('flash/success', 'custom');
  1308. $this->assertFlashMessage('Error 1');
  1309. $this->assertFlashMessageAt(0, 'Error 1');
  1310. $this->assertFlashElementAt(0, 'flash/error');
  1311. $this->assertFlashMessage('Error 2');
  1312. $this->assertFlashMessageAt(1, 'Error 2');
  1313. $this->assertFlashElementAt(1, 'flash/error');
  1314. $this->assertFlashMessage('Success 1', 'custom');
  1315. $this->assertFlashMessageAt(0, 'Success 1', 'custom');
  1316. $this->assertFlashElementAt(0, 'flash/success', 'custom');
  1317. $this->assertFlashMessage('Success 2', 'custom');
  1318. $this->assertFlashMessageAt(1, 'Success 2', 'custom');
  1319. $this->assertFlashElementAt(1, 'flash/success', 'custom');
  1320. }
  1321. /**
  1322. * Tests asserting flash messages without first sending a request
  1323. */
  1324. public function testAssertFlashMessageWithoutSendingRequest(): void
  1325. {
  1326. $this->expectException(AssertionFailedError::class);
  1327. $message = 'There is no stored session data. Perhaps you need to run a request?';
  1328. $message .= ' Additionally, ensure `$this->enableRetainFlashMessages()` has been enabled for the test.';
  1329. $this->expectExceptionMessage($message);
  1330. $this->assertFlashMessage('Will not work');
  1331. }
  1332. /**
  1333. * tests failure messages for assertions
  1334. *
  1335. * @param string $assertion Assertion method
  1336. * @param string $message Expected failure message
  1337. * @param string $url URL to test
  1338. * @param mixed ...$rest
  1339. * @dataProvider assertionFailureMessagesProvider
  1340. */
  1341. public function testAssertionFailureMessages($assertion, $message, $url, ...$rest): void
  1342. {
  1343. $this->expectException(AssertionFailedError::class);
  1344. $this->expectExceptionMessage($message);
  1345. Security::setSalt($this->key);
  1346. $this->get($url);
  1347. call_user_func_array([$this, $assertion], $rest);
  1348. }
  1349. /**
  1350. * Test for assertion message generation for previous.
  1351. *
  1352. * @return void
  1353. */
  1354. public function testAssertMessagePrevious()
  1355. {
  1356. $this->expectException(AssertionFailedError::class);
  1357. $this->expectExceptionMessage('Caused by RuntimeException');
  1358. $this->get('/posts/throw_chained');
  1359. $this->assertContentType('test');
  1360. }
  1361. /**
  1362. * data provider for assertion failure messages
  1363. *
  1364. * @return array
  1365. */
  1366. public function assertionFailureMessagesProvider(): array
  1367. {
  1368. $templateDir = TEST_APP . 'templates' . DS;
  1369. return [
  1370. 'assertContentType' => ['assertContentType', 'Failed asserting that \'test\' is set as the Content-Type (`text/html`).', '/posts/index', 'test'],
  1371. 'assertContentTypeVerbose' => ['assertContentType', 'Possibly related to Cake\Routing\Exception\MissingRouteException: "A route matching "/notfound" could not be found."', '/notfound', 'test'],
  1372. 'assertCookie' => ['assertCookie', 'Failed asserting that \'test\' is in cookie \'remember_me\'.', '/posts/index', 'test', 'remember_me'],
  1373. 'assertCookieVerbose' => ['assertCookie', 'Possibly related to Cake\Routing\Exception\MissingRouteException: "A route matching "/notfound" could not be found."', '/notfound', 'test', 'remember_me'],
  1374. 'assertCookieEncrypted' => ['assertCookieEncrypted', 'Failed asserting that \'test\' is encrypted in cookie \'secrets\'.', '/posts/secretCookie', 'test', 'secrets'],
  1375. 'assertCookieEncryptedVerbose' => ['assertCookieEncrypted', 'Possibly related to Cake\Routing\Exception\MissingRouteException: "A route matching "/notfound" could not be found."', '/notfound', 'test', 'NameOfCookie'],
  1376. 'assertCookieNotSet' => ['assertCookieNotSet', 'Failed asserting that \'remember_me\' cookie is not set.', '/posts/index', 'remember_me'],
  1377. 'assertFileResponse' => ['assertFileResponse', 'Failed asserting that \'test\' file was sent.', '/posts/file', 'test'],
  1378. 'assertFileResponseVerbose' => ['assertFileResponse', 'Possibly related to Cake\Routing\Exception\MissingRouteException: "A route matching "/notfound" could not be found."', '/notfound', 'test'],
  1379. 'assertHeader' => ['assertHeader', 'Failed asserting that \'test\' equals content in header \'X-Cake\' (`custom header`).', '/posts/header', 'X-Cake', 'test'],
  1380. 'assertHeaderContains' => ['assertHeaderContains', 'Failed asserting that \'test\' is in header \'X-Cake\' (`custom header`)', '/posts/header', 'X-Cake', 'test'],
  1381. 'assertHeaderNotContains' => ['assertHeaderNotContains', 'Failed asserting that \'custom header\' is not in header \'X-Cake\' (`custom header`)', '/posts/header', 'X-Cake', 'custom header'],
  1382. 'assertHeaderContainsVerbose' => ['assertHeaderContains', 'Possibly related to Cake\Routing\Exception\MissingRouteException: "A route matching "/notfound" could not be found."', '/notfound', 'X-Cake', 'test'],
  1383. 'assertHeaderNotContainsVerbose' => ['assertHeaderNotContains', 'Possibly related to Cake\Routing\Exception\MissingRouteException: "A route matching "/notfound" could not be found."', '/notfound', 'X-Cake', 'test'],
  1384. 'assertLayout' => ['assertLayout', 'Failed asserting that \'custom_layout\' equals layout file `' . $templateDir . 'layout' . DS . 'default.php`.', '/posts/index', 'custom_layout'],
  1385. 'assertLayoutVerbose' => ['assertLayout', 'Possibly related to Cake\Routing\Exception\MissingRouteException: "A route matching "/notfound" could not be found."', '/notfound', 'custom_layout'],
  1386. 'assertRedirect' => ['assertRedirect', 'Failed asserting that \'http://localhost/\' equals content in header \'Location\' (`http://localhost/posts`).', '/posts/flashNoRender', '/'],
  1387. 'assertRedirectVerbose' => ['assertRedirect', 'Possibly related to Cake\Routing\Exception\MissingRouteException: "A route matching "/notfound" could not be found."', '/notfound', '/'],
  1388. 'assertRedirectContains' => ['assertRedirectContains', 'Failed asserting that \'/posts/somewhere-else\' is in header \'Location\' (`http://localhost/posts`).', '/posts/flashNoRender', '/posts/somewhere-else'],
  1389. 'assertRedirectContainsVerbose' => ['assertRedirectContains', 'Possibly related to Cake\Routing\Exception\MissingRouteException: "A route matching "/notfound" could not be found."', '/notfound', '/posts/somewhere-else'],
  1390. 'assertRedirectNotContainsVerbose' => ['assertRedirectNotContains', 'Possibly related to Cake\Routing\Exception\MissingRouteException: "A route matching "/notfound" could not be found."', '/notfound', '/posts/somewhere-else'],
  1391. 'assertResponseCode' => ['assertResponseCode', 'Failed asserting that `302` matches response status code `200`.', '/posts/index', 302],
  1392. 'assertResponseContains' => ['assertResponseContains', 'Failed asserting that \'test\' is in response body.', '/posts/index', 'test'],
  1393. 'assertResponseEmpty' => ['assertResponseEmpty', 'Failed asserting that response body is empty.', '/posts/index'],
  1394. 'assertResponseEquals' => ['assertResponseEquals', 'Failed asserting that \'test\' matches response body.', '/posts/index', 'test'],
  1395. 'assertResponseEqualsVerbose' => ['assertResponseEquals', 'Possibly related to Cake\Routing\Exception\MissingRouteException: "A route matching "/notfound" could not be found."', '/notfound', 'test'],
  1396. 'assertResponseError' => ['assertResponseError', 'Failed asserting that 200 is between 400 and 429.', '/posts/index'],
  1397. 'assertResponseFailure' => ['assertResponseFailure', 'Failed asserting that 200 is between 500 and 505.', '/posts/index'],
  1398. 'assertResponseNotContains' => ['assertResponseNotContains', 'Failed asserting that \'index\' is not in response body.', '/posts/index', 'index'],
  1399. 'assertResponseNotEmpty' => ['assertResponseNotEmpty', 'Failed asserting that response body is not empty.', '/posts/empty_response'],
  1400. 'assertResponseNotEquals' => ['assertResponseNotEquals', 'Failed asserting that \'posts index\' does not match response body.', '/posts/index/error', 'posts index'],
  1401. 'assertResponseNotRegExp' => ['assertResponseNotRegExp', 'Failed asserting that `/index/` PCRE pattern not found in response body.', '/posts/index/error', '/index/'],
  1402. 'assertResponseNotRegExpVerbose' => ['assertResponseNotRegExp', 'Possibly related to Cake\Routing\Exception\MissingRouteException: "A route matching "/notfound" could not be found."', '/notfound', '/index/'],
  1403. 'assertResponseOk' => ['assertResponseOk', 'Failed asserting that 404 is between 200 and 204.', '/posts/missing', '/index/'],
  1404. 'assertResponseRegExp' => ['assertResponseRegExp', 'Failed asserting that `/test/` PCRE pattern found in response body.', '/posts/index/error', '/test/'],
  1405. 'assertResponseSuccess' => ['assertResponseSuccess', 'Failed asserting that 404 is between 200 and 308.', '/posts/missing'],
  1406. 'assertResponseSuccessVerbose' => ['assertResponseSuccess', 'Possibly related to Cake\Controller\Exception\MissingActionException: "Action PostsController::missing() could not be found, or is not accessible."', '/posts/missing'],
  1407. 'assertSession' => ['assertSession', 'Failed asserting that \'test\' is in session path \'Missing.path\'.', '/posts/index', 'test', 'Missing.path'],
  1408. 'assertSessionHasKey' => ['assertSessionHasKey', 'Failed asserting that \'Missing.path\' is a path present in the session.', '/posts/index', 'Missing.path'],
  1409. 'assertSessionNotHasKey' => ['assertSessionNotHasKey', 'Failed asserting that \'Flash.flash\' is not a path present in the session.', '/posts/index', 'Flash.flash'],
  1410. 'assertTemplate' => ['assertTemplate', 'Failed asserting that \'custom_template\' equals template file `' . $templateDir . 'Posts' . DS . 'index.php`.', '/posts/index', 'custom_template'],
  1411. 'assertTemplateVerbose' => ['assertTemplate', 'Possibly related to Cake\Routing\Exception\MissingRouteException: "A route matching "/notfound" could not be found."', '/notfound', 'custom_template'],
  1412. 'assertFlashMessage' => ['assertFlashMessage', 'Failed asserting that \'missing\' is in \'flash\' message.', '/posts/index', 'missing'],
  1413. 'assertFlashMessageWithKey' => ['assertFlashMessage', 'Failed asserting that \'missing\' is in \'auth\' message.', '/posts/index', 'missing', 'auth'],
  1414. 'assertFlashMessageAt' => ['assertFlashMessageAt', 'Failed asserting that \'missing\' is in \'flash\' message #0.', '/posts/index', 0, 'missing'],
  1415. 'assertFlashMessageAtWithKey' => ['assertFlashMessageAt', 'Failed asserting that \'missing\' is in \'auth\' message #0.', '/posts/index', 0, 'missing', 'auth'],
  1416. 'assertFlashElement' => ['assertFlashElement', 'Failed asserting that \'missing\' is in \'flash\' element.', '/posts/index', 'missing'],
  1417. 'assertFlashElementWithKey' => ['assertFlashElement', 'Failed asserting that \'missing\' is in \'auth\' element.', '/posts/index', 'missing', 'auth'],
  1418. 'assertFlashElementAt' => ['assertFlashElementAt', 'Failed asserting that \'missing\' is in \'flash\' element #0.', '/posts/index', 0, 'missing'],
  1419. 'assertFlashElementAtWithKey' => ['assertFlashElementAt', 'Failed asserting that \'missing\' is in \'auth\' element #0.', '/posts/index', 0, 'missing', 'auth'],
  1420. ];
  1421. }
  1422. /**
  1423. * data provider for HTTP methods
  1424. *
  1425. * @return array
  1426. */
  1427. public function methodsProvider(): array
  1428. {
  1429. return [
  1430. 'GET' => ['get'],
  1431. 'POST' => ['post'],
  1432. 'PATCH' => ['patch'],
  1433. 'PUT' => ['put'],
  1434. 'DELETE' => ['delete'],
  1435. ];
  1436. }
  1437. /**
  1438. * Test assertCookieNotSet is creating a verbose message
  1439. */
  1440. public function testAssertCookieNotSetVerbose(): void
  1441. {
  1442. $this->expectException(AssertionFailedError::class);
  1443. $this->expectExceptionMessage('Possibly related to Cake\Routing\Exception\MissingRouteException: "A route matching "/notfound" could not be found."');
  1444. $this->get('/notfound');
  1445. $this->_response = $this->_response->withCookie(new Cookie('cookie', 'value'));
  1446. $this->assertCookieNotSet('cookie');
  1447. }
  1448. /**
  1449. * Test assertNoRedirect is creating a verbose message
  1450. */
  1451. public function testAssertNoRedirectVerbose(): void
  1452. {
  1453. $this->expectException(AssertionFailedError::class);
  1454. $this->expectExceptionMessage('Possibly related to Cake\Routing\Exception\MissingRouteException: "A route matching "/notfound" could not be found."');
  1455. $this->get('/notfound');
  1456. $this->_response = $this->_response->withHeader('Location', '/redirect');
  1457. $this->assertNoRedirect();
  1458. }
  1459. /**
  1460. * Test the header assertion generating a verbose message.
  1461. */
  1462. public function testAssertHeaderVerbose(): void
  1463. {
  1464. $this->expectException(AssertionFailedError::class);
  1465. $this->expectExceptionMessage('Possibly related to Cake\Routing\Exception\MissingRouteException: "A route matching "/notfound" could not be found."');
  1466. $this->get('/notfound');
  1467. $this->assertHeader('Etag', 'abc123');
  1468. }
  1469. /**
  1470. * Test the assertResponseNotEquals generates a verbose message.
  1471. */
  1472. public function testAssertResponseNotEqualsVerbose(): void
  1473. {
  1474. $this->expectException(AssertionFailedError::class);
  1475. $this->expectExceptionMessage('Possibly related to Cake\Routing\Exception\MissingRouteException: "A route matching "/notfound" could not be found."');
  1476. $this->get('/notfound');
  1477. $this->_response = $this->_response->withStringBody('body');
  1478. $this->assertResponseNotEquals('body');
  1479. }
  1480. /**
  1481. * Test the assertResponseRegExp generates a verbose message.
  1482. */
  1483. public function testAssertResponseRegExpVerbose(): void
  1484. {
  1485. $this->expectException(AssertionFailedError::class);
  1486. $this->expectExceptionMessage('Possibly related to Cake\Routing\Exception\MissingRouteException: "A route matching "/notfound" could not be found."');
  1487. $this->get('/notfound');
  1488. $this->_response = $this->_response->withStringBody('body');
  1489. $this->assertResponseRegExp('/patternNotFound/');
  1490. }
  1491. /**
  1492. * Test the assertion generates a verbose message for session related checks.
  1493. *
  1494. * @dataProvider assertionFailureSessionVerboseProvider
  1495. * @param mixed ...$rest
  1496. */
  1497. public function testAssertSessionRelatedVerboseMessages(string $assertMethod, ...$rest): void
  1498. {
  1499. $this->expectException(AssertionFailedError::class);
  1500. $this->expectExceptionMessage('Possibly related to OutOfBoundsException: "oh no!"');
  1501. $this->get('/posts/throw_exception');
  1502. $this->_requestSession = new Session();
  1503. call_user_func_array([$this, $assertMethod], $rest);
  1504. }
  1505. /**
  1506. * data provider for assertion verbose session related tests
  1507. *
  1508. * @return array
  1509. */
  1510. public function assertionFailureSessionVerboseProvider(): array
  1511. {
  1512. return [
  1513. 'assertFlashMessageVerbose' => ['assertFlashMessage', 'notfound'],
  1514. 'assertFlashMessageAtVerbose' => ['assertFlashMessageAt', 2, 'notfound'],
  1515. 'assertFlashElementVerbose' => ['assertFlashElement', 'notfound'],
  1516. 'assertSessionVerbose' => ['assertSession', 'notfound', 'notfound'],
  1517. ];
  1518. }
  1519. /**
  1520. * Test viewVariable not found
  1521. */
  1522. public function testViewVariableNotFoundShouldReturnNull(): void
  1523. {
  1524. $this->_controller = new Controller();
  1525. $this->assertNull($this->viewVariable('notFound'));
  1526. }
  1527. /**
  1528. * Integration test for a controller with action dependencies.
  1529. */
  1530. public function testHandleWithContainerDependencies(): void
  1531. {
  1532. $this->get('/dependencies/requiredDep');
  1533. $this->assertResponseOk();
  1534. $this->assertResponseContains('"key":"value"', 'Contains the data from the stdClass container object.');
  1535. }
  1536. /**
  1537. * Test that mockService() injects into controllers.
  1538. */
  1539. public function testHandleWithMockServices(): void
  1540. {
  1541. $this->mockService(stdClass::class, function () {
  1542. return json_decode('{"mock":true}');
  1543. });
  1544. $this->get('/dependencies/requiredDep');
  1545. $this->assertResponseOk();
  1546. $this->assertResponseContains('"mock":true', 'Contains the data from the stdClass mock container.');
  1547. }
  1548. /**
  1549. * Test that mockService() injects into controllers.
  1550. */
  1551. public function testHandleWithMockServicesFromReflectionContainer(): void
  1552. {
  1553. $this->mockService(ReflectionDependency::class, function () {
  1554. return new ReflectionDependency();
  1555. });
  1556. $this->get('/dependencies/reflectionDep');
  1557. $this->assertResponseOk();
  1558. $this->assertResponseContains('{"dep":{}}', 'Contains the data from the reflection container');
  1559. }
  1560. /**
  1561. * Test that mockService() injects into controllers.
  1562. */
  1563. public function testHandleWithMockServicesOverwrite(): void
  1564. {
  1565. $this->mockService(stdClass::class, function () {
  1566. return json_decode('{"first":true}');
  1567. });
  1568. $this->mockService(stdClass::class, function () {
  1569. return json_decode('{"second":true}');
  1570. });
  1571. $this->get('/dependencies/requiredDep');
  1572. $this->assertResponseOk();
  1573. $this->assertResponseContains('"second":true', 'Contains the data from the stdClass mock container.');
  1574. }
  1575. /**
  1576. * Test that removeMock() unsets mocks
  1577. */
  1578. public function testHandleWithMockServicesUnset(): void
  1579. {
  1580. $this->mockService(stdClass::class, function () {
  1581. return json_decode('{"first":true}');
  1582. });
  1583. $this->removeMockService(stdClass::class);
  1584. $this->get('/dependencies/requiredDep');
  1585. $this->assertResponseOk();
  1586. $this->assertResponseNotContains('"first":true');
  1587. }
  1588. }