' . $this->esc(t('This product has no technical checklist items defined yet.')) . '
'; + } + + $rows = ''; + foreach ($sections as $section) { + if (!is_array($section)) { + continue; + } + $label = trim((string) ($section['label'] ?? '')); + if ($label !== '') { + $rows .= '| Customer | {{customer_name}} ({{customer_no}}) |
| Domain | {{domain}} |
| Software product | {{product}} |
| Responsible | {{owner}} |
| Status | {{status}} |
x
')); + $this->assertSame('x
', $writes[SecurityReportSettingsGateway::KEY_TEMPLATE_HTML]); + + // Blank input clears the setting (restores the built-in default). + $this->assertTrue($gateway->setTemplateHtml(' ')); + $this->assertNull($writes[SecurityReportSettingsGateway::KEY_TEMPLATE_HTML]); + } +} diff --git a/modules/security/tests/Module/Security/Service/SecurityReportTemplateServiceTest.php b/modules/security/tests/Module/Security/Service/SecurityReportTemplateServiceTest.php new file mode 100644 index 0000000..fe46c60 --- /dev/null +++ b/modules/security/tests/Module/Security/Service/SecurityReportTemplateServiceTest.php @@ -0,0 +1,118 @@ + + */ + private function context(): array + { + return [ + 'title' => 'Security check report', + 'customer_name' => 'Acme', + 'customer_no' => 'D-100', + 'domain' => 'acme.de', + 'product' => 'Intranet', + 'owner' => 'Jane Doe', + 'status_label' => 'In progress', + 'generated_at' => '2026-06-22 10:00', + 'app_name' => 'CoreCore', + 'logo_data_uri' => 'data:image/png;base64,AAAA', + 'summary' => ['percent' => 72, 'tech_done' => 2, 'tech_total' => 3], + 'tech_sections' => [ + ['label' => 'Transport', 'items' => [ + ['label' => 'TLS enforced', 'done' => true, 'note' => 'TLS 1.3'], + ['label' => 'HSTS header', 'done' => false, 'note' => ''], + ]], + ], + 'steps' => [ + ['number' => 1, 'label' => 'Kickoff', 'done' => true, 'completed_at' => '2026-06-18'], + ], + ]; + } + + public function testRenderSubstitutesScalarsAndEscapesData(): void + { + $service = new SecurityReportTemplateService(); + + $out = $service->render( + '