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:
@@ -16,6 +16,10 @@ LOOKUP = SCRIPTS_DIR / "memory_lookup.py"
|
||||
ZHANGSAN = ["--name", "张三", "--gender", "男",
|
||||
"--birth", "1990-05-14 08:30", "--place", "四川省成都市"]
|
||||
|
||||
# 晚子时案例:真太阳时落于 23:00–24:00,子初/子正换日四柱不同
|
||||
WANZI = ["--name", "李四", "--gender", "女",
|
||||
"--birth", "1996-12-08 00:30", "--place", "四川省乐山市"]
|
||||
|
||||
|
||||
def run(script, mem_root, *args, use_env=False):
|
||||
env = dict(os.environ)
|
||||
@@ -117,3 +121,97 @@ class TestLookupNoMatch:
|
||||
def test_no_condition_exit_2(self, tmp_path):
|
||||
r = run(LOOKUP, tmp_path)
|
||||
assert r.returncode == 2
|
||||
|
||||
|
||||
class TestUpdateChart:
|
||||
"""换盘(--update-chart):工作盘唯一——盘面段整体替换为新盘,frontmatter
|
||||
与文件名同步,其余小节保留;旧盘信息由使用方记入反馈与澄清。"""
|
||||
|
||||
def test_zizheng_replaces_chart_and_renames(self, tmp_path):
|
||||
r1 = run(SAVE, tmp_path, *WANZI)
|
||||
assert r1.returncode == 0, f"stderr:\n{r1.stderr}"
|
||||
path = Path(r1.stdout.strip())
|
||||
assert "丙子庚子庚辰丙子" in path.name # 默认子初换日盘
|
||||
|
||||
# 模拟核对后的回写 + 固定 created_at
|
||||
text = path.read_text(encoding="utf-8")
|
||||
text = text.replace("(无)", "- 【已核实】2016 年离乡赴厦门读大学。")
|
||||
text = re.sub(r'^created_at:.*$', 'created_at: "2000-01-01 00:00"',
|
||||
text, count=1, flags=re.M)
|
||||
path.write_text(text, encoding="utf-8")
|
||||
|
||||
# 用户确认子正换日:重排替换盘面段,日柱/时柱变 → 改新主键名
|
||||
r2 = run(SAVE, tmp_path, "--update-chart", str(path),
|
||||
"--day-boundary", "子正")
|
||||
assert r2.returncode == 0, f"stderr:\n{r2.stderr}"
|
||||
assert "日柱 庚辰→己卯" in r2.stderr and "时柱 丙子→甲子" in r2.stderr
|
||||
assert "待复核" in r2.stderr # 变动柱影响条目提示复核
|
||||
new_path = Path(r2.stdout.strip())
|
||||
assert new_path != path and not path.exists()
|
||||
assert "丙子庚子己卯甲子" in new_path.name
|
||||
assert list((tmp_path / "archives").glob("*.md")) == [new_path]
|
||||
|
||||
text = new_path.read_text(encoding="utf-8")
|
||||
meta = read_meta(text)
|
||||
assert meta["pillars"] == '["丙子", "庚子", "己卯", "甲子"]'
|
||||
assert meta["day_master"] == "己"
|
||||
assert meta["created_at"] == "2000-01-01 00:00" # 保留
|
||||
# 盘面段只剩新盘:日界审计与晚子时警示均为子正口径,无旧盘残留
|
||||
assert "- 日界规则:子正换日(真太阳时 00:00)" in text
|
||||
assert "已按「子正换日」定盘" in text
|
||||
assert text.count("# 命盘") == 1
|
||||
chart_section = text.split("## 盘面")[1].split("## 主要关注方向")[0]
|
||||
assert "日柱:己卯" in chart_section and "日柱:庚辰" not in chart_section
|
||||
# 其余小节保留
|
||||
assert "- 【已核实】2016 年离乡赴厦门读大学。" in text
|
||||
|
||||
def test_birth_correction_updates_meta(self, tmp_path):
|
||||
r1 = run(SAVE, tmp_path, *ZHANGSAN)
|
||||
assert r1.returncode == 0, f"stderr:\n{r1.stderr}"
|
||||
path = Path(r1.stdout.strip())
|
||||
|
||||
r2 = run(SAVE, tmp_path, "--update-chart", str(path),
|
||||
"--birth", "1990-05-14 10:30")
|
||||
assert r2.returncode == 0, f"stderr:\n{r2.stderr}"
|
||||
assert "时柱" in r2.stderr and "待复核" in r2.stderr
|
||||
new_path = Path(r2.stdout.strip())
|
||||
assert new_path != path and not path.exists() # hash6 随 birth 变
|
||||
meta = read_meta(new_path.read_text(encoding="utf-8"))
|
||||
assert meta["birth_clock"] == "1990-05-14 10:30"
|
||||
|
||||
def test_same_chart_keeps_filename(self, tmp_path):
|
||||
r1 = run(SAVE, tmp_path, *ZHANGSAN)
|
||||
assert r1.returncode == 0, f"stderr:\n{r1.stderr}"
|
||||
path = Path(r1.stdout.strip())
|
||||
r2 = run(SAVE, tmp_path, "--update-chart", str(path))
|
||||
assert r2.returncode == 0, f"stderr:\n{r2.stderr}"
|
||||
assert Path(r2.stdout.strip()) == path # 四柱未变,文件名不动
|
||||
assert "待复核" not in r2.stderr
|
||||
|
||||
def test_conflict_refuses_overwrite(self, tmp_path):
|
||||
run(SAVE, tmp_path, *WANZI)
|
||||
olds = list((tmp_path / "archives").glob("*.md"))
|
||||
assert len(olds) == 1
|
||||
old = olds[0]
|
||||
r = run(SAVE, tmp_path, "--update-chart", str(old),
|
||||
"--day-boundary", "子正")
|
||||
assert r.returncode == 0, f"stderr:\n{r.stderr}"
|
||||
# 重新建子初盘档案,再换子正 → 目标已存在,拒绝覆盖
|
||||
run(SAVE, tmp_path, *WANZI)
|
||||
r = run(SAVE, tmp_path, "--update-chart", str(old),
|
||||
"--day-boundary", "子正")
|
||||
assert r.returncode == 2
|
||||
assert "已存在" in r.stderr
|
||||
assert len(list((tmp_path / "archives").glob("*.md"))) == 2
|
||||
|
||||
def test_missing_archive_exit_2(self, tmp_path):
|
||||
r = run(SAVE, tmp_path, "--update-chart",
|
||||
str(tmp_path / "archives" / "nope.md"))
|
||||
assert r.returncode == 2
|
||||
|
||||
def test_rejects_name_gender_overrides(self, tmp_path):
|
||||
r1 = run(SAVE, tmp_path, *ZHANGSAN)
|
||||
assert r1.returncode == 0, f"stderr:\n{r1.stderr}"
|
||||
path = r1.stdout.strip()
|
||||
r = run(SAVE, tmp_path, "--update-chart", path, "--name", "王五")
|
||||
assert r.returncode == 2
|
||||
|
||||
Reference in New Issue
Block a user