66 lines
2.6 KiB
Python
66 lines
2.6 KiB
Python
|
|
"""叙事边界:伪 JSON、prompt 标记泄漏启发式。"""
|
||
|
|
|
||
|
|
from __future__ import annotations
|
||
|
|
|
||
|
|
from app.features.memoir.narrative_to_markdown import narrative_to_markdown
|
||
|
|
from app.features.memoir import narrative_safety as ns
|
||
|
|
from app.features.memoir import story_pipeline_sync as sps
|
||
|
|
|
||
|
|
|
||
|
|
def test_narrative_to_markdown_malformed_json_with_paragraphs_shell_returns_empty() -> (
|
||
|
|
None
|
||
|
|
):
|
||
|
|
"""不得以破损 JSON 当正文;上层应回退 oral/旧文。"""
|
||
|
|
raw = '{"paragraphs": [broken'
|
||
|
|
assert raw.strip().startswith("{")
|
||
|
|
assert "paragraphs" in raw
|
||
|
|
assert narrative_to_markdown(raw) == ""
|
||
|
|
|
||
|
|
|
||
|
|
def test_narrative_to_markdown_valid_paragraphs_preserved() -> None:
|
||
|
|
md = narrative_to_markdown(
|
||
|
|
'{"paragraphs": [{"content": "第一段"}, {"content": "第二段"}]}'
|
||
|
|
)
|
||
|
|
assert "第一段" in md
|
||
|
|
assert "第二段" in md
|
||
|
|
|
||
|
|
|
||
|
|
def test_body_contains_prompt_artifact_detects_evidence_marker() -> None:
|
||
|
|
body = "前文\n【仅供参考的相关记忆摘录(不得把其中具体事实写成本轮亲历经历)】\nfoo"
|
||
|
|
assert ns.body_contains_prompt_artifact(body) is True
|
||
|
|
|
||
|
|
|
||
|
|
def test_body_contains_prompt_artifact_clean() -> None:
|
||
|
|
assert ns.body_contains_prompt_artifact("我在河边长大。") is False
|
||
|
|
|
||
|
|
|
||
|
|
def test_evidence_leak_heuristic_flags_long_shared_substring() -> None:
|
||
|
|
oral = "短口述"
|
||
|
|
ev = "独有细节abcdefghijklmnopqrstuvwxyz独有"
|
||
|
|
body = "中间夹着独有细节abcdefghijklmnopqrstuvwxyz独有结尾"
|
||
|
|
# 长公共子串仅在 evidence 与 body 之间,且 oral 未覆盖
|
||
|
|
score = ns.evidence_substring_leak_score(body, ev, min_len=14)
|
||
|
|
assert score >= 14
|
||
|
|
|
||
|
|
|
||
|
|
def test_evidence_scene_anchor_leak_detects_dinner_not_in_oral() -> None:
|
||
|
|
oral = "我们聊了我要去南京了,成家,结婚生子。"
|
||
|
|
ev = "我们大伙前一天晚上还在聚餐,聊了我要去南京了。"
|
||
|
|
body = "回想起来,那晚聚餐时聊到了我将要前往南京以及成家、结婚生子的话题。"
|
||
|
|
assert ns.evidence_scene_anchor_leak(body, ev, oral, "") is True
|
||
|
|
|
||
|
|
|
||
|
|
def test_evidence_scene_anchor_no_flag_when_oral_has_anchor() -> None:
|
||
|
|
oral = "前一晚聚餐时我们聊了很多。"
|
||
|
|
ev = "摘录里也写了聚餐。"
|
||
|
|
body = "前一晚聚餐时我们聊了很多。"
|
||
|
|
assert ns.evidence_scene_anchor_leak(body, ev, oral, "") is False
|
||
|
|
|
||
|
|
|
||
|
|
def test_strip_ungrounded_title_drops_career_segment() -> None:
|
||
|
|
hay = "我与妻子和孩子之间,从不为琐碎小事置气。"
|
||
|
|
raw = "晋升旅长后 · 家庭中的沟通"
|
||
|
|
out = sps._strip_ungrounded_title_segments(raw, hay, chapter_category="family")
|
||
|
|
assert "晋升旅长" not in out
|
||
|
|
assert "家庭" in out
|