feat(dot_alfred/v2): Phase 4 Step 4 — settings + anchor warning
Two small additions that close out Phase 4's loose ends.
Settings — tmdb_cache_ttl_days
class Settings(BaseSettings):
# --- DOT_ALFRED ---
tmdb_cache_ttl_days: int = 14
Default 14 days, matching the dot_alfred_v2 master spec. Will drive
the Phase 5 TTL policy on TVShowLibraryIndexSidecar /
MovieLibraryIndexSidecar (decide when a TMDB-cached entry is stale
and triggers a refresh sync).
Anchor-mismatch warning
DotAlfredTVShowLibraryIndex._load_or_heal and DotAlfredMovieLibraryIndex
._load_or_heal now cross-check each indexed entry's metadata.path
against the on-disk folder layout right after a successful parse.
Drift (sidecar says folder X, X no longer exists under library_root)
is surfaced as a WARNING log — one per missing folder, with the
tmdb_id for cross-reference. No auto-heal on drift; the caller
decides (the heal path remains opt-in via index.heal()).
The warning fires only on the parsed-index path. The heal path
always synthesizes entries from real folder names, so it can never
drift — silent by construction.
Tests
* TestTVShowLibraryIndexAnchorWarning — 3 scenarios:
warn-on-drift / no-warn-on-match / no-warn-on-heal.
* TestMovieLibraryIndexAnchorWarning — symmetric coverage.
Full suite: 1237 passed / 8 skipped / 4 xfailed.
This commit is contained in:
@@ -264,3 +264,81 @@ class TestMovieLibraryIndex:
|
||||
# Placeholder until TMDB sync.
|
||||
assert entry.release_year is None
|
||||
assert any("healing" in r.message for r in caplog.records)
|
||||
|
||||
|
||||
# ════════════════════════════════════════════════════════════════════════════
|
||||
# Anchor-mismatch warnings (parsed-index path only — heal path is silent)
|
||||
# ════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
|
||||
class TestTVShowLibraryIndexAnchorWarning:
|
||||
def test_warns_when_indexed_folder_missing(
|
||||
self, tv_library, foundation_release, foundation_tmdb_info, now_utc, caplog
|
||||
):
|
||||
# Seed the index with a path that exists ("Foundation"), then
|
||||
# delete the folder so the next read detects the drift.
|
||||
index = DotAlfredTVShowLibraryIndex(tv_library)
|
||||
index.upsert(
|
||||
foundation_tmdb_info,
|
||||
foundation_release,
|
||||
path="Foundation",
|
||||
fetched_at=now_utc,
|
||||
)
|
||||
(tv_library / "Foundation").rmdir()
|
||||
with caplog.at_level(logging.WARNING):
|
||||
index.find_by_tmdb_id(TmdbId(84958))
|
||||
assert any(
|
||||
"anchor mismatch" in r.message and "Foundation" in r.message
|
||||
for r in caplog.records
|
||||
)
|
||||
|
||||
def test_no_warning_when_indexed_folder_present(
|
||||
self, tv_library, foundation_release, foundation_tmdb_info, now_utc, caplog
|
||||
):
|
||||
index = DotAlfredTVShowLibraryIndex(tv_library)
|
||||
index.upsert(
|
||||
foundation_tmdb_info,
|
||||
foundation_release,
|
||||
path="Foundation",
|
||||
fetched_at=now_utc,
|
||||
)
|
||||
with caplog.at_level(logging.WARNING):
|
||||
index.find_by_tmdb_id(TmdbId(84958))
|
||||
assert not any(
|
||||
"anchor mismatch" in r.message for r in caplog.records
|
||||
)
|
||||
|
||||
def test_no_warning_on_heal_path(
|
||||
self, tv_library, foundation_release, caplog
|
||||
):
|
||||
# The heal path always synthesizes entries from real folder
|
||||
# names, so it can never drift — no anchor warning should fire.
|
||||
release_repo = DotAlfredSeriesReleaseRepository(tv_library)
|
||||
release_repo.save(foundation_release, show_folder="Foundation")
|
||||
index = DotAlfredTVShowLibraryIndex(tv_library, release_repo=release_repo)
|
||||
with caplog.at_level(logging.WARNING):
|
||||
index.find_by_tmdb_id(TmdbId(84958))
|
||||
assert not any(
|
||||
"anchor mismatch" in r.message for r in caplog.records
|
||||
)
|
||||
|
||||
|
||||
class TestMovieLibraryIndexAnchorWarning:
|
||||
def test_warns_when_indexed_folder_missing(
|
||||
self, movie_library, inception_release, now_utc, caplog
|
||||
):
|
||||
index = DotAlfredMovieLibraryIndex(movie_library)
|
||||
index.upsert(
|
||||
inception_release,
|
||||
name="Inception",
|
||||
release_year=2010,
|
||||
path=inception_release.folder,
|
||||
fetched_at=now_utc,
|
||||
)
|
||||
(movie_library / inception_release.folder).rmdir()
|
||||
with caplog.at_level(logging.WARNING):
|
||||
index.find_by_tmdb_id(TmdbId(27205))
|
||||
assert any(
|
||||
"anchor mismatch" in r.message and inception_release.folder in r.message
|
||||
for r in caplog.records
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user