"""Integration tests for the lazy-materialization foundation: ``TrackRepository.materialize`` and ``Album``/``ArtistRepository.get_or_create_remote``. Requires a reachable Postgres; skips otherwise (same pattern as ``test_upload_stream_api.py``). """ import asyncio import uuid from collections.abc import AsyncIterator import pytest from app.infrastructure.db import Base, dispose_engine, get_engine, session_scope from app.infrastructure.db.models.enums import TrackAvailability from app.infrastructure.db.repositories import ( SqlAlchemyAlbumRepository, SqlAlchemyArtistRepository, SqlAlchemyTrackRepository, ) pytestmark = pytest.mark.asyncio _db_reachable_cache: bool | None = None async def _db_reachable() -> bool: global _db_reachable_cache if _db_reachable_cache is not None: return _db_reachable_cache from sqlalchemy import text try: async with asyncio.timeout(3): async with get_engine().connect() as conn: await conn.execute(text("SELECT 1")) _db_reachable_cache = True except Exception: _db_reachable_cache = False return _db_reachable_cache @pytest.fixture async def db() -> AsyncIterator[None]: if not await _db_reachable(): pytest.skip("Postgres not reachable — integration test skipped.") async with get_engine().begin() as conn: await conn.run_sync(Base.metadata.drop_all) await conn.run_sync(Base.metadata.create_all) yield None async with get_engine().begin() as conn: await conn.run_sync(Base.metadata.drop_all) await dispose_engine() async def test_placeholder_track_materializes_in_place(db: None) -> None: """A remote placeholder (no storage) gets its audio fields filled in by ``materialize`` without changing ``track.id`` — the stable content id that likes/playlists/queue may already reference.""" async with session_scope() as session: artists = SqlAlchemyArtistRepository(session) tracks = SqlAlchemyTrackRepository(session) artist = await artists.get_or_create("Some Artist") track_id = uuid.uuid4() placeholder = await tracks.add( id=track_id, title="Remote Track", artist_id=artist.id, storage_uri=None, file_format=None, file_size=None, source="youtube", source_id="abc123", metadata_status="pending", added_by=None, availability=TrackAvailability.REMOTE.value, ) assert placeholder.availability == "remote" assert placeholder.storage_uri is None materialized = await tracks.materialize( track_id, storage_uri="tracks/ab/abc123.m4a", file_format="m4a", file_size=12345, bitrate=160, ) assert materialized.id == track_id assert materialized.availability == "local" assert materialized.storage_uri == "tracks/ab/abc123.m4a" assert materialized.file_format == "m4a" assert materialized.file_size == 12345 async def test_artist_get_or_create_remote_dedups_by_remote_id(db: None) -> None: async with session_scope() as session: artists = SqlAlchemyArtistRepository(session) first = await artists.get_or_create_remote( name="Daft Punk", source="youtube", source_id="UCabc" ) again = await artists.get_or_create_remote( name="Daft Punk (different display name)", source="youtube", source_id="UCabc" ) assert first.id == again.id assert again.source == "youtube" assert again.source_id == "UCabc" async def test_artist_get_or_create_remote_binds_existing_local_artist(db: None) -> None: async with session_scope() as session: artists = SqlAlchemyArtistRepository(session) local = await artists.get_or_create("Pink Floyd") remote = await artists.get_or_create_remote( name="Pink Floyd", source="youtube", source_id="UCxyz" ) assert remote.id == local.id assert remote.source == "youtube" assert remote.source_id == "UCxyz" async def test_album_get_or_create_remote_dedups_by_remote_id(db: None) -> None: async with session_scope() as session: artists = SqlAlchemyArtistRepository(session) albums = SqlAlchemyAlbumRepository(session) artist = await artists.get_or_create("Daft Punk") first = await albums.get_or_create_remote( title="Discovery", artist_id=artist.id, year=2001, musicbrainz_id=None, source="youtube", source_id="MPREb_abc", ) again = await albums.get_or_create_remote( title="Discovery", artist_id=artist.id, year=None, musicbrainz_id=None, source="youtube", source_id="MPREb_abc", ) assert first.id == again.id assert again.source == "youtube" assert again.source_id == "MPREb_abc" assert again.year == 2001