mirror of
https://github.com/ChuckBuilds/LEDMatrix.git
synced 2026-08-01 16:58:06 +00:00
fix(sports): stop dropping hockey and baseball events on optional feed keys
Both bugs were pinned AS-IS by the B0 characterization suite so this
phase could change them knowingly. Both fixes are adoptions of code the
corresponding plugins already ship, not new inventions.
Hockey: the extractor read competitor["statistics"] unguarded, so a
competitor arriving without that array raised KeyError inside the
generator and the WHOLE event was discarded -- valid scores and status
included. Shot/save counts now default to 0, which is already what the
suite expects for an empty statistics array.
Baseball: for live games the extractor read game_event["status"], the
event TOP-LEVEL status, to get the inning. Real ESPN events duplicate
status there, but MiLB events (synthesized from the MLB Stats API into
an ESPN-like shape) populate only the competition-level one, so the
lookup raised a bare KeyError and dropped the event. It now reads the
competition-level status that _extract_game_details_common has already
validated, so it cannot be missing at that point.
The two characterization tests that pinned the old behaviour are
rewritten to assert the fix rather than deleted, so the suite still
documents the edge case -- and still totals 94.
CHANGELOG records these plus the live-clock change from aaabc61 under
Changed/Fixed, since all three are user-visible. The two new promotion
suites join the CI unit job (449 tests).
Verified: unit job 449 passed, plugin-safety job 60 passed, and the
hockey (16) and baseball (24) plugin harnesses render clean at every
panel size.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FgbA8SMutQQpXkMG8LMmC4
This commit is contained in:
@@ -305,15 +305,25 @@ class TestExtractGameDetailsContract:
|
||||
assert details["home_shots"] == 0
|
||||
assert details["away_shots"] == 0
|
||||
|
||||
def test_hockey_event_without_statistics_returns_none(self):
|
||||
# PINNED AS-IS: the hockey extractor unconditionally iterates
|
||||
# competitor["statistics"]; a competitor without the key raises
|
||||
# KeyError internally and the WHOLE event is dropped (returns
|
||||
# None), even though scores/status are present.
|
||||
def test_hockey_event_without_statistics_still_extracts(self):
|
||||
# FIXED (was pinned as returning None): the hockey extractor used to
|
||||
# iterate competitor["statistics"] unguarded, so a competitor without
|
||||
# the key raised KeyError internally and the WHOLE event was dropped
|
||||
# despite valid scores and status. It now defaults to an empty list,
|
||||
# matching the behaviour already shipped in the hockey plugin, so the
|
||||
# event survives with zeroed shot counts -- the same values
|
||||
# test_hockey_live_power_play_and_default_shots already expects for an
|
||||
# EMPTY statistics array.
|
||||
event = make_event("410", "in", "2026-01-15T18:30:00Z")
|
||||
for comp in event["competitions"][0]["competitors"]:
|
||||
del comp["statistics"]
|
||||
assert extract(Hockey, event) is None
|
||||
details = extract(Hockey, event)
|
||||
assert details is not None
|
||||
assert details["home_abbr"] == "TB"
|
||||
assert details["away_abbr"] == "DAL"
|
||||
assert details["home_score"] == "3"
|
||||
assert details["home_shots"] == 0
|
||||
assert details["away_shots"] == 0
|
||||
|
||||
def test_baseball_live_inning_and_count(self):
|
||||
event = make_event(
|
||||
@@ -337,14 +347,21 @@ class TestExtractGameDetailsContract:
|
||||
assert details["status"] == "status_in_progress"
|
||||
assert details["series_summary"] == ""
|
||||
|
||||
def test_baseball_live_without_top_level_status_returns_none(self):
|
||||
# PINNED AS-IS: for live games the baseball extractor reads
|
||||
# game_event["status"] (the event TOP-LEVEL status, not the
|
||||
# competition status) for the inning; an otherwise-valid live
|
||||
# event lacking that duplicate key is dropped entirely.
|
||||
event = make_event("412", "in", "2026-07-16T23:05:00Z")
|
||||
def test_baseball_live_without_top_level_status_still_extracts(self):
|
||||
# FIXED (was pinned as returning None): the baseball extractor read
|
||||
# game_event["status"] -- the event TOP-LEVEL status -- for the
|
||||
# inning, so an otherwise-valid live event lacking that duplicate key
|
||||
# was dropped entirely. Real ESPN events carry status in both places,
|
||||
# but MiLB events (synthesized from the MLB Stats API into an
|
||||
# ESPN-like shape) populate only the competition-level one. It now
|
||||
# reads the competition-level `status` that
|
||||
# _extract_game_details_common has already validated, so it can never
|
||||
# be missing at that point.
|
||||
event = make_event("412", "in", "2026-07-16T23:05:00Z", period=7)
|
||||
del event["status"]
|
||||
assert extract(Baseball, event) is None
|
||||
details = extract(Baseball, event)
|
||||
assert details is not None
|
||||
assert details["inning"] == 7
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user