#!/usr/bin/env python3 # -*- coding: utf-8 -*- import json import sys from pathlib import Path def test_extract_state_summary_accepts_dominant_key(tmp_path): scripts_dir = Path(__file__).resolve().parents[2] if str(scripts_dir) not in sys.path: sys.path.insert(0, str(scripts_dir)) from extract_chapter_context import extract_state_summary state = { "progress": {"current_chapter": 12, "total_words": 12345}, "protagonist_state": { "power": {"realm": "筑基", "layer": 2}, "location": "宗门", "golden_finger": {"name": "系统", "level": 1}, }, "strand_tracker": { "history": [ {"chapter": 10, "dominant": "quest"}, {"chapter": 11, "dominant": "fire"}, ] }, } webnovel_dir = tmp_path / ".webnovel" webnovel_dir.mkdir(parents=True, exist_ok=True) (webnovel_dir / "state.json").write_text(json.dumps(state, ensure_ascii=False), encoding="utf-8") text = extract_state_summary(tmp_path) assert "Ch10:quest" in text assert "Ch11:fire" in text def test_build_chapter_context_payload_includes_contract_sections(tmp_path): scripts_dir = Path(__file__).resolve().parents[2] if str(scripts_dir) not in sys.path: sys.path.insert(0, str(scripts_dir)) from extract_chapter_context import build_chapter_context_payload from data_modules.config import DataModulesConfig from data_modules.index_manager import IndexManager, ChapterReadingPowerMeta, ReviewMetrics cfg = DataModulesConfig.from_project_root(tmp_path) cfg.ensure_dirs() state = { "project": {"genre": "xuanhuan"}, "progress": {"current_chapter": 3, "total_words": 9000}, "protagonist_state": { "power": {"realm": "筑基", "layer": 2}, "location": "宗门", "golden_finger": {"name": "系统", "level": 1}, }, "strand_tracker": {"history": [{"chapter": 2, "dominant": "quest"}]}, "chapter_meta": {}, "disambiguation_warnings": [], "disambiguation_pending": [], } (cfg.webnovel_dir / "state.json").write_text(json.dumps(state, ensure_ascii=False), encoding="utf-8") summaries_dir = cfg.webnovel_dir / "summaries" summaries_dir.mkdir(parents=True, exist_ok=True) (summaries_dir / "ch0002.md").write_text("## 剧情摘要\n上一章总结", encoding="utf-8") outline_dir = tmp_path / "大纲" outline_dir.mkdir(parents=True, exist_ok=True) (outline_dir / "第1卷 详细大纲.md").write_text("### 第3章:测试标题\n测试大纲", encoding="utf-8") refs_dir = tmp_path / ".claude" / "references" refs_dir.mkdir(parents=True, exist_ok=True) (refs_dir / "genre-profiles.md").write_text("## xuanhuan\n- 升级线清晰", encoding="utf-8") (refs_dir / "reading-power-taxonomy.md").write_text("## xuanhuan\n- 悬念钩优先", encoding="utf-8") idx = IndexManager(cfg) idx.save_chapter_reading_power( ChapterReadingPowerMeta(chapter=2, hook_type="悬念钩", hook_strength="strong", coolpoint_patterns=["身份掉马"]) ) idx.save_review_metrics( ReviewMetrics(start_chapter=1, end_chapter=2, overall_score=71, dimension_scores={"plot": 71}) ) payload = build_chapter_context_payload(tmp_path, 3) assert payload["context_contract_version"] == "v2" assert payload.get("context_weight_stage") in {"early", "mid", "late"} assert "writing_guidance" in payload assert isinstance(payload["writing_guidance"].get("guidance_items"), list) assert isinstance(payload["writing_guidance"].get("checklist"), list) assert isinstance(payload["writing_guidance"].get("checklist_score"), dict) assert payload["genre_profile"].get("genre") == "xuanhuan" def test_render_text_contains_writing_guidance_section(tmp_path): scripts_dir = Path(__file__).resolve().parents[2] if str(scripts_dir) not in sys.path: sys.path.insert(0, str(scripts_dir)) from extract_chapter_context import _render_text payload = { "chapter": 10, "outline": "测试大纲", "previous_summaries": ["### 第9章摘要\n上一章"], "state_summary": "状态", "context_contract_version": "v2", "context_weight_stage": "early", "reader_signal": {"review_trend": {"overall_avg": 72}, "low_score_ranges": [{"start_chapter": 8, "end_chapter": 9}]}, "genre_profile": { "genre": "xuanhuan", "genres": ["xuanhuan", "realistic"], "composite_hints": ["以玄幻主线推进,同时保留现实议题表达"], "reference_hints": ["升级线清晰"], }, "writing_guidance": { "guidance_items": ["先修低分", "钩子差异化"], "checklist": [ { "id": "fix_low_score_range", "label": "修复低分区间问题", "weight": 1.4, "required": True, "source": "reader_signal.low_score_ranges", "verify_hint": "至少完成1处冲突升级", } ], "checklist_score": { "score": 81.5, "completion_rate": 0.66, "required_completion_rate": 0.75, }, }, } text = _render_text(payload) assert "## 写作执行建议" in text assert "先修低分" in text assert "## Contract (v2)" in text assert "- 上下文阶段权重: early" in text assert "### 执行检查清单(可评分)" in text assert "- 总权重: 1.40" in text assert "[必做][w=1.4] 修复低分区间问题" in text assert "### 执行评分" in text assert "- 评分: 81.5" in text assert "- 复合题材: xuanhuan + realistic" in text