test_extract_chapter_context.py 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3. import json
  4. import sys
  5. from pathlib import Path
  6. def test_extract_state_summary_accepts_dominant_key(tmp_path):
  7. scripts_dir = Path(__file__).resolve().parents[2]
  8. if str(scripts_dir) not in sys.path:
  9. sys.path.insert(0, str(scripts_dir))
  10. from extract_chapter_context import extract_state_summary
  11. state = {
  12. "progress": {"current_chapter": 12, "total_words": 12345},
  13. "protagonist_state": {
  14. "power": {"realm": "筑基", "layer": 2},
  15. "location": "宗门",
  16. "golden_finger": {"name": "系统", "level": 1},
  17. },
  18. "strand_tracker": {
  19. "history": [
  20. {"chapter": 10, "dominant": "quest"},
  21. {"chapter": 11, "dominant": "fire"},
  22. ]
  23. },
  24. }
  25. webnovel_dir = tmp_path / ".webnovel"
  26. webnovel_dir.mkdir(parents=True, exist_ok=True)
  27. (webnovel_dir / "state.json").write_text(json.dumps(state, ensure_ascii=False), encoding="utf-8")
  28. text = extract_state_summary(tmp_path)
  29. assert "Ch10:quest" in text
  30. assert "Ch11:fire" in text
  31. def test_build_chapter_context_payload_includes_contract_sections(tmp_path):
  32. scripts_dir = Path(__file__).resolve().parents[2]
  33. if str(scripts_dir) not in sys.path:
  34. sys.path.insert(0, str(scripts_dir))
  35. from extract_chapter_context import build_chapter_context_payload
  36. from data_modules.config import DataModulesConfig
  37. from data_modules.index_manager import IndexManager, ChapterReadingPowerMeta, ReviewMetrics
  38. cfg = DataModulesConfig.from_project_root(tmp_path)
  39. cfg.ensure_dirs()
  40. state = {
  41. "project": {"genre": "xuanhuan"},
  42. "progress": {"current_chapter": 3, "total_words": 9000},
  43. "protagonist_state": {
  44. "power": {"realm": "筑基", "layer": 2},
  45. "location": "宗门",
  46. "golden_finger": {"name": "系统", "level": 1},
  47. },
  48. "strand_tracker": {"history": [{"chapter": 2, "dominant": "quest"}]},
  49. "chapter_meta": {},
  50. "disambiguation_warnings": [],
  51. "disambiguation_pending": [],
  52. }
  53. (cfg.webnovel_dir / "state.json").write_text(json.dumps(state, ensure_ascii=False), encoding="utf-8")
  54. summaries_dir = cfg.webnovel_dir / "summaries"
  55. summaries_dir.mkdir(parents=True, exist_ok=True)
  56. (summaries_dir / "ch0002.md").write_text("## 剧情摘要\n上一章总结", encoding="utf-8")
  57. outline_dir = tmp_path / "大纲"
  58. outline_dir.mkdir(parents=True, exist_ok=True)
  59. (outline_dir / "第1卷 详细大纲.md").write_text("### 第3章:测试标题\n测试大纲", encoding="utf-8")
  60. refs_dir = tmp_path / ".claude" / "references"
  61. refs_dir.mkdir(parents=True, exist_ok=True)
  62. (refs_dir / "genre-profiles.md").write_text("## xuanhuan\n- 升级线清晰", encoding="utf-8")
  63. (refs_dir / "reading-power-taxonomy.md").write_text("## xuanhuan\n- 悬念钩优先", encoding="utf-8")
  64. idx = IndexManager(cfg)
  65. idx.save_chapter_reading_power(
  66. ChapterReadingPowerMeta(chapter=2, hook_type="悬念钩", hook_strength="strong", coolpoint_patterns=["身份掉马"])
  67. )
  68. idx.save_review_metrics(
  69. ReviewMetrics(start_chapter=1, end_chapter=2, overall_score=71, dimension_scores={"plot": 71})
  70. )
  71. payload = build_chapter_context_payload(tmp_path, 3)
  72. assert payload["context_contract_version"] == "v2"
  73. assert "writing_guidance" in payload
  74. assert isinstance(payload["writing_guidance"].get("guidance_items"), list)
  75. assert payload["genre_profile"].get("genre") == "xuanhuan"
  76. def test_render_text_contains_writing_guidance_section(tmp_path):
  77. scripts_dir = Path(__file__).resolve().parents[2]
  78. if str(scripts_dir) not in sys.path:
  79. sys.path.insert(0, str(scripts_dir))
  80. from extract_chapter_context import _render_text
  81. payload = {
  82. "chapter": 10,
  83. "outline": "测试大纲",
  84. "previous_summaries": ["### 第9章摘要\n上一章"],
  85. "state_summary": "状态",
  86. "context_contract_version": "v2",
  87. "reader_signal": {"review_trend": {"overall_avg": 72}, "low_score_ranges": [{"start_chapter": 8, "end_chapter": 9}]},
  88. "genre_profile": {"genre": "xuanhuan", "reference_hints": ["升级线清晰"]},
  89. "writing_guidance": {"guidance_items": ["先修低分", "钩子差异化"]},
  90. }
  91. text = _render_text(payload)
  92. assert "## 写作执行建议" in text
  93. assert "先修低分" in text
  94. assert "## Contract (v2)" in text