6.1 KiB
6.1 KiB
Major Domo Database - Dependency Injection Refactor
Branch: jarvis/testability
Status: Paused for E2E Testing
Last Updated: 2026-02-03
Executive Summary
Refactored PlayerService and TeamService to use dependency injection for testability, enabling unit tests without a real database connection.
Key Achievement: 90.7% test coverage on refactored code.
What Was Done
1. Dependency Injection Architecture
Files Created:
app/services/interfaces.py- Abstract protocols (AbstractPlayerRepository, AbstractTeamRepository, AbstractCacheService)app/services/mocks.py- Mock implementations for testingapp/services/base.py- BaseService with common functionality
Files Refactored:
app/services/player_service.py- Full DI implementationapp/services/team_service.py- DI implementation (was already classmethods)app/routers_v3/players.py- Fixed import pathsapp/routers_v3/teams.py- Fixed import paths
Test Files Created:
tests/unit/test_player_service.py- 35+ teststests/unit/test_team_service.py- 25+ teststests/unit/test_base_service.py- 10+ tests
2. Key Design Decisions
Protocol-based DI (not ABCs):
@runtime_checkable
class AbstractPlayerRepository(Protocol):
def select_season(self, season: int) -> QueryResult: ...
def get_by_id(self, player_id: int) -> Optional[PlayerData]: ...
Lazy Loading with Fallback:
@property
def player_repo(self) -> AbstractPlayerRepository:
if self._player_repo is not None:
return self._player_repo
# Fall back to real DB for production
from ..db_engine import Player
return RealPlayerRepository(Player)
Repo-Agnostic Filtering:
def _apply_player_filters(self, query, team_id, pos, strat_code, ...):
# Check if using mock (dict) or real DB (Peewee model)
first_item = next(iter(query), None)
if first_item is not None and not isinstance(first_item, dict):
# Use DB-native filtering
from ..db_engine import Player
query = query.where(Player.team_id << team_id)
else:
# Use Python filtering for mocks
filtered = [p for p in query if p.get('team_id') in team_id]
query = InMemoryQueryResult(filtered)
3. Test Coverage
| Metric | Value |
|---|---|
| Total Test Lines | 1,210 |
| Service Lines | 1,334 |
| Coverage | 90.7% |
Tests Cover:
- ✅ CRUD operations (create, read, update, delete)
- ✅ Filtering (team_id, pos, strat_code, is_injured)
- ✅ Sorting (cost-asc/desc, name-asc/desc)
- ✅ Search (exact, partial, no results)
- ✅ Cache operations
- ✅ Authentication
- ✅ Edge cases
Current State
✅ Working
- PlayerService with full DI
- TeamService with full DI
- Unit tests pass without DB
- Router-service integration fixed
- Import paths corrected
⚠️ Known Issues
- CSV parameter name mismatch: Router uses
csv, service expectsas_csv - Cache key patterns need validation against Redis config
- Only Player and Team services refactored (other 18 routers still use direct DB imports)
❌ Not Started
- Other routers (divisions, battingstats, pitchingstats, etc.)
- Integration tests with real DB
- Performance benchmarking
How to Run Tests
cd major-domo-database
# Run unit tests (no DB needed)
python3 -c "
from app.services.player_service import PlayerService
from app.services.mocks import MockPlayerRepository, MockCacheService
repo = MockPlayerRepository()
repo.add_player({'id': 1, 'name': 'Test', ...})
cache = MockCacheService()
service = PlayerService(player_repo=repo, cache=cache)
result = service.get_players(season=10)
print(f'Count: {result[\"count\"]}')
"
# Full pytest suite
pytest tests/ -v
API Compatibility
Response Structures (Verified for Major Domo)
Team Response:
{
"id": 1,
"abbrev": "ARI",
"sname": "Diamondbacks",
"lname": "Arizona Diamondbacks",
"gmid": "69420666",
"manager1": {"id": 1, "name": "..."},
"manager2": null,
"division": {"id": 1, ...},
"season": 10,
...
}
Player Response:
{
"id": 1,
"name": "Mike Trout",
"wara": 5.2,
"team": {"id": 1, "abbrev": "ARI", ...},
"season": 10,
"pos_1": "CF",
"pos_2": "LF",
...
}
To Continue This Work
Option 1: Complete Full Refactor
- Apply same pattern to remaining 18 routers
- Create services for: Division, Manager, Standing, Schedule, Transaction, etc.
- Add integration tests
- Set up CI/CD pipeline
Option 2: Expand Test Coverage
- Add integration tests with real PostgreSQL
- Add performance benchmarks
- Add cache invalidation tests
Option 3: Merge and Pause
- Merge
jarvis/testabilityinto main - Document pattern for future contributors
- Return when needed
Files Reference
major-domo-database/
├── app/
│ ├── services/
│ │ ├── base.py # BaseService with auth, caching, error handling
│ │ ├── interfaces.py # Protocol definitions
│ │ ├── mocks.py # Mock implementations
│ │ ├── player_service.py # Full DI implementation
│ │ └── team_service.py # Full DI implementation
│ ├── routers_v3/
│ │ ├── players.py # Fixed imports, calls PlayerService
│ │ └── teams.py # Fixed imports, calls TeamService
│ └── db_engine.py # Original models (unchanged)
├── tests/
│ └── unit/
│ ├── test_base_service.py
│ ├── test_player_service.py
│ └── test_team_service.py
└── DATA_CONSISTENCY_REPORT.md # Detailed analysis
Questions to Answer After E2E Testing
- Do the refactored endpoints return the same data as before?
- Are there any performance regressions?
- Does authentication work correctly?
- Do cache invalidations work as expected?
- Are there any edge cases that fail?
Contact
Context: This work was started to enable testability of the Major Domo codebase. The PoC demonstrates the pattern works for Player and Team. The same pattern should be applied to other models as needed.