create(); [$user, $environment] = createUserWithTenant(tenant: $environment, role: 'owner'); spec403EvidenceSnapshot($environment, $attributes); setAdminPanelContext($environment); session()->put(WorkspaceContext::SESSION_KEY, (int) $environment->workspace_id); $result = app(EvidenceAnchorResolver::class)->currentForScope( $environment->workspace, $environment, $user, ); expect($result->state)->toBe(EvidenceAnchorResult::STATE_NEEDS_ATTENTION) ->and($result->canLink)->toBeFalse() ->and($result->isCurrent)->toBeFalse() ->and($result->primaryReason)->toContain('No complete active evidence snapshot'); EvidenceSnapshot::query() ->where('workspace_id', (int) $environment->workspace_id) ->where('managed_environment_id', (int) $environment->getKey()) ->delete(); $currentSnapshot = spec403EvidenceSnapshot($environment, [ 'status' => EvidenceSnapshotStatus::Active->value, 'completeness_state' => EvidenceCompletenessState::Complete->value, 'generated_at' => now(), 'expires_at' => now()->addDay(), ]); $currentResult = app(EvidenceAnchorResolver::class)->currentForScope( $environment->workspace, $environment, $user, ); expect($currentResult->state)->toBe(EvidenceAnchorResult::STATE_READY) ->and($currentResult->evidenceSnapshotId)->toBe((int) $currentSnapshot->getKey()) ->and($currentResult->canLink)->toBeTrue() ->and($currentResult->isCurrent)->toBeTrue(); })->with([ 'queued evidence' => [[ 'status' => EvidenceSnapshotStatus::Queued->value, 'completeness_state' => EvidenceCompletenessState::Complete->value, ]], 'generating evidence' => [[ 'status' => EvidenceSnapshotStatus::Generating->value, 'completeness_state' => EvidenceCompletenessState::Complete->value, ]], 'failed evidence' => [[ 'status' => EvidenceSnapshotStatus::Failed->value, 'completeness_state' => EvidenceCompletenessState::Complete->value, ]], 'partial evidence' => [[ 'status' => EvidenceSnapshotStatus::Active->value, 'completeness_state' => EvidenceCompletenessState::Partial->value, ]], 'complete evidence with missing dimensions' => [[ 'status' => EvidenceSnapshotStatus::Active->value, 'completeness_state' => EvidenceCompletenessState::Complete->value, 'summary' => [ 'dimension_count' => 2, 'missing_dimensions' => 1, 'stale_dimensions' => 0, ], ]], 'complete evidence with no usable captured dimensions' => [[ 'status' => EvidenceSnapshotStatus::Active->value, 'completeness_state' => EvidenceCompletenessState::Complete->value, 'summary' => [ 'dimension_count' => 0, 'missing_dimensions' => 0, 'stale_dimensions' => 0, ], ]], 'complete evidence with stale dimensions' => [[ 'status' => EvidenceSnapshotStatus::Active->value, 'completeness_state' => EvidenceCompletenessState::Complete->value, 'summary' => [ 'dimension_count' => 2, 'missing_dimensions' => 0, 'stale_dimensions' => 1, ], ]], 'expired evidence' => [[ 'status' => EvidenceSnapshotStatus::Active->value, 'completeness_state' => EvidenceCompletenessState::Complete->value, 'expires_at' => now()->subMinute(), ]], 'superseded evidence' => [[ 'status' => EvidenceSnapshotStatus::Superseded->value, 'completeness_state' => EvidenceCompletenessState::Complete->value, ]], ]); it('Spec403 evidence overview exposes canonical proof states without overclaiming operation proof', function ( string $status, string $outcome, string $expectedState, ): void { $environment = ManagedEnvironment::factory()->create(); [$user, $environment] = createUserWithTenant(tenant: $environment, role: 'owner', workspaceRole: 'manager'); $run = OperationRun::factory()->forTenant($environment)->create([ 'status' => $status, 'outcome' => $outcome, 'started_at' => now()->subMinutes(5), 'completed_at' => $status === OperationRunStatus::Completed->value ? now()->subMinute() : null, 'initiator_name' => 'Spec403 Operator', ]); spec403EvidenceSnapshot($environment, [ 'operation_run_id' => (int) $run->getKey(), 'status' => EvidenceSnapshotStatus::Active->value, 'completeness_state' => EvidenceCompletenessState::Complete->value, 'generated_at' => now()->subMinutes(2), 'expires_at' => now()->addDay(), ]); $payload = spec403EvidenceOverviewPayload($user, $environment); $operationProof = collect($payload['proof_items'])->firstWhere('label', 'Operation proof'); $operationCard = collect($payload['cards'])->firstWhere('label', 'Operation proof'); $allowedStates = [ 'Ready', 'Not configured', 'Running', 'Failed', 'Blocked', 'Expired', 'Needs attention', 'Historical', ]; expect($operationProof)->toBeArray() ->and($operationProof['state'])->toBe($expectedState) ->and($operationProof['url'])->toBeNull() ->and($operationProof['actionLabel'])->toBeNull() ->and($operationCard)->toBeArray() ->and($operationCard['value'])->toBe($expectedState) ->and($operationCard['url'])->toBeNull() ->and($operationCard['description'])->not->toContain('Operation #') ->and($payload['decision_card']['evidence'])->not->toContain('Operation #') ->and($operationProof['description'])->toContain('Spec403 Operator') ->and(array_values(array_diff(collect($payload['proof_items'])->pluck('state')->all(), $allowedStates)))->toBe([]) ->and($operationProof['state'])->not->toBe('Available') ->and($operationProof['state'])->not->toBe('Unknown'); })->with([ 'running operation' => [ OperationRunStatus::Running->value, OperationRunOutcome::Pending->value, 'Running', ], 'succeeded operation' => [ OperationRunStatus::Completed->value, OperationRunOutcome::Succeeded->value, 'Historical', ], 'partially succeeded operation' => [ OperationRunStatus::Completed->value, OperationRunOutcome::PartiallySucceeded->value, 'Needs attention', ], 'blocked operation' => [ OperationRunStatus::Completed->value, OperationRunOutcome::Blocked->value, 'Blocked', ], 'failed operation' => [ OperationRunStatus::Completed->value, OperationRunOutcome::Failed->value, 'Failed', ], 'cancelled operation' => [ OperationRunStatus::Completed->value, OperationRunOutcome::Cancelled->value, 'Failed', ], 'completed pending operation' => [ OperationRunStatus::Completed->value, OperationRunOutcome::Pending->value, 'Needs attention', ], ]); it('Spec403 evidence overview uses canonical states for missing proof flow steps', function (): void { $environment = ManagedEnvironment::factory()->create(); [$user, $environment] = createUserWithTenant(tenant: $environment, role: 'owner', workspaceRole: 'manager'); $payload = spec403EvidenceOverviewPayload($user, $environment); $states = collect($payload['readiness_flow'])->pluck('state')->all(); $proofStates = collect($payload['proof_items'])->pluck('state')->all(); $allowedStates = [ 'Ready', 'Not configured', 'Running', 'Failed', 'Blocked', 'Expired', 'Needs attention', 'Historical', ]; expect($states)->toBe([ 'Ready', 'Not configured', 'Blocked', 'Blocked', 'Not configured', 'Not configured', ]) ->and(array_values(array_diff($proofStates, $allowedStates)))->toBe([]) ->and($proofStates)->not->toContain('Available') ->and($proofStates)->not->toContain('Unavailable') ->and($proofStates)->not->toContain('Not generated') ->and($proofStates)->not->toContain('Not applicable') ->and($proofStates)->not->toContain('Proof incomplete') ->and($proofStates)->not->toContain('Empty') ->and($proofStates)->not->toContain('Collapsed') ->and($proofStates)->not->toContain('Unknown'); }); it('Spec403 evidence overview maps review pack proof lifecycle states canonically', function ( string $status, string $expectedState, ): void { $environment = ManagedEnvironment::factory()->create(); [$user, $environment] = createUserWithTenant(tenant: $environment, role: 'owner', workspaceRole: 'manager'); $snapshot = spec403EvidenceSnapshot($environment, [ 'status' => EvidenceSnapshotStatus::Active->value, 'completeness_state' => EvidenceCompletenessState::Complete->value, 'generated_at' => now()->subMinutes(2), 'expires_at' => now()->addDay(), ]); ReviewPack::factory()->create([ 'managed_environment_id' => (int) $environment->getKey(), 'workspace_id' => (int) $environment->workspace_id, 'evidence_snapshot_id' => (int) $snapshot->getKey(), 'status' => $status, ]); $payload = spec403EvidenceOverviewPayload($user, $environment); $reviewPackProof = collect($payload['proof_items'])->firstWhere('label', 'Review pack'); $reviewPackCard = collect($payload['cards'])->firstWhere('label', 'Review pack'); expect($reviewPackProof)->toBeArray() ->and($reviewPackProof['state'])->toBe($expectedState) ->and($reviewPackCard)->toBeArray() ->and($reviewPackCard['value'])->toBe($expectedState) ->and($reviewPackProof['state'])->not->toBe('Queued') ->and($reviewPackProof['state'])->not->toBe('Generating'); })->with([ 'queued review pack' => [ReviewPackStatus::Queued->value, 'Running'], 'generating review pack' => [ReviewPackStatus::Generating->value, 'Running'], 'failed review pack' => [ReviewPackStatus::Failed->value, 'Failed'], ]); /** * @param array $attributes */ function spec403EvidenceSnapshot(ManagedEnvironment $environment, array $attributes = []): EvidenceSnapshot { return EvidenceSnapshot::query()->create(array_replace([ 'managed_environment_id' => (int) $environment->getKey(), 'workspace_id' => (int) $environment->workspace_id, 'status' => EvidenceSnapshotStatus::Active->value, 'completeness_state' => EvidenceCompletenessState::Complete->value, 'summary' => [ 'dimension_count' => 2, 'missing_dimensions' => 0, 'stale_dimensions' => 0, ], 'generated_at' => now(), 'expires_at' => now()->addDay(), ], $attributes)); } /** * @return array */ function spec403EvidenceOverviewPayload(App\Models\User $user, ManagedEnvironment $environment): array { setAdminPanelContext($environment); session()->put(WorkspaceContext::SESSION_KEY, (int) $environment->workspace_id); return Livewire::withQueryParams([ 'environment_id' => (int) $environment->getKey(), ]) ->actingAs($user) ->test(EvidenceOverview::class) ->instance() ->evidenceDisclosurePayload(); }