Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions tests/tui_gateway/test_cli_exec_streams.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
"""Focused tests for cli.exec structured stdout/stderr payload fields."""

from types import SimpleNamespace

import tui_gateway.methods_tools as m


def _proc(stdout, stderr, returncode=0):
return SimpleNamespace(stdout=stdout, stderr=stderr, returncode=returncode)


def test_structured_streams_split_both():
payload = m._cli_exec_output(_proc("out\n", "warn\n"))
assert payload["output"] == "out\n\nwarn"
assert payload["stdout"] == "out\n"
assert payload["stderr"] == "warn\n"


def test_empty_and_none_streams_yield_placeholders():
for stdout, stderr in (("", ""), (None, None)):
payload = m._cli_exec_output(_proc(stdout, stderr))
assert payload["output"] == "(no output)"
assert payload["stdout"] == ""
assert payload["stderr"] == ""


def test_bounds_and_default_limit_identity():
limited = m._cli_exec_output(_proc("abcdefghijk", "1234567890"), limit=8)
assert limited["stdout"] == "abcdefgh"
assert limited["stderr"] == "12345678"
assert limited["output"] == "abcdefgh"

payload = m._cli_exec_output(_proc("a" * 60_000, "b" * 60_000))
assert len(payload["stdout"]) == 48_000
assert len(payload["stderr"]) == 48_000
assert payload["output"] == "a" * 48_000


def test_payload_key_set_is_exact():
payload = m._cli_exec_output(_proc("out", "warn"))
assert set(payload) == {"output", "stdout", "stderr"}
11 changes: 10 additions & 1 deletion tui_gateway/methods_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,15 @@ def _joined_output(r) -> str:
return "\n".join(p for p in (r.stdout or "", r.stderr or "") if p).strip()


def _cli_exec_output(r, limit: int = 48_000) -> dict[str, str]:
"""Structured cli.exec streams; ``output`` matches the legacy joined bound."""
return {
"output": (_joined_output(r) or "(no output)")[:limit],
"stdout": (r.stdout or "")[:limit],
"stderr": (r.stderr or "")[:limit],
}


def _toolset_rows(params: dict, *, with_tools: bool) -> list[dict]:
toolsets = _tools_mod("toolsets")
session = _sessions.get(params.get("session_id", ""))
Expand Down Expand Up @@ -442,7 +451,7 @@ def _(rid, params: dict) -> dict:
return _captured_exec(
rid, [sys.executable, "-m", "hermes_cli.main", *argv], min(int(params.get("timeout", 240)), 600),
on_result=lambda r: _ok(rid, {
"blocked": False, "code": r.returncode, "output": (_joined_output(r) or "(no output)")[:48_000]}),
"blocked": False, "code": r.returncode, **_cli_exec_output(r)}),
timeout_err=(5016, "cli.exec: timeout"), fail_code=5017,
env=hermes_subprocess_env(inherit_credentials=True))

Expand Down