1<?php 2 3namespace spec\League\Flysystem\Cached; 4 5use League\Flysystem\AdapterInterface; 6use League\Flysystem\Cached\CacheInterface; 7use League\Flysystem\Config; 8use PhpSpec\ObjectBehavior; 9 10class CachedAdapterSpec extends ObjectBehavior 11{ 12 /** 13 * @var AdapterInterface 14 */ 15 private $adapter; 16 17 /** 18 * @var CacheInterface 19 */ 20 private $cache; 21 22 public function let(AdapterInterface $adapter, CacheInterface $cache) 23 { 24 $this->adapter = $adapter; 25 $this->cache = $cache; 26 $this->cache->load()->shouldBeCalled(); 27 $this->beConstructedWith($adapter, $cache); 28 } 29 30 public function it_is_initializable() 31 { 32 $this->shouldHaveType('League\Flysystem\Cached\CachedAdapter'); 33 $this->shouldHaveType('League\Flysystem\AdapterInterface'); 34 } 35 36 public function it_should_forward_read_streams() 37 { 38 $path = 'path.txt'; 39 $response = ['path' => $path]; 40 $this->adapter->readStream($path)->willReturn($response); 41 $this->readStream($path)->shouldbe($response); 42 } 43 44 public function it_should_cache_writes() 45 { 46 $type = 'file'; 47 $path = 'path.txt'; 48 $contents = 'contents'; 49 $config = new Config(); 50 $response = compact('path', 'contents', 'type'); 51 $this->adapter->write($path, $contents, $config)->willReturn($response); 52 $this->cache->updateObject($path, $response, true)->shouldBeCalled(); 53 $this->write($path, $contents, $config)->shouldBe($response); 54 } 55 56 public function it_should_cache_streamed_writes() 57 { 58 $type = 'file'; 59 $path = 'path.txt'; 60 $stream = tmpfile(); 61 $config = new Config(); 62 $response = compact('path', 'stream', 'type'); 63 $this->adapter->writeStream($path, $stream, $config)->willReturn($response); 64 $this->cache->updateObject($path, ['contents' => false] + $response, true)->shouldBeCalled(); 65 $this->writeStream($path, $stream, $config)->shouldBe($response); 66 fclose($stream); 67 } 68 69 public function it_should_cache_streamed_updates() 70 { 71 $type = 'file'; 72 $path = 'path.txt'; 73 $stream = tmpfile(); 74 $config = new Config(); 75 $response = compact('path', 'stream', 'type'); 76 $this->adapter->updateStream($path, $stream, $config)->willReturn($response); 77 $this->cache->updateObject($path, ['contents' => false] + $response, true)->shouldBeCalled(); 78 $this->updateStream($path, $stream, $config)->shouldBe($response); 79 fclose($stream); 80 } 81 82 public function it_should_ignore_failed_writes() 83 { 84 $path = 'path.txt'; 85 $contents = 'contents'; 86 $config = new Config(); 87 $this->adapter->write($path, $contents, $config)->willReturn(false); 88 $this->write($path, $contents, $config)->shouldBe(false); 89 } 90 91 public function it_should_ignore_failed_streamed_writes() 92 { 93 $path = 'path.txt'; 94 $contents = tmpfile(); 95 $config = new Config(); 96 $this->adapter->writeStream($path, $contents, $config)->willReturn(false); 97 $this->writeStream($path, $contents, $config)->shouldBe(false); 98 fclose($contents); 99 } 100 101 public function it_should_cache_updated() 102 { 103 $type = 'file'; 104 $path = 'path.txt'; 105 $contents = 'contents'; 106 $config = new Config(); 107 $response = compact('path', 'contents', 'type'); 108 $this->adapter->update($path, $contents, $config)->willReturn($response); 109 $this->cache->updateObject($path, $response, true)->shouldBeCalled(); 110 $this->update($path, $contents, $config)->shouldBe($response); 111 } 112 113 public function it_should_ignore_failed_updates() 114 { 115 $path = 'path.txt'; 116 $contents = 'contents'; 117 $config = new Config(); 118 $this->adapter->update($path, $contents, $config)->willReturn(false); 119 $this->update($path, $contents, $config)->shouldBe(false); 120 } 121 122 public function it_should_ignore_failed_streamed_updates() 123 { 124 $path = 'path.txt'; 125 $contents = tmpfile(); 126 $config = new Config(); 127 $this->adapter->updateStream($path, $contents, $config)->willReturn(false); 128 $this->updateStream($path, $contents, $config)->shouldBe(false); 129 fclose($contents); 130 } 131 132 public function it_should_cache_renames() 133 { 134 $old = 'old.txt'; 135 $new = 'new.txt'; 136 $this->adapter->rename($old, $new)->willReturn(true); 137 $this->cache->rename($old, $new)->shouldBeCalled(); 138 $this->rename($old, $new)->shouldBe(true); 139 } 140 141 public function it_should_ignore_rename_fails() 142 { 143 $old = 'old.txt'; 144 $new = 'new.txt'; 145 $this->adapter->rename($old, $new)->willReturn(false); 146 $this->rename($old, $new)->shouldBe(false); 147 } 148 149 public function it_should_cache_copies() 150 { 151 $old = 'old.txt'; 152 $new = 'new.txt'; 153 $this->adapter->copy($old, $new)->willReturn(true); 154 $this->cache->copy($old, $new)->shouldBeCalled(); 155 $this->copy($old, $new)->shouldBe(true); 156 } 157 158 public function it_should_ignore_copy_fails() 159 { 160 $old = 'old.txt'; 161 $new = 'new.txt'; 162 $this->adapter->copy($old, $new)->willReturn(false); 163 $this->copy($old, $new)->shouldBe(false); 164 } 165 166 public function it_should_cache_deletes() 167 { 168 $delete = 'delete.txt'; 169 $this->adapter->delete($delete)->willReturn(true); 170 $this->cache->delete($delete)->shouldBeCalled(); 171 $this->delete($delete)->shouldBe(true); 172 } 173 174 public function it_should_ignore_delete_fails() 175 { 176 $delete = 'delete.txt'; 177 $this->adapter->delete($delete)->willReturn(false); 178 $this->delete($delete)->shouldBe(false); 179 } 180 181 public function it_should_cache_dir_deletes() 182 { 183 $delete = 'delete'; 184 $this->adapter->deleteDir($delete)->willReturn(true); 185 $this->cache->deleteDir($delete)->shouldBeCalled(); 186 $this->deleteDir($delete)->shouldBe(true); 187 } 188 189 public function it_should_ignore_delete_dir_fails() 190 { 191 $delete = 'delete'; 192 $this->adapter->deleteDir($delete)->willReturn(false); 193 $this->deleteDir($delete)->shouldBe(false); 194 } 195 196 public function it_should_cache_dir_creates() 197 { 198 $dirname = 'dirname'; 199 $config = new Config(); 200 $response = ['path' => $dirname, 'type' => 'dir']; 201 $this->adapter->createDir($dirname, $config)->willReturn($response); 202 $this->cache->updateObject($dirname, $response, true)->shouldBeCalled(); 203 $this->createDir($dirname, $config)->shouldBe($response); 204 } 205 206 public function it_should_ignore_create_dir_fails() 207 { 208 $dirname = 'dirname'; 209 $config = new Config(); 210 $this->adapter->createDir($dirname, $config)->willReturn(false); 211 $this->createDir($dirname, $config)->shouldBe(false); 212 } 213 214 public function it_should_cache_set_visibility() 215 { 216 $path = 'path.txt'; 217 $visibility = AdapterInterface::VISIBILITY_PUBLIC; 218 $this->adapter->setVisibility($path, $visibility)->willReturn(true); 219 $this->cache->updateObject($path, ['path' => $path, 'visibility' => $visibility], true)->shouldBeCalled(); 220 $this->setVisibility($path, $visibility)->shouldBe(true); 221 } 222 223 public function it_should_ignore_set_visibility_fails() 224 { 225 $dirname = 'delete'; 226 $visibility = AdapterInterface::VISIBILITY_PUBLIC; 227 $this->adapter->setVisibility($dirname, $visibility)->willReturn(false); 228 $this->setVisibility($dirname, $visibility)->shouldBe(false); 229 } 230 231 public function it_should_indicate_missing_files() 232 { 233 $this->cache->has($path = 'path.txt')->willReturn(false); 234 $this->has($path)->shouldBe(false); 235 } 236 237 public function it_should_indicate_file_existance() 238 { 239 $this->cache->has($path = 'path.txt')->willReturn(true); 240 $this->has($path)->shouldBe(true); 241 } 242 243 public function it_should_cache_missing_files() 244 { 245 $this->cache->has($path = 'path.txt')->willReturn(null); 246 $this->adapter->has($path)->willReturn(false); 247 $this->cache->storeMiss($path)->shouldBeCalled(); 248 $this->has($path)->shouldBe(false); 249 } 250 251 public function it_should_delete_when_metadata_is_missing() 252 { 253 $path = 'path.txt'; 254 $this->cache->has($path)->willReturn(true); 255 $this->cache->getSize($path)->willReturn(['path' => $path]); 256 $this->adapter->getSize($path)->willReturn($response = ['path' => $path, 'size' => 1024]); 257 $this->cache->updateObject($path, $response, true)->shouldBeCalled(); 258 $this->getSize($path)->shouldBe($response); 259 } 260 261 public function it_should_cache_has() 262 { 263 $this->cache->has($path = 'path.txt')->willReturn(null); 264 $this->adapter->has($path)->willReturn(true); 265 $this->cache->updateObject($path, compact('path'), true)->shouldBeCalled(); 266 $this->has($path)->shouldBe(true); 267 } 268 269 public function it_should_list_cached_contents() 270 { 271 $this->cache->isComplete($dirname = 'dirname', $recursive = true)->willReturn(true); 272 $response = [['path' => 'path.txt']]; 273 $this->cache->listContents($dirname, $recursive)->willReturn($response); 274 $this->listContents($dirname, $recursive)->shouldBe($response); 275 } 276 277 public function it_should_ignore_failed_list_contents() 278 { 279 $this->cache->isComplete($dirname = 'dirname', $recursive = true)->willReturn(false); 280 $this->adapter->listContents($dirname, $recursive)->willReturn(false); 281 $this->listContents($dirname, $recursive)->shouldBe(false); 282 } 283 284 public function it_should_cache_contents_listings() 285 { 286 $this->cache->isComplete($dirname = 'dirname', $recursive = true)->willReturn(false); 287 $response = [['path' => 'path.txt']]; 288 $this->adapter->listContents($dirname, $recursive)->willReturn($response); 289 $this->cache->storeContents($dirname, $response, $recursive)->shouldBeCalled(); 290 $this->listContents($dirname, $recursive)->shouldBe($response); 291 } 292 293 public function it_should_use_cached_visibility() 294 { 295 $this->make_it_use_getter_cache('getVisibility', 'path.txt', [ 296 'path' => 'path.txt', 297 'visibility' => AdapterInterface::VISIBILITY_PUBLIC, 298 ]); 299 } 300 301 public function it_should_cache_get_visibility() 302 { 303 $path = 'path.txt'; 304 $response = ['visibility' => AdapterInterface::VISIBILITY_PUBLIC, 'path' => $path]; 305 $this->make_it_cache_getter('getVisibility', $path, $response); 306 } 307 308 public function it_should_ignore_failed_get_visibility() 309 { 310 $path = 'path.txt'; 311 $this->make_it_ignore_failed_getter('getVisibility', $path); 312 } 313 314 public function it_should_use_cached_timestamp() 315 { 316 $this->make_it_use_getter_cache('getTimestamp', 'path.txt', [ 317 'path' => 'path.txt', 318 'timestamp' => 1234, 319 ]); 320 } 321 322 public function it_should_cache_timestamps() 323 { 324 $this->make_it_cache_getter('getTimestamp', 'path.txt', [ 325 'path' => 'path.txt', 326 'timestamp' => 1234, 327 ]); 328 } 329 330 public function it_should_ignore_failed_get_timestamps() 331 { 332 $this->make_it_ignore_failed_getter('getTimestamp', 'path.txt'); 333 } 334 335 public function it_should_cache_get_metadata() 336 { 337 $path = 'path.txt'; 338 $response = ['visibility' => AdapterInterface::VISIBILITY_PUBLIC, 'path' => $path]; 339 $this->make_it_cache_getter('getMetadata', $path, $response); 340 } 341 342 public function it_should_use_cached_metadata() 343 { 344 $this->make_it_use_getter_cache('getMetadata', 'path.txt', [ 345 'path' => 'path.txt', 346 'timestamp' => 1234, 347 ]); 348 } 349 350 public function it_should_ignore_failed_get_metadata() 351 { 352 $this->make_it_ignore_failed_getter('getMetadata', 'path.txt'); 353 } 354 355 public function it_should_cache_get_size() 356 { 357 $path = 'path.txt'; 358 $response = ['size' => 1234, 'path' => $path]; 359 $this->make_it_cache_getter('getSize', $path, $response); 360 } 361 362 public function it_should_use_cached_size() 363 { 364 $this->make_it_use_getter_cache('getSize', 'path.txt', [ 365 'path' => 'path.txt', 366 'size' => 1234, 367 ]); 368 } 369 370 public function it_should_ignore_failed_get_size() 371 { 372 $this->make_it_ignore_failed_getter('getSize', 'path.txt'); 373 } 374 375 public function it_should_cache_get_mimetype() 376 { 377 $path = 'path.txt'; 378 $response = ['mimetype' => 'text/plain', 'path' => $path]; 379 $this->make_it_cache_getter('getMimetype', $path, $response); 380 } 381 382 public function it_should_use_cached_mimetype() 383 { 384 $this->make_it_use_getter_cache('getMimetype', 'path.txt', [ 385 'path' => 'path.txt', 386 'mimetype' => 'text/plain', 387 ]); 388 } 389 390 public function it_should_ignore_failed_get_mimetype() 391 { 392 $this->make_it_ignore_failed_getter('getMimetype', 'path.txt'); 393 } 394 395 public function it_should_cache_reads() 396 { 397 $path = 'path.txt'; 398 $response = ['path' => $path, 'contents' => 'contents']; 399 $this->make_it_cache_getter('read', $path, $response); 400 } 401 402 public function it_should_use_cached_file_contents() 403 { 404 $this->make_it_use_getter_cache('read', 'path.txt', [ 405 'path' => 'path.txt', 406 'contents' => 'contents' 407 ]); 408 } 409 410 public function it_should_ignore_failed_reads() 411 { 412 $this->make_it_ignore_failed_getter('read', 'path.txt'); 413 } 414 415 protected function make_it_use_getter_cache($method, $path, $response) 416 { 417 $this->cache->{$method}($path)->willReturn($response); 418 $this->{$method}($path)->shouldBe($response); 419 } 420 421 protected function make_it_cache_getter($method, $path, $response) 422 { 423 $this->cache->{$method}($path)->willReturn(false); 424 $this->adapter->{$method}($path)->willReturn($response); 425 $this->cache->updateObject($path, $response, true)->shouldBeCalled(); 426 $this->{$method}($path)->shouldBe($response); 427 } 428 429 protected function make_it_ignore_failed_getter($method, $path) 430 { 431 $this->cache->{$method}($path)->willReturn(false); 432 $this->adapter->{$method}($path)->willReturn(false); 433 $this->{$method}($path)->shouldBe(false); 434 } 435} 436