Files
sdb2-backend/app/services/task_handlers.py
JSC 03abed6d39 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.
2025-08-28 22:37:43 +02:00

137 lines
5.1 KiB
Python

"""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")