atlas-iac/testing/tests/test_hermes_cli_evidence_edges.py
jenkins 8036101f0b Merge remote-tracking branch 'origin/feature/hermes-review-goal-semantics' into feature/hermes-distributed-worker-pool
# Conflicts:
#	scripts/tests/test_dashboards_render_atlas_drilldowns.py
#	scripts/tests/test_dashboards_render_jobs.py
#	services/hermes/scm-common/scripts/scm_broker.py
#	services/hermes/scripts/cli_lane_dispatch.py
#	services/hermes/scripts/cli_lane_execution.py
#	testing/quality_contract.json
#	testing/tests/test_hermes_agent_access.py
#	testing/tests/test_hermes_agent_security.py
#	testing/tests/test_hermes_chat_config.py
#	testing/tests/test_hermes_chat_images.py
#	testing/tests/test_hermes_chat_provider_auth.py
#	testing/tests/test_hermes_chat_quality.py
#	testing/tests/test_hermes_chat_support.py
#	testing/tests/test_hermes_chat_voice.py
#	testing/tests/test_hermes_cli_finalization_edges.py
#	testing/tests/test_hermes_cli_foundation_coverage.py
#	testing/tests/test_hermes_cli_lanes_configuration.py
#	testing/tests/test_hermes_cli_recovery_edges.py
#	testing/tests/test_hermes_cli_retention_edges.py
#	testing/tests/test_hermes_coordinator.py
#	testing/tests/test_hermes_coordinator_boards.py
#	testing/tests/test_hermes_coordinator_support.py
2026-08-18 01:43:39 -03:00

265 lines
9.4 KiB
Python

