""" 裂变编排层 _score_notes 测试(合格线@80 + 违禁词硬拦截逻辑) mock llm_score_copy,验证 _passed = passed AND not _block 的安全关键规则。 排序/降级落库逻辑(needs_optimization=0 if passed else 1)在 execute_fission_pipeline, 本文件只锁评分写回的 _score/_block/_passed 三字段语义。 """ import sys import os sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..")) import unittest.mock as mock from app.services.fission_pipeline import _score_notes SOURCE = {"title": "源", "content": "源正文"} def _patch_scorer(monkeypatch, results): """按调用顺序返回 results 里的评分 dict。""" calls = {"i": 0} async def fake(client, copy, source, banned=None, pass_score=80): r = results[calls["i"] % len(results)] calls["i"] += 1 return r monkeypatch.setattr("app.services.ai_engine.llm_scorer.llm_score_copy", fake) def test_score_high_no_banned_passes(monkeypatch): _patch_scorer(monkeypatch, [ {"score": 88, "passed": True, "banned_words_found": []}, ]) notes = [{"title": "a", "content": "c", "tags": []}] _score_notes(mock.MagicMock(), notes, SOURCE, []) assert notes[0]["_score"] == 88 assert notes[0]["_block"] is False assert notes[0]["_passed"] is True def test_score_low_marks_not_passed(monkeypatch): # 低于合格线 → 不达标,但分数照写(落库时标 needs_optimization 不丢弃) _patch_scorer(monkeypatch, [ {"score": 62, "passed": False, "banned_words_found": []}, ]) notes = [{"title": "a", "content": "c", "tags": []}] _score_notes(mock.MagicMock(), notes, SOURCE, []) assert notes[0]["_score"] == 62 assert notes[0]["_passed"] is False def test_score_banned_word_hard_blocks_even_if_passed(monkeypatch): # 违禁词硬拦截:即便分数达标 passed=True,命中违禁词也强制 _passed=False _patch_scorer(monkeypatch, [ {"score": 95, "passed": True, "banned_words_found": ["美白"]}, ]) notes = [{"title": "美白神器", "content": "c", "tags": []}] _score_notes(mock.MagicMock(), notes, SOURCE, ["美白"]) assert notes[0]["_block"] is True assert notes[0]["_passed"] is False def test_score_exception_records_zero(monkeypatch): # 评分接口抛异常 → 记0分不达标,不让整批裂变崩 async def boom(*a, **k): raise RuntimeError("中转站429") monkeypatch.setattr("app.services.ai_engine.llm_scorer.llm_score_copy", boom) notes = [{"title": "a", "content": "c", "tags": []}] _score_notes(mock.MagicMock(), notes, SOURCE, []) assert notes[0]["_score"] == 0 assert notes[0]["_passed"] is False def test_score_multiple_notes_each_scored(monkeypatch): _patch_scorer(monkeypatch, [ {"score": 90, "passed": True, "banned_words_found": []}, {"score": 50, "passed": False, "banned_words_found": []}, ]) notes = [{"title": "a", "content": "c", "tags": []}, {"title": "b", "content": "d", "tags": []}] _score_notes(mock.MagicMock(), notes, SOURCE, []) assert notes[0]["_passed"] is True assert notes[1]["_passed"] is False