Files
treg/tests/test_shell.py
SToneX 52998b475a test(cli): prune help-text, argparse and duplicate MCP/CLI tests
Replace the audit busy-wait in agent capture tests with audit.drain().
2026-09-25 21:09:53 +08:00

646 lines
30 KiB
Python

"""Shell mode (`treg shell`) — the MVP / Phase 1 transparent CLI interception.
These exercise the client-side pieces without a live server or a real vendor CLI: the tool→shim
selection, the shim contract (clean PATH + verbatim args + exit-code passthrough → no recursion), the
session wiring (shim dir first on PATH, REALPATH kept clean, teardown), and the stop/guard paths.
See docs/CLI-SHELL-MODE-PLAN.md §7.
"""
from __future__ import annotations
import argparse
import os
import signal
import stat
import subprocess
import pytest
from treg import cli, shell
# ---- selection + routing ------------------------------------------------------------------
def test_plan_shims_filters_and_sorts():
tools = [
{"name": "stripe-tool", "cli": {"bin": "stripe", "enabled": True}},
{"name": "gh", "cli": {"bin": "gh", "enabled": True}},
{"name": "off", "cli": {"bin": "flyctl", "enabled": False}}, # local runs not enabled → skip
{"name": "nobin", "cli": {"enabled": True}}, # no bin → skip
{"name": "nocli"}, # no cli profile → skip
{"name": "escape", "cli": {"bin": "../evil", "enabled": True}}, # not a plain filename → skip
{"name": "dup", "cli": {"bin": "gh", "enabled": True}}, # same bin as gh → first wins
]
entries, warnings = shell.plan_shims(tools)
assert entries == [("gh", "gh", "local"), ("stripe", "stripe-tool", "local")]
assert warnings == []
def test_plan_shims_routes_server_for_runnable_tools():
tools = [
{"name": "stripe-tool", "cli": {"bin": "stripe", "enabled": True}, "server_runnable": True},
{"name": "gh", "cli": {"bin": "gh", "enabled": True}, "server_runnable": False},
]
# by bin (stripe) → server; gh requested but NOT server-runnable → falls back to local + a warning
entries, warnings = shell.plan_shims(tools, frozenset({"stripe", "gh"}))
assert entries == [("gh", "gh", "local"), ("stripe", "stripe-tool", "server")]
assert any("gh" in w and "server" in w for w in warnings)
def test_plan_shims_matches_server_for_by_tool_name():
tools = [{"name": "stripe-tool", "cli": {"bin": "stripe", "enabled": True}, "server_runnable": True}]
entries, _ = shell.plan_shims(tools, frozenset({"stripe-tool"})) # by tool name, not bin
assert entries == [("stripe", "stripe-tool", "server")]
# ---- the shim contract --------------------------------------------------------------------
def test_shim_execs_treg_run_with_clean_path_and_verbatim_args(tmp_path):
"""The heart of it: the shim must call `treg run <tool> -- <args>` verbatim, on the clean PATH,
and hand back the CLI's exit code. Uses a fake `treg` that records its argv + PATH, then exits 7."""
argv_out, path_out = tmp_path / "argv.out", tmp_path / "path.out"
fake_treg = tmp_path / "faketreg"
fake_treg.write_text(
"#!/bin/sh\n"
f'printf "%s" "$PATH" > {path_out}\n'
f': > {argv_out}\n'
f'for a in "$@"; do printf "%s\\n" "$a" >> {argv_out}; done\n'
"exit 7\n"
)
fake_treg.chmod(0o755)
shim_dir = tmp_path / "bin"
shim_dir.mkdir()
shell.write_shims(str(shim_dir), [("stripe", "stripe-tool", "local")], str(fake_treg))
assert stat.S_IMODE((shim_dir / "stripe").stat().st_mode) == 0o755
env = dict(os.environ, TREG_SHELL_REALPATH="/clean/only", PATH="/usr/bin:/bin")
r = subprocess.run([str(shim_dir / "stripe"), "balance", "--live"], env=env, capture_output=True)
assert r.returncode == 7 # the real CLI's exit code passes through
assert path_out.read_text() == "/clean/only" # ran on the clean PATH → no shim recursion
assert argv_out.read_text().splitlines() == ["run", "stripe-tool", "--", "balance", "--live"]
def test_server_route_shim_execs_treg_run_server(tmp_path):
"""A server-routed shim must call `treg run --server <tool>` through a real shell."""
argv_out = tmp_path / "argv.out"
fake_treg = tmp_path / "faketreg"
fake_treg.write_text(
"#!/bin/sh\n"
f': > {argv_out}\n'
f'for a in "$@"; do printf "%s\\n" "$a" >> {argv_out}; done\n'
)
fake_treg.chmod(0o755)
shim_dir = tmp_path / "bin"
shim_dir.mkdir()
shell.write_shims(str(shim_dir), [("stripe", "stripe-tool", "server")], str(fake_treg))
env = dict(os.environ, TREG_SHELL_REALPATH="/usr/bin:/bin")
r = subprocess.run([str(shim_dir / "stripe"), "balance"], env=env, capture_output=True)
assert r.returncode == 0
assert argv_out.read_text().splitlines() == ["run", "--server", "stripe-tool", "--", "balance"]
def test_completion_call_bypasses_treg_real_bin_runs(tmp_path):
"""Through a real shell: `gh __complete …` execs the real bin directly (no treg → no audit/cap),
while `gh <normal>` still routes through treg run."""
real_log, treg_log = tmp_path / "real.out", tmp_path / "treg.out"
real_bin = tmp_path / "realgh"
real_bin.write_text(f'#!/bin/sh\nprintf "REAL %s\\n" "$*" >> {real_log}\n')
real_bin.chmod(0o755)
fake_treg = tmp_path / "faketreg"
fake_treg.write_text(f'#!/bin/sh\nprintf "TREG %s\\n" "$*" >> {treg_log}\n')
fake_treg.chmod(0o755)
shim = tmp_path / "bin" / "gh"
shim.parent.mkdir()
shim.write_text(shell.shim_script("gh", str(fake_treg), real_bin=str(real_bin)))
shim.chmod(0o755)
env = dict(os.environ, TREG_SHELL_REALPATH="/usr/bin:/bin")
subprocess.run([str(shim), "__complete", "sta"], env=env) # completion → real bin
subprocess.run([str(shim), "repo", "list"], env=env) # normal → treg run
assert real_log.read_text() == "REAL __complete sta\n" # completion never hit treg
assert treg_log.read_text() == "TREG run gh -- repo list\n" # normal did
def test_run_subshell_ttl_closes_the_session():
"""The TTL hard cap fires: a subshell that would sleep 30s is terminated by the 1s timer."""
import time
t0 = time.monotonic()
rc = shell._run_subshell(["/bin/sh", "-c", "sleep 30"], dict(os.environ), ttl_seconds=1)
assert time.monotonic() - t0 < 10 # the timer closed it; we did not wait the full 30s
assert rc != 0 # terminated, not a clean exit
def test_registered_bin_resolves_shim_in_a_real_shell(tmp_path):
"""Through a real /bin/sh: a registered CLI name resolves to our shim (first on PATH) while an
unregistered command is untouched — the 'is this registered?' test done for free by name resolution."""
argv_out = tmp_path / "argv.out"
fake_treg = tmp_path / "faketreg"
fake_treg.write_text(
"#!/bin/sh\n"
f': > {argv_out}\n'
f'for a in "$@"; do printf "%s\\n" "$a" >> {argv_out}; done\n'
)
fake_treg.chmod(0o755)
shim_dir = tmp_path / "bin"
shim_dir.mkdir()
shell.write_shims(str(shim_dir), [("stripe", "stripe-tool", "local")], str(fake_treg))
realpath = "/usr/bin:/bin"
env = dict(os.environ, PATH=f"{shim_dir}{os.pathsep}{realpath}", TREG_SHELL_REALPATH=realpath)
# `stripe` → our shim; `true` (not registered, no shim) resolves to the system binary normally
r = subprocess.run(["/bin/sh", "-c", "stripe deploy --prod && true"], env=env, capture_output=True)
assert r.returncode == 0
assert argv_out.read_text().splitlines() == ["run", "stripe-tool", "--", "deploy", "--prod"]
# ---- session base dir ---------------------------------------------------------------------
def test_session_base_dir_prefers_private_per_user(tmp_path, monkeypatch):
monkeypatch.setenv("XDG_RUNTIME_DIR", str(tmp_path))
assert shell.session_base_dir() == str(tmp_path)
monkeypatch.delenv("XDG_RUNTIME_DIR")
monkeypatch.setenv("TMPDIR", str(tmp_path))
assert shell.session_base_dir() == str(tmp_path)
# ---- start_session wiring (no real subshell) ----------------------------------------------
def test_start_session_wires_path_and_tears_down(tmp_path, monkeypatch):
captured: dict = {}
def fake_run(argv, env, ttl_seconds=None):
captured["argv"] = argv
captured["env"] = dict(env)
captured["ttl_seconds"] = ttl_seconds
# the session dir + the shim must exist WHILE the shell runs
captured["dir_exists_during"] = os.path.isdir(env[shell.ENV_DIR])
captured["shim_exists_during"] = os.path.exists(os.path.join(env[shell.ENV_DIR], "bin", "stripe"))
return 3
monkeypatch.setattr(shell, "_run_subshell", fake_run)
monkeypatch.setenv("PATH", "/usr/bin:/bin")
monkeypatch.setenv("TMPDIR", str(tmp_path))
monkeypatch.delenv("XDG_RUNTIME_DIR", raising=False)
rc = shell.start_session([("stripe", "stripe", "local")], "/usr/local/bin/treg", ttl_minutes=30)
assert rc == 3 # the subshell's exit code is returned
env = captured["env"]
shim_dir = os.path.join(env[shell.ENV_DIR], "bin")
assert env["PATH"].startswith(shim_dir + os.pathsep) # our shims resolve first
assert env[shell.ENV_REALPATH] == "/usr/bin:/bin" # REALPATH is clean (no shim dir)
assert shim_dir not in env[shell.ENV_REALPATH]
assert env[shell.ENV_ACTIVE] == "1"
assert env[shell.ENV_PID] == str(os.getpid())
assert captured["ttl_seconds"] == 30 * 60 # --ttl minutes → seconds for the hard cap
assert captured["dir_exists_during"]
assert captured["shim_exists_during"] # a shim was written before the shell launched
# teardown ran on exit
assert not os.path.exists(env[shell.ENV_DIR])
# ---- stop / guards ------------------------------------------------------------------------
def test_stop_outside_a_session_exits(monkeypatch):
monkeypatch.delenv("TREG_SHELL", raising=False)
with pytest.raises(SystemExit):
shell.stop_session()
def test_stop_signals_the_controller(monkeypatch):
sent: dict = {}
monkeypatch.setenv("TREG_SHELL", "1")
monkeypatch.setenv("TREG_SHELL_PID", "4242")
monkeypatch.setattr(shell.os, "kill", lambda pid, sig: sent.update(pid=pid, sig=sig))
shell.stop_session()
assert sent == {"pid": 4242, "sig": signal.SIGTERM}
# ---- cmd_shell_start guards ---------------------------------------------------------------
def test_cmd_shell_start_refuses_when_nested(monkeypatch):
monkeypatch.setenv("TREG_SHELL", "1")
with pytest.raises(SystemExit):
cli.cmd_shell_start(object(), {"token": "t", "base_url": "http://x"})
def test_cmd_shell_start_needs_login(monkeypatch):
monkeypatch.delenv("TREG_SHELL", raising=False)
with pytest.raises(SystemExit):
cli.cmd_shell_start(object(), {"base_url": "http://x"})
def test_cmd_shell_start_no_runnable_clis_exits(monkeypatch):
monkeypatch.delenv("TREG_SHELL", raising=False)
class _Resp:
status_code = 200
def json(self): return [{"name": "api-only", "cli": None}]
class _C:
def __enter__(self): return self
def __exit__(self, *a): return False
def get(self, path): return _Resp()
monkeypatch.setattr(cli, "_client", lambda cfg: _C())
args = cli.build_parser().parse_args(["shell", "start"]) # server_for=None, ttl=None
with pytest.raises(SystemExit):
cli.cmd_shell_start(args, {"token": "t", "base_url": "http://x"})
# ---- --proxy wiring (P4 of docs/LOCAL-PROXY-PLAN.md) ---------------------------------------
def test_start_session_publishes_the_proxy_env_and_stops_it(tmp_path, monkeypatch):
"""The seam `--proxy` uses: extra variables go into the subshell, and the proxy is stopped on the
way out — a listener left running after the shell closed would keep answering for a session that
no longer exists."""
captured: dict = {}
stopped: list = []
def fake_run(argv, env, ttl_seconds=None):
captured["env"] = dict(env)
return 0
monkeypatch.setattr(shell, "_run_subshell", fake_run)
monkeypatch.setenv("PATH", "/usr/bin:/bin")
monkeypatch.setenv("TMPDIR", str(tmp_path))
monkeypatch.delenv("XDG_RUNTIME_DIR", raising=False)
shell.start_session(
[("stripe", "stripe", "local")], "/usr/local/bin/treg",
extra_env={"HTTPS_PROXY": "http://treg:tok@127.0.0.1:18791", "SSL_CERT_FILE": "/x/ca-bundle.pem"},
on_close=lambda: stopped.append(True),
captured_hosts=["api.stripe.com"],
)
assert captured["env"]["HTTPS_PROXY"] == "http://treg:tok@127.0.0.1:18791"
assert captured["env"]["SSL_CERT_FILE"] == "/x/ca-bundle.pem"
assert stopped == [True]
def test_extra_env_cannot_break_the_shims(tmp_path, monkeypatch):
"""Applied AFTER our own variables: an add-on that set PATH or a TREG_SHELL* marker would stop
name resolution finding the shims, or make the session look like a nested one."""
captured: dict = {}
monkeypatch.setattr(shell, "_run_subshell",
lambda argv, env, ttl_seconds=None: captured.update(env=dict(env)) or 0)
monkeypatch.setenv("PATH", "/usr/bin:/bin")
monkeypatch.setenv("TMPDIR", str(tmp_path))
monkeypatch.delenv("XDG_RUNTIME_DIR", raising=False)
shell.start_session([("stripe", "stripe", "local")], "/usr/local/bin/treg",
extra_env={"PATH": "/evil", shell.ENV_REALPATH: "/evil", shell.ENV_PID: "1"})
env = captured["env"]
assert env["PATH"].startswith(os.path.join(env[shell.ENV_DIR], "bin"))
assert env[shell.ENV_REALPATH] == "/usr/bin:/bin"
assert env[shell.ENV_PID] == str(os.getpid())
def test_the_banner_names_the_captured_hosts(capsys):
"""A member must never discover interception by accident: the hosts are listed, and everything
else is named as untouched."""
shell._print_banner([("stripe", "stripe", "local")], None, ["api.stripe.com", "api.intercom.io"])
err = capsys.readouterr().err
assert "Also captured (2 hosts)" in err
assert {"api.stripe.com", "api.intercom.io"} <= set(err.split()) # whole hosts, not substrings
assert "Every other address goes straight out" in err
def test_cmd_shell_start_seeds_the_allow_list_from_the_tool_listing(monkeypatch, tmp_path):
"""`--proxy` reuses the tools already fetched for the shims — every registered host, including
ones with no CLI, and no second request to the registry."""
from treg import localproxy as lpx
tools = [
{"name": "stripe", "host": "api.stripe.com", "cli": {"bin": "stripe", "enabled": True}},
{"name": "intercom", "host": "API.INTERCOM.IO"}, # no CLI: still captured by the proxy
{"name": "odd", "cli": {"bin": "odd", "enabled": True}}, # no host: nothing to capture
]
seen: dict = {}
class _Handle:
def env(self, treg_host):
seen["treg_host"] = treg_host
return {"HTTPS_PROXY": "http://x"}
def stop(self):
seen["stopped"] = True
monkeypatch.setattr(lpx, "ensure_ca", lambda renew=False: seen.setdefault("ca", object()))
monkeypatch.setattr(lpx, "start", lambda cfg: seen.setdefault("cfg", cfg) and None or _Handle())
args = argparse.Namespace(proxy=True, proxy_port=None, renew_ca=False)
env, stop, hosts = cli._start_local_proxy(args, {"base_url": "https://treg.example", "token": "tok",
"active_org": "acme"}, tools)
assert hosts == ["api.intercom.io", "api.stripe.com"] # lowercased, sorted, host-less tool skipped
assert seen["cfg"].hosts == frozenset({"api.stripe.com", "api.intercom.io"})
assert seen["cfg"].treg_token == "tok" and seen["cfg"].org == "acme"
assert seen["cfg"].base_url == "https://treg.example"
assert seen["treg_host"] == "treg.example" # the registry never comes back through us
assert env["HTTPS_PROXY"] == "http://x"
stop()
assert seen["stopped"]
def test_cmd_shell_start_explains_a_missing_certificate_library(monkeypatch):
"""The light CLI has no `cryptography`. Saying exactly what to install beats a traceback."""
from treg import localproxy as lpx
def _boom(renew=False):
raise lpx.ProxyDependencyError('install it with: pip install "tools-registry[proxy]"')
monkeypatch.setattr(lpx, "ensure_ca", _boom)
args = argparse.Namespace(proxy=True, proxy_port=None, renew_ca=False)
with pytest.raises(SystemExit) as exc:
cli._start_local_proxy(args, {"base_url": "https://x", "token": "t"}, [])
assert "tools-registry[proxy]" in str(exc.value)
def test_a_busy_port_is_a_readable_error(monkeypatch):
from treg import localproxy as lpx
monkeypatch.setattr(lpx, "ensure_ca", lambda renew=False: object())
monkeypatch.setattr(lpx, "start", lambda cfg: (_ for _ in ()).throw(OSError("address in use")))
args = argparse.Namespace(proxy=True, proxy_port=18791, renew_ca=False)
with pytest.raises(SystemExit) as exc:
cli._start_local_proxy(args, {"base_url": "https://x", "token": "t"}, [])
assert "--proxy-port" in str(exc.value) and "18791" in str(exc.value)
# ---- treg serve (the daemon front door) -----------------------------------------------------
def test_serve_env_prints_shell_lines_and_the_way_back(tmp_path):
"""A daemon nobody can reach is useless, and typing ten exports by hand is worse. `--unset` is the
way out, because `treg serve stop` cannot reach into a shell that already has the variables."""
env = {"HTTPS_PROXY": "http://treg:a b@127.0.0.1:18791", "SSL_CERT_FILE": "/x/ca.pem"}
lines = cli._serve_export_lines(env)
assert "export SSL_CERT_FILE=/x/ca.pem" in lines
assert "export HTTPS_PROXY='http://treg:a b@127.0.0.1:18791'" in lines # quoted: the token is opaque
assert cli._serve_export_lines(env, unset=True) == "unset HTTPS_PROXY\nunset SSL_CERT_FILE"
def test_serve_start_refuses_a_second_one(monkeypatch, capsys):
from treg import localproxy as lpx
monkeypatch.setattr(lpx, "running", lambda: {"port": 18791, "pid": 4242})
with pytest.raises(SystemExit) as exc:
cli.cmd_serve_start(argparse.Namespace(port=None, renew_ca=False, foreground=False),
{"token": "t", "base_url": "http://x"})
assert "already running" in str(exc.value) and "treg serve stop" in str(exc.value)
def test_serve_start_needs_login(monkeypatch):
with pytest.raises(SystemExit) as exc:
cli.cmd_serve_start(argparse.Namespace(port=None, renew_ca=False, foreground=False),
{"base_url": "http://x"})
assert "treg login" in str(exc.value)
def test_serve_status_says_what_is_captured(monkeypatch, capsys):
from treg import localproxy as lpx
monkeypatch.setattr(lpx, "running", lambda: {
"port": 18791, "pid": 4242, "base_url": "https://treg.example", "org": "acme",
"hosts": ["api.stripe.com", "api.vercel.com"]})
cli.cmd_serve_status(argparse.Namespace(), {})
out = capsys.readouterr().out
assert "127.0.0.1:18791" in out and "acme" in out
assert {"api.stripe.com", "api.vercel.com"} <= set(out.split())
assert "Every other address goes straight out" in out
def test_serve_status_and_env_when_nothing_runs(monkeypatch, capsys):
from treg import localproxy as lpx
monkeypatch.setattr(lpx, "running", lambda: None)
cli.cmd_serve_status(argparse.Namespace(), {})
assert "not running" in capsys.readouterr().out
with pytest.raises(SystemExit) as exc:
cli.cmd_serve_env(argparse.Namespace(unset=False), {})
assert "treg serve start" in str(exc.value)
def test_unset_works_after_the_proxy_is_gone(monkeypatch, capsys):
"""--unset must NOT need a running proxy: it is what you reach for right after `stop`. Requiring
one leaves the shell wedged — its variables point at a dead port, every call fails, and the one
command that fixes it refuses. Found in live testing."""
from treg import localproxy as lpx
monkeypatch.setattr(lpx, "running", lambda: None)
cli.cmd_serve_env(argparse.Namespace(unset=True), {})
out = capsys.readouterr().out
assert "unset HTTPS_PROXY" in out and "unset SSL_CERT_FILE" in out
assert "unset NODE_USE_ENV_PROXY" in out and "export" not in out
def test_serve_stop_reports_when_nothing_runs(monkeypatch):
from treg import localproxy as lpx
monkeypatch.setattr(lpx, "running", lambda: None)
with pytest.raises(SystemExit) as exc:
cli.cmd_serve_stop(argparse.Namespace(), {})
assert "no proxy is running" in str(exc.value)
# ---- `treg <command>` — per-launch opt-in, nothing global -----------------------------------
def test_bare_word_runs_a_program_but_a_typo_stays_a_treg_error(monkeypatch):
"""`treg claude` should launch claude. `treg toool ls` must stay an ordinary treg error, not an
attempt to execute `toool` — which is why the word has to exist on PATH before we fall through."""
seen: dict = {}
monkeypatch.setattr(cli, "cmd_with", lambda args, cfg: seen.update(cmd=args.command, args=args.args))
monkeypatch.setattr(cli, "_load_config", lambda: {"token": "t", "base_url": "http://x"})
cli.main(["sh", "-c", "true"]) # sh is on every machine
assert seen == {"cmd": "sh", "args": ["-c", "true"]}
with pytest.raises(SystemExit) as exc:
cli.main(["toool", "ls"])
assert exc.value.code == 2 # argparse's "invalid choice", not an exec
def test_a_real_treg_command_is_never_shadowed(monkeypatch, tmp_path):
"""If someone has a program called `call` or `org` on their PATH, treg's own command still wins."""
fake = tmp_path / "call"
fake.write_text("#!/bin/sh\n")
fake.chmod(0o755)
monkeypatch.setenv("PATH", f"{tmp_path}:{os.environ['PATH']}")
seen: dict = {}
monkeypatch.setattr(cli, "cmd_call", lambda args, cfg: seen.update(ran="treg-call"))
monkeypatch.setattr(cli, "cmd_with", lambda args, cfg: seen.update(ran="with"))
monkeypatch.setattr(cli, "_load_config", lambda: {"token": "t", "base_url": "http://x"})
cli.main(["call", "https://api.example.com/x"])
assert seen == {"ran": "treg-call"}
def test_with_refuses_a_program_that_does_not_exist():
with pytest.raises(SystemExit) as exc:
cli.cmd_with(argparse.Namespace(command="definitely-not-a-real-binary", args=[], quiet=True),
{"token": "t", "base_url": "http://x"})
assert "not a treg command" in str(exc.value)
def test_with_needs_login():
with pytest.raises(SystemExit) as exc:
cli.cmd_with(argparse.Namespace(command="sh", args=["-c", "true"], quiet=True), {})
assert "treg login" in str(exc.value)
def test_with_starts_its_own_proxy_and_stops_it(monkeypatch):
"""Nothing is left running: the proxy belongs to that one command. Port 0 means the operating
system picks, so two `treg claude` sessions can never collide."""
from treg import localproxy as lpx
events = []
class _Handle:
port = 41234
def env(self, treg_host):
return {"HTTPS_PROXY": "http://treg:tok@127.0.0.1:41234"}
def stop(self):
events.append("stopped")
monkeypatch.setattr(lpx, "running", lambda: None)
monkeypatch.setattr(cli, "_proxy_tools", lambda cfg: [{"host": "api.stripe.com"}])
monkeypatch.setattr(cli, "_start_proxy_handle",
lambda cfg, tools, port=None, renew_ca=False:
(events.append(f"started port={port}"), (_Handle(), ["api.stripe.com"], "x"))[1])
monkeypatch.setattr("treg.shell._run_subshell", lambda argv, env: events.append(("ran", argv[0], env["HTTPS_PROXY"])) or 0)
with pytest.raises(SystemExit) as exc:
cli.cmd_with(argparse.Namespace(command="sh", args=["-c", "true"], quiet=True),
{"token": "t", "base_url": "http://x"})
assert exc.value.code == 0
assert events[0] == "started port=0" # an OS-chosen port, never a fixed one
assert events[1][0] == "ran" and events[1][2].endswith("41234")
assert events[-1] == "stopped" # and it does not outlive the command
def test_with_attaches_to_a_running_daemon_and_leaves_it_up(monkeypatch):
"""A `treg serve` daemon is someone's deliberate choice — borrowing it must not shut it down."""
from treg import localproxy as lpx
events = []
monkeypatch.setattr(lpx, "running", lambda: {
"port": 18800, "pid": 1, "token": "daemon-tok", "base_url": "https://treg.example",
"org": "", "hosts": ["api.stripe.com"]})
monkeypatch.setattr(cli, "_start_proxy_handle",
lambda *a, **k: pytest.fail("must not start a second proxy"))
monkeypatch.setattr("treg.shell._run_subshell",
lambda argv, env: events.append(env["HTTPS_PROXY"]) or 7)
with pytest.raises(SystemExit) as exc:
cli.cmd_with(argparse.Namespace(command="sh", args=[], quiet=True),
{"token": "t", "base_url": "https://treg.example"})
assert exc.value.code == 7 # the command's own exit code
assert events == ["http://treg:daemon-tok@127.0.0.1:18800"]
def test_with_runs_the_program_for_real_and_passes_args_and_exit_code(monkeypatch, tmp_path):
"""End to end through a real process: argv verbatim, the proxy variables present, exit code kept."""
from treg import localproxy as lpx
out = tmp_path / "argv.txt"
monkeypatch.setattr(lpx, "running", lambda: {
"port": 18800, "pid": 1, "token": "tok", "base_url": "https://treg.example",
"org": "", "hosts": []})
script = f'printf "%s\\n" "$@" > {out}; printf "%s" "$HTTPS_PROXY" >> {out}; exit 9'
with pytest.raises(SystemExit) as exc:
cli.cmd_with(argparse.Namespace(command="sh", args=["-c", script, "_", "a b", "--flag"],
quiet=True), {"token": "t", "base_url": "https://treg.example"})
assert exc.value.code == 9
body = out.read_text()
assert "a b\n--flag\n" in body # spaces survive; no re-splitting
assert "127.0.0.1:18800" in body # the child really saw the proxy
# ---- the certificate library: offered, not just described ----------------------------------
def test_the_install_hint_matches_how_treg_was_installed(monkeypatch):
"""`pip install "tools-registry[proxy]"` is right for exactly ONE of the four ways people install
treg — and the installer's own way is not it. A uv-tool or Homebrew venv is not on the ambient
pip's path, so that advice silently does nothing."""
monkeypatch.setattr(cli.importlib.util, "find_spec", lambda n: object() if n == "pip" else None)
for prefix, label in {"/opt/homebrew/Cellar/treg/0.6.0/libexec": "Homebrew",
"/Users/x/venv": "pip"}.items():
monkeypatch.setattr(cli.sys, "prefix", prefix)
got, argv = cli._proxy_install_hint()
assert got == label and argv[:3] == [cli.sys.executable, "-m", "pip"]
assert argv[-1] == "cryptography>=43"
def test_a_pip_less_environment_uses_uv_instead(monkeypatch):
"""Found by running it: a `uv venv` has NO pip, so the obvious `python -m pip install` fails with
'No module named pip'. Probing beats guessing from the path."""
monkeypatch.setattr(cli.importlib.util, "find_spec", lambda n: None)
monkeypatch.setattr(cli.shutil, "which", lambda n: "/usr/bin/uv" if n == "uv" else None)
monkeypatch.setattr(cli.sys, "prefix", "/Users/x/.local/share/uv/tools/tools-registry")
label, argv = cli._proxy_install_hint()
assert label == "uv tool"
assert argv[:2] == ["uv", "pip"] and cli.sys.executable in argv
def test_pipx_is_injected_not_pip_installed(monkeypatch):
monkeypatch.setattr(cli.sys, "prefix", "/Users/x/.local/pipx/venvs/tools-registry")
monkeypatch.setattr(cli.shutil, "which", lambda n: f"/usr/bin/{n}" if n == "pipx" else None)
label, argv = cli._proxy_install_hint()
assert label == "pipx" and argv[:2] == ["pipx", "inject"]
def test_an_environment_with_no_installer_says_so(monkeypatch):
"""Nothing to install with: say that plainly instead of running a command that cannot exist."""
monkeypatch.setattr(cli.importlib.util, "find_spec", lambda n: None)
monkeypatch.setattr(cli.shutil, "which", lambda n: None)
monkeypatch.setattr(cli.sys, "prefix", "/opt/weird")
assert cli._proxy_install_hint()[1] == []
_hide_cryptography(monkeypatch)
with pytest.raises(SystemExit) as exc:
cli.ensure_proxy_dependency(assume_yes=True)
assert "no installer" in str(exc.value)
def test_nothing_happens_when_the_library_is_already_there():
cli.ensure_proxy_dependency() # cryptography is installed in the test env — must be silent
def _hide_cryptography(monkeypatch):
"""Pretend the certificate library is not installed, without uninstalling it."""
import builtins
real = builtins.__import__
def _fake(name, *a, **k):
if name == "cryptography":
raise ModuleNotFoundError(name)
return real(name, *a, **k)
monkeypatch.setattr(builtins, "__import__", _fake)
@pytest.fixture
def proxy_installer(monkeypatch):
# Prompt/error tests need an installer, independently of the machine's PATH and Python setup.
# Installer discovery itself is covered by the tests above.
monkeypatch.setattr(cli, "_proxy_install_hint", lambda: (
"pip", [cli.sys.executable, "-m", "pip", "install", "cryptography>=43"],
))
def test_a_non_interactive_run_prints_the_command_instead_of_prompting(monkeypatch, proxy_installer):
"""In CI or a pipe there is nobody to answer, so it must exit saying exactly what to run — never
hang on a prompt nobody can see."""
_hide_cryptography(monkeypatch)
monkeypatch.setattr(cli.sys.stdin, "isatty", lambda: False)
with pytest.raises(SystemExit) as exc:
cli.ensure_proxy_dependency()
assert "cryptography>=43" in str(exc.value)
def test_declining_leaves_the_command_behind(monkeypatch, proxy_installer):
_hide_cryptography(monkeypatch)
monkeypatch.setattr(cli.sys.stdin, "isatty", lambda: True)
monkeypatch.setattr("builtins.input", lambda _p: "n")
monkeypatch.setattr(cli.subprocess, "call", lambda *a, **k: pytest.fail("must not install"))
with pytest.raises(SystemExit) as exc:
cli.ensure_proxy_dependency()
assert "cryptography>=43" in str(exc.value)
def test_a_failed_install_says_so_rather_than_carrying_on(monkeypatch, proxy_installer):
_hide_cryptography(monkeypatch)
monkeypatch.setattr(cli.subprocess, "call", lambda *a, **k: 1)
with pytest.raises(SystemExit) as exc:
cli.ensure_proxy_dependency(assume_yes=True)
assert "did not work" in str(exc.value)