actingAs($user); ensureDefaultProviderConnection($tenant, 'microsoft'); $policy = Policy::create([ 'managed_environment_id' => (int) $tenant->getKey(), 'external_id' => 'spec332-presenter-policy', 'policy_type' => 'deviceConfiguration', 'display_name' => 'Spec332 Presenter Policy', 'platform' => 'windows', 'metadata' => [], ]); $backupSet = BackupSet::factory()->create([ 'managed_environment_id' => (int) $tenant->getKey(), 'name' => 'Spec332 Presenter Backup', 'status' => 'completed', 'item_count' => 1, ]); $backupItem = BackupItem::factory()->create([ 'managed_environment_id' => (int) $tenant->getKey(), 'backup_set_id' => (int) $backupSet->getKey(), 'policy_id' => (int) $policy->getKey(), 'policy_identifier' => $policy->external_id, 'policy_type' => $policy->policy_type, 'platform' => $policy->platform, 'payload' => [], 'assignments' => [], 'metadata' => [ 'displayName' => 'Spec332 Metadata Only Policy', 'snapshot_source' => 'metadata_only', 'warnings' => ['metadata only fallback'], ], ]); $wizardData = [ 'backup_set_id' => (int) $backupSet->getKey(), 'scope_mode' => 'all', 'backup_item_ids' => [], 'group_mapping' => [], ]; $first = RestoreRunCreatePresenter::contract( data: $wizardData, currentStep: 1, compactFlow: false, tenant: $tenant, user: $user, ); $firstSummary = data_get($first, 'processFlow.steps.0.summary'); expect($firstSummary) ->toBeString() ->toContain('does not contain a usable captured item yet') ->and(data_get($first, 'decisionCard.status'))->toBe('Source not usable') ->and(data_get($first, 'decisionCard.nextAction'))->toBe('Select another backup set.'); $backupItem->update([ 'payload' => [ 'id' => 'spec332-presenter-policy', 'displayName' => 'Spec332 Presenter Policy', 'settings' => ['foo' => 'bar'], ], 'metadata' => [ 'displayName' => 'Spec332 Presenter Policy', ], ]); $second = RestoreRunCreatePresenter::contract( data: $wizardData, currentStep: 1, compactFlow: false, tenant: $tenant, user: $user, ); $secondSummary = data_get($second, 'processFlow.steps.0.summary'); expect($secondSummary) ->toBeString() ->toContain('A usable source backup is selected for this restore draft.') ->not->toContain('does not contain a usable captured item yet') ->and(data_get($second, 'decisionCard.status'))->toBe('Source selected') ->and(data_get($second, 'decisionCard.reason'))->toBe('A usable source backup is selected.') ->and(data_get($second, 'decisionCard.impact'))->toBe('Scope, validation, and preview must still prove this draft before confirmation or real execution.') ->and(data_get($second, 'decisionCard.tone'))->toBe('success') ->and(data_get($second, 'decisionCard.nextAction'))->toBe('Continue to scope.') ->and(data_get($second, 'backupQualityCard.status'))->toBe('Available') ->and(data_get($second, 'backupQualityCard.tone'))->toBe('success') ->and(data_get($second, 'backupQualityCard.summary'))->toBe('No degradations were detected across 1 captured item.'); }); it('does not leak presenter state between independent restore draft contracts', function (): void { [$user, $tenant] = createUserWithTenant(role: 'owner'); $this->actingAs($user); ensureDefaultProviderConnection($tenant, 'microsoft'); $policy = Policy::create([ 'managed_environment_id' => (int) $tenant->getKey(), 'external_id' => 'spec332-presenter-policy-b', 'policy_type' => 'deviceConfiguration', 'display_name' => 'Spec332 Presenter Policy B', 'platform' => 'windows', 'metadata' => [], ]); $metadataOnlyBackup = BackupSet::factory()->create([ 'managed_environment_id' => (int) $tenant->getKey(), 'name' => 'Spec332 Presenter Metadata Backup', 'status' => 'completed', 'item_count' => 1, ]); BackupItem::factory()->create([ 'managed_environment_id' => (int) $tenant->getKey(), 'backup_set_id' => (int) $metadataOnlyBackup->getKey(), 'policy_id' => (int) $policy->getKey(), 'policy_identifier' => $policy->external_id, 'policy_type' => $policy->policy_type, 'platform' => $policy->platform, 'payload' => [], 'assignments' => [], 'metadata' => [ 'displayName' => 'Spec332 Metadata Only Policy', 'snapshot_source' => 'metadata_only', ], ]); $first = RestoreRunCreatePresenter::contract( data: [ 'backup_set_id' => (int) $metadataOnlyBackup->getKey(), 'scope_mode' => 'all', 'backup_item_ids' => [], 'group_mapping' => [], ], currentStep: 1, compactFlow: false, tenant: $tenant, user: $user, ); expect(data_get($first, 'processFlow.steps.0.summary')) ->toBeString() ->toContain('does not contain a usable captured item yet') ->and(data_get($first, 'decisionCard.status'))->toBe('Source not usable'); $usableBackup = BackupSet::factory()->create([ 'managed_environment_id' => (int) $tenant->getKey(), 'name' => 'Spec332 Presenter Usable Backup', 'status' => 'completed', 'item_count' => 1, ]); BackupItem::factory()->create([ 'managed_environment_id' => (int) $tenant->getKey(), 'backup_set_id' => (int) $usableBackup->getKey(), 'policy_id' => (int) $policy->getKey(), 'policy_identifier' => $policy->external_id, 'policy_type' => $policy->policy_type, 'platform' => $policy->platform, 'payload' => [ 'id' => 'spec332-presenter-policy-b', 'displayName' => 'Spec332 Presenter Policy B', 'settings' => ['foo' => 'bar'], ], 'assignments' => [], 'metadata' => [ 'displayName' => 'Spec332 Presenter Policy B', ], ]); $second = RestoreRunCreatePresenter::contract( data: [ 'backup_set_id' => (int) $usableBackup->getKey(), 'scope_mode' => 'all', 'backup_item_ids' => [], 'group_mapping' => [], ], currentStep: 1, compactFlow: false, tenant: $tenant, user: $user, ); expect(data_get($second, 'processFlow.steps.0.summary')) ->toBeString() ->toContain('A usable source backup is selected for this restore draft.') ->not->toContain('does not contain a usable captured item yet') ->and(data_get($second, 'decisionCard.status'))->toBe('Source selected'); });