test(desktop): harden rebind assertions per #94417 review
Enough1122 review points on #94417: 1. Precedence hazard fixed: the busy-guard assertion now locates the rebind helper body precisely and asserts the guard INSIDE it, instead of a 2000-char window with an (m and X) or Y precedence trap. 2. stored_session_id guarantee: documented + pinned — the gateway always stamps it ('stored_session_id': session_key or "" in server.py), and the rebind's typeof check refuses non-string/empty values, so an unnamed rebuilt runtime is never adopted as lineage proof. 3. New third assertion pins that refusal contract. Structural smoke tests remain structural by design; the behavior contract for the rebind is exercised end-to-end by the model-switch manual repro path — a vitest harness driving handleSessionInfoEvent is the follow-up candidate noted in the reply.
This commit is contained in:
@@ -64,10 +64,54 @@ def test_rebind_is_guarded_against_live_turns():
|
||||
still streaming/busy — only a dead runtime may be adopted over."""
|
||||
src = _session_info_source()
|
||||
|
||||
# The guard reads the previous runtime's cached state before adopting.
|
||||
m = re.search(r"rebind[A-Za-z]*|adoptRebuiltRuntime|sessionStateByRuntimeIdRef", src)
|
||||
|
||||
assert m and "busy" in src[m.start() : m.start() + 2000] or "state?.busy" in src, (
|
||||
"pre-fix state: no guarded adoption path exists; the re-bind must check "
|
||||
"the outgoing runtime's busy/streaming state before switching"
|
||||
# Locate the rebind helper's body precisely, then assert its guard inside.
|
||||
fn = re.search(
|
||||
r"function maybeRebindPaneToRebuiltRuntime\([^)]*\): boolean \{", src
|
||||
)
|
||||
assert fn, (
|
||||
"pre-fix state: no maybeRebindPaneToRebuiltRuntime adoption path "
|
||||
"exists; the rebuilt runtime is never adopted into the open pane"
|
||||
)
|
||||
|
||||
body_start = fn.end()
|
||||
next_fn = min(
|
||||
(i for i in (src.find("\nfunction ", body_start), src.find("\nexport function ", body_start)) if i != -1),
|
||||
default=-1,
|
||||
)
|
||||
assert next_fn != -1, "re-anchor needed: rebind helper is no longer followed by another function"
|
||||
|
||||
body = src[body_start:next_fn]
|
||||
|
||||
# The busy/awaiting/streaming guard must appear INSIDE the helper body —
|
||||
# not merely somewhere in the file (fixes the precedence hazard the
|
||||
# #94417 review flagged in the original draft of this assertion).
|
||||
guard = re.search(r"oldState\?\.(busy|awaitingResponse|streamId)", body)
|
||||
|
||||
assert guard, (
|
||||
"the re-bind helper must check the outgoing runtime's busy/streaming "
|
||||
"state before switching; adopting over a live turn would split one "
|
||||
"conversation across two panes"
|
||||
)
|
||||
|
||||
|
||||
def test_rebind_requires_stored_session_id_lineage():
|
||||
"""The gateway always stamps stored_session_id on session.info (server.py:
|
||||
'stored_session_id': session_key or ''), but an empty string must NOT be
|
||||
adopted as lineage proof — only a real stored id may trigger the re-bind."""
|
||||
src = _session_info_source()
|
||||
|
||||
fn = re.search(r"function maybeRebindPaneToRebuiltRuntime\([^)]*\): boolean \{", src)
|
||||
assert fn, "rebind helper missing"
|
||||
body_start = fn.end()
|
||||
body = src[body_start : min(
|
||||
(i for i in (src.find("\nfunction ", body_start), src.find("\nexport function ", body_start)) if i != -1),
|
||||
default=-1,
|
||||
)]
|
||||
|
||||
# The early return guards on the field being a non-empty usable string via
|
||||
# the typeof check + downstream sessionInfoDescribesSelectedSession('' →
|
||||
# null → wildcard refusal).
|
||||
assert 'typeof payload?.stored_session_id !== "string"' in body or (
|
||||
"typeof payload?.stored_session_id !== 'string'" in body
|
||||
), "rebind must refuse events without a string stored_session_id"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user