A kind of initial commit

This commit is contained in:
heboba
2026-02-26 19:33:05 +00:00
commit 9ab125b1a6
37 changed files with 3053 additions and 0 deletions

View File

@@ -0,0 +1,136 @@
from __future__ import annotations
import unittest
from types import SimpleNamespace
import httpx
from app.clients.spotify import SpotifyClient
class RecordingHttpClient:
def __init__(self, responses: list[httpx.Response]) -> None:
self._responses = list(responses)
self.calls: list[dict] = []
async def request(self, method: str, url: str, **kwargs):
self.calls.append(
{
"method": method,
"url": url,
"headers": kwargs.get("headers"),
"params": kwargs.get("params"),
"json": kwargs.get("json"),
}
)
if not self._responses:
raise AssertionError("No queued response for request")
return self._responses.pop(0)
def make_response(method: str, url: str, status_code: int, *, json_body=None, text_body: str | None = None) -> httpx.Response:
request = httpx.Request(method, url)
if json_body is not None:
return httpx.Response(status_code, request=request, json=json_body)
return httpx.Response(status_code, request=request, text=text_body or "")
def make_client(http_client: RecordingHttpClient) -> SpotifyClient:
settings = SimpleNamespace(
spotify_client_id="id",
spotify_client_secret="secret",
spotify_redirect_uri="https://example.test/callback",
)
return SpotifyClient(settings, http_client) # type: ignore[arg-type]
class SpotifyClientPlaylistTests(unittest.IsolatedAsyncioTestCase):
async def test_create_playlist_uses_me_endpoint(self) -> None:
http_client = RecordingHttpClient(
[
make_response(
"POST",
"https://api.spotify.com/v1/me/playlists",
201,
json_body={
"id": "pl1",
"name": "Test Playlist",
"external_urls": {"spotify": "https://open.spotify.com/playlist/pl1"},
},
)
]
)
client = make_client(http_client)
payload = await client.create_playlist(
"token",
user_id="user123",
name="Test Playlist",
description="desc",
public=False,
)
self.assertEqual(payload["id"], "pl1")
self.assertEqual(len(http_client.calls), 1)
call = http_client.calls[0]
self.assertEqual(call["method"], "POST")
self.assertEqual(call["url"], "https://api.spotify.com/v1/me/playlists")
self.assertEqual(call["json"], {"name": "Test Playlist", "description": "desc", "public": False})
self.assertEqual(call["headers"]["Authorization"], "Bearer token")
async def test_delete_playlist_calls_followers_delete(self) -> None:
http_client = RecordingHttpClient(
[
make_response(
"DELETE",
"https://api.spotify.com/v1/playlists/pl123/followers",
200,
text_body="",
)
]
)
client = make_client(http_client)
await client.delete_playlist("token", "pl123")
self.assertEqual(len(http_client.calls), 1)
call = http_client.calls[0]
self.assertEqual(call["method"], "DELETE")
self.assertEqual(call["url"], "https://api.spotify.com/v1/playlists/pl123/followers")
self.assertEqual(call["headers"]["Authorization"], "Bearer token")
self.assertIsNone(call["json"])
async def test_add_playlist_items_uses_items_endpoint_and_chunks(self) -> None:
uris = [f"spotify:track:{i:02d}" for i in range(101)]
http_client = RecordingHttpClient(
[
make_response(
"POST",
"https://api.spotify.com/v1/playlists/pl999/items",
201,
json_body={"snapshot_id": "snap1"},
),
make_response(
"POST",
"https://api.spotify.com/v1/playlists/pl999/items",
201,
json_body={"snapshot_id": "snap2"},
),
]
)
client = make_client(http_client)
await client.add_playlist_items("token", "pl999", uris)
self.assertEqual(len(http_client.calls), 2)
first, second = http_client.calls
self.assertEqual(first["method"], "POST")
self.assertEqual(first["url"], "https://api.spotify.com/v1/playlists/pl999/items")
self.assertEqual(second["url"], "https://api.spotify.com/v1/playlists/pl999/items")
self.assertEqual(len(first["json"]["uris"]), 100)
self.assertEqual(len(second["json"]["uris"]), 1)
self.assertEqual(first["headers"]["Authorization"], "Bearer token")
if __name__ == "__main__":
unittest.main()