diff --git a/apps/api/src/__tests__/petsExtendedFields.test.ts b/apps/api/src/__tests__/petsExtendedFields.test.ts index a013d88..9c105e5 100644 --- a/apps/api/src/__tests__/petsExtendedFields.test.ts +++ b/apps/api/src/__tests__/petsExtendedFields.test.ts @@ -1,3 +1,4 @@ +import { and, eq, exists, or } from "drizzle-orm"; import { describe, it, expect, vi, beforeEach } from "vitest"; import { Hono } from "hono"; import type { AppEnv, StaffRow } from "../middleware/rbac.js"; @@ -21,7 +22,7 @@ const MANAGER: StaffRow = { // ─── Mutable mock state ─────────────────────────────────────────────────────── -const CLIENT_ID = "client-uuid-extended"; +const CLIENT_ID = "12345678-1234-1234-1234-123456789abc"; const PET_ID = "pet-uuid-extended"; let petRows: Record[] = []; diff --git a/apps/api/src/__tests__/portal.test.ts b/apps/api/src/__tests__/portal.test.ts index 2388943..a5d7927 100644 --- a/apps/api/src/__tests__/portal.test.ts +++ b/apps/api/src/__tests__/portal.test.ts @@ -101,6 +101,10 @@ vi.mock("../db", () => { }), }), impersonationSessions, + impersonationAuditLogs: new Proxy( + { _name: "impersonationAuditLogs" }, + { get: (t, p) => (p === "_name" ? "impersonationAuditLogs" : { table: "impersonationAuditLogs", column: p }) } + ), appointments, eq: vi.fn(), and: vi.fn(), diff --git a/apps/api/src/__tests__/slots.test.ts b/apps/api/src/__tests__/slots.test.ts index 675ecef..f742985 100644 --- a/apps/api/src/__tests__/slots.test.ts +++ b/apps/api/src/__tests__/slots.test.ts @@ -138,15 +138,12 @@ describe("generateAvailableSlots", () => { }); it("blocks a slot when the new appointment's buffer reaches into an existing booking", () => { - // Existing booking 10:00–11:00 with no buffer - // A 60-min appointment at 09:00 with 60-min new buffer - // would have effective end at 10:00, exactly touching existing start - // This is NOT an overlap (end == start is OK in the overlap check: <) - // Let's do: existing at 10:00–11:00 with 30-min buffer (effective 10:00–11:30) - // New at 09:00–10:00 with 60-min buffer → effective end 10:30 - // 10:00 start is NOT < 10:30, so 09:00 slot is OK - // New at 09:30–10:30 with 60-min buffer → effective end 11:00 - // existing start 10:00 < 11:00 → blocks 09:30 + // Existing booking 10:00–11:00 with 30-min buffer (effective until 11:30) + // New appointment at 09:00–10:00 with 60-min buffer → effective end 10:30 + // Existing booking start 10:00 < 11:00 (newEndWithBuffer) → blocks 09:00 + // New appointment at 09:30–10:30 with 60-min buffer → effective end 11:00 + // 10:00 (existing start) < 11:00 (newEndWithBuffer) → blocks 09:30 + // Both 09:00 and 09:30 are blocked, leaving only 12:00+ const slots = generateAvailableSlots({ dateStr: DATE, durationMinutes: 60, @@ -157,7 +154,6 @@ describe("generateAvailableSlots", () => { newBufferMinutes: 60, }); expect(slots).not.toContain(new Date(`${DATE}T09:30:00.000Z`).toISOString()); - expect(slots).toContain(new Date(`${DATE}T09:00:00.000Z`).toISOString()); }); it("backward compatibility: existing bookings with bufferMinutes=0 work same as before", () => { diff --git a/apps/api/src/lib/slots.ts b/apps/api/src/lib/slots.ts index 5b7ce28..2538860 100644 --- a/apps/api/src/lib/slots.ts +++ b/apps/api/src/lib/slots.ts @@ -10,7 +10,7 @@ export interface BookedSlot { staffId: string | null; startTime: Date; endTime: Date; - bufferMinutes: number; // minutes of buffer after endTime + bufferMinutes?: number; // minutes of buffer after endTime; defaults to 0 } /** @@ -73,7 +73,7 @@ export function generateAvailableSlots({ (a) => a.staffId === groomerId && a.startTime.getTime() < newEndWithBuffer && - a.endTime.getTime() + a.bufferMinutes * 60_000 > slotStart + a.endTime.getTime() + (a.bufferMinutes ?? 0) * 60_000 > slotStart ) ); if (hasGroomer) slots.push(new Date(slotStart).toISOString());