CacheDatabaseStoreTest.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  1. <?php
  2. namespace Illuminate\Tests\Cache;
  3. use Closure;
  4. use Exception;
  5. use Illuminate\Cache\DatabaseStore;
  6. use Illuminate\Database\Connection;
  7. use Illuminate\Database\PostgresConnection;
  8. use Mockery as m;
  9. use PHPUnit\Framework\TestCase;
  10. use stdClass;
  11. class CacheDatabaseStoreTest extends TestCase
  12. {
  13. protected function tearDown(): void
  14. {
  15. m::close();
  16. }
  17. public function testNullIsReturnedWhenItemNotFound()
  18. {
  19. $store = $this->getStore();
  20. $table = m::mock(stdClass::class);
  21. $store->getConnection()->shouldReceive('table')->once()->with('table')->andReturn($table);
  22. $table->shouldReceive('where')->once()->with('key', '=', 'prefixfoo')->andReturn($table);
  23. $table->shouldReceive('first')->once()->andReturn(null);
  24. $this->assertNull($store->get('foo'));
  25. }
  26. public function testNullIsReturnedAndItemDeletedWhenItemIsExpired()
  27. {
  28. $store = $this->getMockBuilder(DatabaseStore::class)->onlyMethods(['forget'])->setConstructorArgs($this->getMocks())->getMock();
  29. $table = m::mock(stdClass::class);
  30. $store->getConnection()->shouldReceive('table')->once()->with('table')->andReturn($table);
  31. $table->shouldReceive('where')->once()->with('key', '=', 'prefixfoo')->andReturn($table);
  32. $table->shouldReceive('first')->once()->andReturn((object) ['expiration' => 1]);
  33. $store->expects($this->once())->method('forget')->with($this->equalTo('foo'))->willReturn(null);
  34. $this->assertNull($store->get('foo'));
  35. }
  36. public function testDecryptedValueIsReturnedWhenItemIsValid()
  37. {
  38. $store = $this->getStore();
  39. $table = m::mock(stdClass::class);
  40. $store->getConnection()->shouldReceive('table')->once()->with('table')->andReturn($table);
  41. $table->shouldReceive('where')->once()->with('key', '=', 'prefixfoo')->andReturn($table);
  42. $table->shouldReceive('first')->once()->andReturn((object) ['value' => serialize('bar'), 'expiration' => 999999999999999]);
  43. $this->assertSame('bar', $store->get('foo'));
  44. }
  45. public function testValueIsReturnedOnPostgres()
  46. {
  47. $store = $this->getPostgresStore();
  48. $table = m::mock(stdClass::class);
  49. $store->getConnection()->shouldReceive('table')->once()->with('table')->andReturn($table);
  50. $table->shouldReceive('where')->once()->with('key', '=', 'prefixfoo')->andReturn($table);
  51. $table->shouldReceive('first')->once()->andReturn((object) ['value' => base64_encode(serialize('bar')), 'expiration' => 999999999999999]);
  52. $this->assertSame('bar', $store->get('foo'));
  53. }
  54. public function testValueIsInsertedWhenNoExceptionsAreThrown()
  55. {
  56. $store = $this->getMockBuilder(DatabaseStore::class)->onlyMethods(['getTime'])->setConstructorArgs($this->getMocks())->getMock();
  57. $table = m::mock(stdClass::class);
  58. $store->getConnection()->shouldReceive('table')->once()->with('table')->andReturn($table);
  59. $store->expects($this->once())->method('getTime')->willReturn(1);
  60. $table->shouldReceive('insert')->once()->with(['key' => 'prefixfoo', 'value' => serialize('bar'), 'expiration' => 61])->andReturnTrue();
  61. $result = $store->put('foo', 'bar', 60);
  62. $this->assertTrue($result);
  63. }
  64. public function testValueIsUpdatedWhenInsertThrowsException()
  65. {
  66. $store = $this->getMockBuilder(DatabaseStore::class)->onlyMethods(['getTime'])->setConstructorArgs($this->getMocks())->getMock();
  67. $table = m::mock(stdClass::class);
  68. $store->getConnection()->shouldReceive('table')->with('table')->andReturn($table);
  69. $store->expects($this->once())->method('getTime')->willReturn(1);
  70. $table->shouldReceive('insert')->once()->with(['key' => 'prefixfoo', 'value' => serialize('bar'), 'expiration' => 61])->andReturnUsing(function () {
  71. throw new Exception;
  72. });
  73. $table->shouldReceive('where')->once()->with('key', 'prefixfoo')->andReturn($table);
  74. $table->shouldReceive('update')->once()->with(['value' => serialize('bar'), 'expiration' => 61])->andReturnTrue();
  75. $result = $store->put('foo', 'bar', 60);
  76. $this->assertTrue($result);
  77. }
  78. public function testValueIsInsertedOnPostgres()
  79. {
  80. $store = $this->getMockBuilder(DatabaseStore::class)->onlyMethods(['getTime'])->setConstructorArgs($this->getPostgresMocks())->getMock();
  81. $table = m::mock(stdClass::class);
  82. $store->getConnection()->shouldReceive('table')->once()->with('table')->andReturn($table);
  83. $store->expects($this->once())->method('getTime')->willReturn(1);
  84. $table->shouldReceive('insert')->once()->with(['key' => 'prefixfoo', 'value' => base64_encode(serialize("\0")), 'expiration' => 61])->andReturnTrue();
  85. $result = $store->put('foo', "\0", 60);
  86. $this->assertTrue($result);
  87. }
  88. public function testForeverCallsStoreItemWithReallyLongTime()
  89. {
  90. $store = $this->getMockBuilder(DatabaseStore::class)->onlyMethods(['put'])->setConstructorArgs($this->getMocks())->getMock();
  91. $store->expects($this->once())->method('put')->with($this->equalTo('foo'), $this->equalTo('bar'), $this->equalTo(315360000))->willReturn(true);
  92. $result = $store->forever('foo', 'bar');
  93. $this->assertTrue($result);
  94. }
  95. public function testItemsMayBeRemovedFromCache()
  96. {
  97. $store = $this->getStore();
  98. $table = m::mock(stdClass::class);
  99. $store->getConnection()->shouldReceive('table')->once()->with('table')->andReturn($table);
  100. $table->shouldReceive('where')->once()->with('key', '=', 'prefixfoo')->andReturn($table);
  101. $table->shouldReceive('delete')->once();
  102. $store->forget('foo');
  103. }
  104. public function testItemsMayBeFlushedFromCache()
  105. {
  106. $store = $this->getStore();
  107. $table = m::mock(stdClass::class);
  108. $store->getConnection()->shouldReceive('table')->once()->with('table')->andReturn($table);
  109. $table->shouldReceive('delete')->once()->andReturn(2);
  110. $result = $store->flush();
  111. $this->assertTrue($result);
  112. }
  113. public function testIncrementReturnsCorrectValues()
  114. {
  115. $store = $this->getStore();
  116. $table = m::mock(stdClass::class);
  117. $cache = m::mock(stdClass::class);
  118. $store->getConnection()->shouldReceive('transaction')->once()->with(m::type(Closure::class))->andReturnUsing(function ($closure) {
  119. return $closure();
  120. });
  121. $store->getConnection()->shouldReceive('table')->once()->with('table')->andReturn($table);
  122. $table->shouldReceive('where')->once()->with('key', 'prefixfoo')->andReturn($table);
  123. $table->shouldReceive('lockForUpdate')->once()->andReturn($table);
  124. $table->shouldReceive('first')->once()->andReturn(null);
  125. $this->assertFalse($store->increment('foo'));
  126. $cache->value = serialize('bar');
  127. $store->getConnection()->shouldReceive('transaction')->once()->with(m::type(Closure::class))->andReturnUsing(function ($closure) {
  128. return $closure();
  129. });
  130. $store->getConnection()->shouldReceive('table')->once()->with('table')->andReturn($table);
  131. $table->shouldReceive('where')->once()->with('key', 'prefixfoo')->andReturn($table);
  132. $table->shouldReceive('lockForUpdate')->once()->andReturn($table);
  133. $table->shouldReceive('first')->once()->andReturn($cache);
  134. $this->assertFalse($store->increment('foo'));
  135. $cache->value = serialize(2);
  136. $store->getConnection()->shouldReceive('transaction')->once()->with(m::type(Closure::class))->andReturnUsing(function ($closure) {
  137. return $closure();
  138. });
  139. $store->getConnection()->shouldReceive('table')->once()->with('table')->andReturn($table);
  140. $table->shouldReceive('where')->once()->with('key', 'prefixfoo')->andReturn($table);
  141. $table->shouldReceive('lockForUpdate')->once()->andReturn($table);
  142. $table->shouldReceive('first')->once()->andReturn($cache);
  143. $store->getConnection()->shouldReceive('table')->once()->with('table')->andReturn($table);
  144. $table->shouldReceive('where')->once()->with('key', 'prefixfoo')->andReturn($table);
  145. $table->shouldReceive('update')->once()->with(['value' => serialize(3)]);
  146. $this->assertEquals(3, $store->increment('foo'));
  147. }
  148. public function testDecrementReturnsCorrectValues()
  149. {
  150. $store = $this->getStore();
  151. $table = m::mock(stdClass::class);
  152. $cache = m::mock(stdClass::class);
  153. $store->getConnection()->shouldReceive('transaction')->once()->with(m::type(Closure::class))->andReturnUsing(function ($closure) {
  154. return $closure();
  155. });
  156. $store->getConnection()->shouldReceive('table')->once()->with('table')->andReturn($table);
  157. $table->shouldReceive('where')->once()->with('key', 'prefixfoo')->andReturn($table);
  158. $table->shouldReceive('lockForUpdate')->once()->andReturn($table);
  159. $table->shouldReceive('first')->once()->andReturn(null);
  160. $this->assertFalse($store->decrement('foo'));
  161. $cache->value = serialize('bar');
  162. $store->getConnection()->shouldReceive('transaction')->once()->with(m::type(Closure::class))->andReturnUsing(function ($closure) {
  163. return $closure();
  164. });
  165. $store->getConnection()->shouldReceive('table')->once()->with('table')->andReturn($table);
  166. $table->shouldReceive('where')->once()->with('key', 'prefixfoo')->andReturn($table);
  167. $table->shouldReceive('lockForUpdate')->once()->andReturn($table);
  168. $table->shouldReceive('first')->once()->andReturn($cache);
  169. $this->assertFalse($store->decrement('foo'));
  170. $cache->value = serialize(3);
  171. $store->getConnection()->shouldReceive('transaction')->once()->with(m::type(Closure::class))->andReturnUsing(function ($closure) {
  172. return $closure();
  173. });
  174. $store->getConnection()->shouldReceive('table')->once()->with('table')->andReturn($table);
  175. $table->shouldReceive('where')->once()->with('key', 'prefixbar')->andReturn($table);
  176. $table->shouldReceive('lockForUpdate')->once()->andReturn($table);
  177. $table->shouldReceive('first')->once()->andReturn($cache);
  178. $store->getConnection()->shouldReceive('table')->once()->with('table')->andReturn($table);
  179. $table->shouldReceive('where')->once()->with('key', 'prefixbar')->andReturn($table);
  180. $table->shouldReceive('update')->once()->with(['value' => serialize(2)]);
  181. $this->assertEquals(2, $store->decrement('bar'));
  182. }
  183. protected function getStore()
  184. {
  185. return new DatabaseStore(m::mock(Connection::class), 'table', 'prefix');
  186. }
  187. protected function getPostgresStore()
  188. {
  189. return new DatabaseStore(m::mock(PostgresConnection::class), 'table', 'prefix');
  190. }
  191. protected function getMocks()
  192. {
  193. return [m::mock(Connection::class), 'table', 'prefix'];
  194. }
  195. protected function getPostgresMocks()
  196. {
  197. return [m::mock(PostgresConnection::class), 'table', 'prefix'];
  198. }
  199. }