"""大周天演化候选抽取纯函数测试。""" from app.modules.big_cycle.extract import ( EvolutionCandidateDraft, parse_evolution_candidates, ) def test_parse_standard_json_array() -> None: raw = ( '[{"type":"personality","level":"L4","scope":"character_scope",' '"title":"调整身份表达","proposal":"更主动介绍自身能力边界",' '"delta":{"identity_profile":"新的身份描述"},"confidence":0.8,' '"benefit_score":0.7,"risk_score":0.3,"auto_apply_allowed":false,' '"evidence":[0,1]}]' ) drafts = parse_evolution_candidates(raw) assert len(drafts) == 1 d = drafts[0] assert d.type == "personality" assert d.level == "L4" assert d.scope == "character_scope" assert d.title == "调整身份表达" assert d.proposal == "更主动介绍自身能力边界" assert d.delta == {"identity_profile": "新的身份描述"} assert d.confidence == 0.8 assert d.auto_apply_allowed is False assert d.source == "big_cycle" assert d.evidence == [0, 1] def test_parse_markdown_code_block() -> None: raw = ( '```json\n[{"type":"procedural","level":"L2","scope":"task_scope",' '"title":"t","proposal":"p"}]\n```' ) drafts = parse_evolution_candidates(raw) assert len(drafts) == 1 assert drafts[0].type == "procedural" def test_parse_text_mixed_json() -> None: raw = ( '候选如下:\n[{"type":"mood","level":"L1","scope":"character_scope",' '"title":"t","proposal":"p"}]\n以上。' ) drafts = parse_evolution_candidates(raw) assert len(drafts) == 1 def test_parse_invalid_json_returns_empty() -> None: assert parse_evolution_candidates("不是JSON") == [] assert parse_evolution_candidates("[]") == [] def test_skip_entries_missing_required_fields() -> None: raw = ( '[{"type":"personality","title":"缺proposal"},' '{"title":"缺type","proposal":"p"},' '{"type":"x","proposal":"缺title"}]' ) assert parse_evolution_candidates(raw) == [] def test_defaults_use_safe_pessimistic_values() -> None: """缺省时取最严格默认:level=L4(必须人工)、auto_apply_allowed=False。""" raw = '[{"type":"personality","title":"t","proposal":"p"}]' d = parse_evolution_candidates(raw)[0] assert d.level == "L4" assert d.scope == "character_scope" assert d.auto_apply_allowed is False assert d.confidence == 0.5 assert d.benefit_score == 0.5 assert d.risk_score == 0.5 assert d.drift_score == 0.0 assert d.delta == {} def test_clamp_and_default_scores() -> None: raw = ( '[{"type":"personality","title":"t","proposal":"p",' '"confidence":1.5,"benefit_score":-0.2,"risk_score":"high"}]' ) d = parse_evolution_candidates(raw)[0] assert d.confidence == 1.0 assert d.benefit_score == 0.0 assert d.risk_score == 0.5 # 非法值 → 默认 def test_auto_apply_allowed_truthy() -> None: raw = ( '[{"type":"procedural","level":"L2","title":"t","proposal":"p","auto_apply_allowed":true}]' ) d = parse_evolution_candidates(raw)[0] assert d.auto_apply_allowed is True assert isinstance(d, EvolutionCandidateDraft)