Omit undefined reversalOf in admin point ledger writes
- reversalOf 미지정 시 undefined가 원장 문서 스프레드로 흘러가 Firestore가 "Cannot use undefined as a Firestore value"로 지급 전체를 실패시키던 버그 수정 - 조건부 스프레드로 값이 있을 때만 필드를 포함하도록 변경 - 회귀 테스트 추가: reversalOf 없이 지급 시 원장에 키 자체가 없음 + 명시 시 그대로 기록됨 (에뮬레이터 vitest 2건 통과)
This commit is contained in:
parent
c635f16952
commit
5508d2581a
@ -2,4 +2,4 @@ import { firestore } from "../firebase";
|
|||||||
import { HttpError } from "../middleware/errors";
|
import { HttpError } from "../middleware/errors";
|
||||||
import { PointLedgerType } from "../types/points";
|
import { PointLedgerType } from "../types/points";
|
||||||
import { applyPointChangesTx } from "./pointService";
|
import { applyPointChangesTx } from "./pointService";
|
||||||
export async function adminPointChange(uid: string, amount: number, kind: "credit" | "debit", clientIdempotencyKey: string, reason: string, actor: string, reversalOf?: string) { if (!/^[A-Za-z0-9_-]{1,100}$/.test(clientIdempotencyKey) || !Number.isSafeInteger(amount) || amount <= 0 || typeof reason !== "string" || reason.length < 1 || reason.length > 500) throw new HttpError(400, "invalid admin point change", "INVALID_INPUT"); return firestore.runTransaction((tx) => applyPointChangesTx(tx, uid, [{ txId: `${uid}:admin:${clientIdempotencyKey}`, type: kind === "credit" ? PointLedgerType.AdminCredit : PointLedgerType.AdminDebit, amount, adminReason: reason, adminActor: actor, reversalOf }])); }
|
export async function adminPointChange(uid: string, amount: number, kind: "credit" | "debit", clientIdempotencyKey: string, reason: string, actor: string, reversalOf?: string) { if (!/^[A-Za-z0-9_-]{1,100}$/.test(clientIdempotencyKey) || !Number.isSafeInteger(amount) || amount <= 0 || typeof reason !== "string" || reason.length < 1 || reason.length > 500) throw new HttpError(400, "invalid admin point change", "INVALID_INPUT"); return firestore.runTransaction((tx) => applyPointChangesTx(tx, uid, [{ txId: `${uid}:admin:${clientIdempotencyKey}`, type: kind === "credit" ? PointLedgerType.AdminCredit : PointLedgerType.AdminDebit, amount, adminReason: reason, adminActor: actor, ...(reversalOf !== undefined ? { reversalOf } : {}) }])); }
|
||||||
|
|||||||
28
tests/services/adminPointService.test.ts
Normal file
28
tests/services/adminPointService.test.ts
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
import { describe, expect, it } from "vitest";
|
||||||
|
import { firestore } from "../../src/firebase";
|
||||||
|
import { adminPointChange } from "../../src/services/adminPointService";
|
||||||
|
|
||||||
|
const uid = "admin-point-service-test-user";
|
||||||
|
|
||||||
|
describe("adminPointChange", () => {
|
||||||
|
it("reversalOf 없이 지급해도 원장 문서에 undefined 키가 들어가지 않는다", async () => {
|
||||||
|
// 어드민 웹의 일반 지급은 reversalOf 를 보내지 않는다 — undefined 가 Firestore 까지
|
||||||
|
// 흘러가면 "Cannot use undefined as a Firestore value" 로 지급 전체가 실패한다.
|
||||||
|
const key = `no-reversal-${Date.now()}`;
|
||||||
|
const wallet = await adminPointChange(uid, 100, "credit", key, "테스트 지급", "admin-uid");
|
||||||
|
|
||||||
|
expect(wallet?.availableBalance).toBeGreaterThanOrEqual(100);
|
||||||
|
const entry = await firestore.doc(`users/${uid}/pointLedger/${uid}:admin:${key}`).get();
|
||||||
|
expect(entry.exists).toBe(true);
|
||||||
|
expect(entry.data()).not.toHaveProperty("reversalOf");
|
||||||
|
expect(entry.data()).toMatchObject({ adminReason: "테스트 지급", adminActor: "admin-uid", amount: 100 });
|
||||||
|
});
|
||||||
|
|
||||||
|
it("reversalOf 를 명시하면 원장에 그대로 기록된다", async () => {
|
||||||
|
const key = `with-reversal-${Date.now()}`;
|
||||||
|
await adminPointChange(uid, 50, "credit", key, "정정 지급", "admin-uid", "prev-tx-id");
|
||||||
|
|
||||||
|
const entry = await firestore.doc(`users/${uid}/pointLedger/${uid}:admin:${key}`).get();
|
||||||
|
expect(entry.data()).toMatchObject({ reversalOf: "prev-tx-id" });
|
||||||
|
});
|
||||||
|
});
|
||||||
Loading…
x
Reference in New Issue
Block a user