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_USER } from "@/__tests__/helpers/fixtures"; vi.mock("bcryptjs", () => ({ default: { compare: vi.fn(), hash: vi.fn().mockResolvedValue("$2a$10$newhash"), }, })); import bcrypt from "bcryptjs"; import { GET, PUT } from "./route"; const mockCtx = { params: Promise.resolve({}) }; beforeEach(() => { resetPrismaMock(); vi.mocked(bcrypt.compare).mockReset(); }); describe("GET /api/user", () => { it("returns null when no userId provided", async () => { const req = createRequest("/api/user"); const res = await GET(req, mockCtx); const { data } = await parseJsonResponse(res); expect(data).toBeNull(); }); it("returns null when user not found", async () => { prismaMock.user.findUnique.mockResolvedValue(null as never); const req = createRequest("/api/user?id=nonexistent"); const res = await GET(req, mockCtx); const { data } = await parseJsonResponse(res); expect(data).toBeNull(); }); it("returns user info with decision count", async () => { prismaMock.user.findUnique.mockResolvedValue(TEST_USER as never); prismaMock.decision.count.mockResolvedValue(5 as never); const req = createRequest("/api/user?id=user-1"); const res = await GET(req, mockCtx); const { status, data } = await parseJsonResponse(res); expect(status).toBe(200); expect(data.id).toBe("user-1"); expect(data.username).toBe("testuser"); expect(data.decisionCount).toBe(5); }); }); describe("PUT /api/user", () => { it("updates username", async () => { prismaMock.user.findUnique .mockResolvedValueOnce(TEST_USER as never) .mockResolvedValueOnce(null as never); prismaMock.user.update.mockResolvedValue({ ...TEST_USER, username: "newname", preferences: "{}", } as never); const req = createRequest("/api/user", { method: "PUT", body: { userId: "user-1", username: "newname" }, }); const res = await PUT(req, mockCtx); const { status, data } = await parseJsonResponse(res); expect(status).toBe(200); expect(data.username).toBe("newname"); }); it("returns 409 when new username is taken", async () => { prismaMock.user.findUnique .mockResolvedValueOnce(TEST_USER as never) .mockResolvedValueOnce({ id: "other" } as never); const req = createRequest("/api/user", { method: "PUT", body: { userId: "user-1", username: "takenname" }, }); const res = await PUT(req, mockCtx); expect(res.status).toBe(409); }); it("updates password with correct current password", async () => { prismaMock.user.findUnique.mockResolvedValue(TEST_USER as never); vi.mocked(bcrypt.compare).mockResolvedValue(true as never); prismaMock.user.update.mockResolvedValue({ ...TEST_USER, preferences: "{}" } as never); const req = createRequest("/api/user", { method: "PUT", body: { userId: "user-1", currentPassword: "old", newPassword: "newpass123" }, }); const res = await PUT(req, mockCtx); expect(res.status).toBe(200); }); it("returns 403 when current password is wrong", async () => { prismaMock.user.findUnique.mockResolvedValue(TEST_USER as never); vi.mocked(bcrypt.compare).mockResolvedValue(false as never); const req = createRequest("/api/user", { method: "PUT", body: { userId: "user-1", currentPassword: "wrong", newPassword: "newpass123" }, }); const res = await PUT(req, mockCtx); expect(res.status).toBe(403); }); it("returns 400 when no current password for password change", async () => { prismaMock.user.findUnique.mockResolvedValue(TEST_USER as never); const req = createRequest("/api/user", { method: "PUT", body: { userId: "user-1", newPassword: "newpass123" }, }); const res = await PUT(req, mockCtx); expect(res.status).toBe(400); }); it("returns 401 when no userId", async () => { const req = createRequest("/api/user", { method: "PUT", body: { username: "test" }, }); const res = await PUT(req, mockCtx); expect(res.status).toBe(401); }); it("updates avatar", async () => { prismaMock.user.findUnique.mockResolvedValue(TEST_USER as never); prismaMock.user.update.mockResolvedValue({ ...TEST_USER, avatar: "🦊", preferences: "{}" } as never); const req = createRequest("/api/user", { method: "PUT", body: { userId: "user-1", avatar: "🦊" }, }); const res = await PUT(req, mockCtx); const { data } = await parseJsonResponse(res); expect(data.avatar).toBe("🦊"); }); it("updates email with validation", async () => { prismaMock.user.findUnique.mockResolvedValue(TEST_USER as never); prismaMock.user.update.mockResolvedValue({ ...TEST_USER, email: "new@example.com", preferences: "{}", } as never); const req = createRequest("/api/user", { method: "PUT", body: { userId: "user-1", email: "new@example.com" }, }); const res = await PUT(req, mockCtx); expect(res.status).toBe(200); }); it("rejects invalid email", async () => { prismaMock.user.findUnique.mockResolvedValue(TEST_USER as never); const req = createRequest("/api/user", { method: "PUT", body: { userId: "user-1", email: "notanemail" }, }); const res = await PUT(req, mockCtx); expect(res.status).toBe(400); }); });