이전 fixture는 saju-web `getCurrentSolarTerm` 루프 버그로 30 케이스 모두 month branch='丑'으로 고정되어 검증 불가했음. saju-web을 임시 패치(solarlunar.gzMonth 직접 사용)하여 재생성한 뒤 패치는 되돌렸음. 결과: 12개 unique month branch + sxtwl과 49 테스트 전부 PASS. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
117 lines
4.6 KiB
Python
117 lines
4.6 KiB
Python
"""solar_terms.py — sxtwl 기반 24절기 + 月支 매핑 테스트.
|
|
|
|
Reference fixture(`reference_saju.json`)는 saju-web의 solarlunar gzMonth를 직접 사용해
|
|
재생성되었다 (이전 버전은 `getCurrentSolarTerm` 루프 버그로 모든 케이스가 月支='丑'으로 떨어졌음).
|
|
본 테스트는
|
|
- 절기 boundary (입춘 day) 동작 검증
|
|
- sxtwl JieQi index 순서 가정 검증
|
|
- 月支 자체 일관성(여러 날짜에서 기대되는 月支 산출) 검증
|
|
- Reference fixture 30 케이스 sxtwl ↔ solarlunar 일치 검증
|
|
네 가지로 구성한다.
|
|
"""
|
|
import json
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
from app.calculator import solar_terms as st
|
|
|
|
|
|
REF_PATH = Path(__file__).parent / "fixtures" / "reference_saju.json"
|
|
REF = json.loads(REF_PATH.read_text(encoding="utf-8"))
|
|
|
|
BRANCHES = ["子", "丑", "寅", "卯", "辰", "巳", "午", "未", "申", "酉", "戌", "亥"]
|
|
|
|
|
|
def test_jieqi_index_order_lichun_is_3():
|
|
"""sxtwl JieQi index에서 立春은 3(冬至=0 기준 황경 +45°).
|
|
|
|
1995-02-04는 입춘 당일. sxtwl이 그 날짜에 jqIndex=3을 반환해야 함.
|
|
"""
|
|
import sxtwl
|
|
|
|
qi_list = sxtwl.getJieQiByYear(1995)
|
|
# 1995-02-04 ±1일 안에 jqIndex=3인 항목이 있어야 함
|
|
found = False
|
|
for info in qi_list:
|
|
t = sxtwl.JD2DD(info.jd)
|
|
if int(info.jqIndex) == 3 and int(t.Y) == 1995 and int(t.M) == 2 and 3 <= int(t.D) <= 5:
|
|
found = True
|
|
break
|
|
assert found, "1995년 立春(jqIndex=3) 절기가 2/3~2/5 사이에서 발견되지 않음"
|
|
|
|
|
|
def test_get_current_solar_term_ipchun_boundary():
|
|
"""1995-02-04는 입춘 당일."""
|
|
idx_4 = st.get_current_solar_term(1995, 2, 4)
|
|
idx_5 = st.get_current_solar_term(1995, 2, 5)
|
|
idx_3 = st.get_current_solar_term(1995, 2, 3)
|
|
# 2/4와 2/5는 같은 절기 인덱스 (立春=3)
|
|
assert idx_4 == idx_5
|
|
# 2/3은 다른 인덱스 (立春 이전 — 大寒=2)
|
|
assert idx_4 != idx_3
|
|
|
|
|
|
def test_get_solar_term_month_branch_ipchun_day():
|
|
"""1995-02-04는 입춘 → 月支 = 寅(인덱스 2)."""
|
|
branch_idx = st.get_solar_term_month_branch(1995, 2, 4)
|
|
assert BRANCHES[branch_idx] == "寅"
|
|
|
|
|
|
def test_get_solar_term_month_branch_before_ipchun():
|
|
"""1995-02-03은 입춘 이전 → 月支 = 丑(인덱스 1)."""
|
|
branch_idx = st.get_solar_term_month_branch(1995, 2, 3)
|
|
assert BRANCHES[branch_idx] == "丑"
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"year,month,day,expected_branch",
|
|
[
|
|
# 표준 절기 경계 기준 月支 검증 (saju 일반 상식)
|
|
(1990, 5, 15, "巳"), # 立夏(5/6) ~ 芒種(6/6) 사이 → 巳월
|
|
(1990, 6, 6, "午"), # 芒種 당일 → 午월
|
|
(1980, 6, 6, "午"), # 芒種 ~ 小暑 → 午월
|
|
(1995, 2, 5, "寅"), # 立春 다음날 → 寅
|
|
(1995, 2, 3, "丑"), # 立春 이전 → 丑
|
|
(2000, 2, 29, "寅"), # 立春 이후 → 寅
|
|
(2010, 12, 31, "子"), # 大雪 이후 → 子
|
|
(1985, 1, 1, "子"), # 1/1은 小寒(1/5) 이전 → 子
|
|
(1985, 1, 20, "丑"), # 小寒 이후 → 丑
|
|
(1972, 7, 24, "未"), # 小暑 ~ 立秋 사이 → 未
|
|
(1992, 8, 8, "申"), # 立秋 ~ 白露 → 申
|
|
(1988, 9, 9, "酉"), # 白露 ~ 寒露 → 酉
|
|
(2005, 6, 22, "午"), # 芒種 ~ 小暑 → 午
|
|
(2020, 9, 23, "酉"), # 白露 ~ 寒露 → 酉
|
|
],
|
|
)
|
|
def test_month_branch_standard_cases(year, month, day, expected_branch):
|
|
"""일반 사주 상식 기준 月支 검증 — sxtwl 절기와 일치해야 함."""
|
|
branch_idx = st.get_solar_term_month_branch(year, month, day)
|
|
actual = BRANCHES[branch_idx]
|
|
assert actual == expected_branch, (
|
|
f"{year}-{month:02d}-{day:02d}: expected {expected_branch}, got {actual}"
|
|
)
|
|
|
|
|
|
def test_get_days_to_next_solar_term_positive():
|
|
"""다음 절기까지 일수는 항상 양수, 16일 이내."""
|
|
# 立春(2/4) 다음날 → 雨水(2/19)까지 약 15일
|
|
days = st.get_days_to_next_solar_term(1995, 2, 5)
|
|
assert 1 <= days <= 16, f"days = {days} (expected 1~16)"
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"case",
|
|
REF,
|
|
ids=lambda c: f"{c['input']['year']}-{c['input']['month']:02d}-{c['input']['day']:02d}",
|
|
)
|
|
def test_month_branch_matches_reference(case):
|
|
"""Reference fixture의 month branch(solarlunar gzMonth 기준)와 sxtwl 계산 일치."""
|
|
inp = case["input"]
|
|
expected_branch = case["expected"]["saju"]["month"]["branch"]
|
|
branch_idx = st.get_solar_term_month_branch(inp["year"], inp["month"], inp["day"])
|
|
actual_branch = BRANCHES[branch_idx]
|
|
assert actual_branch == expected_branch, (
|
|
f"mismatch for {inp}: got {actual_branch}, expected {expected_branch}"
|
|
)
|