- Firestore Timestamp가 res.json으로 그대로 나가 {_seconds,_nanoseconds}로 직렬화되던 문제 수정
- 앱의 포인트 내역(/reward/ledger)과 예측 기록(/stats/history) 크래시 원인 제거
- 클라 소비 7개 핸들러(reward/attendance/prediction/user/stats/kbo/chat)의 모든 응답에 명시적 DTO 타입 도입
- 날짜 와이어 형식을 순수 UTC ISO 8601(...Z)로 통일 — 채팅의 기존 KST(+09:00) 출력도 UTC로 전환
- DTO는 필드를 명시적으로 나열해 조립 (스프레드 덤프 제거) — 문서에 새 Timestamp 필드가 생겨도 다시 새지 않는다
- 재사용되는 주문 형태만 toOrderDto로 분리, 나머지는 응답 경계에서 직접 조립
- Firestore 저장 형식은 변경하지 않음. 출석 idempotent 재요청 경로는 저장된 Timestamp/문자열을 모두 처리
- DTO 직렬화 회귀 테스트 추가 (_seconds 부재, UTC ISO 형식, optional 키 생략, 미지 필드 차단)
38 lines
1.4 KiB
TypeScript
38 lines
1.4 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import { Timestamp } from "firebase-admin/firestore";
|
|
import { toIso, toIsoOrUndefined } from "../../src/types/dto/iso";
|
|
|
|
const UTC_ISO = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/;
|
|
|
|
describe("toIso", () => {
|
|
it("Timestamp 를 UTC ISO 문자열로 바꾼다", () => {
|
|
const ts = Timestamp.fromDate(new Date("2026-07-20T05:30:00.000Z"));
|
|
expect(toIso(ts)).toBe("2026-07-20T05:30:00.000Z");
|
|
});
|
|
|
|
it("항상 Z 로 끝나는 UTC 형식을 낸다 (KST 오프셋 없음)", () => {
|
|
const ts = Timestamp.fromDate(new Date("2026-07-20T14:30:00+09:00"));
|
|
const iso = toIso(ts);
|
|
expect(iso).toMatch(UTC_ISO);
|
|
expect(iso).not.toContain("+09:00");
|
|
expect(iso).toBe("2026-07-20T05:30:00.000Z");
|
|
});
|
|
});
|
|
|
|
describe("toIsoOrUndefined", () => {
|
|
it("값이 있으면 toIso 와 같은 결과를 낸다", () => {
|
|
const ts = Timestamp.fromDate(new Date("2026-01-02T03:04:05.678Z"));
|
|
expect(toIsoOrUndefined(ts)).toBe("2026-01-02T03:04:05.678Z");
|
|
});
|
|
|
|
it("undefined / null 이면 undefined 를 낸다", () => {
|
|
expect(toIsoOrUndefined(undefined)).toBeUndefined();
|
|
expect(toIsoOrUndefined(null)).toBeUndefined();
|
|
});
|
|
|
|
it("undefined 필드는 JSON 직렬화에서 키가 사라진다", () => {
|
|
const dto = { at: toIsoOrUndefined(undefined) };
|
|
expect(JSON.parse(JSON.stringify(dto))).toEqual({});
|
|
});
|
|
});
|