You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
SoulSync/tests/test_worker_utils_album_tra...

137 lines
4.6 KiB

"""Tests for `worker_utils.set_album_api_track_count` — the shared helper
enrichment workers call to cache authoritative track counts."""
import sqlite3
from core.worker_utils import set_album_api_track_count
class _RecordingCursor:
"""Minimal cursor stand-in that captures execute() calls."""
def __init__(self):
self.calls = []
def execute(self, query, params=None):
self.calls.append((query, params))
# ---------------------------------------------------------------------------
# Happy-path writes
# ---------------------------------------------------------------------------
def test_writes_positive_int_count():
cursor = _RecordingCursor()
set_album_api_track_count(cursor, "album-1", 12)
assert len(cursor.calls) == 1
query, params = cursor.calls[0]
assert "UPDATE albums SET api_track_count = ?" in query
assert "WHERE id = ?" in query
assert params == (12, "album-1")
def test_coerces_numeric_string_to_int():
"""Deezer / raw API dicts often have track counts as strings."""
cursor = _RecordingCursor()
set_album_api_track_count(cursor, "album-2", "14")
assert cursor.calls[0][1] == (14, "album-2")
def test_writes_one_for_single_track_album():
cursor = _RecordingCursor()
set_album_api_track_count(cursor, "album-single", 1)
assert cursor.calls[0][1] == (1, "album-single")
# ---------------------------------------------------------------------------
# Skip-write cases (don't overwrite good values with bad ones)
# ---------------------------------------------------------------------------
def test_skips_write_when_count_is_zero():
"""A source that doesn't report track counts must not clobber a value
written by another source."""
cursor = _RecordingCursor()
set_album_api_track_count(cursor, "album-x", 0)
assert cursor.calls == []
def test_skips_write_when_count_is_none():
cursor = _RecordingCursor()
set_album_api_track_count(cursor, "album-x", None)
assert cursor.calls == []
def test_skips_write_when_count_is_negative():
cursor = _RecordingCursor()
set_album_api_track_count(cursor, "album-x", -1)
assert cursor.calls == []
def test_skips_write_on_non_numeric_string():
cursor = _RecordingCursor()
set_album_api_track_count(cursor, "album-x", "not a number")
assert cursor.calls == []
def test_skips_write_on_non_numeric_object():
cursor = _RecordingCursor()
set_album_api_track_count(cursor, "album-x", object())
assert cursor.calls == []
# ---------------------------------------------------------------------------
# Does not commit — caller owns the transaction
# ---------------------------------------------------------------------------
def test_helper_does_not_commit():
"""Workers batch multiple UPDATEs into one transaction. The helper
must not call commit() or it would break that batching."""
class _StrictCursor(_RecordingCursor):
commits = 0
def commit(self): # pragma: no cover — asserts it's never called
_StrictCursor.commits += 1
cursor = _StrictCursor()
set_album_api_track_count(cursor, "album-y", 5)
assert _StrictCursor.commits == 0
# ---------------------------------------------------------------------------
# Error isolation — a cursor.execute failure must not poison the worker's
# other UPDATEs in the same transaction
# ---------------------------------------------------------------------------
def test_swallows_cursor_execute_errors():
"""If the column doesn't exist yet (e.g., migration hasn't run) or
the DB is otherwise unhappy, the helper must not propagate the error.
Otherwise the worker's other UPDATEs (spotify_album_id, thumb_url,
etc.) batched in the same transaction would roll back."""
class _BrokenCursor:
def execute(self, query, params=None):
raise RuntimeError("no such column: api_track_count")
cursor = _BrokenCursor()
# Should not raise.
set_album_api_track_count(cursor, "album-z", 10)
def test_repairs_missing_api_track_count_column():
conn = sqlite3.connect(":memory:")
cursor = conn.cursor()
cursor.execute("CREATE TABLE albums (id TEXT PRIMARY KEY, title TEXT)")
cursor.execute("INSERT INTO albums (id, title) VALUES ('album-z', 'Album')")
set_album_api_track_count(cursor, "album-z", 10)
cursor.execute("PRAGMA table_info(albums)")
cols = {row[1] for row in cursor.fetchall()}
cursor.execute("SELECT api_track_count FROM albums WHERE id = 'album-z'")
row = cursor.fetchone()
conn.close()
assert 'api_track_count' in cols
assert row[0] == 10