fix: Lint fixes of last tests
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
"""Tests for API token endpoints."""
|
||||
# ruff: noqa: ARG002, PLR2004, PLC0415, BLE001, E501
|
||||
|
||||
from datetime import UTC, datetime, timedelta
|
||||
from unittest.mock import patch
|
||||
@@ -17,7 +18,7 @@ class TestApiTokenEndpoints:
|
||||
self,
|
||||
authenticated_client: AsyncClient,
|
||||
authenticated_user: User,
|
||||
):
|
||||
) -> None:
|
||||
"""Test successful API token generation."""
|
||||
request_data = {"expires_days": 30}
|
||||
|
||||
@@ -45,7 +46,7 @@ class TestApiTokenEndpoints:
|
||||
async def test_generate_api_token_default_expiry(
|
||||
self,
|
||||
authenticated_client: AsyncClient,
|
||||
):
|
||||
) -> None:
|
||||
"""Test API token generation with default expiry."""
|
||||
response = await authenticated_client.post("/api/v1/auth/api-token", json={})
|
||||
|
||||
@@ -54,9 +55,7 @@ class TestApiTokenEndpoints:
|
||||
|
||||
expires_at_str = data["expires_at"]
|
||||
# Handle both ISO format with/without timezone info
|
||||
if expires_at_str.endswith("Z"):
|
||||
expires_at = datetime.fromisoformat(expires_at_str.replace("Z", "+00:00"))
|
||||
elif "+" in expires_at_str or expires_at_str.count("-") > 2:
|
||||
if expires_at_str.endswith("Z") or "+" in expires_at_str or expires_at_str.count("-") > 2:
|
||||
expires_at = datetime.fromisoformat(expires_at_str)
|
||||
else:
|
||||
# Naive datetime, assume UTC
|
||||
@@ -71,7 +70,7 @@ class TestApiTokenEndpoints:
|
||||
async def test_generate_api_token_custom_expiry(
|
||||
self,
|
||||
authenticated_client: AsyncClient,
|
||||
):
|
||||
) -> None:
|
||||
"""Test API token generation with custom expiry."""
|
||||
expires_days = 90
|
||||
request_data = {"expires_days": expires_days}
|
||||
@@ -86,9 +85,7 @@ class TestApiTokenEndpoints:
|
||||
|
||||
expires_at_str = data["expires_at"]
|
||||
# Handle both ISO format with/without timezone info
|
||||
if expires_at_str.endswith("Z"):
|
||||
expires_at = datetime.fromisoformat(expires_at_str.replace("Z", "+00:00"))
|
||||
elif "+" in expires_at_str or expires_at_str.count("-") > 2:
|
||||
if expires_at_str.endswith("Z") or "+" in expires_at_str or expires_at_str.count("-") > 2:
|
||||
expires_at = datetime.fromisoformat(expires_at_str)
|
||||
else:
|
||||
# Naive datetime, assume UTC
|
||||
@@ -103,7 +100,7 @@ class TestApiTokenEndpoints:
|
||||
async def test_generate_api_token_validation_errors(
|
||||
self,
|
||||
authenticated_client: AsyncClient,
|
||||
):
|
||||
) -> None:
|
||||
"""Test API token generation with validation errors."""
|
||||
# Test minimum validation
|
||||
response = await authenticated_client.post(
|
||||
@@ -120,7 +117,7 @@ class TestApiTokenEndpoints:
|
||||
assert response.status_code == 422
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_generate_api_token_unauthenticated(self, client: AsyncClient):
|
||||
async def test_generate_api_token_unauthenticated(self, client: AsyncClient) -> None:
|
||||
"""Test API token generation without authentication."""
|
||||
response = await client.post(
|
||||
"/api/v1/auth/api-token",
|
||||
@@ -132,7 +129,7 @@ class TestApiTokenEndpoints:
|
||||
async def test_get_api_token_status_no_token(
|
||||
self,
|
||||
authenticated_client: AsyncClient,
|
||||
):
|
||||
) -> None:
|
||||
"""Test getting API token status when user has no token."""
|
||||
response = await authenticated_client.get("/api/v1/auth/api-token/status")
|
||||
|
||||
@@ -147,7 +144,7 @@ class TestApiTokenEndpoints:
|
||||
async def test_get_api_token_status_with_token(
|
||||
self,
|
||||
authenticated_client: AsyncClient,
|
||||
):
|
||||
) -> None:
|
||||
"""Test getting API token status when user has a token."""
|
||||
# First generate a token
|
||||
await authenticated_client.post(
|
||||
@@ -170,7 +167,7 @@ class TestApiTokenEndpoints:
|
||||
self,
|
||||
authenticated_client: AsyncClient,
|
||||
authenticated_user: User,
|
||||
):
|
||||
) -> None:
|
||||
"""Test getting API token status with expired token."""
|
||||
# Mock expired token
|
||||
with patch("app.utils.auth.TokenUtils.is_token_expired", return_value=True):
|
||||
@@ -190,7 +187,7 @@ class TestApiTokenEndpoints:
|
||||
assert data["is_expired"] is True
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_api_token_status_unauthenticated(self, client: AsyncClient):
|
||||
async def test_get_api_token_status_unauthenticated(self, client: AsyncClient) -> None:
|
||||
"""Test getting API token status without authentication."""
|
||||
response = await client.get("/api/v1/auth/api-token/status")
|
||||
assert response.status_code == 401
|
||||
@@ -199,7 +196,7 @@ class TestApiTokenEndpoints:
|
||||
async def test_revoke_api_token_success(
|
||||
self,
|
||||
authenticated_client: AsyncClient,
|
||||
):
|
||||
) -> None:
|
||||
"""Test successful API token revocation."""
|
||||
# First generate a token
|
||||
await authenticated_client.post(
|
||||
@@ -230,7 +227,7 @@ class TestApiTokenEndpoints:
|
||||
async def test_revoke_api_token_no_token(
|
||||
self,
|
||||
authenticated_client: AsyncClient,
|
||||
):
|
||||
) -> None:
|
||||
"""Test revoking API token when user has no token."""
|
||||
response = await authenticated_client.delete("/api/v1/auth/api-token")
|
||||
|
||||
@@ -239,7 +236,7 @@ class TestApiTokenEndpoints:
|
||||
assert data["message"] == "API token revoked successfully"
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_revoke_api_token_unauthenticated(self, client: AsyncClient):
|
||||
async def test_revoke_api_token_unauthenticated(self, client: AsyncClient) -> None:
|
||||
"""Test revoking API token without authentication."""
|
||||
response = await client.delete("/api/v1/auth/api-token")
|
||||
assert response.status_code == 401
|
||||
@@ -249,7 +246,7 @@ class TestApiTokenEndpoints:
|
||||
self,
|
||||
client: AsyncClient,
|
||||
authenticated_client: AsyncClient,
|
||||
):
|
||||
) -> None:
|
||||
"""Test successful authentication using API token."""
|
||||
# Generate API token
|
||||
token_response = await authenticated_client.post(
|
||||
@@ -268,7 +265,7 @@ class TestApiTokenEndpoints:
|
||||
assert "email" in data
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_api_token_authentication_invalid_token(self, client: AsyncClient):
|
||||
async def test_api_token_authentication_invalid_token(self, client: AsyncClient) -> None:
|
||||
"""Test authentication with invalid API token."""
|
||||
headers = {"API-TOKEN": "invalid_token"}
|
||||
response = await client.get("/api/v1/auth/me", headers=headers)
|
||||
@@ -282,7 +279,7 @@ class TestApiTokenEndpoints:
|
||||
self,
|
||||
client: AsyncClient,
|
||||
authenticated_client: AsyncClient,
|
||||
):
|
||||
) -> None:
|
||||
"""Test authentication with expired API token."""
|
||||
# Generate API token
|
||||
token_response = await authenticated_client.post(
|
||||
@@ -301,7 +298,7 @@ class TestApiTokenEndpoints:
|
||||
assert "API token has expired" in data["detail"]
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_api_token_authentication_empty_token(self, client: AsyncClient):
|
||||
async def test_api_token_authentication_empty_token(self, client: AsyncClient) -> None:
|
||||
"""Test authentication with empty API-TOKEN header."""
|
||||
# Empty token
|
||||
headers = {"API-TOKEN": ""}
|
||||
@@ -325,7 +322,7 @@ class TestApiTokenEndpoints:
|
||||
client: AsyncClient,
|
||||
authenticated_client: AsyncClient,
|
||||
authenticated_user: User,
|
||||
):
|
||||
) -> None:
|
||||
"""Test authentication with API token for inactive user."""
|
||||
# Generate API token
|
||||
token_response = await authenticated_client.post(
|
||||
@@ -351,7 +348,7 @@ class TestApiTokenEndpoints:
|
||||
client: AsyncClient,
|
||||
authenticated_client: AsyncClient,
|
||||
auth_cookies: dict[str, str],
|
||||
):
|
||||
) -> None:
|
||||
"""Test that flexible authentication prefers API token over cookie."""
|
||||
# Generate API token
|
||||
token_response = await authenticated_client.post(
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
"""Tests for authentication endpoints."""
|
||||
# ruff: noqa: ARG002, PLR2004, E501, PLC0415, ANN401
|
||||
|
||||
from typing import Any
|
||||
from unittest.mock import patch
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
"""Tests for extraction API endpoints."""
|
||||
# ruff: noqa: PLR2004, E501
|
||||
|
||||
|
||||
import pytest
|
||||
@@ -11,7 +12,7 @@ class TestExtractionEndpoints:
|
||||
@pytest.mark.asyncio
|
||||
async def test_create_extraction_success(
|
||||
self, test_client: AsyncClient, auth_cookies: dict[str, str],
|
||||
):
|
||||
) -> None:
|
||||
"""Test successful extraction creation."""
|
||||
# Set cookies on client instance to avoid deprecation warning
|
||||
test_client.cookies.update(auth_cookies)
|
||||
@@ -26,7 +27,7 @@ class TestExtractionEndpoints:
|
||||
assert response.status_code in [200, 400, 500] # Allow any non-auth error
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_create_extraction_unauthenticated(self, test_client: AsyncClient):
|
||||
async def test_create_extraction_unauthenticated(self, test_client: AsyncClient) -> None:
|
||||
"""Test extraction creation without authentication."""
|
||||
response = await test_client.post(
|
||||
"/api/v1/sounds/extract",
|
||||
@@ -37,7 +38,7 @@ class TestExtractionEndpoints:
|
||||
assert response.status_code == 401
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_extraction_unauthenticated(self, test_client: AsyncClient):
|
||||
async def test_get_extraction_unauthenticated(self, test_client: AsyncClient) -> None:
|
||||
"""Test extraction retrieval without authentication."""
|
||||
response = await test_client.get("/api/v1/sounds/extract/1")
|
||||
|
||||
@@ -47,7 +48,7 @@ class TestExtractionEndpoints:
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_processor_status_admin(
|
||||
self, test_client: AsyncClient, admin_cookies: dict[str, str],
|
||||
):
|
||||
) -> None:
|
||||
"""Test getting processor status as admin."""
|
||||
# Set cookies on client instance to avoid deprecation warning
|
||||
test_client.cookies.update(admin_cookies)
|
||||
@@ -63,7 +64,7 @@ class TestExtractionEndpoints:
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_processor_status_non_admin(
|
||||
self, test_client: AsyncClient, auth_cookies: dict[str, str],
|
||||
):
|
||||
) -> None:
|
||||
"""Test getting processor status as non-admin user."""
|
||||
# Set cookies on client instance to avoid deprecation warning
|
||||
test_client.cookies.update(auth_cookies)
|
||||
@@ -77,7 +78,7 @@ class TestExtractionEndpoints:
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_user_extractions(
|
||||
self, test_client: AsyncClient, auth_cookies: dict[str, str],
|
||||
):
|
||||
) -> None:
|
||||
"""Test getting user extractions."""
|
||||
# Set cookies on client instance to avoid deprecation warning
|
||||
test_client.cookies.update(auth_cookies)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
"""Tests for player API endpoints."""
|
||||
# ruff: noqa: ARG002, PLR2004, ANN001, ANN201
|
||||
|
||||
from unittest.mock import AsyncMock, Mock, patch
|
||||
|
||||
@@ -37,7 +38,7 @@ class TestPlayerEndpoints:
|
||||
authenticated_client: AsyncClient,
|
||||
authenticated_user: User,
|
||||
mock_player_service,
|
||||
):
|
||||
) -> None:
|
||||
"""Test starting playback successfully."""
|
||||
response = await authenticated_client.post("/api/v1/player/play")
|
||||
|
||||
@@ -48,7 +49,7 @@ class TestPlayerEndpoints:
|
||||
mock_player_service.play.assert_called_once_with()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_play_unauthenticated(self, client: AsyncClient):
|
||||
async def test_play_unauthenticated(self, client: AsyncClient) -> None:
|
||||
"""Test starting playback without authentication."""
|
||||
response = await client.post("/api/v1/player/play")
|
||||
assert response.status_code == 401
|
||||
@@ -59,7 +60,7 @@ class TestPlayerEndpoints:
|
||||
authenticated_client: AsyncClient,
|
||||
authenticated_user: User,
|
||||
mock_player_service,
|
||||
):
|
||||
) -> None:
|
||||
"""Test starting playback with service error."""
|
||||
mock_player_service.play.side_effect = Exception("Service error")
|
||||
|
||||
@@ -75,7 +76,7 @@ class TestPlayerEndpoints:
|
||||
authenticated_client: AsyncClient,
|
||||
authenticated_user: User,
|
||||
mock_player_service,
|
||||
):
|
||||
) -> None:
|
||||
"""Test playing sound at specific index successfully."""
|
||||
index = 2
|
||||
response = await authenticated_client.post(f"/api/v1/player/play/{index}")
|
||||
@@ -92,7 +93,7 @@ class TestPlayerEndpoints:
|
||||
authenticated_client: AsyncClient,
|
||||
authenticated_user: User,
|
||||
mock_player_service,
|
||||
):
|
||||
) -> None:
|
||||
"""Test playing sound with invalid index."""
|
||||
mock_player_service.play.side_effect = ValueError("Invalid sound index")
|
||||
|
||||
@@ -108,7 +109,7 @@ class TestPlayerEndpoints:
|
||||
authenticated_client: AsyncClient,
|
||||
authenticated_user: User,
|
||||
mock_player_service,
|
||||
):
|
||||
) -> None:
|
||||
"""Test playing sound at index with service error."""
|
||||
mock_player_service.play.side_effect = Exception("Service error")
|
||||
|
||||
@@ -124,7 +125,7 @@ class TestPlayerEndpoints:
|
||||
authenticated_client: AsyncClient,
|
||||
authenticated_user: User,
|
||||
mock_player_service,
|
||||
):
|
||||
) -> None:
|
||||
"""Test pausing playback successfully."""
|
||||
response = await authenticated_client.post("/api/v1/player/pause")
|
||||
|
||||
@@ -135,7 +136,7 @@ class TestPlayerEndpoints:
|
||||
mock_player_service.pause.assert_called_once()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_pause_unauthenticated(self, client: AsyncClient):
|
||||
async def test_pause_unauthenticated(self, client: AsyncClient) -> None:
|
||||
"""Test pausing playback without authentication."""
|
||||
response = await client.post("/api/v1/player/pause")
|
||||
assert response.status_code == 401
|
||||
@@ -146,7 +147,7 @@ class TestPlayerEndpoints:
|
||||
authenticated_client: AsyncClient,
|
||||
authenticated_user: User,
|
||||
mock_player_service,
|
||||
):
|
||||
) -> None:
|
||||
"""Test pausing playback with service error."""
|
||||
mock_player_service.pause.side_effect = Exception("Service error")
|
||||
|
||||
@@ -162,7 +163,7 @@ class TestPlayerEndpoints:
|
||||
authenticated_client: AsyncClient,
|
||||
authenticated_user: User,
|
||||
mock_player_service,
|
||||
):
|
||||
) -> None:
|
||||
"""Test stopping playback successfully."""
|
||||
response = await authenticated_client.post("/api/v1/player/stop")
|
||||
|
||||
@@ -173,7 +174,7 @@ class TestPlayerEndpoints:
|
||||
mock_player_service.stop_playback.assert_called_once()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_stop_unauthenticated(self, client: AsyncClient):
|
||||
async def test_stop_unauthenticated(self, client: AsyncClient) -> None:
|
||||
"""Test stopping playback without authentication."""
|
||||
response = await client.post("/api/v1/player/stop")
|
||||
assert response.status_code == 401
|
||||
@@ -184,7 +185,7 @@ class TestPlayerEndpoints:
|
||||
authenticated_client: AsyncClient,
|
||||
authenticated_user: User,
|
||||
mock_player_service,
|
||||
):
|
||||
) -> None:
|
||||
"""Test stopping playback with service error."""
|
||||
mock_player_service.stop_playback.side_effect = Exception("Service error")
|
||||
|
||||
@@ -200,7 +201,7 @@ class TestPlayerEndpoints:
|
||||
authenticated_client: AsyncClient,
|
||||
authenticated_user: User,
|
||||
mock_player_service,
|
||||
):
|
||||
) -> None:
|
||||
"""Test skipping to next track successfully."""
|
||||
response = await authenticated_client.post("/api/v1/player/next")
|
||||
|
||||
@@ -211,7 +212,7 @@ class TestPlayerEndpoints:
|
||||
mock_player_service.next.assert_called_once()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_next_track_unauthenticated(self, client: AsyncClient):
|
||||
async def test_next_track_unauthenticated(self, client: AsyncClient) -> None:
|
||||
"""Test skipping to next track without authentication."""
|
||||
response = await client.post("/api/v1/player/next")
|
||||
assert response.status_code == 401
|
||||
@@ -222,7 +223,7 @@ class TestPlayerEndpoints:
|
||||
authenticated_client: AsyncClient,
|
||||
authenticated_user: User,
|
||||
mock_player_service,
|
||||
):
|
||||
) -> None:
|
||||
"""Test skipping to next track with service error."""
|
||||
mock_player_service.next.side_effect = Exception("Service error")
|
||||
|
||||
@@ -238,7 +239,7 @@ class TestPlayerEndpoints:
|
||||
authenticated_client: AsyncClient,
|
||||
authenticated_user: User,
|
||||
mock_player_service,
|
||||
):
|
||||
) -> None:
|
||||
"""Test going to previous track successfully."""
|
||||
response = await authenticated_client.post("/api/v1/player/previous")
|
||||
|
||||
@@ -249,7 +250,7 @@ class TestPlayerEndpoints:
|
||||
mock_player_service.previous.assert_called_once()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_previous_track_unauthenticated(self, client: AsyncClient):
|
||||
async def test_previous_track_unauthenticated(self, client: AsyncClient) -> None:
|
||||
"""Test going to previous track without authentication."""
|
||||
response = await client.post("/api/v1/player/previous")
|
||||
assert response.status_code == 401
|
||||
@@ -260,7 +261,7 @@ class TestPlayerEndpoints:
|
||||
authenticated_client: AsyncClient,
|
||||
authenticated_user: User,
|
||||
mock_player_service,
|
||||
):
|
||||
) -> None:
|
||||
"""Test going to previous track with service error."""
|
||||
mock_player_service.previous.side_effect = Exception("Service error")
|
||||
|
||||
@@ -276,7 +277,7 @@ class TestPlayerEndpoints:
|
||||
authenticated_client: AsyncClient,
|
||||
authenticated_user: User,
|
||||
mock_player_service,
|
||||
):
|
||||
) -> None:
|
||||
"""Test seeking to position successfully."""
|
||||
position = 5000
|
||||
response = await authenticated_client.post(
|
||||
@@ -296,7 +297,7 @@ class TestPlayerEndpoints:
|
||||
authenticated_client: AsyncClient,
|
||||
authenticated_user: User,
|
||||
mock_player_service,
|
||||
):
|
||||
) -> None:
|
||||
"""Test seeking with invalid position."""
|
||||
response = await authenticated_client.post(
|
||||
"/api/v1/player/seek",
|
||||
@@ -306,7 +307,7 @@ class TestPlayerEndpoints:
|
||||
assert response.status_code == 422 # Validation error
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_seek_unauthenticated(self, client: AsyncClient):
|
||||
async def test_seek_unauthenticated(self, client: AsyncClient) -> None:
|
||||
"""Test seeking without authentication."""
|
||||
response = await client.post(
|
||||
"/api/v1/player/seek",
|
||||
@@ -320,7 +321,7 @@ class TestPlayerEndpoints:
|
||||
authenticated_client: AsyncClient,
|
||||
authenticated_user: User,
|
||||
mock_player_service,
|
||||
):
|
||||
) -> None:
|
||||
"""Test seeking with service error."""
|
||||
mock_player_service.seek.side_effect = Exception("Service error")
|
||||
|
||||
@@ -339,7 +340,7 @@ class TestPlayerEndpoints:
|
||||
authenticated_client: AsyncClient,
|
||||
authenticated_user: User,
|
||||
mock_player_service,
|
||||
):
|
||||
) -> None:
|
||||
"""Test setting volume successfully."""
|
||||
volume = 75
|
||||
response = await authenticated_client.post(
|
||||
@@ -359,7 +360,7 @@ class TestPlayerEndpoints:
|
||||
authenticated_client: AsyncClient,
|
||||
authenticated_user: User,
|
||||
mock_player_service,
|
||||
):
|
||||
) -> None:
|
||||
"""Test setting volume with invalid range."""
|
||||
# Test volume too high
|
||||
response = await authenticated_client.post(
|
||||
@@ -376,7 +377,7 @@ class TestPlayerEndpoints:
|
||||
assert response.status_code == 422
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_set_volume_unauthenticated(self, client: AsyncClient):
|
||||
async def test_set_volume_unauthenticated(self, client: AsyncClient) -> None:
|
||||
"""Test setting volume without authentication."""
|
||||
response = await client.post(
|
||||
"/api/v1/player/volume",
|
||||
@@ -390,7 +391,7 @@ class TestPlayerEndpoints:
|
||||
authenticated_client: AsyncClient,
|
||||
authenticated_user: User,
|
||||
mock_player_service,
|
||||
):
|
||||
) -> None:
|
||||
"""Test setting volume with service error."""
|
||||
mock_player_service.set_volume.side_effect = Exception("Service error")
|
||||
|
||||
@@ -409,7 +410,7 @@ class TestPlayerEndpoints:
|
||||
authenticated_client: AsyncClient,
|
||||
authenticated_user: User,
|
||||
mock_player_service,
|
||||
):
|
||||
) -> None:
|
||||
"""Test setting playback mode successfully."""
|
||||
mode = PlayerMode.LOOP
|
||||
response = await authenticated_client.post(
|
||||
@@ -429,7 +430,7 @@ class TestPlayerEndpoints:
|
||||
authenticated_client: AsyncClient,
|
||||
authenticated_user: User,
|
||||
mock_player_service,
|
||||
):
|
||||
) -> None:
|
||||
"""Test setting invalid playback mode."""
|
||||
response = await authenticated_client.post(
|
||||
"/api/v1/player/mode",
|
||||
@@ -439,7 +440,7 @@ class TestPlayerEndpoints:
|
||||
assert response.status_code == 422 # Validation error
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_set_mode_unauthenticated(self, client: AsyncClient):
|
||||
async def test_set_mode_unauthenticated(self, client: AsyncClient) -> None:
|
||||
"""Test setting mode without authentication."""
|
||||
response = await client.post(
|
||||
"/api/v1/player/mode",
|
||||
@@ -453,7 +454,7 @@ class TestPlayerEndpoints:
|
||||
authenticated_client: AsyncClient,
|
||||
authenticated_user: User,
|
||||
mock_player_service,
|
||||
):
|
||||
) -> None:
|
||||
"""Test setting mode with service error."""
|
||||
mock_player_service.set_mode.side_effect = Exception("Service error")
|
||||
|
||||
@@ -472,7 +473,7 @@ class TestPlayerEndpoints:
|
||||
authenticated_client: AsyncClient,
|
||||
authenticated_user: User,
|
||||
mock_player_service,
|
||||
):
|
||||
) -> None:
|
||||
"""Test reloading playlist successfully."""
|
||||
response = await authenticated_client.post("/api/v1/player/reload-playlist")
|
||||
|
||||
@@ -483,7 +484,7 @@ class TestPlayerEndpoints:
|
||||
mock_player_service.reload_playlist.assert_called_once()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_reload_playlist_unauthenticated(self, client: AsyncClient):
|
||||
async def test_reload_playlist_unauthenticated(self, client: AsyncClient) -> None:
|
||||
"""Test reloading playlist without authentication."""
|
||||
response = await client.post("/api/v1/player/reload-playlist")
|
||||
assert response.status_code == 401
|
||||
@@ -494,7 +495,7 @@ class TestPlayerEndpoints:
|
||||
authenticated_client: AsyncClient,
|
||||
authenticated_user: User,
|
||||
mock_player_service,
|
||||
):
|
||||
) -> None:
|
||||
"""Test reloading playlist with service error."""
|
||||
mock_player_service.reload_playlist.side_effect = Exception("Service error")
|
||||
|
||||
@@ -510,7 +511,7 @@ class TestPlayerEndpoints:
|
||||
authenticated_client: AsyncClient,
|
||||
authenticated_user: User,
|
||||
mock_player_service,
|
||||
):
|
||||
) -> None:
|
||||
"""Test getting player state successfully."""
|
||||
mock_state = {
|
||||
"status": PlayerStatus.PLAYING.value,
|
||||
@@ -548,7 +549,7 @@ class TestPlayerEndpoints:
|
||||
mock_player_service.get_state.assert_called_once()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_state_unauthenticated(self, client: AsyncClient):
|
||||
async def test_get_state_unauthenticated(self, client: AsyncClient) -> None:
|
||||
"""Test getting player state without authentication."""
|
||||
response = await client.get("/api/v1/player/state")
|
||||
assert response.status_code == 401
|
||||
@@ -559,7 +560,7 @@ class TestPlayerEndpoints:
|
||||
authenticated_client: AsyncClient,
|
||||
authenticated_user: User,
|
||||
mock_player_service,
|
||||
):
|
||||
) -> None:
|
||||
"""Test getting player state with service error."""
|
||||
mock_player_service.get_state.side_effect = Exception("Service error")
|
||||
|
||||
@@ -574,7 +575,7 @@ class TestPlayerEndpoints:
|
||||
self,
|
||||
authenticated_client: AsyncClient,
|
||||
authenticated_user: User,
|
||||
):
|
||||
) -> None:
|
||||
"""Test seeking without request body."""
|
||||
response = await authenticated_client.post("/api/v1/player/seek")
|
||||
assert response.status_code == 422
|
||||
@@ -584,7 +585,7 @@ class TestPlayerEndpoints:
|
||||
self,
|
||||
authenticated_client: AsyncClient,
|
||||
authenticated_user: User,
|
||||
):
|
||||
) -> None:
|
||||
"""Test setting volume without request body."""
|
||||
response = await authenticated_client.post("/api/v1/player/volume")
|
||||
assert response.status_code == 422
|
||||
@@ -594,7 +595,7 @@ class TestPlayerEndpoints:
|
||||
self,
|
||||
authenticated_client: AsyncClient,
|
||||
authenticated_user: User,
|
||||
):
|
||||
) -> None:
|
||||
"""Test setting mode without request body."""
|
||||
response = await authenticated_client.post("/api/v1/player/mode")
|
||||
assert response.status_code == 422
|
||||
@@ -605,7 +606,7 @@ class TestPlayerEndpoints:
|
||||
authenticated_client: AsyncClient,
|
||||
authenticated_user: User,
|
||||
mock_player_service,
|
||||
):
|
||||
) -> None:
|
||||
"""Test playing sound with negative index."""
|
||||
mock_player_service.play.side_effect = ValueError("Invalid sound index")
|
||||
|
||||
@@ -621,7 +622,7 @@ class TestPlayerEndpoints:
|
||||
authenticated_client: AsyncClient,
|
||||
authenticated_user: User,
|
||||
mock_player_service,
|
||||
):
|
||||
) -> None:
|
||||
"""Test seeking to position zero."""
|
||||
response = await authenticated_client.post(
|
||||
"/api/v1/player/seek",
|
||||
@@ -640,7 +641,7 @@ class TestPlayerEndpoints:
|
||||
authenticated_client: AsyncClient,
|
||||
authenticated_user: User,
|
||||
mock_player_service,
|
||||
):
|
||||
) -> None:
|
||||
"""Test setting volume with boundary values."""
|
||||
# Test minimum volume
|
||||
response = await authenticated_client.post(
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
"""Tests for playlist API endpoints."""
|
||||
# ruff: noqa: ARG002, PLR2004, E501, PLC0415
|
||||
|
||||
|
||||
import pytest
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
"""Tests for socket API endpoints."""
|
||||
# ruff: noqa: ARG002, PLR2004, ANN001, ANN201
|
||||
|
||||
from unittest.mock import AsyncMock, patch
|
||||
|
||||
@@ -27,7 +28,7 @@ class TestSocketEndpoints:
|
||||
authenticated_client: AsyncClient,
|
||||
authenticated_user: User,
|
||||
mock_socket_manager,
|
||||
):
|
||||
) -> None:
|
||||
"""Test getting socket status for authenticated user."""
|
||||
response = await authenticated_client.get("/api/v1/socket/status")
|
||||
|
||||
@@ -42,7 +43,7 @@ class TestSocketEndpoints:
|
||||
assert isinstance(data["connected"], bool)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_socket_status_unauthenticated(self, client: AsyncClient):
|
||||
async def test_get_socket_status_unauthenticated(self, client: AsyncClient) -> None:
|
||||
"""Test getting socket status without authentication."""
|
||||
response = await client.get("/api/v1/socket/status")
|
||||
assert response.status_code == 401
|
||||
@@ -53,7 +54,7 @@ class TestSocketEndpoints:
|
||||
authenticated_client: AsyncClient,
|
||||
authenticated_user: User,
|
||||
mock_socket_manager,
|
||||
):
|
||||
) -> None:
|
||||
"""Test sending message to specific user successfully."""
|
||||
target_user_id = 2
|
||||
message = "Hello there!"
|
||||
@@ -87,7 +88,7 @@ class TestSocketEndpoints:
|
||||
authenticated_client: AsyncClient,
|
||||
authenticated_user: User,
|
||||
mock_socket_manager,
|
||||
):
|
||||
) -> None:
|
||||
"""Test sending message to user who is not connected."""
|
||||
target_user_id = 999
|
||||
message = "Hello there!"
|
||||
@@ -108,7 +109,7 @@ class TestSocketEndpoints:
|
||||
assert data["message"] == "User not connected"
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_send_message_unauthenticated(self, client: AsyncClient):
|
||||
async def test_send_message_unauthenticated(self, client: AsyncClient) -> None:
|
||||
"""Test sending message without authentication."""
|
||||
response = await client.post(
|
||||
"/api/v1/socket/send-message",
|
||||
@@ -122,7 +123,7 @@ class TestSocketEndpoints:
|
||||
authenticated_client: AsyncClient,
|
||||
authenticated_user: User,
|
||||
mock_socket_manager,
|
||||
):
|
||||
) -> None:
|
||||
"""Test broadcasting message to all users successfully."""
|
||||
message = "Important announcement!"
|
||||
|
||||
@@ -148,7 +149,7 @@ class TestSocketEndpoints:
|
||||
)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_broadcast_message_unauthenticated(self, client: AsyncClient):
|
||||
async def test_broadcast_message_unauthenticated(self, client: AsyncClient) -> None:
|
||||
"""Test broadcasting message without authentication."""
|
||||
response = await client.post(
|
||||
"/api/v1/socket/broadcast",
|
||||
@@ -159,7 +160,7 @@ class TestSocketEndpoints:
|
||||
@pytest.mark.asyncio
|
||||
async def test_send_message_missing_parameters(
|
||||
self, authenticated_client: AsyncClient, authenticated_user: User,
|
||||
):
|
||||
) -> None:
|
||||
"""Test sending message with missing parameters."""
|
||||
# Missing target_user_id
|
||||
response = await authenticated_client.post(
|
||||
@@ -178,7 +179,7 @@ class TestSocketEndpoints:
|
||||
@pytest.mark.asyncio
|
||||
async def test_broadcast_message_missing_parameters(
|
||||
self, authenticated_client: AsyncClient, authenticated_user: User,
|
||||
):
|
||||
) -> None:
|
||||
"""Test broadcasting message with missing parameters."""
|
||||
response = await authenticated_client.post("/api/v1/socket/broadcast")
|
||||
assert response.status_code == 422
|
||||
@@ -186,7 +187,7 @@ class TestSocketEndpoints:
|
||||
@pytest.mark.asyncio
|
||||
async def test_send_message_invalid_user_id(
|
||||
self, authenticated_client: AsyncClient, authenticated_user: User,
|
||||
):
|
||||
) -> None:
|
||||
"""Test sending message with invalid user ID."""
|
||||
response = await authenticated_client.post(
|
||||
"/api/v1/socket/send-message",
|
||||
@@ -200,7 +201,7 @@ class TestSocketEndpoints:
|
||||
authenticated_client: AsyncClient,
|
||||
authenticated_user: User,
|
||||
mock_socket_manager,
|
||||
):
|
||||
) -> None:
|
||||
"""Test that socket status correctly shows if user is connected."""
|
||||
# Test when user is connected
|
||||
mock_socket_manager.get_connected_users.return_value = [
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
"""Tests for sound API endpoints."""
|
||||
# ruff: noqa: ARG002, PLR2004, E501, PLC0415, ANN001, ANN202
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
from httpx import ASGITransport, AsyncClient
|
||||
|
||||
from app.models.user import User
|
||||
from app.services.sound_normalizer import NormalizationResults
|
||||
from app.services.sound_scanner import ScanResults
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from app.services.sound_normalizer import NormalizationResults
|
||||
from app.services.sound_scanner import ScanResults
|
||||
|
||||
|
||||
class TestSoundEndpoints:
|
||||
@@ -18,7 +22,7 @@ class TestSoundEndpoints:
|
||||
self,
|
||||
authenticated_admin_client: AsyncClient,
|
||||
admin_user: User,
|
||||
):
|
||||
) -> None:
|
||||
"""Test successful sound scanning."""
|
||||
# Mock the scanner service to return successful results
|
||||
mock_results: ScanResults = {
|
||||
@@ -95,7 +99,7 @@ class TestSoundEndpoints:
|
||||
assert results["files"][2]["status"] == "deleted"
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_scan_sounds_unauthenticated(self, client: AsyncClient):
|
||||
async def test_scan_sounds_unauthenticated(self, client: AsyncClient) -> None:
|
||||
"""Test scanning sounds without authentication."""
|
||||
response = await client.post("/api/v1/sounds/scan")
|
||||
|
||||
@@ -108,7 +112,7 @@ class TestSoundEndpoints:
|
||||
self,
|
||||
test_app,
|
||||
test_user: User,
|
||||
):
|
||||
) -> None:
|
||||
"""Test scanning sounds with non-admin user."""
|
||||
from app.core.dependencies import get_current_active_user_flexible
|
||||
|
||||
@@ -142,7 +146,7 @@ class TestSoundEndpoints:
|
||||
self,
|
||||
test_app,
|
||||
admin_user: User,
|
||||
):
|
||||
) -> None:
|
||||
"""Test scanning sounds with admin user."""
|
||||
from app.core.dependencies import get_current_active_user_flexible
|
||||
|
||||
@@ -189,7 +193,7 @@ class TestSoundEndpoints:
|
||||
self,
|
||||
authenticated_admin_client: AsyncClient,
|
||||
admin_user: User,
|
||||
):
|
||||
) -> None:
|
||||
"""Test scanning sounds when service raises an error."""
|
||||
with patch(
|
||||
"app.services.sound_scanner.SoundScannerService.scan_soundboard_directory",
|
||||
@@ -208,7 +212,7 @@ class TestSoundEndpoints:
|
||||
self,
|
||||
authenticated_admin_client: AsyncClient,
|
||||
admin_user: User,
|
||||
):
|
||||
) -> None:
|
||||
"""Test successful custom directory scanning."""
|
||||
mock_results: ScanResults = {
|
||||
"scanned": 2,
|
||||
@@ -272,7 +276,7 @@ class TestSoundEndpoints:
|
||||
self,
|
||||
authenticated_admin_client: AsyncClient,
|
||||
admin_user: User,
|
||||
):
|
||||
) -> None:
|
||||
"""Test custom directory scanning with default sound type."""
|
||||
mock_results: ScanResults = {
|
||||
"scanned": 1,
|
||||
@@ -304,7 +308,7 @@ class TestSoundEndpoints:
|
||||
self,
|
||||
authenticated_admin_client: AsyncClient,
|
||||
admin_user: User,
|
||||
):
|
||||
) -> None:
|
||||
"""Test custom directory scanning with invalid path."""
|
||||
with patch(
|
||||
"app.services.sound_scanner.SoundScannerService.scan_directory",
|
||||
@@ -322,7 +326,7 @@ class TestSoundEndpoints:
|
||||
assert "Directory does not exist" in data["detail"]
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_scan_custom_directory_unauthenticated(self, client: AsyncClient):
|
||||
async def test_scan_custom_directory_unauthenticated(self, client: AsyncClient) -> None:
|
||||
"""Test custom directory scanning without authentication."""
|
||||
response = await client.post(
|
||||
"/api/v1/sounds/scan/custom", params={"directory": "/some/path"},
|
||||
@@ -337,7 +341,7 @@ class TestSoundEndpoints:
|
||||
self,
|
||||
test_app,
|
||||
test_user: User,
|
||||
):
|
||||
) -> None:
|
||||
"""Test custom directory scanning with non-admin user."""
|
||||
from app.core.dependencies import get_current_active_user_flexible
|
||||
|
||||
@@ -374,7 +378,7 @@ class TestSoundEndpoints:
|
||||
self,
|
||||
authenticated_admin_client: AsyncClient,
|
||||
admin_user: User,
|
||||
):
|
||||
) -> None:
|
||||
"""Test custom directory scanning when service raises an error."""
|
||||
with patch(
|
||||
"app.services.sound_scanner.SoundScannerService.scan_directory",
|
||||
@@ -395,7 +399,7 @@ class TestSoundEndpoints:
|
||||
self,
|
||||
authenticated_admin_client: AsyncClient,
|
||||
admin_user: User,
|
||||
):
|
||||
) -> None:
|
||||
"""Test scanning with some errors in results."""
|
||||
mock_results: ScanResults = {
|
||||
"scanned": 3,
|
||||
@@ -467,7 +471,7 @@ class TestSoundEndpoints:
|
||||
self,
|
||||
authenticated_admin_client: AsyncClient,
|
||||
admin_user: User,
|
||||
):
|
||||
) -> None:
|
||||
"""Test that endpoint response has correct structure."""
|
||||
mock_results: ScanResults = {
|
||||
"scanned": 0,
|
||||
@@ -519,7 +523,7 @@ class TestSoundEndpoints:
|
||||
self,
|
||||
authenticated_admin_client: AsyncClient,
|
||||
admin_user: User,
|
||||
):
|
||||
) -> None:
|
||||
"""Test successful normalization of all sounds."""
|
||||
mock_results: NormalizationResults = {
|
||||
"processed": 3,
|
||||
@@ -597,7 +601,7 @@ class TestSoundEndpoints:
|
||||
self,
|
||||
authenticated_admin_client: AsyncClient,
|
||||
admin_user: User,
|
||||
):
|
||||
) -> None:
|
||||
"""Test normalization with force parameter."""
|
||||
mock_results: NormalizationResults = {
|
||||
"processed": 1,
|
||||
@@ -626,7 +630,7 @@ class TestSoundEndpoints:
|
||||
self,
|
||||
authenticated_admin_client: AsyncClient,
|
||||
admin_user: User,
|
||||
):
|
||||
) -> None:
|
||||
"""Test normalization with one_pass parameter."""
|
||||
mock_results: NormalizationResults = {
|
||||
"processed": 1,
|
||||
@@ -651,7 +655,7 @@ class TestSoundEndpoints:
|
||||
mock_normalize.assert_called_once_with(force=False, one_pass=True)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_normalize_all_sounds_unauthenticated(self, client: AsyncClient):
|
||||
async def test_normalize_all_sounds_unauthenticated(self, client: AsyncClient) -> None:
|
||||
"""Test normalizing sounds without authentication."""
|
||||
response = await client.post("/api/v1/sounds/normalize/all")
|
||||
|
||||
@@ -664,7 +668,7 @@ class TestSoundEndpoints:
|
||||
self,
|
||||
test_app,
|
||||
test_user: User,
|
||||
):
|
||||
) -> None:
|
||||
"""Test normalizing sounds with non-admin user."""
|
||||
from app.core.dependencies import get_current_active_user_flexible
|
||||
|
||||
@@ -699,7 +703,7 @@ class TestSoundEndpoints:
|
||||
self,
|
||||
authenticated_admin_client: AsyncClient,
|
||||
admin_user: User,
|
||||
):
|
||||
) -> None:
|
||||
"""Test normalization when service raises an error."""
|
||||
with patch(
|
||||
"app.services.sound_normalizer.SoundNormalizerService.normalize_all_sounds",
|
||||
@@ -720,7 +724,7 @@ class TestSoundEndpoints:
|
||||
self,
|
||||
authenticated_admin_client: AsyncClient,
|
||||
admin_user: User,
|
||||
):
|
||||
) -> None:
|
||||
"""Test successful normalization by sound type."""
|
||||
mock_results: NormalizationResults = {
|
||||
"processed": 2,
|
||||
@@ -787,7 +791,7 @@ class TestSoundEndpoints:
|
||||
self,
|
||||
authenticated_admin_client: AsyncClient,
|
||||
admin_user: User,
|
||||
):
|
||||
) -> None:
|
||||
"""Test normalization with invalid sound type."""
|
||||
response = await authenticated_admin_client.post(
|
||||
"/api/v1/sounds/normalize/type/INVALID",
|
||||
@@ -803,7 +807,7 @@ class TestSoundEndpoints:
|
||||
self,
|
||||
authenticated_admin_client: AsyncClient,
|
||||
admin_user: User,
|
||||
):
|
||||
) -> None:
|
||||
"""Test normalization by type with force and one_pass parameters."""
|
||||
mock_results: NormalizationResults = {
|
||||
"processed": 1,
|
||||
@@ -835,7 +839,7 @@ class TestSoundEndpoints:
|
||||
self,
|
||||
authenticated_admin_client: AsyncClient,
|
||||
admin_user: User,
|
||||
):
|
||||
) -> None:
|
||||
"""Test successful normalization of a specific sound."""
|
||||
# Mock the sound
|
||||
mock_sound = type(
|
||||
@@ -894,7 +898,7 @@ class TestSoundEndpoints:
|
||||
self,
|
||||
authenticated_admin_client: AsyncClient,
|
||||
admin_user: User,
|
||||
):
|
||||
) -> None:
|
||||
"""Test normalization of non-existent sound."""
|
||||
with patch(
|
||||
"app.repositories.sound.SoundRepository.get_by_id",
|
||||
@@ -914,7 +918,7 @@ class TestSoundEndpoints:
|
||||
self,
|
||||
authenticated_admin_client: AsyncClient,
|
||||
admin_user: User,
|
||||
):
|
||||
) -> None:
|
||||
"""Test normalization when the sound normalization fails."""
|
||||
# Mock the sound
|
||||
mock_sound = type(
|
||||
@@ -966,7 +970,7 @@ class TestSoundEndpoints:
|
||||
self,
|
||||
authenticated_admin_client: AsyncClient,
|
||||
admin_user: User,
|
||||
):
|
||||
) -> None:
|
||||
"""Test sound normalization with force and one_pass parameters."""
|
||||
# Mock the sound
|
||||
mock_sound = type(
|
||||
@@ -1013,15 +1017,15 @@ class TestSoundEndpoints:
|
||||
|
||||
# Verify parameters were passed to normalize_sound
|
||||
call_args = mock_normalize_sound.call_args
|
||||
assert call_args[1]["force"] == True
|
||||
assert call_args[1]["one_pass"] == True
|
||||
assert call_args[1]["force"]
|
||||
assert call_args[1]["one_pass"]
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_normalize_sound_by_id_skipped(
|
||||
self,
|
||||
authenticated_admin_client: AsyncClient,
|
||||
admin_user: User,
|
||||
):
|
||||
) -> None:
|
||||
"""Test normalization when sound is already normalized and not forced."""
|
||||
# Mock the sound
|
||||
mock_sound = type(
|
||||
@@ -1071,7 +1075,7 @@ class TestSoundEndpoints:
|
||||
assert data["reason"] == "already normalized"
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_normalize_sound_by_id_unauthenticated(self, client: AsyncClient):
|
||||
async def test_normalize_sound_by_id_unauthenticated(self, client: AsyncClient) -> None:
|
||||
"""Test normalizing a specific sound without authentication."""
|
||||
response = await client.post("/api/v1/sounds/normalize/42")
|
||||
|
||||
@@ -1084,7 +1088,7 @@ class TestSoundEndpoints:
|
||||
self,
|
||||
test_app,
|
||||
test_user: User,
|
||||
):
|
||||
) -> None:
|
||||
"""Test normalizing a specific sound with non-admin user."""
|
||||
from app.core.dependencies import get_current_active_user_flexible
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
"""Tests for VLC player API endpoints."""
|
||||
# ruff: noqa: ARG002, PLR2004, E501
|
||||
|
||||
from unittest.mock import AsyncMock
|
||||
|
||||
@@ -20,7 +21,7 @@ class TestVLCEndpoints:
|
||||
test_app: FastAPI,
|
||||
authenticated_client: AsyncClient,
|
||||
authenticated_user: User,
|
||||
):
|
||||
) -> None:
|
||||
"""Test successful sound playback via VLC."""
|
||||
# Set up mocks
|
||||
mock_vlc_service = AsyncMock()
|
||||
@@ -73,7 +74,7 @@ class TestVLCEndpoints:
|
||||
test_app: FastAPI,
|
||||
authenticated_client: AsyncClient,
|
||||
authenticated_user: User,
|
||||
):
|
||||
) -> None:
|
||||
"""Test VLC playback when sound is not found."""
|
||||
# Set up mocks
|
||||
mock_vlc_service = AsyncMock()
|
||||
@@ -106,7 +107,7 @@ class TestVLCEndpoints:
|
||||
test_app: FastAPI,
|
||||
authenticated_client: AsyncClient,
|
||||
authenticated_user: User,
|
||||
):
|
||||
) -> None:
|
||||
"""Test VLC playback when VLC launch fails."""
|
||||
# Set up mocks
|
||||
mock_vlc_service = AsyncMock()
|
||||
@@ -153,7 +154,7 @@ class TestVLCEndpoints:
|
||||
test_app: FastAPI,
|
||||
authenticated_client: AsyncClient,
|
||||
authenticated_user: User,
|
||||
):
|
||||
) -> None:
|
||||
"""Test VLC playback when service raises an exception."""
|
||||
# Set up mocks
|
||||
mock_vlc_service = AsyncMock()
|
||||
@@ -184,7 +185,7 @@ class TestVLCEndpoints:
|
||||
async def test_play_sound_with_vlc_unauthenticated(
|
||||
self,
|
||||
client: AsyncClient,
|
||||
):
|
||||
) -> None:
|
||||
"""Test VLC playback without authentication."""
|
||||
response = await client.post("/api/v1/sounds/vlc/play/1")
|
||||
assert response.status_code == 401
|
||||
@@ -195,7 +196,7 @@ class TestVLCEndpoints:
|
||||
test_app: FastAPI,
|
||||
authenticated_client: AsyncClient,
|
||||
authenticated_user: User,
|
||||
):
|
||||
) -> None:
|
||||
"""Test successful stopping of all VLC instances."""
|
||||
# Set up mock
|
||||
mock_vlc_service = AsyncMock()
|
||||
@@ -234,7 +235,7 @@ class TestVLCEndpoints:
|
||||
test_app: FastAPI,
|
||||
authenticated_client: AsyncClient,
|
||||
authenticated_user: User,
|
||||
):
|
||||
) -> None:
|
||||
"""Test stopping VLC instances when none are running."""
|
||||
# Set up mock
|
||||
mock_vlc_service = AsyncMock()
|
||||
@@ -268,7 +269,7 @@ class TestVLCEndpoints:
|
||||
test_app: FastAPI,
|
||||
authenticated_client: AsyncClient,
|
||||
authenticated_user: User,
|
||||
):
|
||||
) -> None:
|
||||
"""Test stopping VLC instances with partial success."""
|
||||
# Set up mock
|
||||
mock_vlc_service = AsyncMock()
|
||||
@@ -303,7 +304,7 @@ class TestVLCEndpoints:
|
||||
test_app: FastAPI,
|
||||
authenticated_client: AsyncClient,
|
||||
authenticated_user: User,
|
||||
):
|
||||
) -> None:
|
||||
"""Test stopping VLC instances when service fails."""
|
||||
# Set up mock
|
||||
mock_vlc_service = AsyncMock()
|
||||
@@ -337,7 +338,7 @@ class TestVLCEndpoints:
|
||||
test_app: FastAPI,
|
||||
authenticated_client: AsyncClient,
|
||||
authenticated_user: User,
|
||||
):
|
||||
) -> None:
|
||||
"""Test stopping VLC instances when service raises an exception."""
|
||||
# Set up mock to raise an exception
|
||||
mock_vlc_service = AsyncMock()
|
||||
@@ -360,7 +361,7 @@ class TestVLCEndpoints:
|
||||
async def test_stop_all_vlc_instances_unauthenticated(
|
||||
self,
|
||||
client: AsyncClient,
|
||||
):
|
||||
) -> None:
|
||||
"""Test stopping VLC instances without authentication."""
|
||||
response = await client.post("/api/v1/sounds/vlc/stop-all")
|
||||
assert response.status_code == 401
|
||||
@@ -371,7 +372,7 @@ class TestVLCEndpoints:
|
||||
test_app: FastAPI,
|
||||
authenticated_admin_client: AsyncClient,
|
||||
admin_user: User,
|
||||
):
|
||||
) -> None:
|
||||
"""Test VLC endpoints work with admin user."""
|
||||
# Set up mocks
|
||||
mock_vlc_service = AsyncMock()
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
"""Test configuration and fixtures."""
|
||||
# ruff: noqa: ANN401, ANN001
|
||||
|
||||
import asyncio
|
||||
from collections.abc import AsyncGenerator
|
||||
@@ -7,12 +8,15 @@ from typing import Any
|
||||
import pytest
|
||||
import pytest_asyncio
|
||||
from fastapi import FastAPI
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
from httpx import ASGITransport, AsyncClient
|
||||
from sqlalchemy.ext.asyncio import create_async_engine
|
||||
from sqlmodel import SQLModel, select
|
||||
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
|
||||
from app.models.plan import Plan
|
||||
from app.models.user import User
|
||||
from app.utils.auth import JWTUtils, PasswordUtils
|
||||
@@ -61,14 +65,8 @@ async def test_session(test_engine: Any) -> AsyncGenerator[AsyncSession, None]:
|
||||
|
||||
|
||||
@pytest_asyncio.fixture
|
||||
async def test_app(test_session: AsyncSession):
|
||||
async def test_app(test_session: AsyncSession) -> FastAPI:
|
||||
"""Create a test FastAPI application."""
|
||||
from fastapi import FastAPI
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
|
||||
from app.api import api_router
|
||||
from app.middleware.logging import LoggingMiddleware
|
||||
|
||||
# Create FastAPI app directly for testing (without Socket.IO)
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
"""Tests for authentication service."""
|
||||
# ruff: noqa: S106, SLF001, PLC0415
|
||||
|
||||
import pytest
|
||||
import pytest_asyncio
|
||||
@@ -10,6 +11,11 @@ from app.models.user import User
|
||||
from app.schemas.auth import UserLoginRequest, UserRegisterRequest
|
||||
from app.services.auth import AuthService
|
||||
|
||||
# Constants
|
||||
HTTP_400_BAD_REQUEST = 400
|
||||
HTTP_401_UNAUTHORIZED = 401
|
||||
HTTP_404_NOT_FOUND = 404
|
||||
|
||||
|
||||
class TestAuthService:
|
||||
"""Test authentication service operations."""
|
||||
@@ -62,7 +68,7 @@ class TestAuthService:
|
||||
with pytest.raises(HTTPException) as exc_info:
|
||||
await auth_service.register(request)
|
||||
|
||||
assert exc_info.value.status_code == 400
|
||||
assert exc_info.value.status_code == HTTP_400_BAD_REQUEST
|
||||
assert "Email address is already registered" in exc_info.value.detail
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@@ -100,7 +106,7 @@ class TestAuthService:
|
||||
with pytest.raises(HTTPException) as exc_info:
|
||||
await auth_service.login(request)
|
||||
|
||||
assert exc_info.value.status_code == 401
|
||||
assert exc_info.value.status_code == HTTP_401_UNAUTHORIZED
|
||||
assert "Invalid email or password" in exc_info.value.detail
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@@ -115,7 +121,7 @@ class TestAuthService:
|
||||
with pytest.raises(HTTPException) as exc_info:
|
||||
await auth_service.login(request)
|
||||
|
||||
assert exc_info.value.status_code == 401
|
||||
assert exc_info.value.status_code == HTTP_401_UNAUTHORIZED
|
||||
assert "Invalid email or password" in exc_info.value.detail
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@@ -138,7 +144,7 @@ class TestAuthService:
|
||||
with pytest.raises(HTTPException) as exc_info:
|
||||
await auth_service.login(request)
|
||||
|
||||
assert exc_info.value.status_code == 401
|
||||
assert exc_info.value.status_code == HTTP_401_UNAUTHORIZED
|
||||
assert "Account is deactivated" in exc_info.value.detail
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@@ -161,7 +167,7 @@ class TestAuthService:
|
||||
with pytest.raises(HTTPException) as exc_info:
|
||||
await auth_service.login(request)
|
||||
|
||||
assert exc_info.value.status_code == 401
|
||||
assert exc_info.value.status_code == HTTP_401_UNAUTHORIZED
|
||||
assert "Invalid email or password" in exc_info.value.detail
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@@ -184,7 +190,7 @@ class TestAuthService:
|
||||
with pytest.raises(HTTPException) as exc_info:
|
||||
await auth_service.get_current_user(99999)
|
||||
|
||||
assert exc_info.value.status_code == 404
|
||||
assert exc_info.value.status_code == HTTP_404_NOT_FOUND
|
||||
assert "User not found" in exc_info.value.detail
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@@ -205,7 +211,7 @@ class TestAuthService:
|
||||
with pytest.raises(HTTPException) as exc_info:
|
||||
await auth_service.get_current_user(user_id)
|
||||
|
||||
assert exc_info.value.status_code == 401
|
||||
assert exc_info.value.status_code == HTTP_401_UNAUTHORIZED
|
||||
assert "Account is deactivated" in exc_info.value.detail
|
||||
|
||||
@pytest.mark.asyncio
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
"""Tests for credit service."""
|
||||
# ruff: noqa: ANN001, ANN201, PLR2004, E501
|
||||
|
||||
import json
|
||||
from unittest.mock import AsyncMock, patch
|
||||
@@ -39,7 +40,7 @@ class TestCreditService:
|
||||
)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_check_credits_sufficient(self, credit_service, sample_user):
|
||||
async def test_check_credits_sufficient(self, credit_service, sample_user) -> None:
|
||||
"""Test checking credits when user has sufficient credits."""
|
||||
mock_session = credit_service.db_session_factory()
|
||||
|
||||
@@ -55,7 +56,7 @@ class TestCreditService:
|
||||
mock_session.close.assert_called_once()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_check_credits_insufficient(self, credit_service):
|
||||
async def test_check_credits_insufficient(self, credit_service) -> None:
|
||||
"""Test checking credits when user has insufficient credits."""
|
||||
mock_session = credit_service.db_session_factory()
|
||||
poor_user = User(
|
||||
@@ -78,7 +79,7 @@ class TestCreditService:
|
||||
mock_session.close.assert_called_once()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_check_credits_user_not_found(self, credit_service):
|
||||
async def test_check_credits_user_not_found(self, credit_service) -> None:
|
||||
"""Test checking credits when user is not found."""
|
||||
mock_session = credit_service.db_session_factory()
|
||||
|
||||
@@ -93,7 +94,7 @@ class TestCreditService:
|
||||
mock_session.close.assert_called_once()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_validate_and_reserve_credits_success(self, credit_service, sample_user):
|
||||
async def test_validate_and_reserve_credits_success(self, credit_service, sample_user) -> None:
|
||||
"""Test successful credit validation and reservation."""
|
||||
mock_session = credit_service.db_session_factory()
|
||||
|
||||
@@ -112,7 +113,7 @@ class TestCreditService:
|
||||
mock_session.close.assert_called_once()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_validate_and_reserve_credits_insufficient(self, credit_service):
|
||||
async def test_validate_and_reserve_credits_insufficient(self, credit_service) -> None:
|
||||
"""Test credit validation with insufficient credits."""
|
||||
mock_session = credit_service.db_session_factory()
|
||||
poor_user = User(
|
||||
@@ -139,7 +140,7 @@ class TestCreditService:
|
||||
mock_session.close.assert_called_once()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_validate_and_reserve_credits_user_not_found(self, credit_service):
|
||||
async def test_validate_and_reserve_credits_user_not_found(self, credit_service) -> None:
|
||||
"""Test credit validation when user is not found."""
|
||||
mock_session = credit_service.db_session_factory()
|
||||
|
||||
@@ -156,7 +157,7 @@ class TestCreditService:
|
||||
mock_session.close.assert_called_once()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_deduct_credits_success(self, credit_service, sample_user):
|
||||
async def test_deduct_credits_success(self, credit_service, sample_user) -> None:
|
||||
"""Test successful credit deduction."""
|
||||
mock_session = credit_service.db_session_factory()
|
||||
|
||||
@@ -167,7 +168,7 @@ class TestCreditService:
|
||||
mock_repo.get_by_id.return_value = sample_user
|
||||
mock_socket_manager.send_to_user = AsyncMock()
|
||||
|
||||
transaction = await credit_service.deduct_credits(
|
||||
await credit_service.deduct_credits(
|
||||
1, CreditActionType.VLC_PLAY_SOUND, success=True, metadata={"test": "data"},
|
||||
)
|
||||
|
||||
@@ -202,7 +203,7 @@ class TestCreditService:
|
||||
assert json.loads(added_transaction.metadata_json) == {"test": "data"}
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_deduct_credits_failed_action_requires_success(self, credit_service, sample_user):
|
||||
async def test_deduct_credits_failed_action_requires_success(self, credit_service, sample_user) -> None:
|
||||
"""Test credit deduction when action failed but requires success."""
|
||||
mock_session = credit_service.db_session_factory()
|
||||
|
||||
@@ -213,7 +214,7 @@ class TestCreditService:
|
||||
mock_repo.get_by_id.return_value = sample_user
|
||||
mock_socket_manager.send_to_user = AsyncMock()
|
||||
|
||||
transaction = await credit_service.deduct_credits(
|
||||
await credit_service.deduct_credits(
|
||||
1, CreditActionType.VLC_PLAY_SOUND, success=False, # Action failed
|
||||
)
|
||||
|
||||
@@ -235,7 +236,7 @@ class TestCreditService:
|
||||
assert added_transaction.success is False
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_deduct_credits_insufficient(self, credit_service):
|
||||
async def test_deduct_credits_insufficient(self, credit_service) -> None:
|
||||
"""Test credit deduction with insufficient credits."""
|
||||
mock_session = credit_service.db_session_factory()
|
||||
poor_user = User(
|
||||
@@ -266,7 +267,7 @@ class TestCreditService:
|
||||
mock_session.close.assert_called_once()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_add_credits(self, credit_service, sample_user):
|
||||
async def test_add_credits(self, credit_service, sample_user) -> None:
|
||||
"""Test adding credits to user account."""
|
||||
mock_session = credit_service.db_session_factory()
|
||||
|
||||
@@ -277,7 +278,7 @@ class TestCreditService:
|
||||
mock_repo.get_by_id.return_value = sample_user
|
||||
mock_socket_manager.send_to_user = AsyncMock()
|
||||
|
||||
transaction = await credit_service.add_credits(
|
||||
await credit_service.add_credits(
|
||||
1, 5, "Bonus credits", {"reason": "signup"},
|
||||
)
|
||||
|
||||
@@ -308,7 +309,7 @@ class TestCreditService:
|
||||
assert added_transaction.description == "Bonus credits"
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_add_credits_invalid_amount(self, credit_service):
|
||||
async def test_add_credits_invalid_amount(self, credit_service) -> None:
|
||||
"""Test adding invalid amount of credits."""
|
||||
with pytest.raises(ValueError, match="Amount must be positive"):
|
||||
await credit_service.add_credits(1, 0, "Invalid")
|
||||
@@ -317,7 +318,7 @@ class TestCreditService:
|
||||
await credit_service.add_credits(1, -5, "Invalid")
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_user_balance(self, credit_service, sample_user):
|
||||
async def test_get_user_balance(self, credit_service, sample_user) -> None:
|
||||
"""Test getting user credit balance."""
|
||||
mock_session = credit_service.db_session_factory()
|
||||
|
||||
@@ -332,7 +333,7 @@ class TestCreditService:
|
||||
mock_session.close.assert_called_once()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_user_balance_user_not_found(self, credit_service):
|
||||
async def test_get_user_balance_user_not_found(self, credit_service) -> None:
|
||||
"""Test getting balance for non-existent user."""
|
||||
mock_session = credit_service.db_session_factory()
|
||||
|
||||
@@ -350,7 +351,7 @@ class TestCreditService:
|
||||
class TestInsufficientCreditsError:
|
||||
"""Test InsufficientCreditsError exception."""
|
||||
|
||||
def test_insufficient_credits_error_creation(self):
|
||||
def test_insufficient_credits_error_creation(self) -> None:
|
||||
"""Test creating InsufficientCreditsError."""
|
||||
error = InsufficientCreditsError(5, 2)
|
||||
assert error.required == 5
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
"""Tests for extraction service."""
|
||||
# ruff: noqa: ANN001, ANN201, PLR2004, SLF001, E501
|
||||
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
@@ -26,13 +27,13 @@ class TestExtractionService:
|
||||
with patch("app.services.extraction.Path.mkdir"):
|
||||
return ExtractionService(mock_session)
|
||||
|
||||
def test_init(self, extraction_service):
|
||||
def test_init(self, extraction_service) -> None:
|
||||
"""Test service initialization."""
|
||||
assert extraction_service.session is not None
|
||||
assert extraction_service.extraction_repo is not None
|
||||
assert extraction_service.sound_repo is not None
|
||||
|
||||
def test_sanitize_filename(self, extraction_service):
|
||||
def test_sanitize_filename(self, extraction_service) -> None:
|
||||
"""Test filename sanitization."""
|
||||
test_cases = [
|
||||
("Hello World", "Hello World"),
|
||||
@@ -54,7 +55,7 @@ class TestExtractionService:
|
||||
@pytest.mark.asyncio
|
||||
async def test_detect_service_info_youtube(
|
||||
self, mock_ydl_class, extraction_service,
|
||||
):
|
||||
) -> None:
|
||||
"""Test service detection for YouTube."""
|
||||
mock_ydl = Mock()
|
||||
mock_ydl_class.return_value.__enter__.return_value = mock_ydl
|
||||
@@ -79,7 +80,7 @@ class TestExtractionService:
|
||||
@pytest.mark.asyncio
|
||||
async def test_detect_service_info_failure(
|
||||
self, mock_ydl_class, extraction_service,
|
||||
):
|
||||
) -> None:
|
||||
"""Test service detection failure."""
|
||||
mock_ydl = Mock()
|
||||
mock_ydl_class.return_value.__enter__.return_value = mock_ydl
|
||||
@@ -90,7 +91,7 @@ class TestExtractionService:
|
||||
assert result is None
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_create_extraction_success(self, extraction_service):
|
||||
async def test_create_extraction_success(self, extraction_service) -> None:
|
||||
"""Test successful extraction creation."""
|
||||
url = "https://www.youtube.com/watch?v=test123"
|
||||
user_id = 1
|
||||
@@ -118,7 +119,7 @@ class TestExtractionService:
|
||||
assert result["status"] == "pending"
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_create_extraction_basic(self, extraction_service):
|
||||
async def test_create_extraction_basic(self, extraction_service) -> None:
|
||||
"""Test basic extraction creation without validation."""
|
||||
url = "https://www.youtube.com/watch?v=test123"
|
||||
user_id = 1
|
||||
@@ -144,7 +145,7 @@ class TestExtractionService:
|
||||
assert result["status"] == "pending"
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_create_extraction_any_url(self, extraction_service):
|
||||
async def test_create_extraction_any_url(self, extraction_service) -> None:
|
||||
"""Test extraction creation accepts any URL."""
|
||||
url = "https://invalid.url"
|
||||
user_id = 1
|
||||
@@ -170,7 +171,7 @@ class TestExtractionService:
|
||||
assert result["status"] == "pending"
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_process_extraction_with_service_detection(self, extraction_service):
|
||||
async def test_process_extraction_with_service_detection(self, extraction_service) -> None:
|
||||
"""Test extraction processing with service detection."""
|
||||
extraction_id = 1
|
||||
|
||||
@@ -211,8 +212,8 @@ class TestExtractionService:
|
||||
patch.object(
|
||||
extraction_service, "_create_sound_record",
|
||||
) as mock_create_sound,
|
||||
patch.object(extraction_service, "_normalize_sound") as mock_normalize,
|
||||
patch.object(extraction_service, "_add_to_main_playlist") as mock_playlist,
|
||||
patch.object(extraction_service, "_normalize_sound"),
|
||||
patch.object(extraction_service, "_add_to_main_playlist"),
|
||||
):
|
||||
mock_sound = Sound(id=42, type="EXT", name="Test", filename="test.mp3")
|
||||
mock_extract.return_value = (Path("/fake/audio.mp3"), None)
|
||||
@@ -234,7 +235,7 @@ class TestExtractionService:
|
||||
assert result["service_id"] == "test123"
|
||||
assert result["title"] == "Test Video"
|
||||
|
||||
def test_ensure_unique_filename(self, extraction_service):
|
||||
def test_ensure_unique_filename(self, extraction_service) -> None:
|
||||
"""Test unique filename generation."""
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
temp_path = Path(temp_dir)
|
||||
@@ -255,7 +256,7 @@ class TestExtractionService:
|
||||
assert result == expected_2
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_create_sound_record(self, extraction_service):
|
||||
async def test_create_sound_record(self, extraction_service) -> None:
|
||||
"""Test sound record creation."""
|
||||
# Create temporary audio file
|
||||
with tempfile.NamedTemporaryFile(suffix=".mp3", delete=False) as f:
|
||||
@@ -317,7 +318,7 @@ class TestExtractionService:
|
||||
audio_path.unlink()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_normalize_sound_success(self, extraction_service):
|
||||
async def test_normalize_sound_success(self, extraction_service) -> None:
|
||||
"""Test sound normalization."""
|
||||
sound = Sound(
|
||||
id=1,
|
||||
@@ -349,7 +350,7 @@ class TestExtractionService:
|
||||
mock_normalizer.normalize_sound.assert_called_once_with(sound)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_normalize_sound_failure(self, extraction_service):
|
||||
async def test_normalize_sound_failure(self, extraction_service) -> None:
|
||||
"""Test sound normalization failure."""
|
||||
sound = Sound(
|
||||
id=1,
|
||||
@@ -381,7 +382,7 @@ class TestExtractionService:
|
||||
mock_normalizer.normalize_sound.assert_called_once_with(sound)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_extraction_by_id(self, extraction_service):
|
||||
async def test_get_extraction_by_id(self, extraction_service) -> None:
|
||||
"""Test getting extraction by ID."""
|
||||
extraction = Extraction(
|
||||
id=1,
|
||||
@@ -409,7 +410,7 @@ class TestExtractionService:
|
||||
assert result["sound_id"] == 42
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_extraction_by_id_not_found(self, extraction_service):
|
||||
async def test_get_extraction_by_id_not_found(self, extraction_service) -> None:
|
||||
"""Test getting extraction by ID when not found."""
|
||||
extraction_service.extraction_repo.get_by_id = AsyncMock(return_value=None)
|
||||
|
||||
@@ -418,7 +419,7 @@ class TestExtractionService:
|
||||
assert result is None
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_user_extractions(self, extraction_service):
|
||||
async def test_get_user_extractions(self, extraction_service) -> None:
|
||||
"""Test getting user extractions."""
|
||||
extractions = [
|
||||
Extraction(
|
||||
@@ -455,7 +456,7 @@ class TestExtractionService:
|
||||
assert result[1]["title"] == "Test Video 2"
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_pending_extractions(self, extraction_service):
|
||||
async def test_get_pending_extractions(self, extraction_service) -> None:
|
||||
"""Test getting pending extractions."""
|
||||
pending_extractions = [
|
||||
Extraction(
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
"""Tests for extraction background processor."""
|
||||
# ruff: noqa: ANN001, ANN201, PLR2004, SLF001
|
||||
|
||||
from unittest.mock import AsyncMock, Mock, patch
|
||||
|
||||
@@ -16,7 +17,7 @@ class TestExtractionProcessor:
|
||||
# Use a custom processor instance to avoid affecting the global one
|
||||
return ExtractionProcessor()
|
||||
|
||||
def test_init(self, processor):
|
||||
def test_init(self, processor) -> None:
|
||||
"""Test processor initialization."""
|
||||
assert processor.max_concurrent > 0
|
||||
assert len(processor.running_extractions) == 0
|
||||
@@ -25,12 +26,12 @@ class TestExtractionProcessor:
|
||||
assert processor.processor_task is None
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_start_and_stop(self, processor):
|
||||
async def test_start_and_stop(self, processor) -> None:
|
||||
"""Test starting and stopping the processor."""
|
||||
# Mock the _process_queue method to avoid actual processing
|
||||
with patch.object(
|
||||
processor, "_process_queue", new_callable=AsyncMock,
|
||||
) as mock_process:
|
||||
):
|
||||
# Start the processor
|
||||
await processor.start()
|
||||
assert processor.processor_task is not None
|
||||
@@ -41,7 +42,7 @@ class TestExtractionProcessor:
|
||||
assert processor.processor_task.done()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_start_already_running(self, processor):
|
||||
async def test_start_already_running(self, processor) -> None:
|
||||
"""Test starting processor when already running."""
|
||||
with patch.object(processor, "_process_queue", new_callable=AsyncMock):
|
||||
# Start first time
|
||||
@@ -56,7 +57,7 @@ class TestExtractionProcessor:
|
||||
await processor.stop()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_queue_extraction(self, processor):
|
||||
async def test_queue_extraction(self, processor) -> None:
|
||||
"""Test queuing an extraction."""
|
||||
extraction_id = 123
|
||||
|
||||
@@ -66,7 +67,7 @@ class TestExtractionProcessor:
|
||||
assert extraction_id not in processor.running_extractions
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_queue_extraction_already_running(self, processor):
|
||||
async def test_queue_extraction_already_running(self, processor) -> None:
|
||||
"""Test queuing an extraction that's already running."""
|
||||
extraction_id = 123
|
||||
processor.running_extractions.add(extraction_id)
|
||||
@@ -75,7 +76,7 @@ class TestExtractionProcessor:
|
||||
# Should still be in running extractions
|
||||
assert extraction_id in processor.running_extractions
|
||||
|
||||
def test_get_status(self, processor):
|
||||
def test_get_status(self, processor) -> None:
|
||||
"""Test getting processor status."""
|
||||
status = processor.get_status()
|
||||
|
||||
@@ -89,7 +90,7 @@ class TestExtractionProcessor:
|
||||
assert status["currently_processing"] == 0
|
||||
assert status["available_slots"] == processor.max_concurrent
|
||||
|
||||
def test_get_status_with_running_extractions(self, processor):
|
||||
def test_get_status_with_running_extractions(self, processor) -> None:
|
||||
"""Test getting processor status with running extractions."""
|
||||
processor.running_extractions.add(123)
|
||||
processor.running_extractions.add(456)
|
||||
@@ -101,7 +102,7 @@ class TestExtractionProcessor:
|
||||
assert 123 in status["processing_ids"]
|
||||
assert 456 in status["processing_ids"]
|
||||
|
||||
def test_on_extraction_completed(self, processor):
|
||||
def test_on_extraction_completed(self, processor) -> None:
|
||||
"""Test extraction completion callback."""
|
||||
extraction_id = 123
|
||||
processor.running_extractions.add(extraction_id)
|
||||
@@ -115,7 +116,7 @@ class TestExtractionProcessor:
|
||||
# Should be removed from running extractions
|
||||
assert extraction_id not in processor.running_extractions
|
||||
|
||||
def test_on_extraction_completed_with_exception(self, processor):
|
||||
def test_on_extraction_completed_with_exception(self, processor) -> None:
|
||||
"""Test extraction completion callback with exception."""
|
||||
extraction_id = 123
|
||||
processor.running_extractions.add(extraction_id)
|
||||
@@ -130,7 +131,7 @@ class TestExtractionProcessor:
|
||||
assert extraction_id not in processor.running_extractions
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_process_single_extraction_success(self, processor):
|
||||
async def test_process_single_extraction_success(self, processor) -> None:
|
||||
"""Test processing a single extraction successfully."""
|
||||
extraction_id = 123
|
||||
|
||||
@@ -157,7 +158,7 @@ class TestExtractionProcessor:
|
||||
mock_service.process_extraction.assert_called_once_with(extraction_id)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_process_single_extraction_failure(self, processor):
|
||||
async def test_process_single_extraction_failure(self, processor) -> None:
|
||||
"""Test processing a single extraction with failure."""
|
||||
extraction_id = 123
|
||||
|
||||
@@ -183,7 +184,7 @@ class TestExtractionProcessor:
|
||||
mock_service.process_extraction.assert_called_once_with(extraction_id)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_process_pending_extractions_no_slots(self, processor):
|
||||
async def test_process_pending_extractions_no_slots(self, processor) -> None:
|
||||
"""Test processing when no slots are available."""
|
||||
# Fill all slots
|
||||
for i in range(processor.max_concurrent):
|
||||
@@ -213,7 +214,7 @@ class TestExtractionProcessor:
|
||||
assert 100 not in processor.running_extractions
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_process_pending_extractions_with_slots(self, processor):
|
||||
async def test_process_pending_extractions_with_slots(self, processor) -> None:
|
||||
"""Test processing when slots are available."""
|
||||
# Mock extraction service
|
||||
mock_service = Mock()
|
||||
@@ -230,7 +231,7 @@ class TestExtractionProcessor:
|
||||
) as mock_session_class,
|
||||
patch.object(
|
||||
processor, "_process_single_extraction", new_callable=AsyncMock,
|
||||
) as mock_process,
|
||||
),
|
||||
patch(
|
||||
"app.services.extraction_processor.ExtractionService",
|
||||
return_value=mock_service,
|
||||
@@ -254,7 +255,7 @@ class TestExtractionProcessor:
|
||||
assert mock_create_task.call_count == 2
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_process_pending_extractions_respect_limit(self, processor):
|
||||
async def test_process_pending_extractions_respect_limit(self, processor) -> None:
|
||||
"""Test that processing respects concurrency limit."""
|
||||
# Set max concurrent to 1 for this test
|
||||
processor.max_concurrent = 1
|
||||
@@ -275,7 +276,7 @@ class TestExtractionProcessor:
|
||||
) as mock_session_class,
|
||||
patch.object(
|
||||
processor, "_process_single_extraction", new_callable=AsyncMock,
|
||||
) as mock_process,
|
||||
),
|
||||
patch(
|
||||
"app.services.extraction_processor.ExtractionService",
|
||||
return_value=mock_service,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
"""Tests for OAuth service."""
|
||||
# ruff: noqa: ANN001, PLR2004, ANN401, PT011, ANN202, ANN003, ARG001
|
||||
|
||||
from typing import Any
|
||||
from unittest.mock import AsyncMock, Mock, patch
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
"""Tests for player service."""
|
||||
# ruff: noqa: ANN001, ANN201, ARG002, PLR2004, SLF001, E501, SIM117, ARG005
|
||||
|
||||
import asyncio
|
||||
import threading
|
||||
@@ -24,7 +25,7 @@ from app.services.player import (
|
||||
class TestPlayerState:
|
||||
"""Test player state data structure."""
|
||||
|
||||
def test_init_creates_default_state(self):
|
||||
def test_init_creates_default_state(self) -> None:
|
||||
"""Test that player state initializes with default values."""
|
||||
state = PlayerState()
|
||||
|
||||
@@ -42,7 +43,7 @@ class TestPlayerState:
|
||||
assert state.playlist_duration == 0
|
||||
assert state.playlist_sounds == []
|
||||
|
||||
def test_to_dict_serializes_correctly(self):
|
||||
def test_to_dict_serializes_correctly(self) -> None:
|
||||
"""Test that player state serializes to dict correctly."""
|
||||
state = PlayerState()
|
||||
state.status = PlayerStatus.PLAYING
|
||||
@@ -70,7 +71,7 @@ class TestPlayerState:
|
||||
assert result["playlist"]["length"] == 5
|
||||
assert result["playlist"]["duration"] == 150000
|
||||
|
||||
def test_serialize_sound_with_sound_object(self):
|
||||
def test_serialize_sound_with_sound_object(self) -> None:
|
||||
"""Test serializing a sound object."""
|
||||
state = PlayerState()
|
||||
sound = Sound(
|
||||
@@ -95,7 +96,7 @@ class TestPlayerState:
|
||||
assert result["thumbnail"] == "test.jpg"
|
||||
assert result["play_count"] == 5
|
||||
|
||||
def test_serialize_sound_with_none(self):
|
||||
def test_serialize_sound_with_none(self) -> None:
|
||||
"""Test serializing None sound."""
|
||||
state = PlayerState()
|
||||
result = state._serialize_sound(None)
|
||||
@@ -133,10 +134,9 @@ class TestPlayerService:
|
||||
@pytest.fixture
|
||||
def player_service(self, mock_db_session_factory, mock_vlc_instance, mock_socket_manager):
|
||||
"""Create a player service instance for testing."""
|
||||
service = PlayerService(mock_db_session_factory)
|
||||
return service
|
||||
return PlayerService(mock_db_session_factory)
|
||||
|
||||
def test_init_creates_player_service(self, mock_db_session_factory, mock_vlc_instance):
|
||||
def test_init_creates_player_service(self, mock_db_session_factory, mock_vlc_instance) -> None:
|
||||
"""Test that player service initializes correctly."""
|
||||
with patch("app.services.player.socket_manager"):
|
||||
service = PlayerService(mock_db_session_factory)
|
||||
@@ -153,7 +153,7 @@ class TestPlayerService:
|
||||
assert service._loop is None
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_start_initializes_service(self, player_service, mock_vlc_instance):
|
||||
async def test_start_initializes_service(self, player_service, mock_vlc_instance) -> None:
|
||||
"""Test that start method initializes the service."""
|
||||
with patch.object(player_service, "reload_playlist", new_callable=AsyncMock):
|
||||
await player_service.start()
|
||||
@@ -165,7 +165,7 @@ class TestPlayerService:
|
||||
player_service._player.audio_set_volume.assert_called_once_with(50)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_stop_cleans_up_service(self, player_service):
|
||||
async def test_stop_cleans_up_service(self, player_service) -> None:
|
||||
"""Test that stop method cleans up the service."""
|
||||
# Setup initial state
|
||||
player_service._is_running = True
|
||||
@@ -180,7 +180,7 @@ class TestPlayerService:
|
||||
player_service._player.release.assert_called_once()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_play_new_track(self, player_service, mock_vlc_instance):
|
||||
async def test_play_new_track(self, player_service, mock_vlc_instance) -> None:
|
||||
"""Test playing a new track."""
|
||||
# Setup test sound
|
||||
sound = Sound(
|
||||
@@ -212,7 +212,7 @@ class TestPlayerService:
|
||||
assert 1 in player_service._play_time_tracking
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_play_resume_from_pause(self, player_service):
|
||||
async def test_play_resume_from_pause(self, player_service) -> None:
|
||||
"""Test resuming playback from pause."""
|
||||
# Setup paused state
|
||||
sound = Sound(id=1, name="Test Song", filename="test.mp3", duration=30000)
|
||||
@@ -230,7 +230,7 @@ class TestPlayerService:
|
||||
player_service._player.play.assert_called_once()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_play_invalid_index(self, player_service):
|
||||
async def test_play_invalid_index(self, player_service) -> None:
|
||||
"""Test playing with invalid index raises ValueError."""
|
||||
player_service.state.playlist_sounds = []
|
||||
|
||||
@@ -238,7 +238,7 @@ class TestPlayerService:
|
||||
await player_service.play(0)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_pause_when_playing(self, player_service):
|
||||
async def test_pause_when_playing(self, player_service) -> None:
|
||||
"""Test pausing when currently playing."""
|
||||
player_service.state.status = PlayerStatus.PLAYING
|
||||
|
||||
@@ -249,7 +249,7 @@ class TestPlayerService:
|
||||
player_service._player.pause.assert_called_once()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_pause_when_not_playing(self, player_service):
|
||||
async def test_pause_when_not_playing(self, player_service) -> None:
|
||||
"""Test pausing when not playing does nothing."""
|
||||
player_service.state.status = PlayerStatus.STOPPED
|
||||
|
||||
@@ -260,7 +260,7 @@ class TestPlayerService:
|
||||
mock_broadcast.assert_not_called()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_stop_playback(self, player_service):
|
||||
async def test_stop_playback(self, player_service) -> None:
|
||||
"""Test stopping playback."""
|
||||
player_service.state.status = PlayerStatus.PLAYING
|
||||
player_service.state.current_sound_position = 5000
|
||||
@@ -274,7 +274,7 @@ class TestPlayerService:
|
||||
player_service._player.stop.assert_called_once()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_next_track(self, player_service):
|
||||
async def test_next_track(self, player_service) -> None:
|
||||
"""Test skipping to next track."""
|
||||
sound1 = Sound(id=1, name="Song 1", filename="song1.mp3")
|
||||
sound2 = Sound(id=2, name="Song 2", filename="song2.mp3")
|
||||
@@ -286,7 +286,7 @@ class TestPlayerService:
|
||||
mock_play.assert_called_once_with(1)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_previous_track(self, player_service):
|
||||
async def test_previous_track(self, player_service) -> None:
|
||||
"""Test going to previous track."""
|
||||
sound1 = Sound(id=1, name="Song 1", filename="song1.mp3")
|
||||
sound2 = Sound(id=2, name="Song 2", filename="song2.mp3")
|
||||
@@ -298,7 +298,7 @@ class TestPlayerService:
|
||||
mock_play.assert_called_once_with(0)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_seek_position(self, player_service):
|
||||
async def test_seek_position(self, player_service) -> None:
|
||||
"""Test seeking to specific position."""
|
||||
player_service.state.status = PlayerStatus.PLAYING
|
||||
player_service.state.current_sound_duration = 30000
|
||||
@@ -311,7 +311,7 @@ class TestPlayerService:
|
||||
assert player_service.state.current_sound_position == 15000
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_seek_when_stopped(self, player_service):
|
||||
async def test_seek_when_stopped(self, player_service) -> None:
|
||||
"""Test seeking when stopped does nothing."""
|
||||
player_service.state.status = PlayerStatus.STOPPED
|
||||
|
||||
@@ -322,7 +322,7 @@ class TestPlayerService:
|
||||
mock_broadcast.assert_not_called()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_set_volume(self, player_service):
|
||||
async def test_set_volume(self, player_service) -> None:
|
||||
"""Test setting volume."""
|
||||
with patch.object(player_service, "_broadcast_state", new_callable=AsyncMock):
|
||||
await player_service.set_volume(75)
|
||||
@@ -331,7 +331,7 @@ class TestPlayerService:
|
||||
player_service._player.audio_set_volume.assert_called_once_with(75)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_set_volume_clamping(self, player_service):
|
||||
async def test_set_volume_clamping(self, player_service) -> None:
|
||||
"""Test volume clamping to valid range."""
|
||||
with patch.object(player_service, "_broadcast_state", new_callable=AsyncMock):
|
||||
# Test upper bound
|
||||
@@ -343,7 +343,7 @@ class TestPlayerService:
|
||||
assert player_service.state.volume == 0
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_set_mode(self, player_service):
|
||||
async def test_set_mode(self, player_service) -> None:
|
||||
"""Test setting playback mode."""
|
||||
with patch.object(player_service, "_broadcast_state", new_callable=AsyncMock):
|
||||
await player_service.set_mode(PlayerMode.LOOP)
|
||||
@@ -351,7 +351,7 @@ class TestPlayerService:
|
||||
assert player_service.state.mode == PlayerMode.LOOP
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_reload_playlist(self, player_service):
|
||||
async def test_reload_playlist(self, player_service) -> None:
|
||||
"""Test reloading playlist from database."""
|
||||
mock_session = AsyncMock()
|
||||
player_service.db_session_factory = lambda: mock_session
|
||||
@@ -383,7 +383,7 @@ class TestPlayerService:
|
||||
assert player_service.state.playlist_duration == 75000
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_handle_playlist_id_changed(self, player_service):
|
||||
async def test_handle_playlist_id_changed(self, player_service) -> None:
|
||||
"""Test handling when playlist ID changes."""
|
||||
# Setup initial state
|
||||
player_service.state.status = PlayerStatus.PLAYING
|
||||
@@ -405,7 +405,7 @@ class TestPlayerService:
|
||||
assert player_service.state.current_sound_id == 1
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_handle_playlist_id_changed_empty_playlist(self, player_service):
|
||||
async def test_handle_playlist_id_changed_empty_playlist(self, player_service) -> None:
|
||||
"""Test handling playlist ID change with empty playlist."""
|
||||
player_service.state.status = PlayerStatus.PLAYING
|
||||
|
||||
@@ -418,7 +418,7 @@ class TestPlayerService:
|
||||
assert player_service.state.current_sound_id is None
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_handle_same_playlist_track_exists_same_index(self, player_service):
|
||||
async def test_handle_same_playlist_track_exists_same_index(self, player_service) -> None:
|
||||
"""Test handling same playlist when track exists at same index."""
|
||||
sound1 = Sound(id=1, name="Song 1", filename="song1.mp3", duration=30000)
|
||||
sound2 = Sound(id=2, name="Song 2", filename="song2.mp3", duration=45000)
|
||||
@@ -431,7 +431,7 @@ class TestPlayerService:
|
||||
assert player_service.state.current_sound == sound1
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_handle_same_playlist_track_exists_different_index(self, player_service):
|
||||
async def test_handle_same_playlist_track_exists_different_index(self, player_service) -> None:
|
||||
"""Test handling same playlist when track exists at different index."""
|
||||
sound1 = Sound(id=2, name="Song 2", filename="song2.mp3", duration=45000)
|
||||
sound2 = Sound(id=1, name="Song 1", filename="song1.mp3", duration=30000)
|
||||
@@ -444,7 +444,7 @@ class TestPlayerService:
|
||||
assert player_service.state.current_sound == sound2
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_handle_same_playlist_track_not_found(self, player_service):
|
||||
async def test_handle_same_playlist_track_not_found(self, player_service) -> None:
|
||||
"""Test handling same playlist when current track no longer exists."""
|
||||
player_service.state.status = PlayerStatus.PLAYING
|
||||
sound1 = Sound(id=2, name="Song 2", filename="song2.mp3", duration=45000)
|
||||
@@ -456,7 +456,7 @@ class TestPlayerService:
|
||||
mock_removed.assert_called_once_with(1, sounds)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_handle_track_removed_with_sounds(self, player_service):
|
||||
async def test_handle_track_removed_with_sounds(self, player_service) -> None:
|
||||
"""Test handling when current track is removed with sounds available."""
|
||||
player_service.state.status = PlayerStatus.PLAYING
|
||||
sound1 = Sound(id=2, name="Song 2", filename="song2.mp3", duration=45000)
|
||||
@@ -471,7 +471,7 @@ class TestPlayerService:
|
||||
assert player_service.state.current_sound_id == 2
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_handle_track_removed_empty_playlist(self, player_service):
|
||||
async def test_handle_track_removed_empty_playlist(self, player_service) -> None:
|
||||
"""Test handling when current track is removed with empty playlist."""
|
||||
player_service.state.status = PlayerStatus.PLAYING
|
||||
|
||||
@@ -483,7 +483,7 @@ class TestPlayerService:
|
||||
assert player_service.state.current_sound is None
|
||||
assert player_service.state.current_sound_id is None
|
||||
|
||||
def test_update_playlist_state(self, player_service):
|
||||
def test_update_playlist_state(self, player_service) -> None:
|
||||
"""Test updating playlist state information."""
|
||||
mock_playlist = Mock()
|
||||
mock_playlist.id = 5
|
||||
@@ -501,7 +501,7 @@ class TestPlayerService:
|
||||
assert player_service.state.playlist_length == 2
|
||||
assert player_service.state.playlist_duration == 75000
|
||||
|
||||
def test_find_sound_index_found(self, player_service):
|
||||
def test_find_sound_index_found(self, player_service) -> None:
|
||||
"""Test finding sound index when sound exists."""
|
||||
sound1 = Sound(id=1, name="Song 1", filename="song1.mp3", duration=30000)
|
||||
sound2 = Sound(id=2, name="Song 2", filename="song2.mp3", duration=45000)
|
||||
@@ -510,7 +510,7 @@ class TestPlayerService:
|
||||
index = player_service._find_sound_index(2, sounds)
|
||||
assert index == 1
|
||||
|
||||
def test_find_sound_index_not_found(self, player_service):
|
||||
def test_find_sound_index_not_found(self, player_service) -> None:
|
||||
"""Test finding sound index when sound doesn't exist."""
|
||||
sound1 = Sound(id=1, name="Song 1", filename="song1.mp3", duration=30000)
|
||||
sounds = [sound1]
|
||||
@@ -518,7 +518,7 @@ class TestPlayerService:
|
||||
index = player_service._find_sound_index(999, sounds)
|
||||
assert index is None
|
||||
|
||||
def test_set_first_track_as_current(self, player_service):
|
||||
def test_set_first_track_as_current(self, player_service) -> None:
|
||||
"""Test setting first track as current."""
|
||||
sound1 = Sound(id=1, name="Song 1", filename="song1.mp3", duration=30000)
|
||||
sound2 = Sound(id=2, name="Song 2", filename="song2.mp3", duration=45000)
|
||||
@@ -530,7 +530,7 @@ class TestPlayerService:
|
||||
assert player_service.state.current_sound == sound1
|
||||
assert player_service.state.current_sound_id == 1
|
||||
|
||||
def test_clear_current_track(self, player_service):
|
||||
def test_clear_current_track(self, player_service) -> None:
|
||||
"""Test clearing current track state."""
|
||||
# Set some initial state
|
||||
player_service.state.current_sound_index = 2
|
||||
@@ -544,7 +544,7 @@ class TestPlayerService:
|
||||
assert player_service.state.current_sound_id is None
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_reload_playlist_different_id_scenario(self, player_service):
|
||||
async def test_reload_playlist_different_id_scenario(self, player_service) -> None:
|
||||
"""Test complete reload scenario when playlist ID changes."""
|
||||
# Setup current state
|
||||
player_service.state.playlist_id = 1
|
||||
@@ -580,7 +580,7 @@ class TestPlayerService:
|
||||
assert player_service.state.current_sound_id == 1
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_reload_playlist_same_id_track_moved(self, player_service):
|
||||
async def test_reload_playlist_same_id_track_moved(self, player_service) -> None:
|
||||
"""Test reload when playlist ID same but track moved to different index."""
|
||||
# Setup current state
|
||||
player_service.state.playlist_id = 1
|
||||
@@ -616,7 +616,7 @@ class TestPlayerService:
|
||||
assert player_service.state.current_sound == sound1
|
||||
|
||||
|
||||
def test_get_next_index_continuous_mode(self, player_service):
|
||||
def test_get_next_index_continuous_mode(self, player_service) -> None:
|
||||
"""Test getting next index in continuous mode."""
|
||||
player_service.state.mode = PlayerMode.CONTINUOUS
|
||||
player_service.state.playlist_sounds = [Mock(), Mock(), Mock()]
|
||||
@@ -628,7 +628,7 @@ class TestPlayerService:
|
||||
# Test end of playlist
|
||||
assert player_service._get_next_index(2) is None
|
||||
|
||||
def test_get_next_index_loop_mode(self, player_service):
|
||||
def test_get_next_index_loop_mode(self, player_service) -> None:
|
||||
"""Test getting next index in loop mode."""
|
||||
player_service.state.mode = PlayerMode.LOOP
|
||||
player_service.state.playlist_sounds = [Mock(), Mock(), Mock()]
|
||||
@@ -640,7 +640,7 @@ class TestPlayerService:
|
||||
# Test wrapping to beginning
|
||||
assert player_service._get_next_index(2) == 0
|
||||
|
||||
def test_get_next_index_loop_one_mode(self, player_service):
|
||||
def test_get_next_index_loop_one_mode(self, player_service) -> None:
|
||||
"""Test getting next index in loop one mode."""
|
||||
player_service.state.mode = PlayerMode.LOOP_ONE
|
||||
player_service.state.playlist_sounds = [Mock(), Mock(), Mock()]
|
||||
@@ -650,7 +650,7 @@ class TestPlayerService:
|
||||
assert player_service._get_next_index(1) == 1
|
||||
assert player_service._get_next_index(2) == 2
|
||||
|
||||
def test_get_next_index_single_mode(self, player_service):
|
||||
def test_get_next_index_single_mode(self, player_service) -> None:
|
||||
"""Test getting next index in single mode."""
|
||||
player_service.state.mode = PlayerMode.SINGLE
|
||||
player_service.state.playlist_sounds = [Mock(), Mock(), Mock()]
|
||||
@@ -660,7 +660,7 @@ class TestPlayerService:
|
||||
assert player_service._get_next_index(1) is None
|
||||
assert player_service._get_next_index(2) is None
|
||||
|
||||
def test_get_next_index_random_mode(self, player_service):
|
||||
def test_get_next_index_random_mode(self, player_service) -> None:
|
||||
"""Test getting next index in random mode."""
|
||||
player_service.state.mode = PlayerMode.RANDOM
|
||||
player_service.state.playlist_sounds = [Mock(), Mock(), Mock()]
|
||||
@@ -674,7 +674,7 @@ class TestPlayerService:
|
||||
# Should exclude current index
|
||||
mock_choice.assert_called_once_with([1, 2])
|
||||
|
||||
def test_get_previous_index(self, player_service):
|
||||
def test_get_previous_index(self, player_service) -> None:
|
||||
"""Test getting previous index."""
|
||||
player_service.state.playlist_sounds = [Mock(), Mock(), Mock()]
|
||||
|
||||
@@ -690,7 +690,7 @@ class TestPlayerService:
|
||||
player_service.state.mode = PlayerMode.LOOP
|
||||
assert player_service._get_previous_index(0) == 2
|
||||
|
||||
def test_update_play_time(self, player_service):
|
||||
def test_update_play_time(self, player_service) -> None:
|
||||
"""Test updating play time tracking."""
|
||||
# Setup state
|
||||
player_service.state.status = PlayerStatus.PLAYING
|
||||
@@ -716,7 +716,7 @@ class TestPlayerService:
|
||||
assert tracking["last_update"] == current_time
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_record_play_count(self, player_service):
|
||||
async def test_record_play_count(self, player_service) -> None:
|
||||
"""Test recording play count for a sound."""
|
||||
mock_session = AsyncMock()
|
||||
player_service.db_session_factory = lambda: mock_session
|
||||
@@ -742,7 +742,7 @@ class TestPlayerService:
|
||||
mock_session.add.assert_called_once()
|
||||
mock_session.commit.assert_called_once()
|
||||
|
||||
def test_get_state(self, player_service):
|
||||
def test_get_state(self, player_service) -> None:
|
||||
"""Test getting current player state."""
|
||||
result = player_service.get_state()
|
||||
assert isinstance(result, dict)
|
||||
@@ -750,7 +750,7 @@ class TestPlayerService:
|
||||
assert "mode" in result
|
||||
assert "volume" in result
|
||||
|
||||
def test_uses_shared_sound_path_utility(self, player_service):
|
||||
def test_uses_shared_sound_path_utility(self, player_service) -> None:
|
||||
"""Test that player service uses the shared sound path utility."""
|
||||
sound = Sound(
|
||||
id=1,
|
||||
@@ -767,7 +767,7 @@ class TestPlayerService:
|
||||
mock_path.return_value = mock_file_path
|
||||
|
||||
# This should fail because file doesn't exist
|
||||
result = asyncio.run(player_service.play(0))
|
||||
asyncio.run(player_service.play(0))
|
||||
# Verify the utility was called
|
||||
mock_path.assert_called_once_with(sound)
|
||||
|
||||
@@ -776,7 +776,7 @@ class TestPlayerServiceGlobalFunctions:
|
||||
"""Test global player service functions."""
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_initialize_player_service(self):
|
||||
async def test_initialize_player_service(self) -> None:
|
||||
"""Test initializing global player service."""
|
||||
mock_factory = Mock()
|
||||
|
||||
@@ -790,7 +790,7 @@ class TestPlayerServiceGlobalFunctions:
|
||||
mock_service.start.assert_called_once()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_shutdown_player_service(self):
|
||||
async def test_shutdown_player_service(self) -> None:
|
||||
"""Test shutting down global player service."""
|
||||
# Mock global player service exists
|
||||
with patch("app.services.player.player_service") as mock_global:
|
||||
@@ -802,7 +802,7 @@ class TestPlayerServiceGlobalFunctions:
|
||||
await shutdown_player_service()
|
||||
mock_service.stop.assert_called_once()
|
||||
|
||||
def test_get_player_service_success(self):
|
||||
def test_get_player_service_success(self) -> None:
|
||||
"""Test getting player service when initialized."""
|
||||
mock_service = Mock()
|
||||
|
||||
@@ -810,7 +810,7 @@ class TestPlayerServiceGlobalFunctions:
|
||||
result = get_player_service()
|
||||
assert result is mock_service
|
||||
|
||||
def test_get_player_service_not_initialized(self):
|
||||
def test_get_player_service_not_initialized(self) -> None:
|
||||
"""Test getting player service when not initialized."""
|
||||
with patch("app.services.player.player_service", None):
|
||||
with pytest.raises(RuntimeError, match="Player service not initialized"):
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
"""Tests for playlist service."""
|
||||
# ruff: noqa: ANN001, ARG002, PLR2004, E501, PLC0415
|
||||
|
||||
from collections.abc import AsyncGenerator
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
"""Tests for socket service."""
|
||||
# ruff: noqa: ANN001, ANN201, ARG002, PLR2004, SLF001, E501, ANN202
|
||||
|
||||
from unittest.mock import AsyncMock, patch
|
||||
|
||||
@@ -22,7 +23,7 @@ class TestSocketManager:
|
||||
socket_manager.sio = AsyncMock(spec=socketio.AsyncServer)
|
||||
return socket_manager.sio
|
||||
|
||||
def test_init_creates_socket_server(self):
|
||||
def test_init_creates_socket_server(self) -> None:
|
||||
"""Test that socket manager initializes with proper configuration."""
|
||||
manager = SocketManager()
|
||||
|
||||
@@ -33,7 +34,7 @@ class TestSocketManager:
|
||||
assert len(manager.socket_users) == 0
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_send_to_user_success(self, socket_manager, mock_sio):
|
||||
async def test_send_to_user_success(self, socket_manager, mock_sio) -> None:
|
||||
"""Test sending message to connected user."""
|
||||
user_id = "123"
|
||||
room_id = "user_123"
|
||||
@@ -48,7 +49,7 @@ class TestSocketManager:
|
||||
mock_sio.emit.assert_called_once_with(event, data, room=room_id)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_send_to_user_not_connected(self, socket_manager, mock_sio):
|
||||
async def test_send_to_user_not_connected(self, socket_manager, mock_sio) -> None:
|
||||
"""Test sending message to user who is not connected."""
|
||||
user_id = "999"
|
||||
event = "test_event"
|
||||
@@ -60,7 +61,7 @@ class TestSocketManager:
|
||||
mock_sio.emit.assert_not_called()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_broadcast_to_all(self, socket_manager, mock_sio):
|
||||
async def test_broadcast_to_all(self, socket_manager, mock_sio) -> None:
|
||||
"""Test broadcasting message to all users."""
|
||||
event = "broadcast_event"
|
||||
data = {"message": "announcement"}
|
||||
@@ -69,7 +70,7 @@ class TestSocketManager:
|
||||
|
||||
mock_sio.emit.assert_called_once_with(event, data)
|
||||
|
||||
def test_get_connected_users(self, socket_manager):
|
||||
def test_get_connected_users(self, socket_manager) -> None:
|
||||
"""Test getting list of connected users."""
|
||||
# Add some users
|
||||
socket_manager.user_rooms["1"] = "user_1"
|
||||
@@ -83,7 +84,7 @@ class TestSocketManager:
|
||||
assert "2" in connected_users
|
||||
assert "3" in connected_users
|
||||
|
||||
def test_get_room_info(self, socket_manager):
|
||||
def test_get_room_info(self, socket_manager) -> None:
|
||||
"""Test getting room information."""
|
||||
# Add some users
|
||||
socket_manager.user_rooms["1"] = "user_1"
|
||||
@@ -99,7 +100,7 @@ class TestSocketManager:
|
||||
@patch("app.services.socket.JWTUtils.decode_access_token")
|
||||
async def test_connect_handler_success(
|
||||
self, mock_decode, mock_extract_token, socket_manager, mock_sio,
|
||||
):
|
||||
) -> None:
|
||||
"""Test successful connection with valid token."""
|
||||
# Setup mocks
|
||||
mock_extract_token.return_value = "valid_token"
|
||||
@@ -110,7 +111,6 @@ class TestSocketManager:
|
||||
|
||||
# Access the connect handler directly
|
||||
handlers = {}
|
||||
original_event = socket_manager.sio.event
|
||||
|
||||
def mock_event(func):
|
||||
handlers[func.__name__] = func
|
||||
@@ -134,7 +134,7 @@ class TestSocketManager:
|
||||
@patch("app.services.socket.extract_access_token_from_cookies")
|
||||
async def test_connect_handler_no_token(
|
||||
self, mock_extract_token, socket_manager, mock_sio,
|
||||
):
|
||||
) -> None:
|
||||
"""Test connection with no access token."""
|
||||
# Setup mocks
|
||||
mock_extract_token.return_value = None
|
||||
@@ -144,7 +144,6 @@ class TestSocketManager:
|
||||
|
||||
# Access the connect handler directly
|
||||
handlers = {}
|
||||
original_event = socket_manager.sio.event
|
||||
|
||||
def mock_event(func):
|
||||
handlers[func.__name__] = func
|
||||
@@ -168,7 +167,7 @@ class TestSocketManager:
|
||||
@patch("app.services.socket.JWTUtils.decode_access_token")
|
||||
async def test_connect_handler_invalid_token(
|
||||
self, mock_decode, mock_extract_token, socket_manager, mock_sio,
|
||||
):
|
||||
) -> None:
|
||||
"""Test connection with invalid token."""
|
||||
# Setup mocks
|
||||
mock_extract_token.return_value = "invalid_token"
|
||||
@@ -179,7 +178,6 @@ class TestSocketManager:
|
||||
|
||||
# Access the connect handler directly
|
||||
handlers = {}
|
||||
original_event = socket_manager.sio.event
|
||||
|
||||
def mock_event(func):
|
||||
handlers[func.__name__] = func
|
||||
@@ -203,7 +201,7 @@ class TestSocketManager:
|
||||
@patch("app.services.socket.JWTUtils.decode_access_token")
|
||||
async def test_connect_handler_missing_user_id(
|
||||
self, mock_decode, mock_extract_token, socket_manager, mock_sio,
|
||||
):
|
||||
) -> None:
|
||||
"""Test connection with token missing user ID."""
|
||||
# Setup mocks
|
||||
mock_extract_token.return_value = "token_without_user_id"
|
||||
@@ -214,7 +212,6 @@ class TestSocketManager:
|
||||
|
||||
# Access the connect handler directly
|
||||
handlers = {}
|
||||
original_event = socket_manager.sio.event
|
||||
|
||||
def mock_event(func):
|
||||
handlers[func.__name__] = func
|
||||
@@ -234,7 +231,7 @@ class TestSocketManager:
|
||||
assert len(socket_manager.user_rooms) == 0
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_disconnect_handler(self, socket_manager, mock_sio):
|
||||
async def test_disconnect_handler(self, socket_manager, mock_sio) -> None:
|
||||
"""Test disconnect handler."""
|
||||
# Setup initial state
|
||||
socket_manager.socket_users["test_sid"] = "123"
|
||||
@@ -242,7 +239,6 @@ class TestSocketManager:
|
||||
|
||||
# Access the disconnect handler directly
|
||||
handlers = {}
|
||||
original_event = socket_manager.sio.event
|
||||
|
||||
def mock_event(func):
|
||||
handlers[func.__name__] = func
|
||||
@@ -259,11 +255,10 @@ class TestSocketManager:
|
||||
assert "123" not in socket_manager.user_rooms
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_disconnect_handler_unknown_socket(self, socket_manager, mock_sio):
|
||||
async def test_disconnect_handler_unknown_socket(self, socket_manager, mock_sio) -> None:
|
||||
"""Test disconnect handler with unknown socket."""
|
||||
# Access the disconnect handler directly
|
||||
handlers = {}
|
||||
original_event = socket_manager.sio.event
|
||||
|
||||
def mock_event(func):
|
||||
handlers[func.__name__] = func
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
"""Tests for sound normalizer service."""
|
||||
# ruff: noqa: ANN001, ANN201, ARG002, PLR2004, SLF001, E501, PLC0415
|
||||
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
@@ -28,7 +29,7 @@ class TestSoundNormalizerService:
|
||||
mock_settings.NORMALIZED_AUDIO_PASSES = 2
|
||||
return SoundNormalizerService(mock_session)
|
||||
|
||||
def test_init(self, normalizer_service):
|
||||
def test_init(self, normalizer_service) -> None:
|
||||
"""Test normalizer service initialization."""
|
||||
assert normalizer_service.session is not None
|
||||
assert normalizer_service.sound_repo is not None
|
||||
@@ -40,7 +41,7 @@ class TestSoundNormalizerService:
|
||||
assert "TTS" in normalizer_service.type_directories
|
||||
assert "EXT" in normalizer_service.type_directories
|
||||
|
||||
def test_get_normalized_path(self, normalizer_service):
|
||||
def test_get_normalized_path(self, normalizer_service) -> None:
|
||||
"""Test normalized path generation."""
|
||||
sound = Sound(
|
||||
id=1,
|
||||
@@ -57,7 +58,7 @@ class TestSoundNormalizerService:
|
||||
assert "sounds/normalized/soundboard" in str(normalized_path)
|
||||
assert normalized_path.name == "test_audio.mp3"
|
||||
|
||||
def test_get_original_path(self, normalizer_service):
|
||||
def test_get_original_path(self, normalizer_service) -> None:
|
||||
"""Test original path generation."""
|
||||
sound = Sound(
|
||||
id=1,
|
||||
@@ -74,7 +75,7 @@ class TestSoundNormalizerService:
|
||||
assert "sounds/originals/soundboard" in str(original_path)
|
||||
assert original_path.name == "test_audio.wav"
|
||||
|
||||
def test_get_file_hash(self, normalizer_service):
|
||||
def test_get_file_hash(self, normalizer_service) -> None:
|
||||
"""Test file hash calculation."""
|
||||
# Create a temporary file
|
||||
with tempfile.NamedTemporaryFile(mode="w", delete=False) as f:
|
||||
@@ -90,7 +91,7 @@ class TestSoundNormalizerService:
|
||||
finally:
|
||||
temp_path.unlink()
|
||||
|
||||
def test_get_file_size(self, normalizer_service):
|
||||
def test_get_file_size(self, normalizer_service) -> None:
|
||||
"""Test file size calculation."""
|
||||
# Create a temporary file
|
||||
with tempfile.NamedTemporaryFile(mode="w", delete=False) as f:
|
||||
@@ -107,7 +108,7 @@ class TestSoundNormalizerService:
|
||||
temp_path.unlink()
|
||||
|
||||
@patch("app.utils.audio.ffmpeg.probe")
|
||||
def test_get_audio_duration_success(self, mock_probe, normalizer_service):
|
||||
def test_get_audio_duration_success(self, mock_probe, normalizer_service) -> None:
|
||||
"""Test successful audio duration extraction."""
|
||||
mock_probe.return_value = {"format": {"duration": "123.456"}}
|
||||
|
||||
@@ -120,7 +121,7 @@ class TestSoundNormalizerService:
|
||||
mock_probe.assert_called_once_with(str(temp_path))
|
||||
|
||||
@patch("app.utils.audio.ffmpeg.probe")
|
||||
def test_get_audio_duration_failure(self, mock_probe, normalizer_service):
|
||||
def test_get_audio_duration_failure(self, mock_probe, normalizer_service) -> None:
|
||||
"""Test audio duration extraction failure."""
|
||||
mock_probe.side_effect = Exception("FFmpeg error")
|
||||
|
||||
@@ -133,7 +134,7 @@ class TestSoundNormalizerService:
|
||||
mock_probe.assert_called_once_with(str(temp_path))
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_normalize_sound_already_normalized(self, normalizer_service):
|
||||
async def test_normalize_sound_already_normalized(self, normalizer_service) -> None:
|
||||
"""Test normalizing a sound that's already normalized."""
|
||||
sound = Sound(
|
||||
id=1,
|
||||
@@ -154,7 +155,7 @@ class TestSoundNormalizerService:
|
||||
assert result["id"] == 1
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_normalize_sound_force_already_normalized(self, normalizer_service):
|
||||
async def test_normalize_sound_force_already_normalized(self, normalizer_service) -> None:
|
||||
"""Test force normalizing a sound that's already normalized."""
|
||||
sound = Sound(
|
||||
id=1,
|
||||
@@ -173,7 +174,7 @@ class TestSoundNormalizerService:
|
||||
patch.object(normalizer_service, "_get_normalized_path") as mock_norm_path,
|
||||
patch.object(
|
||||
normalizer_service, "_normalize_audio_two_pass",
|
||||
) as mock_normalize,
|
||||
),
|
||||
patch(
|
||||
"app.services.sound_normalizer.get_audio_duration", return_value=6000,
|
||||
),
|
||||
@@ -203,7 +204,7 @@ class TestSoundNormalizerService:
|
||||
normalizer_service.sound_repo.update.assert_called_once()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_normalize_sound_file_not_found(self, normalizer_service):
|
||||
async def test_normalize_sound_file_not_found(self, normalizer_service) -> None:
|
||||
"""Test normalizing a sound where original file doesn't exist."""
|
||||
sound = Sound(
|
||||
id=1,
|
||||
@@ -228,7 +229,7 @@ class TestSoundNormalizerService:
|
||||
assert result["filename"] == "missing.mp3"
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_normalize_sound_one_pass(self, normalizer_service):
|
||||
async def test_normalize_sound_one_pass(self, normalizer_service) -> None:
|
||||
"""Test normalizing a sound using one-pass method."""
|
||||
sound = Sound(
|
||||
id=1,
|
||||
@@ -275,7 +276,7 @@ class TestSoundNormalizerService:
|
||||
mock_normalize.assert_called_once()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_normalize_sound_normalization_error(self, normalizer_service):
|
||||
async def test_normalize_sound_normalization_error(self, normalizer_service) -> None:
|
||||
"""Test handling normalization errors."""
|
||||
sound = Sound(
|
||||
id=1,
|
||||
@@ -312,7 +313,7 @@ class TestSoundNormalizerService:
|
||||
assert result["filename"] == "test.mp3"
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_normalize_all_sounds(self, normalizer_service):
|
||||
async def test_normalize_all_sounds(self, normalizer_service) -> None:
|
||||
"""Test normalizing all unnormalized sounds."""
|
||||
sounds = [
|
||||
Sound(
|
||||
@@ -382,7 +383,7 @@ class TestSoundNormalizerService:
|
||||
assert len(results["files"]) == 2
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_normalize_sounds_by_type(self, normalizer_service):
|
||||
async def test_normalize_sounds_by_type(self, normalizer_service) -> None:
|
||||
"""Test normalizing sounds by type."""
|
||||
sdb_sounds = [
|
||||
Sound(
|
||||
@@ -432,7 +433,7 @@ class TestSoundNormalizerService:
|
||||
)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_normalize_sounds_with_errors(self, normalizer_service):
|
||||
async def test_normalize_sounds_with_errors(self, normalizer_service) -> None:
|
||||
"""Test normalizing sounds with some errors."""
|
||||
sounds = [
|
||||
Sound(
|
||||
@@ -512,7 +513,7 @@ class TestSoundNormalizerService:
|
||||
self,
|
||||
mock_ffmpeg,
|
||||
normalizer_service,
|
||||
):
|
||||
) -> None:
|
||||
"""Test one-pass audio normalization for MP3."""
|
||||
input_path = Path("/fake/input.wav")
|
||||
output_path = Path("/fake/output.mp3")
|
||||
@@ -547,7 +548,7 @@ class TestSoundNormalizerService:
|
||||
self,
|
||||
mock_ffmpeg,
|
||||
normalizer_service,
|
||||
):
|
||||
) -> None:
|
||||
"""Test two-pass audio normalization analysis phase."""
|
||||
input_path = Path("/fake/input.wav")
|
||||
output_path = Path("/fake/output.mp3")
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
"""Tests for sound scanner service."""
|
||||
# ruff: noqa: ANN001, ANN201, ARG002, PLR2004, SLF001, PLC0415, SIM117
|
||||
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
@@ -24,7 +25,7 @@ class TestSoundScannerService:
|
||||
"""Create a scanner service with mock session."""
|
||||
return SoundScannerService(mock_session)
|
||||
|
||||
def test_init(self, scanner_service):
|
||||
def test_init(self, scanner_service) -> None:
|
||||
"""Test scanner service initialization."""
|
||||
assert scanner_service.session is not None
|
||||
assert scanner_service.sound_repo is not None
|
||||
@@ -32,7 +33,7 @@ class TestSoundScannerService:
|
||||
assert ".mp3" in scanner_service.supported_extensions
|
||||
assert ".wav" in scanner_service.supported_extensions
|
||||
|
||||
def test_get_file_hash(self, scanner_service):
|
||||
def test_get_file_hash(self, scanner_service) -> None:
|
||||
"""Test file hash calculation."""
|
||||
# Create a temporary file
|
||||
with tempfile.NamedTemporaryFile(mode="w", delete=False) as f:
|
||||
@@ -48,7 +49,7 @@ class TestSoundScannerService:
|
||||
finally:
|
||||
temp_path.unlink()
|
||||
|
||||
def test_get_file_size(self, scanner_service):
|
||||
def test_get_file_size(self, scanner_service) -> None:
|
||||
"""Test file size calculation."""
|
||||
# Create a temporary file
|
||||
with tempfile.NamedTemporaryFile(mode="w", delete=False) as f:
|
||||
@@ -64,7 +65,7 @@ class TestSoundScannerService:
|
||||
finally:
|
||||
temp_path.unlink()
|
||||
|
||||
def test_extract_name_from_filename(self, scanner_service):
|
||||
def test_extract_name_from_filename(self, scanner_service) -> None:
|
||||
"""Test name extraction from filename."""
|
||||
test_cases = [
|
||||
("hello_world.mp3", "Hello World"),
|
||||
@@ -79,7 +80,7 @@ class TestSoundScannerService:
|
||||
assert result == expected_name
|
||||
|
||||
@patch("app.utils.audio.ffmpeg.probe")
|
||||
def test_get_audio_duration_success(self, mock_probe, scanner_service):
|
||||
def test_get_audio_duration_success(self, mock_probe, scanner_service) -> None:
|
||||
"""Test successful audio duration extraction."""
|
||||
mock_probe.return_value = {"format": {"duration": "123.456"}}
|
||||
|
||||
@@ -92,7 +93,7 @@ class TestSoundScannerService:
|
||||
mock_probe.assert_called_once_with(str(temp_path))
|
||||
|
||||
@patch("app.utils.audio.ffmpeg.probe")
|
||||
def test_get_audio_duration_failure(self, mock_probe, scanner_service):
|
||||
def test_get_audio_duration_failure(self, mock_probe, scanner_service) -> None:
|
||||
"""Test audio duration extraction failure."""
|
||||
mock_probe.side_effect = Exception("FFmpeg error")
|
||||
|
||||
@@ -105,13 +106,13 @@ class TestSoundScannerService:
|
||||
mock_probe.assert_called_once_with(str(temp_path))
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_scan_directory_nonexistent(self, scanner_service):
|
||||
async def test_scan_directory_nonexistent(self, scanner_service) -> None:
|
||||
"""Test scanning a non-existent directory."""
|
||||
with pytest.raises(ValueError, match="Directory does not exist"):
|
||||
await scanner_service.scan_directory("/non/existent/path")
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_scan_directory_not_directory(self, scanner_service):
|
||||
async def test_scan_directory_not_directory(self, scanner_service) -> None:
|
||||
"""Test scanning a path that is not a directory."""
|
||||
# Create a temporary file
|
||||
with tempfile.NamedTemporaryFile() as f:
|
||||
@@ -119,7 +120,7 @@ class TestSoundScannerService:
|
||||
await scanner_service.scan_directory(f.name)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_sync_audio_file_unchanged(self, scanner_service):
|
||||
async def test_sync_audio_file_unchanged(self, scanner_service) -> None:
|
||||
"""Test syncing file that is unchanged."""
|
||||
# Existing sound with same hash as file
|
||||
existing_sound = Sound(
|
||||
@@ -166,7 +167,7 @@ class TestSoundScannerService:
|
||||
temp_path.unlink()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_sync_audio_file_new(self, scanner_service):
|
||||
async def test_sync_audio_file_new(self, scanner_service) -> None:
|
||||
"""Test syncing a new audio file."""
|
||||
created_sound = Sound(
|
||||
id=1,
|
||||
@@ -221,7 +222,7 @@ class TestSoundScannerService:
|
||||
temp_path.unlink()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_sync_audio_file_updated(self, scanner_service):
|
||||
async def test_sync_audio_file_updated(self, scanner_service) -> None:
|
||||
"""Test syncing a file that was modified (different hash)."""
|
||||
# Existing sound with different hash than file
|
||||
existing_sound = Sound(
|
||||
@@ -280,7 +281,7 @@ class TestSoundScannerService:
|
||||
temp_path.unlink()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_sync_audio_file_custom_type(self, scanner_service):
|
||||
async def test_sync_audio_file_custom_type(self, scanner_service) -> None:
|
||||
"""Test syncing file with custom type."""
|
||||
created_sound = Sound(
|
||||
id=1,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
"""Tests for VLC player service."""
|
||||
# ruff: noqa: ANN001, ANN201, PLR2004, SLF001, SIM117, E501, ANN202, PLC0415
|
||||
|
||||
import asyncio
|
||||
from pathlib import Path
|
||||
@@ -6,7 +7,6 @@ from unittest.mock import AsyncMock, Mock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
from app.models.credit_transaction import CreditTransaction
|
||||
from app.models.sound import Sound
|
||||
from app.models.user import User
|
||||
from app.services.vlc_player import VLCPlayerService, get_vlc_player_service
|
||||
@@ -62,20 +62,20 @@ class TestVLCPlayerService:
|
||||
normalized_filename="normalized.mp3",
|
||||
)
|
||||
|
||||
def test_init(self, vlc_service):
|
||||
def test_init(self, vlc_service) -> None:
|
||||
"""Test VLC service initialization."""
|
||||
assert vlc_service.vlc_executable is not None
|
||||
assert isinstance(vlc_service.vlc_executable, str)
|
||||
|
||||
@patch("app.services.vlc_player.subprocess.run")
|
||||
def test_find_vlc_executable_found_in_path(self, mock_run):
|
||||
def test_find_vlc_executable_found_in_path(self, mock_run) -> None:
|
||||
"""Test VLC executable detection when found in PATH."""
|
||||
mock_run.return_value.returncode = 0
|
||||
service = VLCPlayerService()
|
||||
assert service.vlc_executable == "vlc"
|
||||
|
||||
@patch("app.services.vlc_player.subprocess.run")
|
||||
def test_find_vlc_executable_found_by_path(self, mock_run):
|
||||
def test_find_vlc_executable_found_by_path(self, mock_run) -> None:
|
||||
"""Test VLC executable detection when found by absolute path."""
|
||||
mock_run.return_value.returncode = 1 # which command fails
|
||||
|
||||
@@ -93,7 +93,7 @@ class TestVLCPlayerService:
|
||||
|
||||
@patch("app.services.vlc_player.subprocess.run")
|
||||
@patch("app.services.vlc_player.Path")
|
||||
def test_find_vlc_executable_fallback(self, mock_path, mock_run):
|
||||
def test_find_vlc_executable_fallback(self, mock_path, mock_run) -> None:
|
||||
"""Test VLC executable detection fallback to default."""
|
||||
# Mock all paths as non-existent
|
||||
mock_path_instance = Mock()
|
||||
@@ -111,7 +111,7 @@ class TestVLCPlayerService:
|
||||
@patch("app.services.vlc_player.asyncio.create_subprocess_exec")
|
||||
async def test_play_sound_success(
|
||||
self, mock_subprocess, vlc_service, sample_sound,
|
||||
):
|
||||
) -> None:
|
||||
"""Test successful sound playback."""
|
||||
# Mock subprocess
|
||||
mock_process = Mock()
|
||||
@@ -144,7 +144,7 @@ class TestVLCPlayerService:
|
||||
@pytest.mark.asyncio
|
||||
async def test_play_sound_file_not_found(
|
||||
self, vlc_service, sample_sound,
|
||||
):
|
||||
) -> None:
|
||||
"""Test sound playback when file doesn't exist."""
|
||||
# Mock the file path utility to return a non-existent path
|
||||
with patch("app.services.vlc_player.get_sound_file_path") as mock_get_path:
|
||||
@@ -160,7 +160,7 @@ class TestVLCPlayerService:
|
||||
@patch("app.services.vlc_player.asyncio.create_subprocess_exec")
|
||||
async def test_play_sound_subprocess_error(
|
||||
self, mock_subprocess, vlc_service, sample_sound,
|
||||
):
|
||||
) -> None:
|
||||
"""Test sound playback when subprocess fails."""
|
||||
# Mock the file path utility to return an existing path
|
||||
with patch("app.services.vlc_player.get_sound_file_path") as mock_get_path:
|
||||
@@ -177,7 +177,7 @@ class TestVLCPlayerService:
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@patch("app.services.vlc_player.asyncio.create_subprocess_exec")
|
||||
async def test_stop_all_vlc_instances_success(self, mock_subprocess, vlc_service):
|
||||
async def test_stop_all_vlc_instances_success(self, mock_subprocess, vlc_service) -> None:
|
||||
"""Test successful stopping of all VLC instances."""
|
||||
# Mock pgrep process (find VLC processes)
|
||||
mock_find_process = Mock()
|
||||
@@ -214,7 +214,7 @@ class TestVLCPlayerService:
|
||||
@patch("app.services.vlc_player.asyncio.create_subprocess_exec")
|
||||
async def test_stop_all_vlc_instances_no_processes(
|
||||
self, mock_subprocess, vlc_service,
|
||||
):
|
||||
) -> None:
|
||||
"""Test stopping VLC instances when none are running."""
|
||||
# Mock pgrep process (no VLC processes found)
|
||||
mock_find_process = Mock()
|
||||
@@ -234,7 +234,7 @@ class TestVLCPlayerService:
|
||||
@patch("app.services.vlc_player.asyncio.create_subprocess_exec")
|
||||
async def test_stop_all_vlc_instances_partial_kill(
|
||||
self, mock_subprocess, vlc_service,
|
||||
):
|
||||
) -> None:
|
||||
"""Test stopping VLC instances when some processes remain."""
|
||||
# Mock pgrep process (find VLC processes)
|
||||
mock_find_process = Mock()
|
||||
@@ -267,7 +267,7 @@ class TestVLCPlayerService:
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@patch("app.services.vlc_player.asyncio.create_subprocess_exec")
|
||||
async def test_stop_all_vlc_instances_error(self, mock_subprocess, vlc_service):
|
||||
async def test_stop_all_vlc_instances_error(self, mock_subprocess, vlc_service) -> None:
|
||||
"""Test stopping VLC instances when an error occurs."""
|
||||
# Mock subprocess exception
|
||||
mock_subprocess.side_effect = Exception("Command failed")
|
||||
@@ -280,7 +280,7 @@ class TestVLCPlayerService:
|
||||
assert "error" in result
|
||||
assert result["message"] == "Failed to stop VLC processes"
|
||||
|
||||
def test_get_vlc_player_service_singleton(self):
|
||||
def test_get_vlc_player_service_singleton(self) -> None:
|
||||
"""Test that get_vlc_player_service returns the same instance."""
|
||||
with patch("app.services.vlc_player.VLCPlayerService") as mock_service_class:
|
||||
mock_instance = Mock()
|
||||
@@ -306,7 +306,7 @@ class TestVLCPlayerService:
|
||||
@patch("app.services.vlc_player.asyncio.create_subprocess_exec")
|
||||
async def test_play_sound_with_play_count_tracking(
|
||||
self, mock_subprocess, vlc_service_with_db, sample_sound,
|
||||
):
|
||||
) -> None:
|
||||
"""Test sound playback with play count tracking."""
|
||||
# Mock subprocess
|
||||
mock_process = Mock()
|
||||
@@ -371,7 +371,7 @@ class TestVLCPlayerService:
|
||||
# mocking or using a real async test framework setup
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_record_play_count_success(self, vlc_service_with_db):
|
||||
async def test_record_play_count_success(self, vlc_service_with_db) -> None:
|
||||
"""Test successful play count recording."""
|
||||
# Mock session and repositories
|
||||
mock_session = AsyncMock()
|
||||
@@ -436,14 +436,14 @@ class TestVLCPlayerService:
|
||||
)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_record_play_count_no_session_factory(self, vlc_service):
|
||||
async def test_record_play_count_no_session_factory(self, vlc_service) -> None:
|
||||
"""Test play count recording when no session factory is available."""
|
||||
# This should not raise an error and should log a warning
|
||||
await vlc_service._record_play_count(1, "Test Sound")
|
||||
# The method should return early without doing anything
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_record_play_count_always_creates_record(self, vlc_service_with_db):
|
||||
async def test_record_play_count_always_creates_record(self, vlc_service_with_db) -> None:
|
||||
"""Test play count recording always creates a new SoundPlayed record."""
|
||||
# Mock session and repositories
|
||||
mock_session = AsyncMock()
|
||||
@@ -493,7 +493,7 @@ class TestVLCPlayerService:
|
||||
# Verify commit happened
|
||||
mock_session.commit.assert_called_once()
|
||||
|
||||
def test_uses_shared_sound_path_utility(self, vlc_service, sample_sound):
|
||||
def test_uses_shared_sound_path_utility(self, vlc_service, sample_sound) -> None:
|
||||
"""Test that VLC service uses the shared sound path utility."""
|
||||
with patch("app.services.vlc_player.get_sound_file_path") as mock_path:
|
||||
mock_file_path = Mock(spec=Path)
|
||||
|
||||
Reference in New Issue
Block a user