mirror of
https://github.com/openswarm-ai/openswarm.git
synced 2026-08-20 19:52:23 +02:00
395 lines
13 KiB
Python
395 lines
13 KiB
Python
"""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
|