fix: refresh roster data before validation to prevent stale cache

TransactionBuilder cached roster data indefinitely via _roster_loaded flag,
causing validation to use stale counts when builders persisted across multiple
/ilmove invocations. Now validate_transaction() always fetches fresh roster
data. Also adds dependency injection for roster_service to improve testability.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Cal Corum 2026-03-02 14:56:14 -06:00 committed by cal
parent 6e4191f677
commit c70669d474
2 changed files with 383 additions and 315 deletions

View File

@ -174,7 +174,13 @@ class RosterValidationResult:
class TransactionBuilder:
"""Interactive transaction builder for complex multi-move transactions."""
def __init__(self, team: Team, user_id: int, season: int = get_config().sba_season):
def __init__(
self,
team: Team,
user_id: int,
season: int = get_config().sba_season,
roster_svc=None,
):
"""
Initialize transaction builder.
@ -182,12 +188,14 @@ class TransactionBuilder:
team: Team making the transaction
user_id: Discord user ID of the GM
season: Season number
roster_svc: RosterService instance (defaults to global roster_service)
"""
self.team = team
self.user_id = user_id
self.season = season
self.moves: List[TransactionMove] = []
self.created_at = datetime.now(timezone.utc)
self._roster_svc = roster_svc or roster_service
# Cache for roster data
self._current_roster: Optional[TeamRoster] = None
@ -201,13 +209,19 @@ class TransactionBuilder:
f"TransactionBuilder initialized for {team.abbrev} by user {user_id}"
)
async def load_roster_data(self) -> None:
"""Load current roster data for the team."""
if self._roster_loaded:
async def load_roster_data(self, force_refresh: bool = False) -> None:
"""Load current roster data for the team.
Args:
force_refresh: If True, bypass cache and fetch fresh data from API.
"""
if self._roster_loaded and not force_refresh:
return
try:
self._current_roster = await roster_service.get_current_roster(self.team.id)
self._current_roster = await self._roster_svc.get_current_roster(
self.team.id
)
self._roster_loaded = True
logger.debug(f"Loaded roster data for team {self.team.abbrev}")
except Exception as e:
@ -353,7 +367,9 @@ class TransactionBuilder:
Returns:
RosterValidationResult with validation details
"""
await self.load_roster_data()
# Always refresh roster data before validation to prevent stale cache
# from causing incorrect roster counts (e.g. after a previous /ilmove submission)
await self.load_roster_data(force_refresh=True)
# Load existing transactions if next_week is provided
if next_week is not None:

File diff suppressed because it is too large Load Diff