## Player Model Migration - Migrate Player model from Discord app following Model/Service Architecture pattern - Extract all business logic from Player model to PlayerService - Create pure data model with PostgreSQL relationships (Cardset, PositionRating) - Implement comprehensive PlayerFactory with specialized methods for test data ## PlayerService Implementation - Extract 5 business logic methods from original Player model: - get_batter_card_url() - batting card URL retrieval - get_pitcher_card_url() - pitching card URL retrieval - generate_name_card_link() - markdown link generation - get_formatted_name_with_description() - name formatting - get_player_description() - description from object or dict - Follow BaseService pattern with dependency injection and logging ## Comprehensive Testing - 35 passing Player tests (14 model + 21 service tests) - PlayerFactory with specialized methods (batting/pitching cards, positions) - Test isolation following factory pattern and db_session guidelines - Fix PostgreSQL integer overflow in test ID generation ## Integration Test Infrastructure - Create integration test framework for improving service coverage - Design AIService integration tests targeting uncovered branches - Demonstrate real database query testing with proper isolation - Establish patterns for testing complex game scenarios ## Service Coverage Analysis - Current service coverage: 61% overall - PlayerService: 100% coverage (excellent migration example) - AIService: 60% coverage (improvement opportunities identified) - Integration test strategy designed to achieve 90%+ coverage ## Database Integration - Update Cardset model to include players relationship - Update PositionRating model with proper Player foreign key - Maintain all existing relationships and constraints - Demonstrate data isolation and automatic cleanup in tests ## Test Suite Status - 137 tests passing, 0 failures (maintained 100% pass rate) - Added 35 new tests while preserving all existing functionality - Integration test infrastructure ready for coverage improvements 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
143 lines
5.3 KiB
Python
143 lines
5.3 KiB
Python
"""
|
|
Player Factory
|
|
|
|
Factory for creating Player test instances following test isolation guidelines.
|
|
"""
|
|
import datetime
|
|
import random
|
|
from typing import Optional
|
|
|
|
from sqlmodel import Session
|
|
|
|
from app.models.player import Player
|
|
|
|
|
|
class PlayerFactory:
|
|
"""Factory for creating Player test data."""
|
|
|
|
@staticmethod
|
|
def create(
|
|
session: Session,
|
|
name: Optional[str] = None,
|
|
cost: Optional[int] = None,
|
|
image: Optional[str] = None,
|
|
mlbclub: Optional[str] = None,
|
|
franchise: Optional[str] = None,
|
|
cardset_id: Optional[int] = None,
|
|
set_num: Optional[int] = None,
|
|
rarity_id: Optional[int] = None,
|
|
pos_1: Optional[str] = None,
|
|
description: Optional[str] = None,
|
|
quantity: Optional[int] = None,
|
|
image2: Optional[str] = None,
|
|
headshot: Optional[str] = None,
|
|
**kwargs
|
|
) -> Player:
|
|
"""
|
|
Create a Player instance with randomized data.
|
|
|
|
Args:
|
|
session: Database session
|
|
name: Player name (default: random name)
|
|
cost: Player cost (default: random 1-50)
|
|
image: Primary image URL (default: batting card)
|
|
mlbclub: MLB club (default: random team)
|
|
franchise: Franchise name (default: matches mlbclub)
|
|
cardset_id: Cardset ID (default: random)
|
|
set_num: Set number (default: random)
|
|
rarity_id: Rarity ID (default: random 1-5)
|
|
pos_1: Primary position (default: random position)
|
|
description: Player description (default: random era)
|
|
quantity: Quantity available (default: 999)
|
|
image2: Secondary image URL (default: None)
|
|
headshot: Headshot URL (default: None)
|
|
**kwargs: Additional fields
|
|
|
|
Returns:
|
|
Player: Created and committed player instance
|
|
"""
|
|
player_id = random.randint(1000000, 9999999)
|
|
|
|
# Generate random MLB clubs
|
|
mlb_clubs = ["LAD", "NYY", "BOS", "HOU", "ATL", "SF", "STL", "CHC", "NYM", "PHI"]
|
|
|
|
# Generate random positions
|
|
positions = ["C", "1B", "2B", "3B", "SS", "LF", "CF", "RF", "P"]
|
|
|
|
# Generate random eras
|
|
eras = ["2023", "2022", "2021", "Rookie", "Prime", "Veteran"]
|
|
|
|
# Set defaults
|
|
actual_name = name or f"Player {player_id}"
|
|
actual_mlbclub = mlbclub or random.choice(mlb_clubs)
|
|
actual_pos_1 = pos_1 or random.choice(positions)
|
|
actual_description = description or random.choice(eras)
|
|
|
|
# Create cardset if cardset_id is not provided
|
|
if cardset_id is None:
|
|
from .cardset_factory import CardsetFactory
|
|
cardset = CardsetFactory.create(session)
|
|
actual_cardset_id = cardset.id
|
|
else:
|
|
actual_cardset_id = cardset_id
|
|
|
|
player_data = {
|
|
"id": player_id,
|
|
"name": actual_name,
|
|
"cost": cost or random.randint(1, 50),
|
|
"image": image or f"https://example.com/{actual_name.lower().replace(' ', '_')}_batting.jpg",
|
|
"mlbclub": actual_mlbclub,
|
|
"franchise": franchise or actual_mlbclub,
|
|
"cardset_id": actual_cardset_id,
|
|
"set_num": set_num or random.randint(1, 500),
|
|
"rarity_id": rarity_id or random.randint(1, 5),
|
|
"pos_1": actual_pos_1,
|
|
"description": actual_description,
|
|
"quantity": quantity if quantity is not None else 999,
|
|
"image2": image2,
|
|
"headshot": headshot,
|
|
"created": datetime.datetime.now(),
|
|
**kwargs
|
|
}
|
|
|
|
player = Player(**player_data)
|
|
session.add(player)
|
|
session.commit()
|
|
session.refresh(player)
|
|
return player
|
|
|
|
@staticmethod
|
|
def create_with_batting_card(session: Session, **kwargs) -> Player:
|
|
"""Create a player with a batting card URL."""
|
|
kwargs.setdefault("image", "https://example.com/player_batting.jpg")
|
|
kwargs.setdefault("image2", None)
|
|
return PlayerFactory.create(session, **kwargs)
|
|
|
|
@staticmethod
|
|
def create_with_pitching_card(session: Session, **kwargs) -> Player:
|
|
"""Create a player with a pitching card URL."""
|
|
kwargs.setdefault("image", "https://example.com/player_pitching.jpg")
|
|
kwargs.setdefault("image2", None)
|
|
return PlayerFactory.create(session, **kwargs)
|
|
|
|
@staticmethod
|
|
def create_with_both_cards(session: Session, **kwargs) -> Player:
|
|
"""Create a player with both batting and pitching card URLs."""
|
|
kwargs.setdefault("image", "https://example.com/player_batting.jpg")
|
|
kwargs.setdefault("image2", "https://example.com/player_pitching.jpg")
|
|
return PlayerFactory.create(session, **kwargs)
|
|
|
|
@staticmethod
|
|
def create_catcher(session: Session, **kwargs) -> Player:
|
|
"""Create a catcher player."""
|
|
kwargs.setdefault("pos_1", "C")
|
|
kwargs.setdefault("name", f"Catcher {random.randint(1000, 9999)}")
|
|
return PlayerFactory.create(session, **kwargs)
|
|
|
|
@staticmethod
|
|
def create_pitcher(session: Session, **kwargs) -> Player:
|
|
"""Create a pitcher player."""
|
|
kwargs.setdefault("pos_1", "P")
|
|
kwargs.setdefault("name", f"Pitcher {random.randint(1000, 9999)}")
|
|
kwargs.setdefault("image", "https://example.com/pitcher_pitching.jpg")
|
|
return PlayerFactory.create(session, **kwargs) |