95 lines
2.8 KiB
Python
95 lines
2.8 KiB
Python
"""Tests for NavidromePlaybackService.proxy_head / proxy_stream."""
|
|
|
|
from unittest.mock import AsyncMock, MagicMock, patch
|
|
|
|
import pytest
|
|
|
|
from services.navidrome_playback_service import NavidromePlaybackService
|
|
from core.exceptions import ExternalServiceError
|
|
|
|
|
|
def _make_service():
|
|
repo = MagicMock()
|
|
service = NavidromePlaybackService(navidrome_repo=repo)
|
|
return service, repo
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_proxy_head_returns_response():
|
|
service, repo = _make_service()
|
|
|
|
from repositories.navidrome_repository import StreamProxyResult
|
|
mock_result = StreamProxyResult(
|
|
status_code=200,
|
|
headers={"Content-Type": "audio/flac", "Content-Length": "12345"},
|
|
media_type="audio/flac",
|
|
body_chunks=None,
|
|
)
|
|
repo.proxy_head_stream = AsyncMock(return_value=mock_result)
|
|
|
|
response = await service.proxy_head("song-1")
|
|
assert response.status_code == 200
|
|
assert response.headers.get("Content-Type") == "audio/flac"
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_proxy_head_raises_on_error():
|
|
service, repo = _make_service()
|
|
repo.proxy_head_stream = AsyncMock(side_effect=ExternalServiceError("Failed to reach Navidrome"))
|
|
|
|
with pytest.raises(ExternalServiceError):
|
|
await service.proxy_head("song-1")
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_proxy_stream_returns_streaming_response():
|
|
service, repo = _make_service()
|
|
|
|
async def fake_chunks():
|
|
yield b"chunk1"
|
|
yield b"chunk2"
|
|
|
|
from repositories.navidrome_repository import StreamProxyResult
|
|
mock_result = StreamProxyResult(
|
|
status_code=200,
|
|
headers={"Content-Type": "audio/mpeg"},
|
|
media_type="audio/mpeg",
|
|
body_chunks=fake_chunks(),
|
|
)
|
|
repo.proxy_get_stream = AsyncMock(return_value=mock_result)
|
|
|
|
response = await service.proxy_stream("song-1", None)
|
|
assert response.status_code == 200
|
|
assert response.media_type == "audio/mpeg"
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_proxy_stream_with_range_header():
|
|
service, repo = _make_service()
|
|
|
|
async def fake_chunks():
|
|
yield b"partial"
|
|
|
|
from repositories.navidrome_repository import StreamProxyResult
|
|
mock_result = StreamProxyResult(
|
|
status_code=206,
|
|
headers={"Content-Type": "audio/mpeg", "Content-Range": "bytes 0-999/5000"},
|
|
media_type="audio/mpeg",
|
|
body_chunks=fake_chunks(),
|
|
)
|
|
repo.proxy_get_stream = AsyncMock(return_value=mock_result)
|
|
|
|
response = await service.proxy_stream("song-1", "bytes=0-999")
|
|
assert response.status_code == 206
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_proxy_stream_raises_416():
|
|
service, repo = _make_service()
|
|
repo.proxy_get_stream = AsyncMock(
|
|
side_effect=ExternalServiceError("416 Range not satisfiable")
|
|
)
|
|
|
|
with pytest.raises(ExternalServiceError, match="416"):
|
|
await service.proxy_stream("song-1", "bytes=9999-")
|