186 lines
5.6 KiB
Python
186 lines
5.6 KiB
Python
|
|
"""Degraded quarantine-path coverage for terminal journal safety."""
|
||
|
|
|
||
|
|
from __future__ import annotations
|
||
|
|
|
||
|
|
import os
|
||
|
|
from pathlib import Path
|
||
|
|
|
||
|
|
from testing.tests.test_hermes_cli_support import lanes
|
||
|
|
|
||
|
|
|
||
|
|
def test_quarantine_refuses_missing_or_unsafe_board_directory(
|
||
|
|
tmp_path: Path,
|
||
|
|
capsys,
|
||
|
|
):
|
||
|
|
path = tmp_path / "missing" / "journal"
|
||
|
|
assert lanes._quarantine_terminal(path, None, "invalid") == path
|
||
|
|
assert "unsafe board directory" in capsys.readouterr().err
|
||
|
|
|
||
|
|
regular_parent = tmp_path / "regular"
|
||
|
|
regular_parent.write_text("not a directory", encoding="utf-8")
|
||
|
|
unsafe = regular_parent / "journal"
|
||
|
|
assert lanes._quarantine_terminal(unsafe, None, "invalid") == unsafe
|
||
|
|
|
||
|
|
|
||
|
|
def test_quarantine_accepts_a_lexically_outside_but_pinned_path(
|
||
|
|
tmp_path: Path,
|
||
|
|
monkeypatch,
|
||
|
|
):
|
||
|
|
monkeypatch.setattr(lanes, "STATE_ROOT", tmp_path / "other-root")
|
||
|
|
board = tmp_path / "board"
|
||
|
|
board.mkdir()
|
||
|
|
path = board / "journal"
|
||
|
|
path.write_bytes(b"invalid")
|
||
|
|
|
||
|
|
destination = lanes._quarantine_terminal(path, None, "bad reason / outside")
|
||
|
|
|
||
|
|
assert destination.parent == board / "quarantine"
|
||
|
|
assert not path.exists()
|
||
|
|
|
||
|
|
|
||
|
|
def test_quarantine_rejects_changed_pinned_board_identity(
|
||
|
|
tmp_path: Path,
|
||
|
|
monkeypatch,
|
||
|
|
capsys,
|
||
|
|
):
|
||
|
|
monkeypatch.setattr(lanes, "STATE_ROOT", tmp_path)
|
||
|
|
board = tmp_path / "board"
|
||
|
|
board.mkdir()
|
||
|
|
path = board / "journal"
|
||
|
|
path.write_bytes(b"invalid")
|
||
|
|
snapshot = lanes._open_terminal_recovery_snapshot(path)
|
||
|
|
assert snapshot is not None
|
||
|
|
real_fstat = lanes.os.fstat
|
||
|
|
|
||
|
|
def changed_board(descriptor):
|
||
|
|
observed = real_fstat(descriptor)
|
||
|
|
if descriptor == snapshot.directory_descriptor:
|
||
|
|
return os.stat_result(
|
||
|
|
(*observed[:1], observed.st_ino + 1, *observed[2:])
|
||
|
|
)
|
||
|
|
return observed
|
||
|
|
|
||
|
|
monkeypatch.setattr(lanes.os, "fstat", changed_board)
|
||
|
|
try:
|
||
|
|
lanes._quarantine_terminal(path, None, "changed-board", snapshot=snapshot)
|
||
|
|
finally:
|
||
|
|
snapshot.close()
|
||
|
|
assert not path.exists()
|
||
|
|
assert "degraded safely" in capsys.readouterr().err
|
||
|
|
|
||
|
|
|
||
|
|
def test_quarantine_rejects_source_replaced_during_open(
|
||
|
|
tmp_path: Path,
|
||
|
|
monkeypatch,
|
||
|
|
capsys,
|
||
|
|
):
|
||
|
|
monkeypatch.setattr(lanes, "STATE_ROOT", tmp_path)
|
||
|
|
board = tmp_path / "board"
|
||
|
|
board.mkdir()
|
||
|
|
path = board / "journal"
|
||
|
|
path.write_bytes(b"original")
|
||
|
|
replacement = board / "replacement"
|
||
|
|
replacement.write_bytes(b"replacement")
|
||
|
|
real_open = lanes.os.open
|
||
|
|
swapped = {"value": False}
|
||
|
|
|
||
|
|
def swap_before_source_open(target, flags, *args, **kwargs):
|
||
|
|
if target == path.name and kwargs.get("dir_fd") is not None and not swapped["value"]:
|
||
|
|
os.replace(replacement, path)
|
||
|
|
swapped["value"] = True
|
||
|
|
return real_open(target, flags, *args, **kwargs)
|
||
|
|
|
||
|
|
monkeypatch.setattr(lanes.os, "open", swap_before_source_open)
|
||
|
|
lanes._quarantine_terminal(path, None, "source-race")
|
||
|
|
assert path.exists()
|
||
|
|
assert path.read_bytes() == b"replacement"
|
||
|
|
assert "degraded safely" in capsys.readouterr().err
|
||
|
|
|
||
|
|
|
||
|
|
def test_quarantine_unlinks_an_invalid_destination_before_degrading(
|
||
|
|
tmp_path: Path,
|
||
|
|
monkeypatch,
|
||
|
|
capsys,
|
||
|
|
):
|
||
|
|
monkeypatch.setattr(lanes, "STATE_ROOT", tmp_path)
|
||
|
|
board = tmp_path / "board"
|
||
|
|
board.mkdir()
|
||
|
|
path = board / "journal"
|
||
|
|
path.write_bytes(b"invalid")
|
||
|
|
real_fstat = lanes.os.fstat
|
||
|
|
target_descriptors = set()
|
||
|
|
real_fdopen = lanes.os.fdopen
|
||
|
|
|
||
|
|
def remember_target(descriptor, *args, **kwargs):
|
||
|
|
target_descriptors.add(descriptor)
|
||
|
|
return real_fdopen(descriptor, *args, **kwargs)
|
||
|
|
|
||
|
|
def unsafe_target(descriptor):
|
||
|
|
observed = real_fstat(descriptor)
|
||
|
|
if descriptor in target_descriptors:
|
||
|
|
return os.stat_result(
|
||
|
|
(*observed[:3], 0o100644, *observed[4:])
|
||
|
|
)
|
||
|
|
return observed
|
||
|
|
|
||
|
|
monkeypatch.setattr(lanes.os, "fdopen", remember_target)
|
||
|
|
monkeypatch.setattr(lanes.os, "fstat", unsafe_target)
|
||
|
|
lanes._quarantine_terminal(path, None, "unsafe-target")
|
||
|
|
|
||
|
|
assert not list((board / "quarantine").glob("*.quarantine"))
|
||
|
|
assert "degraded safely" in capsys.readouterr().err
|
||
|
|
|
||
|
|
|
||
|
|
def test_quarantine_destination_collision_is_bounded(
|
||
|
|
tmp_path: Path,
|
||
|
|
monkeypatch,
|
||
|
|
capsys,
|
||
|
|
):
|
||
|
|
monkeypatch.setattr(lanes, "STATE_ROOT", tmp_path)
|
||
|
|
board = tmp_path / "board"
|
||
|
|
board.mkdir()
|
||
|
|
path = board / "journal"
|
||
|
|
path.write_bytes(b"invalid")
|
||
|
|
real_open = lanes.os.open
|
||
|
|
|
||
|
|
def collide_on_destination(target, flags, *args, **kwargs):
|
||
|
|
if (
|
||
|
|
isinstance(target, str)
|
||
|
|
and target.endswith(".quarantine")
|
||
|
|
and flags & os.O_EXCL
|
||
|
|
):
|
||
|
|
raise FileExistsError(target)
|
||
|
|
return real_open(target, flags, *args, **kwargs)
|
||
|
|
|
||
|
|
monkeypatch.setattr(lanes.os, "open", collide_on_destination)
|
||
|
|
destination = lanes._quarantine_terminal(path, None, "collision")
|
||
|
|
|
||
|
|
assert destination == path
|
||
|
|
assert not path.exists()
|
||
|
|
assert "could not reserve" in capsys.readouterr().err
|
||
|
|
|
||
|
|
|
||
|
|
def test_quarantine_deferred_retirement_does_not_raise(
|
||
|
|
tmp_path: Path,
|
||
|
|
monkeypatch,
|
||
|
|
capsys,
|
||
|
|
):
|
||
|
|
monkeypatch.setattr(lanes, "STATE_ROOT", tmp_path)
|
||
|
|
board = tmp_path / "board"
|
||
|
|
board.mkdir()
|
||
|
|
path = board / "journal"
|
||
|
|
path.write_bytes(b"invalid")
|
||
|
|
calls = {"value": 0}
|
||
|
|
|
||
|
|
def fail_retirement(*_args, **_kwargs):
|
||
|
|
calls["value"] += 1
|
||
|
|
raise OSError("retirement unavailable")
|
||
|
|
|
||
|
|
monkeypatch.setattr(lanes, "_retire_terminal_entry", fail_retirement)
|
||
|
|
destination = lanes._quarantine_terminal(path, None, "retire-failure")
|
||
|
|
|
||
|
|
assert destination.exists()
|
||
|
|
assert path.exists()
|
||
|
|
assert calls["value"] == 2
|
||
|
|
assert "retirement=deferred" in capsys.readouterr().err
|