Files
hermes-webui/tests/test_issue2341_pending_user_reattach.py
2026-06-26 11:12:50 +00:00

78 lines
3.6 KiB
Python

from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
SESSIONS_JS = (ROOT / "static" / "sessions.js").read_text(encoding="utf-8")
UI_JS = (ROOT / "static" / "ui.js").read_text(encoding="utf-8")
def _load_session_inflight_branch() -> str:
start = SESSIONS_JS.find("if(INFLIGHT[sid]){")
assert start != -1, "loadSession INFLIGHT branch not found"
end = SESSIONS_JS.find("}else{", start)
assert end != -1, "loadSession INFLIGHT branch end not found"
return SESSIONS_JS[start:end]
def test_load_session_inflight_reattach_merges_pending_user_message_before_render():
"""#2341: Reattaching to an active stream must show the initiating user turn.
A reload or session switch can hydrate a running turn from INFLIGHT while the
backend still carries the user prompt only as pending_user_message. Without
merging that pending row before renderMessages(), the user sees assistant
thinking/tool activity without the prompt that started it.
"""
block = _load_session_inflight_branch()
merge_pos = block.find("_mergePendingSessionMessage")
# #3326 added an optional {preserveScroll} arg to the INFLIGHT-branch render
# call; match the call form rather than the bare `renderMessages();`.
render_pos = block.find("renderMessages(")
assert merge_pos != -1, (
"loadSession's INFLIGHT reattach branch must merge pending_user_message "
"into S.messages before rendering the running turn"
)
assert render_pos != -1, "INFLIGHT branch render call not found"
assert merge_pos < render_pos, (
"The pending user row must be present before renderMessages() rebuilds "
"the active transcript"
)
assert "restoreLiveTurnHtmlForSession(sid)" in block, (
"Session restore may keep a live DOM snapshot instead of always "
"recreating a fresh Thinking row after renderMessages()"
)
assert "INFLIGHT[sid].messages=S.messages;" in block, (
"After merging the pending user row, the INFLIGHT cache should be updated "
"so later session switches keep the same visible turn"
)
assert "messages.findIndex(m=>m&&m.role==='assistant'&&m._live)" in SESSIONS_JS
assert "messages.splice(liveAssistantIdx,0,pendingMsg)" in SESSIONS_JS
def test_pending_user_message_dedup_checks_current_message_array():
"""#2341: Pending merge must not duplicate an already-visible user row."""
assert "function getPendingSessionMessage(session, messagesOverride=null)" in UI_JS
helper_start = UI_JS.find("function getPendingSessionMessage(session, messagesOverride=null)")
assert helper_start != -1, "getPendingSessionMessage helper not found"
helper_end = UI_JS.find("async function checkInflightOnBoot", helper_start)
assert helper_end != -1, "getPendingSessionMessage helper end not found"
helper = UI_JS[helper_start:helper_end]
assert "messagesOverride" in helper
assert "Array.isArray(messagesOverride)?messagesOverride" in helper.replace(" ", ""), (
"Pending-message dedup must inspect the current S.messages/INFLIGHT "
"array, not only session.messages from the metadata response"
)
assert "_pendingCurrentTailUserMessage(messages)" in helper, (
"Pending-message dedup must inspect only the current tail user row, "
"not a historical same-text row"
)
assert "sameCurrentTurn" in helper and "return null;" in helper, (
"Pending-message merge must still suppress duplicates when the current "
"tail user row already matches pending_user_message"
)
assert "[...messages].reverse().find" not in helper, (
"Pending-message dedup must not reverse-scan historical user rows"
)