Added jobs, controllers, and PDF generation logic for management report runtime as defined in Spec 379. Includes artifact migrations, payload builders, and testing coverage. Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de> Reviewed-on: #450
222 lines
7.9 KiB
PHP
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');
|
|
}
|
|
}
|