diff --git a/tests/test_import_page_album_lookup_pattern.py b/tests/test_import_page_album_lookup_pattern.py deleted file mode 100644 index 5bf4ab9d..00000000 --- a/tests/test_import_page_album_lookup_pattern.py +++ /dev/null @@ -1,132 +0,0 @@ -"""Pin the import-page album-lookup cache pattern in -``webui/static/stats-automations.js`` — github issue #524 regression -guard at the source-text level. - -Why a structural test instead of a behavioral JS test: - -``stats-automations.js`` is a ~7k-line file with a lot of global state -+ inline DOM rendering. Loading it into a sandboxed Node `vm` context -(the pattern used in `tests/static/test_discover_section_controller.mjs`) -would require stubbing dozens of unrelated dependencies. The file -needs to be modularized before behavioral tests are practical for -arbitrary functions in it. - -Until then, this test fails the suite if the critical pattern from -the #524 fix gets removed: - -1. The album cache (``_albumLookup`` field on ``importPageState``) -2. Card renderers populating the cache before emitting the onclick -3. The match-POST builder reading source/name/artist from the cache - -If anyone deletes the cache, the click handler, or the cache writes, -this test catches it before the regression ships. -""" - -from __future__ import annotations - -import re -from pathlib import Path - -import pytest - - -_REPO_ROOT = Path(__file__).resolve().parents[1] -_SOURCE = _REPO_ROOT / "webui" / "static" / "stats-automations.js" - - -@pytest.fixture(scope="module") -def js_source() -> str: - return _SOURCE.read_text(encoding="utf-8") - - -def test_album_lookup_cache_field_exists_on_state(js_source: str): - """importPageState must have an `_albumLookup` field. Without it, - card renderers have nowhere to stash source/name/artist for the - click handler to read.""" - assert "_albumLookup:" in js_source, ( - "importPageState._albumLookup field missing — the album cache " - "that backs the source-routing fix for issue #524 has been " - "removed. The click handler will fall back to passing only " - "album_id and the backend will silently misroute lookups again." - ) - - -def test_select_album_handler_reads_cache(js_source: str): - """importPageSelectAlbum must read source / name / artist from - the cache and include them in the match POST body. The whole - point of the fix.""" - # Find the function body - match = re.search( - r"async function importPageSelectAlbum\([^)]*\) \{(.*?)^\}", - js_source, re.DOTALL | re.MULTILINE, - ) - assert match, "importPageSelectAlbum function not found" - body = match.group(1) - - # Must read from the lookup cache - assert "_albumLookup[" in body, ( - "importPageSelectAlbum no longer reads from " - "importPageState._albumLookup — match POST will drop source " - "again, see issue #524." - ) - - # Must build a matchBody that includes source + album_name + album_artist - for required_field in ("source:", "album_name:", "album_artist:"): - assert required_field in body, ( - f"matchBody missing required field {required_field!r}. " - "Backend's get_artist_album_tracks needs source to route " - "the lookup to the correct metadata client. Without it, " - "cross-source album_ids fall through to the failure-fallback " - "dict (Unknown Artist / album_id-as-title / 0 tracks). " - "See issue #524 for the original symptom." - ) - - -def test_card_renderer_populates_cache_before_onclick(js_source: str): - """The shared card-renderer ``_renderSuggestionCard`` must write to - ``_albumLookup`` before emitting the onclick — otherwise the click - handler reads an empty cache for newly-displayed albums. - - Originally this test required >=2 cache writes (one per inline - renderer), but the search-results inline render was consolidated - into a single ``_renderSuggestionCard`` call as part of the #681 - fix. The invariant now is: the shared renderer populates the cache, - and every render call site goes through it (no inline duplicates).""" - # 1. The shared renderer must contain the cache write. - match = re.search( - r"function _renderSuggestionCard\([^)]*\) \{(.*?)^\}", - js_source, re.DOTALL | re.MULTILINE, - ) - assert match, "_renderSuggestionCard function not found" - body = match.group(1) - assert re.search(r"_albumLookup\[a\.id\]\s*=\s*\{", body), ( - "_renderSuggestionCard no longer writes to _albumLookup before " - "emitting the onclick — every card rendered through this helper " - "would have an empty cache on click, regressing issue #524." - ) - - # 2. No inline card render allowed outside the shared helper. - # A second `_albumLookup[a.id] = {` write means a caller is - # re-implementing the renderer instead of calling the helper — - # that's exactly the duplication the #524 fix consolidated away. - cache_writes = re.findall(r"_albumLookup\[a\.id\]\s*=\s*\{", js_source) - assert len(cache_writes) == 1, ( - f"Expected exactly 1 _albumLookup write (inside _renderSuggestionCard), " - f"found {len(cache_writes)}. A new inline card-render site has " - "duplicated the cache-write logic — route the new caller through " - "_renderSuggestionCard(a, primarySource) instead." - ) - - -def test_cache_entry_carries_source_field(js_source: str): - """The cache must store `source:` per entry — not just id/name/artist.""" - write_blocks = re.findall( - r"_albumLookup\[a\.id\]\s*=\s*\{[^}]*\}", - js_source, - ) - assert write_blocks, "no _albumLookup writes found" - assert any("source:" in block for block in write_blocks), ( - "_albumLookup cache entries must include `source` — that's the " - "field the click handler forwards to /api/import/album/match " - "to route the lookup to the correct provider." - )