Prechádzať zdrojové kódy

fix: preserve corrupt state.json before rebuilding

lingfengQAQ 1 týždeň pred
rodič
commit
d28a23ccde

+ 3 - 3
docs/superpowers/plans/2026-06-10-audit-fix-plan.md

@@ -85,9 +85,9 @@ def rollback(self, chapter: int) -> bool:
 - Modify: `webnovel-writer/scripts/init_project.py:294-300,366`
 - Test: `webnovel-writer/scripts/data_modules/tests/test_init_project_pruning.py`
 
-- [ ] **Step 1: 写测试**:项目里放一个非法 JSON 的 state.json,重跑 init,断言 (a) 生成 `state.corrupt_*.json` 副本且内容等于原损坏文本;(b) 输出包含警告。
-- [ ] **Step 2: 实现**:捕获 `json.JSONDecodeError` 时先 `shutil.copy2(state_path, state_path.with_name(f"state.corrupt_{ts}.json"))` 再重建,打印"⚠️ 原 state.json 已损坏,已另存为 ... 供手工抢救"。
-- [ ] **Step 3: 提交** `fix: preserve corrupt state.json before rebuilding`。
+- [x] **Step 1: 写测试**:项目里放一个非法 JSON 的 state.json,重跑 init,断言 (a) 生成 `state.corrupt_*.json` 副本且内容等于原损坏文本;(b) 输出包含警告。
+- [x] **Step 2: 实现**:捕获 `json.JSONDecodeError` 时先 `shutil.copy2(state_path, state_path.with_name(f"state.corrupt_{ts}.json"))` 再重建,打印"⚠️ 原 state.json 已损坏,已另存为 ... 供手工抢救"。
+- [x] **Step 3: 提交** `fix: preserve corrupt state.json before rebuilding`。
 
 ### Task 5: 迁移脚本带错不精简、写回原子化
 

+ 24 - 0
webnovel-writer/scripts/data_modules/tests/test_init_project_pruning.py

@@ -119,3 +119,27 @@ def test_init_rejects_english_profile_key_before_writing_state(tmp_path, monkeyp
     assert "rules-mystery" in message
     assert "规则怪谈" in message
     assert not (project_root / ".webnovel" / "state.json").exists()
+
+
+def test_init_preserves_corrupt_state_json_before_rebuilding(tmp_path, monkeypatch, capsys):
+    import init_project as init_project_module
+
+    monkeypatch.setattr(init_project_module, "is_git_available", lambda: False)
+    project_root = tmp_path / "book"
+    state_dir = project_root / ".webnovel"
+    state_dir.mkdir(parents=True)
+    corrupt_text = '{"project_info": '
+    (state_dir / "state.json").write_text(corrupt_text, encoding="utf-8")
+
+    init_project_module.init_project(
+        str(project_root),
+        title="测试书",
+        genre="仙侠",
+        protagonist_name="陆鸣",
+        target_chapters=50,
+    )
+
+    corrupt_copies = sorted(state_dir.glob("state.corrupt_*.json"))
+    assert len(corrupt_copies) == 1
+    assert corrupt_copies[0].read_text(encoding="utf-8") == corrupt_text
+    assert "原 state.json 已损坏" in capsys.readouterr().out

+ 5 - 0
webnovel-writer/scripts/init_project.py

@@ -17,6 +17,7 @@ from __future__ import annotations
 
 import argparse
 import json
+import shutil
 import subprocess
 import sys
 from datetime import datetime
@@ -295,6 +296,10 @@ def init_project(
         try:
             state: Dict[str, Any] = json.loads(state_path.read_text(encoding="utf-8"))
         except json.JSONDecodeError:
+            timestamp = datetime.now().strftime("%Y%m%d_%H%M%S_%f")
+            corrupt_path = state_path.with_name(f"state.corrupt_{timestamp}.json")
+            shutil.copy2(state_path, corrupt_path)
+            print(f"⚠️ 原 state.json 已损坏,已另存为 {corrupt_path} 供手工抢救")
             state = {}
     else:
         state = {}