browser()->timeout(60_000); beforeEach(function (): void { Storage::fake('exports'); }); it('Spec379 smokes the management PDF Review Pack detail states', function (): void { config([ 'tenantpilot.pdf_renderer.enabled' => true, 'tenantpilot.pdf_renderer.driver' => 'gotenberg', 'tenantpilot.pdf_renderer.runtime_validated' => false, ]); [$user, $tenant, $review, $pack] = spec379BrowserCurrentReadyPack(); spec379BrowserAuthenticate($this, $user, $tenant); $packUrl = ReviewPackResource::getUrl('view', ['record' => $pack], tenant: $tenant, panel: 'admin'); $page = visit($packUrl) ->resize(1280, 900) ->waitForText('More') ->assertSee('View customer-safe report') ->assertSee('Download customer-safe review pack') ->assertSee('More') ->assertDontSee('Generate management PDF') ->click('More') ->waitForText('Management PDF unavailable') ->assertSee('Management PDF unavailable') ->assertSee('Regenerate review pack') ->assertDontSee('Generate management PDF') ->assertNoJavaScriptErrors() ->assertNoConsoleLogs() ->screenshot(true, 'spec379-management-report-pdf-generate-state'); spec379BrowserCopyScreenshot('generate-state'); spec379BrowserReadyManagementPdf($pack); $page = visit($packUrl) ->resize(1280, 900) ->waitForText('Download management PDF') ->assertSee('Download management PDF') ->assertSee('More') ->assertDontSee('Generate management PDF') ->assertNoJavaScriptErrors() ->assertNoConsoleLogs() ->screenshot(true, 'spec379-management-report-pdf-download-state'); spec379BrowserCopyScreenshot('download-state'); expect($review)->toBeInstanceOf(EnvironmentReview::class); }); /** * @return array{0: User, 1: ManagedEnvironment, 2: EnvironmentReview, 3: ReviewPack} */ function spec379BrowserCurrentReadyPack(): array { [$user, $tenant] = createUserWithTenant(role: 'owner', workspaceRole: 'manager', clearCapabilityCaches: true); $tenant->forceFill(['name' => 'Spec379 Browser Environment'])->save(); $snapshot = seedEnvironmentReviewEvidence($tenant, findingCount: 0, driftCount: 0); $review = composeEnvironmentReviewForTest($tenant, $user, $snapshot); $review->forceFill([ 'status' => 'published', 'published_at' => now(), 'published_by_user_id' => (int) $user->getKey(), ])->save(); $review = markEnvironmentReviewCustomerSafeReady($review); $zipContents = spec379BrowserZipContents(); $filePath = sprintf('review-packs/%s/spec379-browser.zip', $tenant->external_id); Storage::disk('exports')->put($filePath, $zipContents); $pack = ReviewPack::factory()->ready()->create([ 'managed_environment_id' => (int) $tenant->getKey(), 'workspace_id' => (int) $tenant->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, ], 'summary' => [ 'governance_package' => [ 'executive_summary' => 'Spec379 browser management handoff summary.', ], 'control_interpretation' => [ 'non_certification_disclosure' => 'TenantPilot summarizes available evidence. This report is not a certification, legal attestation, audit opinion, or compliance guarantee.', ], ], 'file_path' => $filePath, 'file_disk' => 'exports', 'file_size' => strlen($zipContents), 'sha256' => hash('sha256', $zipContents), 'expires_at' => now()->addDay(), ]); $review->forceFill([ 'current_export_review_pack_id' => (int) $pack->getKey(), ])->save(); return [$user, $tenant, $review->fresh(['sections', 'evidenceSnapshot', 'currentExportReviewPack']), $pack->fresh(['tenant', 'environmentReview'])]; } function spec379BrowserReadyManagementPdf(ReviewPack $pack): StoredReport { $pack->loadMissing('tenant'); $pdfBytes = '%PDF-1.7 Spec379 browser management report'; $filePath = sprintf('management-reports/%s/browser-%d.pdf', $pack->tenant->external_id, (int) $pack->getKey()); Storage::disk('exports')->put($filePath, $pdfBytes); return StoredReport::factory()->managementReportPdf([ 'title' => 'TenantPilot Management Report', ])->create([ 'workspace_id' => (int) $pack->workspace_id, 'managed_environment_id' => (int) $pack->managed_environment_id, 'source_environment_review_id' => (int) $pack->environment_review_id, 'source_review_pack_id' => (int) $pack->getKey(), 'profile' => ReportProfileRegistry::CUSTOMER_EXECUTIVE, 'file_disk' => 'exports', 'file_path' => $filePath, 'file_size' => strlen($pdfBytes), 'sha256' => hash('sha256', $pdfBytes), 'generated_at' => now(), ]); } function spec379BrowserZipContents(): string { $tempFile = tempnam(sys_get_temp_dir(), 'spec379-browser-pack-'); if ($tempFile === false) { throw new RuntimeException('Failed to allocate Spec379 browser archive.'); } try { $zip = new ZipArchive; $result = $zip->open($tempFile, ZipArchive::CREATE | ZipArchive::OVERWRITE); if ($result !== true) { throw new RuntimeException("Failed to create Spec379 browser archive: {$result}"); } $zip->addFromString('metadata.json', json_encode(['fixture' => 'spec379-browser'], JSON_THROW_ON_ERROR)); $zip->addFromString('executive-summary.md', 'Spec379 browser review pack.'); $zip->close(); $contents = file_get_contents($tempFile); if (! is_string($contents) || $contents === '') { throw new RuntimeException('Spec379 browser archive is empty.'); } return $contents; } finally { if (file_exists($tempFile)) { unlink($tempFile); } } } function spec379BrowserAuthenticate(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 spec379BrowserCopyScreenshot(string $name): void { $filename = 'spec379-management-report-pdf-'.$name.'.png'; $source = base_path('tests/Browser/Screenshots/'.$filename); $targetDirectory = repo_path('specs/379-management-report-pdf-runtime/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'); } }