from __future__ import annotations from core.divination.derivation import ( _get_kong_wang, _resolve_liu_shou, _wu_xing_status, derive_divination, ) from schemas.domain.divination import DivinationPayload, DivinationMethod, YaoType class TestKongWangCalculation: def test_kong_wang_jia_zi(self) -> None: assert _get_kong_wang("甲子") == "戌亥" def test_kong_wang_jia_xu(self) -> None: assert _get_kong_wang("甲戌") == "申酉" def test_kong_wang_jia_shen(self) -> None: assert _get_kong_wang("甲申") == "午未" def test_kong_wang_jia_wu(self) -> None: assert _get_kong_wang("甲午") == "辰巳" def test_kong_wang_jia_chen(self) -> None: assert _get_kong_wang("甲辰") == "寅卯" def test_kong_wang_jia_yin(self) -> None: assert _get_kong_wang("甲寅") == "子丑" class TestLiuShouCalculation: def test_liu_shou_jia_yi(self) -> None: assert _resolve_liu_shou("甲") == ("龙", "雀", "勾", "蛇", "虎", "玄") assert _resolve_liu_shou("乙") == ("龙", "雀", "勾", "蛇", "虎", "玄") def test_liu_shou_bing_ding(self) -> None: assert _resolve_liu_shou("丙") == ("雀", "勾", "蛇", "虎", "玄", "龙") assert _resolve_liu_shou("丁") == ("雀", "勾", "蛇", "虎", "玄", "龙") def test_liu_shou_wu(self) -> None: assert _resolve_liu_shou("戊") == ("勾", "蛇", "虎", "玄", "龙", "雀") def test_liu_shou_ji(self) -> None: assert _resolve_liu_shou("己") == ("蛇", "虎", "玄", "龙", "雀", "勾") def test_liu_shou_geng_xin(self) -> None: assert _resolve_liu_shou("庚") == ("虎", "玄", "龙", "雀", "勾", "蛇") assert _resolve_liu_shou("辛") == ("虎", "玄", "龙", "雀", "勾", "蛇") def test_liu_shou_ren_gui(self) -> None: assert _resolve_liu_shou("壬") == ("玄", "龙", "雀", "勾", "蛇", "虎") assert _resolve_liu_shou("癸") == ("玄", "龙", "雀", "勾", "蛇", "虎") class TestWuXingStatus: def test_wood_wang_in_yin_mao(self) -> None: assert _wu_xing_status("寅", "木") == "旺" assert _wu_xing_status("卯", "木") == "旺" def test_fire_wang_in_si_wu(self) -> None: assert _wu_xing_status("巳", "火") == "旺" assert _wu_xing_status("午", "火") == "旺" def test_metal_wang_in_shen_you(self) -> None: assert _wu_xing_status("申", "金") == "旺" assert _wu_xing_status("酉", "金") == "旺" def test_water_wang_in_hai_zi(self) -> None: assert _wu_xing_status("亥", "水") == "旺" assert _wu_xing_status("子", "水") == "旺" def test_earth_wang_in_chen_wei_chou_xu(self) -> None: assert _wu_xing_status("辰", "土") == "旺" assert _wu_xing_status("未", "土") == "旺" assert _wu_xing_status("戌", "土") == "旺" assert _wu_xing_status("丑", "土") == "旺" class TestKongWangFromDayOnly: def test_kong_wang_only_from_day_gan_zhi(self) -> None: payload = DivinationPayload( divinationMethod=DivinationMethod.MANUAL, questionType="测试", question="测试空亡仅从日柱", divinationTimeIso="2025-01-15T12:00:00+08:00", yaoLines=[ YaoType.SHAO_YIN, YaoType.SHAO_YIN, YaoType.SHAO_YANG, YaoType.SHAO_YIN, YaoType.SHAO_YIN, YaoType.SHAO_YANG, ], ) result = derive_divination(payload) assert result.ganzhi.day_kong_wang == "午未" assert result.ganzhi.time_kong_wang == "戌亥" kong_wang_yao = [s for s in result.special_status if "旬空" in s] for status in kong_wang_yao: if "午" in status or "未" in status: pass else: assert "戌" not in status assert "亥" not in status class TestAnDongLogic: def test_an_dong_wang_xiang_ri_chong(self) -> None: payload = DivinationPayload( divinationMethod=DivinationMethod.MANUAL, questionType="测试", question="测试暗动", divinationTimeIso="2025-05-15T12:00:00+08:00", yaoLines=[ YaoType.SHAO_YIN, YaoType.SHAO_YIN, YaoType.SHAO_YANG, YaoType.SHAO_YIN, YaoType.SHAO_YIN, YaoType.SHAO_YANG, ], ) result = derive_divination(payload) an_dong_status = [s for s in result.special_status if "暗动" in s] for status in an_dong_status: assert "旺相" in status or "日冲" in status def test_changing_yao_not_marked_as_kong_wang(self) -> None: payload = DivinationPayload( divinationMethod=DivinationMethod.MANUAL, questionType="测试", question="测试动爻不标空亡", divinationTimeIso="2025-01-15T12:00:00+08:00", yaoLines=[ YaoType.SHAO_YANG, YaoType.SHAO_YANG, YaoType.LAO_YANG, YaoType.SHAO_YANG, YaoType.SHAO_YANG, YaoType.SHAO_YANG, ], ) result = derive_divination(payload) for yao in result.yao_info_list: if yao.is_changing: for status in result.special_status: if f"第{yao.position}爻" in status: assert "旬空" not in status class TestYuePoLogic: def test_yue_po_independently_marked(self) -> None: payload = DivinationPayload( divinationMethod=DivinationMethod.MANUAL, questionType="测试", question="测试月破", divinationTimeIso="2025-06-15T12:00:00+08:00", yaoLines=[ YaoType.SHAO_YIN, YaoType.SHAO_YIN, YaoType.SHAO_YANG, YaoType.SHAO_YIN, YaoType.SHAO_YIN, YaoType.SHAO_YANG, ], ) result = derive_divination(payload) yue_po_status = [s for s in result.special_status if "月破" in s] for status in yue_po_status: assert "月破" in status assert "暗动" not in status class TestWangBuWeiKong: def test_wang_xiang_yao_not_marked_as_kong_wang(self) -> None: payload = DivinationPayload( divinationMethod=DivinationMethod.MANUAL, questionType="测试", question="测试旺不为空", divinationTimeIso="2025-01-15T12:00:00+08:00", yaoLines=[ YaoType.SHAO_YANG, YaoType.SHAO_YANG, YaoType.SHAO_YANG, YaoType.SHAO_YANG, YaoType.SHAO_YANG, YaoType.SHAO_YANG, ], ) result = derive_divination(payload) for status in result.special_status: if "旬空" in status: if "丑" in status or "土" in status: assert "旬空" not in status or "旺" not in status