577 lines
21 KiB
Python
577 lines
21 KiB
Python
"""Tests for favorite API endpoints."""
|
|
|
|
import pytest
|
|
import pytest_asyncio
|
|
from httpx import AsyncClient
|
|
from sqlmodel.ext.asyncio.session import AsyncSession
|
|
|
|
from app.models.favorite import Favorite
|
|
from app.models.playlist import Playlist
|
|
from app.models.sound import Sound
|
|
|
|
|
|
class TestFavoriteEndpoints:
|
|
"""Test favorite API endpoints."""
|
|
|
|
@pytest_asyncio.fixture
|
|
async def test_sound(
|
|
self,
|
|
test_session: AsyncSession,
|
|
) -> Sound:
|
|
"""Create a test sound."""
|
|
sound = Sound(
|
|
filename="test_sound.mp3",
|
|
name="Test Sound",
|
|
type="SDB",
|
|
duration=5000, # 5 seconds in ms
|
|
size=1024000, # 1MB
|
|
hash="abcdef123456789",
|
|
)
|
|
test_session.add(sound)
|
|
await test_session.commit()
|
|
await test_session.refresh(sound)
|
|
return sound
|
|
|
|
@pytest_asyncio.fixture
|
|
async def test_sound2(
|
|
self,
|
|
test_session: AsyncSession,
|
|
) -> Sound:
|
|
"""Create a second test sound."""
|
|
sound = Sound(
|
|
filename="test_sound2.mp3",
|
|
name="Test Sound 2",
|
|
type="TTS",
|
|
duration=3000, # 3 seconds in ms
|
|
size=512000, # 512KB
|
|
hash="xyz789456123abc",
|
|
)
|
|
test_session.add(sound)
|
|
await test_session.commit()
|
|
await test_session.refresh(sound)
|
|
return sound
|
|
|
|
@pytest_asyncio.fixture
|
|
async def test_playlist(
|
|
self,
|
|
test_session: AsyncSession,
|
|
) -> Playlist:
|
|
"""Create a test playlist."""
|
|
playlist = Playlist(
|
|
user_id=1, # Use hardcoded ID
|
|
name="Test Playlist",
|
|
description="A test playlist",
|
|
genre="test",
|
|
is_main=False,
|
|
is_current=False,
|
|
is_deletable=True,
|
|
)
|
|
test_session.add(playlist)
|
|
await test_session.commit()
|
|
await test_session.refresh(playlist)
|
|
return playlist
|
|
|
|
@pytest_asyncio.fixture
|
|
async def test_playlist2(
|
|
self,
|
|
test_session: AsyncSession,
|
|
) -> Playlist:
|
|
"""Create a second test playlist."""
|
|
playlist = Playlist(
|
|
user_id=1, # Use hardcoded ID
|
|
name="Test Playlist 2",
|
|
description="Another test playlist",
|
|
genre="test2",
|
|
is_main=False,
|
|
is_current=False,
|
|
is_deletable=True,
|
|
)
|
|
test_session.add(playlist)
|
|
await test_session.commit()
|
|
await test_session.refresh(playlist)
|
|
return playlist
|
|
|
|
@pytest_asyncio.fixture
|
|
async def existing_sound_favorite(
|
|
self,
|
|
test_session: AsyncSession,
|
|
) -> Favorite:
|
|
"""Create an existing sound favorite."""
|
|
favorite = Favorite(
|
|
user_id=1, # Use hardcoded ID
|
|
sound_id=1, # Use hardcoded ID
|
|
)
|
|
test_session.add(favorite)
|
|
await test_session.commit()
|
|
await test_session.refresh(favorite)
|
|
return favorite
|
|
|
|
@pytest_asyncio.fixture
|
|
async def existing_playlist_favorite(
|
|
self,
|
|
test_session: AsyncSession,
|
|
) -> Favorite:
|
|
"""Create an existing playlist favorite."""
|
|
favorite = Favorite(
|
|
user_id=1, # Use hardcoded ID
|
|
playlist_id=1, # Use hardcoded ID
|
|
)
|
|
test_session.add(favorite)
|
|
await test_session.commit()
|
|
await test_session.refresh(favorite)
|
|
return favorite
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_add_sound_favorite_success(
|
|
self,
|
|
authenticated_client: AsyncClient,
|
|
test_sound: Sound,
|
|
) -> None:
|
|
"""Test successfully adding a sound to favorites."""
|
|
# Clean up any existing favorite first
|
|
try:
|
|
await authenticated_client.delete("/api/v1/favorites/sounds/1")
|
|
except:
|
|
pass # It's ok if it doesn't exist
|
|
|
|
response = await authenticated_client.post("/api/v1/favorites/sounds/1")
|
|
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert "id" in data
|
|
assert data["sound_id"] == 1
|
|
assert data["playlist_id"] is None
|
|
assert "created_at" in data
|
|
assert "updated_at" in data
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_add_sound_favorite_not_found(
|
|
self,
|
|
authenticated_client: AsyncClient,
|
|
) -> None:
|
|
"""Test adding a favorite for non-existent sound."""
|
|
response = await authenticated_client.post("/api/v1/favorites/sounds/99999")
|
|
|
|
assert response.status_code == 404
|
|
assert "Sound with ID 99999 not found" in response.json()["detail"]
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_add_sound_favorite_already_exists(
|
|
self,
|
|
authenticated_client: AsyncClient,
|
|
existing_sound_favorite: Favorite,
|
|
test_sound: Sound,
|
|
) -> None:
|
|
"""Test adding a sound favorite that already exists."""
|
|
response = await authenticated_client.post(f"/api/v1/favorites/sounds/{test_sound.id}")
|
|
|
|
assert response.status_code == 409
|
|
assert "already favorited" in response.json()["detail"]
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_add_playlist_favorite_success(
|
|
self,
|
|
authenticated_client: AsyncClient,
|
|
test_playlist: Playlist,
|
|
) -> None:
|
|
"""Test successfully adding a playlist to favorites."""
|
|
# Clean up any existing favorite first
|
|
try:
|
|
await authenticated_client.delete("/api/v1/favorites/playlists/1")
|
|
except:
|
|
pass # It's ok if it doesn't exist
|
|
|
|
response = await authenticated_client.post("/api/v1/favorites/playlists/1")
|
|
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert "id" in data
|
|
assert data["sound_id"] is None
|
|
assert data["playlist_id"] == 1
|
|
assert "created_at" in data
|
|
assert "updated_at" in data
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_add_playlist_favorite_not_found(
|
|
self,
|
|
authenticated_client: AsyncClient,
|
|
) -> None:
|
|
"""Test adding a favorite for non-existent playlist."""
|
|
response = await authenticated_client.post("/api/v1/favorites/playlists/99999")
|
|
|
|
assert response.status_code == 404
|
|
assert "Playlist with ID 99999 not found" in response.json()["detail"]
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_add_playlist_favorite_already_exists(
|
|
self,
|
|
authenticated_client: AsyncClient,
|
|
existing_playlist_favorite: Favorite,
|
|
test_playlist: Playlist,
|
|
) -> None:
|
|
"""Test adding a playlist favorite that already exists."""
|
|
response = await authenticated_client.post(f"/api/v1/favorites/playlists/{test_playlist.id}")
|
|
|
|
assert response.status_code == 409
|
|
assert "already favorited" in response.json()["detail"]
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_remove_sound_favorite_success(
|
|
self,
|
|
authenticated_client: AsyncClient,
|
|
existing_sound_favorite: Favorite,
|
|
test_sound: Sound,
|
|
) -> None:
|
|
"""Test successfully removing a sound from favorites."""
|
|
response = await authenticated_client.delete(f"/api/v1/favorites/sounds/{test_sound.id}")
|
|
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert data["message"] == "Sound removed from favorites"
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_remove_sound_favorite_not_found(
|
|
self,
|
|
authenticated_client: AsyncClient,
|
|
test_sound: Sound,
|
|
) -> None:
|
|
"""Test removing a sound favorite that doesn't exist."""
|
|
response = await authenticated_client.delete(f"/api/v1/favorites/sounds/{test_sound.id}")
|
|
|
|
assert response.status_code == 404
|
|
assert "is not favorited" in response.json()["detail"]
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_remove_playlist_favorite_success(
|
|
self,
|
|
authenticated_client: AsyncClient,
|
|
existing_playlist_favorite: Favorite,
|
|
test_playlist: Playlist,
|
|
) -> None:
|
|
"""Test successfully removing a playlist from favorites."""
|
|
response = await authenticated_client.delete(f"/api/v1/favorites/playlists/{test_playlist.id}")
|
|
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert data["message"] == "Playlist removed from favorites"
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_remove_playlist_favorite_not_found(
|
|
self,
|
|
authenticated_client: AsyncClient,
|
|
test_playlist: Playlist,
|
|
) -> None:
|
|
"""Test removing a playlist favorite that doesn't exist."""
|
|
response = await authenticated_client.delete(f"/api/v1/favorites/playlists/{test_playlist.id}")
|
|
|
|
assert response.status_code == 404
|
|
assert "is not favorited" in response.json()["detail"]
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_get_all_favorites_empty(
|
|
self,
|
|
authenticated_client: AsyncClient,
|
|
) -> None:
|
|
"""Test getting all favorites when user has none."""
|
|
# First get all existing favorites for the test user
|
|
response = await authenticated_client.get("/api/v1/favorites/")
|
|
assert response.status_code == 200
|
|
existing_data = response.json()
|
|
|
|
# Clean up any existing favorites for this test user
|
|
for favorite in existing_data["favorites"]:
|
|
if favorite.get("sound_id"):
|
|
await authenticated_client.delete(f"/api/v1/favorites/sounds/{favorite['sound_id']}")
|
|
elif favorite.get("playlist_id"):
|
|
await authenticated_client.delete(f"/api/v1/favorites/playlists/{favorite['playlist_id']}")
|
|
|
|
# Now test that the user has no favorites
|
|
response = await authenticated_client.get("/api/v1/favorites/")
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert data["favorites"] == []
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_get_all_favorites_with_data(
|
|
self,
|
|
authenticated_client: AsyncClient,
|
|
existing_sound_favorite: Favorite,
|
|
existing_playlist_favorite: Favorite,
|
|
) -> None:
|
|
"""Test getting all favorites with mixed data."""
|
|
# Clean up any existing favorites first
|
|
response = await authenticated_client.get("/api/v1/favorites/")
|
|
assert response.status_code == 200
|
|
existing_data = response.json()
|
|
|
|
for favorite in existing_data["favorites"]:
|
|
if favorite.get("sound_id"):
|
|
await authenticated_client.delete(f"/api/v1/favorites/sounds/{favorite['sound_id']}")
|
|
elif favorite.get("playlist_id"):
|
|
await authenticated_client.delete(f"/api/v1/favorites/playlists/{favorite['playlist_id']}")
|
|
|
|
# Add the test favorites using hardcoded IDs
|
|
await authenticated_client.post("/api/v1/favorites/sounds/1")
|
|
await authenticated_client.post("/api/v1/favorites/playlists/1")
|
|
|
|
response = await authenticated_client.get("/api/v1/favorites/")
|
|
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert len(data["favorites"]) == 2
|
|
|
|
# Check that we have both types
|
|
sound_favorite = next((f for f in data["favorites"] if f["sound_id"] is not None), None)
|
|
playlist_favorite = next((f for f in data["favorites"] if f["playlist_id"] is not None), None)
|
|
|
|
assert sound_favorite is not None
|
|
assert playlist_favorite is not None
|
|
assert sound_favorite["sound_id"] == 1
|
|
assert playlist_favorite["playlist_id"] == 1
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_get_all_favorites_pagination(
|
|
self,
|
|
authenticated_client: AsyncClient,
|
|
test_session: AsyncSession,
|
|
) -> None:
|
|
"""Test pagination for get all favorites."""
|
|
# Create multiple favorites
|
|
favorite1 = Favorite(user_id=1, sound_id=1)
|
|
favorite2 = Favorite(user_id=1, sound_id=2)
|
|
test_session.add(favorite1)
|
|
test_session.add(favorite2)
|
|
await test_session.commit()
|
|
|
|
# Test limit
|
|
response = await authenticated_client.get("/api/v1/favorites/?limit=1")
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert len(data["favorites"]) == 1
|
|
|
|
# Test offset
|
|
response = await authenticated_client.get("/api/v1/favorites/?offset=1&limit=1")
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert len(data["favorites"]) == 1
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_get_sound_favorites(
|
|
self,
|
|
authenticated_client: AsyncClient,
|
|
existing_sound_favorite: Favorite,
|
|
existing_playlist_favorite: Favorite,
|
|
) -> None:
|
|
"""Test getting only sound favorites."""
|
|
# Clean up any existing favorites first
|
|
response = await authenticated_client.get("/api/v1/favorites/")
|
|
assert response.status_code == 200
|
|
existing_data = response.json()
|
|
|
|
for favorite in existing_data["favorites"]:
|
|
if favorite.get("sound_id"):
|
|
await authenticated_client.delete(f"/api/v1/favorites/sounds/{favorite['sound_id']}")
|
|
elif favorite.get("playlist_id"):
|
|
await authenticated_client.delete(f"/api/v1/favorites/playlists/{favorite['playlist_id']}")
|
|
|
|
# Add test favorites using hardcoded IDs
|
|
await authenticated_client.post("/api/v1/favorites/sounds/1")
|
|
await authenticated_client.post("/api/v1/favorites/playlists/1")
|
|
|
|
response = await authenticated_client.get("/api/v1/favorites/sounds")
|
|
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert len(data["favorites"]) == 1
|
|
assert data["favorites"][0]["sound_id"] == 1
|
|
assert data["favorites"][0]["playlist_id"] is None
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_get_playlist_favorites(
|
|
self,
|
|
authenticated_client: AsyncClient,
|
|
existing_sound_favorite: Favorite,
|
|
existing_playlist_favorite: Favorite,
|
|
) -> None:
|
|
"""Test getting only playlist favorites."""
|
|
# Clean up any existing favorites first
|
|
response = await authenticated_client.get("/api/v1/favorites/")
|
|
assert response.status_code == 200
|
|
existing_data = response.json()
|
|
|
|
for favorite in existing_data["favorites"]:
|
|
if favorite.get("sound_id"):
|
|
await authenticated_client.delete(f"/api/v1/favorites/sounds/{favorite['sound_id']}")
|
|
elif favorite.get("playlist_id"):
|
|
await authenticated_client.delete(f"/api/v1/favorites/playlists/{favorite['playlist_id']}")
|
|
|
|
# Add test favorites using hardcoded IDs
|
|
await authenticated_client.post("/api/v1/favorites/sounds/1")
|
|
await authenticated_client.post("/api/v1/favorites/playlists/1")
|
|
|
|
response = await authenticated_client.get("/api/v1/favorites/playlists")
|
|
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert len(data["favorites"]) == 1
|
|
assert data["favorites"][0]["playlist_id"] == 1
|
|
assert data["favorites"][0]["sound_id"] is None
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_get_favorite_counts(
|
|
self,
|
|
authenticated_client: AsyncClient,
|
|
existing_sound_favorite: Favorite,
|
|
existing_playlist_favorite: Favorite,
|
|
) -> None:
|
|
"""Test getting favorite counts."""
|
|
# Clean up any existing favorites first
|
|
response = await authenticated_client.get("/api/v1/favorites/")
|
|
assert response.status_code == 200
|
|
existing_data = response.json()
|
|
|
|
for favorite in existing_data["favorites"]:
|
|
if favorite.get("sound_id"):
|
|
await authenticated_client.delete(f"/api/v1/favorites/sounds/{favorite['sound_id']}")
|
|
elif favorite.get("playlist_id"):
|
|
await authenticated_client.delete(f"/api/v1/favorites/playlists/{favorite['playlist_id']}")
|
|
|
|
# Add test favorites using hardcoded IDs
|
|
await authenticated_client.post("/api/v1/favorites/sounds/1")
|
|
await authenticated_client.post("/api/v1/favorites/playlists/1")
|
|
|
|
response = await authenticated_client.get("/api/v1/favorites/counts")
|
|
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert data["total"] == 2
|
|
assert data["sounds"] == 1
|
|
assert data["playlists"] == 1
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_check_sound_favorited_true(
|
|
self,
|
|
authenticated_client: AsyncClient,
|
|
existing_sound_favorite: Favorite,
|
|
test_sound: Sound,
|
|
) -> None:
|
|
"""Test checking if a sound is favorited (true case)."""
|
|
# Add a favorite for sound ID 1 using hardcoded ID
|
|
await authenticated_client.post("/api/v1/favorites/sounds/1")
|
|
|
|
response = await authenticated_client.get("/api/v1/favorites/sounds/1/check")
|
|
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert data["is_favorited"] is True
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_check_sound_favorited_false(
|
|
self,
|
|
authenticated_client: AsyncClient,
|
|
test_sound: Sound,
|
|
) -> None:
|
|
"""Test checking if a sound is favorited (false case)."""
|
|
# Make sure sound 1 is not favorited
|
|
try:
|
|
await authenticated_client.delete("/api/v1/favorites/sounds/1")
|
|
except:
|
|
pass # It's ok if it doesn't exist
|
|
|
|
response = await authenticated_client.get("/api/v1/favorites/sounds/1/check")
|
|
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert data["is_favorited"] is False
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_check_playlist_favorited_true(
|
|
self,
|
|
authenticated_client: AsyncClient,
|
|
existing_playlist_favorite: Favorite,
|
|
test_playlist: Playlist,
|
|
) -> None:
|
|
"""Test checking if a playlist is favorited (true case)."""
|
|
# Add a favorite for playlist ID 1 using hardcoded ID
|
|
await authenticated_client.post("/api/v1/favorites/playlists/1")
|
|
|
|
response = await authenticated_client.get("/api/v1/favorites/playlists/1/check")
|
|
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert data["is_favorited"] is True
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_check_playlist_favorited_false(
|
|
self,
|
|
authenticated_client: AsyncClient,
|
|
test_playlist: Playlist,
|
|
) -> None:
|
|
"""Test checking if a playlist is favorited (false case)."""
|
|
# Make sure playlist 1 is not favorited
|
|
try:
|
|
await authenticated_client.delete("/api/v1/favorites/playlists/1")
|
|
except:
|
|
pass # It's ok if it doesn't exist
|
|
|
|
response = await authenticated_client.get("/api/v1/favorites/playlists/1/check")
|
|
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert data["is_favorited"] is False
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_endpoints_require_authentication(
|
|
self,
|
|
test_client: AsyncClient,
|
|
test_sound: Sound,
|
|
test_playlist: Playlist,
|
|
) -> None:
|
|
"""Test that all endpoints require authentication."""
|
|
# Use hardcoded IDs to avoid session context issues
|
|
endpoints = [
|
|
("GET", "/api/v1/favorites/"),
|
|
("GET", "/api/v1/favorites/sounds"),
|
|
("GET", "/api/v1/favorites/playlists"),
|
|
("GET", "/api/v1/favorites/counts"),
|
|
("POST", "/api/v1/favorites/sounds/1"),
|
|
("POST", "/api/v1/favorites/playlists/1"),
|
|
("DELETE", "/api/v1/favorites/sounds/1"),
|
|
("DELETE", "/api/v1/favorites/playlists/1"),
|
|
("GET", "/api/v1/favorites/sounds/1/check"),
|
|
("GET", "/api/v1/favorites/playlists/1/check"),
|
|
]
|
|
|
|
for method, endpoint in endpoints:
|
|
if method == "GET":
|
|
response = await test_client.get(endpoint)
|
|
elif method == "POST":
|
|
response = await test_client.post(endpoint)
|
|
elif method == "DELETE":
|
|
response = await test_client.delete(endpoint)
|
|
|
|
assert response.status_code == 401, f"Endpoint {method} {endpoint} should require authentication"
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_query_parameter_validation(
|
|
self,
|
|
authenticated_client: AsyncClient,
|
|
) -> None:
|
|
"""Test query parameter validation."""
|
|
# Test invalid limit (too small)
|
|
response = await authenticated_client.get("/api/v1/favorites/?limit=0")
|
|
assert response.status_code == 422
|
|
|
|
# Test invalid limit (too large)
|
|
response = await authenticated_client.get("/api/v1/favorites/?limit=101")
|
|
assert response.status_code == 422
|
|
|
|
# Test invalid offset (negative)
|
|
response = await authenticated_client.get("/api/v1/favorites/?offset=-1")
|
|
assert response.status_code == 422
|
|
|
|
# Test valid parameters
|
|
response = await authenticated_client.get("/api/v1/favorites/?limit=50&offset=0")
|
|
assert response.status_code == 200
|
|
|