"""Failure-boundary coverage for immutable lane evidence and retention."""
from __future__ import annotations
import errno
import os
from pathlib import Path
from types import SimpleNamespace
import pytest
from testing.tests.test_hermes_cli_support import _pending_terminal_record, lanes
def test_rename_noreplace_rejects_missing_kernel_support(monkeypatch):
monkeypatch.setattr(lanes.ctypes, "CDLL", lambda *_args, **_kwargs: object())
with pytest.raises(OSError) as raised:
lanes._rename_noreplace("a", "b", source_dir=1, destination_dir=1)
assert raised.value.errno == errno.ENOSYS
def test_rename_noreplace_surfaces_kernel_errno(monkeypatch):
class Rename:
argtypes = None
restype = None
def __call__(self, *_args):
return -1
monkeypatch.setattr(
lanes.ctypes,
"CDLL",
lambda *_args, **_kwargs: SimpleNamespace(renameat2=Rename()),
)
monkeypatch.setattr(lanes.ctypes, "get_errno", lambda: errno.EEXIST)
with pytest.raises(FileExistsError):
lanes._rename_noreplace("a", "b", source_dir=1, destination_dir=1)
def test_retirement_reports_missing_replacement_collision_and_restoration(
tmp_path: Path,
monkeypatch,
):
path = tmp_path / "pending"
path.write_text("old", encoding="utf-8")
source = path.stat()
source_descriptor = os.open(path, os.O_RDONLY)
directory = os.open(tmp_path, os.O_RDONLY | getattr(os, "O_DIRECTORY", 0))
try:
path.unlink()
assert lanes._retire_terminal_entry(
path,
source,
board_descriptor=directory,
quarantine_descriptor=directory,
source_descriptor=source_descriptor,
) == "missing"
path.write_text("new", encoding="utf-8")
assert lanes._retire_terminal_entry(
path,
source,
board_descriptor=directory,
quarantine_descriptor=directory,
source_descriptor=source_descriptor,
) == "replacement"
os.close(source_descriptor)
source_descriptor = os.open(path, os.O_RDONLY)
replacement = path.stat()
monkeypatch.setattr(
lanes,
"_rename_noreplace",
lambda *_args, **_kwargs: (_ for _ in ()).throw(FileExistsError()),
)
assert lanes._retire_terminal_entry(
path,
replacement,
board_descriptor=directory,
quarantine_descriptor=directory,
source_descriptor=source_descriptor,
) == "collision"
finally:
os.close(source_descriptor)
os.close(directory)
def test_retirement_preserves_a_postcheck_replacement(tmp_path: Path, monkeypatch):
path = tmp_path / "pending"
path.write_text("old", encoding="utf-8")
source = path.stat()
source_descriptor = os.open(path, os.O_RDONLY)
directory = os.open(tmp_path, os.O_RDONLY | getattr(os, "O_DIRECTORY", 0))
calls = []
def race(source_name, destination, **_kwargs):
calls.append((source_name, destination))
if len(calls) == 1:
os.rename(source_name, destination, src_dir_fd=directory, dst_dir_fd=directory)
staged = tmp_path / destination
staged.unlink()
staged.write_text("replacement", encoding="utf-8")
else:
os.rename(source_name, destination, src_dir_fd=directory, dst_dir_fd=directory)
monkeypatch.setattr(lanes, "_rename_noreplace", race)
try:
assert lanes._retire_terminal_entry(
path,
source,
board_descriptor=directory,
quarantine_descriptor=directory,
source_descriptor=source_descriptor,
) == "replacement"
finally:
os.close(source_descriptor)
os.close(directory)
assert path.read_text(encoding="utf-8") == "replacement"
def test_evidence_writer_bounds_payload_and_cleans_failed_temporary(
tmp_path: Path,
monkeypatch,
):
monkeypatch.setattr(lanes, "MAX_TERMINAL_RECORD_BYTES", 8)
with pytest.raises(ValueError, match="bounded"):
lanes._write_json_noreplace(tmp_path / "large.json", {"value": "x" * 5000})
monkeypatch.setattr(lanes, "MAX_TERMINAL_RECORD_BYTES", 1024 * 1024)
real_retire = lanes._retire_terminal_entry
monkeypatch.setattr(
lanes,
"_rename_noreplace",
lambda *_args, **_kwargs: (_ for _ in ()).throw(OSError("rename failed")),
)
monkeypatch.setattr(
lanes,
"_retire_terminal_entry",
lambda *_args, **_kwargs: (_ for _ in ()).throw(OSError("cleanup failed")),
)
with pytest.raises(OSError, match="rename failed"):
lanes._write_json_noreplace(tmp_path / "failed.json", {"value": "small"})
monkeypatch.setattr(lanes, "_retire_terminal_entry", real_retire)
def test_evidence_writer_closes_unwrapped_and_rejects_unsafe_temporary_files(
tmp_path: Path,
monkeypatch,
):
destination = tmp_path / "evidence.json"
real_fdopen = lanes.os.fdopen
opened = []
def fail_fdopen(descriptor, *_args, **_kwargs):
opened.append(descriptor)
raise OSError("fdopen failed")
monkeypatch.setattr(lanes.os, "fdopen", fail_fdopen)
with pytest.raises(OSError, match="fdopen failed"):
lanes._write_json_noreplace(destination, {"value": "bounded"})
with pytest.raises(OSError):
os.fstat(opened[0])
monkeypatch.setattr(lanes.os, "fdopen", real_fdopen)
with monkeypatch.context() as unsafe:
unsafe.setattr(lanes.stat, "S_IMODE", lambda _mode: 0o644)
with pytest.raises(OSError, match="not private"):
lanes._write_json_noreplace(destination, {"value": "bounded"})
def test_evidence_paths_and_collision_validation_fail_closed(
tmp_path: Path,
monkeypatch,
):
monkeypatch.setattr(lanes, "STATE_ROOT", tmp_path / "cli-lanes")
identity = lanes.TerminalIdentity("cassandra", "t_evidence", 9, "pending")
document = _pending_terminal_record("cassandra", "t_evidence", 9, "accepted")
with pytest.raises(ValueError, match="evidence state"):
lanes._terminal_evidence_path(identity, "pending", document)
monkeypatch.setattr(lanes, "_write_json_noreplace", lambda *_args: False)
monkeypatch.setattr(lanes, "_terminal_evidence_identity", lambda _path: None)
with pytest.raises(OSError, match="identity is invalid"):
lanes._persist_prepared_evidence(identity, document)
with pytest.raises(OSError, match="conflict evidence identity"):
lanes._persist_conflict_evidence(identity, document, "loser")
def test_existing_evidence_with_different_result_is_rejected(
tmp_path: Path,
monkeypatch,
):
monkeypatch.setattr(lanes, "STATE_ROOT", tmp_path / "cli-lanes")
identity = lanes.TerminalIdentity("cassandra", "t_evidence", 10, "pending")
document = _pending_terminal_record("cassandra", "t_evidence", 10, "accepted")
prepared_identity = lanes.TerminalIdentity("cassandra", "t_evidence", 10, "prepared")
conflict_identity = lanes.TerminalIdentity("cassandra", "t_evidence", 10, "conflict")
monkeypatch.setattr(lanes, "_write_json_noreplace", lambda *_args: False)
monkeypatch.setattr(
lanes,
"_terminal_evidence_identity",
lambda path: conflict_identity if "conflict" in path.name else prepared_identity,
)
monkeypatch.setattr(lanes, "_load_small_json", lambda _path: {})
with pytest.raises(OSError, match="conflicting result"):
lanes._persist_prepared_evidence(identity, document)
with pytest.raises(OSError, match="different result"):
lanes._persist_conflict_evidence(identity, document, "loser")
def test_retention_failure_paths_are_bounded(tmp_path: Path, monkeypatch, capsys):
path = tmp_path / "artifact"
path.write_text("data", encoding="utf-8")
observed = path.stat()
monkeypatch.setattr(
lanes,
"_retire_terminal_entry",
lambda *_args, **_kwargs: (_ for _ in ()).throw(OSError("busy")),
)
assert lanes._unlink_artifact_if_same(path, observed) is False
monkeypatch.setattr(lanes, "LAST_ARTIFACT_GC", 0.0)
monkeypatch.setattr(
lanes,
"gc_lane_artifacts",
lambda **_kwargs: (_ for _ in ()).throw(OSError("volume")),
)
assert lanes.maybe_gc_lane_artifacts(now=1000) == 0
assert "retention deferred" in capsys.readouterr().err
def test_evidence_writer_rejects_a_tampered_temporary(tmp_path: Path, monkeypatch):
"""A shared or replaced temporary inode never becomes durable evidence."""
real_fstat = os.fstat
calls = []
def hardlinked_fstat(descriptor):
observed = real_fstat(descriptor)
calls.append(observed)
if len(calls) == 2:
return os.stat_result((*observed[:3], 2, *observed[4:]))
return observed
monkeypatch.setattr(lanes.os, "fstat", hardlinked_fstat)
with pytest.raises(OSError, match="not private and singly linked"):
lanes._write_json_noreplace(tmp_path / "tampered.json", {"value": "x"})
assert list(tmp_path.iterdir()) == []
def test_evidence_writer_releases_descriptor_when_inspection_fails(
tmp_path: Path,
monkeypatch,
):
"""An fstat fault closes the created descriptor without staging cleanup."""
def failing_fstat(_descriptor):
raise OSError("inspection failed")
monkeypatch.setattr(lanes.os, "fstat", failing_fstat)
with pytest.raises(OSError, match="inspection failed"):
lanes._write_json_noreplace(tmp_path / "orphaned.json", {"value": "x"})