Passed
Push — master ( 4fbdae...9f873b )
by Alexander
01:12
created

MemcachedTest.php$0 ➔ testSetInvalidKey()   A

Complexity

Conditions 1

Size

Total Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
dl 0
loc 5
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace Yiisoft\Cache\Memcached\Tests;
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\Memcached\CacheException;
12
use Yiisoft\Cache\Memcached\Memcached;
13
use Yiisoft\Cache\Memcached\MockHelper;
14
15
class MemcachedTest extends TestCase
16
{
17
    public static function setUpBeforeClass(): void
18
    {
19
        if (!extension_loaded('memcached')) {
20
            self::markTestSkipped('Required extension "memcached" is not loaded');
21
        }
22
23
        // check whether memcached is running and skip tests if not.
24
        if (!@stream_socket_client(MEMCACHED_HOST . ':' . MEMCACHED_PORT, $errorNumber, $errorDescription, 0.5)) {
25
            self::markTestSkipped('No memcached server running at ' . MEMCACHED_HOST . ':' . MEMCACHED_PORT . ' : ' . $errorNumber . ' - ' . $errorDescription);
26
        }
27
    }
28
29
    protected function tearDown(): void
30
    {
31
        MockHelper::$time = null;
32
    }
33
34
    protected function createCacheInstance($persistentId = '', array $servers = []): CacheInterface
35
    {
36
        if ($servers === []) {
37
            $servers = [[MEMCACHED_HOST, MEMCACHED_PORT]];
38
        }
39
        return new Memcached($persistentId, $servers);
40
    }
41
42
    public function testDeleteMultipleReturnsFalse(): void
43
    {
44
        $cache = $this->createCacheInstance();
45
46
        $memcachedStub = $this->createMock(\Memcached::class);
47
        $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

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

185
    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...
186
    {
187
        $cache = $this->createCacheInstance();
188
        $cache = $this->prepare($cache);
189
190
        $this->assertTrue($cache->clear());
191
        $this->assertNull($cache->get($key));
192
    }
193
194
    /**
195
     * @dataProvider dataProviderSetMultiple
196
     * @param int|null $ttl
197
     * @throws InvalidArgumentException
198
     */
199
    public function testSetMultiple(?int $ttl): void
200
    {
201
        $cache = $this->createCacheInstance();
202
        $cache->clear();
203
204
        $data = $this->getDataProviderData();
205
206
        $cache->setMultiple($data, $ttl);
207
208
        foreach ($data as $key => $value) {
209
            $this->assertSameExceptObject($value, $cache->get((string)$key));
210
        }
211
    }
212
213
    /**
214
     * @return array testing multiSet with and without expiry
215
     */
216
    public function dataProviderSetMultiple(): array
217
    {
218
        return [
219
            [null],
220
            [2],
221
        ];
222
    }
223
224
    public function testGetMultiple(): void
225
    {
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_map('strval', array_keys($data))));
234
    }
235
236
    public function testDeleteMultiple(): void
237
    {
238
        $cache = $this->createCacheInstance();
239
        $cache->clear();
240
241
        $data = $this->getDataProviderData();
242
        $keys = array_map('strval', array_keys($data));
243
244
        $cache->setMultiple($data);
245
246
        $this->assertSameExceptObject($data, $cache->getMultiple($keys));
247
248
        $cache->deleteMultiple($keys);
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($keys));
255
    }
256
257
    public function testZeroAndNegativeTtl(): void
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 = $this->createCacheInstance();
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
            ['', 0],
302
            [null, null],
303
            [0, 0],
304
            [new DateInterval('PT6H8M'), 6 * 3600 + 8 * 60],
305
            [new DateInterval('P2Y4D'), 2 * 365 * 24 * 3600 + 4 * 24 * 3600],
306
        ];
307
    }
308
309
    /**
310
     * @dataProvider ttlToExpirationProvider
311
     * @param mixed $ttl
312
     * @param mixed $expected
313
     * @throws ReflectionException
314
     */
315
    public function testTtlToExpiration($ttl, $expected): void
