set('enhance.disk', 'public'); config()->set('enhance.lifecycle.cleanup_chunk_size', 10); Storage::fake('public'); }); it('does not delete files during cleanup dry run', function (): void { $owner = User::factory()->create(); Storage::disk('public')->put('enhance/sources/1/source.png', UploadedFile::fake()->image('source.png')->get()); Storage::disk('public')->put('enhance/outputs/1/output.webp', UploadedFile::fake()->image('output.webp')->get()); Storage::disk('public')->put('enhance/previews/1/preview.webp', UploadedFile::fake()->image('preview.webp')->get()); $job = EnhanceJob::query()->create([ 'user_id' => $owner->id, 'status' => EnhanceJob::STATUS_COMPLETED, 'engine' => EnhanceJob::ENGINE_STUB, 'mode' => 'standard', 'scale' => 2, 'source_disk' => 'public', 'source_path' => 'enhance/sources/1/source.png', 'output_disk' => 'public', 'output_path' => 'enhance/outputs/1/output.webp', 'preview_disk' => 'public', 'preview_path' => 'enhance/previews/1/preview.webp', 'expires_at' => now()->subMinute(), ]); $this->artisan('enhance:cleanup --dry-run')->assertSuccessful(); $job->refresh(); expect($job->status)->toBe(EnhanceJob::STATUS_COMPLETED); Storage::disk('public')->assertExists('enhance/sources/1/source.png'); Storage::disk('public')->assertExists('enhance/outputs/1/output.webp'); Storage::disk('public')->assertExists('enhance/previews/1/preview.webp'); }); it('deletes expired completed job files in force mode', function (): void { $owner = User::factory()->create(); Storage::disk('public')->put('enhance/sources/2/source.png', UploadedFile::fake()->image('source.png')->get()); Storage::disk('public')->put('enhance/outputs/2/output.webp', UploadedFile::fake()->image('output.webp')->get()); Storage::disk('public')->put('enhance/previews/2/preview.webp', UploadedFile::fake()->image('preview.webp')->get()); $job = EnhanceJob::query()->create([ 'user_id' => $owner->id, 'status' => EnhanceJob::STATUS_COMPLETED, 'engine' => EnhanceJob::ENGINE_STUB, 'mode' => 'standard', 'scale' => 2, 'source_disk' => 'public', 'source_path' => 'enhance/sources/2/source.png', 'output_disk' => 'public', 'output_path' => 'enhance/outputs/2/output.webp', 'preview_disk' => 'public', 'preview_path' => 'enhance/previews/2/preview.webp', 'expires_at' => now()->subMinute(), ]); $this->artisan('enhance:cleanup --only=expired --force')->assertSuccessful(); $job->refresh(); expect($job->status)->toBe(EnhanceJob::STATUS_EXPIRED); expect($job->source_path)->toBeNull(); expect($job->output_path)->toBeNull(); expect($job->preview_path)->toBeNull(); expect($job->metadata['cleanup']['reason'])->toBe('expired'); Storage::disk('public')->assertMissing('enhance/sources/2/source.png'); Storage::disk('public')->assertMissing('enhance/outputs/2/output.webp'); Storage::disk('public')->assertMissing('enhance/previews/2/preview.webp'); }); it('does not delete non enhance paths during cleanup', function (): void { $owner = User::factory()->create(); Storage::disk('public')->put('uploads/artworks/unsafe.png', 'unsafe'); Storage::disk('public')->put('enhance/outputs/3/output.webp', UploadedFile::fake()->image('output.webp')->get()); $job = EnhanceJob::query()->create([ 'user_id' => $owner->id, 'status' => EnhanceJob::STATUS_FAILED, 'engine' => EnhanceJob::ENGINE_STUB, 'mode' => 'standard', 'scale' => 2, 'source_disk' => 'public', 'source_path' => 'uploads/artworks/unsafe.png', 'output_disk' => 'public', 'output_path' => 'enhance/outputs/3/output.webp', 'finished_at' => now()->subDays(10), ]); $this->artisan('enhance:cleanup --only=failed --days=3 --force')->assertSuccessful(); $job->refresh(); expect($job->source_path)->toBe('uploads/artworks/unsafe.png'); expect($job->output_path)->toBeNull(); Storage::disk('public')->assertExists('uploads/artworks/unsafe.png'); Storage::disk('public')->assertMissing('enhance/outputs/3/output.webp'); }); it('cleans failed and soft deleted enhance files and records cleanup metadata', function (): void { $owner = User::factory()->create(); Storage::disk('public')->put('enhance/sources/4/source.png', UploadedFile::fake()->image('source.png')->get()); $failed = EnhanceJob::query()->create([ 'user_id' => $owner->id, 'status' => EnhanceJob::STATUS_FAILED, 'engine' => EnhanceJob::ENGINE_STUB, 'mode' => 'standard', 'scale' => 2, 'source_disk' => 'public', 'source_path' => 'enhance/sources/4/source.png', 'finished_at' => now()->subDays(10), ]); Storage::disk('public')->put('enhance/sources/5/source.png', UploadedFile::fake()->image('source.png')->get()); $deleted = EnhanceJob::query()->create([ 'user_id' => $owner->id, 'status' => EnhanceJob::STATUS_COMPLETED, 'engine' => EnhanceJob::ENGINE_STUB, 'mode' => 'standard', 'scale' => 2, 'source_disk' => 'public', 'source_path' => 'enhance/sources/5/source.png', ]); $deleted->delete(); $deleted->forceFill(['deleted_at' => now()->subDays(5)])->saveQuietly(); $this->artisan('enhance:cleanup --only=failed --days=3 --force')->assertSuccessful(); $this->artisan('enhance:cleanup --only=deleted --days=3 --force')->assertSuccessful(); $failed->refresh(); $deleted = EnhanceJob::withTrashed()->findOrFail($deleted->id); expect($failed->metadata['cleanup']['reason'])->toBe('failed-expired'); expect($deleted->metadata['cleanup']['reason'])->toBe('deleted-grace'); Storage::disk('public')->assertMissing('enhance/sources/4/source.png'); Storage::disk('public')->assertMissing('enhance/sources/5/source.png'); });