diff --git a/tests/kbo/schedule.test.ts b/tests/kbo/schedule.test.ts new file mode 100644 index 0000000..6536bb8 --- /dev/null +++ b/tests/kbo/schedule.test.ts @@ -0,0 +1,91 @@ +import { beforeAll, describe, expect, it } from "vitest"; +import { + fetchSchedule, + type ScheduleGame, +} from "../../src/kbo/schedule"; + +// 실제 KBO `fetchSchedule` (cli `npx tsx src/kbo/cli.ts schedule 2026 4 --json`이 쓰는 동일 경로)을 +// 호출하여, 알려진 회귀 케이스가 정확히 파싱되는지 검증한다. 과거 경기는 결과가 불변이라 결정론적. + +const TIMEOUT_MS = 30_000; + +describe("fetchSchedule (실제 KBO 호출) — 케이스별 회귀", () => { + let april: ScheduleGame[]; + let may: ScheduleGame[]; + + beforeAll(async () => { + [april, may] = await Promise.all([ + fetchSchedule({ year: 2026, month: 4 }).then((r) => r.games), + fetchSchedule({ year: 2026, month: 5 }).then((r) => r.games), + ]); + }, TIMEOUT_MS); + + function find(games: ScheduleGame[], gameId: string): ScheduleGame { + const g = games.find((x) => x.gameId === gameId); + if (!g) throw new Error(`gameId ${gameId} not found in fetched schedule`); + return g; + } + + it("win/lose: 20260401LTNC0 (LT 4 @ NC 5) → completed, score 정확", () => { + const g = find(april, "20260401LTNC0"); + expect(g.status).toBe("completed"); + expect(g.awayTeamCode).toBe("LT"); + expect(g.homeTeamCode).toBe("NC"); + expect(g.awayScore).toBe(4); + expect(g.homeScore).toBe(5); + expect(g.note).toBe("-"); + }); + + it("draw(4월): 20260426LTHT0 (LT 5 @ HT 5) → completed, 동점 score 파싱 (parsePlayCell 회귀)", () => { + const g = find(april, "20260426LTHT0"); + expect(g.status).toBe("completed"); + expect(g.awayTeamCode).toBe("LT"); + expect(g.homeTeamCode).toBe("HT"); + expect(g.awayScore).toBe(5); + expect(g.homeScore).toBe(5); + }); + + it("draw(5월): 20260505NCSK0 (NC 7 @ SSG 7) → completed, 동점 score 파싱", () => { + const g = find(may, "20260505NCSK0"); + expect(g.status).toBe("completed"); + expect(g.awayTeamCode).toBe("NC"); + expect(g.homeTeamCode).toBe("SK"); + expect(g.awayScore).toBe(7); + expect(g.homeScore).toBe(7); + }); + + it("cancelled: 20260409WOOB0 (우천취소) → status=cancelled, score=null", () => { + const g = find(april, "20260409WOOB0"); + expect(g.status).toBe("cancelled"); + expect(g.awayTeamCode).toBe("WO"); + expect(g.homeTeamCode).toBe("OB"); + expect(g.awayScore).toBeNull(); + expect(g.homeScore).toBeNull(); + expect(g.note).toContain("우천"); + }); + + it("scheduled(미래): 20260507OBLG0 → status=scheduled, score=null", () => { + const g = find(may, "20260507OBLG0"); + expect(g.status).toBe("scheduled"); + expect(g.awayScore).toBeNull(); + expect(g.homeScore).toBeNull(); + }); + + it("4월 모든 completed 경기: score는 null 아님 (무승부 포함 회귀)", () => { + const completed = april.filter((g) => g.status === "completed"); + expect(completed.length).toBeGreaterThan(0); + for (const g of completed) { + expect(g.awayScore, `${g.gameId} awayScore`).not.toBeNull(); + expect(g.homeScore, `${g.gameId} homeScore`).not.toBeNull(); + } + }); + + it("4월 모든 cancelled 경기: score는 null이어야 함", () => { + const cancelled = april.filter((g) => g.status === "cancelled"); + expect(cancelled.length).toBeGreaterThan(0); + for (const g of cancelled) { + expect(g.awayScore, `${g.gameId} awayScore`).toBeNull(); + expect(g.homeScore, `${g.gameId} homeScore`).toBeNull(); + } + }); +});