Add comprehensive tests for scheduled task repository, scheduler service, and task handlers
- Implemented tests for ScheduledTaskRepository covering task creation, retrieval, filtering, and status updates. - Developed tests for SchedulerService including task creation, cancellation, user task retrieval, and maintenance jobs. - Created tests for TaskHandlerRegistry to validate task execution for various types, including credit recharge and sound playback. - Ensured proper error handling and edge cases in task execution scenarios. - Added fixtures and mocks to facilitate isolated testing of services and repositories.
This commit is contained in:
137
app/services/task_handlers.py
Normal file
137
app/services/task_handlers.py
Normal file
@@ -0,0 +1,137 @@
|
||||
"""Task execution handlers for different task types."""
|
||||
|
||||
import uuid
|
||||
from typing import Any, Dict, Optional
|
||||
|
||||
from sqlmodel.ext.asyncio.session import AsyncSession
|
||||
|
||||
from app.core.logging import get_logger
|
||||
from app.models.scheduled_task import ScheduledTask, TaskType
|
||||
from app.repositories.playlist import PlaylistRepository
|
||||
from app.repositories.sound import SoundRepository
|
||||
from app.services.credit import CreditService
|
||||
from app.services.player import PlayerService
|
||||
|
||||
logger = get_logger(__name__)
|
||||
|
||||
|
||||
class TaskExecutionError(Exception):
|
||||
"""Exception raised when task execution fails."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class TaskHandlerRegistry:
|
||||
"""Registry for task execution handlers."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
db_session: AsyncSession,
|
||||
credit_service: CreditService,
|
||||
player_service: PlayerService,
|
||||
) -> None:
|
||||
"""Initialize the task handler registry."""
|
||||
self.db_session = db_session
|
||||
self.credit_service = credit_service
|
||||
self.player_service = player_service
|
||||
self.sound_repository = SoundRepository(db_session)
|
||||
self.playlist_repository = PlaylistRepository(db_session)
|
||||
|
||||
# Register handlers
|
||||
self._handlers = {
|
||||
TaskType.CREDIT_RECHARGE: self._handle_credit_recharge,
|
||||
TaskType.PLAY_SOUND: self._handle_play_sound,
|
||||
TaskType.PLAY_PLAYLIST: self._handle_play_playlist,
|
||||
}
|
||||
|
||||
async def execute_task(self, task: ScheduledTask) -> None:
|
||||
"""Execute a task based on its type."""
|
||||
handler = self._handlers.get(task.task_type)
|
||||
if not handler:
|
||||
raise TaskExecutionError(f"No handler registered for task type: {task.task_type}")
|
||||
|
||||
logger.info(f"Executing task {task.id} ({task.task_type.value}): {task.name}")
|
||||
|
||||
try:
|
||||
await handler(task)
|
||||
logger.info(f"Task {task.id} executed successfully")
|
||||
except Exception as e:
|
||||
logger.exception(f"Task {task.id} execution failed: {str(e)}")
|
||||
raise TaskExecutionError(f"Task execution failed: {str(e)}") from e
|
||||
|
||||
async def _handle_credit_recharge(self, task: ScheduledTask) -> None:
|
||||
"""Handle credit recharge task."""
|
||||
parameters = task.parameters
|
||||
user_id = parameters.get("user_id")
|
||||
|
||||
if user_id:
|
||||
# Recharge specific user
|
||||
user_uuid = uuid.UUID(user_id) if isinstance(user_id, str) else user_id
|
||||
stats = await self.credit_service.recharge_user_credits(user_uuid)
|
||||
logger.info(f"Recharged credits for user {user_id}: {stats}")
|
||||
else:
|
||||
# Recharge all users (system task)
|
||||
stats = await self.credit_service.recharge_all_users_credits()
|
||||
logger.info(f"Recharged credits for all users: {stats}")
|
||||
|
||||
async def _handle_play_sound(self, task: ScheduledTask) -> None:
|
||||
"""Handle play sound task."""
|
||||
parameters = task.parameters
|
||||
sound_id = parameters.get("sound_id")
|
||||
|
||||
if not sound_id:
|
||||
raise TaskExecutionError("sound_id parameter is required for PLAY_SOUND tasks")
|
||||
|
||||
try:
|
||||
sound_uuid = uuid.UUID(sound_id) if isinstance(sound_id, str) else sound_id
|
||||
except (ValueError, TypeError) as e:
|
||||
raise TaskExecutionError(f"Invalid sound_id format: {sound_id}") from e
|
||||
|
||||
# Get the sound from database
|
||||
sound = await self.sound_repository.get_by_id(sound_uuid)
|
||||
if not sound:
|
||||
raise TaskExecutionError(f"Sound not found: {sound_id}")
|
||||
|
||||
# Play the sound through VLC
|
||||
from app.services.vlc_player import VLCPlayerService
|
||||
|
||||
vlc_service = VLCPlayerService(lambda: self.db_session)
|
||||
await vlc_service.play_sound(sound)
|
||||
|
||||
logger.info(f"Played sound {sound.filename} via scheduled task")
|
||||
|
||||
async def _handle_play_playlist(self, task: ScheduledTask) -> None:
|
||||
"""Handle play playlist task."""
|
||||
parameters = task.parameters
|
||||
playlist_id = parameters.get("playlist_id")
|
||||
play_mode = parameters.get("play_mode", "continuous")
|
||||
shuffle = parameters.get("shuffle", False)
|
||||
|
||||
if not playlist_id:
|
||||
raise TaskExecutionError("playlist_id parameter is required for PLAY_PLAYLIST tasks")
|
||||
|
||||
try:
|
||||
playlist_uuid = uuid.UUID(playlist_id) if isinstance(playlist_id, str) else playlist_id
|
||||
except (ValueError, TypeError) as e:
|
||||
raise TaskExecutionError(f"Invalid playlist_id format: {playlist_id}") from e
|
||||
|
||||
# Get the playlist from database
|
||||
playlist = await self.playlist_repository.get_by_id(playlist_uuid)
|
||||
if not playlist:
|
||||
raise TaskExecutionError(f"Playlist not found: {playlist_id}")
|
||||
|
||||
# Load playlist in player
|
||||
await self.player_service.load_playlist(playlist_uuid)
|
||||
|
||||
# Set play mode if specified
|
||||
if play_mode in ["continuous", "loop", "loop_one", "random", "single"]:
|
||||
self.player_service.set_mode(play_mode)
|
||||
|
||||
# Enable shuffle if requested
|
||||
if shuffle:
|
||||
self.player_service.set_shuffle(True)
|
||||
|
||||
# Start playing
|
||||
self.player_service.play()
|
||||
|
||||
logger.info(f"Started playing playlist {playlist.name} via scheduled task")
|
||||
Reference in New Issue
Block a user