"""Mirror tests for the frontend label/result logic. The JS implementations live in: - frontend/src/app/pages/AgentChat/toolLabels.ts - frontend/src/app/pages/AgentChat/ToolCallBubble.tsx (getResultSummary, getInputSummary, parseMcpToolName, bashCommandDetail, prettyPath, prettyUrl, quoteQuery) We re-implement the rules in Python and pin them as tests so we get regression coverage from `pytest` too. Any drift between the JS source and these Python mirrors is the production-side breakage we want to catch. """ from __future__ import annotations import random import re import pytest # =========================================================================== Mirror: parseMcpToolName.displayName (sentence-case rule) =========================================================================== def parse_mcp_tool_name_display(raw_name: str) -> str | None: """Mirror of frontend parseMcpToolName().displayName.""" m = re.match(r"^mcp__([^_]+(?:-[^_]+)*)__(.+)$", raw_name) if not m: return None action = m.group(2) spaced = action.replace("_", " ").lower() return spaced[0].upper() + spaced[1:] if spaced else "" def test_parse_mcp_tool_name_get_message_details(): assert parse_mcp_tool_name_display( "mcp__google-workspace__get_message_details" ) == "Get message details" def test_parse_mcp_tool_name_send_email(): assert parse_mcp_tool_name_display( "mcp__google-workspace__send_gmail_message" ) == "Send gmail message" def test_parse_mcp_tool_name_search_emails(): assert parse_mcp_tool_name_display( "mcp__google-workspace__query_gmail_emails" ) == "Query gmail emails" def test_parse_mcp_tool_name_returns_none_for_non_mcp(): assert parse_mcp_tool_name_display("Bash") is None assert parse_mcp_tool_name_display("Read") is None def test_parse_mcp_tool_name_no_title_case(): """Regression test: NEVER capitalize every word.""" bad = parse_mcp_tool_name_display("mcp__notion__create_a_new_page") assert bad == "Create a new page" assert "A New Page" not in bad # =========================================================================== Mirror: getResultSummary (glyph-free regression test) =========================================================================== def get_result_summary_bash_success(stdout: str, exit_code: int = 0) -> str: """Mirror of getResultSummary for bash success case.""" if exit_code != 0: return f"exit {exit_code}" lines = [l for l in stdout.split("\n") if l.strip()] n = len(lines) return f"{n} line{'s' if n != 1 else ''}" def test_bash_success_summary_no_glyph(): """Regression: bash success used to return '✓ N lines'. Must now be glyph-free.""" assert "✓" not in get_result_summary_bash_success("hello\nworld") assert get_result_summary_bash_success("hello\nworld") == "2 lines" assert get_result_summary_bash_success("just one line") == "1 line" assert get_result_summary_bash_success("") == "0 lines" def test_bash_failure_summary_no_glyph(): """Failure summary too: 'exit 1' not '✗ exit 1'.""" assert get_result_summary_bash_success("", exit_code=1) == "exit 1" assert "✗" not in get_result_summary_bash_success("", exit_code=1) assert "✓" not in get_result_summary_bash_success("", exit_code=1) def test_no_check_glyph_in_summaries(): """Sweep: every plausible summary string never contains a check glyph.""" summaries = [ get_result_summary_bash_success("a"), get_result_summary_bash_success("a\nb\nc"), get_result_summary_bash_success("", exit_code=1), get_result_summary_bash_success("", exit_code=127), ] for s in summaries: assert "✓" not in s and "✔" not in s and "✗" not in s and "✘" not in s # =========================================================================== Mirror: bashCommandDetail extraction =========================================================================== def bash_command_detail(raw_cmd: str) -> str: """Mirror of frontend bashCommandDetail.""" if not raw_cmd: return "" cmd = raw_cmd.strip() # strip env var assignments + sudo/time/nice/env cmd = re.sub(r"^(?:[A-Z_][A-Z0-9_]*=\S+\s+)+", "", cmd) cmd = re.sub(r"^(?:sudo|time|nice|env)\s+", "", cmd) tokens = cmd.split() if not tokens: return "" bin_path = tokens[0].split("/")[-1] if bin_path == "git": sub = (tokens[1] if len(tokens) > 1 else "").lower() if sub in ("commit", "status", "log", "diff", "pull", "push", "fetch"): return "" return tokens[2].split("/")[-1] if len(tokens) > 2 else "" if bin_path in ("npm", "pnpm", "yarn", "bun", "pip", "pip3", "brew", "apt", "apt-get"): if len(tokens) > 2: args = [t for t in tokens[2:] if not t.startswith("-")][:2] return " ".join(args) return "" # First non-flag positional arg arg = next((t for t in tokens[1:] if not t.startswith("-")), "") if not arg: return "" if "/" in arg or "\\" in arg: # basename cleaned = arg.rstrip("/\\") parts = cleaned.replace("\\", "/").split("/") return parts[-1] if parts[-1] else cleaned return arg if len(arg) <= 50 else arg[:47] + "..." def test_bash_detail_rm_extracts_path(): assert bash_command_detail("rm /tmp/foo.txt") == "foo.txt" assert bash_command_detail("rm foo.txt") == "foo.txt" def test_bash_detail_git_commit_empty(): """git commit -m 'message' → no detail (verb covers it).""" assert bash_command_detail("git commit -m 'fix bug'") == "" assert bash_command_detail("git commit -m hi") == "" def test_bash_detail_git_status_empty(): assert bash_command_detail("git status") == "" def test_bash_detail_git_checkout_branch(): assert bash_command_detail("git checkout main") == "main" def test_bash_detail_npm_install(): assert bash_command_detail("npm install lodash") == "lodash" assert bash_command_detail("npm install lodash @types/node") == "lodash @types/node" def test_bash_detail_strips_sudo(): assert bash_command_detail("sudo rm /etc/foo") == "foo" def test_bash_detail_strips_env_assignments(): assert bash_command_detail("FOO=bar BAZ=qux rm /tmp/a") == "a" def test_bash_detail_handles_empty(): assert bash_command_detail("") == "" assert bash_command_detail(" ") == "" # =========================================================================== Mirror: prettyPath (basename a path) =========================================================================== def pretty_path(p: str) -> str: if not p: return "" cleaned = p.rstrip("/\\") parts = cleaned.replace("\\", "/").split("/") return parts[-1] if parts[-1] else cleaned def test_pretty_path_absolute(): assert pretty_path("/Users/eric/Downloads/openswarm/foo.ts") == "foo.ts" def test_pretty_path_relative(): assert pretty_path("a/b/c.tsx") == "c.tsx" def test_pretty_path_trailing_slash(): assert pretty_path("/a/b/c/") == "c" def test_pretty_path_empty(): assert pretty_path("") == "" # =========================================================================== Mirror: prettyUrl (host-only) =========================================================================== def pretty_url(u: str) -> str: if not u: return "" try: from urllib.parse import urlparse host = urlparse(u).hostname or "" return host[4:] if host.startswith("www.") else host or u[:60] except Exception: no_proto = re.sub(r"^https?://", "", u).split("/")[0].split("?")[0].split("#")[0] return no_proto[:60] def test_pretty_url_https(): assert pretty_url("https://example.com/long/path?q=1") == "example.com" def test_pretty_url_strips_www(): assert pretty_url("https://www.example.com/path") == "example.com" def test_pretty_url_subdomain_kept(): assert pretty_url("https://api.example.com/v1") == "api.example.com" def test_pretty_url_empty(): assert pretty_url("") == "" # =========================================================================== Mirror: quoteQuery =========================================================================== def quote_query(q: str, max_len: int = 60) -> str: if not q: return "" trimmed = q if len(q) <= max_len else q[:max_len - 1] + "…" return f'"{trimmed}"' def test_quote_query_short(): assert quote_query("TODO") == '"TODO"' def test_quote_query_long_truncated(): long = "a" * 100 result = quote_query(long) assert result.startswith('"') assert result.endswith('"') assert len(result) <= 62 # 60 chars + 2 quotes def test_quote_query_empty(): assert quote_query("") == "" # =========================================================================== Mirror: stable-seeded variant pick (djb2 hash → mod n) =========================================================================== def stable_index(seed: str | None, n: int) -> int: """Mirror of frontend _stableIndex.""" if n <= 1 or not seed: return 0 h = 5381 for ch in seed: h = ((h << 5) + h + ord(ch)) & 0xFFFFFFFF # 32-bit # JS does `| 0` which produces signed int; Math.abs handles that if h >= 0x80000000: h -= 0x100000000 return abs(h) % n def test_stable_index_same_seed_same_result(): """Critical: same call.id always → same variant index.""" n = 5 for seed in ("abc-123", "xyz-789", "tool-call-uuid-deadbeef"): a = stable_index(seed, n) b = stable_index(seed, n) c = stable_index(seed, n) assert a == b == c, f"unstable for seed={seed!r}" def test_stable_index_different_seeds_diverge(): """Different seeds usually give different results (probabilistic).""" n = 7 seeds = [f"seed-{i}-{random.randint(0, 99999)}" for i in range(50)] indices = [stable_index(s, n) for s in seeds] # All same is statistically extremely unlikely assert len(set(indices)) > 1 def test_stable_index_in_range(): """Index always in [0, n-1].""" for _ in range(200): seed = "".join(random.choices(string.ascii_letters + string.digits, k=20)) n = random.randint(2, 20) idx = stable_index(seed, n) assert 0 <= idx < n, f"out of range: {idx} for n={n}" def test_stable_index_empty_seed_zero(): """No seed → safe-default (index 0).""" assert stable_index(None, 5) == 0 assert stable_index("", 5) == 0 def test_stable_index_n_one(): """Single-variant pool → always index 0.""" assert stable_index("anything", 1) == 0 # =========================================================================== Mirror: bash verb extraction (the leading-binary lookup) =========================================================================== BIN_VERB_MAP = { "rm": ("Deleting", "Deleted"), "mv": ("Moving", "Moved"), "cp": ("Copying", "Copied"), "mkdir": ("Creating folder", "Created folder"), "ls": ("Listing folder", "Listed folder"), "find": ("Hunting for files", "Hunted for files"), "grep": ("Searching files", "Searched files"), "cat": ("Reading", "Read"), "echo": ("Printing", "Printed"), "make": ("Building", "Built"), } GIT_VERB_MAP = { "commit": ("Committing", "Committed"), "push": ("Pushing to git", "Pushed to git"), "pull": ("Pulling from git", "Pulled from git"), "checkout": ("Switching branches", "Switched branches"), "merge": ("Merging", "Merged"), } PKG_VERB_MAP_INSTALL = ("Installing packages", "Installed packages") PKG_VERB_MAP_UNINSTALL = ("Removing packages", "Removed packages") def bash_verb(cmd: str, past: bool = False): if not cmd: return None stripped = re.sub(r"^(?:[A-Z_][A-Z0-9_]*=\S+\s+)+", "", cmd.strip()) stripped = re.sub(r"^(?:sudo|time|nice|env)\s+", "", stripped) tokens = stripped.split() if not tokens: return None bin_path = tokens[0].split("/")[-1].lower() sub = (tokens[1] if len(tokens) > 1 else "").lower() if bin_path == "git" and sub in GIT_VERB_MAP: return GIT_VERB_MAP[sub][1 if past else 0] if bin_path in ("npm", "pnpm", "yarn", "pip", "pip3", "brew"): if sub in ("install", "add", "i"): return PKG_VERB_MAP_INSTALL[1 if past else 0] if sub in ("uninstall", "remove", "rm"): return PKG_VERB_MAP_UNINSTALL[1 if past else 0] if bin_path in BIN_VERB_MAP: return BIN_VERB_MAP[bin_path][1 if past else 0] return None def test_bash_verb_rm_deleted(): assert bash_verb("rm foo", past=True) == "Deleted" assert bash_verb("rm foo", past=False) == "Deleting" def test_bash_verb_git_commit(): assert bash_verb("git commit -m hi", past=True) == "Committed" def test_bash_verb_git_push(): assert bash_verb("git push origin main", past=True) == "Pushed to git" def test_bash_verb_npm_install(): assert bash_verb("npm install lodash", past=True) == "Installed packages" def test_bash_verb_unknown_returns_none(): """Truly unknown command falls through to default 'Ran command'.""" assert bash_verb("supercustomtool foo bar") is None def test_bash_verb_strips_sudo(): assert bash_verb("sudo rm -rf /tmp/x", past=True) == "Deleted" def test_bash_verb_strips_env(): assert bash_verb("DEBUG=1 npm test", past=False) is None # 'test' isn't in pkg map for bash_verb # string is needed for stable_index test import string # noqa: E402