fake()->uuid(), 'name' => 'Restore Tenant', 'metadata' => [], 'rbac_status' => 'ok', 'rbac_last_checked_at' => now(), ]); $tenant->makeCurrent(); if ($withProviderConnection) { ensureDefaultProviderConnection($tenant, 'microsoft'); } $policy = Policy::create([ 'tenant_id' => $tenant->id, 'external_id' => fake()->uuid(), 'policy_type' => 'deviceConfiguration', 'display_name' => 'Device Config Policy', 'platform' => 'windows', ]); $backupSet = BackupSet::create([ 'tenant_id' => $tenant->id, 'name' => 'Backup', 'status' => 'completed', 'item_count' => 1, ]); $backupItem = BackupItem::create([ 'tenant_id' => $tenant->id, 'backup_set_id' => $backupSet->id, 'policy_id' => $policy->id, 'policy_identifier' => $policy->external_id, 'policy_type' => $policy->policy_type, 'platform' => $policy->platform, 'payload' => ['id' => $policy->external_id], 'metadata' => [ 'displayName' => 'Backup Policy', ], ]); $user = User::factory()->create([ 'email' => 'restore@example.com', 'name' => 'Restore Operator', ]); $user->tenants()->syncWithoutDetaching([ $tenant->getKey() => ['role' => 'owner'], ]); Filament::setTenant($tenant, true); return [$tenant, $backupSet, $backupItem, $user]; } it('starts restore execution with explicit provider connection context', function (): void { Bus::fake(); [$tenant, $backupSet, $backupItem, $user] = seedRestoreStartContext(); $this->actingAs($user); Livewire::test(CreateRestoreRun::class) ->fillForm([ 'backup_set_id' => $backupSet->id, ]) ->goToNextWizardStep() ->fillForm([ 'scope_mode' => 'selected', 'backup_item_ids' => [$backupItem->id], ]) ->goToNextWizardStep() ->callFormComponentAction('check_results', 'run_restore_checks') ->goToNextWizardStep() ->callFormComponentAction('preview_diffs', 'run_restore_preview') ->goToNextWizardStep() ->fillForm([ 'is_dry_run' => false, 'acknowledged_impact' => true, 'tenant_confirm' => 'Restore Tenant', ]) ->call('create') ->assertHasNoFormErrors(); $restoreRun = RestoreRun::query()->latest('id')->first(); $operationRun = OperationRun::query() ->where('tenant_id', (int) $tenant->getKey()) ->where('type', 'restore.execute') ->latest('id') ->first(); expect($restoreRun)->not->toBeNull(); expect($restoreRun?->status)->toBe(RestoreRunStatus::Queued->value); expect($operationRun)->not->toBeNull(); expect($operationRun?->context['provider_connection_id'] ?? null)->toBeInt(); Bus::assertDispatched(ExecuteRestoreRunJob::class, function (ExecuteRestoreRunJob $job) use ($restoreRun, $operationRun): bool { return $job->restoreRunId === (int) $restoreRun?->getKey() && $job->providerConnectionId === ($operationRun?->context['provider_connection_id'] ?? null) && $job->operationRun?->is($operationRun); }); }); it('blocks restore reruns before queue when no provider connection is available', function (): void { Bus::fake(); [$tenant, $backupSet, $backupItem, $user] = seedRestoreStartContext(withProviderConnection: false); $this->actingAs($user); $run = RestoreRun::create([ 'tenant_id' => $tenant->id, 'backup_set_id' => $backupSet->id, 'status' => 'failed', 'is_dry_run' => false, 'requested_items' => [$backupItem->id], 'group_mapping' => [], ]); Livewire::test(ListRestoreRuns::class) ->callTableAction('rerun', $run); expect(RestoreRun::query()->where('tenant_id', (int) $tenant->getKey())->count())->toBe(1); $operationRun = OperationRun::query() ->where('tenant_id', (int) $tenant->getKey()) ->where('type', 'restore.execute') ->latest('id') ->first(); expect($operationRun)->not->toBeNull(); expect($operationRun?->outcome)->toBe('blocked'); expect($operationRun?->context['reason_code'] ?? null)->toBe(ProviderReasonCodes::ProviderConnectionMissing); Bus::assertNotDispatched(ExecuteRestoreRunJob::class); });