import { afterEach, describe, expect, it, vi } from "vitest"; import { createRequire } from "node:module"; const require = createRequire(import.meta.url); const mock = require("mock-require"); const graphClientModuleId = require.resolve("../graphql-client/graphql-client"); const queriesModuleId = require.resolve("../graphql-client/queries"); const emailQueueModuleId = require.resolve("./queues/emailQueue"); const appQueueModuleId = require.resolve("./queues/appQueue"); const fcmQueueModuleId = require.resolve("./queues/fcmQueue"); const dispatcherModuleId = require.resolve("./dispatchJobWatcherNotification"); const dispatchEmailsToQueueMock = vi.fn(); const dispatchAppsToQueueMock = vi.fn(); const dispatchFcmsToQueueMock = vi.fn(); const loadDispatcher = ({ requestMock }) => { mock.stopAll(); dispatchEmailsToQueueMock.mockReset(); dispatchAppsToQueueMock.mockReset(); dispatchFcmsToQueueMock.mockReset(); mock(graphClientModuleId, { client: { request: requestMock } }); mock(queriesModuleId, { GET_JOB_WATCHERS: "GET_JOB_WATCHERS", GET_NOTIFICATION_ASSOCIATIONS: "GET_NOTIFICATION_ASSOCIATIONS" }); mock(emailQueueModuleId, { dispatchEmailsToQueue: dispatchEmailsToQueueMock }); mock(appQueueModuleId, { dispatchAppsToQueue: dispatchAppsToQueueMock }); mock(fcmQueueModuleId, { dispatchFcmsToQueue: dispatchFcmsToQueueMock }); delete require.cache[dispatcherModuleId]; return require(dispatcherModuleId); }; afterEach(() => { mock.stopAll(); delete require.cache[dispatcherModuleId]; }); describe("dispatchJobWatcherNotification", () => { it("dispatches queue payloads using watcher settings plus fallback defaults for extra recipients", async () => { const requestMock = vi.fn(async (query) => { if (query === "GET_JOB_WATCHERS") { return { job_watchers: [ { user_email: "watcher@example.com", user: { employee: { id: "emp-1", first_name: "Pat", last_name: "Lee" } } } ], job: { ro_number: "RO-123", bodyshop: { id: "shop-1", shopname: "ImEX", timezone: "America/Toronto" } } }; } if (query === "GET_NOTIFICATION_ASSOCIATIONS") { return { associations: [ { id: "assoc-1", useremail: "watcher@example.com", notification_settings: { "esign-document-opened": { app: true, email: false, fcm: true } } }, { id: "assoc-2", useremail: "creator@example.com", notification_settings: {} } ] }; } return {}; }); const { dispatchJobWatcherNotification } = loadDispatcher({ requestMock }); const logger = { log: vi.fn() }; const result = await dispatchJobWatcherNotification({ jobId: "job-1", scenarioKey: "esign-document-opened", key: "notifications.job.esignDocumentOpened", body: '"Repair Authorization" has been opened.', variables: { documentId: "123" }, extraRecipientEmails: ["creator@example.com"], defaultChannelPreferences: { app: true, email: false, fcm: false }, logger }); expect(result).toBe(true); expect(requestMock).toHaveBeenCalledTimes(2); expect(dispatchEmailsToQueueMock).not.toHaveBeenCalled(); expect(dispatchAppsToQueueMock).toHaveBeenCalledTimes(1); expect(dispatchAppsToQueueMock.mock.calls[0][0]).toEqual({ appsToDispatch: [ expect.objectContaining({ jobId: "job-1", jobRoNumber: "RO-123", bodyShopId: "shop-1", scenarioKey: "esign-document-opened", key: "notifications.job.esignDocumentOpened", recipients: [ { user: "watcher@example.com", bodyShopId: "shop-1", employeeId: "emp-1", associationId: "assoc-1" }, { user: "creator@example.com", bodyShopId: "shop-1", employeeId: null, associationId: "assoc-2" } ] }) ], logger }); expect(dispatchFcmsToQueueMock).toHaveBeenCalledTimes(1); expect(dispatchFcmsToQueueMock.mock.calls[0][0]).toEqual({ fcmsToDispatch: [ expect.objectContaining({ jobId: "job-1", scenarioKey: "esign-document-opened", recipients: [ { user: "watcher@example.com", bodyShopId: "shop-1", employeeId: "emp-1", associationId: "assoc-1" } ] }) ], logger }); }); it("returns false when no recipients have any enabled channels", async () => { const requestMock = vi.fn(async (query) => { if (query === "GET_JOB_WATCHERS") { return { job_watchers: [ { user_email: "watcher@example.com", user: { employee: { id: "emp-1", first_name: "Pat", last_name: "Lee" } } } ], job: { ro_number: "RO-123", bodyshop: { id: "shop-1", shopname: "ImEX", timezone: "America/Toronto" } } }; } if (query === "GET_NOTIFICATION_ASSOCIATIONS") { return { associations: [ { id: "assoc-1", useremail: "watcher@example.com", notification_settings: { "esign-document-opened": { app: false, email: false, fcm: false } } } ] }; } return {}; }); const { dispatchJobWatcherNotification } = loadDispatcher({ requestMock }); const result = await dispatchJobWatcherNotification({ jobId: "job-1", scenarioKey: "esign-document-opened", key: "notifications.job.esignDocumentOpened", body: '"Repair Authorization" has been opened.', logger: { log: vi.fn() } }); expect(result).toBe(false); expect(dispatchEmailsToQueueMock).not.toHaveBeenCalled(); expect(dispatchAppsToQueueMock).not.toHaveBeenCalled(); expect(dispatchFcmsToQueueMock).not.toHaveBeenCalled(); }); });