seed(DatabaseSeeder::class); $path = 'exports/finance/old-export.xlsx'; Storage::disk('local')->put($path, 'export content'); $log = ExportLog::query()->create([ 'generated_at' => now()->subDays(31), 'report_type' => 'finance_verification_list', 'file_name' => 'old-export.xlsx', 'disk' => 'local', 'path' => $path, 'created_at' => now()->subDays(31), 'updated_at' => now()->subDays(31), ]); $this->artisan('exports:purge', ['--days' => 30]) ->assertSuccessful() ->expectsOutputToContain('Purged 1 export file(s).'); Storage::disk('local')->assertMissing($path); $this->assertNotNull($log->fresh()->purged_at); } public function test_command_does_not_purge_files_within_retention_period(): void { Storage::fake('local'); $this->seed(DatabaseSeeder::class); $path = 'exports/finance/recent-export.xlsx'; Storage::disk('local')->put($path, 'export content'); $log = ExportLog::query()->create([ 'generated_at' => now()->subDays(10), 'report_type' => 'finance_verification_list', 'file_name' => 'recent-export.xlsx', 'disk' => 'local', 'path' => $path, ]); $this->artisan('exports:purge', ['--days' => 30]) ->assertSuccessful() ->expectsOutputToContain('No export files eligible'); Storage::disk('local')->assertExists($path); $this->assertNull($log->fresh()->purged_at); } public function test_command_marks_log_as_purged_even_when_file_missing(): void { $this->seed(DatabaseSeeder::class); $log = ExportLog::query()->create([ 'generated_at' => now()->subDays(40), 'report_type' => 'attendance_detail_by_pusat', 'file_name' => 'missing-file.xlsx', 'disk' => 'local', 'path' => 'exports/attendance/missing-file.xlsx', 'created_at' => now()->subDays(40), 'updated_at' => now()->subDays(40), ]); $this->artisan('exports:purge', ['--days' => 30]) ->assertSuccessful() ->expectsOutputToContain('1 record(s) had no file on disk'); $this->assertNotNull($log->fresh()->purged_at); } public function test_command_does_not_re_purge_already_purged_records(): void { Storage::fake('local'); $this->seed(DatabaseSeeder::class); $path = 'exports/finance/already-purged.xlsx'; $log = ExportLog::query()->create([ 'generated_at' => now()->subDays(60), 'report_type' => 'finance_verification_list', 'file_name' => 'already-purged.xlsx', 'disk' => 'local', 'path' => $path, 'purged_at' => now()->subDays(5), 'created_at' => now()->subDays(60), 'updated_at' => now()->subDays(60), ]); $this->artisan('exports:purge', ['--days' => 30]) ->assertSuccessful() ->expectsOutputToContain('No export files eligible'); // purged_at should not change $this->assertEquals( $log->fresh()->purged_at->toDateString(), now()->subDays(5)->toDateString() ); } public function test_command_accepts_custom_retention_days(): void { Storage::fake('local'); $this->seed(DatabaseSeeder::class); $path = 'exports/finance/week-old.xlsx'; Storage::disk('local')->put($path, 'export content'); $log = ExportLog::query()->create([ 'generated_at' => now()->subDays(8), 'report_type' => 'finance_verification_list', 'file_name' => 'week-old.xlsx', 'disk' => 'local', 'path' => $path, 'created_at' => now()->subDays(8), 'updated_at' => now()->subDays(8), ]); // With default 30 days — should NOT purge $this->artisan('exports:purge', ['--days' => 30]) ->assertSuccessful() ->expectsOutputToContain('No export files eligible'); $this->assertNull($log->fresh()->purged_at); // With 7 days — should purge $this->artisan('exports:purge', ['--days' => 7]) ->assertSuccessful() ->expectsOutputToContain('Purged 1 export file(s).'); $this->assertNotNull($log->fresh()->purged_at); Storage::disk('local')->assertMissing($path); } public function test_command_records_activity_log_on_purge(): void { Storage::fake('local'); $this->seed(DatabaseSeeder::class); $path = 'exports/finance/log-test.xlsx'; Storage::disk('local')->put($path, 'export content'); ExportLog::query()->create([ 'generated_at' => now()->subDays(31), 'report_type' => 'finance_verification_list', 'file_name' => 'log-test.xlsx', 'disk' => 'local', 'path' => $path, 'created_at' => now()->subDays(31), 'updated_at' => now()->subDays(31), ]); $this->artisan('exports:purge', ['--days' => 30])->assertSuccessful(); $this->assertDatabaseHas('activity_log', [ 'log_name' => 'exports', 'description' => 'export_file_purged', ]); } public function test_command_rejects_invalid_days_option(): void { $this->artisan('exports:purge', ['--days' => 0]) ->assertFailed(); } }