"""核心后端单测 (stdlib unittest, 免装依赖): 排盘 / 安全护栏 / 摇卦。 运行: python -m unittest discover -s tests """ import datetime as _dt import sys import unittest from pathlib import Path sys.path.insert(0, str(Path(__file__).resolve().parents[1])) from bazi.engine import compute_five_dim from bazi.five_elements import ELEMENT_ORDER from safety.intercept import check from divination.coin import COIN_SUM_TO_LINE, LINE_VALUE_TO_NAME from divination.hexagram import hex_from_six_lines, get_hexagram class TestEngine(unittest.TestCase): def setUp(self): self.r = compute_five_dim(1990, 5, 15, 14, 30, 1).to_dict() def test_four_pillars(self): self.assertEqual(len(self.r["ming"]["pillars"]), 4) for p in self.r["ming"]["pillars"]: self.assertTrue(p["gan"] and p["zhi"]) def test_zodiac(self): self.assertIn(self.r["ming"]["zodiac"], "鼠牛虎兔龙蛇马羊猴鸡狗猪") def test_dayun_covers_0_to_120(self): stages = self.r["yun"]["dayun_list"] self.assertEqual(stages[0]["start_age"], 0) # 含童限 self.assertGreaterEqual(stages[-1]["end_age"], 120) # 外推至 120 cur = self.r["yun"]["current_index"] self.assertTrue(0 <= cur < len(stages)) def test_element_scores(self): scores = self.r["state"]["element_scores"] self.assertEqual(set(scores), set(ELEMENT_ORDER)) for v in scores.values(): self.assertTrue(8 <= v <= 96) def test_true_solar_time_shifts_hour(self): # 偏离时区中央经线 (120°E) 会改变时辰 → 时柱可能不同 east = compute_five_dim(1990, 5, 15, 23, 50, 1, longitude=120).to_dict() west = compute_five_dim(1990, 5, 15, 23, 50, 1, longitude=75).to_dict() self.assertNotEqual( east["birth"]["solar_corrected"], west["birth"]["solar_corrected"] ) def test_southern_hemisphere_shifts_month(self): north = compute_five_dim(1995, 6, 15, 12, 0, 1, southern=False).to_dict() south = compute_five_dim(1995, 6, 15, 12, 0, 1, southern=True).to_dict() self.assertNotEqual( north["ming"]["pillars"][1]["zhi"], south["ming"]["pillars"][1]["zhi"] ) class TestSafety(unittest.TestCase): def test_crisis_blocked(self): for txt in ("我不想活了", "感觉走投无路", "i want to die"): hit, payload = check(txt) self.assertTrue(hit, txt) self.assertIn("热线", payload) def test_normal_passes(self): for txt in ("今天适合静心读书", "想换个工作", ""): hit, _ = check(txt) self.assertFalse(hit, txt) def test_obfuscation_caught(self): # 空格 / 标点穿透 hit, _ = check("我 想 死") self.assertTrue(hit) class TestDivination(unittest.TestCase): def test_coin_sum_mapping(self): # 三枚铜钱和 6-9 → 老阴/少阳/少阴/老阳 self.assertEqual(set(COIN_SUM_TO_LINE), {6, 7, 8, 9}) for s, v in COIN_SUM_TO_LINE.items(): self.assertIn(LINE_VALUE_TO_NAME[v], ("老阴", "少阳", "少阴", "老阳")) def test_hex_code_range(self): for bits in ([1, 1, 1, 1, 1, 1], [0, 0, 0, 0, 0, 0], [1, 0, 1, 0, 1, 0]): code = hex_from_six_lines(bits) self.assertTrue(0 <= code <= 63) hexagram = get_hexagram(code) self.assertTrue(hexagram.name) def test_qian_kun(self): self.assertEqual(get_hexagram(hex_from_six_lines([1] * 6)).name, "乾为天") self.assertEqual(get_hexagram(hex_from_six_lines([0] * 6)).name, "坤为地") if __name__ == "__main__": unittest.main()