316
    {
317
        if ($expected === 'calculate_expiration') {
318
            MockHelper::$time = \time();
319
            $expected = MockHelper::$time + $ttl;
320
        }
321
        $cache = $this->createCacheInstance();
322
        $this->assertSameExceptObject($expected, $this->invokeMethod($cache, 'ttlToExpiration', [$ttl]));
323
    }
324
325
    public function ttlToExpirationProvider(): array
326
    {
327
        return [
328
            [3, 'calculate_expiration'],
329
            [null, 0],
330
            [-5, -1],
331
        ];
332
    }
333
334
    /**
335
     * @dataProvider iterableProvider
336
     * @param array $array
337
     * @param iterable $iterable
338
     * @throws InvalidArgumentException
339
     */
340
    public function testValuesAsIterable(array $array, iterable $iterable): void
341
    {
342
        $cache = $this->createCacheInstance();
343
        $cache->clear();
344
345
        $cache->setMultiple($iterable);
346
347
        $this->assertSameExceptObject($array, $cache->getMultiple(array_keys($array)));
348
    }
349
350
    public function iterableProvider(): array
351
    {
352
        return [
353
            'array' => [
354
                ['a' => 1, 'b' => 2,],
355
                ['a' => 1, 'b' => 2,],
356
            ],
357
            'ArrayIterator' => [
358
                ['a' => 1, 'b' => 2,],
359
                new \ArrayIterator(['a' => 1, 'b' => 2,]),
360
            ],
361
            'IteratorAggregate' => [
362
                ['a' => 1, 'b' => 2,],
363
                new class() implements \IteratorAggregate
364
                {
365
                    public function getIterator()
366
                    {
367
                        return new \ArrayIterator(['a' => 1, 'b' => 2,]);
368
                    }
369
                }
370
            ],
371
            'generator' => [
372
                ['a' => 1, 'b' => 2,],
373
                (static function () {
374
                    yield 'a' => 1;
375
                    yield 'b' => 2;
376
                })()
377
            ]
378
        ];
379
    }
380
381
    public function testGetCache(): void
382
    {
383
        $cache = $this->createCacheInstance();
384
        $memcached = $cache->getCache();
385
        $this->assertInstanceOf(\Memcached::class, $memcached);
386
    }
387
388
    public function testPersistentId(): void
389
    {
390
        $cache1 = $this->createCacheInstance();
391
        $memcached1 = $cache1->getCache();
392
        $this->assertFalse($memcached1->isPersistent());
393
394
        $cache2 = $this->createCacheInstance(microtime() . __METHOD__);
395
        $memcached2 = $cache2->getCache();
396
        $this->assertTrue($memcached2->isPersistent());
397
    }
398
399
    public function testGetNewServers(): void
400
    {
401
        $cache = $this->createCacheInstance();
402
403
        $memcachedStub = $this->createMock(\Memcached::class);
404
        $memcachedStub->method('getServerList')->willReturn([['host' => '1.1.1.1', 'port' => 11211]]);
405
406
        $this->setInaccessibleProperty($cache, 'cache', $memcachedStub);
407
408
        $newServers = $this->invokeMethod($cache, 'getNewServers', [
409
            [
410
                ['1.1.1.1', 11211, 1],
411
                ['2.2.2.2', 11211, 1],
412
            ],
413
        ]);
414
415
        $this->assertEquals([['2.2.2.2', 11211, 1]], $newServers);
416
    }
417
418
    public function testThatServerWeightIsOptional(): void
419
    {
420
        $cache = $this->createCacheInstance(microtime() . __METHOD__, [
421
            ['1.1.1.1', 11211, 1],
422
            ['2.2.2.2', 11211],
423
        ]);
424
425
        $memcached = $cache->getCache();
426
        $this->assertEquals([
427
            [
428
                'host' => '1.1.1.1',
429
                'port' => 11211,
430
                'type' => 'TCP',
431
            ],
432
            [
433
                'host' => '2.2.2.2',
434
                'port' => 11211,
435
                'type' => 'TCP',
436
            ],
437
        ], $memcached->getServerList());
438
    }
