TenantAtlas/apps/platform/tests/Feature/Filament/Spec337EvidenceReviewPackProductFlowTest.php

363 lines
16 KiB
PHP

<?php
declare(strict_types=1);
use App\Filament\Pages\Monitoring\EvidenceOverview;
use App\Models\EnvironmentReview;
use App\Models\EvidenceSnapshot;
use App\Models\ManagedEnvironment;
use App\Models\OperationRun;
use App\Models\ReviewPack;
use App\Models\StoredReport;
use App\Models\User;
use App\Support\EnvironmentReviewCompletenessState;
use App\Support\EnvironmentReviewStatus;
use App\Support\Evidence\EvidenceCompletenessState;
use App\Support\Evidence\EvidenceSnapshotStatus;
use App\Support\OperationRunOutcome;
use App\Support\OperationRunStatus;
use App\Support\OperationRunType;
use App\Support\ReviewPackStatus;
use App\Support\Workspaces\WorkspaceContext;
use Livewire\Livewire;
it('renders missing evidence as the first evidence readiness blocker', function (): void {
[$user, $environment] = createUserWithTenant(role: 'owner', workspaceRole: 'manager');
$component = spec337EvidenceOverviewLivewire($user, $environment)
->assertSee('Is this evidence package ready for customer or auditor consumption?')
->assertSee('Evidence snapshot required')
->assertSee('Review pack output cannot be trusted or exported yet.')
->assertSee('Generate evidence snapshot')
->assertSee('Evidence readiness flow')
->assertSee('Evidence proof')
->assertSee('Diagnostics - Collapsed')
->assertDontSee('raw payload should stay hidden')
->assertDontSee('provider response should stay hidden')
->assertDontSee('stack trace should stay hidden');
$content = $component->html();
spec337AssertFlowStep($content, 'Source data selected', 'Available', false);
spec337AssertFlowStep($content, 'Evidence snapshot', 'Missing', true);
spec337AssertFlowStep($content, 'Stored report', 'Unavailable', false);
spec337AssertFlowStep($content, 'Review pack', 'Unavailable', false);
spec337AssertFlowStep($content, 'Customer-safe output', 'Not ready', false);
spec337AssertFlowStep($content, 'Export / delivery', 'Unavailable', false);
expect(substr_count($content, 'data-testid="evidence-readiness-step"'))->toBe(6)
->and($content)->toContain('data-testid="evidence-readiness-flow" class="@container')
->and($content)->not->toContain('data-testid="evidence-disclosure-diagnostics" open');
});
it('renders stored report missing without inventing review-pack or export readiness', function (): void {
[$user, $environment] = createUserWithTenant(role: 'owner', workspaceRole: 'manager');
spec337CreateEvidenceSnapshot($environment);
$component = spec337EvidenceOverviewLivewire($user, $environment)
->assertSee('Stored report required')
->assertSee('Evidence snapshot exists, but no stored report is available for this review output.')
->assertSee('Open evidence snapshot')
->assertDontSee('Review pack export available')
->assertDontSee('Customer-safe output ready')
->assertDontSee('Download export');
$content = $component->html();
spec337AssertFlowStep($content, 'Evidence snapshot', 'Available', false);
spec337AssertFlowStep($content, 'Stored report', 'Missing', true);
spec337AssertFlowStep($content, 'Review pack', 'Unavailable', false);
spec337AssertFlowStep($content, 'Customer-safe output', 'Not ready', false);
spec337AssertFlowStep($content, 'Export / delivery', 'Unavailable', false);
});
it('renders review pack required with capability-aware primary action', function (): void {
[$owner, $environment] = createUserWithTenant(role: 'owner', workspaceRole: 'manager');
[$readonly] = createUserWithTenant(tenant: $environment, role: 'readonly', workspaceRole: 'readonly');
$snapshot = spec337CreateEvidenceSnapshot($environment);
spec337CreateStoredReport($environment);
$ownerComponent = spec337EvidenceOverviewLivewire($owner, $environment)
->assertSee('Review pack required')
->assertSee('Stored report exists, but a review pack has not been generated.')
->assertSee('Generate review pack')
->assertDontSee('Customer-safe output ready')
->assertDontSee('Review pack export available');
spec337AssertFlowStep($ownerComponent->html(), 'Review pack', 'Required', true);
spec337EvidenceOverviewLivewire($readonly, $environment)
->assertSee('Review pack required')
->assertDontSee('Generate review pack')
->assertSee('Open evidence snapshot')
->assertSee((string) $snapshot->tenant?->name);
});
it('renders repo-backed customer-safe and export-ready output without broad readiness claims', function (): void {
[$user, $environment] = createUserWithTenant(role: 'owner', workspaceRole: 'manager');
$snapshot = spec337CreateEvidenceSnapshot($environment);
spec337CreateStoredReport($environment);
[$review, $pack] = spec337CreatePublishedReviewWithReadyPack($environment, $user, $snapshot);
$component = spec337EvidenceOverviewLivewire($user, $environment)
->assertSee('Review pack export available')
->assertSee('A generated export artifact is available for authorized download.')
->assertSee('external sharing remains governed by workspace policy')
->assertSee('Download export')
->assertSee('Customer-safe output')
->assertSee('Ready')
->assertSee('Review pack contents / coverage')
->assertSee('Findings included')
->assertSee('Evidence dimensions')
->assertDontSee('Auditor-ready')
->assertDontSee('environment is healthy')
->assertDontSee('compliant');
$content = $component->html();
spec337AssertFlowStep($content, 'Evidence snapshot', 'Available', false);
spec337AssertFlowStep($content, 'Stored report', 'Available', false);
spec337AssertFlowStep($content, 'Review pack', 'Available', false);
spec337AssertFlowStep($content, 'Customer-safe output', 'Ready', false);
spec337AssertFlowStep($content, 'Export / delivery', 'Available', true);
expect($review->current_export_review_pack_id)->toBe($pack->getKey());
});
it('renders generating and failed operation proof separately from usable output', function (): void {
[$user, $generatingEnvironment] = createUserWithTenant(role: 'owner', workspaceRole: 'manager');
$generatingRun = OperationRun::factory()->forTenant($generatingEnvironment)->create([
'type' => OperationRunType::EvidenceSnapshotGenerate->value,
'status' => OperationRunStatus::Running->value,
'outcome' => OperationRunOutcome::Pending->value,
'started_at' => now()->subMinute(),
'initiator_name' => 'Spec337 Operator',
]);
spec337CreateEvidenceSnapshot($generatingEnvironment, [
'operation_run_id' => (int) $generatingRun->getKey(),
'status' => EvidenceSnapshotStatus::Generating->value,
'generated_at' => null,
]);
$generatingComponent = spec337EvidenceOverviewLivewire($user, $generatingEnvironment)
->assertSee('Evidence generation in progress')
->assertSee('View operation progress')
->assertSee('Operation proof')
->assertSee('Generating')
->assertSee('Spec337 Operator')
->assertDontSee('Customer-safe output ready');
spec337AssertFlowStep($generatingComponent->html(), 'Evidence snapshot', 'Generating', true);
$failedEnvironment = createUserWithTenant(user: $user, role: 'owner', workspaceRole: 'manager')[1];
$failedRun = OperationRun::factory()->forTenant($failedEnvironment)->create([
'type' => OperationRunType::ReviewPackGenerate->value,
'status' => OperationRunStatus::Completed->value,
'outcome' => OperationRunOutcome::Failed->value,
'started_at' => now()->subMinutes(3),
'completed_at' => now()->subMinute(),
'initiator_name' => 'Spec337 Reviewer',
]);
$failedSnapshot = spec337CreateEvidenceSnapshot($failedEnvironment);
spec337CreateStoredReport($failedEnvironment);
ReviewPack::factory()->failed()->create([
'managed_environment_id' => (int) $failedEnvironment->getKey(),
'workspace_id' => (int) $failedEnvironment->workspace_id,
'evidence_snapshot_id' => (int) $failedSnapshot->getKey(),
'operation_run_id' => (int) $failedRun->getKey(),
]);
$failedComponent = spec337EvidenceOverviewLivewire($user, $failedEnvironment)
->assertSee('Review pack generation failed')
->assertSee('Review operation')
->assertSee('Operation proof')
->assertSee('Failed')
->assertSee('Spec337 Reviewer')
->assertDontSee('Review pack export available');
spec337AssertFlowStep($failedComponent->html(), 'Review pack', 'Failed', true);
});
it('preserves workspace isolation and avoids legacy tenant aliases in rendered links', function (): void {
[$user, $environment] = createUserWithTenant(role: 'owner', workspaceRole: 'manager');
$foreignEnvironment = ManagedEnvironment::factory()->active()->create([
'name' => 'Spec337 Foreign Workspace Environment',
]);
spec337CreateEvidenceSnapshot($foreignEnvironment);
$this->actingAs($user)
->withSession([WorkspaceContext::SESSION_KEY => (int) $environment->workspace_id])
->get(route('admin.evidence.overview'))
->assertOk()
->assertDontSee('Spec337 Foreign Workspace Environment');
$this->actingAs($user)
->withSession([WorkspaceContext::SESSION_KEY => (int) $environment->workspace_id])
->get(route('admin.evidence.overview', ['environment_id' => (int) $foreignEnvironment->getKey()]))
->assertNotFound();
$content = spec337EvidenceOverviewLivewire($user, $environment)->html();
expect($content)
->not->toContain('/admin/t/')
->not->toContain('tenant_id=');
}
);
function spec337EvidenceOverviewLivewire(User $user, ManagedEnvironment $environment): mixed
{
session()->put(WorkspaceContext::SESSION_KEY, (int) $environment->workspace_id);
setAdminPanelContext($environment);
return Livewire::withQueryParams([
'environment_id' => (int) $environment->getKey(),
])
->actingAs($user)
->test(EvidenceOverview::class);
}
/**
* @param array<string, mixed> $attributes
*/
function spec337CreateEvidenceSnapshot(ManagedEnvironment $environment, array $attributes = []): EvidenceSnapshot
{
$snapshot = EvidenceSnapshot::query()->create(array_replace([
'managed_environment_id' => (int) $environment->getKey(),
'workspace_id' => (int) $environment->workspace_id,
'status' => EvidenceSnapshotStatus::Active->value,
'fingerprint' => sha1('spec337-snapshot-'.$environment->getKey().'-'.microtime(true)),
'completeness_state' => EvidenceCompletenessState::Complete->value,
'summary' => [
'dimension_count' => 5,
'missing_dimensions' => 0,
'stale_dimensions' => 0,
'raw_payload' => 'raw payload should stay hidden',
'provider_response' => 'provider response should stay hidden',
'stack_trace' => 'stack trace should stay hidden',
],
'generated_at' => now(),
'expires_at' => now()->addDays(30),
], $attributes));
$snapshot->items()->create([
'managed_environment_id' => (int) $environment->getKey(),
'workspace_id' => (int) $environment->workspace_id,
'dimension_key' => 'permission_posture',
'state' => (string) ($snapshot->completeness_state ?: EvidenceCompletenessState::Complete->value),
'required' => true,
'source_kind' => 'stored_report',
'source_record_type' => StoredReport::class,
'source_record_id' => null,
'source_fingerprint' => null,
'measured_at' => now(),
'freshness_at' => now(),
'summary_payload' => [
'label' => 'Permission posture',
'state' => 'complete',
],
'sort_order' => 1,
]);
return $snapshot->load([
'tenant',
'operationRun',
'reviewPacks.operationRun',
'reviewPacks.environmentReview.currentExportReviewPack',
'items',
]);
}
function spec337CreateStoredReport(ManagedEnvironment $environment): StoredReport
{
return StoredReport::factory()->permissionPosture([
'raw_payload' => 'raw payload should stay hidden',
'provider_response' => 'provider response should stay hidden',
'stack_trace' => 'stack trace should stay hidden',
])->create([
'managed_environment_id' => (int) $environment->getKey(),
'workspace_id' => (int) $environment->workspace_id,
'fingerprint' => sha1('spec337-report-'.$environment->getKey().'-'.microtime(true)),
]);
}
/**
* @return array{0: EnvironmentReview, 1: ReviewPack}
*/
function spec337CreatePublishedReviewWithReadyPack(
ManagedEnvironment $environment,
User $user,
EvidenceSnapshot $snapshot,
): array {
$review = EnvironmentReview::query()->create([
'workspace_id' => (int) $environment->workspace_id,
'managed_environment_id' => (int) $environment->getKey(),
'evidence_snapshot_id' => (int) $snapshot->getKey(),
'initiated_by_user_id' => (int) $user->getKey(),
'published_by_user_id' => (int) $user->getKey(),
'fingerprint' => sha1('spec337-review-'.$environment->getKey().'-'.microtime(true)),
'status' => EnvironmentReviewStatus::Published->value,
'completeness_state' => EnvironmentReviewCompletenessState::Complete->value,
'summary' => [
'governance_package' => [
'decision_summary' => [
'status' => 'ready',
'evidence_state' => EnvironmentReviewCompletenessState::Complete->value,
'decision_data_state' => 'complete',
],
],
],
'generated_at' => now()->subMinutes(5),
'published_at' => now()->subMinutes(3),
]);
$run = OperationRun::factory()->forTenant($environment)->create([
'type' => OperationRunType::ReviewPackGenerate->value,
'status' => OperationRunStatus::Completed->value,
'outcome' => OperationRunOutcome::Succeeded->value,
'started_at' => now()->subMinutes(4),
'completed_at' => now()->subMinutes(2),
'initiator_name' => $user->name,
]);
$pack = ReviewPack::factory()->ready()->create([
'managed_environment_id' => (int) $environment->getKey(),
'workspace_id' => (int) $environment->workspace_id,
'evidence_snapshot_id' => (int) $snapshot->getKey(),
'environment_review_id' => (int) $review->getKey(),
'operation_run_id' => (int) $run->getKey(),
'initiated_by_user_id' => (int) $user->getKey(),
'status' => ReviewPackStatus::Ready->value,
'summary' => [
'finding_count' => 3,
'report_count' => 2,
'operation_count' => 1,
'section_count' => 4,
'evidence_resolution' => [
'required_dimensions' => [
'findings_summary',
'permission_posture',
'entra_admin_roles',
],
],
],
]);
$review->forceFill([
'current_export_review_pack_id' => (int) $pack->getKey(),
])->save();
return [$review->refresh(), $pack->refresh()];
}
function spec337AssertFlowStep(string $content, string $label, string $state, bool $currentBlocker): void
{
$blocker = $currentBlocker ? 'true' : 'false';
expect($content)->toMatch(
'/data-step-label="'.preg_quote($label, '/').'"[\s\S]*?data-step-state="'.preg_quote($state, '/').'"[\s\S]*?data-step-current-blocker="'.$blocker.'"[\s\S]*?>\s*'.preg_quote($state, '/').'\s*</'
);
}