Add Tank01 provider and test harness ahead of the ESPN swap (#363) #390

Merged
johnsturgeon merged 5 commits from migrate-from-espn-api-to-thesportsdb-363 into main 2026-08-20 18:05:21 +02:00
Owner

Introduces app/football_api, a Tank01-via-RapidAPI wrapper presenting the
same public surface as app/espn_nfl, plus the test infrastructure the
migration needs. Nothing is switched over yet -- create_picks,
update_game, sync_team_records, and model_helpers all still construct
ESPNNfl. That swap is the next commit.

Test suite and fixtures were written by Claude (Fable & Opus 5) as a
TDD contract BEFORE the provider existed; the app/football_api
implementation is hand-written, with Claude reviewing on request.

Added:

  • app/football_api: games, scores, teams, and betting odds, with Tank01's
    strings normalized to the ints and STATUS_* values the models already
    expect. Home spread is the median across sportsbooks, so a single
    outlier book can't skew the pick.
  • tests/: 74 tests served entirely from committed fixtures via respx, so
    the suite is deterministic and offline. tests/test_espn_nfl.py
    characterizes the current ESPN behavior and is the contract
    tests/test_football_api.py holds the replacement to.
  • Live contract tests (-m live, excluded by default) plus a weekly
    api-drift workflow, so a provider changing shape surfaces on Tuesday
    morning rather than during Wednesday's create-picks run.
  • test.yml as its own status check; lint.yml no longer stubs pytest out.

Fixed on the ESPN side, surfaced by the characterization tests:

  • _http_get_with_retry called sentry_sdk.logger.warning with only the
    package imported, raising AttributeError the first time a retry fired.
  • games/teams/standings were plain methods where every other accessor was
    a property; made them properties and updated the call sites, which is
    also what lets both providers share one surface.

Refs #363. Draft until the interface swap lands.

Tests ONLY Co-Authored by: Claude Opus 5 noreply@anthropic.com

Introduces app/football_api, a Tank01-via-RapidAPI wrapper presenting the same public surface as app/espn_nfl, plus the test infrastructure the migration needs. Nothing is switched over yet -- create_picks, update_game, sync_team_records, and model_helpers all still construct ESPNNfl. That swap is the next commit. Test suite and fixtures were written by Claude (Fable & Opus 5) as a TDD contract BEFORE the provider existed; the app/football_api implementation is hand-written, with Claude reviewing on request. Added: - app/football_api: games, scores, teams, and betting odds, with Tank01's strings normalized to the ints and STATUS_* values the models already expect. Home spread is the median across sportsbooks, so a single outlier book can't skew the pick. - tests/: 74 tests served entirely from committed fixtures via respx, so the suite is deterministic and offline. tests/test_espn_nfl.py characterizes the current ESPN behavior and is the contract tests/test_football_api.py holds the replacement to. - Live contract tests (-m live, excluded by default) plus a weekly api-drift workflow, so a provider changing shape surfaces on Tuesday morning rather than during Wednesday's create-picks run. - test.yml as its own status check; lint.yml no longer stubs pytest out. Fixed on the ESPN side, surfaced by the characterization tests: - _http_get_with_retry called sentry_sdk.logger.warning with only the package imported, raising AttributeError the first time a retry fired. - games/teams/standings were plain methods where every other accessor was a property; made them properties and updated the call sites, which is also what lets both providers share one surface. Refs #363. Draft until the interface swap lands. Tests ONLY Co-Authored by: Claude Opus 5 <noreply@anthropic.com>
Add Tank01 provider and test harness ahead of the ESPN swap (#363)
Some checks failed
Claude Code Review / claude-review (pull_request) Has been skipped
Python FastAPI Jinja Linting / build (pull_request) Failing after 32s
Tests / pytest (pull_request) Successful in 16s
34fe1f1f09
Introduces app/football_api, a Tank01-via-RapidAPI wrapper presenting the
same public surface as app/espn_nfl, plus the test infrastructure the
migration needs. Nothing is switched over yet -- create_picks,
update_game, sync_team_records, and model_helpers all still construct
ESPNNfl. That swap is the next commit.

Test suite and fixtures were written by Claude (Fable & Opus 5) as a
TDD contract BEFORE the provider existed; the app/football_api
implementation is hand-written, with Claude reviewing on request.

Added:
- app/football_api: games, scores, teams, and betting odds, with Tank01's
  strings normalized to the ints and STATUS_* values the models already
  expect. Home spread is the median across sportsbooks, so a single
  outlier book can't skew the pick.
- tests/: 74 tests served entirely from committed fixtures via respx, so
  the suite is deterministic and offline. tests/test_espn_nfl.py
  characterizes the current ESPN behavior and is the contract
  tests/test_football_api.py holds the replacement to.
- Live contract tests (-m live, excluded by default) plus a weekly
  api-drift workflow, so a provider changing shape surfaces on Tuesday
  morning rather than during Wednesday's create-picks run.
- test.yml as its own status check; lint.yml no longer stubs pytest out.

Fixed on the ESPN side, surfaced by the characterization tests:
- _http_get_with_retry called sentry_sdk.logger.warning with only the
  package imported, raising AttributeError the first time a retry fired.
- games/teams/standings were plain methods where every other accessor was
  a property; made them properties and updated the call sites, which is
  also what lets both providers share one surface.

Refs #363. Draft until the interface swap lands.

Tests ONLY Co-Authored by: Claude Opus 5 <noreply@anthropic.com>
Swap the NFL provider from ESPN to Tank01 (#363)
All checks were successful
Claude Code Review / claude-review (pull_request) Has been skipped
Python FastAPI Jinja Linting / build (pull_request) Successful in 25s
Tests / pytest (pull_request) Successful in 9s
1e93e707db
Points every consumer at app/tank01_api and deletes app/espn_nfl. The
wrapper and its test suite landed in the previous commit; this is the
cutover.

The implementation is hand-written; Claude wrote the contract test suite
and fixtures that the wrapper was built against, reviewed the swap, and
authored app/models/season.py and docs/TECH_DEBT.md.

Renamed for legibility, since fixtures, scripts and live tests were
already Tank01-named and only the module was not:
- app/football_api -> app/tank01_api
- FOOTBALL_API_KEY / FOOTBALL_API_HOST -> TANK01_API_* (1Password item
  and the Forgejo Actions secret updated to match)

Season types no longer come from a provider:
- app/models/season.py holds SeasonType (1/2/3) and the display labels.
  The strings are ours now -- ESPN said "Post Season", Tank01 says
  "Postseason" -- so a future provider swap cannot move UI text.
- WeekInfo.season_type_name reads from it. ESPNNfl.SEASON_TYPES is gone.
- is_skip_week is stubbed to False rather than deleted: it existed for
  ESPN's Pro Bowl slot in a 5-week postseason, and Tank01 numbers the
  postseason 1-4 with no week for the Pro Bowl at all. Its four call
  sites are now dead branches, removed separately.

Provider-neutral ids:
- DataSource.THE_SPORTS_DB -> TANK01; Game and Team both default to it.
- Team external ids are strings (Tank01 issues "30"), matching the
  column type and how game ids already worked.

Behavior differences worth knowing:
- favored_team is non-Optional now. ESPN returned None for a game no
  book quoted and create_picks patched it to "home favored by 0.0";
  Tank01 resolves that case to home favored by 0.5 directly, so the
  workaround is gone rather than ported.
- update_game passes season explicitly, so updating an older game can no
  longer resolve against whatever season the API currently reports.

Also drops --continue-on-collection-errors from pytest addopts. It
existed so the TDD-red contract suite would not abort the run, and that
reason expired when the module was written -- it was hiding the deleted
ESPN suite's collection error behind a green summary.

Adds docs/TECH_DEBT.md: eleven deferred items, each with what breaks and
when. The two that compound are the missing seed path for hand-managed
tables (Team.discord_emoji exists only in the database) and the absence
of any test coverage outside the provider wrappers.

Verified: 40 unit tests green offline, 9 live contract tests green
against the real API, pylint 10.00/10, and a manual smoke test of the
running app -- which matters here because nothing in the suite covers
routes or jobs.

Refs #363.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
johnsturgeon changed title from WIP: Add Tank01 provider and test harness ahead of the ESPN swap (#363) to Add Tank01 provider and test harness ahead of the ESPN swap (#363) 2026-08-20 18:04:44 +02:00
johnsturgeon deleted branch migrate-from-espn-api-to-thesportsdb-363 2026-08-20 18:05:22 +02:00
Sign in to join this conversation.
No description provided.