normalize(['type' => 'assignment_created', 'recipient' => 'A@EXAMPLE.COM', 'read' => 'unread', 'dedup_key' => ' Assignment:7 '])['is_read'], 'Unread aliases should normalize to false.'); notification_assert_same(true, $records->normalize(['type' => 'assignment_created', 'recipient' => 'a@example.com', 'read' => 'read', 'dedup_key' => 'assignment:7'])['is_read'], 'Read aliases should normalize to true.'); notification_assert_same(true, $records->validateMarkRead(['notification_id' => '12'])['valid'], 'A positive notification ID should be accepted for marking read.'); notification_assert_same(12, $records->validateMarkRead(['id' => '12'])['notification_id'], 'Mark-read IDs should normalize to integers.'); notification_assert_same(false, $records->validateMarkRead(['notification_id' => '0'])['valid'], 'Zero is not a valid notification ID.'); $assignment = $records->assignmentCreated([ 'jobcard_id' => '42', 'jobcard_reference' => 'JC-2026-000042', 'recipients' => ['Tech@Example.com'], 'technician_name' => ' Sam ', ]); notification_assert_same('assignment_created', $assignment['type'], 'Assignment factory should set its event type.'); notification_assert_same('assignment:42', $assignment['deduplication_key'], 'Assignment factory should use a stable deduplication key.'); notification_assert_same(['tech@example.com'], $assignment['recipients'], 'Factories should normalize recipients.'); $status = $records->statusChanged(['jobcard_id' => 42, 'from_status' => 'new', 'to_status' => 'assigned', 'recipients' => ['ops@example.com']]); notification_assert_same('jobcard_status_changed', $status['type'], 'Status factory should set its event type.'); notification_assert_same('jobcard:42:status:assigned', $status['deduplication_key'], 'Status factory should key by jobcard and destination status.'); $sla = $records->slaThreshold(['client_id' => 9, 'threshold' => 'critical', 'used_hours' => 9, 'allocated_hours' => 10, 'recipients' => ['ops@example.com']]); notification_assert_same('sla_threshold', $sla['type'], 'SLA factory should set its event type.'); notification_assert_same('sla:9:critical', $sla['deduplication_key'], 'SLA factory should key by client and threshold.'); $mapped = (new NotificationQueue())->mapForUser(['type' => 'assignment_created', 'recipients' => ['a@example.com', 'b@example.com'], 'title' => 'Assigned', 'deduplication_key' => 'assignment:42'], 'b@example.com'); notification_assert_same(['type' => 'assignment_created', 'recipient' => 'b@example.com', 'title' => 'Assigned', 'body' => null, 'is_read' => false, 'deduplication_key' => 'assignment:42'], $mapped, 'Queue mapping should produce one per-user DTO.'); notification_assert_same(2, count($records->toQueueDtos(['type' => 'assignment_created', 'recipients' => ['a@example.com', 'b@example.com'], 'title' => 'Assigned', 'deduplication_key' => 'assignment:42'])), 'Queue DTO mapping should produce one DTO per recipient.'); printf("Notification domain tests: 11 passed\n");