This commit is contained in:
Stefan Lazic committed 2026-08-03 21:15:24 +01:00
1 parent ec5583f972
commit 546613e677
189 files changed
+9873 -655

No files matched your search

@@ -1,35 +1,104 @@
import { describe, expect, it } from "vitest";
import { beforeEach, describe, expect, it, vi } from "vitest";
import { PermissionDeniedError, assertCanAccessAppointment, canAccessAppointment } from "../authorization";
/**
* The registered-customer branch never touches the database (it's a plain
* userId comparison), so it's tested here in isolation. The guest-token
* branch's real hash/expiry/revocation behaviour is covered against a real
* database in customer-booking.integration.test.ts — this file only checks
* that authorization.ts calls into validateGuestAccessToken correctly and
* respects its result.
*/
vi.mock("@/services/scheduling/guest-access.service", () => ({
validateGuestAccessToken: vi.fn(),
}));
const { validateGuestAccessToken } = await import("@/services/scheduling/guest-access.service");
const mockValidate = vi.mocked(validateGuestAccessToken);
beforeEach(() => {
mockValidate.mockClear();
});
// Not a real PrismaClient — the mocked guest-access service never touches it.
const prisma = {} as never;
describe("assertCanAccessAppointment", () => {
it("allows a registered customer to access their own appointment", () => {
expect(() =>
assertCanAccessAppointment({ customer: { userId: "user_1" } }, "user_1"),
).not.toThrow();
it("allows a registered customer to access their own appointment", async () => {
await expect(
assertCanAccessAppointment(prisma, { id: "a1", customer: { userId: "user_1" } }, { sessionUserId: "user_1" }),
).resolves.toBeUndefined();
});
it("denies a different signed-in user from accessing a registered customer's appointment", () => {
expect(() => assertCanAccessAppointment({ customer: { userId: "user_1" } }, "user_2")).toThrow(
PermissionDeniedError,
);
it("denies a different signed-in user from accessing a registered customer's appointment", async () => {
await expect(
assertCanAccessAppointment(prisma, { id: "a1", customer: { userId: "user_1" } }, { sessionUserId: "user_2" }),
).rejects.toThrow(PermissionDeniedError);
});
it("denies an unauthenticated caller from accessing a registered customer's appointment", () => {
expect(() => assertCanAccessAppointment({ customer: { userId: "user_1" } }, undefined)).toThrow(
PermissionDeniedError,
);
it("denies an unauthenticated caller from accessing a registered customer's appointment", async () => {
await expect(
assertCanAccessAppointment(prisma, { id: "a1", customer: { userId: "user_1" } }, { sessionUserId: undefined }),
).rejects.toThrow(PermissionDeniedError);
});
it("allows access to a guest appointment (no linked user) regardless of session — the documented guest access model", () => {
expect(() => assertCanAccessAppointment({ customer: { userId: null } }, undefined)).not.toThrow();
expect(() => assertCanAccessAppointment({ customer: { userId: null } }, "some_other_user")).not.toThrow();
it("never even calls the guest-token validator for a registered customer's appointment", async () => {
mockValidate.mockClear();
await assertCanAccessAppointment(
prisma,
{ id: "a1", customer: { userId: "user_1" } },
{ sessionUserId: "user_1" },
).catch(() => {});
expect(mockValidate).not.toHaveBeenCalled();
});
it("allows a guest appointment when the guest-token validator confirms the token", async () => {
mockValidate.mockResolvedValueOnce(true);
await expect(
assertCanAccessAppointment(
prisma,
{ id: "a1", customer: { userId: null } },
{ sessionUserId: undefined, guestToken: "correct-token" },
),
).resolves.toBeUndefined();
expect(mockValidate).toHaveBeenCalledWith(prisma, { appointmentId: "a1", rawToken: "correct-token" });
});
it("denies a guest appointment when no token is supplied at all — the appointment id is no longer sufficient", async () => {
await expect(
assertCanAccessAppointment(prisma, { id: "a1", customer: { userId: null } }, { sessionUserId: undefined }),
).rejects.toThrow(PermissionDeniedError);
expect(mockValidate).not.toHaveBeenCalled();
});
it("denies a guest appointment when the guest-token validator rejects the token", async () => {
mockValidate.mockResolvedValueOnce(false);
await expect(
assertCanAccessAppointment(
prisma,
{ id: "a1", customer: { userId: null } },
{ sessionUserId: undefined, guestToken: "wrong-token" },
),
).rejects.toThrow(PermissionDeniedError);
});
});
describe("canAccessAppointment", () => {
it("mirrors assertCanAccessAppointment as a boolean", () => {
expect(canAccessAppointment({ customer: { userId: "user_1" } }, "user_1")).toBe(true);
expect(canAccessAppointment({ customer: { userId: "user_1" } }, "user_2")).toBe(false);
expect(canAccessAppointment({ customer: { userId: null } }, undefined)).toBe(true);
it("mirrors assertCanAccessAppointment as a boolean", async () => {
expect(
await canAccessAppointment(prisma, { id: "a1", customer: { userId: "user_1" } }, { sessionUserId: "user_1" }),
).toBe(true);
expect(
await canAccessAppointment(prisma, { id: "a1", customer: { userId: "user_1" } }, { sessionUserId: "user_2" }),
).toBe(false);
mockValidate.mockResolvedValueOnce(true);
expect(
await canAccessAppointment(
prisma,
{ id: "a1", customer: { userId: null } },
{ sessionUserId: undefined, guestToken: "correct-token" },
),
).toBe(true);
});
});