79 lines
2.3 KiB
Python
79 lines
2.3 KiB
Python
"""calculator/core.py — saju-web TS 엔진과 1:1 매칭 검증.
|
|
|
|
fixtures/reference_saju.json 의 30 case (TS calculateSaju 결과)와 Python 구현 일치 여부 검증.
|
|
"""
|
|
import json
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
from app.calculator.core import calculate_saju
|
|
|
|
|
|
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'].get('hour')}-{c['input']['gender']}",
|
|
)
|
|
def test_calculate_saju_matches_reference(case):
|
|
inp = case["input"]
|
|
expected = _normalize(case["expected"]["saju"])
|
|
|
|
actual = calculate_saju(
|
|
inp["year"], inp["month"], inp["day"],
|
|
inp.get("hour"), inp["gender"],
|
|
)
|
|
|
|
# 4기둥 비교
|
|
for pillar in ["year", "month", "day", "hour"]:
|
|
if expected.get(pillar) is None:
|
|
assert actual.get(pillar) is None, f"{pillar} should be None"
|
|
continue
|
|
if pillar == "hour" and inp.get("hour") is None:
|
|
assert actual.get(pillar) is None
|
|
continue
|
|
ep = expected[pillar]
|
|
ap = actual[pillar]
|
|
for field in ["stem", "branch", "stem_kr", "branch_kr", "element", "ten_god", "fortune"]:
|
|
assert ap[field] == ep[field], (
|
|
f"{pillar}.{field}: actual={ap[field]} expected={ep[field]} "
|
|
f"(input={inp})"
|
|
)
|
|
|
|
# day_stem
|
|
assert actual["day_stem"] == expected["day_stem"]
|
|
|
|
# gender
|
|
assert actual["gender"] == expected["gender"]
|
|
|
|
# birth_date
|
|
bd = actual["birth_date"]
|
|
assert bd["year"] == inp["year"]
|
|
assert bd["month"] == inp["month"]
|
|
assert bd["day"] == inp["day"]
|
|
if inp.get("hour") is not None:
|
|
assert bd.get("hour") == inp["hour"]
|