RestTest.php 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457
  1. <?php
  2. use Codeception\Test\Unit;
  3. use Codeception\Util\Stub as Stub;
  4. /**
  5. * Class RestTest
  6. * @group appveyor
  7. */
  8. class RestTest extends Unit
  9. {
  10. /**
  11. * @var \Codeception\Module\REST
  12. */
  13. protected $module;
  14. public function setUp()
  15. {
  16. $connector = new \Codeception\Lib\Connector\Universal();
  17. $connector->setIndex(\Codeception\Configuration::dataDir() . '/rest/index.php');
  18. $connectionModule = new \Codeception\Module\UniversalFramework(make_container());
  19. $connectionModule->client = $connector;
  20. $connectionModule->_initialize();
  21. $this->module = Stub::make('\Codeception\Module\REST');
  22. $this->module->_inject($connectionModule);
  23. $this->module->_initialize();
  24. $this->module->_before(Stub::makeEmpty('\Codeception\Test\Test'));
  25. $this->module->client->setServerParameters([
  26. 'SCRIPT_FILENAME' => 'index.php',
  27. 'SCRIPT_NAME' => 'index',
  28. 'SERVER_NAME' => 'localhost',
  29. 'SERVER_PROTOCOL' => 'http'
  30. ]);
  31. }
  32. public function testConflictsWithAPI()
  33. {
  34. $this->assertInstanceOf('Codeception\Lib\Interfaces\ConflictsWithModule', $this->module);
  35. $this->assertEquals('Codeception\Lib\Interfaces\API', $this->module->_conflicts());
  36. }
  37. private function setStubResponse($response)
  38. {
  39. $connectionModule = Stub::make('\Codeception\Module\UniversalFramework', ['_getResponseContent' => $response]);
  40. $this->module->_inject($connectionModule);
  41. $this->module->_initialize();
  42. $this->module->_before(Stub::makeEmpty('\Codeception\Test\Test'));
  43. }
  44. public function testBeforeHookResetsVariables()
  45. {
  46. $this->module->haveHttpHeader('Origin', 'http://www.example.com');
  47. $this->module->sendGET('/rest/user/');
  48. $server = $this->module->client->getInternalRequest()->getServer();
  49. $this->assertArrayHasKey('HTTP_ORIGIN', $server);
  50. $this->module->_before(Stub::makeEmpty('\Codeception\Test\Test'));
  51. $this->module->sendGET('/rest/user/');
  52. $server = $this->module->client->getInternalRequest()->getServer();
  53. $this->assertArrayNotHasKey('HTTP_ORIGIN', $server);
  54. }
  55. public function testGet()
  56. {
  57. $this->module->sendGET('/rest/user/');
  58. $this->module->seeResponseIsJson();
  59. $this->module->seeResponseContains('davert');
  60. $this->module->seeResponseContainsJson(['name' => 'davert']);
  61. $this->module->seeResponseCodeIs(200);
  62. $this->module->dontSeeResponseCodeIs(404);
  63. }
  64. public function testPost()
  65. {
  66. $this->module->sendPOST('/rest/user/', ['name' => 'john']);
  67. $this->module->seeResponseContains('john');
  68. $this->module->seeResponseContainsJson(['name' => 'john']);
  69. }
  70. public function testPut()
  71. {
  72. $this->module->sendPUT('/rest/user/', ['name' => 'laura']);
  73. $this->module->seeResponseContains('davert@mail.ua');
  74. $this->module->seeResponseContainsJson(['name' => 'laura']);
  75. $this->module->dontSeeResponseContainsJson(['name' => 'john']);
  76. }
  77. public function testGrabDataFromResponseByJsonPath()
  78. {
  79. $this->module->sendGET('/rest/user/');
  80. // simple assoc array
  81. $this->assertEquals(['davert@mail.ua'], $this->module->grabDataFromResponseByJsonPath('$.email'));
  82. // nested assoc array
  83. $this->assertEquals(['Kyiv'], $this->module->grabDataFromResponseByJsonPath('$.address.city'));
  84. // nested index array
  85. $this->assertEquals(['DavertMik'], $this->module->grabDataFromResponseByJsonPath('$.aliases[0]'));
  86. // empty if data not found
  87. $this->assertEquals([], $this->module->grabDataFromResponseByJsonPath('$.address.street'));
  88. }
  89. public function testValidJson()
  90. {
  91. $this->setStubResponse('{"xxx": "yyy"}');
  92. $this->module->seeResponseIsJson();
  93. $this->setStubResponse('{"xxx": "yyy", "zzz": ["a","b"]}');
  94. $this->module->seeResponseIsJson();
  95. $this->module->seeResponseEquals('{"xxx": "yyy", "zzz": ["a","b"]}');
  96. }
  97. public function testInvalidJson()
  98. {
  99. $this->setExpectedException('PHPUnit\Framework\ExpectationFailedException');
  100. $this->setStubResponse('{xxx = yyy}');
  101. $this->module->seeResponseIsJson();
  102. }
  103. public function testValidXml()
  104. {
  105. $this->setStubResponse('<xml></xml>');
  106. $this->module->seeResponseIsXml();
  107. $this->setStubResponse('<xml><name>John</name></xml>');
  108. $this->module->seeResponseIsXml();
  109. $this->module->seeResponseEquals('<xml><name>John</name></xml>');
  110. }
  111. public function testXmlResponseEquals()
  112. {
  113. $this->setStubResponse('<xml></xml>');
  114. $this->module->seeResponseIsXml();
  115. $this->module->seeXmlResponseEquals('<xml></xml>');
  116. }
  117. public function testInvalidXml()
  118. {
  119. $this->setExpectedException('PHPUnit\Framework\ExpectationFailedException');
  120. $this->setStubResponse('<xml><name>John</surname></xml>');
  121. $this->module->seeResponseIsXml();
  122. }
  123. public function testSeeInJsonResponse()
  124. {
  125. $this->setStubResponse(
  126. '{"ticket": {"title": "Bug should be fixed", "user": {"name": "Davert"}, "labels": null}}'
  127. );
  128. $this->module->seeResponseIsJson();
  129. $this->module->seeResponseContainsJson(['name' => 'Davert']);
  130. $this->module->seeResponseContainsJson(['user' => ['name' => 'Davert']]);
  131. $this->module->seeResponseContainsJson(['ticket' => ['title' => 'Bug should be fixed']]);
  132. $this->module->seeResponseContainsJson(['ticket' => ['user' => ['name' => 'Davert']]]);
  133. $this->module->seeResponseContainsJson(['ticket' => ['labels' => null]]);
  134. }
  135. public function testSeeInJsonCollection()
  136. {
  137. $this->setStubResponse(
  138. '[{"user":"Blacknoir","age":"42","tags":["wed-dev","php"]},'
  139. . '{"user":"John Doe","age":27,"tags":["web-dev","java"]}]'
  140. );
  141. $this->module->seeResponseIsJson();
  142. $this->module->seeResponseContainsJson(['tags' => ['web-dev', 'java']]);
  143. $this->module->seeResponseContainsJson(['user' => 'John Doe', 'age' => 27]);
  144. $this->module->seeResponseContainsJson([['user' => 'John Doe', 'age' => 27]]);
  145. $this->module->seeResponseContainsJson(
  146. [['user' => 'Blacknoir', 'age' => 42], ['user' => 'John Doe', 'age' => "27"]]
  147. );
  148. }
  149. public function testArrayJson()
  150. {
  151. $this->setStubResponse(
  152. '[{"id":1,"title": "Bug should be fixed"},{"title": "Feature should be implemented","id":2}]'
  153. );
  154. $this->module->seeResponseContainsJson(['id' => 1]);
  155. }
  156. public function testDontSeeInJson()
  157. {
  158. $this->setStubResponse('{"ticket": {"title": "Bug should be fixed", "user": {"name": "Davert"}}}');
  159. $this->module->seeResponseIsJson();
  160. $this->module->dontSeeResponseContainsJson(['name' => 'Davet']);
  161. $this->module->dontSeeResponseContainsJson(['user' => ['name' => 'Davet']]);
  162. $this->module->dontSeeResponseContainsJson(['user' => ['title' => 'Bug should be fixed']]);
  163. }
  164. public function testApplicationJsonIncludesJsonAsContent()
  165. {
  166. $this->module->haveHttpHeader('Content-Type', 'application/json');
  167. $this->module->sendPOST('/', ['name' => 'john']);
  168. /** @var $request \Symfony\Component\BrowserKit\Request **/
  169. $request = $this->module->client->getRequest();
  170. $this->assertContains('application/json', $request->getServer());
  171. $server = $request->getServer();
  172. $this->assertEquals('application/json', $server['HTTP_CONTENT_TYPE']);
  173. $this->assertJson($request->getContent());
  174. $this->assertEmpty($request->getParameters());
  175. }
  176. public function testApplicationJsonIncludesObjectSerialized()
  177. {
  178. $this->module->haveHttpHeader('Content-Type', 'application/json');
  179. $this->module->sendPOST('/', new JsonSerializedItem());
  180. /** @var $request \Symfony\Component\BrowserKit\Request **/
  181. $request = $this->module->client->getRequest();
  182. $this->assertContains('application/json', $request->getServer());
  183. $this->assertJson($request->getContent());
  184. }
  185. public function testGetApplicationJsonNotIncludesJsonAsContent()
  186. {
  187. $this->module->haveHttpHeader('Content-Type', 'application/json');
  188. $this->module->sendGET('/', ['name' => 'john']);
  189. /** @var $request \Symfony\Component\BrowserKit\Request **/
  190. $request = $this->module->client->getRequest();
  191. $this->assertNull($request->getContent());
  192. $this->assertContains('john', $request->getParameters());
  193. }
  194. public function testUrlIsFull()
  195. {
  196. $this->module->sendGET('/api/v1/users');
  197. /** @var $request \Symfony\Component\BrowserKit\Request **/
  198. $request = $this->module->client->getRequest();
  199. $this->assertEquals('http://localhost/api/v1/users', $request->getUri());
  200. }
  201. public function testSeeHeaders()
  202. {
  203. $response = new \Symfony\Component\BrowserKit\Response("", 200, [
  204. 'Cache-Control' => ['no-cache', 'no-store'],
  205. 'Content_Language' => 'en-US'
  206. ]);
  207. $this->module->client->mockResponse($response);
  208. $this->module->sendGET('/');
  209. $this->module->seeHttpHeader('Cache-Control');
  210. $this->module->seeHttpHeader('content_language', 'en-US');
  211. $this->module->seeHttpHeader('Content-Language', 'en-US');
  212. $this->module->dontSeeHttpHeader('Content-Language', 'en-RU');
  213. $this->module->dontSeeHttpHeader('Content-Language1');
  214. $this->module->seeHttpHeaderOnce('Content-Language');
  215. $this->assertEquals('en-US', $this->module->grabHttpHeader('Content-Language'));
  216. $this->assertEquals('no-cache', $this->module->grabHttpHeader('Cache-Control'));
  217. $this->assertEquals(['no-cache', 'no-store'], $this->module->grabHttpHeader('Cache-Control', false));
  218. }
  219. public function testSeeHeadersOnce()
  220. {
  221. $this->shouldFail();
  222. $response = new \Symfony\Component\BrowserKit\Response("", 200, [
  223. 'Cache-Control' => ['no-cache', 'no-store'],
  224. ]);
  225. $this->module->client->mockResponse($response);
  226. $this->module->sendGET('/');
  227. $this->module->seeHttpHeaderOnce('Cache-Control');
  228. }
  229. public function testSeeResponseJsonMatchesXpath()
  230. {
  231. $this->setStubResponse(
  232. '[{"user":"Blacknoir","age":27,"tags":["wed-dev","php"]},'
  233. . '{"user":"John Doe","age":27,"tags":["web-dev","java"]}]'
  234. );
  235. $this->module->seeResponseIsJson();
  236. $this->module->seeResponseJsonMatchesXpath('//user');
  237. }
  238. public function testSeeResponseJsonMatchesJsonPath()
  239. {
  240. $this->setStubResponse(
  241. '[{"user":"Blacknoir","age":27,"tags":["wed-dev","php"]},'
  242. . '{"user":"John Doe","age":27,"tags":["web-dev","java"]}]'
  243. );
  244. $this->module->seeResponseJsonMatchesJsonPath('$[*].user');
  245. $this->module->seeResponseJsonMatchesJsonPath('$[1].tags');
  246. }
  247. public function testDontSeeResponseJsonMatchesJsonPath()
  248. {
  249. $this->setStubResponse(
  250. '[{"user":"Blacknoir","age":27,"tags":["wed-dev","php"]},'
  251. . '{"user":"John Doe","age":27,"tags":["web-dev","java"]}]'
  252. );
  253. $this->module->dontSeeResponseJsonMatchesJsonPath('$[*].profile');
  254. }
  255. public function testDontSeeResponseJsonMatchesXpath()
  256. {
  257. $this->setStubResponse(
  258. '[{"user":"Blacknoir","age":27,"tags":["wed-dev","php"]},'
  259. . '{"user":"John Doe","age":27,"tags":["web-dev","java"]}]'
  260. );
  261. $this->module->dontSeeResponseJsonMatchesXpath('//status');
  262. }
  263. public function testDontSeeResponseJsonMatchesXpathFails()
  264. {
  265. $this->shouldFail();
  266. $this->setStubResponse(
  267. '[{"user":"Blacknoir","age":27,"tags":["wed-dev","php"]},'
  268. . '{"user":"John Doe","age":27,"tags":["web-dev","java"]}]'
  269. );
  270. $this->module->dontSeeResponseJsonMatchesXpath('//user');
  271. }
  272. /**
  273. * @Issue https://github.com/Codeception/Codeception/issues/2775
  274. */
  275. public function testSeeResponseJsonMatchesXPathWorksWithAmpersand()
  276. {
  277. $this->setStubResponse('{ "product":[ { "category":[ { "comment":"something & something" } ] } ] }');
  278. $this->module->seeResponseIsJson();
  279. $this->module->seeResponseJsonMatchesXpath('//comment');
  280. }
  281. public function testSeeResponseJsonMatchesJsonPathFails()
  282. {
  283. $this->shouldFail();
  284. $this->setStubResponse(
  285. '[{"user":"Blacknoir","age":27,"tags":["wed-dev","php"]},'
  286. . '{"user":"John Doe","age":27,"tags":["web-dev","java"]}]'
  287. );
  288. $this->module->seeResponseIsJson();
  289. $this->module->seeResponseJsonMatchesJsonPath('$[*].profile');
  290. }
  291. public function testStructuredJsonPathAndXPath()
  292. {
  293. $this->setStubResponse(
  294. '{ "store": {"book": [{ "category": "reference", "author": "Nigel Rees", '
  295. . '"title": "Sayings of the Century", "price": 8.95 }, { "category": "fiction", "author": "Evelyn Waugh", '
  296. . '"title": "Sword of Honour", "price": 12.99 }, { "category": "fiction", "author": "Herman Melville", '
  297. . '"title": "Moby Dick", "isbn": "0-553-21311-3", "price": 8.99 }, { "category": "fiction", '
  298. . '"author": "J. R. R. Tolkien", "title": "The Lord of the Rings", "isbn": "0-395-19395-8", '
  299. . '"price": 22.99 } ], "bicycle": {"color": "red", "price": 19.95 } } }'
  300. );
  301. $this->module->seeResponseIsJson();
  302. $this->module->seeResponseJsonMatchesXpath('//book/category');
  303. $this->module->seeResponseJsonMatchesJsonPath('$..book');
  304. $this->module->seeResponseJsonMatchesJsonPath('$.store.book[2].author');
  305. $this->module->dontSeeResponseJsonMatchesJsonPath('$.invalid');
  306. $this->module->dontSeeResponseJsonMatchesJsonPath('$.store.book.*.invalidField');
  307. }
  308. public function testApplicationJsonSubtypeIncludesObjectSerialized()
  309. {
  310. $this->module->haveHttpHeader('Content-Type', 'application/resource+json');
  311. $this->module->sendPOST('/', new JsonSerializedItem());
  312. /** @var $request \Symfony\Component\BrowserKit\Request **/
  313. $request = $this->module->client->getRequest();
  314. $this->assertContains('application/resource+json', $request->getServer());
  315. $this->assertJson($request->getContent());
  316. }
  317. public function testJsonTypeMatches()
  318. {
  319. $this->setStubResponse('{"xxx": "yyy", "user_id": 1}');
  320. $this->module->seeResponseMatchesJsonType(['xxx' => 'string', 'user_id' => 'integer:<10']);
  321. $this->module->dontSeeResponseMatchesJsonType(['xxx' => 'integer', 'user_id' => 'integer:<10']);
  322. }
  323. public function testJsonTypeMatchesWithJsonPath()
  324. {
  325. $this->setStubResponse('{"users": [{ "name": "davert"}, {"id": 1}]}');
  326. $this->module->seeResponseMatchesJsonType(['name' => 'string'], '$.users[0]');
  327. $this->module->seeResponseMatchesJsonType(['id' => 'integer'], '$.users[1]');
  328. $this->module->dontSeeResponseMatchesJsonType(['id' => 'integer'], '$.users[0]');
  329. }
  330. public function testMatchJsonTypeFailsWithNiceMessage()
  331. {
  332. $this->setStubResponse('{"xxx": "yyy", "user_id": 1}');
  333. try {
  334. $this->module->seeResponseMatchesJsonType(['zzz' => 'string']);
  335. $this->fail('it had to throw exception');
  336. } catch (PHPUnit\Framework\AssertionFailedError $e) {
  337. $this->assertEquals('Key `zzz` doesn\'t exist in {"xxx":"yyy","user_id":1}', $e->getMessage());
  338. }
  339. }
  340. public function testDontMatchJsonTypeFailsWithNiceMessage()
  341. {
  342. $this->setStubResponse('{"xxx": "yyy", "user_id": 1}');
  343. try {
  344. $this->module->dontSeeResponseMatchesJsonType(['xxx' => 'string']);
  345. $this->fail('it had to throw exception');
  346. } catch (PHPUnit\Framework\AssertionFailedError $e) {
  347. $this->assertEquals('Unexpectedly response matched: {"xxx":"yyy","user_id":1}', $e->getMessage());
  348. }
  349. }
  350. public function testSeeResponseIsJsonFailsWhenResponseIsEmpty()
  351. {
  352. $this->shouldFail();
  353. $this->setStubResponse('');
  354. $this->module->seeResponseIsJson();
  355. }
  356. public function testSeeResponseIsJsonFailsWhenResponseIsInvalidJson()
  357. {
  358. $this->shouldFail();
  359. $this->setStubResponse('{');
  360. $this->module->seeResponseIsJson();
  361. }
  362. public function testSeeResponseJsonMatchesXpathCanHandleResponseWithOneElement()
  363. {
  364. $this->setStubResponse('{"success": 1}');
  365. $this->module->seeResponseJsonMatchesXpath('//success');
  366. }
  367. public function testSeeResponseJsonMatchesXpathCanHandleResponseWithTwoElements()
  368. {
  369. $this->setStubResponse('{"success": 1, "info": "test"}');
  370. $this->module->seeResponseJsonMatchesXpath('//success');
  371. }
  372. public function testSeeResponseJsonMatchesXpathCanHandleResponseWithOneSubArray()
  373. {
  374. $this->setStubResponse('{"array": {"success": 1}}');
  375. $this->module->seeResponseJsonMatchesXpath('//array/success');
  376. }
  377. public function testSeeBinaryResponseEquals()
  378. {
  379. $data = base64_decode('/9j/2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgGBgUGCQgKCgkICQkKDA8MCgsOCwkJDRENDg8QEBEQCgwSExIQEw8QEBD/yQALCAABAAEBAREA/8wABgAQEAX/2gAIAQEAAD8A0s8g/9k=');
  380. $this->setStubResponse($data);
  381. $this->module->seeBinaryResponseEquals(md5($data));
  382. }
  383. public function testDontSeeBinaryResponseEquals()
  384. {
  385. $data = base64_decode('/9j/2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgGBgUGCQgKCgkICQkKDA8MCgsOCwkJDRENDg8QEBEQCgwSExIQEw8QEBD/yQALCAABAAEBAREA/8wABgAQEAX/2gAIAQEAAD8A0s8g/9k=');
  386. $this->setStubResponse($data);
  387. $this->module->dontSeeBinaryResponseEquals('024f615102cdb3c8c7cf75cdc5a83d15');
  388. }
  389. public function testAmDigestAuthenticatedThrowsExceptionWithFunctionalModules()
  390. {
  391. $this->setExpectedException('\Codeception\Exception\ModuleException', 'Not supported by functional modules');
  392. $this->module->amDigestAuthenticated('username', 'password');
  393. }
  394. protected function shouldFail()
  395. {
  396. $this->setExpectedException('PHPUnit\Framework\AssertionFailedError');
  397. }
  398. }
  399. class JsonSerializedItem implements JsonSerializable
  400. {
  401. public function jsonSerialize()
  402. {
  403. return array("hello" => "world");
  404. }
  405. }