fix(plex,audit): E30 review round 2 — only a missing file is a dead headshot, one spelling per portrait

- warm_portraits: only 404/410 mark a headshot dead (measured: Plex answers a dead CDN file 404);
  a refused token or a 5xx is Plex failing, ends the run after three in a row and marks nothing.
  'failed' counts every failure, not only the current streak.
- is_portrait_url: plain path segments only — no dot or empty segment an HTTP client folds onto
  one file; person_thumb_url uses the same predicate (27 852 stored headshots all pass).
- Tests: Plex refusing (401/403/429/5xx), failures between successes (portraits and bios), the kept
  original served from cache, the audit_clear / demo_reset / discovery_purge rows' counts as data,
  and the 0094 backfill on real rows.
This commit is contained in:
2026-10-01 08:31:03 +02:00
parent fa55e01ef0
commit 6ac9ffd45e
4 changed files with 141 additions and 23 deletions
+39
View File
@@ -6,10 +6,14 @@ Uses the DB lane (`db` fixture) — skipped when no Postgres is reachable. Runs
(migrations 0063/0064) so the JSONB + STORED generated columns are exercised.
"""
from alembic import command
from alembic.config import Config
from sqlalchemy import func, select
from sqlalchemy.orm import Session
import app.plex.sync as syncmod
from app import sysconfig
from app.db import engine
from app.models import PlexItem, PlexLibrary, PlexShow
from app.plex.genres import hu_genre, hu_genres
from app.plex.sync import _apply_item, _canonical_guid, _sync_overlay
@@ -378,3 +382,38 @@ def test_the_genre_map_is_idempotent_and_keeps_an_unknown_word():
assert once == ["Dráma", "Akció"]
assert hu_genres(once) == once
assert hu_genres(None) is None and hu_genres([]) is None
def test_0094_gives_every_row_without_a_hungarian_list_one(db):
cfg = Config("alembic.ini")
command.downgrade(cfg, "0093_plex_person_known_for_votes")
upgraded = False
try:
with Session(engine) as s:
lib = PlexLibrary(plex_key="1", title="Movies", kind="movie", enabled=True)
s.add(lib)
s.flush()
rows = {
"bare": PlexItem(genres=["Action", "Biography"]),
"overlaid": PlexItem(
genres=["Action"], genres_hu=["Kaland"]
), # its twin's own list
"genreless": PlexItem(genres=[]),
}
for key, row in rows.items():
row.rating_key, row.library_id, row.kind, row.title = key, lib.id, "movie", key
show = PlexShow(rating_key="s", library_id=lib.id, title="s", genres=["Comedy"])
s.add_all([*rows.values(), show])
s.commit()
ids = {k: r.id for k, r in rows.items()}
show_id = show.id
command.upgrade(cfg, "head")
upgraded = True
with Session(engine) as s:
got = {k: s.get(PlexItem, i).genres_hu for k, i in ids.items()} # type: ignore[union-attr]
show_hu = s.get(PlexShow, show_id).genres_hu # type: ignore[union-attr]
assert got == {"bare": ["Akció", "Életrajzi"], "overlaid": ["Kaland"], "genreless": None}
assert show_hu == ["Vígjáték"]
finally:
if not upgraded:
command.upgrade(cfg, "head")