TenantAtlas/apps/platform/tests/Browser/Spec379ManagementReportPdfSmokeTest.php
Ahmed Darrazi 6c7a80e275
Some checks failed
PR Fast Feedback / fast-feedback (pull_request) Failing after 5m7s
feat(report): implement management report pdf runtime
Added jobs, controllers, and PDF generation logic for management report runtime as defined in Spec 379. Includes artifact migrations, payload builders, and testing coverage.
2026-06-15 13:24:41 +02:00

222 lines
7.9 KiB
PHP

<?php
declare(strict_types=1);
use App\Filament\Resources\ReviewPackResource;
use App\Models\EnvironmentReview;
use App\Models\ManagedEnvironment;
use App\Models\ReviewPack;
use App\Models\StoredReport;
use App\Models\User;
use App\Support\ReviewPacks\ReportProfileRegistry;
use App\Support\Workspaces\WorkspaceContext;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Storage;
uses(RefreshDatabase::class);
pest()->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');
}
}