Passed
Branch master (0b4094)
by Alexander
01:32
created

MemcachedTest::testInvalidServersConfig()

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 2
nc 1
nop 1
dl 0
loc 4
c 1
b 0
f 0
1
<?php
2
3
namespace Yiisoft\Cache\Tests\Memcached;
4
5
require_once __DIR__ . '/../MockHelper.php';
6
7
use DateInterval;
8
use Psr\SimpleCache\CacheInterface;
9
use Psr\SimpleCache\InvalidArgumentException;
10
use ReflectionException;
11
use Yiisoft\Cache\Cache;
12
use Yiisoft\Cache\Exception\InvalidConfigException;
13
use Yiisoft\Cache\Memcached;
14
use Yiisoft\Cache\MockHelper;
15
use Yiisoft\Cache\Tests\TestCase;
16
17
class MemcachedTest extends TestCase
18
{
19
    public static function setUpBeforeClass(): void
20
    {
21
        if (!extension_loaded('memcached')) {
22
            self::markTestSkipped('Required extension "memcached" is not loaded');
23
        }
24
25
        // check whether memcached is running and skip tests if not.
26
        if (!@stream_socket_client('127.0.0.1:11211', $errorNumber, $errorDescription, 0.5)) {
27
            self::markTestSkipped('No memcached server running at ' . '127.0.0.1:11211' . ' : ' . $errorNumber . ' - ' . $errorDescription);
28
        }
29
    }
30
31
    protected function tearDown(): void
32
    {
33
        MockHelper::$time = null;
34
    }
35
36
    protected function createCacheInstance(): CacheInterface
37
    {
38
        return new Memcached();
39
    }
40
41
    public function testDeleteMultipleReturnsFalse(): void
42
    {
43
        $cache = new Memcached();
44
45
        $memcachedStub = $this->createMock(\Memcached::class);
46
        $memcachedStub->method('deleteMulti')->willReturn([false]);
0 ignored issues
show
Bug introduced by
The method method() does not exist on PHPUnit\Framework\MockObject\MockObject. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

46
        $memcachedStub->/** @scrutinizer ignore-call */ 
47
                        method('deleteMulti')->willReturn([false]);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
47
48
        $this->setInaccessibleProperty($cache, 'cache', $memcachedStub);
49
50
        $this->assertFalse($cache->deleteMultiple(['a', 'b']));
51
    }
52
53
    public function testExpire(): void
54
    {
55
        $ttl = 2;
56
        MockHelper::$time = \time();
57
        $expiration = MockHelper::$time + $ttl;
58
59
        $cache = new Memcached();
60
61
        $memcached = $this->createMock(\Memcached::class);
62
63
        $memcached->expects($this->once())
64
            ->method('set')
65
            ->with($this->equalTo('key'), $this->equalTo('value'), $expiration)
66
            ->willReturn(true);
67
68
        $this->setInaccessibleProperty($cache, 'cache', $memcached);
69
70
        $cache->set('key', 'value', $ttl);
71
    }
72
73
    /**
74
     * @dataProvider dataProvider
75
     * @param $key
76
     * @param $value
77
     * @throws InvalidArgumentException
78
     */
79
    public function testSet($key, $value): void
80
    {
81
        $cache = $this->createCacheInstance();
82
        $cache->clear();
83
84
        for ($i = 0; $i < 2; $i++) {
85
            $this->assertTrue($cache->set($key, $value));
86
        }
87
    }
88
89
    /**
90
     * @dataProvider dataProvider
91
     * @param $key
92
     * @param $value
93
     * @throws InvalidArgumentException
94
     */
95
    public function testGet($key, $value): void
96
    {
97
        $cache = $this->createCacheInstance();
98
        $cache->clear();
99
100
        $cache->set($key, $value);
101
        $valueFromCache = $cache->get($key, 'default');
102
103
        $this->assertSameExceptObject($value, $valueFromCache);
104
    }
105
106
    /**
107
     * @dataProvider dataProvider
108
     * @param $key
109
     * @param $value
110
     * @throws InvalidArgumentException
111
     */
112
    public function testValueInCacheCannotBeChanged($key, $value): void
113
    {
114
        $cache = $this->createCacheInstance();
115
        $cache->clear();
116
117
        $cache->set($key, $value);
118
        $valueFromCache = $cache->get($key, 'default');
119
120
        $this->assertSameExceptObject($value, $valueFromCache);
121
122
        if (is_object($value)) {
123
            $originalValue = clone $value;
124
            $valueFromCache->test_field = 'changed';
125
            $value->test_field = 'changed';
126
            $valueFromCacheNew = $cache->get($key, 'default');
127
            $this->assertSameExceptObject($originalValue, $valueFromCacheNew);
128
        }
129
    }
130
131
    /**
132
     * @dataProvider dataProvider
133
     * @param $key
134
     * @param $value
135
     * @throws InvalidArgumentException
136
     */
137
    public function testHas($key, $value): void
138
    {
139
        $cache = $this->createCacheInstance();
140
        $cache->clear();
141
142
        $cache->set($key, $value);
143
144
        $this->assertTrue($cache->has($key));
145
        // check whether exists affects the value
146
        $this->assertSameExceptObject($value, $cache->get($key));
147
148
        $this->assertTrue($cache->has($key));
149
        $this->assertFalse($cache->has('not_exists'));
150
    }
151
152
    public function testGetNonExistent(): void
153
    {
154
        $cache = $this->createCacheInstance();
155
        $cache->clear();
156
157
        $this->assertNull($cache->get('non_existent_key'));
158
    }
159
160
    /**
161
     * @dataProvider dataProvider
162
     * @param $key
163
     * @param $value
164
     * @throws InvalidArgumentException
165
     */
166
    public function testDelete($key, $value): void
167
    {
168
        $cache = $this->createCacheInstance();
169
        $cache->clear();
170
171
        $cache->set($key, $value);
172
173
        $this->assertSameExceptObject($value, $cache->get($key));
174
        $this->assertTrue($cache->delete($key));
175
        $this->assertNull($cache->get($key));
176
    }
177
178
    /**
179
     * @dataProvider dataProvider
180
     * @param $key
181
     * @param $value
182
     * @throws InvalidArgumentException
183
     */
184
    public function testClear($key, $value): void
0 ignored issues
show
Unused Code introduced by
The parameter $value is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

184
    public function testClear($key, /** @scrutinizer ignore-unused */ $value): void

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
185
    {
186
        $cache = $this->createCacheInstance();
187
        $cache = $this->prepare($cache);
188
189
        $this->assertTrue($cache->clear());
190
        $this->assertNull($cache->get($key));
191
    }
192
193
    /**
194
     * @dataProvider dataProviderSetMultiple
195
     * @param int|null $ttl
196
     * @throws InvalidArgumentException
197
     */
198
    public function testSetMultiple(?int $ttl): void
199
    {
200
        $cache = $this->createCacheInstance();
201
        $cache->clear();
202
203
        $data = $this->getDataProviderData();
204
205
        $cache->setMultiple($data, $ttl);
206
207
        foreach ($data as $key => $value) {
208
            $this->assertSameExceptObject($value, $cache->get($key));
209
        }
210
    }
211
212
    /**
213
     * @return array testing multiSet with and without expiry
214
     */
215
    public function dataProviderSetMultiple(): array
216
    {
217
        return [
218
            [null],
219
            [2],
220
        ];
221
    }
222
223
    public function testGetMultiple(): void
224
    {
225
        /** @var Cache $cache */
226
        $cache = $this->createCacheInstance();
227
        $cache->clear();
228
229
        $data = $this->getDataProviderData();
230
231
        $cache->setMultiple($data);
232
233
        $this->assertSameExceptObject($data, $cache->getMultiple(array_keys($data)));
234
    }
235
236
    public function testDeleteMultiple(): void
237
    {
238
        /** @var Cache $cache */
239
        $cache = $this->createCacheInstance();
240
        $cache->clear();
241
242
        $data = $this->getDataProviderData();
243
244
        $cache->setMultiple($data);
245
246
        $this->assertSameExceptObject($data, $cache->getMultiple(array_keys($data)));
247
248
        $cache->deleteMultiple(array_keys($data));
249
250
        $emptyData = array_map(static function ($v) {
0 ignored issues
show
Unused Code introduced by
The parameter $v is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

250
        $emptyData = array_map(static function (/** @scrutinizer ignore-unused */ $v) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
251
            return null;
252
        }, $data);
253
254
        $this->assertSameExceptObject($emptyData, $cache->getMultiple(array_keys($data)));
255
    }
256
257
    public function testZeroAndNegativeTtl()
258
    {
259
        $cache = $this->createCacheInstance();
260
        $cache->clear();
261
        $cache->setMultiple([
262
            'a' => 1,
263
            'b' => 2,
264
        ]);
265
266
        $this->assertTrue($cache->has('a'));
267
        $this->assertTrue($cache->has('b'));
268
269
        $cache->set('a', 11, -1);
270
271
        $this->assertFalse($cache->has('a'));
272
273
        $cache->set('b', 22, 0);
274
275
        $this->assertFalse($cache->has('b'));
276
    }
277
278
    /**
279
     * @dataProvider dataProviderNormalizeTtl
280
     * @param mixed $ttl
281
     * @param mixed $expectedResult
282
     * @throws ReflectionException
283
     */
284
    public function testNormalizeTtl($ttl, $expectedResult): void
285
    {
286
        $cache = new Memcached();
287
        $this->assertSameExceptObject($expectedResult, $this->invokeMethod($cache, 'normalizeTtl', [$ttl]));
288
    }
289
290
    /**
291
     * Data provider for {@see testNormalizeTtl()}
292
     * @return array test data
293
     *
294
     * @throws \Exception
295
     */
296
    public function dataProviderNormalizeTtl(): array
297
    {
298
        return [
299
            [123, 123],
300
            ['123', 123],
301
            [null, null],
302
            [0, 0],
303
            [new DateInterval('PT6H8M'), 6 * 3600 + 8 * 60],
304
            [new DateInterval('P2Y4D'), 2 * 365 * 24 * 3600 + 4 * 24 * 3600],
305
        ];
306
    }
307
308
    /**
309
     * @dataProvider ttlToExpirationProvider
310
     * @param mixed $ttl
311
     * @param mixed $expected
312
     * @throws ReflectionException
313
     */
314
    public function testTtlToExpiration($ttl, $expected): void
315
    {
316
        if ($expected === 'calculate_expiration') {
317
            MockHelper::$time = \time();
318
            $expected = MockHelper::$time + $ttl;
319
        }
320
        $cache = new Memcached();
321
        $this->assertSameExceptObject($expected, $this->invokeMethod($cache, 'ttlToExpiration', [$ttl]));
322
    }
323
324
    public function ttlToExpirationProvider(): array
325
    {
326
        return [
327
            [3, 'calculate_expiration'],
328
            [null, 0],
329
            [-5, -1],
330
        ];
331
    }
332
333
    /**
334
     * @dataProvider iterableProvider
335
     * @param array $array
336
     * @param iterable $iterable
337
     * @throws InvalidArgumentException
338
     */
339
    public function testValuesAsIterable(array $array, iterable $iterable): void
340
    {
341
        $cache = $this->createCacheInstance();
342
        $cache->clear();
343
344
        $cache->setMultiple($iterable);
345
346
        $this->assertSameExceptObject($array, $cache->getMultiple(array_keys($array)));
347
    }
348
349
    public function iterableProvider(): array
350
    {
351
        return [
352
            'array' => [
353
                ['a' => 1, 'b' => 2,],
354
                ['a' => 1, 'b' => 2,],
355
            ],
356
            'ArrayIterator' => [
357
                ['a' => 1, 'b' => 2,],
358
                new \ArrayIterator(['a' => 1, 'b' => 2,]),
359
            ],
360
            'IteratorAggregate' => [
361
                ['a' => 1, 'b' => 2,],
362
                new class() implements \IteratorAggregate {
363
                    public function getIterator()
364
                    {
365
                        return new \ArrayIterator(['a' => 1, 'b' => 2,]);
366
                    }
367
                }
368
            ],
369
            'generator' => [
370
                ['a' => 1, 'b' => 2,],
371
                (static function () {
372
                    yield 'a' => 1;
373
                    yield 'b' => 2;
374
                })()
375
            ]
376
        ];
377
    }
378
379
    public function testGetCache()
380
    {
381
        $cache = new Memcached();
382
        $memcached = $cache->getCache();
383
        $this->assertInstanceOf(\Memcached::class, $memcached);
384
    }
385
386
    public function testPersistentId()
387
    {
388
        $cache1 = new Memcached();
389
        $memcached1 = $cache1->getCache();
390
        $this->assertFalse($memcached1->isPersistent());
391
392
        $cache2 = new Memcached(microtime() . __METHOD__);
393
        $memcached2 = $cache2->getCache();
394
        $this->assertTrue($memcached2->isPersistent());
395
    }
396
397
    public function testGetNewServers(): void
398
    {
399
        $cache = new Memcached();
400
401
        $memcachedStub = $this->createMock(\Memcached::class);
402
        $memcachedStub->method('getServerList')->willReturn([['host' => '1.1.1.1', 'port' => 11211]]);
403
404
        $this->setInaccessibleProperty($cache, 'cache', $memcachedStub);
405
406
        $newServers = $this->invokeMethod($cache, 'getNewServers', [
407
            [
408
                ['1.1.1.1', 11211, 1],
409
                ['2.2.2.2', 11211, 1],
410
            ],
411
        ]);
412
413
        $this->assertEquals([['2.2.2.2', 11211, 1]], $newServers);
414
    }
415
416
    public function testThatServerWeightIsOptional()
417
    {
418
        $cache = new Memcached(microtime() . __METHOD__, [
419
            ['1.1.1.1', 11211, 1],
420
            ['2.2.2.2', 11211],
421
        ]);
422
423
        $memcached = $cache->getCache();
424
        $this->assertEquals([
425
            [
426
                'host' => '1.1.1.1',
427
                'port' => 11211,
428
                'type' => 'TCP',
429
            ],
430
            [
431
                'host' => '2.2.2.2',
432
                'port' => 11211,
433
                'type' => 'TCP',
434
            ],
435
        ], $memcached->getServerList());
436
    }
437
438
    /**
439
     * @dataProvider invalidServersConfigProvider
440
     * @param $servers
441
     */
442
    public function testInvalidServersConfig($servers)
443
    {
444
        $this->expectException(InvalidConfigException::class);
445
        $cache = new Memcached('', $servers);
0 ignored issues
show
Unused Code introduced by
The assignment to $cache is dead and can be removed.
Loading history...
446
    }
447
448
    public function invalidServersConfigProvider()
449
    {
450
        return [
451
            [[[]]],
452
            [[['1.1.1.1']]],
453
        ];
454
    }
455
456
    public function testSetWithDateIntervalTtl()
457
    {
458
        $cache = $this->createCacheInstance();
459
        $cache->clear();
460
461
        $cache->set('a', 1, new DateInterval('PT1H'));
462
        $this->assertSameExceptObject(1, $cache->get('a'));
463
464
        $cache->setMultiple(['b' => 2]);
465
        $this->assertSameExceptObject(['b' => 2], $cache->getMultiple(['b']));
466
    }
467
}
468