v3.6.0 同步发版

- 交互契约改对话式询问(禁点选工具、一次1~2问、先铺垫再问),规则库 3.6
- 盘面图默认生成+chart_shot 截图;品牌区、大运/流年小卡信息补全、交流群二维码卡
- day_boundary 子正换日;memory_save --update-chart 换盘留痕;share_export 事件简介+城市级地域
- share_push 失败诊断+代理引导;_runtime_guard;修 STAR_ORDER 三奇贵人 KeyError
- README 规则计数 541→542;manifest 重建(1275 文件);测试 142 项全绿
This commit is contained in:
2026-10-10 13:13:41 +08:00
parent f725df8ff6
commit 433c9b4a41
32 changed files with 1468 additions and 389 deletions
+107 -10
View File
@@ -60,19 +60,116 @@ class HtmlRenderTest(unittest.TestCase):
for sect in ("排盘", "大运", "流年", "流月", "空亡", "神煞", "纳音"):
self.assertIn(sect, html)
def test_brand_head_embedded(self):
"""顶部品牌区:logo 与 LogoFont 均 base64 内嵌,口号在。"""
from render.html import build_html
html = build_html(_chart())
self.assertIn("灵爻妙解", html)
self.assertIn("传承易学智慧", html)
self.assertIn('class="brand-head"', html)
self.assertIn("data:image/png;base64,", html)
self.assertIn("@font-face", html)
self.assertIn("data:font/ttf;base64,", html)
def test_qrcode_card(self):
"""尾部交流群卡:二维码 jpeg base64 内嵌,标题与正文文案各出现一次。"""
from render.html import build_html
html = build_html(_chart())
self.assertIn('class="card qrcard"', html)
self.assertIn("data:image/jpeg;base64,", html)
self.assertEqual(html.count("灵爻妙解 · 传统文化交流群"), 1)
self.assertEqual(
html.count("扫码加入微信群:反馈解读偏差、交流文化心得、"
"参与档案共建,版本更新动态第一时间同步。"), 1)
def test_audit_versions_only_in_comment(self):
"""技术信息并入用户信息卡:展示行删版本段,完整原文留在 HTML 注释。"""
from render.html import build_html
html = build_html(_chart())
self.assertEqual(html.count("tyme4py"), 1)
self.assertEqual(html.count("schema"), 1)
m = re.search(r"<!--[^>]*tyme4py[^>]*-->", html)
self.assertIsNotNone(m, "注释中应保留含 tyme4py/schema 的完整审计原文")
self.assertIn("schema", m.group(0))
self.assertIn("时区", m.group(0))
self.assertIn("月令", html)
self.assertIn("日界 子初换日(23:00)", html)
def test_shensha_notes_stripped(self):
"""神煞括号注释在装配层剔除(全角/半角),Markdown 侧不受影响。"""
from render.html import _strip_sha_note, build_html
self.assertEqual(_strip_sha_note("将星(以年支子查,见于月支子)"), "将星")
self.assertEqual(_strip_sha_note("天乙贵人(以日干查)"), "天乙贵人")
self.assertEqual(_strip_sha_note("驿马"), "驿马")
html = build_html(_chart())
self.assertNotIn("(以", html)
def test_luck_flow_card_removed(self):
"""「大运 · 流年」对照卡已整张删除;排盘卡仍只含四柱。"""
from render.html import build_html
html = build_html(_chart())
self.assertNotIn("大运 · 流年", html)
self.assertNotIn("当前一步大运与本年流年对照", html)
self.assertNotIn("LUCK_FLOW_TABLE", html)
pan_seg = html[html.find("排盘<span"):html.find('class="sect">大运')]
self.assertIn("年柱", pan_seg)
self.assertNotIn("大运", pan_seg)
self.assertNotIn("流年", pan_seg)
def test_luck_strip_enriched(self):
"""大运格:起运年+干支之外带十神·星运/空亡/纳音/神煞(2018 甲申运对拍)。"""
from render.html import build_html
html = build_html(_chart())
seg = html[html.find('class="sect">大运'):html.find('class="sect">流年')]
self.assertIn("2018 · 28 岁起", seg)
self.assertIn("正官 · 沐浴", seg) # 甲对日干己 / 己对申
self.assertIn("午未", seg) # 甲申旬空
self.assertIn("泉中水", seg) # 甲申纳音
self.assertIn("天乙贵人 · 金舆 · 驿马 · 劫煞 · 孤辰", seg) # 甲申岁运神煞
self.assertIn('class="cell cur"', seg) # 当前一步高亮
def test_flow_year_strip_enriched(self):
"""流年格:年份+干支之外带主星/星运/空亡/纳音/神煞(2026 丙午对拍)。"""
from render.html import build_html
html = build_html(_chart())
seg = html[html.find('class="sect">流年'):html.find('class="sect">流月')]
self.assertIn("正印 · 临官", seg) # 丙对日干己 / 己对午
self.assertIn("寅卯", seg) # 丙午旬空(甲辰旬)
self.assertIn("天河水", seg) # 丙午纳音
self.assertIn("天德合 · 禄神 · 将星", seg) # 丙午岁运神煞
self.assertIn('class="cell cur"', seg) # 本年高亮
def test_strips_have_no_field_labels(self):
"""条带小卡只排值:不出现「主星/星运/空亡/纳音/神煞」字段标签。"""
from render.html import build_html
html = build_html(_chart())
segs = [html[html.find('class="sect">大运'):html.find('class="sect">流年')],
html[html.find('class="sect">流年'):html.find('class="sect">流月')]]
for seg in segs:
for label in ("主星", "星运", "空亡", "纳音", "神煞"):
self.assertNotIn(label, seg)
def test_ten_god_table(self):
from render.html import _ten_god
self.assertEqual(_ten_god("庚", "丁"), "正官")
self.assertEqual(_ten_god("庚", "丙"), "七杀")
self.assertEqual(_ten_god("庚", "辛"), "劫财")
self.assertEqual(_ten_god("庚", "壬"), "食神")
from chart.ganzhi import ten_god
self.assertEqual(ten_god("庚", "丁"), "正官")
self.assertEqual(ten_god("庚", "丙"), "七杀")
self.assertEqual(ten_god("庚", "辛"), "劫财")
self.assertEqual(ten_god("庚", "壬"), "食神")
def test_void_and_terrain(self):
from render.html import _gz_void, _terrain
self.assertEqual(_gz_void("庚子"), "辰巳")
self.assertEqual(_gz_void("甲子"), "戌亥")
self.assertEqual(_terrain("庚", "巳"), "长生")
self.assertEqual(_terrain("庚", "子"), "死")
from chart.ganzhi import gz_void, terrain
self.assertEqual(gz_void("庚子"), "辰巳")
self.assertEqual(gz_void("甲子"), "戌亥")
self.assertEqual(terrain("庚", "巳"), "长生")
self.assertEqual(terrain("庚", "子"), "死")
def test_nayin_shared_with_shensha_module(self):
"""纳音表集中于 chart/ganzhi.py,chart/shensha.py 复用同一份。"""
from chart.ganzhi import NAYIN
from chart.shensha import NAYIN as SHA_NAYIN
self.assertIs(NAYIN, SHA_NAYIN)
self.assertEqual(NAYIN["甲辰"], "覆灯火")
self.assertEqual(NAYIN["丙午"], "天河水")
class CastHtmlE2ETest(unittest.TestCase):