import "dotenv/config"; import { describe, it, expect, beforeEach, vi } from "vitest"; import { signAccessToken } from "@/features/auth/authCrypto"; const BASE_URL = "http://localhost"; const inMemoryProjects: any[] = []; const inMemoryFormSubmissions: any[] = []; vi.mock("@prisma/client", () => { class PrismaClientMock { project = { findUnique: async ({ where: { slug } }: any) => { return inMemoryProjects.find((p) => p.slug === slug) ?? null; }, }; formSubmission = { findMany: async ({ where: { projectId }, orderBy, take }: any) => { let items = inMemoryFormSubmissions.filter((s) => s.projectId === projectId); if (orderBy?.createdAt === "desc") { items = items.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime()); } if (typeof take === "number") { items = items.slice(0, take); } return items; }, }; } return { PrismaClient: PrismaClientMock }; }); const TEST_USER = { id: "user-1", email: "submissions@example.com", tokenVersion: 1 }; const OTHER_USER = { id: "user-2", email: "submissions2@example.com", tokenVersion: 1 }; async function buildAuthHeadersFor(user: { id: string; email: string; tokenVersion: number }) { const token = await signAccessToken(user); return { cookie: `pb_access=${token}` }; } async function buildAuthHeaders() { return buildAuthHeadersFor(TEST_USER); } beforeEach(() => { process.env.AUTH_JWT_SECRET = "test-jwt-secret-submissions"; process.env.AUTH_ENCRYPTION_KEY = "0123456789abcdef0123456789abcdef"; inMemoryProjects.length = 0; inMemoryFormSubmissions.length = 0; }); describe("/api/projects/[slug]/submissions", () => { it("로그인한 소유자가 자신의 프로젝트 제출 내역을 조회할 수 있어야 한다", async () => { const project = { id: "proj-1", slug: "project-with-submissions", userId: TEST_USER.id, }; inMemoryProjects.push(project); const createdAt = new Date(); inMemoryFormSubmissions.push({ id: "sub-1", projectId: project.id, projectSlug: project.slug, userId: TEST_USER.id, createdAt, payloadJson: { name: "홍길동", message: "문의" }, sensitiveEnc: undefined, metaJson: null, }); const { GET: getSubmissions } = await import("@/app/api/projects/[slug]/submissions/route"); const headers = await buildAuthHeaders(); const res = await getSubmissions( new Request(`${BASE_URL}/api/projects/${project.slug}/submissions`, { headers, }), { params: { slug: project.slug } } as any, ); expect(res.status).toBe(200); const json = (await res.json()) as any[]; expect(Array.isArray(json)).toBe(true); expect(json.length).toBe(1); expect(json[0].id).toBe("sub-1"); expect(json[0].projectSlug).toBe(project.slug); expect(json[0].payload.name).toBe("홍길동"); expect(json[0].payload.message).toBe("문의"); }); it("민감정보는 암호화되어 저장되더라도 조회 시 복호화된 값으로 응답 payload 에 포함되어야 한다", async () => { const project = { id: "proj-2", slug: "project-sensitive", userId: TEST_USER.id, }; inMemoryProjects.push(project); const { encryptJson } = await import("@/features/auth/authCrypto"); const sensitive = { email: "test@example.com", phone: "010-0000-0000" }; const sensitiveEnc = await encryptJson(sensitive); const createdAt = new Date(); inMemoryFormSubmissions.push({ id: "sub-2", projectId: project.id, projectSlug: project.slug, userId: TEST_USER.id, createdAt, payloadJson: { name: "김철수" }, sensitiveEnc, metaJson: null, }); const { GET: getSubmissions } = await import("@/app/api/projects/[slug]/submissions/route"); const headers = await buildAuthHeaders(); const res = await getSubmissions( new Request(`${BASE_URL}/api/projects/${project.slug}/submissions`, { headers, }), { params: { slug: project.slug } } as any, ); expect(res.status).toBe(200); const json = (await res.json()) as any[]; expect(json.length).toBe(1); expect(json[0].payload.name).toBe("김철수"); expect(json[0].payload.email).toBe("test@example.com"); expect(json[0].payload.phone).toBe("010-0000-0000"); }); it("로그인하지 않은 경우 401 을 반환해야 한다", async () => { const { GET: getSubmissions } = await import("@/app/api/projects/[slug]/submissions/route"); const res = await getSubmissions( new Request(`${BASE_URL}/api/projects/any/submissions`), { params: { slug: "any" } } as any, ); expect(res.status).toBe(401); }); it("다른 사용자가 소유한 프로젝트 제출 내역에 접근하면 404 를 반환해야 한다", async () => { const project = { id: "proj-3", slug: "owner-only-project", userId: OTHER_USER.id, }; inMemoryProjects.push(project); const { GET: getSubmissions } = await import("@/app/api/projects/[slug]/submissions/route"); const headers = await buildAuthHeaders(); const res = await getSubmissions( new Request(`${BASE_URL}/api/projects/${project.slug}/submissions`, { headers, }), { params: { slug: project.slug } } as any, ); expect(res.status).toBe(404); }); });