Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
152 / 152
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\InMemoryCache;
11use Phexium\Plugin\Clock\Adapter\FrozenClock;
12use Phexium\Plugin\Clock\Adapter\OffsetClock;
13
14beforeEach(function (): void {
15    $this->clock = new OffsetClock(new FrozenClock('2025-01-01T12:00:00+00:00'));
16    $this->cache = new InMemoryCache($this->clock);
17});
18
19test('get returns null for non-existent key', function (): void {
20    expect($this->cache->get('non-existent'))->toBeNull();
21});
22
23test('get returns default for non-existent key', function (): void {
24    expect($this->cache->get('non-existent', 'default'))->toBe('default');
25});
26
27test('set and get stores and retrieves value', function (): void {
28    $this->cache->set('key', 'value');
29
30    expect($this->cache->get('key'))->toBe('value');
31});
32
33test('set returns true on success', function (): void {
34    expect($this->cache->set('key', 'value'))->toBeTrue();
35});
36
37test('set overwrites existing value', function (): void {
38    $this->cache->set('key', 'first');
39    $this->cache->set('key', 'second');
40
41    expect($this->cache->get('key'))->toBe('second');
42});
43
44test('has returns false for non-existent key', function (): void {
45    expect($this->cache->has('non-existent'))->toBeFalse();
46});
47
48test('has returns true for existing key', function (): void {
49    $this->cache->set('key', 'value');
50
51    expect($this->cache->has('key'))->toBeTrue();
52});
53
54test('delete removes existing key and returns true', function (): void {
55    $this->cache->set('key', 'value');
56
57    expect($this->cache->delete('key'))->toBeTrue();
58    expect($this->cache->has('key'))->toBeFalse();
59});
60
61test('delete returns false for non-existent key', function (): void {
62    expect($this->cache->delete('non-existent'))->toBeFalse();
63});
64
65test('clear removes all keys', function (): void {
66    $this->cache->set('key1', 'value1');
67    $this->cache->set('key2', 'value2');
68
69    $this->cache->clear();
70
71    expect($this->cache->has('key1'))->toBeFalse();
72    expect($this->cache->has('key2'))->toBeFalse();
73});
74
75test('clear returns true', function (): void {
76    expect($this->cache->clear())->toBeTrue();
77});
78
79test('set with TTL zero does not store value and returns true', function (): void {
80    expect($this->cache->set('key', 'value', 0))->toBeTrue();
81    expect($this->cache->delete('key'))->toBeFalse();
82});
83
84test('set with TTL 1 stores value', function (): void {
85    $this->cache->set('key', 'value', 1);
86
87    expect($this->cache->has('key'))->toBeTrue();
88});
89
90test('set with negative TTL deletes existing entry', function (): void {
91    $this->cache->set('key', 'value');
92    expect($this->cache->has('key'))->toBeTrue();
93
94    $this->cache->set('key', 'new-value', -1);
95
96    expect($this->cache->has('key'))->toBeFalse();
97});
98
99test('set with DateInterval TTL', function (): void {
100    $this->cache->set('key', 'value', new DateInterval('PT60S'));
101
102    expect($this->cache->has('key'))->toBeTrue();
103    expect($this->cache->get('key'))->toBe('value');
104
105    $this->clock->advanceSeconds(59);
106    expect($this->cache->has('key'))->toBeTrue();
107
108    $this->clock->advanceSeconds(1);
109    expect($this->cache->has('key'))->toBeFalse();
110});
111
112test('stores and retrieves object values', function (): void {
113    $object = new stdClass();
114    $this->cache->set('object', $object);
115
116    expect($this->cache->get('object'))->toBe($object);
117});
118
119test('stores and retrieves array values', function (): void {
120    $data = ['foo' => 'bar', 'nested' => ['a' => 1]];
121    $this->cache->set('array', $data);
122
123    expect($this->cache->get('array'))->toBe($data);
124});
125
126test('stores null as valid value', function (): void {
127    $this->cache->set('null-key', null);
128
129    expect($this->cache->has('null-key'))->toBeTrue();
130    expect($this->cache->get('null-key'))->toBeNull();
131});
132
133test('stores false as valid value', function (): void {
134    $this->cache->set('false-key', false);
135
136    expect($this->cache->has('false-key'))->toBeTrue();
137    expect($this->cache->get('false-key'))->toBeFalse();
138});
139
140test('stores empty string as valid value', function (): void {
141    $this->cache->set('empty-key', '');
142
143    expect($this->cache->has('empty-key'))->toBeTrue();
144    expect($this->cache->get('empty-key'))->toBe('');
145});
146
147test('stores zero as valid value', function (): void {
148    $this->cache->set('zero-key', 0);
149
150    expect($this->cache->has('zero-key'))->toBeTrue();
151    expect($this->cache->get('zero-key'))->toBe(0);
152});
153
154test('value expires after TTL seconds', function (): void {
155    $this->cache->set('key', 'value', 60);
156
157    expect($this->cache->has('key'))->toBeTrue();
158    expect($this->cache->get('key'))->toBe('value');
159
160    $this->clock->advanceSeconds(59);
161    expect($this->cache->has('key'))->toBeTrue();
162    expect($this->cache->get('key'))->toBe('value');
163
164    $this->clock->advanceSeconds(1);
165    expect($this->cache->has('key'))->toBeFalse();
166    expect($this->cache->get('key'))->toBeNull();
167});
168
169test('value without TTL never expires', function (): void {
170    $this->cache->set('key', 'value');
171
172    expect($this->cache->has('key'))->toBeTrue();
173    expect($this->cache->get('key'))->toBe('value');
174
175    $this->clock->advanceSeconds(10 * 365 * 24 * 60 * 60);
176
177    expect($this->cache->has('key'))->toBeTrue();
178    expect($this->cache->get('key'))->toBe('value');
179});
180
181test('expired items are lazily removed on access', function (): void {
182    $this->cache->set('key', 'value', 60);
183
184    $this->clock->advanceSeconds(61);
185
186    expect($this->cache->has('key'))->toBeFalse();
187    expect($this->cache->delete('key'))->toBeFalse();
188});
189
190test('getMultiple returns values with defaults', function (): void {
191    $this->cache->set('key1', 'value1');
192
193    $result = $this->cache->getMultiple(['key1', 'key2'], 'default');
194
195    expect($result)->toBe(['key1' => 'value1', 'key2' => 'default']);
196});
197
198test('setMultiple stores all values', function (): void {
199    $result = $this->cache->setMultiple(['key1' => 'value1', 'key2' => 'value2']);
200
201    expect($result)->toBeTrue();
202    expect($this->cache->get('key1'))->toBe('value1');
203    expect($this->cache->get('key2'))->toBe('value2');
204});
205
206test('setMultiple with TTL', function (): void {
207    $this->cache->setMultiple(['key1' => 'value1', 'key2' => 'value2'], 60);
208
209    $this->clock->advanceSeconds(61);
210
211    expect($this->cache->has('key1'))->toBeFalse();
212    expect($this->cache->has('key2'))->toBeFalse();
213});
214
215test('deleteMultiple removes all keys', function (): void {
216    $this->cache->set('key1', 'value1');
217    $this->cache->set('key2', 'value2');
218    $this->cache->set('key3', 'value3');
219
220    $result = $this->cache->deleteMultiple(['key1', 'key2']);
221
222    expect($result)->toBeTrue();
223    expect($this->cache->has('key1'))->toBeFalse();
224    expect($this->cache->has('key2'))->toBeFalse();
225    expect($this->cache->has('key3'))->toBeTrue();
226});