92 lines
3.5 KiB
TypeScript
92 lines
3.5 KiB
TypeScript
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
|
|
const mocks = vi.hoisted(() => ({
|
|
db: {
|
|
availabilityRule: { findMany: vi.fn() },
|
|
availabilityException: { findMany: vi.fn() },
|
|
appointment: { findMany: vi.fn() },
|
|
calendarEvent: { findMany: vi.fn() },
|
|
},
|
|
computeSlots: vi.fn(),
|
|
}));
|
|
|
|
vi.mock("~/utils/db.server", () => ({ db: mocks.db }));
|
|
vi.mock("./slotEngine", () => ({ computeSlots: mocks.computeSlots }));
|
|
|
|
import { loadSlotsForDate } from "./SlotQueryService.server";
|
|
|
|
describe("SlotQueryService.server", () => {
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
mocks.db.availabilityRule.findMany.mockResolvedValue([]);
|
|
mocks.db.availabilityException.findMany.mockResolvedValue([]);
|
|
mocks.db.appointment.findMany.mockResolvedValue([]);
|
|
mocks.db.calendarEvent.findMany.mockResolvedValue([]);
|
|
mocks.computeSlots.mockReturnValue([]);
|
|
});
|
|
|
|
it("loads any number of providers with four bulk queries", async () => {
|
|
const startAt = new Date("2026-07-20T09:00:00.000Z");
|
|
const endAt = new Date("2026-07-20T10:00:00.000Z");
|
|
mocks.db.availabilityRule.findMany.mockResolvedValue([
|
|
{ providerUserId: "provider_2", dayOfWeek: "monday", startTime: "09:00", endTime: "17:00", timezone: "Europe/Berlin" },
|
|
]);
|
|
mocks.db.appointment.findMany.mockResolvedValue([{ providerUserId: "provider_1", startAt, endAt }]);
|
|
mocks.db.calendarEvent.findMany.mockResolvedValue([
|
|
{ userId: "provider_2", startsAt: startAt, endsAt: endAt, isBusyForAvailability: true, archivedAt: null },
|
|
]);
|
|
|
|
await loadSlotsForDate({
|
|
tenantId: "tenant_1",
|
|
providerUserIds: ["provider_1", "provider_2"],
|
|
date: "2026-07-20",
|
|
now: new Date("2026-07-10T00:00:00.000Z"),
|
|
config: {
|
|
durationMinutes: 30,
|
|
bufferBeforeMinutes: 0,
|
|
bufferAfterMinutes: 0,
|
|
slotIncrementMinutes: 15,
|
|
minNoticeHours: 4,
|
|
maxAdvanceDays: 60,
|
|
},
|
|
});
|
|
|
|
expect(mocks.db.availabilityRule.findMany).toHaveBeenCalledTimes(1);
|
|
expect(mocks.db.availabilityException.findMany).toHaveBeenCalledTimes(1);
|
|
expect(mocks.db.appointment.findMany).toHaveBeenCalledTimes(1);
|
|
expect(mocks.db.calendarEvent.findMany).toHaveBeenCalledTimes(1);
|
|
expect(mocks.db.availabilityRule.findMany).toHaveBeenCalledWith(
|
|
expect.objectContaining({ where: { tenantId: "tenant_1", providerUserId: { in: ["provider_1", "provider_2"] } } }),
|
|
);
|
|
expect(mocks.computeSlots).toHaveBeenCalledWith(
|
|
expect.objectContaining({
|
|
providers: [
|
|
expect.objectContaining({ providerUserId: "provider_1", busyIntervals: [{ from: startAt, to: endAt }] }),
|
|
expect.objectContaining({ providerUserId: "provider_2", rules: [expect.objectContaining({ dayOfWeek: "monday" })], busyIntervals: [{ from: startAt, to: endAt }] }),
|
|
],
|
|
}),
|
|
);
|
|
});
|
|
|
|
it("short-circuits without querying when no provider is configured", async () => {
|
|
await expect(
|
|
loadSlotsForDate({
|
|
tenantId: "tenant_1",
|
|
providerUserIds: [],
|
|
date: "2026-07-20",
|
|
now: new Date("2026-07-10T00:00:00.000Z"),
|
|
config: {
|
|
durationMinutes: 30,
|
|
bufferBeforeMinutes: 0,
|
|
bufferAfterMinutes: 0,
|
|
slotIncrementMinutes: 15,
|
|
minNoticeHours: 4,
|
|
maxAdvanceDays: 60,
|
|
},
|
|
}),
|
|
).resolves.toEqual([]);
|
|
expect(mocks.db.availabilityRule.findMany).not.toHaveBeenCalled();
|
|
expect(mocks.computeSlots).not.toHaveBeenCalled();
|
|
});
|
|
});
|