feat: add Spotify authentication and sessions

This commit is contained in:
Justin Visser 2026-08-10 11:31:06 +02:00
parent 000b435b9a
commit cdab1b4dd5
5 changed files with 304 additions and 0 deletions

View file

@ -0,0 +1,45 @@
"""Tests for Spotify PKCE and token handling."""
import asyncio
import base64
import hashlib
import time
import httpx2
from app.adapters.spotify.auth import (
TokenSet,
derive_code_challenge,
refresh_access_token,
)
def test_code_challenge_is_unpadded_base64url_sha256() -> None:
verifier = "a-fixed-code-verifier"
expected = base64.urlsafe_b64encode(hashlib.sha256(verifier.encode()).digest())
challenge = derive_code_challenge(verifier)
assert challenge == expected.rstrip(b"=").decode()
assert "=" not in challenge
def test_refresh_keeps_existing_refresh_token_when_omitted() -> None:
async def run() -> None:
async def handler(request: httpx2.Request) -> httpx2.Response:
assert request.url == "https://accounts.spotify.com/api/token"
return httpx2.Response(200, json={"access_token": "new", "expires_in": 3600})
tokens = TokenSet("old", "keep-me", time.monotonic() + 3600)
async with httpx2.AsyncClient(transport=httpx2.MockTransport(handler)) as http:
refreshed = await refresh_access_token(http, client_id="client", tokens=tokens)
assert refreshed.access_token == "new"
assert refreshed.refresh_token == "keep-me"
asyncio.run(run())
def test_token_expiry_uses_sixty_second_skew() -> None:
assert TokenSet("access", "refresh", time.monotonic() + 59).is_expired
assert not TokenSet("access", "refresh", time.monotonic() + 61).is_expired