paper-dynasty-card-creation/tests/test_rarity_cost_adjustments.py
Cal Corum cb471d8057 CLAUDE: Extract rarity cost adjustment logic into data-driven function
This commit eliminates 150+ lines of duplicated, error-prone nested if/elif
logic by extracting rarity cost calculations into a lookup table and function.

## Changes Made

1. **Add RARITY_COST_ADJUSTMENTS lookup table** (creation_helpers.py)
   - Maps (old_rarity, new_rarity) → (cost_adjustment, minimum_cost)
   - Covers all 30 possible rarity transitions
   - Self-documenting with comments for each rarity tier
   - Single source of truth for all cost adjustments

2. **Add calculate_rarity_cost_adjustment() function** (creation_helpers.py)
   - Takes old_rarity, new_rarity, old_cost
   - Returns new cost with adjustments and minimums applied
   - Includes comprehensive docstring with examples
   - Handles edge cases (same rarity, undefined transitions)
   - Logs warnings for undefined transitions

3. **Update batters/creation.py**
   - Import calculate_rarity_cost_adjustment
   - Replace 75-line nested if/elif block with 7-line function call
   - Identical behavior, much cleaner code

4. **Update pitchers/creation.py**
   - Import calculate_rarity_cost_adjustment
   - Replace 75-line nested if/elif block with 7-line function call
   - Eliminates duplication between batters and pitchers

5. **Add comprehensive tests** (tests/test_rarity_cost_adjustments.py)
   - 22 tests covering all scenarios
   - Tests individual transitions (Diamond→Gold, Common→Bronze, etc.)
   - Tests all upward and downward transitions
   - Tests minimum cost enforcement
   - Tests edge cases (zero cost, very high cost, negative cost)
   - Tests symmetry (up then down returns close to original)

## Impact

### Lines Eliminated
- **Batters:** 75 lines → 7 lines (89% reduction)
- **Pitchers:** 75 lines → 7 lines (89% reduction)
- **Total:** 150 lines of nested logic eliminated

### Benefits
 Eliminates 150+ lines of duplicated code
 Data-driven approach makes adjustments clear and modifiable
 Single source of truth prevents inconsistencies
 Independently testable business logic
 22 comprehensive tests ensure correctness
 Easy to add new rarity tiers or modify costs
 Reduced risk of typos in magic numbers

## Test Results
 22/22 new tests pass
 All existing tests still pass
 100% backward compatible - identical behavior

## Files Modified
- creation_helpers.py: +104 lines (table + function + docs)
- batters/creation.py: -68 lines (replaced nested logic)
- pitchers/creation.py: -68 lines (replaced nested logic)
- tests/test_rarity_cost_adjustments.py: +174 lines (new tests)

**Net change:** 150+ lines of complex logic replaced with simple,
tested, data-driven approach.

Part of ongoing refactoring to reduce code fragility.
2025-10-31 22:49:35 -05:00

185 lines
8.6 KiB
Python

