Add tests for sound repository, user OAuth repository, credit service, and credit decorators
- Implement comprehensive tests for SoundRepository covering CRUD operations and search functionalities. - Create tests for UserOauthRepository to validate OAuth record management. - Develop tests for CreditService to ensure proper credit management, including validation, deduction, and addition of credits. - Add tests for credit-related decorators to verify correct behavior in credit management scenarios.
This commit is contained in:
121
app/models/credit_action.py
Normal file
121
app/models/credit_action.py
Normal file
@@ -0,0 +1,121 @@
|
||||
"""Credit action definitions for the credit system."""
|
||||
|
||||
from enum import Enum
|
||||
from typing import Any
|
||||
|
||||
|
||||
class CreditActionType(str, Enum):
|
||||
"""Types of actions that consume credits."""
|
||||
|
||||
VLC_PLAY_SOUND = "vlc_play_sound"
|
||||
AUDIO_EXTRACTION = "audio_extraction"
|
||||
TEXT_TO_SPEECH = "text_to_speech"
|
||||
SOUND_NORMALIZATION = "sound_normalization"
|
||||
API_REQUEST = "api_request"
|
||||
PLAYLIST_CREATION = "playlist_creation"
|
||||
|
||||
|
||||
class CreditAction:
|
||||
"""Definition of a credit-consuming action."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
action_type: CreditActionType,
|
||||
cost: int,
|
||||
description: str,
|
||||
*,
|
||||
requires_success: bool = True,
|
||||
) -> None:
|
||||
"""Initialize a credit action.
|
||||
|
||||
Args:
|
||||
action_type: The type of action
|
||||
cost: Number of credits required
|
||||
description: Human-readable description
|
||||
requires_success: Whether credits are only deducted on successful completion
|
||||
|
||||
"""
|
||||
self.action_type = action_type
|
||||
self.cost = cost
|
||||
self.description = description
|
||||
self.requires_success = requires_success
|
||||
|
||||
def __str__(self) -> str:
|
||||
"""Return string representation of the action."""
|
||||
return f"{self.action_type.value} ({self.cost} credits)"
|
||||
|
||||
def to_dict(self) -> dict[str, Any]:
|
||||
"""Convert to dictionary for serialization."""
|
||||
return {
|
||||
"action_type": self.action_type.value,
|
||||
"cost": self.cost,
|
||||
"description": self.description,
|
||||
"requires_success": self.requires_success,
|
||||
}
|
||||
|
||||
|
||||
# Predefined credit actions
|
||||
CREDIT_ACTIONS = {
|
||||
CreditActionType.VLC_PLAY_SOUND: CreditAction(
|
||||
action_type=CreditActionType.VLC_PLAY_SOUND,
|
||||
cost=1,
|
||||
description="Play a sound using VLC player",
|
||||
requires_success=True,
|
||||
),
|
||||
CreditActionType.AUDIO_EXTRACTION: CreditAction(
|
||||
action_type=CreditActionType.AUDIO_EXTRACTION,
|
||||
cost=5,
|
||||
description="Extract audio from external URL",
|
||||
requires_success=True,
|
||||
),
|
||||
CreditActionType.TEXT_TO_SPEECH: CreditAction(
|
||||
action_type=CreditActionType.TEXT_TO_SPEECH,
|
||||
cost=2,
|
||||
description="Generate speech from text",
|
||||
requires_success=True,
|
||||
),
|
||||
CreditActionType.SOUND_NORMALIZATION: CreditAction(
|
||||
action_type=CreditActionType.SOUND_NORMALIZATION,
|
||||
cost=1,
|
||||
description="Normalize audio levels",
|
||||
requires_success=True,
|
||||
),
|
||||
CreditActionType.API_REQUEST: CreditAction(
|
||||
action_type=CreditActionType.API_REQUEST,
|
||||
cost=1,
|
||||
description="API request (rate limiting)",
|
||||
requires_success=False, # Charged even if request fails
|
||||
),
|
||||
CreditActionType.PLAYLIST_CREATION: CreditAction(
|
||||
action_type=CreditActionType.PLAYLIST_CREATION,
|
||||
cost=3,
|
||||
description="Create a new playlist",
|
||||
requires_success=True,
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
def get_credit_action(action_type: CreditActionType) -> CreditAction:
|
||||
"""Get a credit action definition by type.
|
||||
|
||||
Args:
|
||||
action_type: The action type to look up
|
||||
|
||||
Returns:
|
||||
The credit action definition
|
||||
|
||||
Raises:
|
||||
KeyError: If action type is not found
|
||||
|
||||
"""
|
||||
return CREDIT_ACTIONS[action_type]
|
||||
|
||||
|
||||
def get_all_credit_actions() -> dict[CreditActionType, CreditAction]:
|
||||
"""Get all available credit actions.
|
||||
|
||||
Returns:
|
||||
Dictionary of all credit actions
|
||||
|
||||
"""
|
||||
return CREDIT_ACTIONS.copy()
|
||||
29
app/models/credit_transaction.py
Normal file
29
app/models/credit_transaction.py
Normal file
@@ -0,0 +1,29 @@
|
||||
"""Credit transaction model for tracking credit usage."""
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from sqlmodel import Field, Relationship
|
||||
|
||||
from app.models.base import BaseModel
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from app.models.user import User
|
||||
|
||||
|
||||
class CreditTransaction(BaseModel, table=True):
|
||||
"""Database model for credit transactions."""
|
||||
|
||||
__tablename__ = "credit_transaction" # pyright: ignore[reportAssignmentType]
|
||||
|
||||
user_id: int = Field(foreign_key="user.id", nullable=False)
|
||||
action_type: str = Field(nullable=False)
|
||||
amount: int = Field(nullable=False) # Negative for deductions, positive for additions
|
||||
balance_before: int = Field(nullable=False)
|
||||
balance_after: int = Field(nullable=False)
|
||||
description: str = Field(nullable=False)
|
||||
success: bool = Field(nullable=False, default=True)
|
||||
# JSON string for additional data
|
||||
metadata_json: str | None = Field(default=None)
|
||||
|
||||
# relationships
|
||||
user: "User" = Relationship(back_populates="credit_transactions")
|
||||
@@ -6,6 +6,7 @@ from sqlmodel import Field, Relationship
|
||||
from app.models.base import BaseModel
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from app.models.credit_transaction import CreditTransaction
|
||||
from app.models.extraction import Extraction
|
||||
from app.models.plan import Plan
|
||||
from app.models.playlist import Playlist
|
||||
@@ -35,3 +36,4 @@ class User(BaseModel, table=True):
|
||||
playlists: list["Playlist"] = Relationship(back_populates="user")
|
||||
sounds_played: list["SoundPlayed"] = Relationship(back_populates="user")
|
||||
extractions: list["Extraction"] = Relationship(back_populates="user")
|
||||
credit_transactions: list["CreditTransaction"] = Relationship(back_populates="user")
|
||||
|
||||
Reference in New Issue
Block a user