45 lines
1.4 KiB
Python
45 lines
1.4 KiB
Python
"""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
|