From 46c85e68747a74b2cff6f07f2c069b871fe0d600 Mon Sep 17 00:00:00 2001 From: Cal Corum Date: Thu, 19 Mar 2026 10:31:16 -0500 Subject: [PATCH] fix: stale docstring + add decision-only pitcher test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - evaluate_card() docstring: "Override for PlayerSeasonStats" → "Override for BattingSeasonStats/PitchingSeasonStats" - New test_decision_only_pitcher: exercises the edge case where a pitcher has a Decision row but no StratPlay rows, verifying _get_player_pairs() correctly includes them via the Decision table scan Co-Authored-By: Claude Opus 4.6 (1M context) --- app/services/evolution_evaluator.py | 4 +- tests/test_season_stats_update.py | 57 +++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+), 2 deletions(-) diff --git a/app/services/evolution_evaluator.py b/app/services/evolution_evaluator.py index 1b2c033..bb72136 100644 --- a/app/services/evolution_evaluator.py +++ b/app/services/evolution_evaluator.py @@ -65,8 +65,8 @@ def evaluate_card( Args: player_id: Player primary key. team_id: Team primary key. - _stats_model: Override for PlayerSeasonStats (used in tests to avoid - importing from db_engine before WP-07 merges). + _stats_model: Override for BattingSeasonStats/PitchingSeasonStats + (used in tests to inject a stub model with all stat fields). _state_model: Override for EvolutionCardState (used in tests to avoid importing from db_engine before WP-05 merges). _compute_value_fn: Override for formula_engine.compute_value_for_track diff --git a/tests/test_season_stats_update.py b/tests/test_season_stats_update.py index faf9c44..b833cea 100644 --- a/tests/test_season_stats_update.py +++ b/tests/test_season_stats_update.py @@ -851,3 +851,60 @@ def test_partial_reprocessing_heals( assert stats.doubles == 1 assert stats.strikeouts == 1 assert stats.games == 1 + + +def test_decision_only_pitcher(team_a, team_b, player_batter, player_pitcher, game): + """A pitcher with a Decision but no StratPlay rows still gets stats recorded. + + What: Create a second pitcher who has a Decision (win) for the game but + does not appear in any StratPlay rows. After update_season_stats(), the + decision-only pitcher should have a PitchingSeasonStats row with wins=1 + and all play-level stats at 0. + + Why: In rare cases a pitcher may be credited with a decision without + recording any plays (e.g. inherited runner scoring rules, edge cases in + game simulation). The old code handled this in _apply_decisions(); the + new code must include Decision-scanned pitchers in _get_player_pairs(). + """ + relief_pitcher = _make_player("Relief Pitcher", pos="RP") + + # The main pitcher has plays + make_play( + game, + 1, + player_batter, + team_a, + player_pitcher, + team_b, + pa=1, + ab=1, + outs=1, + ) + + # The relief pitcher has a Decision but NO StratPlay rows + Decision.create( + season=11, + game=game, + pitcher=relief_pitcher, + pitcher_team=team_b, + win=1, + loss=0, + is_save=0, + hold=0, + b_save=0, + is_start=False, + ) + + update_season_stats(game.id) + + # The relief pitcher should have a PitchingSeasonStats row + stats = PitchingSeasonStats.get( + PitchingSeasonStats.player == relief_pitcher, + PitchingSeasonStats.team == team_b, + PitchingSeasonStats.season == 11, + ) + assert stats.wins == 1 + assert stats.games == 0 # no plays, so COUNT(DISTINCT game) = 0 + assert stats.outs == 0 + assert stats.strikeouts == 0 + assert stats.games_started == 0