Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
138 / 138
n/a
0 / 0
CRAP
n/a
0 / 0
1<?php
2
3// ╔════════════════════════════════════════════════════════════╗
4// ║ MIT Licence (#Expat) - https://opensource.org/licenses/MIT ║
5// ║ Copyright 2026 Frederic Poeydomenge <dyno@phexium.com>     ║
6// ╚════════════════════════════════════════════════════════════╝
7
8declare(strict_types=1);
9
10use Phexium\Plugin\Cache\Adapter\RedisCache;
11
12beforeEach(function (): void {
13    $this->redis = new Redis();
14    $this->redis->connect('redis', 6379);
15    $this->cache = new RedisCache($this->redis, [stdClass::class]);
16});
17
18afterEach(function (): void {
19    $this->redis->flushDB();
20    $this->redis->close();
21});
22
23test('get returns null for non-existent key', function (): void {
24    expect($this->cache->get('non-existent'))->toBeNull();
25});
26
27test('get returns default for non-existent key', function (): void {
28    expect($this->cache->get('non-existent', 'default'))->toBe('default');
29});
30
31test('set and get stores and retrieves value', function (): void {
32    $this->cache->set('key', 'value');
33
34    expect($this->cache->get('key'))->toBe('value');
35});
36
37test('set returns true on success', function (): void {
38    expect($this->cache->set('key', 'value'))->toBeTrue();
39});
40
41test('set overwrites existing value', function (): void {
42    $this->cache->set('key', 'first');
43    $this->cache->set('key', 'second');
44
45    expect($this->cache->get('key'))->toBe('second');
46});
47
48test('has returns false for non-existent key', function (): void {
49    expect($this->cache->has('non-existent'))->toBeFalse();
50});
51
52test('has returns true for existing key', function (): void {
53    $this->cache->set('key', 'value');
54
55    expect($this->cache->has('key'))->toBeTrue();
56});
57
58test('delete removes existing key and returns true', function (): void {
59    $this->cache->set('key', 'value');
60
61    expect($this->cache->delete('key'))->toBeTrue();
62    expect($this->cache->has('key'))->toBeFalse();
63});
64
65test('delete returns false for non-existent key', function (): void {
66    expect($this->cache->delete('non-existent'))->toBeFalse();
67});
68
69test('clear removes all keys', function (): void {
70    $this->cache->set('key1', 'value1');
71    $this->cache->set('key2', 'value2');
72
73    $this->cache->clear();
74
75    expect($this->cache->has('key1'))->toBeFalse();
76    expect($this->cache->has('key2'))->toBeFalse();
77});
78
79test('clear returns true', function (): void {
80    expect($this->cache->clear())->toBeTrue();
81});
82
83test('set with TTL zero does not store value and returns true', function (): void {
84    expect($this->cache->set('key', 'value', 0))->toBeTrue();
85    expect($this->cache->delete('key'))->toBeFalse();
86});
87
88test('set with negative TTL deletes existing entry', function (): void {
89    $this->cache->set('key', 'value');
90    expect($this->cache->has('key'))->toBeTrue();
91
92    $this->cache->set('key', 'new-value', -1);
93
94    expect($this->cache->has('key'))->toBeFalse();
95});
96
97test('stores and retrieves object values', function (): void {
98    $object = new stdClass();
99    $object->name = 'test';
100    $this->cache->set('object', $object);
101
102    $retrieved = $this->cache->get('object');
103    expect($retrieved)->toBeInstanceOf(stdClass::class);
104    expect($retrieved->name)->toBe('test');
105});
106
107test('stores and retrieves array values', function (): void {
108    $data = ['foo' => 'bar', 'nested' => ['a' => 1]];
109    $this->cache->set('array', $data);
110
111    expect($this->cache->get('array'))->toBe($data);
112});
113
114test('stores null as valid value', function (): void {
115    $this->cache->set('null-key', null);
116
117    expect($this->cache->has('null-key'))->toBeTrue();
118    expect($this->cache->get('null-key'))->toBeNull();
119});
120
121test('stores false as valid value', function (): void {
122    $this->cache->set('false-key', false);
123
124    expect($this->cache->has('false-key'))->toBeTrue();
125    expect($this->cache->get('false-key'))->toBeFalse();
126});
127
128test('stores empty string as valid value', function (): void {
129    $this->cache->set('empty-key', '');
130
131    expect($this->cache->has('empty-key'))->toBeTrue();
132    expect($this->cache->get('empty-key'))->toBe('');
133});
134
135test('stores zero as valid value', function (): void {
136    $this->cache->set('zero-key', 0);
137
138    expect($this->cache->has('zero-key'))->toBeTrue();
139    expect($this->cache->get('zero-key'))->toBe(0);
140});
141
142test('value without TTL never expires', function (): void {
143    $this->cache->set('key', 'value');
144
145    expect($this->cache->has('key'))->toBeTrue();
146    expect($this->cache->get('key'))->toBe('value');
147
148    expect($this->redis->ttl('key'))->toBe(-1);
149});
150
151test('getMultiple returns values with defaults', function (): void {
152    $this->cache->set('key1', 'value1');
153
154    $result = $this->cache->getMultiple(['key1', 'key2'], 'default');
155
156    expect($result)->toBe(['key1' => 'value1', 'key2' => 'default']);
157});
158
159test('setMultiple stores all values', function (): void {
160    $result = $this->cache->setMultiple(['key1' => 'value1', 'key2' => 'value2']);
161
162    expect($result)->toBeTrue();
163    expect($this->cache->get('key1'))->toBe('value1');
164    expect($this->cache->get('key2'))->toBe('value2');
165});
166
167test('deleteMultiple removes all keys', function (): void {
168    $this->cache->set('key1', 'value1');
169    $this->cache->set('key2', 'value2');
170    $this->cache->set('key3', 'value3');
171
172    $result = $this->cache->deleteMultiple(['key1', 'key2']);
173
174    expect($result)->toBeTrue();
175    expect($this->cache->has('key1'))->toBeFalse();
176    expect($this->cache->has('key2'))->toBeFalse();
177    expect($this->cache->has('key3'))->toBeTrue();
178});
179
180test('set with TTL 100ms stores value', function (): void {
181    $ttl = new DateInterval('PT0S');
182    $ttl->f = 0.01; // 0.01 sec
183
184    $this->cache->set('key', 'value', $ttl);
185
186    expect($this->cache->has('key'))->toBeTrue();
187
188    usleep(12_000); // 0.012 sec
189
190    expect($this->cache->has('key'))->toBeFalse();
191});
192
193test('rejects deserialization of non-allowed classes', function (): void {
194    // Store object using a cache that allows DateTime
195    $cacheWithDateTime = new RedisCache($this->redis, [DateTime::class]);
196    $cacheWithDateTime->set('datetime', new DateTime('2026-01-27'));
197
198    // Retrieve with a cache that does not allow DateTime
199    $restrictedCache = new RedisCache($this->redis, []);
200    $retrieved = $restrictedCache->get('datetime');
201
202    // Should return __PHP_Incomplete_Class, not DateTime
203    expect($retrieved)->not->toBeInstanceOf(DateTime::class);
204    expect($retrieved::class)->toBe('__PHP_Incomplete_Class');
205});