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
|
||||
|
||||
@@ -16,8 +16,19 @@ from sqlmodel.ext.asyncio.session import AsyncSession
|
||||
from app.api import api_router
|
||||
from app.core.database import get_db
|
||||
from app.middleware.logging import LoggingMiddleware
|
||||
|
||||
# Import all models to ensure SQLAlchemy relationships are properly resolved
|
||||
from app.models.credit_action import CreditAction # noqa: F401
|
||||
from app.models.credit_transaction import CreditTransaction # noqa: F401
|
||||
from app.models.extraction import Extraction # noqa: F401
|
||||
from app.models.favorite import Favorite # noqa: F401
|
||||
from app.models.plan import Plan
|
||||
from app.models.playlist import Playlist # noqa: F401
|
||||
from app.models.playlist_sound import PlaylistSound # noqa: F401
|
||||
from app.models.sound import Sound # noqa: F401
|
||||
from app.models.sound_played import SoundPlayed # noqa: F401
|
||||
from app.models.user import User
|
||||
from app.models.user_oauth import UserOauth # noqa: F401
|
||||
from app.utils.auth import JWTUtils, PasswordUtils
|
||||
|
||||
|
||||
@@ -48,7 +59,7 @@ async def test_engine() -> Any:
|
||||
|
||||
|
||||
@pytest_asyncio.fixture
|
||||
async def test_session(test_engine: Any) -> AsyncGenerator[AsyncSession, None]:
|
||||
async def test_session(test_engine: Any) -> AsyncGenerator[AsyncSession]:
|
||||
"""Create a test database session."""
|
||||
connection = await test_engine.connect()
|
||||
transaction = await connection.begin()
|
||||
@@ -80,7 +91,7 @@ async def test_app(test_session: AsyncSession) -> FastAPI:
|
||||
app.include_router(api_router)
|
||||
|
||||
# Override the database dependency
|
||||
async def override_get_db() -> AsyncGenerator[AsyncSession, None]:
|
||||
async def override_get_db() -> AsyncGenerator[AsyncSession]:
|
||||
yield test_session
|
||||
|
||||
app.dependency_overrides[get_db] = override_get_db
|
||||
@@ -89,7 +100,7 @@ async def test_app(test_session: AsyncSession) -> FastAPI:
|
||||
|
||||
|
||||
@pytest_asyncio.fixture
|
||||
async def test_client(test_app) -> AsyncGenerator[AsyncClient, None]:
|
||||
async def test_client(test_app) -> AsyncGenerator[AsyncClient]:
|
||||
"""Create a test HTTP client."""
|
||||
async with AsyncClient(
|
||||
transport=ASGITransport(app=test_app),
|
||||
@@ -102,7 +113,7 @@ async def test_client(test_app) -> AsyncGenerator[AsyncClient, None]:
|
||||
async def authenticated_client(
|
||||
test_app: FastAPI,
|
||||
auth_cookies: dict[str, str],
|
||||
) -> AsyncGenerator[AsyncClient, None]:
|
||||
) -> AsyncGenerator[AsyncClient]:
|
||||
"""Create a test HTTP client with authentication cookies."""
|
||||
async with AsyncClient(
|
||||
transport=ASGITransport(app=test_app),
|
||||
@@ -116,7 +127,7 @@ async def authenticated_client(
|
||||
async def authenticated_admin_client(
|
||||
test_app: FastAPI,
|
||||
admin_cookies: dict[str, str],
|
||||
) -> AsyncGenerator[AsyncClient, None]:
|
||||
) -> AsyncGenerator[AsyncClient]:
|
||||
"""Create a test HTTP client with admin authentication cookies."""
|
||||
async with AsyncClient(
|
||||
transport=ASGITransport(app=test_app),
|
||||
|
||||
@@ -32,7 +32,7 @@ class TestCreditTransactionRepository:
|
||||
async def credit_transaction_repository(
|
||||
self,
|
||||
test_session: AsyncSession,
|
||||
) -> AsyncGenerator[CreditTransactionRepository, None]:
|
||||
) -> AsyncGenerator[CreditTransactionRepository]:
|
||||
"""Create a credit transaction repository instance."""
|
||||
yield CreditTransactionRepository(test_session)
|
||||
|
||||
@@ -50,7 +50,7 @@ class TestCreditTransactionRepository:
|
||||
self,
|
||||
test_session: AsyncSession,
|
||||
test_user_id: int,
|
||||
) -> AsyncGenerator[list[CreditTransaction], None]:
|
||||
) -> AsyncGenerator[list[CreditTransaction]]:
|
||||
"""Create test credit transactions."""
|
||||
transactions = []
|
||||
user_id = test_user_id
|
||||
@@ -115,7 +115,7 @@ class TestCreditTransactionRepository:
|
||||
self,
|
||||
test_session: AsyncSession,
|
||||
ensure_plans: tuple[Any, ...],
|
||||
) -> AsyncGenerator[CreditTransaction, None]:
|
||||
) -> AsyncGenerator[CreditTransaction]:
|
||||
"""Create a transaction for a different user."""
|
||||
# Create another user
|
||||
user_repo = UserRepository(test_session)
|
||||
|
||||
653
tests/repositories/test_favorite.py
Normal file
653
tests/repositories/test_favorite.py
Normal file
@@ -0,0 +1,653 @@
|
||||
"""Tests for favorite repository."""
|
||||
|
||||
from collections.abc import AsyncGenerator
|
||||
|
||||
import pytest
|
||||
import pytest_asyncio
|
||||
from sqlmodel.ext.asyncio.session import AsyncSession
|
||||
|
||||
from app.models.playlist import Playlist
|
||||
from app.models.sound import Sound
|
||||
from app.models.user import User
|
||||
from app.repositories.favorite import FavoriteRepository
|
||||
|
||||
|
||||
class TestFavoriteRepository:
|
||||
"""Test favorite repository operations."""
|
||||
|
||||
@pytest_asyncio.fixture
|
||||
async def favorite_repository(
|
||||
self,
|
||||
test_session: AsyncSession,
|
||||
) -> AsyncGenerator[FavoriteRepository]:
|
||||
"""Create a favorite repository instance."""
|
||||
yield FavoriteRepository(test_session)
|
||||
|
||||
@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,
|
||||
size=1024000,
|
||||
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,
|
||||
size=512000,
|
||||
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 other_user(
|
||||
self,
|
||||
test_session: AsyncSession,
|
||||
) -> User:
|
||||
"""Create another test user."""
|
||||
from app.utils.auth import PasswordUtils
|
||||
|
||||
user = User(
|
||||
email="other@example.com",
|
||||
name="Other User",
|
||||
password_hash=PasswordUtils.hash_password("otherpassword123"),
|
||||
role="user",
|
||||
is_active=True,
|
||||
plan_id=1, # Use hardcoded plan ID
|
||||
credits=100,
|
||||
)
|
||||
test_session.add(user)
|
||||
await test_session.commit()
|
||||
await test_session.refresh(user)
|
||||
return user
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_create_sound_favorite(
|
||||
self,
|
||||
favorite_repository: FavoriteRepository,
|
||||
) -> None:
|
||||
"""Test creating a sound favorite."""
|
||||
# Use hardcoded IDs to avoid session issues
|
||||
favorite_data = {
|
||||
"user_id": 1,
|
||||
"sound_id": 1,
|
||||
"playlist_id": None,
|
||||
}
|
||||
|
||||
favorite = await favorite_repository.create(favorite_data)
|
||||
|
||||
assert favorite.id is not None
|
||||
assert favorite.user_id == 1
|
||||
assert favorite.sound_id == 1
|
||||
assert favorite.playlist_id is None
|
||||
assert favorite.created_at is not None
|
||||
assert favorite.updated_at is not None
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_create_playlist_favorite(
|
||||
self,
|
||||
favorite_repository: FavoriteRepository,
|
||||
) -> None:
|
||||
"""Test creating a playlist favorite."""
|
||||
favorite_data = {
|
||||
"user_id": 1,
|
||||
"sound_id": None,
|
||||
"playlist_id": 1,
|
||||
}
|
||||
|
||||
favorite = await favorite_repository.create(favorite_data)
|
||||
|
||||
assert favorite.id is not None
|
||||
assert favorite.user_id == 1
|
||||
assert favorite.sound_id is None
|
||||
assert favorite.playlist_id == 1
|
||||
assert favorite.created_at is not None
|
||||
assert favorite.updated_at is not None
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_by_id(
|
||||
self,
|
||||
favorite_repository: FavoriteRepository,
|
||||
) -> None:
|
||||
"""Test getting a favorite by ID."""
|
||||
# Create favorite
|
||||
favorite_data = {
|
||||
"user_id": 1,
|
||||
"sound_id": 1,
|
||||
"playlist_id": None,
|
||||
}
|
||||
created_favorite = await favorite_repository.create(favorite_data)
|
||||
favorite_id = created_favorite.id
|
||||
|
||||
# Get by ID
|
||||
retrieved_favorite = await favorite_repository.get_by_id(favorite_id)
|
||||
|
||||
assert retrieved_favorite is not None
|
||||
assert retrieved_favorite.id == favorite_id
|
||||
assert retrieved_favorite.user_id == 1
|
||||
assert retrieved_favorite.sound_id == 1
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_by_id_not_found(
|
||||
self,
|
||||
favorite_repository: FavoriteRepository,
|
||||
) -> None:
|
||||
"""Test getting a favorite by non-existent ID."""
|
||||
result = await favorite_repository.get_by_id(99999)
|
||||
assert result is None
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_user_favorites_empty(
|
||||
self,
|
||||
favorite_repository: FavoriteRepository,
|
||||
test_user: User,
|
||||
) -> None:
|
||||
"""Test getting favorites for user with no favorites."""
|
||||
favorites = await favorite_repository.get_user_favorites(test_user.id)
|
||||
assert favorites == []
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_user_favorites_mixed(
|
||||
self,
|
||||
favorite_repository: FavoriteRepository,
|
||||
) -> None:
|
||||
"""Test getting mixed favorites (sounds and playlists)."""
|
||||
# Create sound favorite
|
||||
sound_favorite_data = {
|
||||
"user_id": 1,
|
||||
"sound_id": 1,
|
||||
"playlist_id": None,
|
||||
}
|
||||
await favorite_repository.create(sound_favorite_data)
|
||||
|
||||
# Create playlist favorite
|
||||
playlist_favorite_data = {
|
||||
"user_id": 1,
|
||||
"sound_id": None,
|
||||
"playlist_id": 1,
|
||||
}
|
||||
await favorite_repository.create(playlist_favorite_data)
|
||||
|
||||
# Get all favorites
|
||||
favorites = await favorite_repository.get_user_favorites(1)
|
||||
|
||||
assert len(favorites) == 2
|
||||
|
||||
# Should be ordered by created_at desc (most recent first)
|
||||
sound_favorite = next((f for f in favorites if f.sound_id is not None), None)
|
||||
playlist_favorite = next((f for f in 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_user_favorites_pagination(
|
||||
self,
|
||||
favorite_repository: FavoriteRepository,
|
||||
) -> None:
|
||||
"""Test pagination for get user favorites."""
|
||||
# Create multiple favorites
|
||||
for sound_id in [1, 2]:
|
||||
favorite_data = {
|
||||
"user_id": 1,
|
||||
"sound_id": sound_id,
|
||||
"playlist_id": None,
|
||||
}
|
||||
await favorite_repository.create(favorite_data)
|
||||
|
||||
# Test limit
|
||||
favorites = await favorite_repository.get_user_favorites(1, limit=1)
|
||||
assert len(favorites) == 1
|
||||
|
||||
# Test offset
|
||||
favorites_page1 = await favorite_repository.get_user_favorites(1, limit=1, offset=0)
|
||||
favorites_page2 = await favorite_repository.get_user_favorites(1, limit=1, offset=1)
|
||||
|
||||
assert len(favorites_page1) == 1
|
||||
assert len(favorites_page2) == 1
|
||||
assert favorites_page1[0].id != favorites_page2[0].id
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_user_sound_favorites(
|
||||
self,
|
||||
favorite_repository: FavoriteRepository,
|
||||
) -> None:
|
||||
"""Test getting only sound favorites."""
|
||||
# Create sound favorite
|
||||
sound_favorite_data = {
|
||||
"user_id": 1,
|
||||
"sound_id": 1,
|
||||
"playlist_id": None,
|
||||
}
|
||||
await favorite_repository.create(sound_favorite_data)
|
||||
|
||||
# Create playlist favorite
|
||||
playlist_favorite_data = {
|
||||
"user_id": 1,
|
||||
"sound_id": None,
|
||||
"playlist_id": 1,
|
||||
}
|
||||
await favorite_repository.create(playlist_favorite_data)
|
||||
|
||||
# Get only sound favorites
|
||||
sound_favorites = await favorite_repository.get_user_sound_favorites(1)
|
||||
|
||||
assert len(sound_favorites) == 1
|
||||
assert sound_favorites[0].sound_id == 1
|
||||
assert sound_favorites[0].playlist_id is None
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_user_playlist_favorites(
|
||||
self,
|
||||
favorite_repository: FavoriteRepository,
|
||||
) -> None:
|
||||
"""Test getting only playlist favorites."""
|
||||
# Create sound favorite
|
||||
sound_favorite_data = {
|
||||
"user_id": 1,
|
||||
"sound_id": 1,
|
||||
"playlist_id": None,
|
||||
}
|
||||
await favorite_repository.create(sound_favorite_data)
|
||||
|
||||
# Create playlist favorite
|
||||
playlist_favorite_data = {
|
||||
"user_id": 1,
|
||||
"sound_id": None,
|
||||
"playlist_id": 1,
|
||||
}
|
||||
await favorite_repository.create(playlist_favorite_data)
|
||||
|
||||
# Get only playlist favorites
|
||||
playlist_favorites = await favorite_repository.get_user_playlist_favorites(1)
|
||||
|
||||
assert len(playlist_favorites) == 1
|
||||
assert playlist_favorites[0].playlist_id == 1
|
||||
assert playlist_favorites[0].sound_id is None
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_by_user_and_sound_found(
|
||||
self,
|
||||
favorite_repository: FavoriteRepository,
|
||||
) -> None:
|
||||
"""Test getting favorite by user and sound when it exists."""
|
||||
# Create favorite
|
||||
favorite_data = {
|
||||
"user_id": 1,
|
||||
"sound_id": 1,
|
||||
"playlist_id": None,
|
||||
}
|
||||
created_favorite = await favorite_repository.create(favorite_data)
|
||||
favorite_id = created_favorite.id
|
||||
|
||||
# Find by user and sound
|
||||
found_favorite = await favorite_repository.get_by_user_and_sound(1, 1)
|
||||
|
||||
assert found_favorite is not None
|
||||
assert found_favorite.id == favorite_id
|
||||
assert found_favorite.user_id == 1
|
||||
assert found_favorite.sound_id == 1
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_by_user_and_sound_not_found(
|
||||
self,
|
||||
favorite_repository: FavoriteRepository,
|
||||
) -> None:
|
||||
"""Test getting favorite by user and sound when it doesn't exist."""
|
||||
result = await favorite_repository.get_by_user_and_sound(1, 1)
|
||||
assert result is None
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_by_user_and_playlist_found(
|
||||
self,
|
||||
favorite_repository: FavoriteRepository,
|
||||
) -> None:
|
||||
"""Test getting favorite by user and playlist when it exists."""
|
||||
# Create favorite
|
||||
favorite_data = {
|
||||
"user_id": 1,
|
||||
"sound_id": None,
|
||||
"playlist_id": 1,
|
||||
}
|
||||
created_favorite = await favorite_repository.create(favorite_data)
|
||||
favorite_id = created_favorite.id
|
||||
|
||||
# Find by user and playlist
|
||||
found_favorite = await favorite_repository.get_by_user_and_playlist(1, 1)
|
||||
|
||||
assert found_favorite is not None
|
||||
assert found_favorite.id == favorite_id
|
||||
assert found_favorite.user_id == 1
|
||||
assert found_favorite.playlist_id == 1
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_by_user_and_playlist_not_found(
|
||||
self,
|
||||
favorite_repository: FavoriteRepository,
|
||||
) -> None:
|
||||
"""Test getting favorite by user and playlist when it doesn't exist."""
|
||||
result = await favorite_repository.get_by_user_and_playlist(1, 1)
|
||||
assert result is None
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_is_sound_favorited_true(
|
||||
self,
|
||||
favorite_repository: FavoriteRepository,
|
||||
) -> None:
|
||||
"""Test checking if sound is favorited when it is."""
|
||||
# Create favorite
|
||||
favorite_data = {
|
||||
"user_id": 1,
|
||||
"sound_id": 1,
|
||||
"playlist_id": None,
|
||||
}
|
||||
await favorite_repository.create(favorite_data)
|
||||
|
||||
result = await favorite_repository.is_sound_favorited(1, 1)
|
||||
assert result is True
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_is_sound_favorited_false(
|
||||
self,
|
||||
favorite_repository: FavoriteRepository,
|
||||
) -> None:
|
||||
"""Test checking if sound is favorited when it's not."""
|
||||
result = await favorite_repository.is_sound_favorited(1, 1)
|
||||
assert result is False
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_is_playlist_favorited_true(
|
||||
self,
|
||||
favorite_repository: FavoriteRepository,
|
||||
) -> None:
|
||||
"""Test checking if playlist is favorited when it is."""
|
||||
# Create favorite
|
||||
favorite_data = {
|
||||
"user_id": 1,
|
||||
"sound_id": None,
|
||||
"playlist_id": 1,
|
||||
}
|
||||
await favorite_repository.create(favorite_data)
|
||||
|
||||
result = await favorite_repository.is_playlist_favorited(1, 1)
|
||||
assert result is True
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_is_playlist_favorited_false(
|
||||
self,
|
||||
favorite_repository: FavoriteRepository,
|
||||
) -> None:
|
||||
"""Test checking if playlist is favorited when it's not."""
|
||||
result = await favorite_repository.is_playlist_favorited(1, 1)
|
||||
assert result is False
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_count_user_favorites(
|
||||
self,
|
||||
favorite_repository: FavoriteRepository,
|
||||
) -> None:
|
||||
"""Test counting user favorites."""
|
||||
# Initially no favorites
|
||||
count = await favorite_repository.count_user_favorites(1)
|
||||
assert count == 0
|
||||
|
||||
# Create sound favorite
|
||||
sound_favorite_data = {
|
||||
"user_id": 1,
|
||||
"sound_id": 1,
|
||||
"playlist_id": None,
|
||||
}
|
||||
await favorite_repository.create(sound_favorite_data)
|
||||
|
||||
count = await favorite_repository.count_user_favorites(1)
|
||||
assert count == 1
|
||||
|
||||
# Create playlist favorite
|
||||
playlist_favorite_data = {
|
||||
"user_id": 1,
|
||||
"sound_id": None,
|
||||
"playlist_id": 1,
|
||||
}
|
||||
await favorite_repository.create(playlist_favorite_data)
|
||||
|
||||
count = await favorite_repository.count_user_favorites(1)
|
||||
assert count == 2
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_count_sound_favorites(
|
||||
self,
|
||||
favorite_repository: FavoriteRepository,
|
||||
) -> None:
|
||||
"""Test counting favorites for a sound."""
|
||||
# Initially no favorites
|
||||
count = await favorite_repository.count_sound_favorites(1)
|
||||
assert count == 0
|
||||
|
||||
# Create favorite by first user
|
||||
favorite_data1 = {
|
||||
"user_id": 1,
|
||||
"sound_id": 1,
|
||||
"playlist_id": None,
|
||||
}
|
||||
await favorite_repository.create(favorite_data1)
|
||||
|
||||
count = await favorite_repository.count_sound_favorites(1)
|
||||
assert count == 1
|
||||
|
||||
# Create favorite by second user
|
||||
favorite_data2 = {
|
||||
"user_id": 2,
|
||||
"sound_id": 1,
|
||||
"playlist_id": None,
|
||||
}
|
||||
await favorite_repository.create(favorite_data2)
|
||||
|
||||
count = await favorite_repository.count_sound_favorites(1)
|
||||
assert count == 2
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_count_playlist_favorites(
|
||||
self,
|
||||
favorite_repository: FavoriteRepository,
|
||||
) -> None:
|
||||
"""Test counting favorites for a playlist."""
|
||||
# Initially no favorites
|
||||
count = await favorite_repository.count_playlist_favorites(1)
|
||||
assert count == 0
|
||||
|
||||
# Create favorite by first user
|
||||
favorite_data1 = {
|
||||
"user_id": 1,
|
||||
"sound_id": None,
|
||||
"playlist_id": 1,
|
||||
}
|
||||
await favorite_repository.create(favorite_data1)
|
||||
|
||||
count = await favorite_repository.count_playlist_favorites(1)
|
||||
assert count == 1
|
||||
|
||||
# Create favorite by second user
|
||||
favorite_data2 = {
|
||||
"user_id": 2,
|
||||
"sound_id": None,
|
||||
"playlist_id": 1,
|
||||
}
|
||||
await favorite_repository.create(favorite_data2)
|
||||
|
||||
count = await favorite_repository.count_playlist_favorites(1)
|
||||
assert count == 2
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_update_favorite(
|
||||
self,
|
||||
favorite_repository: FavoriteRepository,
|
||||
) -> None:
|
||||
"""Test updating a favorite."""
|
||||
# Create favorite
|
||||
favorite_data = {
|
||||
"user_id": 1,
|
||||
"sound_id": 1,
|
||||
"playlist_id": None,
|
||||
}
|
||||
created_favorite = await favorite_repository.create(favorite_data)
|
||||
favorite_id = created_favorite.id
|
||||
original_updated_at = created_favorite.updated_at
|
||||
|
||||
# Update (note: there's not much to update in a favorite, but we can test the mechanism)
|
||||
updated_favorite = await favorite_repository.update(created_favorite, {})
|
||||
|
||||
assert updated_favorite.id == favorite_id
|
||||
assert updated_favorite.updated_at >= original_updated_at
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_delete_favorite(
|
||||
self,
|
||||
favorite_repository: FavoriteRepository,
|
||||
) -> None:
|
||||
"""Test deleting a favorite."""
|
||||
# Create favorite
|
||||
favorite_data = {
|
||||
"user_id": 1,
|
||||
"sound_id": 1,
|
||||
"playlist_id": None,
|
||||
}
|
||||
created_favorite = await favorite_repository.create(favorite_data)
|
||||
favorite_id = created_favorite.id
|
||||
|
||||
# Verify it exists
|
||||
found_favorite = await favorite_repository.get_by_id(favorite_id)
|
||||
assert found_favorite is not None
|
||||
|
||||
# Delete it
|
||||
await favorite_repository.delete(created_favorite)
|
||||
|
||||
# Verify it's gone
|
||||
deleted_favorite = await favorite_repository.get_by_id(favorite_id)
|
||||
assert deleted_favorite is None
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_unique_constraints(
|
||||
self,
|
||||
favorite_repository: FavoriteRepository,
|
||||
) -> None:
|
||||
"""Test that unique constraints are enforced."""
|
||||
# Create sound favorite
|
||||
sound_favorite_data = {
|
||||
"user_id": 1,
|
||||
"sound_id": 1,
|
||||
"playlist_id": None,
|
||||
}
|
||||
await favorite_repository.create(sound_favorite_data)
|
||||
|
||||
# Try to create duplicate sound favorite - should raise exception
|
||||
from sqlalchemy.exc import IntegrityError
|
||||
with pytest.raises(IntegrityError):
|
||||
await favorite_repository.create(sound_favorite_data)
|
||||
|
||||
# Create playlist favorite
|
||||
playlist_favorite_data = {
|
||||
"user_id": 1,
|
||||
"sound_id": None,
|
||||
"playlist_id": 1,
|
||||
}
|
||||
await favorite_repository.create(playlist_favorite_data)
|
||||
|
||||
# Try to create duplicate playlist favorite - should raise exception
|
||||
with pytest.raises(IntegrityError):
|
||||
await favorite_repository.create(playlist_favorite_data)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_user_isolation(
|
||||
self,
|
||||
favorite_repository: FavoriteRepository,
|
||||
) -> None:
|
||||
"""Test that favorites are properly isolated by user."""
|
||||
# Clean up any existing favorites for user 1 first
|
||||
existing_favorites = await favorite_repository.get_user_favorites(1)
|
||||
for favorite in existing_favorites:
|
||||
await favorite_repository.delete(favorite)
|
||||
|
||||
# Create favorite for user 1
|
||||
favorite_data = {
|
||||
"user_id": 1,
|
||||
"sound_id": 1,
|
||||
"playlist_id": None,
|
||||
}
|
||||
await favorite_repository.create(favorite_data)
|
||||
|
||||
# Check that user 1 has the favorite
|
||||
user1_favorites = await favorite_repository.get_user_favorites(1)
|
||||
assert len(user1_favorites) == 1
|
||||
|
||||
# Check that user 2 doesn't have any favorites
|
||||
user2_favorites = await favorite_repository.get_user_favorites(2)
|
||||
assert len(user2_favorites) == 0
|
||||
|
||||
# Check that the sound is favorited by user 1 but not user 2
|
||||
assert await favorite_repository.is_sound_favorited(1, 1) is True
|
||||
assert await favorite_repository.is_sound_favorited(2, 1) is False
|
||||
|
||||
@@ -30,7 +30,7 @@ class TestPlaylistRepository:
|
||||
async def playlist_repository(
|
||||
self,
|
||||
test_session: AsyncSession,
|
||||
) -> AsyncGenerator[PlaylistRepository, None]:
|
||||
) -> AsyncGenerator[PlaylistRepository]:
|
||||
"""Create a playlist repository instance."""
|
||||
yield PlaylistRepository(test_session)
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ class TestSoundRepository:
|
||||
async def sound_repository(
|
||||
self,
|
||||
test_session: AsyncSession,
|
||||
) -> AsyncGenerator[SoundRepository, None]:
|
||||
) -> AsyncGenerator[SoundRepository]:
|
||||
"""Create a sound repository instance."""
|
||||
yield SoundRepository(test_session)
|
||||
|
||||
@@ -29,7 +29,7 @@ class TestSoundRepository:
|
||||
async def test_sound(
|
||||
self,
|
||||
test_session: AsyncSession,
|
||||
) -> AsyncGenerator[Sound, None]:
|
||||
) -> AsyncGenerator[Sound]:
|
||||
"""Create a test sound."""
|
||||
sound_data = {
|
||||
"name": "Test Sound",
|
||||
@@ -51,7 +51,7 @@ class TestSoundRepository:
|
||||
async def normalized_sound(
|
||||
self,
|
||||
test_session: AsyncSession,
|
||||
) -> AsyncGenerator[Sound, None]:
|
||||
) -> AsyncGenerator[Sound]:
|
||||
"""Create a normalized test sound."""
|
||||
sound_data = {
|
||||
"name": "Normalized Sound",
|
||||
|
||||
@@ -20,7 +20,7 @@ class TestUserRepository:
|
||||
async def user_repository(
|
||||
self,
|
||||
test_session: AsyncSession,
|
||||
) -> AsyncGenerator[UserRepository, None]: # type: ignore[misc]
|
||||
) -> AsyncGenerator[UserRepository]: # type: ignore[misc]
|
||||
"""Create a user repository instance."""
|
||||
yield UserRepository(test_session)
|
||||
|
||||
@@ -209,6 +209,17 @@ class TestUserRepository:
|
||||
assert user.id is not None
|
||||
user_id = user.id
|
||||
|
||||
# Clean up any favorites that might reference this specific user to avoid foreign key constraint violations
|
||||
from sqlmodel import select
|
||||
|
||||
from app.models.favorite import Favorite
|
||||
|
||||
# Only delete favorites for the user we're about to delete, not all favorites
|
||||
existing_favorites = await test_session.exec(select(Favorite).where(Favorite.user_id == user_id))
|
||||
for favorite in existing_favorites.all():
|
||||
await test_session.delete(favorite)
|
||||
await test_session.commit()
|
||||
|
||||
# Delete the user
|
||||
await user_repository.delete(user)
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ class TestUserOauthRepository:
|
||||
async def user_oauth_repository(
|
||||
self,
|
||||
test_session: AsyncSession,
|
||||
) -> AsyncGenerator[UserOauthRepository, None]:
|
||||
) -> AsyncGenerator[UserOauthRepository]:
|
||||
"""Create a user OAuth repository instance."""
|
||||
yield UserOauthRepository(test_session)
|
||||
|
||||
@@ -37,7 +37,7 @@ class TestUserOauthRepository:
|
||||
self,
|
||||
test_session: AsyncSession,
|
||||
test_user_id: int,
|
||||
) -> AsyncGenerator[UserOauth, None]:
|
||||
) -> AsyncGenerator[UserOauth]:
|
||||
"""Create a test OAuth record."""
|
||||
oauth_data = {
|
||||
"user_id": test_user_id,
|
||||
|
||||
548
tests/services/test_favorite.py
Normal file
548
tests/services/test_favorite.py
Normal file
@@ -0,0 +1,548 @@
|
||||
"""Tests for favorite service."""
|
||||
|
||||
from collections.abc import AsyncGenerator
|
||||
from unittest.mock import AsyncMock, MagicMock, patch
|
||||
|
||||
import pytest
|
||||
import pytest_asyncio
|
||||
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
|
||||
from app.models.user import User
|
||||
from app.services.favorite import FavoriteService
|
||||
|
||||
|
||||
class TestFavoriteService:
|
||||
"""Test favorite service operations."""
|
||||
|
||||
@pytest_asyncio.fixture
|
||||
async def mock_session_factory(self) -> AsyncGenerator[MagicMock]:
|
||||
"""Create a mock session factory."""
|
||||
mock_session = AsyncMock(spec=AsyncSession)
|
||||
mock_factory = MagicMock(return_value=mock_session)
|
||||
mock_factory.return_value.__aenter__ = AsyncMock(return_value=mock_session)
|
||||
mock_factory.return_value.__aexit__ = AsyncMock(return_value=None)
|
||||
yield mock_factory
|
||||
|
||||
@pytest_asyncio.fixture
|
||||
async def favorite_service(
|
||||
self,
|
||||
mock_session_factory: MagicMock,
|
||||
) -> AsyncGenerator[FavoriteService]:
|
||||
"""Create a favorite service instance with mocked dependencies."""
|
||||
yield FavoriteService(mock_session_factory)
|
||||
|
||||
@pytest_asyncio.fixture
|
||||
async def test_sound(self) -> Sound:
|
||||
"""Create a test sound."""
|
||||
return Sound(
|
||||
id=1,
|
||||
filename="test_sound.mp3",
|
||||
name="Test Sound",
|
||||
type="SDB",
|
||||
duration=5000,
|
||||
size=1024000,
|
||||
hash="abcdef123456789",
|
||||
)
|
||||
|
||||
@pytest_asyncio.fixture
|
||||
async def test_playlist(self, test_user: User) -> Playlist:
|
||||
"""Create a test playlist."""
|
||||
return Playlist(
|
||||
id=1,
|
||||
user_id=test_user.id,
|
||||
name="Test Playlist",
|
||||
description="A test playlist",
|
||||
genre="test",
|
||||
is_main=False,
|
||||
is_current=False,
|
||||
is_deletable=True,
|
||||
)
|
||||
|
||||
@pytest_asyncio.fixture
|
||||
async def mock_repositories(self) -> dict:
|
||||
"""Create mock repositories."""
|
||||
return {
|
||||
"favorite_repo": AsyncMock(),
|
||||
"user_repo": AsyncMock(),
|
||||
"sound_repo": AsyncMock(),
|
||||
"playlist_repo": AsyncMock(),
|
||||
}
|
||||
|
||||
@patch("app.services.favorite.socket_manager")
|
||||
@patch("app.services.favorite.FavoriteRepository")
|
||||
@patch("app.services.favorite.UserRepository")
|
||||
@patch("app.services.favorite.SoundRepository")
|
||||
@pytest.mark.asyncio
|
||||
async def test_add_sound_favorite_success(
|
||||
self,
|
||||
mock_sound_repo_class: AsyncMock,
|
||||
mock_user_repo_class: AsyncMock,
|
||||
mock_favorite_repo_class: AsyncMock,
|
||||
mock_socket_manager: AsyncMock,
|
||||
favorite_service: FavoriteService,
|
||||
test_user: User,
|
||||
test_sound: Sound,
|
||||
) -> None:
|
||||
"""Test successfully adding a sound favorite."""
|
||||
# Setup mocks
|
||||
mock_favorite_repo = AsyncMock()
|
||||
mock_user_repo = AsyncMock()
|
||||
mock_sound_repo = AsyncMock()
|
||||
|
||||
mock_favorite_repo_class.return_value = mock_favorite_repo
|
||||
mock_user_repo_class.return_value = mock_user_repo
|
||||
mock_sound_repo_class.return_value = mock_sound_repo
|
||||
|
||||
mock_user_repo.get_by_id.return_value = test_user
|
||||
mock_sound_repo.get_by_id.return_value = test_sound
|
||||
mock_favorite_repo.get_by_user_and_sound.return_value = None
|
||||
|
||||
expected_favorite = Favorite(
|
||||
id=1,
|
||||
user_id=test_user.id,
|
||||
sound_id=test_sound.id,
|
||||
playlist_id=None,
|
||||
)
|
||||
mock_favorite_repo.create.return_value = expected_favorite
|
||||
mock_favorite_repo.count_sound_favorites.return_value = 1
|
||||
|
||||
# Execute
|
||||
result = await favorite_service.add_sound_favorite(test_user.id, test_sound.id)
|
||||
|
||||
# Verify
|
||||
assert result == expected_favorite
|
||||
mock_user_repo.get_by_id.assert_called_once_with(test_user.id)
|
||||
mock_sound_repo.get_by_id.assert_called_once_with(test_sound.id)
|
||||
mock_favorite_repo.get_by_user_and_sound.assert_called_once_with(test_user.id, test_sound.id)
|
||||
mock_favorite_repo.create.assert_called_once_with({
|
||||
"user_id": test_user.id,
|
||||
"sound_id": test_sound.id,
|
||||
"playlist_id": None,
|
||||
})
|
||||
mock_socket_manager.broadcast_to_all.assert_called_once()
|
||||
|
||||
@patch("app.services.favorite.UserRepository")
|
||||
@pytest.mark.asyncio
|
||||
async def test_add_sound_favorite_user_not_found(
|
||||
self,
|
||||
mock_user_repo_class: AsyncMock,
|
||||
favorite_service: FavoriteService,
|
||||
) -> None:
|
||||
"""Test adding sound favorite when user not found."""
|
||||
mock_user_repo = AsyncMock()
|
||||
mock_user_repo_class.return_value = mock_user_repo
|
||||
mock_user_repo.get_by_id.return_value = None
|
||||
|
||||
with pytest.raises(ValueError, match="User with ID 1 not found"):
|
||||
await favorite_service.add_sound_favorite(1, 1)
|
||||
|
||||
@patch("app.services.favorite.SoundRepository")
|
||||
@patch("app.services.favorite.UserRepository")
|
||||
@pytest.mark.asyncio
|
||||
async def test_add_sound_favorite_sound_not_found(
|
||||
self,
|
||||
mock_user_repo_class: AsyncMock,
|
||||
mock_sound_repo_class: AsyncMock,
|
||||
favorite_service: FavoriteService,
|
||||
test_user: User,
|
||||
) -> None:
|
||||
"""Test adding sound favorite when sound not found."""
|
||||
mock_user_repo = AsyncMock()
|
||||
mock_sound_repo = AsyncMock()
|
||||
mock_user_repo_class.return_value = mock_user_repo
|
||||
mock_sound_repo_class.return_value = mock_sound_repo
|
||||
|
||||
mock_user_repo.get_by_id.return_value = test_user
|
||||
mock_sound_repo.get_by_id.return_value = None
|
||||
|
||||
with pytest.raises(ValueError, match="Sound with ID 1 not found"):
|
||||
await favorite_service.add_sound_favorite(test_user.id, 1)
|
||||
|
||||
@patch("app.services.favorite.FavoriteRepository")
|
||||
@patch("app.services.favorite.SoundRepository")
|
||||
@patch("app.services.favorite.UserRepository")
|
||||
@pytest.mark.asyncio
|
||||
async def test_add_sound_favorite_already_exists(
|
||||
self,
|
||||
mock_user_repo_class: AsyncMock,
|
||||
mock_sound_repo_class: AsyncMock,
|
||||
mock_favorite_repo_class: AsyncMock,
|
||||
favorite_service: FavoriteService,
|
||||
test_user: User,
|
||||
test_sound: Sound,
|
||||
) -> None:
|
||||
"""Test adding sound favorite that already exists."""
|
||||
mock_user_repo = AsyncMock()
|
||||
mock_sound_repo = AsyncMock()
|
||||
mock_favorite_repo = AsyncMock()
|
||||
|
||||
mock_user_repo_class.return_value = mock_user_repo
|
||||
mock_sound_repo_class.return_value = mock_sound_repo
|
||||
mock_favorite_repo_class.return_value = mock_favorite_repo
|
||||
|
||||
mock_user_repo.get_by_id.return_value = test_user
|
||||
mock_sound_repo.get_by_id.return_value = test_sound
|
||||
existing_favorite = Favorite(user_id=test_user.id, sound_id=test_sound.id)
|
||||
mock_favorite_repo.get_by_user_and_sound.return_value = existing_favorite
|
||||
|
||||
with pytest.raises(ValueError, match="already favorited"):
|
||||
await favorite_service.add_sound_favorite(test_user.id, test_sound.id)
|
||||
|
||||
@patch("app.services.favorite.FavoriteRepository")
|
||||
@patch("app.services.favorite.PlaylistRepository")
|
||||
@patch("app.services.favorite.UserRepository")
|
||||
@pytest.mark.asyncio
|
||||
async def test_add_playlist_favorite_success(
|
||||
self,
|
||||
mock_user_repo_class: AsyncMock,
|
||||
mock_playlist_repo_class: AsyncMock,
|
||||
mock_favorite_repo_class: AsyncMock,
|
||||
favorite_service: FavoriteService,
|
||||
test_user: User,
|
||||
test_playlist: Playlist,
|
||||
) -> None:
|
||||
"""Test successfully adding a playlist favorite."""
|
||||
# Setup mocks
|
||||
mock_favorite_repo = AsyncMock()
|
||||
mock_user_repo = AsyncMock()
|
||||
mock_playlist_repo = AsyncMock()
|
||||
|
||||
mock_favorite_repo_class.return_value = mock_favorite_repo
|
||||
mock_user_repo_class.return_value = mock_user_repo
|
||||
mock_playlist_repo_class.return_value = mock_playlist_repo
|
||||
|
||||
mock_user_repo.get_by_id.return_value = test_user
|
||||
mock_playlist_repo.get_by_id.return_value = test_playlist
|
||||
mock_favorite_repo.get_by_user_and_playlist.return_value = None
|
||||
|
||||
expected_favorite = Favorite(
|
||||
id=1,
|
||||
user_id=test_user.id,
|
||||
sound_id=None,
|
||||
playlist_id=test_playlist.id,
|
||||
)
|
||||
mock_favorite_repo.create.return_value = expected_favorite
|
||||
|
||||
# Execute
|
||||
result = await favorite_service.add_playlist_favorite(test_user.id, test_playlist.id)
|
||||
|
||||
# Verify
|
||||
assert result == expected_favorite
|
||||
mock_user_repo.get_by_id.assert_called_once_with(test_user.id)
|
||||
mock_playlist_repo.get_by_id.assert_called_once_with(test_playlist.id)
|
||||
mock_favorite_repo.get_by_user_and_playlist.assert_called_once_with(test_user.id, test_playlist.id)
|
||||
mock_favorite_repo.create.assert_called_once_with({
|
||||
"user_id": test_user.id,
|
||||
"sound_id": None,
|
||||
"playlist_id": test_playlist.id,
|
||||
})
|
||||
|
||||
@patch("app.services.favorite.socket_manager")
|
||||
@patch("app.services.favorite.FavoriteRepository")
|
||||
@patch("app.services.favorite.SoundRepository")
|
||||
@patch("app.services.favorite.UserRepository")
|
||||
@pytest.mark.asyncio
|
||||
async def test_remove_sound_favorite_success(
|
||||
self,
|
||||
mock_user_repo_class: AsyncMock,
|
||||
mock_sound_repo_class: AsyncMock,
|
||||
mock_favorite_repo_class: AsyncMock,
|
||||
mock_socket_manager: AsyncMock,
|
||||
favorite_service: FavoriteService,
|
||||
test_user: User,
|
||||
test_sound: Sound,
|
||||
) -> None:
|
||||
"""Test successfully removing a sound favorite."""
|
||||
mock_favorite_repo = AsyncMock()
|
||||
mock_user_repo = AsyncMock()
|
||||
mock_sound_repo = AsyncMock()
|
||||
|
||||
mock_favorite_repo_class.return_value = mock_favorite_repo
|
||||
mock_user_repo_class.return_value = mock_user_repo
|
||||
mock_sound_repo_class.return_value = mock_sound_repo
|
||||
|
||||
existing_favorite = Favorite(user_id=test_user.id, sound_id=test_sound.id)
|
||||
mock_favorite_repo.get_by_user_and_sound.return_value = existing_favorite
|
||||
mock_user_repo.get_by_id.return_value = test_user
|
||||
mock_sound_repo.get_by_id.return_value = test_sound
|
||||
mock_favorite_repo.count_sound_favorites.return_value = 0
|
||||
|
||||
# Execute
|
||||
await favorite_service.remove_sound_favorite(test_user.id, test_sound.id)
|
||||
|
||||
# Verify
|
||||
mock_favorite_repo.get_by_user_and_sound.assert_called_once_with(test_user.id, test_sound.id)
|
||||
mock_favorite_repo.delete.assert_called_once_with(existing_favorite)
|
||||
mock_socket_manager.broadcast_to_all.assert_called_once()
|
||||
|
||||
@patch("app.services.favorite.FavoriteRepository")
|
||||
@pytest.mark.asyncio
|
||||
async def test_remove_sound_favorite_not_found(
|
||||
self,
|
||||
mock_favorite_repo_class: AsyncMock,
|
||||
favorite_service: FavoriteService,
|
||||
) -> None:
|
||||
"""Test removing sound favorite that doesn't exist."""
|
||||
mock_favorite_repo = AsyncMock()
|
||||
mock_favorite_repo_class.return_value = mock_favorite_repo
|
||||
mock_favorite_repo.get_by_user_and_sound.return_value = None
|
||||
|
||||
with pytest.raises(ValueError, match="is not favorited"):
|
||||
await favorite_service.remove_sound_favorite(1, 1)
|
||||
|
||||
@patch("app.services.favorite.FavoriteRepository")
|
||||
@pytest.mark.asyncio
|
||||
async def test_remove_playlist_favorite_success(
|
||||
self,
|
||||
mock_favorite_repo_class: AsyncMock,
|
||||
favorite_service: FavoriteService,
|
||||
test_user: User,
|
||||
test_playlist: Playlist,
|
||||
) -> None:
|
||||
"""Test successfully removing a playlist favorite."""
|
||||
mock_favorite_repo = AsyncMock()
|
||||
mock_favorite_repo_class.return_value = mock_favorite_repo
|
||||
|
||||
existing_favorite = Favorite(user_id=test_user.id, playlist_id=test_playlist.id)
|
||||
mock_favorite_repo.get_by_user_and_playlist.return_value = existing_favorite
|
||||
|
||||
# Execute
|
||||
await favorite_service.remove_playlist_favorite(test_user.id, test_playlist.id)
|
||||
|
||||
# Verify
|
||||
mock_favorite_repo.get_by_user_and_playlist.assert_called_once_with(test_user.id, test_playlist.id)
|
||||
mock_favorite_repo.delete.assert_called_once_with(existing_favorite)
|
||||
|
||||
@patch("app.services.favorite.FavoriteRepository")
|
||||
@pytest.mark.asyncio
|
||||
async def test_remove_playlist_favorite_not_found(
|
||||
self,
|
||||
mock_favorite_repo_class: AsyncMock,
|
||||
favorite_service: FavoriteService,
|
||||
) -> None:
|
||||
"""Test removing playlist favorite that doesn't exist."""
|
||||
mock_favorite_repo = AsyncMock()
|
||||
mock_favorite_repo_class.return_value = mock_favorite_repo
|
||||
mock_favorite_repo.get_by_user_and_playlist.return_value = None
|
||||
|
||||
with pytest.raises(ValueError, match="is not favorited"):
|
||||
await favorite_service.remove_playlist_favorite(1, 1)
|
||||
|
||||
@patch("app.services.favorite.FavoriteRepository")
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_user_favorites(
|
||||
self,
|
||||
mock_favorite_repo_class: AsyncMock,
|
||||
favorite_service: FavoriteService,
|
||||
) -> None:
|
||||
"""Test getting user favorites."""
|
||||
mock_favorite_repo = AsyncMock()
|
||||
mock_favorite_repo_class.return_value = mock_favorite_repo
|
||||
|
||||
expected_favorites = [
|
||||
Favorite(id=1, user_id=1, sound_id=1),
|
||||
Favorite(id=2, user_id=1, playlist_id=1),
|
||||
]
|
||||
mock_favorite_repo.get_user_favorites.return_value = expected_favorites
|
||||
|
||||
# Execute
|
||||
result = await favorite_service.get_user_favorites(1, 10, 0)
|
||||
|
||||
# Verify
|
||||
assert result == expected_favorites
|
||||
mock_favorite_repo.get_user_favorites.assert_called_once_with(1, 10, 0)
|
||||
|
||||
@patch("app.services.favorite.FavoriteRepository")
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_user_sound_favorites(
|
||||
self,
|
||||
mock_favorite_repo_class: AsyncMock,
|
||||
favorite_service: FavoriteService,
|
||||
) -> None:
|
||||
"""Test getting user sound favorites."""
|
||||
mock_favorite_repo = AsyncMock()
|
||||
mock_favorite_repo_class.return_value = mock_favorite_repo
|
||||
|
||||
expected_favorites = [Favorite(id=1, user_id=1, sound_id=1)]
|
||||
mock_favorite_repo.get_user_sound_favorites.return_value = expected_favorites
|
||||
|
||||
# Execute
|
||||
result = await favorite_service.get_user_sound_favorites(1, 10, 0)
|
||||
|
||||
# Verify
|
||||
assert result == expected_favorites
|
||||
mock_favorite_repo.get_user_sound_favorites.assert_called_once_with(1, 10, 0)
|
||||
|
||||
@patch("app.services.favorite.FavoriteRepository")
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_user_playlist_favorites(
|
||||
self,
|
||||
mock_favorite_repo_class: AsyncMock,
|
||||
favorite_service: FavoriteService,
|
||||
) -> None:
|
||||
"""Test getting user playlist favorites."""
|
||||
mock_favorite_repo = AsyncMock()
|
||||
mock_favorite_repo_class.return_value = mock_favorite_repo
|
||||
|
||||
expected_favorites = [Favorite(id=1, user_id=1, playlist_id=1)]
|
||||
mock_favorite_repo.get_user_playlist_favorites.return_value = expected_favorites
|
||||
|
||||
# Execute
|
||||
result = await favorite_service.get_user_playlist_favorites(1, 10, 0)
|
||||
|
||||
# Verify
|
||||
assert result == expected_favorites
|
||||
mock_favorite_repo.get_user_playlist_favorites.assert_called_once_with(1, 10, 0)
|
||||
|
||||
@patch("app.services.favorite.FavoriteRepository")
|
||||
@pytest.mark.asyncio
|
||||
async def test_is_sound_favorited(
|
||||
self,
|
||||
mock_favorite_repo_class: AsyncMock,
|
||||
favorite_service: FavoriteService,
|
||||
) -> None:
|
||||
"""Test checking if sound is favorited."""
|
||||
mock_favorite_repo = AsyncMock()
|
||||
mock_favorite_repo_class.return_value = mock_favorite_repo
|
||||
|
||||
mock_favorite_repo.is_sound_favorited.return_value = True
|
||||
|
||||
# Execute
|
||||
result = await favorite_service.is_sound_favorited(1, 1)
|
||||
|
||||
# Verify
|
||||
assert result is True
|
||||
mock_favorite_repo.is_sound_favorited.assert_called_once_with(1, 1)
|
||||
|
||||
@patch("app.services.favorite.FavoriteRepository")
|
||||
@pytest.mark.asyncio
|
||||
async def test_is_playlist_favorited(
|
||||
self,
|
||||
mock_favorite_repo_class: AsyncMock,
|
||||
favorite_service: FavoriteService,
|
||||
) -> None:
|
||||
"""Test checking if playlist is favorited."""
|
||||
mock_favorite_repo = AsyncMock()
|
||||
mock_favorite_repo_class.return_value = mock_favorite_repo
|
||||
|
||||
mock_favorite_repo.is_playlist_favorited.return_value = False
|
||||
|
||||
# Execute
|
||||
result = await favorite_service.is_playlist_favorited(1, 1)
|
||||
|
||||
# Verify
|
||||
assert result is False
|
||||
mock_favorite_repo.is_playlist_favorited.assert_called_once_with(1, 1)
|
||||
|
||||
@patch("app.services.favorite.FavoriteRepository")
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_favorite_counts(
|
||||
self,
|
||||
mock_favorite_repo_class: AsyncMock,
|
||||
favorite_service: FavoriteService,
|
||||
) -> None:
|
||||
"""Test getting favorite counts."""
|
||||
mock_favorite_repo = AsyncMock()
|
||||
mock_favorite_repo_class.return_value = mock_favorite_repo
|
||||
|
||||
mock_favorite_repo.count_user_favorites.return_value = 3
|
||||
mock_favorite_repo.get_user_sound_favorites.return_value = [1, 2] # 2 sounds
|
||||
mock_favorite_repo.get_user_playlist_favorites.return_value = [1] # 1 playlist
|
||||
|
||||
# Execute
|
||||
result = await favorite_service.get_favorite_counts(1)
|
||||
|
||||
# Verify
|
||||
expected = {
|
||||
"total": 3,
|
||||
"sounds": 2,
|
||||
"playlists": 1,
|
||||
}
|
||||
assert result == expected
|
||||
|
||||
@patch("app.services.favorite.FavoriteRepository")
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_sound_favorite_count(
|
||||
self,
|
||||
mock_favorite_repo_class: AsyncMock,
|
||||
favorite_service: FavoriteService,
|
||||
) -> None:
|
||||
"""Test getting sound favorite count."""
|
||||
mock_favorite_repo = AsyncMock()
|
||||
mock_favorite_repo_class.return_value = mock_favorite_repo
|
||||
|
||||
mock_favorite_repo.count_sound_favorites.return_value = 5
|
||||
|
||||
# Execute
|
||||
result = await favorite_service.get_sound_favorite_count(1)
|
||||
|
||||
# Verify
|
||||
assert result == 5
|
||||
mock_favorite_repo.count_sound_favorites.assert_called_once_with(1)
|
||||
|
||||
@patch("app.services.favorite.FavoriteRepository")
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_playlist_favorite_count(
|
||||
self,
|
||||
mock_favorite_repo_class: AsyncMock,
|
||||
favorite_service: FavoriteService,
|
||||
) -> None:
|
||||
"""Test getting playlist favorite count."""
|
||||
mock_favorite_repo = AsyncMock()
|
||||
mock_favorite_repo_class.return_value = mock_favorite_repo
|
||||
|
||||
mock_favorite_repo.count_playlist_favorites.return_value = 3
|
||||
|
||||
# Execute
|
||||
result = await favorite_service.get_playlist_favorite_count(1)
|
||||
|
||||
# Verify
|
||||
assert result == 3
|
||||
mock_favorite_repo.count_playlist_favorites.assert_called_once_with(1)
|
||||
|
||||
@patch("app.services.favorite.socket_manager")
|
||||
@patch("app.services.favorite.FavoriteRepository")
|
||||
@patch("app.services.favorite.SoundRepository")
|
||||
@patch("app.services.favorite.UserRepository")
|
||||
@pytest.mark.asyncio
|
||||
async def test_socket_broadcast_error_handling(
|
||||
self,
|
||||
mock_user_repo_class: AsyncMock,
|
||||
mock_sound_repo_class: AsyncMock,
|
||||
mock_favorite_repo_class: AsyncMock,
|
||||
mock_socket_manager: AsyncMock,
|
||||
favorite_service: FavoriteService,
|
||||
test_user: User,
|
||||
test_sound: Sound,
|
||||
) -> None:
|
||||
"""Test that socket broadcast errors don't affect the operation."""
|
||||
# Setup mocks
|
||||
mock_favorite_repo = AsyncMock()
|
||||
mock_user_repo = AsyncMock()
|
||||
mock_sound_repo = AsyncMock()
|
||||
|
||||
mock_favorite_repo_class.return_value = mock_favorite_repo
|
||||
mock_user_repo_class.return_value = mock_user_repo
|
||||
mock_sound_repo_class.return_value = mock_sound_repo
|
||||
|
||||
mock_user_repo.get_by_id.return_value = test_user
|
||||
mock_sound_repo.get_by_id.return_value = test_sound
|
||||
mock_favorite_repo.get_by_user_and_sound.return_value = None
|
||||
|
||||
expected_favorite = Favorite(id=1, user_id=test_user.id, sound_id=test_sound.id)
|
||||
mock_favorite_repo.create.return_value = expected_favorite
|
||||
mock_favorite_repo.count_sound_favorites.return_value = 1
|
||||
|
||||
# Make socket broadcast raise an exception
|
||||
mock_socket_manager.broadcast_to_all.side_effect = Exception("Socket error")
|
||||
|
||||
# Execute - should not raise exception despite socket error
|
||||
result = await favorite_service.add_sound_favorite(test_user.id, test_sound.id)
|
||||
|
||||
# Verify operation still succeeded
|
||||
assert result == expected_favorite
|
||||
mock_favorite_repo.create.assert_called_once()
|
||||
|
||||
@@ -20,7 +20,7 @@ class TestPlaylistService:
|
||||
async def playlist_service(
|
||||
self,
|
||||
test_session: AsyncSession,
|
||||
) -> AsyncGenerator[PlaylistService, None]:
|
||||
) -> AsyncGenerator[PlaylistService]:
|
||||
"""Create a playlist service instance."""
|
||||
yield PlaylistService(test_session)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user