2
2
3
3
namespace spec \Http \Client \Common \Plugin ;
4
4
5
+ use Prophecy \Argument ;
5
6
use Http \Message \StreamFactory ;
6
7
use Http \Promise \FulfilledPromise ;
7
8
use PhpSpec \ObjectBehavior ;
@@ -15,7 +16,10 @@ class CachePluginSpec extends ObjectBehavior
15
16
{
16
17
function let (CacheItemPoolInterface $ pool , StreamFactory $ streamFactory )
17
18
{
18
- $ this ->beConstructedWith ($ pool , $ streamFactory , ['default_ttl ' =>60 ]);
19
+ $ this ->beConstructedWith ($ pool , $ streamFactory , [
20
+ 'default_ttl ' => 60 ,
21
+ 'cache_lifetime ' => 1000
22
+ ]);
19
23
}
20
24
21
25
function it_is_initializable (CacheItemPoolInterface $ pool )
@@ -39,14 +43,22 @@ function it_caches_responses(CacheItemPoolInterface $pool, CacheItemInterface $i
39
43
$ request ->getUri ()->willReturn ('/ ' );
40
44
$ response ->getStatusCode ()->willReturn (200 );
41
45
$ response ->getBody ()->willReturn ($ stream );
42
- $ response ->getHeader ('Cache-Control ' )->willReturn (array ());
43
- $ response ->getHeader ('Expires ' )->willReturn (array ());
46
+ $ response ->getHeader ('Cache-Control ' )->willReturn (array ())->shouldBeCalled ();
47
+ $ response ->getHeader ('Expires ' )->willReturn (array ())->shouldBeCalled ();
48
+ $ response ->getHeader ('ETag ' )->willReturn (array ())->shouldBeCalled ();
44
49
45
50
$ pool ->getItem ('d20f64acc6e70b6079845f2fe357732929550ae1 ' )->shouldBeCalled ()->willReturn ($ item );
46
51
$ 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 ();
52
+ $ item ->expiresAfter (1060 )->willReturn ($ item )->shouldBeCalled ();
53
+
54
+ $ item ->set ($ this ->getCacheItemMatcher ([
55
+ 'response ' => $ response ->getWrappedObject (),
56
+ 'body ' => $ httpBody ,
57
+ 'expiresAt ' => 0 ,
58
+ 'createdAt ' => 0 ,
59
+ 'etag ' => []
60
+ ]))->willReturn ($ item )->shouldBeCalled ();
61
+ $ pool ->save (Argument::any ())->shouldBeCalled ();
50
62
51
63
$ next = function (RequestInterface $ request ) use ($ response ) {
52
64
return new FulfilledPromise ($ response ->getWrappedObject ());
@@ -100,13 +112,20 @@ function it_calculate_age_from_response(CacheItemPoolInterface $pool, CacheItemI
100
112
$ response ->getHeader ('Cache-Control ' )->willReturn (array ('max-age=40 ' ));
101
113
$ response ->getHeader ('Age ' )->willReturn (array ('15 ' ));
102
114
$ response ->getHeader ('Expires ' )->willReturn (array ());
115
+ $ response ->getHeader ('ETag ' )->willReturn (array ());
103
116
104
117
$ pool ->getItem ('d20f64acc6e70b6079845f2fe357732929550ae1 ' )->shouldBeCalled ()->willReturn ($ item );
105
118
$ item ->isHit ()->willReturn (false );
106
119
107
- // 40-15 should be 25
108
- $ item ->set (['response ' => $ response , 'body ' => $ httpBody ])->willReturn ($ item )->shouldBeCalled ();
109
- $ item ->expiresAfter (25 )->willReturn ($ item )->shouldBeCalled ();
120
+ $ item ->set ($ this ->getCacheItemMatcher ([
121
+ 'response ' => $ response ->getWrappedObject (),
122
+ 'body ' => $ httpBody ,
123
+ 'expiresAt ' => 0 ,
124
+ 'createdAt ' => 0 ,
125
+ 'etag ' => []
126
+ ]))->willReturn ($ item )->shouldBeCalled ();
127
+ // 40-15 should be 25 + the default 1000
128
+ $ item ->expiresAfter (1025 )->willReturn ($ item )->shouldBeCalled ();
110
129
$ pool ->save ($ item )->shouldBeCalled ();
111
130
112
131
$ next = function (RequestInterface $ request ) use ($ response ) {
@@ -115,4 +134,171 @@ function it_calculate_age_from_response(CacheItemPoolInterface $pool, CacheItemI
115
134
116
135
$ this ->handleRequest ($ request , $ next , function () {});
117
136
}
137
+
138
+ function it_saves_etag (CacheItemPoolInterface $ pool , CacheItemInterface $ item , RequestInterface $ request , ResponseInterface $ response , StreamInterface $ stream )
139
+ {
140
+ $ httpBody = 'body ' ;
141
+ $ stream ->__toString ()->willReturn ($ httpBody );
142
+ $ stream ->isSeekable ()->willReturn (true );
143
+ $ stream ->rewind ()->shouldBeCalled ();
144
+
145
+ $ request ->getMethod ()->willReturn ('GET ' );
146
+ $ request ->getUri ()->willReturn ('/ ' );
147
+ $ response ->getStatusCode ()->willReturn (200 );
148
+ $ response ->getBody ()->willReturn ($ stream );
149
+ $ response ->getHeader ('Cache-Control ' )->willReturn (array ());
150
+ $ response ->getHeader ('Expires ' )->willReturn (array ());
151
+ $ response ->getHeader ('ETag ' )->willReturn (array ('foo_etag ' ));
152
+
153
+ $ pool ->getItem ('d20f64acc6e70b6079845f2fe357732929550ae1 ' )->shouldBeCalled ()->willReturn ($ item );
154
+ $ item ->isHit ()->willReturn (false );
155
+ $ item ->expiresAfter (1060 )->willReturn ($ item );
156
+
157
+ $ item ->set ($ this ->getCacheItemMatcher ([
158
+ 'response ' => $ response ->getWrappedObject (),
159
+ 'body ' => $ httpBody ,
160
+ 'expiresAt ' => 0 ,
161
+ 'createdAt ' => 0 ,
162
+ 'etag ' => ['foo_etag ' ]
163
+ ]))->willReturn ($ item )->shouldBeCalled ();
164
+ $ pool ->save (Argument::any ())->shouldBeCalled ();
165
+
166
+ $ next = function (RequestInterface $ request ) use ($ response ) {
167
+ return new FulfilledPromise ($ response ->getWrappedObject ());
168
+ };
169
+
170
+ $ this ->handleRequest ($ request , $ next , function () {});
171
+ }
172
+
173
+ function it_adds_etag_and_modfied_since_to_request (CacheItemPoolInterface $ pool , CacheItemInterface $ item , RequestInterface $ request , ResponseInterface $ response , StreamInterface $ stream )
174
+ {
175
+ $ httpBody = 'body ' ;
176
+
177
+ $ request ->getMethod ()->willReturn ('GET ' );
178
+ $ request ->getUri ()->willReturn ('/ ' );
179
+
180
+ $ request ->withHeader ('If-Modified-Since ' , 'Thursday, 01-Jan-70 01:18:31 GMT ' )->shouldBeCalled ()->willReturn ($ request );
181
+ $ request ->withHeader ('If-None-Match ' , 'foo_etag ' )->shouldBeCalled ()->willReturn ($ request );
182
+
183
+ $ response ->getStatusCode ()->willReturn (304 );
184
+
185
+ $ pool ->getItem ('d20f64acc6e70b6079845f2fe357732929550ae1 ' )->shouldBeCalled ()->willReturn ($ item );
186
+ $ item ->isHit ()->willReturn (true , false );
187
+ $ item ->get ()->willReturn ([
188
+ 'response ' => $ response ,
189
+ 'body ' => $ httpBody ,
190
+ 'expiresAt ' => 0 ,
191
+ 'createdAt ' => 4711 ,
192
+ 'etag ' => ['foo_etag ' ]
193
+ ])->shouldBeCalled ();
194
+
195
+ $ next = function (RequestInterface $ request ) use ($ response ) {
196
+ return new FulfilledPromise ($ response ->getWrappedObject ());
197
+ };
198
+
199
+ $ this ->handleRequest ($ request , $ next , function () {});
200
+ }
201
+
202
+ function it_servces_a_cached_response (CacheItemPoolInterface $ pool , CacheItemInterface $ item , RequestInterface $ request , ResponseInterface $ response , StreamInterface $ stream , StreamFactory $ streamFactory )
203
+ {
204
+ $ httpBody = 'body ' ;
205
+
206
+ $ request ->getMethod ()->willReturn ('GET ' );
207
+ $ request ->getUri ()->willReturn ('/ ' );
208
+
209
+ $ pool ->getItem ('d20f64acc6e70b6079845f2fe357732929550ae1 ' )->shouldBeCalled ()->willReturn ($ item );
210
+ $ item ->isHit ()->willReturn (true );
211
+ $ item ->get ()->willReturn ([
212
+ 'response ' => $ response ,
213
+ 'body ' => $ httpBody ,
214
+ 'expiresAt ' => time ()+1000000 , //It is in the future
215
+ 'createdAt ' => 4711 ,
216
+ 'etag ' => []
217
+ ])->shouldBeCalled ();
218
+
219
+ // Make sure we add back the body
220
+ $ response ->withBody ($ stream )->willReturn ($ response )->shouldBeCalled ();
221
+ $ streamFactory ->createStream ($ httpBody )->shouldBeCalled ()->willReturn ($ stream );
222
+
223
+ $ next = function (RequestInterface $ request ) use ($ response ) {
224
+ return new FulfilledPromise ($ response ->getWrappedObject ());
225
+ };
226
+
227
+ $ this ->handleRequest ($ request , $ next , function () {});
228
+ }
229
+
230
+ function it_serves_and_resaved_expired_response (CacheItemPoolInterface $ pool , CacheItemInterface $ item , RequestInterface $ request , ResponseInterface $ response , StreamInterface $ stream , StreamFactory $ streamFactory )
231
+ {
232
+ $ httpBody = 'body ' ;
233
+
234
+ $ request ->getMethod ()->willReturn ('GET ' );
235
+ $ request ->getUri ()->willReturn ('/ ' );
236
+
237
+ $ request ->withHeader (Argument::any (), Argument::any ())->willReturn ($ request );
238
+ $ request ->withHeader (Argument::any (), Argument::any ())->willReturn ($ request );
239
+
240
+ $ response ->getStatusCode ()->willReturn (304 );
241
+ $ response ->getHeader ('Cache-Control ' )->willReturn (array ());
242
+ $ response ->getHeader ('Expires ' )->willReturn (array ())->shouldBeCalled ();
243
+
244
+ // Make sure we add back the body
245
+ $ response ->withBody ($ stream )->willReturn ($ response )->shouldBeCalled ();
246
+
247
+ $ pool ->getItem ('d20f64acc6e70b6079845f2fe357732929550ae1 ' )->shouldBeCalled ()->willReturn ($ item );
248
+ $ item ->isHit ()->willReturn (true , true );
249
+ $ item ->expiresAfter (1060 )->willReturn ($ item )->shouldBeCalled ();
250
+ $ item ->get ()->willReturn ([
251
+ 'response ' => $ response ,
252
+ 'body ' => $ httpBody ,
253
+ 'expiresAt ' => 0 ,
254
+ 'createdAt ' => 4711 ,
255
+ 'etag ' => ['foo_etag ' ]
256
+ ])->shouldBeCalled ();
257
+
258
+ $ item ->set ($ this ->getCacheItemMatcher ([
259
+ 'response ' => $ response ->getWrappedObject (),
260
+ 'body ' => $ httpBody ,
261
+ 'expiresAt ' => 0 ,
262
+ 'createdAt ' => 0 ,
263
+ 'etag ' => ['foo_etag ' ]
264
+ ]))->willReturn ($ item )->shouldBeCalled ();
265
+ $ pool ->save (Argument::any ())->shouldBeCalled ();
266
+
267
+ $ streamFactory ->createStream ($ httpBody )->shouldBeCalled ()->willReturn ($ stream );
268
+
269
+ $ next = function (RequestInterface $ request ) use ($ response ) {
270
+ return new FulfilledPromise ($ response ->getWrappedObject ());
271
+ };
272
+
273
+ $ this ->handleRequest ($ request , $ next , function () {});
274
+ }
275
+
276
+
277
+ /**
278
+ * Private function to match cache item data.
279
+ *
280
+ * @param array $expectedData
281
+ *
282
+ * @return \Closure
283
+ */
284
+ private function getCacheItemMatcher (array $ expectedData )
285
+ {
286
+ return Argument::that (function (array $ actualData ) use ($ expectedData ) {
287
+ foreach ($ expectedData as $ key => $ value ) {
288
+ if (!isset ($ actualData [$ key ])) {
289
+ return false ;
290
+ }
291
+
292
+ if ($ key === 'expiresAt ' || $ key === 'createdAt ' ) {
293
+ // We do not need to validate the value of these fields.
294
+ continue ;
295
+ }
296
+
297
+ if ($ actualData [$ key ] !== $ value ) {
298
+ return false ;
299
+ }
300
+ }
301
+ return true ;
302
+ });
303
+ }
118
304
}
0 commit comments