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,7 @@ 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 , ['default_ttl ' =>60 , ' cache_lifetime ' => 1000 ]);
19
20
}
20
21
21
22
function it_is_initializable (CacheItemPoolInterface $ pool )
@@ -39,14 +40,22 @@ function it_caches_responses(CacheItemPoolInterface $pool, CacheItemInterface $i
39
40
$ request ->getUri ()->willReturn ('/ ' );
40
41
$ response ->getStatusCode ()->willReturn (200 );
41
42
$ 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 ();
44
46
45
47
$ pool ->getItem ('d20f64acc6e70b6079845f2fe357732929550ae1 ' )->shouldBeCalled ()->willReturn ($ item );
46
48
$ 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 ();
50
59
51
60
$ next = function (RequestInterface $ request ) use ($ response ) {
52
61
return new FulfilledPromise ($ response ->getWrappedObject ());
@@ -100,13 +109,20 @@ function it_calculate_age_from_response(CacheItemPoolInterface $pool, CacheItemI
100
109
$ response ->getHeader ('Cache-Control ' )->willReturn (array ('max-age=40 ' ));
101
110
$ response ->getHeader ('Age ' )->willReturn (array ('15 ' ));
102
111
$ response ->getHeader ('Expires ' )->willReturn (array ());
112
+ $ response ->getHeader ('ETag ' )->willReturn (array ());
103
113
104
114
$ pool ->getItem ('d20f64acc6e70b6079845f2fe357732929550ae1 ' )->shouldBeCalled ()->willReturn ($ item );
105
115
$ item ->isHit ()->willReturn (false );
106
116
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 ();
110
126
$ pool ->save ($ item )->shouldBeCalled ();
111
127
112
128
$ next = function (RequestInterface $ request ) use ($ response ) {
@@ -115,4 +131,171 @@ function it_calculate_age_from_response(CacheItemPoolInterface $pool, CacheItemI
115
131
116
132
$ this ->handleRequest ($ request , $ next , function () {});
117
133
}
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
+ }
118
301
}
0 commit comments