major-domo-database/app/services/interfaces.py
root e5452cf0bf refactor: Add dependency injection for testability
- Created ServiceConfig for dependency configuration
- Created Abstract interfaces (Protocols) for mocking
- Created MockPlayerRepository, MockTeamRepository, MockCacheService
- Refactored BaseService and PlayerService to accept injectable dependencies
- Added pytest configuration and unit tests
- Tests can run without real database (uses mocks)

Benefits:
- Unit tests run in seconds without DB
- Easy to swap implementations
- Clear separation of concerns
2026-02-03 15:59:04 +00:00

112 lines
2.4 KiB
Python

"""
Abstract Base Classes (Protocols) for Dependency Injection
Defines interfaces that can be mocked for testing.
"""
from typing import List, Dict, Any, Optional, Protocol
class PlayerData(Dict):
"""Player data structure matching Peewee model."""
pass
class TeamData(Dict):
"""Team data structure matching Peewee model."""
pass
class QueryResult(Protocol):
"""Protocol for query-like objects."""
def where(self, *conditions) -> 'QueryResult':
...
def order_by(self, *fields) -> 'QueryResult':
...
def count(self) -> int:
...
def __iter__(self):
...
def __len__(self) -> int:
...
class CacheProtocol(Protocol):
"""Protocol for cache operations."""
def get(self, key: str) -> Optional[str]:
...
def setex(self, key: str, ttl: int, value: str) -> bool:
...
def keys(self, pattern: str) -> List[str]:
...
def delete(self, *keys: str) -> int:
...
class AbstractPlayerRepository(Protocol):
"""Abstract interface for player data access."""
def select_season(self, season: int) -> QueryResult:
...
def get_by_id(self, player_id: int) -> Optional[PlayerData]:
...
def get_or_none(self, *conditions) -> Optional[PlayerData]:
...
def update(self, data: Dict, *conditions) -> int:
...
def insert_many(self, data: List[Dict]) -> int:
...
def delete_by_id(self, player_id: int) -> int:
...
class AbstractTeamRepository(Protocol):
"""Abstract interface for team data access."""
def select_season(self, season: int) -> QueryResult:
...
def get_by_id(self, team_id: int) -> Optional[TeamData]:
...
def get_or_none(self, *conditions) -> Optional[TeamData]:
...
def update(self, data: Dict, *conditions) -> int:
...
def insert_many(self, data: List[Dict]) -> int:
...
def delete_by_id(self, team_id: int) -> int:
...
class AbstractCacheService(Protocol):
"""Abstract interface for cache operations."""
def get(self, key: str) -> Optional[str]:
...
def set(self, key: str, value: str, ttl: int = 300) -> bool:
...
def invalidate_pattern(self, pattern: str) -> int:
...
def exists(self, key: str) -> bool:
...