Passed
Pull Request — master (#3)
by Alexander
01:15
created

php$0 ➔ testDeleteMultipleInvalidKeys()   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_keys($data)));
234
    }
235
236
    public function testDeleteMultiple(): void
237
    {
238
        $cache = $this->createCacheInstance();
239
        $cache->clear();
240
241
        $data = $this->getDataProviderData();
242
243
        $cache->setMultiple($data);
244
245
        $this->assertSameExceptObject($data, $cache->getMultiple(array_keys($data)));
246
247
        $cache->deleteMultiple(array_keys($data));
248
249
        $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

249
        $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...
250
            return null;
251
        }, $data);
252
253
        $this->assertSameExceptObject($emptyData, $cache->getMultiple(array_keys($data)));
254
    }
255
256
    public function testZeroAndNegativeTtl(): void
257
    {
258
        $cache = $this->createCacheInstance();
259
        $cache->clear();
260
        $cache->setMultiple([
261
            'a' => 1,
262
            'b' => 2,
263
        ]);
264
265
        $this->assertTrue($cache->has('a'));
266
        $this->assertTrue($cache->has('b'));
267
268
        $cache->set('a', 11, -1);
269
270
        $this->assertFalse($cache->has('a'));
271
272
        $cache->set('b', 22, 0);
273
274
        $this->assertFalse($cache->has('b'));
275
    }
276
277
    /**
278
     * @dataProvider dataProviderNormalizeTtl
279
     * @param mixed $ttl
280
     * @param mixed $expectedResult
281
     * @throws ReflectionException
282
     */
283
    public function testNormalizeTtl($ttl, $expectedResult): void
284
    {
285
        $cache = $this->createCacheInstance();
286
        $this->assertSameExceptObject($expectedResult, $this->invokeMethod($cache, 'normalizeTtl', [$ttl]));
287
    }
288
289
    /**
290
     * Data provider for {@see testNormalizeTtl()}
291
     * @return array test data
292
     *
293
     * @throws \Exception
294
     */
295
    public function dataProviderNormalizeTtl(): array
296
    {
297
        return [
298
            [123, 123],
299
            ['123', 123],
300
            ['', 0],
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 = $this->createCacheInstance();
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
                {
364
                    public function getIterator()
365
                    {
366
                        return new \ArrayIterator(['a' => 1, 'b' => 2,]);
367
                    }
368
                }
369
            ],
370
            'generator' => [
371
                ['a' => 1, 'b' => 2,],
372
                (static function () {
373
                    yield 'a' => 1;
374
                    yield 'b' => 2;
375
                })()
376
            ]
377
        ];
378
    }
379
380
    public function testGetCache(): void
381
    {
382
        $cache = $this->createCacheInstance();
383
        $memcached = $cache->getCache();
384
        $this->assertInstanceOf(\Memcached::class, $memcached);
385
    }
386
387
    public function testPersistentId(): void
388
    {
389
        $cache1 = $this->createCacheInstance();
390
        $memcached1 = $cache1->getCache();
391
        $this->assertFalse($memcached1->isPersistent());
392
393
        $cache2 = $this->createCacheInstance(microtime() . __METHOD__);
394
        $memcached2 = $cache2->getCache();
395
        $this->assertTrue($memcached2->isPersistent());
396
    }
397
398
    public function testGetNewServers(): void
399
    {
400
        $cache = $this->createCacheInstance();
401
402
        $memcachedStub = $this->createMock(\Memcached::class);
403
        $memcachedStub->method('getServerList')->willReturn([['host' => '1.1.1.1', 'port' => 11211]]);
404
405
        $this->setInaccessibleProperty($cache, 'cache', $memcachedStub);
406
407
        $newServers = $this->invokeMethod($cache, 'getNewServers', [
408
            [
409
                ['1.1.1.1', 11211, 1],
410
                ['2.2.2.2', 11211, 1],
411
            ],
412
        ]);
413
414
        $this->assertEquals([['2.2.2.2', 11211, 1]], $newServers);
415
    }
416
417
    public function testThatServerWeightIsOptional(): void
