import json import os import shutil import subprocess import time from pathlib import Path import pytest def test_make_install_uses_isolated_uv_tool_install(): makefile = Path("Makefile").read_text(encoding="utf-8") assert "install:" in makefile assert "uv tool install --force --reinstall --refresh ." in makefile assert "tamq --version" in makefile assert "uninstall:" in makefile assert "uv tool uninstall tmux-amq" in makefile @pytest.mark.skipif( shutil.which("uv") is None or shutil.which("tmux") is None, reason="installed-package smoke requires uv and tmux", ) def test_isolated_installed_tool_session_smoke(tmp_path): project = Path(__file__).resolve().parents[1] tool_dir = tmp_path / "tools" bin_dir = tmp_path / "bin" runtime_dir = tmp_path / "runtime" state_dir = tmp_path / "state" repo_a = tmp_path / "railiance-platform" repo_b = tmp_path / "activity-core" for path in (bin_dir, runtime_dir, repo_a, repo_b): path.mkdir(parents=True) gita = bin_dir / "gita" gita.write_text( "#!/usr/bin/env python3\n" "import sys\n" f"paths = {{'railiance-platform': {str(repo_a)!r}, 'activity-core': {str(repo_b)!r}}}\n" "if sys.argv[1:] == ['ls']:\n" " print('railiance-platform activity-core')\n" "elif sys.argv[1:] == ['freeze']:\n" " for slug, path in paths.items(): print(f'local,{slug},{path}')\n" "else:\n" " raise SystemExit(2)\n", encoding="utf-8", ) agent = bin_dir / "alpha-agent" agent.write_text( "#!/bin/sh\nprintf 'installed-alpha-ready\\n'\nexec sleep 30\n", encoding="utf-8", ) gita.chmod(0o755) agent.chmod(0o755) socket_name = f"tamq-installed-{os.getpid()}" env = os.environ.copy() env.update( { "PATH": f"{bin_dir}:{env['PATH']}", "UV_TOOL_DIR": str(tool_dir), "UV_TOOL_BIN_DIR": str(bin_dir), "UV_CACHE_DIR": str(tmp_path / "uv-cache"), "XDG_RUNTIME_DIR": str(runtime_dir), "TAMQ_STATE_DIR": str(state_dir), "TAMQ_TMUX_SOCKET": socket_name, } ) tamq = bin_dir / "tamq" tmux = ["tmux", "-L", socket_name] def run(*args: str) -> subprocess.CompletedProcess[str]: return subprocess.run( args, cwd=tmp_path, env=env, text=True, capture_output=True, check=True, timeout=30, ) try: subprocess.run( ["uv", "tool", "install", "--force", "--reinstall", "--refresh", str(project)], cwd=project, env=env, text=True, capture_output=True, check=True, timeout=60, ) assert run(str(tamq), "--version").stdout.strip() == "0.1.0" root_help = run(str(tamq), "--help").stdout assert "tamq [--detach] [--command COMMAND] [--mode MODE] REPO" in root_help started = json.loads( run( str(tamq), "railiance-platform", "activity-core", "--detach", ).stdout ) assert started["repos"] == ["railiance-platform", "activity-core"] assert started["delivery_mode"] == "output" rows = run( *tmux, "list-windows", "-t", "tamq", "-F", "#{window_name}|#{pane_current_path}|#{pane_pid}|#{pane_current_command}", ).stdout.splitlines() parsed = { name: (path, pid, command) for name, path, pid, command in (row.split("|", 3) for row in rows) } assert parsed["railiance-platform"][0] == str(repo_a) assert parsed["activity-core"][0] == str(repo_b) assert all(command != "alpha-agent" for _, _, command in parsed.values()) assert all( "installed-alpha-ready" not in run(*tmux, "capture-pane", "-p", "-t", f"tamq:{repo}").stdout for repo in ("railiance-platform", "activity-core") ) for repo in ("railiance-platform", "activity-core"): run( *tmux, "send-keys", "-t", f"tamq:{repo}", "printf 'TAMQ_REPO=%s\\n' \"$TAMQ_REPO\"", "C-m", ) deadline = time.monotonic() + 5 while time.monotonic() < deadline: identity_captures = { repo: run(*tmux, "capture-pane", "-p", "-t", f"tamq:{repo}").stdout for repo in ("railiance-platform", "activity-core") } if all(f"TAMQ_REPO={repo}" in output for repo, output in identity_captures.items()): break time.sleep(0.05) assert all(f"TAMQ_REPO={repo}" in output for repo, output in identity_captures.items()) repeated = json.loads( run( str(tamq), "start", "--detach", "railiance-platform", "activity-core", ).stdout ) assert repeated["instance_id"] == started["instance_id"] repeated_rows = run( *tmux, "list-windows", "-t", "tamq", "-F", "#{window_name}|#{pane_current_path}|#{pane_pid}|#{pane_current_command}", ).stdout.splitlines() assert repeated_rows == rows pre_delivery_status = json.loads(run(str(tamq), "status").stdout) assert pre_delivery_status["service"] is True assert [item["endpoint_id"] for item in pre_delivery_status["endpoints"]] == [ started["instance_id"] ] assert pre_delivery_status["endpoints"][0]["delivery_mode"] == "output" target_before = run( *tmux, "capture-pane", "-p", "-t", "tamq:activity-core" ).stdout run( *tmux, "send-keys", "-t", "tamq:railiance-platform", "@activity-core: installed-message", "C-m", ) deadline = time.monotonic() + 5 inbox = [] target_after = target_before while time.monotonic() < deadline: inbox = [ json.loads(line) for line in run( str(tamq), "inbox", "--repo", "activity-core", "--json" ).stdout.splitlines() ] target_after = run( *tmux, "capture-pane", "-p", "-t", "tamq:activity-core" ).stdout if inbox and inbox[-1]["displayed_at"] is not None and "#railiance-platform: installed-message" in target_after: break time.sleep(0.05) assert inbox, run( *tmux, "capture-pane", "-p", "-t", "tamq:railiance-platform" ).stdout assert inbox[-1]["sender_repo"] == "railiance-platform" assert inbox[-1]["body"] == "installed-message" assert inbox[-1]["state"] == "pending" assert inbox[-1]["displayed_at"] is not None message_id = inbox[-1]["message_id"] assert run(str(tamq), "inbox", "--repo", "activity-core").stdout == ( f"#railiance-platform: installed-message [{message_id}]\n" ) assert target_after != target_before assert f"#railiance-platform: installed-message [{message_id}]" in target_after assert run(str(tamq), "ack", message_id).stdout.strip() == message_id assert run(str(tamq), "inbox", "--repo", "activity-core").stdout == "" all_messages = [ json.loads(line) for line in run( str(tamq), "inbox", "--repo", "activity-core", "--all", "--json" ).stdout.splitlines() ] assert all_messages[-1]["state"] == "acknowledged" run( *tmux, "send-keys", "-t", "tamq:railiance-platform", "@activity-core filtered-message", "C-m", ) deadline = time.monotonic() + 5 while time.monotonic() < deadline: pending = run( str(tamq), "inbox", "--repo", "activity-core", "--json" ).stdout if "filtered-message" in pending: break time.sleep(0.05) message_log = tmp_path / "msg.log" run( str(tamq), "inbox", "--repo", "activity-core", "--filter", f"cat >> {message_log}", ) assert "#railiance-platform: filtered-message [m-" in message_log.read_text( encoding="utf-8" ) assert run(str(tamq), "inbox", "--repo", "activity-core").stdout == "" status = json.loads(run(str(tamq), "status").stdout) assert status["service"] is True assert [item["endpoint_id"] for item in status["endpoints"]] == [started["instance_id"]] assert run(str(tamq), "stop").returncode == 0 assert json.loads(run(str(tamq), "status").stdout)["service"] is False restarted = json.loads( run( str(tamq), "start", "--detach", "railiance-platform", "activity-core", ).stdout ) assert restarted["instance_id"] == started["instance_id"] assert run( *tmux, "list-windows", "-t", "tamq", "-F", "#{window_name}|#{pane_current_path}|#{pane_pid}|#{pane_current_command}", ).stdout.splitlines() == rows run(*tmux, "kill-session", "-t", "tamq") deadline = time.monotonic() + 5 while time.monotonic() < deadline: status = json.loads(run(str(tamq), "status").stdout) if status["endpoints"] == []: break time.sleep(0.05) assert status["endpoints"] == [] recovered = json.loads( run( str(tamq), "start", "--detach", "--command", "alpha-agent", "railiance-platform", "activity-core", ).stdout ) assert recovered["instance_id"] != started["instance_id"] assert recovered["delivery_mode"] == "output" recovered_rows = run( *tmux, "list-windows", "-t", "tamq", "-F", "#{window_name}|#{pane_current_path}", ).stdout.splitlines() assert recovered_rows == [ f"railiance-platform|{repo_a}", f"activity-core|{repo_b}", ] deadline = time.monotonic() + 5 while time.monotonic() < deadline: captures = [ run(*tmux, "capture-pane", "-p", "-t", f"tamq:{repo}").stdout for repo in ("railiance-platform", "activity-core") ] if all("installed-alpha-ready" in output for output in captures): break time.sleep(0.05) assert all("installed-alpha-ready" in output for output in captures) assert run(str(tamq), "stop").returncode == 0 finally: subprocess.run( [str(tamq), "stop"], cwd=tmp_path, env=env, text=True, capture_output=True, check=False, timeout=5, ) subprocess.run( [*tmux, "kill-server"], env=env, text=True, capture_output=True, check=False, )