Compare commits

..

3 Commits

Author SHA1 Message Date
JSC
43be92c8f9 fix: Update linter command in CI
All checks were successful
Backend CI / lint (push) Successful in 9m31s
Backend CI / test (push) Successful in 4m7s
2025-08-01 09:44:53 +02:00
JSC
f68f4d9046 refactor: Compiled ignored ruff rules in pyproject 2025-08-01 09:40:15 +02:00
JSC
fceff92ca1 fix: Lint fixes of last tests 2025-08-01 09:30:15 +02:00
27 changed files with 329 additions and 325 deletions

View File

@@ -30,7 +30,7 @@ jobs:
run: uv sync --locked --all-extras --dev
- name: Run linter
run: uv run ruff check --exclude tests
run: uv run ruff check
test:
runs-on: ubuntu-latest

View File

@@ -43,8 +43,28 @@ exclude = ["alembic"]
select = ["ALL"]
ignore = ["D100", "D103", "TRY301"]
[tool.ruff.per-file-ignores]
"tests/**/*.py" = ["S101", "S105"]
[tool.ruff.lint.per-file-ignores]
"tests/**/*.py" = [
"S101", # Use of assert detected
"S105", # Possible hardcoded password
"S106", # Possible hardcoded password
"ANN001", # Missing type annotation for function argument
"ANN003", # Missing type annotation for **kwargs
"ANN201", # Missing return type annotation for public function
"ANN202", # Missing return type annotation for private function
"ANN401", # Dynamically typed expressions (typing.Any) are disallowed
"ARG001", # Unused function argument
"ARG002", # Unused method argument
"ARG005", # Unused lambda argument
"BLE001", # Do not catch blind exception
"E501", # Line too long
"PLR2004", # Magic value used in comparison
"PLC0415", # `import` should be at top-level
"SLF001", # Private member accessed
"SIM117", # Use a single `if` statement
"PT011", # `pytest.raises()` is too broad
"PT012", # `pytest.raises()` block should contain a single simple statement
]
[tool.pytest.ini_options]
filterwarnings = [

View File

@@ -17,7 +17,7 @@ class TestApiTokenEndpoints:
self,
authenticated_client: AsyncClient,
authenticated_user: User,
):
) -> None:
"""Test successful API token generation."""
request_data = {"expires_days": 30}
@@ -45,7 +45,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 +54,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 +69,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 +84,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 +99,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 +116,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 +128,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 +143,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 +166,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 +186,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 +195,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 +226,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 +235,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 +245,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 +264,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 +278,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 +297,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 +321,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 +347,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(

View File

@@ -11,7 +11,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 +26,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 +37,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 +47,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 +63,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 +77,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)

View File

