ZttpTest.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515
  1. <?php
  2. use Zttp\Zttp;
  3. use Zttp\ZttpResponse;
  4. use PHPUnit\Framework\TestCase;
  5. class ZttpTest extends TestCase
  6. {
  7. public static function setUpBeforeClass()
  8. {
  9. ZttpServer::start();
  10. }
  11. function url($url)
  12. {
  13. return vsprintf('%s/%s', [
  14. 'http://localhost:' . getenv('TEST_SERVER_PORT'),
  15. ltrim($url, '/'),
  16. ]);
  17. }
  18. /** @test */
  19. function query_parameters_can_be_passed_as_an_array()
  20. {
  21. $response = Zttp::get($this->url('/get'), [
  22. 'foo' => 'bar',
  23. 'baz' => 'qux',
  24. ]);
  25. $this->assertArraySubset([
  26. 'query' => [
  27. 'foo' => 'bar',
  28. 'baz' => 'qux',
  29. ]
  30. ], $response->json());
  31. }
  32. /** @test */
  33. function query_parameters_in_urls_are_respected()
  34. {
  35. $response = Zttp::get($this->url('/get?foo=bar&baz=qux'));
  36. $this->assertArraySubset([
  37. 'query' => [
  38. 'foo' => 'bar',
  39. 'baz' => 'qux',
  40. ]
  41. ], $response->json());
  42. }
  43. /** @test */
  44. function query_parameters_in_urls_can_be_combined_with_array_parameters()
  45. {
  46. $response = Zttp::get($this->url('/get?foo=bar'), [
  47. 'baz' => 'qux'
  48. ]);
  49. $this->assertArraySubset([
  50. 'query' => [
  51. 'foo' => 'bar',
  52. 'baz' => 'qux',
  53. ]
  54. ], $response->json());
  55. }
  56. /** @test */
  57. function options_can_be_set_all_at_once()
  58. {
  59. $response = Zttp::withOptions([
  60. 'headers' => [
  61. 'accept' => ['text/xml'],
  62. ]
  63. ])->get($this->url('/get'));
  64. $this->assertArraySubset([
  65. 'headers' => [
  66. 'accept' => ['text/xml'],
  67. ]
  68. ], $response->json());
  69. }
  70. /** @test */
  71. function post_content_is_json_by_default()
  72. {
  73. $response = Zttp::post($this->url('/post'), [
  74. 'foo' => 'bar',
  75. 'baz' => 'qux',
  76. ]);
  77. $this->assertArraySubset([
  78. 'headers' => [
  79. 'content-type' => ['application/json'],
  80. ],
  81. 'json' => [
  82. 'foo' => 'bar',
  83. 'baz' => 'qux',
  84. ]
  85. ], $response->json());
  86. }
  87. /** @test */
  88. function post_content_can_be_sent_as_form_params()
  89. {
  90. $response = Zttp::asFormParams()->post($this->url('/post'), [
  91. 'foo' => 'bar',
  92. 'baz' => 'qux',
  93. ]);
  94. $this->assertArraySubset([
  95. 'headers' => [
  96. 'content-type' => ['application/x-www-form-urlencoded'],
  97. ],
  98. 'form_params' => [
  99. 'foo' => 'bar',
  100. 'baz' => 'qux',
  101. ]
  102. ], $response->json());
  103. }
  104. /** @test */
  105. function post_content_can_be_sent_as_multipart()
  106. {
  107. $response = Zttp::asMultipart()->post($this->url('/multi-part'), [
  108. [
  109. 'name' => 'foo',
  110. 'contents' => 'bar'
  111. ],
  112. [
  113. 'name' => 'baz',
  114. 'contents' => 'qux',
  115. ],
  116. [
  117. 'name' => 'test-file',
  118. 'contents' => 'test contents',
  119. 'filename' => 'test-file.txt',
  120. ],
  121. ])->json();
  122. $this->assertEquals(['foo' => 'bar', 'baz' => 'qux'], $response['body_content']);
  123. $this->assertTrue($response['has_file']);
  124. $this->assertEquals($response['file_content'], 'test contents');
  125. $this->assertStringStartsWith('multipart', $response['headers']['content-type'][0]);
  126. }
  127. /** @test */
  128. function post_content_can_be_sent_as_json_explicitly()
  129. {
  130. $response = Zttp::asJson()->post($this->url('/post'), [
  131. 'foo' => 'bar',
  132. 'baz' => 'qux',
  133. ]);
  134. $this->assertArraySubset([
  135. 'headers' => [
  136. 'content-type' => ['application/json'],
  137. ],
  138. 'json' => [
  139. 'foo' => 'bar',
  140. 'baz' => 'qux',
  141. ]
  142. ], $response->json());
  143. }
  144. /** @test */
  145. function get_with_additional_headers()
  146. {
  147. $response = Zttp::withHeaders(['Custom' => 'Header'])->get($this->url('/get'));
  148. $this->assertArraySubset([
  149. 'headers' => [
  150. 'custom' => ['Header'],
  151. ],
  152. ], $response->json());
  153. }
  154. /** @test */
  155. function post_with_additional_headers()
  156. {
  157. $response = Zttp::withHeaders(['Custom' => 'Header'])->post($this->url('/post'));
  158. $this->assertArraySubset([
  159. 'headers' => [
  160. 'custom' => ['Header'],
  161. ],
  162. ], $response->json());
  163. }
  164. /** @test */
  165. function the_accept_header_can_be_set_via_shortcut()
  166. {
  167. $response = Zttp::accept('banana/sandwich')->post($this->url('/post'));
  168. $this->assertArraySubset([
  169. 'headers' => [
  170. 'accept' => ['banana/sandwich'],
  171. ],
  172. ], $response->json());
  173. }
  174. /** @test */
  175. function exceptions_are_not_thrown_for_40x_responses()
  176. {
  177. $response = Zttp::withHeaders(['Z-Status' => 418])->get($this->url('/get'));
  178. $this->assertEquals(418, $response->status());
  179. }
  180. /** @test */
  181. function exceptions_are_not_thrown_for_50x_responses()
  182. {
  183. $response = Zttp::withHeaders(['Z-Status' => 508])->get($this->url('/get'));
  184. $this->assertEquals(508, $response->status());
  185. }
  186. /** @test */
  187. function redirects_are_followed_by_default()
  188. {
  189. $response = Zttp::get($this->url('/redirect'));
  190. $this->assertEquals(200, $response->status());
  191. $this->assertEquals('Redirected!', $response->body());
  192. }
  193. /** @test */
  194. function redirects_can_be_disabled()
  195. {
  196. $response = Zttp::withoutRedirecting()->get($this->url('/redirect'));
  197. $this->assertEquals(302, $response->status());
  198. $this->assertEquals($this->url('/redirected'), $response->header('Location'));
  199. }
  200. /** @test */
  201. function patch_requests_are_supported()
  202. {
  203. $response = Zttp::patch($this->url('/patch'), [
  204. 'foo' => 'bar',
  205. 'baz' => 'qux',
  206. ]);
  207. $this->assertArraySubset([
  208. 'json' => [
  209. 'foo' => 'bar',
  210. 'baz' => 'qux',
  211. ]
  212. ], $response->json());
  213. }
  214. /** @test */
  215. function put_requests_are_supported()
  216. {
  217. $response = Zttp::put($this->url('/put'), [
  218. 'foo' => 'bar',
  219. 'baz' => 'qux',
  220. ]);
  221. $this->assertArraySubset([
  222. 'json' => [
  223. 'foo' => 'bar',
  224. 'baz' => 'qux',
  225. ]
  226. ], $response->json());
  227. }
  228. /** @test */
  229. function delete_requests_are_supported()
  230. {
  231. $response = Zttp::delete($this->url('/delete'), [
  232. 'foo' => 'bar',
  233. 'baz' => 'qux',
  234. ]);
  235. $this->assertArraySubset([
  236. 'json' => [
  237. 'foo' => 'bar',
  238. 'baz' => 'qux',
  239. ]
  240. ], $response->json());
  241. }
  242. /** @test */
  243. function query_parameters_are_respected_in_post_requests()
  244. {
  245. $response = Zttp::post($this->url('/post?banana=sandwich'), [
  246. 'foo' => 'bar',
  247. 'baz' => 'qux',
  248. ]);
  249. $this->assertArraySubset([
  250. 'query' => [
  251. 'banana' => 'sandwich',
  252. ],
  253. 'json' => [
  254. 'foo' => 'bar',
  255. 'baz' => 'qux',
  256. ]
  257. ], $response->json());
  258. }
  259. /** @test */
  260. function query_parameters_are_respected_in_put_requests()
  261. {
  262. $response = Zttp::put($this->url('/put?banana=sandwich'), [
  263. 'foo' => 'bar',
  264. 'baz' => 'qux',
  265. ]);
  266. $this->assertArraySubset([
  267. 'query' => [
  268. 'banana' => 'sandwich',
  269. ],
  270. 'json' => [
  271. 'foo' => 'bar',
  272. 'baz' => 'qux',
  273. ]
  274. ], $response->json());
  275. }
  276. /** @test */
  277. function query_parameters_are_respected_in_patch_requests()
  278. {
  279. $response = Zttp::patch($this->url('/patch?banana=sandwich'), [
  280. 'foo' => 'bar',
  281. 'baz' => 'qux',
  282. ]);
  283. $this->assertArraySubset([
  284. 'query' => [
  285. 'banana' => 'sandwich',
  286. ],
  287. 'json' => [
  288. 'foo' => 'bar',
  289. 'baz' => 'qux',
  290. ]
  291. ], $response->json());
  292. }
  293. /** @test */
  294. function query_parameters_are_respected_in_delete_requests()
  295. {
  296. $response = Zttp::delete($this->url('/delete?banana=sandwich'), [
  297. 'foo' => 'bar',
  298. 'baz' => 'qux',
  299. ]);
  300. $this->assertArraySubset([
  301. 'query' => [
  302. 'banana' => 'sandwich',
  303. ],
  304. 'json' => [
  305. 'foo' => 'bar',
  306. 'baz' => 'qux',
  307. ]
  308. ], $response->json());
  309. }
  310. /** @test */
  311. function can_retrieve_the_raw_response_body()
  312. {
  313. $response = Zttp::get($this->url('/simple-response'));
  314. $this->assertEquals("A simple string response", $response->body());
  315. }
  316. /** @test */
  317. function can_retrieve_response_header_values()
  318. {
  319. $response = Zttp::get($this->url('/get'));
  320. $this->assertEquals('application/json', $response->header('Content-Type'));
  321. $this->assertEquals('application/json', $response->headers()['Content-Type']);
  322. }
  323. /** @test */
  324. function can_check_if_a_response_is_success()
  325. {
  326. $response = Zttp::withHeaders(['Z-Status' => 200])->get($this->url('/get'));
  327. $this->assertTrue($response->isSuccess());
  328. $this->assertFalse($response->isRedirect());
  329. $this->assertFalse($response->isClientError());
  330. $this->assertFalse($response->isServerError());
  331. }
  332. /** @test */
  333. function can_check_if_a_response_is_redirect()
  334. {
  335. $response = Zttp::withHeaders(['Z-Status' => 302])->get($this->url('/get'));
  336. $this->assertTrue($response->isRedirect());
  337. $this->assertFalse($response->isSuccess());
  338. $this->assertFalse($response->isClientError());
  339. $this->assertFalse($response->isServerError());
  340. }
  341. /** @test */
  342. function can_check_if_a_response_is_client_error()
  343. {
  344. $response = Zttp::withHeaders(['Z-Status' => 404])->get($this->url('/get'));
  345. $this->assertTrue($response->isClientError());
  346. $this->assertFalse($response->isSuccess());
  347. $this->assertFalse($response->isRedirect());
  348. $this->assertFalse($response->isServerError());
  349. }
  350. /** @test */
  351. function can_check_if_a_response_is_server_error()
  352. {
  353. $response = Zttp::withHeaders(['Z-Status' => 508])->get($this->url('/get'));
  354. $this->assertTrue($response->isServerError());
  355. $this->assertFalse($response->isSuccess());
  356. $this->assertFalse($response->isRedirect());
  357. $this->assertFalse($response->isClientError());
  358. }
  359. /** @test */
  360. function is_ok_is_an_alias_for_is_success()
  361. {
  362. $response = Zttp::withHeaders(['Z-Status' => 200])->get($this->url('/get'));
  363. $this->assertTrue($response->isOk());
  364. $this->assertTrue($response->isSuccess());
  365. $this->assertFalse($response->isRedirect());
  366. $this->assertFalse($response->isClientError());
  367. $this->assertFalse($response->isServerError());
  368. }
  369. /** @test */
  370. function multiple_callbacks_can_be_run_before_sending_the_request()
  371. {
  372. $state = [];
  373. $response = Zttp::beforeSending(function ($request) use (&$state) {
  374. return tap($request, function ($request) use (&$state) {
  375. $state['url'] = $request->url();
  376. $state['method'] = $request->method();
  377. });
  378. })->beforeSending(function ($request) use (&$state) {
  379. return tap($request, function ($request) use (&$state) {
  380. $state['headers'] = $request->headers();
  381. $state['body'] = $request->body();
  382. });
  383. })->withHeaders(['Z-Status' => 200])->post($this->url('/post'), ['foo' => 'bar']);
  384. $this->assertEquals($this->url('/post'), $state['url']);
  385. $this->assertEquals('POST', $state['method']);
  386. $this->assertArrayHasKey('User-Agent', $state['headers']);
  387. $this->assertEquals(200, $state['headers']['Z-Status']);
  388. $this->assertEquals(json_encode(['foo' => 'bar']), $state['body']);
  389. }
  390. /** @test */
  391. function response_can_use_macros()
  392. {
  393. ZttpResponse::macro('testMacro', function () {
  394. return vsprintf('%s %s', [
  395. $this->json()['json']['foo'],
  396. $this->json()['json']['baz'],
  397. ]);
  398. });
  399. $response = Zttp::post($this->url('/post'), [
  400. 'foo' => 'bar',
  401. 'baz' => 'qux',
  402. ]);
  403. $this->assertEquals('bar qux', $response->testMacro());
  404. }
  405. /** @test */
  406. function can_use_basic_auth()
  407. {
  408. $response = Zttp::withBasicAuth('zttp', 'secret')->get($this->url('/basic-auth'));
  409. $this->assertTrue($response->isOk());
  410. }
  411. /** @test */
  412. function can_use_digest_auth()
  413. {
  414. $response = Zttp::withDigestAuth('zttp', 'secret')->get($this->url('/digest-auth'));
  415. $this->assertTrue($response->isOk());
  416. }
  417. /**
  418. * @test
  419. * @expectedException \Zttp\ConnectionException
  420. */
  421. function client_will_force_timeout()
  422. {
  423. Zttp::timeout(1)->get($this->url('/timeout'));
  424. }
  425. }
  426. class ZttpServer
  427. {
  428. static function start()
  429. {
  430. $pid = exec('php -S ' . 'localhost:' . getenv('TEST_SERVER_PORT') . ' -t ./tests/server/public > /dev/null 2>&1 & echo $!');
  431. while (@file_get_contents('http://localhost:' . getenv('TEST_SERVER_PORT') . '/get') === false) {
  432. usleep(1000);
  433. }
  434. register_shutdown_function(function () use ($pid) {
  435. exec('kill ' . $pid);
  436. });
  437. }
  438. }