Add end-to-end wire format test for point ledger
- Firestore에 저장된 Timestamp가 DTO를 거쳐 JSON으로 나갈 때까지 전 구간 검증 - 앱 포인트 내역 크래시(_seconds Map을 as String으로 캐스트)의 회귀 방지용 - relatedDate 같은 날짜 전용 필드의 YYYY-MM-DD 형식 유지도 함께 확인
This commit is contained in:
parent
107f75c23a
commit
8cb31b8122
75
tests/repositories/ledgerWireFormat.test.ts
Normal file
75
tests/repositories/ledgerWireFormat.test.ts
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
import { beforeEach, describe, expect, it } from "vitest";
|
||||||
|
import { Timestamp } from "firebase-admin/firestore";
|
||||||
|
import { firestore } from "../../src/firebase";
|
||||||
|
import { listLedger } from "../../src/repositories/pointLedgerRepository";
|
||||||
|
import { getWallet } from "../../src/repositories/walletRepository";
|
||||||
|
import { toLedgerEntryDto, toWalletDto } from "../../src/types/dto/rewardDto";
|
||||||
|
import { PointLedgerType } from "../../src/types/points";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 앱의 "포인트 내역" 화면 크래시 재현 방지.
|
||||||
|
*
|
||||||
|
* Firestore 에 실제로 저장된 Timestamp 를 읽어 응답 DTO 까지 태웠을 때
|
||||||
|
* 와이어에 `{_seconds,_nanoseconds}` 가 나오지 않는지 끝까지 확인한다.
|
||||||
|
* (클라이언트는 이 필드를 `DateTime.parse(... as String)` 로 받으므로
|
||||||
|
* Map 이 오면 캐스트 예외로 화면 전체가 죽는다.)
|
||||||
|
*/
|
||||||
|
|
||||||
|
const uid = "ledger-wire-user";
|
||||||
|
const UTC_ISO = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/;
|
||||||
|
|
||||||
|
describe("포인트 내역 와이어 형식 (Firestore -> DTO)", () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
await firestore.recursiveDelete(firestore.doc(`users/${uid}`));
|
||||||
|
});
|
||||||
|
|
||||||
|
it("저장된 Timestamp 가 UTC ISO 문자열로 나가고 _seconds 가 남지 않는다", async () => {
|
||||||
|
const createdAt = Timestamp.fromDate(new Date("2026-07-20T05:30:00.000Z"));
|
||||||
|
await firestore.doc(`users/${uid}/pointLedger/tx-1`).set({
|
||||||
|
txId: "tx-1",
|
||||||
|
uid,
|
||||||
|
type: PointLedgerType.AttendanceDaily,
|
||||||
|
op: "credit",
|
||||||
|
amount: 20,
|
||||||
|
availableBefore: 0,
|
||||||
|
availableAfter: 20,
|
||||||
|
reservedBefore: 0,
|
||||||
|
reservedAfter: 0,
|
||||||
|
relatedDate: "2026-07-20",
|
||||||
|
createdAt,
|
||||||
|
});
|
||||||
|
|
||||||
|
const page = await listLedger(uid, 20);
|
||||||
|
expect(page.items).toHaveLength(1);
|
||||||
|
|
||||||
|
const dto = page.items.map((entry) => toLedgerEntryDto(entry.id, entry));
|
||||||
|
const json = JSON.stringify({ items: dto, cursor: page.cursor });
|
||||||
|
|
||||||
|
expect(json).not.toContain("_seconds");
|
||||||
|
expect(json).not.toContain("_nanoseconds");
|
||||||
|
expect(JSON.parse(json).items[0].createdAt).toBe("2026-07-20T05:30:00.000Z");
|
||||||
|
// 날짜 전용 필드는 형식이 유지되어야 한다
|
||||||
|
expect(JSON.parse(json).items[0].relatedDate).toBe("2026-07-20");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("지갑 문서의 createdAt/updatedAt 도 UTC ISO 문자열로 나간다", async () => {
|
||||||
|
const now = Timestamp.fromDate(new Date("2026-07-20T05:30:00.000Z"));
|
||||||
|
await firestore.doc(`users/${uid}/wallet/current`).set({
|
||||||
|
availableBalance: 20,
|
||||||
|
reservedBalance: 0,
|
||||||
|
totalEarned: 20,
|
||||||
|
totalSpent: 0,
|
||||||
|
version: 1,
|
||||||
|
createdAt: now,
|
||||||
|
updatedAt: now,
|
||||||
|
});
|
||||||
|
|
||||||
|
const wallet = await getWallet(uid);
|
||||||
|
expect(wallet).not.toBeNull();
|
||||||
|
|
||||||
|
const json = JSON.stringify(toWalletDto(wallet!));
|
||||||
|
expect(json).not.toContain("_seconds");
|
||||||
|
expect(JSON.parse(json).createdAt).toMatch(UTC_ISO);
|
||||||
|
expect(JSON.parse(json).updatedAt).toMatch(UTC_ISO);
|
||||||
|
});
|
||||||
|
});
|
||||||
Loading…
x
Reference in New Issue
Block a user