feat: implement collaborator reactivation logic, add access denied handling, and improve metric filtering
This commit is contained in:
@@ -0,0 +1,220 @@
|
||||
/**
|
||||
* 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();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user