Skip to content

Commit 20170c7

Browse files
authored
Merge 0e6792a into acc6599
2 parents acc6599 + 0e6792a commit 20170c7

File tree

3 files changed

+305
-16
lines changed

3 files changed

+305
-16
lines changed

composer.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,11 @@
2626
"Http\\Client\\Common\\Plugin\\": "src/"
2727
}
2828
},
29+
"autoload-dev": {
30+
"psr-4": {
31+
"spec\\Http\\Client\\Common\\Plugin\\": "spec/"
32+
}
33+
},
2934
"scripts": {
3035
"test": "vendor/bin/phpspec run",
3136
"test-ci": "vendor/bin/phpspec run -c phpspec.ci.yml"

spec/CachePluginSpec.php

Lines changed: 192 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace spec\Http\Client\Common\Plugin;
44

5+
use Prophecy\Argument;
56
use Http\Message\StreamFactory;
67
use Http\Promise\FulfilledPromise;
78
use PhpSpec\ObjectBehavior;
@@ -15,7 +16,7 @@ class CachePluginSpec extends ObjectBehavior
1516
{
1617
function let(CacheItemPoolInterface $pool, StreamFactory $streamFactory)
1718
{
18-
$this->beConstructedWith($pool, $streamFactory, ['default_ttl'=>60]);
19+
$this->beConstructedWith($pool, $streamFactory, ['default_ttl'=>60, 'cache_lifetime'=>1000]);
1920
}
2021

2122
function it_is_initializable(CacheItemPoolInterface $pool)
@@ -39,14 +40,22 @@ function it_caches_responses(CacheItemPoolInterface $pool, CacheItemInterface $i
3940
$request->getUri()->willReturn('/');
4041
$response->getStatusCode()->willReturn(200);
4142
$response->getBody()->willReturn($stream);
42-
$response->getHeader('Cache-Control')->willReturn(array());
43-
$response->getHeader('Expires')->willReturn(array());
43+
$response->getHeader('Cache-Control')->willReturn(array())->shouldBeCalled();
44+
$response->getHeader('Expires')->willReturn(array())->shouldBeCalled();
45+
$response->getHeader('ETag')->willReturn(array())->shouldBeCalled();
4446

4547
$pool->getItem('d20f64acc6e70b6079845f2fe357732929550ae1')->shouldBeCalled()->willReturn($item);
4648
$item->isHit()->willReturn(false);
47-
$item->set(['response' => $response, 'body' => $httpBody])->willReturn($item)->shouldBeCalled();
48-
$item->expiresAfter(60)->willReturn($item)->shouldBeCalled();
49-
$pool->save($item)->shouldBeCalled();
49+
$item->expiresAfter(1060)->willReturn($item)->shouldBeCalled();
50+
51+
$item->set($this->getCacheItemMatcher([
52+
'response' => $response->getWrappedObject(),
53+
'body' => $httpBody,
54+
'expiresAt' => 0,
55+
'createdAt' => 0,
56+
'etag' => []
57+
]))->willReturn($item)->shouldBeCalled();
58+
$pool->save(Argument::any())->shouldBeCalled();
5059

5160
$next = function (RequestInterface $request) use ($response) {
5261
return new FulfilledPromise($response->getWrappedObject());
@@ -100,13 +109,20 @@ function it_calculate_age_from_response(CacheItemPoolInterface $pool, CacheItemI
100109
$response->getHeader('Cache-Control')->willReturn(array('max-age=40'));
101110
$response->getHeader('Age')->willReturn(array('15'));
102111
$response->getHeader('Expires')->willReturn(array());
112+
$response->getHeader('ETag')->willReturn(array());
103113

104114
$pool->getItem('d20f64acc6e70b6079845f2fe357732929550ae1')->shouldBeCalled()->willReturn($item);
105115
$item->isHit()->willReturn(false);
106116

107-
// 40-15 should be 25
108-
$item->set(['response' => $response, 'body' => $httpBody])->willReturn($item)->shouldBeCalled();
109-
$item->expiresAfter(25)->willReturn($item)->shouldBeCalled();
117+
$item->set($this->getCacheItemMatcher([
118+
'response' => $response->getWrappedObject(),
119+
'body' => $httpBody,
120+
'expiresAt' => 0,
121+
'createdAt' => 0,
122+
'etag' => []
123+
]))->willReturn($item)->shouldBeCalled();
124+
// 40-15 should be 25 + the default 1000
125+
$item->expiresAfter(1025)->willReturn($item)->shouldBeCalled();
110126
$pool->save($item)->shouldBeCalled();
111127

112128
$next = function (RequestInterface $request) use ($response) {
@@ -115,4 +131,171 @@ function it_calculate_age_from_response(CacheItemPoolInterface $pool, CacheItemI
115131

116132
$this->handleRequest($request, $next, function () {});
117133
}
134+
135+
function it_saves_etag(CacheItemPoolInterface $pool, CacheItemInterface $item, RequestInterface $request, ResponseInterface $response, StreamInterface $stream)
136+
{
137+
$httpBody = 'body';
138+
$stream->__toString()->willReturn($httpBody);
139+
$stream->isSeekable()->willReturn(true);
140+
$stream->rewind()->shouldBeCalled();
141+
142+
$request->getMethod()->willReturn('GET');
143+
$request->getUri()->willReturn('/');
144+
$response->getStatusCode()->willReturn(200);
145+
$response->getBody()->willReturn($stream);
146+
$response->getHeader('Cache-Control')->willReturn(array());
147+
$response->getHeader('Expires')->willReturn(array());
148+
$response->getHeader('ETag')->willReturn(array('foo_etag'));
149+
150+
$pool->getItem('d20f64acc6e70b6079845f2fe357732929550ae1')->shouldBeCalled()->willReturn($item);
151+
$item->isHit()->willReturn(false);
152+
$item->expiresAfter(1060)->willReturn($item);
153+
154+
$item->set($this->getCacheItemMatcher([
155+
'response' => $response->getWrappedObject(),
156+
'body' => $httpBody,
157+
'expiresAt' => 0,
158+
'createdAt' => 0,
159+
'etag' => ['foo_etag']
160+
]))->willReturn($item)->shouldBeCalled();
161+
$pool->save(Argument::any())->shouldBeCalled();
162+
163+
$next = function (RequestInterface $request) use ($response) {
164+
return new FulfilledPromise($response->getWrappedObject());
165+
};
166+
167+
$this->handleRequest($request, $next, function () {});
168+
}
169+
170+
function it_adds_etag_and_modfied_since_to_request(CacheItemPoolInterface $pool, CacheItemInterface $item, RequestInterface $request, ResponseInterface $response, StreamInterface $stream)
171+
{
172+
$httpBody = 'body';
173+
174+
$request->getMethod()->willReturn('GET');
175+
$request->getUri()->willReturn('/');
176+
177+
$request->withHeader('If-Modified-Since', 'Thursday, 01-Jan-70 01:18:31 GMT')->shouldBeCalled()->willReturn($request);
178+
$request->withHeader('If-None-Match', 'foo_etag')->shouldBeCalled()->willReturn($request);
179+
180+
$response->getStatusCode()->willReturn(304);
181+
182+
$pool->getItem('d20f64acc6e70b6079845f2fe357732929550ae1')->shouldBeCalled()->willReturn($item);
183+
$item->isHit()->willReturn(true, false);
184+
$item->get()->willReturn([
185+
'response' => $response,
186+
'body' => $httpBody,
187+
'expiresAt' => 0,
188+
'createdAt' => 4711,
189+
'etag' => ['foo_etag']
190+
])->shouldBeCalled();
191+
192+
$next = function (RequestInterface $request) use ($response) {
193+
return new FulfilledPromise($response->getWrappedObject());
194+
};
195+
196+
$this->handleRequest($request, $next, function () {});
197+
}
198+
199+
function it_servces_a_cached_response(CacheItemPoolInterface $pool, CacheItemInterface $item, RequestInterface $request, ResponseInterface $response, StreamInterface $stream, StreamFactory $streamFactory)
200+
{
201+
$httpBody = 'body';
202+
203+
$request->getMethod()->willReturn('GET');
204+
$request->getUri()->willReturn('/');
205+
206+
$pool->getItem('d20f64acc6e70b6079845f2fe357732929550ae1')->shouldBeCalled()->willReturn($item);
207+
$item->isHit()->willReturn(true);
208+
$item->get()->willReturn([
209+
'response' => $response,
210+
'body' => $httpBody,
211+
'expiresAt' => time()+1000000, //It is in the future
212+
'createdAt' => 4711,
213+
'etag' => []
214+
])->shouldBeCalled();
215+
216+
// Make sure we add back the body
217+
$response->withBody($stream)->willReturn($response)->shouldBeCalled();
218+
$streamFactory->createStream($httpBody)->shouldBeCalled()->willReturn($stream);
219+
220+
$next = function (RequestInterface $request) use ($response) {
221+
return new FulfilledPromise($response->getWrappedObject());
222+
};
223+
224+
$this->handleRequest($request, $next, function () {});
225+
}
226+
227+
function it_serves_and_resaved_expired_response(CacheItemPoolInterface $pool, CacheItemInterface $item, RequestInterface $request, ResponseInterface $response, StreamInterface $stream, StreamFactory $streamFactory)
228+
{
229+
$httpBody = 'body';
230+
231+
$request->getMethod()->willReturn('GET');
232+
$request->getUri()->willReturn('/');
233+
234+
$request->withHeader(Argument::any(), Argument::any())->willReturn($request);
235+
$request->withHeader(Argument::any(), Argument::any())->willReturn($request);
236+
237+
$response->getStatusCode()->willReturn(304);
238+
$response->getHeader('Cache-Control')->willReturn(array());
239+
$response->getHeader('Expires')->willReturn(array())->shouldBeCalled();
240+
241+
// Make sure we add back the body
242+
$response->withBody($stream)->willReturn($response)->shouldBeCalled();
243+
244+
$pool->getItem('d20f64acc6e70b6079845f2fe357732929550ae1')->shouldBeCalled()->willReturn($item);
245+
$item->isHit()->willReturn(true, true);
246+
$item->expiresAfter(1060)->willReturn($item)->shouldBeCalled();
247+
$item->get()->willReturn([
248+
'response' => $response,
249+
'body' => $httpBody,
250+
'expiresAt' => 0,
251+
'createdAt' => 4711,
252+
'etag' => ['foo_etag']
253+
])->shouldBeCalled();
254+
255+
$item->set($this->getCacheItemMatcher([
256+
'response' => $response->getWrappedObject(),
257+
'body' => $httpBody,
258+
'expiresAt' => 0,
259+
'createdAt' => 0,
260+
'etag' => ['foo_etag']
261+
]))->willReturn($item)->shouldBeCalled();
262+
$pool->save(Argument::any())->shouldBeCalled();
263+
264+
$streamFactory->createStream($httpBody)->shouldBeCalled()->willReturn($stream);
265+
266+
$next = function (RequestInterface $request) use ($response) {
267+
return new FulfilledPromise($response->getWrappedObject());
268+
};
269+
270+
$this->handleRequest($request, $next, function () {});
271+
}
272+
273+
274+
/**
275+
* Private function to match cache item data.
276+
*
277+
* @param array $expectedData
278+
*
279+
* @return \Closure
280+
*/
281+
private function getCacheItemMatcher(array $expectedData)
282+
{
283+
return Argument::that(function(array $actualData) use ($expectedData) {
284+
foreach ($expectedData as $key => $value) {
285+
if (!isset($actualData[$key])) {
286+
return false;
287+
}
288+
289+
if ($key === 'expiresAt' || $key === 'createdAt') {
290+
// We do not need to validate the value of these fields.
291+
continue;
292+
}
293+
294+
if ($actualData[$key] !== $value) {
295+
return false;
296+
}
297+
}
298+
return true;
299+
});
300+
}
118301
}

0 commit comments

Comments
 (0)