Files
turnosxpress/server/src/Models/NotificationPolicies/__tests__/PolicyResolver.test.ts
T

352 lines
13 KiB
TypeScript

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"]);
});
});