add Stripe
This commit is contained in:
@@ -7,6 +7,9 @@ import {
|
||||
confirmHold,
|
||||
createHold,
|
||||
expireStaleHolds,
|
||||
markComplete,
|
||||
markNoShow,
|
||||
rescheduleBooking,
|
||||
} from "@/lib/booking";
|
||||
import { seed, type SeedResult } from "@/lib/seed";
|
||||
|
||||
@@ -309,3 +312,168 @@ describe("expireStaleHolds", () => {
|
||||
expect(staleRow?.cancelReason).toBe("hold expired");
|
||||
});
|
||||
});
|
||||
|
||||
describe("rescheduleBooking", () => {
|
||||
test("cancels old + creates new at the new time, atomically", async () => {
|
||||
const original = await createHold(db, {
|
||||
customerId,
|
||||
serviceId,
|
||||
therapistId: therapistA,
|
||||
roomId: roomA,
|
||||
startsAt: D("2026-05-05T14:00:00Z"),
|
||||
});
|
||||
await confirmHold(db, original.id);
|
||||
|
||||
const result = await rescheduleBooking(db, {
|
||||
oldBookingId: original.id,
|
||||
newStartsAt: D("2026-05-06T14:00:00Z"),
|
||||
cancelledByUserId: customerId,
|
||||
});
|
||||
expect(result.oldBookingId).toBe(original.id);
|
||||
expect(result.newBookingId).not.toBe(original.id);
|
||||
|
||||
const oldRow = await db.booking.findUnique({ where: { id: original.id } });
|
||||
const newRow = await db.booking.findUnique({ where: { id: result.newBookingId } });
|
||||
expect(oldRow?.status).toBe("CANCELLED");
|
||||
expect(oldRow?.cancelReason).toBe("rescheduled");
|
||||
expect(newRow?.status).toBe("CONFIRMED");
|
||||
expect(newRow?.startsAt).toEqual(D("2026-05-06T14:00:00Z"));
|
||||
});
|
||||
|
||||
test("rolls back if the new slot conflicts (old booking stays CONFIRMED)", async () => {
|
||||
// Block the target slot with a CONFIRMED booking on therapistA at 11:00.
|
||||
const blocker = await createHold(db, {
|
||||
customerId,
|
||||
serviceId,
|
||||
therapistId: therapistA,
|
||||
roomId: roomA,
|
||||
startsAt: D("2026-05-06T15:00:00Z"),
|
||||
});
|
||||
await confirmHold(db, blocker.id);
|
||||
|
||||
const original = await createHold(db, {
|
||||
customerId,
|
||||
serviceId,
|
||||
therapistId: therapistA,
|
||||
roomId: roomA,
|
||||
startsAt: D("2026-05-05T14:00:00Z"),
|
||||
});
|
||||
await confirmHold(db, original.id);
|
||||
|
||||
await expect(
|
||||
rescheduleBooking(db, {
|
||||
oldBookingId: original.id,
|
||||
newStartsAt: D("2026-05-06T15:00:00Z"), // collides with blocker
|
||||
cancelledByUserId: customerId,
|
||||
}),
|
||||
).rejects.toBeInstanceOf(BookingConflictError);
|
||||
|
||||
// Both bookings unchanged
|
||||
const orig = await db.booking.findUnique({ where: { id: original.id } });
|
||||
const blk = await db.booking.findUnique({ where: { id: blocker.id } });
|
||||
expect(orig?.status).toBe("CONFIRMED");
|
||||
expect(blk?.status).toBe("CONFIRMED");
|
||||
});
|
||||
|
||||
test("rejects rescheduling a CANCELLED booking", async () => {
|
||||
const original = await createHold(db, {
|
||||
customerId,
|
||||
serviceId,
|
||||
therapistId: therapistA,
|
||||
roomId: roomA,
|
||||
startsAt: D("2026-05-05T14:00:00Z"),
|
||||
});
|
||||
await cancelBooking(db, {
|
||||
bookingId: original.id,
|
||||
cancelledByUserId: customerId,
|
||||
});
|
||||
await expect(
|
||||
rescheduleBooking(db, {
|
||||
oldBookingId: original.id,
|
||||
newStartsAt: D("2026-05-06T14:00:00Z"),
|
||||
cancelledByUserId: customerId,
|
||||
}),
|
||||
).rejects.toThrow(/Cannot reschedule/);
|
||||
});
|
||||
|
||||
test("can change therapist/room/service on reschedule", async () => {
|
||||
const original = await createHold(db, {
|
||||
customerId,
|
||||
serviceId,
|
||||
therapistId: therapistA,
|
||||
roomId: roomA,
|
||||
startsAt: D("2026-05-05T14:00:00Z"),
|
||||
});
|
||||
await confirmHold(db, original.id);
|
||||
|
||||
const result = await rescheduleBooking(db, {
|
||||
oldBookingId: original.id,
|
||||
newStartsAt: D("2026-05-06T14:00:00Z"),
|
||||
newTherapistId: therapistB,
|
||||
newRoomId: roomB,
|
||||
cancelledByUserId: customerId,
|
||||
});
|
||||
const newRow = await db.booking.findUnique({ where: { id: result.newBookingId } });
|
||||
expect(newRow?.therapistId).toBe(therapistB);
|
||||
expect(newRow?.roomId).toBe(roomB);
|
||||
});
|
||||
});
|
||||
|
||||
describe("markComplete / markNoShow", () => {
|
||||
test("markComplete: CONFIRMED → COMPLETED", async () => {
|
||||
const hold = await createHold(db, {
|
||||
customerId,
|
||||
serviceId,
|
||||
therapistId: therapistA,
|
||||
roomId: roomA,
|
||||
startsAt: D("2026-05-05T14:00:00Z"),
|
||||
});
|
||||
await confirmHold(db, hold.id);
|
||||
const result = await markComplete(db, hold.id);
|
||||
expect(result.ok).toBe(true);
|
||||
const row = await db.booking.findUnique({ where: { id: hold.id } });
|
||||
expect(row?.status).toBe("COMPLETED");
|
||||
});
|
||||
|
||||
test("markComplete is idempotent", async () => {
|
||||
const hold = await createHold(db, {
|
||||
customerId,
|
||||
serviceId,
|
||||
therapistId: therapistA,
|
||||
roomId: roomA,
|
||||
startsAt: D("2026-05-05T14:00:00Z"),
|
||||
});
|
||||
await confirmHold(db, hold.id);
|
||||
await markComplete(db, hold.id);
|
||||
const second = await markComplete(db, hold.id);
|
||||
expect(second.ok).toBe(true);
|
||||
});
|
||||
|
||||
test("markComplete rejects HOLD bookings", async () => {
|
||||
const hold = await createHold(db, {
|
||||
customerId,
|
||||
serviceId,
|
||||
therapistId: therapistA,
|
||||
roomId: roomA,
|
||||
startsAt: D("2026-05-05T14:00:00Z"),
|
||||
});
|
||||
const result = await markComplete(db, hold.id);
|
||||
expect(result.ok).toBe(false);
|
||||
if (!result.ok) expect(result.reason).toMatch(/HOLD/);
|
||||
});
|
||||
|
||||
test("markNoShow: CONFIRMED → NO_SHOW", async () => {
|
||||
const hold = await createHold(db, {
|
||||
customerId,
|
||||
serviceId,
|
||||
therapistId: therapistA,
|
||||
roomId: roomA,
|
||||
startsAt: D("2026-05-05T14:00:00Z"),
|
||||
});
|
||||
await confirmHold(db, hold.id);
|
||||
const result = await markNoShow(db, hold.id);
|
||||
expect(result.ok).toBe(true);
|
||||
const row = await db.booking.findUnique({ where: { id: hold.id } });
|
||||
expect(row?.status).toBe("NO_SHOW");
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user