Files
JobcardSystem/tests/DomainWorkflowRegressionTest.php

52 lines
3.7 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 regression_assert(bool $condition, string $message): void
{
if (!$condition) throw new RuntimeException($message);
}
$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],
];
$contact = new ContactEditCommand();
$badEdit = $contact->validateEdit('not-an-id', ['client_id' => 7, 'name' => 'John'], $contacts);
regression_assert($badEdit['valid'] === false && isset($badEdit['errors']['id']), 'Malformed contact IDs must return validation errors.');
$move = $contact->validateEdit(2, ['client_id' => 8, 'name' => 'John'], $contacts);
regression_assert($move['valid'] === false && isset($move['errors']['client_id']), 'A contact client ID must be immutable during edit.');
$sole = $contact->validateDelete('2', [['id' => 2, 'client_id' => 7, 'is_primary' => true]]);
regression_assert($sole['valid'] === false && isset($sole['errors']['delete']), 'The sole contact must not be deletable.');
$badPrimary = $contact->setPrimary('0', $contacts);
regression_assert($badPrimary['valid'] === false && isset($badPrimary['errors']['id']), 'Malformed primary IDs must return validation errors.');
$primary = $contact->setPrimary(2, $contacts);
regression_assert($primary['valid'] === true && $primary['replace_primary_contact_ids'] === [1] && $primary['audit']['event'] === 'client_contact_primary_set', 'Set-primary must nominate demotions and expose an audit payload.');
$history = new ClientHistoryService();
$badHistory = $history->validateForClient('7x', [['id' => 1, 'client_id' => 7, 'changed_at' => '2026-09-01']]);
regression_assert($badHistory['valid'] === false && isset($badHistory['errors']['client_id']), 'Malformed history client IDs must return validation errors.');
$timeline = $history->timeline([['id' => 2, 'client_id' => 7, 'changed_at' => '2026-09-02'], ['id' => 1, 'client_id' => 7, 'changed_at' => '2026-09-01']], 7);
regression_assert($timeline['valid'] === true && array_column($timeline['timeline'], 'id') === [1, 2], 'Client history timeline must be deterministic and controller-ready.');
$entry = ['id' => 9, 'jobcard_id' => 12, 'technician_id' => 4, 'work_date' => '2026-09-01', 'hours' => 2, 'notes' => 'old', 'counts_toward_sla' => true, 'voided' => false];
$correction = new TimeEntryCorrectionCommand();
$immutable = $correction->validateCorrection($entry, ['technician_id' => 99]);
regression_assert($immutable['valid'] === false && isset($immutable['errors']['technician_id']), 'Technician ID must be immutable during correction.');
$fixed = $correction->validateCorrection($entry, ['hours' => '3.25', 'notes' => ' corrected ']);
regression_assert($fixed['valid'] === true && $fixed['audit']['changed_fields'] === ['hours', 'notes'], 'Correction must expose changed fields for audit.');
$missingReason = $correction->validateVoid($entry, []);
regression_assert($missingReason['valid'] === false && isset($missingReason['errors']['reason']), 'Void reason is mandatory.');
$void = $correction->validateVoid($entry, ['reason' => 'Duplicate entry']);
regression_assert($void['valid'] === true && $void['audit']['void_reason'] === 'Duplicate entry', 'Void must expose the normalized reason in its audit payload.');
printf("Domain workflow regression tests: 10 passed\n");