实现本地 Markdown 清洗评审器

This commit is contained in:
2026-08-24 01:02:16 +08:00
parent 80001a8ab9
commit ac798a2610
39 changed files with 9357 additions and 119 deletions
+132
View File
@@ -0,0 +1,132 @@
from __future__ import annotations
from pathlib import Path
from typing import Any, cast
import pytest
from reviewer.server import ReviewArtifactError, ReviewArtifacts
from tests.reviewer_fixture import create_review_run, read_json, write_json
def test_reviewer_returns_full_comparison_and_all_component_stages(tmp_path: Path) -> None:
fixture = create_review_run(tmp_path)
repository = ReviewArtifacts(fixture["run_directory"])
run = cast(dict[str, Any], repository.run_summary())
comparison = cast(dict[str, Any], repository.document_comparison("paper"))
changed = cast(dict[str, Any], repository.component_stage("paper", 0))
unchanged = cast(dict[str, Any], repository.component_stage("paper", 1))
assert run["summary"] == {
"document_count": 1,
"success_count": 1,
"failed_count": 0,
"unstable_count": 0,
"change_count": 1,
}
assert run["documents"][0]["source_available"] is True
assert comparison["original_markdown"] == fixture["original"]
assert comparison["cleaned_markdown"] == fixture["cleaned"]
assert comparison["changes"][0]["editor_range"] == {"start": 4, "end": 7}
assert changed["before_markdown"] == fixture["original"]
assert changed["after_markdown"] == fixture["cleaned"]
assert unchanged["before_markdown"] == fixture["cleaned"]
assert unchanged["after_markdown"] == fixture["cleaned"]
assert unchanged["changes"] == []
@pytest.mark.parametrize("status", ["failed", "unstable"])
def test_non_success_documents_expose_audit_but_no_formal_output(tmp_path: Path, status: str) -> None:
fixture = create_review_run(tmp_path, status=status)
repository = ReviewArtifacts(fixture["run_directory"])
comparison = cast(dict[str, Any], repository.document_comparison("paper"))
assert comparison["original_markdown"] == fixture["original"]
assert comparison["cleaned_markdown"] is None
if status == "failed":
assert comparison["errors"]
assert comparison["residual_proposals"] == []
else:
assert comparison["errors"] == []
assert comparison["residual_proposals"]
with pytest.raises(ReviewArtifactError, match="success"):
repository.component_stage("paper", 0)
def test_historical_run_without_locator_reports_unavailable_source(tmp_path: Path) -> None:
fixture = create_review_run(tmp_path, with_locator=False)
repository = ReviewArtifacts(fixture["run_directory"])
summary = cast(dict[str, Any], repository.run_summary())
assert summary["documents"][0]["source_available"] is False
assert "review-locator.json" in summary["documents"][0]["availability_error"]
comparison = cast(dict[str, Any], repository.document_comparison("paper"))
assert comparison["original_markdown"] is None
assert comparison["cleaned_markdown"] == fixture["cleaned"]
assert comparison["changes"][0]["editor_range"] is None
with pytest.raises(ReviewArtifactError, match=r"review-locator\.json"):
repository.component_stage("paper", 0)
def test_source_hash_change_is_not_silently_displayed(tmp_path: Path) -> None:
fixture = create_review_run(tmp_path)
repository = ReviewArtifacts(fixture["run_directory"])
source_path = fixture["source_path"]
assert isinstance(source_path, Path)
source_path.write_text("changed\n", encoding="utf-8")
summary = cast(dict[str, Any], repository.run_summary())
assert summary["documents"][0]["source_available"] is False
assert "哈希" in summary["documents"][0]["availability_error"]
comparison = cast(dict[str, Any], repository.document_comparison("paper"))
assert comparison["original_markdown"] is None
assert comparison["changes"][0]["editor_range"] is None
with pytest.raises(ReviewArtifactError, match="哈希"):
repository.component_stage("paper", 0)
def test_reviewer_rejects_unknown_schema_and_artifact_path_escape(tmp_path: Path) -> None:
fixture = create_review_run(tmp_path)
manifest_path = fixture["manifest_path"]
assert isinstance(manifest_path, Path)
manifest = read_json(manifest_path)
manifest["schema_version"] = 2
write_json(manifest_path, manifest)
with pytest.raises(ReviewArtifactError) as unknown:
ReviewArtifacts(fixture["run_directory"])
assert unknown.value.code == "unsupported_schema"
second = create_review_run(tmp_path / "second")
second_manifest_path = second["manifest_path"]
assert isinstance(second_manifest_path, Path)
second_manifest = read_json(second_manifest_path)
second_manifest["documents"][0]["result_path"] = "../outside.json"
write_json(second_manifest_path, second_manifest)
with pytest.raises(ReviewArtifactError, match="路径"):
ReviewArtifacts(second["run_directory"])
def test_reviewer_rejects_tampered_snapshot_chain_and_unknown_identity(tmp_path: Path) -> None:
fixture = create_review_run(tmp_path)
result_path = fixture["result_path"]
assert isinstance(result_path, Path)
result = read_json(result_path)
result["changes"][0]["after_sha256"] = "0" * 64
write_json(result_path, result)
repository = ReviewArtifacts(fixture["run_directory"])
with pytest.raises(ReviewArtifactError) as replay_error:
repository.document_comparison("paper")
assert replay_error.value.code == "untrusted_replay"
with pytest.raises(ReviewArtifactError) as document_error:
repository.document_comparison("missing")
assert document_error.value.http_status == 404
with pytest.raises(ReviewArtifactError) as component_error:
repository.component_stage("paper", 99)
assert component_error.value.http_status == 404