221 lines
6.5 KiB
TypeScript
221 lines
6.5 KiB
TypeScript
/**
|
|
* Tests for Metrics.calculateMetrics — excludes removed employees from count.
|
|
*/
|
|
|
|
jest.mock("../../Companies/Companies", () => ({
|
|
__esModule: true,
|
|
default: {
|
|
companies: {
|
|
find: jest.fn(),
|
|
findOne: jest.fn(),
|
|
},
|
|
},
|
|
}));
|
|
|
|
jest.mock("../../Employees/Employee", () => ({
|
|
__esModule: true,
|
|
default: {
|
|
employees: {
|
|
find: jest.fn(),
|
|
},
|
|
},
|
|
}));
|
|
|
|
jest.mock("../../Services/Service", () => ({
|
|
__esModule: true,
|
|
default: {
|
|
services: {
|
|
find: jest.fn(),
|
|
},
|
|
},
|
|
}));
|
|
|
|
jest.mock("../../Appointments/Appointments", () => ({
|
|
__esModule: true,
|
|
default: {
|
|
countAppointmentsByMonth: jest.fn(),
|
|
},
|
|
}));
|
|
|
|
jest.mock("../../Clients/Clients", () => ({
|
|
__esModule: true,
|
|
default: {
|
|
clients: {
|
|
find: jest.fn(),
|
|
},
|
|
},
|
|
}));
|
|
|
|
jest.mock("../../Repeats/Repeats", () => ({
|
|
__esModule: true,
|
|
default: {
|
|
repeats: {
|
|
find: jest.fn(),
|
|
},
|
|
},
|
|
}));
|
|
|
|
jest.mock("../../PlanSubscriptions/PlanSubscriptons", () => ({
|
|
__esModule: true,
|
|
default: {
|
|
findOne: jest.fn(),
|
|
},
|
|
}));
|
|
|
|
jest.mock("../../Plans/Plans", () => ({
|
|
__esModule: true,
|
|
default: {
|
|
plans: {
|
|
findOne: jest.fn(),
|
|
},
|
|
},
|
|
}));
|
|
|
|
jest.mock("../../PlanUsageCycle/PlanUsageCycle", () => ({
|
|
__esModule: true,
|
|
default: {
|
|
getAppointmentsCount: jest.fn(),
|
|
},
|
|
}));
|
|
|
|
import MetricsList from "../Metrics";
|
|
import CompaniesList from "../../Companies/Companies";
|
|
import EmployeesList from "../../Employees/Employee";
|
|
import ServiceList from "../../Services/Service";
|
|
import AppointmentList from "../../Appointments/Appointments";
|
|
import ClientsList from "../../Clients/Clients";
|
|
import RepeatsList from "../../Repeats/Repeats";
|
|
|
|
describe("calculateMetrics — excludes removed employees", () => {
|
|
let originalAdapter: any;
|
|
|
|
beforeEach(() => {
|
|
jest.clearAllMocks();
|
|
|
|
originalAdapter = MetricsList.metrics;
|
|
|
|
const mockSave = jest.fn().mockResolvedValue(true);
|
|
const mockMetricsDoc = {
|
|
userId: "owner-user",
|
|
organizationsCount: 0,
|
|
employeesCount: 0,
|
|
servicesCount: 0,
|
|
appointmentsCount: 0,
|
|
clientsCount: 0,
|
|
repeatsCount: 0,
|
|
month: 6,
|
|
year: 2026,
|
|
save: mockSave,
|
|
};
|
|
|
|
(MetricsList as any).metrics = {
|
|
getMetrics: jest.fn().mockResolvedValue(mockMetricsDoc),
|
|
reset: jest.fn().mockResolvedValue(mockMetricsDoc),
|
|
};
|
|
});
|
|
|
|
afterEach(() => {
|
|
(MetricsList as any).metrics = originalAdapter;
|
|
});
|
|
|
|
it("counts only non-removed employees across companies", async () => {
|
|
// Two companies owned by the same user
|
|
(CompaniesList.companies.find as jest.Mock).mockResolvedValue([
|
|
{ _id: "company-001", id: "company-001" },
|
|
{ _id: "company-002", id: "company-002" },
|
|
]);
|
|
|
|
// Company 001: 2 active + 1 removed → should count 2
|
|
// Company 002: 1 active → should count 1
|
|
// Total: 3 employees (not 4)
|
|
(EmployeesList.employees.find as jest.Mock)
|
|
.mockResolvedValueOnce([
|
|
{ _id: "emp-1", removed: false },
|
|
{ _id: "emp-2", removed: false },
|
|
{ _id: "emp-3", removed: true }, // filtered by query but just in case
|
|
])
|
|
.mockResolvedValueOnce([
|
|
{ _id: "emp-4", removed: false },
|
|
]);
|
|
|
|
// Services: 2 in company-001, 1 in company-002
|
|
(ServiceList.services.find as jest.Mock)
|
|
.mockResolvedValueOnce([{ _id: "svc-1" }, { _id: "svc-2" }])
|
|
.mockResolvedValueOnce([{ _id: "svc-3" }]);
|
|
|
|
// Appointments count per company
|
|
(AppointmentList.countAppointmentsByMonth as jest.Mock)
|
|
.mockResolvedValueOnce(5)
|
|
.mockResolvedValueOnce(3);
|
|
|
|
// Clients per company
|
|
(ClientsList.clients.find as jest.Mock)
|
|
.mockResolvedValueOnce([{ _id: "c-1" }])
|
|
.mockResolvedValueOnce([{ _id: "c-2" }, { _id: "c-3" }]);
|
|
|
|
// Repeats per company
|
|
(RepeatsList.repeats.find as jest.Mock)
|
|
.mockResolvedValueOnce([])
|
|
.mockResolvedValueOnce([]);
|
|
|
|
await MetricsList.calculateMetrics({ userId: "owner-user" });
|
|
|
|
// Verify employee find was called with removed filter for BOTH companies
|
|
expect(EmployeesList.employees.find).toHaveBeenCalledWith(
|
|
expect.objectContaining({
|
|
companyId: "company-001",
|
|
removed: { $ne: true },
|
|
})
|
|
);
|
|
expect(EmployeesList.employees.find).toHaveBeenCalledWith(
|
|
expect.objectContaining({
|
|
companyId: "company-002",
|
|
removed: { $ne: true },
|
|
})
|
|
);
|
|
|
|
// Verify the metrics document was saved
|
|
const metricsDoc = await (MetricsList as any).metrics.getMetrics();
|
|
expect(metricsDoc.save).toHaveBeenCalled();
|
|
});
|
|
|
|
it("produces zero employee count when all employees are removed", async () => {
|
|
(CompaniesList.companies.find as jest.Mock).mockResolvedValue([
|
|
{ _id: "company-001", id: "company-001" },
|
|
]);
|
|
|
|
// All employees in this company are removed
|
|
(EmployeesList.employees.find as jest.Mock).mockResolvedValue([]);
|
|
|
|
(ServiceList.services.find as jest.Mock).mockResolvedValue([]);
|
|
(AppointmentList.countAppointmentsByMonth as jest.Mock).mockResolvedValue(0);
|
|
(ClientsList.clients.find as jest.Mock).mockResolvedValue([]);
|
|
(RepeatsList.repeats.find as jest.Mock).mockResolvedValue([]);
|
|
|
|
const mockSave = jest.fn().mockResolvedValue(true);
|
|
const mockMetricsDoc = {
|
|
userId: "owner-user",
|
|
organizationsCount: 0,
|
|
employeesCount: 5, // pre-existing count
|
|
servicesCount: 0,
|
|
appointmentsCount: 0,
|
|
clientsCount: 0,
|
|
repeatsCount: 0,
|
|
month: 6,
|
|
year: 2026,
|
|
save: mockSave,
|
|
};
|
|
|
|
(MetricsList as any).metrics = {
|
|
getMetrics: jest.fn().mockResolvedValue(mockMetricsDoc),
|
|
reset: jest.fn().mockResolvedValue(mockMetricsDoc),
|
|
};
|
|
|
|
await MetricsList.calculateMetrics({ userId: "owner-user" });
|
|
|
|
// employeesCount should be set to 0 (no non-removed employees)
|
|
expect(mockMetricsDoc.employeesCount).toBe(0);
|
|
expect(mockSave).toHaveBeenCalled();
|
|
});
|
|
});
|