SIGN IN SIGN UP
laravel / framework UNCLAIMED

Laravel is a web application framework with expressive, elegant syntax.

<?php
namespace Illuminate\Tests\Cache;
use ArrayIterator;
use BadMethodCallException;
use DateInterval;
use DateTime;
use DateTimeImmutable;
use Illuminate\Cache\ArrayStore;
use Illuminate\Cache\FileStore;
use Illuminate\Cache\Lock;
use Illuminate\Cache\MemcachedStore;
use Illuminate\Cache\RedisStore;
use Illuminate\Cache\Repository;
use Illuminate\Cache\TaggableStore;
use Illuminate\Cache\TaggedCache;
use Illuminate\Container\Container;
use Illuminate\Contracts\Cache\LockProvider;
use Illuminate\Contracts\Cache\LockTimeoutException;
use Illuminate\Contracts\Cache\Store;
use Illuminate\Events\Dispatcher;
use Illuminate\Filesystem\Filesystem;
use Illuminate\Support\Carbon;
use InvalidArgumentException;
use Mockery as m;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\TestCase;
class CacheRepositoryTest extends TestCase
{
protected function setUp(): void
{
parent::setUp();
Carbon::setTestNow(Carbon::parse(self::getTestDate()));
}
protected function tearDown(): void
{
Carbon::setTestNow(null);
parent::tearDown();
}
public function testGetReturnsValueFromCache()
{
$repo = $this->getRepository();
$repo->getStore()->shouldReceive('get')->once()->with('foo')->andReturn('bar');
$this->assertSame('bar', $repo->get('foo'));
}
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']));
}
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]));
}
public function testDefaultValueIsReturned()
{
$repo = $this->getRepository();
$repo->getStore()->shouldReceive('get')->times(2)->andReturn(null);
$this->assertSame('bar', $repo->get('foo', 'bar'));
$this->assertSame('baz', $repo->get('boom', function () {
2016-05-28 22:16:16 +01:00
return 'baz';
}));
}
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');
$repo->getStore()->shouldReceive('get')->once()->with('baz')->andReturn(false);
$this->assertTrue($repo->has('bar'));
$this->assertFalse($repo->has('foo'));
$this->assertTrue($repo->has('baz'));
}
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'));
}
public function testRememberMethodCallsPutAndReturnsDefault()
{
$repo = $this->getRepository();
$repo->getStore()->shouldReceive('get')->once()->andReturn(null);
$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';
});
$this->assertSame('bar', $result);
$repo = $this->getRepository();
$repo->getStore()->shouldReceive('get')->times(2)->andReturn(null);
$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';
});
$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';
});
$this->assertSame('qux', $result);
2021-11-18 14:46:23 +00:00
// Use a callable...
$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);
}
public function testRememberForeverMethodCallsForeverAndReturnsDefault()
{
$repo = $this->getRepository();
$repo->getStore()->shouldReceive('get')->once()->andReturn(null);
$repo->getStore()->shouldReceive('forever')->once()->with('foo', 'bar');
2016-05-28 22:16:16 +01:00
$result = $repo->rememberForever('foo', function () {
return 'bar';
});
$this->assertSame('bar', $result);
}
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);
}
public function testSettingMultipleItemsInCacheArray()
{
$repo = $this->getRepository();
$repo->getStore()->shouldReceive('putMany')->once()->with(['foo' => 'bar', 'bar' => 'baz'], 1)->andReturn(true);
$result = $repo->setMultiple(['foo' => 'bar', 'bar' => 'baz'], 1);
$this->assertTrue($result);
}
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);
}
Fix PSR-16 TTL conformity These changes to the Cache Repository interface and implementation will make our cache implementation compliant with the PSR-16 standard's TTL requirements. It contains some significant changes in how we handle and store cache values. The main change that was done is that now a TTL with a value of NULL will be considerd an attempt to store the item forever. In addition to this, any value with a TTL equal or lower than zero will be considerd an attempt to remove the item from the cache. The `put`, `putMany` and `add` methods were updated to reflect these changes. Before these changes a request like `Cache::put('foo', 'bar')` wouldn't do anything since a NULL TTL meant that the call was simply ignored. Now any request without a specified TTL will have the default TTL of NULL and thus the request is considered to attempting to store the item forever. For the `putMany` call a NULL TTL now means that every single item will be stored forever individually. As there isn't a `putManyForever` equivalent on the Repository or the PSR interface, there was no way to tell a cache store to store the given items in one go. For the `add` call, the behavior to ignore the call when a zero or less TTL was given is kept but when a NULL TTL is now passed, it's correctly passed to the cache store. This will ignore a custom `add` method on the cache store as any NULL TTL simply could be considered to re-store the item indefinitely. No changes were made to the cache stores themselves. Only the Repository was modified. This way, the cache stores keep their current behavior as theyaren't implementations of the PSR Simple Cache interface. All in all these changes will now make us conform with the PSR specs much better.
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()
{
$repo = $this->getRepository();
$repo->getStore()->shouldReceive('put')->never();
Fix PSR-16 TTL conformity These changes to the Cache Repository interface and implementation will make our cache implementation compliant with the PSR-16 standard's TTL requirements. It contains some significant changes in how we handle and store cache values. The main change that was done is that now a TTL with a value of NULL will be considerd an attempt to store the item forever. In addition to this, any value with a TTL equal or lower than zero will be considerd an attempt to remove the item from the cache. The `put`, `putMany` and `add` methods were updated to reflect these changes. Before these changes a request like `Cache::put('foo', 'bar')` wouldn't do anything since a NULL TTL meant that the call was simply ignored. Now any request without a specified TTL will have the default TTL of NULL and thus the request is considered to attempting to store the item forever. For the `putMany` call a NULL TTL now means that every single item will be stored forever individually. As there isn't a `putManyForever` equivalent on the Repository or the PSR interface, there was no way to tell a cache store to store the given items in one go. For the `add` call, the behavior to ignore the call when a zero or less TTL was given is kept but when a NULL TTL is now passed, it's correctly passed to the cache store. This will ignore a custom `add` method on the cache store as any NULL TTL simply could be considered to re-store the item indefinitely. No changes were made to the cache stores themselves. Only the Repository was modified. This way, the cache stores keep their current behavior as theyaren't implementations of the PSR Simple Cache interface. All in all these changes will now make us conform with the PSR specs much better.
2019-01-18 17:25:55 +01:00
$repo->getStore()->shouldReceive('forget')->twice()->andReturn(true);
$result = $repo->put('foo', 'bar', Carbon::now()->subMinutes(10));
Fix PSR-16 TTL conformity These changes to the Cache Repository interface and implementation will make our cache implementation compliant with the PSR-16 standard's TTL requirements. It contains some significant changes in how we handle and store cache values. The main change that was done is that now a TTL with a value of NULL will be considerd an attempt to store the item forever. In addition to this, any value with a TTL equal or lower than zero will be considerd an attempt to remove the item from the cache. The `put`, `putMany` and `add` methods were updated to reflect these changes. Before these changes a request like `Cache::put('foo', 'bar')` wouldn't do anything since a NULL TTL meant that the call was simply ignored. Now any request without a specified TTL will have the default TTL of NULL and thus the request is considered to attempting to store the item forever. For the `putMany` call a NULL TTL now means that every single item will be stored forever individually. As there isn't a `putManyForever` equivalent on the Repository or the PSR interface, there was no way to tell a cache store to store the given items in one go. For the `add` call, the behavior to ignore the call when a zero or less TTL was given is kept but when a NULL TTL is now passed, it's correctly passed to the cache store. This will ignore a custom `add` method on the cache store as any NULL TTL simply could be considered to re-store the item indefinitely. No changes were made to the cache stores themselves. Only the Repository was modified. This way, the cache stores keep their current behavior as theyaren't implementations of the PSR Simple Cache interface. All in all these changes will now make us conform with the PSR specs much better.
2019-01-18 17:25:55 +01:00
$this->assertTrue($result);
$result = $repo->put('foo', 'bar', Carbon::now());
Fix PSR-16 TTL conformity These changes to the Cache Repository interface and implementation will make our cache implementation compliant with the PSR-16 standard's TTL requirements. It contains some significant changes in how we handle and store cache values. The main change that was done is that now a TTL with a value of NULL will be considerd an attempt to store the item forever. In addition to this, any value with a TTL equal or lower than zero will be considerd an attempt to remove the item from the cache. The `put`, `putMany` and `add` methods were updated to reflect these changes. Before these changes a request like `Cache::put('foo', 'bar')` wouldn't do anything since a NULL TTL meant that the call was simply ignored. Now any request without a specified TTL will have the default TTL of NULL and thus the request is considered to attempting to store the item forever. For the `putMany` call a NULL TTL now means that every single item will be stored forever individually. As there isn't a `putManyForever` equivalent on the Repository or the PSR interface, there was no way to tell a cache store to store the given items in one go. For the `add` call, the behavior to ignore the call when a zero or less TTL was given is kept but when a NULL TTL is now passed, it's correctly passed to the cache store. This will ignore a custom `add` method on the cache store as any NULL TTL simply could be considered to re-store the item indefinitely. No changes were made to the cache stores themselves. Only the Repository was modified. This way, the cache stores keep their current behavior as theyaren't implementations of the PSR Simple Cache interface. All in all these changes will now make us conform with the PSR specs much better.
2019-01-18 17:25:55 +01:00
$this->assertTrue($result);
}
Fix PSR-16 TTL conformity These changes to the Cache Repository interface and implementation will make our cache implementation compliant with the PSR-16 standard's TTL requirements. It contains some significant changes in how we handle and store cache values. The main change that was done is that now a TTL with a value of NULL will be considerd an attempt to store the item forever. In addition to this, any value with a TTL equal or lower than zero will be considerd an attempt to remove the item from the cache. The `put`, `putMany` and `add` methods were updated to reflect these changes. Before these changes a request like `Cache::put('foo', 'bar')` wouldn't do anything since a NULL TTL meant that the call was simply ignored. Now any request without a specified TTL will have the default TTL of NULL and thus the request is considered to attempting to store the item forever. For the `putMany` call a NULL TTL now means that every single item will be stored forever individually. As there isn't a `putManyForever` equivalent on the Repository or the PSR interface, there was no way to tell a cache store to store the given items in one go. For the `add` call, the behavior to ignore the call when a zero or less TTL was given is kept but when a NULL TTL is now passed, it's correctly passed to the cache store. This will ignore a custom `add` method on the cache store as any NULL TTL simply could be considered to re-store the item indefinitely. No changes were made to the cache stores themselves. Only the Repository was modified. This way, the cache stores keep their current behavior as theyaren't implementations of the PSR Simple Cache interface. All in all these changes will now make us conform with the PSR specs much better.
2019-01-18 17:25:55 +01:00
public function testPutManyWithNullTTLRemembersItemsForever()
{
$repo = $this->getRepository();
Fix PSR-16 TTL conformity These changes to the Cache Repository interface and implementation will make our cache implementation compliant with the PSR-16 standard's TTL requirements. It contains some significant changes in how we handle and store cache values. The main change that was done is that now a TTL with a value of NULL will be considerd an attempt to store the item forever. In addition to this, any value with a TTL equal or lower than zero will be considerd an attempt to remove the item from the cache. The `put`, `putMany` and `add` methods were updated to reflect these changes. Before these changes a request like `Cache::put('foo', 'bar')` wouldn't do anything since a NULL TTL meant that the call was simply ignored. Now any request without a specified TTL will have the default TTL of NULL and thus the request is considered to attempting to store the item forever. For the `putMany` call a NULL TTL now means that every single item will be stored forever individually. As there isn't a `putManyForever` equivalent on the Repository or the PSR interface, there was no way to tell a cache store to store the given items in one go. For the `add` call, the behavior to ignore the call when a zero or less TTL was given is kept but when a NULL TTL is now passed, it's correctly passed to the cache store. This will ignore a custom `add` method on the cache store as any NULL TTL simply could be considered to re-store the item indefinitely. No changes were made to the cache stores themselves. Only the Repository was modified. This way, the cache stores keep their current behavior as theyaren't implementations of the PSR Simple Cache interface. All in all these changes will now make us conform with the PSR specs much better.
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']));
}
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()
{
$store = m::mock(RedisStore::class);
2016-10-13 01:06:07 +03:30
$store->shouldReceive('add')->once()->with('k', 'v', 60)->andReturn(true);
$repository = new Repository($store);
2016-10-13 01:06:07 +03:30
$this->assertTrue($repository->add('k', 'v', 60));
}
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)));
}
Fix PSR-16 TTL conformity These changes to the Cache Repository interface and implementation will make our cache implementation compliant with the PSR-16 standard's TTL requirements. It contains some significant changes in how we handle and store cache values. The main change that was done is that now a TTL with a value of NULL will be considerd an attempt to store the item forever. In addition to this, any value with a TTL equal or lower than zero will be considerd an attempt to remove the item from the cache. The `put`, `putMany` and `add` methods were updated to reflect these changes. Before these changes a request like `Cache::put('foo', 'bar')` wouldn't do anything since a NULL TTL meant that the call was simply ignored. Now any request without a specified TTL will have the default TTL of NULL and thus the request is considered to attempting to store the item forever. For the `putMany` call a NULL TTL now means that every single item will be stored forever individually. As there isn't a `putManyForever` equivalent on the Repository or the PSR interface, there was no way to tell a cache store to store the given items in one go. For the `add` call, the behavior to ignore the call when a zero or less TTL was given is kept but when a NULL TTL is now passed, it's correctly passed to the cache store. This will ignore a custom `add` method on the cache store as any NULL TTL simply could be considered to re-store the item indefinitely. No changes were made to the cache stores themselves. Only the Repository was modified. This way, the cache stores keep their current behavior as theyaren't implementations of the PSR Simple Cache interface. All in all these changes will now make us conform with the PSR specs much better.
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);
$result = $repo->add('foo', 'bar', -1);
$this->assertFalse($result);
Fix PSR-16 TTL conformity These changes to the Cache Repository interface and implementation will make our cache implementation compliant with the PSR-16 standard's TTL requirements. It contains some significant changes in how we handle and store cache values. The main change that was done is that now a TTL with a value of NULL will be considerd an attempt to store the item forever. In addition to this, any value with a TTL equal or lower than zero will be considerd an attempt to remove the item from the cache. The `put`, `putMany` and `add` methods were updated to reflect these changes. Before these changes a request like `Cache::put('foo', 'bar')` wouldn't do anything since a NULL TTL meant that the call was simply ignored. Now any request without a specified TTL will have the default TTL of NULL and thus the request is considered to attempting to store the item forever. For the `putMany` call a NULL TTL now means that every single item will be stored forever individually. As there isn't a `putManyForever` equivalent on the Repository or the PSR interface, there was no way to tell a cache store to store the given items in one go. For the `add` call, the behavior to ignore the call when a zero or less TTL was given is kept but when a NULL TTL is now passed, it's correctly passed to the cache store. This will ignore a custom `add` method on the cache store as any NULL TTL simply could be considered to re-store the item indefinitely. No changes were made to the cache stores themselves. Only the Repository was modified. This way, the cache stores keep their current behavior as theyaren't implementations of the PSR Simple Cache interface. All in all these changes will now make us conform with the PSR specs much better.
2019-01-18 17:25:55 +01:00
}
public static function dataProviderTestGetSeconds()
{
return [
[Carbon::parse(self::getTestDate())->addMinutes(5)],
[(new DateTime(self::getTestDate()))->modify('+5 minutes')],
[(new DateTimeImmutable(self::getTestDate()))->modify('+5 minutes')],
[new DateInterval('PT5M')],
[300],
];
}
/**
2020-03-28 18:00:42 +01:00
* @param mixed $duration
*/
#[DataProvider('dataProviderTestGetSeconds')]
public function testGetSeconds($duration)
{
$repo = $this->getRepository();
$repo->getStore()->shouldReceive('put')->once()->with($key = 'foo', $value = 'bar', 300);
$repo->put($key, $value, $duration);
}
public function testRegisterMacroWithNonStaticCall()
{
$repo = $this->getRepository();
2016-05-28 22:16:16 +01:00
$repo::macro(__CLASS__, function () {
return 'Taylor';
});
$this->assertSame('Taylor', $repo->{__CLASS__}());
}
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();
$repo->getStore()->shouldReceive('put')->with($key = 'foo', $value = 'bar', 1)->andReturn(true);
$result = $repo->set($key, $value, 1);
$this->assertTrue($result);
}
public function testClearingWholeCache()
{
$repo = $this->getRepository();
$repo->getStore()->shouldReceive('flush')->andReturn(true);
$repo->clear();
}
public function testGettingMultipleValuesFromCache()
{
$keys = ['key1', 'key2', 'key3'];
$default = 5;
$repo = $this->getRepository();
$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));
}
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);
$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']));
}
public function testAllTagsArePassedToTaggableStore()
{
$store = m::mock(ArrayStore::class);
$repo = new Repository($store);
$taggedCache = m::mock();
$taggedCache->shouldReceive('setDefaultCacheTime');
$store->shouldReceive('tags')->once()->with(['foo', 'bar', 'baz'])->andReturn($taggedCache);
$repo->tags('foo', 'bar', 'baz');
}
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());
}
public function testFlushLocksDelegatesToStore()
{
$flushable = m::mock(RedisStore::class);
$flushable->shouldReceive('flushLocks')->once()->andReturn(true);
$repo = new Repository($flushable);
$this->assertTrue($repo->flushLocks());
}
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());
}
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();
}
public function testTouchWithSecondsTtlCorrectlyProxiesToStore(): void
{
$key = 'key';
$ttl = 60;
2025-08-12 18:00:47 +00: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")));
}
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);
}
}
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));
}
protected function getRepository()
{
$dispatcher = new Dispatcher(m::mock(Container::class));
$repository = new Repository(m::mock(Store::class));
$repository->setEventDispatcher($dispatcher);
return $repository;
}
protected static function getTestDate()
{
return '2030-07-25 12:13:14 UTC';
}
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');
}
}
enum TestCacheKey: string
{
case FOO = 'foo';
}