2013-03-23 16:15:36 -05:00
|
|
|
<?php
|
|
|
|
|
|
2017-01-17 14:30:19 +00:00
|
|
|
namespace Illuminate\Tests\Cache;
|
|
|
|
|
|
2019-07-02 21:38:00 +01:00
|
|
|
use ArrayIterator;
|
2022-02-09 18:07:21 +03:30
|
|
|
use BadMethodCallException;
|
2019-09-10 17:16:05 +02:00
|
|
|
use DateInterval;
|
|
|
|
|
use DateTime;
|
2017-07-13 15:10:52 +02:00
|
|
|
use DateTimeImmutable;
|
2018-09-30 23:03:32 +03:00
|
|
|
use Illuminate\Cache\ArrayStore;
|
2020-10-13 15:28:10 +02:00
|
|
|
use Illuminate\Cache\FileStore;
|
2026-01-08 17:00:56 -03:00
|
|
|
use Illuminate\Cache\Lock;
|
2026-02-25 23:27:43 +03:30
|
|
|
use Illuminate\Cache\MemcachedStore;
|
2018-09-30 23:03:32 +03:00
|
|
|
use Illuminate\Cache\RedisStore;
|
|
|
|
|
use Illuminate\Cache\Repository;
|
2020-10-13 15:28:10 +02:00
|
|
|
use Illuminate\Cache\TaggableStore;
|
2022-02-09 18:07:21 +03:30
|
|
|
use Illuminate\Cache\TaggedCache;
|
2018-09-30 23:03:32 +03:00
|
|
|
use Illuminate\Container\Container;
|
2026-01-08 17:00:56 -03:00
|
|
|
use Illuminate\Contracts\Cache\LockProvider;
|
|
|
|
|
use Illuminate\Contracts\Cache\LockTimeoutException;
|
2018-09-30 23:03:32 +03:00
|
|
|
use Illuminate\Contracts\Cache\Store;
|
2019-09-10 17:16:05 +02:00
|
|
|
use Illuminate\Events\Dispatcher;
|
2022-02-09 18:07:21 +03:30
|
|
|
use Illuminate\Filesystem\Filesystem;
|
2019-09-10 17:16:05 +02:00
|
|
|
use Illuminate\Support\Carbon;
|
2026-01-30 00:32:55 -06:00
|
|
|
use InvalidArgumentException;
|
2019-09-10 17:16:05 +02:00
|
|
|
use Mockery as m;
|
2023-10-26 22:09:14 +08:00
|
|
|
use PHPUnit\Framework\Attributes\DataProvider;
|
2019-09-10 17:16:05 +02:00
|
|
|
use PHPUnit\Framework\TestCase;
|
2013-03-23 16:15:36 -05:00
|
|
|
|
2016-12-30 21:31:11 +01:00
|
|
|
class CacheRepositoryTest extends TestCase
|
2015-06-01 16:26:53 +01:00
|
|
|
{
|
2022-04-04 17:11:11 +02:00
|
|
|
protected function setUp(): void
|
|
|
|
|
{
|
|
|
|
|
parent::setUp();
|
|
|
|
|
|
2022-11-23 05:10:22 +08:00
|
|
|
Carbon::setTestNow(Carbon::parse(self::getTestDate()));
|
2022-04-04 17:11:11 +02:00
|
|
|
}
|
|
|
|
|
|
2019-02-07 16:07:08 +01:00
|
|
|
protected function tearDown(): void
|
2015-06-01 15:56:31 +01:00
|
|
|
{
|
2022-02-03 17:12:45 +01:00
|
|
|
Carbon::setTestNow(null);
|
2026-01-05 18:12:30 +01:00
|
|
|
|
|
|
|
|
parent::tearDown();
|
2015-06-01 15:56:31 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testGetReturnsValueFromCache()
|
|
|
|
|
{
|
|
|
|
|
$repo = $this->getRepository();
|
|
|
|
|
$repo->getStore()->shouldReceive('get')->once()->with('foo')->andReturn('bar');
|
2019-08-27 14:48:17 +02:00
|
|
|
$this->assertSame('bar', $repo->get('foo'));
|
2015-06-01 15:56:31 +01:00
|
|
|
}
|
|
|
|
|
|
2015-12-01 13:55:43 -06:00
|
|
|
public function testGetReturnsMultipleValuesFromCacheWhenGivenAnArray()
|
|
|
|
|
{
|
|
|
|
|
$repo = $this->getRepository();
|
|
|
|
|
$repo->getStore()->shouldReceive('many')->once()->with(['foo', 'bar'])->andReturn(['foo' => 'bar', 'bar' => 'baz']);
|
|
|
|
|
$this->assertEquals(['foo' => 'bar', 'bar' => 'baz'], $repo->get(['foo', 'bar']));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testGetReturnsMultipleValuesFromCacheWhenGivenAnArrayWithDefaultValues()
|
|
|
|
|
{
|
|
|
|
|
$repo = $this->getRepository();
|
|
|
|
|
$repo->getStore()->shouldReceive('many')->once()->with(['foo', 'bar'])->andReturn(['foo' => null, 'bar' => 'baz']);
|
|
|
|
|
$this->assertEquals(['foo' => 'default', 'bar' => 'baz'], $repo->get(['foo' => 'default', 'bar']));
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-16 01:45:02 +03:00
|
|
|
public function testGetReturnsMultipleValuesFromCacheWhenGivenAnArrayOfOneTwoThree()
|
|
|
|
|
{
|
|
|
|
|
$repo = $this->getRepository();
|
|
|
|
|
$repo->getStore()->shouldReceive('many')->once()->with([1, 2, 3])->andReturn([1 => null, 2 => null, 3 => null]);
|
|
|
|
|
$this->assertEquals([1 => null, 2 => null, 3 => null], $repo->get([1, 2, 3]));
|
|
|
|
|
}
|
|
|
|
|
|
2015-06-01 15:56:31 +01:00
|
|
|
public function testDefaultValueIsReturned()
|
|
|
|
|
{
|
|
|
|
|
$repo = $this->getRepository();
|
2018-01-12 22:51:45 +01:00
|
|
|
$repo->getStore()->shouldReceive('get')->times(2)->andReturn(null);
|
2019-08-27 14:48:17 +02:00
|
|
|
$this->assertSame('bar', $repo->get('foo', 'bar'));
|
|
|
|
|
$this->assertSame('baz', $repo->get('boom', function () {
|
2016-05-28 22:16:16 +01:00
|
|
|
return 'baz';
|
|
|
|
|
}));
|
2015-06-01 15:56:31 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testSettingDefaultCacheTime()
|
|
|
|
|
{
|
|
|
|
|
$repo = $this->getRepository();
|
|
|
|
|
$repo->setDefaultCacheTime(10);
|
|
|
|
|
$this->assertEquals(10, $repo->getDefaultCacheTime());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testHasMethod()
|
|
|
|
|
{
|
|
|
|
|
$repo = $this->getRepository();
|
|
|
|
|
$repo->getStore()->shouldReceive('get')->once()->with('foo')->andReturn(null);
|
|
|
|
|
$repo->getStore()->shouldReceive('get')->once()->with('bar')->andReturn('bar');
|
2019-02-22 21:22:47 +01:00
|
|
|
$repo->getStore()->shouldReceive('get')->once()->with('baz')->andReturn(false);
|
2015-06-01 15:56:31 +01:00
|
|
|
|
|
|
|
|
$this->assertTrue($repo->has('bar'));
|
|
|
|
|
$this->assertFalse($repo->has('foo'));
|
2019-02-22 21:22:47 +01:00
|
|
|
$this->assertTrue($repo->has('baz'));
|
2015-06-01 15:56:31 +01:00
|
|
|
}
|
|
|
|
|
|
2018-11-02 15:31:57 +02:00
|
|
|
public function testMissingMethod()
|
|
|
|
|
{
|
|
|
|
|
$repo = $this->getRepository();
|
|
|
|
|
$repo->getStore()->shouldReceive('get')->once()->with('foo')->andReturn(null);
|
|
|
|
|
$repo->getStore()->shouldReceive('get')->once()->with('bar')->andReturn('bar');
|
|
|
|
|
|
|
|
|
|
$this->assertTrue($repo->missing('foo'));
|
|
|
|
|
$this->assertFalse($repo->missing('bar'));
|
|
|
|
|
}
|
|
|
|
|
|
2015-06-01 15:56:31 +01:00
|
|
|
public function testRememberMethodCallsPutAndReturnsDefault()
|
|
|
|
|
{
|
|
|
|
|
$repo = $this->getRepository();
|
2018-01-12 22:51:45 +01:00
|
|
|
$repo->getStore()->shouldReceive('get')->once()->andReturn(null);
|
2015-06-01 15:56:31 +01:00
|
|
|
$repo->getStore()->shouldReceive('put')->once()->with('foo', 'bar', 10);
|
2016-05-28 22:16:16 +01:00
|
|
|
$result = $repo->remember('foo', 10, function () {
|
|
|
|
|
return 'bar';
|
|
|
|
|
});
|
2019-08-27 14:48:17 +02:00
|
|
|
$this->assertSame('bar', $result);
|
2015-06-01 15:56:31 +01:00
|
|
|
|
2015-09-10 19:28:55 +02:00
|
|
|
$repo = $this->getRepository();
|
2018-01-12 22:51:45 +01:00
|
|
|
$repo->getStore()->shouldReceive('get')->times(2)->andReturn(null);
|
2019-01-22 17:10:50 +01:00
|
|
|
$repo->getStore()->shouldReceive('put')->once()->with('foo', 'bar', 602);
|
|
|
|
|
$repo->getStore()->shouldReceive('put')->once()->with('baz', 'qux', 598);
|
2016-05-28 22:16:16 +01:00
|
|
|
$result = $repo->remember('foo', Carbon::now()->addMinutes(10)->addSeconds(2), function () {
|
|
|
|
|
return 'bar';
|
|
|
|
|
});
|
2019-08-27 14:48:17 +02:00
|
|
|
$this->assertSame('bar', $result);
|
2016-05-28 22:16:16 +01:00
|
|
|
$result = $repo->remember('baz', Carbon::now()->addMinutes(10)->subSeconds(2), function () {
|
|
|
|
|
return 'qux';
|
|
|
|
|
});
|
2019-08-27 14:48:17 +02:00
|
|
|
$this->assertSame('qux', $result);
|
2021-11-18 14:46:23 +00:00
|
|
|
|
2025-03-12 09:21:02 -05:00
|
|
|
// Use a callable...
|
2021-11-18 15:46:00 +01:00
|
|
|
$repo = $this->getRepository();
|
|
|
|
|
$repo->getStore()->shouldReceive('get')->once()->andReturn(null);
|
|
|
|
|
$repo->getStore()->shouldReceive('put')->once()->with('foo', 'bar', 10);
|
|
|
|
|
$result = $repo->remember('foo', function () {
|
|
|
|
|
return 10;
|
|
|
|
|
}, function () {
|
|
|
|
|
return 'bar';
|
|
|
|
|
});
|
|
|
|
|
$this->assertSame('bar', $result);
|
2015-06-01 15:56:31 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testRememberForeverMethodCallsForeverAndReturnsDefault()
|
|
|
|
|
{
|
|
|
|
|
$repo = $this->getRepository();
|
2018-01-12 22:51:45 +01:00
|
|
|
$repo->getStore()->shouldReceive('get')->once()->andReturn(null);
|
2015-06-01 15:56:31 +01:00
|
|
|
$repo->getStore()->shouldReceive('forever')->once()->with('foo', 'bar');
|
2016-05-28 22:16:16 +01:00
|
|
|
$result = $repo->rememberForever('foo', function () {
|
|
|
|
|
return 'bar';
|
|
|
|
|
});
|
2019-08-27 14:48:17 +02:00
|
|
|
$this->assertSame('bar', $result);
|
2015-06-01 15:56:31 +01:00
|
|
|
}
|
|
|
|
|
|
2015-12-01 13:55:43 -06:00
|
|
|
public function testPuttingMultipleItemsInCache()
|
|
|
|
|
{
|
|
|
|
|
$repo = $this->getRepository();
|
|
|
|
|
$repo->getStore()->shouldReceive('putMany')->once()->with(['foo' => 'bar', 'bar' => 'baz'], 1);
|
|
|
|
|
$repo->put(['foo' => 'bar', 'bar' => 'baz'], 1);
|
|
|
|
|
}
|
|
|
|
|
|
2019-07-02 21:38:00 +01:00
|
|
|
public function testSettingMultipleItemsInCacheArray()
|
2017-07-26 14:43:39 +02:00
|
|
|
{
|
|
|
|
|
$repo = $this->getRepository();
|
2018-12-01 20:30:44 +01:00
|
|
|
$repo->getStore()->shouldReceive('putMany')->once()->with(['foo' => 'bar', 'bar' => 'baz'], 1)->andReturn(true);
|
|
|
|
|
$result = $repo->setMultiple(['foo' => 'bar', 'bar' => 'baz'], 1);
|
|
|
|
|
$this->assertTrue($result);
|
2017-07-26 14:43:39 +02:00
|
|
|
}
|
|
|
|
|
|
2019-07-02 21:38:00 +01:00
|
|
|
public function testSettingMultipleItemsInCacheIterator()
|
|
|
|
|
{
|
|
|
|
|
$repo = $this->getRepository();
|
|
|
|
|
$repo->getStore()->shouldReceive('putMany')->once()->with(['foo' => 'bar', 'bar' => 'baz'], 1)->andReturn(true);
|
|
|
|
|
$result = $repo->setMultiple(new ArrayIterator(['foo' => 'bar', 'bar' => 'baz']), 1);
|
|
|
|
|
$this->assertTrue($result);
|
|
|
|
|
}
|
|
|
|
|
|
2019-01-18 17:25:55 +01:00
|
|
|
public function testPutWithNullTTLRemembersItemForever()
|
|
|
|
|
{
|
|
|
|
|
$repo = $this->getRepository();
|
|
|
|
|
$repo->getStore()->shouldReceive('forever')->once()->with('foo', 'bar')->andReturn(true);
|
|
|
|
|
$this->assertTrue($repo->put('foo', 'bar'));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testPutWithDatetimeInPastOrZeroSecondsRemovesOldItem()
|
2015-09-10 18:46:09 +02:00
|
|
|
{
|
|
|
|
|
$repo = $this->getRepository();
|
|
|
|
|
$repo->getStore()->shouldReceive('put')->never();
|
2019-01-18 17:25:55 +01:00
|
|
|
$repo->getStore()->shouldReceive('forget')->twice()->andReturn(true);
|
2018-12-01 20:30:44 +01:00
|
|
|
$result = $repo->put('foo', 'bar', Carbon::now()->subMinutes(10));
|
2019-01-18 17:25:55 +01:00
|
|
|
$this->assertTrue($result);
|
2018-12-01 20:30:44 +01:00
|
|
|
$result = $repo->put('foo', 'bar', Carbon::now());
|
2019-01-18 17:25:55 +01:00
|
|
|
$this->assertTrue($result);
|
2015-09-10 18:46:09 +02:00
|
|
|
}
|
|
|
|
|
|
2019-01-18 17:25:55 +01:00
|
|
|
public function testPutManyWithNullTTLRemembersItemsForever()
|
2015-09-10 18:46:53 +02:00
|
|
|
{
|
|
|
|
|
$repo = $this->getRepository();
|
2019-01-18 17:25:55 +01:00
|
|
|
$repo->getStore()->shouldReceive('forever')->with('foo', 'bar')->andReturn(true);
|
|
|
|
|
$repo->getStore()->shouldReceive('forever')->with('bar', 'baz')->andReturn(true);
|
|
|
|
|
$this->assertTrue($repo->putMany(['foo' => 'bar', 'bar' => 'baz']));
|
2015-09-10 18:46:53 +02:00
|
|
|
}
|
|
|
|
|
|
2019-01-14 17:21:59 +01:00
|
|
|
public function testAddWithStoreFailureReturnsFalse()
|
|
|
|
|
{
|
|
|
|
|
$repo = $this->getRepository();
|
|
|
|
|
$repo->getStore()->shouldReceive('add')->never();
|
|
|
|
|
$repo->getStore()->shouldReceive('get')->andReturn(null);
|
|
|
|
|
$repo->getStore()->shouldReceive('put')->andReturn(false);
|
|
|
|
|
$this->assertFalse($repo->add('foo', 'bar', 60));
|
|
|
|
|
}
|
|
|
|
|
|
2016-10-13 01:06:07 +03:30
|
|
|
public function testCacheAddCallsRedisStoreAdd()
|
|
|
|
|
{
|
2018-09-30 23:03:32 +03:00
|
|
|
$store = m::mock(RedisStore::class);
|
2016-10-13 01:06:07 +03:30
|
|
|
$store->shouldReceive('add')->once()->with('k', 'v', 60)->andReturn(true);
|
2018-09-30 23:03:32 +03:00
|
|
|
$repository = new Repository($store);
|
2016-10-13 01:06:07 +03:30
|
|
|
$this->assertTrue($repository->add('k', 'v', 60));
|
|
|
|
|
}
|
|
|
|
|
|
2022-02-05 18:44:21 +03:30
|
|
|
public function testAddMethodCanAcceptDateIntervals()
|
|
|
|
|
{
|
|
|
|
|
$storeWithAdd = m::mock(RedisStore::class);
|
|
|
|
|
$storeWithAdd->shouldReceive('add')->once()->with('k', 'v', 61)->andReturn(true);
|
|
|
|
|
$repository = new Repository($storeWithAdd);
|
|
|
|
|
$this->assertTrue($repository->add('k', 'v', DateInterval::createFromDateString('61 seconds')));
|
|
|
|
|
|
|
|
|
|
$storeWithoutAdd = m::mock(ArrayStore::class);
|
|
|
|
|
$this->assertFalse(method_exists(ArrayStore::class, 'add'), 'This store should not have add method on it.');
|
|
|
|
|
$storeWithoutAdd->shouldReceive('get')->once()->with('k')->andReturn(null);
|
|
|
|
|
$storeWithoutAdd->shouldReceive('put')->once()->with('k', 'v', 60)->andReturn(true);
|
|
|
|
|
$repository = new Repository($storeWithoutAdd);
|
|
|
|
|
$this->assertTrue($repository->add('k', 'v', DateInterval::createFromDateString('60 seconds')));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testAddMethodCanAcceptDateTimeInterface()
|
|
|
|
|
{
|
|
|
|
|
$withAddStore = m::mock(RedisStore::class);
|
|
|
|
|
$withAddStore->shouldReceive('add')->once()->with('k', 'v', 61)->andReturn(true);
|
|
|
|
|
$repository = new Repository($withAddStore);
|
|
|
|
|
$this->assertTrue($repository->add('k', 'v', Carbon::now()->addSeconds(61)));
|
|
|
|
|
|
|
|
|
|
$noAddStore = m::mock(ArrayStore::class);
|
|
|
|
|
$this->assertFalse(method_exists(ArrayStore::class, 'add'), 'This store should not have add method on it.');
|
|
|
|
|
$noAddStore->shouldReceive('get')->once()->with('k')->andReturn(null);
|
|
|
|
|
$noAddStore->shouldReceive('put')->once()->with('k', 'v', 62)->andReturn(true);
|
|
|
|
|
$repository = new Repository($noAddStore);
|
|
|
|
|
$this->assertTrue($repository->add('k', 'v', Carbon::now()->addSeconds(62)));
|
|
|
|
|
}
|
|
|
|
|
|
2019-01-18 17:25:55 +01:00
|
|
|
public function testAddWithNullTTLRemembersItemForever()
|
|
|
|
|
{
|
|
|
|
|
$repo = $this->getRepository();
|
|
|
|
|
$repo->getStore()->shouldReceive('get')->once()->with('foo')->andReturn(null);
|
|
|
|
|
$repo->getStore()->shouldReceive('forever')->once()->with('foo', 'bar')->andReturn(true);
|
|
|
|
|
$this->assertTrue($repo->add('foo', 'bar'));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testAddWithDatetimeInPastOrZeroSecondsReturnsImmediately()
|
|
|
|
|
{
|
|
|
|
|
$repo = $this->getRepository();
|
|
|
|
|
$repo->getStore()->shouldReceive('add', 'get', 'put')->never();
|
|
|
|
|
$result = $repo->add('foo', 'bar', Carbon::now()->subMinutes(10));
|
|
|
|
|
$this->assertFalse($result);
|
|
|
|
|
$result = $repo->add('foo', 'bar', Carbon::now());
|
|
|
|
|
$this->assertFalse($result);
|
2022-02-05 18:44:21 +03:30
|
|
|
$result = $repo->add('foo', 'bar', -1);
|
|
|
|
|
$this->assertFalse($result);
|
2019-01-18 17:25:55 +01:00
|
|
|
}
|
|
|
|
|
|
2022-11-23 05:10:22 +08:00
|
|
|
public static function dataProviderTestGetSeconds()
|
2017-07-13 15:10:52 +02:00
|
|
|
{
|
|
|
|
|
return [
|
2022-11-23 05:10:22 +08:00
|
|
|
[Carbon::parse(self::getTestDate())->addMinutes(5)],
|
|
|
|
|
[(new DateTime(self::getTestDate()))->modify('+5 minutes')],
|
|
|
|
|
[(new DateTimeImmutable(self::getTestDate()))->modify('+5 minutes')],
|
2017-07-13 15:10:52 +02:00
|
|
|
[new DateInterval('PT5M')],
|
2019-01-22 17:10:50 +01:00
|
|
|
[300],
|
2017-07-13 15:10:52 +02:00
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2020-03-28 18:00:42 +01:00
|
|
|
* @param mixed $duration
|
2017-07-13 15:10:52 +02:00
|
|
|
*/
|
2023-10-26 22:09:14 +08:00
|
|
|
#[DataProvider('dataProviderTestGetSeconds')]
|
2019-01-22 17:10:50 +01:00
|
|
|
public function testGetSeconds($duration)
|
2017-07-13 15:10:52 +02:00
|
|
|
{
|
|
|
|
|
$repo = $this->getRepository();
|
2019-01-22 17:10:50 +01:00
|
|
|
$repo->getStore()->shouldReceive('put')->once()->with($key = 'foo', $value = 'bar', 300);
|
2017-07-13 15:10:52 +02:00
|
|
|
$repo->put($key, $value, $duration);
|
|
|
|
|
}
|
|
|
|
|
|
2015-06-01 15:56:31 +01:00
|
|
|
public function testRegisterMacroWithNonStaticCall()
|
|
|
|
|
{
|
|
|
|
|
$repo = $this->getRepository();
|
2016-05-28 22:16:16 +01:00
|
|
|
$repo::macro(__CLASS__, function () {
|
|
|
|
|
return 'Taylor';
|
|
|
|
|
});
|
2020-10-05 21:58:25 +03:00
|
|
|
$this->assertSame('Taylor', $repo->{__CLASS__}());
|
2015-06-01 15:56:31 +01:00
|
|
|
}
|
|
|
|
|
|
2017-07-26 14:43:39 +02:00
|
|
|
public function testForgettingCacheKey()
|
|
|
|
|
{
|
|
|
|
|
$repo = $this->getRepository();
|
|
|
|
|
$repo->getStore()->shouldReceive('forget')->once()->with('a-key')->andReturn(true);
|
|
|
|
|
$repo->forget('a-key');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testRemovingCacheKey()
|
|
|
|
|
{
|
|
|
|
|
// Alias of Forget
|
|
|
|
|
$repo = $this->getRepository();
|
|
|
|
|
$repo->getStore()->shouldReceive('forget')->once()->with('a-key')->andReturn(true);
|
|
|
|
|
$repo->delete('a-key');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testSettingCache()
|
|
|
|
|
{
|
|
|
|
|
$repo = $this->getRepository();
|
2018-12-01 20:30:44 +01:00
|
|
|
$repo->getStore()->shouldReceive('put')->with($key = 'foo', $value = 'bar', 1)->andReturn(true);
|
|
|
|
|
$result = $repo->set($key, $value, 1);
|
|
|
|
|
$this->assertTrue($result);
|
2017-07-26 14:43:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testClearingWholeCache()
|
|
|
|
|
{
|
|
|
|
|
$repo = $this->getRepository();
|
|
|
|
|
$repo->getStore()->shouldReceive('flush')->andReturn(true);
|
|
|
|
|
$repo->clear();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testGettingMultipleValuesFromCache()
|
|
|
|
|
{
|
|
|
|
|
$keys = ['key1', 'key2', 'key3'];
|
2019-07-03 00:36:53 +01:00
|
|
|
$default = 5;
|
2017-07-26 14:43:39 +02:00
|
|
|
|
|
|
|
|
$repo = $this->getRepository();
|
2019-07-03 00:36:53 +01:00
|
|
|
$repo->getStore()->shouldReceive('many')->once()->with(['key1', 'key2', 'key3'])->andReturn(['key1' => 1, 'key2' => null, 'key3' => null]);
|
|
|
|
|
$this->assertEquals(['key1' => 1, 'key2' => 5, 'key3' => 5], $repo->getMultiple($keys, $default));
|
2017-07-26 14:43:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testRemovingMultipleKeys()
|
|
|
|
|
{
|
|
|
|
|
$repo = $this->getRepository();
|
|
|
|
|
$repo->getStore()->shouldReceive('forget')->once()->with('a-key')->andReturn(true);
|
|
|
|
|
$repo->getStore()->shouldReceive('forget')->once()->with('a-second-key')->andReturn(true);
|
2019-01-22 15:54:08 +01:00
|
|
|
|
|
|
|
|
$this->assertTrue($repo->deleteMultiple(['a-key', 'a-second-key']));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testRemovingMultipleKeysFailsIfOneFails()
|
|
|
|
|
{
|
|
|
|
|
$repo = $this->getRepository();
|
|
|
|
|
$repo->getStore()->shouldReceive('forget')->once()->with('a-key')->andReturn(true);
|
|
|
|
|
$repo->getStore()->shouldReceive('forget')->once()->with('a-second-key')->andReturn(false);
|
|
|
|
|
|
|
|
|
|
$this->assertFalse($repo->deleteMultiple(['a-key', 'a-second-key']));
|
2017-07-26 14:43:39 +02:00
|
|
|
}
|
|
|
|
|
|
2018-07-12 06:52:00 +08:00
|
|
|
public function testAllTagsArePassedToTaggableStore()
|
|
|
|
|
{
|
2018-09-30 23:03:32 +03:00
|
|
|
$store = m::mock(ArrayStore::class);
|
|
|
|
|
$repo = new Repository($store);
|
2018-07-12 06:52:00 +08:00
|
|
|
|
|
|
|
|
$taggedCache = m::mock();
|
|
|
|
|
$taggedCache->shouldReceive('setDefaultCacheTime');
|
|
|
|
|
$store->shouldReceive('tags')->once()->with(['foo', 'bar', 'baz'])->andReturn($taggedCache);
|
|
|
|
|
$repo->tags('foo', 'bar', 'baz');
|
|
|
|
|
}
|
|
|
|
|
|
2022-02-09 18:07:21 +03:30
|
|
|
public function testItThrowsExceptionWhenStoreDoesNotSupportTags()
|
|
|
|
|
{
|
|
|
|
|
$this->expectException(BadMethodCallException::class);
|
|
|
|
|
|
|
|
|
|
$store = new FileStore(new Filesystem, '/usr');
|
|
|
|
|
$this->assertFalse(method_exists($store, 'tags'), 'Store should not support tagging.');
|
|
|
|
|
(new Repository($store))->tags('foo');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testTagMethodReturnsTaggedCache()
|
|
|
|
|
{
|
|
|
|
|
$store = (new Repository(new ArrayStore()))->tags('foo');
|
|
|
|
|
|
|
|
|
|
$this->assertInstanceOf(TaggedCache::class, $store);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testPossibleInputTypesToTags()
|
|
|
|
|
{
|
|
|
|
|
$repo = new Repository(new ArrayStore());
|
|
|
|
|
|
|
|
|
|
$store = $repo->tags('foo');
|
|
|
|
|
$this->assertEquals(['foo'], $store->getTags()->getNames());
|
|
|
|
|
|
|
|
|
|
$store = $repo->tags(['foo!', 'Kangaroo']);
|
|
|
|
|
$this->assertEquals(['foo!', 'Kangaroo'], $store->getTags()->getNames());
|
|
|
|
|
|
|
|
|
|
$store = $repo->tags('r1', 'r2', 'r3');
|
|
|
|
|
$this->assertEquals(['r1', 'r2', 'r3'], $store->getTags()->getNames());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testEventDispatcherIsPassedToStoreFromRepository()
|
|
|
|
|
{
|
|
|
|
|
$repo = new Repository(new ArrayStore());
|
|
|
|
|
$repo->setEventDispatcher(new Dispatcher());
|
|
|
|
|
|
|
|
|
|
$store = $repo->tags('foo');
|
|
|
|
|
|
|
|
|
|
$this->assertSame($store->getEventDispatcher(), $repo->getEventDispatcher());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testDefaultCacheLifeTimeIsSetOnTaggableStore()
|
|
|
|
|
{
|
|
|
|
|
$repo = new Repository(new ArrayStore());
|
|
|
|
|
$repo->setDefaultCacheTime(random_int(1, 100));
|
|
|
|
|
|
|
|
|
|
$store = $repo->tags('foo');
|
|
|
|
|
|
|
|
|
|
$this->assertSame($store->getDefaultCacheTime(), $repo->getDefaultCacheTime());
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-25 23:27:43 +03:30
|
|
|
public function testFlushLocksDelegatesToStore()
|
|
|
|
|
{
|
|
|
|
|
$flushable = m::mock(RedisStore::class);
|
|
|
|
|
$flushable->shouldReceive('flushLocks')->once()->andReturn(true);
|
|
|
|
|
|
|
|
|
|
$repo = new Repository($flushable);
|
|
|
|
|
|
|
|
|
|
$this->assertTrue($repo->flushLocks());
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-13 15:28:10 +02:00
|
|
|
public function testTaggableRepositoriesSupportTags()
|
|
|
|
|
{
|
|
|
|
|
$taggable = m::mock(TaggableStore::class);
|
|
|
|
|
$taggableRepo = new Repository($taggable);
|
|
|
|
|
|
|
|
|
|
$this->assertTrue($taggableRepo->supportsTags());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testNonTaggableRepositoryDoesNotSupportTags()
|
|
|
|
|
{
|
|
|
|
|
$nonTaggable = m::mock(FileStore::class);
|
|
|
|
|
$nonTaggableRepo = new Repository($nonTaggable);
|
|
|
|
|
|
|
|
|
|
$this->assertFalse($nonTaggableRepo->supportsTags());
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-25 23:27:43 +03:30
|
|
|
public function testFlushableLockRepositorySupportsFlushingLocks()
|
|
|
|
|
{
|
|
|
|
|
$flushable = m::mock(RedisStore::class);
|
|
|
|
|
$flushableRepo = new Repository($flushable);
|
|
|
|
|
|
|
|
|
|
$this->assertTrue($flushableRepo->supportsFlushingLocks());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testNonFlushableLockRepositoryDoesNotSupportFlushingLocks()
|
|
|
|
|
{
|
|
|
|
|
$nonFlushable = m::mock(MemcachedStore::class);
|
|
|
|
|
$nonFlushableRepo = new Repository($nonFlushable);
|
|
|
|
|
|
|
|
|
|
$this->assertFalse($nonFlushableRepo->supportsFlushingLocks());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testItThrowsExceptionWhenStoreDoesNotSupportFlushingLocks()
|
|
|
|
|
{
|
|
|
|
|
$this->expectException(BadMethodCallException::class);
|
|
|
|
|
|
|
|
|
|
$nonFlushable = m::mock(MemcachedStore::class);
|
|
|
|
|
$nonFlushableRepo = new Repository($nonFlushable);
|
|
|
|
|
|
|
|
|
|
$nonFlushableRepo->flushLocks();
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-12 13:59:56 -04:00
|
|
|
public function testTouchWithSecondsTtlCorrectlyProxiesToStore(): void
|
|
|
|
|
{
|
|
|
|
|
$key = 'key';
|
|
|
|
|
$ttl = 60;
|
2025-08-12 18:00:47 +00:00
|
|
|
|
2025-08-12 13:59:56 -04:00
|
|
|
$repo = $this->getRepository();
|
|
|
|
|
$repo->getStore()->shouldReceive('touch')->once()->with($key, $ttl)->andReturn(true);
|
|
|
|
|
$this->assertTrue($repo->touch($key, $ttl));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testTouchWithDatetimeTtlCorrectlyProxiesToStore(): void
|
|
|
|
|
{
|
|
|
|
|
$key = 'key';
|
|
|
|
|
$ttl = 60;
|
|
|
|
|
|
|
|
|
|
Carbon::setTestNow($now = Carbon::now());
|
|
|
|
|
|
|
|
|
|
$repo = $this->getRepository();
|
|
|
|
|
$repo->getStore()->shouldReceive('touch')->once()->with($key, $ttl)->andReturn(true);
|
|
|
|
|
$this->assertTrue($repo->touch($key, $now->addSeconds($ttl)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testTouchWithDateIntervalTtlCorrectlyProxiesToStore(): void
|
|
|
|
|
{
|
|
|
|
|
$key = 'key';
|
|
|
|
|
$ttl = 60;
|
|
|
|
|
|
|
|
|
|
$repo = $this->getRepository();
|
|
|
|
|
$repo->getStore()->shouldReceive('touch')->once()->with($key, $ttl)->andReturn(true);
|
|
|
|
|
$this->assertTrue($repo->touch($key, DateInterval::createFromDateString("$ttl seconds")));
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-08 17:00:56 -03:00
|
|
|
public function testAtomicExecutesCallbackAndReturnsResult()
|
|
|
|
|
{
|
|
|
|
|
$repo = new Repository(new ArrayStore);
|
|
|
|
|
|
|
|
|
|
$result = $repo->withoutOverlapping('foo', function () {
|
|
|
|
|
return 'bar';
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
$this->assertSame('bar', $result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testAtomicPassesLockAndWaitSecondsToLock()
|
|
|
|
|
{
|
|
|
|
|
$store = m::mock(Store::class, LockProvider::class);
|
|
|
|
|
$repo = new Repository($store);
|
|
|
|
|
$lock = m::mock(Lock::class);
|
|
|
|
|
|
|
|
|
|
$store->shouldReceive('lock')->once()->with('foo', 30, null)->andReturn($lock);
|
|
|
|
|
$lock->shouldReceive('block')->once()->with(15, m::type('callable'))->andReturnUsing(function ($seconds, $callback) {
|
|
|
|
|
return $callback();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
$result = $repo->withoutOverlapping('foo', function () {
|
|
|
|
|
return 'bar';
|
|
|
|
|
}, 30, 15);
|
|
|
|
|
|
|
|
|
|
$this->assertSame('bar', $result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testAtomicPassesOwnerToLock()
|
|
|
|
|
{
|
|
|
|
|
$store = m::mock(Store::class, LockProvider::class);
|
|
|
|
|
$repo = new Repository($store);
|
|
|
|
|
$lock = m::mock(Lock::class);
|
|
|
|
|
|
|
|
|
|
$store->shouldReceive('lock')->once()->with('foo', 10, 'my-owner')->andReturn($lock);
|
|
|
|
|
$lock->shouldReceive('block')->once()->with(10, m::type('callable'))->andReturnUsing(function ($seconds, $callback) {
|
|
|
|
|
return $callback();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
$result = $repo->withoutOverlapping('foo', function () {
|
|
|
|
|
return 'bar';
|
|
|
|
|
}, 10, 10, 'my-owner');
|
|
|
|
|
|
|
|
|
|
$this->assertSame('bar', $result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testAtomicThrowsOnLockTimeout()
|
|
|
|
|
{
|
|
|
|
|
$repo = new Repository(new ArrayStore);
|
|
|
|
|
|
|
|
|
|
$repo->getStore()->lock('foo', 10)->acquire();
|
|
|
|
|
|
|
|
|
|
$called = false;
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
$repo->withoutOverlapping('foo', function () use (&$called) {
|
|
|
|
|
$called = true;
|
|
|
|
|
}, 10, 0);
|
|
|
|
|
|
|
|
|
|
$this->fail('Expected LockTimeoutException was not thrown.');
|
|
|
|
|
} catch (LockTimeoutException) {
|
|
|
|
|
$this->assertFalse($called);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-24 16:16:27 +00:00
|
|
|
public function testTaggedCacheWorksWithEnumKey()
|
|
|
|
|
{
|
|
|
|
|
$cache = (new Repository(new ArrayStore()))->tags('test-tag');
|
|
|
|
|
|
|
|
|
|
$cache->put(TestCacheKey::FOO, 5);
|
|
|
|
|
$this->assertSame(6, $cache->increment(TestCacheKey::FOO));
|
|
|
|
|
$this->assertSame(5, $cache->decrement(TestCacheKey::FOO));
|
|
|
|
|
}
|
|
|
|
|
|
2015-06-01 15:56:31 +01:00
|
|
|
protected function getRepository()
|
|
|
|
|
{
|
2018-09-30 23:03:32 +03:00
|
|
|
$dispatcher = new Dispatcher(m::mock(Container::class));
|
|
|
|
|
$repository = new Repository(m::mock(Store::class));
|
2015-06-01 15:56:31 +01:00
|
|
|
|
|
|
|
|
$repository->setEventDispatcher($dispatcher);
|
|
|
|
|
|
|
|
|
|
return $repository;
|
|
|
|
|
}
|
2018-01-12 22:51:32 +01:00
|
|
|
|
2022-11-23 05:10:22 +08:00
|
|
|
protected static function getTestDate()
|
2018-01-12 22:51:32 +01:00
|
|
|
{
|
|
|
|
|
return '2030-07-25 12:13:14 UTC';
|
|
|
|
|
}
|
2026-01-30 00:32:55 -06:00
|
|
|
|
|
|
|
|
public function testItGetsAsString()
|
|
|
|
|
{
|
|
|
|
|
$repo = $this->getRepository();
|
|
|
|
|
$repo->getStore()->shouldReceive('get')->once()->with('foo')->andReturn('bar');
|
|
|
|
|
$this->assertSame('bar', $repo->string('foo'));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testItGetsAsStringWithDefault()
|
|
|
|
|
{
|
|
|
|
|
$repo = $this->getRepository();
|
|
|
|
|
$repo->getStore()->shouldReceive('get')->once()->with('foo')->andReturn(null);
|
|
|
|
|
$this->assertSame('default', $repo->string('foo', 'default'));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testItThrowsExceptionWhenGettingNonStringAsString()
|
|
|
|
|
{
|
|
|
|
|
$this->expectException(InvalidArgumentException::class);
|
|
|
|
|
$this->expectExceptionMessage('Cache value for key [foo] must be a string, integer given.');
|
|
|
|
|
|
|
|
|
|
$repo = $this->getRepository();
|
|
|
|
|
$repo->getStore()->shouldReceive('get')->once()->with('foo')->andReturn(123);
|
|
|
|
|
$repo->string('foo');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testItGetsAsInteger()
|
|
|
|
|
{
|
|
|
|
|
$repo = $this->getRepository();
|
|
|
|
|
$repo->getStore()->shouldReceive('get')->once()->with('foo')->andReturn(123);
|
|
|
|
|
$this->assertSame(123, $repo->integer('foo'));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testItGetsAsIntegerWithDefault()
|
|
|
|
|
{
|
|
|
|
|
$repo = $this->getRepository();
|
|
|
|
|
$repo->getStore()->shouldReceive('get')->once()->with('foo')->andReturn(null);
|
|
|
|
|
$this->assertSame(456, $repo->integer('foo', 456));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testItGetsAsIntegerFromNumericString()
|
|
|
|
|
{
|
|
|
|
|
$repo = $this->getRepository();
|
|
|
|
|
$repo->getStore()->shouldReceive('get')->once()->with('foo')->andReturn('123');
|
|
|
|
|
$this->assertSame(123, $repo->integer('foo'));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testItThrowsExceptionWhenGettingNonIntegerAsInteger()
|
|
|
|
|
{
|
|
|
|
|
$this->expectException(InvalidArgumentException::class);
|
|
|
|
|
$this->expectExceptionMessage('Cache value for key [foo] must be an integer, string given.');
|
|
|
|
|
|
|
|
|
|
$repo = $this->getRepository();
|
|
|
|
|
$repo->getStore()->shouldReceive('get')->once()->with('foo')->andReturn('bar');
|
|
|
|
|
$repo->integer('foo');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testItThrowsExceptionWhenGettingFloatStringAsInteger()
|
|
|
|
|
{
|
|
|
|
|
$this->expectException(InvalidArgumentException::class);
|
|
|
|
|
$this->expectExceptionMessage('Cache value for key [foo] must be an integer, string given.');
|
|
|
|
|
|
|
|
|
|
$repo = $this->getRepository();
|
|
|
|
|
$repo->getStore()->shouldReceive('get')->once()->with('foo')->andReturn('1.5');
|
|
|
|
|
$repo->integer('foo');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testItGetsAsFloat()
|
|
|
|
|
{
|
|
|
|
|
$repo = $this->getRepository();
|
|
|
|
|
$repo->getStore()->shouldReceive('get')->once()->with('foo')->andReturn(1.5);
|
|
|
|
|
$this->assertSame(1.5, $repo->float('foo'));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testItGetsAsFloatWithDefault()
|
|
|
|
|
{
|
|
|
|
|
$repo = $this->getRepository();
|
|
|
|
|
$repo->getStore()->shouldReceive('get')->once()->with('foo')->andReturn(null);
|
|
|
|
|
$this->assertSame(2.5, $repo->float('foo', 2.5));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testItGetsAsFloatFromNumericString()
|
|
|
|
|
{
|
|
|
|
|
$repo = $this->getRepository();
|
|
|
|
|
$repo->getStore()->shouldReceive('get')->once()->with('foo')->andReturn('1.5');
|
|
|
|
|
$this->assertSame(1.5, $repo->float('foo'));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testItThrowsExceptionWhenGettingNonFloatAsFloat()
|
|
|
|
|
{
|
|
|
|
|
$this->expectException(InvalidArgumentException::class);
|
|
|
|
|
$this->expectExceptionMessage('Cache value for key [foo] must be a float, string given.');
|
|
|
|
|
|
|
|
|
|
$repo = $this->getRepository();
|
|
|
|
|
$repo->getStore()->shouldReceive('get')->once()->with('foo')->andReturn('bar');
|
|
|
|
|
$repo->float('foo');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testItGetsAsBoolean()
|
|
|
|
|
{
|
|
|
|
|
$repo = $this->getRepository();
|
|
|
|
|
$repo->getStore()->shouldReceive('get')->once()->with('foo')->andReturn(true);
|
|
|
|
|
$this->assertTrue($repo->boolean('foo'));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testItGetsAsBooleanWithDefault()
|
|
|
|
|
{
|
|
|
|
|
$repo = $this->getRepository();
|
|
|
|
|
$repo->getStore()->shouldReceive('get')->once()->with('foo')->andReturn(null);
|
|
|
|
|
$this->assertFalse($repo->boolean('foo', false));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testItThrowsExceptionWhenGettingNonBooleanAsBoolean()
|
|
|
|
|
{
|
|
|
|
|
$this->expectException(InvalidArgumentException::class);
|
|
|
|
|
$this->expectExceptionMessage('Cache value for key [foo] must be a boolean, string given.');
|
|
|
|
|
|
|
|
|
|
$repo = $this->getRepository();
|
|
|
|
|
$repo->getStore()->shouldReceive('get')->once()->with('foo')->andReturn('bar');
|
|
|
|
|
$repo->boolean('foo');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testItGetsAsArray()
|
|
|
|
|
{
|
|
|
|
|
$repo = $this->getRepository();
|
|
|
|
|
$repo->getStore()->shouldReceive('get')->once()->with('foo')->andReturn(['bar', 'baz']);
|
|
|
|
|
$this->assertSame(['bar', 'baz'], $repo->array('foo'));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testItGetsAsArrayWithDefault()
|
|
|
|
|
{
|
|
|
|
|
$repo = $this->getRepository();
|
|
|
|
|
$repo->getStore()->shouldReceive('get')->once()->with('foo')->andReturn(null);
|
|
|
|
|
$this->assertSame(['default'], $repo->array('foo', ['default']));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testItThrowsExceptionWhenGettingNonArrayAsArray()
|
|
|
|
|
{
|
|
|
|
|
$this->expectException(InvalidArgumentException::class);
|
|
|
|
|
$this->expectExceptionMessage('Cache value for key [foo] must be an array, string given.');
|
|
|
|
|
|
|
|
|
|
$repo = $this->getRepository();
|
|
|
|
|
$repo->getStore()->shouldReceive('get')->once()->with('foo')->andReturn('bar');
|
|
|
|
|
$repo->array('foo');
|
|
|
|
|
}
|
2014-02-27 02:40:31 +01:00
|
|
|
}
|
2026-01-24 16:16:27 +00:00
|
|
|
|
|
|
|
|
enum TestCacheKey: string
|
|
|
|
|
{
|
|
|
|
|
case FOO = 'foo';
|
|
|
|
|
}
|