tmux-amq/tests/test_install_target.py
tegwick 68475922bb
Some checks failed
tamq-ci / test (push) Failing after 6s
fix: restore interactive PTY behavior
Assistant: codex
Assistant-Model: gpt-5.6-sol
Assistant-Session: 01a03397-4d51-7fd1-8ff2-946eb22ea2bc
2026-08-24 19:28:49 +02:00

237 lines
7.4 KiB
Python

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"
started = json.loads(
run(
str(tamq),
"start",
"--detach",
"--command",
"alpha-agent",
"railiance-platform",
"activity-core",
).stdout
)
assert started["repos"] == ["railiance-platform", "activity-core"]
rows = run(
*tmux,
"list-windows",
"-t",
"tamq",
"-F",
"#{window_name}|#{pane_current_path}|#{pane_pid}",
).stdout.splitlines()
parsed = {name: (path, pid) for name, path, pid in (row.split("|", 2) for row in rows)}
assert parsed["railiance-platform"][0] == str(repo_a)
assert parsed["activity-core"][0] == str(repo_b)
repeated = json.loads(
run(
str(tamq),
"start",
"--detach",
"--command",
"alpha-agent",
"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}",
).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"]
]
message_id = run(
str(tamq), "send", "@activity-core:", "installed-message"
).stdout.strip()
deadline = time.monotonic() + 5
history = []
while time.monotonic() < deadline:
history = [
json.loads(line)
for line in run(str(tamq), "history", "--repo", "activity-core").stdout.splitlines()
]
if history and history[-1]["state"] == "injected":
break
time.sleep(0.05)
assert history[-1]["message_id"] == message_id
assert history[-1]["state"] == "injected"
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",
"--command",
"alpha-agent",
"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}",
).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"]
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}",
]
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,
)