[BUGFIX] Add missing namespace parts
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Tests / Unit / Cache / Backend / RedisBackendTest.php
1 <?php
2 namespace TYPO3\CMS\Core\Tests\Unit\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 use TYPO3\CMS\Core\Cache\Exception\InvalidDataException;
17
18 /**
19 * Testcase for the cache to redis backend
20 *
21 * This class has functional tests as well as implementation tests:
22 * - The functional tests make API calls to the backend and check expected behaviour
23 * - The implementation tests make additional calls with an own redis instance to
24 * check stored data structures in the redis server, which can not be checked
25 * by functional tests alone. Those tests will fail if any changes
26 * to the internal data structure are done.
27 *
28 * Warning:
29 * The unit tests use and flush redis database numbers 0 and 1!
30 */
31 class RedisBackendTest extends \TYPO3\Components\TestingFramework\Core\Unit\UnitTestCase
32 {
33 /**
34 * If set, the tearDown() method will flush the cache used by this unit test.
35 *
36 * @var \TYPO3\CMS\Core\Cache\Backend\RedisBackend
37 */
38 protected $backend = null;
39
40 /**
41 * Own redis instance used in implementation tests
42 *
43 * @var Redis
44 */
45 protected $redis = null;
46
47 /**
48 * Set up this testcase
49 */
50 protected function setUp()
51 {
52 if (!extension_loaded('redis')) {
53 $this->markTestSkipped('redis extension was not available');
54 }
55 try {
56 if (!@fsockopen('127.0.0.1', 6379)) {
57 $this->markTestSkipped('redis server not reachable');
58 }
59 } catch (\Exception $e) {
60 $this->markTestSkipped('redis server not reachable');
61 }
62 }
63
64 /**
65 * Sets up the redis backend used for testing
66 *
67 * @param array $backendOptions Options for the redis backend
68 */
69 protected function setUpBackend(array $backendOptions = [])
70 {
71 $mockCache = $this->createMock(\TYPO3\CMS\Core\Cache\Frontend\FrontendInterface::class);
72 $mockCache->expects($this->any())->method('getIdentifier')->will($this->returnValue('TestCache'));
73 $this->backend = new \TYPO3\CMS\Core\Cache\Backend\RedisBackend('Testing', $backendOptions);
74 $this->backend->setCache($mockCache);
75 $this->backend->initializeObject();
76 }
77
78 /**
79 * Sets up an own redis instance for implementation tests
80 */
81 protected function setUpRedis()
82 {
83 $this->redis = new \Redis();
84 $this->redis->connect('127.0.0.1', 6379);
85 }
86
87 /**
88 * Tear down this testcase
89 */
90 protected function tearDown()
91 {
92 if ($this->backend instanceof \TYPO3\CMS\Core\Cache\Backend\RedisBackend) {
93 $this->backend->flush();
94 }
95 parent::tearDown();
96 }
97
98 /**
99 * @test Functional
100 */
101 public function initializeObjectThrowsNoExceptionIfGivenDatabaseWasSuccessfullySelected()
102 {
103 try {
104 $this->setUpBackend(['database' => 1]);
105 } catch (Exception $e) {
106 $this->assertTrue();
107 }
108 }
109
110 /**
111 * @test Functional
112 */
113 public function setDatabaseThrowsExceptionIfGivenDatabaseNumberIsNotAnInteger()
114 {
115 $this->expectException(\InvalidArgumentException::class);
116 $this->expectExceptionCode(1279763057);
117
118 $this->setUpBackend(['database' => 'foo']);
119 }
120
121 /**
122 * @test Functional
123 */
124 public function setDatabaseThrowsExceptionIfGivenDatabaseNumberIsNegative()
125 {
126 $this->expectException(\InvalidArgumentException::class);
127 $this->expectExceptionCode(1279763534);
128
129 $this->setUpBackend(['database' => -1]);
130 }
131
132 /**
133 * @test Functional
134 */
135 public function setCompressionThrowsExceptionIfCompressionParameterIsNotOfTypeBoolean()
136 {
137 $this->expectException(\InvalidArgumentException::class);
138 $this->expectExceptionCode(1289679153);
139
140 $this->setUpBackend(['compression' => 'foo']);
141 }
142
143 /**
144 * @test Functional
145 */
146 public function setCompressionLevelThrowsExceptionIfCompressionLevelIsNotInteger()
147 {
148 $this->expectException(\InvalidArgumentException::class);
149 $this->expectExceptionCode(1289679154);
150
151 $this->setUpBackend(['compressionLevel' => 'foo']);
152 }
153
154 /**
155 * @test Functional
156 */
157 public function setCompressionLevelThrowsExceptionIfCompressionLevelIsNotBetweenMinusOneAndNine()
158 {
159 $this->expectException(\InvalidArgumentException::class);
160 $this->expectExceptionCode(1289679155);
161
162 $this->setUpBackend(['compressionLevel' => 11]);
163 }
164
165 /**
166 * @test Functional
167 */
168 public function setThrowsExceptionIfIdentifierIsNotAString()
169 {
170 $this->expectException(\InvalidArgumentException::class);
171 $this->expectExceptionCode(1377006651);
172
173 $this->setUpBackend();
174 $this->backend->set([], 'data');
175 }
176
177 /**
178 * @test Functional
179 */
180 public function setThrowsExceptionIfDataIsNotAString()
181 {
182 $this->expectException(InvalidDataException::class);
183 $this->expectExceptionCode(1279469941);
184
185 $this->setUpBackend();
186 $this->backend->set($this->getUniqueId('identifier'), []);
187 }
188
189 /**
190 * @test Functional
191 */
192 public function setThrowsExceptionIfLifetimeIsNegative()
193 {
194 $this->expectException(\InvalidArgumentException::class);
195 $this->expectExceptionCode(1279487573);
196
197 $this->setUpBackend();
198 $this->backend->set($this->getUniqueId('identifier'), 'data', [], -42);
199 }
200
201 /**
202 * @test Functional
203 */
204 public function setThrowsExceptionIfLifetimeIsNotNullOrAnInteger()
205 {
206 $this->expectException(\InvalidArgumentException::class);
207 $this->expectExceptionCode(1279488008);
208
209 $this->setUpBackend();
210 $this->backend->set($this->getUniqueId('identifier'), 'data', [], []);
211 }
212
213 /**
214 * @test Implementation
215 */
216 public function setStoresEntriesInSelectedDatabase()
217 {
218 $this->setUpRedis();
219 $this->redis->select(1);
220 $this->setUpBackend(['database' => 1]);
221 $identifier = $this->getUniqueId('identifier');
222 $this->backend->set($identifier, 'data');
223 $this->assertTrue($this->redis->exists('identData:' . $identifier));
224 }
225
226 /**
227 * @test Implementation
228 */
229 public function setSavesStringDataTypeForIdentifierToDataEntry()
230 {
231 $this->setUpBackend();
232 $this->setUpRedis();
233 $identifier = $this->getUniqueId('identifier');
234 $this->backend->set($identifier, 'data');
235 $this->assertSame(\Redis::REDIS_STRING, $this->redis->type('identData:' . $identifier));
236 }
237
238 /**
239 * @test Implementation
240 */
241 public function setSavesEntryWithDefaultLifeTime()
242 {
243 $this->setUpBackend();
244 $this->setUpRedis();
245 $identifier = $this->getUniqueId('identifier');
246 $defaultLifetime = 42;
247 $this->backend->setDefaultLifetime($defaultLifetime);
248 $this->backend->set($identifier, 'data');
249 $lifetimeRegisteredInBackend = $this->redis->ttl('identData:' . $identifier);
250 $this->assertSame($defaultLifetime, $lifetimeRegisteredInBackend);
251 }
252
253 /**
254 * @test Implementation
255 */
256 public function setSavesEntryWithSpecifiedLifeTime()
257 {
258 $this->setUpBackend();
259 $this->setUpRedis();
260 $identifier = $this->getUniqueId('identifier');
261 $lifetime = 43;
262 $this->backend->set($identifier, 'data', [], $lifetime);
263 $lifetimeRegisteredInBackend = $this->redis->ttl('identData:' . $identifier);
264 $this->assertSame($lifetime, $lifetimeRegisteredInBackend);
265 }
266
267 /**
268 * @test Implementation
269 */
270 public function setSavesEntryWithUnlimitedLifeTime()
271 {
272 $this->setUpBackend();
273 $this->setUpRedis();
274 $identifier = $this->getUniqueId('identifier');
275 $this->backend->set($identifier, 'data', [], 0);
276 $lifetimeRegisteredInBackend = $this->redis->ttl('identData:' . $identifier);
277 $this->assertSame(31536000, $lifetimeRegisteredInBackend);
278 }
279
280 /**
281 * @test Functional
282 */
283 public function setOverwritesExistingEntryWithNewData()
284 {
285 $this->setUpBackend();
286 $data = 'data 1';
287 $identifier = $this->getUniqueId('identifier');
288 $this->backend->set($identifier, $data);
289 $otherData = 'data 2';
290 $this->backend->set($identifier, $otherData);
291 $fetchedData = $this->backend->get($identifier);
292 $this->assertSame($otherData, $fetchedData);
293 }
294
295 /**
296 * @test Implementation
297 */
298 public function setOverwritesExistingEntryWithSpecifiedLifetime()
299 {
300 $this->setUpBackend();
301 $this->setUpRedis();
302 $data = 'data';
303 $identifier = $this->getUniqueId('identifier');
304 $this->backend->set($identifier, $data);
305 $lifetime = 42;
306 $this->backend->set($identifier, $data, [], $lifetime);
307 $lifetimeRegisteredInBackend = $this->redis->ttl('identData:' . $identifier);
308 $this->assertSame($lifetime, $lifetimeRegisteredInBackend);
309 }
310
311 /**
312 * @test Implementation
313 */
314 public function setOverwritesExistingEntryWithNewDefaultLifetime()
315 {
316 $this->setUpBackend();
317 $this->setUpRedis();
318 $data = 'data';
319 $identifier = $this->getUniqueId('identifier');
320 $lifetime = 42;
321 $this->backend->set($identifier, $data, [], $lifetime);
322 $newDefaultLifetime = 43;
323 $this->backend->setDefaultLifetime($newDefaultLifetime);
324 $this->backend->set($identifier, $data, [], $newDefaultLifetime);
325 $lifetimeRegisteredInBackend = $this->redis->ttl('identData:' . $identifier);
326 $this->assertSame($newDefaultLifetime, $lifetimeRegisteredInBackend);
327 }
328
329 /**
330 * @test Implementation
331 */
332 public function setOverwritesExistingEntryWithNewUnlimitedLifetime()
333 {
334 $this->setUpBackend();
335 $this->setUpRedis();
336 $data = 'data';
337 $identifier = $this->getUniqueId('identifier');
338 $lifetime = 42;
339 $this->backend->set($identifier, $data, [], $lifetime);
340 $this->backend->set($identifier, $data, [], 0);
341 $lifetimeRegisteredInBackend = $this->redis->ttl('identData:' . $identifier);
342 $this->assertSame(31536000, $lifetimeRegisteredInBackend);
343 }
344
345 /**
346 * @test Implementation
347 */
348 public function setSavesSetDataTypeForIdentifierToTagsSet()
349 {
350 $this->setUpBackend();
351 $this->setUpRedis();
352 $identifier = $this->getUniqueId('identifier');
353 $this->backend->set($identifier, 'data', ['tag']);
354 $this->assertSame(\Redis::REDIS_SET, $this->redis->type('identTags:' . $identifier));
355 }
356
357 /**
358 * @test Implementation
359 */
360 public function setSavesSpecifiedTagsInIdentifierToTagsSet()
361 {
362 $this->setUpBackend();
363 $this->setUpRedis();
364 $identifier = $this->getUniqueId('identifier');
365 $tags = ['thatTag', 'thisTag'];
366 $this->backend->set($identifier, 'data', $tags);
367 $savedTags = $this->redis->sMembers('identTags:' . $identifier);
368 sort($savedTags);
369 $this->assertSame($tags, $savedTags);
370 }
371
372 /**
373 * @test Implementation
374 */
375 public function setRemovesAllPreviouslySetTagsFromIdentifierToTagsSet()
376 {
377 $this->setUpBackend();
378 $this->setUpRedis();
379 $identifier = $this->getUniqueId('identifier');
380 $tags = ['fooTag', 'barTag'];
381 $this->backend->set($identifier, 'data', $tags);
382 $this->backend->set($identifier, 'data', []);
383 $this->assertSame([], $this->redis->sMembers('identTags:' . $identifier));
384 }
385
386 /**
387 * @test Implementation
388 */
389 public function setRemovesMultiplePreviouslySetTagsFromIdentifierToTagsSet()
390 {
391 $this->setUpBackend();
392 $this->setUpRedis();
393 $identifier = $this->getUniqueId('identifier');
394 $firstTagSet = ['tag1', 'tag2', 'tag3', 'tag4'];
395 $this->backend->set($identifier, 'data', $firstTagSet);
396 $secondTagSet = ['tag1', 'tag3'];
397 $this->backend->set($identifier, 'data', $secondTagSet);
398 $actualTagSet = $this->redis->sMembers('identTags:' . $identifier);
399 sort($actualTagSet);
400 $this->assertSame($secondTagSet, $actualTagSet);
401 }
402
403 /**
404 * @test Implementation
405 */
406 public function setSavesSetDataTypeForTagToIdentifiersSet()
407 {
408 $this->setUpBackend();
409 $this->setUpRedis();
410 $identifier = $this->getUniqueId('identifier');
411 $tag = 'tag';
412 $this->backend->set($identifier, 'data', [$tag]);
413 $this->assertSame(\Redis::REDIS_SET, $this->redis->type('tagIdents:' . $tag));
414 }
415
416 /**
417 * @test Implementation
418 */
419 public function setSavesIdentifierInTagToIdentifiersSetOfSpecifiedTag()
420 {
421 $this->setUpBackend();
422 $this->setUpRedis();
423 $identifier = $this->getUniqueId('identifier');
424 $tag = 'thisTag';
425 $this->backend->set($identifier, 'data', [$tag]);
426 $savedTagToIdentifiersMemberArray = $this->redis->sMembers('tagIdents:' . $tag);
427 $this->assertSame([$identifier], $savedTagToIdentifiersMemberArray);
428 }
429
430 /**
431 * @test Implementation
432 */
433 public function setAppendsSecondIdentifierInTagToIdentifiersEntry()
434 {
435 $this->setUpBackend();
436 $this->setUpRedis();
437 $firstIdentifier = $this->getUniqueId('identifier1-');
438 $tag = 'thisTag';
439 $this->backend->set($firstIdentifier, 'data', [$tag]);
440 $secondIdentifier = $this->getUniqueId('identifier2-');
441 $this->backend->set($secondIdentifier, 'data', [$tag]);
442 $savedTagToIdentifiersMemberArray = $this->redis->sMembers('tagIdents:' . $tag);
443 sort($savedTagToIdentifiersMemberArray);
444 $identifierArray = [$firstIdentifier, $secondIdentifier];
445 sort($identifierArray);
446 $this->assertSame([$firstIdentifier, $secondIdentifier], $savedTagToIdentifiersMemberArray);
447 }
448
449 /**
450 * @test Implementation
451 */
452 public function setRemovesIdentifierFromTagToIdentifiersEntryIfTagIsOmittedOnConsecutiveSet()
453 {
454 $this->setUpBackend();
455 $this->setUpRedis();
456 $identifier = $this->getUniqueId('identifier');
457 $tag = 'thisTag';
458 $this->backend->set($identifier, 'data', [$tag]);
459 $this->backend->set($identifier, 'data', []);
460 $savedTagToIdentifiersMemberArray = $this->redis->sMembers('tagIdents:' . $tag);
461 $this->assertSame([], $savedTagToIdentifiersMemberArray);
462 }
463
464 /**
465 * @test Implementation
466 */
467 public function setAddsIdentifierInTagToIdentifiersEntryIfTagIsAddedOnConsecutiveSet()
468 {
469 $this->setUpBackend();
470 $this->setUpRedis();
471 $identifier = $this->getUniqueId('identifier');
472 $this->backend->set($identifier, 'data');
473 $tag = 'thisTag';
474 $this->backend->set($identifier, 'data', [$tag]);
475 $savedTagToIdentifiersMemberArray = $this->redis->sMembers('tagIdents:' . $tag);
476 $this->assertSame([$identifier], $savedTagToIdentifiersMemberArray);
477 }
478
479 /**
480 * @test Implementation
481 */
482 public function setSavesCompressedDataWithEnabledCompression()
483 {
484 $this->setUpBackend([
485 'compression' => true
486 ]);
487 $this->setUpRedis();
488 $identifier = $this->getUniqueId('identifier');
489 $data = 'some data ' . microtime();
490 $this->backend->set($identifier, $data);
491 $uncompresedStoredData = '';
492 try {
493 $uncompresedStoredData = @gzuncompress($this->redis->get(('identData:' . $identifier)));
494 } catch (\Exception $e) {
495 }
496 $this->assertEquals($data, $uncompresedStoredData, 'Original and compressed data don\'t match');
497 }
498
499 /**
500 * @test Implementation
501 */
502 public function setSavesPlaintextDataWithEnabledCompressionAndCompressionLevel0()
503 {
504 $this->setUpBackend([
505 'compression' => true,
506 'compressionLevel' => 0
507 ]);
508 $this->setUpRedis();
509 $identifier = $this->getUniqueId('identifier');
510 $data = 'some data ' . microtime();
511 $this->backend->set($identifier, $data);
512 $this->assertGreaterThan(0, substr_count($this->redis->get('identData:' . $identifier), $data), 'Plaintext data not found');
513 }
514
515 /**
516 * @test Functional
517 */
518 public function hasThrowsExceptionIfIdentifierIsNotAString()
519 {
520 $this->expectException(\InvalidArgumentException::class);
521 $this->expectExceptionCode(1377006653);
522
523 $this->setUpBackend();
524 $this->backend->has([]);
525 }
526
527 /**
528 * @test Functional
529 */
530 public function hasReturnsFalseForNotExistingEntry()
531 {
532 $this->setUpBackend();
533 $identifier = $this->getUniqueId('identifier');
534 $this->assertFalse($this->backend->has($identifier));
535 }
536
537 /**
538 * @test Functional
539 */
540 public function hasReturnsTrueForPreviouslySetEntry()
541 {
542 $this->setUpBackend();
543 $identifier = $this->getUniqueId('identifier');
544 $this->backend->set($identifier, 'data');
545 $this->assertTrue($this->backend->has($identifier));
546 }
547
548 /**
549 * @test Functional
550 */
551 public function getThrowsExceptionIfIdentifierIsNotAString()
552 {
553 $this->expectException(\InvalidArgumentException::class);
554 //@todo Add exception code with redis extension
555
556 $this->setUpBackend();
557 $this->backend->get([]);
558 }
559
560 /**
561 * @test Functional
562 */
563 public function getReturnsPreviouslyCompressedSetEntry()
564 {
565 $this->setUpBackend([
566 'compression' => true
567 ]);
568 $data = 'data';
569 $identifier = $this->getUniqueId('identifier');
570 $this->backend->set($identifier, $data);
571 $fetchedData = $this->backend->get($identifier);
572 $this->assertSame($data, $fetchedData);
573 }
574
575 /**
576 * @test Functional
577 */
578 public function getReturnsPreviouslySetEntry()
579 {
580 $this->setUpBackend();
581 $data = 'data';
582 $identifier = $this->getUniqueId('identifier');
583 $this->backend->set($identifier, $data);
584 $fetchedData = $this->backend->get($identifier);
585 $this->assertSame($data, $fetchedData);
586 }
587
588 /**
589 * @test Functional
590 */
591 public function removeThrowsExceptionIfIdentifierIsNotAString()
592 {
593 $this->expectException(\InvalidArgumentException::class);
594 $this->expectExceptionCode(1377006654);
595
596 $this->setUpBackend();
597 $this->backend->remove([]);
598 }
599
600 /**
601 * @test Functional
602 */
603 public function removeReturnsFalseIfNoEntryWasDeleted()
604 {
605 $this->setUpBackend();
606 $this->assertFalse($this->backend->remove($this->getUniqueId('identifier')));
607 }
608
609 /**
610 * @test Functional
611 */
612 public function removeReturnsTrueIfAnEntryWasDeleted()
613 {
614 $this->setUpBackend();
615 $identifier = $this->getUniqueId('identifier');
616 $this->backend->set($identifier, 'data');
617 $this->assertTrue($this->backend->remove($identifier));
618 }
619
620 /**
621 * @test Functional
622 */
623 public function removeDeletesEntryFromCache()
624 {
625 $this->setUpBackend();
626 $identifier = $this->getUniqueId('identifier');
627 $this->backend->set($identifier, 'data');
628 $this->backend->remove($identifier);
629 $this->assertFalse($this->backend->has($identifier));
630 }
631
632 /**
633 * @test Implementation
634 */
635 public function removeDeletesIdentifierToTagEntry()
636 {
637 $this->setUpBackend();
638 $this->setUpRedis();
639 $identifier = $this->getUniqueId('identifier');
640 $tag = 'thisTag';
641 $this->backend->set($identifier, 'data', [$tag]);
642 $this->backend->remove($identifier);
643 $this->assertFalse($this->redis->exists('identTags:' . $identifier));
644 }
645
646 /**
647 * @test Implementation
648 */
649 public function removeDeletesIdentifierFromTagToIdentifiersSet()
650 {
651 $this->setUpBackend();
652 $this->setUpRedis();
653 $identifier = $this->getUniqueId('identifier');
654 $tag = 'thisTag';
655 $this->backend->set($identifier, 'data', [$tag]);
656 $this->backend->remove($identifier);
657 $tagToIdentifiersMemberArray = $this->redis->sMembers('tagIdents:' . $tag);
658 $this->assertSame([], $tagToIdentifiersMemberArray);
659 }
660
661 /**
662 * @test Implementation
663 */
664 public function removeDeletesIdentifierFromTagToIdentifiersSetWithMultipleEntries()
665 {
666 $this->setUpBackend();
667 $this->setUpRedis();
668 $firstIdentifier = $this->getUniqueId('identifier');
669 $secondIdentifier = $this->getUniqueId('identifier');
670 $tag = 'thisTag';
671 $this->backend->set($firstIdentifier, 'data', [$tag]);
672 $this->backend->set($secondIdentifier, 'data', [$tag]);
673 $this->backend->remove($firstIdentifier);
674 $tagToIdentifiersMemberArray = $this->redis->sMembers('tagIdents:' . $tag);
675 $this->assertSame([$secondIdentifier], $tagToIdentifiersMemberArray);
676 }
677
678 /**
679 * @test Functional
680 */
681 public function findIdentifiersByTagThrowsExceptionIfTagIsNotAString()
682 {
683 $this->expectException(\InvalidArgumentException::class);
684 $this->expectExceptionCode(1377006655);
685
686 $this->setUpBackend();
687 $this->backend->findIdentifiersByTag([]);
688 }
689
690 /**
691 * @test Functional
692 */
693 public function findIdentifiersByTagReturnsEmptyArrayForNotExistingTag()
694 {
695 $this->setUpBackend();
696 $this->assertSame([], $this->backend->findIdentifiersByTag('thisTag'));
697 }
698
699 /**
700 * @test Functional
701 */
702 public function findIdentifiersByTagReturnsAllIdentifiersTagedWithSpecifiedTag()
703 {
704 $this->setUpBackend();
705 $firstIdentifier = $this->getUniqueId('identifier1-');
706 $secondIdentifier = $this->getUniqueId('identifier2-');
707 $thirdIdentifier = $this->getUniqueId('identifier3-');
708 $tagsForFirstIdentifier = ['thisTag'];
709 $tagsForSecondIdentifier = ['thatTag'];
710 $tagsForThirdIdentifier = ['thisTag', 'thatTag'];
711 $this->backend->set($firstIdentifier, 'data', $tagsForFirstIdentifier);
712 $this->backend->set($secondIdentifier, 'data', $tagsForSecondIdentifier);
713 $this->backend->set($thirdIdentifier, 'data', $tagsForThirdIdentifier);
714 $expectedResult = [$firstIdentifier, $thirdIdentifier];
715 $actualResult = $this->backend->findIdentifiersByTag('thisTag');
716 sort($actualResult);
717 $this->assertSame($expectedResult, $actualResult);
718 }
719
720 /**
721 * @test Implementation
722 */
723 public function flushRemovesAllEntriesFromCache()
724 {
725 $this->setUpBackend();
726 $this->setUpRedis();
727 $identifier = $this->getUniqueId('identifier');
728 $this->backend->set($identifier, 'data');
729 $this->backend->flush();
730 $this->assertSame([], $this->redis->getKeys('*'));
731 }
732
733 /**
734 * @test Functional
735 */
736 public function flushByTagThrowsExceptionIfTagIsNotAString()
737 {
738 $this->expectException(\InvalidArgumentException::class);
739 $this->expectExceptionCode(1377006656);
740
741 $this->setUpBackend();
742 $this->backend->flushByTag([]);
743 }
744
745 /**
746 * @test Functional
747 */
748 public function flushByTagRemovesEntriesTaggedWithSpecifiedTag()
749 {
750 $this->setUpBackend();
751 $identifier = $this->getUniqueId('identifier');
752 $this->backend->set($identifier . 'A', 'data', ['tag1']);
753 $this->backend->set($identifier . 'B', 'data', ['tag2']);
754 $this->backend->set($identifier . 'C', 'data', ['tag1', 'tag2']);
755 $this->backend->flushByTag('tag1');
756 $expectedResult = [false, true, false];
757 $actualResult = [
758 $this->backend->has($identifier . 'A'),
759 $this->backend->has($identifier . 'B'),
760 $this->backend->has($identifier . 'C')
761 ];
762 $this->assertSame($expectedResult, $actualResult);
763 }
764
765 /**
766 * @test Functional
767 */
768 public function flushByTagsRemovesEntriesTaggedWithSpecifiedTags()
769 {
770 $this->setUpBackend();
771 $identifier = $this->getUniqueId('identifier');
772 $this->backend->set($identifier . 'A', 'data', ['tag1']);
773 $this->backend->set($identifier . 'B', 'data', ['tag2']);
774 $this->backend->set($identifier . 'C', 'data', ['tag1', 'tag2']);
775 $this->backend->set($identifier . 'D', 'data', ['tag3']);
776 $this->backend->flushByTags(['tag1', 'tag2']);
777 $expectedResult = [false, false, false, true];
778 $actualResult = [
779 $this->backend->has($identifier . 'A'),
780 $this->backend->has($identifier . 'B'),
781 $this->backend->has($identifier . 'C'),
782 $this->backend->has($identifier . 'D')
783 ];
784 $this->assertSame($expectedResult, $actualResult);
785 }
786
787 /**
788 * @test Implementation
789 */
790 public function flushByTagRemovesTemporarySet()
791 {
792 $this->setUpBackend();
793 $this->setUpRedis();
794 $identifier = $this->getUniqueId('identifier');
795 $this->backend->set($identifier . 'A', 'data', ['tag1']);
796 $this->backend->set($identifier . 'C', 'data', ['tag1', 'tag2']);
797 $this->backend->flushByTag('tag1');
798 $this->assertSame([], $this->redis->getKeys('temp*'));
799 }
800
801 /**
802 * @test Implementation
803 */
804 public function flushByTagRemovesIdentifierToTagsSetOfEntryTaggedWithGivenTag()
805 {
806 $this->setUpBackend();
807 $this->setUpRedis();
808 $identifier = $this->getUniqueId('identifier');
809 $tag = 'tag1';
810 $this->backend->set($identifier, 'data', [$tag]);
811 $this->backend->flushByTag($tag);
812 $this->assertFalse($this->redis->exists('identTags:' . $identifier));
813 }
814
815 /**
816 * @test Implementation
817 */
818 public function flushByTagDoesNotRemoveIdentifierToTagsSetOfUnrelatedEntry()
819 {
820 $this->setUpBackend();
821 $this->setUpRedis();
822 $identifierToBeRemoved = $this->getUniqueId('identifier');
823 $tagToRemove = 'tag1';
824 $this->backend->set($identifierToBeRemoved, 'data', [$tagToRemove]);
825 $identifierNotToBeRemoved = $this->getUniqueId('identifier');
826 $tagNotToRemove = 'tag2';
827 $this->backend->set($identifierNotToBeRemoved, 'data', [$tagNotToRemove]);
828 $this->backend->flushByTag($tagToRemove);
829 $this->assertSame([$tagNotToRemove], $this->redis->sMembers('identTags:' . $identifierNotToBeRemoved));
830 }
831
832 /**
833 * @test Implementation
834 */
835 public function flushByTagRemovesTagToIdentifiersSetOfGivenTag()
836 {
837 $this->setUpBackend();
838 $this->setUpRedis();
839 $identifier = $this->getUniqueId('identifier');
840 $tag = 'tag1';
841 $this->backend->set($identifier, 'data', [$tag]);
842 $this->backend->flushByTag($tag);
843 $this->assertFalse($this->redis->exists('tagIdents:' . $tag));
844 }
845
846 /**
847 * @test Implementation
848 */
849 public function flushByTagRemovesIdentifiersTaggedWithGivenTagFromTagToIdentifiersSets()
850 {
851 $this->setUpBackend();
852 $this->setUpRedis();
853 $identifier = $this->getUniqueId('identifier');
854 $this->backend->set($identifier . 'A', 'data', ['tag1', 'tag2']);
855 $this->backend->set($identifier . 'B', 'data', ['tag1', 'tag2']);
856 $this->backend->set($identifier . 'C', 'data', ['tag2']);
857 $this->backend->flushByTag('tag1');
858 $this->assertSame([$identifier . 'C'], $this->redis->sMembers('tagIdents:tag2'));
859 }
860
861 /**
862 * @test Implementation
863 */
864 public function collectGarbageDoesNotRemoveNotExpiredIdentifierToDataEntry()
865 {
866 $this->setUpBackend();
867 $this->setUpRedis();
868 $identifier = $this->getUniqueId('identifier');
869 $this->backend->set($identifier . 'A', 'data', ['tag']);
870 $this->backend->set($identifier . 'B', 'data', ['tag']);
871 $this->redis->delete('identData:' . $identifier . 'A');
872 $this->backend->collectGarbage();
873 $this->assertTrue($this->redis->exists('identData:' . $identifier . 'B'));
874 }
875
876 /**
877 * @test Implementation
878 */
879 public function collectGarbageRemovesLeftOverIdentifierToTagsSet()
880 {
881 $this->setUpBackend();
882 $this->setUpRedis();
883 $identifier = $this->getUniqueId('identifier');
884 $this->backend->set($identifier . 'A', 'data', ['tag']);
885 $this->backend->set($identifier . 'B', 'data', ['tag']);
886 $this->redis->delete('identData:' . $identifier . 'A');
887 $this->backend->collectGarbage();
888 $expectedResult = [false, true];
889 $actualResult = [
890 $this->redis->exists('identTags:' . $identifier . 'A'),
891 $this->redis->exists('identTags:' . $identifier . 'B')
892 ];
893 $this->assertSame($expectedResult, $actualResult);
894 }
895
896 /**
897 * @test Implementation
898 */
899 public function collectGarbageRemovesExpiredIdentifierFromTagsToIdentifierSet()
900 {
901 $this->setUpBackend();
902 $this->setUpRedis();
903 $identifier = $this->getUniqueId('identifier');
904 $this->backend->set($identifier . 'A', 'data', ['tag1', 'tag2']);
905 $this->backend->set($identifier . 'B', 'data', ['tag2']);
906 $this->redis->delete('identData:' . $identifier . 'A');
907 $this->backend->collectGarbage();
908 $expectedResult = [
909 [],
910 [$identifier . 'B']
911 ];
912 $actualResult = [
913 $this->redis->sMembers('tagIdents:tag1'),
914 $this->redis->sMembers('tagIdents:tag2')
915 ];
916 $this->assertSame($expectedResult, $actualResult);
917 }
918 }