mirror of
https://github.com/got-feedBack/feedBack.git
synced 2026-08-11 03:09:57 +00:00
refactor(server): move the metadata-enrichment subsystem into lib/enrichment.py (R3) (#861)
* refactor(server): move the metadata-enrichment subsystem into lib/enrichment.py (R3)
MusicBrainz / Cover-Art-Archive / AcoustID transport, the match-scorer glue, and
the background enrichment worker (~930 lines, 61 defs) leave server.py as one
cohesive unit. Bodies are verbatim; the only changes are seam reads:
meta_db / config_dir / sloppak_cache_dir / art_cache_dir -> appstate.<slot>
_song_pack_art_exists / _art_override_paths (stay in server.py for the art +
delete routes) -> appstate.<callable> (new seam slots, injected by reference)
_env_flag -> env_compat.env_flag_compat (the existing identical helper)
_artist_title_from_filename -> imported from metadata_db (its home)
the User-Agent VERSION lookup: Path(__file__).parent ->
Path(__file__).resolve().parents[1] (lib/enrichment.py -> app root)
server.py drives the worker through the module (import enrichment; the routes +
scan lifecycle call enrichment.X). Tests that faked the network on `server`
(_mb_http_get, _enrich_network_enabled, _caa_http_get, ...) now patch the same
names on `enrichment` — the module attribute is resolved at call time, so one
setattr reaches both the routes and the worker's internal callers. Acyclic:
enrichment imports appstate/appconfig/dlc_paths/metadata_db/mb_match/
acoustid_match/sloppak/loosefolder, never server.
server.py: 6,917 -> 5,988 (-929).
Verified: pyflakes clean (bar the pre-existing File/safe_join/tuning_name/ET);
route table IDENTICAL (143); full pytest 2400 passed (140 enrichment/art cases
incl the offline-safety + transport-error-pauses-pass contracts that fake the
network); test_packaging 44 passed (enrichment.py resolves under lib/); eslint 0.
Boot smoke: /api/enrichment/status + POST /kick serve from the new module.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* test: reset enrichment worker state between tests (CodeRabbit)
lib/enrichment.py now owns the worker, and it stays imported for the whole
session while the `server` fixtures pop-and-reimport `server` — so the cancel
Event / status dict / caches would leak across tests, and a stale `_enrich_cancel`
could short-circuit a later direct `_background_enrich()`. An autouse conftest
fixture clears that process-global state before each test.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* test: tighten the enrichment-reset fixture (CodeRabbit)
Narrow the import guard to ImportError (not blind Exception, BLE001), and stop
clearing _caa_index_locks — it's guarded by _caa_index_locks_guard, so an
unlocked clear() would race a still-alive worker, and its per-release mutexes
carry no test state.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
508829c012
commit
165475d115
@@ -8,6 +8,7 @@ flag is only force-enabled where a test needs the pipeline to run.
|
||||
"""
|
||||
|
||||
import importlib
|
||||
import enrichment
|
||||
import io as _io
|
||||
import sys
|
||||
|
||||
@@ -57,8 +58,8 @@ class FakeMB:
|
||||
@pytest.fixture()
|
||||
def mb(server, monkeypatch):
|
||||
fake = FakeMB()
|
||||
monkeypatch.setattr(server, "_mb_http_get", fake)
|
||||
monkeypatch.setattr(server, "_enrich_network_enabled", lambda: True)
|
||||
monkeypatch.setattr(enrichment, "_mb_http_get", fake)
|
||||
monkeypatch.setattr(enrichment, "_enrich_network_enabled", lambda: True)
|
||||
return fake
|
||||
|
||||
|
||||
@@ -116,13 +117,13 @@ def test_musicbrainz_source_off_stamps_without_matching(server, mb, client):
|
||||
client.post("/api/settings", json={"enrich_src_musicbrainz": False})
|
||||
_put(server, "a.sloppak")
|
||||
mb.search_response = {"recordings": [mb_doc()]}
|
||||
server._background_enrich()
|
||||
enrichment._background_enrich()
|
||||
assert mb.calls == []
|
||||
row = server.meta_db.get_enrichment("a.sloppak")
|
||||
assert row["match_state"] == "unscanned" # hash stamped, no match
|
||||
# Re-enabling picks the same row up on the next pass.
|
||||
client.post("/api/settings", json={"enrich_src_musicbrainz": True})
|
||||
server._background_enrich()
|
||||
enrichment._background_enrich()
|
||||
assert server.meta_db.get_enrichment("a.sloppak")["match_state"] == "matched"
|
||||
|
||||
|
||||
@@ -133,7 +134,7 @@ def test_field_toggles_strip_auto_applied_fields(server, mb, client):
|
||||
"enrich_apply_genres": False})
|
||||
_put(server, "a.sloppak")
|
||||
mb.search_response = {"recordings": [mb_doc()]}
|
||||
server._background_enrich()
|
||||
enrichment._background_enrich()
|
||||
row = server.meta_db.get_enrichment("a.sloppak")
|
||||
assert row["match_state"] == "matched"
|
||||
assert row["canon_artist"] == "AC/DC"
|
||||
@@ -150,7 +151,7 @@ def test_names_toggle_keeps_ids_and_other_fields(server, mb, client):
|
||||
client.post("/api/settings", json={"enrich_apply_names": False})
|
||||
_put(server, "a.sloppak")
|
||||
mb.search_response = {"recordings": [mb_doc()]}
|
||||
server._background_enrich()
|
||||
enrichment._background_enrich()
|
||||
row = server.meta_db.get_enrichment("a.sloppak")
|
||||
assert row["match_state"] == "matched"
|
||||
assert row["canon_artist"] is None
|
||||
@@ -170,7 +171,7 @@ def test_review_accept_applies_all_fields_despite_toggles(server, mb, client):
|
||||
# Partial artist agreement → review tier (candidates stored unfiltered).
|
||||
_put(server, "a.sloppak", artist="AC/DC ft Nobody")
|
||||
mb.search_response = {"recordings": [mb_doc()]}
|
||||
server._background_enrich()
|
||||
enrichment._background_enrich()
|
||||
assert server.meta_db.get_enrichment("a.sloppak")["match_state"] == "review"
|
||||
r = client.post("/api/enrichment/review/a.sloppak/accept",
|
||||
json={"recording_id": "rec-1"})
|
||||
@@ -192,21 +193,21 @@ def test_reenabling_field_backfills_matched_row(server, mb, client):
|
||||
client.post("/api/settings", json={"enrich_apply_year": False})
|
||||
_put(server, "a.sloppak")
|
||||
mb.search_response = {"recordings": [mb_doc()]}
|
||||
server._background_enrich()
|
||||
enrichment._background_enrich()
|
||||
row = server.meta_db.get_enrichment("a.sloppak")
|
||||
assert row["match_state"] == "matched"
|
||||
assert row["canon_year"] is None # suppressed
|
||||
assert row["apply_mask"] == "enrich_apply_year" # …and remembered
|
||||
# Re-enable → next pass re-queues and backfills the year (hash unchanged).
|
||||
client.post("/api/settings", json={"enrich_apply_year": True})
|
||||
server._background_enrich()
|
||||
enrichment._background_enrich()
|
||||
row = server.meta_db.get_enrichment("a.sloppak")
|
||||
assert row["match_state"] == "matched"
|
||||
assert row["canon_year"] == "1990" # backfilled
|
||||
assert row["apply_mask"] in (None, "") # fully applied now
|
||||
# Converged: a fully-applied row is not re-queued again.
|
||||
assert server.meta_db.enrichment_pending(
|
||||
allowed_keys=frozenset(server._ENRICH_APPLY_FIELDS)) == []
|
||||
allowed_keys=frozenset(enrichment._ENRICH_APPLY_FIELDS)) == []
|
||||
|
||||
|
||||
def test_partial_match_is_not_a_cache_donor(server):
|
||||
@@ -271,8 +272,8 @@ def caa(server, monkeypatch):
|
||||
calls.append(release_id)
|
||||
return art.get(release_id)
|
||||
fake.calls = calls
|
||||
monkeypatch.setattr(server, "_caa_http_get", fake)
|
||||
monkeypatch.setattr(server, "_enrich_network_enabled", lambda: True)
|
||||
monkeypatch.setattr(enrichment, "_caa_http_get", fake)
|
||||
monkeypatch.setattr(enrichment, "_enrich_network_enabled", lambda: True)
|
||||
return fake
|
||||
|
||||
|
||||
@@ -280,13 +281,13 @@ def test_caa_source_toggle_gates_art_fetch(server, client, caa):
|
||||
make_sloppak(server, "a.sloppak")
|
||||
_match_row(server, "a.sloppak")
|
||||
client.post("/api/settings", json={"enrich_src_caa": False})
|
||||
server._background_enrich()
|
||||
enrichment._background_enrich()
|
||||
assert caa.calls == []
|
||||
row = server.meta_db.get_enrichment("a.sloppak")
|
||||
assert row["art_state"] is None # not forfeited, just skipped
|
||||
# Re-enable → the same row is picked up.
|
||||
client.post("/api/settings", json={"enrich_src_caa": True})
|
||||
server._background_enrich()
|
||||
enrichment._background_enrich()
|
||||
assert server.meta_db.get_enrichment("a.sloppak")["art_state"] == "caa"
|
||||
|
||||
|
||||
@@ -294,7 +295,7 @@ def test_apply_art_toggle_gates_art_fetch(server, client, caa):
|
||||
make_sloppak(server, "a.sloppak")
|
||||
_match_row(server, "a.sloppak")
|
||||
client.post("/api/settings", json={"enrich_apply_art": False})
|
||||
server._background_enrich()
|
||||
enrichment._background_enrich()
|
||||
assert caa.calls == []
|
||||
assert server.meta_db.get_enrichment("a.sloppak")["art_state"] is None
|
||||
|
||||
|
||||
Reference in New Issue
Block a user