1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/* |
4
|
|
|
* This file is part of the FOSHttpCacheBundle package. |
5
|
|
|
* |
6
|
|
|
* (c) FriendsOfSymfony <http://friendsofsymfony.github.com/> |
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 FOS\HttpCacheBundle\Tests\Unit\EventListener; |
13
|
|
|
|
14
|
|
|
use FOS\HttpCacheBundle\EventListener\CacheControlSubscriber; |
15
|
|
|
use Symfony\Component\HttpFoundation\Request; |
16
|
|
|
use Symfony\Component\HttpFoundation\Response; |
17
|
|
|
use Symfony\Component\HttpKernel\Event\FilterResponseEvent; |
18
|
|
|
|
19
|
|
|
class CacheControlSubscriberTest extends \PHPUnit_Framework_TestCase |
20
|
|
|
{ |
21
|
|
|
public function testDefaultHeaders() |
22
|
|
|
{ |
23
|
|
|
$event = $this->buildEvent(); |
24
|
|
|
$headers = array( |
25
|
|
|
'overwrite' => false, |
26
|
|
|
'last_modified' => '13.07.2003', |
27
|
|
|
'cache_control' => array( |
28
|
|
|
'max_age' => '900', |
29
|
|
|
's_maxage' => '300', |
30
|
|
|
'public' => true, |
31
|
|
|
'private' => false, |
32
|
|
|
), |
33
|
|
|
); |
34
|
|
|
$subscriber = $this->getCacheControl($headers); |
35
|
|
|
|
36
|
|
|
$subscriber->onKernelResponse($event); |
37
|
|
|
$newHeaders = $event->getResponse()->headers->all(); |
38
|
|
|
|
39
|
|
|
$this->assertEquals('max-age=900, public, s-maxage=300', $newHeaders['cache-control'][0]); |
40
|
|
|
$this->assertArrayHasKey('last-modified', $newHeaders, implode(',', array_keys($newHeaders))); |
41
|
|
|
$this->assertEquals(strtotime('13.07.2003'), strtotime($newHeaders['last-modified'][0])); |
42
|
|
|
} |
43
|
|
|
|
44
|
|
View Code Duplication |
public function testExtraHeaders() |
|
|
|
|
45
|
|
|
{ |
46
|
|
|
$event = $this->buildEvent(); |
47
|
|
|
$headers = array( |
48
|
|
|
'overwrite' => false, |
49
|
|
|
'cache_control' => array( |
50
|
|
|
'must_revalidate' => true, |
51
|
|
|
'proxy_revalidate' => true, |
52
|
|
|
'no_transform' => true, |
53
|
|
|
'stale_if_error' => '300', |
54
|
|
|
'stale_while_revalidate' => '400', |
55
|
|
|
), |
56
|
|
|
); |
57
|
|
|
$subscriber = $this->getCacheControl($headers); |
58
|
|
|
|
59
|
|
|
$subscriber->onKernelResponse($event); |
60
|
|
|
$newHeaders = $event->getResponse()->headers->all(); |
61
|
|
|
|
62
|
|
|
$this->assertEquals('must-revalidate, no-transform, proxy-revalidate, stale-if-error=300, stale-while-revalidate=400, private', $newHeaders['cache-control'][0]); |
63
|
|
|
} |
64
|
|
|
|
65
|
|
|
public function testCompoundHeaders() |
66
|
|
|
{ |
67
|
|
|
$event = $this->buildEvent(); |
68
|
|
|
$headers = array( |
69
|
|
|
'overwrite' => false, |
70
|
|
|
'cache_control' => array( |
71
|
|
|
'max_age' => '900', |
72
|
|
|
's_maxage' => '300', |
73
|
|
|
'public' => true, |
74
|
|
|
'private' => false, |
75
|
|
|
'must_revalidate' => true, |
76
|
|
|
'proxy_revalidate' => true, |
77
|
|
|
'no_transform' => true, |
78
|
|
|
'stale_if_error' => '300', |
79
|
|
|
'stale_while_revalidate' => '400', |
80
|
|
|
), |
81
|
|
|
); |
82
|
|
|
$subscriber = $this->getCacheControl($headers); |
83
|
|
|
|
84
|
|
|
$subscriber->onKernelResponse($event); |
85
|
|
|
$newHeaders = $event->getResponse()->headers->all(); |
86
|
|
|
|
87
|
|
|
$this->assertEquals('max-age=900, must-revalidate, no-transform, proxy-revalidate, public, s-maxage=300, stale-if-error=300, stale-while-revalidate=400', $newHeaders['cache-control'][0]); |
88
|
|
|
} |
89
|
|
|
|
90
|
|
View Code Duplication |
public function testSetNoCacheHeaders() |
|
|
|
|
91
|
|
|
{ |
92
|
|
|
$event = $this->buildEvent(); |
93
|
|
|
$headers = array( |
94
|
|
|
'overwrite' => false, |
95
|
|
|
'cache_control' => array( |
96
|
|
|
'max_age' => '0', |
97
|
|
|
's_maxage' => '0', |
98
|
|
|
'private' => true, |
99
|
|
|
'no_cache' => true, |
100
|
|
|
'must_revalidate' => true, |
101
|
|
|
), |
102
|
|
|
'last_modified' => '13.07.2003', |
103
|
|
|
); |
104
|
|
|
$subscriber = $this->getCacheControl($headers); |
105
|
|
|
|
106
|
|
|
$subscriber->onKernelResponse($event); |
107
|
|
|
$newHeaders = $event->getResponse()->headers->all(); |
108
|
|
|
|
109
|
|
|
$this->assertEquals('max-age=0, must-revalidate, no-cache, private, s-maxage=0', $newHeaders['cache-control'][0]); |
110
|
|
|
} |
111
|
|
|
|
112
|
|
|
public function testMergeHeaders() |
113
|
|
|
{ |
114
|
|
|
$event = $this->buildEvent(); |
115
|
|
|
$headers = array( |
116
|
|
|
'overwrite' => false, |
117
|
|
|
'cache_control' => array( |
118
|
|
|
'max_age' => '900', |
119
|
|
|
's_maxage' => '300', |
120
|
|
|
'public' => true, |
121
|
|
|
'private' => false, |
122
|
|
|
'must_revalidate' => true, |
123
|
|
|
'proxy_revalidate' => true, |
124
|
|
|
'no_transform' => true, |
125
|
|
|
'stale_if_error' => '300', |
126
|
|
|
'stale_while_revalidate' => '400', |
127
|
|
|
), |
128
|
|
|
'vary' => array( |
129
|
|
|
'Cookie', |
130
|
|
|
), |
131
|
|
|
'etag' => true, |
132
|
|
|
'last_modified' => '2014-10-10 GMT', |
133
|
|
|
); |
134
|
|
|
$subscriber = $this->getCacheControl($headers); |
135
|
|
|
$response = $event->getResponse(); |
136
|
|
|
$response->setPublic(); |
137
|
|
|
$response->setCache(array('max_age' => 0)); |
138
|
|
|
$response->headers->addCacheControlDirective('stale-if-error', 0); |
139
|
|
|
$response->setVary('Encoding'); |
140
|
|
|
$response->setEtag('foo'); |
141
|
|
|
$response->setLastModified(new \DateTime('2013-09-09 GMT')); |
142
|
|
|
|
143
|
|
|
$subscriber->onKernelResponse($event); |
144
|
|
|
$newHeaders = $event->getResponse()->headers->all(); |
145
|
|
|
|
146
|
|
|
$this->assertEquals('max-age=0, must-revalidate, no-transform, proxy-revalidate, public, s-maxage=300, stale-if-error=0, stale-while-revalidate=400', $newHeaders['cache-control'][0]); |
147
|
|
|
$this->assertEquals(array('Encoding', 'Cookie'), $newHeaders['vary']); |
148
|
|
|
$this->assertEquals('"foo"', $newHeaders['etag'][0]); |
149
|
|
|
$this->assertEquals('Mon, 09 Sep 2013 00:00:00 GMT', $newHeaders['last-modified'][0]); |
150
|
|
|
} |
151
|
|
|
|
152
|
|
|
public function testOverwriteHeaders() |
153
|
|
|
{ |
154
|
|
|
$event = $this->buildEvent(); |
155
|
|
|
$headers = array( |
156
|
|
|
'overwrite' => true, |
157
|
|
|
'cache_control' => array( |
158
|
|
|
'max_age' => '900', |
159
|
|
|
's_maxage' => '300', |
160
|
|
|
'public' => true, |
161
|
|
|
'private' => false, |
162
|
|
|
'must_revalidate' => true, |
163
|
|
|
'proxy_revalidate' => true, |
164
|
|
|
'no_transform' => true, |
165
|
|
|
'stale_if_error' => '300', |
166
|
|
|
'stale_while_revalidate' => '400', |
167
|
|
|
), |
168
|
|
|
'vary' => array( |
169
|
|
|
'Cookie', |
170
|
|
|
), |
171
|
|
|
'etag' => true, |
172
|
|
|
'last_modified' => '2014-10-10 GMT', |
173
|
|
|
); |
174
|
|
|
$subscriber = $this->getCacheControl($headers); |
175
|
|
|
$response = $event->getResponse(); |
176
|
|
|
$response->setPublic(); |
177
|
|
|
$response->setCache(array('max_age' => 0)); |
178
|
|
|
$response->headers->addCacheControlDirective('stale-if-error', 0); |
179
|
|
|
$response->setVary('Encoding'); |
180
|
|
|
$response->setEtag('foo'); |
181
|
|
|
$response->setLastModified(new \DateTime('2013-09-09 GMT')); |
182
|
|
|
|
183
|
|
|
$subscriber->onKernelResponse($event); |
184
|
|
|
$newHeaders = $event->getResponse()->headers->all(); |
185
|
|
|
|
186
|
|
|
$this->assertEquals('max-age=900, must-revalidate, no-transform, proxy-revalidate, public, s-maxage=300, stale-if-error=300, stale-while-revalidate=400', $newHeaders['cache-control'][0]); |
187
|
|
|
$this->assertEquals(array('Cookie'), $newHeaders['vary']); |
188
|
|
|
$this->assertEquals('"'.md5('').'"', $response->getEtag()); |
189
|
|
|
$this->assertEquals('Fri, 10 Oct 2014 00:00:00 GMT', $newHeaders['last-modified'][0]); |
190
|
|
|
} |
191
|
|
|
|
192
|
|
|
public function testMergePublicPrivate() |
193
|
|
|
{ |
194
|
|
|
$event = $this->buildEvent(); |
195
|
|
|
$headers = array( |
196
|
|
|
'overwrite' => false, |
197
|
|
|
'cache_control' => array( |
198
|
|
|
'private' => true, |
199
|
|
|
), ); |
200
|
|
|
$subscriber = $this->getCacheControl($headers); |
201
|
|
|
$response = $event->getResponse(); |
202
|
|
|
$response->setPublic(); |
203
|
|
|
|
204
|
|
|
$subscriber->onKernelResponse($event); |
205
|
|
|
$newHeaders = $response->headers->all(); |
206
|
|
|
|
207
|
|
|
$this->assertEquals('public', $newHeaders['cache-control'][0]); |
208
|
|
|
} |
209
|
|
|
|
210
|
|
|
/** |
211
|
|
|
* The no_cache header is never actually called as its already set. |
212
|
|
|
*/ |
213
|
|
|
public function testSetOnlyNoCacheHeader() |
214
|
|
|
{ |
215
|
|
|
$event = $this->buildEvent(); |
216
|
|
|
$headers = array( |
217
|
|
|
'overwrite' => false, |
218
|
|
|
'cache_control' => array( |
219
|
|
|
'no_cache' => true, |
220
|
|
|
), |
221
|
|
|
); |
222
|
|
|
$subscriber = $this->getCacheControl($headers); |
223
|
|
|
|
224
|
|
|
$subscriber->onKernelResponse($event); |
225
|
|
|
$newHeaders = $event->getResponse()->headers->all(); |
226
|
|
|
|
227
|
|
|
$this->assertEquals('no-cache', $newHeaders['cache-control'][0]); |
228
|
|
|
} |
229
|
|
|
|
230
|
|
|
public function testSkip() |
231
|
|
|
{ |
232
|
|
|
$event = $this->buildEvent(); |
233
|
|
|
$subscriber = $this->getMockBuilder('FOS\HttpCacheBundle\EventListener\CacheControlSubscriber') |
234
|
|
|
->setMethods(array('matchRule')) |
235
|
|
|
->getMock() |
236
|
|
|
; |
237
|
|
|
$subscriber->expects($this->never()) |
238
|
|
|
->method('matchRule') |
239
|
|
|
; |
240
|
|
|
|
241
|
|
|
$subscriber->setSkip(); |
242
|
|
|
$subscriber->onKernelResponse($event); |
243
|
|
|
$newHeaders = $event->getResponse()->headers->all(); |
244
|
|
|
|
245
|
|
|
$this->assertEquals('no-cache', $newHeaders['cache-control'][0]); |
246
|
|
|
} |
247
|
|
|
|
248
|
|
|
public function testVary() |
249
|
|
|
{ |
250
|
|
|
$event = $this->buildEvent(); |
251
|
|
|
$headers = array( |
252
|
|
|
'overwrite' => false, |
253
|
|
|
'vary' => array( |
254
|
|
|
'Cookie', |
255
|
|
|
'Accept-Language', |
256
|
|
|
'Encoding', |
257
|
|
|
), |
258
|
|
|
); |
259
|
|
|
$subscriber = $this->getCacheControl($headers); |
260
|
|
|
|
261
|
|
|
$subscriber->onKernelResponse($event); |
262
|
|
|
$newHeaders = $event->getResponse()->headers->all(); |
263
|
|
|
|
264
|
|
|
$this->assertTrue(isset($newHeaders['vary']), implode(',', array_keys($newHeaders))); |
265
|
|
|
$this->assertEquals($headers['vary'], $newHeaders['vary']); |
266
|
|
|
} |
267
|
|
|
|
268
|
|
|
public function testReverseProxyTtl() |
269
|
|
|
{ |
270
|
|
|
$event = $this->buildEvent(); |
271
|
|
|
$headers = array( |
272
|
|
|
'reverse_proxy_ttl' => 600, |
273
|
|
|
); |
274
|
|
|
$subscriber = $this->getCacheControl($headers); |
275
|
|
|
|
276
|
|
|
$subscriber->onKernelResponse($event); |
277
|
|
|
$newHeaders = $event->getResponse()->headers->all(); |
278
|
|
|
|
279
|
|
|
$this->assertTrue(isset($newHeaders['x-reverse-proxy-ttl']), implode(',', array_keys($newHeaders))); |
280
|
|
|
$this->assertEquals(600, $newHeaders['x-reverse-proxy-ttl'][0]); |
281
|
|
|
} |
282
|
|
|
|
283
|
|
|
public function testDebugHeader() |
284
|
|
|
{ |
285
|
|
|
$subscriber = \Mockery::mock('FOS\HttpCacheBundle\EventListener\CacheControlSubscriber[matchRule]', array('X-Cache-Debug')) |
286
|
|
|
->shouldAllowMockingProtectedMethods() |
287
|
|
|
->shouldReceive('matchRule')->once()->andReturn(false) |
288
|
|
|
->getMock() |
289
|
|
|
; |
290
|
|
|
$event = $this->buildEvent(); |
291
|
|
|
|
292
|
|
|
$subscriber->onKernelResponse($event); |
293
|
|
|
$newHeaders = $event->getResponse()->headers->all(); |
294
|
|
|
|
295
|
|
|
$this->assertTrue(isset($newHeaders['x-cache-debug']), implode(',', array_keys($newHeaders))); |
296
|
|
|
$this->assertTrue(isset($newHeaders['x-cache-debug'][0])); |
297
|
|
|
} |
298
|
|
|
|
299
|
|
|
public function testMatchRule() |
300
|
|
|
{ |
301
|
|
|
$event = $this->buildEvent(); |
302
|
|
|
$request = $event->getRequest(); |
303
|
|
|
$response = $event->getResponse(); |
304
|
|
|
$event2 = $this->buildEvent(); |
305
|
|
|
$request2 = $event2->getRequest(); |
306
|
|
|
$response2 = $event2->getResponse(); |
307
|
|
|
|
308
|
|
|
$headers = array( |
309
|
|
|
'overwrite' => false, |
310
|
|
|
'cache_control' => array( |
311
|
|
|
'max_age' => '900', |
312
|
|
|
's_maxage' => '300', |
313
|
|
|
'public' => true, |
314
|
|
|
'private' => false, |
315
|
|
|
), ); |
316
|
|
|
|
317
|
|
|
$mockMatcher = \Mockery::mock('FOS\HttpCacheBundle\Http\RuleMatcherInterface') |
|
|
|
|
318
|
|
|
->shouldReceive('matches')->once()->with($request, $response)->andReturn(true) |
319
|
|
|
->shouldReceive('matches')->once()->with($request2, $response2)->andReturn(false) |
320
|
|
|
->getMock() |
321
|
|
|
; |
322
|
|
|
|
323
|
|
|
$subscriber = new CacheControlSubscriber(); |
324
|
|
|
$subscriber->addRule( |
325
|
|
|
$mockMatcher, |
326
|
|
|
$headers |
327
|
|
|
); |
328
|
|
|
|
329
|
|
|
$subscriber->onKernelResponse($event); |
330
|
|
|
$newHeaders = $response->headers->all(); |
331
|
|
|
$this->assertEquals('max-age=900, public, s-maxage=300', $newHeaders['cache-control'][0]); |
332
|
|
|
|
333
|
|
|
$subscriber->onKernelResponse($event2); |
334
|
|
|
$newHeaders = $response2->headers->all(); |
335
|
|
|
$this->assertEquals('no-cache', $newHeaders['cache-control'][0]); |
336
|
|
|
} |
337
|
|
|
|
338
|
|
|
/** |
339
|
|
|
* Unsafe methods should abort before even attempting to match rules. |
340
|
|
|
*/ |
341
|
|
View Code Duplication |
public function testUnsafeMethod() |
|
|
|
|
342
|
|
|
{ |
343
|
|
|
$subscriber = $this->getMockBuilder('FOS\HttpCacheBundle\EventListener\CacheControlSubscriber') |
344
|
|
|
->setMethods(array('matchRule')) |
345
|
|
|
->getMock() |
346
|
|
|
; |
347
|
|
|
$subscriber->expects($this->never()) |
348
|
|
|
->method('matchRule') |
349
|
|
|
; |
350
|
|
|
$event = $this->buildEvent('POST'); |
351
|
|
|
|
352
|
|
|
$subscriber->onKernelResponse($event); |
353
|
|
|
} |
354
|
|
|
|
355
|
|
|
/** |
356
|
|
|
* Safe methods should get into the matchRule |
357
|
|
|
* @dataProvider getSafeMethods |
358
|
|
|
* @param $method |
359
|
|
|
*/ |
360
|
|
View Code Duplication |
public function testSafeMethodsTriggersMatchRule($method) |
|
|
|
|
361
|
|
|
{ |
362
|
|
|
$subscriber = $this->getMockBuilder('FOS\HttpCacheBundle\EventListener\CacheControlSubscriber') |
363
|
|
|
->setMethods(array('matchRule')) |
364
|
|
|
->getMock() |
365
|
|
|
; |
366
|
|
|
$subscriber->expects($this->once()) |
367
|
|
|
->method('matchRule') |
368
|
|
|
; |
369
|
|
|
$event = $this->buildEvent($method); |
370
|
|
|
|
371
|
|
|
$subscriber->onKernelResponse($event); |
372
|
|
|
} |
373
|
|
|
|
374
|
|
|
/** |
375
|
|
|
* Safe Methods data |
376
|
|
|
* @return array |
377
|
|
|
*/ |
378
|
|
|
public function getSafeMethods() |
379
|
|
|
{ |
380
|
|
|
return array( |
381
|
|
|
'testSafeMethod for GET' => array('GET'), |
382
|
|
|
'testSafeMethod for HEAD' => array('HEAD'), |
383
|
|
|
'testSafeMethod for OPTIONS' => array('OPTIONS'), |
384
|
|
|
); |
385
|
|
|
} |
386
|
|
|
|
387
|
|
|
/** |
388
|
|
|
* Build the filter response event with a mock kernel and default request |
389
|
|
|
* and response objects. |
390
|
|
|
* |
391
|
|
|
* @param string $method |
392
|
|
|
* |
393
|
|
|
* @return FilterResponseEvent |
394
|
|
|
*/ |
395
|
|
|
protected function buildEvent($method = 'GET') |
396
|
|
|
{ |
397
|
|
|
$kernel = $this->getMock('Symfony\Component\HttpKernel\HttpKernelInterface'); |
398
|
|
|
$response = new Response(); |
399
|
|
|
$request = new Request(); |
400
|
|
|
$request->setMethod($method); |
401
|
|
|
|
402
|
|
|
return new FilterResponseEvent($kernel, $request, $method, $response); |
403
|
|
|
} |
404
|
|
|
|
405
|
|
|
/** |
406
|
|
|
* We mock the matchRule method for tests about applying the rules. |
407
|
|
|
* |
408
|
|
|
* @param array $headers The headers to return in matchRule |
409
|
|
|
* |
410
|
|
|
* @return \PHPUnit_Framework_MockObject_MockObject|CacheControlSubscriber |
411
|
|
|
*/ |
412
|
|
View Code Duplication |
protected function getCacheControl(array $headers) |
|
|
|
|
413
|
|
|
{ |
414
|
|
|
$subscriber = $this->getMockBuilder('FOS\HttpCacheBundle\EventListener\CacheControlSubscriber') |
415
|
|
|
->setMethods(array('matchRule')) |
416
|
|
|
->getMock() |
417
|
|
|
; |
418
|
|
|
|
419
|
|
|
$subscriber->expects($this->once())->method('matchRule')->will($this->returnValue($headers)); |
420
|
|
|
|
421
|
|
|
return $subscriber; |
422
|
|
|
} |
423
|
|
|
} |
424
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.