- gameRepository에 날짜 키 공유 캐시(MemCache, 15초) 추가 — 날짜별 경기 목록은 유저 독립 전역 데이터라 요청 간 공유할 수 있다. GET /prediction/games가 요청마다 range 쿼리를 재발행하던 것을 인스턴스당 분당 수 회로 수렴시킨다 - games 문서 쓰기 경로에 무효화 연결: syncGamesForMonth는 여러 날짜에 걸치므로 전량 폐기, forceSyncDay는 해당 날짜만 - createGameDayCache가 공유 캐시를 경유하도록 변경 — 요청마다 새로 만드는 짧은 수명 인스턴스도 실제 쿼리를 유발하지 않는다 - predictionHandlers의 games 분기에 Cache-Control public max-age=15 추가(무인증·전 유저 공통 응답이라 CDN·브라우저 중복 제거가 가능한데 그동안 헤더가 없었다) - MemCache.getOrFetch가 truthy 대신 히트 여부로 판정하도록 수정 — 캐시된 null이 미스로 취급돼 fetcher가 매번 재실행되던 문제(존재하지 않는 상품 조회가 요청마다 Firestore read 유발) - MemCache에 peek·clear 추가, 단위 테스트 6건 신설
71 lines
2.3 KiB
TypeScript
71 lines
2.3 KiB
TypeScript
import { beforeEach, describe, expect, it } from "vitest";
|
|
import { Timestamp } from "firebase-admin/firestore";
|
|
import { firestore } from "../../src/firebase";
|
|
import {
|
|
getGame,
|
|
invalidateAllGameDays,
|
|
listByDate,
|
|
} from "../../src/repositories/gameRepository";
|
|
import type { DateString } from "../../src/types/dateString";
|
|
|
|
describe("gameRepository", () => {
|
|
beforeEach(async () => {
|
|
await firestore.recursiveDelete(firestore.collection("games"));
|
|
invalidateAllGameDays();
|
|
});
|
|
|
|
it("존재하지 않는 경기는 null을 반환한다", async () => {
|
|
const result = await getGame("nonexistent");
|
|
expect(result).toBeNull();
|
|
});
|
|
|
|
describe("listByDate", () => {
|
|
it("지정한 KST 날짜의 경기만 시간순으로 반환한다", async () => {
|
|
// 2026-04-12 18:30 KST = 2026-04-12 09:30 UTC
|
|
const game1 = {
|
|
time: Timestamp.fromDate(new Date("2026-04-12T09:30:00.000Z")),
|
|
stadium: "잠실",
|
|
status: "scheduled" as const,
|
|
homeTeamCode: "LG",
|
|
awayTeamCode: "HT",
|
|
};
|
|
// 2026-04-12 14:00 KST = 2026-04-12 05:00 UTC (먼저)
|
|
const game2 = {
|
|
time: Timestamp.fromDate(new Date("2026-04-12T05:00:00.000Z")),
|
|
stadium: "문학",
|
|
status: "scheduled" as const,
|
|
homeTeamCode: "SK",
|
|
awayTeamCode: "WO",
|
|
};
|
|
// 2026-04-11 18:30 KST = 범위 밖
|
|
const game3 = {
|
|
time: Timestamp.fromDate(new Date("2026-04-11T09:30:00.000Z")),
|
|
stadium: "사직",
|
|
status: "completed" as const,
|
|
homeTeamCode: "LT",
|
|
awayTeamCode: "KT",
|
|
};
|
|
|
|
await firestore.collection("games").doc("g1").set(game1);
|
|
await firestore.collection("games").doc("g2").set(game2);
|
|
await firestore.collection("games").doc("g3").set(game3);
|
|
|
|
const result = await listByDate("2026-04-12" as DateString);
|
|
expect(result.map((g) => g.gameId)).toEqual(["g2", "g1"]);
|
|
});
|
|
});
|
|
|
|
it("저장된 경기를 정상적으로 조회한다", async () => {
|
|
const gameId = "20260412HTLG0";
|
|
const gameData = {
|
|
date: "2026-04-12",
|
|
homeTeam: "LG",
|
|
awayTeam: "KIA",
|
|
status: "scheduled",
|
|
};
|
|
await firestore.collection("games").doc(gameId).set(gameData);
|
|
|
|
const result = await getGame(gameId);
|
|
expect(result).toMatchObject(gameData);
|
|
});
|
|
}); |