from __future__ import annotations import json import httpx import pytest from somafm_tui import bandcamp TRACK_PAYLOAD = { "auto": { "results": [ { "type": "t", "name": "Rauschebart", "band_name": "Jens Buchert", "album_name": "Gravity", "item_url_root": "https://jensbuchert.bandcamp.com", "item_url_path": ( "https://jensbuchert.bandcamp.com/track/rauschebart?from=search" ), } ] } } ALBUM_PAYLOAD = { "auto": { "results": [ { "type": "a", "name": "Gravity", "band_name": "Jens Buchert", "item_url_root": "https://jensbuchert.bandcamp.com", "item_url_path": ( "https://jensbuchert.bandcamp.com/album/gravity?from=search" ), } ] } } ALBUM_PAGE_HTML = """
""" def test_parse_search_payload() -> None: results = bandcamp.parse_search_payload(TRACK_PAYLOAD) assert len(results) == 1 result = results[0] assert result.title == "Rauschebart" assert result.artist == "Jens Buchert" assert result.album == "Gravity" assert result.item_type == "track" # Tracking parameters are removed. assert result.url == "https://jensbuchert.bandcamp.com/track/rauschebart" def test_parse_search_payload_accepts_custom_domains_and_skips_other_types() -> None: payload = { "auto": { "results": [ { "type": "t", "name": "Rauschebart", "band_name": "Jens Buchert", "album_name": "Gravity", "item_url_path": "https://music.example.com/track/rauschebart", }, { "type": "b", "name": "Jens Buchert", "item_url_root": "https://jensbuchert.bandcamp.com", }, {"type": "t", "name": "", "item_url_path": "https://x.test/track/x"}, ] } } results = bandcamp.parse_search_payload(payload) assert [result.url for result in results] == [ "https://music.example.com/track/rauschebart" ] def test_parse_album_tracks_from_tralbum() -> None: results = bandcamp.parse_album_tracks( ALBUM_PAGE_HTML, "https://jensbuchert.bandcamp.com/album/gravity" ) assert [entry.title for entry in results] == ["Rauschebart", "Other Track"] assert results[0].url == "https://jensbuchert.bandcamp.com/track/rauschebart" def test_parse_album_tracks_from_json_ld_and_anchor_fallback() -> None: json_ld = """ """ results = bandcamp.parse_album_tracks( json_ld, "https://jensbuchert.bandcamp.com/album/gravity" ) assert results[0].url == "https://jensbuchert.bandcamp.com/track/rauschebart" anchors = """ Rauschebart Gravity """ results = bandcamp.parse_album_tracks( anchors, "https://jensbuchert.bandcamp.com/album/gravity" ) assert [entry.title for entry in results] == ["Rauschebart"] def test_pick_track_and_album_ignore_mismatches() -> None: results = bandcamp.parse_search_payload( { "auto": { "results": [ { "type": "a", "name": "Gravity", "band_name": "Jens Buchert", "item_url_path": "https://x.bandcamp.com/album/gravity", }, { "type": "t", "name": "Rauschebart", "band_name": "Someone Else", "album_name": "Other", "item_url_path": "https://other.bandcamp.com/track/rauschebart", }, ] } } ) assert bandcamp.pick_track(results, "Jens Buchert", "Gravity", "Rauschebart") is None assert bandcamp.pick_album(results, "Jens Buchert", "Gravity") == ( "https://x.bandcamp.com/album/gravity" ) assert bandcamp.pick_album(results, "Someone Else", "Gravity") is None @pytest.mark.asyncio async def test_find_track_uses_track_search_first() -> None: requests: list[httpx.Request] = [] def handler(request: httpx.Request) -> httpx.Response: requests.append(request) assert request.method == "POST" assert json.loads(request.content) == { "search_text": "Jens Buchert Rauschebart", "search_filter": "t", "full_page": True, "fan_id": None, } return httpx.Response(200, json=TRACK_PAYLOAD) async with httpx.AsyncClient(transport=httpx.MockTransport(handler)) as client: url = await bandcamp.find_track(client, "Jens Buchert", "Gravity", "Rauschebart") assert url == "https://jensbuchert.bandcamp.com/track/rauschebart" assert len(requests) == 1 @pytest.mark.asyncio async def test_find_track_falls_back_to_album_then_track_on_page() -> None: requests: list[httpx.Request] = [] def handler(request: httpx.Request) -> httpx.Response: requests.append(request) if request.url.path == "/api/bcsearch_public_api/1/autocomplete_elastic": body = json.loads(request.content) if body["search_filter"] == "t": return httpx.Response(200, json={"auto": {"results": []}}) if body["search_filter"] == "a": return httpx.Response(200, json=ALBUM_PAYLOAD) if request.url.path == "/album/gravity": return httpx.Response(200, text=ALBUM_PAGE_HTML) return httpx.Response(404) async with httpx.AsyncClient(transport=httpx.MockTransport(handler)) as client: url = await bandcamp.find_track( client, "Jens Buchert", "Gravity", "Rauschebart" ) assert url == "https://jensbuchert.bandcamp.com/track/rauschebart" assert len(requests) == 3 search_bodies = [json.loads(request.content) for request in requests[:2]] assert [body["search_text"] for body in search_bodies] == [ "Jens Buchert Rauschebart", "Jens Buchert Gravity", ] assert [body["search_filter"] for body in search_bodies] == ["t", "a"] assert requests[2].method == "GET" assert requests[2].url.path == "/album/gravity" @pytest.mark.asyncio async def test_find_track_returns_none_for_challenge_or_no_results() -> None: requests: list[httpx.Request] = [] def challenge(request: httpx.Request) -> httpx.Response: requests.append(request) return httpx.Response(200, text="