atlas-iac/testing/tests/test_hermes_cli_capabilities.py

393 lines
12 KiB
Python
Raw Normal View History

2026-08-17 08:16:35 -03:00
"""Mixed-image capability gates for mounted Hermes CLI lane scripts."""
from __future__ import annotations
import json
import runpy
2026-08-17 08:16:35 -03:00
import sys
from contextlib import nullcontext
from datetime import datetime, timedelta, timezone
2026-08-17 08:16:35 -03:00
from pathlib import Path
from types import SimpleNamespace
import pytest
from testing.tests.test_hermes_cli_support import SCRIPTS, _completed_result, lanes
2026-08-17 08:16:35 -03:00
def _old_complete(
_conn,
_task_id,
*,
result=None,
summary=None,
metadata=None,
expected_run_id=None,
):
return bool(result or summary or metadata or expected_run_id)
def _new_complete(
_conn,
_task_id,
*,
result=None,
summary=None,
metadata=None,
expected_run_id=None,
replay_ended_run_id=None,
):
return bool(result or summary or metadata or expected_run_id or replay_ended_run_id)
def _old_reclaim(_conn, _task_id, *, reason, signal_fn=None):
return bool(reason or signal_fn)
def _new_reclaim(
_conn,
_task_id,
*,
reason,
signal_fn=None,
expected_run_id=None,
):
return bool(reason or signal_fn or expected_run_id)
def _db(*, patched: bool):
return SimpleNamespace(
complete_task=_new_complete if patched else _old_complete,
reclaim_task=_new_reclaim if patched else _old_reclaim,
)
def _terminal_document(run_id: int = 7) -> dict:
structured = _completed_result("accepted result")
return {
"board": "cassandra",
"task_id": "t_compat",
"expected_run_id": run_id,
"result": json.dumps(structured, sort_keys=True),
"summary": "accepted result",
"metadata": {},
"kanban_state": "pending",
"recorded_at": lanes.utc_now(),
}
def test_capability_detection_requires_explicit_safety_keywords():
old = lanes.detect_kanban_capabilities(_db(patched=False))
new = lanes.detect_kanban_capabilities(_db(patched=True))
assert old.exact_run_completion is True
assert old.ended_run_replay is False
assert old.exact_run_reclaim is False
assert old.ready is False
assert old.deferred_features == ("ended-run-replay", "exact-run-reclaim")
assert new.ready is True
assert new.deferred_features == ()
variadic = SimpleNamespace(
complete_task=lambda *_args, **_kwargs: True,
reclaim_task=lambda *_args, **_kwargs: True,
)
assert lanes.detect_kanban_capabilities(variadic).ready is False
assert lanes._explicit_keyword(object(), "expected_run_id") is False
missing_completion = lanes.KanbanCapabilities(False, True, True)
assert missing_completion.deferred_features == ("exact-run-completion",)
2026-08-17 08:16:35 -03:00
def test_deferred_features_name_every_absent_capability():
"""A fully legacy image defers all three safety features by exact name."""
fully_legacy = lanes.KanbanCapabilities(False, False, False)
assert fully_legacy.ready is False
assert fully_legacy.deferred_features == (
"exact-run-completion",
"ended-run-replay",
"exact-run-reclaim",
)
2026-08-17 08:16:35 -03:00
def test_startup_health_moves_from_bounded_deferred_to_ready(
tmp_path: Path,
capsys,
monkeypatch,
):
monkeypatch.setattr(lanes, "STATE_ROOT", tmp_path / "cli-lanes")
health = tmp_path / "runtime-health.json"
old = lanes.initialize_kanban_capabilities(_db(patched=False), health_path=health)
old_document = lanes.runtime_health(health)
assert old.ready is False
assert old_document["state"] == "deferred"
assert old_document["schema_version"] == 1
2026-08-17 08:16:35 -03:00
assert old_document["ready"] is False
assert old_document["active_run_completion_safe"] is True
assert old_document["deferred_features"] == [
"ended-run-replay",
"exact-run-reclaim",
]
assert health.stat().st_size < 2048
assert health.stat().st_mode & 0o777 == 0o600
assert "compatibility deferred" in capsys.readouterr().err
new_db = _db(patched=True)
lanes.kanban_capabilities(new_db)
new = lanes.initialize_kanban_capabilities(new_db, health_path=health)
2026-08-17 08:16:35 -03:00
assert new.ready is True
assert lanes.kanban_capabilities(new_db) is new
2026-08-17 08:16:35 -03:00
assert lanes.runtime_health(health)["state"] == "ready"
def test_readiness_fails_closed_without_disclosing_health_values(
tmp_path: Path,
capsys,
monkeypatch,
):
"""Readiness distinguishes startup, malformed, deferred, stale, and ready."""
health = tmp_path / "runtime-health.json"
now = datetime(2026, 8, 17, 12, 0, tzinfo=timezone.utc)
assert lanes.readiness_issue(health, now=now) == "runtime health is missing"
health.write_text("not-json", encoding="utf-8")
assert lanes.readiness_issue(health, now=now) == "runtime health is malformed"
deferred = lanes._health_document(lanes.KanbanCapabilities(True, False, False))
deferred["observed_at"] = now.isoformat()
health.write_text(json.dumps(deferred), encoding="utf-8")
assert lanes.readiness_issue(health, now=now) == "runtime compatibility is deferred"
ready = lanes._health_document(lanes.KanbanCapabilities(True, True, True))
ready["observed_at"] = (now - timedelta(seconds=61)).isoformat()
health.write_text(json.dumps(ready), encoding="utf-8")
assert lanes.readiness_issue(health, now=now) == "runtime health is stale"
ready["observed_at"] = now.isoformat()
health.write_text(json.dumps(ready), encoding="utf-8")
assert lanes.readiness_issue(health, now=now) is None
monkeypatch.setattr(lanes, "STATE_ROOT", tmp_path)
monkeypatch.setenv("HERMES_CLI_HEALTH_MAX_AGE_SECONDS", "31536000")
assert sys.modules["cli_lane_capabilities"].main() == 0
assert capsys.readouterr().out == ""
@pytest.mark.parametrize(
"mutation",
[
{"schema_version": 2},
{"state": "ready", "ready": False},
{"state": "ready", "capabilities": []},
{"observed_at": 5},
{"observed_at": "not-a-date"},
{"observed_at": "2026-08-17T12:00:00"},
],
)
def test_readiness_rejects_inconsistent_documents(
tmp_path: Path,
mutation: dict,
):
"""Inconsistent ready documents remain not-ready."""
now = datetime(2026, 8, 17, 12, 0, tzinfo=timezone.utc)
document = lanes._health_document(lanes.KanbanCapabilities(True, True, True))
document["observed_at"] = now.isoformat()
document.update(mutation)
health = tmp_path / "runtime-health.json"
health.write_text(json.dumps(document), encoding="utf-8")
assert lanes.readiness_issue(health, now=now) == "runtime health is malformed"
def test_readiness_command_fails_with_only_a_bounded_reason(
tmp_path: Path,
capsys,
monkeypatch,
):
"""The probe command exits nonzero and discloses nothing but the reason."""
monkeypatch.setattr(lanes, "STATE_ROOT", tmp_path)
assert sys.modules["cli_lane_capabilities"].main() == 1
captured = capsys.readouterr()
assert captured.out == ""
assert captured.err.strip() == "runtime health is missing"
def test_capabilities_module_is_the_deployed_probe_entrypoint(
tmp_path: Path,
monkeypatch,
):
"""`python cli_lane_capabilities.py` runs the readiness probe directly."""
monkeypatch.setattr(lanes, "STATE_ROOT", tmp_path)
with pytest.raises(SystemExit) as excinfo:
runpy.run_path(
str(SCRIPTS / "cli_lane_capabilities.py"),
run_name="__main__",
)
assert excinfo.value.code == 1
def test_readiness_rejects_future_health(tmp_path: Path):
"""A clock-skewed future observation cannot make a stale process ready."""
now = datetime(2026, 8, 17, 12, 0, tzinfo=timezone.utc)
document = lanes._health_document(lanes.KanbanCapabilities(True, True, True))
document["observed_at"] = (now + timedelta(seconds=6)).isoformat()
health = tmp_path / "runtime-health.json"
health.write_text(json.dumps(document), encoding="utf-8")
assert lanes.readiness_issue(health, now=now) == "runtime health is stale"
2026-08-17 08:16:35 -03:00
@pytest.mark.parametrize(
("status", "run_id", "expected"),
[("running", 7, "committed"), ("blocked", None, "deferred")],
)
def test_old_image_completes_only_the_exact_active_run(
tmp_path: Path,
monkeypatch,
status: str,
run_id: int | None,
expected: str,
):
monkeypatch.setattr(lanes, "STATE_ROOT", tmp_path / "cli-lanes")
task = SimpleNamespace(
id="t_compat",
status=status,
current_run_id=run_id,
completed_run_id=None,
result=None,
assignee="cli-auto",
)
calls = []
def complete(
_conn,
_task_id,
*,
result=None,
summary=None,
metadata=None,
expected_run_id=None,
):
calls.append(expected_run_id)
task.status = "done"
task.current_run_id = None
task.completed_run_id = expected_run_id
task.result = result
return True
db = SimpleNamespace(
scoped_current_board=lambda _board: nullcontext(),
connect=lambda board: SimpleNamespace(close=lambda: None),
get_task=lambda _conn, _task_id: task,
complete_task=complete,
reclaim_task=_old_reclaim,
)
identity = lanes.TerminalIdentity("cassandra", "t_compat", 7, "pending")
outcome = lanes._finalize_document_db(db, identity, _terminal_document())
assert outcome == expected
assert calls == ([7] if status == "running" else [])
if expected == "deferred":
assert task.status == "blocked"
def test_old_image_preserves_pending_and_prepared_journals(
tmp_path: Path,
monkeypatch,
):
monkeypatch.setattr(lanes, "STATE_ROOT", tmp_path / "cli-lanes")
task = SimpleNamespace(
id="t_compat",
status="blocked",
current_run_id=None,
completed_run_id=None,
result=None,
assignee="cli-auto",
)
db = SimpleNamespace(
scoped_current_board=lambda _board: nullcontext(),
connect=lambda board: SimpleNamespace(close=lambda: None),
get_task=lambda _conn, _task_id: task,
complete_task=_old_complete,
reclaim_task=_old_reclaim,
)
monkeypatch.setitem(sys.modules, "hermes_cli", SimpleNamespace(kanban_db=db))
pending, _record = lanes._write_terminal_record(
lanes.state_path("cassandra", "t_compat"),
board="cassandra",
task_id="t_compat",
run_id=7,
structured=_completed_result("accepted result"),
summary="accepted result",
metadata={},
)
assert lanes.recover_pending_finalizations() == 0
assert pending.exists()
assert len(list(pending.parent.glob("*.terminal.prepared-*.json"))) == 1
assert task.status == "blocked"
def test_old_image_never_calls_unguarded_reclaim(tmp_path: Path, monkeypatch):
monkeypatch.setattr(lanes, "STATE_ROOT", tmp_path / "cli-lanes")
task = SimpleNamespace(
id="t_running",
status="running",
current_run_id=11,
assignee="cli-auto",
)
calls = []
def reclaim(_conn, _task_id, *, reason, signal_fn=None):
calls.append((reason, signal_fn))
return True
db = SimpleNamespace(
complete_task=_old_complete,
reclaim_task=reclaim,
list_boards=lambda include_archived=False: [{"slug": "cassandra"}],
scoped_current_board=lambda _board: nullcontext(),
connect=lambda board: SimpleNamespace(close=lambda: None),
list_tasks=lambda _conn: [task],
)
monkeypatch.setitem(sys.modules, "hermes_cli", SimpleNamespace(kanban_db=db))
monkeypatch.setattr(lanes, "recover_pending_finalizations", lambda: 0)
lanes.recover_orphans()
assert calls == []
def test_main_detects_capabilities_before_startup_recovery(
tmp_path: Path,
monkeypatch,
):
events = []
db = _db(patched=False)
monkeypatch.setitem(sys.modules, "hermes_cli", SimpleNamespace(kanban_db=db))
monkeypatch.setattr(lanes, "RESULT_SCHEMA_PATH", tmp_path / "result.schema.json")
monkeypatch.setattr(
lanes,
"initialize_kanban_capabilities",
lambda candidate: events.append(("detect", candidate))
or lanes.KanbanCapabilities(True, False, False),
)
def stop_after_detection():
events.append(("recover", db))
raise RuntimeError("stop after startup boundary")
monkeypatch.setattr(lanes, "recover_orphans", stop_after_detection)
with pytest.raises(RuntimeError, match="startup boundary"):
lanes.main()
assert events == [("detect", db), ("recover", db)]