TenantAtlas/apps/platform/tests/Feature/Monitoring/Spec403EvidenceCurrentnessRuntimeClosureTest.php
ahmido b0b5088568 feat: add evidence anchor runtime closure contract proofs (#474)
Automated PR provided by Codex via Gitea API.

Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #474
2026-06-23 15:12:38 +00:00

319 lines
12 KiB
PHP

<?php
declare(strict_types=1);
use App\Filament\Pages\Monitoring\EvidenceOverview;
use App\Models\EvidenceSnapshot;
use App\Models\ManagedEnvironment;
use App\Models\OperationRun;
use App\Models\ReviewPack;
use App\Services\Evidence\EvidenceAnchorResolver;
use App\Services\Evidence\EvidenceAnchorResult;
use App\Support\Evidence\EvidenceCompletenessState;
use App\Support\Evidence\EvidenceSnapshotStatus;
use App\Support\OperationRunOutcome;
use App\Support\OperationRunStatus;
use App\Support\ReviewPackStatus;
use App\Support\Workspaces\WorkspaceContext;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Livewire\Livewire;
uses(RefreshDatabase::class);
it('Spec403 current evidence anchors require scoped active complete non-expired evidence', function (array $attributes): void {
$environment = ManagedEnvironment::factory()->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<string, mixed> $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<string, mixed>
*/
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();
}