browser()->timeout(60_000); beforeEach(function (): void { Storage::fake('exports'); }); it('Spec347 smokes review pack output readiness states', function (): void { [$user, $readyEnvironment] = createUserWithTenant(role: 'owner', workspaceRole: 'manager'); $readyEnvironment->forceFill(['name' => 'Spec347 Browser Ready'])->save(); $limitedEnvironment = spec347BrowserEnvironmentFor($user, $readyEnvironment, 'Spec347 Browser Limited'); $internalEnvironment = spec347BrowserEnvironmentFor($user, $readyEnvironment, 'Spec347 Browser Internal'); spec347BrowserCreatePublishedReviewWithPack( $readyEnvironment, $user, seedEnvironmentReviewEvidence($readyEnvironment, findingCount: 0, driftCount: 0), [], [ 'include_pii' => false, 'include_operations' => true, ], 'review-packs/spec347-browser-ready.zip', ); spec347BrowserCreatePublishedReviewWithPack( $limitedEnvironment, $user, seedPartialEnvironmentReviewEvidence($limitedEnvironment, findingCount: 0, driftCount: 0), [ 'governance_package' => [ 'decision_summary' => [ 'status' => 'incomplete', 'evidence_state' => EnvironmentReviewCompletenessState::Partial->value, 'decision_data_state' => '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' => [], ], ], ], [ 'include_pii' => false, 'include_operations' => true, ], 'review-packs/spec347-browser-limited.zip', normalizeOutputReadiness: false, ); spec347BrowserCreatePublishedReviewWithPack( $internalEnvironment, $user, seedEnvironmentReviewEvidence($internalEnvironment, findingCount: 0, driftCount: 0), [], [ 'include_pii' => true, 'include_operations' => true, ], 'review-packs/spec347-browser-internal.zip', ); spec347AuthenticateBrowser($this, $user, $readyEnvironment); $page = visit(CustomerReviewWorkspace::environmentFilterUrl($readyEnvironment)) ->resize(1236, 900) ->waitForText('Customer-safe review pack ready') ->assertSee('Download customer-safe review pack') ->assertSee('PII excluded') ->assertDontSee('Ready to share') ->assertNoJavaScriptErrors() ->assertNoConsoleLogs(); $page->screenshot(true, spec347BrowserScreenshotName('01-customer-safe-ready')); spec347CopyBrowserScreenshot('01-customer-safe-ready'); $page = visit(CustomerReviewWorkspace::environmentFilterUrl($limitedEnvironment)) ->waitForText('Output not customer-ready') ->assertSee('Review blockers are still recorded for this output.') ->assertSee('Download review pack with limitations') ->assertSee('Requires review') ->assertNoJavaScriptErrors() ->assertNoConsoleLogs(); $page->screenshot(true, spec347BrowserScreenshotName('02-published-with-limitations')); spec347CopyBrowserScreenshot('02-published-with-limitations'); $page = visit(CustomerReviewWorkspace::environmentFilterUrl($internalEnvironment)) ->waitForText('Internal review package available') ->assertSee('Contains PII') ->assertSee('Review PII/redaction state') ->assertSee('Download internal review pack') ->assertSee('Internal only') ->assertNoJavaScriptErrors() ->assertNoConsoleLogs(); $page->screenshot(true, spec347BrowserScreenshotName('03-internal-review-package')); spec347CopyBrowserScreenshot('03-internal-review-package'); }); function spec347BrowserScreenshotName(string $name): string { return 'spec347-review-pack-output-readiness-'.$name; } function spec347CopyBrowserScreenshot(string $name): void { $filename = spec347BrowserScreenshotName($name).'.png'; $source = base_path('tests/Browser/Screenshots/'.$filename); $targetDirectory = repo_path('specs/347-review-pack-output-contract-readiness-semantics/artifacts/screenshots'); if (! is_dir($targetDirectory)) { @mkdir($targetDirectory, 0755, true); } if (! is_file($source)) { $source = \Pest\Browser\Support\Screenshot::path($filename); } for ($attempt = 0; $attempt < 10 && ! is_file($source); $attempt++) { usleep(100_000); clearstatcache(true, $source); } if (is_file($source) && is_dir($targetDirectory) && is_writable($targetDirectory)) { @copy($source, $targetDirectory.DIRECTORY_SEPARATOR.$name.'.png'); } } function spec347AuthenticateBrowser(mixed $test, User $user, ManagedEnvironment $environment): void { $workspaceId = (int) $environment->workspace_id; $test->actingAs($user)->withSession([ WorkspaceContext::SESSION_KEY => $workspaceId, WorkspaceContext::LAST_ENVIRONMENT_IDS_SESSION_KEY => [ (string) $workspaceId => (int) $environment->getKey(), ], ]); session()->put(WorkspaceContext::SESSION_KEY, $workspaceId); session()->put(WorkspaceContext::LAST_ENVIRONMENT_IDS_SESSION_KEY, [ (string) $workspaceId => (int) $environment->getKey(), ]); setAdminPanelContext($environment); } function spec347BrowserEnvironmentFor(User $user, ManagedEnvironment $baseEnvironment, string $name): ManagedEnvironment { $environment = ManagedEnvironment::factory()->active()->create([ 'workspace_id' => (int) $baseEnvironment->workspace_id, 'name' => $name, ]); createUserWithTenant(tenant: $environment, user: $user, role: 'owner', workspaceRole: 'manager'); return $environment; } /** * @param array $summaryOverrides * @param array $packOptions * @return array{0: EnvironmentReview, 1: ReviewPack} */ function spec347BrowserCreatePublishedReviewWithPack( ManagedEnvironment $environment, User $user, EvidenceSnapshot $snapshot, array $summaryOverrides = [], array $packOptions = [], string $filePath = 'review-packs/spec347-browser-review-pack.zip', bool $normalizeOutputReadiness = true, ): array { $review = composeEnvironmentReviewForTest($environment, $user, $snapshot); $summary = array_replace_recursive( is_array($review->summary) ? $review->summary : [], [ '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' => [], ], ], ], $summaryOverrides, ); Storage::disk('exports')->put($filePath, 'PK-spec347-browser-test'); $review->forceFill([ 'status' => EnvironmentReviewStatus::Published->value, 'completeness_state' => EnvironmentReviewCompletenessState::Complete->value, 'summary' => $summary, 'generated_at' => now()->subMinutes(5), 'published_at' => now()->subMinutes(3), 'published_by_user_id' => (int) $user->getKey(), ])->save(); if ($normalizeOutputReadiness) { $review = markEnvironmentReviewCustomerSafeReady($review); } $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' => array_replace([ 'include_pii' => false, 'include_operations' => true, ], $packOptions), 'file_path' => $filePath, 'file_disk' => 'exports', 'generated_at' => now()->subMinutes(4), ]); $review->forceFill([ 'current_export_review_pack_id' => (int) $pack->getKey(), ])->save(); return [$review->refresh(), $pack->refresh()]; }