TenantAtlas/apps/platform/tests/Feature/Filament/Spec342CustomerReviewWorkspaceConsumptionTest.php
Ahmed Darrazi 549a9a0004
Some checks failed
PR Fast Feedback / fast-feedback (pull_request) Failing after 1m0s
feat: review pack output contract and readiness semantics (spec 347)
Implemented the output contract and readiness semantics for review packs. Also added spec 348.
Includes changes to ChooseEnvironment, CustomerReviewWorkspace, GenerateReviewPackJob and related blade views.
Added comprehensive tests.
2026-06-03 01:14:29 +02:00

359 lines
16 KiB
PHP

<?php
declare(strict_types=1);
use App\Filament\Pages\Reviews\CustomerReviewWorkspace;
use App\Models\Finding;
use App\Models\FindingException;
use App\Models\ManagedEnvironment;
use App\Models\OperationRun;
use App\Models\ReviewPack;
use App\Models\User;
use App\Support\EnvironmentReviewCompletenessState;
use App\Support\EnvironmentReviewStatus;
use App\Support\Governance\Controls\ComplianceEvidenceMappingV1;
use App\Support\OperationRunStatus;
use App\Support\OperationRunType;
use App\Support\Workspaces\WorkspaceContext;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Livewire\Livewire;
uses(RefreshDatabase::class);
it('renders a decision-first consumption contract with one primary action and six flow steps', function (): void {
$environment = ManagedEnvironment::factory()->create(['name' => 'Spec342 Ready Environment']);
[$user, $environment] = createUserWithTenant(tenant: $environment, role: 'owner', workspaceRole: 'manager');
$snapshot = seedEnvironmentReviewEvidence($environment, findingCount: 0, driftCount: 0);
$run = OperationRun::factory()->forTenant($environment)->create([
'type' => OperationRunType::ReviewPackGenerate->value,
'status' => OperationRunStatus::Completed->value,
'initiator_name' => 'Spec342 Operator',
]);
$review = spec342PublishedReview($environment, $user, $snapshot, [
'control_interpretation' => [
'version_key' => ComplianceEvidenceMappingV1::VERSION_KEY,
'controls' => [
[
'control_key' => 'customer-output',
'title' => 'Customer output',
'readiness_bucket' => 'evidence_on_record',
'readiness_label' => 'Evidence on record',
'primary_reason' => 'Evidence path is complete.',
'recommended_next_action' => 'Open the current customer review pack.',
],
],
],
'governance_package' => [
'decision_summary' => [
'status' => 'none',
'evidence_state' => EnvironmentReviewCompletenessState::Complete->value,
'decision_data_state' => 'complete',
'total_count' => 0,
'summary' => 'No governance decisions require customer awareness.',
'next_action' => 'Open the current customer review pack.',
'entries' => [],
],
],
]);
$pack = ReviewPack::factory()->ready()->create([
'managed_environment_id' => (int) $environment->getKey(),
'workspace_id' => (int) $environment->workspace_id,
'environment_review_id' => (int) $review->getKey(),
'evidence_snapshot_id' => (int) $snapshot->getKey(),
'operation_run_id' => (int) $run->getKey(),
'initiated_by_user_id' => (int) $user->getKey(),
'options' => [
'include_pii' => false,
'include_operations' => true,
],
]);
$review->forceFill([
'current_export_review_pack_id' => (int) $pack->getKey(),
'operation_run_id' => (int) $run->getKey(),
])->save();
$component = spec342WorkspaceComponent($user, $environment);
$component
->assertSee('What is the current review pack output state?')
->assertSee('Customer-safe review pack ready')
->assertSee('Stakeholders can use the current review pack and released review as the evidence path.')
->assertSee('Review consumption flow')
->assertSee('Review data')
->assertSee('Findings triaged')
->assertSee('Accepted risks reviewed')
->assertSee('Customer-safe output')
->assertSee('Findings needing attention')
->assertSee('No open findings require customer action.')
->assertSee('Review pack state')
->assertSee('Export ready')
->assertSee('Download customer-safe review pack')
->assertSee('Operation proof')
->assertSee('Spec342 Operator')
->assertDontSee('Auditor-ready')
->assertDontSee('environment is healthy')
->assertDontSee('compliant');
$html = $component->html();
expect(substr_count($html, 'data-testid="customer-review-primary-action"'))->toBe(1)
->and(substr_count($html, 'data-testid="customer-review-readiness-step"'))->toBe(6)
->and($html)->toContain('source_surface=customer_review_workspace')
->and($html)->not->toContain('/admin/t/');
});
it('shows not-ready proof states without raw diagnostics or false output claims', function (): void {
$environment = ManagedEnvironment::factory()->create(['name' => 'Spec342 Evidence Missing']);
[$user, $environment] = createUserWithTenant(tenant: $environment, role: 'readonly');
$snapshot = seedPartialEnvironmentReviewEvidence($environment, findingCount: 0, driftCount: 0);
$review = spec342PublishedReview($environment, $user, $snapshot, [
'debug_payload' => 'raw payload should stay hidden',
'provider_response' => 'provider response should stay hidden',
'stack_trace' => 'stack trace should stay hidden',
'source_fingerprint' => 'spec342-hidden-fingerprint',
'control_interpretation' => [
'version_key' => ComplianceEvidenceMappingV1::VERSION_KEY,
'controls' => [
[
'control_key' => 'customer-output',
'title' => 'Customer output',
'readiness_bucket' => 'review_recommended',
'readiness_label' => 'Review recommended',
'primary_reason' => 'Evidence basis needs review.',
'recommended_next_action' => 'Review evidence before sharing.',
],
],
],
'governance_package' => [
'decision_summary' => [
'status' => 'incomplete',
'total_count' => 1,
'summary' => 'Decision evidence is incomplete for this released review.',
'next_action' => 'Review the evidence basis before relying on the decision summary.',
'entries' => [],
],
],
], normalizeOutputReadiness: false);
$pack = ReviewPack::factory()->ready()->create([
'managed_environment_id' => (int) $environment->getKey(),
'workspace_id' => (int) $environment->workspace_id,
'environment_review_id' => (int) $review->getKey(),
'evidence_snapshot_id' => (int) $snapshot->getKey(),
'initiated_by_user_id' => (int) $user->getKey(),
'options' => [
'include_pii' => false,
'include_operations' => true,
],
]);
$review->forceFill(['current_export_review_pack_id' => (int) $pack->getKey()])->save();
$component = spec342WorkspaceComponent($user, $environment)
->assertSee('Published with limitations')
->assertSee('The review package is published, but the evidence basis is incomplete.')
->assertSee('Customer-safe output')
->assertSee('Needs review')
->assertSee('Diagnostics')
->assertSee('Collapsed')
->assertDontSee('Ready to share')
->assertSee('Export ready')
->assertSee('Download review pack with limitations')
->assertDontSee('raw payload should stay hidden')
->assertDontSee('provider response should stay hidden')
->assertDontSee('stack trace should stay hidden')
->assertDontSee('spec342-hidden-fingerprint');
expect($component->html())->not->toContain('data-testid="customer-review-diagnostics" open');
});
it('uses concrete findings follow-up copy and keeps review-pack download primary-only', function (): void {
$environment = ManagedEnvironment::factory()->create(['name' => 'Spec342 Findings Follow-up']);
[$user, $environment] = createUserWithTenant(tenant: $environment, role: 'owner', workspaceRole: 'manager');
$snapshot = seedEnvironmentReviewEvidence($environment, findingCount: 0, driftCount: 0);
$run = OperationRun::factory()->forTenant($environment)->create([
'type' => OperationRunType::ReviewPackGenerate->value,
'status' => OperationRunStatus::Completed->value,
'initiator_name' => 'Spec342 Operator',
]);
$review = spec342PublishedReview($environment, $user, $snapshot);
$pack = ReviewPack::factory()->ready()->create([
'managed_environment_id' => (int) $environment->getKey(),
'workspace_id' => (int) $environment->workspace_id,
'environment_review_id' => (int) $review->getKey(),
'evidence_snapshot_id' => (int) $snapshot->getKey(),
'operation_run_id' => (int) $run->getKey(),
'initiated_by_user_id' => (int) $user->getKey(),
'options' => [
'include_pii' => false,
'include_operations' => true,
],
]);
$review->forceFill([
'current_export_review_pack_id' => (int) $pack->getKey(),
'operation_run_id' => (int) $run->getKey(),
])->save();
Finding::factory()->create([
'managed_environment_id' => (int) $environment->getKey(),
'workspace_id' => (int) $environment->workspace_id,
'severity' => Finding::SEVERITY_CRITICAL,
'status' => Finding::STATUS_NEW,
]);
$component = spec342WorkspaceComponent($user, $environment)
->assertSee('Published with limitations')
->assertSee('1 open finding needs attention; 1 is high impact. Keep open findings visible before customer handoff.')
->assertSee('Do not treat this review as share-ready until open findings are resolved, accepted, or explicitly reviewed.')
->assertSee('Open review')
->assertSee('Review pack state')
->assertSee('Download review pack with limitations')
->assertDontSee('TenantPilot recorded an access, scope, or configuration issue');
$html = $component->html();
expect(substr_count($html, 'data-testid="customer-review-primary-action"'))->toBe(1)
->and($html)->toContain('data-testid="customer-review-findings-summary"')
->and($html)->not->toContain('data-testid="customer-review-readiness-dimensions"');
});
it('surfaces accepted-risk owner rationale and missing review dates when repo-backed', function (): void {
$environment = ManagedEnvironment::factory()->create(['name' => 'Spec342 Accepted Risk']);
[$user, $environment] = createUserWithTenant(tenant: $environment, role: 'readonly');
$owner = User::factory()->create(['name' => 'Spec342 Risk Owner']);
$finding = Finding::factory()->create([
'managed_environment_id' => (int) $environment->getKey(),
'workspace_id' => (int) $environment->workspace_id,
'status' => Finding::STATUS_RISK_ACCEPTED,
]);
$findingWithoutReviewDate = Finding::factory()->create([
'managed_environment_id' => (int) $environment->getKey(),
'workspace_id' => (int) $environment->workspace_id,
'status' => Finding::STATUS_RISK_ACCEPTED,
]);
FindingException::query()->create([
'workspace_id' => (int) $environment->workspace_id,
'managed_environment_id' => (int) $environment->getKey(),
'finding_id' => (int) $findingWithoutReviewDate->getKey(),
'status' => FindingException::STATUS_ACTIVE,
'current_validity_state' => FindingException::VALIDITY_VALID,
'requested_by_user_id' => (int) $user->getKey(),
'request_reason' => 'Customer-approved maintenance window.',
'owner_user_id' => (int) $owner->getKey(),
'approved_by_user_id' => (int) $owner->getKey(),
'requested_at' => now()->subDays(3),
'approved_at' => now()->subDays(2),
'effective_from' => now()->subDays(2),
'review_due_at' => now()->addDays(30),
]);
FindingException::query()->create([
'workspace_id' => (int) $environment->workspace_id,
'managed_environment_id' => (int) $environment->getKey(),
'finding_id' => (int) $finding->getKey(),
'status' => FindingException::STATUS_ACTIVE,
'current_validity_state' => FindingException::VALIDITY_EXPIRING,
'requested_by_user_id' => (int) $user->getKey(),
'request_reason' => 'Pending owner confirmation.',
'requested_at' => now()->subDay(),
'approved_at' => now()->subDay(),
'effective_from' => now()->subDay(),
'review_due_at' => null,
'expires_at' => null,
]);
$snapshot = seedEnvironmentReviewEvidence($environment, findingCount: 0, driftCount: 0);
spec342PublishedReview($environment, $user, $snapshot, [
'governance_package' => [
'accepted_risks' => [
[
'title' => 'Accepted risk renewal',
'governance_state' => 'expiring_exception',
'customer_summary' => 'Accepted risk requires customer awareness.',
],
],
],
]);
spec342WorkspaceComponent($user, $environment)
->assertSee('Accepted risks')
->assertSee('2')
->assertSee('Spec342 Risk Owner')
->assertSee(now()->addDays(30)->toDateString())
->assertSee('Customer-approved maintenance window.')
->assertSee('Review date not recorded')
->assertSee('Accepted risk requires customer awareness.')
->assertDontSee('raw operation');
});
it('keeps environment_id as the only canonical workspace filter and rejects cross-workspace targets', function (): void {
$allowed = ManagedEnvironment::factory()->create(['name' => 'Spec342 Allowed Environment']);
[$user, $allowed] = createUserWithTenant(tenant: $allowed, role: 'readonly');
$other = ManagedEnvironment::factory()->create([
'workspace_id' => (int) $allowed->workspace_id,
'name' => 'Spec342 Other Environment',
]);
createUserWithTenant(tenant: $other, user: $user, role: 'readonly');
$foreign = ManagedEnvironment::factory()->create(['name' => 'Spec342 Foreign Environment']);
spec342PublishedReview($allowed, $user, seedEnvironmentReviewEvidence($allowed));
spec342PublishedReview($other, $user, seedEnvironmentReviewEvidence($other));
$this->actingAs($user)->withSession([WorkspaceContext::SESSION_KEY => (int) $allowed->workspace_id]);
Livewire::withQueryParams([
'environment_id' => (int) $allowed->getKey(),
'tenant' => (string) $other->external_id,
'tenant_id' => (int) $other->getKey(),
])
->actingAs($user)
->test(CustomerReviewWorkspace::class)
->assertSet('tableFilters.managed_environment_id.value', (string) $allowed->getKey())
->assertCanSeeTableRecords([$allowed->fresh()])
->assertCanNotSeeTableRecords([$other->fresh()])
->assertSee('Environment filter:')
->assertSee('Spec342 Allowed Environment')
->assertDontSee('/admin/t', false)
->assertDontSee('tenant_id=', false);
$this->actingAs($user)
->withSession([WorkspaceContext::SESSION_KEY => (int) $allowed->workspace_id])
->get(CustomerReviewWorkspace::getUrl(panel: 'admin').'?environment_id='.(string) $foreign->getKey())
->assertNotFound();
});
function spec342WorkspaceComponent(User $user, ManagedEnvironment $environment): mixed
{
$workspaceId = (int) $environment->workspace_id;
session()->put(WorkspaceContext::SESSION_KEY, $workspaceId);
setAdminPanelContext();
return Livewire::actingAs($user)
->test(CustomerReviewWorkspace::class);
}
function spec342PublishedReview(
ManagedEnvironment $environment,
User $user,
\App\Models\EvidenceSnapshot $snapshot,
array $summaryOverrides = [],
bool $normalizeOutputReadiness = true,
): \App\Models\EnvironmentReview {
$review = composeEnvironmentReviewForTest($environment, $user, $snapshot);
$summary = array_replace_recursive(is_array($review->summary) ? $review->summary : [], $summaryOverrides);
$review->forceFill([
'status' => EnvironmentReviewStatus::Published->value,
'summary' => $summary,
'generated_at' => now(),
'published_at' => now(),
'published_by_user_id' => (int) $user->getKey(),
])->save();
if ($normalizeOutputReadiness) {
return markEnvironmentReviewCustomerSafeReady($review);
}
return $review->refresh();
}