""" Unit tests for card pricing logic in Player model. Player.change_on_sell() and Player.change_on_buy() are critical business logic used whenever cards are traded. These tests verify the price update math and the floor/ceiling behaviour. """ import math import pytest class TestPlayerChangeOnSell: """Tests for Player.change_on_sell() — price decreases 5% on each sale.""" def test_sell_reduces_cost_by_5_percent(self, sample_player): """Selling a card should reduce its cost to floor(cost * 0.95).""" sample_player.cost = 100 sample_player.change_on_sell() assert sample_player.cost == math.floor(100 * 0.95) # 95 def test_sell_saves_to_db(self, sample_player): """change_on_sell() should persist the new price to the database.""" from app.db_engine import Player sample_player.cost = 200 sample_player.change_on_sell() refreshed = Player.get_by_id(sample_player.pk) assert refreshed.cost == math.floor(200 * 0.95) # 190 def test_sell_floors_at_1(self, sample_player): """Price should never drop below 1, even at very low starting values.""" sample_player.cost = 1 sample_player.change_on_sell() assert sample_player.cost == 1 def test_sell_large_price(self, sample_player): """Large prices should still apply the 5% reduction correctly.""" sample_player.cost = 10000 sample_player.change_on_sell() assert sample_player.cost == math.floor(10000 * 0.95) # 9500 def test_sell_rounds_down(self, sample_player): """floor() means fractional results are rounded down, not up.""" sample_player.cost = 21 # 21 * 0.95 = 19.95 → floor → 19 sample_player.change_on_sell() assert sample_player.cost == 19 class TestPlayerChangeOnBuy: """Tests for Player.change_on_buy() — price increases 10% on each purchase.""" def test_buy_increases_cost_by_10_percent(self, sample_player): """Buying a card should increase its cost to ceil(cost * 1.1).""" sample_player.cost = 100 sample_player.change_on_buy() assert sample_player.cost == math.ceil(100 * 1.1) # 110 def test_buy_saves_to_db(self, sample_player): """change_on_buy() should persist the new price to the database.""" from app.db_engine import Player sample_player.cost = 200 sample_player.change_on_buy() refreshed = Player.get_by_id(sample_player.pk) assert refreshed.cost == math.ceil(200 * 1.1) # 220 def test_buy_from_low_price(self, sample_player): """Low prices should still apply the 10% increase.""" sample_player.cost = 1 sample_player.change_on_buy() assert sample_player.cost == math.ceil(1 * 1.1) # 2 (ceil of 1.1) def test_buy_rounds_up(self, sample_player): """ceil() means fractional results are rounded up.""" sample_player.cost = 9 # 9 * 1.1 = 9.9 → ceil → 10 sample_player.change_on_buy() assert sample_player.cost == 10 def test_buy_large_price(self, sample_player): """Large prices should still apply the 10% increase correctly.""" sample_player.cost = 5000 sample_player.change_on_buy() assert sample_player.cost == math.ceil(5000 * 1.1) # 5500 class TestPlayerGetAllPos: """Tests for Player.get_all_pos() — returns non-null, non-CP position list.""" def test_returns_primary_position(self, sample_player): """A player with only pos_1 set should return a list with one position.""" sample_player.pos_1 = "1B" assert sample_player.get_all_pos() == ["1B"] def test_excludes_cp_position(self, sample_player): """CP (closing pitcher) is excluded from the position list.""" sample_player.pos_1 = "SP" sample_player.pos_2 = "CP" positions = sample_player.get_all_pos() assert "CP" not in positions assert "SP" in positions def test_excludes_null_positions(self, sample_player): """None positions should not appear in the result.""" sample_player.pos_1 = "CF" sample_player.pos_2 = None assert sample_player.get_all_pos() == ["CF"] def test_multiple_positions(self, sample_player): """Players with multiple eligible positions should return all of them.""" sample_player.pos_1 = "1B" sample_player.pos_2 = "OF" sample_player.pos_3 = "DH" positions = sample_player.get_all_pos() assert positions == ["1B", "OF", "DH"]