"""daeun.py — 대운 8개 계산 검증. fixtures/reference_saju.json 의 30 case (TS) 와 Python 구현 1:1 일치 검증. """ import json from pathlib import Path import pytest from app.calculator.core import calculate_saju from app.calculator.daeun import calculate_daeun, get_current_daeun, get_daeun_description REF_PATH = Path(__file__).parent / "fixtures" / "reference_saju.json" REF = json.loads(REF_PATH.read_text(encoding="utf-8")) def _camel_to_snake(name: str) -> str: out = [] for ch in name: if ch.isupper(): out.append("_" + ch.lower()) else: out.append(ch) return "".join(out) def _normalize(d): """TS camelCase → Python snake_case (deep).""" if isinstance(d, dict): return {_camel_to_snake(k): _normalize(v) for k, v in d.items()} if isinstance(d, list): return [_normalize(x) for x in d] return d @pytest.mark.parametrize( "case", REF, ids=lambda c: f"{c['input']['year']}-{c['input']['month']:02d}-{c['input']['day']:02d}-{c['input']['gender']}", ) def test_calculate_daeun_matches_reference(case): inp = case["input"] expected = [_normalize(x) for x in case["expected"]["daeun"]] saju = calculate_saju( inp["year"], inp["month"], inp["day"], inp.get("hour"), inp["gender"] ) actual = calculate_daeun( inp["year"], inp["month"], inp["day"], inp["gender"], saju["month"]["stem"], saju["month"]["branch"], ) assert len(actual) == len(expected), ( f"len mismatch for {inp}: {len(actual)} vs {len(expected)}" ) for i, (a, e) in enumerate(zip(actual, expected)): for key in ("stem", "branch", "stem_kr", "branch_kr"): assert a.get(key) == e.get(key), ( f"{inp} daeun[{i}].{key}: actual={a.get(key)} expected={e.get(key)}" ) # age + start_year/end_year assert a.get("age") == e.get("age"), ( f"{inp} daeun[{i}].age: {a.get('age')} vs {e.get('age')}" ) assert a.get("start_year") == e.get("start_year"), ( f"{inp} daeun[{i}].start_year: {a.get('start_year')} vs {e.get('start_year')}" ) assert a.get("end_year") == e.get("end_year"), ( f"{inp} daeun[{i}].end_year: {a.get('end_year')} vs {e.get('end_year')}" ) def test_get_current_daeun_returns_match(): daeun_list = [ {"age": 10, "start_year": 2000, "end_year": 2009}, {"age": 20, "start_year": 2010, "end_year": 2019}, {"age": 30, "start_year": 2020, "end_year": 2029}, ] assert get_current_daeun(daeun_list, 2005) == daeun_list[0] assert get_current_daeun(daeun_list, 2015) == daeun_list[1] assert get_current_daeun(daeun_list, 2025) == daeun_list[2] assert get_current_daeun(daeun_list, 1999) is None assert get_current_daeun(daeun_list, 2030) is None def test_get_daeun_description_returns_string(): daeun = { "age": 10, "start_year": 2000, "end_year": 2009, "stem": "壬", "branch": "午", "stem_kr": "임", "branch_kr": "오", } desc = get_daeun_description(daeun, "辛") assert isinstance(desc, str) assert "임오" in desc assert "壬午" in desc assert "10세" in desc assert "19세" in desc