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/search/test_search_library_check.py

283 lines
9.3 KiB

"""Tests for core/search/library_check.py — library/wishlist presence + thumb resolution."""
from __future__ import annotations
import json
import pytest
from core.search import library_check
from database.music_database import MusicDatabase
@pytest.fixture
def db(tmp_path):
return MusicDatabase(str(tmp_path / "music.db"))
# ---------------------------------------------------------------------------
# Fakes for plex / config_manager
# ---------------------------------------------------------------------------
class _FakePlexServer:
def __init__(self, base, token):
self._baseurl = base
self._token = token
class _FakePlexClient:
def __init__(self, base='https://plex.local:32400', token='abc123'):
self.server = _FakePlexServer(base, token)
class _NoServerPlexClient:
"""Plex client that hasn't connected yet."""
server = None
class _FakeConfigManager:
def __init__(self, plex_cfg=None):
self._plex_cfg = plex_cfg or {}
def get_plex_config(self):
return dict(self._plex_cfg)
def get(self, key, default=None):
return default
# ---------------------------------------------------------------------------
# DB seed helpers
# ---------------------------------------------------------------------------
_id_counter = {'n': 0}
def _next_id(prefix):
_id_counter['n'] += 1
return f"{prefix}-{_id_counter['n']}"
def _seed_artist(db, name):
aid = _next_id('art')
conn = db._get_connection()
try:
c = conn.cursor()
c.execute("INSERT INTO artists (id, name) VALUES (?, ?)", (aid, name))
conn.commit()
return aid
finally:
conn.close()
def _seed_album(db, artist_id, title, thumb=None):
alb = _next_id('alb')
conn = db._get_connection()
try:
c = conn.cursor()
c.execute(
"INSERT INTO albums (id, artist_id, title, thumb_url) VALUES (?, ?, ?, ?)",
(alb, artist_id, title, thumb),
)
conn.commit()
return alb
finally:
conn.close()
def _seed_track(db, album_id, artist_id, title, file_path=None):
tid = _next_id('trk')
conn = db._get_connection()
try:
c = conn.cursor()
c.execute(
"INSERT INTO tracks (id, album_id, artist_id, title, file_path) VALUES (?, ?, ?, ?, ?)",
(tid, album_id, artist_id, title, file_path),
)
conn.commit()
return tid
finally:
conn.close()
def _seed_wishlist(db, profile_id, name, artist_name):
spotify_data = {'name': name, 'artists': [{'name': artist_name}]}
conn = db._get_connection()
try:
c = conn.cursor()
c.execute("PRAGMA table_info(wishlist_tracks)")
cols = [r[1] for r in c.fetchall()]
if 'profile_id' in cols:
c.execute(
"INSERT INTO wishlist_tracks (spotify_track_id, spotify_data, profile_id) VALUES (?, ?, ?)",
(f"sp-{name}-{artist_name}", json.dumps(spotify_data), profile_id),
)
else:
c.execute(
"INSERT INTO wishlist_tracks (spotify_track_id, spotify_data) VALUES (?, ?)",
(f"sp-{name}-{artist_name}", json.dumps(spotify_data)),
)
conn.commit()
finally:
conn.close()
# ---------------------------------------------------------------------------
# Plex thumb resolution
# ---------------------------------------------------------------------------
def test_resolve_plex_thumb_already_absolute_passes_through():
assert library_check._resolve_plex_thumb('http://x/y.jpg', 'https://plex', 'tok') == 'http://x/y.jpg'
def test_resolve_plex_thumb_relative_gets_base_and_token():
out = library_check._resolve_plex_thumb('/library/x.jpg', 'https://plex.local:32400', 'tok123')
assert out == 'https://plex.local:32400/library/x.jpg?X-Plex-Token=tok123'
def test_resolve_plex_thumb_no_token_omits_query_string():
out = library_check._resolve_plex_thumb('/library/x.jpg', 'https://plex.local:32400', '')
assert out == 'https://plex.local:32400/library/x.jpg'
def test_resolve_plex_thumb_no_base_passes_through():
assert library_check._resolve_plex_thumb('/library/x.jpg', '', 'tok') == '/library/x.jpg'
def test_resolve_plex_thumb_empty_passes_through():
assert library_check._resolve_plex_thumb('', 'https://plex', 'tok') == ''
def test_resolve_plex_credentials_uses_live_client_first():
cfg = _FakeConfigManager({'base_url': 'https://wrong', 'token': 'wrongtok'})
base, token = library_check._resolve_plex_credentials(_FakePlexClient(), cfg)
assert base == 'https://plex.local:32400'
assert token == 'abc123'
def test_resolve_plex_credentials_falls_back_to_config():
cfg = _FakeConfigManager({'base_url': 'https://configured/', 'token': 'cfgtok'})
base, token = library_check._resolve_plex_credentials(_NoServerPlexClient(), cfg)
assert base == 'https://configured'
assert token == 'cfgtok'
def test_resolve_plex_credentials_handles_no_config():
cfg = _FakeConfigManager({})
base, token = library_check._resolve_plex_credentials(_NoServerPlexClient(), cfg)
assert base == ''
assert token == ''
# ---------------------------------------------------------------------------
# check_library_presence — albums
# ---------------------------------------------------------------------------
def test_album_in_library_returns_true(db):
aid = _seed_artist(db, 'Pink Floyd')
_seed_album(db, aid, 'DSOTM')
cfg = _FakeConfigManager({})
result = library_check.check_library_presence(
db, _NoServerPlexClient(), cfg, profile_id=1,
albums=[{'name': 'DSOTM', 'artist': 'Pink Floyd'}],
tracks=[],
)
assert result['albums'] == [True]
def test_album_not_in_library_returns_false(db):
cfg = _FakeConfigManager({})
result = library_check.check_library_presence(
db, _NoServerPlexClient(), cfg, profile_id=1,
albums=[{'name': 'Phantom', 'artist': 'Nobody'}],
tracks=[],
)
assert result['albums'] == [False]
def test_album_lookup_uses_first_artist_in_csv(db):
aid = _seed_artist(db, 'Pink Floyd')
_seed_album(db, aid, 'DSOTM')
cfg = _FakeConfigManager({})
result = library_check.check_library_presence(
db, _NoServerPlexClient(), cfg, profile_id=1,
albums=[{'name': 'DSOTM', 'artist': 'Pink Floyd, Roger Waters'}],
tracks=[],
)
assert result['albums'] == [True]
# ---------------------------------------------------------------------------
# check_library_presence — tracks
# ---------------------------------------------------------------------------
def test_track_in_library_returns_full_match_metadata(db):
aid = _seed_artist(db, 'Pink Floyd')
alb = _seed_album(db, aid, 'DSOTM', thumb='/library/dsotm.jpg')
tid = _seed_track(db, alb, aid, 'Money', file_path='/m/money.flac')
cfg = _FakeConfigManager({})
result = library_check.check_library_presence(
db, _FakePlexClient(), cfg, profile_id=1,
albums=[],
tracks=[{'name': 'Money', 'artist': 'Pink Floyd'}],
)
track = result['tracks'][0]
assert track['in_library'] is True
assert track['track_id'] == tid
assert track['file_path'] == '/m/money.flac'
assert track['title'] == 'Money'
assert track['artist_name'] == 'Pink Floyd'
assert track['album_title'] == 'DSOTM'
assert 'X-Plex-Token=abc123' in track['album_thumb_url']
assert track['album_thumb_url'].startswith('https://plex.local:32400')
def test_track_not_in_library_returns_minimal_shape(db):
cfg = _FakeConfigManager({})
result = library_check.check_library_presence(
db, _NoServerPlexClient(), cfg, profile_id=1,
albums=[],
tracks=[{'name': 'Phantom', 'artist': 'Nobody'}],
)
assert result['tracks'] == [{'in_library': False, 'in_wishlist': False}]
def test_track_in_wishlist_returns_in_wishlist_true(db):
_seed_wishlist(db, profile_id=1, name='HUMBLE.', artist_name='Kendrick Lamar')
cfg = _FakeConfigManager({})
result = library_check.check_library_presence(
db, _NoServerPlexClient(), cfg, profile_id=1,
albums=[],
tracks=[{'name': 'HUMBLE.', 'artist': 'Kendrick Lamar'}],
)
assert result['tracks'][0] == {'in_library': False, 'in_wishlist': True}
def test_track_in_library_and_wishlist_both_set(db):
aid = _seed_artist(db, 'Kendrick Lamar')
alb = _seed_album(db, aid, 'DAMN.')
_seed_track(db, alb, aid, 'HUMBLE.')
_seed_wishlist(db, profile_id=1, name='HUMBLE.', artist_name='Kendrick Lamar')
cfg = _FakeConfigManager({})
result = library_check.check_library_presence(
db, _NoServerPlexClient(), cfg, profile_id=1,
albums=[],
tracks=[{'name': 'HUMBLE.', 'artist': 'Kendrick Lamar'}],
)
assert result['tracks'][0]['in_library'] is True
assert result['tracks'][0]['in_wishlist'] is True
def test_track_artist_csv_uses_first_only(db):
aid = _seed_artist(db, 'Kendrick Lamar')
alb = _seed_album(db, aid, 'DAMN.')
_seed_track(db, alb, aid, 'HUMBLE.', file_path='/x.flac')
cfg = _FakeConfigManager({})
result = library_check.check_library_presence(
db, _NoServerPlexClient(), cfg, profile_id=1,
albums=[],
tracks=[{'name': 'HUMBLE.', 'artist': 'Kendrick Lamar, J. Cole'}],
)
assert result['tracks'][0]['in_library'] is True