Files
hermes-webui/tests/test_issue1867_upload_size_preflight.py
nesquena-hermes ceea2438eb fix: include attachments in steer submissions
Steering (submitting mid-stream) dropped any pending composer attachments. Now the
pending files are uploaded before the /steer payload is sent, the uploaded file
paths are appended to the steer text so the active agent can inspect them with
tools, file chips stay staged if the steer fails (so nothing is lost), and
file-only busy submissions are handled.

Contributor stage; gate-pass. Co-authored-by: ruizanthony <ruizanthony@users.noreply.github.com>
2026-07-05 06:50:34 +00:00

77 lines
2.7 KiB
Python

from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
UI_JS = ROOT / "static" / "ui.js"
I18N_JS = ROOT / "static" / "i18n.js"
CONFIG_PY = ROOT / "api" / "config.py"
UPLOAD_PY = ROOT / "api" / "upload.py"
def _function_body(src: str, name: str) -> str:
marker = f"function {name}"
start = src.index(marker)
signature_end = src.index(")", start)
brace = src.index("{", signature_end)
depth = 0
for idx in range(brace, len(src)):
if src[idx] == "{":
depth += 1
elif src[idx] == "}":
depth -= 1
if depth == 0:
return src[brace : idx + 1]
raise AssertionError(f"{name} function body not found")
def test_upload_limit_constant_matches_server_limit():
"""The browser preflight should read the runtime upload limit."""
ui = UI_JS.read_text(encoding="utf-8")
config = CONFIG_PY.read_text(encoding="utf-8")
assert "window.__HERMES_CONFIG__.maxUploadBytes" in ui
assert 'MAX_UPLOAD_BYTES = _env_mb_bytes("HERMES_WEBUI_MAX_UPLOAD_MB", 20)' in config
def test_file_picker_rejects_oversize_files_before_queueing():
"""Selecting an oversized file should never add it to pending uploads."""
src = UI_JS.read_text(encoding="utf-8")
body = _function_body(src, "addFiles")
size_gate = body.index("f&&f.size>MAX_UPLOAD_BYTES")
status_notice = body.index("_showUploadTooLarge(f)")
push_pending = body.index("S.pendingFiles.push(f)")
assert size_gate < status_notice < push_pending
assert "continue;" in body[size_gate:push_pending]
def test_pending_uploads_skip_fetch_for_oversize_files():
"""Restored or queued oversized files should fail locally before fetch()."""
src = UI_JS.read_text(encoding="utf-8")
body = _function_body(src, "uploadPendingFiles")
size_gate = body.index("f&&f.size>MAX_UPLOAD_BYTES")
form_data = body.index("const fd=new FormData()")
upload_fetch = body.index("fetch(url")
assert size_gate < form_data < upload_fetch
assert "throw new Error(_uploadTooLargeMessage(f))" in body[size_gate:form_data]
def test_upload_too_large_has_user_facing_message():
"""The status toast should explain the upload limit instead of a network reset."""
i18n = I18N_JS.read_text(encoding="utf-8")
ui = UI_JS.read_text(encoding="utf-8")
assert "upload_too_large" in i18n
assert "Maximum upload size is" in i18n
assert "_uploadTooLargeMessage(file)" in ui
def test_archive_extraction_limit_tracks_upload_limit():
"""Archive extraction guard should scale with the configured upload limit."""
upload = UPLOAD_PY.read_text(encoding="utf-8")
assert "_MAX_EXTRACTED_BYTES = 10 * MAX_UPLOAD_BYTES" in upload