soulsync/tests
Broque Thomas cceffbd8ec Honor manually-matched source IDs in per-source enrichment workers
GitHub issue #501 (@Tacobell444). After manually matching an album to
a specific source ID via the match-chip UI, clicking "Enrich" on that
album would fuzzy-search by name and overwrite the manual match with
whatever the search returned — or revert the match status to
``not_found`` if name search missed. Reorganize then read the now-
wrong ID and moved files to the wrong destination.

Root cause was in the per-source enrichment workers'
``_process_*_individual`` methods. Several workers (Spotify, iTunes)
ran search-by-name unconditionally with no check for an existing
stored ID. Others (Deezer, Tidal, Qobuz) skipped on existing-ID but
without refreshing metadata — preserved the ID but didn't actually
honor the user's intent of "use this match to pull fresh data".

Cin-shape lift: same fix needed in 5 workers, so extracted the shared
behavior into ``core/enrichment/manual_match_honoring.py``:

    honor_stored_match(
        db, entity_table, entity_id, id_column,
        client_fetch_fn, on_match_fn, log_prefix,
    ) -> bool

Per-worker variability (DB column name, client fetch method, response
shape) plugs in via callbacks. Workers call the helper at the top of
``_process_album_individual`` / ``_process_track_individual``; if it
returns True, the manual match was honored and the search-by-name
fallback is skipped. If False (no stored ID, fetch failed, or empty
response), the worker's existing search-by-name flow runs as before.

Workers wired:

- spotify_worker — album + track (was overwriting; now honors)
- itunes_worker — album + track (was overwriting; now honors)
- deezer_worker — album + track (was skip-on-id; now refreshes)
- tidal_worker — album + track (was skip-on-id; now refreshes)
- qobuz_worker — album + track (was skip-on-id; now refreshes)

Workers left alone (already correct):

- discogs_worker — already had inline stored-ID fast path that
  refreshes metadata. Same behavior, just inline; refactoring to use
  the shared helper would be churn for zero behavior change.
- audiodb_worker — same — inline fast path with full metadata refresh.
- musicbrainz_worker — preserves existing MBID and marks status,
  which is the correct behavior for MB (the MBID itself is the match
  payload — no separate metadata fetch).
- lastfm_worker / genius_worker — name-based services with no
  source-specific IDs to honor. Inherent re-search per call.

Reorganize fixed indirectly — it always honored stored IDs correctly
via ``library_reorganize._extract_source_ids``. The "Reorganize broken"
symptom was downstream of broken Enrich corrupting the stored ID.

Tests:

- ``tests/enrichment/test_manual_match_honoring.py`` — 11 tests
  pinning the shared helper contract: stored-ID fast path, no-ID
  fallthrough, empty-string treated as no ID, missing row, fetch
  exception caught and falls through, fetch returns None falls
  through, callback exceptions propagate, configurable table +
  column, defensive table-name whitelist.

- Per-worker wiring NOT tested individually — the workers depend
  on live DB / client objects that are heavy to mock. The shared
  helper's contract is pinned; per-worker call sites are short
  enough to verify by code review.

2173/2173 full suite green.

