import { NotificationChannel } from "../../NotificationJobs/NotificationJobs.Interface"; // Mock all external dependencies before importing the module under test jest.mock("../../PlanSubscriptions/PlanSubscriptons", () => ({ __esModule: true, default: { checkFeature: jest.fn(), }, })); jest.mock("../CompanyNotificationPolicy.Adapter.Mongoose", () => ({ CompanyNotificationPolicyAdapterMongoose: jest.fn().mockImplementation(() => ({ findOne: jest.fn(), })), })); jest.mock("../ClientNotificationPreferences.Adapter.Mongoose", () => ({ ClientNotificationPreferencesAdapterMongoose: jest.fn().mockImplementation(() => ({ findOne: jest.fn(), })), })); jest.mock("../ClientCompanyNotificationOverride.Adapter.Mongoose", () => ({ ClientCompanyNotificationOverrideAdapterMongoose: jest.fn().mockImplementation(() => ({ findOne: jest.fn(), })), })); jest.mock("../../Companies/Companies", () => ({ __esModule: true, default: { companies: { findOne: jest.fn(), }, }, })); jest.mock("../../Clients/Clients", () => ({ __esModule: true, default: { clients: { findOne: jest.fn(), }, }, })); import { PolicyResolver } from "../PolicyResolver"; import PlanSubscriptionsList from "../../PlanSubscriptions/PlanSubscriptons"; import CompaniesManager from "../../Companies/Companies"; import ClientsList from "../../Clients/Clients"; import { PlanFeatures } from "../../Plans/Plans.interface"; describe("PolicyResolver", () => { let resolver: PolicyResolver; let mockCompanyPolicyFindOne: jest.Mock; let mockClientPreferencesFindOne: jest.Mock; let mockOverrideFindOne: jest.Mock; beforeEach(() => { jest.clearAllMocks(); resolver = new PolicyResolver(); // Access the mocked adapter instances mockCompanyPolicyFindOne = ( resolver as any ).companyPolicyAdapter.findOne as jest.Mock; mockClientPreferencesFindOne = ( resolver as any ).clientPreferencesAdapter.findOne as jest.Mock; mockOverrideFindOne = ( resolver as any ).clientCompanyOverrideAdapter.findOne as jest.Mock; // Mock CompaniesManager to return a company with an ownerId (CompaniesManager.companies.findOne as jest.Mock).mockResolvedValue({ _id: "company-1", ownerId: "owner-user-1", }); (ClientsList.clients.findOne as jest.Mock).mockResolvedValue({ _id: "client-1", userId: "client-user-1", }); }); it("returns system defaults when no policies exist", async () => { mockCompanyPolicyFindOne.mockResolvedValue(null); mockClientPreferencesFindOne.mockResolvedValue(null); mockOverrideFindOne.mockResolvedValue(null); (PlanSubscriptionsList.checkFeature as jest.Mock).mockResolvedValue(true); const result = await resolver.resolve("company-1", "client-1"); expect(result.channels).toEqual( expect.arrayContaining(["whatsapp", "email", "system"]) ); expect(result.channels).toHaveLength(3); }); it("uses company policy defaults when no client override exists", async () => { mockCompanyPolicyFindOne.mockResolvedValue({ companyId: "company-1", defaultChannels: ["email" as NotificationChannel], timezone: "America/Argentina/Buenos_Aires", }); mockClientPreferencesFindOne.mockResolvedValue(null); mockOverrideFindOne.mockResolvedValue(null); (PlanSubscriptionsList.checkFeature as jest.Mock).mockResolvedValue(true); const result = await resolver.resolve("company-1", "client-1"); expect(result.channels).toEqual(["email"]); }); it("client override wins over company default", async () => { mockCompanyPolicyFindOne.mockResolvedValue({ companyId: "company-1", defaultChannels: ["email" as NotificationChannel], timezone: "America/Argentina/Buenos_Aires", }); mockClientPreferencesFindOne.mockResolvedValue(null); mockOverrideFindOne.mockResolvedValue({ clientId: "client-1", companyId: "company-1", preferredChannels: ["whatsapp" as NotificationChannel], mutedChannels: [], }); (PlanSubscriptionsList.checkFeature as jest.Mock).mockResolvedValue(true); const result = await resolver.resolve("company-1", "client-1"); expect(result.channels).toEqual(["whatsapp"]); }); it("client global preferences used when no override exists", async () => { mockCompanyPolicyFindOne.mockResolvedValue({ companyId: "company-1", defaultChannels: ["whatsapp" as NotificationChannel, "email" as NotificationChannel], timezone: "America/Argentina/Buenos_Aires", }); mockClientPreferencesFindOne.mockResolvedValue({ userId: "client-user-1", preferredChannels: ["email" as NotificationChannel], mutedChannels: [], }); mockOverrideFindOne.mockResolvedValue(null); (PlanSubscriptionsList.checkFeature as jest.Mock).mockResolvedValue(true); const result = await resolver.resolve("company-1", "client-1"); expect(result.channels).toEqual(["email"]); }); it("excludes muted channels from override", async () => { mockCompanyPolicyFindOne.mockResolvedValue(null); mockClientPreferencesFindOne.mockResolvedValue(null); mockOverrideFindOne.mockResolvedValue({ clientId: "client-1", companyId: "company-1", preferredChannels: [ "whatsapp" as NotificationChannel, "email" as NotificationChannel, ], mutedChannels: ["whatsapp" as NotificationChannel], }); (PlanSubscriptionsList.checkFeature as jest.Mock).mockResolvedValue(true); const result = await resolver.resolve("company-1", "client-1"); expect(result.channels).toEqual(["email"]); expect(result.channels).not.toContain("whatsapp"); }); it("excludes muted channels from client preferences", async () => { mockCompanyPolicyFindOne.mockResolvedValue(null); mockClientPreferencesFindOne.mockResolvedValue({ userId: "client-user-1", preferredChannels: [ "whatsapp" as NotificationChannel, "email" as NotificationChannel, "system" as NotificationChannel, ], mutedChannels: ["email" as NotificationChannel], }); mockOverrideFindOne.mockResolvedValue(null); (PlanSubscriptionsList.checkFeature as jest.Mock).mockResolvedValue(true); const result = await resolver.resolve("company-1", "client-1"); expect(result.channels).toEqual( expect.arrayContaining(["whatsapp", "system"]) ); expect(result.channels).not.toContain("email"); }); it("loads global preferences by the appointment client's userId", async () => { mockCompanyPolicyFindOne.mockResolvedValue(null); mockClientPreferencesFindOne.mockResolvedValue({ userId: "client-user-1", preferredChannels: ["system" as NotificationChannel], mutedChannels: [], }); mockOverrideFindOne.mockResolvedValue(null); (PlanSubscriptionsList.checkFeature as jest.Mock).mockResolvedValue(true); const result = await resolver.resolve("company-1", "client-1"); expect(ClientsList.clients.findOne).toHaveBeenCalledWith({ _id: "client-1" }); expect(mockClientPreferencesFindOne).toHaveBeenCalledWith({ userId: "client-user-1" }); expect(result.channels).toEqual(["system"]); }); it("plan blocks WhatsApp when feature not available", async () => { mockCompanyPolicyFindOne.mockResolvedValue({ companyId: "company-1", defaultChannels: [ "whatsapp" as NotificationChannel, "email" as NotificationChannel, ], timezone: "America/Argentina/Buenos_Aires", }); mockClientPreferencesFindOne.mockResolvedValue(null); mockOverrideFindOne.mockResolvedValue(null); (PlanSubscriptionsList.checkFeature as jest.Mock).mockImplementation( async (params: { feature: string }) => { if (params.feature === PlanFeatures.WAP_NOTIFICATIONS) return false; return true; } ); const result = await resolver.resolve("company-1", "client-1"); expect(result.channels).not.toContain("whatsapp"); expect(result.channels).toContain("email"); }); it("plan blocks email when feature not available", async () => { mockCompanyPolicyFindOne.mockResolvedValue({ companyId: "company-1", defaultChannels: [ "whatsapp" as NotificationChannel, "email" as NotificationChannel, ], timezone: "America/Argentina/Buenos_Aires", }); mockClientPreferencesFindOne.mockResolvedValue(null); mockOverrideFindOne.mockResolvedValue(null); (PlanSubscriptionsList.checkFeature as jest.Mock).mockImplementation( async (params: { feature: string }) => { if (params.feature === PlanFeatures.MAIL_NOTIFICATIONS) return false; return true; } ); const result = await resolver.resolve("company-1", "client-1"); expect(result.channels).not.toContain("email"); expect(result.channels).toContain("whatsapp"); }); it("returns timezone from company policy", async () => { mockCompanyPolicyFindOne.mockResolvedValue({ companyId: "company-1", defaultChannels: ["system" as NotificationChannel], timezone: "America/New_York", quietHours: { from: "22:00", to: "07:00" }, }); mockClientPreferencesFindOne.mockResolvedValue(null); mockOverrideFindOne.mockResolvedValue(null); (PlanSubscriptionsList.checkFeature as jest.Mock).mockResolvedValue(true); const result = await resolver.resolve("company-1", "client-1"); expect(result.timezone).toBe("America/New_York"); expect(result.quietHours).toEqual({ from: "22:00", to: "07:00" }); }); it("returns default timezone when no company policy", async () => { mockCompanyPolicyFindOne.mockResolvedValue(null); mockClientPreferencesFindOne.mockResolvedValue(null); mockOverrideFindOne.mockResolvedValue(null); (PlanSubscriptionsList.checkFeature as jest.Mock).mockResolvedValue(true); const result = await resolver.resolve("company-1", "client-1"); expect(result.timezone).toBe("America/Argentina/Buenos_Aires"); }); it("returns empty channels when all are muted via override", async () => { mockCompanyPolicyFindOne.mockResolvedValue(null); mockClientPreferencesFindOne.mockResolvedValue(null); mockOverrideFindOne.mockResolvedValue({ clientId: "client-1", companyId: "company-1", preferredChannels: [ "whatsapp" as NotificationChannel, "email" as NotificationChannel, "system" as NotificationChannel, ], mutedChannels: [ "whatsapp" as NotificationChannel, "email" as NotificationChannel, "system" as NotificationChannel, ], }); (PlanSubscriptionsList.checkFeature as jest.Mock).mockResolvedValue(true); const result = await resolver.resolve("company-1", "client-1"); expect(result.channels).toEqual([]); }); it("resolves available channels from company defaults and plan limits without client preferences", async () => { mockCompanyPolicyFindOne.mockResolvedValue({ companyId: "company-1", defaultChannels: ["whatsapp" as NotificationChannel, "email" as NotificationChannel], mutedChannels: ["email" as NotificationChannel], timezone: "America/Argentina/Buenos_Aires", }); (PlanSubscriptionsList.checkFeature as jest.Mock).mockResolvedValue(true); const result = await resolver.resolveAvailableChannels("company-1", [ "whatsapp", "email", "system", ]); expect(result).toEqual(["whatsapp", "email"]); expect(mockClientPreferencesFindOne).not.toHaveBeenCalled(); expect(mockOverrideFindOne).not.toHaveBeenCalled(); }); it("removes available channels disallowed by the owner plan", async () => { mockCompanyPolicyFindOne.mockResolvedValue({ companyId: "company-1", defaultChannels: ["whatsapp" as NotificationChannel, "email" as NotificationChannel], timezone: "America/Argentina/Buenos_Aires", }); (PlanSubscriptionsList.checkFeature as jest.Mock).mockImplementation( async (params: { feature: string }) => params.feature !== PlanFeatures.WAP_NOTIFICATIONS ); const result = await resolver.resolveAvailableChannels("company-1", [ "whatsapp", "email", ]); expect(result).toEqual(["email"]); }); });