from __future__ import annotations from pathlib import Path from headroom.install.models import DeploymentManifest from headroom.install.runtime import ( _clear_pid, _read_pid, build_runtime_command, resolve_headroom_command, runtime_status, stop_runtime, ) def test_build_runtime_command_for_docker_includes_deployment_env( monkeypatch, tmp_path: Path ) -> None: monkeypatch.setattr(Path, "home", lambda: tmp_path) manifest = DeploymentManifest( profile="default", preset="persistent-docker", runtime_kind="docker", supervisor_kind="none", scope="user", provider_mode="manual", targets=["claude"], port=8787, host="127.0.0.1", backend="anthropic", image="ghcr.io/chopratejas/headroom:latest", base_env={"HEADROOM_PORT": "8787"}, proxy_args=["--host", "127.0.0.1", "--port", "8787"], ) command = build_runtime_command(manifest) joined = " ".join(command) assert command[:3] == ["docker", "run", "--rm"] assert "HEADROOM_DEPLOYMENT_PROFILE=default" in joined assert "HEADROOM_DEPLOYMENT_PRESET=persistent-docker" in joined assert "127.0.0.1:8787:8787" in joined assert "ghcr.io/chopratejas/headroom:latest" in command # Canonical Headroom filesystem contract (issue #175) forwarded into # the container. assert "HEADROOM_WORKSPACE_DIR=/tmp/headroom-home/.headroom" in command assert "HEADROOM_CONFIG_DIR=/tmp/headroom-home/.headroom/config" in command def test_build_runtime_command_for_docker_matches_wrapper_parity( monkeypatch, tmp_path: Path ) -> None: monkeypatch.setattr(Path, "home", lambda: tmp_path) monkeypatch.setenv("ANTHROPIC_API_KEY", "test-key") monkeypatch.setenv("OPENAI_API_KEY", "test-openai") manifest = DeploymentManifest( profile="default", preset="persistent-docker", runtime_kind="docker", supervisor_kind="none", scope="user", provider_mode="manual", targets=["claude"], port=8787, host="127.0.0.1", backend="anthropic", image="ghcr.io/chopratejas/headroom:latest", base_env={"HEADROOM_PORT": "8787"}, proxy_args=["--host", "127.0.0.1", "--port", "8787"], ) command = build_runtime_command(manifest) assert (tmp_path / ".headroom").is_dir() assert (tmp_path / ".claude").is_dir() assert (tmp_path / ".codex").is_dir() assert (tmp_path / ".gemini").is_dir() assert "--env" in command joined = " ".join(command) assert "ANTHROPIC_API_KEY" in joined assert "OPENAI_API_KEY" in joined def test_resolve_headroom_command_prefers_headroom_binary(monkeypatch) -> None: monkeypatch.setattr( "shutil.which", lambda name: "/usr/bin/headroom" if name == "headroom" else None ) assert resolve_headroom_command() == ["/usr/bin/headroom"] def test_read_pid_handles_invalid_content(monkeypatch, tmp_path: Path) -> None: monkeypatch.setattr(Path, "home", lambda: tmp_path) pid_file = tmp_path / ".headroom" / "deploy" / "default" / "runner.pid" pid_file.parent.mkdir(parents=True) pid_file.write_text("not-a-pid", encoding="utf-8") assert _read_pid("default") is None _clear_pid("default") assert not pid_file.exists() def test_stop_runtime_for_docker_stops_and_removes_container(monkeypatch) -> None: calls: list[list[str]] = [] manifest = DeploymentManifest( profile="default", preset="persistent-docker", runtime_kind="docker", supervisor_kind="none", scope="user", provider_mode="manual", targets=[], port=8787, host="127.0.0.1", backend="anthropic", container_name="headroom-default", ) monkeypatch.setattr( "headroom.install.runtime.subprocess.run", lambda command, **kwargs: calls.append(command), ) stop_runtime(manifest) assert calls == [ ["docker", "stop", "headroom-default"], ["docker", "rm", "-f", "headroom-default"], ] def test_runtime_status_reads_container_and_pid_state(monkeypatch, tmp_path: Path) -> None: docker_manifest = DeploymentManifest( profile="default", preset="persistent-docker", runtime_kind="docker", supervisor_kind="none", scope="user", provider_mode="manual", targets=[], port=8787, host="127.0.0.1", backend="anthropic", container_name="headroom-default", ) class Result: def __init__(self, stdout: str = "") -> None: self.stdout = stdout monkeypatch.setattr( "headroom.install.runtime.subprocess.run", lambda command, **kwargs: Result(stdout="headroom-default\n"), ) assert runtime_status(docker_manifest) == "running" monkeypatch.setattr(Path, "home", lambda: tmp_path) pid_file = tmp_path / ".headroom" / "deploy" / "default" / "runner.pid" pid_file.parent.mkdir(parents=True) pid_file.write_text("123", encoding="utf-8") monkeypatch.setattr("headroom.install.runtime.os.kill", lambda pid, sig: None) python_manifest = DeploymentManifest( profile="default", preset="persistent-service", runtime_kind="python", supervisor_kind="service", scope="user", provider_mode="manual", targets=[], port=8787, host="127.0.0.1", backend="anthropic", ) assert runtime_status(python_manifest) == "running"