""" Unit tests for Player model. Tests the pure data model functionality, field validation, and database relationships following test isolation guidelines. """ import datetime import pytest from sqlalchemy.exc import IntegrityError from app.models.player import Player, PlayerBase from tests.factories.player_factory import PlayerFactory class TestPlayerBase: """Test PlayerBase model functionality.""" def test_create_player_base_with_required_fields(self): """Test creating PlayerBase with minimum required fields.""" player_data = { "id": None, # Explicitly set to None since it's Optional but Pydantic requires explicit None "name": "Test Player", "cost": 25, "image": "https://example.com/test.jpg", "mlbclub": "LAD", "franchise": "LAD", "set_num": 1, "pos_1": "C", "description": "2023" } player = PlayerBase(**player_data) assert player.name == "Test Player" assert player.cost == 25 assert player.image == "https://example.com/test.jpg" assert player.mlbclub == "LAD" assert player.franchise == "LAD" assert player.set_num == 1 assert player.pos_1 == "C" assert player.description == "2023" def test_player_base_defaults(self): """Test PlayerBase default values.""" player_data = { "id": None, "name": "Test Player", "cost": 25, "image": "https://example.com/test.jpg", "mlbclub": "LAD", "franchise": "LAD", "set_num": 1, "pos_1": "C", "description": "2023" } player = PlayerBase(**player_data) assert player.quantity == 999 assert player.image2 is None assert player.pos_2 is None assert player.pos_3 is None assert player.pos_4 is None assert player.pos_5 is None assert player.pos_6 is None assert player.pos_7 is None assert player.pos_8 is None assert player.headshot is None assert player.vanity_card is None assert player.strat_code is None assert player.bbref_id is None assert player.fangr_id is None assert player.mlbplayer_id is None assert isinstance(player.created, datetime.datetime) def test_position_field_validation_uppercase(self): """Test that position fields are converted to uppercase.""" player_data = { "id": None, "name": "Test Player", "cost": 25, "image": "https://example.com/test.jpg", "mlbclub": "LAD", "franchise": "LAD", "set_num": 1, "pos_1": "c", # lowercase "pos_2": "1b", # lowercase "description": "2023" } player = PlayerBase(**player_data) assert player.pos_1 == "C" assert player.pos_2 == "1B" def test_position_field_validation_none_values(self): """Test that None position values are preserved.""" player_data = { "id": None, "name": "Test Player", "cost": 25, "image": "https://example.com/test.jpg", "mlbclub": "LAD", "franchise": "LAD", "set_num": 1, "pos_1": "C", "description": "2023" } player = PlayerBase(**player_data) # None values should remain None assert player.pos_2 is None assert player.pos_3 is None def test_all_position_fields_uppercase(self): """Test all position fields are converted to uppercase.""" player_data = { "id": None, "name": "Test Player", "cost": 25, "image": "https://example.com/test.jpg", "mlbclub": "LAD", "franchise": "LAD", "set_num": 1, "pos_1": "c", "pos_2": "1b", "pos_3": "2b", "pos_4": "3b", "pos_5": "ss", "pos_6": "lf", "pos_7": "cf", "pos_8": "rf", "description": "2023" } player = PlayerBase(**player_data) assert player.pos_1 == "C" assert player.pos_2 == "1B" assert player.pos_3 == "2B" assert player.pos_4 == "3B" assert player.pos_5 == "SS" assert player.pos_6 == "LF" assert player.pos_7 == "CF" assert player.pos_8 == "RF" class TestPlayerModel: """Test Player model database functionality.""" def test_create_player_in_database(self, db_session): """Test creating and saving a Player to database.""" player = PlayerFactory.create(db_session, name="Database Player") assert player.id is not None assert player.name == "Database Player" # Verify it exists in database retrieved = db_session.get(Player, player.id) assert retrieved is not None assert retrieved.name == "Database Player" def test_player_unique_id_constraint(self, db_session): """Test that player IDs must be unique.""" player1 = PlayerFactory.create(db_session, name="Player One") player1_id = player1.id # Attempt to create player with same ID should fail with pytest.raises(IntegrityError): duplicate_player = Player( id=player1_id, name="Player Two", cost=20, image="https://example.com/test2.jpg", mlbclub="NYY", franchise="NYY", set_num=2, pos_1="1B", description="2022" ) db_session.add(duplicate_player) db_session.commit() def test_player_with_cardset_relationship(self, db_session): """Test Player relationship with Cardset.""" from tests.factories.cardset_factory import CardsetFactory cardset = CardsetFactory.create(db_session, name="Test Set") player = PlayerFactory.create( db_session, name="Related Player", cardset_id=cardset.id ) assert player.cardset_id == cardset.id # Relationship should be accessible (when cardset is properly loaded) db_session.refresh(player) assert player.cardset is not None assert player.cardset.name == "Test Set" def test_player_factory_methods(self, db_session): """Test PlayerFactory convenience methods.""" # Test batting card factory batter = PlayerFactory.create_with_batting_card(db_session) assert "batting" in batter.image assert batter.image2 is None # Test pitching card factory pitcher = PlayerFactory.create_with_pitching_card(db_session) assert "pitching" in pitcher.image assert pitcher.image2 is None # Test both cards factory both = PlayerFactory.create_with_both_cards(db_session) assert "batting" in both.image assert "pitching" in both.image2 # Test position-specific factories catcher = PlayerFactory.create_catcher(db_session) assert catcher.pos_1 == "C" pitcher = PlayerFactory.create_pitcher(db_session) assert pitcher.pos_1 == "P" assert "pitching" in pitcher.image class TestPlayerBusinessLogicRemoval: """Test that business logic has been properly removed from model.""" def test_no_business_logic_methods(self, db_session): """Test that business logic methods are not present in Player model.""" player = PlayerFactory.create(db_session) # These methods should NOT exist (moved to PlayerService) assert not hasattr(player, 'batter_card_url') assert not hasattr(player, 'pitcher_card_url') assert not hasattr(player, 'name_card_link') assert not hasattr(player, 'name_with_desc') def test_player_is_pure_data_model(self, db_session): """Test that Player model only contains data fields and relationships.""" player = PlayerFactory.create(db_session) # Should have data fields assert hasattr(player, 'name') assert hasattr(player, 'cost') assert hasattr(player, 'image') assert hasattr(player, 'mlbclub') # Should have relationships (defined in class) assert hasattr(Player, 'cardset') # assert hasattr(Player, 'cards') # Commented out until Card model is created # assert hasattr(Player, 'lineups') # Commented out until Lineup model is created assert hasattr(Player, 'positions') # Should NOT have business logic methods methods = [method for method in dir(player) if not method.startswith('_')] business_methods = ['batter_card_url', 'pitcher_card_url', 'name_card_link', 'name_with_desc'] for method in business_methods: assert method not in methods, f"Business logic method {method} should be moved to service" class TestPlayerDataIntegrity: """Test Player model data integrity and constraints.""" def test_player_required_fields(self, db_session): """Test that required fields exist in the model.""" # Since SQLModel table models don't raise validation errors for missing fields # at instantiation time, we test by checking the required field exists player = PlayerFactory.create(db_session) # These fields should always be present assert hasattr(player, 'name') assert hasattr(player, 'cost') assert hasattr(player, 'image') assert hasattr(player, 'mlbclub') assert hasattr(player, 'franchise') assert hasattr(player, 'set_num') assert hasattr(player, 'pos_1') assert hasattr(player, 'description') def test_player_field_types(self, db_session): """Test that field types are enforced.""" player = PlayerFactory.create(db_session) assert isinstance(player.name, str) assert isinstance(player.cost, int) assert isinstance(player.image, str) assert isinstance(player.created, datetime.datetime) assert isinstance(player.quantity, int) def test_player_optional_fields(self, db_session): """Test that optional fields can be None.""" player = PlayerFactory.create( db_session, image2=None, headshot=None, vanity_card=None, strat_code=None, bbref_id=None, fangr_id=None, mlbplayer_id=None ) assert player.image2 is None assert player.headshot is None assert player.vanity_card is None assert player.strat_code is None assert player.bbref_id is None assert player.fangr_id is None assert player.mlbplayer_id is None