Add comprehensive tests for playlist service and refactor socket service tests
- Introduced a new test suite for the PlaylistService covering various functionalities including creation, retrieval, updating, and deletion of playlists. - Added tests for handling sounds within playlists, ensuring correct behavior when adding/removing sounds and managing current playlists. - Refactored socket service tests for improved readability by adjusting function signatures. - Cleaned up unnecessary whitespace in sound normalizer and sound scanner tests for consistency. - Enhanced audio utility tests to ensure accurate hash and size calculations, including edge cases for nonexistent files. - Removed redundant blank lines in cookie utility tests for cleaner code.
This commit is contained in:
@@ -14,7 +14,9 @@ class TestApiTokenEndpoints:
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_generate_api_token_success(
|
||||
self, authenticated_client: AsyncClient, authenticated_user: User,
|
||||
self,
|
||||
authenticated_client: AsyncClient,
|
||||
authenticated_user: User,
|
||||
):
|
||||
"""Test successful API token generation."""
|
||||
request_data = {"expires_days": 30}
|
||||
@@ -33,6 +35,7 @@ class TestApiTokenEndpoints:
|
||||
|
||||
# Verify token format (should be URL-safe base64)
|
||||
import base64
|
||||
|
||||
try:
|
||||
base64.urlsafe_b64decode(data["api_token"] + "===") # Add padding
|
||||
except Exception:
|
||||
@@ -40,7 +43,8 @@ class TestApiTokenEndpoints:
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_generate_api_token_default_expiry(
|
||||
self, authenticated_client: AsyncClient,
|
||||
self,
|
||||
authenticated_client: AsyncClient,
|
||||
):
|
||||
"""Test API token generation with default expiry."""
|
||||
response = await authenticated_client.post("/api/v1/auth/api-token", json={})
|
||||
@@ -65,7 +69,8 @@ class TestApiTokenEndpoints:
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_generate_api_token_custom_expiry(
|
||||
self, authenticated_client: AsyncClient,
|
||||
self,
|
||||
authenticated_client: AsyncClient,
|
||||
):
|
||||
"""Test API token generation with custom expiry."""
|
||||
expires_days = 90
|
||||
@@ -96,7 +101,8 @@ class TestApiTokenEndpoints:
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_generate_api_token_validation_errors(
|
||||
self, authenticated_client: AsyncClient,
|
||||
self,
|
||||
authenticated_client: AsyncClient,
|
||||
):
|
||||
"""Test API token generation with validation errors."""
|
||||
# Test minimum validation
|
||||
@@ -124,7 +130,8 @@ class TestApiTokenEndpoints:
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_api_token_status_no_token(
|
||||
self, authenticated_client: AsyncClient,
|
||||
self,
|
||||
authenticated_client: AsyncClient,
|
||||
):
|
||||
"""Test getting API token status when user has no token."""
|
||||
response = await authenticated_client.get("/api/v1/auth/api-token/status")
|
||||
@@ -138,7 +145,8 @@ class TestApiTokenEndpoints:
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_api_token_status_with_token(
|
||||
self, authenticated_client: AsyncClient,
|
||||
self,
|
||||
authenticated_client: AsyncClient,
|
||||
):
|
||||
"""Test getting API token status when user has a token."""
|
||||
# First generate a token
|
||||
@@ -159,14 +167,18 @@ class TestApiTokenEndpoints:
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_api_token_status_expired_token(
|
||||
self, authenticated_client: AsyncClient, authenticated_user: User,
|
||||
self,
|
||||
authenticated_client: AsyncClient,
|
||||
authenticated_user: User,
|
||||
):
|
||||
"""Test getting API token status with expired token."""
|
||||
# Mock expired token
|
||||
with patch("app.utils.auth.TokenUtils.is_token_expired", return_value=True):
|
||||
# Set a token on the user
|
||||
authenticated_user.api_token = "expired_token"
|
||||
authenticated_user.api_token_expires_at = datetime.now(UTC) - timedelta(days=1)
|
||||
authenticated_user.api_token_expires_at = datetime.now(UTC) - timedelta(
|
||||
days=1
|
||||
)
|
||||
|
||||
response = await authenticated_client.get("/api/v1/auth/api-token/status")
|
||||
|
||||
@@ -185,7 +197,8 @@ class TestApiTokenEndpoints:
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_revoke_api_token_success(
|
||||
self, authenticated_client: AsyncClient,
|
||||
self,
|
||||
authenticated_client: AsyncClient,
|
||||
):
|
||||
"""Test successful API token revocation."""
|
||||
# First generate a token
|
||||
@@ -195,7 +208,9 @@ class TestApiTokenEndpoints:
|
||||
)
|
||||
|
||||
# Verify token exists
|
||||
status_response = await authenticated_client.get("/api/v1/auth/api-token/status")
|
||||
status_response = await authenticated_client.get(
|
||||
"/api/v1/auth/api-token/status"
|
||||
)
|
||||
assert status_response.json()["has_token"] is True
|
||||
|
||||
# Revoke the token
|
||||
@@ -206,12 +221,15 @@ class TestApiTokenEndpoints:
|
||||
assert data["message"] == "API token revoked successfully"
|
||||
|
||||
# Verify token is gone
|
||||
status_response = await authenticated_client.get("/api/v1/auth/api-token/status")
|
||||
status_response = await authenticated_client.get(
|
||||
"/api/v1/auth/api-token/status"
|
||||
)
|
||||
assert status_response.json()["has_token"] is False
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_revoke_api_token_no_token(
|
||||
self, authenticated_client: AsyncClient,
|
||||
self,
|
||||
authenticated_client: AsyncClient,
|
||||
):
|
||||
"""Test revoking API token when user has no token."""
|
||||
response = await authenticated_client.delete("/api/v1/auth/api-token")
|
||||
@@ -228,7 +246,9 @@ class TestApiTokenEndpoints:
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_api_token_authentication_success(
|
||||
self, client: AsyncClient, authenticated_client: AsyncClient,
|
||||
self,
|
||||
client: AsyncClient,
|
||||
authenticated_client: AsyncClient,
|
||||
):
|
||||
"""Test successful authentication using API token."""
|
||||
# Generate API token
|
||||
@@ -259,7 +279,9 @@ class TestApiTokenEndpoints:
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_api_token_authentication_expired_token(
|
||||
self, client: AsyncClient, authenticated_client: AsyncClient,
|
||||
self,
|
||||
client: AsyncClient,
|
||||
authenticated_client: AsyncClient,
|
||||
):
|
||||
"""Test authentication with expired API token."""
|
||||
# Generate API token
|
||||
@@ -299,7 +321,10 @@ class TestApiTokenEndpoints:
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_api_token_authentication_inactive_user(
|
||||
self, client: AsyncClient, authenticated_client: AsyncClient, authenticated_user: User,
|
||||
self,
|
||||
client: AsyncClient,
|
||||
authenticated_client: AsyncClient,
|
||||
authenticated_user: User,
|
||||
):
|
||||
"""Test authentication with API token for inactive user."""
|
||||
# Generate API token
|
||||
@@ -322,7 +347,10 @@ class TestApiTokenEndpoints:
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_flexible_authentication_prefers_api_token(
|
||||
self, client: AsyncClient, authenticated_client: AsyncClient, auth_cookies: dict[str, str],
|
||||
self,
|
||||
client: AsyncClient,
|
||||
authenticated_client: AsyncClient,
|
||||
auth_cookies: dict[str, str],
|
||||
):
|
||||
"""Test that flexible authentication prefers API token over cookie."""
|
||||
# Generate API token
|
||||
|
||||
@@ -73,7 +73,9 @@ class TestAuthEndpoints:
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_register_duplicate_email(
|
||||
self, test_client: AsyncClient, test_user: User,
|
||||
self,
|
||||
test_client: AsyncClient,
|
||||
test_user: User,
|
||||
) -> None:
|
||||
"""Test registration with duplicate email."""
|
||||
user_data = {
|
||||
@@ -128,7 +130,10 @@ class TestAuthEndpoints:
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_login_success(
|
||||
self, test_client: AsyncClient, test_user: User, test_login_data: dict[str, str],
|
||||
self,
|
||||
test_client: AsyncClient,
|
||||
test_user: User,
|
||||
test_login_data: dict[str, str],
|
||||
) -> None:
|
||||
"""Test successful user login."""
|
||||
response = await test_client.post("/api/v1/auth/login", json=test_login_data)
|
||||
@@ -161,7 +166,9 @@ class TestAuthEndpoints:
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_login_invalid_password(
|
||||
self, test_client: AsyncClient, test_user: User,
|
||||
self,
|
||||
test_client: AsyncClient,
|
||||
test_user: User,
|
||||
) -> None:
|
||||
"""Test login with invalid password."""
|
||||
login_data = {"email": test_user.email, "password": "wrongpassword"}
|
||||
@@ -183,7 +190,10 @@ class TestAuthEndpoints:
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_current_user_success(
|
||||
self, test_client: AsyncClient, test_user: User, auth_cookies: dict[str, str],
|
||||
self,
|
||||
test_client: AsyncClient,
|
||||
test_user: User,
|
||||
auth_cookies: dict[str, str],
|
||||
) -> None:
|
||||
"""Test getting current user info successfully."""
|
||||
# Set cookies on client instance to avoid deprecation warning
|
||||
@@ -210,7 +220,8 @@ class TestAuthEndpoints:
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_current_user_invalid_token(
|
||||
self, test_client: AsyncClient,
|
||||
self,
|
||||
test_client: AsyncClient,
|
||||
) -> None:
|
||||
"""Test getting current user with invalid token."""
|
||||
# Set invalid cookies on client instance
|
||||
@@ -223,7 +234,9 @@ class TestAuthEndpoints:
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_current_user_expired_token(
|
||||
self, test_client: AsyncClient, test_user: User,
|
||||
self,
|
||||
test_client: AsyncClient,
|
||||
test_user: User,
|
||||
) -> None:
|
||||
"""Test getting current user with expired token."""
|
||||
from datetime import timedelta
|
||||
@@ -237,7 +250,8 @@ class TestAuthEndpoints:
|
||||
"role": "user",
|
||||
}
|
||||
expired_token = JWTUtils.create_access_token(
|
||||
token_data, expires_delta=timedelta(seconds=-1),
|
||||
token_data,
|
||||
expires_delta=timedelta(seconds=-1),
|
||||
)
|
||||
|
||||
# Set expired cookies on client instance
|
||||
@@ -262,7 +276,9 @@ class TestAuthEndpoints:
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_admin_access_with_user_role(
|
||||
self, test_client: AsyncClient, auth_cookies: dict[str, str],
|
||||
self,
|
||||
test_client: AsyncClient,
|
||||
auth_cookies: dict[str, str],
|
||||
) -> None:
|
||||
"""Test that regular users cannot access admin endpoints."""
|
||||
# This test would be for admin-only endpoints when they're created
|
||||
@@ -293,7 +309,9 @@ class TestAuthEndpoints:
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_admin_access_with_admin_role(
|
||||
self, test_client: AsyncClient, admin_cookies: dict[str, str],
|
||||
self,
|
||||
test_client: AsyncClient,
|
||||
admin_cookies: dict[str, str],
|
||||
) -> None:
|
||||
"""Test that admin users can access admin endpoints."""
|
||||
from app.core.dependencies import get_admin_user
|
||||
@@ -357,7 +375,8 @@ class TestAuthEndpoints:
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_oauth_authorize_invalid_provider(
|
||||
self, test_client: AsyncClient,
|
||||
self,
|
||||
test_client: AsyncClient,
|
||||
) -> None:
|
||||
"""Test OAuth authorization with invalid provider."""
|
||||
response = await test_client.get("/api/v1/auth/invalid/authorize")
|
||||
@@ -368,7 +387,9 @@ class TestAuthEndpoints:
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_oauth_callback_new_user(
|
||||
self, test_client: AsyncClient, ensure_plans: tuple[Any, Any],
|
||||
self,
|
||||
test_client: AsyncClient,
|
||||
ensure_plans: tuple[Any, Any],
|
||||
) -> None:
|
||||
"""Test OAuth callback for new user creation."""
|
||||
# Mock OAuth user info
|
||||
@@ -400,7 +421,10 @@ class TestAuthEndpoints:
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_oauth_callback_existing_user_link(
|
||||
self, test_client: AsyncClient, test_user: Any, ensure_plans: tuple[Any, Any],
|
||||
self,
|
||||
test_client: AsyncClient,
|
||||
test_user: Any,
|
||||
ensure_plans: tuple[Any, Any],
|
||||
) -> None:
|
||||
"""Test OAuth callback for linking to existing user."""
|
||||
# Mock OAuth user info with same email as test user
|
||||
@@ -442,7 +466,8 @@ class TestAuthEndpoints:
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_oauth_callback_invalid_provider(
|
||||
self, test_client: AsyncClient,
|
||||
self,
|
||||
test_client: AsyncClient,
|
||||
) -> None:
|
||||
"""Test OAuth callback with invalid provider."""
|
||||
response = await test_client.get(
|
||||
|
||||
1170
tests/api/v1/test_playlist_endpoints.py
Normal file
1170
tests/api/v1/test_playlist_endpoints.py
Normal file
File diff suppressed because it is too large
Load Diff
@@ -22,7 +22,12 @@ class TestSocketEndpoints:
|
||||
"""Test socket API endpoints."""
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_socket_status_authenticated(self, authenticated_client: AsyncClient, authenticated_user: User, mock_socket_manager):
|
||||
async def test_get_socket_status_authenticated(
|
||||
self,
|
||||
authenticated_client: AsyncClient,
|
||||
authenticated_user: User,
|
||||
mock_socket_manager,
|
||||
):
|
||||
"""Test getting socket status for authenticated user."""
|
||||
response = await authenticated_client.get("/api/v1/socket/status")
|
||||
|
||||
@@ -43,7 +48,12 @@ class TestSocketEndpoints:
|
||||
assert response.status_code == 401
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_send_message_to_user_success(self, authenticated_client: AsyncClient, authenticated_user: User, mock_socket_manager):
|
||||
async def test_send_message_to_user_success(
|
||||
self,
|
||||
authenticated_client: AsyncClient,
|
||||
authenticated_user: User,
|
||||
mock_socket_manager,
|
||||
):
|
||||
"""Test sending message to specific user successfully."""
|
||||
target_user_id = 2
|
||||
message = "Hello there!"
|
||||
@@ -72,7 +82,12 @@ class TestSocketEndpoints:
|
||||
)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_send_message_to_user_not_connected(self, authenticated_client: AsyncClient, authenticated_user: User, mock_socket_manager):
|
||||
async def test_send_message_to_user_not_connected(
|
||||
self,
|
||||
authenticated_client: AsyncClient,
|
||||
authenticated_user: User,
|
||||
mock_socket_manager,
|
||||
):
|
||||
"""Test sending message to user who is not connected."""
|
||||
target_user_id = 999
|
||||
message = "Hello there!"
|
||||
@@ -102,7 +117,12 @@ class TestSocketEndpoints:
|
||||
assert response.status_code == 401
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_broadcast_message_success(self, authenticated_client: AsyncClient, authenticated_user: User, mock_socket_manager):
|
||||
async def test_broadcast_message_success(
|
||||
self,
|
||||
authenticated_client: AsyncClient,
|
||||
authenticated_user: User,
|
||||
mock_socket_manager,
|
||||
):
|
||||
"""Test broadcasting message to all users successfully."""
|
||||
message = "Important announcement!"
|
||||
|
||||
@@ -137,7 +157,9 @@ class TestSocketEndpoints:
|
||||
assert response.status_code == 401
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_send_message_missing_parameters(self, authenticated_client: AsyncClient, authenticated_user: User):
|
||||
async def test_send_message_missing_parameters(
|
||||
self, authenticated_client: AsyncClient, authenticated_user: User
|
||||
):
|
||||
"""Test sending message with missing parameters."""
|
||||
# Missing target_user_id
|
||||
response = await authenticated_client.post(
|
||||
@@ -154,13 +176,17 @@ class TestSocketEndpoints:
|
||||
assert response.status_code == 422
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_broadcast_message_missing_parameters(self, authenticated_client: AsyncClient, authenticated_user: User):
|
||||
async def test_broadcast_message_missing_parameters(
|
||||
self, authenticated_client: AsyncClient, authenticated_user: User
|
||||
):
|
||||
"""Test broadcasting message with missing parameters."""
|
||||
response = await authenticated_client.post("/api/v1/socket/broadcast")
|
||||
assert response.status_code == 422
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_send_message_invalid_user_id(self, authenticated_client: AsyncClient, authenticated_user: User):
|
||||
async def test_send_message_invalid_user_id(
|
||||
self, authenticated_client: AsyncClient, authenticated_user: User
|
||||
):
|
||||
"""Test sending message with invalid user ID."""
|
||||
response = await authenticated_client.post(
|
||||
"/api/v1/socket/send-message",
|
||||
@@ -169,10 +195,19 @@ class TestSocketEndpoints:
|
||||
assert response.status_code == 422
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_socket_status_shows_user_connection(self, authenticated_client: AsyncClient, authenticated_user: User, mock_socket_manager):
|
||||
async def test_socket_status_shows_user_connection(
|
||||
self,
|
||||
authenticated_client: AsyncClient,
|
||||
authenticated_user: User,
|
||||
mock_socket_manager,
|
||||
):
|
||||
"""Test that socket status correctly shows if user is connected."""
|
||||
# Test when user is connected
|
||||
mock_socket_manager.get_connected_users.return_value = [str(authenticated_user.id), "2", "3"]
|
||||
mock_socket_manager.get_connected_users.return_value = [
|
||||
str(authenticated_user.id),
|
||||
"2",
|
||||
"3",
|
||||
]
|
||||
|
||||
response = await authenticated_client.get("/api/v1/socket/status")
|
||||
data = response.json()
|
||||
|
||||
@@ -870,7 +870,6 @@ class TestSoundEndpoints:
|
||||
) as mock_normalize_sound,
|
||||
patch("app.repositories.sound.SoundRepository.get_by_id") as mock_get_sound,
|
||||
):
|
||||
|
||||
mock_get_sound.return_value = mock_sound
|
||||
mock_normalize_sound.return_value = mock_result
|
||||
|
||||
@@ -950,7 +949,6 @@ class TestSoundEndpoints:
|
||||
) as mock_normalize_sound,
|
||||
patch("app.repositories.sound.SoundRepository.get_by_id") as mock_get_sound,
|
||||
):
|
||||
|
||||
mock_get_sound.return_value = mock_sound
|
||||
mock_normalize_sound.return_value = mock_result
|
||||
|
||||
@@ -1003,7 +1001,6 @@ class TestSoundEndpoints:
|
||||
) as mock_normalize_sound,
|
||||
patch("app.repositories.sound.SoundRepository.get_by_id") as mock_get_sound,
|
||||
):
|
||||
|
||||
mock_get_sound.return_value = mock_sound
|
||||
mock_normalize_sound.return_value = mock_result
|
||||
|
||||
@@ -1059,7 +1056,6 @@ class TestSoundEndpoints:
|
||||
) as mock_normalize_sound,
|
||||
patch("app.repositories.sound.SoundRepository.get_by_id") as mock_get_sound,
|
||||
):
|
||||
|
||||
mock_get_sound.return_value = mock_sound
|
||||
mock_normalize_sound.return_value = mock_result
|
||||
|
||||
|
||||
Reference in New Issue
Block a user