import { beforeAll, describe, expect, it } from "vitest"; import { Timestamp } from "firebase-admin/firestore"; import { firestore } from "../../src/firebase"; import { listAdminUsers, searchAdminUsers } from "../../src/services/adminUserService"; // 다른 테스트가 만드는 users 문서와 섞이지 않도록 고유 접두어를 쓴다. const PREFIX = "검색전용"; const seedUsers = [ { uid: "admin-user-search-test-1", displayName: `${PREFIX}호랑이`, email: "search-test-1@example.com", provider: "google", active: true, createdAt: Timestamp.fromDate(new Date("2026-07-01T00:00:00Z")), }, { uid: "admin-user-search-test-2", displayName: `${PREFIX}독수리`, email: "search-test-2@example.com", provider: "apple", photoUrl: "https://example.com/p.png", active: false, deactivatedAt: Timestamp.fromDate(new Date("2026-07-10T00:00:00Z")), createdAt: Timestamp.fromDate(new Date("2026-07-02T00:00:00Z")), }, ] as const; beforeAll(async () => { for (const { uid, ...doc } of seedUsers) { await firestore.doc(`users/${uid}`).set(doc); } }); describe("searchAdminUsers", () => { it("displayName 접두어로 검색되고 날짜는 UTC ISO 문자열로 나온다", async () => { const results = await searchAdminUsers(PREFIX, 10); const uids = results.map((u) => u.uid); expect(uids).toContain("admin-user-search-test-1"); expect(uids).toContain("admin-user-search-test-2"); for (const dto of results) { // Timestamp 인스턴스/_seconds 누출 금지 — 어드민 웹이 브라우저에서 그대로 소비한다. expect(typeof dto.createdAt).toBe("string"); expect(dto.createdAt?.endsWith("Z")).toBe(true); } }); it("비활성 유저도 검색되며 active=false 로 표시된다", async () => { const results = await searchAdminUsers(`${PREFIX}독수리`, 10); expect(results).toHaveLength(1); expect(results[0]).toMatchObject({ uid: "admin-user-search-test-2", active: false, photoUrl: "https://example.com/p.png", }); expect(typeof results[0].deactivatedAt).toBe("string"); }); it("uid 정확 일치로도 찾는다", async () => { const results = await searchAdminUsers("admin-user-search-test-1", 10); expect(results[0]?.uid).toBe("admin-user-search-test-1"); expect(results[0]?.displayName).toBe(`${PREFIX}호랑이`); }); it("빈 q 는 400", async () => { await expect(searchAdminUsers(" ", 10)).rejects.toMatchObject({ status: 400 }); }); }); describe("listAdminUsers", () => { it("가입일 내림차순 + 커서 페이지네이션", async () => { const first = await listAdminUsers(1); expect(first.items).toHaveLength(1); expect(first.cursor).toBe(first.items[0].uid); const second = await listAdminUsers(1, first.cursor ?? undefined); if (second.items.length > 0) { const firstDate = first.items[0].createdAt ?? ""; const secondDate = second.items[0].createdAt ?? ""; expect(secondDate <= firstDate).toBe(true); expect(second.items[0].uid).not.toBe(first.items[0].uid); } }); });