diff --git a/src/Zip.php b/src/Zip.php index 30c8919..853d65f 100644 --- a/src/Zip.php +++ b/src/Zip.php @@ -155,7 +155,7 @@ protected function cache(): Builder|StreamedResponse $disk = Storage::disk(config('zipper.disk')); if ($disk->exists($filename)) { - return $disk->download($filename, $zip->getName()); + return $disk->download($filename, $zip->getOutputName()); } $adapter = $disk->getAdapter(); diff --git a/tests/ZipTest.php b/tests/ZipTest.php new file mode 100644 index 0000000..7e7e395 --- /dev/null +++ b/tests/ZipTest.php @@ -0,0 +1,102 @@ +filename('my-files'); +} + +function cachedZipPath(Zip $zip): string +{ + return (new ReflectionMethod($zip, 'create'))->invoke($zip)->getFingerprint().'.zip'; +} + +function cacheZipOnDisk(Zip $zip, string $contents): void +{ + Storage::disk('cache')->put(cachedZipPath($zip), $contents); +} + +beforeEach(function () { + config(['filesystems.disks.test' => [ + 'driver' => 'local', + 'root' => __DIR__.'/__fixtures__/assets', + ]]); + + config(['zipper.disk' => 'cache']); + + Storage::fake('cache'); + + $this->container = AssetContainer::make('test')->disk('test')->save(); +}); + +test('caches the zip to disk when caching is enabled and none is cached yet', function () { + config(['zipper.save' => true]); + + $zip = makeZip($this->container->assets()->first()); + + expect(Storage::disk('cache')->exists(cachedZipPath($zip)))->toBeFalse(); + + $response = $zip->get(); + + // Nothing was cached yet, so Zipper builds a fresh zip (a streaming Builder) + // and writes it to the cache disk for subsequent requests to reuse. + expect($response)->toBeInstanceOf(Builder::class) + ->and(Storage::disk('cache')->exists(cachedZipPath($zip)))->toBeTrue(); +}); + +test('downloads the cached zip from disk when one already exists', function () { + config(['zipper.save' => true]); + + $zip = makeZip($this->container->assets()->first()); + + // Pretend a previous request already cached this exact zip to disk. + cacheZipOnDisk($zip, 'the-cached-zip'); + + $response = $zip->get(); + + ob_start(); + $response->sendContent(); + $streamed = ob_get_clean(); + + // We streamed the file that was already on disk rather than building a new + // zip, so the response body is the cached file byte-for-byte. + expect($response)->toBeInstanceOf(StreamedResponse::class) + ->and($streamed)->toBe('the-cached-zip'); +}); + +test('returns a fresh zip on every request when caching is disabled', function () { + config(['zipper.save' => false]); + + $first = makeZip($this->container->assets()->first())->get(); + $second = makeZip($this->container->assets()->first())->get(); + + // Each request builds a new zip and nothing is ever written to the cache disk. + expect($first)->toBeInstanceOf(Builder::class) + ->and($second)->toBeInstanceOf(Builder::class) + ->and($first)->not->toBe($second) + ->and(Storage::disk('cache')->allFiles())->toBeEmpty(); +}); + +test('ignores a cached zip on disk when caching is disabled', function () { + config(['zipper.save' => false]); + + $zip = makeZip($this->container->assets()->first()); + + // A cached zip exists on disk, but with caching turned off it must not be used. + cacheZipOnDisk($zip, 'the-cached-zip'); + + $response = $zip->get(); + + // Zipper builds a fresh zip (a streaming Builder) instead of downloading the + // cached file on disk. + expect($response)->toBeInstanceOf(Builder::class); +});