"""
Tests for rarity cost adjustment logic.
This test verifies that calculate_rarity_cost_adjustment() correctly:
1. Adjusts costs when rarity changes
2. Enforces minimum costs where specified
3. Handles all rarity transitions (1-5, 99)
4. Returns original cost when rarity doesn't change
"""
import pytest
from creation_helpers import calculate_rarity_cost_adjustment
class TestRarityCostAdjustments:
"""Test suite for rarity cost adjustment calculations."""
def test_no_change_same_rarity(self):
"""Cost should not change if rarity stays the same."""
assert calculate_rarity_cost_adjustment(1, 1, 1000) == 1000
assert calculate_rarity_cost_adjustment(5, 5, 50) == 50
assert calculate_rarity_cost_adjustment(99, 99, 2400) == 2400
def test_diamond_to_gold(self):
"""Diamond (1) to Gold (2): -540, min 100."""
assert calculate_rarity_cost_adjustment(1, 2, 1000) == 460
assert calculate_rarity_cost_adjustment(1, 2, 500) == 100 # min enforced
assert calculate_rarity_cost_adjustment(1, 2, 50) == 100 # min enforced
def test_diamond_to_silver(self):
"""Diamond (1) to Silver (3): -720, min 50."""
assert calculate_rarity_cost_adjustment(1, 3, 1000) == 280
assert calculate_rarity_cost_adjustment(1, 3, 500) == 50 # min enforced
assert calculate_rarity_cost_adjustment(1, 3, 100) == 50 # min enforced
def test_diamond_to_bronze(self):
"""Diamond (1) to Bronze (4): -780, min 15."""
assert calculate_rarity_cost_adjustment(1, 4, 1000) == 220
assert calculate_rarity_cost_adjustment(1, 4, 500) == 15 # min enforced
assert calculate_rarity_cost_adjustment(1, 4, 50) == 15 # min enforced
def test_diamond_to_common(self):
"""Diamond (1) to Common (5): -800, min 5."""
assert calculate_rarity_cost_adjustment(1, 5, 1000) == 200
assert calculate_rarity_cost_adjustment(1, 5, 500) == 5 # min enforced
assert calculate_rarity_cost_adjustment(1, 5, 100) == 5 # min enforced
def test_diamond_to_special(self):
"""Diamond (1) to Special (99): +1600, no min."""
assert calculate_rarity_cost_adjustment(1, 99, 1000) == 2600
assert calculate_rarity_cost_adjustment(1, 99, 100) == 1700
def test_gold_to_diamond(self):
"""Gold (2) to Diamond (1): +540, no min."""
assert calculate_rarity_cost_adjustment(2, 1, 200) == 740
assert calculate_rarity_cost_adjustment(2, 1, 100) == 640
def test_gold_to_silver(self):
"""Gold (2) to Silver (3): -180, min 50."""
assert calculate_rarity_cost_adjustment(2, 3, 300) == 120
assert calculate_rarity_cost_adjustment(2, 3, 100) == 50 # min enforced
def test_silver_to_gold(self):
"""Silver (3) to Gold (2): +180, no min."""
assert calculate_rarity_cost_adjustment(3, 2, 100) == 280
assert calculate_rarity_cost_adjustment(3, 2, 50) == 230
def test_silver_to_bronze(self):
"""Silver (3) to Bronze (4): -60, min 15."""
assert calculate_rarity_cost_adjustment(3, 4, 100) == 40
assert calculate_rarity_cost_adjustment(3, 4, 50) == 15 # min enforced
def test_bronze_to_common(self):
"""Bronze (4) to Common (5): -20, min 5."""
assert calculate_rarity_cost_adjustment(4, 5, 50) == 30
assert calculate_rarity_cost_adjustment(4, 5, 20) == 5 # min enforced
def test_common_to_bronze(self):
"""Common (5) to Bronze (4): +20, no min."""
assert calculate_rarity_cost_adjustment(5, 4, 10) == 30
assert calculate_rarity_cost_adjustment(5, 4, 50) == 70
def test_common_to_diamond(self):
"""Common (5) to Diamond (1): +800, no min."""
assert calculate_rarity_cost_adjustment(5, 1, 10) == 810
assert calculate_rarity_cost_adjustment(5, 1, 50) == 850
def test_special_to_diamond(self):
"""Special (99) to Diamond (1): -1600, min 800."""
assert calculate_rarity_cost_adjustment(99, 1, 2400) == 800
assert calculate_rarity_cost_adjustment(99, 1, 2000) == 800 # min enforced
assert calculate_rarity_cost_adjustment(99, 1, 3000) == 1400
def test_special_to_gold(self):
"""Special (99) to Gold (2): -2140, min 100."""
assert calculate_rarity_cost_adjustment(99, 2, 2400) == 260
assert calculate_rarity_cost_adjustment(99, 2, 2000) == 100 # min enforced
def test_special_to_common(self):
"""Special (99) to Common (5): -2400, min 5."""
assert calculate_rarity_cost_adjustment(99, 5, 2400) == 5 # min enforced
assert calculate_rarity_cost_adjustment(99, 5, 3000) == 600
def test_all_upward_transitions(self):
"""Test all transitions that increase rarity (decrease number)."""
# Common (5) moving up
assert calculate_rarity_cost_adjustment(5, 4, 10) == 30 # +20
assert calculate_rarity_cost_adjustment(5, 3, 10) == 90 # +80
assert calculate_rarity_cost_adjustment(5, 2, 10) == 270 # +260
assert calculate_rarity_cost_adjustment(5, 1, 10) == 810 # +800
# Bronze (4) moving up
assert calculate_rarity_cost_adjustment(4, 3, 30) == 90 # +60
assert calculate_rarity_cost_adjustment(4, 2, 30) == 270 # +240
assert calculate_rarity_cost_adjustment(4, 1, 30) == 810 # +780
# Silver (3) moving up
assert calculate_rarity_cost_adjustment(3, 2, 90) == 270 # +180
assert calculate_rarity_cost_adjustment(3, 1, 90) == 810 # +720
# Gold (2) moving up
assert calculate_rarity_cost_adjustment(2, 1, 270) == 810 # +540
def test_all_downward_transitions_with_minimums(self):
"""Test all transitions that decrease rarity (increase number) with minimum enforcement."""
# Diamond (1) moving down - all have minimums
assert calculate_rarity_cost_adjustment(1, 2, 100) == 100 # would be -440, min 100
assert calculate_rarity_cost_adjustment(1, 3, 100) == 50 # would be -620, min 50
assert calculate_rarity_cost_adjustment(1, 4, 100) == 15 # would be -680, min 15
assert calculate_rarity_cost_adjustment(1, 5, 100) == 5 # would be -700, min 5
# Gold (2) moving down - all have minimums
assert calculate_rarity_cost_adjustment(2, 3, 100) == 50 # would be -80, min 50
assert calculate_rarity_cost_adjustment(2, 4, 100) == 15 # would be -140, min 15
assert calculate_rarity_cost_adjustment(2, 5, 100) == 5 # would be -160, min 5
# Silver (3) moving down - all have minimums
assert calculate_rarity_cost_adjustment(3, 4, 50) == 15 # would be -10, min 15
assert calculate_rarity_cost_adjustment(3, 5, 50) == 5 # would be -30, min 5
def test_edge_cases(self):
"""Test edge cases: zero cost, very high cost, etc."""
# Zero cost
assert calculate_rarity_cost_adjustment(5, 1, 0) == 800
assert calculate_rarity_cost_adjustment(1, 5, 0) == 5 # min enforced
# Very high cost
assert calculate_rarity_cost_adjustment(5, 1, 10000) == 10800
assert calculate_rarity_cost_adjustment(99, 1, 10000) == 8400
def test_symmetry(self):
"""Test that adjustments are symmetric (up then down returns close to original)."""
# Diamond to Common and back (won't be exact due to minimums, but should be logical)
original = 810
after_down = calculate_rarity_cost_adjustment(1, 5, original) # 810 - 800 = 10, min 5 → 5
after_up = calculate_rarity_cost_adjustment(5, 1, after_down) # 5 + 800 = 805
assert after_down == 10
assert after_up == 810
# Gold to Bronze and back
original = 270
after_down = calculate_rarity_cost_adjustment(2, 4, original) # 270 - 240 = 30
after_up = calculate_rarity_cost_adjustment(4, 2, after_down) # 30 + 240 = 270
assert after_down == 30
assert after_up == 270
class TestRarityCostAdjustmentEdgeCases:
"""Test edge cases and error handling."""
def test_undefined_transition(self):
"""Test that undefined transitions are handled gracefully."""
# There's no transition from 1 to 1 (same), but it's handled
result = calculate_rarity_cost_adjustment(1, 1, 500)
assert result == 500
# There's no transition from rarity 10 (doesn't exist) but should return old cost
result = calculate_rarity_cost_adjustment(10, 5, 500)
assert result == 500 # Falls back to old cost
def test_negative_costs(self):
"""Test behavior with negative costs (shouldn't happen, but test it)."""
# Negative costs should still get adjusted
result = calculate_rarity_cost_adjustment(5, 1, -100)
assert result == 700 # -100 + 800 = 700