"""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") )