import { afterAll, beforeEach, describe, expect, it } from "vitest"; import db from "../../app/db.server"; import { compileCustomerData, purgeShopData, redactCustomerData } from "../../app/services/gdpr.server"; const shopDomain = "gdpr-integration-test.myshopify.com"; async function cleanup() { await db.booking.deleteMany({ where: { shopDomain } }); await db.location.deleteMany({ where: { shopDomain } }); await db.shop.deleteMany({ where: { shopDomain } }); await db.session.deleteMany({ where: { shop: shopDomain } }); } async function seed() { await db.shop.create({ data: { shopDomain } }); const location = await db.location.create({ data: { shopDomain, name: "Test Location", address: "", timezone: "America/Toronto" }, }); const zone = await db.zone.create({ data: { shopDomain, locationId: location.id, name: "Local", type: "postal", postalCodes: ["M5V"] }, }); await db.rate.create({ data: { shopDomain, method: "LOCAL_DELIVERY", zoneId: zone.id, name: "Standard", priceCents: 500, keyedBy: "zone" }, }); await db.slotTemplate.create({ data: { shopDomain, locationId: location.id, method: "PICKUP", weekday: 1, startMin: 540, endMin: 600, capacity: 5 }, }); const booking = await db.booking.create({ data: { shopDomain, orderId: "gid://shopify/Order/gdpr-1", locationId: location.id, method: "PICKUP", slotStart: new Date("2026-08-25T13:00:00.000Z"), slotEnd: new Date("2026-08-25T13:30:00.000Z"), customerEmail: "shopper@example.com", customerPhone: "+15551234567", }, }); return { location, zone, booking }; } describe("GDPR handlers", () => { beforeEach(cleanup); afterAll(async () => { await cleanup(); await db.$disconnect(); }); it("compileCustomerData finds Bookings matching the customer's email", async () => { await seed(); const results = await compileCustomerData(shopDomain, { email: "shopper@example.com" }); expect(results).toHaveLength(1); expect(results[0].orderId).toBe("gid://shopify/Order/gdpr-1"); }); it("compileCustomerData finds Bookings matching the customer's phone", async () => { await seed(); const results = await compileCustomerData(shopDomain, { phone: "+15551234567" }); expect(results).toHaveLength(1); }); it("compileCustomerData returns nothing for an unrelated customer", async () => { await seed(); const results = await compileCustomerData(shopDomain, { email: "someone-else@example.com" }); expect(results).toHaveLength(0); }); it("redactCustomerData anonymizes matching Bookings but keeps the row", async () => { const { booking } = await seed(); const { count } = await redactCustomerData(shopDomain, { email: "shopper@example.com" }); expect(count).toBe(1); const after = await db.booking.findUnique({ where: { id: booking.id } }); expect(after).not.toBeNull(); expect(after?.customerEmail).toBeNull(); expect(after?.customerPhone).toBeNull(); expect(after?.orderId).toBe("gid://shopify/Order/gdpr-1"); // booking history preserved }); it("purgeShopData deletes every shopDomain-scoped row, respecting FK order", async () => { const { location, zone, booking } = await seed(); await purgeShopData(shopDomain); expect(await db.booking.findUnique({ where: { id: booking.id } })).toBeNull(); expect(await db.location.findUnique({ where: { id: location.id } })).toBeNull(); expect(await db.zone.findUnique({ where: { id: zone.id } })).toBeNull(); // cascaded from Location expect(await db.rate.findMany({ where: { shopDomain } })).toHaveLength(0); // cascaded from Zone expect(await db.slotTemplate.findMany({ where: { shopDomain } })).toHaveLength(0); // cascaded from Location expect(await db.shop.findUnique({ where: { shopDomain } })).toBeNull(); }); });