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