439
440
    /**
441
     * @dataProvider invalidServersConfigProvider
442
     * @param $servers
443
     */
444
    public function testInvalidServersConfig($servers): void
445
    {
446
        $this->expectException(CacheException::class);
447
        $cache = $this->createCacheInstance('', $servers);
0 ignored issues
show
Unused Code introduced by
The assignment to $cache is dead and can be removed.
Loading history...
448
    }
449
450
    public function invalidServersConfigProvider(): array
451
    {
452
        return [
453
            [[[]]],
454
            [[['1.1.1.1']]],
455
        ];
456
    }
457
458
    public function testSetWithDateIntervalTtl(): void
459
    {
460
        $cache = $this->createCacheInstance();
461
        $cache->clear();
462
463
        $cache->set('a', 1, new DateInterval('PT1H'));
464
        $this->assertSameExceptObject(1, $cache->get('a'));
465
466
        $cache->setMultiple(['b' => 2]);
467
        $this->assertSameExceptObject(['b' => 2], $cache->getMultiple(['b']));
468
    }
469
470
    public function testFailInitServers(): void
471
    {
472
        $this->expectException(CacheException::class);
473
474
        $cache = $this->createCacheInstance();
475
476
        $memcachedStub = $this->createMock(\Memcached::class);
477
        $memcachedStub->method('addServers')->willReturn(false);
478
479
        $this->setInaccessibleProperty($cache, 'cache', $memcachedStub);
480
481
        $this->invokeMethod($cache, 'initServers', [[]]);
482
    }
483
484
    public function testInitDefaultServer(): void
485
    {
486
        $cache = new Memcached();
487
        $memcached = $cache->getCache();
488
        $this->assertEquals([
489
            [
490
                'host' => '127.0.0.1',
491
                'port' => 11211,
492
                'type' => 'TCP',
493
            ],
494
        ], $memcached->getServerList());
495
    }
496
497
    public function testGetInvalidKey(): void
498
    {
499
        $this->expectException(InvalidArgumentException::class);
500
        $cache = $this->createCacheInstance();
501
        $cache->get(1);
502
    }
503
504
    public function testSetInvalidKey(): void
505
    {
506
        $this->expectException(InvalidArgumentException::class);
507
        $cache = $this->createCacheInstance();
508
        $cache->set(1, 1);
509
    }
510
511
    public function testDeleteInvalidKey(): void
512
    {
513
        $this->expectException(InvalidArgumentException::class);
514
        $cache = $this->createCacheInstance();
515
        $cache->delete(1);
516
    }
517
518
    public function testGetMultipleInvalidKeys(): void
519
    {
520
        $this->expectException(InvalidArgumentException::class);
521
        $cache = $this->createCacheInstance();
522
        $cache->getMultiple([true]);
523
    }
524
525
    public function testGetMultipleInvalidKeysNotIterable(): void
526
    {
527
        $this->expectException(InvalidArgumentException::class);
528
        $cache = $this->createCacheInstance();
529
        $cache->getMultiple(1);
530
    }
531
532
    public function testSetMultipleInvalidKeysNotIterable(): void
533
    {
534
        $this->expectException(InvalidArgumentException::class);
535
        $cache = $this->createCacheInstance();
536
        $cache->setMultiple(1);
537
    }
538
539
    public function testDeleteMultipleInvalidKeys(): void
540
    {
541
        $this->expectException(InvalidArgumentException::class);
542
        $cache = $this->createCacheInstance();
543
        $cache->deleteMultiple([true]);
544
    }
545
546
    public function testDeleteMultipleInvalidKeysNotIterable(): void
547
    {
548
        $this->expectException(InvalidArgumentException::class);
549
        $cache = $this->createCacheInstance();
550
        $cache->deleteMultiple(1);
551
    }
552
553
    public function testHasInvalidKey(): void
554
    {
555
        $this->expectException(InvalidArgumentException::class);
556
        $cache = $this->createCacheInstance();
557
        $cache->has(1);
558
    }
559
}
560