"""大周天内驱力抽取纯函数测试。""" from app.modules.drive.extract import ( CuriosityDraft, SubgoalDraft, parse_curiosities, ) def test_parse_standard_with_subgoals() -> None: raw = ( '[{"question":"用户为何频繁提到项目X","source":"memory_gap",' '"expected_value":0.7,"urgency":0.5,"risk_level":"L2",' '"related_entities":["项目X"],' '"subgoals":[{"title":"询问项目X目标","description":"主动询问用户",' '"expected_information_gain":0.6,"interruption_cost":0.3,"risk_level":"L2"}]}]' ) drafts = parse_curiosities(raw) assert len(drafts) == 1 c = drafts[0] assert c.question == "用户为何频繁提到项目X" assert c.source == "memory_gap" assert c.expected_value == 0.7 assert c.risk_level == "L2" assert c.related_entities == ["项目X"] assert len(c.subgoals) == 1 sg = c.subgoals[0] assert sg.title == "询问项目X目标" assert sg.expected_information_gain == 0.6 assert sg.interruption_cost == 0.3 def test_parse_markdown_block() -> None: raw = '```json\n[{"question":"q"}]\n```' drafts = parse_curiosities(raw) assert len(drafts) == 1 assert drafts[0].question == "q" def test_parse_invalid_returns_empty() -> None: assert parse_curiosities("不是JSON") == [] assert parse_curiosities("[]") == [] def test_skip_missing_question() -> None: raw = '[{"source":"memory_gap"},{"question":"q"}]' drafts = parse_curiosities(raw) assert len(drafts) == 1 assert drafts[0].question == "q" def test_defaults_use_safe_pessimistic_values() -> None: """缺省取最严格默认:risk_level=L4、source=reflection。""" raw = '[{"question":"q"}]' c = parse_curiosities(raw)[0] assert c.source == "reflection" assert c.risk_level == "L4" assert c.expected_value == 0.5 assert c.urgency == 0.5 assert c.related_entities == [] assert c.subgoals == [] def test_clamp_values() -> None: raw = '[{"question":"q","expected_value":1.5,"urgency":-0.2}]' c = parse_curiosities(raw)[0] assert c.expected_value == 1.0 assert c.urgency == 0.0 def test_invalid_source_defaults() -> None: raw = '[{"question":"q","source":"unknown_source"}]' assert parse_curiosities(raw)[0].source == "reflection" def test_invalid_risk_level_defaults() -> None: raw = '[{"question":"q","risk_level":"L9"}]' assert parse_curiosities(raw)[0].risk_level == "L4" def test_subgoal_defaults_and_types() -> None: raw = '[{"question":"q","subgoals":[{"title":"t","description":"d"}]}]' c = parse_curiosities(raw)[0] assert isinstance(c, CuriosityDraft) sg = c.subgoals[0] assert isinstance(sg, SubgoalDraft) assert sg.risk_level == "L4" # 安全默认 assert sg.expected_information_gain == 0.5 assert sg.interruption_cost == 0.5 def test_skip_subgoal_missing_title_or_description() -> None: raw = '[{"question":"q","subgoals":[{"title":"t"},{"description":"d"},{"title":"t","description":"d"}]}]' c = parse_curiosities(raw)[0] assert len(c.subgoals) == 1 # 只有完整的保留