booking flow, loader, email, admin cli
This commit is contained in:
102
test/email.test.ts
Normal file
102
test/email.test.ts
Normal file
@@ -0,0 +1,102 @@
|
||||
import { afterAll, beforeAll, beforeEach, describe, expect, test } from "vitest";
|
||||
import { PrismaPg } from "@prisma/adapter-pg";
|
||||
import { PrismaClient } from "@/generated/prisma/client";
|
||||
import { createHold } from "@/lib/booking";
|
||||
import { resetEmailTransport, sendBookingConfirmation } from "@/lib/email";
|
||||
import { seed, type SeedResult } from "@/lib/seed";
|
||||
|
||||
const adapter = new PrismaPg({ connectionString: process.env.DATABASE_URL });
|
||||
const db = new PrismaClient({ adapter });
|
||||
|
||||
const MAILPIT = "http://localhost:8025";
|
||||
|
||||
let fx: SeedResult;
|
||||
let serviceId: string;
|
||||
let therapistA: string;
|
||||
let roomA: string;
|
||||
let customer: { id: string; email: string };
|
||||
|
||||
beforeAll(async () => {
|
||||
fx = await seed(db);
|
||||
serviceId = fx.services.find((s) => s.name === "60-minute Swedish")!.id;
|
||||
therapistA = fx.therapists[0].id;
|
||||
roomA = fx.rooms[0].id;
|
||||
customer = fx.customers[0];
|
||||
// Point email transport at Mailpit even though .env.test doesn't define it.
|
||||
process.env.SMTP_HOST = "localhost";
|
||||
process.env.SMTP_PORT = "1025";
|
||||
process.env.SMTP_FROM = "TouchBase <noreply@touchbase.local>";
|
||||
resetEmailTransport();
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await db.$disconnect();
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
await db.notification.deleteMany();
|
||||
await db.booking.deleteMany();
|
||||
// Clear Mailpit between tests to keep assertions clean.
|
||||
await fetch(`${MAILPIT}/api/v1/messages`, { method: "DELETE" });
|
||||
});
|
||||
|
||||
const D = (iso: string) => new Date(iso);
|
||||
|
||||
describe("sendBookingConfirmation", () => {
|
||||
test("delivers an email to Mailpit and writes a sent Notification row", async () => {
|
||||
const hold = await createHold(db, {
|
||||
customerId: customer.id,
|
||||
serviceId,
|
||||
therapistId: therapistA,
|
||||
roomId: roomA,
|
||||
startsAt: D("2026-05-05T14:00:00Z"),
|
||||
});
|
||||
|
||||
const result = await sendBookingConfirmation({ db, bookingId: hold.id });
|
||||
expect(result.status).toBe("sent");
|
||||
|
||||
// Notification row recorded
|
||||
const noti = await db.notification.findUnique({
|
||||
where: { id: result.notificationId },
|
||||
});
|
||||
expect(noti?.status).toBe("sent");
|
||||
expect(noti?.template).toBe("booking_confirmation");
|
||||
expect(noti?.bookingId).toBe(hold.id);
|
||||
expect(noti?.userId).toBe(customer.id);
|
||||
expect(noti?.to).toBe(customer.email);
|
||||
expect(noti?.sentAt).toBeTruthy();
|
||||
|
||||
// Mailpit received the message
|
||||
const res = await fetch(`${MAILPIT}/api/v1/messages`);
|
||||
const json = (await res.json()) as { messages: { To: { Address: string }[]; Subject: string }[] };
|
||||
expect(json.messages).toHaveLength(1);
|
||||
expect(json.messages[0].To[0].Address).toBe(customer.email);
|
||||
expect(json.messages[0].Subject).toContain("60-minute Swedish");
|
||||
});
|
||||
|
||||
test("formats local time in the practice timezone (America/Detroit)", async () => {
|
||||
process.env.APP_TZ = "America/Detroit";
|
||||
const hold = await createHold(db, {
|
||||
customerId: customer.id,
|
||||
serviceId,
|
||||
therapistId: therapistA,
|
||||
roomId: roomA,
|
||||
startsAt: D("2026-05-05T14:00:00Z"), // 10:00 EDT
|
||||
});
|
||||
await sendBookingConfirmation({ db, bookingId: hold.id });
|
||||
|
||||
const res = await fetch(`${MAILPIT}/api/v1/messages`);
|
||||
const list = (await res.json()) as { messages: { ID: string }[] };
|
||||
const msgRes = await fetch(`${MAILPIT}/api/v1/message/${list.messages[0].ID}`);
|
||||
const msg = (await msgRes.json()) as { Text: string };
|
||||
// 14:00 UTC → 10:00 EDT
|
||||
expect(msg.Text).toContain("10:00");
|
||||
expect(msg.Text).toContain("EDT");
|
||||
});
|
||||
|
||||
test("missing booking id throws", async () => {
|
||||
await expect(
|
||||
sendBookingConfirmation({ db, bookingId: "no-such-booking" }),
|
||||
).rejects.toThrow(/not found/);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user