cmux/tests/test_cli_claude_teams_env.py
Lawrence Chen 00587ed856
Add cmux claude-teams launcher (#1179)
* Add claude-teams CLI command

* Add claude-teams launcher regression test

* Exec claude-teams launcher in place

* Add existing-shim claude-teams regression test

* Reuse claude-teams shim and refresh dev CLI

* Add wrapper-selection claude-teams regression test

* Launch real claude binary for claude-teams

* Add claude-teams auto-mode launcher regression test

* Default claude-teams to fake tmux auto mode

* Build tagged reloads under DerivedData

* Add claude-teams tmux sequence regression test

* Fix claude-teams tmux teammate compatibility

* Add claude-teams split focus regression test

* Keep claude-teams leader pane focused

* Tighten claude-teams review fixes

* Pass claude-teams help through to Claude

* Use sentinel TERM_PROGRAM in claude-teams test
2026-03-11 02:42:33 -07:00

192 lines
6.8 KiB
Python

#!/usr/bin/env python3
"""
Regression test: `cmux claude-teams` injects the tmux-style auto-mode env.
"""
from __future__ import annotations
import os
import subprocess
import tempfile
from pathlib import Path
from claude_teams_test_utils import resolve_cmux_cli
def make_executable(path: Path, content: str) -> None:
path.write_text(content, encoding="utf-8")
path.chmod(0o755)
def read_text(path: Path) -> str:
if not path.exists():
return ""
return path.read_text(encoding="utf-8").strip()
def main() -> int:
try:
cli_path = resolve_cmux_cli()
except Exception as exc:
print(f"FAIL: {exc}")
return 1
with tempfile.TemporaryDirectory(prefix="cmux-claude-teams-env-") as td:
tmp = Path(td)
real_bin = tmp / "real-bin"
real_bin.mkdir(parents=True, exist_ok=True)
env_log = tmp / "agent-teams.log"
tmux_log = tmp / "tmux-path.log"
cmux_bin_log = tmp / "cmux-bin.log"
argv_log = tmp / "argv.log"
tmux_env_log = tmp / "tmux-env.log"
tmux_pane_log = tmp / "tmux-pane.log"
term_log = tmp / "term.log"
term_program_log = tmp / "term-program.log"
socket_path_log = tmp / "socket-path.log"
socket_password_log = tmp / "socket-password.log"
fake_home = tmp / "home"
fake_home.mkdir(parents=True, exist_ok=True)
make_executable(
real_bin / "claude",
"""#!/usr/bin/env bash
set -euo pipefail
printf '%s\\n' "${CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS-__UNSET__}" > "$FAKE_AGENT_TEAMS_LOG"
command -v tmux > "$FAKE_TMUX_PATH_LOG"
printf '%s\\n' "${CMUX_CLAUDE_TEAMS_CMUX_BIN-__UNSET__}" > "$FAKE_CMUX_BIN_LOG"
printf '%s\\n' "$@" > "$FAKE_ARGV_LOG"
printf '%s\\n' "${TMUX-__UNSET__}" > "$FAKE_TMUX_ENV_LOG"
printf '%s\\n' "${TMUX_PANE-__UNSET__}" > "$FAKE_TMUX_PANE_LOG"
printf '%s\\n' "${TERM-__UNSET__}" > "$FAKE_TERM_LOG"
printf '%s\\n' "${TERM_PROGRAM-__UNSET__}" > "$FAKE_TERM_PROGRAM_LOG"
printf '%s\\n' "${CMUX_SOCKET_PATH-__UNSET__}" > "$FAKE_SOCKET_PATH_LOG"
printf '%s\\n' "${CMUX_SOCKET_PASSWORD-__UNSET__}" > "$FAKE_SOCKET_PASSWORD_LOG"
""",
)
env = os.environ.copy()
env["HOME"] = str(fake_home)
env["PATH"] = f"{real_bin}:/usr/bin:/bin"
env["FAKE_AGENT_TEAMS_LOG"] = str(env_log)
env["FAKE_TMUX_PATH_LOG"] = str(tmux_log)
env["FAKE_CMUX_BIN_LOG"] = str(cmux_bin_log)
env["FAKE_ARGV_LOG"] = str(argv_log)
env["FAKE_TMUX_ENV_LOG"] = str(tmux_env_log)
env["FAKE_TMUX_PANE_LOG"] = str(tmux_pane_log)
env["FAKE_TERM_LOG"] = str(term_log)
env["FAKE_TERM_PROGRAM_LOG"] = str(term_program_log)
env["FAKE_SOCKET_PATH_LOG"] = str(socket_path_log)
env["FAKE_SOCKET_PASSWORD_LOG"] = str(socket_password_log)
env["TMUX"] = "__HOST_TMUX__"
env["TMUX_PANE"] = "%999"
env["TERM"] = "xterm-256color"
env["TERM_PROGRAM"] = "__HOST_TERM_PROGRAM__"
explicit_socket_path = str(tmp / "explicit-cmux.sock")
explicit_socket_password = "topsecret"
proc = subprocess.run(
[
cli_path,
"--socket",
explicit_socket_path,
"--password",
explicit_socket_password,
"claude-teams",
"--version",
],
capture_output=True,
text=True,
check=False,
env=env,
timeout=30,
)
if proc.returncode != 0:
print("FAIL: `cmux claude-teams --version` exited non-zero")
print(f"exit={proc.returncode}")
print(f"stdout={proc.stdout.strip()}")
print(f"stderr={proc.stderr.strip()}")
return 1
agent_teams_value = read_text(env_log)
if agent_teams_value != "1":
print(f"FAIL: expected CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1, got {agent_teams_value!r}")
return 1
tmux_path = read_text(tmux_log)
if not tmux_path:
print("FAIL: fake claude did not observe a tmux binary in PATH")
return 1
tmux_name = Path(tmux_path).name
if tmux_name != "tmux":
print(f"FAIL: expected tmux shim path to end with 'tmux', got {tmux_path!r}")
return 1
if "claude-teams-bin" not in tmux_path:
print(f"FAIL: expected stable tmux shim path, got {tmux_path!r}")
return 1
if tmux_path.startswith(str(real_bin)):
print(f"FAIL: expected cmux tmux shim to shadow PATH, got {tmux_path!r}")
return 1
cmux_bin_value = read_text(cmux_bin_log)
if not cmux_bin_value or cmux_bin_value == "__UNSET__":
print("FAIL: missing CMUX_CLAUDE_TEAMS_CMUX_BIN")
return 1
if not os.path.exists(cmux_bin_value):
print(f"FAIL: CMUX_CLAUDE_TEAMS_CMUX_BIN does not exist: {cmux_bin_value!r}")
return 1
argv_lines = argv_log.read_text(encoding="utf-8").splitlines()
if argv_lines[:2] != ["--teammate-mode", "auto"]:
print(f"FAIL: expected launcher to prepend --teammate-mode auto, got {argv_lines!r}")
return 1
if "--version" not in argv_lines:
print(f"FAIL: expected launcher to preserve user args, got {argv_lines!r}")
return 1
tmux_env_value = read_text(tmux_env_log)
if tmux_env_value in {"", "__UNSET__"}:
print("FAIL: expected a fake TMUX env value")
return 1
tmux_pane_value = read_text(tmux_pane_log)
if tmux_pane_value in {"", "__UNSET__"} or not tmux_pane_value.startswith("%"):
print(f"FAIL: expected a fake TMUX_PANE value, got {tmux_pane_value!r}")
return 1
term_value = read_text(term_log)
if term_value != "screen-256color":
print(f"FAIL: expected TERM=screen-256color, got {term_value!r}")
return 1
term_program_value = read_text(term_program_log)
if term_program_value != "__UNSET__":
print(f"FAIL: expected TERM_PROGRAM to be unset, got {term_program_value!r}")
return 1
socket_path_value = read_text(socket_path_log)
if socket_path_value != explicit_socket_path:
print(f"FAIL: expected CMUX_SOCKET_PATH={explicit_socket_path!r}, got {socket_path_value!r}")
return 1
socket_password_value = read_text(socket_password_log)
if socket_password_value != explicit_socket_password:
print(
"FAIL: expected CMUX_SOCKET_PASSWORD to preserve the explicit CLI override, "
f"got {socket_password_value!r}"
)
return 1
print("PASS: cmux claude-teams injects the auto-mode tmux env and shim")
return 0
if __name__ == "__main__":
raise SystemExit(main())