forked from fa/breadcrumb-the-shire
158 lines
6.4 KiB
PHP
158 lines
6.4 KiB
PHP
<?php
|
|
|
|
namespace MintyPHP\Tests\Module\Security\Service;
|
|
|
|
use MintyPHP\Module\Security\Service\SecurityCheckProcess;
|
|
use MintyPHP\Module\Security\Service\SecurityReportPdfService;
|
|
use MintyPHP\Module\Security\Service\SecurityReportSettingsGateway;
|
|
use MintyPHP\Module\Security\Service\SecurityReportTemplateService;
|
|
use MintyPHP\Service\Branding\BrandingLogoService;
|
|
use PHPUnit\Framework\TestCase;
|
|
|
|
class SecurityReportPdfServiceTest extends TestCase
|
|
{
|
|
private function makeService(): SecurityReportPdfService
|
|
{
|
|
$branding = $this->createMock(BrandingLogoService::class);
|
|
$branding->method('hasLogo')->willReturn(false);
|
|
|
|
// Null tenant-logo service: render falls back to the static brand asset.
|
|
return new SecurityReportPdfService(new SecurityCheckProcess(), $branding, null);
|
|
}
|
|
|
|
/**
|
|
* @return array<string, mixed>
|
|
*/
|
|
private function enrichedCheck(): array
|
|
{
|
|
$process = new SecurityCheckProcess();
|
|
$processState = [];
|
|
foreach ($process->manualStepKeys() as $key) {
|
|
$processState[$key] = ['done' => true, 'at' => '2026-06-18 10:00:00'];
|
|
}
|
|
$snapshot = ['version' => 1, 'items' => [
|
|
['type' => 'section', 'label' => 'Transport'],
|
|
['type' => 'check', 'key' => 'tls', 'label' => 'TLS enforced'],
|
|
['type' => 'check', 'key' => 'hsts', 'label' => 'HSTS header'],
|
|
['type' => 'section', 'label' => 'Application'],
|
|
['type' => 'check', 'key' => 'csrf', 'label' => 'CSRF protection'],
|
|
]];
|
|
$techState = [
|
|
'tls' => ['done' => true, 'note' => 'TLS 1.3'],
|
|
'hsts' => ['done' => true, 'note' => ''],
|
|
'csrf' => ['done' => false, 'note' => 'pending review'],
|
|
];
|
|
|
|
return [
|
|
'debitor_name' => 'Acme GmbH',
|
|
'debitor_no' => 'D-100',
|
|
'domain_url' => 'acme.de',
|
|
'domain_no' => 'DNS-1',
|
|
'product_name' => 'Intranet',
|
|
'product_code' => 'intranet',
|
|
'owner_name' => 'Jane Doe',
|
|
'status' => SecurityCheckProcess::STATUS_IN_PROGRESS,
|
|
'process_state' => $processState,
|
|
'tech_schema_snapshot' => $snapshot,
|
|
'tech_state' => $techState,
|
|
'progress' => $process->computeProgress($processState, $snapshot, $techState),
|
|
];
|
|
}
|
|
|
|
public function testBuildReportContextMapsCheckDataIntoRenderShape(): void
|
|
{
|
|
$context = $this->makeService()->buildReportContext($this->enrichedCheck());
|
|
|
|
$this->assertSame('Acme GmbH', $context['customer_name']);
|
|
$this->assertSame('D-100', $context['customer_no']);
|
|
$this->assertSame('acme.de', $context['domain']);
|
|
$this->assertSame('Intranet', $context['product']);
|
|
$this->assertSame('Jane Doe', $context['owner']);
|
|
$this->assertNotSame('', $context['title']);
|
|
|
|
// Summary mirrors the progress math (3 tech items, 2 done).
|
|
$this->assertSame(3, $context['summary']['tech_total']);
|
|
$this->assertSame(2, $context['summary']['tech_done']);
|
|
|
|
// Five manual milestones, numbered with the technical step skipped.
|
|
$this->assertCount(5, $context['steps']);
|
|
$this->assertSame(1, $context['steps'][0]['number']);
|
|
$this->assertTrue($context['steps'][0]['done']);
|
|
$this->assertSame('2026-06-18 10:00:00', $context['steps'][0]['completed_at']);
|
|
// 'report' is step 6 (the tech checklist step 5 is skipped from the milestone list).
|
|
$this->assertSame(6, $context['steps'][4]['number']);
|
|
|
|
// Tech items grouped under their sections, carrying state + notes.
|
|
$this->assertCount(2, $context['tech_sections']);
|
|
$this->assertSame('Transport', $context['tech_sections'][0]['label']);
|
|
$this->assertCount(2, $context['tech_sections'][0]['items']);
|
|
$this->assertSame('TLS enforced', $context['tech_sections'][0]['items'][0]['label']);
|
|
$this->assertTrue($context['tech_sections'][0]['items'][0]['done']);
|
|
$this->assertSame('TLS 1.3', $context['tech_sections'][0]['items'][0]['note']);
|
|
$this->assertSame('Application', $context['tech_sections'][1]['label']);
|
|
$this->assertFalse($context['tech_sections'][1]['items'][0]['done']);
|
|
}
|
|
|
|
public function testBuildReportContextHandlesEmptyCheckSafely(): void
|
|
{
|
|
$context = $this->makeService()->buildReportContext([]);
|
|
|
|
$this->assertSame('', $context['customer_name']);
|
|
$this->assertSame('', $context['domain']);
|
|
$this->assertSame([], $context['tech_sections']);
|
|
$this->assertSame(0, $context['summary']['total']);
|
|
$this->assertSame(0, $context['summary']['percent']);
|
|
// The fixed process always yields its five manual milestones, all open.
|
|
$this->assertCount(5, $context['steps']);
|
|
$this->assertFalse($context['steps'][0]['done']);
|
|
}
|
|
|
|
public function testBuildReportFilenameSlugifiesCustomerAndDomain(): void
|
|
{
|
|
$name = $this->makeService()->buildReportFilename($this->enrichedCheck());
|
|
|
|
$this->assertSame('security-check-report-acme-gmbh-acme-de.pdf', $name);
|
|
}
|
|
|
|
public function testBuildReportFilenameFallsBackWhenNoIdentifiers(): void
|
|
{
|
|
$name = $this->makeService()->buildReportFilename([]);
|
|
|
|
$this->assertSame('security-check-report.pdf', $name);
|
|
}
|
|
|
|
public function testRenderCustomerReportPdfProducesPdfBytes(): void
|
|
{
|
|
$pdf = $this->makeService()->renderCustomerReportPdf($this->enrichedCheck(), [
|
|
'app_name' => 'CoreCore',
|
|
]);
|
|
|
|
$this->assertNotSame('', $pdf);
|
|
$this->assertStringStartsWith('%PDF-', $pdf);
|
|
}
|
|
|
|
public function testRenderCustomerReportPdfUsesConfiguredCustomTemplate(): void
|
|
{
|
|
$branding = $this->createMock(BrandingLogoService::class);
|
|
$branding->method('hasLogo')->willReturn(false);
|
|
|
|
$settings = $this->createMock(SecurityReportSettingsGateway::class);
|
|
$settings->method('getTemplateHtml')->willReturn(
|
|
'<!doctype html><html><body><h1>Custom {{customer_name}}</h1>{{checklist}}</body></html>'
|
|
);
|
|
|
|
$service = new SecurityReportPdfService(
|
|
new SecurityCheckProcess(),
|
|
$branding,
|
|
null,
|
|
$settings,
|
|
new SecurityReportTemplateService(),
|
|
null
|
|
);
|
|
|
|
$pdf = $service->renderCustomerReportPdf($this->enrichedCheck(), ['app_name' => 'CoreCore']);
|
|
|
|
$this->assertStringStartsWith('%PDF-', $pdf);
|
|
}
|
|
}
|