"""Shared loader and fixtures for the handoff acceptance harness tests. The harness lives in ``scripts/ops`` as sibling modules rather than an installed package, so tests import it the way the entry point does: by putting that directory on the path once and importing by name. """ from __future__ import annotations import importlib import sys from pathlib import Path from typing import Any REPO_ROOT = Path(__file__).resolve().parents[2] OPS = REPO_ROOT / "scripts" / "ops" if str(OPS) not in sys.path: sys.path.insert(0, str(OPS)) def load_handoff_module(name: str) -> Any: """Import one harness module by name.""" return importlib.import_module(name) exec_module = load_handoff_module("hermes_handoff_exec") model = load_handoff_module("hermes_handoff_model") def outcome( stdout: str = "", stderr: str = "", returncode: int | None = 0, error: str | None = None, truncated: bool = False, vantage: str = "self", argv: tuple[str, ...] = ("kubectl", "get", "pods"), ) -> Any: """Return a synthetic command outcome for evaluator tests.""" return exec_module.Outcome( argv=argv, vantage=vantage, returncode=returncode, stdout=stdout, stderr=stderr, error=error, truncated=truncated, ) def spec( rule: str, expect: dict | None = None, steps: tuple = (), identifier: str = "test.check", mandatory: bool = True, scope: str | None = None, ) -> Any: """Return a synthetic check specification.""" return model.CheckSpec( id=identifier, title="test check", group="test", rule=rule, steps=steps, expect=expect or {}, mandatory=mandatory, scope=scope or model.ALWAYS, ) def step( key: str, kind: str | None = None, vantage: str = "self", optional: bool = False ) -> Any: """Return a synthetic step.""" return model.Step( key=key, vantage=vantage, argv=("kubectl", "get", "pods", "-o", "name"), kind=kind or model.READ, optional=optional, ) class FakeClock: """A monotonic clock the tests advance by hand.""" def __init__(self, start: float = 0.0) -> None: self.now = start def __call__(self) -> float: return self.now def advance(self, seconds: float) -> None: """Move the clock forward.""" self.now += seconds class FakeSpawn: """A ``subprocess.run`` stand-in driven by a queue of scripted results.""" def __init__(self, results: list) -> None: self.results = list(results) self.calls: list[dict] = [] def __call__(self, argv, **kwargs): self.calls.append({"argv": list(argv), **kwargs}) if not self.results: raise AssertionError(f"unexpected command: {argv}") outcome_or_error = self.results.pop(0) if isinstance(outcome_or_error, Exception): raise outcome_or_error return outcome_or_error