Refactor user endpoint tests to include pagination and response structure validation

- Updated tests for listing users to validate pagination and response format.
- Changed mock return values to include total count and pagination details.
- Refactored user creation mocks for clarity and consistency.
- Enhanced assertions to check for presence of pagination fields in responses.
- Adjusted test cases for user retrieval and updates to ensure proper handling of user data.
- Improved readability by restructuring mock definitions and assertions across various test files.
This commit is contained in:
JSC
2025-08-17 12:36:52 +02:00
parent e6f796a3c9
commit 6b55ff0e81
35 changed files with 863 additions and 503 deletions

View File

@@ -454,7 +454,10 @@ class AuthService:
return user
async def change_user_password(
self, user: User, current_password: str | None, new_password: str,
self,
user: User,
current_password: str | None,
new_password: str,
) -> None:
"""Change user's password."""
# Store user email before any operations to avoid session detachment issues
@@ -484,8 +487,11 @@ class AuthService:
self.session.add(user)
await self.session.commit()
logger.info("Password %s successfully for user: %s",
"changed" if had_existing_password else "set", user_email)
logger.info(
"Password %s successfully for user: %s",
"changed" if had_existing_password else "set",
user_email,
)
async def user_to_response(self, user: User) -> UserResponse:
"""Convert User model to UserResponse with plan information."""

View File

@@ -72,9 +72,7 @@ class DashboardService:
"play_count": sound["play_count"],
"duration": sound["duration"],
"created_at": (
sound["created_at"].isoformat()
if sound["created_at"]
else None
sound["created_at"].isoformat() if sound["created_at"] else None
),
}
for sound in top_sounds

View File

