Skip to content

Commit c761f55

Browse files
committed
[WIP] Adding test for CachingHttpClient
1 parent fe4d5f2 commit c761f55

File tree

4 files changed

+245
-6
lines changed

4 files changed

+245
-6
lines changed

src/Symfony/Component/HttpClient/Response/MockResponse.php

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,13 @@ public function __construct($body = '', array $info = [])
4949
}
5050

5151
$rawHeaders = [];
52-
53-
foreach ($info['raw_headers'] as $k => $v) {
54-
foreach ((array) $v as $v) {
55-
$rawHeaders[] = (\is_string($k) ? $k.': ' : '').$v;
52+
foreach ($this->info['raw_headers'] as $k => $v) {
53+
if (\is_array($v)) {
54+
$rawHeaders[] = ucwords($k, ' -') . ': ' . implode(', ', $v);
55+
} else {
56+
foreach ((array) $v as $v) {
57+
$rawHeaders[] = (\is_string($k) ? $k . ': ' : '') . $v;
58+
}
5659
}
5760
}
5861

@@ -242,11 +245,11 @@ private static function readResponse(self $response, array $options, ResponseInt
242245
$response->addRawHeaders($info['raw_headers'] ?? [], $response->info, $response->headers);
243246
$dlSize = (int) ($response->headers['content-length'][0] ?? 0);
244247

245-
$response->info = [
248+
$response->info = $response->info + $info + [
246249
'start_time' => $response->info['start_time'],
247250
'user_data' => $response->info['user_data'],
248251
'http_code' => $response->info['http_code'],
249-
] + $info + $response->info;
252+
];
250253

251254
if (isset($response->info['total_time'])) {
252255
$response->info['total_time'] = microtime(true) - $response->info['start_time'];

src/Symfony/Component/HttpClient/Response/ResponseTrait.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,8 +188,10 @@ abstract protected static function select(\stdClass $multi, float $timeout): int
188188

189189
private static function addRawHeaders(array $rawHeaders, array &$info, array &$headers): void
190190
{
191+
$hasStatusLine = false;
191192
foreach ($rawHeaders as $h) {
192193
if (11 <= \strlen($h) && '/' === $h[4] && preg_match('#^HTTP/\d+(?:\.\d+)? ([12345]\d\d) .*#', $h, $m)) {
194+
$hasStatusLine = true;
193195
$headers = [];
194196
$info['http_code'] = (int) $m[1];
195197
} elseif (2 === \count($m = explode(':', $h, 2))) {
@@ -199,6 +201,9 @@ private static function addRawHeaders(array $rawHeaders, array &$info, array &$h
199201
$info['raw_headers'][] = $h;
200202
}
201203

204+
if (!$hasStatusLine && !empty($info['http_code'])) {
205+
array_unshift($info['raw_headers'], sprintf('HTTP/1.1 %s OK', $info['http_code']));
206+
}
202207
if (!$info['http_code']) {
203208
throw new TransportException('Invalid or missing HTTP status line.');
204209
}
Lines changed: 230 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,230 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\HttpClient\Tests;
13+
14+
use Psr\Log\LoggerInterface;
15+
use Symfony\Component\HttpClient\CachingHttpClient;
16+
use Symfony\Component\HttpClient\Exception\TransportException;
17+
use Symfony\Component\HttpClient\MockHttpClient;
18+
use Symfony\Component\HttpClient\NativeHttpClient;
19+
use Symfony\Component\HttpClient\Response\MockResponse;
20+
use Symfony\Component\HttpKernel\HttpCache\StoreInterface;
21+
use Symfony\Contracts\HttpClient\HttpClientInterface;
22+
use Symfony\Contracts\HttpClient\ResponseInterface;
23+
use Symfony\Contracts\HttpClient\Test\HttpClientTestCase;
24+
25+
class TestCachingHttpClient extends CachingHttpClient
26+
{
27+
protected $client;
28+
29+
public function __construct(StoreInterface $storeInterface, $testCase)
30+
{
31+
parent::__construct(new NativeHttpClient(), $storeInterface);
32+
33+
$headers = [
34+
'Host: localhost:8057',
35+
'Content-Type: application/json',
36+
];
37+
38+
$body = '{
39+
"SERVER_PROTOCOL": "HTTP/1.1",
40+
"SERVER_NAME": "127.0.0.1",
41+
"REQUEST_URI": "/",
42+
"REQUEST_METHOD": "GET",
43+
"HTTP_FOO": "baR",
44+
"HTTP_HOST": "localhost:8057"
45+
}';
46+
47+
$this->client = new MockHttpClient(function (string $method, string $url, array $options) use ($headers, $body, $testCase) {
48+
49+
switch ($testCase) {
50+
default:
51+
// force the request to be completed so that we don't test side effects of the transport
52+
$response = $this->request($method, $url, $options);
53+
$content = $response->getContent(false);
54+
55+
return new MockResponse($content, $response->getInfo());
56+
57+
case 'testGetRequest':
58+
array_unshift($headers, 'HTTP/1.1 200 OK');
59+
60+
if (preg_match('/length-broken$/', $url)){
61+
$headers = [
62+
'Host: localhost:8057',
63+
'Content-Length: 1000',
64+
'Content-Type: application/json',
65+
];
66+
67+
return new MockResponse($body, ['raw_headers' => $headers]);
68+
}
69+
70+
return new MockResponse($body, ['raw_headers' => $headers]);
71+
//
72+
// case 'testDnsError':
73+
// $mock = $this->getMockBuilder(ResponseInterface::class)->getMock();
74+
// $mock->expects($this->any())
75+
// ->method('getStatusCode')
76+
// ->willThrowException(new TransportException('DSN error'));
77+
// $mock->expects($this->any())
78+
// ->method('getInfo')
79+
// ->willReturn([]);
80+
//
81+
// $responses[] = $mock;
82+
// $responses[] = $mock;
83+
// break;
84+
//
85+
// case 'testBadRequestBody':
86+
// case 'testOnProgressCancel':
87+
// case 'testOnProgressError':
88+
// $responses[] = new MockResponse($body, ['raw_headers' => $headers]);
89+
// break;
90+
//
91+
// case 'testTimeoutOnAccess':
92+
// $mock = $this->getMockBuilder(ResponseInterface::class)->getMock();
93+
// $mock->expects($this->any())
94+
// ->method('getHeaders')
95+
// ->willThrowException(new TransportException('Timeout'));
96+
//
97+
// $responses[] = $mock;
98+
// break;
99+
//
100+
// case 'testResolve':
101+
// $responses[] = new MockResponse($body, ['raw_headers' => $headers]);
102+
// $responses[] = new MockResponse($body, ['raw_headers' => $headers]);
103+
// $responses[] = $client->request('GET', 'http://symfony.com:8057/');
104+
// break;
105+
//
106+
// case 'testTimeoutOnStream':
107+
// case 'testUncheckedTimeoutThrows':
108+
// $body = ['<1>', '', '<2>'];
109+
// $responses[] = new MockResponse($body, ['raw_headers' => $headers]);
110+
// break;
111+
}
112+
});
113+
}
114+
}
115+
116+
class CachingHttpClientTest extends HttpClientTestCase
117+
{
118+
protected function getHttpClient(string $testCase): HttpClientInterface
119+
{
120+
$headers = [
121+
'Host: localhost:8057',
122+
'Content-Type: application/json',
123+
];
124+
125+
$body = '{
126+
"SERVER_PROTOCOL": "HTTP/1.1",
127+
"SERVER_NAME": "127.0.0.1",
128+
"REQUEST_URI": "/",
129+
"REQUEST_METHOD": "GET",
130+
"HTTP_FOO": "baR",
131+
"HTTP_HOST": "localhost:8057"
132+
}';
133+
134+
// switch ($testCase) {
135+
// default:
136+
// return new MockHttpClient(function(string $method, string $url, array $options) {
137+
// // force the request to be completed so that we don't test side effects of the transport
138+
// $response = $this->request($method, $url, $options);
139+
// $content = $response->getContent(false);
140+
//
141+
// return new MockResponse($content, $response->getInfo());
142+
// });
143+
//
144+
// case 'testBadRequestBody':
145+
// case 'testOnProgressCancel':
146+
// case 'testOnProgressError':
147+
// $responses[] = new MockResponse($body, ['raw_headers' => $headers]);
148+
// break;
149+
//
150+
// case 'testTimeoutOnAccess':
151+
// $mock = $this->getMockBuilder(ResponseInterface::class)->getMock();
152+
// $mock->expects($this->any())
153+
// ->method('getHeaders')
154+
// ->willThrowException(new TransportException('Timeout'));
155+
//
156+
// $responses[] = $mock;
157+
// break;
158+
//
159+
// case 'testResolve':
160+
// $responses[] = new MockResponse($body, ['raw_headers' => $headers]);
161+
// $responses[] = new MockResponse($body, ['raw_headers' => $headers]);
162+
// $responses[] = $client->request('GET', 'http://symfony.com:8057/');
163+
// break;
164+
//
165+
// case 'testTimeoutOnStream':
166+
// case 'testUncheckedTimeoutThrows':
167+
// $body = ['<1>', '', '<2>'];
168+
// $responses[] = new MockResponse($body, ['raw_headers' => $headers]);
169+
// break;
170+
// }
171+
172+
$client = new MockHttpClient(function (string $method, string $url, array $options) use ($headers, $body, $testCase) {
173+
switch ($testCase) {
174+
case 'testGetRequest':
175+
if (preg_match('/length-broken$/', $url)){
176+
$headers = [
177+
'Host: localhost:8057',
178+
'Content-Length: 1000',
179+
'Content-Type: application/json',
180+
];
181+
}
182+
183+
array_unshift($headers, 'HTTP/1.1 200 OK');
184+
return new MockResponse($body, ['raw_headers' => $headers]);
185+
186+
case 'testDnsError':
187+
$mock = $this->getMockBuilder(ResponseInterface::class)->getMock();
188+
$mock->expects($this->any())
189+
->method('getStatusCode')
190+
->willThrowException(new TransportException('DSN error'));
191+
$mock->expects($this->any())
192+
->method('getInfo')
193+
->willReturn([]);
194+
195+
return $mock;
196+
break;
197+
198+
case 'testBadRequestBody':
199+
case 'testOnProgressCancel':
200+
case 'testOnProgressError':
201+
return new MockResponse($body, ['raw_headers' => $headers]);
202+
203+
case 'testTimeoutOnAccess':
204+
$mock = $this->getMockBuilder(ResponseInterface::class)->getMock();
205+
$mock->expects($this->any())
206+
->method('getHeaders')
207+
->willThrowException(new TransportException('Timeout'));
208+
209+
return $mock;
210+
211+
// case 'testResolve':
212+
// $responses[] = new MockResponse($body, ['raw_headers' => $headers]);
213+
// $responses[] = new MockResponse($body, ['raw_headers' => $headers]);
214+
// $responses[] = $client->request('GET', 'http://symfony.com:8057/');
215+
// break;
216+
217+
case 'testTimeoutOnStream':
218+
case 'testUncheckedTimeoutThrows':
219+
$body = ['<1>', '', '<2>'];
220+
return new MockResponse($body, ['raw_headers' => $headers]);
221+
default:
222+
return new MockResponse();
223+
}
224+
});
225+
226+
$storeInterface = $this->createMock(StoreInterface::class);
227+
228+
return new CachingHttpClient($client, $storeInterface);
229+
}
230+
}

src/Symfony/Contracts/HttpClient/Test/HttpClientTestCase.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ public function testGetRequest()
4848
$info = $response->getInfo();
4949
$this->assertNull($info['error']);
5050
$this->assertSame(0, $info['redirect_count']);
51+
$this->assertNotEmpty($info['raw_headers']);
5152
$this->assertSame('HTTP/1.1 200 OK', $info['raw_headers'][0]);
5253
$this->assertSame('Host: localhost:8057', $info['raw_headers'][1]);
5354
$this->assertSame('http://localhost:8057/', $info['url']);

0 commit comments

Comments
 (0)