fix: allow archiving backup sets with restore runs

This commit is contained in:
Ahmed Darrazi 2025-12-25 13:41:54 +01:00
parent 70fd5d2e68
commit 160c5e42a9
6 changed files with 42 additions and 29 deletions

View File

@ -78,16 +78,6 @@ public static function table(Table $table): Table
->requiresConfirmation()
->visible(fn (BackupSet $record) => ! $record->trashed())
->action(function (BackupSet $record, AuditLogger $auditLogger) {
if ($record->restoreRuns()->withTrashed()->exists()) {
Notification::make()
->title('Cannot archive backup set')
->body('Backup sets used by restore runs cannot be archived.')
->danger()
->send();
return;
}
$record->delete();
if ($record->tenant) {
@ -159,7 +149,7 @@ public static function table(Table $table): Table
return $isOnlyTrashed;
})
->modalDescription('This archives backup sets (soft delete). Backup sets referenced by restore runs will be skipped.')
->modalDescription('This archives backup sets (soft delete). Already archived backup sets will be skipped.')
->form(function (Collection $records) {
if ($records->count() >= 10) {
return [

View File

@ -82,14 +82,6 @@ public function handle(BulkOperationService $service): void
continue;
}
if ($backupSet->restoreRuns()->withTrashed()->exists()) {
$service->recordSkippedWithReason($run, (string) $backupSet->id, 'Referenced by restore runs');
$skipped++;
$skipReasons['Referenced by restore runs'] = ($skipReasons['Referenced by restore runs'] ?? 0) + 1;
continue;
}
$backupSet->delete();
$service->recordSuccess($run);
$succeeded++;

View File

@ -32,7 +32,7 @@ public function tenant(): BelongsTo
public function backupSet(): BelongsTo
{
return $this->belongsTo(BackupSet::class);
return $this->belongsTo(BackupSet::class)->withTrashed();
}
public function scopeDeletable($query)

View File

@ -4,6 +4,7 @@
use App\Models\BackupItem;
use App\Models\BackupSet;
use App\Models\BulkOperationRun;
use App\Models\RestoreRun;
use App\Models\Tenant;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
@ -55,6 +56,35 @@
expect($bulkRun->status)->toBe('completed');
});
test('backup sets can be archived even when referenced by restore runs', function () {
$tenant = Tenant::factory()->create();
$tenant->makeCurrent();
$user = User::factory()->create();
$set = BackupSet::create([
'tenant_id' => $tenant->id,
'name' => 'Backup',
'status' => 'completed',
'item_count' => 0,
]);
$restoreRun = RestoreRun::create([
'tenant_id' => $tenant->id,
'backup_set_id' => $set->id,
'status' => 'completed',
'is_dry_run' => true,
'requested_by' => 'tester@example.com',
]);
Livewire::actingAs($user)
->test(BackupSetResource\Pages\ListBackupSets::class)
->callTableBulkAction('bulk_delete', collect([$set]))
->assertHasNoTableBulkActionErrors();
expect(BackupSet::withTrashed()->find($set->id)?->trashed())->toBeTrue();
expect(RestoreRun::withTrashed()->find($restoreRun->id))->not->toBeNull();
});
test('backup sets table bulk archive requires type-to-confirm for 10+ sets', function () {
$tenant = Tenant::factory()->create();
$tenant->makeCurrent();

View File

@ -53,7 +53,7 @@
]);
});
test('backup set archive is blocked when restore runs exist', function () {
test('backup set can be archived when restore runs exist', function () {
$tenant = Tenant::create([
'tenant_id' => 'tenant-2',
'name' => 'Tenant 2',
@ -65,7 +65,7 @@
'status' => 'completed',
]);
RestoreRun::create([
$restoreRun = RestoreRun::create([
'tenant_id' => $tenant->id,
'backup_set_id' => $backupSet->id,
'status' => 'completed',
@ -77,12 +77,13 @@
Livewire::test(ListBackupSets::class)
->callTableAction('archive', $backupSet);
$this->assertDatabaseMissing('audit_logs', [
$this->assertSoftDeleted('backup_sets', ['id' => $backupSet->id]);
$this->assertDatabaseHas('audit_logs', [
'resource_type' => 'backup_set',
'resource_id' => (string) $backupSet->id,
'action' => 'backup.deleted',
]);
$this->assertDatabaseHas('backup_sets', ['id' => $backupSet->id, 'deleted_at' => null]);
$this->assertDatabaseHas('restore_runs', ['id' => $restoreRun->id]);
});
test('backup set can be force deleted when trashed and unused', function () {

View File

@ -55,7 +55,7 @@
});
});
test('bulk backup set delete job skips sets referenced by restore runs', function () {
test('bulk backup set delete job archives sets even when referenced by restore runs', function () {
$tenant = Tenant::factory()->create();
$user = User::factory()->create();
@ -82,10 +82,10 @@
$run->refresh();
expect($run->status)->toBe('completed')
->and($run->processed_items)->toBe(1)
->and($run->succeeded)->toBe(0)
->and($run->succeeded)->toBe(1)
->and($run->failed)->toBe(0)
->and($run->skipped)->toBe(1);
->and($run->skipped)->toBe(0);
expect(collect($run->failures)->pluck('reason')->join(' '))->toContain('restore runs');
expect(BackupSet::withTrashed()->find($set->id)?->trashed())->toBeFalse();
expect(BackupSet::withTrashed()->find($set->id)?->trashed())->toBeTrue();
expect(RestoreRun::query()->where('backup_set_id', $set->id)->exists())->toBeTrue();
});