forked from fa/breadcrumb-the-shire
219 lines
8.4 KiB
PHP
219 lines
8.4 KiB
PHP
<?php
|
|
|
|
namespace MintyPHP\Tests\Module\Security\Service;
|
|
|
|
use MintyPHP\Module\Security\Service\SecurityCheckProcess;
|
|
use PHPUnit\Framework\TestCase;
|
|
|
|
class SecurityCheckProcessTest extends TestCase
|
|
{
|
|
public function testStepsContainSixOrderedSteps(): void
|
|
{
|
|
$steps = (new SecurityCheckProcess())->steps();
|
|
|
|
$this->assertCount(6, $steps);
|
|
$this->assertSame('beauftragung', $steps[0]['key']);
|
|
$this->assertSame(SecurityCheckProcess::STEP_PERFORM_CHECK, $steps[4]['key']);
|
|
$this->assertSame('report', $steps[5]['key']);
|
|
}
|
|
|
|
public function testManualStepKeysExcludeTechChecklistStep(): void
|
|
{
|
|
$keys = (new SecurityCheckProcess())->manualStepKeys();
|
|
|
|
$this->assertCount(5, $keys);
|
|
$this->assertNotContains(SecurityCheckProcess::STEP_PERFORM_CHECK, $keys);
|
|
$this->assertContains(SecurityCheckProcess::STEP_INFORMATION, $keys);
|
|
}
|
|
|
|
public function testInfoStepFieldsAreDefined(): void
|
|
{
|
|
$fields = (new SecurityCheckProcess())->stepFields(SecurityCheckProcess::STEP_INFORMATION);
|
|
|
|
$this->assertNotEmpty($fields);
|
|
$keys = array_column($fields, 'key');
|
|
$this->assertContains('technical_contact', $keys);
|
|
$this->assertContains('external_systems', $keys);
|
|
}
|
|
|
|
public function testRequiredFieldKeysExcludeOptionalFields(): void
|
|
{
|
|
$keys = (new SecurityCheckProcess())->requiredFieldKeys(SecurityCheckProcess::STEP_INFORMATION);
|
|
|
|
$this->assertContains('technical_contact', $keys);
|
|
$this->assertContains('deployment', $keys);
|
|
$this->assertContains('system_info', $keys);
|
|
$this->assertContains('external_systems', $keys);
|
|
$this->assertNotContains('special_notes', $keys);
|
|
}
|
|
|
|
public function testSchedulingAndBlockerStepsRequireTwoDatetimeFields(): void
|
|
{
|
|
$process = new SecurityCheckProcess();
|
|
$expected = [
|
|
'scheduling' => ['golive_start', 'golive_end'],
|
|
'blocker_appointment' => ['blocker_start', 'blocker_end'],
|
|
];
|
|
|
|
foreach ($expected as $stepKey => $expectedKeys) {
|
|
$fields = $process->stepFields($stepKey);
|
|
$this->assertCount(2, $fields);
|
|
foreach ($fields as $field) {
|
|
$this->assertSame(SecurityCheckProcess::FIELD_DATETIME, $field['type']);
|
|
$this->assertTrue($field['required']);
|
|
}
|
|
$this->assertSame($expectedKeys, $process->requiredFieldKeys($stepKey));
|
|
}
|
|
}
|
|
|
|
public function testTechCheckItemsExtractsOnlyCheckItemsWithKeys(): void
|
|
{
|
|
$snapshot = ['items' => [
|
|
['type' => 'section', 'label' => 'Group'],
|
|
['type' => 'check', 'key' => 'tls', 'label' => 'TLS'],
|
|
['type' => 'check', 'key' => '', 'label' => 'No key'],
|
|
['type' => 'check', 'label' => 'Missing key'],
|
|
'not-an-array',
|
|
]];
|
|
|
|
$items = SecurityCheckProcess::techCheckItems($snapshot);
|
|
|
|
$this->assertCount(1, $items);
|
|
$this->assertSame('tls', $items[0]['key']);
|
|
}
|
|
|
|
public function testComputeProgressOpenWhenNothingDone(): void
|
|
{
|
|
$progress = (new SecurityCheckProcess())->computeProgress([], [], []);
|
|
|
|
$this->assertSame(5, $progress['manual_total']);
|
|
$this->assertSame(0, $progress['tech_total']);
|
|
$this->assertSame(0, $progress['done']);
|
|
$this->assertSame(0, $progress['percent']);
|
|
$this->assertSame(SecurityCheckProcess::STATUS_OPEN, $progress['status']);
|
|
$this->assertTrue($progress['step6_done']); // no tech items → trivially done
|
|
}
|
|
|
|
public function testComputeProgressInProgressWhenPartiallyDone(): void
|
|
{
|
|
$processState = ['beauftragung' => ['done' => true]];
|
|
|
|
$progress = (new SecurityCheckProcess())->computeProgress($processState, [], []);
|
|
|
|
$this->assertSame(1, $progress['done']);
|
|
$this->assertSame(SecurityCheckProcess::STATUS_IN_PROGRESS, $progress['status']);
|
|
}
|
|
|
|
public function testComputeProgressCompletedWhenAllDone(): void
|
|
{
|
|
$process = new SecurityCheckProcess();
|
|
$snapshot = ['items' => [
|
|
['type' => 'check', 'key' => 'a', 'label' => 'A'],
|
|
['type' => 'check', 'key' => 'b', 'label' => 'B'],
|
|
]];
|
|
|
|
$processState = [];
|
|
foreach ($process->manualStepKeys() as $key) {
|
|
$processState[$key] = ['done' => true];
|
|
}
|
|
$techState = ['a' => ['done' => true], 'b' => ['done' => true]];
|
|
|
|
$progress = $process->computeProgress($processState, $snapshot, $techState);
|
|
|
|
$this->assertSame(7, $progress['total']); // 5 manual + 2 tech
|
|
$this->assertSame(7, $progress['done']);
|
|
$this->assertSame(100, $progress['percent']);
|
|
$this->assertTrue($progress['step6_done']);
|
|
$this->assertSame(SecurityCheckProcess::STATUS_COMPLETED, $progress['status']);
|
|
}
|
|
|
|
public function testComputeProgressStep6NotDoneWhenSomeTechItemsOpen(): void
|
|
{
|
|
$snapshot = ['items' => [
|
|
['type' => 'check', 'key' => 'a', 'label' => 'A'],
|
|
['type' => 'check', 'key' => 'b', 'label' => 'B'],
|
|
]];
|
|
$techState = ['a' => ['done' => true]];
|
|
|
|
$progress = (new SecurityCheckProcess())->computeProgress([], $snapshot, $techState);
|
|
|
|
$this->assertFalse($progress['step6_done']);
|
|
$this->assertSame(SecurityCheckProcess::STATUS_IN_PROGRESS, $progress['status']);
|
|
}
|
|
|
|
public function testComputeProgressArchivedOverridesDerivedStatus(): void
|
|
{
|
|
$progress = (new SecurityCheckProcess())->computeProgress([], [], [], true);
|
|
|
|
$this->assertSame(SecurityCheckProcess::STATUS_ARCHIVED, $progress['status']);
|
|
}
|
|
|
|
public function testReportPrerequisitesMetWhenAllPrecedingStepsComplete(): void
|
|
{
|
|
$process = new SecurityCheckProcess();
|
|
$snapshot = ['items' => [['type' => 'check', 'key' => 'a', 'label' => 'A']]];
|
|
|
|
$processState = [];
|
|
foreach ($process->manualStepKeys() as $key) {
|
|
// The report step itself is intentionally left open — it does not gate itself.
|
|
$processState[$key] = ['done' => $key !== SecurityCheckProcess::STEP_REPORT];
|
|
}
|
|
$techState = ['a' => ['done' => true]];
|
|
|
|
$this->assertTrue($process->reportPrerequisitesMet($processState, $snapshot, $techState));
|
|
}
|
|
|
|
public function testReportPrerequisitesNotMetWhenAManualStepIsOpen(): void
|
|
{
|
|
$process = new SecurityCheckProcess();
|
|
$snapshot = ['items' => [['type' => 'check', 'key' => 'a', 'label' => 'A']]];
|
|
|
|
$processState = [];
|
|
foreach ($process->manualStepKeys() as $key) {
|
|
$processState[$key] = ['done' => true];
|
|
}
|
|
$processState['scheduling']['done'] = false; // one preceding step still open
|
|
$techState = ['a' => ['done' => true]];
|
|
|
|
$this->assertFalse($process->reportPrerequisitesMet($processState, $snapshot, $techState));
|
|
}
|
|
|
|
public function testReportPrerequisitesNotMetWhenTechChecklistIncomplete(): void
|
|
{
|
|
$process = new SecurityCheckProcess();
|
|
$snapshot = ['items' => [
|
|
['type' => 'check', 'key' => 'a', 'label' => 'A'],
|
|
['type' => 'check', 'key' => 'b', 'label' => 'B'],
|
|
]];
|
|
|
|
$processState = [];
|
|
foreach ($process->manualStepKeys() as $key) {
|
|
$processState[$key] = ['done' => true];
|
|
}
|
|
$techState = ['a' => ['done' => true]]; // 'b' still open
|
|
|
|
$this->assertFalse($process->reportPrerequisitesMet($processState, $snapshot, $techState));
|
|
}
|
|
|
|
public function testReportPrerequisitesMetWithEmptyTechChecklist(): void
|
|
{
|
|
$process = new SecurityCheckProcess();
|
|
|
|
$processState = [];
|
|
foreach ($process->manualStepKeys() as $key) {
|
|
$processState[$key] = ['done' => true];
|
|
}
|
|
|
|
// No tech items → checklist trivially complete; only the manual steps gate.
|
|
$this->assertTrue($process->reportPrerequisitesMet($processState, [], []));
|
|
}
|
|
|
|
public function testStatusVariantMapping(): void
|
|
{
|
|
$this->assertSame('neutral', SecurityCheckProcess::statusVariant(SecurityCheckProcess::STATUS_OPEN));
|
|
$this->assertSame('warning', SecurityCheckProcess::statusVariant(SecurityCheckProcess::STATUS_IN_PROGRESS));
|
|
$this->assertSame('success', SecurityCheckProcess::statusVariant(SecurityCheckProcess::STATUS_COMPLETED));
|
|
$this->assertSame('neutral', SecurityCheckProcess::statusVariant(SecurityCheckProcess::STATUS_ARCHIVED));
|
|
}
|
|
}
|