paper-dynasty-gameplay-webapp/tests/factories/player_factory.py
Cal Corum 559fe73f07 CLAUDE: Complete Player model migration with service layer and integration test infrastructure
## 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>
2025-09-29 16:20:29 -05:00

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)