Files
JobcardSystem/tests/DomainCommandServiceTest.php
T

52 lines
3.4 KiB
PHP

<?php
declare(strict_types=1);
require_once __DIR__ . '/../app/Domain/Client/ContactEditCommand.php';
require_once __DIR__ . '/../app/Domain/Reporting/ClientHistoryService.php';
require_once __DIR__ . '/../app/Domain/Jobcard/TimeEntryCorrectionCommand.php';
use App\Domain\Client\ContactEditCommand;
use App\Domain\Reporting\ClientHistoryService;
use App\Domain\Jobcard\TimeEntryCorrectionCommand;
function domain_command_assert_same(mixed $expected, mixed $actual, string $message): void
{
if ($expected !== $actual) throw new RuntimeException($message . "\nExpected: " . var_export($expected, true) . "\nActual: " . var_export($actual, true));
}
$contacts = [
['id' => 1, 'client_id' => 7, 'name' => 'Jane', 'email' => 'jane@example.test', 'is_primary' => true],
['id' => 2, 'client_id' => 7, 'name' => 'John', 'email' => 'john@example.test', 'is_primary' => false],
];
$contactCommand = new ContactEditCommand();
$edit = $contactCommand->validateEdit(2, ['client_id' => 7, 'name' => ' John Smith ', 'is_primary' => 'yes'], $contacts);
domain_command_assert_same(true, $edit['valid'], 'A contact edit should validate.');
domain_command_assert_same([1], $edit['replace_primary_contact_ids'], 'Promoting an edited contact should demote the old primary.');
$delete = $contactCommand->validateDelete(1, $contacts);
domain_command_assert_same(true, $delete['valid'], 'A primary contact may be deleted when a replacement exists.');
domain_command_assert_same(2, $delete['replacement_primary_contact_id'], 'Deleting the primary should nominate a replacement.');
$last = $contactCommand->validateDelete(2, [['id' => 2, 'client_id' => 7, 'is_primary' => true]]);
domain_command_assert_same(false, $last['valid'], 'Deleting the only contact must be rejected.');
$history = new ClientHistoryService();
$rows = [
['id' => 2, 'client_id' => 7, 'changed_at' => '2026-09-03', 'to_status' => 'closed'],
['id' => 1, 'client_id' => 8, 'changed_at' => '2026-09-02', 'to_status' => 'open'],
['id' => 3, 'client_id' => 7, 'changed_at' => '2026-10-01', 'to_status' => 'open'],
];
domain_command_assert_same([2], array_column($history->filter($rows, ['client_id' => 7, 'date_to' => '2026-09-30']), 'id'), 'History filtering should apply client and date bounds.');
$correction = new TimeEntryCorrectionCommand();
$existing = ['id' => 9, 'jobcard_id' => 12, 'technician_id' => 4, 'work_date' => '2026-09-01', 'hours' => 2, 'notes' => 'old', 'counts_toward_sla' => true, 'voided' => false];
$fixed = $correction->validateCorrection($existing, ['hours' => '3.25', 'notes' => ' corrected ']);
domain_command_assert_same(true, $fixed['valid'], 'A time correction should validate against the existing entry.');
domain_command_assert_same(3.25, $fixed['entry']['hours'], 'A time correction should normalize replacement hours.');
domain_command_assert_same(12, $fixed['entry']['jobcard_id'], 'A correction must retain the existing jobcard.');
$void = $correction->validateVoid($existing, ['reason' => 'Duplicate entry']);
domain_command_assert_same(true, $void['valid'], 'Voiding should require and retain a reason.');
domain_command_assert_same('void', $void['action'], 'Voiding should be an explicit logical action.');
$voided = $correction->validateVoid([...$existing, 'voided' => true], ['reason' => 'again']);
domain_command_assert_same(false, $voided['valid'], 'An already voided entry cannot be voided twice.');
printf("Domain command/service tests: 10 passed\n");