refactor review-plan safety and validation

This commit is contained in:
windyboy
2026-04-07 11:00:47 +08:00
parent fb128c70d6
commit 0a6bddcc7e
19 changed files with 1246 additions and 628 deletions
+108
View File
@@ -614,6 +614,114 @@ def test_generate_plan_with_analysis_by_reputation_missing_scores_uses_fallback_
assert "评分依据不足" in plan.human_summary
def test_generate_plan_with_analysis_by_quality_missing_quality_data_marks_manual_review(config):
"""Missing quality entries should not silently keep the first duplicate."""
config.duplicate_keep = "by_quality"
p_low = Path("/mnt/nas/videos/movie/Test.2020.720p.WEB-DL.mkv")
p_high = Path("/mnt/nas/videos/movie/Test.2020.1080p.BluRay.mkv")
vf_low = VideoFile(
path=p_low,
filename="Test.2020.720p.WEB-DL.mkv",
size_bytes=1000000,
modified_timestamp=datetime.now(timezone.utc),
category="movie",
)
vf_high = VideoFile(
path=p_high,
filename="Test.2020.1080p.BluRay.mkv",
size_bytes=2000000,
modified_timestamp=datetime.now(timezone.utc),
category="movie",
)
identity = MovieIdentity(
title="Test",
year=2020,
confidence=0.9,
needs_review=False,
original_filename="Test.2020.mkv",
)
plan = generate_plan(
[(vf_low, identity), (vf_high, identity)],
config,
analysis_data={
"metadata": {"source_identities": "identities.json"},
"completeness": [],
"duplicates": [
{
"identity": {"type": "movie", "title": "Test", "year": 2020},
"files": [str(p_low), str(p_high)],
"quality_comparison": [
{"path": str(p_low), "resolution": "1280x720", "size_bytes": 1000000},
],
}
],
},
)
assert all(op.operation_type == "no-op" for op in plan.operations)
assert "Duplicate group needs manual review" in plan.operations[0].reason
issues = plan.metadata["validation_snapshot"]["duplicate_resolution_issues"]
assert len(issues) == 1
assert "missing quality comparison entries" in issues[0]["reason"]
assert "转人工复核" in plan.human_summary
def test_generate_plan_normalizes_duplicate_paths_before_matching(config):
"""Duplicate matching should survive harmless path-format differences."""
config.duplicate_keep = "by_quality"
p_720 = Path("/mnt/nas/videos/movie/Test.2020.720p.WEB-DL.mkv")
p_1080 = Path("/mnt/nas/videos/movie/Test.2020.1080p.BluRay.mkv")
vf_720 = VideoFile(
path=p_720,
filename="Test.2020.720p.WEB-DL.mkv",
size_bytes=1000000,
modified_timestamp=datetime.now(timezone.utc),
category="movie",
)
vf_1080 = VideoFile(
path=p_1080,
filename="Test.2020.1080p.BluRay.mkv",
size_bytes=2000000,
modified_timestamp=datetime.now(timezone.utc),
category="movie",
)
identity = MovieIdentity(
title="Test",
year=2020,
confidence=0.9,
needs_review=False,
original_filename="Test.2020.mkv",
)
normalized_low = str(p_720.parent / "." / p_720.name)
normalized_high = str(p_1080.parent / "." / p_1080.name)
plan = generate_plan(
[(vf_720, identity), (vf_1080, identity)],
config,
analysis_data={
"metadata": {"source_identities": "identities.json"},
"completeness": [],
"duplicates": [
{
"identity": {"type": "movie", "title": "Test", "year": 2020},
"files": [normalized_low, normalized_high],
"quality_comparison": [
{"path": normalized_low, "resolution": "1280x720", "size_bytes": 1000000},
{"path": normalized_high, "resolution": "1920x1080", "size_bytes": 2000000},
],
}
],
},
)
assert plan.summary["quarantine"] == 1
assert plan.operations[0].operation_type == "quarantine"
assert plan.operations[1].operation_type == "move"
def test_generate_plan_human_summary_marks_disc_files_as_high_risk(config):
"""Disc/part files should be listed with a conservative risk note in summary."""
config.duplicate_keep = "by_reputation"