Fix CI: remove orphaned tests for the deleted retag worker
Removing the old Retag Tool (d91e6a38) deleted core/library/retag.py but left
its tests behind. tests/library/test_retag.py imported the gone module at
module top, so pytest aborted collection of the ENTIRE suite (CI red on every
run). tests/test_lyrics_reembed_from_sidecar.py had two RetagDeps tests for the
same removed dataclass (lazy imports — would fail at runtime once collection
proceeded).
Delete the orphaned test file + drop the two dead RetagDeps tests (the rest of
the lyrics-sidecar file is unrelated and stays). Also drop a pointless
f-string in test_library_retag_job. Suite now collects all 5008 tests.
This commit is contained in:
parent
b5d22bede5
commit
07801aeb5b
1 changed files with 0 additions and 321 deletions
|
|
@ -1,321 +0,0 @@
|
||||||
"""Tests for core/library/retag.py — retag worker."""
|
|
||||||
|
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
import threading
|
|
||||||
from dataclasses import dataclass
|
|
||||||
|
|
||||||
import pytest
|
|
||||||
|
|
||||||
from core.library import retag as ret
|
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
# Fakes
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
class _FakeSpotify:
|
|
||||||
def __init__(self, album=None, tracks=None):
|
|
||||||
self._album = album
|
|
||||||
self._tracks = tracks
|
|
||||||
|
|
||||||
def get_album(self, album_id):
|
|
||||||
return self._album
|
|
||||||
|
|
||||||
def get_album_tracks(self, album_id):
|
|
||||||
return self._tracks
|
|
||||||
|
|
||||||
|
|
||||||
class _FakeDB:
|
|
||||||
def __init__(self, retag_tracks=None):
|
|
||||||
self._tracks = retag_tracks or []
|
|
||||||
self.path_updates = []
|
|
||||||
self.group_updates = []
|
|
||||||
|
|
||||||
def get_retag_tracks(self, group_id):
|
|
||||||
return self._tracks
|
|
||||||
|
|
||||||
def update_retag_track_path(self, track_id, new_path):
|
|
||||||
self.path_updates.append((track_id, new_path))
|
|
||||||
|
|
||||||
def update_retag_group(self, group_id, **kwargs):
|
|
||||||
self.group_updates.append((group_id, kwargs))
|
|
||||||
|
|
||||||
|
|
||||||
def _build_deps(
|
|
||||||
*,
|
|
||||||
spotify_album=None,
|
|
||||||
spotify_tracks=None,
|
|
||||||
retag_tracks=None,
|
|
||||||
state=None,
|
|
||||||
enhance_calls=None,
|
|
||||||
move_calls=None,
|
|
||||||
cover_calls=None,
|
|
||||||
build_path_result=None,
|
|
||||||
):
|
|
||||||
state = state if state is not None else {}
|
|
||||||
enhance_calls = enhance_calls if enhance_calls is not None else []
|
|
||||||
move_calls = move_calls if move_calls is not None else []
|
|
||||||
cover_calls = cover_calls if cover_calls is not None else []
|
|
||||||
db = _FakeDB(retag_tracks=retag_tracks or [])
|
|
||||||
|
|
||||||
deps = ret.RetagDeps(
|
|
||||||
config_manager=type('C', (), {'get': lambda self, k, d=None: d})(),
|
|
||||||
retag_lock=threading.Lock(),
|
|
||||||
spotify_client=_FakeSpotify(album=spotify_album, tracks=spotify_tracks),
|
|
||||||
get_audio_quality_string=lambda fp: 'FLAC 16bit',
|
|
||||||
enhance_file_metadata=lambda fp, ctx, artist, ai: enhance_calls.append((fp, ctx, artist, ai)),
|
|
||||||
build_final_path_for_track=lambda ctx, artist, ai, ext: (
|
|
||||||
(build_path_result if build_path_result is not None else ctx['original_search_result']['title'] + ext),
|
|
||||||
True,
|
|
||||||
),
|
|
||||||
safe_move_file=lambda src, dst: move_calls.append((src, dst)),
|
|
||||||
cleanup_empty_directories=lambda transfer_dir, file_path: None,
|
|
||||||
download_cover_art=lambda ai, dest_dir, ctx: cover_calls.append((ai, dest_dir)),
|
|
||||||
docker_resolve_path=lambda p: p,
|
|
||||||
_get_retag_state=lambda: state,
|
|
||||||
_set_retag_state=lambda v: state.clear() or state.update(v),
|
|
||||||
get_database=lambda: db,
|
|
||||||
)
|
|
||||||
deps._db = db
|
|
||||||
deps._state = state
|
|
||||||
deps._enhance_calls = enhance_calls
|
|
||||||
deps._move_calls = move_calls
|
|
||||||
deps._cover_calls = cover_calls
|
|
||||||
return deps
|
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
# Setup error paths
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
def test_no_album_data_marks_state_error(tmp_path):
|
|
||||||
"""spotify.get_album returning None → state.error_message set, state status='error'."""
|
|
||||||
deps = _build_deps(spotify_album=None)
|
|
||||||
ret.execute_retag('g1', 'alb-1', deps)
|
|
||||||
assert deps._state['status'] == 'error'
|
|
||||||
assert 'Could not fetch album' in deps._state['error_message']
|
|
||||||
|
|
||||||
|
|
||||||
def test_no_album_tracks_marks_state_error():
|
|
||||||
"""spotify.get_album_tracks returning None → error state."""
|
|
||||||
deps = _build_deps(spotify_album={'name': 'A', 'artists': []}, spotify_tracks=None)
|
|
||||||
ret.execute_retag('g1', 'alb-1', deps)
|
|
||||||
assert deps._state['status'] == 'error'
|
|
||||||
|
|
||||||
|
|
||||||
def test_no_existing_tracks_marks_state_error():
|
|
||||||
"""retag_group has no tracks → error state."""
|
|
||||||
deps = _build_deps(
|
|
||||||
spotify_album={'name': 'A', 'artists': [{'name': 'X', 'id': '1'}], 'images': [], 'release_date': '', 'total_tracks': 1},
|
|
||||||
spotify_tracks={'items': [{'name': 'T1', 'track_number': 1, 'disc_number': 1, 'id': 'sp1', 'artists': [], 'duration_ms': 1000}]},
|
|
||||||
retag_tracks=[],
|
|
||||||
)
|
|
||||||
ret.execute_retag('g1', 'alb-1', deps)
|
|
||||||
assert deps._state['status'] == 'error'
|
|
||||||
assert 'No tracks found' in deps._state['error_message']
|
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
# Successful retag — track-number match
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
def test_track_number_match_priority_1(tmp_path):
|
|
||||||
"""Existing track with matching track+disc number → matched even if title differs."""
|
|
||||||
src_file = tmp_path / 'old.flac'
|
|
||||||
src_file.touch()
|
|
||||||
|
|
||||||
deps = _build_deps(
|
|
||||||
spotify_album={'name': 'New Album', 'artists': [{'name': 'Artist A', 'id': 'a1'}],
|
|
||||||
'images': [{'url': 'http://img'}], 'release_date': '2024-01-01', 'total_tracks': 1},
|
|
||||||
spotify_tracks={'items': [{'name': 'Brand New Title', 'track_number': 5,
|
|
||||||
'disc_number': 1, 'id': 'sp5', 'artists': [{'name': 'X'}],
|
|
||||||
'duration_ms': 1000}]},
|
|
||||||
retag_tracks=[{
|
|
||||||
'id': 1,
|
|
||||||
'title': 'Completely Unrelated Old Name',
|
|
||||||
'track_number': 5,
|
|
||||||
'disc_number': 1,
|
|
||||||
'file_path': str(src_file),
|
|
||||||
}],
|
|
||||||
build_path_result=str(tmp_path / 'new.flac'),
|
|
||||||
)
|
|
||||||
|
|
||||||
ret.execute_retag('g1', 'alb-x', deps)
|
|
||||||
|
|
||||||
# Match found via priority 1 (track number) — enhance_file_metadata called
|
|
||||||
assert len(deps._enhance_calls) == 1
|
|
||||||
fp, ctx, artist, _ai = deps._enhance_calls[0]
|
|
||||||
assert fp == str(src_file)
|
|
||||||
assert ctx['original_search_result']['spotify_clean_title'] == 'Brand New Title'
|
|
||||||
# State marks finished
|
|
||||||
assert deps._state['status'] == 'finished'
|
|
||||||
assert deps._state['progress'] == 100
|
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
# Title-similarity fallback (priority 2)
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
def test_title_similarity_fallback_when_no_track_number_match():
|
|
||||||
"""No track-number match → falls back to fuzzy title match."""
|
|
||||||
deps = _build_deps(
|
|
||||||
spotify_album={'name': 'A', 'artists': [{'name': 'X', 'id': '1'}], 'images': [],
|
|
||||||
'release_date': '', 'total_tracks': 1},
|
|
||||||
spotify_tracks={'items': [{'name': 'Hello World', 'track_number': 99,
|
|
||||||
'disc_number': 99, 'id': 'sp1', 'artists': [],
|
|
||||||
'duration_ms': 1000}]},
|
|
||||||
retag_tracks=[{
|
|
||||||
'id': 1,
|
|
||||||
'title': 'Hello World', # title matches
|
|
||||||
'track_number': 1, 'disc_number': 1, # but numbers don't
|
|
||||||
'file_path': '/nonexistent/old.flac',
|
|
||||||
}],
|
|
||||||
)
|
|
||||||
ret.execute_retag('g1', 'alb-x', deps)
|
|
||||||
|
|
||||||
# File doesn't exist so enhance is skipped, but match was made
|
|
||||||
# (state.processed == 1 confirms loop iterated)
|
|
||||||
assert deps._state['processed'] == 1
|
|
||||||
|
|
||||||
|
|
||||||
def test_no_match_skips_track():
|
|
||||||
"""No track-number AND title similarity below 0.6 → no match, no enhance call."""
|
|
||||||
deps = _build_deps(
|
|
||||||
spotify_album={'name': 'A', 'artists': [{'name': 'X', 'id': '1'}], 'images': [],
|
|
||||||
'release_date': '', 'total_tracks': 1},
|
|
||||||
spotify_tracks={'items': [{'name': 'Completely Different', 'track_number': 99,
|
|
||||||
'disc_number': 99, 'id': 'sp1', 'artists': [],
|
|
||||||
'duration_ms': 1000}]},
|
|
||||||
retag_tracks=[{
|
|
||||||
'id': 1,
|
|
||||||
'title': 'Hello World',
|
|
||||||
'track_number': 1, 'disc_number': 1,
|
|
||||||
'file_path': '/nonexistent/old.flac',
|
|
||||||
}],
|
|
||||||
)
|
|
||||||
ret.execute_retag('g1', 'alb-x', deps)
|
|
||||||
|
|
||||||
# No match, no enhance call
|
|
||||||
assert deps._enhance_calls == []
|
|
||||||
assert deps._state['processed'] == 1
|
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
# Missing file
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
def test_missing_file_skipped(tmp_path):
|
|
||||||
"""If the audio file doesn't exist, enhance_file_metadata is NOT called."""
|
|
||||||
deps = _build_deps(
|
|
||||||
spotify_album={'name': 'A', 'artists': [{'name': 'X', 'id': '1'}], 'images': [],
|
|
||||||
'release_date': '', 'total_tracks': 1},
|
|
||||||
spotify_tracks={'items': [{'name': 'T1', 'track_number': 1, 'disc_number': 1,
|
|
||||||
'id': 'sp1', 'artists': [], 'duration_ms': 1000}]},
|
|
||||||
retag_tracks=[{
|
|
||||||
'id': 1, 'title': 'T1', 'track_number': 1, 'disc_number': 1,
|
|
||||||
'file_path': '/this/path/does/not/exist.flac',
|
|
||||||
}],
|
|
||||||
)
|
|
||||||
ret.execute_retag('g1', 'alb-x', deps)
|
|
||||||
|
|
||||||
assert deps._enhance_calls == []
|
|
||||||
assert deps._state['status'] == 'finished'
|
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
# Path move
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
def test_file_moved_when_path_changes(tmp_path):
|
|
||||||
"""When build_final_path_for_track returns a different path, file is moved."""
|
|
||||||
src_file = tmp_path / 'old.flac'
|
|
||||||
src_file.touch()
|
|
||||||
new_path = str(tmp_path / 'subdir' / 'new.flac')
|
|
||||||
|
|
||||||
deps = _build_deps(
|
|
||||||
spotify_album={'name': 'A', 'artists': [{'name': 'X', 'id': '1'}], 'images': [],
|
|
||||||
'release_date': '', 'total_tracks': 1},
|
|
||||||
spotify_tracks={'items': [{'name': 'T1', 'track_number': 1, 'disc_number': 1,
|
|
||||||
'id': 'sp1', 'artists': [], 'duration_ms': 1000}]},
|
|
||||||
retag_tracks=[{
|
|
||||||
'id': 1, 'title': 'T1', 'track_number': 1, 'disc_number': 1,
|
|
||||||
'file_path': str(src_file),
|
|
||||||
}],
|
|
||||||
build_path_result=new_path,
|
|
||||||
)
|
|
||||||
|
|
||||||
ret.execute_retag('g1', 'alb-x', deps)
|
|
||||||
|
|
||||||
assert len(deps._move_calls) == 1
|
|
||||||
assert deps._move_calls[0] == (str(src_file), new_path)
|
|
||||||
assert (1, new_path) in deps._db.path_updates
|
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
# Group record update
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
def test_spotify_album_id_used_for_alphanumeric_id():
|
|
||||||
"""Non-numeric album IDs → spotify_album_id set, itunes_album_id None."""
|
|
||||||
deps = _build_deps(
|
|
||||||
spotify_album={'name': 'A', 'artists': [{'name': 'X', 'id': '1'}], 'images': [],
|
|
||||||
'release_date': '', 'total_tracks': 1},
|
|
||||||
spotify_tracks={'items': [{'name': 'T1', 'track_number': 1, 'disc_number': 1,
|
|
||||||
'id': 'sp1', 'artists': [], 'duration_ms': 1000}]},
|
|
||||||
retag_tracks=[{
|
|
||||||
'id': 1, 'title': 'T1', 'track_number': 1, 'disc_number': 1,
|
|
||||||
'file_path': '/missing.flac',
|
|
||||||
}],
|
|
||||||
)
|
|
||||||
ret.execute_retag('g1', 'spotify_alpha_id_xyz', deps)
|
|
||||||
|
|
||||||
assert len(deps._db.group_updates) == 1
|
|
||||||
_gid, kwargs = deps._db.group_updates[0]
|
|
||||||
assert kwargs['spotify_album_id'] == 'spotify_alpha_id_xyz'
|
|
||||||
assert kwargs['itunes_album_id'] is None
|
|
||||||
|
|
||||||
|
|
||||||
def test_itunes_album_id_used_for_numeric_id():
|
|
||||||
"""Numeric album IDs → itunes_album_id set, spotify_album_id None."""
|
|
||||||
deps = _build_deps(
|
|
||||||
spotify_album={'name': 'A', 'artists': [{'name': 'X', 'id': '1'}], 'images': [],
|
|
||||||
'release_date': '', 'total_tracks': 1},
|
|
||||||
spotify_tracks={'items': [{'name': 'T1', 'track_number': 1, 'disc_number': 1,
|
|
||||||
'id': 'sp1', 'artists': [], 'duration_ms': 1000}]},
|
|
||||||
retag_tracks=[{
|
|
||||||
'id': 1, 'title': 'T1', 'track_number': 1, 'disc_number': 1,
|
|
||||||
'file_path': '/missing.flac',
|
|
||||||
}],
|
|
||||||
)
|
|
||||||
ret.execute_retag('g1', '987654321', deps)
|
|
||||||
|
|
||||||
_gid, kwargs = deps._db.group_updates[0]
|
|
||||||
assert kwargs['itunes_album_id'] == '987654321'
|
|
||||||
assert kwargs['spotify_album_id'] is None
|
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
# Multi-disc detection
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
def test_multi_disc_total_discs_computed():
|
|
||||||
"""total_discs derived from max disc_number across all spotify tracks."""
|
|
||||||
deps = _build_deps(
|
|
||||||
spotify_album={'name': 'A', 'artists': [{'name': 'X', 'id': '1'}], 'images': [],
|
|
||||||
'release_date': '', 'total_tracks': 3},
|
|
||||||
spotify_tracks={'items': [
|
|
||||||
{'name': 'T1', 'track_number': 1, 'disc_number': 1, 'id': 'sp1', 'artists': [], 'duration_ms': 1000},
|
|
||||||
{'name': 'T2', 'track_number': 1, 'disc_number': 2, 'id': 'sp2', 'artists': [], 'duration_ms': 1000},
|
|
||||||
{'name': 'T3', 'track_number': 1, 'disc_number': 3, 'id': 'sp3', 'artists': [], 'duration_ms': 1000},
|
|
||||||
]},
|
|
||||||
retag_tracks=[{
|
|
||||||
'id': 1, 'title': 'T1', 'track_number': 1, 'disc_number': 1,
|
|
||||||
'file_path': '/missing.flac',
|
|
||||||
}],
|
|
||||||
)
|
|
||||||
ret.execute_retag('g1', 'alb-x', deps)
|
|
||||||
|
|
||||||
# Verify multi-disc reflected in retag — group update has total_tracks
|
|
||||||
# (total_discs not stored on group; check via state instead)
|
|
||||||
assert deps._state['status'] == 'finished'
|
|
||||||
Loading…
Reference in a new issue