from __future__ import annotations import unittest from backend.features.heaven.engine import build_five_phase_field class FivePhaseFrameworkTests(unittest.TestCase): def test_public_field_uses_year_current_qi_day_contract(self): field = build_five_phase_field("2026-06-15") self.assertEqual( field["framework"]["weights"], { "year_movement": 30, "sitian_zaiquan": 20, "sitian": 15, "zaiquan": 5, "host_qi": 20, "guest_qi": 25, "day": 5, }, ) self.assertEqual( [(layer["id"], layer["weight"]) for layer in field["framework"]["layers"]], [("year", 50), ("current", 45), ("day", 5)], ) self.assertEqual(sum(item["score"] for item in field["balance"]), 100) def test_sitian_and_zaiquan_follow_half_year_dominance(self): first_half = build_five_phase_field("2026-06-15") second_half = build_five_phase_field("2026-08-20") self.assertEqual(first_half["framework"]["weights"]["sitian"], 15) self.assertEqual(first_half["framework"]["weights"]["zaiquan"], 5) self.assertEqual(first_half["six_qi"]["ruling"], "司天") self.assertEqual(second_half["framework"]["weights"]["sitian"], 5) self.assertEqual(second_half["framework"]["weights"]["zaiquan"], 15) self.assertEqual(second_half["six_qi"]["ruling"], "在泉") def test_guest_host_relation_and_anchor_alignment_are_explicit(self): third_qi = build_five_phase_field("2026-06-15") final_qi = build_five_phase_field("2026-12-10") controlled = build_five_phase_field("2025-02-10") self.assertEqual(third_qi["framework"]["relations"]["guest_host"]["label"], "客主同气") self.assertEqual(third_qi["six_qi"]["alignment"], "司天同位") self.assertEqual(final_qi["framework"]["relations"]["guest_host"]["label"], "客生主") self.assertEqual(final_qi["six_qi"]["alignment"], "在泉同位") self.assertEqual(controlled["framework"]["relations"]["guest_host"]["order"], "客胜为从") def test_tianfu_and_suihui_use_traditional_year_positions(self): taiyi = build_five_phase_field("2038-06-15") non_suihui = build_five_phase_field("2022-06-15") self.assertEqual( taiyi["framework"]["relations"]["annual_pattern"]["primary"], "太乙天符", ) self.assertFalse( non_suihui["framework"]["relations"]["annual_pattern"]["is_suihui"] ) def test_public_field_has_no_observation_hour(self): field = build_five_phase_field("2026-07-18") self.assertNotIn("time", field["pillars"]) self.assertNotIn("observation_time", field) def test_sector_catalog_lists_all_rules_and_applies_manual_overrides(self): field = build_five_phase_field( "2026-07-18", {"电力": "水", "低空经济": "木"}, ) groups = {item["element"]: item["industries"] for item in field["sector_catalog"]} names = { element: {item["name"]: item["classification_source"] for item in items} for element, items in groups.items() } self.assertEqual(set(groups), {"木", "火", "土", "金", "水"}) self.assertNotIn("电力", names["火"]) self.assertEqual(names["水"]["电力"], "manual") self.assertEqual(names["木"]["低空经济"], "manual") self.assertEqual(sum(len(items) for items in groups.values()), 144) if __name__ == "__main__": unittest.main()