Closes #501.
2026-05-06 19:00:53 -07:00
..
artists Direct ID lookup in Enhance Quality, like Download Discography 2026-05-06 12:05:41 -07:00
automation Surface handler-returned errors in automation last_error 2026-04-30 15:45:28 -07:00
discovery MS Cin-5: Drop per-server globals — engine owns the clients 2026-05-05 18:05:45 -07:00
downloads Add 5 test additions JohnBaumb suggested 2026-05-05 13:04:18 -07:00
enrichment Honor manually-matched source IDs in per-source enrichment workers 2026-05-06 19:00:53 -07:00
imports Migrate album-info builders to typed Album path 2026-05-03 22:53:12 -07:00
library Fix Album Completeness Auto-Fill on Docker / shared-library setups (#476) 2026-05-03 10:11:06 -07:00
media_server Add cross-section dedup to all-libraries listing layer 2026-05-06 16:01:37 -07:00
metadata Fix: maintenance findings badge inflated when scan dedup-skipped 2026-05-04 08:55:13 -07:00
playlists Lift _prepare_stream_task + playlist_explorer_build_tree to core/ 2026-04-29 11:26:07 -07:00
search Cin-6: Rename soulseek_client global → download_orchestrator 2026-05-04 23:23:32 -07:00
stats Lift /api/stats/* and /api/listening-stats/* into core/stats/ 2026-04-27 14:27:03 -07:00
streaming Cin-6: Rename soulseek_client global → download_orchestrator 2026-05-04 23:23:32 -07:00
watchlist Lift _process_watchlist_scan_automatically to core/watchlist/auto_scan.py 2026-04-29 08:17:41 -07:00
wishlist Coerce wishlist track counts before category checks 2026-04-30 21:42:43 +03:00
conftest.py Split metadata source and Spotify status 2026-05-02 22:02:00 +03:00
test_acoustid_scanner.py Fix null-pointer error in acoustid_scanner 2026-04-17 19:17:24 +03:00
test_acoustid_skip_logic.py Fix: tasks showed Completed when file was quarantined 2026-05-03 18:28:32 -07:00
test_album_completeness_job.py Fix: maintenance findings badge inflated when scan dedup-skipped 2026-05-04 08:55:13 -07:00
test_album_mbid_consistency.py Update mbid consistency test mock to match new create_finding bool contract 2026-05-04 09:11:01 -07:00
test_auth_usage_throttle.py test: auth last_used_at write throttle 2026-04-19 15:22:24 -07:00
test_config_save_retry.py Stop config retry tests from writing to the real DB 2026-04-30 14:07:00 -07:00
test_content_filter_regex.py Fix watchlist content filters: live false positives + auto-scan bypass 2026-04-21 19:16:25 -07:00
test_create_finding_dedup_counter.py Fix: maintenance findings badge inflated when scan dedup-skipped 2026-05-04 08:55:13 -07:00
test_dashboard_events.py Realign test file names 2026-04-17 21:25:01 +03:00
test_discogs_collection_source.py Foundation: typed Album/Track/Artist + per-provider converters 2026-05-03 22:21:32 -07:00
test_discogs_track_count.py Discogs: count rows with empty type_ as real tracks too 2026-04-25 08:27:44 -07:00
test_download_orchestrator_soundcloud.py Cin-5: Drop per-source attrs from orchestrator 2026-05-04 23:14:05 -07:00
test_download_plugin_conformance.py Cin-5: Drop per-source attrs from orchestrator 2026-05-04 23:14:05 -07:00
test_downloads_pagination.py Lift shared runtime state into core 2026-04-27 19:54:44 +03:00
test_duplicate_detector_mount_paths.py Filter same-physical-file duplicates from duplicate detector 2026-04-30 14:06:10 -07:00
test_duplicate_detector_slskd_dedup.py Fix: maintenance findings badge inflated when scan dedup-skipped 2026-05-04 08:55:13 -07:00
test_enrichment_services.py Consolidate enrichment bubble routes behind a service registry 2026-05-01 20:14:30 -07:00
test_featured_artist_completion.py Match featured-artist tracks across discography completion 2026-04-30 19:31:11 -07:00
test_hifi_instance_methods.py Fix "no such table: hifi_instances" via defensive lazy-create 2026-05-06 17:49:44 -07:00
test_hls_parsing.py fix: pick OS-specific ffmpeg binary in hls demux fallback test 2026-04-28 21:13:54 -07:00
test_import_singles_parallel.py Restore soulsync logger state between parallel-imports tests 2026-05-01 08:45:17 -07:00
test_integrity_failure_marks_task_failed.py Fix: tasks showed Completed when file was quarantined 2026-05-03 18:28:32 -07:00
test_library_disk_usage.py Add Library Disk Usage card to System Statistics 2026-05-03 20:17:06 -07:00
test_library_reorganize.py Fix CI test failures from incomplete dummy config and encoding 2026-04-19 13:04:55 -07:00
test_library_reorganize_orchestrator.py Reorganize: fix moved-count + status/total UX issues from PR #377 review 2026-04-25 09:07:44 -07:00
test_library_track_identity.py Make extract_external_ids recognize all source-tagging conventions 2026-05-02 18:26:12 -07:00
test_lidarr_download_client.py Fix three Lidarr bugs that prevented it from being a real download source 2026-05-03 15:49:38 -07:00
test_listening_stats_batch_queries.py test: listening stats worker batched query paths 2026-04-19 15:22:25 -07:00
test_missing_cover_art.py Fix: maintenance findings badge inflated when scan dedup-skipped 2026-05-04 08:55:13 -07:00
test_provenance_id_persistence.py Persist source IDs at download time + backfill onto tracks on sync 2026-05-02 17:44:10 -07:00
test_qobuz_credential_sync.py Address Copilot review nits 2026-05-05 11:22:01 -07:00
test_reorganize_db_methods.py Reorganize queue: race + dedupe fixes from kettui review 2026-04-26 08:40:24 -07:00
test_reorganize_queue.py Reorganize queue: race + dedupe fixes from kettui review 2026-04-26 08:40:24 -07:00
test_reorganize_runner.py Library reorganize: FIFO queue with live status panel 2026-04-25 18:01:32 -07:00
test_repair_worker_album_fill.py Fix CI test failures from incomplete dummy config and encoding 2026-04-19 13:04:55 -07:00
test_replaygain_summary_parse.py Fix: ReplayGain wrote same +52 dB gain to every track 2026-05-03 19:08:35 -07:00
test_request_cleanup_timer.py test: api/request periodic cleanup timer 2026-04-19 15:22:25 -07:00
test_runtime_state.py Tighten metadata and import safety 2026-04-27 20:28:05 +03:00
test_script_split_integrity.py Unify artist detail: route source artists to standalone page, retire inline Artists page 2026-04-22 17:00:52 -07:00
test_socketio_cors.py Socket.IO CORS: handle self-review nits 2026-04-26 19:24:43 -07:00
test_soundcloud_client.py Lift shared download dataclasses + boot via singleton factory 2026-05-05 09:08:39 -07:00
test_spa_deep_linking.py Rename Search page id from 'downloads' to 'search', bump to 2.43 2026-04-22 13:22:49 -07:00
test_sync_discovery_scan_events.py Realign test file names 2026-04-17 21:25:01 +03:00
test_tidal_auth_instructions.py Show Tidal callback port (not Spotify's) in auth instructions 2026-04-30 20:54:06 -07:00
test_tidal_auth_redirect_uri.py Honor configured Tidal redirect_uri, drop request-host fallback 2026-05-02 19:20:18 -07:00
test_tidal_search_shortening.py Reject Tidal streams that silently downgrade from the requested quality 2026-04-24 13:12:30 -07:00
test_tool_progress_events.py Realign test file names 2026-04-17 21:25:01 +03:00
test_track_artist_matching.py Add fallback negative-case test for track-artist matching 2026-04-30 17:13:15 -07:00
test_track_number_repair.py Fix CI test failures from incomplete dummy config and encoding 2026-04-19 13:04:55 -07:00
test_track_search_single_query.py test: api_search_tracks single-query track search 2026-04-19 15:22:24 -07:00
test_unknown_artist_fixer.py Fix test using old deezer_track_id column name 2026-04-20 21:11:45 -07:00
test_watchlist_album_match.py Stop watchlist re-downloading compilation tracks; catch slskd dedup orphans 2026-05-01 12:57:50 -07:00
test_watchlist_bulk_add.py Bulk watchlist add: fall back through every source ID, not just active 2026-04-30 20:27:42 -07:00
test_watchlist_scanner_scan.py Move the import flows and related post-processing pipelines into separate modules 2026-04-27 19:54:43 +03:00
test_websocket_client_transport.py Repoint websocket transport test to core.js after split 2026-04-22 11:30:39 -07:00
test_websocket_infrastructure.py Split metadata source and Spotify status 2026-05-02 22:02:00 +03:00
test_wishlist_pagination.py test: wishlist SQL pagination and category filter 2026-04-19 15:22:24 -07:00
test_worker_existing_id_marks_matched.py test: enrichment worker re-processing fix and migration backfill 2026-04-19 15:22:24 -07:00
test_worker_utils_album_track_count.py Fix Album Completeness job reporting zero findings for every album 2026-04-24 12:39:41 -07:00