172 lines
5.0 KiB
Python
172 lines
5.0 KiB
Python
"""Unit tests for enrichment pipeline."""
|
|||
|
|
|
||
|
|
from vlm.config import Config
|
||
|
|
from vlm.enrichment import enrich_identities_data
|
||
|
|
from vlm.providers.base import ProviderResult
|
||
|
|
|
||
|
|
|
||
|
|
class DummyProvider:
|
||
|
|
name = "dummy"
|
||
|
|
|
||
|
|
def __init__(self):
|
||
|
|
self.calls = 0
|
||
|
|
|
||
|
|
def enrich(self, *, title: str, media_type: str, year=None):
|
||
|
|
self.calls += 1
|
||
|
|
return ProviderResult(
|
||
|
|
provider="dummy",
|
||
|
|
canonical_id=f"dummy:{title}",
|
||
|
|
title_zh="测试中文名",
|
||
|
|
title_en="Test English Title",
|
||
|
|
translation_source="dummy",
|
||
|
|
reputation_score=8.2,
|
||
|
|
reputation_votes=100,
|
||
|
|
reputation_source="dummy",
|
||
|
|
)
|
||
|
|
|
||
|
|
|
||
|
|
def test_enrich_incremental_cache_hit(tmp_path, monkeypatch):
|
||
|
|
"""Second enrichment run should hit cache when fingerprint is unchanged."""
|
||
|
|
config = Config(
|
||
|
|
library_root=tmp_path,
|
||
|
|
enrichment_cache_db=tmp_path / "cache.db",
|
||
|
|
enrichment_providers=["dummy"],
|
||
|
|
translation_fallback_machine=False,
|
||
|
|
)
|
||
|
|
|
||
|
|
provider = DummyProvider()
|
||
|
|
monkeypatch.setattr(
|
||
|
|
"vlm.enrichment._build_providers",
|
||
|
|
lambda _config, request_timeout, retries: [provider],
|
||
|
|
)
|
||
|
|
|
||
|
|
identities = {
|
||
|
|
"metadata": {},
|
||
|
|
"movies": [
|
||
|
|
{
|
||
|
|
"path": "/library/movie/The.Matrix.1999.mkv",
|
||
|
|
"filename": "The.Matrix.1999.mkv",
|
||
|
|
"category": "movie",
|
||
|
|
"title": "The Matrix",
|
||
|
|
"year": 1999,
|
||
|
|
"confidence": 0.9,
|
||
|
|
"needs_review": False,
|
||
|
|
}
|
||
|
|
],
|
||
|
|
"series": [],
|
||
|
|
"anime": [],
|
||
|
|
"other": [],
|
||
|
|
}
|
||
|
|
|
||
|
|
_, stats_first = enrich_identities_data(identities, config)
|
||
|
|
assert stats_first["enriched"] == 1
|
||
|
|
assert stats_first["cache_hits"] == 0
|
||
|
|
assert stats_first["api_calls"] == 1
|
||
|
|
assert provider.calls == 1
|
||
|
|
|
||
|
|
_, stats_second = enrich_identities_data(identities, config)
|
||
|
|
assert stats_second["cache_hits"] == 1
|
||
|
|
assert stats_second["api_calls"] == 0
|
||
|
|
assert provider.calls == 1
|
||
|
|
|
||
|
|
movie = identities["movies"][0]
|
||
|
|
assert movie["title_zh"] == "测试中文名"
|
||
|
|
assert movie["title_en"] == "Test English Title"
|
||
|
|
assert movie["display_title"] == "测试中文名 Test English Title"
|
||
|
|
|
||
|
|
|
||
|
|
def test_enrich_flags_low_reputation_for_review(tmp_path, monkeypatch):
|
||
|
|
"""Low reputation should set needs_review when vote count is high enough."""
|
||
|
|
|
||
|
|
class LowScoreProvider(DummyProvider):
|
||
|
|
def enrich(self, *, title: str, media_type: str, year=None):
|
||
|
|
self.calls += 1
|
||
|
|
return ProviderResult(
|
||
|
|
provider="dummy",
|
||
|
|
canonical_id=f"dummy:{title}",
|
||
|
|
title_zh="低分作品",
|
||
|
|
title_en="Low Score",
|
||
|
|
translation_source="dummy",
|
||
|
|
reputation_score=4.5,
|
||
|
|
reputation_votes=500,
|
||
|
|
reputation_source="dummy",
|
||
|
|
)
|
||
|
|
|
||
|
|
config = Config(
|
||
|
|
library_root=tmp_path,
|
||
|
|
enrichment_cache_db=tmp_path / "cache.db",
|
||
|
|
enrichment_providers=["dummy"],
|
||
|
|
translation_fallback_machine=False,
|
||
|
|
reputation_low_score_threshold=6.0,
|
||
|
|
reputation_min_votes=50,
|
||
|
|
)
|
||
|
|
|
||
|
|
provider = LowScoreProvider()
|
||
|
|
monkeypatch.setattr(
|
||
|
|
"vlm.enrichment._build_providers",
|
||
|
|
lambda _config, request_timeout, retries: [provider],
|
||
|
|
)
|
||
|
|
|
||
|
|
identities = {
|
||
|
|
"metadata": {},
|
||
|
|
"movies": [
|
||
|
|
{
|
||
|
|
"path": "/library/movie/Unknown.2020.mkv",
|
||
|
|
"filename": "Unknown.2020.mkv",
|
||
|
|
"category": "movie",
|
||
|
|
"title": "Unknown",
|
||
|
|
"year": 2020,
|
||
|
|
"confidence": 0.9,
|
||
|
|
"needs_review": False,
|
||
|
|
}
|
||
|
|
],
|
||
|
|
"series": [],
|
||
|
|
"anime": [],
|
||
|
|
"other": [],
|
||
|
|
}
|
||
|
|
|
||
|
|
enrich_identities_data(identities, config)
|
||
|
|
|
||
|
|
assert identities["movies"][0]["needs_review"] is True
|
||
|
|
|
||
|
|
|
||
|
|
def test_enrich_refresh_all_bypasses_cache(tmp_path, monkeypatch):
|
||
|
|
"""refresh_all should not use cache and should invoke provider again."""
|
||
|
|
config = Config(
|
||
|
|
library_root=tmp_path,
|
||
|
|
enrichment_cache_db=tmp_path / "cache.db",
|
||
|
|
enrichment_providers=["dummy"],
|
||
|
|
translation_fallback_machine=False,
|
||
|
|
)
|
||
|
|
|
||
|
|
provider = DummyProvider()
|
||
|
|
monkeypatch.setattr(
|
||
|
|
"vlm.enrichment._build_providers",
|
||
|
|
lambda _config, request_timeout, retries: [provider],
|
||
|
|
)
|
||
|
|
|
||
|
|
identities = {
|
||
|
|
"metadata": {},
|
||
|
|
"movies": [
|
||
|
|
{
|
||
|
|
"path": "/library/movie/Test.2020.mkv",
|
||
|
|
"filename": "Test.2020.mkv",
|
||
|
|
"category": "movie",
|
||
|
|
"title": "Test",
|
||
|
|
"year": 2020,
|
||
|
|
"confidence": 0.9,
|
||
|
|
"needs_review": False,
|
||
|
|
}
|
||
|
|
],
|
||
|
|
"series": [],
|
||
|
|
"anime": [],
|
||
|
|
"other": [],
|
||
|
|
}
|
||
|
|
|
||
|
|
enrich_identities_data(identities, config, refresh_mode="incremental")
|
||
|
|
assert provider.calls == 1
|
||
|
|
|
||
|
|
_, stats = enrich_identities_data(identities, config, refresh_mode="refresh_all")
|
||
|
|
assert provider.calls == 2
|
||
|
|
assert stats["cache_hits"] == 0
|