lllin000_PaperForge/tests/test_prepare_rollback.py

145 lines
5.6 KiB
Python
Raw Normal View History

"""Test rollback behavior in prepare_deep_reading.
Covers D-13..D-15: partial failure cleanup.
"""
from __future__ import annotations
import sys
from pathlib import Path
from unittest.mock import patch
import pytest
REPO_ROOT = Path(__file__).resolve().parent.parent
if str(REPO_ROOT) not in sys.path:
sys.path.insert(0, str(REPO_ROOT))
from tests.conftest import create_test_vault
def _import_ld_deep(path: Path | None = None):
"""Import ld_deep.py using importlib, with Python 3.14 workaround."""
if path is None:
path = REPO_ROOT / "paperforge" / "skills" / "literature-qa" / "scripts" / "ld_deep.py"
import importlib.util
module_name = "_test_rollback_ld_deep"
if module_name in sys.modules:
del sys.modules[module_name]
spec = importlib.util.spec_from_file_location(module_name, path)
if spec is None or spec.loader is None:
pytest.skip(f"Could not create spec for {path}")
module = importlib.util.module_from_spec(spec)
sys.modules[module_name] = module
try:
spec.loader.exec_module(module)
except AttributeError as e:
if "'NoneType' object has no attribute '__dict__'" in str(e):
pytest.skip(f"Python 3.14 dataclass bug prevents import: {e}")
raise
return module
class TestPrepareRollback:
"""Task 5: rollback behavior in prepare_deep_reading."""
def test_prepare_rollback_on_figure_map_failure(self) -> None:
"""Mock build_figure_map to raise; assert no partial files remain."""
vault = create_test_vault()
module = _import_ld_deep()
prepare_deep_reading = module.prepare_deep_reading
ocr_dir = vault / "99_System" / "PaperForge" / "ocr" / "TSTONE001"
figure_map_path = ocr_dir / "figure-map.json"
chart_type_map_path = ocr_dir / "chart-type-map.json"
formal_note = (
vault
/ "03_Resources"
/ "Literature"
/ "骨科"
/ "TSTONE001.md"
)
# Remove pre-existing fixture files to test clean rollback
if figure_map_path.exists():
figure_map_path.unlink()
if chart_type_map_path.exists():
chart_type_map_path.unlink()
original_note_text = formal_note.read_text(encoding="utf-8")
with patch.object(module, "build_figure_map", side_effect=RuntimeError("mock figure map failure")):
result = prepare_deep_reading(vault, "TSTONE001")
assert result["status"] == "error", "should return error status"
assert "mock figure map failure" in result["message"], "message should contain error"
assert not figure_map_path.exists(), "figure-map.json should not exist (rollback or never created)"
assert not chart_type_map_path.exists(), "chart-type-map.json should not exist"
assert formal_note.read_text(encoding="utf-8") == original_note_text, "formal note should be restored"
def test_prepare_rollback_on_scaffold_failure(self) -> None:
"""Mock ensure_study_section to raise; assert files cleaned up, note restored."""
vault = create_test_vault()
module = _import_ld_deep()
prepare_deep_reading = module.prepare_deep_reading
ocr_dir = vault / "99_System" / "PaperForge" / "ocr" / "TSTONE001"
figure_map_path = ocr_dir / "figure-map.json"
chart_type_map_path = ocr_dir / "chart-type-map.json"
formal_note = (
vault
/ "03_Resources"
/ "Literature"
/ "骨科"
/ "TSTONE001.md"
)
# Remove pre-existing fixture files to test clean rollback
if figure_map_path.exists():
figure_map_path.unlink()
if chart_type_map_path.exists():
chart_type_map_path.unlink()
original_note_text = formal_note.read_text(encoding="utf-8")
with patch.object(module, "ensure_study_section", side_effect=RuntimeError("mock scaffold failure")):
result = prepare_deep_reading(vault, "TSTONE001")
assert result["status"] == "error", "should return error status"
assert "mock scaffold failure" in result["message"], "message should contain error"
assert not figure_map_path.exists(), "figure-map.json should be deleted on rollback"
assert not chart_type_map_path.exists(), "chart-type-map.json should be deleted on rollback"
assert formal_note.read_text(encoding="utf-8") == original_note_text, "formal note should be restored"
def test_prepare_success_no_rollback(self) -> None:
"""Normal flow: all files exist, note updated."""
vault = create_test_vault()
module = _import_ld_deep()
prepare_deep_reading = module.prepare_deep_reading
ocr_dir = vault / "99_System" / "PaperForge" / "ocr" / "TSTONE001"
figure_map_path = ocr_dir / "figure-map.json"
chart_type_map_path = ocr_dir / "chart-type-map.json"
formal_note = (
vault
/ "03_Resources"
/ "Literature"
/ "骨科"
/ "TSTONE001.md"
)
original_note_text = formal_note.read_text(encoding="utf-8")
result = prepare_deep_reading(vault, "TSTONE001")
assert result["status"] == "ok", f"should succeed: {result.get('message')}"
assert figure_map_path.exists(), "figure-map.json should exist"
assert chart_type_map_path.exists(), "chart-type-map.json should exist"
updated_text = formal_note.read_text(encoding="utf-8")
assert "## 🔍 精读" in updated_text, "formal note should have 精读 section"
assert updated_text != original_note_text, "formal note should be modified"