Spaces:
Running
Running
| """核心后端单测 (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() | |