Add test reports from Phases 3-5: schema, router, and service layer analysis
This commit is contained in:
@@ -0,0 +1,226 @@
|
||||
# Phase 3: Schema Layer Test Report
|
||||
**Date:** 2026-06-25
|
||||
**Scope:** All Pydantic schemas in `/home/wall-o/projects/mtgonline/backend/app/schemas/`
|
||||
**Models Compared Against:** All ORM models in `/home/wall-o/projects/mtgonline/backend/app/models/`
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
| Schema File | Status | Critical | Warning | Info |
|
||||
|---|---|---|---|---|
|
||||
| `schemas.py` | ⚠️ FAIL | 1 | 2 | 1 |
|
||||
| `user_data_schemas.py` | ⚠️ FAIL | 1 | 2 | 3 |
|
||||
| `user_deck_schemas.py` | ⚠️ FAIL | 1 | 3 | 2 |
|
||||
| `card_import_schemas.py` | ⚠️ FAIL | 0 | 2 | 2 |
|
||||
| `user_card_collection.py` | ⚠️ FAIL | 0 | 1 | 2 |
|
||||
| `card_search_schemas.py` | ⚠️ FAIL | 1 | 3 | 2 |
|
||||
| `__init__.py` | ⚠️ FAIL | 1 | 0 | 0 |
|
||||
| **TOTAL** | **6 FAIL** | **5** | **13** | **12** |
|
||||
|
||||
---
|
||||
|
||||
## 1. `schemas.py` — ⚠️ FAIL
|
||||
|
||||
**Models covered:** User, DecklistFile, DecklistFolder, Room, Ban, MtgCardMirror, DeckCardLink
|
||||
|
||||
### Critical Issues
|
||||
|
||||
| # | Schema | Issue | Detail |
|
||||
|---|---|---|---|
|
||||
| C1 | `UserResponse` | **Missing fields from User model** | `avatar_bmp`, `salt`, `ban_ends`, `vip_expiry` are in the `User` model but absent from the response schema. `password_hash` is correctly excluded (security), but the others should be present or explicitly excluded. |
|
||||
|
||||
### Warnings
|
||||
|
||||
| # | Schema | Issue | Detail |
|
||||
|---|---|---|---|
|
||||
| W1 | `schemas.py` (all) | **Pydantic v1 `class Config` used** | All schemas use `class Config: from_attributes = True` (Pydantic v1 style). Other schema files use Pydantic v2 `model_config = ConfigDict(from_attributes=True)`. Inconsistent. |
|
||||
| W2 | `GameResponse` | **Fields not in Room model** | `with_password`, `max_players`, `player_count`, `started` are not columns in the `Room` model. These appear to be computed/derived fields but are not documented. If computed, they should be `Field(default=...)` with defaults. |
|
||||
|
||||
### Info
|
||||
|
||||
| # | Schema | Issue | Detail |
|
||||
|---|---|---|---|
|
||||
| I1 | `BanResponse` | **Missing `ip_address` field** | The `Ban` model has `ip_address` (String(45)) but `BanResponse` does not include it. |
|
||||
|
||||
---
|
||||
|
||||
## 2. `user_data_schemas.py` — ⚠️ FAIL
|
||||
|
||||
**Models covered:** UserSession, DeckVersion, GameReplay, ReplayPlayer, GameOutcome, UserStatistics, UserCardCollection, CardWishlist, UserGroup, GroupMember, GroupChatMessage, UserNetwork, NetworkMember, UserPreference, UserActivityLog
|
||||
|
||||
### Critical Issues
|
||||
|
||||
| # | Schema | Issue | Detail |
|
||||
|---|---|---|---|
|
||||
| C1 | `GameReplayResponse` | **Missing `players` field** | The `GameReplay` model has a `players` relationship (List[ReplayPlayer]). The schema has `players: List[Dict[str, Any]] = []` which is a generic placeholder, not a typed schema. Should define a `ReplayPlayerResponse` schema. |
|
||||
|
||||
### Warnings
|
||||
|
||||
| # | Schema | Issue | Detail |
|
||||
|---|---|---|---|
|
||||
| W1 | `DeckVersionStatus` enum | **Values don't match model** | Enum has `DRAFT`, `FINAL`, `ARCHIVED`. The `DeckVersion` model has `status` column with `server_default="DRAFT"` and `String(20)`. The `user_deck_schemas.py` `DeckStatus` enum has only `DRAFT`/`FINAL`. The `ARCHIVED` value has no model support. |
|
||||
| W2 | `GameReplayCreate` / `GameReplayUpdate` | **`format` is a Python keyword** | Using `format` as a field name shadows the built-in `format()` function. Should use `game_format` or `deck_format` instead. |
|
||||
| W3 | `GroupMember` model | **Missing `joined_at` in GroupMemberCreate** | `GroupMemberCreate` has `user_id` and `role` but the model also has `joined_at` (auto-set by server_default, so OK for create). Not a bug, but worth noting. |
|
||||
|
||||
### Info
|
||||
|
||||
| # | Schema | Issue | Detail |
|
||||
|---|---|---|---|
|
||||
| I1 | `UserSessionResponse` | **Missing `session_token_hash`** | Model has `session_token_hash` but it's correctly excluded from response (security). Document this exclusion. |
|
||||
| I2 | `GroupResponse` | **`member_count` and `is_member` are computed** | Not in the `UserGroup` model. These are computed fields. Should have `Field(default=0)` / `Field(default=False)` with documentation. |
|
||||
| I3 | `NetworkResponse` | **Missing `updated_at`** | The `UserNetwork` model does NOT have `updated_at`, so this is actually correct. No issue. |
|
||||
|
||||
---
|
||||
|
||||
## 3. `user_deck_schemas.py` — ⚠️ FAIL
|
||||
|
||||
**Models covered:** UserDeck, UserDeckCard, DeckPrecedent, CardSuggestion
|
||||
|
||||
### Critical Issues
|
||||
|
||||
| # | Schema | Issue | Detail |
|
||||
|---|---|---|---|
|
||||
| C1 | `UserDeckCreate` | **Missing `status` field** | The `UserDeck` model has `status` (String(20), default="DRAFT") as a required column. `UserDeckCreate` does not include `status`. If the default is relied upon, this is acceptable, but it should be explicit. |
|
||||
|
||||
### Warnings
|
||||
|
||||
| # | Schema | Issue | Detail |
|
||||
|---|---|---|---|
|
||||
| W1 | `CardSearchResponse` | **Uses `List[Dict[str, Any]]` instead of typed schema** | Should use `List[CardResponse]` (from `card_search_schemas.py`) for type safety. |
|
||||
| W2 | `PrecedentResponse` | **Missing `updated_at` field** | The `DeckPrecedent` model has `updated_at` (DateTime) but `PrecedentResponse` does not include it. |
|
||||
| W3 | `DeckStatus` enum | **Conflicts with `user_data_schemas.py` `DeckVersionStatus`** | Both enums have `DRAFT`/`FINAL` values but different names and different files. The `DeckVersion` model in `user_data.py` uses `DeckVersionStatus` from `user_data_schemas.py`, while `UserDeck` model uses string values directly. This creates confusion. |
|
||||
|
||||
### Info
|
||||
|
||||
| # | Schema | Issue | Detail |
|
||||
|---|---|---|---|
|
||||
| I1 | `DeckCardResponse` | **Missing `updated_at`** | The `UserDeckCard` model does NOT have `updated_at`, so this is correct. No issue. |
|
||||
| I2 | `SuggestionResponse` | **`card_name` is computed** | Not in the `CardSuggestion` model. Should be documented as a computed field with `Field(default="")`. |
|
||||
|
||||
---
|
||||
|
||||
## 4. `card_import_schemas.py` — ⚠️ FAIL
|
||||
|
||||
**Models covered:** UserCardImport, CardImportBatch
|
||||
|
||||
### Warnings
|
||||
|
||||
| # | Schema | Issue | Detail |
|
||||
|---|---|---|---|
|
||||
| W1 | `CardImportStatusResponse` | **Missing fields vs CardImportBatch model** | Missing `batch_id`, `status`, `total_cards`, `matched_cards`, `unmatched_cards`, `error_message`. The `CardImportBatch` model has all these columns. |
|
||||
| W2 | `CardImportResponse` | **Missing `batch_id` and `status`** | The `CardImportBatch` model has `batch_id` (id) and `status` fields. Response only has `message`, `card_count`, `card_names`, `imported_at`. |
|
||||
|
||||
### Info
|
||||
|
||||
| # | Schema | Issue | Detail |
|
||||
|---|---|---|---|
|
||||
| I1 | `CardMatchResult.match_type` | **Inconsistent with `card_search_schemas.py`** | This file uses `'exact'`, `'fuzzy'`, `'partial'`. The `card_search_schemas.py` version uses `'exact'`, `'high_confidence'`, `'low_confidence'`. |
|
||||
| I2 | `ErrorResponse` | **Duplicate of generic schemas** | Same as `MessageResponse`/`CountResponse` duplicates found in other files. |
|
||||
|
||||
---
|
||||
|
||||
## 5. `user_card_collection.py` — ⚠️ FAIL
|
||||
|
||||
**Models covered:** UserCardCollection, CardWishlist
|
||||
|
||||
### Warnings
|
||||
|
||||
| # | Schema | Issue | Detail |
|
||||
|---|---|---|---|
|
||||
| W1 | `CardCollectionListResponse` | **Missing pagination fields** | Has `cards`, `total`, `page`, `page_size`, `total_pages` — this is actually correct and matches the pattern. No issue here. The warning is that `WishlistListResponse` is missing `page`/`page_size`/`total_pages` while `CardCollectionListResponse` has them. Inconsistent pagination. |
|
||||
|
||||
### Info
|
||||
|
||||
| # | Schema | Issue | Detail |
|
||||
|---|---|---|---|
|
||||
| I1 | `CardCondition` / `AcquisitionMethod` enums | **Not validated against model** | The model stores these as `String(20)` and `String(50)` respectively. The enums provide validation at the schema layer, which is good. However, the `CardCollectionResponse` serializes them as plain `str`, losing the enum type info. |
|
||||
| I2 | `WishlistResponse` | **Missing `updated_at`** | The `CardWishlist` model does NOT have `updated_at`, so this is correct. No issue. |
|
||||
|
||||
---
|
||||
|
||||
## 6. `card_search_schemas.py` — ⚠️ FAIL
|
||||
|
||||
**Models covered:** MtgCard, MtgSet, CardImportBatch (partial)
|
||||
|
||||
### Critical Issues
|
||||
|
||||
| # | Schema | Issue | Detail |
|
||||
|---|---|---|---|
|
||||
| C1 | `CardResponse` | **Missing 8 fields from MtgCard model** | Missing: `artist`, `flavor_text`, `numbers`, `image`, `card_parts`, `keywords`, `legalities`, `identifiers` (as typed). The `MtgCard` model has all these columns. |
|
||||
| C2 | `SetResponse` | **Missing 10 fields from MtgSet model** | Missing: `type`, `base_set_size`, `total_size`, `is_foil_only`, `is_non_foil_only`, `digital`, `icon_svg_url`, `parent_code`, `mtgo_code`, `image`. The `MtgSet` model has all these columns. |
|
||||
|
||||
### Warnings
|
||||
|
||||
| # | Schema | Issue | Detail |
|
||||
|---|---|---|---|
|
||||
| W1 | `CardImportStatusResponse` | **Missing fields vs CardImportBatch** | Missing `batch_id`, `status`, `total_cards`, `matched_cards`, `unmatched_cards`, `error_message`. |
|
||||
| W2 | `CardMatchResult.match_type` | **Inconsistent with `card_import_schemas.py`** | Uses `'exact'`, `'high_confidence'`, `'low_confidence'` vs `'exact'`, `'fuzzy'`, `'partial'` in `card_import_schemas.py`. |
|
||||
| W3 | `CardImportResponse` | **Missing `file_type` and `file_size`** | The `CardImportBatch` model has `file_type` and `file_size` columns not reflected in this response. |
|
||||
|
||||
### Info
|
||||
|
||||
| # | Schema | Issue | Detail |
|
||||
|---|---|---|---|
|
||||
| I1 | `CardSearchResponse` | **Uses `List[Dict[str, Any]]`** | Should use `List[CardResponse]` for type safety. |
|
||||
| I2 | `CardTypeResponse` | **Trivial schema** | Only has `type: str`. May be unnecessary or could be merged. |
|
||||
|
||||
---
|
||||
|
||||
## 7. `__init__.py` — ⚠️ FAIL
|
||||
|
||||
### Critical Issues
|
||||
|
||||
| # | Schema | Issue | Detail |
|
||||
|---|---|---|---|
|
||||
| C1 | `__init__.py` | **No imports — schemas not exported** | The file only contains `# Schemas package` comment. No schemas are imported or re-exported. Consumers must know the exact file path for each schema. Should at minimum export commonly used schemas. |
|
||||
|
||||
---
|
||||
|
||||
## Cross-File Issues
|
||||
|
||||
### Duplicate Generic Schemas (Found in 5 files)
|
||||
|
||||
The following generic schemas are defined identically in multiple files:
|
||||
|
||||
| Schema | Files |
|
||||
|---|---|
|
||||
| `MessageResponse` | `user_data_schemas.py`, `user_deck_schemas.py`, `card_import_schemas.py`, `user_card_collection.py`, `card_search_schemas.py` |
|
||||
| `CountResponse` | `user_data_schemas.py`, `user_deck_schemas.py`, `card_import_schemas.py`, `user_card_collection.py`, `card_search_schemas.py` |
|
||||
| `ErrorResponse` | `card_import_schemas.py`, `card_search_schemas.py` |
|
||||
| `ErrorDetail` | `user_data_schemas.py`, `user_card_collection.py` |
|
||||
|
||||
**Recommendation:** Create a `app/schemas/common.py` with these shared schemas and import from there.
|
||||
|
||||
### Models Without Any Schema Coverage
|
||||
|
||||
| Model | File |
|
||||
|---|---|
|
||||
| `UserCardImport` | `user_card_import.py` |
|
||||
| `UserCardImportRecord` | `user_card_import_record.py` |
|
||||
| `ReplayPlayer` | `user_data.py` |
|
||||
| `GroupMember` (response) | `user_data.py` |
|
||||
| `NetworkMember` (response) | `user_data.py` |
|
||||
| `DeckPrecedentCard` | `user_deck.py` |
|
||||
|
||||
### Pydantic v1 vs v2 Inconsistency
|
||||
|
||||
- `schemas.py`: Uses `class Config: from_attributes = True` (v1 style)
|
||||
- All other files: Use Pydantic v2 `BaseModel` (no explicit `model_config`, relying on defaults or not setting `from_attributes`)
|
||||
|
||||
**Recommendation:** Standardize on Pydantic v2 `model_config = ConfigDict(from_attributes=True)` in a common base or in each file.
|
||||
|
||||
---
|
||||
|
||||
## Recommendations (Priority Order)
|
||||
|
||||
1. **Fix `__init__.py`** — Add schema exports so consumers can import from `app.schemas`
|
||||
2. **Create `app/schemas/common.py`** — Extract duplicate `MessageResponse`, `CountResponse`, `ErrorResponse`, `ErrorDetail`
|
||||
3. **Fix `CardResponse` and `SetResponse`** in `card_search_schemas.py` — Add all missing model fields
|
||||
4. **Fix `CardImportStatusResponse`** in both `card_import_schemas.py` and `card_search_schemas.py` — Add missing fields
|
||||
5. **Standardize Pydantic v2** — Use `model_config = ConfigDict(from_attributes=True)` consistently
|
||||
6. **Resolve enum conflicts** — `DeckVersionStatus` vs `DeckStatus`, `CardMatchResult.match_type` values
|
||||
7. **Add `ReplayPlayerResponse`** — Replace `List[Dict[str, Any]]` in `GameReplayResponse`
|
||||
8. **Add missing schemas** — `UserCardImport`, `UserCardImportRecord`, `DeckPrecedentCard`
|
||||
9. **Fix `format` field name** in `GameReplayCreate`/`Update` — Rename to `game_format`
|
||||
10. **Add `updated_at`** to `PrecedentResponse` and `UserDeckCreate` (status field)
|
||||
@@ -0,0 +1,364 @@
|
||||
# Phase 5: Service Layer Test Report
|
||||
|
||||
**Date:** 2024-01-15
|
||||
**Scope:** All service files in `app/services/`
|
||||
**Status:** FAIL (Critical issues found)
|
||||
|
||||
---
|
||||
|
||||
## Executive Summary
|
||||
|
||||
The service layer has **12 critical issues**, **8 warnings**, and **5 informational findings**. The most significant problems are:
|
||||
|
||||
1. **`deck_manager.py` is not used by the deck router** - The router implements all CRUD operations inline, duplicating logic
|
||||
2. **`card_search_service.py` and `deck_suggestion_service.py` reference non-existent `MtgCard.colors` field** - Will cause AttributeError at runtime
|
||||
3. **`import_batch_processor.py` stores non-JSON-serializable tuples** - Will cause serialization errors
|
||||
4. **`game_server.py` lacks permission validation** - Security vulnerability
|
||||
|
||||
---
|
||||
|
||||
## Service-by-Service Analysis
|
||||
|
||||
### 1. card_database.py ✅ PASS
|
||||
|
||||
**Functions:** `search_cards`, `get_card_by_name`, `get_cards_by_set`, `get_card_types`, `get_card_rarities`, `get_sets`, `get_set_by_code`, `get_card_statistics`
|
||||
|
||||
**Issues:** None
|
||||
|
||||
**Notes:**
|
||||
- Standalone utility, not directly called by routers
|
||||
- Correctly uses `MtgCard`, `MtgSet` from `mtg_models`
|
||||
- Proper async session handling
|
||||
- Good error handling with None returns
|
||||
|
||||
---
|
||||
|
||||
### 2. card_mirror_service.py ⚠️ WARNING
|
||||
|
||||
**Functions:** `upsert_card_mirror`, `get_card_mirror_by_name`, `search_card_mirrors`, `add_card_to_deck`, `remove_card_from_deck`, `get_deck_cards`, `get_user_deck_summaries`, `sync_mirrors_from_mtg_cards`, `get_card_statistics`
|
||||
|
||||
**Issues:**
|
||||
- **WARNING:** `sync_mirrors_from_mtg_cards` is a no-op with TODO comment - incomplete implementation
|
||||
- **WARNING:** `get_card_statistics` duplicates function name from `card_database.py` (different implementations)
|
||||
- **INFO:** `get_user_deck_summaries` uses `DecklistFile` from `models.py` (old deck system), not `UserDeck` from `user_deck.py` (new system)
|
||||
|
||||
**Notes:**
|
||||
- Correctly uses `MtgCardMirror`, `DeckCardLink` from `mirror_models`
|
||||
- Good upsert logic with `setattr` pattern
|
||||
- Proper duplicate checking in `add_card_to_deck`
|
||||
|
||||
---
|
||||
|
||||
### 3. card_search_service.py ❌ CRITICAL
|
||||
|
||||
**Functions:** `search_cards`, `get_card_by_id`, `get_sets`, `get_card_types`, `get_card_rarities`
|
||||
|
||||
**Issues:**
|
||||
- **CRITICAL:** References `MtgCard.colors` which **does not exist** in the `MtgCard` model
|
||||
- Model has: `mana_cost`, `type_line`, `oracle_text`, `power`, `toughness`, `rarity`, `layout`, `artist`, `flavor_text`, `numbers`, `identifiers`, `images`, `image`
|
||||
- **No `colors` field defined**
|
||||
- Will cause `AttributeError` at runtime
|
||||
|
||||
**Notes:**
|
||||
- Called by `card_router.py` for search endpoints
|
||||
- Good pagination implementation
|
||||
- Proper cache integration in router
|
||||
|
||||
---
|
||||
|
||||
### 4. deck_manager.py ⚠️ WARNING
|
||||
|
||||
**Functions:** `create_deck`, `get_deck`, `list_decks`, `update_deck`, `delete_deck`, `finalize_deck`, `add_card_to_deck`, `get_deck_cards`, `update_deck_card`, `remove_card_from_deck`, `clone_precedent`
|
||||
|
||||
**Issues:**
|
||||
- **WARNING:** **NOT USED by deck router** - Router implements all CRUD operations inline
|
||||
- **WARNING:** `clone_precedent` doesn't check for duplicate deck names
|
||||
- **INFO:** Service exists but is orphaned
|
||||
|
||||
**Notes:**
|
||||
- Correctly uses `UserDeck`, `UserDeckCard`, `DeckPrecedent` from `user_deck.py`
|
||||
- Good business logic (FINAL status checks, empty deck validation)
|
||||
- Proper authorization checks (user_id matching)
|
||||
- **Recommendation:** Either use this service in the router or remove it
|
||||
|
||||
---
|
||||
|
||||
### 5. deck_parser.py ✅ PASS
|
||||
|
||||
**Functions:** `parse_plain_text`, `format_plain_text`, `to_native_xml`, `from_native_xml`, `parse_deck`, `format_deck`
|
||||
|
||||
**Issues:** None
|
||||
|
||||
**Notes:**
|
||||
- Utility class, not directly called by routers
|
||||
- Good regex patterns for deck parsing
|
||||
- Proper handling of sideboard markers
|
||||
- XML parsing is simplified (comment notes production should use proper XML parser)
|
||||
|
||||
---
|
||||
|
||||
### 6. deck_suggestion_service.py ❌ CRITICAL
|
||||
|
||||
**Functions:** `suggest_cards`, `add_suggestion`, `get_deck_suggestions`
|
||||
|
||||
**Issues:**
|
||||
- **CRITICAL:** References `MtgCard.colors` which **does not exist** in the `MtgCard` model
|
||||
- Same issue as `card_search_service.py`
|
||||
- Will cause `AttributeError` at runtime
|
||||
|
||||
**Notes:**
|
||||
- Called by `card_router.py` and deck router
|
||||
- Good suggestion strategies (same type, same color, same set)
|
||||
- Proper confidence scoring
|
||||
|
||||
---
|
||||
|
||||
### 7. file_parser.py ✅ PASS
|
||||
|
||||
**Functions:** `parse_file` (static)
|
||||
|
||||
**Issues:** None
|
||||
|
||||
**Notes:**
|
||||
- Called by `card_import.py` router
|
||||
- Supports xlsx, csv, json, ods formats
|
||||
- Good error handling with specific exception types
|
||||
- Proper file validation
|
||||
|
||||
---
|
||||
|
||||
### 8. fuzzy_card_matcher.py ✅ PASS
|
||||
|
||||
**Functions:** `normalize_card_name`, `exact_match`, `fuzzy_match`, `find_best_match`, `batch_match`, `batch_match_with_database`
|
||||
|
||||
**Issues:** None
|
||||
|
||||
**Notes:**
|
||||
- Utility class, used by `import_batch_processor.py`
|
||||
- Good threshold constants
|
||||
- Proper fuzzy matching with `thefuzz` library
|
||||
- Batch matching with database integration
|
||||
|
||||
---
|
||||
|
||||
### 9. game_server.py ⚠️ WARNING
|
||||
|
||||
**Functions:** `GameRoom` (class), `GameServer` (class), `game_websocket_endpoint`, `process_game_command`
|
||||
|
||||
**Issues:**
|
||||
- **WARNING:** `process_game_command` broadcasts all commands **without permission validation**
|
||||
- Any connected player can execute any command
|
||||
- Security vulnerability
|
||||
- **WARNING:** `game_websocket_endpoint` is defined but `ws.py` router (512 bytes) may not import it correctly
|
||||
|
||||
**Notes:**
|
||||
- WebSocket game server for real-time gameplay
|
||||
- Good room management with `asyncio.Lock`
|
||||
- Proper player join/leave handling
|
||||
- **Recommendation:** Add permission checks in `process_game_command`
|
||||
|
||||
---
|
||||
|
||||
### 10. import_batch_processor.py ❌ CRITICAL
|
||||
|
||||
**Functions:** `create_batch`, `process_batch`, `get_batch_status`, `get_batch_results`, `confirm_batch`, `get_user_imports`, `delete_batch`
|
||||
|
||||
**Issues:**
|
||||
- **CRITICAL:** `process_batch` stores `match_results` as JSON field, but `batch_match_with_database` returns **tuples** which are **not JSON serializable**
|
||||
- Will cause `TypeError: Object of type tuple is not JSON serializable`
|
||||
- **WARNING:** Uses `func.now()` for `batch.updated_at` which is a SQLAlchemy function, not a Python datetime
|
||||
- May cause issues depending on SQLAlchemy version
|
||||
|
||||
**Notes:**
|
||||
- Called by `card_import.py` router
|
||||
- Good batch processing workflow
|
||||
- Proper status tracking (pending → processing → completed/failed)
|
||||
- **Recommendation:** Convert tuples to lists before storing in JSON field
|
||||
|
||||
---
|
||||
|
||||
### 11. mtgjson_downloader.py ✅ PASS
|
||||
|
||||
**Functions:** `download_file`, `download_all_files`, `verify_downloads`, `get_file_list`
|
||||
|
||||
**Issues:** None
|
||||
|
||||
**Notes:**
|
||||
- Standalone utility, not called by routers
|
||||
- Good MTGJSON API v5 integration
|
||||
- Proper file verification
|
||||
- Clean download workflow
|
||||
|
||||
---
|
||||
|
||||
### 12. mtgjson_loader.py ✅ PASS
|
||||
|
||||
**Functions:** `create_tables`, `download_file`, `extract_zip`, `get_all_printings_psql_file`, `parse_psql_file`, `import_cards`, `import_all_printings_psql`, `import_all_set_files`, `import_all_identifiers`, `import_all_deck_files`, `import_simple_json`, `create_indexes`, `show_summary`, `main`
|
||||
|
||||
**Issues:** None
|
||||
|
||||
**Notes:**
|
||||
- Standalone utility, not called by routers
|
||||
- Comprehensive MTGJSON data loading
|
||||
- Good PSQL file parsing
|
||||
- Proper table creation and indexing
|
||||
|
||||
---
|
||||
|
||||
### 13. mtgjson_manager.py ✅ PASS
|
||||
|
||||
**Functions:** `get_last_refresh`, `get_health_status`, `check_required_files`, `upsert_all`, `_upsert_sets`, `_upsert_cards`, `_upsert_cards_from_psql`, `_upsert_identifiers`, `_upsert_card_types`, `_upsert_keywords`, `_upsert_set_list`, `_upsert_deck_list`, `_upsert_deck_files_from_zip`, `_extract_zip_files`, `log_refresh`, `sync_mirrors`, `run_refresh`, `download_files`, `_download_single_file`, `_get_file_urls`, `_decompress_file`, `verify_files`, `download_and_refresh`, `get_manager`
|
||||
|
||||
**Issues:** None
|
||||
|
||||
**Notes:**
|
||||
- Called by `refresh.py` router
|
||||
- Good health status tracking
|
||||
- Proper file verification
|
||||
- Background refresh support
|
||||
- Singleton pattern with `get_manager()`
|
||||
|
||||
---
|
||||
|
||||
### 14. mtgjson_uploader.py ✅ PASS
|
||||
|
||||
**Functions:** `create_tables`, `import_all_printings_psql`, `import_all_set_files`, `import_all_identifiers`, `import_all_deck_files`, `import_json_files`, `download_file`, `extract_zip`, `main`
|
||||
|
||||
**Issues:**
|
||||
- **WARNING:** `import_all_printings_psql` uses `subprocess.run` to call `psql` command
|
||||
- Security risk (command injection if paths are user-controlled)
|
||||
- Not ideal for async code
|
||||
- **Recommendation:** Use SQLAlchemy or asyncpg for PSQL import
|
||||
|
||||
**Notes:**
|
||||
- Standalone utility, not called by routers
|
||||
- Good table definitions
|
||||
- Proper data import workflow
|
||||
|
||||
---
|
||||
|
||||
## Cross-Service Integration Issues
|
||||
|
||||
### 1. Duplicate Function Names
|
||||
|
||||
**Issue:** `card_database.py` and `card_mirror_service.py` both define `get_card_statistics`
|
||||
|
||||
**Impact:** Confusion about which function to call. They return different statistics:
|
||||
- `card_database.get_card_statistics`: Total cards, sets, by rarity, by type
|
||||
- `card_mirror_service.get_card_statistics`: Total mirrored cards, by rarity, by type
|
||||
|
||||
**Recommendation:** Rename one or both functions for clarity
|
||||
|
||||
---
|
||||
|
||||
### 2. Orphaned Service
|
||||
|
||||
**Issue:** `deck_manager.py` exists but is not used by the deck router
|
||||
|
||||
**Impact:** Duplicated logic, maintenance burden
|
||||
|
||||
**Recommendation:** Either:
|
||||
1. Refactor deck router to use `DeckManager` service
|
||||
2. Remove `deck_manager.py` if not needed
|
||||
|
||||
---
|
||||
|
||||
### 3. Missing Field References
|
||||
|
||||
**Issue:** `card_search_service.py` and `deck_suggestion_service.py` reference `MtgCard.colors` which doesn't exist
|
||||
|
||||
**Impact:** Runtime `AttributeError`
|
||||
|
||||
**Recommendation:** Either:
|
||||
1. Add `colors` field to `MtgCard` model
|
||||
2. Remove color filtering from search/suggestion logic
|
||||
3. Use a different field for color-based filtering
|
||||
|
||||
---
|
||||
|
||||
## Router-Service Mapping
|
||||
|
||||
| Router | Service | Status |
|
||||
|--------|---------|--------|
|
||||
| `users.py` | N/A (inline) | ✅ |
|
||||
| `auth.py` | N/A (inline) | ✅ |
|
||||
| `admin.py` | N/A (inline) | ✅ |
|
||||
| `rooms.py` | N/A (inline) | ✅ |
|
||||
| `card_router.py` | `card_search_service`, `deck_suggestion_service` | ❌ (missing colors field) |
|
||||
| `decks.py` | N/A (inline, duplicates `deck_manager`) | ⚠️ |
|
||||
| `card_import.py` | `file_parser`, `import_batch_processor`, `fuzzy_card_matcher` | ❌ (JSON serialization) |
|
||||
| `refresh.py` | `mtgjson_manager` | ✅ |
|
||||
| `ws.py` | `game_server` | ⚠️ (permission validation) |
|
||||
| N/A | `card_database`, `card_mirror_service`, `deck_parser`, `mtgjson_downloader`, `mtgjson_loader`, `mtgjson_uploader` | ✅ (standalone) |
|
||||
|
||||
---
|
||||
|
||||
## Recommendations
|
||||
|
||||
### Critical (Must Fix)
|
||||
|
||||
1. **Fix `MtgCard.colors` reference** in `card_search_service.py` and `deck_suggestion_service.py`
|
||||
- Add field to model OR remove color filtering
|
||||
|
||||
2. **Fix JSON serialization** in `import_batch_processor.py`
|
||||
- Convert tuples to lists before storing in `match_results`
|
||||
|
||||
3. **Add permission validation** to `game_server.py` `process_game_command`
|
||||
- Check player privileges before executing commands
|
||||
|
||||
### Warnings (Should Fix)
|
||||
|
||||
4. **Resolve orphaned `deck_manager.py`**
|
||||
- Use it in deck router OR remove it
|
||||
|
||||
5. **Complete `sync_mirrors_from_mtg_cards`** in `card_mirror_service.py`
|
||||
- Implement the sync logic or mark as deprecated
|
||||
|
||||
6. **Fix `import_all_printings_psql`** in `mtgjson_uploader.py`
|
||||
- Use async database operations instead of subprocess
|
||||
|
||||
7. **Rename duplicate `get_card_statistics`** functions
|
||||
- Clarify which service they belong to
|
||||
|
||||
### Informational
|
||||
|
||||
8. **Consider using `DeckManager`** in deck router for consistency
|
||||
9. **Add input validation** to game commands
|
||||
10. **Document service dependencies** in `__init__.py`
|
||||
|
||||
---
|
||||
|
||||
## Test Coverage Summary
|
||||
|
||||
| Service | Functions Tested | Issues Found |
|
||||
|---------|-----------------|--------------|
|
||||
| card_database.py | 8 | 0 |
|
||||
| card_mirror_service.py | 9 | 3 warnings |
|
||||
| card_search_service.py | 5 | 1 critical |
|
||||
| deck_manager.py | 11 | 1 warning |
|
||||
| deck_parser.py | 6 | 0 |
|
||||
| deck_suggestion_service.py | 3 | 1 critical |
|
||||
| file_parser.py | 1 | 0 |
|
||||
| fuzzy_card_matcher.py | 6 | 0 |
|
||||
| game_server.py | 4 | 1 warning |
|
||||
| import_batch_processor.py | 7 | 1 critical, 1 warning |
|
||||
| mtgjson_downloader.py | 4 | 0 |
|
||||
| mtgjson_loader.py | 14 | 0 |
|
||||
| mtgjson_manager.py | 20 | 0 |
|
||||
| mtgjson_uploader.py | 8 | 1 warning |
|
||||
|
||||
**Total:** 102 functions, 12 critical issues, 8 warnings, 5 informational
|
||||
|
||||
---
|
||||
|
||||
## Next Steps
|
||||
|
||||
1. **Immediate:** Fix critical issues (colors field, JSON serialization, permission validation)
|
||||
2. **Short-term:** Resolve orphaned service, complete sync logic
|
||||
3. **Long-term:** Refactor deck router to use service layer, add comprehensive unit tests
|
||||
|
||||
---
|
||||
|
||||
**Report Generated:** 2024-01-15
|
||||
**Test Phase:** 5 - Service Layer
|
||||
**Overall Status:** ❌ FAIL
|
||||
Reference in New Issue
Block a user