From e7d21e0eb8f360959a5310554e57b28651dd580b Mon Sep 17 00:00:00 2001 From: Ahmed Darrazi Date: Tue, 30 Dec 2025 10:44:18 +0100 Subject: [PATCH] feat: gate restore wizard to dry-run --- app/Filament/Resources/RestoreRunResource.php | 6 ++- .../Pages/CreateRestoreRun.php | 8 +++ specs/011-restore-run-wizard/tasks.md | 5 +- .../Feature/RestoreRunWizardMetadataTest.php | 51 +++++++++++++++++++ 4 files changed, 67 insertions(+), 3 deletions(-) diff --git a/app/Filament/Resources/RestoreRunResource.php b/app/Filament/Resources/RestoreRunResource.php index d0f2d69..5b0faec 100644 --- a/app/Filament/Resources/RestoreRunResource.php +++ b/app/Filament/Resources/RestoreRunResource.php @@ -284,7 +284,9 @@ public static function getWizardSteps(): array ->schema([ Forms\Components\Toggle::make('is_dry_run') ->label('Preview only (dry-run)') - ->default(true), + ->default(true) + ->disabled() + ->helperText('Execution will be enabled once checks, preview, and confirmations are implemented (Phase 6).'), Forms\Components\Placeholder::make('preview_placeholder') ->label('Preview') ->content('Preview diff summary will be added in Phase 5.'), @@ -776,7 +778,7 @@ public static function createRestoreRun(array $data): RestoreRun tenant: $tenant, backupSet: $backupSet, selectedItemIds: is_array($selectedItemIds) ? $selectedItemIds : null, - dryRun: (bool) ($data['is_dry_run'] ?? true), + dryRun: true, actorEmail: auth()->user()?->email, actorName: auth()->user()?->name, groupMapping: $data['group_mapping'] ?? [], diff --git a/app/Filament/Resources/RestoreRunResource/Pages/CreateRestoreRun.php b/app/Filament/Resources/RestoreRunResource/Pages/CreateRestoreRun.php index 1ddc73c..0fff9d4 100644 --- a/app/Filament/Resources/RestoreRunResource/Pages/CreateRestoreRun.php +++ b/app/Filament/Resources/RestoreRunResource/Pages/CreateRestoreRun.php @@ -3,6 +3,7 @@ namespace App\Filament\Resources\RestoreRunResource\Pages; use App\Filament\Resources\RestoreRunResource; +use Filament\Actions\Action; use Filament\Resources\Pages\Concerns\HasWizard; use Filament\Resources\Pages\CreateRecord; use Illuminate\Database\Eloquent\Model; @@ -18,6 +19,13 @@ public function getSteps(): array return RestoreRunResource::getWizardSteps(); } + protected function getSubmitFormAction(): Action + { + return parent::getSubmitFormAction() + ->label('Create preview (dry-run)') + ->icon('heroicon-o-eye'); + } + protected function handleRecordCreation(array $data): Model { return RestoreRunResource::createRestoreRun($data); diff --git a/specs/011-restore-run-wizard/tasks.md b/specs/011-restore-run-wizard/tasks.md index a8066f8..2a0893f 100644 --- a/specs/011-restore-run-wizard/tasks.md +++ b/specs/011-restore-run-wizard/tasks.md @@ -14,7 +14,7 @@ ## Phase 1 — Data Model + Status Semantics ## Phase 2 — Filament Wizard (Create Restore Run) - [x] T005 Replace current single-form create with a 5-step wizard (Step 1–5 as in spec). - [x] T006 Ensure changing `backup_set_id` resets downstream wizard state. -- [ ] T007 Enforce “dry-run default ON” and keep execute disabled until all gates satisfied. +- [x] T007 Enforce “dry-run default ON” and keep execute disabled until all gates satisfied. ## Phase 3 — Restore Scope UX - [ ] T008 Implement scoped selection UI grouped by policy type + platform with search and bulk toggle. @@ -40,3 +40,6 @@ ## Phase 7 — Tests + Formatting - [ ] T020 Add Pest tests for preview summary generation. - [ ] T021 Run `./vendor/bin/pint --dirty`. - [ ] T022 Run targeted tests (e.g. `./vendor/bin/sail artisan test --filter=RestoreRunWizard` once tests exist). + +## Phase 8 — Policy Version Entry Point (later) +- [ ] T023 Add a “Restore via Wizard” action on `PolicyVersion` that creates a 1-item Backup Set (source = policy_version) and opens the Restore Run wizard prefilled/scoped to that item. diff --git a/tests/Feature/RestoreRunWizardMetadataTest.php b/tests/Feature/RestoreRunWizardMetadataTest.php index 1f81df3..b8f13e1 100644 --- a/tests/Feature/RestoreRunWizardMetadataTest.php +++ b/tests/Feature/RestoreRunWizardMetadataTest.php @@ -86,3 +86,54 @@ expect($run->metadata['environment'])->toBe('test'); expect($run->metadata['highlander_label'])->toBe('Tenant One'); }); + +test('restore run wizard always creates dry-run previews in phase 2', function () { + $tenant = Tenant::create([ + 'tenant_id' => 'tenant-2', + 'name' => 'Tenant Two', + 'metadata' => [], + ]); + + $tenant->makeCurrent(); + + $backupSet = BackupSet::create([ + 'tenant_id' => $tenant->id, + 'name' => 'Backup', + 'status' => 'completed', + 'item_count' => 1, + ]); + + BackupItem::create([ + 'tenant_id' => $tenant->id, + 'backup_set_id' => $backupSet->id, + 'policy_id' => null, + 'policy_identifier' => 'policy-2', + 'policy_type' => 'deviceConfiguration', + 'platform' => 'windows', + 'payload' => ['id' => 'policy-2'], + 'metadata' => [ + 'displayName' => 'Backup Policy Two', + ], + ]); + + $user = User::factory()->create(); + $this->actingAs($user); + + Livewire::test(CreateRestoreRun::class) + ->fillForm([ + 'backup_set_id' => $backupSet->id, + ]) + ->goToNextWizardStep() + ->goToNextWizardStep() + ->goToNextWizardStep() + ->set('data.is_dry_run', false) + ->goToNextWizardStep() + ->call('create') + ->assertHasNoFormErrors(); + + $run = RestoreRun::query()->latest('id')->first(); + + expect($run)->not->toBeNull(); + expect($run->is_dry_run)->toBeTrue(); + expect($run->status)->toBe('previewed'); +});