Files
hermes-agent/tests/hermes_cli/test_terminal_backend_docker_probe.py
kshitijk4poor e4739c6d2d fix(cli): doctor and terminal setup resolve the container runtime too
`hermes doctor` and `hermes setup terminal` only ever looked for a `docker` binary
(`_safe_which("docker")` / `shutil.which("docker")`) and probed `docker version` by
literal name, so a podman-only machine was told Docker was missing while the docker
terminal backend was already running containers through Podman — the same mis-report
the dashboard probe had.

Both now resolve the CLI through `find_docker()` (HERMES_DOCKER_BINARY → docker →
podman → macOS Docker Desktop paths) and name the runtime they actually found, via
`docker_runtime_name()` and `docker_runtime_start_hint()` next to `find_docker()` —
shared with the dashboard probe instead of a per-module copy. The probe's version call
goes through the backend's `run_capture()`, and a Podman row no longer advises starting
a daemon: Podman is daemonless, so it points at `podman machine start`.

Tests that pinned the old resolution (`_safe_which` / the global `shutil.which`) now pin
`find_docker()`, so they no longer depend on whether the host has Docker Desktop at a
known macOS path.
2026-09-18 21:47:13 +05:30

64 lines
2.2 KiB
Python

"""Dashboard docker-backend probe must follow the same runtime resolution as the agent.
Regression: Desktop showed "Docker CLI not found" on podman-only machines even
when the terminal backend was already running containers via HERMES_DOCKER_BINARY
or a PATH podman. The probe looked only for a `docker` binary and called
`docker info --format {{.ServerVersion}}` (a Docker-only field).
"""
from __future__ import annotations
import subprocess
import pytest
from hermes_cli.web_routers import tools as tools_mod
from tools.environments import docker as docker_mod
from tools.environments import remote_common
@pytest.fixture(autouse=True)
def _reset_docker_cache():
docker_mod._docker_executable = None
yield
docker_mod._docker_executable = None
def _hide_host_runtimes(monkeypatch):
"""Force find_docker() off the host PATH so the probe cannot luck into a real docker."""
monkeypatch.delenv("HERMES_DOCKER_BINARY", raising=False)
monkeypatch.setattr(docker_mod.shutil, "which", lambda name: None)
monkeypatch.setattr(docker_mod, "_DOCKER_SEARCH_PATHS", [])
monkeypatch.setattr(tools_mod.shutil, "which", lambda name: None)
def test_probe_ready_when_only_podman_is_configured(tmp_path, monkeypatch):
"""HERMES_DOCKER_BINARY=podman + working `version` is ready; never `docker info`."""
_hide_host_runtimes(monkeypatch)
fake = tmp_path / "podman"
fake.write_text("#!/bin/sh\nexit 0\n")
fake.chmod(0o755)
monkeypatch.setenv("HERMES_DOCKER_BINARY", str(fake))
captured: list[list[str]] = []
def _run(argv, **kwargs):
captured.append(list(argv))
return subprocess.CompletedProcess(argv, 0, stdout="podman version 5.0.0\n", stderr="")
monkeypatch.setattr(remote_common, "run_capture", _run)
status, detail = tools_mod._probe_docker_backend({})
assert status == "ready"
assert detail == ""
assert captured == [[str(fake), "version"]]
def test_probe_needs_setup_when_docker_and_podman_are_absent(monkeypatch):
_hide_host_runtimes(monkeypatch)
status, detail = tools_mod._probe_docker_backend({})
assert status == "needs_setup"
assert "not found" in detail.lower()
assert "podman" in detail.lower()