mmday-firebase/tests/repositories/productRepository.test.ts
윤정민 c635f16952 Add admin console read APIs, claim script, and CORS hedge
- 어드민 웹(fanit_admin) 지원: GET products/list(비노출 포함 전체 상품, AdminProductDto)·GET orders/list(전 유저 주문, createdAt desc 커서 페이지네이션) 추가 — Hosting rewrite·직접 호출 양쪽에서 일관되는 2세그먼트 라우트
- 지갑·원장 어드민 응답도 DTO로 감싸 Timestamp({_seconds}) 유출 제거
- admin 함수에 단일 origin CORS 헤지 추가(OPTIONS preflight를 requireAdmin보다 먼저 처리, 에러 응답에도 헤더 적용)
- scripts/set-admin-claim.ts로 운영자 admin 클레임 부여(부여 후 재로그인 필요)
- Storage 규칙: write만 admin 클레임으로 제한, read는 불변(앱 이미지 표시 계약 유지)
- 신규 조회·DTO vitest 테스트 추가
2026-07-21 13:57:42 +09:00

52 lines
1.8 KiB
TypeScript

import { beforeEach, describe, expect, it } from "vitest";
import { Timestamp } from "firebase-admin/firestore";
import { firestore } from "../../src/firebase";
import { listAllProducts, listProducts } from "../../src/repositories/productRepository";
import type { ProductDoc } from "../../src/types/reward";
const now = Timestamp.now();
function makeProduct(overrides: Partial<ProductDoc> = {}): ProductDoc {
return {
name: "상품",
pointPrice: 1000,
active: true,
redeemable: true,
displayOrder: 0,
mainImages: [],
detailImages: [],
createdAt: now,
updatedAt: now,
...overrides,
};
}
describe("productRepository", () => {
beforeEach(async () => {
await firestore.recursiveDelete(firestore.collection("products"));
});
describe("listAllProducts", () => {
it("active/redeemable 필터 없이 비노출 상품도 포함해 전체를 반환한다", async () => {
await firestore.doc("products/exposed").set(makeProduct({ active: true, redeemable: true }));
await firestore.doc("products/hidden-inactive").set(makeProduct({ active: false, redeemable: true }));
await firestore.doc("products/hidden-non-redeemable").set(makeProduct({ active: true, redeemable: false }));
const all = await listAllProducts();
expect(all.map((p) => p.id).sort()).toEqual([
"exposed",
"hidden-inactive",
"hidden-non-redeemable",
]);
});
it("listProducts(소비자용)는 여전히 active+redeemable 필터가 걸려 있다", async () => {
await firestore.doc("products/exposed").set(makeProduct({ active: true, redeemable: true }));
await firestore.doc("products/hidden").set(makeProduct({ active: false, redeemable: true }));
const consumerList = await listProducts();
expect(consumerList.map((p) => p.id)).toEqual(["exposed"]);
});
});
});