Refactor code structure for improved readability and maintainability
This commit is contained in:
576
tests/api/v1/test_favorite_endpoints.py
Normal file
576
tests/api/v1/test_favorite_endpoints.py
Normal file
@@ -0,0 +1,576 @@
|
||||
"""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
|
||||
|
||||
Reference in New Issue
Block a user