Files
hermes-webui/tests/test_1694_prompt_ownership.py
2026-05-07 16:58:40 +00:00

107 lines
4.6 KiB
Python

"""Regression tests for #1694 approval/clarify prompt ownership.
Prompt state belongs to the session that owns the running stream. A background
session's approval/clarify event must not render over or hide the currently
active pane's card, but the pending prompt should remain available when the user
switches back to that session.
"""
from pathlib import Path
REPO_ROOT = Path(__file__).parent.parent
MESSAGES_JS = (REPO_ROOT / "static" / "messages.js").read_text(encoding="utf-8")
SESSIONS_JS = (REPO_ROOT / "static" / "sessions.js").read_text(encoding="utf-8")
def _body_from_brace(src: str, brace: int, label: str) -> str:
assert brace >= 0, f"body opening brace not found for: {label}"
depth = 1
i = brace + 1
while i < len(src) and depth:
ch = src[i]
if ch == "{":
depth += 1
elif ch == "}":
depth -= 1
i += 1
assert depth == 0, f"body did not close for: {label}"
return src[brace + 1 : i - 1]
def _brace_body_after(src: str, marker: str) -> str:
start = src.find(marker)
assert start >= 0, f"marker not found: {marker}"
brace = src.find("{", start)
return _body_from_brace(src, brace, marker)
def _function_body(src: str, name: str) -> str:
marker = f"function {name}("
start = src.find(marker)
assert start >= 0, f"function not found: {name}"
signature_end = src.find(")", start)
assert signature_end >= 0, f"function signature not found: {name}"
brace = src.find("{", signature_end)
return _body_from_brace(src, brace, name)
def _event_body(event_name: str) -> str:
return _brace_body_after(MESSAGES_JS, f"source.addEventListener('{event_name}'")
def test_stream_prompt_events_use_session_owned_show_helpers():
"""Background stream prompts should be cached by owner before pane render."""
approval_body = _event_body("approval")
clarify_body = _event_body("clarify")
assert "showApprovalForSession(activeSid" in approval_body
assert "showApprovalCard(d, 1)" not in approval_body
assert "showClarifyForSession(activeSid" in clarify_body
assert "showClarifyCard(d)" not in clarify_body
def test_approval_card_render_is_gated_to_active_session_and_cached():
body = _function_body(MESSAGES_JS, "showApprovalCard")
assert "_rememberApprovalPending(" in body
assert "_approvalPromptBelongsToActiveSession(sid)" in body
assert "return;" in body
assert "let _approvalPendingBySession" in MESSAGES_JS
assert "function _renderPendingPromptsForActiveSession()" in MESSAGES_JS
def test_clarify_card_render_is_gated_to_active_session_and_cached():
body = _function_body(MESSAGES_JS, "showClarifyCard")
assert "_rememberClarifyPending(" in body
assert "_clarifyPromptBelongsToActiveSession(sid)" in body
assert "return;" in body
assert "let _clarifyPendingBySession" in MESSAGES_JS
assert "function _renderPendingPromptsForActiveSession()" in MESSAGES_JS
def test_polling_empty_state_clears_only_the_owner_prompt():
approval_poll = _function_body(MESSAGES_JS, "startApprovalPolling")
approval_fallback = _function_body(MESSAGES_JS, "_startApprovalFallbackPoll")
clarify_poll = _function_body(MESSAGES_JS, "startClarifyPolling")
clarify_fallback = _function_body(MESSAGES_JS, "_startClarifyFallbackPoll")
combined = "\n".join([approval_poll, approval_fallback, clarify_poll, clarify_fallback])
assert "_clearApprovalPendingForSession(sid)" in combined
assert "_hideApprovalCardIfOwner(sid" in combined
assert "_clearClarifyPendingForSession(sid)" in combined
assert "_hideClarifyCardIfOwner(sid" in combined
assert "else { hideApprovalCard(); }" not in combined
assert "else { hideClarifyCard(false, 'expired'); }" not in combined
assert "stopApprovalPolling(); hideApprovalCard(true); return;" not in combined
assert "stopClarifyPolling(); hideClarifyCard(true, 'session'); return;" not in combined
def test_load_session_rerenders_cached_prompt_for_new_active_session():
body = _function_body(SESSIONS_JS, "loadSession")
assert "_renderPendingPromptsForActiveSession();" in body
def test_prompt_rerender_hides_previous_session_cards_without_clearing_cache():
approval_body = _function_body(MESSAGES_JS, "_renderPendingApprovalForActiveSession")
clarify_body = _function_body(MESSAGES_JS, "_renderPendingClarifyForActiveSession")
assert "_approvalSessionId && _approvalSessionId !== sid" in approval_body
assert "hideApprovalCard(true)" in approval_body
assert "_clarifySessionId && _clarifySessionId !== sid" in clarify_body
assert "hideClarifyCard(true, 'session')" in clarify_body