@@ -37,7 +37,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 +48,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 +59,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 +75,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 +92,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 +108,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 +124,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 +135,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 +146,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 +162,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 +173,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 +184,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 +200,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 +211,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 +222,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 +238,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 +249,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 +260,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 +276,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 +296,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 +306,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 +320,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 +339,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 +359,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 +376,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 +390,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 +409,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 +429,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 +439,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 +453,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 +472,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 +483,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 +494,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 +510,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 +548,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 +559,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 +574,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 +584,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 +594,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 +605,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 +621,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 +640,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(

View File

@@ -27,7 +27,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 +42,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 +53,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 +87,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 +108,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 +122,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 +148,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 +159,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 +178,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 +186,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 +200,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 = [

View File

@@ -1,11 +1,14 @@
"""Tests for sound API endpoints."""
from typing import TYPE_CHECKING
from unittest.mock import patch
import pytest
from httpx import ASGITransport, AsyncClient
from app.models.user import User
if TYPE_CHECKING:
from app.services.sound_normalizer import NormalizationResults
from app.services.sound_scanner import ScanResults
@@ -18,7 +21,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 +98,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 +111,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 +145,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 +192,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 +211,7 @@ class TestSoundEndpoints:
self,
authenticated_admin_client: AsyncClient,
admin_user: User,
):
) -> None:
"""Test successful custom directory scanning."""
mock_results: ScanResults = {
"scanned": 2,
@@ -272,7 +275,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 +307,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 +325,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 +340,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 +377,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 +398,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 +470,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 +522,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 +600,7 @@ class TestSoundEndpoints:
self,
authenticated_admin_client: AsyncClient,
admin_user: User,
):
) -> None:
"""Test normalization with force parameter."""
mock_results: NormalizationResults = {
"processed": 1,
@@ -626,7 +629,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 +654,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 +667,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 +702,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 +723,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 +790,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 +806,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 +838,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 +897,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 +917,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 +969,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 +1016,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 +1074,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 +1087,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

View File

@@ -20,7 +20,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 +73,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 +106,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 +153,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 +184,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 +195,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 +234,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 +268,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 +303,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 +337,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 +360,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 +371,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()

View File

@@ -7,12 +7,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 +64,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()

View File

@@ -1,5 +1,4 @@
"""Tests for API token authentication dependencies."""
# ruff: noqa: S106
from datetime import UTC, datetime, timedelta
from unittest.mock import AsyncMock

View File

@@ -1,5 +1,4 @@
"""Tests for credit transaction repository."""
# ruff: noqa: ARG002, E501
import json
from collections.abc import AsyncGenerator

View File

@@ -1,5 +1,4 @@
"""Tests for extraction repository."""
# ruff: noqa: ANN001, ANN201
from unittest.mock import AsyncMock, Mock

View File

@@ -1,5 +1,4 @@
"""Tests for playlist repository."""
# ruff: noqa: PLR2004, ANN401
from collections.abc import AsyncGenerator
from typing import Any

View File

@@ -1,5 +1,4 @@
"""Tests for sound repository."""
# ruff: noqa: ARG002, PLR2004
from collections.abc import AsyncGenerator

View File

@@ -1,5 +1,4 @@
"""Tests for user repository."""
# ruff: noqa: ARG002
from collections.abc import AsyncGenerator

View File

@@ -1,5 +1,4 @@
"""Tests for user OAuth repository."""
# ruff: noqa: ARG002
from collections.abc import AsyncGenerator

View File

@@ -10,6 +10,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 +67,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 +105,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 +120,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 +143,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 +166,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 +189,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 +210,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

View File

@@ -39,7 +39,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 +55,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 +78,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 +93,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 +112,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 +139,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 +156,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 +167,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 +202,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 +213,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 +235,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 +266,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 +277,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 +308,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 +317,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 +332,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 +350,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

View File

@@ -26,13 +26,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 +54,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 +79,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 +90,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 +118,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 +144,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 +170,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 +211,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 +234,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 +255,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 +317,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 +349,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 +381,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 +409,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 +418,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 +455,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(

View File

@@ -16,7 +16,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 +25,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 +41,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 +56,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 +66,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 +75,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 +89,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 +101,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 +115,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 +130,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 +157,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 +183,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 +213,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 +230,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 +254,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 +275,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,

View File

@@ -24,7 +24,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 +42,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 +70,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 +95,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 +133,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 +152,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 +164,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 +179,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 +211,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 +229,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 +237,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 +248,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 +259,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 +273,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 +285,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 +297,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 +310,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 +321,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 +330,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 +342,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 +350,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 +382,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 +404,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 +417,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 +430,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 +443,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 +455,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 +470,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 +482,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 +500,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 +509,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 +517,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 +529,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 +543,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 +579,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 +615,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 +627,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 +639,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 +649,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 +659,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 +673,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 +689,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 +715,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 +741,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 +749,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 +766,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 +775,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 +789,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 +801,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 +809,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"):

View File

@@ -22,7 +22,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 +33,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 +48,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 +60,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 +69,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 +83,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 +99,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 +110,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 +133,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 +143,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 +166,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 +177,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 +200,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 +211,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 +230,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 +238,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 +254,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

View File

@@ -28,7 +28,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 +40,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 +57,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 +74,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 +90,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 +107,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 +120,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 +133,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 +154,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 +173,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 +203,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 +228,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 +275,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 +312,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 +382,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 +432,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 +512,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 +547,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")

View File

@@ -24,7 +24,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 +32,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 +48,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 +64,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 +79,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 +92,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 +105,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 +119,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 +166,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 +221,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 +280,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,

View File

@@ -6,7 +6,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 +61,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 +92,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 +110,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 +143,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 +159,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 +176,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 +213,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 +233,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 +266,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 +279,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 +305,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 +370,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 +435,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 +492,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)

View File

@@ -1,5 +1,4 @@
"""Tests for cookie utilities."""
# ruff: noqa: ANN201, E501
from app.utils.cookies import extract_access_token_from_cookies, parse_cookies

View File

@@ -1,5 +1,4 @@
"""Tests for credit decorators."""
# ruff: noqa: ARG001, ANN001, E501, PT012
from collections.abc import Callable
from typing import Never