major-domo-database/app/services/interfaces.py
root bcec206bb4 fix: Complete dependency injection for PlayerService
- Moved peewee/fastapi imports inside methods to enable testing without DB
- Added InMemoryQueryResult for mock-compatible filtering/sorting
- Updated interfaces with @runtime_checkable for isinstance() checks
- Fixed get_or_none() to accept keyword arguments
- _player_to_dict() now handles both dicts and Peewee models

Result: All 14 tests pass without database connection.
Service can now be fully tested with MockPlayerRepository.
2026-02-03 16:49:50 +00:00

109 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, runtime_checkable
class PlayerData(Dict):
"""Player data structure matching Peewee model."""
pass
class TeamData(Dict):
"""Team data structure matching Peewee model."""
pass
@runtime_checkable
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:
...
@runtime_checkable
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, **field_conditions) -> Optional[PlayerData]:
...
def update(self, data: Dict, *conditions, **field_conditions) -> int:
...
def insert_many(self, data: List[Dict]) -> int:
...
def delete_by_id(self, player_id: int) -> int:
...
@runtime_checkable
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, **field_conditions) -> Optional[TeamData]:
...
def update(self, data: Dict, *conditions, **field_conditions) -> int:
...
def insert_many(self, data: List[Dict]) -> int:
...
def delete_by_id(self, team_id: int) -> int:
...
@runtime_checkable
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 setex(self, key: str, ttl: int, value: str) -> bool:
...
def keys(self, pattern: str) -> List[str]:
...
def delete(self, *keys: str) -> int:
...
def invalidate_pattern(self, pattern: str) -> int:
...
def exists(self, key: str) -> bool:
...