- Add UserRepository and LinkedAccountRepository protocols to protocols.py
- Add UserEntry and LinkedAccountEntry DTOs for service layer decoupling
- Implement PostgresUserRepository and PostgresLinkedAccountRepository
- Refactor UserService to use constructor-injected repositories
- Add get_user_service factory and UserServiceDep to API deps
- Update auth.py and users.py endpoints to use UserServiceDep
- Rewrite tests to use FastAPI dependency overrides (no monkey patching)
This follows the established repository pattern used by DeckService and
CollectionService, enabling future offline fork support.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Three changes to fail fast instead of silently degrading:
1. GameService.create_game: Raise GameCreationError when energy card
definition not found instead of logging warning and continuing.
A deck with missing energy cards is fundamentally broken.
2. CardService.load_all: Collect all card file load failures and raise
CardServiceLoadError at end with comprehensive error report. Prevents
startup with partial card data that causes cryptic runtime errors.
New exceptions: CardLoadError, CardServiceLoadError
3. GameStateManager.recover_active_games: Return RecoveryResult dataclass
with recovered count, failed game IDs with error messages, and total.
Enables proper monitoring and alerting for corrupted game state.
Tests added for energy card error case. Existing tests updated for
new RecoveryResult return type.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Create tests/conftest.py with testcontainers for Postgres and Redis
- Auto-detect Docker Desktop socket and disable Ryuk for compatibility
- Update tests/db/conftest.py and tests/services/conftest.py to use shared fixtures
- Fix test_resolve_effect_logs_exceptions: logger was disabled by pytest
- Fix test_save_and_load_with_real_redis: use redis_url fixture
- Minor lint fix in engine_validation.py
Tests now auto-start containers on run - no need for `docker compose up`
All 1199 tests passing.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
UNSET sentinel pattern:
- Add UNSET sentinel in protocols.py for nullable field updates
- Fix inability to clear deck description (UNSET=keep, None=clear)
- Fix repository inability to clear validation_errors
Starter deck improvements:
- Remove unused has_starter_deck from CollectionService
- Add deprecation notes to old starter deck methods
Validation improvements:
- Add energy type validation in deck_validator.py
- Add energy type validation in deck schemas
- Add VALID_ENERGY_TYPES constant
Game loading fix:
- Fix get_deck_for_game silently skipping invalid cards
- Now raises ValueError with clear error message
Tests:
- Add TestEnergyTypeValidation test class
- Add TestGetDeckForGame test class
- Add tests for validate_energy_types utility function
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Critical fixes:
- Add admin API key authentication for admin endpoints
- Add race condition protection via unique partial index for starter decks
- Make starter deck selection atomic with combined method
Moderate fixes:
- Fix DI pattern violation in validate_deck_endpoint
- Add card ID format validation (regex pattern)
- Add card quantity validation (1-99 range)
- Fix exception chaining with from None (B904)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Complete OAuth-based authentication with JWT session management:
Core Services:
- JWT service for access/refresh token creation and verification
- Token store with Redis-backed refresh token revocation
- User service for CRUD operations and OAuth-based creation
- Google and Discord OAuth services with full flow support
API Endpoints:
- GET /api/auth/{google,discord} - Start OAuth flows
- GET /api/auth/{google,discord}/callback - Handle OAuth callbacks
- POST /api/auth/refresh - Exchange refresh token for new access token
- POST /api/auth/logout - Revoke single refresh token
- POST /api/auth/logout-all - Revoke all user sessions
- GET/PATCH /api/users/me - User profile management
- GET /api/users/me/linked-accounts - List OAuth providers
- GET /api/users/me/sessions - Count active sessions
Infrastructure:
- Pydantic schemas for auth/user request/response models
- FastAPI dependencies (get_current_user, get_current_premium_user)
- OAuthLinkedAccount model for multi-provider support
- Alembic migration for oauth_linked_accounts table
Dependencies added: email-validator, fakeredis (dev), respx (dev)
84 new tests, 1058 total passing
- Rename data/cards/ to data/raw/ for scraped data
- Add data/definitions/ as authoritative card data source
- Add convert_cards.py script to transform raw -> definitions
- Generate 378 card definitions (344 Pokemon, 24 Trainers, 10 Energy)
- Add CardService for loading and querying card definitions
- In-memory indexes for fast lookups by type, set, pokemon_type
- search() with multiple filter criteria
- get_all_cards() for GameEngine integration
- Add SetInfo model for set metadata
- Update Attack model with damage_display field for variable damage
- Update CardDefinition with image_path, illustrator, flavor_text
- Add 45 tests (21 converter + 24 CardService)
- Update scraper output path to data/raw/
Card data is JSON-authoritative (no database) to support offline fork goal.