CacheRepositoryTest.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393
  1. <?php
  2. namespace Illuminate\Tests\Cache;
  3. use ArrayIterator;
  4. use DateInterval;
  5. use DateTime;
  6. use DateTimeImmutable;
  7. use Illuminate\Cache\ArrayStore;
  8. use Illuminate\Cache\FileStore;
  9. use Illuminate\Cache\RedisStore;
  10. use Illuminate\Cache\Repository;
  11. use Illuminate\Cache\TaggableStore;
  12. use Illuminate\Container\Container;
  13. use Illuminate\Contracts\Cache\Store;
  14. use Illuminate\Events\Dispatcher;
  15. use Illuminate\Support\Carbon;
  16. use Mockery as m;
  17. use PHPUnit\Framework\TestCase;
  18. class CacheRepositoryTest extends TestCase
  19. {
  20. protected function tearDown(): void
  21. {
  22. m::close();
  23. Carbon::setTestNow();
  24. }
  25. public function testGetReturnsValueFromCache()
  26. {
  27. $repo = $this->getRepository();
  28. $repo->getStore()->shouldReceive('get')->once()->with('foo')->andReturn('bar');
  29. $this->assertSame('bar', $repo->get('foo'));
  30. }
  31. public function testGetReturnsMultipleValuesFromCacheWhenGivenAnArray()
  32. {
  33. $repo = $this->getRepository();
  34. $repo->getStore()->shouldReceive('many')->once()->with(['foo', 'bar'])->andReturn(['foo' => 'bar', 'bar' => 'baz']);
  35. $this->assertEquals(['foo' => 'bar', 'bar' => 'baz'], $repo->get(['foo', 'bar']));
  36. }
  37. public function testGetReturnsMultipleValuesFromCacheWhenGivenAnArrayWithDefaultValues()
  38. {
  39. $repo = $this->getRepository();
  40. $repo->getStore()->shouldReceive('many')->once()->with(['foo', 'bar'])->andReturn(['foo' => null, 'bar' => 'baz']);
  41. $this->assertEquals(['foo' => 'default', 'bar' => 'baz'], $repo->get(['foo' => 'default', 'bar']));
  42. }
  43. public function testDefaultValueIsReturned()
  44. {
  45. $repo = $this->getRepository();
  46. $repo->getStore()->shouldReceive('get')->times(2)->andReturn(null);
  47. $this->assertSame('bar', $repo->get('foo', 'bar'));
  48. $this->assertSame('baz', $repo->get('boom', function () {
  49. return 'baz';
  50. }));
  51. }
  52. public function testSettingDefaultCacheTime()
  53. {
  54. $repo = $this->getRepository();
  55. $repo->setDefaultCacheTime(10);
  56. $this->assertEquals(10, $repo->getDefaultCacheTime());
  57. }
  58. public function testHasMethod()
  59. {
  60. $repo = $this->getRepository();
  61. $repo->getStore()->shouldReceive('get')->once()->with('foo')->andReturn(null);
  62. $repo->getStore()->shouldReceive('get')->once()->with('bar')->andReturn('bar');
  63. $repo->getStore()->shouldReceive('get')->once()->with('baz')->andReturn(false);
  64. $this->assertTrue($repo->has('bar'));
  65. $this->assertFalse($repo->has('foo'));
  66. $this->assertTrue($repo->has('baz'));
  67. }
  68. public function testMissingMethod()
  69. {
  70. $repo = $this->getRepository();
  71. $repo->getStore()->shouldReceive('get')->once()->with('foo')->andReturn(null);
  72. $repo->getStore()->shouldReceive('get')->once()->with('bar')->andReturn('bar');
  73. $this->assertTrue($repo->missing('foo'));
  74. $this->assertFalse($repo->missing('bar'));
  75. }
  76. public function testRememberMethodCallsPutAndReturnsDefault()
  77. {
  78. $repo = $this->getRepository();
  79. $repo->getStore()->shouldReceive('get')->once()->andReturn(null);
  80. $repo->getStore()->shouldReceive('put')->once()->with('foo', 'bar', 10);
  81. $result = $repo->remember('foo', 10, function () {
  82. return 'bar';
  83. });
  84. $this->assertSame('bar', $result);
  85. /*
  86. * Use Carbon object...
  87. */
  88. Carbon::setTestNow(Carbon::now());
  89. $repo = $this->getRepository();
  90. $repo->getStore()->shouldReceive('get')->times(2)->andReturn(null);
  91. $repo->getStore()->shouldReceive('put')->once()->with('foo', 'bar', 602);
  92. $repo->getStore()->shouldReceive('put')->once()->with('baz', 'qux', 598);
  93. $result = $repo->remember('foo', Carbon::now()->addMinutes(10)->addSeconds(2), function () {
  94. return 'bar';
  95. });
  96. $this->assertSame('bar', $result);
  97. $result = $repo->remember('baz', Carbon::now()->addMinutes(10)->subSeconds(2), function () {
  98. return 'qux';
  99. });
  100. $this->assertSame('qux', $result);
  101. /*
  102. * Use a callable...
  103. */
  104. $repo = $this->getRepository();
  105. $repo->getStore()->shouldReceive('get')->once()->andReturn(null);
  106. $repo->getStore()->shouldReceive('put')->once()->with('foo', 'bar', 10);
  107. $result = $repo->remember('foo', function () {
  108. return 10;
  109. }, function () {
  110. return 'bar';
  111. });
  112. $this->assertSame('bar', $result);
  113. }
  114. public function testRememberForeverMethodCallsForeverAndReturnsDefault()
  115. {
  116. $repo = $this->getRepository();
  117. $repo->getStore()->shouldReceive('get')->once()->andReturn(null);
  118. $repo->getStore()->shouldReceive('forever')->once()->with('foo', 'bar');
  119. $result = $repo->rememberForever('foo', function () {
  120. return 'bar';
  121. });
  122. $this->assertSame('bar', $result);
  123. }
  124. public function testPuttingMultipleItemsInCache()
  125. {
  126. $repo = $this->getRepository();
  127. $repo->getStore()->shouldReceive('putMany')->once()->with(['foo' => 'bar', 'bar' => 'baz'], 1);
  128. $repo->put(['foo' => 'bar', 'bar' => 'baz'], 1);
  129. }
  130. public function testSettingMultipleItemsInCacheArray()
  131. {
  132. $repo = $this->getRepository();
  133. $repo->getStore()->shouldReceive('putMany')->once()->with(['foo' => 'bar', 'bar' => 'baz'], 1)->andReturn(true);
  134. $result = $repo->setMultiple(['foo' => 'bar', 'bar' => 'baz'], 1);
  135. $this->assertTrue($result);
  136. }
  137. public function testSettingMultipleItemsInCacheIterator()
  138. {
  139. $repo = $this->getRepository();
  140. $repo->getStore()->shouldReceive('putMany')->once()->with(['foo' => 'bar', 'bar' => 'baz'], 1)->andReturn(true);
  141. $result = $repo->setMultiple(new ArrayIterator(['foo' => 'bar', 'bar' => 'baz']), 1);
  142. $this->assertTrue($result);
  143. }
  144. public function testPutWithNullTTLRemembersItemForever()
  145. {
  146. $repo = $this->getRepository();
  147. $repo->getStore()->shouldReceive('forever')->once()->with('foo', 'bar')->andReturn(true);
  148. $this->assertTrue($repo->put('foo', 'bar'));
  149. }
  150. public function testPutWithDatetimeInPastOrZeroSecondsRemovesOldItem()
  151. {
  152. $repo = $this->getRepository();
  153. $repo->getStore()->shouldReceive('put')->never();
  154. $repo->getStore()->shouldReceive('forget')->twice()->andReturn(true);
  155. $result = $repo->put('foo', 'bar', Carbon::now()->subMinutes(10));
  156. $this->assertTrue($result);
  157. $result = $repo->put('foo', 'bar', Carbon::now());
  158. $this->assertTrue($result);
  159. }
  160. public function testPutManyWithNullTTLRemembersItemsForever()
  161. {
  162. $repo = $this->getRepository();
  163. $repo->getStore()->shouldReceive('forever')->with('foo', 'bar')->andReturn(true);
  164. $repo->getStore()->shouldReceive('forever')->with('bar', 'baz')->andReturn(true);
  165. $this->assertTrue($repo->putMany(['foo' => 'bar', 'bar' => 'baz']));
  166. }
  167. public function testAddWithStoreFailureReturnsFalse()
  168. {
  169. $repo = $this->getRepository();
  170. $repo->getStore()->shouldReceive('add')->never();
  171. $repo->getStore()->shouldReceive('get')->andReturn(null);
  172. $repo->getStore()->shouldReceive('put')->andReturn(false);
  173. $this->assertFalse($repo->add('foo', 'bar', 60));
  174. }
  175. public function testCacheAddCallsRedisStoreAdd()
  176. {
  177. $store = m::mock(RedisStore::class);
  178. $store->shouldReceive('add')->once()->with('k', 'v', 60)->andReturn(true);
  179. $repository = new Repository($store);
  180. $this->assertTrue($repository->add('k', 'v', 60));
  181. }
  182. public function testAddMethodCanAcceptDateIntervals()
  183. {
  184. $storeWithAdd = m::mock(RedisStore::class);
  185. $storeWithAdd->shouldReceive('add')->once()->with('k', 'v', 61)->andReturn(true);
  186. $repository = new Repository($storeWithAdd);
  187. $this->assertTrue($repository->add('k', 'v', DateInterval::createFromDateString('61 seconds')));
  188. $storeWithoutAdd = m::mock(ArrayStore::class);
  189. $this->assertFalse(method_exists(ArrayStore::class, 'add'), 'This store should not have add method on it.');
  190. $storeWithoutAdd->shouldReceive('get')->once()->with('k')->andReturn(null);
  191. $storeWithoutAdd->shouldReceive('put')->once()->with('k', 'v', 60)->andReturn(true);
  192. $repository = new Repository($storeWithoutAdd);
  193. $this->assertTrue($repository->add('k', 'v', DateInterval::createFromDateString('60 seconds')));
  194. }
  195. public function testAddMethodCanAcceptDateTimeInterface()
  196. {
  197. $withAddStore = m::mock(RedisStore::class);
  198. $withAddStore->shouldReceive('add')->once()->with('k', 'v', 61)->andReturn(true);
  199. $repository = new Repository($withAddStore);
  200. $this->assertTrue($repository->add('k', 'v', Carbon::now()->addSeconds(61)));
  201. $noAddStore = m::mock(ArrayStore::class);
  202. $this->assertFalse(method_exists(ArrayStore::class, 'add'), 'This store should not have add method on it.');
  203. $noAddStore->shouldReceive('get')->once()->with('k')->andReturn(null);
  204. $noAddStore->shouldReceive('put')->once()->with('k', 'v', 62)->andReturn(true);
  205. $repository = new Repository($noAddStore);
  206. $this->assertTrue($repository->add('k', 'v', Carbon::now()->addSeconds(62)));
  207. }
  208. public function testAddWithNullTTLRemembersItemForever()
  209. {
  210. $repo = $this->getRepository();
  211. $repo->getStore()->shouldReceive('get')->once()->with('foo')->andReturn(null);
  212. $repo->getStore()->shouldReceive('forever')->once()->with('foo', 'bar')->andReturn(true);
  213. $this->assertTrue($repo->add('foo', 'bar'));
  214. }
  215. public function testAddWithDatetimeInPastOrZeroSecondsReturnsImmediately()
  216. {
  217. $repo = $this->getRepository();
  218. $repo->getStore()->shouldReceive('add', 'get', 'put')->never();
  219. $result = $repo->add('foo', 'bar', Carbon::now()->subMinutes(10));
  220. $this->assertFalse($result);
  221. $result = $repo->add('foo', 'bar', Carbon::now());
  222. $this->assertFalse($result);
  223. $result = $repo->add('foo', 'bar', -1);
  224. $this->assertFalse($result);
  225. }
  226. public function dataProviderTestGetSeconds()
  227. {
  228. Carbon::setTestNow(Carbon::parse($this->getTestDate()));
  229. return [
  230. [Carbon::now()->addMinutes(5)],
  231. [(new DateTime($this->getTestDate()))->modify('+5 minutes')],
  232. [(new DateTimeImmutable($this->getTestDate()))->modify('+5 minutes')],
  233. [new DateInterval('PT5M')],
  234. [300],
  235. ];
  236. }
  237. /**
  238. * @dataProvider dataProviderTestGetSeconds
  239. *
  240. * @param mixed $duration
  241. */
  242. public function testGetSeconds($duration)
  243. {
  244. Carbon::setTestNow(Carbon::parse($this->getTestDate()));
  245. $repo = $this->getRepository();
  246. $repo->getStore()->shouldReceive('put')->once()->with($key = 'foo', $value = 'bar', 300);
  247. $repo->put($key, $value, $duration);
  248. }
  249. public function testRegisterMacroWithNonStaticCall()
  250. {
  251. $repo = $this->getRepository();
  252. $repo::macro(__CLASS__, function () {
  253. return 'Taylor';
  254. });
  255. $this->assertSame('Taylor', $repo->{__CLASS__}());
  256. }
  257. public function testForgettingCacheKey()
  258. {
  259. $repo = $this->getRepository();
  260. $repo->getStore()->shouldReceive('forget')->once()->with('a-key')->andReturn(true);
  261. $repo->forget('a-key');
  262. }
  263. public function testRemovingCacheKey()
  264. {
  265. // Alias of Forget
  266. $repo = $this->getRepository();
  267. $repo->getStore()->shouldReceive('forget')->once()->with('a-key')->andReturn(true);
  268. $repo->delete('a-key');
  269. }
  270. public function testSettingCache()
  271. {
  272. $repo = $this->getRepository();
  273. $repo->getStore()->shouldReceive('put')->with($key = 'foo', $value = 'bar', 1)->andReturn(true);
  274. $result = $repo->set($key, $value, 1);
  275. $this->assertTrue($result);
  276. }
  277. public function testClearingWholeCache()
  278. {
  279. $repo = $this->getRepository();
  280. $repo->getStore()->shouldReceive('flush')->andReturn(true);
  281. $repo->clear();
  282. }
  283. public function testGettingMultipleValuesFromCache()
  284. {
  285. $keys = ['key1', 'key2', 'key3'];
  286. $default = 5;
  287. $repo = $this->getRepository();
  288. $repo->getStore()->shouldReceive('many')->once()->with(['key1', 'key2', 'key3'])->andReturn(['key1' => 1, 'key2' => null, 'key3' => null]);
  289. $this->assertEquals(['key1' => 1, 'key2' => 5, 'key3' => 5], $repo->getMultiple($keys, $default));
  290. }
  291. public function testRemovingMultipleKeys()
  292. {
  293. $repo = $this->getRepository();
  294. $repo->getStore()->shouldReceive('forget')->once()->with('a-key')->andReturn(true);
  295. $repo->getStore()->shouldReceive('forget')->once()->with('a-second-key')->andReturn(true);
  296. $this->assertTrue($repo->deleteMultiple(['a-key', 'a-second-key']));
  297. }
  298. public function testRemovingMultipleKeysFailsIfOneFails()
  299. {
  300. $repo = $this->getRepository();
  301. $repo->getStore()->shouldReceive('forget')->once()->with('a-key')->andReturn(true);
  302. $repo->getStore()->shouldReceive('forget')->once()->with('a-second-key')->andReturn(false);
  303. $this->assertFalse($repo->deleteMultiple(['a-key', 'a-second-key']));
  304. }
  305. public function testAllTagsArePassedToTaggableStore()
  306. {
  307. $store = m::mock(ArrayStore::class);
  308. $repo = new Repository($store);
  309. $taggedCache = m::mock();
  310. $taggedCache->shouldReceive('setDefaultCacheTime');
  311. $store->shouldReceive('tags')->once()->with(['foo', 'bar', 'baz'])->andReturn($taggedCache);
  312. $repo->tags('foo', 'bar', 'baz');
  313. }
  314. public function testTaggableRepositoriesSupportTags()
  315. {
  316. $taggable = m::mock(TaggableStore::class);
  317. $taggableRepo = new Repository($taggable);
  318. $this->assertTrue($taggableRepo->supportsTags());
  319. }
  320. public function testNonTaggableRepositoryDoesNotSupportTags()
  321. {
  322. $nonTaggable = m::mock(FileStore::class);
  323. $nonTaggableRepo = new Repository($nonTaggable);
  324. $this->assertFalse($nonTaggableRepo->supportsTags());
  325. }
  326. protected function getRepository()
  327. {
  328. $dispatcher = new Dispatcher(m::mock(Container::class));
  329. $repository = new Repository(m::mock(Store::class));
  330. $repository->setEventDispatcher($dispatcher);
  331. return $repository;
  332. }
  333. protected function getTestDate()
  334. {
  335. return '2030-07-25 12:13:14 UTC';
  336. }
  337. }