Split the three oversized test files into support modules and topic files, evaluate the rmdir dir_fd capability once at import, and drop unused imports. Claude-Session: https://claude.ai/code/session_01Agoc9bRVE9KL7mmWtey9Vq
223 lines
6.4 KiB
Python
223 lines
6.4 KiB
Python
from __future__ import annotations
|
|
|
|
import os
|
|
from pathlib import Path
|
|
from typing import Any
|
|
|
|
import pytest
|
|
|
|
from elydora import cli
|
|
from elydora.plugins import (
|
|
_directory_removal,
|
|
_transaction,
|
|
)
|
|
from elydora.plugins._runtime_removal import (
|
|
commit_runtime_removal,
|
|
prepare_runtime_removal,
|
|
)
|
|
from runtime_removal_support import (
|
|
AGENT_ID,
|
|
AGENT_NAME,
|
|
PRIVATE_KEY,
|
|
prepare_runtime,
|
|
set_home,
|
|
write_secret,
|
|
)
|
|
|
|
|
|
@pytest.mark.skipif(os.name == "nt", reason="POSIX parent-dirfd contract")
|
|
def test_posix_runtime_directory_removal_cleans_exact_quarantine(
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
tmp_path: Path,
|
|
) -> None:
|
|
_root, agent = prepare_runtime(monkeypatch, tmp_path)
|
|
plan = prepare_runtime_removal(AGENT_ID, AGENT_NAME)
|
|
assert plan is not None
|
|
original_rename = _directory_removal._rename_posix_into_quarantine
|
|
calls: list[tuple[int, str, int, str, str]] = []
|
|
|
|
def record_rename(
|
|
parent_descriptor: int,
|
|
source_name: str,
|
|
quarantine_descriptor: int,
|
|
source_path: str,
|
|
destination_path: str,
|
|
) -> None:
|
|
calls.append(
|
|
(
|
|
parent_descriptor,
|
|
source_name,
|
|
quarantine_descriptor,
|
|
source_path,
|
|
destination_path,
|
|
)
|
|
)
|
|
original_rename(
|
|
parent_descriptor,
|
|
source_name,
|
|
quarantine_descriptor,
|
|
source_path,
|
|
destination_path,
|
|
)
|
|
|
|
monkeypatch.setattr(
|
|
_directory_removal,
|
|
"_rename_posix_into_quarantine",
|
|
record_rename,
|
|
)
|
|
|
|
commit_runtime_removal(plan)
|
|
|
|
assert len(calls) == 1
|
|
assert isinstance(calls[0][0], int)
|
|
assert calls[0][1] == AGENT_ID
|
|
assert isinstance(calls[0][2], int)
|
|
assert Path(calls[0][3]) == agent
|
|
assert Path(calls[0][4]).name == "entry"
|
|
quarantined = list(
|
|
Path(plan.runtime_root).glob(
|
|
f".{AGENT_ID}.*.elydora-removed/entry"
|
|
)
|
|
)
|
|
assert quarantined == []
|
|
assert list(Path(plan.runtime_root).glob(f".{AGENT_ID}.*.elydora-removed")) == []
|
|
assert not agent.exists()
|
|
|
|
|
|
@pytest.mark.skipif(os.name == "nt", reason="POSIX quarantine contract")
|
|
def test_posix_runtime_quarantine_preserves_atomic_boundary_swap(
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
tmp_path: Path,
|
|
) -> None:
|
|
root, agent = prepare_runtime(monkeypatch, tmp_path)
|
|
plan = prepare_runtime_removal(AGENT_ID, AGENT_NAME)
|
|
assert plan is not None
|
|
replacement = root / "replacement"
|
|
replacement.mkdir()
|
|
marker = replacement / "user-owned"
|
|
marker.write_text("preserve", encoding="utf-8")
|
|
original = root / "agent-original"
|
|
real_rename = _directory_removal._rename_posix_into_quarantine
|
|
|
|
def swap_then_quarantine(
|
|
parent_descriptor: int,
|
|
source_name: str,
|
|
quarantine_descriptor: int,
|
|
source_path: str,
|
|
destination_path: str,
|
|
) -> None:
|
|
agent.rename(original)
|
|
replacement.rename(agent)
|
|
real_rename(
|
|
parent_descriptor,
|
|
source_name,
|
|
quarantine_descriptor,
|
|
source_path,
|
|
destination_path,
|
|
)
|
|
|
|
real_rmdir = _directory_removal.os.rmdir
|
|
|
|
def reject_path_rmdir(
|
|
path: str,
|
|
*,
|
|
dir_fd: int | None = None,
|
|
) -> None:
|
|
if dir_fd is None:
|
|
raise AssertionError("POSIX runtime cleanup must use a parent dirfd")
|
|
real_rmdir(path, dir_fd=dir_fd)
|
|
|
|
monkeypatch.setattr(
|
|
_directory_removal,
|
|
"_rename_posix_into_quarantine",
|
|
swap_then_quarantine,
|
|
)
|
|
monkeypatch.setattr(_directory_removal.os, "rmdir", reject_path_rmdir)
|
|
|
|
with pytest.raises(OSError, match="changed while quarantining"):
|
|
commit_runtime_removal(plan)
|
|
|
|
quarantined_markers = list(
|
|
root.glob(f".{AGENT_ID}.*.elydora-removed/entry/user-owned")
|
|
)
|
|
assert len(quarantined_markers) == 1
|
|
assert quarantined_markers[0].read_text(encoding="utf-8") == "preserve"
|
|
assert original.is_dir()
|
|
|
|
|
|
@pytest.mark.skipif(os.name != "nt", reason="Windows exact-handle contract")
|
|
def test_windows_runtime_directory_removal_marks_exact_pinned_handle(
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
tmp_path: Path,
|
|
) -> None:
|
|
_root, agent = prepare_runtime(monkeypatch, tmp_path)
|
|
plan = prepare_runtime_removal(AGENT_ID, AGENT_NAME)
|
|
assert plan is not None
|
|
original_mark = _directory_removal._mark_windows_directory_for_deletion
|
|
observed: list[tuple[int, int]] = []
|
|
|
|
def record_mark(directory: Any) -> None:
|
|
assert directory.descriptor is not None
|
|
opened = os.fstat(directory.descriptor)
|
|
observed.append((opened.st_dev, opened.st_ino))
|
|
assert observed[-1] == (
|
|
plan.agent_snapshot.device,
|
|
plan.agent_snapshot.inode,
|
|
)
|
|
original_mark(directory)
|
|
|
|
monkeypatch.setattr(
|
|
_directory_removal,
|
|
"_mark_windows_directory_for_deletion",
|
|
record_mark,
|
|
)
|
|
|
|
commit_runtime_removal(plan)
|
|
|
|
assert observed == [(plan.agent_snapshot.device, plan.agent_snapshot.inode)]
|
|
assert not agent.exists()
|
|
|
|
|
|
def test_kiroide_cli_install_failure_leaves_no_runtime_root(
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
tmp_path: Path,
|
|
) -> None:
|
|
home = tmp_path / "home"
|
|
workspace = tmp_path / "workspace"
|
|
home.mkdir()
|
|
workspace.mkdir()
|
|
set_home(monkeypatch, home)
|
|
monkeypatch.chdir(workspace)
|
|
key_path = tmp_path / "install-private.key"
|
|
write_secret(key_path, PRIVATE_KEY)
|
|
args = cli.build_parser().parse_args(
|
|
[
|
|
"install",
|
|
"--agent",
|
|
"kiroide",
|
|
"--org_id",
|
|
"org-1",
|
|
"--agent_id",
|
|
AGENT_ID,
|
|
"--private_key_file",
|
|
str(key_path),
|
|
"--kid",
|
|
"kid-1",
|
|
]
|
|
)
|
|
|
|
def fail_commit(_staged: object) -> None:
|
|
raise OSError("injected Kiro IDE CLI commit failure")
|
|
|
|
monkeypatch.setattr(_transaction, "_commit", fail_commit)
|
|
|
|
with pytest.raises(OSError, match="injected Kiro IDE CLI commit failure"):
|
|
cli.cmd_install(args)
|
|
|
|
# The durable transaction state directory survives failures by design;
|
|
# everything else under the runtime root must be rolled back.
|
|
elydora_root = home / ".elydora"
|
|
if elydora_root.exists():
|
|
assert [entry.name for entry in elydora_root.iterdir()] == ["transactions"]
|
|
assert not (workspace / ".kiro").exists()
|