fix(#20): heid-bug-hunt fixups — admin-stream + bifrost hardening (slice-6)

Cold spec-free panel (Gróa + Hulda + Regin, source-verified by Heid): the adapter's
core re-wrap is sound, but 4 real hardening gaps the conformance CR couldn't see —
all in failure-path normalization + open-world degrade, judged against the general
ConnectFailed floor + the degrade-never-crash promise. All fixed:

- [bug, 3/3] `stream_admin_events` never mapped `ConnectFailed` — the SDK admin-stream
  open raises it on a connect-time / auth-resolution failure (the general transport
  floor; confirmed in the SDK source), and `stream_turn` + the bifrost GET both catch
  it, and this endpoint's OWN comment claimed it did. An unmapped ConnectFailed escaped
  the web gen's `except (Sse*)` and aborted the SSE with no `stream_error`. Now mapped
  → `SseConnectFailed`, mirroring stream_turn.
- [bug, 2/3] non-str `type` crashed the web filter — the re-wrap used `ev.type or ""`
  (falsy-only), so a truthy non-str `type` (123, a list) reached `.startswith` →
  AttributeError. Now `ev.type if isinstance(ev.type, str) else ""` (matches the
  admin_id/data isinstance guards — same container-type class as slice-5).
- [robustness] `_session_bifrost_endpoint` did `dict(bstate)` on the open-world 200
  body — a non-mapping (list/scalar) → TypeError/500. Now degrades to `{}` (I introduced
  this in slice-6 by changing `JSONResponse(bstate)` → `dict(bstate)`).
- [robustness] `_admin_events_endpoint.gen` allocated the transport + built `_wt_client`
  BEFORE the try/finally — a construction failure would leak the httpx transport. Moved
  `_wt_client` inside the try so the finally always closes it.

Voided (Heid): Regin's `dict(ev.data)` TypeError — the `isinstance(_, Mapping)` guard
already routes non-mappings to `{}` before `dict()`.

Added adapter tests (ConnectFailed→SseConnectFailed; non-str type→"") + a web test
(non-mapping bifrost body → 200 {}). Suite 494 green; my code ruff-clean (13 E501/F841
in test_web_server.py are PRE-EXISTING, HEAD-identical, untouched); mypy clean on wt.py.
Live smoke re-run clean (real session.created event re-wrapped; bifrost 404 envelope).
Patch bump 0.21.19 → 0.21.20.
This commit is contained in:
vh
2026-07-19 13:13:44 -07:00
parent bba57e1b39
commit 11ae2f056e
6 changed files with 56 additions and 15 deletions
+18 -2
View File
@@ -1205,13 +1205,29 @@ class TestStreamAdminEventsWt:
assert fake.calls[-1] == ("stream_events", (), {"last_event_id": 42})
async def test_non_200_apierror_maps_to_sse_connect_failed(self) -> None:
# The SDK admin stream raises ApiError("admin_stream_failed", status=…) on a
# non-200 open (NOT ConnectFailed) — mapped → SseConnectFailed for the web.
# A non-200 open raises ApiError("admin_stream_failed", status=…) → SseConnectFailed.
fake = _FakeAdmin(stream_error=ApiError("admin_stream_failed", "no", status=502))
with pytest.raises(SseConnectFailed) as ei:
await _drain(stream_admin_events(_wtad(fake)))
assert ei.value.status == 502
async def test_connect_failed_maps_to_sse_connect_failed(self) -> None:
# A connect-time transport / auth-resolution failure surfaces as ConnectFailed
# (the SDK's general floor) → SseConnectFailed, mirroring stream_turn — else it
# escapes the web gen's Sse* handler and aborts the SSE (heid bug-hunt slice-6).
fake = _FakeAdmin(stream_error=wtsdk.ConnectFailed("connect_failed", "refused", status=0))
with pytest.raises(SseConnectFailed) as ei:
await _drain(stream_admin_events(_wtad(fake)))
assert ei.value.status == 0
async def test_nonstr_type_degrades_to_empty(self) -> None:
# A truthy NON-str `type` (a partial/wrong open-world wire) must degrade to ""
# so the web filter's `.startswith` never AttributeErrors — `or ""` (falsy-only)
# would let it through; the isinstance guard catches it (heid bug-hunt slice-6).
fake = _FakeAdmin(events=[_SdkAdminEvent(1, 123, "t", {"session_id": "s"})])
out = await _drain(stream_admin_events(_wtad(fake)))
assert out[0].type == ""
async def test_connection_dropped_carries_cursor(self) -> None:
# A mid-stream drop / resumable EOF carries the resume cursor.
fake = _FakeAdmin(stream_error=wtsdk.ConnectionDropped("42"))