RestTest.php 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449
  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 testInvalidXml()
  112. {
  113. $this->setExpectedException('PHPUnit\Framework\ExpectationFailedException');
  114. $this->setStubResponse('<xml><name>John</surname></xml>');
  115. $this->module->seeResponseIsXml();
  116. }
  117. public function testSeeInJsonResponse()
  118. {
  119. $this->setStubResponse(
  120. '{"ticket": {"title": "Bug should be fixed", "user": {"name": "Davert"}, "labels": null}}'
  121. );
  122. $this->module->seeResponseIsJson();
  123. $this->module->seeResponseContainsJson(['name' => 'Davert']);
  124. $this->module->seeResponseContainsJson(['user' => ['name' => 'Davert']]);
  125. $this->module->seeResponseContainsJson(['ticket' => ['title' => 'Bug should be fixed']]);
  126. $this->module->seeResponseContainsJson(['ticket' => ['user' => ['name' => 'Davert']]]);
  127. $this->module->seeResponseContainsJson(['ticket' => ['labels' => null]]);
  128. }
  129. public function testSeeInJsonCollection()
  130. {
  131. $this->setStubResponse(
  132. '[{"user":"Blacknoir","age":"42","tags":["wed-dev","php"]},'
  133. . '{"user":"John Doe","age":27,"tags":["web-dev","java"]}]'
  134. );
  135. $this->module->seeResponseIsJson();
  136. $this->module->seeResponseContainsJson(['tags' => ['web-dev', 'java']]);
  137. $this->module->seeResponseContainsJson(['user' => 'John Doe', 'age' => 27]);
  138. $this->module->seeResponseContainsJson([['user' => 'John Doe', 'age' => 27]]);
  139. $this->module->seeResponseContainsJson(
  140. [['user' => 'Blacknoir', 'age' => 42], ['user' => 'John Doe', 'age' => "27"]]
  141. );
  142. }
  143. public function testArrayJson()
  144. {
  145. $this->setStubResponse(
  146. '[{"id":1,"title": "Bug should be fixed"},{"title": "Feature should be implemented","id":2}]'
  147. );
  148. $this->module->seeResponseContainsJson(['id' => 1]);
  149. }
  150. public function testDontSeeInJson()
  151. {
  152. $this->setStubResponse('{"ticket": {"title": "Bug should be fixed", "user": {"name": "Davert"}}}');
  153. $this->module->seeResponseIsJson();
  154. $this->module->dontSeeResponseContainsJson(['name' => 'Davet']);
  155. $this->module->dontSeeResponseContainsJson(['user' => ['name' => 'Davet']]);
  156. $this->module->dontSeeResponseContainsJson(['user' => ['title' => 'Bug should be fixed']]);
  157. }
  158. public function testApplicationJsonIncludesJsonAsContent()
  159. {
  160. $this->module->haveHttpHeader('Content-Type', 'application/json');
  161. $this->module->sendPOST('/', ['name' => 'john']);
  162. /** @var $request \Symfony\Component\BrowserKit\Request **/
  163. $request = $this->module->client->getRequest();
  164. $this->assertContains('application/json', $request->getServer());
  165. $server = $request->getServer();
  166. $this->assertEquals('application/json', $server['HTTP_CONTENT_TYPE']);
  167. $this->assertJson($request->getContent());
  168. $this->assertEmpty($request->getParameters());
  169. }
  170. public function testApplicationJsonIncludesObjectSerialized()
  171. {
  172. $this->module->haveHttpHeader('Content-Type', 'application/json');
  173. $this->module->sendPOST('/', new JsonSerializedItem());
  174. /** @var $request \Symfony\Component\BrowserKit\Request **/
  175. $request = $this->module->client->getRequest();
  176. $this->assertContains('application/json', $request->getServer());
  177. $this->assertJson($request->getContent());
  178. }
  179. public function testGetApplicationJsonNotIncludesJsonAsContent()
  180. {
  181. $this->module->haveHttpHeader('Content-Type', 'application/json');
  182. $this->module->sendGET('/', ['name' => 'john']);
  183. /** @var $request \Symfony\Component\BrowserKit\Request **/
  184. $request = $this->module->client->getRequest();
  185. $this->assertNull($request->getContent());
  186. $this->assertContains('john', $request->getParameters());
  187. }
  188. public function testUrlIsFull()
  189. {
  190. $this->module->sendGET('/api/v1/users');
  191. /** @var $request \Symfony\Component\BrowserKit\Request **/
  192. $request = $this->module->client->getRequest();
  193. $this->assertEquals('http://localhost/api/v1/users', $request->getUri());
  194. }
  195. public function testSeeHeaders()
  196. {
  197. $response = new \Symfony\Component\BrowserKit\Response("", 200, [
  198. 'Cache-Control' => ['no-cache', 'no-store'],
  199. 'Content_Language' => 'en-US'
  200. ]);
  201. $this->module->client->mockResponse($response);
  202. $this->module->sendGET('/');
  203. $this->module->seeHttpHeader('Cache-Control');
  204. $this->module->seeHttpHeader('content_language', 'en-US');
  205. $this->module->seeHttpHeader('Content-Language', 'en-US');
  206. $this->module->dontSeeHttpHeader('Content-Language', 'en-RU');
  207. $this->module->dontSeeHttpHeader('Content-Language1');
  208. $this->module->seeHttpHeaderOnce('Content-Language');
  209. $this->assertEquals('en-US', $this->module->grabHttpHeader('Content-Language'));
  210. $this->assertEquals('no-cache', $this->module->grabHttpHeader('Cache-Control'));
  211. $this->assertEquals(['no-cache', 'no-store'], $this->module->grabHttpHeader('Cache-Control', false));
  212. }
  213. public function testSeeHeadersOnce()
  214. {
  215. $this->shouldFail();
  216. $response = new \Symfony\Component\BrowserKit\Response("", 200, [
  217. 'Cache-Control' => ['no-cache', 'no-store'],
  218. ]);
  219. $this->module->client->mockResponse($response);
  220. $this->module->sendGET('/');
  221. $this->module->seeHttpHeaderOnce('Cache-Control');
  222. }
  223. public function testSeeResponseJsonMatchesXpath()
  224. {
  225. $this->setStubResponse(
  226. '[{"user":"Blacknoir","age":27,"tags":["wed-dev","php"]},'
  227. . '{"user":"John Doe","age":27,"tags":["web-dev","java"]}]'
  228. );
  229. $this->module->seeResponseIsJson();
  230. $this->module->seeResponseJsonMatchesXpath('//user');
  231. }
  232. public function testSeeResponseJsonMatchesJsonPath()
  233. {
  234. $this->setStubResponse(
  235. '[{"user":"Blacknoir","age":27,"tags":["wed-dev","php"]},'
  236. . '{"user":"John Doe","age":27,"tags":["web-dev","java"]}]'
  237. );
  238. $this->module->seeResponseJsonMatchesJsonPath('$[*].user');
  239. $this->module->seeResponseJsonMatchesJsonPath('$[1].tags');
  240. }
  241. public function testDontSeeResponseJsonMatchesJsonPath()
  242. {
  243. $this->setStubResponse(
  244. '[{"user":"Blacknoir","age":27,"tags":["wed-dev","php"]},'
  245. . '{"user":"John Doe","age":27,"tags":["web-dev","java"]}]'
  246. );
  247. $this->module->dontSeeResponseJsonMatchesJsonPath('$[*].profile');
  248. }
  249. public function testDontSeeResponseJsonMatchesXpath()
  250. {
  251. $this->setStubResponse(
  252. '[{"user":"Blacknoir","age":27,"tags":["wed-dev","php"]},'
  253. . '{"user":"John Doe","age":27,"tags":["web-dev","java"]}]'
  254. );
  255. $this->module->dontSeeResponseJsonMatchesXpath('//status');
  256. }
  257. public function testDontSeeResponseJsonMatchesXpathFails()
  258. {
  259. $this->shouldFail();
  260. $this->setStubResponse(
  261. '[{"user":"Blacknoir","age":27,"tags":["wed-dev","php"]},'
  262. . '{"user":"John Doe","age":27,"tags":["web-dev","java"]}]'
  263. );
  264. $this->module->dontSeeResponseJsonMatchesXpath('//user');
  265. }
  266. /**
  267. * @Issue https://github.com/Codeception/Codeception/issues/2775
  268. */
  269. public function testSeeResponseJsonMatchesXPathWorksWithAmpersand()
  270. {
  271. $this->setStubResponse('{ "product":[ { "category":[ { "comment":"something & something" } ] } ] }');
  272. $this->module->seeResponseIsJson();
  273. $this->module->seeResponseJsonMatchesXpath('//comment');
  274. }
  275. public function testSeeResponseJsonMatchesJsonPathFails()
  276. {
  277. $this->shouldFail();
  278. $this->setStubResponse(
  279. '[{"user":"Blacknoir","age":27,"tags":["wed-dev","php"]},'
  280. . '{"user":"John Doe","age":27,"tags":["web-dev","java"]}]'
  281. );
  282. $this->module->seeResponseIsJson();
  283. $this->module->seeResponseJsonMatchesJsonPath('$[*].profile');
  284. }
  285. public function testStructuredJsonPathAndXPath()
  286. {
  287. $this->setStubResponse(
  288. '{ "store": {"book": [{ "category": "reference", "author": "Nigel Rees", '
  289. . '"title": "Sayings of the Century", "price": 8.95 }, { "category": "fiction", "author": "Evelyn Waugh", '
  290. . '"title": "Sword of Honour", "price": 12.99 }, { "category": "fiction", "author": "Herman Melville", '
  291. . '"title": "Moby Dick", "isbn": "0-553-21311-3", "price": 8.99 }, { "category": "fiction", '
  292. . '"author": "J. R. R. Tolkien", "title": "The Lord of the Rings", "isbn": "0-395-19395-8", '
  293. . '"price": 22.99 } ], "bicycle": {"color": "red", "price": 19.95 } } }'
  294. );
  295. $this->module->seeResponseIsJson();
  296. $this->module->seeResponseJsonMatchesXpath('//book/category');
  297. $this->module->seeResponseJsonMatchesJsonPath('$..book');
  298. $this->module->seeResponseJsonMatchesJsonPath('$.store.book[2].author');
  299. $this->module->dontSeeResponseJsonMatchesJsonPath('$.invalid');
  300. $this->module->dontSeeResponseJsonMatchesJsonPath('$.store.book.*.invalidField');
  301. }
  302. public function testApplicationJsonSubtypeIncludesObjectSerialized()
  303. {
  304. $this->module->haveHttpHeader('Content-Type', 'application/resource+json');
  305. $this->module->sendPOST('/', new JsonSerializedItem());
  306. /** @var $request \Symfony\Component\BrowserKit\Request **/
  307. $request = $this->module->client->getRequest();
  308. $this->assertContains('application/resource+json', $request->getServer());
  309. $this->assertJson($request->getContent());
  310. }
  311. public function testJsonTypeMatches()
  312. {
  313. $this->setStubResponse('{"xxx": "yyy", "user_id": 1}');
  314. $this->module->seeResponseMatchesJsonType(['xxx' => 'string', 'user_id' => 'integer:<10']);
  315. $this->module->dontSeeResponseMatchesJsonType(['xxx' => 'integer', 'user_id' => 'integer:<10']);
  316. }
  317. public function testJsonTypeMatchesWithJsonPath()
  318. {
  319. $this->setStubResponse('{"users": [{ "name": "davert"}, {"id": 1}]}');
  320. $this->module->seeResponseMatchesJsonType(['name' => 'string'], '$.users[0]');
  321. $this->module->seeResponseMatchesJsonType(['id' => 'integer'], '$.users[1]');
  322. $this->module->dontSeeResponseMatchesJsonType(['id' => 'integer'], '$.users[0]');
  323. }
  324. public function testMatchJsonTypeFailsWithNiceMessage()
  325. {
  326. $this->setStubResponse('{"xxx": "yyy", "user_id": 1}');
  327. try {
  328. $this->module->seeResponseMatchesJsonType(['zzz' => 'string']);
  329. $this->fail('it had to throw exception');
  330. } catch (PHPUnit\Framework\AssertionFailedError $e) {
  331. $this->assertEquals('Key `zzz` doesn\'t exist in {"xxx":"yyy","user_id":1}', $e->getMessage());
  332. }
  333. }
  334. public function testDontMatchJsonTypeFailsWithNiceMessage()
  335. {
  336. $this->setStubResponse('{"xxx": "yyy", "user_id": 1}');
  337. try {
  338. $this->module->dontSeeResponseMatchesJsonType(['xxx' => 'string']);
  339. $this->fail('it had to throw exception');
  340. } catch (PHPUnit\Framework\AssertionFailedError $e) {
  341. $this->assertEquals('Unexpectedly response matched: {"xxx":"yyy","user_id":1}', $e->getMessage());
  342. }
  343. }
  344. public function testSeeResponseIsJsonFailsWhenResponseIsEmpty()
  345. {
  346. $this->shouldFail();
  347. $this->setStubResponse('');
  348. $this->module->seeResponseIsJson();
  349. }
  350. public function testSeeResponseIsJsonFailsWhenResponseIsInvalidJson()
  351. {
  352. $this->shouldFail();
  353. $this->setStubResponse('{');
  354. $this->module->seeResponseIsJson();
  355. }
  356. public function testSeeResponseJsonMatchesXpathCanHandleResponseWithOneElement()
  357. {
  358. $this->setStubResponse('{"success": 1}');
  359. $this->module->seeResponseJsonMatchesXpath('//success');
  360. }
  361. public function testSeeResponseJsonMatchesXpathCanHandleResponseWithTwoElements()
  362. {
  363. $this->setStubResponse('{"success": 1, "info": "test"}');
  364. $this->module->seeResponseJsonMatchesXpath('//success');
  365. }
  366. public function testSeeResponseJsonMatchesXpathCanHandleResponseWithOneSubArray()
  367. {
  368. $this->setStubResponse('{"array": {"success": 1}}');
  369. $this->module->seeResponseJsonMatchesXpath('//array/success');
  370. }
  371. public function testSeeBinaryResponseEquals()
  372. {
  373. $data = base64_decode('/9j/2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgGBgUGCQgKCgkICQkKDA8MCgsOCwkJDRENDg8QEBEQCgwSExIQEw8QEBD/yQALCAABAAEBAREA/8wABgAQEAX/2gAIAQEAAD8A0s8g/9k=');
  374. $this->setStubResponse($data);
  375. $this->module->seeBinaryResponseEquals(md5($data));
  376. }
  377. public function testDontSeeBinaryResponseEquals()
  378. {
  379. $data = base64_decode('/9j/2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgGBgUGCQgKCgkICQkKDA8MCgsOCwkJDRENDg8QEBEQCgwSExIQEw8QEBD/yQALCAABAAEBAREA/8wABgAQEAX/2gAIAQEAAD8A0s8g/9k=');
  380. $this->setStubResponse($data);
  381. $this->module->dontSeeBinaryResponseEquals('024f615102cdb3c8c7cf75cdc5a83d15');
  382. }
  383. public function testAmDigestAuthenticatedThrowsExceptionWithFunctionalModules()
  384. {
  385. $this->setExpectedException('\Codeception\Exception\ModuleException', 'Not supported by functional modules');
  386. $this->module->amDigestAuthenticated('username', 'password');
  387. }
  388. protected function shouldFail()
  389. {
  390. $this->setExpectedException('PHPUnit\Framework\AssertionFailedError');
  391. }
  392. }
  393. class JsonSerializedItem implements JsonSerializable
  394. {
  395. public function jsonSerialize()
  396. {
  397. return array("hello" => "world");
  398. }
  399. }