allowedInitialStatuses() !== ['new']) { throw new RuntimeException('Expected new to be the only allowed initial status.'); } if ($workflow->allowedPriorities() !== ['low', 'normal', 'high', 'critical']) { throw new RuntimeException('Expected the four supported priorities.'); } $valid = $workflow->validateCommand([ 'client_id' => '42', 'work_requested' => ' Replace the failed pump ', 'priority' => 'high', ]); if (!$valid['valid'] || $valid['errors'] !== [] || $valid['client_id'] !== 42 || $valid['work_requested'] !== 'Replace the failed pump') { throw new RuntimeException('Expected a valid jobcard command to be normalized.'); } $invalid = $workflow->validateCommand(['client_id' => 0, 'work_requested' => '', 'priority' => 'urgent']); if ($invalid['valid'] || !isset($invalid['errors']['client_id'], $invalid['errors']['work_requested'], $invalid['errors']['priority'])) { throw new RuntimeException('Expected invalid jobcard command fields to be reported.'); } $transition = $workflow->validateTransition('in_progress', 'completed', '2026-09-01 12:00:00'); if (!$transition['valid'] || $transition['errors'] !== []) { throw new RuntimeException('Expected completion transition with a timestamp to be valid.'); } $closed = $workflow->validateTransition('completed', 'closed', '2026-09-01 12:00:00', '2026-09-01 13:00:00'); if (!$closed['valid'] || $closed['errors'] !== []) { throw new RuntimeException('Expected closure transition with ordered timestamps to be valid.'); } $invalidTransition = $workflow->validateTransition('new', 'completed'); if ($invalidTransition['valid'] || !isset($invalidTransition['errors']['transition'], $invalidTransition['errors']['completed_at'])) { throw new RuntimeException('Expected invalid transition and missing completion timestamp errors.'); } $invalidDates = $workflow->validateTransition('completed', 'closed', '2026-09-01 14:00:00', '2026-09-01 13:00:00'); if ($invalidDates['valid'] || !isset($invalidDates['errors']['timestamps'])) { throw new RuntimeException('Expected closure before completion to be rejected.'); } printf("Jobcard workflow tests: 7 passed\n");