Make a stalled draw wait observable (B-36)
_wait_for_next_block had no timeout, no log, and no audit entry: a connection that stopped advancing the tip left a round silently frozen in "drawing" with nothing in /admin to explain why. Log progress periodically, write a draw_stalled audit entry past a threshold (a few block-time multiples), and surface the wait via a new Round.drawing_started_at column, exposed as draw_waiting_since in GET /rounds/current. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -134,6 +134,36 @@ async def test_jackpot_comes_from_the_participants_actual_bets(client):
|
||||
assert body["jackpot_sats"] == (999_800_000 * 2) * 70 // 100
|
||||
|
||||
|
||||
async def test_draw_waiting_since_is_exposed_only_while_drawing(client):
|
||||
"""B-36: the "drawing" wait on a future block has no timeout, so the frontend
|
||||
needs draw_waiting_since to show "still waiting" instead of implying a bounded
|
||||
countdown. It must not leak for any other status, where it's meaningless."""
|
||||
from datetime import datetime, timezone
|
||||
|
||||
from app.db.models import Round, RoundConfig
|
||||
|
||||
ac, session_factory = client
|
||||
|
||||
started_at = datetime(2026, 7, 27, 10, 0, 0)
|
||||
async with session_factory() as session:
|
||||
session.add(RoundConfig(fee_address=""))
|
||||
session.add(Round(id=60, status="drawing", drawing_started_at=started_at))
|
||||
await session.commit()
|
||||
|
||||
body = (await ac.get("/rounds/current")).json()
|
||||
assert body["draw_waiting_since"] == "2026-07-27T10:00:00+00:00"
|
||||
|
||||
async with session_factory() as session:
|
||||
from sqlalchemy import select
|
||||
|
||||
round_ = (await session.scalars(select(Round).where(Round.id == 60))).one()
|
||||
round_.status = "paying_out"
|
||||
await session.commit()
|
||||
|
||||
body = (await ac.get("/rounds/current")).json()
|
||||
assert body["draw_waiting_since"] is None
|
||||
|
||||
|
||||
async def test_unhandled_errors_use_the_structured_detail_shape(client):
|
||||
"""B-24: the catch-all handler answered with a bare-string `detail`, while
|
||||
app/api/errors.py documents detail as {"code", "message", "params"}. Clients then
|
||||
|
||||
Reference in New Issue
Block a user