[TASK] Disable a series of functional tests with mssql
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Tests / Functional / Cache / Backend / Typo3DatabaseBackendTest.php
1 <?php
2 namespace TYPO3\CMS\Core\Tests\Functional\Cache\Backend;
3
4 /*
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
17 use TYPO3\CMS\Core\Cache\Backend\Typo3DatabaseBackend;
18 use TYPO3\CMS\Core\Cache\Frontend\FrontendInterface;
19 use TYPO3\CMS\Core\Database\Connection;
20 use TYPO3\CMS\Core\Database\ConnectionPool;
21
22 /**
23 * Test case
24 */
25 class Typo3DatabaseBackendTest extends \TYPO3\TestingFramework\Core\Functional\FunctionalTestCase
26 {
27
28 /**
29 * @test
30 */
31 public function getReturnsPreviouslySetEntry()
32 {
33 $frontendProphecy = $this->prophesize(FrontendInterface::class);
34 $frontendProphecy->getIdentifier()->willReturn('cache_pages');
35
36 $subject = new Typo3DatabaseBackend('Testing');
37 $subject->setCache($frontendProphecy->reveal());
38
39 $subject->set('myIdentifier', 'myData');
40 $this->assertSame('myData', $subject->get('myIdentifier'));
41 }
42
43 /**
44 * @test
45 */
46 public function getReturnsPreviouslySetEntryWithNewContentIfSetWasCalledMultipleTimes()
47 {
48 $frontendProphecy = $this->prophesize(FrontendInterface::class);
49 $frontendProphecy->getIdentifier()->willReturn('cache_pages');
50
51 $subject = new Typo3DatabaseBackend('Testing');
52 $subject->setCache($frontendProphecy->reveal());
53
54 $subject->set('myIdentifier', 'myData');
55 $subject->set('myIdentifier', 'myNewData');
56 $this->assertSame('myNewData', $subject->get('myIdentifier'));
57 }
58
59 /**
60 * @test
61 */
62 public function setInsertsDataWithTagsIntoCacheTable()
63 {
64 $frontendProphecy = $this->prophesize(FrontendInterface::class);
65 $frontendProphecy->getIdentifier()->willReturn('cache_pages');
66
67 $subject = new Typo3DatabaseBackend('Testing');
68 $subject->setCache($frontendProphecy->reveal());
69
70 $subject->set('myIdentifier', 'myData', ['aTag', 'anotherTag']);
71
72 $cacheTableConnection = (new ConnectionPool())->getConnectionForTable('cf_cache_pages');
73 $tagsTableConnection = (new ConnectionPool())->getConnectionForTable('cf_cache_pages_tags');
74 $this->assertSame(1, $cacheTableConnection->count('*', 'cf_cache_pages', ['identifier' => 'myIdentifier']));
75 $this->assertSame(1, $tagsTableConnection->count('*', 'cf_cache_pages_tags', ['identifier' => 'myIdentifier', 'tag' => 'aTag']));
76 $this->assertSame(1, $tagsTableConnection->count('*', 'cf_cache_pages_tags', ['identifier' => 'myIdentifier', 'tag' => 'anotherTag']));
77 }
78
79 /**
80 * @test
81 */
82 public function setStoresCompressedContent()
83 {
84 $frontendProphecy = $this->prophesize(FrontendInterface::class);
85 $frontendProphecy->getIdentifier()->willReturn('cache_pages');
86
87 // Have backend with compression enabled
88 $subject = new Typo3DatabaseBackend('Testing', ['compression' => true]);
89 $subject->setCache($frontendProphecy->reveal());
90
91 $subject->set('myIdentifier', 'myCachedContent');
92
93 $row = (new ConnectionPool())
94 ->getConnectionForTable('cf_cache_pages')
95 ->select(
96 ['content'],
97 'cf_cache_pages',
98 ['identifier' => 'myIdentifier']
99 )
100 ->fetch();
101
102 // Content comes back uncompressed
103 $this->assertSame('myCachedContent', gzuncompress($row['content']));
104 }
105
106 /**
107 * @test
108 */
109 public function getReturnsFalseIfNoCacheEntryExists()
110 {
111 $frontendProphecy = $this->prophesize(FrontendInterface::class);
112 $frontendProphecy->getIdentifier()->willReturn('cache_pages');
113
114 $subject = new Typo3DatabaseBackend('Testing');
115 $subject->setCache($frontendProphecy->reveal());
116
117 $this->assertFalse($subject->get('myIdentifier'));
118 }
119
120 /**
121 * @test
122 */
123 public function getReturnsFalseForExpiredCacheEntry()
124 {
125 $frontendProphecy = $this->prophesize(FrontendInterface::class);
126 $frontendProphecy->getIdentifier()->willReturn('cache_pages');
127
128 // Push an expired row into db
129 (new ConnectionPool())->getConnectionForTable('cf_cache_pages')->insert(
130 'cf_cache_pages',
131 [
132 'identifier' => 'myIdentifier',
133 'expires' => $GLOBALS['EXEC_TIME'] - 60,
134 'content' => 'myCachedContent',
135 ],
136 [
137 'content' => Connection::PARAM_LOB
138 ]
139 );
140
141 $subject = new Typo3DatabaseBackend('Testing');
142 $subject->setCache($frontendProphecy->reveal());
143
144 $this->assertFalse($subject->get('myIdentifier'));
145 }
146
147 /**
148 * @test
149 */
150 public function getReturnsNotExpiredCacheEntry()
151 {
152 $frontendProphecy = $this->prophesize(FrontendInterface::class);
153 $frontendProphecy->getIdentifier()->willReturn('cache_pages');
154
155 // Push a row into db
156 (new ConnectionPool())->getConnectionForTable('cf_cache_pages')->insert(
157 'cf_cache_pages',
158 [
159 'identifier' => 'myIdentifier',
160 'expires' => $GLOBALS['EXEC_TIME'] + 60,
161 'content' => 'myCachedContent',
162 ],
163 [
164 'content' => Connection::PARAM_LOB
165 ]
166 );
167
168 $subject = new Typo3DatabaseBackend('Testing');
169 $subject->setCache($frontendProphecy->reveal());
170
171 $this->assertSame('myCachedContent', $subject->get('myIdentifier'));
172 }
173
174 /**
175 * @test
176 */
177 public function getReturnsUnzipsNotExpiredCacheEntry()
178 {
179 $frontendProphecy = $this->prophesize(FrontendInterface::class);
180 $frontendProphecy->getIdentifier()->willReturn('cache_pages');
181
182 // Push a compressed row into db
183 (new ConnectionPool())->getConnectionForTable('cf_cache_pages')->insert(
184 'cf_cache_pages',
185 [
186 'identifier' => 'myIdentifier',
187 'expires' => $GLOBALS['EXEC_TIME'] + 60,
188 'content' => gzcompress('myCachedContent'),
189 ],
190 [
191 'content' => Connection::PARAM_LOB
192 ]
193 );
194
195 // Have backend with compression enabled
196 $subject = new Typo3DatabaseBackend('Testing', ['compression' => true]);
197 $subject->setCache($frontendProphecy->reveal());
198
199 // Content comes back uncompressed
200 $this->assertSame('myCachedContent', $subject->get('myIdentifier'));
201 }
202
203 /**
204 * @test
205 */
206 public function getReturnsEmptyStringUnzipped()
207 {
208 $frontendProphecy = $this->prophesize(FrontendInterface::class);
209 $frontendProphecy->getIdentifier()->willReturn('cache_pages');
210
211 // Push a compressed row into db
212 (new ConnectionPool())->getConnectionForTable('cf_cache_pages')->insert(
213 'cf_cache_pages',
214 [
215 'identifier' => 'myIdentifier',
216 'expires' => $GLOBALS['EXEC_TIME'] + 60,
217 'content' => gzcompress(''),
218 ],
219 [
220 'content' => Connection::PARAM_LOB
221 ]
222 );
223
224 // Have backend with compression enabled
225 $subject = new Typo3DatabaseBackend('Testing', ['compression' => true]);
226 $subject->setCache($frontendProphecy->reveal());
227
228 // Content comes back uncompressed
229 $this->assertSame('', $subject->get('myIdentifier'));
230 }
231
232 /**
233 * @test
234 */
235 public function hasReturnsFalseIfNoCacheEntryExists()
236 {
237 $frontendProphecy = $this->prophesize(FrontendInterface::class);
238 $frontendProphecy->getIdentifier()->willReturn('cache_pages');
239
240 $subject = new Typo3DatabaseBackend('Testing');
241 $subject->setCache($frontendProphecy->reveal());
242
243 $this->assertFalse($subject->has('myIdentifier'));
244 }
245
246 /**
247 * @test
248 */
249 public function hasReturnsFalseForExpiredCacheEntry()
250 {
251 $frontendProphecy = $this->prophesize(FrontendInterface::class);
252 $frontendProphecy->getIdentifier()->willReturn('cache_pages');
253
254 // Push an expired row into db
255 (new ConnectionPool())->getConnectionForTable('cf_cache_pages')->insert(
256 'cf_cache_pages',
257 [
258 'identifier' => 'myIdentifier',
259 'expires' => $GLOBALS['EXEC_TIME'] - 60,
260 'content' => 'myCachedContent',
261 ],
262 [
263 'content' => Connection::PARAM_LOB
264 ]
265 );
266
267 $subject = new Typo3DatabaseBackend('Testing');
268 $subject->setCache($frontendProphecy->reveal());
269
270 $this->assertFalse($subject->has('myIdentifier'));
271 }
272
273 /**
274 * @test
275 */
276 public function hasReturnsNotExpiredCacheEntry()
277 {
278 $frontendProphecy = $this->prophesize(FrontendInterface::class);
279 $frontendProphecy->getIdentifier()->willReturn('cache_pages');
280
281 // Push a row into db
282 (new ConnectionPool())->getConnectionForTable('cf_cache_pages')->insert(
283 'cf_cache_pages',
284 [
285 'identifier' => 'myIdentifier',
286 'expires' => $GLOBALS['EXEC_TIME'] + 60,
287 'content' => 'myCachedContent',
288 ],
289 [
290 'content' => Connection::PARAM_LOB
291 ]
292 );
293
294 $subject = new Typo3DatabaseBackend('Testing');
295 $subject->setCache($frontendProphecy->reveal());
296
297 $this->assertTrue($subject->has('myIdentifier'));
298 }
299
300 /**
301 * @test
302 */
303 public function removeReturnsFalseIfNoEntryHasBeenRemoved()
304 {
305 $frontendProphecy = $this->prophesize(FrontendInterface::class);
306 $frontendProphecy->getIdentifier()->willReturn('cache_pages');
307
308 $subject = new Typo3DatabaseBackend('Testing');
309 $subject->setCache($frontendProphecy->reveal());
310
311 $this->assertFalse($subject->remove('myIdentifier'));
312 }
313
314 /**
315 * @test
316 */
317 public function removeReturnsTrueIfAnEntryHasBeenRemoved()
318 {
319 $frontendProphecy = $this->prophesize(FrontendInterface::class);
320 $frontendProphecy->getIdentifier()->willReturn('cache_pages');
321
322 // Push a row into db
323 (new ConnectionPool())->getConnectionForTable('cf_cache_pages')->insert(
324 'cf_cache_pages',
325 [
326 'identifier' => 'myIdentifier',
327 'expires' => $GLOBALS['EXEC_TIME'] + 60,
328 'content' => 'myCachedContent',
329 ],
330 [
331 'content' => Connection::PARAM_LOB
332 ]
333 );
334
335 $subject = new Typo3DatabaseBackend('Testing');
336 $subject->setCache($frontendProphecy->reveal());
337
338 $this->assertTrue($subject->remove('myIdentifier'));
339 }
340
341 /**
342 * @test
343 */
344 public function removeRemovesCorrectEntriesFromDatabase()
345 {
346 $frontendProphecy = $this->prophesize(FrontendInterface::class);
347 $frontendProphecy->getIdentifier()->willReturn('cache_pages');
348
349 // Add one cache row to remove and another one that shouldn't be removed
350 $cacheTableConnection = (new ConnectionPool())->getConnectionForTable('cf_cache_pages');
351 $cacheTableConnection->bulkInsert(
352 'cf_cache_pages',
353 [
354 ['myIdentifier', $GLOBALS['EXEC_TIME'] + 60, 'myCachedContent'],
355 ['otherIdentifier', $GLOBALS['EXEC_TIME'] + 60, 'otherCachedContent'],
356 ],
357 ['identifier', 'expires', 'content'],
358 [
359 'content' => Connection::PARAM_LOB
360 ]
361 );
362 $subject = new Typo3DatabaseBackend('Testing');
363 $subject->setCache($frontendProphecy->reveal());
364
365 // Add a couple of tags
366 $tagsTableConnection = (new ConnectionPool())->getConnectionForTable('cf_cache_pages_tags');
367 $tagsTableConnection->bulkInsert(
368 'cf_cache_pages_tags',
369 [
370 ['myIdentifier', 'aTag'],
371 ['myIdentifier', 'otherTag'],
372 ['otherIdentifier', 'aTag'],
373 ['otherIdentifier', 'otherTag'],
374 ],
375 ['identifier', 'tag']
376 );
377
378 $subject->remove('myIdentifier');
379
380 // cache row with removed identifier has been removed, other one exists
381 $this->assertSame(0, $cacheTableConnection->count('*', 'cf_cache_pages', ['identifier' => 'myIdentifier']));
382 $this->assertSame(1, $cacheTableConnection->count('*', 'cf_cache_pages', ['identifier' => 'otherIdentifier']));
383
384 // tags of myIdentifier should have been removed, others exist
385 $this->assertSame(0, $tagsTableConnection->count('*', 'cf_cache_pages_tags', ['identifier' => 'myIdentifier']));
386 $this->assertSame(2, $tagsTableConnection->count('*', 'cf_cache_pages_tags', ['identifier' => 'otherIdentifier']));
387 }
388
389 /**
390 * @test
391 */
392 public function findIdentifiersByTagReturnsIdentifierTaggedWithGivenTag()
393 {
394 $subject = $this->getSubjectObject();
395
396 $this->assertEquals(['idA' => 'idA'], $subject->findIdentifiersByTag('tagA'));
397 $this->assertEquals(['idA' => 'idA', 'idB' => 'idB'], $subject->findIdentifiersByTag('tagB'));
398 $this->assertEquals(['idB' => 'idB', 'idC' => 'idC'], $subject->findIdentifiersByTag('tagC'));
399 }
400
401 /**
402 * @test
403 *
404 * @group not-postgres
405 * @group not-mssql
406 */
407 public function flushByTagWorksWithEmptyCacheTablesWithMysql()
408 {
409 $subject = $this->getSubjectObject(true);
410 $subject->flushByTag('tagB');
411 }
412
413 /**
414 * @test
415 *
416 * @group not-postgres
417 * @group not-mssql
418 */
419 public function flushByTagsWorksWithEmptyCacheTablesWithMysql()
420 {
421 $subject = $this->getSubjectObject(true);
422 $subject->flushByTags(['tagB']);
423 }
424
425 /**
426 * @test
427 *
428 * @group not-postgres
429 * @group not-mssql
430 */
431 public function flushByTagRemovesCorrectRowsFromDatabaseWithMysql()
432 {
433 $subject = $this->getSubjectObject(true);
434 $subject->flushByTag('tagB');
435
436 $cacheTableConnection = (new ConnectionPool())->getConnectionForTable('cf_cache_pages');
437 $this->assertSame(0, $cacheTableConnection->count('*', 'cf_cache_pages', ['identifier' => 'idA']));
438 $this->assertSame(0, $cacheTableConnection->count('*', 'cf_cache_pages', ['identifier' => 'idB']));
439 $this->assertSame(1, $cacheTableConnection->count('*', 'cf_cache_pages', ['identifier' => 'idC']));
440 $tagsTableConnection = (new ConnectionPool())->getConnectionForTable('cf_cache_pages_tags');
441 $this->assertSame(0, $tagsTableConnection->count('*', 'cf_cache_pages_tags', ['identifier' => 'idA']));
442 $this->assertSame(0, $tagsTableConnection->count('*', 'cf_cache_pages_tags', ['identifier' => 'idB']));
443 $this->assertSame(2, $tagsTableConnection->count('*', 'cf_cache_pages_tags', ['identifier' => 'idC']));
444 }
445
446 /**
447 * @test
448 *
449 * @group not-postgres
450 * @group not-mssql
451 */
452 public function flushByTagsRemovesCorrectRowsFromDatabaseWithMysql()
453 {
454 $subject = $this->getSubjectObject(true);
455 $subject->flushByTags(['tagC', 'tagD']);
456
457 $cacheTableConnection = (new ConnectionPool())->getConnectionForTable('cf_cache_pages');
458 $this->assertSame(1, $cacheTableConnection->count('*', 'cf_cache_pages', ['identifier' => 'idA']));
459 $this->assertSame(0, $cacheTableConnection->count('*', 'cf_cache_pages', ['identifier' => 'idB']));
460 $this->assertSame(0, $cacheTableConnection->count('*', 'cf_cache_pages', ['identifier' => 'idC']));
461 $tagsTableConnection = (new ConnectionPool())->getConnectionForTable('cf_cache_pages_tags');
462 $this->assertSame(2, $tagsTableConnection->count('*', 'cf_cache_pages_tags', ['identifier' => 'idA']));
463 $this->assertSame(0, $tagsTableConnection->count('*', 'cf_cache_pages_tags', ['identifier' => 'idB']));
464 $this->assertSame(0, $tagsTableConnection->count('*', 'cf_cache_pages_tags', ['identifier' => 'idC']));
465 }
466
467 /**
468 * @test
469 */
470 public function flushByTagWorksWithEmptyCacheTablesWithNonMysql()
471 {
472 $subject = $this->getSubjectObject(true, false);
473 $subject->flushByTag('tagB');
474 }
475
476 /**
477 * @test
478 */
479 public function flushByTagsWorksWithEmptyCacheTablesWithNonMysql()
480 {
481 $subject = $this->getSubjectObject(true, false);
482 $subject->flushByTags(['tagB', 'tagC']);
483 }
484
485 /**
486 * @test
487 */
488 public function flushByTagRemovesCorrectRowsFromDatabaseWithNonMysql()
489 {
490 $subject = $this->getSubjectObject(true, false);
491 $subject->flushByTag('tagB');
492
493 $cacheTableConnection = (new ConnectionPool())->getConnectionForTable('cf_cache_pages');
494 $this->assertSame(0, $cacheTableConnection->count('*', 'cf_cache_pages', ['identifier' => 'idA']));
495 $this->assertSame(0, $cacheTableConnection->count('*', 'cf_cache_pages', ['identifier' => 'idB']));
496 $this->assertSame(1, $cacheTableConnection->count('*', 'cf_cache_pages', ['identifier' => 'idC']));
497 $tagsTableConnection = (new ConnectionPool())->getConnectionForTable('cf_cache_pages_tags');
498 $this->assertSame(0, $tagsTableConnection->count('*', 'cf_cache_pages_tags', ['identifier' => 'idA']));
499 $this->assertSame(0, $tagsTableConnection->count('*', 'cf_cache_pages_tags', ['identifier' => 'idB']));
500 $this->assertSame(2, $tagsTableConnection->count('*', 'cf_cache_pages_tags', ['identifier' => 'idC']));
501 }
502
503 /**
504 * @test
505 */
506 public function flushByTagsRemovesCorrectRowsFromDatabaseWithNonMysql()
507 {
508 $subject = $this->getSubjectObject(true, false);
509 $subject->flushByTags(['tagC', 'tagD']);
510
511 $cacheTableConnection = (new ConnectionPool())->getConnectionForTable('cf_cache_pages');
512 $this->assertSame(1, $cacheTableConnection->count('*', 'cf_cache_pages', ['identifier' => 'idA']));
513 $this->assertSame(0, $cacheTableConnection->count('*', 'cf_cache_pages', ['identifier' => 'idB']));
514 $this->assertSame(0, $cacheTableConnection->count('*', 'cf_cache_pages', ['identifier' => 'idC']));
515 $tagsTableConnection = (new ConnectionPool())->getConnectionForTable('cf_cache_pages_tags');
516 $this->assertSame(2, $tagsTableConnection->count('*', 'cf_cache_pages_tags', ['identifier' => 'idA']));
517 $this->assertSame(0, $tagsTableConnection->count('*', 'cf_cache_pages_tags', ['identifier' => 'idB']));
518 $this->assertSame(0, $tagsTableConnection->count('*', 'cf_cache_pages_tags', ['identifier' => 'idC']));
519 }
520
521 /**
522 * @test
523 *
524 * @group not-postgres
525 * @group not-mssql
526 */
527 public function collectGarbageWorksWithEmptyTableWithMysql()
528 {
529 $subject = $this->getSubjectObject(true);
530 $subject->collectGarbage();
531 }
532
533 /**
534 * @test
535 *
536 * @group not-postgres
537 * @group not-mssql
538 */
539 public function collectGarbageRemovesCacheEntryWithExpiredLifetimeWithMysql()
540 {
541 $frontendProphecy = $this->prophesize(FrontendInterface::class);
542 $frontendProphecy->getIdentifier()->willReturn('cache_pages');
543
544 // Must be mocked here to test for "mysql" version implementation
545 $subject = $this->getMockBuilder(Typo3DatabaseBackend::class)
546 ->setMethods(['isConnectionMysql'])
547 ->setConstructorArgs(['Testing'])
548 ->getMock();
549 $subject->expects($this->once())->method('isConnectionMysql')->willReturn(true);
550 $subject->setCache($frontendProphecy->reveal());
551
552 // idA should be expired after EXEC_TIME manipulation, idB should stay
553 $subject->set('idA', 'dataA', [], 60);
554 $subject->set('idB', 'dataB', [], 240);
555
556 $GLOBALS['EXEC_TIME'] = $GLOBALS['EXEC_TIME'] + 120;
557
558 $subject->collectGarbage();
559
560 $cacheTableConnection = (new ConnectionPool())->getConnectionForTable('cf_cache_pages');
561 $this->assertSame(0, $cacheTableConnection->count('*', 'cf_cache_pages', ['identifier' => 'idA']));
562 $this->assertSame(1, $cacheTableConnection->count('*', 'cf_cache_pages', ['identifier' => 'idB']));
563 }
564
565 /**
566 * @test
567 *
568 * @group not-postgres
569 * @group not-mssql
570 */
571 public function collectGarbageRemovesTagEntriesForCacheEntriesWithExpiredLifetimeWithMysql()
572 {
573 $frontendProphecy = $this->prophesize(FrontendInterface::class);
574 $frontendProphecy->getIdentifier()->willReturn('cache_pages');
575
576 // Must be mocked here to test for "mysql" version implementation
577 $subject = $this->getMockBuilder(Typo3DatabaseBackend::class)
578 ->setMethods(['isConnectionMysql'])
579 ->setConstructorArgs(['Testing'])
580 ->getMock();
581 $subject->expects($this->once())->method('isConnectionMysql')->willReturn(true);
582 $subject->setCache($frontendProphecy->reveal());
583
584 // tag rows tagA and tagB should be removed by garbage collector after EXEC_TIME manipulation
585 $subject->set('idA', 'dataA', ['tagA', 'tagB'], 60);
586 $subject->set('idB', 'dataB', ['tagB', 'tagC'], 240);
587
588 $GLOBALS['EXEC_TIME'] = $GLOBALS['EXEC_TIME'] + 120;
589
590 $subject->collectGarbage();
591
592 $tagsTableConnection = (new ConnectionPool())->getConnectionForTable('cf_cache_pages_tags');
593 $this->assertSame(0, $tagsTableConnection->count('*', 'cf_cache_pages_tags', ['identifier' => 'idA']));
594 $this->assertSame(2, $tagsTableConnection->count('*', 'cf_cache_pages_tags', ['identifier' => 'idB']));
595 }
596
597 /**
598 * @test
599 *
600 * @group not-postgres
601 * @group not-mssql
602 */
603 public function collectGarbageRemovesOrphanedTagEntriesFromTagsTableWithMysql()
604 {
605 $frontendProphecy = $this->prophesize(FrontendInterface::class);
606 $frontendProphecy->getIdentifier()->willReturn('cache_pages');
607
608 // Must be mocked here to test for "mysql" version implementation
609 $subject = $this->getMockBuilder(Typo3DatabaseBackend::class)
610 ->setMethods(['isConnectionMysql'])
611 ->setConstructorArgs(['Testing'])
612 ->getMock();
613 $subject->expects($this->once())->method('isConnectionMysql')->willReturn(true);
614 $subject->setCache($frontendProphecy->reveal());
615
616 // tag rows tagA and tagB should be removed by garbage collector after EXEC_TIME manipulation
617 $subject->set('idA', 'dataA', ['tagA', 'tagB'], 60);
618 $subject->set('idB', 'dataB', ['tagB', 'tagC'], 240);
619
620 $tagsTableConnection = (new ConnectionPool())->getConnectionForTable('cf_cache_pages_tags');
621
622 // Push two orphaned tag row into db - tags that have no related cache record anymore for whatever reason
623 $tagsTableConnection->insert(
624 'cf_cache_pages_tags',
625 [
626 'identifier' => 'idC',
627 'tag' => 'tagC'
628 ]
629 );
630 $tagsTableConnection->insert(
631 'cf_cache_pages_tags',
632 [
633 'identifier' => 'idC',
634 'tag' => 'tagD'
635 ]
636 );
637
638 $GLOBALS['EXEC_TIME'] = $GLOBALS['EXEC_TIME'] + 120;
639
640 $subject->collectGarbage();
641
642 $this->assertSame(0, $tagsTableConnection->count('*', 'cf_cache_pages_tags', ['identifier' => 'idA']));
643 $this->assertSame(2, $tagsTableConnection->count('*', 'cf_cache_pages_tags', ['identifier' => 'idB']));
644 $this->assertSame(0, $tagsTableConnection->count('*', 'cf_cache_pages_tags', ['identifier' => 'idC']));
645 }
646
647 /**
648 * @test
649 */
650 public function collectGarbageWorksWithEmptyTableWithNonMysql()
651 {
652 $frontendProphecy = $this->prophesize(FrontendInterface::class);
653 $frontendProphecy->getIdentifier()->willReturn('cache_pages');
654
655 // Must be mocked here to test for "mysql" version implementation
656 $subject = $this->getMockBuilder(Typo3DatabaseBackend::class)
657 ->setMethods(['isConnectionMysql'])
658 ->setConstructorArgs(['Testing'])
659 ->getMock();
660 $subject->expects($this->once())->method('isConnectionMysql')->willReturn(false);
661 $subject->setCache($frontendProphecy->reveal());
662
663 $subject->collectGarbage();
664 }
665
666 /**
667 * @test
668 */
669 public function collectGarbageRemovesCacheEntryWithExpiredLifetimeWithNonMysql()
670 {
671 $frontendProphecy = $this->prophesize(FrontendInterface::class);
672 $frontendProphecy->getIdentifier()->willReturn('cache_pages');
673
674 // Must be mocked here to test for "mysql" version implementation
675 $subject = $this->getMockBuilder(Typo3DatabaseBackend::class)
676 ->setMethods(['isConnectionMysql'])
677 ->setConstructorArgs(['Testing'])
678 ->getMock();
679 $subject->expects($this->once())->method('isConnectionMysql')->willReturn(false);
680 $subject->setCache($frontendProphecy->reveal());
681
682 // idA should be expired after EXEC_TIME manipulation, idB should stay
683 $subject->set('idA', 'dataA', [], 60);
684 $subject->set('idB', 'dataB', [], 240);
685
686 $GLOBALS['EXEC_TIME'] = $GLOBALS['EXEC_TIME'] + 120;
687
688 $subject->collectGarbage();
689
690 $cacheTableConnection = (new ConnectionPool())->getConnectionForTable('cf_cache_pages');
691 $this->assertSame(0, $cacheTableConnection->count('*', 'cf_cache_pages', ['identifier' => 'idA']));
692 $this->assertSame(1, $cacheTableConnection->count('*', 'cf_cache_pages', ['identifier' => 'idB']));
693 }
694
695 /**
696 * @test
697 */
698 public function collectGarbageRemovesTagEntriesForCacheEntriesWithExpiredLifetimeWithNonMysql()
699 {
700 $frontendProphecy = $this->prophesize(FrontendInterface::class);
701 $frontendProphecy->getIdentifier()->willReturn('cache_pages');
702
703 // Must be mocked here to test for "mysql" version implementation
704 $subject = $this->getMockBuilder(Typo3DatabaseBackend::class)
705 ->setMethods(['isConnectionMysql'])
706 ->setConstructorArgs(['Testing'])
707 ->getMock();
708 $subject->expects($this->once())->method('isConnectionMysql')->willReturn(false);
709 $subject->setCache($frontendProphecy->reveal());
710
711 // tag rows tagA and tagB should be removed by garbage collector after EXEC_TIME manipulation
712 $subject->set('idA', 'dataA', ['tagA', 'tagB'], 60);
713 $subject->set('idB', 'dataB', ['tagB', 'tagC'], 240);
714
715 $GLOBALS['EXEC_TIME'] = $GLOBALS['EXEC_TIME'] + 120;
716
717 $subject->collectGarbage();
718
719 $tagsTableConnection = (new ConnectionPool())->getConnectionForTable('cf_cache_pages_tags');
720 $this->assertSame(0, $tagsTableConnection->count('*', 'cf_cache_pages_tags', ['identifier' => 'idA']));
721 $this->assertSame(2, $tagsTableConnection->count('*', 'cf_cache_pages_tags', ['identifier' => 'idB']));
722 }
723
724 /**
725 * @test
726 */
727 public function collectGarbageRemovesOrphanedTagEntriesFromTagsTableWithNonMysql()
728 {
729 $frontendProphecy = $this->prophesize(FrontendInterface::class);
730 $frontendProphecy->getIdentifier()->willReturn('cache_pages');
731
732 // Must be mocked here to test for "mysql" version implementation
733 $subject = $this->getMockBuilder(Typo3DatabaseBackend::class)
734 ->setMethods(['isConnectionMysql'])
735 ->setConstructorArgs(['Testing'])
736 ->getMock();
737 $subject->expects($this->once())->method('isConnectionMysql')->willReturn(false);
738 $subject->setCache($frontendProphecy->reveal());
739
740 // tag rows tagA and tagB should be removed by garbage collector after EXEC_TIME manipulation
741 $subject->set('idA', 'dataA', ['tagA', 'tagB'], 60);
742 $subject->set('idB', 'dataB', ['tagB', 'tagC'], 240);
743
744 $tagsTableConnection = (new ConnectionPool())->getConnectionForTable('cf_cache_pages_tags');
745
746 // Push two orphaned tag row into db - tags that have no related cache record anymore for whatever reason
747 $tagsTableConnection->insert(
748 'cf_cache_pages_tags',
749 [
750 'identifier' => 'idC',
751 'tag' => 'tagC'
752 ]
753 );
754 $tagsTableConnection->insert(
755 'cf_cache_pages_tags',
756 [
757 'identifier' => 'idC',
758 'tag' => 'tagD'
759 ]
760 );
761
762 $GLOBALS['EXEC_TIME'] = $GLOBALS['EXEC_TIME'] + 120;
763
764 $subject->collectGarbage();
765
766 $this->assertSame(0, $tagsTableConnection->count('*', 'cf_cache_pages_tags', ['identifier' => 'idA']));
767 $this->assertSame(2, $tagsTableConnection->count('*', 'cf_cache_pages_tags', ['identifier' => 'idB']));
768 $this->assertSame(0, $tagsTableConnection->count('*', 'cf_cache_pages_tags', ['identifier' => 'idC']));
769 }
770
771 /**
772 * @test
773 */
774 public function flushLeavesCacheAndTagsTableEmpty()
775 {
776 $frontendProphecy = $this->prophesize(FrontendInterface::class);
777 $frontendProphecy->getIdentifier()->willReturn('cache_pages');
778
779 $subject = new Typo3DatabaseBackend('Testing');
780 $subject->setCache($frontendProphecy->reveal());
781
782 $subject->set('idA', 'dataA', ['tagA', 'tagB']);
783
784 $subject->flush();
785
786 $cacheTableConnection = (new ConnectionPool())->getConnectionForTable('cf_cache_pages');
787 $tagsTableConnection = (new ConnectionPool())->getConnectionForTable('cf_cache_pages_tags');
788 $this->assertSame(0, $cacheTableConnection->count('*', 'cf_cache_pages', []));
789 $this->assertSame(0, $tagsTableConnection->count('*', 'cf_cache_pages_tags', []));
790 }
791
792 /**
793 * @param bool $returnMockObject
794 * @param bool $isConnectionMysql
795 *
796 * @return Typo3DatabaseBackend
797 */
798 protected function getSubjectObject($returnMockObject = false, $isConnectionMysql = true)
799 {
800 $frontendProphecy = $this->prophesize(FrontendInterface::class);
801 $frontendProphecy->getIdentifier()->willReturn('cache_pages');
802
803 if (!$returnMockObject) {
804 $subject = new Typo3DatabaseBackend('Testing');
805 } else {
806 $subject = $this->getMockBuilder(Typo3DatabaseBackend::class)
807 ->setMethods(['isConnectionMysql'])
808 ->setConstructorArgs(['Testing'])
809 ->getMock();
810 $subject->expects($this->once())->method('isConnectionMysql')->willReturn($isConnectionMysql);
811 }
812 $subject->setCache($frontendProphecy->reveal());
813
814 $subject->set('idA', 'dataA', ['tagA', 'tagB']);
815 $subject->set('idB', 'dataB', ['tagB', 'tagC']);
816 $subject->set('idC', 'dataC', ['tagC', 'tagD']);
817
818 return $subject;
819 }
820 }