/** * Tests for GET /api/social/profile/[userId] — postsCount + followingCount * * Covers: * - postsCount reflects live communityPost.count (not 0 when posts exist) * - followingCount reflects live userFollow.count * - followersCount passes through from profile row (denormalized) */ import { describe, expect, it, vi, beforeEach } from "vitest"; const mocks = vi.hoisted(() => ({ communityPost: { findMany: vi.fn(), count: vi.fn(), }, userFollow: { findUnique: vi.fn(), count: vi.fn(), }, profile: { findUnique: vi.fn(), }, userScore: { findUnique: vi.fn(), }, domainSubmission: { count: vi.fn(), }, })); vi.mock("../../server/utils/prisma", () => ({ usePrisma: () => ({ communityPost: mocks.communityPost, userFollow: mocks.userFollow, profile: mocks.profile, userScore: mocks.userScore, domainSubmission: mocks.domainSubmission, }), })); vi.mock("../../server/utils/auth", () => ({ requireUser: vi.fn().mockRejectedValue( Object.assign(new Error("Unauthorized"), { statusCode: 401 }), ), })); vi.mock("../../server/db/profile", () => ({ getProfile: vi.fn().mockResolvedValue({ id: "user-1", username: "testuser", followersCount: 3, createdAt: new Date("2026-01-01T00:00:00Z"), }), })); vi.mock("../../server/db/scores", () => ({ getUserScore: vi.fn().mockResolvedValue({ tier: "beginner", totalPoints: 0 }), })); vi.mock("../../server/db/social", () => ({ getFollowRelation: vi.fn().mockResolvedValue(null), })); vi.mock("../../server/utils/getUsersMeta", () => ({ getUsersMeta: vi.fn().mockResolvedValue({ "user-1": { nickname: "Tester", avatar: null }, }), })); // Stub Nitro globals needed by the endpoint file const g = globalThis as Record; if (typeof g.getRouterParam === "undefined") { g.getRouterParam = (_event: unknown, key: string) => key === "userId" ? "user-1" : undefined; } beforeEach(() => { vi.clearAllMocks(); mocks.communityPost.findMany.mockResolvedValue([]); mocks.communityPost.count.mockResolvedValue(0); mocks.userFollow.count.mockResolvedValue(0); mocks.userFollow.findUnique.mockResolvedValue(null); mocks.domainSubmission.count.mockResolvedValue(0); }); async function callHandler() { const mod = await import("../../server/api/social/profile/[userId].get"); const handler = typeof mod.default === "function" ? mod.default : (mod.default as { handler?: unknown }).handler; return (handler as (e: unknown) => Promise)({ body: null }); } describe("postsCount", () => { it("returns postsCount > 0 when user has unmoderated posts", async () => { mocks.communityPost.count.mockResolvedValueOnce(5); mocks.userFollow.count.mockResolvedValueOnce(2); const result = (await callHandler()) as Record; expect(result.postsCount).toBe(5); expect(mocks.communityPost.count).toHaveBeenCalledWith({ where: { userId: "user-1", isModerated: false }, }); }); it("returns postsCount = 0 when user has no posts", async () => { mocks.communityPost.count.mockResolvedValueOnce(0); mocks.userFollow.count.mockResolvedValueOnce(0); const result = (await callHandler()) as Record; expect(result.postsCount).toBe(0); }); }); describe("followingCount", () => { it("returns followingCount from userFollow.count", async () => { mocks.communityPost.count.mockResolvedValueOnce(2); mocks.userFollow.count.mockResolvedValueOnce(7); const result = (await callHandler()) as Record; expect(result.followingCount).toBe(7); expect(mocks.userFollow.count).toHaveBeenCalledWith({ where: { followerId: "user-1" }, }); }); }); describe("followersCount", () => { it("passes through denormalized followersCount from profile row", async () => { mocks.communityPost.count.mockResolvedValueOnce(0); mocks.userFollow.count.mockResolvedValueOnce(0); const result = (await callHandler()) as Record; expect(result.followersCount).toBe(3); }); });