TenantAtlas/apps/platform/tests/Feature/Filament/Spec342CustomerReviewWorkspaceConsumptionTest.php
ahmido 12ea7f9924 feat: review pack output contract and readiness semantics (spec 347/348) (#419)
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.

Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #419
2026-06-02 23:17:08 +00: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();
}