Import AITURK IDE 1.0.0-beta.1 from Hermes 63279301; preserve MIT license
This commit is contained in:
@@ -0,0 +1,87 @@
|
||||
"""A backend-reclaimed session must tell the clients still holding it.
|
||||
|
||||
The idle-TTL reaper, the LRU cap, and the WS-orphan reap all tear a live
|
||||
session down without the client asking. Before ``session.reclaimed`` the client
|
||||
kept a runtime id the backend had already forgotten, and only discovered it by
|
||||
failing a later prompt — the "sessions suddenly lost in the backend" report.
|
||||
|
||||
Contracts here: a reclaim broadcasts with its reason, a user-initiated close
|
||||
stays silent, and a notify failure never breaks teardown.
|
||||
"""
|
||||
|
||||
import pytest
|
||||
|
||||
from tui_gateway import server
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def captured(monkeypatch):
|
||||
events = []
|
||||
monkeypatch.setattr(
|
||||
server, "_broadcast_global_event", lambda ev, payload=None: events.append((ev, payload))
|
||||
)
|
||||
# Teardown's real work (finalize, agent close, notifier unregister) is out
|
||||
# of scope — this is about what reaches the client.
|
||||
monkeypatch.setattr(server, "_finalize_session", lambda *a, **k: None)
|
||||
return events
|
||||
|
||||
|
||||
def _session():
|
||||
return {"_sid": "live-abc", "session_key": "20260731_120000_aaaaaa"}
|
||||
|
||||
|
||||
@pytest.mark.parametrize("reason", ["idle_timeout", "lru_evict", "ws_orphan_reap"])
|
||||
def test_reclaim_reasons_announce_to_clients(captured, reason):
|
||||
server._teardown_session(_session(), end_reason=reason)
|
||||
|
||||
assert captured == [
|
||||
(
|
||||
"session.reclaimed",
|
||||
{
|
||||
"session_id": "live-abc",
|
||||
"stored_session_id": "20260731_120000_aaaaaa",
|
||||
"reason": reason,
|
||||
},
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.parametrize("reason", ["tui_close", "tui_shutdown", "ws_disconnect", "branched"])
|
||||
def test_client_initiated_closes_stay_silent(captured, reason):
|
||||
"""The client asked for these, so announcing them would be noise."""
|
||||
server._teardown_session(_session(), end_reason=reason)
|
||||
|
||||
assert captured == []
|
||||
|
||||
|
||||
def test_broadcast_failure_does_not_break_teardown(monkeypatch):
|
||||
"""A wedged peer must not leave a session half torn down."""
|
||||
finalized = []
|
||||
|
||||
def _boom(*_a, **_k):
|
||||
raise RuntimeError("transport gone")
|
||||
|
||||
monkeypatch.setattr(server, "_broadcast_global_event", _boom)
|
||||
monkeypatch.setattr(
|
||||
server, "_finalize_session", lambda s, **k: finalized.append(k.get("end_reason"))
|
||||
)
|
||||
|
||||
server._teardown_session(_session(), end_reason="ws_orphan_reap")
|
||||
|
||||
assert finalized == ["ws_orphan_reap"]
|
||||
|
||||
|
||||
def test_reap_paths_stamp_the_runtime_id_the_client_holds():
|
||||
"""_pop_session_by_id stamps ``_sid``; the payload is useless without it.
|
||||
|
||||
All three reclaim paths pop before tearing down, so this is the invariant
|
||||
that makes the broadcast addressable on the client.
|
||||
"""
|
||||
server._sessions["live-xyz"] = {"session_key": "k"}
|
||||
try:
|
||||
popped = server._pop_session_by_id("live-xyz")
|
||||
finally:
|
||||
server._sessions.pop("live-xyz", None)
|
||||
|
||||
assert popped is not None
|
||||
assert popped["_sid"] == "live-xyz"
|
||||
Reference in New Issue
Block a user