import { describe, it, expect, vi, beforeEach } from "vitest"; import { prismaMock, resetPrismaMock } from "@/__tests__/helpers/prisma-mock"; import { createRequest, createRouteContext, parseJsonResponse } from "@/__tests__/helpers/api-test-utils"; import { TEST_BLINDBOX_ROOM } from "@/__tests__/helpers/fixtures"; vi.mock("@/lib/auth", () => ({ getAuthUserId: vi.fn().mockResolvedValue("user-1"), })); vi.mock("@/lib/blindbox", () => ({ getRoomByCode: vi.fn(), requireMembership: vi.fn().mockResolvedValue({}), })); import { GET, PATCH, DELETE } from "./route"; import { getRoomByCode } from "@/lib/blindbox"; import { getAuthUserId } from "@/lib/auth"; const mockGetRoomByCode = vi.mocked(getRoomByCode); beforeEach(() => { resetPrismaMock(); vi.clearAllMocks(); vi.mocked(getAuthUserId).mockResolvedValue("user-1"); }); describe("GET /api/blindbox/room/[code]", () => { it("returns room data", async () => { mockGetRoomByCode.mockResolvedValue({ ...TEST_BLINDBOX_ROOM, _count: { ideas: 3 }, members: [ { user: { id: "user-1", username: "test", avatar: "🐱" }, joinedAt: new Date() }, ], } as never); const req = createRequest("/api/blindbox/room/ABC123"); const ctx = createRouteContext({ code: "ABC123" }); const res = await GET(req, ctx); const { status, data } = await parseJsonResponse(res); expect(status).toBe(200); expect(data.code).toBe("ABC123"); expect(data.poolCount).toBe(3); expect(data.members).toHaveLength(1); }); it("returns 404 for nonexistent room", async () => { mockGetRoomByCode.mockResolvedValue(null); const req = createRequest("/api/blindbox/room/BADCODE"); const ctx = createRouteContext({ code: "BADCODE" }); const res = await GET(req, ctx); expect(res.status).toBe(404); }); }); describe("PATCH /api/blindbox/room/[code]", () => { it("updates room location", async () => { prismaMock.blindBoxRoom.findUnique.mockResolvedValue(TEST_BLINDBOX_ROOM as never); prismaMock.blindBoxRoom.update.mockResolvedValue({ ...TEST_BLINDBOX_ROOM, city: "上海", lat: 31.2, lng: 121.4, } as never); const req = createRequest("/api/blindbox/room/ABC123", { method: "PATCH", body: { city: "上海", lat: 31.2, lng: 121.4 }, }); const ctx = createRouteContext({ code: "ABC123" }); const res = await PATCH(req, ctx); const { status, data } = await parseJsonResponse(res); expect(status).toBe(200); expect(data.city).toBe("上海"); }); it("returns 400 for invalid coordinates", async () => { prismaMock.blindBoxRoom.findUnique.mockResolvedValue(TEST_BLINDBOX_ROOM as never); const req = createRequest("/api/blindbox/room/ABC123", { method: "PATCH", body: { lat: 999, lng: 121.4 }, }); const ctx = createRouteContext({ code: "ABC123" }); const res = await PATCH(req, ctx); expect(res.status).toBe(400); }); }); describe("DELETE /api/blindbox/room/[code]", () => { it("deletes room when creator", async () => { // user-1 is creator (TEST_BLINDBOX_ROOM.creatorId = "user-1") prismaMock.blindBoxRoom.findUnique.mockResolvedValue(TEST_BLINDBOX_ROOM as never); prismaMock.blindBoxRoom.delete.mockResolvedValue({} as never); const req = createRequest("/api/blindbox/room/ABC123", { method: "DELETE", body: {} }); const ctx = createRouteContext({ code: "ABC123" }); const res = await DELETE(req, ctx); const { data } = await parseJsonResponse(res); expect(data.action).toBe("deleted"); }); it("leaves room when not creator", async () => { // Authenticate as user-2 (not creator) vi.mocked(getAuthUserId).mockResolvedValueOnce("user-2"); prismaMock.blindBoxRoom.findUnique.mockResolvedValue(TEST_BLINDBOX_ROOM as never); prismaMock.blindBoxMember.findUnique.mockResolvedValue({ id: "member-2" } as never); prismaMock.blindBoxMember.delete.mockResolvedValue({} as never); const req = createRequest("/api/blindbox/room/ABC123", { method: "DELETE", body: {} }); const ctx = createRouteContext({ code: "ABC123" }); const res = await DELETE(req, ctx); const { data } = await parseJsonResponse(res); expect(data.action).toBe("left"); }); it("returns 403 when not a member and not creator", async () => { // Authenticate as stranger (not creator, not member) vi.mocked(getAuthUserId).mockResolvedValueOnce("stranger"); prismaMock.blindBoxRoom.findUnique.mockResolvedValue(TEST_BLINDBOX_ROOM as never); prismaMock.blindBoxMember.findUnique.mockResolvedValue(null as never); const req = createRequest("/api/blindbox/room/ABC123", { method: "DELETE", body: {} }); const ctx = createRouteContext({ code: "ABC123" }); const res = await DELETE(req, ctx); expect(res.status).toBe(403); }); });