feat: Add endpoint to retrieve sounds with optional type filtering and implement corresponding repository method
Some checks failed
Backend CI / lint (push) Successful in 9m41s
Backend CI / test (push) Failing after 1m39s

This commit is contained in:
JSC
2025-08-01 22:03:09 +02:00
parent d2d0240fdb
commit 4bbae4c5d4
3 changed files with 127 additions and 2 deletions

View File

@@ -2,12 +2,13 @@
from typing import Annotated from typing import Annotated
from fastapi import APIRouter, Depends, HTTPException, status from fastapi import APIRouter, Depends, HTTPException, Query, status
from sqlmodel.ext.asyncio.session import AsyncSession from sqlmodel.ext.asyncio.session import AsyncSession
from app.core.database import get_db, get_session_factory from app.core.database import get_db, get_session_factory
from app.core.dependencies import get_current_active_user_flexible from app.core.dependencies import get_current_active_user_flexible
from app.models.credit_action import CreditActionType from app.models.credit_action import CreditActionType
from app.models.sound import Sound
from app.models.user import User from app.models.user import User
from app.repositories.sound import SoundRepository from app.repositories.sound import SoundRepository
from app.services.credit import CreditService, InsufficientCreditsError from app.services.credit import CreditService, InsufficientCreditsError
@@ -34,6 +35,27 @@ async def get_sound_repository(
return SoundRepository(session) return SoundRepository(session)
@router.get("/")
async def get_sounds(
current_user: Annotated[User, Depends(get_current_active_user_flexible)], # noqa: ARG001
sound_repo: Annotated[SoundRepository, Depends(get_sound_repository)],
types: Annotated[
list[str] | None,
Query(description="Filter by sound types (e.g., SDB, TTS, EXT)"),
] = None,
) -> dict[str, list[Sound]]:
"""Get all sounds, optionally filtered by types."""
try:
sounds = await sound_repo.get_by_types(types)
except Exception as e:
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"Failed to get sounds: {e!s}",
) from e
else:
return {"sounds": sounds}
# VLC PLAYER # VLC PLAYER
@router.post("/play/{sound_id}") @router.post("/play/{sound_id}")

View File

@@ -1,7 +1,7 @@
"""Sound repository for database operations.""" """Sound repository for database operations."""
from sqlalchemy import func from sqlalchemy import func
from sqlmodel import select from sqlmodel import col, select
from sqlmodel.ext.asyncio.session import AsyncSession from sqlmodel.ext.asyncio.session import AsyncSession
from app.core.logging import get_logger from app.core.logging import get_logger
@@ -95,3 +95,15 @@ class SoundRepository(BaseRepository[Sound]):
sound_type, sound_type,
) )
raise raise
async def get_by_types(self, sound_types: list[str] | None = None) -> list[Sound]:
"""Get sounds by types. If types is None or empty, return all sounds."""
try:
statement = select(Sound)
if sound_types:
statement = statement.where(col(Sound.type).in_(sound_types))
result = await self.session.exec(statement)
return list(result.all())
except Exception:
logger.exception("Failed to get sounds by types: %s", sound_types)
raise

View File

@@ -306,3 +306,94 @@ class TestSoundEndpoints:
data = response.json() data = response.json()
assert data["message"] == "All VLC instances stopped" assert data["message"] == "All VLC instances stopped"
assert data["stopped_count"] == 3 assert data["stopped_count"] == 3
@pytest.mark.asyncio
async def test_get_sounds_unauthenticated(self, client: AsyncClient) -> None:
"""Test getting sounds without authentication."""
response = await client.get("/api/v1/sounds/")
assert response.status_code == 401
data = response.json()
assert "Could not validate credentials" in data["detail"]
@pytest.mark.asyncio
async def test_get_sounds_authenticated(
self,
authenticated_client: AsyncClient,
authenticated_user: User,
) -> None:
"""Test getting sounds with authentication."""
from app.models.sound import Sound
with patch("app.repositories.sound.SoundRepository.get_by_types") as mock_get:
# Create mock sounds with all required fields
mock_sound_1 = Sound(
id=1,
name="Test Sound 1",
type="SDB",
filename="test1.mp3",
duration=5000,
size=1024,
hash="test_hash_1",
play_count=0,
is_normalized=False,
is_music=False,
is_deletable=True,
)
mock_sound_2 = Sound(
id=2,
name="Test Sound 2",
type="EXT",
filename="test2.mp3",
duration=7000,
size=2048,
hash="test_hash_2",
play_count=5,
is_normalized=False,
is_music=False,
is_deletable=True,
)
mock_get.return_value = [mock_sound_1, mock_sound_2]
response = await authenticated_client.get("/api/v1/sounds/")
assert response.status_code == 200
data = response.json()
assert "sounds" in data
assert len(data["sounds"]) == 2
@pytest.mark.asyncio
async def test_get_sounds_with_type_filter_authenticated(
self,
authenticated_client: AsyncClient,
authenticated_user: User,
) -> None:
"""Test getting sounds with type filtering."""
from app.models.sound import Sound
with patch("app.repositories.sound.SoundRepository.get_by_types") as mock_get:
# Create mock sound with all required fields
mock_sound = Sound(
id=1,
name="Test Sound 1",
type="SDB",
filename="test1.mp3",
duration=5000,
size=1024,
hash="test_hash_1",
play_count=0,
is_normalized=False,
is_music=False,
is_deletable=True,
)
mock_get.return_value = [mock_sound]
response = await authenticated_client.get("/api/v1/sounds/?types=SDB")
assert response.status_code == 200
data = response.json()
assert "sounds" in data
assert len(data["sounds"]) == 1
# Verify the repository was called with the correct types
mock_get.assert_called_once_with(["SDB"])