fix: one current river level, not two
CI/CD Pipeline - Northern Thailand Ping River Monitor / Test Suite (3.11) (push) Failing after 29s
CI/CD Pipeline - Northern Thailand Ping River Monitor / Build Docker Image (push) Skipped
CI/CD Pipeline - Northern Thailand Ping River Monitor / Integration Test with Services (push) Skipped
CI/CD Pipeline - Northern Thailand Ping River Monitor / Deploy to Staging (push) Skipped
CI/CD Pipeline - Northern Thailand Ping River Monitor / Deploy to Production (push) Skipped
CI/CD Pipeline - Northern Thailand Ping River Monitor / Performance Test (push) Skipped
CI/CD Pipeline - Northern Thailand Ping River Monitor / Code Quality (push) Successful in 18s
CI/CD Pipeline - Northern Thailand Ping River Monitor / Cleanup (push) Successful in 1s
CI/CD Pipeline - Northern Thailand Ping River Monitor / Test Suite (3.11) (push) Failing after 29s
CI/CD Pipeline - Northern Thailand Ping River Monitor / Build Docker Image (push) Skipped
CI/CD Pipeline - Northern Thailand Ping River Monitor / Integration Test with Services (push) Skipped
CI/CD Pipeline - Northern Thailand Ping River Monitor / Deploy to Staging (push) Skipped
CI/CD Pipeline - Northern Thailand Ping River Monitor / Deploy to Production (push) Skipped
CI/CD Pipeline - Northern Thailand Ping River Monitor / Performance Test (push) Skipped
CI/CD Pipeline - Northern Thailand Ping River Monitor / Code Quality (push) Successful in 18s
CI/CD Pipeline - Northern Thailand Ping River Monitor / Cleanup (push) Successful in 1s
The verdict banner and the P.1 outlook each wrote state.p1Now from a different feed — the banner from the latest measurement, the outlook from current_level on the forecast rows, which carries whatever the model saw at its as_of. Forecasts are precomputed hourly, so the two drifted apart: production showed 1.66 m in the banner and 1.52 m in the outlook directly below it. Harmless at low water; at flood stage two contradictory river levels on one screen undermine the warning. setP1Level() now arbitrates: freshest timestamp wins, and the replay and demo hooks pass force since they deliberately pin a level that is not the live one. The outlook renders the arbitrated value and clamps the shown peak to at least the current level, so a stale forecast can no longer predict a peak below where the river already is. endReplay drops the replayed level so live data re-arbitrates cleanly. Verified against a stub reproducing the exact production conditions (gauge 1.66 at 08:35 vs forecast 1.52 at 08:00): both now read 1.66; a 2.50 m rise against a stale 1.81 m peak renders 2.50/2.50; the 2024 replay still tracks its frames and returns to live on stop.
This commit is contained in:
@@ -175,3 +175,25 @@ def test_dashboard_default_language_respects_browser_order():
|
||||
|
||||
assert "langs.some" not in html # the old any-English-wins test
|
||||
assert "if (code.startsWith('th')) return 'th';" in html
|
||||
|
||||
|
||||
def test_dashboard_shows_one_current_river_level():
|
||||
"""The verdict banner and the P.1 outlook must not disagree about "now".
|
||||
|
||||
Production served 1.66 m in the banner and 1.52 m in the outlook at the
|
||||
same moment: the banner used the latest measurement, the outlook used the
|
||||
forecast payload's current_level from an older as_of.
|
||||
"""
|
||||
html = DASHBOARD_PATH.read_text(encoding="utf-8")
|
||||
|
||||
# One arbitrated writer, freshest-wins
|
||||
assert "function setP1Level(" in html
|
||||
assert "state.p1NowAt" in html
|
||||
|
||||
# Neither feed may assign the level directly any more
|
||||
assert "state.p1Now = Number(p1.water_level)" not in html
|
||||
assert "state.p1Now = row.current_level" not in html
|
||||
|
||||
# The outlook renders the arbitrated level, and never a peak below it
|
||||
assert "const shownNow = state.p1Now != null" in html
|
||||
assert "Math.max(Number(row.predicted_max_level), shownNow)" in html
|
||||
|
||||
Reference in New Issue
Block a user