fix(odds): don't return the cached no-odds marker as odds (#662)

A game ESPN had no odds for is cached as {"no_odds": True}, so it isn't
re-requested on every update. On the next update get_odds() returned that
marker from the cache as if it were odds: a truthy dict that callers took
to mean the game had some. It's still a cache hit (its ttl decides when to
ask again), but get_odds() now returns None for it -- on the cache hit and
in the stale-cache fallback after a failed fetch -- as the plugins' bundled
copies already did.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Chuck
2026-09-28 14:59:40 -04:00
committed by GitHub
co-authored by Claude Opus 5.5
parent 09103a8a7d
commit d469fe39d2
3 changed files with 29 additions and 4 deletions
+12 -2
View File
@@ -115,16 +115,26 @@ class TestGetOdds:
assert result == {'spread': -3.0}
mock_get.assert_not_called()
def test_cached_no_odds_sentinel_returned_verbatim(
def test_cached_no_odds_marker_is_a_hit_but_not_odds(
self, manager, cache_manager, mock_get):
# The marker keeps a game with no odds from being re-requested every
# update, but it isn't odds: returned as-is, callers saw a truthy
# dict and treated the game as having some.
cache_manager.get_with_auto_strategy.return_value = {'no_odds': True}
result = manager.get_odds('football', 'nfl', '401')
assert result == {'no_odds': True}
assert result is None
mock_get.assert_not_called()
assert manager.is_odds_available(result) is False
def test_the_no_odds_marker_is_not_the_stale_fallback_either(
self, manager, cache_manager, mock_get):
mock_get.side_effect = requests.exceptions.ConnectionError("down")
cache_manager.get_with_auto_strategy.side_effect = [None, {'no_odds': True}]
assert manager.get_odds('football', 'nfl', '401') is None
def test_success_caches_extracted_data_with_interval_ttl(
self, manager, cache_manager, mock_get):
result = manager.get_odds('football', 'nfl', '401',