@@ -532,7 +532,8 @@ class ExtractionService:
"""Add the sound to the user's main playlist."""
try:
await self.playlist_service._add_sound_to_main_playlist_internal( # noqa: SLF001
sound_id, user_id,
sound_id,
user_id,
)
logger.info(
"Added sound %d to main playlist for user %d",
@@ -554,6 +555,10 @@ class ExtractionService:
if not extraction:
return None
# Get user information
user = await self.user_repo.get_by_id(extraction.user_id)
user_name = user.name if user else None
return {
"id": extraction.id or 0, # Should never be None for existing extraction
"url": extraction.url,
@@ -564,11 +569,12 @@ class ExtractionService:
"error": extraction.error,
"sound_id": extraction.sound_id,
"user_id": extraction.user_id,
"user_name": user_name,
"created_at": extraction.created_at.isoformat(),
"updated_at": extraction.updated_at.isoformat(),
}
async def get_user_extractions(
async def get_user_extractions( # noqa: PLR0913
self,
user_id: int,
search: str | None = None,
@@ -580,7 +586,10 @@ class ExtractionService:
) -> PaginatedExtractionsResponse:
"""Get all extractions for a user with filtering, search, and sorting."""
offset = (page - 1) * limit
extraction_user_tuples, total_count = await self.extraction_repo.get_user_extractions_filtered(
(
extraction_user_tuples,
total_count,
) = await self.extraction_repo.get_user_extractions_filtered(
user_id=user_id,
search=search,
sort_by=sort_by,
@@ -619,7 +628,7 @@ class ExtractionService:
"total_pages": total_pages,
}
async def get_all_extractions(
async def get_all_extractions( # noqa: PLR0913
self,
search: str | None = None,
sort_by: str = "created_at",
@@ -630,7 +639,10 @@ class ExtractionService:
) -> PaginatedExtractionsResponse:
"""Get all extractions with filtering, search, and sorting."""
offset = (page - 1) * limit
extraction_user_tuples, total_count = await self.extraction_repo.get_all_extractions_filtered(
(
extraction_user_tuples,
total_count,
) = await self.extraction_repo.get_all_extractions_filtered(
search=search,
sort_by=sort_by,
sort_order=sort_order,

View File

@@ -49,12 +49,14 @@ class FavoriteService:
# Verify user exists
user = await user_repo.get_by_id(user_id)
if not user:
raise ValueError(f"User with ID {user_id} not found")
msg = f"User with ID {user_id} not found"
raise ValueError(msg)
# Verify sound exists
sound = await sound_repo.get_by_id(sound_id)
if not sound:
raise ValueError(f"Sound with ID {sound_id} not found")
msg = f"Sound with ID {sound_id} not found"
raise ValueError(msg)
# Get data for the event immediately after loading
sound_name = sound.name
@@ -63,9 +65,8 @@ class FavoriteService:
# Check if already favorited
existing = await favorite_repo.get_by_user_and_sound(user_id, sound_id)
if existing:
raise ValueError(
f"Sound {sound_id} is already favorited by user {user_id}",
)
msg = f"Sound {sound_id} is already favorited by user {user_id}"
raise ValueError(msg)
# Create favorite
favorite_data = {
@@ -120,12 +121,14 @@ class FavoriteService:
# Verify user exists
user = await user_repo.get_by_id(user_id)
if not user:
raise ValueError(f"User with ID {user_id} not found")
msg = f"User with ID {user_id} not found"
raise ValueError(msg)
# Verify playlist exists
playlist = await playlist_repo.get_by_id(playlist_id)
if not playlist:
raise ValueError(f"Playlist with ID {playlist_id} not found")
msg = f"Playlist with ID {playlist_id} not found"
raise ValueError(msg)
# Check if already favorited
existing = await favorite_repo.get_by_user_and_playlist(
@@ -133,9 +136,8 @@ class FavoriteService:
playlist_id,
)
if existing:
raise ValueError(
f"Playlist {playlist_id} is already favorited by user {user_id}",
)
msg = f"Playlist {playlist_id} is already favorited by user {user_id}"
raise ValueError(msg)
# Create favorite
favorite_data = {
@@ -163,7 +165,8 @@ class FavoriteService:
favorite = await favorite_repo.get_by_user_and_sound(user_id, sound_id)
if not favorite:
raise ValueError(f"Sound {sound_id} is not favorited by user {user_id}")
msg = f"Sound {sound_id} is not favorited by user {user_id}"
raise ValueError(msg)
# Get user and sound info before deletion for the event
user_repo = UserRepository(session)
@@ -192,7 +195,8 @@ class FavoriteService:
}
await socket_manager.broadcast_to_all("sound_favorited", event_data)
logger.info(
"Broadcasted sound_favorited event for sound %s removal", sound_id,
"Broadcasted sound_favorited event for sound %s removal",
sound_id,
)
except Exception:
logger.exception(
@@ -219,9 +223,8 @@ class FavoriteService:
playlist_id,
)
if not favorite:
raise ValueError(
f"Playlist {playlist_id} is not favorited by user {user_id}",
)
msg = f"Playlist {playlist_id} is not favorited by user {user_id}"
raise ValueError(msg)
await favorite_repo.delete(favorite)
logger.info(

View File

@@ -16,6 +16,7 @@ logger = get_logger(__name__)
class PaginatedPlaylistsResponse(TypedDict):
"""Response type for paginated playlists."""
playlists: list[dict]
total: int
page: int
@@ -286,7 +287,7 @@ class PlaylistService:
) -> PaginatedPlaylistsResponse:
"""Search and sort playlists with pagination."""
offset = (page - 1) * limit
playlists, total_count = await self.playlist_repo.search_and_sort(
search_query=search_query,
sort_by=sort_by,
@@ -299,9 +300,9 @@ class PlaylistService:
current_user_id=current_user_id,
return_count=True,
)
total_pages = (total_count + limit - 1) // limit # Ceiling division
return PaginatedPlaylistsResponse(
playlists=playlists,
total=total_count,
@@ -468,7 +469,9 @@ class PlaylistService:
}
async def add_sound_to_main_playlist(
self, sound_id: int, user_id: int, # noqa: ARG002
self,
sound_id: int, # noqa: ARG002
user_id: int, # noqa: ARG002
) -> None:
"""Add a sound to the global main playlist."""
raise HTTPException(
@@ -477,7 +480,9 @@ class PlaylistService:
)
async def _add_sound_to_main_playlist_internal(
self, sound_id: int, user_id: int,
self,
sound_id: int,
user_id: int,
) -> None:
"""Add sound to main playlist bypassing restrictions.