418
    {
419
        $cache = $this->createCacheInstance(microtime() . __METHOD__, [
420
            ['1.1.1.1', 11211, 1],
421
            ['2.2.2.2', 11211],
422
        ]);
423
424
        $memcached = $cache->getCache();
425
        $this->assertEquals([
426
            [
427
                'host' => '1.1.1.1',
428
                'port' => 11211,
429
                'type' => 'TCP',
430
            ],
431
            [
432
                'host' => '2.2.2.2',
433
                'port' => 11211,
434
                'type' => 'TCP',
435
            ],
436
        ], $memcached->getServerList());
437
    }
438
439
    /**
440
     * @dataProvider invalidServersConfigProvider
441
     * @param $servers
442
     */
443
    public function testInvalidServersConfig($servers): void
444
    {
445
        $this->expectException(CacheException::class);
446
        $cache = $this->createCacheInstance('', $servers);
0 ignored issues
show
Unused Code introduced by
The assignment to $cache is dead and can be removed.
Loading history...
447
    }
448
449
    public function invalidServersConfigProvider(): array
450
    {
451
        return [
452
            [[[]]],
453
            [[['1.1.1.1']]],
454
        ];
455
    }
456
457
    public function testSetWithDateIntervalTtl(): void
458
    {
459
        $cache = $this->createCacheInstance();
460
        $cache->clear();
461
462
        $cache->set('a', 1, new DateInterval('PT1H'));
463
        $this->assertSameExceptObject(1, $cache->get('a'));
464
465
        $cache->setMultiple(['b' => 2]);
466
        $this->assertSameExceptObject(['b' => 2], $cache->getMultiple(['b']));
467
    }
468
469
    public function testFailInitServers(): void
470
    {
471
        $this->expectException(CacheException::class);
472
473
        $cache = $this->createCacheInstance();
474
475
        $memcachedStub = $this->createMock(\Memcached::class);
476
        $memcachedStub->method('addServers')->willReturn(false);
477
478
        $this->setInaccessibleProperty($cache, 'cache', $memcachedStub);
479
480
        $this->invokeMethod($cache, 'initServers', [[]]);
481
    }
482
483
    public function testInitDefaultServer(): void
484
    {
485
        $cache = new Memcached();
486
        $memcached = $cache->getCache();
487
        $this->assertEquals([
488
            [
489
                'host' => '127.0.0.1',
490
                'port' => 11211,
491
                'type' => 'TCP',
492
            ],
493
        ], $memcached->getServerList());
494
    }
495
496
    public function testGetInvalidKey(): void
497
    {
498
        $this->expectException(InvalidArgumentException::class);
499
        $cache = $this->createCacheInstance();
500
        $cache->get(1);
501
    }
502
503
    public function testSetInvalidKey(): void
504
    {
505
        $this->expectException(InvalidArgumentException::class);
506
        $cache = $this->createCacheInstance();
507
        $cache->set(1, 1);
508
    }
509
510
    public function testDeleteInvalidKey(): void
511
    {
512
        $this->expectException(InvalidArgumentException::class);
513
        $cache = $this->createCacheInstance();
514
        $cache->delete(1);
515
    }
516
517
    public function testGetMultipleInvalidKeys(): void
518
    {
519
        $this->expectException(InvalidArgumentException::class);
520
        $cache = $this->createCacheInstance();
521
        $cache->getMultiple([true]);
522
    }
523
524
    public function testGetMultipleInvalidKeysNotIterable(): void
525
    {
526
        $this->expectException(InvalidArgumentException::class);
527
        $cache = $this->createCacheInstance();
528
        $cache->getMultiple(1);
529
    }
530
531
    public function testSetMultipleInvalidKeysNotIterable(): void
532
    {
533
        $this->expectException(InvalidArgumentException::class);
534
        $cache = $this->createCacheInstance();
535
        $cache->setMultiple(1);
536
    }
537
538
    public function testDeleteMultipleInvalidKeys(): void
539
    {
540
        $this->expectException(InvalidArgumentException::class);
541
        $cache = $this->createCacheInstance();
542
        $cache->deleteMultiple([true]);
543
    }
544
545
    public function testDeleteMultipleInvalidKeysNotIterable(): void
546
    {
547
        $this->expectException(InvalidArgumentException::class);
548
        $cache = $this->createCacheInstance();
549
        $cache->deleteMultiple(1);
550
    }
551
552
    public function testHasInvalidKey(): void
553
    {
554
        $this->expectException(InvalidArgumentException::class);
555
        $cache = $this->createCacheInstance();
556
        $cache->has(1);
557
    }
558
}
559