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:
189
app/schemas/scheduler.py
Normal file
189
app/schemas/scheduler.py
Normal file
@@ -0,0 +1,189 @@
|
||||
"""Schemas for scheduled task API."""
|
||||
|
||||
from datetime import datetime
|
||||
from typing import Any, Dict, Optional
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from app.models.scheduled_task import RecurrenceType, TaskStatus, TaskType
|
||||
|
||||
|
||||
class ScheduledTaskBase(BaseModel):
|
||||
"""Base schema for scheduled tasks."""
|
||||
|
||||
name: str = Field(description="Human-readable task name")
|
||||
task_type: TaskType = Field(description="Type of task to execute")
|
||||
scheduled_at: datetime = Field(description="When the task should be executed")
|
||||
timezone: str = Field(default="UTC", description="Timezone for scheduling")
|
||||
parameters: Dict[str, Any] = Field(
|
||||
default_factory=dict,
|
||||
description="Task-specific parameters",
|
||||
)
|
||||
recurrence_type: RecurrenceType = Field(
|
||||
default=RecurrenceType.NONE,
|
||||
description="Recurrence pattern",
|
||||
)
|
||||
cron_expression: Optional[str] = Field(
|
||||
default=None,
|
||||
description="Cron expression for custom recurrence",
|
||||
)
|
||||
recurrence_count: Optional[int] = Field(
|
||||
default=None,
|
||||
description="Number of times to repeat (None for infinite)",
|
||||
)
|
||||
expires_at: Optional[datetime] = Field(
|
||||
default=None,
|
||||
description="When the task expires (optional)",
|
||||
)
|
||||
|
||||
|
||||
class ScheduledTaskCreate(ScheduledTaskBase):
|
||||
"""Schema for creating a scheduled task."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class ScheduledTaskUpdate(BaseModel):
|
||||
"""Schema for updating a scheduled task."""
|
||||
|
||||
name: Optional[str] = None
|
||||
scheduled_at: Optional[datetime] = None
|
||||
timezone: Optional[str] = None
|
||||
parameters: Optional[Dict[str, Any]] = None
|
||||
is_active: Optional[bool] = None
|
||||
expires_at: Optional[datetime] = None
|
||||
|
||||
|
||||
class ScheduledTaskResponse(ScheduledTaskBase):
|
||||
"""Schema for scheduled task responses."""
|
||||
|
||||
id: int
|
||||
status: TaskStatus
|
||||
user_id: Optional[int] = None
|
||||
executions_count: int
|
||||
last_executed_at: Optional[datetime] = None
|
||||
next_execution_at: Optional[datetime] = None
|
||||
error_message: Optional[str] = None
|
||||
is_active: bool
|
||||
created_at: datetime
|
||||
updated_at: datetime
|
||||
|
||||
class Config:
|
||||
"""Pydantic configuration."""
|
||||
|
||||
from_attributes = True
|
||||
|
||||
|
||||
# Task-specific parameter schemas
|
||||
class CreditRechargeParameters(BaseModel):
|
||||
"""Parameters for credit recharge tasks."""
|
||||
|
||||
user_id: Optional[int] = Field(
|
||||
default=None,
|
||||
description="Specific user ID to recharge (None for all users)",
|
||||
)
|
||||
|
||||
|
||||
class PlaySoundParameters(BaseModel):
|
||||
"""Parameters for play sound tasks."""
|
||||
|
||||
sound_id: int = Field(description="ID of the sound to play")
|
||||
|
||||
|
||||
class PlayPlaylistParameters(BaseModel):
|
||||
"""Parameters for play playlist tasks."""
|
||||
|
||||
playlist_id: int = Field(description="ID of the playlist to play")
|
||||
play_mode: str = Field(
|
||||
default="continuous",
|
||||
description="Play mode (continuous, loop, loop_one, random, single)",
|
||||
)
|
||||
shuffle: bool = Field(default=False, description="Whether to shuffle the playlist")
|
||||
|
||||
|
||||
# Convenience schemas for creating specific task types
|
||||
class CreateCreditRechargeTask(BaseModel):
|
||||
"""Schema for creating credit recharge tasks."""
|
||||
|
||||
name: str = "Credit Recharge"
|
||||
scheduled_at: datetime
|
||||
timezone: str = "UTC"
|
||||
recurrence_type: RecurrenceType = RecurrenceType.NONE
|
||||
cron_expression: Optional[str] = None
|
||||
recurrence_count: Optional[int] = None
|
||||
expires_at: Optional[datetime] = None
|
||||
user_id: Optional[int] = None
|
||||
|
||||
def to_task_create(self) -> ScheduledTaskCreate:
|
||||
"""Convert to generic task creation schema."""
|
||||
return ScheduledTaskCreate(
|
||||
name=self.name,
|
||||
task_type=TaskType.CREDIT_RECHARGE,
|
||||
scheduled_at=self.scheduled_at,
|
||||
timezone=self.timezone,
|
||||
parameters={"user_id": self.user_id},
|
||||
recurrence_type=self.recurrence_type,
|
||||
cron_expression=self.cron_expression,
|
||||
recurrence_count=self.recurrence_count,
|
||||
expires_at=self.expires_at,
|
||||
)
|
||||
|
||||
|
||||
class CreatePlaySoundTask(BaseModel):
|
||||
"""Schema for creating play sound tasks."""
|
||||
|
||||
name: str
|
||||
scheduled_at: datetime
|
||||
sound_id: int
|
||||
timezone: str = "UTC"
|
||||
recurrence_type: RecurrenceType = RecurrenceType.NONE
|
||||
cron_expression: Optional[str] = None
|
||||
recurrence_count: Optional[int] = None
|
||||
expires_at: Optional[datetime] = None
|
||||
|
||||
def to_task_create(self) -> ScheduledTaskCreate:
|
||||
"""Convert to generic task creation schema."""
|
||||
return ScheduledTaskCreate(
|
||||
name=self.name,
|
||||
task_type=TaskType.PLAY_SOUND,
|
||||
scheduled_at=self.scheduled_at,
|
||||
timezone=self.timezone,
|
||||
parameters={"sound_id": self.sound_id},
|
||||
recurrence_type=self.recurrence_type,
|
||||
cron_expression=self.cron_expression,
|
||||
recurrence_count=self.recurrence_count,
|
||||
expires_at=self.expires_at,
|
||||
)
|
||||
|
||||
|
||||
class CreatePlayPlaylistTask(BaseModel):
|
||||
"""Schema for creating play playlist tasks."""
|
||||
|
||||
name: str
|
||||
scheduled_at: datetime
|
||||
playlist_id: int
|
||||
play_mode: str = "continuous"
|
||||
shuffle: bool = False
|
||||
timezone: str = "UTC"
|
||||
recurrence_type: RecurrenceType = RecurrenceType.NONE
|
||||
cron_expression: Optional[str] = None
|
||||
recurrence_count: Optional[int] = None
|
||||
expires_at: Optional[datetime] = None
|
||||
|
||||
def to_task_create(self) -> ScheduledTaskCreate:
|
||||
"""Convert to generic task creation schema."""
|
||||
return ScheduledTaskCreate(
|
||||
name=self.name,
|
||||
task_type=TaskType.PLAY_PLAYLIST,
|
||||
scheduled_at=self.scheduled_at,
|
||||
timezone=self.timezone,
|
||||
parameters={
|
||||
"playlist_id": self.playlist_id,
|
||||
"play_mode": self.play_mode,
|
||||
"shuffle": self.shuffle,
|
||||
},
|
||||
recurrence_type=self.recurrence_type,
|
||||
cron_expression=self.cron_expression,
|
||||
recurrence_count=self.recurrence_count,
|
||||
expires_at=self.expires_at,
|
||||
)
|
||||
Reference in New Issue
Block a user