Files
breadcrumb-the-shire/modules/security/tests/Module/Security/Service/SecurityReportPdfServiceTest.php

130 lines
5.4 KiB
PHP
Raw Normal View History

2026-06-22 13:19:05 +02:00
<?php
namespace MintyPHP\Tests\Module\Security\Service;
use MintyPHP\Module\Security\Service\SecurityCheckProcess;
use MintyPHP\Module\Security\Service\SecurityReportPdfService;
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',
'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->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);
}
}