const { calculateDueAt, remindOverdueWorkflowTask, } = require("../../../src/jobs/workflowOverdueReminderJob"); const { NOTIFICATION_TYPES } = require("../../../src/constants/notificationTypes"); describe("workflow overdue reminder job", () => { it("calculates fallback due time from task creation", () => { const dueAt = calculateDueAt({ created_at: new Date("2026-05-11T10:00:00.000Z"), due_date: null, }); expect(dueAt.toISOString()).toBe("2026-05-12T10:00:00.000Z"); }); it("creates history, audit and notification once cooldown allows", async () => { const task = { id: "11111111-1111-4111-8111-111111111111", instance_id: "22222222-2222-4222-8222-222222222222", step_id: "manager", step_name: "经理审批", assignee_id: "33333333-3333-4333-8333-333333333333", assignee_role: null, created_at: new Date("2026-05-11T07:00:00.000Z"), due_date: null, workflow_instance: { id: "22222222-2222-4222-8222-222222222222", entity_type: "reimbursement", entity_id: "44444444-4444-4444-8444-444444444444", reimbursement_form: { form_number: "BX-20260512-001", total_amount: "120.00", owner: { name: "测试员工" }, }, }, }; const createdHistory = []; const createdAudit = []; const createdNotifications = []; const tx = { workflowHistory: { findFirst: vi.fn().mockResolvedValue(null), create: vi.fn().mockImplementation(async ({ data }) => { createdHistory.push(data); return { id: "history-1", ...data }; }), }, auditLog: { create: vi.fn().mockImplementation(async ({ data }) => { createdAudit.push(data); return { id: "audit-1", ...data }; }), }, notification: { create: vi.fn().mockImplementation(async ({ data }) => { const row = { id: `notification-${createdNotifications.length + 1}`, ...data }; createdNotifications.push(row); return row; }), }, }; const result = await remindOverdueWorkflowTask(tx, task, new Date("2026-05-12T08:00:00.000Z")); expect(result.skipped).toBe(false); expect(createdHistory[0]).toEqual(expect.objectContaining({ action: "overdue_remind" })); expect(createdAudit[0]).toEqual(expect.objectContaining({ action: "WORKFLOW_TASK_OVERDUE_NOTIFIED" })); expect(createdNotifications[0]).toEqual( expect.objectContaining({ recipient_employee_id: "33333333-3333-4333-8333-333333333333", type: NOTIFICATION_TYPES.workflowTaskOverdue, target_type: "workflow_task", target_id: task.id, }), ); expect(result.dispatchTask).toEqual( expect.objectContaining({ type: NOTIFICATION_TYPES.workflowTaskOverdue, targetPath: `/mobile/approvals?taskId=${task.id}`, }), ); }); });