rebreak-monorepo/backend/tests/social/profile-counts.test.ts

142 lines
4.0 KiB
TypeScript

/**
* 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<string, unknown>;
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<unknown>)({ 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<string, unknown>;
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<string, unknown>;
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<string, unknown>;
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<string, unknown>;
expect(result.followersCount).toBe(3);
});
});