import "dotenv/config"; import { describe, it, expect, beforeEach, afterEach, vi } from "vitest"; import { signAccessToken } from "@/features/auth/authCrypto"; const BASE_URL = "http://localhost"; // in-memory Prisma 목 데이터 const inMemoryProjects: any[] = []; const inMemorySubmissions: any[] = []; vi.mock("@prisma/client", () => { class PrismaClientMock { project = { findMany: async ({ where }: any) => { if (!where || !where.userId) return []; return inMemoryProjects.filter((p) => p.userId === where.userId); }, }; formSubmission = { findMany: async ({ where }: any) => { if (!where || !where.userId) return []; return inMemorySubmissions.filter((s) => s.userId === where.userId); }, }; } return { PrismaClient: PrismaClientMock }; }); beforeEach(() => { inMemoryProjects.length = 0; inMemorySubmissions.length = 0; process.env.AUTH_JWT_SECRET = "test-jwt-secret-reports-leads"; process.env.AUTH_ENCRYPTION_KEY = "0123456789abcdef0123456789abcdef"; vi.useFakeTimers(); vi.setSystemTime(new Date("2025-01-10T00:00:00.000Z")); }); afterEach(() => { vi.useRealTimers(); }); describe("/api/reports/leads", () => { it("range=all 에서 필터된 모든 리드를 최신순으로 반환해야 한다", async () => { const { GET: leads } = await import("@/app/api/reports/leads/route"); const userId = "user-reports-leads-all"; inMemoryProjects.push( { id: "p1", userId, title: "Project A", slug: "project-a" }, { id: "p2", userId, title: "Project B", slug: "project-b" }, ); inMemorySubmissions.push( { id: "s1", userId, projectId: "p1", projectSlug: "project-a", createdAt: new Date("2025-01-09T12:00:00.000Z"), payloadJson: { name: "Alice" }, }, { id: "s2", userId, projectId: "p2", projectSlug: "project-b", createdAt: new Date("2025-01-08T09:00:00.000Z"), payloadJson: { email: "bob@example.com" }, }, { id: "s3", userId, projectId: "p1", projectSlug: "project-a", createdAt: new Date("2025-01-10T00:00:00.000Z"), payloadJson: { phone: "010-0000-0000" }, }, ); const token = await signAccessToken({ id: userId, email: "reports-leads@example.com", tokenVersion: 1, emailVerified: true, } as any); const res = await leads( new Request(`${BASE_URL}/api/reports/leads?range=all`, { headers: { cookie: `pb_access=${token}` }, }), ); expect(res.status).toBe(200); const json = (await res.json()) as any; expect(json.total).toBe(3); expect(Array.isArray(json.items)).toBe(true); expect(json.items.length).toBe(3); // 최신순 정렬: createdAt 이 가장 최근인 s3 가 첫 번째여야 한다. expect(json.items[0].id).toBe("s3"); expect(json.items[1].id).toBe("s1"); expect(json.items[2].id).toBe("s2"); const first = json.items[0]; expect(first.projectSlug).toBe("project-a"); expect(first.projectTitle).toBe("Project A"); expect(first.payload).toEqual({ phone: "010-0000-0000" }); }); it("range=7d 와 projectId 가 적용되면 해당 조건에 맞는 리드만 반환해야 한다", async () => { const { GET: leads } = await import("@/app/api/reports/leads/route"); const userId = "user-reports-leads-filter"; inMemoryProjects.push({ id: "p1", userId, title: "Project A", slug: "project-a" }); // 기준 시간: 2025-01-10 inMemorySubmissions.push( { id: "s1", userId, projectId: "p1", projectSlug: "project-a", createdAt: new Date("2025-01-09T12:00:00.000Z"), // within 7d payloadJson: { name: "Alice" }, }, { id: "s2", userId, projectId: "p1", projectSlug: "project-a", createdAt: new Date("2025-01-01T12:00:00.000Z"), // older than 7d payloadJson: { name: "Old" }, }, ); const token = await signAccessToken({ id: userId, email: "reports-leads-filter@example.com", tokenVersion: 1, emailVerified: true, } as any); const res = await leads( new Request(`${BASE_URL}/api/reports/leads?range=7d&projectId=p1`, { headers: { cookie: `pb_access=${token}` }, }), ); expect(res.status).toBe(200); const json = (await res.json()) as any; expect(json.total).toBe(1); expect(json.items.length).toBe(1); expect(json.items[0].id).toBe("s1"); }); it("from/to 쿼리로 커스텀 기간의 리드만 반환해야 한다", async () => { const { GET: leads } = await import("@/app/api/reports/leads/route"); const userId = "user-reports-leads-from-to"; inMemoryProjects.push({ id: "p1", userId, title: "Project A", slug: "project-a" }); inMemorySubmissions.push( { id: "s1", userId, projectId: "p1", projectSlug: "project-a", createdAt: new Date("2025-01-01T12:00:00.000Z"), payloadJson: { label: "old" }, }, { id: "s2", userId, projectId: "p1", projectSlug: "project-a", createdAt: new Date("2025-01-05T12:00:00.000Z"), payloadJson: { label: "mid" }, }, { id: "s3", userId, projectId: "p1", projectSlug: "project-a", createdAt: new Date("2025-01-09T12:00:00.000Z"), payloadJson: { label: "new" }, }, ); const token = await signAccessToken({ id: userId, email: "reports-leads-from-to@example.com", tokenVersion: 1, emailVerified: true, } as any); const res = await leads( new Request(`${BASE_URL}/api/reports/leads?from=2025-01-02&to=2025-01-08`, { headers: { cookie: `pb_access=${token}` }, }), ); expect(res.status).toBe(200); const json = (await res.json()) as any; expect(json.total).toBe(1); expect(json.items.length).toBe(1); expect(json.items[0].id).toBe("s2"); }); it("JWT 가 없으면 401 을 반환해야 한다", async () => { const { GET: leads } = await import("@/app/api/reports/leads/route"); const res = await leads(new Request(`${BASE_URL}/api/reports/leads?range=all`)); expect(res.status).toBe(401); }); });