ClientTest.php 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805
  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. * Redistributions of files must retain the above copyright notice.
  9. *
  10. * @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
  11. * @link https://cakephp.org CakePHP(tm) Project
  12. * @since 3.0.0
  13. * @license https://opensource.org/licenses/mit-license.php MIT License
  14. */
  15. namespace Cake\Test\TestCase\Http;
  16. use Cake\Http\Client;
  17. use Cake\Http\Client\Request;
  18. use Cake\Http\Client\Response;
  19. use Cake\Http\Cookie\Cookie;
  20. use Cake\Http\Cookie\CookieCollection;
  21. use Cake\TestSuite\TestCase;
  22. use InvalidArgumentException;
  23. /**
  24. * HTTP client test.
  25. */
  26. class ClientTest extends TestCase
  27. {
  28. /**
  29. * Test storing config options and modifying them.
  30. *
  31. * @return void
  32. */
  33. public function testConstructConfig()
  34. {
  35. $config = [
  36. 'scheme' => 'http',
  37. 'host' => 'example.org',
  38. ];
  39. $http = new Client($config);
  40. $result = $http->getConfig();
  41. foreach ($config as $key => $val) {
  42. $this->assertEquals($val, $result[$key]);
  43. }
  44. $result = $http->setConfig([
  45. 'auth' => ['username' => 'mark', 'password' => 'secret'],
  46. ]);
  47. $this->assertSame($result, $http);
  48. $result = $http->getConfig();
  49. $expected = [
  50. 'scheme' => 'http',
  51. 'host' => 'example.org',
  52. 'auth' => ['username' => 'mark', 'password' => 'secret'],
  53. ];
  54. foreach ($expected as $key => $val) {
  55. $this->assertEquals($val, $result[$key]);
  56. }
  57. }
  58. /**
  59. * testAdapterInstanceCheck
  60. *
  61. * @return void
  62. */
  63. public function testAdapterInstanceCheck()
  64. {
  65. $this->expectException(InvalidArgumentException::class);
  66. $this->expectExceptionMessage('Adapter must be an instance of Cake\Http\Client\AdapterInterface');
  67. new Client(['adapter' => 'stdClass']);
  68. }
  69. /**
  70. * Data provider for buildUrl() tests
  71. *
  72. * @return array
  73. */
  74. public static function urlProvider()
  75. {
  76. return [
  77. [
  78. 'http://example.com/test.html',
  79. 'http://example.com/test.html',
  80. [],
  81. null,
  82. 'Null options',
  83. ],
  84. [
  85. 'http://example.com/test.html',
  86. 'http://example.com/test.html',
  87. [],
  88. [],
  89. 'Simple string',
  90. ],
  91. [
  92. 'http://example.com/test.html',
  93. '/test.html',
  94. [],
  95. ['host' => 'example.com'],
  96. 'host name option',
  97. ],
  98. [
  99. 'https://example.com/test.html',
  100. '/test.html',
  101. [],
  102. ['host' => 'example.com', 'scheme' => 'https'],
  103. 'HTTPS',
  104. ],
  105. [
  106. 'http://example.com:8080/test.html',
  107. '/test.html',
  108. [],
  109. ['host' => 'example.com', 'port' => '8080'],
  110. 'Non standard port',
  111. ],
  112. [
  113. 'http://example.com/test.html',
  114. '/test.html',
  115. [],
  116. ['host' => 'example.com', 'port' => '80'],
  117. 'standard port, does not display',
  118. ],
  119. [
  120. 'https://example.com/test.html',
  121. '/test.html',
  122. [],
  123. ['host' => 'example.com', 'scheme' => 'https', 'port' => '443'],
  124. 'standard port, does not display',
  125. ],
  126. [
  127. 'http://example.com/test.html',
  128. 'http://example.com/test.html',
  129. [],
  130. ['host' => 'example.com', 'scheme' => 'https'],
  131. 'options do not duplicate',
  132. ],
  133. [
  134. 'http://example.com/search?q=hi+there&cat%5Bid%5D%5B0%5D=2&cat%5Bid%5D%5B1%5D=3',
  135. 'http://example.com/search',
  136. ['q' => 'hi there', 'cat' => ['id' => [2, 3]]],
  137. [],
  138. 'query string data.',
  139. ],
  140. [
  141. 'http://example.com/search?q=hi+there&id=12',
  142. 'http://example.com/search?q=hi+there',
  143. ['id' => '12'],
  144. [],
  145. 'query string data with some already on the url.',
  146. ],
  147. [
  148. 'http://example.com/test.html',
  149. '//test.html',
  150. [],
  151. [
  152. 'scheme' => 'http',
  153. 'host' => 'example.com',
  154. 'protocolRelative' => false,
  155. ],
  156. 'url with a double slash',
  157. ],
  158. [
  159. 'http://example.com/test.html',
  160. '//example.com/test.html',
  161. [],
  162. [
  163. 'scheme' => 'http',
  164. 'protocolRelative' => true,
  165. ],
  166. 'protocol relative url',
  167. ],
  168. ];
  169. }
  170. /**
  171. * @dataProvider urlProvider
  172. */
  173. public function testBuildUrl($expected, $url, $query, $opts)
  174. {
  175. $http = new Client();
  176. $result = $http->buildUrl($url, $query, (array)$opts);
  177. $this->assertEquals($expected, $result);
  178. }
  179. /**
  180. * test simple get request with headers & cookies.
  181. *
  182. * @return void
  183. */
  184. public function testGetSimpleWithHeadersAndCookies()
  185. {
  186. $response = new Response();
  187. $headers = [
  188. 'User-Agent' => 'Cake',
  189. 'Connection' => 'close',
  190. 'Content-Type' => 'application/x-www-form-urlencoded',
  191. ];
  192. $cookies = [
  193. 'split' => 'value',
  194. ];
  195. $mock = $this->getMockBuilder('Cake\Http\Client\Adapter\Stream')
  196. ->setMethods(['send'])
  197. ->getMock();
  198. $mock->expects($this->once())
  199. ->method('send')
  200. ->with($this->callback(function ($request) use ($cookies, $headers) {
  201. $this->assertInstanceOf('Cake\Http\Client\Request', $request);
  202. $this->assertEquals(Request::METHOD_GET, $request->getMethod());
  203. $this->assertEquals('http://cakephp.org/test.html', $request->getUri() . '');
  204. $this->assertEquals('split=value', $request->getHeaderLine('Cookie'));
  205. $this->assertEquals($headers['Content-Type'], $request->getHeaderLine('content-type'));
  206. $this->assertEquals($headers['Connection'], $request->getHeaderLine('connection'));
  207. return true;
  208. }))
  209. ->will($this->returnValue([$response]));
  210. $http = new Client(['adapter' => $mock]);
  211. $result = $http->get('http://cakephp.org/test.html', [], [
  212. 'headers' => $headers,
  213. 'cookies' => $cookies,
  214. ]);
  215. $this->assertSame($result, $response);
  216. }
  217. /**
  218. * test get request with no data
  219. *
  220. * @return void
  221. */
  222. public function testGetNoData()
  223. {
  224. $response = new Response();
  225. $mock = $this->getMockBuilder('Cake\Http\Client\Adapter\Stream')
  226. ->setMethods(['send'])
  227. ->getMock();
  228. $mock->expects($this->once())
  229. ->method('send')
  230. ->with($this->callback(function ($request) {
  231. $this->assertEquals(Request::METHOD_GET, $request->getMethod());
  232. $this->assertEmpty($request->getHeaderLine('Content-Type'), 'Should have no content-type set');
  233. $this->assertEquals(
  234. 'http://cakephp.org/search',
  235. $request->getUri() . ''
  236. );
  237. return true;
  238. }))
  239. ->will($this->returnValue([$response]));
  240. $http = new Client([
  241. 'host' => 'cakephp.org',
  242. 'adapter' => $mock,
  243. ]);
  244. $result = $http->get('/search');
  245. $this->assertSame($result, $response);
  246. }
  247. /**
  248. * test get request with querystring data
  249. *
  250. * @return void
  251. */
  252. public function testGetQuerystring()
  253. {
  254. $response = new Response();
  255. $mock = $this->getMockBuilder('Cake\Http\Client\Adapter\Stream')
  256. ->setMethods(['send'])
  257. ->getMock();
  258. $mock->expects($this->once())
  259. ->method('send')
  260. ->with($this->callback(function ($request) {
  261. $this->assertEquals(Request::METHOD_GET, $request->getMethod());
  262. $this->assertEquals(
  263. 'http://cakephp.org/search?q=hi+there&Category%5Bid%5D%5B0%5D=2&Category%5Bid%5D%5B1%5D=3',
  264. $request->getUri() . ''
  265. );
  266. return true;
  267. }))
  268. ->will($this->returnValue([$response]));
  269. $http = new Client([
  270. 'host' => 'cakephp.org',
  271. 'adapter' => $mock,
  272. ]);
  273. $result = $http->get('/search', [
  274. 'q' => 'hi there',
  275. 'Category' => ['id' => [2, 3]],
  276. ]);
  277. $this->assertSame($result, $response);
  278. }
  279. /**
  280. * test get request with string of query data.
  281. *
  282. * @return void
  283. */
  284. public function testGetQuerystringString()
  285. {
  286. $response = new Response();
  287. $mock = $this->getMockBuilder('Cake\Http\Client\Adapter\Stream')
  288. ->setMethods(['send'])
  289. ->getMock();
  290. $mock->expects($this->once())
  291. ->method('send')
  292. ->with($this->callback(function ($request) {
  293. $this->assertEquals(
  294. 'http://cakephp.org/search?q=hi+there&Category%5Bid%5D%5B0%5D=2&Category%5Bid%5D%5B1%5D=3',
  295. $request->getUri() . ''
  296. );
  297. return true;
  298. }))
  299. ->will($this->returnValue([$response]));
  300. $http = new Client([
  301. 'host' => 'cakephp.org',
  302. 'adapter' => $mock,
  303. ]);
  304. $data = [
  305. 'q' => 'hi there',
  306. 'Category' => ['id' => [2, 3]],
  307. ];
  308. $result = $http->get('/search', http_build_query($data));
  309. $this->assertSame($response, $result);
  310. }
  311. /**
  312. * Test a GET with a request body. Services like
  313. * elasticsearch use this feature.
  314. *
  315. * @return void
  316. */
  317. public function testGetWithContent()
  318. {
  319. $response = new Response();
  320. $mock = $this->getMockBuilder('Cake\Http\Client\Adapter\Stream')
  321. ->setMethods(['send'])
  322. ->getMock();
  323. $mock->expects($this->once())
  324. ->method('send')
  325. ->with($this->callback(function ($request) {
  326. $this->assertEquals(Request::METHOD_GET, $request->getMethod());
  327. $this->assertEquals('http://cakephp.org/search', '' . $request->getUri());
  328. $this->assertEquals('some data', '' . $request->getBody());
  329. return true;
  330. }))
  331. ->will($this->returnValue([$response]));
  332. $http = new Client([
  333. 'host' => 'cakephp.org',
  334. 'adapter' => $mock,
  335. ]);
  336. $result = $http->get('/search', [
  337. '_content' => 'some data',
  338. ]);
  339. $this->assertSame($result, $response);
  340. }
  341. /**
  342. * Test invalid authentication types throw exceptions.
  343. *
  344. * @return void
  345. */
  346. public function testInvalidAuthenticationType()
  347. {
  348. $this->expectException(\Cake\Core\Exception\Exception::class);
  349. $mock = $this->getMockBuilder('Cake\Http\Client\Adapter\Stream')
  350. ->setMethods(['send'])
  351. ->getMock();
  352. $mock->expects($this->never())
  353. ->method('send');
  354. $http = new Client([
  355. 'host' => 'cakephp.org',
  356. 'adapter' => $mock,
  357. ]);
  358. $result = $http->get('/', [], [
  359. 'auth' => ['type' => 'horribly broken'],
  360. ]);
  361. }
  362. /**
  363. * Test setting basic authentication with get
  364. *
  365. * @return void
  366. */
  367. public function testGetWithAuthenticationAndProxy()
  368. {
  369. $response = new Response();
  370. $mock = $this->getMockBuilder('Cake\Http\Client\Adapter\Stream')
  371. ->setMethods(['send'])
  372. ->getMock();
  373. $headers = [
  374. 'Authorization' => 'Basic ' . base64_encode('mark:secret'),
  375. 'Proxy-Authorization' => 'Basic ' . base64_encode('mark:pass'),
  376. ];
  377. $mock->expects($this->once())
  378. ->method('send')
  379. ->with($this->callback(function ($request) use ($headers) {
  380. $this->assertEquals(Request::METHOD_GET, $request->getMethod());
  381. $this->assertEquals('http://cakephp.org/', '' . $request->getUri());
  382. $this->assertEquals($headers['Authorization'], $request->getHeaderLine('Authorization'));
  383. $this->assertEquals($headers['Proxy-Authorization'], $request->getHeaderLine('Proxy-Authorization'));
  384. return true;
  385. }))
  386. ->will($this->returnValue([$response]));
  387. $http = new Client([
  388. 'host' => 'cakephp.org',
  389. 'adapter' => $mock,
  390. ]);
  391. $result = $http->get('/', [], [
  392. 'auth' => ['username' => 'mark', 'password' => 'secret'],
  393. 'proxy' => ['username' => 'mark', 'password' => 'pass'],
  394. ]);
  395. $this->assertSame($result, $response);
  396. }
  397. /**
  398. * Return a list of HTTP methods.
  399. *
  400. * @return array
  401. */
  402. public static function methodProvider()
  403. {
  404. return [
  405. [Request::METHOD_GET],
  406. [Request::METHOD_POST],
  407. [Request::METHOD_PUT],
  408. [Request::METHOD_DELETE],
  409. [Request::METHOD_PATCH],
  410. [Request::METHOD_OPTIONS],
  411. [Request::METHOD_TRACE],
  412. ];
  413. }
  414. /**
  415. * test simple POST request.
  416. *
  417. * @dataProvider methodProvider
  418. * @return void
  419. */
  420. public function testMethodsSimple($method)
  421. {
  422. $response = new Response();
  423. $mock = $this->getMockBuilder('Cake\Http\Client\Adapter\Stream')
  424. ->setMethods(['send'])
  425. ->getMock();
  426. $mock->expects($this->once())
  427. ->method('send')
  428. ->with($this->callback(function ($request) use ($method) {
  429. $this->assertInstanceOf('Cake\Http\Client\Request', $request);
  430. $this->assertEquals($method, $request->getMethod());
  431. $this->assertEquals('http://cakephp.org/projects/add', '' . $request->getUri());
  432. return true;
  433. }))
  434. ->will($this->returnValue([$response]));
  435. $http = new Client([
  436. 'host' => 'cakephp.org',
  437. 'adapter' => $mock,
  438. ]);
  439. $result = $http->{$method}('/projects/add');
  440. $this->assertSame($result, $response);
  441. }
  442. /**
  443. * Provider for testing the type option.
  444. *
  445. * @return array
  446. */
  447. public static function typeProvider()
  448. {
  449. return [
  450. ['application/json', 'application/json'],
  451. ['json', 'application/json'],
  452. ['xml', 'application/xml'],
  453. ['application/xml', 'application/xml'],
  454. ];
  455. }
  456. /**
  457. * Test that using the 'type' option sets the correct headers
  458. *
  459. * @dataProvider typeProvider
  460. * @return void
  461. */
  462. public function testPostWithTypeKey($type, $mime)
  463. {
  464. $response = new Response();
  465. $data = 'some data';
  466. $headers = [
  467. 'Content-Type' => $mime,
  468. 'Accept' => $mime,
  469. ];
  470. $mock = $this->getMockBuilder('Cake\Http\Client\Adapter\Stream')
  471. ->setMethods(['send'])
  472. ->getMock();
  473. $mock->expects($this->once())
  474. ->method('send')
  475. ->with($this->callback(function ($request) use ($headers) {
  476. $this->assertEquals(Request::METHOD_POST, $request->getMethod());
  477. $this->assertEquals($headers['Content-Type'], $request->getHeaderLine('Content-Type'));
  478. $this->assertEquals($headers['Accept'], $request->getHeaderLine('Accept'));
  479. return true;
  480. }))
  481. ->will($this->returnValue([$response]));
  482. $http = new Client([
  483. 'host' => 'cakephp.org',
  484. 'adapter' => $mock,
  485. ]);
  486. $http->post('/projects/add', $data, ['type' => $type]);
  487. }
  488. /**
  489. * Test that string payloads with no content type have a default content-type set.
  490. *
  491. * @return void
  492. */
  493. public function testPostWithStringDataDefaultsToFormEncoding()
  494. {
  495. $response = new Response();
  496. $data = 'some=value&more=data';
  497. $mock = $this->getMockBuilder('Cake\Http\Client\Adapter\Stream')
  498. ->setMethods(['send'])
  499. ->getMock();
  500. $mock->expects($this->any())
  501. ->method('send')
  502. ->with($this->callback(function ($request) use ($data) {
  503. $this->assertEquals($data, '' . $request->getBody());
  504. $this->assertEquals('application/x-www-form-urlencoded', $request->getHeaderLine('content-type'));
  505. return true;
  506. }))
  507. ->will($this->returnValue([$response]));
  508. $http = new Client([
  509. 'host' => 'cakephp.org',
  510. 'adapter' => $mock,
  511. ]);
  512. $http->post('/projects/add', $data);
  513. $http->put('/projects/add', $data);
  514. $http->delete('/projects/add', $data);
  515. }
  516. /**
  517. * Test that exceptions are raised on invalid types.
  518. *
  519. * @return void
  520. */
  521. public function testExceptionOnUnknownType()
  522. {
  523. $this->expectException(\Cake\Core\Exception\Exception::class);
  524. $mock = $this->getMockBuilder('Cake\Http\Client\Adapter\Stream')
  525. ->setMethods(['send'])
  526. ->getMock();
  527. $mock->expects($this->never())
  528. ->method('send');
  529. $http = new Client([
  530. 'host' => 'cakephp.org',
  531. 'adapter' => $mock,
  532. ]);
  533. $http->post('/projects/add', 'it works', ['type' => 'invalid']);
  534. }
  535. /**
  536. * Test that Client stores cookies
  537. *
  538. * @return void
  539. */
  540. public function testCookieStorage()
  541. {
  542. $adapter = $this->getMockBuilder('Cake\Http\Client\Adapter\Stream')
  543. ->setMethods(['send'])
  544. ->getMock();
  545. $headers = [
  546. 'HTTP/1.0 200 Ok',
  547. 'Set-Cookie: first=1',
  548. 'Set-Cookie: expiring=now; Expires=Wed, 09-Jun-1999 10:18:14 GMT',
  549. ];
  550. $response = new Response($headers, '');
  551. $adapter->expects($this->at(0))
  552. ->method('send')
  553. ->will($this->returnValue([$response]));
  554. $http = new Client([
  555. 'host' => 'cakephp.org',
  556. 'adapter' => $adapter,
  557. ]);
  558. $http->get('/projects');
  559. $cookies = $http->cookies();
  560. $this->assertCount(1, $cookies);
  561. $this->assertTrue($cookies->has('first'));
  562. $this->assertFalse($cookies->has('expiring'));
  563. }
  564. /**
  565. * Test cookieJar config option.
  566. *
  567. * @return void
  568. */
  569. public function testCookieJar()
  570. {
  571. $jar = new CookieCollection();
  572. $http = new Client([
  573. 'cookieJar' => $jar,
  574. ]);
  575. $this->assertSame($jar, $http->cookies());
  576. }
  577. /**
  578. * Test addCookie() method.
  579. *
  580. * @return void
  581. */
  582. public function testAddCookie()
  583. {
  584. $client = new Client();
  585. $cookie = new Cookie('foo', '', null, '/', 'example.com');
  586. $this->assertFalse($client->cookies()->has('foo'));
  587. $client->addCookie($cookie);
  588. $this->assertTrue($client->cookies()->has('foo'));
  589. }
  590. /**
  591. * Test addCookie() method without a domain.
  592. *
  593. * @return void
  594. */
  595. public function testAddCookieWithoutDomain()
  596. {
  597. $this->expectException(\InvalidArgumentException::class);
  598. $this->expectExceptionMessage('Cookie must have a domain and a path set.');
  599. $client = new Client();
  600. $cookie = new Cookie('foo', '', null, '/', '');
  601. $this->assertFalse($client->cookies()->has('foo'));
  602. $client->addCookie($cookie);
  603. $this->assertTrue($client->cookies()->has('foo'));
  604. }
  605. /**
  606. * Test addCookie() method without a path.
  607. *
  608. * @return void
  609. */
  610. public function testAddCookieWithoutPath()
  611. {
  612. $this->expectException(\InvalidArgumentException::class);
  613. $this->expectExceptionMessage('Cookie must have a domain and a path set.');
  614. $client = new Client();
  615. $cookie = new Cookie('foo', '', null, '', 'example.com');
  616. $this->assertFalse($client->cookies()->has('foo'));
  617. $client->addCookie($cookie);
  618. $this->assertTrue($client->cookies()->has('foo'));
  619. }
  620. /**
  621. * test head request with querystring data
  622. *
  623. * @return void
  624. */
  625. public function testHeadQuerystring()
  626. {
  627. $response = new Response();
  628. $mock = $this->getMockBuilder('Cake\Http\Client\Adapter\Stream')
  629. ->setMethods(['send'])
  630. ->getMock();
  631. $mock->expects($this->once())
  632. ->method('send')
  633. ->with($this->callback(function ($request) {
  634. $this->assertInstanceOf('Cake\Http\Client\Request', $request);
  635. $this->assertEquals(Request::METHOD_HEAD, $request->getMethod());
  636. $this->assertEquals('http://cakephp.org/search?q=hi+there', '' . $request->getUri());
  637. return true;
  638. }))
  639. ->will($this->returnValue([$response]));
  640. $http = new Client([
  641. 'host' => 'cakephp.org',
  642. 'adapter' => $mock,
  643. ]);
  644. $result = $http->head('/search', [
  645. 'q' => 'hi there',
  646. ]);
  647. $this->assertSame($result, $response);
  648. }
  649. /**
  650. * test redirects
  651. *
  652. * @return void
  653. */
  654. public function testRedirects()
  655. {
  656. $url = 'http://cakephp.org';
  657. $adapter = $this->getMockBuilder(Client\Adapter\Stream::class)
  658. ->setMethods(['send'])
  659. ->getMock();
  660. $redirect = new Response([
  661. 'HTTP/1.0 301',
  662. 'Location: http://cakephp.org/redirect1?foo=bar',
  663. 'Set-Cookie: redirect1=true;path=/',
  664. ]);
  665. $adapter->expects($this->at(0))
  666. ->method('send')
  667. ->with(
  668. $this->callback(function ($request) use ($url) {
  669. $this->assertInstanceOf(Request::class, $request);
  670. $this->assertEquals($url, $request->getUri());
  671. return true;
  672. }),
  673. $this->callback(function ($options) {
  674. $this->assertArrayNotHasKey('redirect', $options);
  675. return true;
  676. })
  677. )
  678. ->willReturn([$redirect]);
  679. $redirect2 = new Response([
  680. 'HTTP/1.0 301',
  681. 'Location: /redirect2#foo',
  682. 'Set-Cookie: redirect2=true;path=/',
  683. ]);
  684. $adapter->expects($this->at(1))
  685. ->method('send')
  686. ->with(
  687. $this->callback(function ($request) use ($url) {
  688. $this->assertInstanceOf(Request::class, $request);
  689. $this->assertEquals($url . '/redirect1?foo=bar', $request->getUri());
  690. return true;
  691. }),
  692. $this->callback(function ($options) {
  693. $this->assertArrayNotHasKey('redirect', $options);
  694. return true;
  695. })
  696. )
  697. ->willReturn([$redirect2]);
  698. $response = new Response([
  699. 'HTTP/1.0 200',
  700. ]);
  701. $adapter->expects($this->at(2))
  702. ->method('send')
  703. ->with($this->callback(function ($request) use ($url) {
  704. $this->assertInstanceOf(Request::class, $request);
  705. $this->assertEquals($url . '/redirect2#foo', $request->getUri());
  706. return true;
  707. }))
  708. ->willReturn([$response]);
  709. $client = new Client([
  710. 'adapter' => $adapter,
  711. ]);
  712. $result = $client->send(new Request($url), [
  713. 'redirect' => 10,
  714. ]);
  715. $this->assertInstanceOf(Response::class, $result);
  716. $this->assertTrue($result->isOk());
  717. $cookies = $client->cookies();
  718. $this->assertTrue($cookies->has('redirect1'));
  719. $this->assertTrue($cookies->has('redirect2'));
  720. }
  721. }