import { describe, it, expect, vi, beforeEach } from "vitest"; import { prismaMock, resetPrismaMock } from "@/__tests__/helpers/prisma-mock"; import { createRequest, parseJsonResponse } from "@/__tests__/helpers/api-test-utils"; import { TEST_BLINDBOX_IDEA } from "@/__tests__/helpers/fixtures"; vi.mock("@/lib/blindbox", () => ({ requireMembership: vi.fn().mockResolvedValue({}), })); vi.mock("@/lib/ai", () => ({ tagIdea: vi.fn().mockResolvedValue({ category: "outdoor", timeSlot: "morning", estimatedMinutes: 120, costLevel: "free", intensity: "active", needsBooking: false, searchQuery: "公园", searchType: "category", }), })); import { POST, GET, PUT, DELETE } from "./route"; const mockCtx = { params: Promise.resolve({}) }; beforeEach(() => { resetPrismaMock(); }); describe("POST /api/blindbox (create idea)", () => { it("creates an idea successfully", async () => { prismaMock.blindBoxIdea.create.mockResolvedValue(TEST_BLINDBOX_IDEA as never); prismaMock.blindBoxIdea.update.mockResolvedValue(TEST_BLINDBOX_IDEA as never); const req = createRequest("/api/blindbox", { method: "POST", body: { roomId: "bb-room-1", userId: "user-1", content: "去公园野餐" }, }); const res = await POST(req, mockCtx); const { status, data } = await parseJsonResponse(res); expect(status).toBe(201); expect(data.id).toBe("idea-1"); }); it("returns 401 when no userId", async () => { const req = createRequest("/api/blindbox", { method: "POST", body: { roomId: "bb-room-1", content: "test" }, }); const res = await POST(req, mockCtx); expect(res.status).toBe(401); }); it("returns 400 when content is empty", async () => { const req = createRequest("/api/blindbox", { method: "POST", body: { roomId: "bb-room-1", userId: "user-1", content: "" }, }); const res = await POST(req, mockCtx); expect(res.status).toBe(400); }); it("returns 400 when content over 200 chars", async () => { const req = createRequest("/api/blindbox", { method: "POST", body: { roomId: "bb-room-1", userId: "user-1", content: "a".repeat(201) }, }); const res = await POST(req, mockCtx); expect(res.status).toBe(400); }); }); describe("GET /api/blindbox (get pool data)", () => { it("returns pool data for valid member", async () => { prismaMock.blindBoxIdea.count.mockResolvedValue(5 as never); prismaMock.blindBoxIdea.findMany .mockResolvedValueOnce([TEST_BLINDBOX_IDEA] as never) .mockResolvedValueOnce([] as never); const req = createRequest("/api/blindbox?userId=user-1&roomId=bb-room-1"); const res = await GET(req, mockCtx); const { status, data } = await parseJsonResponse(res); expect(status).toBe(200); expect(data.poolCount).toBe(5); expect(data.myIdeas).toHaveLength(1); expect(data.drawn).toHaveLength(0); }); it("returns 401 when no userId", async () => { const req = createRequest("/api/blindbox?roomId=bb-room-1"); const res = await GET(req, mockCtx); expect(res.status).toBe(401); }); }); describe("PUT /api/blindbox (edit idea)", () => { it("edits an idea successfully", async () => { prismaMock.blindBoxIdea.updateMany.mockResolvedValue({ count: 1 } as never); const req = createRequest("/api/blindbox", { method: "PUT", body: { ideaId: "idea-1", userId: "user-1", content: "去公园散步" }, }); const res = await PUT(req, mockCtx); const { status, data } = await parseJsonResponse(res); expect(status).toBe(200); expect(data.content).toBe("去公园散步"); }); it("returns 404 when idea not found or already drawn", async () => { prismaMock.blindBoxIdea.updateMany.mockResolvedValue({ count: 0 } as never); const req = createRequest("/api/blindbox", { method: "PUT", body: { ideaId: "nonexistent", userId: "user-1", content: "test" }, }); const res = await PUT(req, mockCtx); expect(res.status).toBe(404); }); }); describe("DELETE /api/blindbox (delete idea)", () => { it("deletes an idea successfully", async () => { prismaMock.blindBoxIdea.deleteMany.mockResolvedValue({ count: 1 } as never); const req = createRequest("/api/blindbox", { method: "DELETE", body: { ideaId: "idea-1", userId: "user-1" }, }); const res = await DELETE(req, mockCtx); const { data } = await parseJsonResponse(res); expect(data.deleted).toBe(true); }); it("returns 404 when idea not found or not owned", async () => { prismaMock.blindBoxIdea.deleteMany.mockResolvedValue({ count: 0 } as never); const req = createRequest("/api/blindbox", { method: "DELETE", body: { ideaId: "nonexistent", userId: "user-1" }, }); const res = await DELETE(req, mockCtx); expect(res.status).toBe(404); }); });