Compare commits
3 Commits
dc29915fbc
...
43be92c8f9
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
43be92c8f9 | ||
|
|
f68f4d9046 | ||
|
|
fceff92ca1 |
@@ -30,7 +30,7 @@ jobs:
|
|||||||
run: uv sync --locked --all-extras --dev
|
run: uv sync --locked --all-extras --dev
|
||||||
|
|
||||||
- name: Run linter
|
- name: Run linter
|
||||||
run: uv run ruff check --exclude tests
|
run: uv run ruff check
|
||||||
|
|
||||||
test:
|
test:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|||||||
@@ -43,8 +43,28 @@ exclude = ["alembic"]
|
|||||||
select = ["ALL"]
|
select = ["ALL"]
|
||||||
ignore = ["D100", "D103", "TRY301"]
|
ignore = ["D100", "D103", "TRY301"]
|
||||||
|
|
||||||
[tool.ruff.per-file-ignores]
|
[tool.ruff.lint.per-file-ignores]
|
||||||
"tests/**/*.py" = ["S101", "S105"]
|
"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]
|
[tool.pytest.ini_options]
|
||||||
filterwarnings = [
|
filterwarnings = [
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ class TestApiTokenEndpoints:
|
|||||||
self,
|
self,
|
||||||
authenticated_client: AsyncClient,
|
authenticated_client: AsyncClient,
|
||||||
authenticated_user: User,
|
authenticated_user: User,
|
||||||
):
|
) -> None:
|
||||||
"""Test successful API token generation."""
|
"""Test successful API token generation."""
|
||||||
request_data = {"expires_days": 30}
|
request_data = {"expires_days": 30}
|
||||||
|
|
||||||
@@ -45,7 +45,7 @@ class TestApiTokenEndpoints:
|
|||||||
async def test_generate_api_token_default_expiry(
|
async def test_generate_api_token_default_expiry(
|
||||||
self,
|
self,
|
||||||
authenticated_client: AsyncClient,
|
authenticated_client: AsyncClient,
|
||||||
):
|
) -> None:
|
||||||
"""Test API token generation with default expiry."""
|
"""Test API token generation with default expiry."""
|
||||||
response = await authenticated_client.post("/api/v1/auth/api-token", json={})
|
response = await authenticated_client.post("/api/v1/auth/api-token", json={})
|
||||||
|
|
||||||
@@ -54,9 +54,7 @@ class TestApiTokenEndpoints:
|
|||||||
|
|
||||||
expires_at_str = data["expires_at"]
|
expires_at_str = data["expires_at"]
|
||||||
# Handle both ISO format with/without timezone info
|
# Handle both ISO format with/without timezone info
|
||||||
if expires_at_str.endswith("Z"):
|
if expires_at_str.endswith("Z") or "+" in expires_at_str or expires_at_str.count("-") > 2:
|
||||||
expires_at = datetime.fromisoformat(expires_at_str.replace("Z", "+00:00"))
|
|
||||||
elif "+" in expires_at_str or expires_at_str.count("-") > 2:
|
|
||||||
expires_at = datetime.fromisoformat(expires_at_str)
|
expires_at = datetime.fromisoformat(expires_at_str)
|
||||||
else:
|
else:
|
||||||
# Naive datetime, assume UTC
|
# Naive datetime, assume UTC
|
||||||
@@ -71,7 +69,7 @@ class TestApiTokenEndpoints:
|
|||||||
async def test_generate_api_token_custom_expiry(
|
async def test_generate_api_token_custom_expiry(
|
||||||
self,
|
self,
|
||||||
authenticated_client: AsyncClient,
|
authenticated_client: AsyncClient,
|
||||||
):
|
) -> None:
|
||||||
"""Test API token generation with custom expiry."""
|
"""Test API token generation with custom expiry."""
|
||||||
expires_days = 90
|
expires_days = 90
|
||||||
request_data = {"expires_days": expires_days}
|
request_data = {"expires_days": expires_days}
|
||||||
@@ -86,9 +84,7 @@ class TestApiTokenEndpoints:
|
|||||||
|
|
||||||
expires_at_str = data["expires_at"]
|
expires_at_str = data["expires_at"]
|
||||||
# Handle both ISO format with/without timezone info
|
# Handle both ISO format with/without timezone info
|
||||||
if expires_at_str.endswith("Z"):
|
if expires_at_str.endswith("Z") or "+" in expires_at_str or expires_at_str.count("-") > 2:
|
||||||
expires_at = datetime.fromisoformat(expires_at_str.replace("Z", "+00:00"))
|
|
||||||
elif "+" in expires_at_str or expires_at_str.count("-") > 2:
|
|
||||||
expires_at = datetime.fromisoformat(expires_at_str)
|
expires_at = datetime.fromisoformat(expires_at_str)
|
||||||
else:
|
else:
|
||||||
# Naive datetime, assume UTC
|
# Naive datetime, assume UTC
|
||||||
@@ -103,7 +99,7 @@ class TestApiTokenEndpoints:
|
|||||||
async def test_generate_api_token_validation_errors(
|
async def test_generate_api_token_validation_errors(
|
||||||
self,
|
self,
|
||||||
authenticated_client: AsyncClient,
|
authenticated_client: AsyncClient,
|
||||||
):
|
) -> None:
|
||||||
"""Test API token generation with validation errors."""
|
"""Test API token generation with validation errors."""
|
||||||
# Test minimum validation
|
# Test minimum validation
|
||||||
response = await authenticated_client.post(
|
response = await authenticated_client.post(
|
||||||
@@ -120,7 +116,7 @@ class TestApiTokenEndpoints:
|
|||||||
assert response.status_code == 422
|
assert response.status_code == 422
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@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."""
|
"""Test API token generation without authentication."""
|
||||||
response = await client.post(
|
response = await client.post(
|
||||||
"/api/v1/auth/api-token",
|
"/api/v1/auth/api-token",
|
||||||
@@ -132,7 +128,7 @@ class TestApiTokenEndpoints:
|
|||||||
async def test_get_api_token_status_no_token(
|
async def test_get_api_token_status_no_token(
|
||||||
self,
|
self,
|
||||||
authenticated_client: AsyncClient,
|
authenticated_client: AsyncClient,
|
||||||
):
|
) -> None:
|
||||||
"""Test getting API token status when user has no token."""
|
"""Test getting API token status when user has no token."""
|
||||||
response = await authenticated_client.get("/api/v1/auth/api-token/status")
|
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(
|
async def test_get_api_token_status_with_token(
|
||||||
self,
|
self,
|
||||||
authenticated_client: AsyncClient,
|
authenticated_client: AsyncClient,
|
||||||
):
|
) -> None:
|
||||||
"""Test getting API token status when user has a token."""
|
"""Test getting API token status when user has a token."""
|
||||||
# First generate a token
|
# First generate a token
|
||||||
await authenticated_client.post(
|
await authenticated_client.post(
|
||||||
@@ -170,7 +166,7 @@ class TestApiTokenEndpoints:
|
|||||||
self,
|
self,
|
||||||
authenticated_client: AsyncClient,
|
authenticated_client: AsyncClient,
|
||||||
authenticated_user: User,
|
authenticated_user: User,
|
||||||
):
|
) -> None:
|
||||||
"""Test getting API token status with expired token."""
|
"""Test getting API token status with expired token."""
|
||||||
# Mock expired token
|
# Mock expired token
|
||||||
with patch("app.utils.auth.TokenUtils.is_token_expired", return_value=True):
|
with patch("app.utils.auth.TokenUtils.is_token_expired", return_value=True):
|
||||||
@@ -190,7 +186,7 @@ class TestApiTokenEndpoints:
|
|||||||
assert data["is_expired"] is True
|
assert data["is_expired"] is True
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@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."""
|
"""Test getting API token status without authentication."""
|
||||||
response = await client.get("/api/v1/auth/api-token/status")
|
response = await client.get("/api/v1/auth/api-token/status")
|
||||||
assert response.status_code == 401
|
assert response.status_code == 401
|
||||||
@@ -199,7 +195,7 @@ class TestApiTokenEndpoints:
|
|||||||
async def test_revoke_api_token_success(
|
async def test_revoke_api_token_success(
|
||||||
self,
|
self,
|
||||||
authenticated_client: AsyncClient,
|
authenticated_client: AsyncClient,
|
||||||
):
|
) -> None:
|
||||||
"""Test successful API token revocation."""
|
"""Test successful API token revocation."""
|
||||||
# First generate a token
|
# First generate a token
|
||||||
await authenticated_client.post(
|
await authenticated_client.post(
|
||||||
@@ -230,7 +226,7 @@ class TestApiTokenEndpoints:
|
|||||||
async def test_revoke_api_token_no_token(
|
async def test_revoke_api_token_no_token(
|
||||||
self,
|
self,
|
||||||
authenticated_client: AsyncClient,
|
authenticated_client: AsyncClient,
|
||||||
):
|
) -> None:
|
||||||
"""Test revoking API token when user has no token."""
|
"""Test revoking API token when user has no token."""
|
||||||
response = await authenticated_client.delete("/api/v1/auth/api-token")
|
response = await authenticated_client.delete("/api/v1/auth/api-token")
|
||||||
|
|
||||||
@@ -239,7 +235,7 @@ class TestApiTokenEndpoints:
|
|||||||
assert data["message"] == "API token revoked successfully"
|
assert data["message"] == "API token revoked successfully"
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@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."""
|
"""Test revoking API token without authentication."""
|
||||||
response = await client.delete("/api/v1/auth/api-token")
|
response = await client.delete("/api/v1/auth/api-token")
|
||||||
assert response.status_code == 401
|
assert response.status_code == 401
|
||||||
@@ -249,7 +245,7 @@ class TestApiTokenEndpoints:
|
|||||||
self,
|
self,
|
||||||
client: AsyncClient,
|
client: AsyncClient,
|
||||||
authenticated_client: AsyncClient,
|
authenticated_client: AsyncClient,
|
||||||
):
|
) -> None:
|
||||||
"""Test successful authentication using API token."""
|
"""Test successful authentication using API token."""
|
||||||
# Generate API token
|
# Generate API token
|
||||||
token_response = await authenticated_client.post(
|
token_response = await authenticated_client.post(
|
||||||
@@ -268,7 +264,7 @@ class TestApiTokenEndpoints:
|
|||||||
assert "email" in data
|
assert "email" in data
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@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."""
|
"""Test authentication with invalid API token."""
|
||||||
headers = {"API-TOKEN": "invalid_token"}
|
headers = {"API-TOKEN": "invalid_token"}
|
||||||
response = await client.get("/api/v1/auth/me", headers=headers)
|
response = await client.get("/api/v1/auth/me", headers=headers)
|
||||||
@@ -282,7 +278,7 @@ class TestApiTokenEndpoints:
|
|||||||
self,
|
self,
|
||||||
client: AsyncClient,
|
client: AsyncClient,
|
||||||
authenticated_client: AsyncClient,
|
authenticated_client: AsyncClient,
|
||||||
):
|
) -> None:
|
||||||
"""Test authentication with expired API token."""
|
"""Test authentication with expired API token."""
|
||||||
# Generate API token
|
# Generate API token
|
||||||
token_response = await authenticated_client.post(
|
token_response = await authenticated_client.post(
|
||||||
@@ -301,7 +297,7 @@ class TestApiTokenEndpoints:
|
|||||||
assert "API token has expired" in data["detail"]
|
assert "API token has expired" in data["detail"]
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@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."""
|
"""Test authentication with empty API-TOKEN header."""
|
||||||
# Empty token
|
# Empty token
|
||||||
headers = {"API-TOKEN": ""}
|
headers = {"API-TOKEN": ""}
|
||||||
@@ -325,7 +321,7 @@ class TestApiTokenEndpoints:
|
|||||||
client: AsyncClient,
|
client: AsyncClient,
|
||||||
authenticated_client: AsyncClient,
|
authenticated_client: AsyncClient,
|
||||||
authenticated_user: User,
|
authenticated_user: User,
|
||||||
):
|
) -> None:
|
||||||
"""Test authentication with API token for inactive user."""
|
"""Test authentication with API token for inactive user."""
|
||||||
# Generate API token
|
# Generate API token
|
||||||
token_response = await authenticated_client.post(
|
token_response = await authenticated_client.post(
|
||||||
@@ -351,7 +347,7 @@ class TestApiTokenEndpoints:
|
|||||||
client: AsyncClient,
|
client: AsyncClient,
|
||||||
authenticated_client: AsyncClient,
|
authenticated_client: AsyncClient,
|
||||||
auth_cookies: dict[str, str],
|
auth_cookies: dict[str, str],
|
||||||
):
|
) -> None:
|
||||||
"""Test that flexible authentication prefers API token over cookie."""
|
"""Test that flexible authentication prefers API token over cookie."""
|
||||||
# Generate API token
|
# Generate API token
|
||||||
token_response = await authenticated_client.post(
|
token_response = await authenticated_client.post(
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ class TestExtractionEndpoints:
|
|||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_create_extraction_success(
|
async def test_create_extraction_success(
|
||||||
self, test_client: AsyncClient, auth_cookies: dict[str, str],
|
self, test_client: AsyncClient, auth_cookies: dict[str, str],
|
||||||
):
|
) -> None:
|
||||||
"""Test successful extraction creation."""
|
"""Test successful extraction creation."""
|
||||||
# Set cookies on client instance to avoid deprecation warning
|
# Set cookies on client instance to avoid deprecation warning
|
||||||
test_client.cookies.update(auth_cookies)
|
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
|
assert response.status_code in [200, 400, 500] # Allow any non-auth error
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@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."""
|
"""Test extraction creation without authentication."""
|
||||||
response = await test_client.post(
|
response = await test_client.post(
|
||||||
"/api/v1/sounds/extract",
|
"/api/v1/sounds/extract",
|
||||||
@@ -37,7 +37,7 @@ class TestExtractionEndpoints:
|
|||||||
assert response.status_code == 401
|
assert response.status_code == 401
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@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."""
|
"""Test extraction retrieval without authentication."""
|
||||||
response = await test_client.get("/api/v1/sounds/extract/1")
|
response = await test_client.get("/api/v1/sounds/extract/1")
|
||||||
|
|
||||||
@@ -47,7 +47,7 @@ class TestExtractionEndpoints:
|
|||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_get_processor_status_admin(
|
async def test_get_processor_status_admin(
|
||||||
self, test_client: AsyncClient, admin_cookies: dict[str, str],
|
self, test_client: AsyncClient, admin_cookies: dict[str, str],
|
||||||
):
|
) -> None:
|
||||||
"""Test getting processor status as admin."""
|
"""Test getting processor status as admin."""
|
||||||
# Set cookies on client instance to avoid deprecation warning
|
# Set cookies on client instance to avoid deprecation warning
|
||||||
test_client.cookies.update(admin_cookies)
|
test_client.cookies.update(admin_cookies)
|
||||||
@@ -63,7 +63,7 @@ class TestExtractionEndpoints:
|
|||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_get_processor_status_non_admin(
|
async def test_get_processor_status_non_admin(
|
||||||
self, test_client: AsyncClient, auth_cookies: dict[str, str],
|
self, test_client: AsyncClient, auth_cookies: dict[str, str],
|
||||||
):
|
) -> None:
|
||||||
"""Test getting processor status as non-admin user."""
|
"""Test getting processor status as non-admin user."""
|
||||||
# Set cookies on client instance to avoid deprecation warning
|
# Set cookies on client instance to avoid deprecation warning
|
||||||
test_client.cookies.update(auth_cookies)
|
test_client.cookies.update(auth_cookies)
|
||||||
@@ -77,7 +77,7 @@ class TestExtractionEndpoints:
|
|||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_get_user_extractions(
|
async def test_get_user_extractions(
|
||||||
self, test_client: AsyncClient, auth_cookies: dict[str, str],
|
self, test_client: AsyncClient, auth_cookies: dict[str, str],
|
||||||
):
|
) -> None:
|
||||||
"""Test getting user extractions."""
|
"""Test getting user extractions."""
|
||||||
# Set cookies on client instance to avoid deprecation warning
|
# Set cookies on client instance to avoid deprecation warning
|
||||||
test_client.cookies.update(auth_cookies)
|
test_client.cookies.update(auth_cookies)
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ class TestPlayerEndpoints:
|
|||||||
authenticated_client: AsyncClient,
|
authenticated_client: AsyncClient,
|
||||||
authenticated_user: User,
|
authenticated_user: User,
|
||||||
mock_player_service,
|
mock_player_service,
|
||||||
):
|
) -> None:
|
||||||
"""Test starting playback successfully."""
|
"""Test starting playback successfully."""
|
||||||
response = await authenticated_client.post("/api/v1/player/play")
|
response = await authenticated_client.post("/api/v1/player/play")
|
||||||
|
|
||||||
@@ -48,7 +48,7 @@ class TestPlayerEndpoints:
|
|||||||
mock_player_service.play.assert_called_once_with()
|
mock_player_service.play.assert_called_once_with()
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@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."""
|
"""Test starting playback without authentication."""
|
||||||
response = await client.post("/api/v1/player/play")
|
response = await client.post("/api/v1/player/play")
|
||||||
assert response.status_code == 401
|
assert response.status_code == 401
|
||||||
@@ -59,7 +59,7 @@ class TestPlayerEndpoints:
|
|||||||
authenticated_client: AsyncClient,
|
authenticated_client: AsyncClient,
|
||||||
authenticated_user: User,
|
authenticated_user: User,
|
||||||
mock_player_service,
|
mock_player_service,
|
||||||
):
|
) -> None:
|
||||||
"""Test starting playback with service error."""
|
"""Test starting playback with service error."""
|
||||||
mock_player_service.play.side_effect = Exception("Service error")
|
mock_player_service.play.side_effect = Exception("Service error")
|
||||||
|
|
||||||
@@ -75,7 +75,7 @@ class TestPlayerEndpoints:
|
|||||||
authenticated_client: AsyncClient,
|
authenticated_client: AsyncClient,
|
||||||
authenticated_user: User,
|
authenticated_user: User,
|
||||||
mock_player_service,
|
mock_player_service,
|
||||||
):
|
) -> None:
|
||||||
"""Test playing sound at specific index successfully."""
|
"""Test playing sound at specific index successfully."""
|
||||||
index = 2
|
index = 2
|
||||||
response = await authenticated_client.post(f"/api/v1/player/play/{index}")
|
response = await authenticated_client.post(f"/api/v1/player/play/{index}")
|
||||||
@@ -92,7 +92,7 @@ class TestPlayerEndpoints:
|
|||||||
authenticated_client: AsyncClient,
|
authenticated_client: AsyncClient,
|
||||||
authenticated_user: User,
|
authenticated_user: User,
|
||||||
mock_player_service,
|
mock_player_service,
|
||||||
):
|
) -> None:
|
||||||
"""Test playing sound with invalid index."""
|
"""Test playing sound with invalid index."""
|
||||||
mock_player_service.play.side_effect = ValueError("Invalid sound index")
|
mock_player_service.play.side_effect = ValueError("Invalid sound index")
|
||||||
|
|
||||||
@@ -108,7 +108,7 @@ class TestPlayerEndpoints:
|
|||||||
authenticated_client: AsyncClient,
|
authenticated_client: AsyncClient,
|
||||||
authenticated_user: User,
|
authenticated_user: User,
|
||||||
mock_player_service,
|
mock_player_service,
|
||||||
):
|
) -> None:
|
||||||
"""Test playing sound at index with service error."""
|
"""Test playing sound at index with service error."""
|
||||||
mock_player_service.play.side_effect = Exception("Service error")
|
mock_player_service.play.side_effect = Exception("Service error")
|
||||||
|
|
||||||
@@ -124,7 +124,7 @@ class TestPlayerEndpoints:
|
|||||||
authenticated_client: AsyncClient,
|
authenticated_client: AsyncClient,
|
||||||
authenticated_user: User,
|
authenticated_user: User,
|
||||||
mock_player_service,
|
mock_player_service,
|
||||||
):
|
) -> None:
|
||||||
"""Test pausing playback successfully."""
|
"""Test pausing playback successfully."""
|
||||||
response = await authenticated_client.post("/api/v1/player/pause")
|
response = await authenticated_client.post("/api/v1/player/pause")
|
||||||
|
|
||||||
@@ -135,7 +135,7 @@ class TestPlayerEndpoints:
|
|||||||
mock_player_service.pause.assert_called_once()
|
mock_player_service.pause.assert_called_once()
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@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."""
|
"""Test pausing playback without authentication."""
|
||||||
response = await client.post("/api/v1/player/pause")
|
response = await client.post("/api/v1/player/pause")
|
||||||
assert response.status_code == 401
|
assert response.status_code == 401
|
||||||
@@ -146,7 +146,7 @@ class TestPlayerEndpoints:
|
|||||||
authenticated_client: AsyncClient,
|
authenticated_client: AsyncClient,
|
||||||
authenticated_user: User,
|
authenticated_user: User,
|
||||||
mock_player_service,
|
mock_player_service,
|
||||||
):
|
) -> None:
|
||||||
"""Test pausing playback with service error."""
|
"""Test pausing playback with service error."""
|
||||||
mock_player_service.pause.side_effect = Exception("Service error")
|
mock_player_service.pause.side_effect = Exception("Service error")
|
||||||
|
|
||||||
@@ -162,7 +162,7 @@ class TestPlayerEndpoints:
|
|||||||
authenticated_client: AsyncClient,
|
authenticated_client: AsyncClient,
|
||||||
authenticated_user: User,
|
authenticated_user: User,
|
||||||
mock_player_service,
|
mock_player_service,
|
||||||
):
|
) -> None:
|
||||||
"""Test stopping playback successfully."""
|
"""Test stopping playback successfully."""
|
||||||
response = await authenticated_client.post("/api/v1/player/stop")
|
response = await authenticated_client.post("/api/v1/player/stop")
|
||||||
|
|
||||||
@@ -173,7 +173,7 @@ class TestPlayerEndpoints:
|
|||||||
mock_player_service.stop_playback.assert_called_once()
|
mock_player_service.stop_playback.assert_called_once()
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@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."""
|
"""Test stopping playback without authentication."""
|
||||||
response = await client.post("/api/v1/player/stop")
|
response = await client.post("/api/v1/player/stop")
|
||||||
assert response.status_code == 401
|
assert response.status_code == 401
|
||||||
@@ -184,7 +184,7 @@ class TestPlayerEndpoints:
|
|||||||
authenticated_client: AsyncClient,
|
authenticated_client: AsyncClient,
|
||||||
authenticated_user: User,
|
authenticated_user: User,
|
||||||
mock_player_service,
|
mock_player_service,
|
||||||
):
|
) -> None:
|
||||||
"""Test stopping playback with service error."""
|
"""Test stopping playback with service error."""
|
||||||
mock_player_service.stop_playback.side_effect = Exception("Service error")
|
mock_player_service.stop_playback.side_effect = Exception("Service error")
|
||||||
|
|
||||||
@@ -200,7 +200,7 @@ class TestPlayerEndpoints:
|
|||||||
authenticated_client: AsyncClient,
|
authenticated_client: AsyncClient,
|
||||||
authenticated_user: User,
|
authenticated_user: User,
|
||||||
mock_player_service,
|
mock_player_service,
|
||||||
):
|
) -> None:
|
||||||
"""Test skipping to next track successfully."""
|
"""Test skipping to next track successfully."""
|
||||||
response = await authenticated_client.post("/api/v1/player/next")
|
response = await authenticated_client.post("/api/v1/player/next")
|
||||||
|
|
||||||
@@ -211,7 +211,7 @@ class TestPlayerEndpoints:
|
|||||||
mock_player_service.next.assert_called_once()
|
mock_player_service.next.assert_called_once()
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@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."""
|
"""Test skipping to next track without authentication."""
|
||||||
response = await client.post("/api/v1/player/next")
|
response = await client.post("/api/v1/player/next")
|
||||||
assert response.status_code == 401
|
assert response.status_code == 401
|
||||||
@@ -222,7 +222,7 @@ class TestPlayerEndpoints:
|
|||||||
authenticated_client: AsyncClient,
|
authenticated_client: AsyncClient,
|
||||||
authenticated_user: User,
|
authenticated_user: User,
|
||||||
mock_player_service,
|
mock_player_service,
|
||||||
):
|
) -> None:
|
||||||
"""Test skipping to next track with service error."""
|
"""Test skipping to next track with service error."""
|
||||||
mock_player_service.next.side_effect = Exception("Service error")
|
mock_player_service.next.side_effect = Exception("Service error")
|
||||||
|
|
||||||
@@ -238,7 +238,7 @@ class TestPlayerEndpoints:
|
|||||||
authenticated_client: AsyncClient,
|
authenticated_client: AsyncClient,
|
||||||
authenticated_user: User,
|
authenticated_user: User,
|
||||||
mock_player_service,
|
mock_player_service,
|
||||||
):
|
) -> None:
|
||||||
"""Test going to previous track successfully."""
|
"""Test going to previous track successfully."""
|
||||||
response = await authenticated_client.post("/api/v1/player/previous")
|
response = await authenticated_client.post("/api/v1/player/previous")
|
||||||
|
|
||||||
@@ -249,7 +249,7 @@ class TestPlayerEndpoints:
|
|||||||
mock_player_service.previous.assert_called_once()
|
mock_player_service.previous.assert_called_once()
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@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."""
|
"""Test going to previous track without authentication."""
|
||||||
response = await client.post("/api/v1/player/previous")
|
response = await client.post("/api/v1/player/previous")
|
||||||
assert response.status_code == 401
|
assert response.status_code == 401
|
||||||
@@ -260,7 +260,7 @@ class TestPlayerEndpoints:
|
|||||||
authenticated_client: AsyncClient,
|
authenticated_client: AsyncClient,
|
||||||
authenticated_user: User,
|
authenticated_user: User,
|
||||||
mock_player_service,
|
mock_player_service,
|
||||||
):
|
) -> None:
|
||||||
"""Test going to previous track with service error."""
|
"""Test going to previous track with service error."""
|
||||||
mock_player_service.previous.side_effect = Exception("Service error")
|
mock_player_service.previous.side_effect = Exception("Service error")
|
||||||
|
|
||||||
@@ -276,7 +276,7 @@ class TestPlayerEndpoints:
|
|||||||
authenticated_client: AsyncClient,
|
authenticated_client: AsyncClient,
|
||||||
authenticated_user: User,
|
authenticated_user: User,
|
||||||
mock_player_service,
|
mock_player_service,
|
||||||
):
|
) -> None:
|
||||||
"""Test seeking to position successfully."""
|
"""Test seeking to position successfully."""
|
||||||
position = 5000
|
position = 5000
|
||||||
response = await authenticated_client.post(
|
response = await authenticated_client.post(
|
||||||
@@ -296,7 +296,7 @@ class TestPlayerEndpoints:
|
|||||||
authenticated_client: AsyncClient,
|
authenticated_client: AsyncClient,
|
||||||
authenticated_user: User,
|
authenticated_user: User,
|
||||||
mock_player_service,
|
mock_player_service,
|
||||||
):
|
) -> None:
|
||||||
"""Test seeking with invalid position."""
|
"""Test seeking with invalid position."""
|
||||||
response = await authenticated_client.post(
|
response = await authenticated_client.post(
|
||||||
"/api/v1/player/seek",
|
"/api/v1/player/seek",
|
||||||
@@ -306,7 +306,7 @@ class TestPlayerEndpoints:
|
|||||||
assert response.status_code == 422 # Validation error
|
assert response.status_code == 422 # Validation error
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_seek_unauthenticated(self, client: AsyncClient):
|
async def test_seek_unauthenticated(self, client: AsyncClient) -> None:
|
||||||
"""Test seeking without authentication."""
|
"""Test seeking without authentication."""
|
||||||
response = await client.post(
|
response = await client.post(
|
||||||
"/api/v1/player/seek",
|
"/api/v1/player/seek",
|
||||||
@@ -320,7 +320,7 @@ class TestPlayerEndpoints:
|
|||||||
authenticated_client: AsyncClient,
|
authenticated_client: AsyncClient,
|
||||||
authenticated_user: User,
|
authenticated_user: User,
|
||||||
mock_player_service,
|
mock_player_service,
|
||||||
):
|
) -> None:
|
||||||
"""Test seeking with service error."""
|
"""Test seeking with service error."""
|
||||||
mock_player_service.seek.side_effect = Exception("Service error")
|
mock_player_service.seek.side_effect = Exception("Service error")
|
||||||
|
|
||||||
@@ -339,7 +339,7 @@ class TestPlayerEndpoints:
|
|||||||
authenticated_client: AsyncClient,
|
authenticated_client: AsyncClient,
|
||||||
authenticated_user: User,
|
authenticated_user: User,
|
||||||
mock_player_service,
|
mock_player_service,
|
||||||
):
|
) -> None:
|
||||||
"""Test setting volume successfully."""
|
"""Test setting volume successfully."""
|
||||||
volume = 75
|
volume = 75
|
||||||
response = await authenticated_client.post(
|
response = await authenticated_client.post(
|
||||||
@@ -359,7 +359,7 @@ class TestPlayerEndpoints:
|
|||||||
authenticated_client: AsyncClient,
|
authenticated_client: AsyncClient,
|
||||||
authenticated_user: User,
|
authenticated_user: User,
|
||||||
mock_player_service,
|
mock_player_service,
|
||||||
):
|
) -> None:
|
||||||
"""Test setting volume with invalid range."""
|
"""Test setting volume with invalid range."""
|
||||||
# Test volume too high
|
# Test volume too high
|
||||||
response = await authenticated_client.post(
|
response = await authenticated_client.post(
|
||||||
@@ -376,7 +376,7 @@ class TestPlayerEndpoints:
|
|||||||
assert response.status_code == 422
|
assert response.status_code == 422
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@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."""
|
"""Test setting volume without authentication."""
|
||||||
response = await client.post(
|
response = await client.post(
|
||||||
"/api/v1/player/volume",
|
"/api/v1/player/volume",
|
||||||
@@ -390,7 +390,7 @@ class TestPlayerEndpoints:
|
|||||||
authenticated_client: AsyncClient,
|
authenticated_client: AsyncClient,
|
||||||
authenticated_user: User,
|
authenticated_user: User,
|
||||||
mock_player_service,
|
mock_player_service,
|
||||||
):
|
) -> None:
|
||||||
"""Test setting volume with service error."""
|
"""Test setting volume with service error."""
|
||||||
mock_player_service.set_volume.side_effect = Exception("Service error")
|
mock_player_service.set_volume.side_effect = Exception("Service error")
|
||||||
|
|
||||||
@@ -409,7 +409,7 @@ class TestPlayerEndpoints:
|
|||||||
authenticated_client: AsyncClient,
|
authenticated_client: AsyncClient,
|
||||||
authenticated_user: User,
|
authenticated_user: User,
|
||||||
mock_player_service,
|
mock_player_service,
|
||||||
):
|
) -> None:
|
||||||
"""Test setting playback mode successfully."""
|
"""Test setting playback mode successfully."""
|
||||||
mode = PlayerMode.LOOP
|
mode = PlayerMode.LOOP
|
||||||
response = await authenticated_client.post(
|
response = await authenticated_client.post(
|
||||||
@@ -429,7 +429,7 @@ class TestPlayerEndpoints:
|
|||||||
authenticated_client: AsyncClient,
|
authenticated_client: AsyncClient,
|
||||||
authenticated_user: User,
|
authenticated_user: User,
|
||||||
mock_player_service,
|
mock_player_service,
|
||||||
):
|
) -> None:
|
||||||
"""Test setting invalid playback mode."""
|
"""Test setting invalid playback mode."""
|
||||||
response = await authenticated_client.post(
|
response = await authenticated_client.post(
|
||||||
"/api/v1/player/mode",
|
"/api/v1/player/mode",
|
||||||
@@ -439,7 +439,7 @@ class TestPlayerEndpoints:
|
|||||||
assert response.status_code == 422 # Validation error
|
assert response.status_code == 422 # Validation error
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@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."""
|
"""Test setting mode without authentication."""
|
||||||
response = await client.post(
|
response = await client.post(
|
||||||
"/api/v1/player/mode",
|
"/api/v1/player/mode",
|
||||||
@@ -453,7 +453,7 @@ class TestPlayerEndpoints:
|
|||||||
authenticated_client: AsyncClient,
|
authenticated_client: AsyncClient,
|
||||||
authenticated_user: User,
|
authenticated_user: User,
|
||||||
mock_player_service,
|
mock_player_service,
|
||||||
):
|
) -> None:
|
||||||
"""Test setting mode with service error."""
|
"""Test setting mode with service error."""
|
||||||
mock_player_service.set_mode.side_effect = Exception("Service error")
|
mock_player_service.set_mode.side_effect = Exception("Service error")
|
||||||
|
|
||||||
@@ -472,7 +472,7 @@ class TestPlayerEndpoints:
|
|||||||
authenticated_client: AsyncClient,
|
authenticated_client: AsyncClient,
|
||||||
authenticated_user: User,
|
authenticated_user: User,
|
||||||
mock_player_service,
|
mock_player_service,
|
||||||
):
|
) -> None:
|
||||||
"""Test reloading playlist successfully."""
|
"""Test reloading playlist successfully."""
|
||||||
response = await authenticated_client.post("/api/v1/player/reload-playlist")
|
response = await authenticated_client.post("/api/v1/player/reload-playlist")
|
||||||
|
|
||||||
@@ -483,7 +483,7 @@ class TestPlayerEndpoints:
|
|||||||
mock_player_service.reload_playlist.assert_called_once()
|
mock_player_service.reload_playlist.assert_called_once()
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@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."""
|
"""Test reloading playlist without authentication."""
|
||||||
response = await client.post("/api/v1/player/reload-playlist")
|
response = await client.post("/api/v1/player/reload-playlist")
|
||||||
assert response.status_code == 401
|
assert response.status_code == 401
|
||||||
@@ -494,7 +494,7 @@ class TestPlayerEndpoints:
|
|||||||
authenticated_client: AsyncClient,
|
authenticated_client: AsyncClient,
|
||||||
authenticated_user: User,
|
authenticated_user: User,
|
||||||
mock_player_service,
|
mock_player_service,
|
||||||
):
|
) -> None:
|
||||||
"""Test reloading playlist with service error."""
|
"""Test reloading playlist with service error."""
|
||||||
mock_player_service.reload_playlist.side_effect = Exception("Service error")
|
mock_player_service.reload_playlist.side_effect = Exception("Service error")
|
||||||
|
|
||||||
@@ -510,7 +510,7 @@ class TestPlayerEndpoints:
|
|||||||
authenticated_client: AsyncClient,
|
authenticated_client: AsyncClient,
|
||||||
authenticated_user: User,
|
authenticated_user: User,
|
||||||
mock_player_service,
|
mock_player_service,
|
||||||
):
|
) -> None:
|
||||||
"""Test getting player state successfully."""
|
"""Test getting player state successfully."""
|
||||||
mock_state = {
|
mock_state = {
|
||||||
"status": PlayerStatus.PLAYING.value,
|
"status": PlayerStatus.PLAYING.value,
|
||||||
@@ -548,7 +548,7 @@ class TestPlayerEndpoints:
|
|||||||
mock_player_service.get_state.assert_called_once()
|
mock_player_service.get_state.assert_called_once()
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@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."""
|
"""Test getting player state without authentication."""
|
||||||
response = await client.get("/api/v1/player/state")
|
response = await client.get("/api/v1/player/state")
|
||||||
assert response.status_code == 401
|
assert response.status_code == 401
|
||||||
@@ -559,7 +559,7 @@ class TestPlayerEndpoints:
|
|||||||
authenticated_client: AsyncClient,
|
authenticated_client: AsyncClient,
|
||||||
authenticated_user: User,
|
authenticated_user: User,
|
||||||
mock_player_service,
|
mock_player_service,
|
||||||
):
|
) -> None:
|
||||||
"""Test getting player state with service error."""
|
"""Test getting player state with service error."""
|
||||||
mock_player_service.get_state.side_effect = Exception("Service error")
|
mock_player_service.get_state.side_effect = Exception("Service error")
|
||||||
|
|
||||||
@@ -574,7 +574,7 @@ class TestPlayerEndpoints:
|
|||||||
self,
|
self,
|
||||||
authenticated_client: AsyncClient,
|
authenticated_client: AsyncClient,
|
||||||
authenticated_user: User,
|
authenticated_user: User,
|
||||||
):
|
) -> None:
|
||||||
"""Test seeking without request body."""
|
"""Test seeking without request body."""
|
||||||
response = await authenticated_client.post("/api/v1/player/seek")
|
response = await authenticated_client.post("/api/v1/player/seek")
|
||||||
assert response.status_code == 422
|
assert response.status_code == 422
|
||||||
@@ -584,7 +584,7 @@ class TestPlayerEndpoints:
|
|||||||
self,
|
self,
|
||||||
authenticated_client: AsyncClient,
|
authenticated_client: AsyncClient,
|
||||||
authenticated_user: User,
|
authenticated_user: User,
|
||||||
):
|
) -> None:
|
||||||
"""Test setting volume without request body."""
|
"""Test setting volume without request body."""
|
||||||
response = await authenticated_client.post("/api/v1/player/volume")
|
response = await authenticated_client.post("/api/v1/player/volume")
|
||||||
assert response.status_code == 422
|
assert response.status_code == 422
|
||||||
@@ -594,7 +594,7 @@ class TestPlayerEndpoints:
|
|||||||
self,
|
self,
|
||||||
authenticated_client: AsyncClient,
|
authenticated_client: AsyncClient,
|
||||||
authenticated_user: User,
|
authenticated_user: User,
|
||||||
):
|
) -> None:
|
||||||
"""Test setting mode without request body."""
|
"""Test setting mode without request body."""
|
||||||
response = await authenticated_client.post("/api/v1/player/mode")
|
response = await authenticated_client.post("/api/v1/player/mode")
|
||||||
assert response.status_code == 422
|
assert response.status_code == 422
|
||||||
@@ -605,7 +605,7 @@ class TestPlayerEndpoints:
|
|||||||
authenticated_client: AsyncClient,
|
authenticated_client: AsyncClient,
|
||||||
authenticated_user: User,
|
authenticated_user: User,
|
||||||
mock_player_service,
|
mock_player_service,
|
||||||
):
|
) -> None:
|
||||||
"""Test playing sound with negative index."""
|
"""Test playing sound with negative index."""
|
||||||
mock_player_service.play.side_effect = ValueError("Invalid sound index")
|
mock_player_service.play.side_effect = ValueError("Invalid sound index")
|
||||||
|
|
||||||
@@ -621,7 +621,7 @@ class TestPlayerEndpoints:
|
|||||||
authenticated_client: AsyncClient,
|
authenticated_client: AsyncClient,
|
||||||
authenticated_user: User,
|
authenticated_user: User,
|
||||||
mock_player_service,
|
mock_player_service,
|
||||||
):
|
) -> None:
|
||||||
"""Test seeking to position zero."""
|
"""Test seeking to position zero."""
|
||||||
response = await authenticated_client.post(
|
response = await authenticated_client.post(
|
||||||
"/api/v1/player/seek",
|
"/api/v1/player/seek",
|
||||||
@@ -640,7 +640,7 @@ class TestPlayerEndpoints:
|
|||||||
authenticated_client: AsyncClient,
|
authenticated_client: AsyncClient,
|
||||||
authenticated_user: User,
|
authenticated_user: User,
|
||||||
mock_player_service,
|
mock_player_service,
|
||||||
):
|
) -> None:
|
||||||
"""Test setting volume with boundary values."""
|
"""Test setting volume with boundary values."""
|
||||||
# Test minimum volume
|
# Test minimum volume
|
||||||
response = await authenticated_client.post(
|
response = await authenticated_client.post(
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ class TestSocketEndpoints:
|
|||||||
authenticated_client: AsyncClient,
|
authenticated_client: AsyncClient,
|
||||||
authenticated_user: User,
|
authenticated_user: User,
|
||||||
mock_socket_manager,
|
mock_socket_manager,
|
||||||
):
|
) -> None:
|
||||||
"""Test getting socket status for authenticated user."""
|
"""Test getting socket status for authenticated user."""
|
||||||
response = await authenticated_client.get("/api/v1/socket/status")
|
response = await authenticated_client.get("/api/v1/socket/status")
|
||||||
|
|
||||||
@@ -42,7 +42,7 @@ class TestSocketEndpoints:
|
|||||||
assert isinstance(data["connected"], bool)
|
assert isinstance(data["connected"], bool)
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@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."""
|
"""Test getting socket status without authentication."""
|
||||||
response = await client.get("/api/v1/socket/status")
|
response = await client.get("/api/v1/socket/status")
|
||||||
assert response.status_code == 401
|
assert response.status_code == 401
|
||||||
@@ -53,7 +53,7 @@ class TestSocketEndpoints:
|
|||||||
authenticated_client: AsyncClient,
|
authenticated_client: AsyncClient,
|
||||||
authenticated_user: User,
|
authenticated_user: User,
|
||||||
mock_socket_manager,
|
mock_socket_manager,
|
||||||
):
|
) -> None:
|
||||||
"""Test sending message to specific user successfully."""
|
"""Test sending message to specific user successfully."""
|
||||||
target_user_id = 2
|
target_user_id = 2
|
||||||
message = "Hello there!"
|
message = "Hello there!"
|
||||||
@@ -87,7 +87,7 @@ class TestSocketEndpoints:
|
|||||||
authenticated_client: AsyncClient,
|
authenticated_client: AsyncClient,
|
||||||
authenticated_user: User,
|
authenticated_user: User,
|
||||||
mock_socket_manager,
|
mock_socket_manager,
|
||||||
):
|
) -> None:
|
||||||
"""Test sending message to user who is not connected."""
|
"""Test sending message to user who is not connected."""
|
||||||
target_user_id = 999
|
target_user_id = 999
|
||||||
message = "Hello there!"
|
message = "Hello there!"
|
||||||
@@ -108,7 +108,7 @@ class TestSocketEndpoints:
|
|||||||
assert data["message"] == "User not connected"
|
assert data["message"] == "User not connected"
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@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."""
|
"""Test sending message without authentication."""
|
||||||
response = await client.post(
|
response = await client.post(
|
||||||
"/api/v1/socket/send-message",
|
"/api/v1/socket/send-message",
|
||||||
@@ -122,7 +122,7 @@ class TestSocketEndpoints:
|
|||||||
authenticated_client: AsyncClient,
|
authenticated_client: AsyncClient,
|
||||||
authenticated_user: User,
|
authenticated_user: User,
|
||||||
mock_socket_manager,
|
mock_socket_manager,
|
||||||
):
|
) -> None:
|
||||||
"""Test broadcasting message to all users successfully."""
|
"""Test broadcasting message to all users successfully."""
|
||||||
message = "Important announcement!"
|
message = "Important announcement!"
|
||||||
|
|
||||||
@@ -148,7 +148,7 @@ class TestSocketEndpoints:
|
|||||||
)
|
)
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@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."""
|
"""Test broadcasting message without authentication."""
|
||||||
response = await client.post(
|
response = await client.post(
|
||||||
"/api/v1/socket/broadcast",
|
"/api/v1/socket/broadcast",
|
||||||
@@ -159,7 +159,7 @@ class TestSocketEndpoints:
|
|||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_send_message_missing_parameters(
|
async def test_send_message_missing_parameters(
|
||||||
self, authenticated_client: AsyncClient, authenticated_user: User,
|
self, authenticated_client: AsyncClient, authenticated_user: User,
|
||||||
):
|
) -> None:
|
||||||
"""Test sending message with missing parameters."""
|
"""Test sending message with missing parameters."""
|
||||||
# Missing target_user_id
|
# Missing target_user_id
|
||||||
response = await authenticated_client.post(
|
response = await authenticated_client.post(
|
||||||
@@ -178,7 +178,7 @@ class TestSocketEndpoints:
|
|||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_broadcast_message_missing_parameters(
|
async def test_broadcast_message_missing_parameters(
|
||||||
self, authenticated_client: AsyncClient, authenticated_user: User,
|
self, authenticated_client: AsyncClient, authenticated_user: User,
|
||||||
):
|
) -> None:
|
||||||
"""Test broadcasting message with missing parameters."""
|
"""Test broadcasting message with missing parameters."""
|
||||||
response = await authenticated_client.post("/api/v1/socket/broadcast")
|
response = await authenticated_client.post("/api/v1/socket/broadcast")
|
||||||
assert response.status_code == 422
|
assert response.status_code == 422
|
||||||
@@ -186,7 +186,7 @@ class TestSocketEndpoints:
|
|||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_send_message_invalid_user_id(
|
async def test_send_message_invalid_user_id(
|
||||||
self, authenticated_client: AsyncClient, authenticated_user: User,
|
self, authenticated_client: AsyncClient, authenticated_user: User,
|
||||||
):
|
) -> None:
|
||||||
"""Test sending message with invalid user ID."""
|
"""Test sending message with invalid user ID."""
|
||||||
response = await authenticated_client.post(
|
response = await authenticated_client.post(
|
||||||
"/api/v1/socket/send-message",
|
"/api/v1/socket/send-message",
|
||||||
@@ -200,7 +200,7 @@ class TestSocketEndpoints:
|
|||||||
authenticated_client: AsyncClient,
|
authenticated_client: AsyncClient,
|
||||||
authenticated_user: User,
|
authenticated_user: User,
|
||||||
mock_socket_manager,
|
mock_socket_manager,
|
||||||
):
|
) -> None:
|
||||||
"""Test that socket status correctly shows if user is connected."""
|
"""Test that socket status correctly shows if user is connected."""
|
||||||
# Test when user is connected
|
# Test when user is connected
|
||||||
mock_socket_manager.get_connected_users.return_value = [
|
mock_socket_manager.get_connected_users.return_value = [
|
||||||
|
|||||||
@@ -1,11 +1,14 @@
|
|||||||
"""Tests for sound API endpoints."""
|
"""Tests for sound API endpoints."""
|
||||||
|
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from httpx import ASGITransport, AsyncClient
|
from httpx import ASGITransport, AsyncClient
|
||||||
|
|
||||||
from app.models.user import User
|
from app.models.user import User
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
from app.services.sound_normalizer import NormalizationResults
|
from app.services.sound_normalizer import NormalizationResults
|
||||||
from app.services.sound_scanner import ScanResults
|
from app.services.sound_scanner import ScanResults
|
||||||
|
|
||||||
@@ -18,7 +21,7 @@ class TestSoundEndpoints:
|
|||||||
self,
|
self,
|
||||||
authenticated_admin_client: AsyncClient,
|
authenticated_admin_client: AsyncClient,
|
||||||
admin_user: User,
|
admin_user: User,
|
||||||
):
|
) -> None:
|
||||||
"""Test successful sound scanning."""
|
"""Test successful sound scanning."""
|
||||||
# Mock the scanner service to return successful results
|
# Mock the scanner service to return successful results
|
||||||
mock_results: ScanResults = {
|
mock_results: ScanResults = {
|
||||||
@@ -95,7 +98,7 @@ class TestSoundEndpoints:
|
|||||||
assert results["files"][2]["status"] == "deleted"
|
assert results["files"][2]["status"] == "deleted"
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@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."""
|
"""Test scanning sounds without authentication."""
|
||||||
response = await client.post("/api/v1/sounds/scan")
|
response = await client.post("/api/v1/sounds/scan")
|
||||||
|
|
||||||
@@ -108,7 +111,7 @@ class TestSoundEndpoints:
|
|||||||
self,
|
self,
|
||||||
test_app,
|
test_app,
|
||||||
test_user: User,
|
test_user: User,
|
||||||
):
|
) -> None:
|
||||||
"""Test scanning sounds with non-admin user."""
|
"""Test scanning sounds with non-admin user."""
|
||||||
from app.core.dependencies import get_current_active_user_flexible
|
from app.core.dependencies import get_current_active_user_flexible
|
||||||
|
|
||||||
@@ -142,7 +145,7 @@ class TestSoundEndpoints:
|
|||||||
self,
|
self,
|
||||||
test_app,
|
test_app,
|
||||||
admin_user: User,
|
admin_user: User,
|
||||||
):
|
) -> None:
|
||||||
"""Test scanning sounds with admin user."""
|
"""Test scanning sounds with admin user."""
|
||||||
from app.core.dependencies import get_current_active_user_flexible
|
from app.core.dependencies import get_current_active_user_flexible
|
||||||
|
|
||||||
@@ -189,7 +192,7 @@ class TestSoundEndpoints:
|
|||||||
self,
|
self,
|
||||||
authenticated_admin_client: AsyncClient,
|
authenticated_admin_client: AsyncClient,
|
||||||
admin_user: User,
|
admin_user: User,
|
||||||
):
|
) -> None:
|
||||||
"""Test scanning sounds when service raises an error."""
|
"""Test scanning sounds when service raises an error."""
|
||||||
with patch(
|
with patch(
|
||||||
"app.services.sound_scanner.SoundScannerService.scan_soundboard_directory",
|
"app.services.sound_scanner.SoundScannerService.scan_soundboard_directory",
|
||||||
@@ -208,7 +211,7 @@ class TestSoundEndpoints:
|
|||||||
self,
|
self,
|
||||||
authenticated_admin_client: AsyncClient,
|
authenticated_admin_client: AsyncClient,
|
||||||
admin_user: User,
|
admin_user: User,
|
||||||
):
|
) -> None:
|
||||||
"""Test successful custom directory scanning."""
|
"""Test successful custom directory scanning."""
|
||||||
mock_results: ScanResults = {
|
mock_results: ScanResults = {
|
||||||
"scanned": 2,
|
"scanned": 2,
|
||||||
@@ -272,7 +275,7 @@ class TestSoundEndpoints:
|
|||||||
self,
|
self,
|
||||||
authenticated_admin_client: AsyncClient,
|
authenticated_admin_client: AsyncClient,
|
||||||
admin_user: User,
|
admin_user: User,
|
||||||
):
|
) -> None:
|
||||||
"""Test custom directory scanning with default sound type."""
|
"""Test custom directory scanning with default sound type."""
|
||||||
mock_results: ScanResults = {
|
mock_results: ScanResults = {
|
||||||
"scanned": 1,
|
"scanned": 1,
|
||||||
@@ -304,7 +307,7 @@ class TestSoundEndpoints:
|
|||||||
self,
|
self,
|
||||||
authenticated_admin_client: AsyncClient,
|
authenticated_admin_client: AsyncClient,
|
||||||
admin_user: User,
|
admin_user: User,
|
||||||
):
|
) -> None:
|
||||||
"""Test custom directory scanning with invalid path."""
|
"""Test custom directory scanning with invalid path."""
|
||||||
with patch(
|
with patch(
|
||||||
"app.services.sound_scanner.SoundScannerService.scan_directory",
|
"app.services.sound_scanner.SoundScannerService.scan_directory",
|
||||||
@@ -322,7 +325,7 @@ class TestSoundEndpoints:
|
|||||||
assert "Directory does not exist" in data["detail"]
|
assert "Directory does not exist" in data["detail"]
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@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."""
|
"""Test custom directory scanning without authentication."""
|
||||||
response = await client.post(
|
response = await client.post(
|
||||||
"/api/v1/sounds/scan/custom", params={"directory": "/some/path"},
|
"/api/v1/sounds/scan/custom", params={"directory": "/some/path"},
|
||||||
@@ -337,7 +340,7 @@ class TestSoundEndpoints:
|
|||||||
self,
|
self,
|
||||||
test_app,
|
test_app,
|
||||||
test_user: User,
|
test_user: User,
|
||||||
):
|
) -> None:
|
||||||
"""Test custom directory scanning with non-admin user."""
|
"""Test custom directory scanning with non-admin user."""
|
||||||
from app.core.dependencies import get_current_active_user_flexible
|
from app.core.dependencies import get_current_active_user_flexible
|
||||||
|
|
||||||
@@ -374,7 +377,7 @@ class TestSoundEndpoints:
|
|||||||
self,
|
self,
|
||||||
authenticated_admin_client: AsyncClient,
|
authenticated_admin_client: AsyncClient,
|
||||||
admin_user: User,
|
admin_user: User,
|
||||||
):
|
) -> None:
|
||||||
"""Test custom directory scanning when service raises an error."""
|
"""Test custom directory scanning when service raises an error."""
|
||||||
with patch(
|
with patch(
|
||||||
"app.services.sound_scanner.SoundScannerService.scan_directory",
|
"app.services.sound_scanner.SoundScannerService.scan_directory",
|
||||||
@@ -395,7 +398,7 @@ class TestSoundEndpoints:
|
|||||||
self,
|
self,
|
||||||
authenticated_admin_client: AsyncClient,
|
authenticated_admin_client: AsyncClient,
|
||||||
admin_user: User,
|
admin_user: User,
|
||||||
):
|
) -> None:
|
||||||
"""Test scanning with some errors in results."""
|
"""Test scanning with some errors in results."""
|
||||||
mock_results: ScanResults = {
|
mock_results: ScanResults = {
|
||||||
"scanned": 3,
|
"scanned": 3,
|
||||||
@@ -467,7 +470,7 @@ class TestSoundEndpoints:
|
|||||||
self,
|
self,
|
||||||
authenticated_admin_client: AsyncClient,
|
authenticated_admin_client: AsyncClient,
|
||||||
admin_user: User,
|
admin_user: User,
|
||||||
):
|
) -> None:
|
||||||
"""Test that endpoint response has correct structure."""
|
"""Test that endpoint response has correct structure."""
|
||||||
mock_results: ScanResults = {
|
mock_results: ScanResults = {
|
||||||
"scanned": 0,
|
"scanned": 0,
|
||||||
@@ -519,7 +522,7 @@ class TestSoundEndpoints:
|
|||||||
self,
|
self,
|
||||||
authenticated_admin_client: AsyncClient,
|
authenticated_admin_client: AsyncClient,
|
||||||
admin_user: User,
|
admin_user: User,
|
||||||
):
|
) -> None:
|
||||||
"""Test successful normalization of all sounds."""
|
"""Test successful normalization of all sounds."""
|
||||||
mock_results: NormalizationResults = {
|
mock_results: NormalizationResults = {
|
||||||
"processed": 3,
|
"processed": 3,
|
||||||
@@ -597,7 +600,7 @@ class TestSoundEndpoints:
|
|||||||
self,
|
self,
|
||||||
authenticated_admin_client: AsyncClient,
|
authenticated_admin_client: AsyncClient,
|
||||||
admin_user: User,
|
admin_user: User,
|
||||||
):
|
) -> None:
|
||||||
"""Test normalization with force parameter."""
|
"""Test normalization with force parameter."""
|
||||||
mock_results: NormalizationResults = {
|
mock_results: NormalizationResults = {
|
||||||
"processed": 1,
|
"processed": 1,
|
||||||
@@ -626,7 +629,7 @@ class TestSoundEndpoints:
|
|||||||
self,
|
self,
|
||||||
authenticated_admin_client: AsyncClient,
|
authenticated_admin_client: AsyncClient,
|
||||||
admin_user: User,
|
admin_user: User,
|
||||||
):
|
) -> None:
|
||||||
"""Test normalization with one_pass parameter."""
|
"""Test normalization with one_pass parameter."""
|
||||||
mock_results: NormalizationResults = {
|
mock_results: NormalizationResults = {
|
||||||
"processed": 1,
|
"processed": 1,
|
||||||
@@ -651,7 +654,7 @@ class TestSoundEndpoints:
|
|||||||
mock_normalize.assert_called_once_with(force=False, one_pass=True)
|
mock_normalize.assert_called_once_with(force=False, one_pass=True)
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@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."""
|
"""Test normalizing sounds without authentication."""
|
||||||
response = await client.post("/api/v1/sounds/normalize/all")
|
response = await client.post("/api/v1/sounds/normalize/all")
|
||||||
|
|
||||||
@@ -664,7 +667,7 @@ class TestSoundEndpoints:
|
|||||||
self,
|
self,
|
||||||
test_app,
|
test_app,
|
||||||
test_user: User,
|
test_user: User,
|
||||||
):
|
) -> None:
|
||||||
"""Test normalizing sounds with non-admin user."""
|
"""Test normalizing sounds with non-admin user."""
|
||||||
from app.core.dependencies import get_current_active_user_flexible
|
from app.core.dependencies import get_current_active_user_flexible
|
||||||
|
|
||||||
@@ -699,7 +702,7 @@ class TestSoundEndpoints:
|
|||||||
self,
|
self,
|
||||||
authenticated_admin_client: AsyncClient,
|
authenticated_admin_client: AsyncClient,
|
||||||
admin_user: User,
|
admin_user: User,
|
||||||
):
|
) -> None:
|
||||||
"""Test normalization when service raises an error."""
|
"""Test normalization when service raises an error."""
|
||||||
with patch(
|
with patch(
|
||||||
"app.services.sound_normalizer.SoundNormalizerService.normalize_all_sounds",
|
"app.services.sound_normalizer.SoundNormalizerService.normalize_all_sounds",
|
||||||
@@ -720,7 +723,7 @@ class TestSoundEndpoints:
|
|||||||
self,
|
self,
|
||||||
authenticated_admin_client: AsyncClient,
|
authenticated_admin_client: AsyncClient,
|
||||||
admin_user: User,
|
admin_user: User,
|
||||||
):
|
) -> None:
|
||||||
"""Test successful normalization by sound type."""
|
"""Test successful normalization by sound type."""
|
||||||
mock_results: NormalizationResults = {
|
mock_results: NormalizationResults = {
|
||||||
"processed": 2,
|
"processed": 2,
|
||||||
@@ -787,7 +790,7 @@ class TestSoundEndpoints:
|
|||||||
self,
|
self,
|
||||||
authenticated_admin_client: AsyncClient,
|
authenticated_admin_client: AsyncClient,
|
||||||
admin_user: User,
|
admin_user: User,
|
||||||
):
|
) -> None:
|
||||||
"""Test normalization with invalid sound type."""
|
"""Test normalization with invalid sound type."""
|
||||||
response = await authenticated_admin_client.post(
|
response = await authenticated_admin_client.post(
|
||||||
"/api/v1/sounds/normalize/type/INVALID",
|
"/api/v1/sounds/normalize/type/INVALID",
|
||||||
@@ -803,7 +806,7 @@ class TestSoundEndpoints:
|
|||||||
self,
|
self,
|
||||||
authenticated_admin_client: AsyncClient,
|
authenticated_admin_client: AsyncClient,
|
||||||
admin_user: User,
|
admin_user: User,
|
||||||
):
|
) -> None:
|
||||||
"""Test normalization by type with force and one_pass parameters."""
|
"""Test normalization by type with force and one_pass parameters."""
|
||||||
mock_results: NormalizationResults = {
|
mock_results: NormalizationResults = {
|
||||||
"processed": 1,
|
"processed": 1,
|
||||||
@@ -835,7 +838,7 @@ class TestSoundEndpoints:
|
|||||||
self,
|
self,
|
||||||
authenticated_admin_client: AsyncClient,
|
authenticated_admin_client: AsyncClient,
|
||||||
admin_user: User,
|
admin_user: User,
|
||||||
):
|
) -> None:
|
||||||
"""Test successful normalization of a specific sound."""
|
"""Test successful normalization of a specific sound."""
|
||||||
# Mock the sound
|
# Mock the sound
|
||||||
mock_sound = type(
|
mock_sound = type(
|
||||||
@@ -894,7 +897,7 @@ class TestSoundEndpoints:
|
|||||||
self,
|
self,
|
||||||
authenticated_admin_client: AsyncClient,
|
authenticated_admin_client: AsyncClient,
|
||||||
admin_user: User,
|
admin_user: User,
|
||||||
):
|
) -> None:
|
||||||
"""Test normalization of non-existent sound."""
|
"""Test normalization of non-existent sound."""
|
||||||
with patch(
|
with patch(
|
||||||
"app.repositories.sound.SoundRepository.get_by_id",
|
"app.repositories.sound.SoundRepository.get_by_id",
|
||||||
@@ -914,7 +917,7 @@ class TestSoundEndpoints:
|
|||||||
self,
|
self,
|
||||||
authenticated_admin_client: AsyncClient,
|
authenticated_admin_client: AsyncClient,
|
||||||
admin_user: User,
|
admin_user: User,
|
||||||
):
|
) -> None:
|
||||||
"""Test normalization when the sound normalization fails."""
|
"""Test normalization when the sound normalization fails."""
|
||||||
# Mock the sound
|
# Mock the sound
|
||||||
mock_sound = type(
|
mock_sound = type(
|
||||||
@@ -966,7 +969,7 @@ class TestSoundEndpoints:
|
|||||||
self,
|
self,
|
||||||
authenticated_admin_client: AsyncClient,
|
authenticated_admin_client: AsyncClient,
|
||||||
admin_user: User,
|
admin_user: User,
|
||||||
):
|
) -> None:
|
||||||
"""Test sound normalization with force and one_pass parameters."""
|
"""Test sound normalization with force and one_pass parameters."""
|
||||||
# Mock the sound
|
# Mock the sound
|
||||||
mock_sound = type(
|
mock_sound = type(
|
||||||
@@ -1013,15 +1016,15 @@ class TestSoundEndpoints:
|
|||||||
|
|
||||||
# Verify parameters were passed to normalize_sound
|
# Verify parameters were passed to normalize_sound
|
||||||
call_args = mock_normalize_sound.call_args
|
call_args = mock_normalize_sound.call_args
|
||||||
assert call_args[1]["force"] == True
|
assert call_args[1]["force"]
|
||||||
assert call_args[1]["one_pass"] == True
|
assert call_args[1]["one_pass"]
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_normalize_sound_by_id_skipped(
|
async def test_normalize_sound_by_id_skipped(
|
||||||
self,
|
self,
|
||||||
authenticated_admin_client: AsyncClient,
|
authenticated_admin_client: AsyncClient,
|
||||||
admin_user: User,
|
admin_user: User,
|
||||||
):
|
) -> None:
|
||||||
"""Test normalization when sound is already normalized and not forced."""
|
"""Test normalization when sound is already normalized and not forced."""
|
||||||
# Mock the sound
|
# Mock the sound
|
||||||
mock_sound = type(
|
mock_sound = type(
|
||||||
@@ -1071,7 +1074,7 @@ class TestSoundEndpoints:
|
|||||||
assert data["reason"] == "already normalized"
|
assert data["reason"] == "already normalized"
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@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."""
|
"""Test normalizing a specific sound without authentication."""
|
||||||
response = await client.post("/api/v1/sounds/normalize/42")
|
response = await client.post("/api/v1/sounds/normalize/42")
|
||||||
|
|
||||||
@@ -1084,7 +1087,7 @@ class TestSoundEndpoints:
|
|||||||
self,
|
self,
|
||||||
test_app,
|
test_app,
|
||||||
test_user: User,
|
test_user: User,
|
||||||
):
|
) -> None:
|
||||||
"""Test normalizing a specific sound with non-admin user."""
|
"""Test normalizing a specific sound with non-admin user."""
|
||||||
from app.core.dependencies import get_current_active_user_flexible
|
from app.core.dependencies import get_current_active_user_flexible
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ class TestVLCEndpoints:
|
|||||||
test_app: FastAPI,
|
test_app: FastAPI,
|
||||||
authenticated_client: AsyncClient,
|
authenticated_client: AsyncClient,
|
||||||
authenticated_user: User,
|
authenticated_user: User,
|
||||||
):
|
) -> None:
|
||||||
"""Test successful sound playback via VLC."""
|
"""Test successful sound playback via VLC."""
|
||||||
# Set up mocks
|
# Set up mocks
|
||||||
mock_vlc_service = AsyncMock()
|
mock_vlc_service = AsyncMock()
|
||||||
@@ -73,7 +73,7 @@ class TestVLCEndpoints:
|
|||||||
test_app: FastAPI,
|
test_app: FastAPI,
|
||||||
authenticated_client: AsyncClient,
|
authenticated_client: AsyncClient,
|
||||||
authenticated_user: User,
|
authenticated_user: User,
|
||||||
):
|
) -> None:
|
||||||
"""Test VLC playback when sound is not found."""
|
"""Test VLC playback when sound is not found."""
|
||||||
# Set up mocks
|
# Set up mocks
|
||||||
mock_vlc_service = AsyncMock()
|
mock_vlc_service = AsyncMock()
|
||||||
@@ -106,7 +106,7 @@ class TestVLCEndpoints:
|
|||||||
test_app: FastAPI,
|
test_app: FastAPI,
|
||||||
authenticated_client: AsyncClient,
|
authenticated_client: AsyncClient,
|
||||||
authenticated_user: User,
|
authenticated_user: User,
|
||||||
):
|
) -> None:
|
||||||
"""Test VLC playback when VLC launch fails."""
|
"""Test VLC playback when VLC launch fails."""
|
||||||
# Set up mocks
|
# Set up mocks
|
||||||
mock_vlc_service = AsyncMock()
|
mock_vlc_service = AsyncMock()
|
||||||
@@ -153,7 +153,7 @@ class TestVLCEndpoints:
|
|||||||
test_app: FastAPI,
|
test_app: FastAPI,
|
||||||
authenticated_client: AsyncClient,
|
authenticated_client: AsyncClient,
|
||||||
authenticated_user: User,
|
authenticated_user: User,
|
||||||
):
|
) -> None:
|
||||||
"""Test VLC playback when service raises an exception."""
|
"""Test VLC playback when service raises an exception."""
|
||||||
# Set up mocks
|
# Set up mocks
|
||||||
mock_vlc_service = AsyncMock()
|
mock_vlc_service = AsyncMock()
|
||||||
@@ -184,7 +184,7 @@ class TestVLCEndpoints:
|
|||||||
async def test_play_sound_with_vlc_unauthenticated(
|
async def test_play_sound_with_vlc_unauthenticated(
|
||||||
self,
|
self,
|
||||||
client: AsyncClient,
|
client: AsyncClient,
|
||||||
):
|
) -> None:
|
||||||
"""Test VLC playback without authentication."""
|
"""Test VLC playback without authentication."""
|
||||||
response = await client.post("/api/v1/sounds/vlc/play/1")
|
response = await client.post("/api/v1/sounds/vlc/play/1")
|
||||||
assert response.status_code == 401
|
assert response.status_code == 401
|
||||||
@@ -195,7 +195,7 @@ class TestVLCEndpoints:
|
|||||||
test_app: FastAPI,
|
test_app: FastAPI,
|
||||||
authenticated_client: AsyncClient,
|
authenticated_client: AsyncClient,
|
||||||
authenticated_user: User,
|
authenticated_user: User,
|
||||||
):
|
) -> None:
|
||||||
"""Test successful stopping of all VLC instances."""
|
"""Test successful stopping of all VLC instances."""
|
||||||
# Set up mock
|
# Set up mock
|
||||||
mock_vlc_service = AsyncMock()
|
mock_vlc_service = AsyncMock()
|
||||||
@@ -234,7 +234,7 @@ class TestVLCEndpoints:
|
|||||||
test_app: FastAPI,
|
test_app: FastAPI,
|
||||||
authenticated_client: AsyncClient,
|
authenticated_client: AsyncClient,
|
||||||
authenticated_user: User,
|
authenticated_user: User,
|
||||||
):
|
) -> None:
|
||||||
"""Test stopping VLC instances when none are running."""
|
"""Test stopping VLC instances when none are running."""
|
||||||
# Set up mock
|
# Set up mock
|
||||||
mock_vlc_service = AsyncMock()
|
mock_vlc_service = AsyncMock()
|
||||||
@@ -268,7 +268,7 @@ class TestVLCEndpoints:
|
|||||||
test_app: FastAPI,
|
test_app: FastAPI,
|
||||||
authenticated_client: AsyncClient,
|
authenticated_client: AsyncClient,
|
||||||
authenticated_user: User,
|
authenticated_user: User,
|
||||||
):
|
) -> None:
|
||||||
"""Test stopping VLC instances with partial success."""
|
"""Test stopping VLC instances with partial success."""
|
||||||
# Set up mock
|
# Set up mock
|
||||||
mock_vlc_service = AsyncMock()
|
mock_vlc_service = AsyncMock()
|
||||||
@@ -303,7 +303,7 @@ class TestVLCEndpoints:
|
|||||||
test_app: FastAPI,
|
test_app: FastAPI,
|
||||||
authenticated_client: AsyncClient,
|
authenticated_client: AsyncClient,
|
||||||
authenticated_user: User,
|
authenticated_user: User,
|
||||||
):
|
) -> None:
|
||||||
"""Test stopping VLC instances when service fails."""
|
"""Test stopping VLC instances when service fails."""
|
||||||
# Set up mock
|
# Set up mock
|
||||||
mock_vlc_service = AsyncMock()
|
mock_vlc_service = AsyncMock()
|
||||||
@@ -337,7 +337,7 @@ class TestVLCEndpoints:
|
|||||||
test_app: FastAPI,
|
test_app: FastAPI,
|
||||||
authenticated_client: AsyncClient,
|
authenticated_client: AsyncClient,
|
||||||
authenticated_user: User,
|
authenticated_user: User,
|
||||||
):
|
) -> None:
|
||||||
"""Test stopping VLC instances when service raises an exception."""
|
"""Test stopping VLC instances when service raises an exception."""
|
||||||
# Set up mock to raise an exception
|
# Set up mock to raise an exception
|
||||||
mock_vlc_service = AsyncMock()
|
mock_vlc_service = AsyncMock()
|
||||||
@@ -360,7 +360,7 @@ class TestVLCEndpoints:
|
|||||||
async def test_stop_all_vlc_instances_unauthenticated(
|
async def test_stop_all_vlc_instances_unauthenticated(
|
||||||
self,
|
self,
|
||||||
client: AsyncClient,
|
client: AsyncClient,
|
||||||
):
|
) -> None:
|
||||||
"""Test stopping VLC instances without authentication."""
|
"""Test stopping VLC instances without authentication."""
|
||||||
response = await client.post("/api/v1/sounds/vlc/stop-all")
|
response = await client.post("/api/v1/sounds/vlc/stop-all")
|
||||||
assert response.status_code == 401
|
assert response.status_code == 401
|
||||||
@@ -371,7 +371,7 @@ class TestVLCEndpoints:
|
|||||||
test_app: FastAPI,
|
test_app: FastAPI,
|
||||||
authenticated_admin_client: AsyncClient,
|
authenticated_admin_client: AsyncClient,
|
||||||
admin_user: User,
|
admin_user: User,
|
||||||
):
|
) -> None:
|
||||||
"""Test VLC endpoints work with admin user."""
|
"""Test VLC endpoints work with admin user."""
|
||||||
# Set up mocks
|
# Set up mocks
|
||||||
mock_vlc_service = AsyncMock()
|
mock_vlc_service = AsyncMock()
|
||||||
|
|||||||
@@ -7,12 +7,15 @@ from typing import Any
|
|||||||
import pytest
|
import pytest
|
||||||
import pytest_asyncio
|
import pytest_asyncio
|
||||||
from fastapi import FastAPI
|
from fastapi import FastAPI
|
||||||
|
from fastapi.middleware.cors import CORSMiddleware
|
||||||
from httpx import ASGITransport, AsyncClient
|
from httpx import ASGITransport, AsyncClient
|
||||||
from sqlalchemy.ext.asyncio import create_async_engine
|
from sqlalchemy.ext.asyncio import create_async_engine
|
||||||
from sqlmodel import SQLModel, select
|
from sqlmodel import SQLModel, select
|
||||||
from sqlmodel.ext.asyncio.session import AsyncSession
|
from sqlmodel.ext.asyncio.session import AsyncSession
|
||||||
|
|
||||||
|
from app.api import api_router
|
||||||
from app.core.database import get_db
|
from app.core.database import get_db
|
||||||
|
from app.middleware.logging import LoggingMiddleware
|
||||||
from app.models.plan import Plan
|
from app.models.plan import Plan
|
||||||
from app.models.user import User
|
from app.models.user import User
|
||||||
from app.utils.auth import JWTUtils, PasswordUtils
|
from app.utils.auth import JWTUtils, PasswordUtils
|
||||||
@@ -61,14 +64,8 @@ async def test_session(test_engine: Any) -> AsyncGenerator[AsyncSession, None]:
|
|||||||
|
|
||||||
|
|
||||||
@pytest_asyncio.fixture
|
@pytest_asyncio.fixture
|
||||||
async def test_app(test_session: AsyncSession):
|
async def test_app(test_session: AsyncSession) -> FastAPI:
|
||||||
"""Create a test FastAPI application."""
|
"""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)
|
# Create FastAPI app directly for testing (without Socket.IO)
|
||||||
app = FastAPI()
|
app = FastAPI()
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
"""Tests for API token authentication dependencies."""
|
"""Tests for API token authentication dependencies."""
|
||||||
# ruff: noqa: S106
|
|
||||||
|
|
||||||
from datetime import UTC, datetime, timedelta
|
from datetime import UTC, datetime, timedelta
|
||||||
from unittest.mock import AsyncMock
|
from unittest.mock import AsyncMock
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
"""Tests for credit transaction repository."""
|
"""Tests for credit transaction repository."""
|
||||||
# ruff: noqa: ARG002, E501
|
|
||||||
|
|
||||||
import json
|
import json
|
||||||
from collections.abc import AsyncGenerator
|
from collections.abc import AsyncGenerator
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
"""Tests for extraction repository."""
|
"""Tests for extraction repository."""
|
||||||
# ruff: noqa: ANN001, ANN201
|
|
||||||
|
|
||||||
from unittest.mock import AsyncMock, Mock
|
from unittest.mock import AsyncMock, Mock
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
"""Tests for playlist repository."""
|
"""Tests for playlist repository."""
|
||||||
# ruff: noqa: PLR2004, ANN401
|
|
||||||
|
|
||||||
from collections.abc import AsyncGenerator
|
from collections.abc import AsyncGenerator
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
"""Tests for sound repository."""
|
"""Tests for sound repository."""
|
||||||
# ruff: noqa: ARG002, PLR2004
|
|
||||||
|
|
||||||
from collections.abc import AsyncGenerator
|
from collections.abc import AsyncGenerator
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
"""Tests for user repository."""
|
"""Tests for user repository."""
|
||||||
# ruff: noqa: ARG002
|
|
||||||
|
|
||||||
from collections.abc import AsyncGenerator
|
from collections.abc import AsyncGenerator
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
"""Tests for user OAuth repository."""
|
"""Tests for user OAuth repository."""
|
||||||
# ruff: noqa: ARG002
|
|
||||||
|
|
||||||
from collections.abc import AsyncGenerator
|
from collections.abc import AsyncGenerator
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,11 @@ from app.models.user import User
|
|||||||
from app.schemas.auth import UserLoginRequest, UserRegisterRequest
|
from app.schemas.auth import UserLoginRequest, UserRegisterRequest
|
||||||
from app.services.auth import AuthService
|
from app.services.auth import AuthService
|
||||||
|
|
||||||
|
# Constants
|
||||||
|
HTTP_400_BAD_REQUEST = 400
|
||||||
|
HTTP_401_UNAUTHORIZED = 401
|
||||||
|
HTTP_404_NOT_FOUND = 404
|
||||||
|
|
||||||
|
|
||||||
class TestAuthService:
|
class TestAuthService:
|
||||||
"""Test authentication service operations."""
|
"""Test authentication service operations."""
|
||||||
@@ -62,7 +67,7 @@ class TestAuthService:
|
|||||||
with pytest.raises(HTTPException) as exc_info:
|
with pytest.raises(HTTPException) as exc_info:
|
||||||
await auth_service.register(request)
|
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
|
assert "Email address is already registered" in exc_info.value.detail
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
@@ -100,7 +105,7 @@ class TestAuthService:
|
|||||||
with pytest.raises(HTTPException) as exc_info:
|
with pytest.raises(HTTPException) as exc_info:
|
||||||
await auth_service.login(request)
|
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
|
assert "Invalid email or password" in exc_info.value.detail
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
@@ -115,7 +120,7 @@ class TestAuthService:
|
|||||||
with pytest.raises(HTTPException) as exc_info:
|
with pytest.raises(HTTPException) as exc_info:
|
||||||
await auth_service.login(request)
|
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
|
assert "Invalid email or password" in exc_info.value.detail
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
@@ -138,7 +143,7 @@ class TestAuthService:
|
|||||||
with pytest.raises(HTTPException) as exc_info:
|
with pytest.raises(HTTPException) as exc_info:
|
||||||
await auth_service.login(request)
|
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
|
assert "Account is deactivated" in exc_info.value.detail
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
@@ -161,7 +166,7 @@ class TestAuthService:
|
|||||||
with pytest.raises(HTTPException) as exc_info:
|
with pytest.raises(HTTPException) as exc_info:
|
||||||
await auth_service.login(request)
|
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
|
assert "Invalid email or password" in exc_info.value.detail
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
@@ -184,7 +189,7 @@ class TestAuthService:
|
|||||||
with pytest.raises(HTTPException) as exc_info:
|
with pytest.raises(HTTPException) as exc_info:
|
||||||
await auth_service.get_current_user(99999)
|
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
|
assert "User not found" in exc_info.value.detail
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
@@ -205,7 +210,7 @@ class TestAuthService:
|
|||||||
with pytest.raises(HTTPException) as exc_info:
|
with pytest.raises(HTTPException) as exc_info:
|
||||||
await auth_service.get_current_user(user_id)
|
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
|
assert "Account is deactivated" in exc_info.value.detail
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ class TestCreditService:
|
|||||||
)
|
)
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@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."""
|
"""Test checking credits when user has sufficient credits."""
|
||||||
mock_session = credit_service.db_session_factory()
|
mock_session = credit_service.db_session_factory()
|
||||||
|
|
||||||
@@ -55,7 +55,7 @@ class TestCreditService:
|
|||||||
mock_session.close.assert_called_once()
|
mock_session.close.assert_called_once()
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@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."""
|
"""Test checking credits when user has insufficient credits."""
|
||||||
mock_session = credit_service.db_session_factory()
|
mock_session = credit_service.db_session_factory()
|
||||||
poor_user = User(
|
poor_user = User(
|
||||||
@@ -78,7 +78,7 @@ class TestCreditService:
|
|||||||
mock_session.close.assert_called_once()
|
mock_session.close.assert_called_once()
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@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."""
|
"""Test checking credits when user is not found."""
|
||||||
mock_session = credit_service.db_session_factory()
|
mock_session = credit_service.db_session_factory()
|
||||||
|
|
||||||
@@ -93,7 +93,7 @@ class TestCreditService:
|
|||||||
mock_session.close.assert_called_once()
|
mock_session.close.assert_called_once()
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@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."""
|
"""Test successful credit validation and reservation."""
|
||||||
mock_session = credit_service.db_session_factory()
|
mock_session = credit_service.db_session_factory()
|
||||||
|
|
||||||
@@ -112,7 +112,7 @@ class TestCreditService:
|
|||||||
mock_session.close.assert_called_once()
|
mock_session.close.assert_called_once()
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@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."""
|
"""Test credit validation with insufficient credits."""
|
||||||
mock_session = credit_service.db_session_factory()
|
mock_session = credit_service.db_session_factory()
|
||||||
poor_user = User(
|
poor_user = User(
|
||||||
@@ -139,7 +139,7 @@ class TestCreditService:
|
|||||||
mock_session.close.assert_called_once()
|
mock_session.close.assert_called_once()
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@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."""
|
"""Test credit validation when user is not found."""
|
||||||
mock_session = credit_service.db_session_factory()
|
mock_session = credit_service.db_session_factory()
|
||||||
|
|
||||||
@@ -156,7 +156,7 @@ class TestCreditService:
|
|||||||
mock_session.close.assert_called_once()
|
mock_session.close.assert_called_once()
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@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."""
|
"""Test successful credit deduction."""
|
||||||
mock_session = credit_service.db_session_factory()
|
mock_session = credit_service.db_session_factory()
|
||||||
|
|
||||||
@@ -167,7 +167,7 @@ class TestCreditService:
|
|||||||
mock_repo.get_by_id.return_value = sample_user
|
mock_repo.get_by_id.return_value = sample_user
|
||||||
mock_socket_manager.send_to_user = AsyncMock()
|
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"},
|
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"}
|
assert json.loads(added_transaction.metadata_json) == {"test": "data"}
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@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."""
|
"""Test credit deduction when action failed but requires success."""
|
||||||
mock_session = credit_service.db_session_factory()
|
mock_session = credit_service.db_session_factory()
|
||||||
|
|
||||||
@@ -213,7 +213,7 @@ class TestCreditService:
|
|||||||
mock_repo.get_by_id.return_value = sample_user
|
mock_repo.get_by_id.return_value = sample_user
|
||||||
mock_socket_manager.send_to_user = AsyncMock()
|
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
|
1, CreditActionType.VLC_PLAY_SOUND, success=False, # Action failed
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -235,7 +235,7 @@ class TestCreditService:
|
|||||||
assert added_transaction.success is False
|
assert added_transaction.success is False
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@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."""
|
"""Test credit deduction with insufficient credits."""
|
||||||
mock_session = credit_service.db_session_factory()
|
mock_session = credit_service.db_session_factory()
|
||||||
poor_user = User(
|
poor_user = User(
|
||||||
@@ -266,7 +266,7 @@ class TestCreditService:
|
|||||||
mock_session.close.assert_called_once()
|
mock_session.close.assert_called_once()
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@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."""
|
"""Test adding credits to user account."""
|
||||||
mock_session = credit_service.db_session_factory()
|
mock_session = credit_service.db_session_factory()
|
||||||
|
|
||||||
@@ -277,7 +277,7 @@ class TestCreditService:
|
|||||||
mock_repo.get_by_id.return_value = sample_user
|
mock_repo.get_by_id.return_value = sample_user
|
||||||
mock_socket_manager.send_to_user = AsyncMock()
|
mock_socket_manager.send_to_user = AsyncMock()
|
||||||
|
|
||||||
transaction = await credit_service.add_credits(
|
await credit_service.add_credits(
|
||||||
1, 5, "Bonus credits", {"reason": "signup"},
|
1, 5, "Bonus credits", {"reason": "signup"},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -308,7 +308,7 @@ class TestCreditService:
|
|||||||
assert added_transaction.description == "Bonus credits"
|
assert added_transaction.description == "Bonus credits"
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@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."""
|
"""Test adding invalid amount of credits."""
|
||||||
with pytest.raises(ValueError, match="Amount must be positive"):
|
with pytest.raises(ValueError, match="Amount must be positive"):
|
||||||
await credit_service.add_credits(1, 0, "Invalid")
|
await credit_service.add_credits(1, 0, "Invalid")
|
||||||
@@ -317,7 +317,7 @@ class TestCreditService:
|
|||||||
await credit_service.add_credits(1, -5, "Invalid")
|
await credit_service.add_credits(1, -5, "Invalid")
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@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."""
|
"""Test getting user credit balance."""
|
||||||
mock_session = credit_service.db_session_factory()
|
mock_session = credit_service.db_session_factory()
|
||||||
|
|
||||||
@@ -332,7 +332,7 @@ class TestCreditService:
|
|||||||
mock_session.close.assert_called_once()
|
mock_session.close.assert_called_once()
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@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."""
|
"""Test getting balance for non-existent user."""
|
||||||
mock_session = credit_service.db_session_factory()
|
mock_session = credit_service.db_session_factory()
|
||||||
|
|
||||||
@@ -350,7 +350,7 @@ class TestCreditService:
|
|||||||
class TestInsufficientCreditsError:
|
class TestInsufficientCreditsError:
|
||||||
"""Test InsufficientCreditsError exception."""
|
"""Test InsufficientCreditsError exception."""
|
||||||
|
|
||||||
def test_insufficient_credits_error_creation(self):
|
def test_insufficient_credits_error_creation(self) -> None:
|
||||||
"""Test creating InsufficientCreditsError."""
|
"""Test creating InsufficientCreditsError."""
|
||||||
error = InsufficientCreditsError(5, 2)
|
error = InsufficientCreditsError(5, 2)
|
||||||
assert error.required == 5
|
assert error.required == 5
|
||||||
|
|||||||
@@ -26,13 +26,13 @@ class TestExtractionService:
|
|||||||
with patch("app.services.extraction.Path.mkdir"):
|
with patch("app.services.extraction.Path.mkdir"):
|
||||||
return ExtractionService(mock_session)
|
return ExtractionService(mock_session)
|
||||||
|
|
||||||
def test_init(self, extraction_service):
|
def test_init(self, extraction_service) -> None:
|
||||||
"""Test service initialization."""
|
"""Test service initialization."""
|
||||||
assert extraction_service.session is not None
|
assert extraction_service.session is not None
|
||||||
assert extraction_service.extraction_repo is not None
|
assert extraction_service.extraction_repo is not None
|
||||||
assert extraction_service.sound_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 filename sanitization."""
|
||||||
test_cases = [
|
test_cases = [
|
||||||
("Hello World", "Hello World"),
|
("Hello World", "Hello World"),
|
||||||
@@ -54,7 +54,7 @@ class TestExtractionService:
|
|||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_detect_service_info_youtube(
|
async def test_detect_service_info_youtube(
|
||||||
self, mock_ydl_class, extraction_service,
|
self, mock_ydl_class, extraction_service,
|
||||||
):
|
) -> None:
|
||||||
"""Test service detection for YouTube."""
|
"""Test service detection for YouTube."""
|
||||||
mock_ydl = Mock()
|
mock_ydl = Mock()
|
||||||
mock_ydl_class.return_value.__enter__.return_value = mock_ydl
|
mock_ydl_class.return_value.__enter__.return_value = mock_ydl
|
||||||
@@ -79,7 +79,7 @@ class TestExtractionService:
|
|||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_detect_service_info_failure(
|
async def test_detect_service_info_failure(
|
||||||
self, mock_ydl_class, extraction_service,
|
self, mock_ydl_class, extraction_service,
|
||||||
):
|
) -> None:
|
||||||
"""Test service detection failure."""
|
"""Test service detection failure."""
|
||||||
mock_ydl = Mock()
|
mock_ydl = Mock()
|
||||||
mock_ydl_class.return_value.__enter__.return_value = mock_ydl
|
mock_ydl_class.return_value.__enter__.return_value = mock_ydl
|
||||||
@@ -90,7 +90,7 @@ class TestExtractionService:
|
|||||||
assert result is None
|
assert result is None
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@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."""
|
"""Test successful extraction creation."""
|
||||||
url = "https://www.youtube.com/watch?v=test123"
|
url = "https://www.youtube.com/watch?v=test123"
|
||||||
user_id = 1
|
user_id = 1
|
||||||
@@ -118,7 +118,7 @@ class TestExtractionService:
|
|||||||
assert result["status"] == "pending"
|
assert result["status"] == "pending"
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@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."""
|
"""Test basic extraction creation without validation."""
|
||||||
url = "https://www.youtube.com/watch?v=test123"
|
url = "https://www.youtube.com/watch?v=test123"
|
||||||
user_id = 1
|
user_id = 1
|
||||||
@@ -144,7 +144,7 @@ class TestExtractionService:
|
|||||||
assert result["status"] == "pending"
|
assert result["status"] == "pending"
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@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."""
|
"""Test extraction creation accepts any URL."""
|
||||||
url = "https://invalid.url"
|
url = "https://invalid.url"
|
||||||
user_id = 1
|
user_id = 1
|
||||||
@@ -170,7 +170,7 @@ class TestExtractionService:
|
|||||||
assert result["status"] == "pending"
|
assert result["status"] == "pending"
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@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."""
|
"""Test extraction processing with service detection."""
|
||||||
extraction_id = 1
|
extraction_id = 1
|
||||||
|
|
||||||
@@ -211,8 +211,8 @@ class TestExtractionService:
|
|||||||
patch.object(
|
patch.object(
|
||||||
extraction_service, "_create_sound_record",
|
extraction_service, "_create_sound_record",
|
||||||
) as mock_create_sound,
|
) as mock_create_sound,
|
||||||
patch.object(extraction_service, "_normalize_sound") as mock_normalize,
|
patch.object(extraction_service, "_normalize_sound"),
|
||||||
patch.object(extraction_service, "_add_to_main_playlist") as mock_playlist,
|
patch.object(extraction_service, "_add_to_main_playlist"),
|
||||||
):
|
):
|
||||||
mock_sound = Sound(id=42, type="EXT", name="Test", filename="test.mp3")
|
mock_sound = Sound(id=42, type="EXT", name="Test", filename="test.mp3")
|
||||||
mock_extract.return_value = (Path("/fake/audio.mp3"), None)
|
mock_extract.return_value = (Path("/fake/audio.mp3"), None)
|
||||||
@@ -234,7 +234,7 @@ class TestExtractionService:
|
|||||||
assert result["service_id"] == "test123"
|
assert result["service_id"] == "test123"
|
||||||
assert result["title"] == "Test Video"
|
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."""
|
"""Test unique filename generation."""
|
||||||
with tempfile.TemporaryDirectory() as temp_dir:
|
with tempfile.TemporaryDirectory() as temp_dir:
|
||||||
temp_path = Path(temp_dir)
|
temp_path = Path(temp_dir)
|
||||||
@@ -255,7 +255,7 @@ class TestExtractionService:
|
|||||||
assert result == expected_2
|
assert result == expected_2
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@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."""
|
"""Test sound record creation."""
|
||||||
# Create temporary audio file
|
# Create temporary audio file
|
||||||
with tempfile.NamedTemporaryFile(suffix=".mp3", delete=False) as f:
|
with tempfile.NamedTemporaryFile(suffix=".mp3", delete=False) as f:
|
||||||
@@ -317,7 +317,7 @@ class TestExtractionService:
|
|||||||
audio_path.unlink()
|
audio_path.unlink()
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@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."""
|
"""Test sound normalization."""
|
||||||
sound = Sound(
|
sound = Sound(
|
||||||
id=1,
|
id=1,
|
||||||
@@ -349,7 +349,7 @@ class TestExtractionService:
|
|||||||
mock_normalizer.normalize_sound.assert_called_once_with(sound)
|
mock_normalizer.normalize_sound.assert_called_once_with(sound)
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@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."""
|
"""Test sound normalization failure."""
|
||||||
sound = Sound(
|
sound = Sound(
|
||||||
id=1,
|
id=1,
|
||||||
@@ -381,7 +381,7 @@ class TestExtractionService:
|
|||||||
mock_normalizer.normalize_sound.assert_called_once_with(sound)
|
mock_normalizer.normalize_sound.assert_called_once_with(sound)
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@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."""
|
"""Test getting extraction by ID."""
|
||||||
extraction = Extraction(
|
extraction = Extraction(
|
||||||
id=1,
|
id=1,
|
||||||
@@ -409,7 +409,7 @@ class TestExtractionService:
|
|||||||
assert result["sound_id"] == 42
|
assert result["sound_id"] == 42
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@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."""
|
"""Test getting extraction by ID when not found."""
|
||||||
extraction_service.extraction_repo.get_by_id = AsyncMock(return_value=None)
|
extraction_service.extraction_repo.get_by_id = AsyncMock(return_value=None)
|
||||||
|
|
||||||
@@ -418,7 +418,7 @@ class TestExtractionService:
|
|||||||
assert result is None
|
assert result is None
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@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."""
|
"""Test getting user extractions."""
|
||||||
extractions = [
|
extractions = [
|
||||||
Extraction(
|
Extraction(
|
||||||
@@ -455,7 +455,7 @@ class TestExtractionService:
|
|||||||
assert result[1]["title"] == "Test Video 2"
|
assert result[1]["title"] == "Test Video 2"
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@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."""
|
"""Test getting pending extractions."""
|
||||||
pending_extractions = [
|
pending_extractions = [
|
||||||
Extraction(
|
Extraction(
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ class TestExtractionProcessor:
|
|||||||
# Use a custom processor instance to avoid affecting the global one
|
# Use a custom processor instance to avoid affecting the global one
|
||||||
return ExtractionProcessor()
|
return ExtractionProcessor()
|
||||||
|
|
||||||
def test_init(self, processor):
|
def test_init(self, processor) -> None:
|
||||||
"""Test processor initialization."""
|
"""Test processor initialization."""
|
||||||
assert processor.max_concurrent > 0
|
assert processor.max_concurrent > 0
|
||||||
assert len(processor.running_extractions) == 0
|
assert len(processor.running_extractions) == 0
|
||||||
@@ -25,12 +25,12 @@ class TestExtractionProcessor:
|
|||||||
assert processor.processor_task is None
|
assert processor.processor_task is None
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@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."""
|
"""Test starting and stopping the processor."""
|
||||||
# Mock the _process_queue method to avoid actual processing
|
# Mock the _process_queue method to avoid actual processing
|
||||||
with patch.object(
|
with patch.object(
|
||||||
processor, "_process_queue", new_callable=AsyncMock,
|
processor, "_process_queue", new_callable=AsyncMock,
|
||||||
) as mock_process:
|
):
|
||||||
# Start the processor
|
# Start the processor
|
||||||
await processor.start()
|
await processor.start()
|
||||||
assert processor.processor_task is not None
|
assert processor.processor_task is not None
|
||||||
@@ -41,7 +41,7 @@ class TestExtractionProcessor:
|
|||||||
assert processor.processor_task.done()
|
assert processor.processor_task.done()
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@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."""
|
"""Test starting processor when already running."""
|
||||||
with patch.object(processor, "_process_queue", new_callable=AsyncMock):
|
with patch.object(processor, "_process_queue", new_callable=AsyncMock):
|
||||||
# Start first time
|
# Start first time
|
||||||
@@ -56,7 +56,7 @@ class TestExtractionProcessor:
|
|||||||
await processor.stop()
|
await processor.stop()
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_queue_extraction(self, processor):
|
async def test_queue_extraction(self, processor) -> None:
|
||||||
"""Test queuing an extraction."""
|
"""Test queuing an extraction."""
|
||||||
extraction_id = 123
|
extraction_id = 123
|
||||||
|
|
||||||
@@ -66,7 +66,7 @@ class TestExtractionProcessor:
|
|||||||
assert extraction_id not in processor.running_extractions
|
assert extraction_id not in processor.running_extractions
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@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."""
|
"""Test queuing an extraction that's already running."""
|
||||||
extraction_id = 123
|
extraction_id = 123
|
||||||
processor.running_extractions.add(extraction_id)
|
processor.running_extractions.add(extraction_id)
|
||||||
@@ -75,7 +75,7 @@ class TestExtractionProcessor:
|
|||||||
# Should still be in running extractions
|
# Should still be in running extractions
|
||||||
assert extraction_id in processor.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."""
|
"""Test getting processor status."""
|
||||||
status = processor.get_status()
|
status = processor.get_status()
|
||||||
|
|
||||||
@@ -89,7 +89,7 @@ class TestExtractionProcessor:
|
|||||||
assert status["currently_processing"] == 0
|
assert status["currently_processing"] == 0
|
||||||
assert status["available_slots"] == processor.max_concurrent
|
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."""
|
"""Test getting processor status with running extractions."""
|
||||||
processor.running_extractions.add(123)
|
processor.running_extractions.add(123)
|
||||||
processor.running_extractions.add(456)
|
processor.running_extractions.add(456)
|
||||||
@@ -101,7 +101,7 @@ class TestExtractionProcessor:
|
|||||||
assert 123 in status["processing_ids"]
|
assert 123 in status["processing_ids"]
|
||||||
assert 456 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."""
|
"""Test extraction completion callback."""
|
||||||
extraction_id = 123
|
extraction_id = 123
|
||||||
processor.running_extractions.add(extraction_id)
|
processor.running_extractions.add(extraction_id)
|
||||||
@@ -115,7 +115,7 @@ class TestExtractionProcessor:
|
|||||||
# Should be removed from running extractions
|
# Should be removed from running extractions
|
||||||
assert extraction_id not in processor.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."""
|
"""Test extraction completion callback with exception."""
|
||||||
extraction_id = 123
|
extraction_id = 123
|
||||||
processor.running_extractions.add(extraction_id)
|
processor.running_extractions.add(extraction_id)
|
||||||
@@ -130,7 +130,7 @@ class TestExtractionProcessor:
|
|||||||
assert extraction_id not in processor.running_extractions
|
assert extraction_id not in processor.running_extractions
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@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."""
|
"""Test processing a single extraction successfully."""
|
||||||
extraction_id = 123
|
extraction_id = 123
|
||||||
|
|
||||||
@@ -157,7 +157,7 @@ class TestExtractionProcessor:
|
|||||||
mock_service.process_extraction.assert_called_once_with(extraction_id)
|
mock_service.process_extraction.assert_called_once_with(extraction_id)
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@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."""
|
"""Test processing a single extraction with failure."""
|
||||||
extraction_id = 123
|
extraction_id = 123
|
||||||
|
|
||||||
@@ -183,7 +183,7 @@ class TestExtractionProcessor:
|
|||||||
mock_service.process_extraction.assert_called_once_with(extraction_id)
|
mock_service.process_extraction.assert_called_once_with(extraction_id)
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@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."""
|
"""Test processing when no slots are available."""
|
||||||
# Fill all slots
|
# Fill all slots
|
||||||
for i in range(processor.max_concurrent):
|
for i in range(processor.max_concurrent):
|
||||||
@@ -213,7 +213,7 @@ class TestExtractionProcessor:
|
|||||||
assert 100 not in processor.running_extractions
|
assert 100 not in processor.running_extractions
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@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."""
|
"""Test processing when slots are available."""
|
||||||
# Mock extraction service
|
# Mock extraction service
|
||||||
mock_service = Mock()
|
mock_service = Mock()
|
||||||
@@ -230,7 +230,7 @@ class TestExtractionProcessor:
|
|||||||
) as mock_session_class,
|
) as mock_session_class,
|
||||||
patch.object(
|
patch.object(
|
||||||
processor, "_process_single_extraction", new_callable=AsyncMock,
|
processor, "_process_single_extraction", new_callable=AsyncMock,
|
||||||
) as mock_process,
|
),
|
||||||
patch(
|
patch(
|
||||||
"app.services.extraction_processor.ExtractionService",
|
"app.services.extraction_processor.ExtractionService",
|
||||||
return_value=mock_service,
|
return_value=mock_service,
|
||||||
@@ -254,7 +254,7 @@ class TestExtractionProcessor:
|
|||||||
assert mock_create_task.call_count == 2
|
assert mock_create_task.call_count == 2
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@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."""
|
"""Test that processing respects concurrency limit."""
|
||||||
# Set max concurrent to 1 for this test
|
# Set max concurrent to 1 for this test
|
||||||
processor.max_concurrent = 1
|
processor.max_concurrent = 1
|
||||||
@@ -275,7 +275,7 @@ class TestExtractionProcessor:
|
|||||||
) as mock_session_class,
|
) as mock_session_class,
|
||||||
patch.object(
|
patch.object(
|
||||||
processor, "_process_single_extraction", new_callable=AsyncMock,
|
processor, "_process_single_extraction", new_callable=AsyncMock,
|
||||||
) as mock_process,
|
),
|
||||||
patch(
|
patch(
|
||||||
"app.services.extraction_processor.ExtractionService",
|
"app.services.extraction_processor.ExtractionService",
|
||||||
return_value=mock_service,
|
return_value=mock_service,
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ from app.services.player import (
|
|||||||
class TestPlayerState:
|
class TestPlayerState:
|
||||||
"""Test player state data structure."""
|
"""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."""
|
"""Test that player state initializes with default values."""
|
||||||
state = PlayerState()
|
state = PlayerState()
|
||||||
|
|
||||||
@@ -42,7 +42,7 @@ class TestPlayerState:
|
|||||||
assert state.playlist_duration == 0
|
assert state.playlist_duration == 0
|
||||||
assert state.playlist_sounds == []
|
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."""
|
"""Test that player state serializes to dict correctly."""
|
||||||
state = PlayerState()
|
state = PlayerState()
|
||||||
state.status = PlayerStatus.PLAYING
|
state.status = PlayerStatus.PLAYING
|
||||||
@@ -70,7 +70,7 @@ class TestPlayerState:
|
|||||||
assert result["playlist"]["length"] == 5
|
assert result["playlist"]["length"] == 5
|
||||||
assert result["playlist"]["duration"] == 150000
|
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."""
|
"""Test serializing a sound object."""
|
||||||
state = PlayerState()
|
state = PlayerState()
|
||||||
sound = Sound(
|
sound = Sound(
|
||||||
@@ -95,7 +95,7 @@ class TestPlayerState:
|
|||||||
assert result["thumbnail"] == "test.jpg"
|
assert result["thumbnail"] == "test.jpg"
|
||||||
assert result["play_count"] == 5
|
assert result["play_count"] == 5
|
||||||
|
|
||||||
def test_serialize_sound_with_none(self):
|
def test_serialize_sound_with_none(self) -> None:
|
||||||
"""Test serializing None sound."""
|
"""Test serializing None sound."""
|
||||||
state = PlayerState()
|
state = PlayerState()
|
||||||
result = state._serialize_sound(None)
|
result = state._serialize_sound(None)
|
||||||
@@ -133,10 +133,9 @@ class TestPlayerService:
|
|||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def player_service(self, mock_db_session_factory, mock_vlc_instance, mock_socket_manager):
|
def player_service(self, mock_db_session_factory, mock_vlc_instance, mock_socket_manager):
|
||||||
"""Create a player service instance for testing."""
|
"""Create a player service instance for testing."""
|
||||||
service = PlayerService(mock_db_session_factory)
|
return PlayerService(mock_db_session_factory)
|
||||||
return service
|
|
||||||
|
|
||||||
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."""
|
"""Test that player service initializes correctly."""
|
||||||
with patch("app.services.player.socket_manager"):
|
with patch("app.services.player.socket_manager"):
|
||||||
service = PlayerService(mock_db_session_factory)
|
service = PlayerService(mock_db_session_factory)
|
||||||
@@ -153,7 +152,7 @@ class TestPlayerService:
|
|||||||
assert service._loop is None
|
assert service._loop is None
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@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."""
|
"""Test that start method initializes the service."""
|
||||||
with patch.object(player_service, "reload_playlist", new_callable=AsyncMock):
|
with patch.object(player_service, "reload_playlist", new_callable=AsyncMock):
|
||||||
await player_service.start()
|
await player_service.start()
|
||||||
@@ -165,7 +164,7 @@ class TestPlayerService:
|
|||||||
player_service._player.audio_set_volume.assert_called_once_with(50)
|
player_service._player.audio_set_volume.assert_called_once_with(50)
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@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."""
|
"""Test that stop method cleans up the service."""
|
||||||
# Setup initial state
|
# Setup initial state
|
||||||
player_service._is_running = True
|
player_service._is_running = True
|
||||||
@@ -180,7 +179,7 @@ class TestPlayerService:
|
|||||||
player_service._player.release.assert_called_once()
|
player_service._player.release.assert_called_once()
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@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."""
|
"""Test playing a new track."""
|
||||||
# Setup test sound
|
# Setup test sound
|
||||||
sound = Sound(
|
sound = Sound(
|
||||||
@@ -212,7 +211,7 @@ class TestPlayerService:
|
|||||||
assert 1 in player_service._play_time_tracking
|
assert 1 in player_service._play_time_tracking
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@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."""
|
"""Test resuming playback from pause."""
|
||||||
# Setup paused state
|
# Setup paused state
|
||||||
sound = Sound(id=1, name="Test Song", filename="test.mp3", duration=30000)
|
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()
|
player_service._player.play.assert_called_once()
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@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."""
|
"""Test playing with invalid index raises ValueError."""
|
||||||
player_service.state.playlist_sounds = []
|
player_service.state.playlist_sounds = []
|
||||||
|
|
||||||
@@ -238,7 +237,7 @@ class TestPlayerService:
|
|||||||
await player_service.play(0)
|
await player_service.play(0)
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@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."""
|
"""Test pausing when currently playing."""
|
||||||
player_service.state.status = PlayerStatus.PLAYING
|
player_service.state.status = PlayerStatus.PLAYING
|
||||||
|
|
||||||
@@ -249,7 +248,7 @@ class TestPlayerService:
|
|||||||
player_service._player.pause.assert_called_once()
|
player_service._player.pause.assert_called_once()
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@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."""
|
"""Test pausing when not playing does nothing."""
|
||||||
player_service.state.status = PlayerStatus.STOPPED
|
player_service.state.status = PlayerStatus.STOPPED
|
||||||
|
|
||||||
@@ -260,7 +259,7 @@ class TestPlayerService:
|
|||||||
mock_broadcast.assert_not_called()
|
mock_broadcast.assert_not_called()
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_stop_playback(self, player_service):
|
async def test_stop_playback(self, player_service) -> None:
|
||||||
"""Test stopping playback."""
|
"""Test stopping playback."""
|
||||||
player_service.state.status = PlayerStatus.PLAYING
|
player_service.state.status = PlayerStatus.PLAYING
|
||||||
player_service.state.current_sound_position = 5000
|
player_service.state.current_sound_position = 5000
|
||||||
@@ -274,7 +273,7 @@ class TestPlayerService:
|
|||||||
player_service._player.stop.assert_called_once()
|
player_service._player.stop.assert_called_once()
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@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."""
|
"""Test skipping to next track."""
|
||||||
sound1 = Sound(id=1, name="Song 1", filename="song1.mp3")
|
sound1 = Sound(id=1, name="Song 1", filename="song1.mp3")
|
||||||
sound2 = Sound(id=2, name="Song 2", filename="song2.mp3")
|
sound2 = Sound(id=2, name="Song 2", filename="song2.mp3")
|
||||||
@@ -286,7 +285,7 @@ class TestPlayerService:
|
|||||||
mock_play.assert_called_once_with(1)
|
mock_play.assert_called_once_with(1)
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@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."""
|
"""Test going to previous track."""
|
||||||
sound1 = Sound(id=1, name="Song 1", filename="song1.mp3")
|
sound1 = Sound(id=1, name="Song 1", filename="song1.mp3")
|
||||||
sound2 = Sound(id=2, name="Song 2", filename="song2.mp3")
|
sound2 = Sound(id=2, name="Song 2", filename="song2.mp3")
|
||||||
@@ -298,7 +297,7 @@ class TestPlayerService:
|
|||||||
mock_play.assert_called_once_with(0)
|
mock_play.assert_called_once_with(0)
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@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."""
|
"""Test seeking to specific position."""
|
||||||
player_service.state.status = PlayerStatus.PLAYING
|
player_service.state.status = PlayerStatus.PLAYING
|
||||||
player_service.state.current_sound_duration = 30000
|
player_service.state.current_sound_duration = 30000
|
||||||
@@ -311,7 +310,7 @@ class TestPlayerService:
|
|||||||
assert player_service.state.current_sound_position == 15000
|
assert player_service.state.current_sound_position == 15000
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@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."""
|
"""Test seeking when stopped does nothing."""
|
||||||
player_service.state.status = PlayerStatus.STOPPED
|
player_service.state.status = PlayerStatus.STOPPED
|
||||||
|
|
||||||
@@ -322,7 +321,7 @@ class TestPlayerService:
|
|||||||
mock_broadcast.assert_not_called()
|
mock_broadcast.assert_not_called()
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_set_volume(self, player_service):
|
async def test_set_volume(self, player_service) -> None:
|
||||||
"""Test setting volume."""
|
"""Test setting volume."""
|
||||||
with patch.object(player_service, "_broadcast_state", new_callable=AsyncMock):
|
with patch.object(player_service, "_broadcast_state", new_callable=AsyncMock):
|
||||||
await player_service.set_volume(75)
|
await player_service.set_volume(75)
|
||||||
@@ -331,7 +330,7 @@ class TestPlayerService:
|
|||||||
player_service._player.audio_set_volume.assert_called_once_with(75)
|
player_service._player.audio_set_volume.assert_called_once_with(75)
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@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."""
|
"""Test volume clamping to valid range."""
|
||||||
with patch.object(player_service, "_broadcast_state", new_callable=AsyncMock):
|
with patch.object(player_service, "_broadcast_state", new_callable=AsyncMock):
|
||||||
# Test upper bound
|
# Test upper bound
|
||||||
@@ -343,7 +342,7 @@ class TestPlayerService:
|
|||||||
assert player_service.state.volume == 0
|
assert player_service.state.volume == 0
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_set_mode(self, player_service):
|
async def test_set_mode(self, player_service) -> None:
|
||||||
"""Test setting playback mode."""
|
"""Test setting playback mode."""
|
||||||
with patch.object(player_service, "_broadcast_state", new_callable=AsyncMock):
|
with patch.object(player_service, "_broadcast_state", new_callable=AsyncMock):
|
||||||
await player_service.set_mode(PlayerMode.LOOP)
|
await player_service.set_mode(PlayerMode.LOOP)
|
||||||
@@ -351,7 +350,7 @@ class TestPlayerService:
|
|||||||
assert player_service.state.mode == PlayerMode.LOOP
|
assert player_service.state.mode == PlayerMode.LOOP
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@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."""
|
"""Test reloading playlist from database."""
|
||||||
mock_session = AsyncMock()
|
mock_session = AsyncMock()
|
||||||
player_service.db_session_factory = lambda: mock_session
|
player_service.db_session_factory = lambda: mock_session
|
||||||
@@ -383,7 +382,7 @@ class TestPlayerService:
|
|||||||
assert player_service.state.playlist_duration == 75000
|
assert player_service.state.playlist_duration == 75000
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@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."""
|
"""Test handling when playlist ID changes."""
|
||||||
# Setup initial state
|
# Setup initial state
|
||||||
player_service.state.status = PlayerStatus.PLAYING
|
player_service.state.status = PlayerStatus.PLAYING
|
||||||
@@ -405,7 +404,7 @@ class TestPlayerService:
|
|||||||
assert player_service.state.current_sound_id == 1
|
assert player_service.state.current_sound_id == 1
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@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."""
|
"""Test handling playlist ID change with empty playlist."""
|
||||||
player_service.state.status = PlayerStatus.PLAYING
|
player_service.state.status = PlayerStatus.PLAYING
|
||||||
|
|
||||||
@@ -418,7 +417,7 @@ class TestPlayerService:
|
|||||||
assert player_service.state.current_sound_id is None
|
assert player_service.state.current_sound_id is None
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@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."""
|
"""Test handling same playlist when track exists at same index."""
|
||||||
sound1 = Sound(id=1, name="Song 1", filename="song1.mp3", duration=30000)
|
sound1 = Sound(id=1, name="Song 1", filename="song1.mp3", duration=30000)
|
||||||
sound2 = Sound(id=2, name="Song 2", filename="song2.mp3", duration=45000)
|
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
|
assert player_service.state.current_sound == sound1
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@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."""
|
"""Test handling same playlist when track exists at different index."""
|
||||||
sound1 = Sound(id=2, name="Song 2", filename="song2.mp3", duration=45000)
|
sound1 = Sound(id=2, name="Song 2", filename="song2.mp3", duration=45000)
|
||||||
sound2 = Sound(id=1, name="Song 1", filename="song1.mp3", duration=30000)
|
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
|
assert player_service.state.current_sound == sound2
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@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."""
|
"""Test handling same playlist when current track no longer exists."""
|
||||||
player_service.state.status = PlayerStatus.PLAYING
|
player_service.state.status = PlayerStatus.PLAYING
|
||||||
sound1 = Sound(id=2, name="Song 2", filename="song2.mp3", duration=45000)
|
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)
|
mock_removed.assert_called_once_with(1, sounds)
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@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."""
|
"""Test handling when current track is removed with sounds available."""
|
||||||
player_service.state.status = PlayerStatus.PLAYING
|
player_service.state.status = PlayerStatus.PLAYING
|
||||||
sound1 = Sound(id=2, name="Song 2", filename="song2.mp3", duration=45000)
|
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
|
assert player_service.state.current_sound_id == 2
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@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."""
|
"""Test handling when current track is removed with empty playlist."""
|
||||||
player_service.state.status = PlayerStatus.PLAYING
|
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 is None
|
||||||
assert player_service.state.current_sound_id 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."""
|
"""Test updating playlist state information."""
|
||||||
mock_playlist = Mock()
|
mock_playlist = Mock()
|
||||||
mock_playlist.id = 5
|
mock_playlist.id = 5
|
||||||
@@ -501,7 +500,7 @@ class TestPlayerService:
|
|||||||
assert player_service.state.playlist_length == 2
|
assert player_service.state.playlist_length == 2
|
||||||
assert player_service.state.playlist_duration == 75000
|
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."""
|
"""Test finding sound index when sound exists."""
|
||||||
sound1 = Sound(id=1, name="Song 1", filename="song1.mp3", duration=30000)
|
sound1 = Sound(id=1, name="Song 1", filename="song1.mp3", duration=30000)
|
||||||
sound2 = Sound(id=2, name="Song 2", filename="song2.mp3", duration=45000)
|
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)
|
index = player_service._find_sound_index(2, sounds)
|
||||||
assert index == 1
|
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."""
|
"""Test finding sound index when sound doesn't exist."""
|
||||||
sound1 = Sound(id=1, name="Song 1", filename="song1.mp3", duration=30000)
|
sound1 = Sound(id=1, name="Song 1", filename="song1.mp3", duration=30000)
|
||||||
sounds = [sound1]
|
sounds = [sound1]
|
||||||
@@ -518,7 +517,7 @@ class TestPlayerService:
|
|||||||
index = player_service._find_sound_index(999, sounds)
|
index = player_service._find_sound_index(999, sounds)
|
||||||
assert index is None
|
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."""
|
"""Test setting first track as current."""
|
||||||
sound1 = Sound(id=1, name="Song 1", filename="song1.mp3", duration=30000)
|
sound1 = Sound(id=1, name="Song 1", filename="song1.mp3", duration=30000)
|
||||||
sound2 = Sound(id=2, name="Song 2", filename="song2.mp3", duration=45000)
|
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 == sound1
|
||||||
assert player_service.state.current_sound_id == 1
|
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."""
|
"""Test clearing current track state."""
|
||||||
# Set some initial state
|
# Set some initial state
|
||||||
player_service.state.current_sound_index = 2
|
player_service.state.current_sound_index = 2
|
||||||
@@ -544,7 +543,7 @@ class TestPlayerService:
|
|||||||
assert player_service.state.current_sound_id is None
|
assert player_service.state.current_sound_id is None
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@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."""
|
"""Test complete reload scenario when playlist ID changes."""
|
||||||
# Setup current state
|
# Setup current state
|
||||||
player_service.state.playlist_id = 1
|
player_service.state.playlist_id = 1
|
||||||
@@ -580,7 +579,7 @@ class TestPlayerService:
|
|||||||
assert player_service.state.current_sound_id == 1
|
assert player_service.state.current_sound_id == 1
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@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."""
|
"""Test reload when playlist ID same but track moved to different index."""
|
||||||
# Setup current state
|
# Setup current state
|
||||||
player_service.state.playlist_id = 1
|
player_service.state.playlist_id = 1
|
||||||
@@ -616,7 +615,7 @@ class TestPlayerService:
|
|||||||
assert player_service.state.current_sound == sound1
|
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."""
|
"""Test getting next index in continuous mode."""
|
||||||
player_service.state.mode = PlayerMode.CONTINUOUS
|
player_service.state.mode = PlayerMode.CONTINUOUS
|
||||||
player_service.state.playlist_sounds = [Mock(), Mock(), Mock()]
|
player_service.state.playlist_sounds = [Mock(), Mock(), Mock()]
|
||||||
@@ -628,7 +627,7 @@ class TestPlayerService:
|
|||||||
# Test end of playlist
|
# Test end of playlist
|
||||||
assert player_service._get_next_index(2) is None
|
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."""
|
"""Test getting next index in loop mode."""
|
||||||
player_service.state.mode = PlayerMode.LOOP
|
player_service.state.mode = PlayerMode.LOOP
|
||||||
player_service.state.playlist_sounds = [Mock(), Mock(), Mock()]
|
player_service.state.playlist_sounds = [Mock(), Mock(), Mock()]
|
||||||
@@ -640,7 +639,7 @@ class TestPlayerService:
|
|||||||
# Test wrapping to beginning
|
# Test wrapping to beginning
|
||||||
assert player_service._get_next_index(2) == 0
|
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."""
|
"""Test getting next index in loop one mode."""
|
||||||
player_service.state.mode = PlayerMode.LOOP_ONE
|
player_service.state.mode = PlayerMode.LOOP_ONE
|
||||||
player_service.state.playlist_sounds = [Mock(), Mock(), Mock()]
|
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(1) == 1
|
||||||
assert player_service._get_next_index(2) == 2
|
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."""
|
"""Test getting next index in single mode."""
|
||||||
player_service.state.mode = PlayerMode.SINGLE
|
player_service.state.mode = PlayerMode.SINGLE
|
||||||
player_service.state.playlist_sounds = [Mock(), Mock(), Mock()]
|
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(1) is None
|
||||||
assert player_service._get_next_index(2) 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."""
|
"""Test getting next index in random mode."""
|
||||||
player_service.state.mode = PlayerMode.RANDOM
|
player_service.state.mode = PlayerMode.RANDOM
|
||||||
player_service.state.playlist_sounds = [Mock(), Mock(), Mock()]
|
player_service.state.playlist_sounds = [Mock(), Mock(), Mock()]
|
||||||
@@ -674,7 +673,7 @@ class TestPlayerService:
|
|||||||
# Should exclude current index
|
# Should exclude current index
|
||||||
mock_choice.assert_called_once_with([1, 2])
|
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."""
|
"""Test getting previous index."""
|
||||||
player_service.state.playlist_sounds = [Mock(), Mock(), Mock()]
|
player_service.state.playlist_sounds = [Mock(), Mock(), Mock()]
|
||||||
|
|
||||||
@@ -690,7 +689,7 @@ class TestPlayerService:
|
|||||||
player_service.state.mode = PlayerMode.LOOP
|
player_service.state.mode = PlayerMode.LOOP
|
||||||
assert player_service._get_previous_index(0) == 2
|
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."""
|
"""Test updating play time tracking."""
|
||||||
# Setup state
|
# Setup state
|
||||||
player_service.state.status = PlayerStatus.PLAYING
|
player_service.state.status = PlayerStatus.PLAYING
|
||||||
@@ -716,7 +715,7 @@ class TestPlayerService:
|
|||||||
assert tracking["last_update"] == current_time
|
assert tracking["last_update"] == current_time
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@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."""
|
"""Test recording play count for a sound."""
|
||||||
mock_session = AsyncMock()
|
mock_session = AsyncMock()
|
||||||
player_service.db_session_factory = lambda: mock_session
|
player_service.db_session_factory = lambda: mock_session
|
||||||
@@ -742,7 +741,7 @@ class TestPlayerService:
|
|||||||
mock_session.add.assert_called_once()
|
mock_session.add.assert_called_once()
|
||||||
mock_session.commit.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."""
|
"""Test getting current player state."""
|
||||||
result = player_service.get_state()
|
result = player_service.get_state()
|
||||||
assert isinstance(result, dict)
|
assert isinstance(result, dict)
|
||||||
@@ -750,7 +749,7 @@ class TestPlayerService:
|
|||||||
assert "mode" in result
|
assert "mode" in result
|
||||||
assert "volume" 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."""
|
"""Test that player service uses the shared sound path utility."""
|
||||||
sound = Sound(
|
sound = Sound(
|
||||||
id=1,
|
id=1,
|
||||||
@@ -767,7 +766,7 @@ class TestPlayerService:
|
|||||||
mock_path.return_value = mock_file_path
|
mock_path.return_value = mock_file_path
|
||||||
|
|
||||||
# This should fail because file doesn't exist
|
# 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
|
# Verify the utility was called
|
||||||
mock_path.assert_called_once_with(sound)
|
mock_path.assert_called_once_with(sound)
|
||||||
|
|
||||||
@@ -776,7 +775,7 @@ class TestPlayerServiceGlobalFunctions:
|
|||||||
"""Test global player service functions."""
|
"""Test global player service functions."""
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_initialize_player_service(self):
|
async def test_initialize_player_service(self) -> None:
|
||||||
"""Test initializing global player service."""
|
"""Test initializing global player service."""
|
||||||
mock_factory = Mock()
|
mock_factory = Mock()
|
||||||
|
|
||||||
@@ -790,7 +789,7 @@ class TestPlayerServiceGlobalFunctions:
|
|||||||
mock_service.start.assert_called_once()
|
mock_service.start.assert_called_once()
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_shutdown_player_service(self):
|
async def test_shutdown_player_service(self) -> None:
|
||||||
"""Test shutting down global player service."""
|
"""Test shutting down global player service."""
|
||||||
# Mock global player service exists
|
# Mock global player service exists
|
||||||
with patch("app.services.player.player_service") as mock_global:
|
with patch("app.services.player.player_service") as mock_global:
|
||||||
@@ -802,7 +801,7 @@ class TestPlayerServiceGlobalFunctions:
|
|||||||
await shutdown_player_service()
|
await shutdown_player_service()
|
||||||
mock_service.stop.assert_called_once()
|
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."""
|
"""Test getting player service when initialized."""
|
||||||
mock_service = Mock()
|
mock_service = Mock()
|
||||||
|
|
||||||
@@ -810,7 +809,7 @@ class TestPlayerServiceGlobalFunctions:
|
|||||||
result = get_player_service()
|
result = get_player_service()
|
||||||
assert result is mock_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."""
|
"""Test getting player service when not initialized."""
|
||||||
with patch("app.services.player.player_service", None):
|
with patch("app.services.player.player_service", None):
|
||||||
with pytest.raises(RuntimeError, match="Player service not initialized"):
|
with pytest.raises(RuntimeError, match="Player service not initialized"):
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ class TestSocketManager:
|
|||||||
socket_manager.sio = AsyncMock(spec=socketio.AsyncServer)
|
socket_manager.sio = AsyncMock(spec=socketio.AsyncServer)
|
||||||
return socket_manager.sio
|
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."""
|
"""Test that socket manager initializes with proper configuration."""
|
||||||
manager = SocketManager()
|
manager = SocketManager()
|
||||||
|
|
||||||
@@ -33,7 +33,7 @@ class TestSocketManager:
|
|||||||
assert len(manager.socket_users) == 0
|
assert len(manager.socket_users) == 0
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@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."""
|
"""Test sending message to connected user."""
|
||||||
user_id = "123"
|
user_id = "123"
|
||||||
room_id = "user_123"
|
room_id = "user_123"
|
||||||
@@ -48,7 +48,7 @@ class TestSocketManager:
|
|||||||
mock_sio.emit.assert_called_once_with(event, data, room=room_id)
|
mock_sio.emit.assert_called_once_with(event, data, room=room_id)
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@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."""
|
"""Test sending message to user who is not connected."""
|
||||||
user_id = "999"
|
user_id = "999"
|
||||||
event = "test_event"
|
event = "test_event"
|
||||||
@@ -60,7 +60,7 @@ class TestSocketManager:
|
|||||||
mock_sio.emit.assert_not_called()
|
mock_sio.emit.assert_not_called()
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@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."""
|
"""Test broadcasting message to all users."""
|
||||||
event = "broadcast_event"
|
event = "broadcast_event"
|
||||||
data = {"message": "announcement"}
|
data = {"message": "announcement"}
|
||||||
@@ -69,7 +69,7 @@ class TestSocketManager:
|
|||||||
|
|
||||||
mock_sio.emit.assert_called_once_with(event, data)
|
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."""
|
"""Test getting list of connected users."""
|
||||||
# Add some users
|
# Add some users
|
||||||
socket_manager.user_rooms["1"] = "user_1"
|
socket_manager.user_rooms["1"] = "user_1"
|
||||||
@@ -83,7 +83,7 @@ class TestSocketManager:
|
|||||||
assert "2" in connected_users
|
assert "2" in connected_users
|
||||||
assert "3" 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."""
|
"""Test getting room information."""
|
||||||
# Add some users
|
# Add some users
|
||||||
socket_manager.user_rooms["1"] = "user_1"
|
socket_manager.user_rooms["1"] = "user_1"
|
||||||
@@ -99,7 +99,7 @@ class TestSocketManager:
|
|||||||
@patch("app.services.socket.JWTUtils.decode_access_token")
|
@patch("app.services.socket.JWTUtils.decode_access_token")
|
||||||
async def test_connect_handler_success(
|
async def test_connect_handler_success(
|
||||||
self, mock_decode, mock_extract_token, socket_manager, mock_sio,
|
self, mock_decode, mock_extract_token, socket_manager, mock_sio,
|
||||||
):
|
) -> None:
|
||||||
"""Test successful connection with valid token."""
|
"""Test successful connection with valid token."""
|
||||||
# Setup mocks
|
# Setup mocks
|
||||||
mock_extract_token.return_value = "valid_token"
|
mock_extract_token.return_value = "valid_token"
|
||||||
@@ -110,7 +110,6 @@ class TestSocketManager:
|
|||||||
|
|
||||||
# Access the connect handler directly
|
# Access the connect handler directly
|
||||||
handlers = {}
|
handlers = {}
|
||||||
original_event = socket_manager.sio.event
|
|
||||||
|
|
||||||
def mock_event(func):
|
def mock_event(func):
|
||||||
handlers[func.__name__] = func
|
handlers[func.__name__] = func
|
||||||
@@ -134,7 +133,7 @@ class TestSocketManager:
|
|||||||
@patch("app.services.socket.extract_access_token_from_cookies")
|
@patch("app.services.socket.extract_access_token_from_cookies")
|
||||||
async def test_connect_handler_no_token(
|
async def test_connect_handler_no_token(
|
||||||
self, mock_extract_token, socket_manager, mock_sio,
|
self, mock_extract_token, socket_manager, mock_sio,
|
||||||
):
|
) -> None:
|
||||||
"""Test connection with no access token."""
|
"""Test connection with no access token."""
|
||||||
# Setup mocks
|
# Setup mocks
|
||||||
mock_extract_token.return_value = None
|
mock_extract_token.return_value = None
|
||||||
@@ -144,7 +143,6 @@ class TestSocketManager:
|
|||||||
|
|
||||||
# Access the connect handler directly
|
# Access the connect handler directly
|
||||||
handlers = {}
|
handlers = {}
|
||||||
original_event = socket_manager.sio.event
|
|
||||||
|
|
||||||
def mock_event(func):
|
def mock_event(func):
|
||||||
handlers[func.__name__] = func
|
handlers[func.__name__] = func
|
||||||
@@ -168,7 +166,7 @@ class TestSocketManager:
|
|||||||
@patch("app.services.socket.JWTUtils.decode_access_token")
|
@patch("app.services.socket.JWTUtils.decode_access_token")
|
||||||
async def test_connect_handler_invalid_token(
|
async def test_connect_handler_invalid_token(
|
||||||
self, mock_decode, mock_extract_token, socket_manager, mock_sio,
|
self, mock_decode, mock_extract_token, socket_manager, mock_sio,
|
||||||
):
|
) -> None:
|
||||||
"""Test connection with invalid token."""
|
"""Test connection with invalid token."""
|
||||||
# Setup mocks
|
# Setup mocks
|
||||||
mock_extract_token.return_value = "invalid_token"
|
mock_extract_token.return_value = "invalid_token"
|
||||||
@@ -179,7 +177,6 @@ class TestSocketManager:
|
|||||||
|
|
||||||
# Access the connect handler directly
|
# Access the connect handler directly
|
||||||
handlers = {}
|
handlers = {}
|
||||||
original_event = socket_manager.sio.event
|
|
||||||
|
|
||||||
def mock_event(func):
|
def mock_event(func):
|
||||||
handlers[func.__name__] = func
|
handlers[func.__name__] = func
|
||||||
@@ -203,7 +200,7 @@ class TestSocketManager:
|
|||||||
@patch("app.services.socket.JWTUtils.decode_access_token")
|
@patch("app.services.socket.JWTUtils.decode_access_token")
|
||||||
async def test_connect_handler_missing_user_id(
|
async def test_connect_handler_missing_user_id(
|
||||||
self, mock_decode, mock_extract_token, socket_manager, mock_sio,
|
self, mock_decode, mock_extract_token, socket_manager, mock_sio,
|
||||||
):
|
) -> None:
|
||||||
"""Test connection with token missing user ID."""
|
"""Test connection with token missing user ID."""
|
||||||
# Setup mocks
|
# Setup mocks
|
||||||
mock_extract_token.return_value = "token_without_user_id"
|
mock_extract_token.return_value = "token_without_user_id"
|
||||||
@@ -214,7 +211,6 @@ class TestSocketManager:
|
|||||||
|
|
||||||
# Access the connect handler directly
|
# Access the connect handler directly
|
||||||
handlers = {}
|
handlers = {}
|
||||||
original_event = socket_manager.sio.event
|
|
||||||
|
|
||||||
def mock_event(func):
|
def mock_event(func):
|
||||||
handlers[func.__name__] = func
|
handlers[func.__name__] = func
|
||||||
@@ -234,7 +230,7 @@ class TestSocketManager:
|
|||||||
assert len(socket_manager.user_rooms) == 0
|
assert len(socket_manager.user_rooms) == 0
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@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."""
|
"""Test disconnect handler."""
|
||||||
# Setup initial state
|
# Setup initial state
|
||||||
socket_manager.socket_users["test_sid"] = "123"
|
socket_manager.socket_users["test_sid"] = "123"
|
||||||
@@ -242,7 +238,6 @@ class TestSocketManager:
|
|||||||
|
|
||||||
# Access the disconnect handler directly
|
# Access the disconnect handler directly
|
||||||
handlers = {}
|
handlers = {}
|
||||||
original_event = socket_manager.sio.event
|
|
||||||
|
|
||||||
def mock_event(func):
|
def mock_event(func):
|
||||||
handlers[func.__name__] = func
|
handlers[func.__name__] = func
|
||||||
@@ -259,11 +254,10 @@ class TestSocketManager:
|
|||||||
assert "123" not in socket_manager.user_rooms
|
assert "123" not in socket_manager.user_rooms
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@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."""
|
"""Test disconnect handler with unknown socket."""
|
||||||
# Access the disconnect handler directly
|
# Access the disconnect handler directly
|
||||||
handlers = {}
|
handlers = {}
|
||||||
original_event = socket_manager.sio.event
|
|
||||||
|
|
||||||
def mock_event(func):
|
def mock_event(func):
|
||||||
handlers[func.__name__] = func
|
handlers[func.__name__] = func
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ class TestSoundNormalizerService:
|
|||||||
mock_settings.NORMALIZED_AUDIO_PASSES = 2
|
mock_settings.NORMALIZED_AUDIO_PASSES = 2
|
||||||
return SoundNormalizerService(mock_session)
|
return SoundNormalizerService(mock_session)
|
||||||
|
|
||||||
def test_init(self, normalizer_service):
|
def test_init(self, normalizer_service) -> None:
|
||||||
"""Test normalizer service initialization."""
|
"""Test normalizer service initialization."""
|
||||||
assert normalizer_service.session is not None
|
assert normalizer_service.session is not None
|
||||||
assert normalizer_service.sound_repo 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 "TTS" in normalizer_service.type_directories
|
||||||
assert "EXT" 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."""
|
"""Test normalized path generation."""
|
||||||
sound = Sound(
|
sound = Sound(
|
||||||
id=1,
|
id=1,
|
||||||
@@ -57,7 +57,7 @@ class TestSoundNormalizerService:
|
|||||||
assert "sounds/normalized/soundboard" in str(normalized_path)
|
assert "sounds/normalized/soundboard" in str(normalized_path)
|
||||||
assert normalized_path.name == "test_audio.mp3"
|
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."""
|
"""Test original path generation."""
|
||||||
sound = Sound(
|
sound = Sound(
|
||||||
id=1,
|
id=1,
|
||||||
@@ -74,7 +74,7 @@ class TestSoundNormalizerService:
|
|||||||
assert "sounds/originals/soundboard" in str(original_path)
|
assert "sounds/originals/soundboard" in str(original_path)
|
||||||
assert original_path.name == "test_audio.wav"
|
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."""
|
"""Test file hash calculation."""
|
||||||
# Create a temporary file
|
# Create a temporary file
|
||||||
with tempfile.NamedTemporaryFile(mode="w", delete=False) as f:
|
with tempfile.NamedTemporaryFile(mode="w", delete=False) as f:
|
||||||
@@ -90,7 +90,7 @@ class TestSoundNormalizerService:
|
|||||||
finally:
|
finally:
|
||||||
temp_path.unlink()
|
temp_path.unlink()
|
||||||
|
|
||||||
def test_get_file_size(self, normalizer_service):
|
def test_get_file_size(self, normalizer_service) -> None:
|
||||||
"""Test file size calculation."""
|
"""Test file size calculation."""
|
||||||
# Create a temporary file
|
# Create a temporary file
|
||||||
with tempfile.NamedTemporaryFile(mode="w", delete=False) as f:
|
with tempfile.NamedTemporaryFile(mode="w", delete=False) as f:
|
||||||
@@ -107,7 +107,7 @@ class TestSoundNormalizerService:
|
|||||||
temp_path.unlink()
|
temp_path.unlink()
|
||||||
|
|
||||||
@patch("app.utils.audio.ffmpeg.probe")
|
@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."""
|
"""Test successful audio duration extraction."""
|
||||||
mock_probe.return_value = {"format": {"duration": "123.456"}}
|
mock_probe.return_value = {"format": {"duration": "123.456"}}
|
||||||
|
|
||||||
@@ -120,7 +120,7 @@ class TestSoundNormalizerService:
|
|||||||
mock_probe.assert_called_once_with(str(temp_path))
|
mock_probe.assert_called_once_with(str(temp_path))
|
||||||
|
|
||||||
@patch("app.utils.audio.ffmpeg.probe")
|
@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."""
|
"""Test audio duration extraction failure."""
|
||||||
mock_probe.side_effect = Exception("FFmpeg error")
|
mock_probe.side_effect = Exception("FFmpeg error")
|
||||||
|
|
||||||
@@ -133,7 +133,7 @@ class TestSoundNormalizerService:
|
|||||||
mock_probe.assert_called_once_with(str(temp_path))
|
mock_probe.assert_called_once_with(str(temp_path))
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@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."""
|
"""Test normalizing a sound that's already normalized."""
|
||||||
sound = Sound(
|
sound = Sound(
|
||||||
id=1,
|
id=1,
|
||||||
@@ -154,7 +154,7 @@ class TestSoundNormalizerService:
|
|||||||
assert result["id"] == 1
|
assert result["id"] == 1
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@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."""
|
"""Test force normalizing a sound that's already normalized."""
|
||||||
sound = Sound(
|
sound = Sound(
|
||||||
id=1,
|
id=1,
|
||||||
@@ -173,7 +173,7 @@ class TestSoundNormalizerService:
|
|||||||
patch.object(normalizer_service, "_get_normalized_path") as mock_norm_path,
|
patch.object(normalizer_service, "_get_normalized_path") as mock_norm_path,
|
||||||
patch.object(
|
patch.object(
|
||||||
normalizer_service, "_normalize_audio_two_pass",
|
normalizer_service, "_normalize_audio_two_pass",
|
||||||
) as mock_normalize,
|
),
|
||||||
patch(
|
patch(
|
||||||
"app.services.sound_normalizer.get_audio_duration", return_value=6000,
|
"app.services.sound_normalizer.get_audio_duration", return_value=6000,
|
||||||
),
|
),
|
||||||
@@ -203,7 +203,7 @@ class TestSoundNormalizerService:
|
|||||||
normalizer_service.sound_repo.update.assert_called_once()
|
normalizer_service.sound_repo.update.assert_called_once()
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@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."""
|
"""Test normalizing a sound where original file doesn't exist."""
|
||||||
sound = Sound(
|
sound = Sound(
|
||||||
id=1,
|
id=1,
|
||||||
@@ -228,7 +228,7 @@ class TestSoundNormalizerService:
|
|||||||
assert result["filename"] == "missing.mp3"
|
assert result["filename"] == "missing.mp3"
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@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."""
|
"""Test normalizing a sound using one-pass method."""
|
||||||
sound = Sound(
|
sound = Sound(
|
||||||
id=1,
|
id=1,
|
||||||
@@ -275,7 +275,7 @@ class TestSoundNormalizerService:
|
|||||||
mock_normalize.assert_called_once()
|
mock_normalize.assert_called_once()
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@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."""
|
"""Test handling normalization errors."""
|
||||||
sound = Sound(
|
sound = Sound(
|
||||||
id=1,
|
id=1,
|
||||||
@@ -312,7 +312,7 @@ class TestSoundNormalizerService:
|
|||||||
assert result["filename"] == "test.mp3"
|
assert result["filename"] == "test.mp3"
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@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."""
|
"""Test normalizing all unnormalized sounds."""
|
||||||
sounds = [
|
sounds = [
|
||||||
Sound(
|
Sound(
|
||||||
@@ -382,7 +382,7 @@ class TestSoundNormalizerService:
|
|||||||
assert len(results["files"]) == 2
|
assert len(results["files"]) == 2
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@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."""
|
"""Test normalizing sounds by type."""
|
||||||
sdb_sounds = [
|
sdb_sounds = [
|
||||||
Sound(
|
Sound(
|
||||||
@@ -432,7 +432,7 @@ class TestSoundNormalizerService:
|
|||||||
)
|
)
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@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."""
|
"""Test normalizing sounds with some errors."""
|
||||||
sounds = [
|
sounds = [
|
||||||
Sound(
|
Sound(
|
||||||
@@ -512,7 +512,7 @@ class TestSoundNormalizerService:
|
|||||||
self,
|
self,
|
||||||
mock_ffmpeg,
|
mock_ffmpeg,
|
||||||
normalizer_service,
|
normalizer_service,
|
||||||
):
|
) -> None:
|
||||||
"""Test one-pass audio normalization for MP3."""
|
"""Test one-pass audio normalization for MP3."""
|
||||||
input_path = Path("/fake/input.wav")
|
input_path = Path("/fake/input.wav")
|
||||||
output_path = Path("/fake/output.mp3")
|
output_path = Path("/fake/output.mp3")
|
||||||
@@ -547,7 +547,7 @@ class TestSoundNormalizerService:
|
|||||||
self,
|
self,
|
||||||
mock_ffmpeg,
|
mock_ffmpeg,
|
||||||
normalizer_service,
|
normalizer_service,
|
||||||
):
|
) -> None:
|
||||||
"""Test two-pass audio normalization analysis phase."""
|
"""Test two-pass audio normalization analysis phase."""
|
||||||
input_path = Path("/fake/input.wav")
|
input_path = Path("/fake/input.wav")
|
||||||
output_path = Path("/fake/output.mp3")
|
output_path = Path("/fake/output.mp3")
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ class TestSoundScannerService:
|
|||||||
"""Create a scanner service with mock session."""
|
"""Create a scanner service with mock session."""
|
||||||
return SoundScannerService(mock_session)
|
return SoundScannerService(mock_session)
|
||||||
|
|
||||||
def test_init(self, scanner_service):
|
def test_init(self, scanner_service) -> None:
|
||||||
"""Test scanner service initialization."""
|
"""Test scanner service initialization."""
|
||||||
assert scanner_service.session is not None
|
assert scanner_service.session is not None
|
||||||
assert scanner_service.sound_repo 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 ".mp3" in scanner_service.supported_extensions
|
||||||
assert ".wav" 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."""
|
"""Test file hash calculation."""
|
||||||
# Create a temporary file
|
# Create a temporary file
|
||||||
with tempfile.NamedTemporaryFile(mode="w", delete=False) as f:
|
with tempfile.NamedTemporaryFile(mode="w", delete=False) as f:
|
||||||
@@ -48,7 +48,7 @@ class TestSoundScannerService:
|
|||||||
finally:
|
finally:
|
||||||
temp_path.unlink()
|
temp_path.unlink()
|
||||||
|
|
||||||
def test_get_file_size(self, scanner_service):
|
def test_get_file_size(self, scanner_service) -> None:
|
||||||
"""Test file size calculation."""
|
"""Test file size calculation."""
|
||||||
# Create a temporary file
|
# Create a temporary file
|
||||||
with tempfile.NamedTemporaryFile(mode="w", delete=False) as f:
|
with tempfile.NamedTemporaryFile(mode="w", delete=False) as f:
|
||||||
@@ -64,7 +64,7 @@ class TestSoundScannerService:
|
|||||||
finally:
|
finally:
|
||||||
temp_path.unlink()
|
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 name extraction from filename."""
|
||||||
test_cases = [
|
test_cases = [
|
||||||
("hello_world.mp3", "Hello World"),
|
("hello_world.mp3", "Hello World"),
|
||||||
@@ -79,7 +79,7 @@ class TestSoundScannerService:
|
|||||||
assert result == expected_name
|
assert result == expected_name
|
||||||
|
|
||||||
@patch("app.utils.audio.ffmpeg.probe")
|
@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."""
|
"""Test successful audio duration extraction."""
|
||||||
mock_probe.return_value = {"format": {"duration": "123.456"}}
|
mock_probe.return_value = {"format": {"duration": "123.456"}}
|
||||||
|
|
||||||
@@ -92,7 +92,7 @@ class TestSoundScannerService:
|
|||||||
mock_probe.assert_called_once_with(str(temp_path))
|
mock_probe.assert_called_once_with(str(temp_path))
|
||||||
|
|
||||||
@patch("app.utils.audio.ffmpeg.probe")
|
@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."""
|
"""Test audio duration extraction failure."""
|
||||||
mock_probe.side_effect = Exception("FFmpeg error")
|
mock_probe.side_effect = Exception("FFmpeg error")
|
||||||
|
|
||||||
@@ -105,13 +105,13 @@ class TestSoundScannerService:
|
|||||||
mock_probe.assert_called_once_with(str(temp_path))
|
mock_probe.assert_called_once_with(str(temp_path))
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@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."""
|
"""Test scanning a non-existent directory."""
|
||||||
with pytest.raises(ValueError, match="Directory does not exist"):
|
with pytest.raises(ValueError, match="Directory does not exist"):
|
||||||
await scanner_service.scan_directory("/non/existent/path")
|
await scanner_service.scan_directory("/non/existent/path")
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@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."""
|
"""Test scanning a path that is not a directory."""
|
||||||
# Create a temporary file
|
# Create a temporary file
|
||||||
with tempfile.NamedTemporaryFile() as f:
|
with tempfile.NamedTemporaryFile() as f:
|
||||||
@@ -119,7 +119,7 @@ class TestSoundScannerService:
|
|||||||
await scanner_service.scan_directory(f.name)
|
await scanner_service.scan_directory(f.name)
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@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."""
|
"""Test syncing file that is unchanged."""
|
||||||
# Existing sound with same hash as file
|
# Existing sound with same hash as file
|
||||||
existing_sound = Sound(
|
existing_sound = Sound(
|
||||||
@@ -166,7 +166,7 @@ class TestSoundScannerService:
|
|||||||
temp_path.unlink()
|
temp_path.unlink()
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@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."""
|
"""Test syncing a new audio file."""
|
||||||
created_sound = Sound(
|
created_sound = Sound(
|
||||||
id=1,
|
id=1,
|
||||||
@@ -221,7 +221,7 @@ class TestSoundScannerService:
|
|||||||
temp_path.unlink()
|
temp_path.unlink()
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@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)."""
|
"""Test syncing a file that was modified (different hash)."""
|
||||||
# Existing sound with different hash than file
|
# Existing sound with different hash than file
|
||||||
existing_sound = Sound(
|
existing_sound = Sound(
|
||||||
@@ -280,7 +280,7 @@ class TestSoundScannerService:
|
|||||||
temp_path.unlink()
|
temp_path.unlink()
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@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."""
|
"""Test syncing file with custom type."""
|
||||||
created_sound = Sound(
|
created_sound = Sound(
|
||||||
id=1,
|
id=1,
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ from unittest.mock import AsyncMock, Mock, patch
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from app.models.credit_transaction import CreditTransaction
|
|
||||||
from app.models.sound import Sound
|
from app.models.sound import Sound
|
||||||
from app.models.user import User
|
from app.models.user import User
|
||||||
from app.services.vlc_player import VLCPlayerService, get_vlc_player_service
|
from app.services.vlc_player import VLCPlayerService, get_vlc_player_service
|
||||||
@@ -62,20 +61,20 @@ class TestVLCPlayerService:
|
|||||||
normalized_filename="normalized.mp3",
|
normalized_filename="normalized.mp3",
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_init(self, vlc_service):
|
def test_init(self, vlc_service) -> None:
|
||||||
"""Test VLC service initialization."""
|
"""Test VLC service initialization."""
|
||||||
assert vlc_service.vlc_executable is not None
|
assert vlc_service.vlc_executable is not None
|
||||||
assert isinstance(vlc_service.vlc_executable, str)
|
assert isinstance(vlc_service.vlc_executable, str)
|
||||||
|
|
||||||
@patch("app.services.vlc_player.subprocess.run")
|
@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."""
|
"""Test VLC executable detection when found in PATH."""
|
||||||
mock_run.return_value.returncode = 0
|
mock_run.return_value.returncode = 0
|
||||||
service = VLCPlayerService()
|
service = VLCPlayerService()
|
||||||
assert service.vlc_executable == "vlc"
|
assert service.vlc_executable == "vlc"
|
||||||
|
|
||||||
@patch("app.services.vlc_player.subprocess.run")
|
@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."""
|
"""Test VLC executable detection when found by absolute path."""
|
||||||
mock_run.return_value.returncode = 1 # which command fails
|
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.subprocess.run")
|
||||||
@patch("app.services.vlc_player.Path")
|
@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."""
|
"""Test VLC executable detection fallback to default."""
|
||||||
# Mock all paths as non-existent
|
# Mock all paths as non-existent
|
||||||
mock_path_instance = Mock()
|
mock_path_instance = Mock()
|
||||||
@@ -111,7 +110,7 @@ class TestVLCPlayerService:
|
|||||||
@patch("app.services.vlc_player.asyncio.create_subprocess_exec")
|
@patch("app.services.vlc_player.asyncio.create_subprocess_exec")
|
||||||
async def test_play_sound_success(
|
async def test_play_sound_success(
|
||||||
self, mock_subprocess, vlc_service, sample_sound,
|
self, mock_subprocess, vlc_service, sample_sound,
|
||||||
):
|
) -> None:
|
||||||
"""Test successful sound playback."""
|
"""Test successful sound playback."""
|
||||||
# Mock subprocess
|
# Mock subprocess
|
||||||
mock_process = Mock()
|
mock_process = Mock()
|
||||||
@@ -144,7 +143,7 @@ class TestVLCPlayerService:
|
|||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_play_sound_file_not_found(
|
async def test_play_sound_file_not_found(
|
||||||
self, vlc_service, sample_sound,
|
self, vlc_service, sample_sound,
|
||||||
):
|
) -> None:
|
||||||
"""Test sound playback when file doesn't exist."""
|
"""Test sound playback when file doesn't exist."""
|
||||||
# Mock the file path utility to return a non-existent path
|
# 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:
|
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")
|
@patch("app.services.vlc_player.asyncio.create_subprocess_exec")
|
||||||
async def test_play_sound_subprocess_error(
|
async def test_play_sound_subprocess_error(
|
||||||
self, mock_subprocess, vlc_service, sample_sound,
|
self, mock_subprocess, vlc_service, sample_sound,
|
||||||
):
|
) -> None:
|
||||||
"""Test sound playback when subprocess fails."""
|
"""Test sound playback when subprocess fails."""
|
||||||
# Mock the file path utility to return an existing path
|
# Mock the file path utility to return an existing path
|
||||||
with patch("app.services.vlc_player.get_sound_file_path") as mock_get_path:
|
with patch("app.services.vlc_player.get_sound_file_path") as mock_get_path:
|
||||||
@@ -177,7 +176,7 @@ class TestVLCPlayerService:
|
|||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
@patch("app.services.vlc_player.asyncio.create_subprocess_exec")
|
@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."""
|
"""Test successful stopping of all VLC instances."""
|
||||||
# Mock pgrep process (find VLC processes)
|
# Mock pgrep process (find VLC processes)
|
||||||
mock_find_process = Mock()
|
mock_find_process = Mock()
|
||||||
@@ -214,7 +213,7 @@ class TestVLCPlayerService:
|
|||||||
@patch("app.services.vlc_player.asyncio.create_subprocess_exec")
|
@patch("app.services.vlc_player.asyncio.create_subprocess_exec")
|
||||||
async def test_stop_all_vlc_instances_no_processes(
|
async def test_stop_all_vlc_instances_no_processes(
|
||||||
self, mock_subprocess, vlc_service,
|
self, mock_subprocess, vlc_service,
|
||||||
):
|
) -> None:
|
||||||
"""Test stopping VLC instances when none are running."""
|
"""Test stopping VLC instances when none are running."""
|
||||||
# Mock pgrep process (no VLC processes found)
|
# Mock pgrep process (no VLC processes found)
|
||||||
mock_find_process = Mock()
|
mock_find_process = Mock()
|
||||||
@@ -234,7 +233,7 @@ class TestVLCPlayerService:
|
|||||||
@patch("app.services.vlc_player.asyncio.create_subprocess_exec")
|
@patch("app.services.vlc_player.asyncio.create_subprocess_exec")
|
||||||
async def test_stop_all_vlc_instances_partial_kill(
|
async def test_stop_all_vlc_instances_partial_kill(
|
||||||
self, mock_subprocess, vlc_service,
|
self, mock_subprocess, vlc_service,
|
||||||
):
|
) -> None:
|
||||||
"""Test stopping VLC instances when some processes remain."""
|
"""Test stopping VLC instances when some processes remain."""
|
||||||
# Mock pgrep process (find VLC processes)
|
# Mock pgrep process (find VLC processes)
|
||||||
mock_find_process = Mock()
|
mock_find_process = Mock()
|
||||||
@@ -267,7 +266,7 @@ class TestVLCPlayerService:
|
|||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
@patch("app.services.vlc_player.asyncio.create_subprocess_exec")
|
@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."""
|
"""Test stopping VLC instances when an error occurs."""
|
||||||
# Mock subprocess exception
|
# Mock subprocess exception
|
||||||
mock_subprocess.side_effect = Exception("Command failed")
|
mock_subprocess.side_effect = Exception("Command failed")
|
||||||
@@ -280,7 +279,7 @@ class TestVLCPlayerService:
|
|||||||
assert "error" in result
|
assert "error" in result
|
||||||
assert result["message"] == "Failed to stop VLC processes"
|
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."""
|
"""Test that get_vlc_player_service returns the same instance."""
|
||||||
with patch("app.services.vlc_player.VLCPlayerService") as mock_service_class:
|
with patch("app.services.vlc_player.VLCPlayerService") as mock_service_class:
|
||||||
mock_instance = Mock()
|
mock_instance = Mock()
|
||||||
@@ -306,7 +305,7 @@ class TestVLCPlayerService:
|
|||||||
@patch("app.services.vlc_player.asyncio.create_subprocess_exec")
|
@patch("app.services.vlc_player.asyncio.create_subprocess_exec")
|
||||||
async def test_play_sound_with_play_count_tracking(
|
async def test_play_sound_with_play_count_tracking(
|
||||||
self, mock_subprocess, vlc_service_with_db, sample_sound,
|
self, mock_subprocess, vlc_service_with_db, sample_sound,
|
||||||
):
|
) -> None:
|
||||||
"""Test sound playback with play count tracking."""
|
"""Test sound playback with play count tracking."""
|
||||||
# Mock subprocess
|
# Mock subprocess
|
||||||
mock_process = Mock()
|
mock_process = Mock()
|
||||||
@@ -371,7 +370,7 @@ class TestVLCPlayerService:
|
|||||||
# mocking or using a real async test framework setup
|
# mocking or using a real async test framework setup
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@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."""
|
"""Test successful play count recording."""
|
||||||
# Mock session and repositories
|
# Mock session and repositories
|
||||||
mock_session = AsyncMock()
|
mock_session = AsyncMock()
|
||||||
@@ -436,14 +435,14 @@ class TestVLCPlayerService:
|
|||||||
)
|
)
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@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."""
|
"""Test play count recording when no session factory is available."""
|
||||||
# This should not raise an error and should log a warning
|
# This should not raise an error and should log a warning
|
||||||
await vlc_service._record_play_count(1, "Test Sound")
|
await vlc_service._record_play_count(1, "Test Sound")
|
||||||
# The method should return early without doing anything
|
# The method should return early without doing anything
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@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."""
|
"""Test play count recording always creates a new SoundPlayed record."""
|
||||||
# Mock session and repositories
|
# Mock session and repositories
|
||||||
mock_session = AsyncMock()
|
mock_session = AsyncMock()
|
||||||
@@ -493,7 +492,7 @@ class TestVLCPlayerService:
|
|||||||
# Verify commit happened
|
# Verify commit happened
|
||||||
mock_session.commit.assert_called_once()
|
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."""
|
"""Test that VLC service uses the shared sound path utility."""
|
||||||
with patch("app.services.vlc_player.get_sound_file_path") as mock_path:
|
with patch("app.services.vlc_player.get_sound_file_path") as mock_path:
|
||||||
mock_file_path = Mock(spec=Path)
|
mock_file_path = Mock(spec=Path)
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
"""Tests for cookie utilities."""
|
"""Tests for cookie utilities."""
|
||||||
# ruff: noqa: ANN201, E501
|
|
||||||
|
|
||||||
from app.utils.cookies import extract_access_token_from_cookies, parse_cookies
|
from app.utils.cookies import extract_access_token_from_cookies, parse_cookies
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
"""Tests for credit decorators."""
|
"""Tests for credit decorators."""
|
||||||
# ruff: noqa: ARG001, ANN001, E501, PT012
|
|
||||||
|
|
||||||
from collections.abc import Callable
|
from collections.abc import Callable
|
||||||
from typing import Never
|
from typing import Never
|
||||||
|
|||||||
Reference in New Issue
Block a user