atlas-iac/testing/tests/test_hermes_execution_pool_project.py
Hermes Agent 7a55b259bf hermes: add the fenced three-node distributed execution pool
Three fenced worker Pods claim Hermes Kanban runs through a coordinator that
owns every state transition, with per-ordinal HMAC authority, a mediated
broker-only SCM path, and durable per-ordinal workspaces.

Content is the reviewed head of PR #18 (689bcb6e) with PR 16's and PR 19's
contributions removed: they were merged in only to validate co-existence and are
not prerequisites, so this branch no longer carries them as ancestors. Only PR 14
and PR 15 remain, because the broker boundary and the cli_lane_* decomposition
are load-bearing for two of the fixed P0 boundaries.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-17 16:31:15 +00:00

212 lines
7.5 KiB
Python

"""Canonical board-registry and Git-ref assignment contracts."""
from __future__ import annotations
import json
import shutil
import subprocess
import sys
from pathlib import Path
from types import SimpleNamespace
import pytest
SCRIPTS = Path(__file__).parents[2] / "services/hermes/scripts"
sys.path.insert(0, str(SCRIPTS))
import execution_pool_project as project # noqa: E402
def git(*arguments: str, cwd: Path | None = None) -> str:
return subprocess.check_output(
["git", *arguments], cwd=cwd, text=True, stderr=subprocess.DEVNULL
).strip()
def registry(tmp_path, monkeypatch, board="metis", remote=None, base=None):
projects = tmp_path / "projects"
boards = tmp_path / "boards"
checkout = projects / board
checkout.mkdir(parents=True)
git("init", "-q", cwd=checkout)
git(
"remote", "add", "origin",
remote or f"https://scm.bstein.dev/atlas/{board}.git",
cwd=checkout,
)
if base:
git(
"symbolic-ref", "refs/remotes/origin/HEAD", f"refs/remotes/origin/{base}",
cwd=checkout,
)
entry = boards / board
entry.mkdir(parents=True)
(entry / "board.json").write_text(
json.dumps({"slug": board, "default_workdir": str(checkout)})
)
monkeypatch.setattr(project, "PROJECT_ROOT", projects)
monkeypatch.setattr(project, "BOARD_ROOT", boards)
return checkout, entry / "board.json"
@pytest.mark.parametrize(
"branch",
[
"feature/safe", "fix/safe", "chore/safe", "docs/safe", "test/safe",
"refactor/safe", "wt/t_deadbeef", "review/t_deadbeef", "hermes/safe",
"handoff/safe",
],
)
def test_every_established_feature_namespace_is_accepted(branch):
assert project.validate_branch(branch, feature=True) == branch
@pytest.mark.parametrize(
"branch",
["main", "unknown/safe", "../main", "feature/../../main", "feature/.hidden", "-bad", "é/safe", "x" * 201],
)
def test_ref_validation_denies_traversal_and_unreviewed_namespaces(branch):
with pytest.raises(project.ProjectPolicyError):
project.validate_branch(branch, feature=True)
def test_canonical_registry_resolves_repo_base_and_default_branch(tmp_path, monkeypatch):
checkout, _ = registry(tmp_path, monkeypatch, base="trunk")
assert project.resolve_project("metis") == (
"https://scm.bstein.dev/atlas/metis.git", "trunk", checkout.resolve()
)
task = SimpleNamespace(id="t_deadbeef", branch_name="")
assert project.resolve_assignment("metis", task) == (
"https://scm.bstein.dev/atlas/metis.git", "wt/t_deadbeef", "trunk"
)
@pytest.mark.parametrize("board", ["cassandra", "metis", "soteria", "titan-iac"])
def test_every_atlas_project_uses_its_own_registry_checkout(tmp_path, monkeypatch, board):
registry(tmp_path, monkeypatch, board=board)
repo, base, _ = project.resolve_project(board)
assert repo == f"https://scm.bstein.dev/atlas/{board}.git"
assert base == "main"
def test_registered_project_without_checkout_keeps_its_own_repo_identity(
tmp_path, monkeypatch
):
checkout, _ = registry(tmp_path, monkeypatch, board="metis")
shutil.rmtree(checkout)
assert project.resolve_project("metis") == (
"https://scm.bstein.dev/atlas/metis.git",
"main",
checkout.resolve(),
)
def test_registry_rejects_project_slug_too_long_for_an_atlas_repo(
tmp_path, monkeypatch
):
board = "m" * 101
checkout, _ = registry(tmp_path, monkeypatch, board=board)
shutil.rmtree(checkout)
with pytest.raises(project.ProjectPolicyError, match="repository identity"):
project.resolve_project(board)
def test_noncanonical_remote_head_falls_back_to_main(tmp_path, monkeypatch):
checkout, _ = registry(tmp_path, monkeypatch)
real_run = project._run_git
def run_git(workdir, *arguments):
if arguments[0] == "symbolic-ref":
return "heads/not-origin"
return real_run(workdir, *arguments)
monkeypatch.setattr(project, "_run_git", run_git)
assert project.resolve_project("metis") == (
"https://scm.bstein.dev/atlas/metis.git",
"main",
checkout.resolve(),
)
def test_registry_rejects_slug_traversal_symlink_and_malformed_documents(tmp_path, monkeypatch):
_, board_file = registry(tmp_path, monkeypatch)
with pytest.raises(project.ProjectPolicyError, match="slug"):
project._read_board("../metis")
board_file.write_text("not-json")
with pytest.raises(project.ProjectPolicyError, match="malformed"):
project._read_board("metis")
board_file.write_text(json.dumps({"slug": "other", "default_workdir": "/tmp"}))
with pytest.raises(project.ProjectPolicyError, match="identity"):
project._read_board("metis")
board_file.unlink()
board_file.symlink_to("/etc/passwd")
with pytest.raises(OSError):
project._read_board("metis")
def test_registry_rejects_oversized_archived_and_non_regular_entry(tmp_path, monkeypatch):
_, board_file = registry(tmp_path, monkeypatch)
board_file.write_text("x" * (project.MAX_BOARD_BYTES + 1))
with pytest.raises(project.ProjectPolicyError, match="bounded"):
project._read_board("metis")
board_file.write_text(json.dumps({"slug": "metis", "archived": True}))
with pytest.raises(project.ProjectPolicyError, match="archived"):
project._read_board("metis")
board_file.unlink()
board_file.mkdir()
with pytest.raises(project.ProjectPolicyError, match="regular"):
project._read_board("metis")
@pytest.mark.parametrize(
"remote",
[
"https://token@scm.bstein.dev/atlas/metis.git",
"https://evil.example/atlas/metis.git",
"ssh://git@scm.bstein.dev/atlas/metis.git",
],
)
def test_registry_rejects_credentialed_or_non_atlas_origin(tmp_path, monkeypatch, remote):
registry(tmp_path, monkeypatch, remote=remote)
with pytest.raises(project.ProjectPolicyError, match="origin"):
project.resolve_project("metis")
def test_registry_rejects_missing_or_outside_workdir(tmp_path, monkeypatch):
checkout, board_file = registry(tmp_path, monkeypatch)
board_file.write_text(json.dumps({"slug": "metis"}))
with pytest.raises(project.ProjectPolicyError, match="default_workdir"):
project.resolve_project("metis")
outside = tmp_path / "outside"
checkout.rename(outside)
board_file.write_text(
json.dumps({"slug": "metis", "default_workdir": str(outside)})
)
with pytest.raises(project.ProjectPolicyError, match="outside"):
project.resolve_project("metis")
def test_registry_rejects_non_directory_checkout(tmp_path, monkeypatch):
checkout, _ = registry(tmp_path, monkeypatch)
shutil.rmtree(checkout)
checkout.write_text("not a checkout")
with pytest.raises(project.ProjectPolicyError, match="not a directory"):
project.resolve_project("metis")
def test_git_metadata_failure_and_invalid_task_identity_fail_closed(tmp_path, monkeypatch):
checkout, _ = registry(tmp_path, monkeypatch)
with pytest.raises(project.ProjectPolicyError, match="metadata"):
project._run_git(checkout, "remote", "get-url", "missing")
with pytest.raises(project.ProjectPolicyError, match="identity"):
project.resolve_assignment("metis", SimpleNamespace(id="../../bad"))
def test_workspace_migration_boundary_is_explicit():
assert project.distributed_workspace_eligible(SimpleNamespace(workspace_path=""))
assert project.distributed_workspace_eligible(SimpleNamespace())
assert not project.distributed_workspace_eligible(
SimpleNamespace(workspace_path="/opt/data/workspace/live")
)