chore(oss): audit the server package and resync the SDK mirrors

Split the 657-line operation service into eor, submit, and verify
modules, add the AGENT_REVOKED and KEY_RETIRED codes the Backend already
emits, and condense every multi-line comment. Mirror Node, Python, and
Go at 2.1.0 with the deep-health 503, non-idempotent retry, rotation
grace, and path-encoding fixes. Document which routes and response
fields the self-hosted server does not provide.

Claude-Session: https://claude.ai/code/session_01Agoc9bRVE9KL7mmWtey9Vq
This commit is contained in:
雷电芽衣 committed 2026-09-03 20:48:20 -04:00
1 parent 2010175a82
commit bc6983a62e
364 files changed
+10180 -23349

No files matched your search

+27 -681
View File
@@ -10,31 +10,13 @@ import time
import pytest
from elydora.plugins import _transaction, _transaction_staging
from elydora.plugins._managed_files import read_physical_directory, read_physical_file
def _write(path: Path, source: str) -> None:
path.write_bytes(source.encode("utf-8"))
def _change(
path: Path,
original: str | None,
next_source: str | None,
mode: int = 0o600,
enforce_mode: bool = False,
) -> _transaction.FileChange:
change = _transaction.source_change(
str(path),
path.name,
original,
next_source,
mode,
enforce_mode=enforce_mode,
)
assert change is not None
return change
from elydora.plugins import _transaction
from elydora.plugins._managed_files import read_physical_file
from transaction_support import (
make_change,
transaction_subprocess_environment,
write_source,
)
def test_post_replace_precondition_failure_restores_original_target(
@@ -43,8 +25,8 @@ def test_post_replace_precondition_failure_restores_original_target(
) -> None:
target = tmp_path / "target.txt"
provider = tmp_path / "provider.txt"
_write(target, "old\n")
_write(provider, "stable\n")
write_source(target, "old\n")
write_source(provider, "stable\n")
provider_snapshot = read_physical_file(str(provider), "provider")
real_replace = _transaction._replace_physical
@@ -55,7 +37,7 @@ def test_post_replace_precondition_failure_restores_original_target(
) -> None:
real_replace(source, destination, directory)
if os.path.abspath(destination) == os.path.abspath(str(target)):
_write(provider, "concurrent\n")
write_source(provider, "concurrent\n")
monkeypatch.setattr(
_transaction, "_replace_physical", replace_then_change_provider
@@ -63,7 +45,7 @@ def test_post_replace_precondition_failure_restores_original_target(
with pytest.raises(OSError, match="provider changed during"):
_transaction.write_changes(
[_change(target, "old\n", "new\n")],
[make_change(target, "old\n", "new\n")],
"provider transaction",
[_transaction.FilePrecondition(str(provider), "provider", provider_snapshot)],
)
@@ -78,7 +60,7 @@ def test_commit_boundary_concurrent_write_is_restored_and_rejected(
tmp_path: Path,
) -> None:
target = tmp_path / "target.txt"
_write(target, "old\n")
write_source(target, "old\n")
real_capture = _transaction._capture_physical
changed = False
@@ -94,14 +76,14 @@ def test_commit_boundary_concurrent_write_is_restored_and_rejected(
and destination.endswith(".rollback")
):
changed = True
_write(target, "concurrent\n")
write_source(target, "concurrent\n")
real_capture(source, destination, directory)
monkeypatch.setattr(_transaction, "_capture_physical", write_before_capture)
with pytest.raises(OSError, match="changed at commit boundary"):
_transaction.write_changes(
[_change(target, "old\n", "new\n")],
[make_change(target, "old\n", "new\n")],
"commit boundary",
)
@@ -116,7 +98,7 @@ def test_existing_target_remains_visible_during_atomic_update(
tmp_path: Path,
) -> None:
target = tmp_path / "target.txt"
_write(target, "old\n")
write_source(target, "old\n")
replace_started = threading.Event()
release_replace = threading.Event()
failures: list[BaseException] = []
@@ -135,7 +117,7 @@ def test_existing_target_remains_visible_during_atomic_update(
def update() -> None:
try:
_transaction.write_changes(
[_change(target, "old\n", "new\n")],
[make_change(target, "old\n", "new\n")],
"visible atomic update",
)
except BaseException as error:
@@ -156,13 +138,6 @@ def test_existing_target_remains_visible_during_atomic_update(
assert target.read_text(encoding="utf-8") == "new\n"
def _transaction_subprocess_environment(home: Path) -> dict[str, str]:
environment = os.environ.copy()
environment["HOME"] = str(home)
environment["USERPROFILE"] = str(home)
return environment
def test_process_exit_after_atomic_replace_recovers_on_restart(
monkeypatch: pytest.MonkeyPatch,
tmp_path: Path,
@@ -170,7 +145,7 @@ def test_process_exit_after_atomic_replace_recovers_on_restart(
home = tmp_path / "home"
home.mkdir()
target = tmp_path / "target.txt"
_write(target, "old\n")
write_source(target, "old\n")
monkeypatch.setenv("HOME", str(home))
monkeypatch.setenv("USERPROFILE", str(home))
script = """
@@ -193,7 +168,7 @@ _transaction.write_changes([change], "crash recovery")
completed = subprocess.run(
[sys.executable, "-c", script, str(target)],
cwd=Path(__file__).resolve().parents[1],
env=_transaction_subprocess_environment(home),
env=transaction_subprocess_environment(home),
check=False,
)
@@ -212,7 +187,7 @@ def test_process_exit_after_committed_marker_keeps_atomic_update(
home = tmp_path / "home"
home.mkdir()
target = tmp_path / "target.txt"
_write(target, "old\n")
write_source(target, "old\n")
monkeypatch.setenv("HOME", str(home))
monkeypatch.setenv("USERPROFILE", str(home))
script = """
@@ -230,7 +205,7 @@ _transaction.write_changes([change], "committed crash recovery")
completed = subprocess.run(
[sys.executable, "-c", script, str(target)],
cwd=Path(__file__).resolve().parents[1],
env=_transaction_subprocess_environment(home),
env=transaction_subprocess_environment(home),
check=False,
)
@@ -250,8 +225,8 @@ def test_process_exit_mid_transaction_restores_all_targets(
home.mkdir()
first = tmp_path / "first.txt"
second = tmp_path / "second.txt"
_write(first, "first-old\n")
_write(second, "second-old\n")
write_source(first, "first-old\n")
write_source(second, "second-old\n")
monkeypatch.setenv("HOME", str(home))
monkeypatch.setenv("USERPROFILE", str(home))
script = """
@@ -281,7 +256,7 @@ _transaction.write_changes(changes, "multi-file crash recovery")
completed = subprocess.run(
[sys.executable, "-c", script, str(first), str(second)],
cwd=Path(__file__).resolve().parents[1],
env=_transaction_subprocess_environment(home),
env=transaction_subprocess_environment(home),
check=False,
)
@@ -313,7 +288,7 @@ def test_process_exit_recovers_atomic_create_and_remove(
home.mkdir()
target = tmp_path / "target.txt"
if original is not None:
_write(target, original)
write_source(target, original)
monkeypatch.setenv("HOME", str(home))
monkeypatch.setenv("USERPROFILE", str(home))
script = """
@@ -343,7 +318,7 @@ _transaction.write_changes([change], "create-remove crash recovery")
str(exit_code),
],
cwd=Path(__file__).resolve().parents[1],
env=_transaction_subprocess_environment(home),
env=transaction_subprocess_environment(home),
check=False,
)
@@ -366,8 +341,8 @@ def test_cross_process_writers_are_serialized(
ready = tmp_path / "ready"
second_ready = tmp_path / "second-ready"
release = tmp_path / "release"
_write(target, "old\n")
environment = _transaction_subprocess_environment(home)
write_source(target, "old\n")
environment = transaction_subprocess_environment(home)
first_script = """
import pathlib
import sys
@@ -443,632 +418,3 @@ except OSError as error:
assert second.returncode == 19, output
assert "changed during" in output
assert target.read_text(encoding="utf-8") == "first\n"
def test_final_replace_exception_after_success_restores_original(
monkeypatch: pytest.MonkeyPatch,
tmp_path: Path,
) -> None:
target = tmp_path / "target.txt"
_write(target, "old\n")
real_replace = _transaction._replace_physical
def replace_then_raise(
source: str,
destination: str,
directory: _transaction.PinnedDirectory,
) -> None:
real_replace(source, destination, directory)
if os.path.abspath(destination) == os.path.abspath(str(target)):
raise OSError("injected post-replace failure")
monkeypatch.setattr(_transaction, "_replace_physical", replace_then_raise)
with pytest.raises(OSError, match="injected post-replace failure"):
_transaction.write_changes(
[_change(target, "old\n", "new\n")],
"post-replace failure",
)
assert target.read_text(encoding="utf-8") == "old\n"
assert list(tmp_path.glob(".*.rollback")) == []
def test_capture_exception_after_success_restores_original(
monkeypatch: pytest.MonkeyPatch,
tmp_path: Path,
) -> None:
target = tmp_path / "target.txt"
_write(target, "old\n")
real_capture = _transaction._capture_physical
def capture_then_raise(
source: str,
destination: str,
directory: _transaction.PinnedDirectory,
) -> None:
real_capture(source, destination, directory)
raise OSError("injected post-capture failure")
monkeypatch.setattr(_transaction, "_capture_physical", capture_then_raise)
with pytest.raises(OSError, match="injected post-capture failure"):
_transaction.write_changes(
[_change(target, "old\n", "new\n")],
"post-capture failure",
)
assert target.read_text(encoding="utf-8") == "old\n"
assert list(tmp_path.glob(".*.rollback")) == []
def test_rollback_reservation_collision_preserves_victim_and_original(
monkeypatch: pytest.MonkeyPatch,
tmp_path: Path,
) -> None:
target = tmp_path / "target.txt"
_write(target, "old\n")
victim = "concurrent rollback destination\n"
collision_path: Path | None = None
def collide_with_rollback_destination(
_source: str,
destination: str,
_directory: _transaction.PinnedDirectory,
) -> None:
nonlocal collision_path
if destination.endswith(".rollback"):
collision_path = Path(destination)
_write(collision_path, victim)
monkeypatch.setattr(
_transaction,
"_capture_physical",
collide_with_rollback_destination,
)
with pytest.raises(OSError, match="exist"):
_transaction.write_changes(
[_change(target, "old\n", "new\n")],
"rollback destination collision",
)
assert collision_path is not None
assert target.read_text(encoding="utf-8") == "old\n"
assert collision_path.read_text(encoding="utf-8") == victim
def test_final_install_does_not_overwrite_concurrent_target(
monkeypatch: pytest.MonkeyPatch,
tmp_path: Path,
) -> None:
target = tmp_path / "target.txt"
_write(target, "old\n")
real_replace = _transaction._replace_physical
injected = False
def create_target_before_install(
source: str,
destination: str,
directory: _transaction.PinnedDirectory,
) -> None:
nonlocal injected
if not injected and os.path.abspath(destination) == os.path.abspath(target):
injected = True
_write(target, "concurrent\n")
real_replace(source, destination, directory)
monkeypatch.setattr(
_transaction,
"_replace_physical",
create_target_before_install,
)
with pytest.raises(OSError, match="original content preserved"):
_transaction.write_changes(
[_change(target, "old\n", "new\n")],
"no-clobber install",
)
assert injected
assert target.read_text(encoding="utf-8") == "concurrent\n"
rollbacks = list(tmp_path.glob(".*.rollback"))
assert len(rollbacks) == 1
assert rollbacks[0].read_text(encoding="utf-8") == "old\n"
def test_restore_does_not_overwrite_concurrent_target(
monkeypatch: pytest.MonkeyPatch,
tmp_path: Path,
) -> None:
target = tmp_path / "target.txt"
second = tmp_path / "second.txt"
_write(target, "old\n")
_write(second, "second-old\n")
real_replace = _transaction._replace_physical
real_restore = _transaction._restore_physical
injected = False
def fail_second_commit(
source: str,
destination: str,
directory: _transaction.PinnedDirectory,
) -> None:
if os.path.abspath(destination) == os.path.abspath(second):
raise OSError("injected second failure")
real_replace(source, destination, directory)
def create_target_before_restore(
source: str,
destination: str,
directory: _transaction.PinnedDirectory,
) -> None:
nonlocal injected
if not injected and os.path.abspath(destination) == os.path.abspath(target):
injected = True
_write(target, "concurrent recovery\n")
real_restore(source, destination, directory)
monkeypatch.setattr(_transaction, "_replace_physical", fail_second_commit)
monkeypatch.setattr(
_transaction,
"_restore_physical",
create_target_before_restore,
)
with pytest.raises(OSError, match="original content preserved"):
_transaction.write_changes(
[
_change(target, "old\n", "new\n"),
_change(second, "second-old\n", "second-new\n"),
],
"no-clobber restoration",
)
assert injected
assert target.read_text(encoding="utf-8") == "concurrent recovery\n"
rollbacks = list(tmp_path.glob(".*.rollback"))
assert len(rollbacks) == 1
assert rollbacks[0].read_text(encoding="utf-8") == "old\n"
assert Path(_transaction.active_journal_path()).is_file()
_transaction.clear_journal()
def test_restore_source_replacement_reports_existing_preserved_path(
monkeypatch: pytest.MonkeyPatch,
tmp_path: Path,
) -> None:
target = tmp_path / "target.txt"
second = tmp_path / "second.txt"
displaced = tmp_path / "displaced-original.txt"
_write(target, "old\n")
_write(second, "second-old\n")
real_replace = _transaction._replace_physical
real_restore = _transaction._restore_physical
rollback_path: Path | None = None
def fail_second_commit(
source: str,
destination: str,
directory: _transaction.PinnedDirectory,
) -> None:
if os.path.abspath(destination) == os.path.abspath(second):
raise OSError("injected second failure")
real_replace(source, destination, directory)
def replace_restore_source(
source: str,
destination: str,
directory: _transaction.PinnedDirectory,
) -> None:
nonlocal rollback_path
if (
rollback_path is None
and source.endswith(".rollback")
and os.path.abspath(destination) == os.path.abspath(target)
):
rollback_path = Path(source)
rollback_path.rename(displaced)
_write(rollback_path, "concurrent recovery source\n")
real_restore(source, destination, directory)
monkeypatch.setattr(_transaction, "_replace_physical", fail_second_commit)
monkeypatch.setattr(_transaction, "_restore_physical", replace_restore_source)
with pytest.raises(OSError, match="recovery source changed") as raised:
_transaction.write_changes(
[
_change(target, "old\n", "new\n"),
_change(second, "second-old\n", "second-new\n"),
],
"restore source replacement",
)
assert rollback_path is not None
assert f"concurrent data preserved at {rollback_path}" in str(raised.value)
assert rollback_path.read_text(encoding="utf-8") == "concurrent recovery source\n"
assert displaced.read_text(encoding="utf-8") == "old\n"
assert target.read_text(encoding="utf-8") == "new\n"
assert second.read_text(encoding="utf-8") == "second-old\n"
assert Path(_transaction.active_journal_path()).is_file()
_transaction.clear_journal()
def test_creation_rollback_captures_and_preserves_concurrent_replacement(
monkeypatch: pytest.MonkeyPatch,
tmp_path: Path,
) -> None:
created = tmp_path / "created.txt"
second = tmp_path / "second.txt"
_write(second, "second-old\n")
real_replace = _transaction._replace_physical
real_capture = _transaction._capture_physical
injected = False
def fail_second_commit(
source: str,
destination: str,
directory: _transaction.PinnedDirectory,
) -> None:
if os.path.abspath(destination) == os.path.abspath(second):
raise OSError("injected second failure")
real_replace(source, destination, directory)
def replace_created_before_recovery_capture(
source: str,
destination: str,
directory: _transaction.PinnedDirectory,
) -> None:
nonlocal injected
if (
not injected
and os.path.abspath(source) == os.path.abspath(created)
and destination.endswith(".recovery")
):
injected = True
_write(created, "concurrent replacement\n")
real_capture(source, destination, directory)
monkeypatch.setattr(_transaction, "_replace_physical", fail_second_commit)
monkeypatch.setattr(
_transaction,
"_capture_physical",
replace_created_before_recovery_capture,
)
with pytest.raises(OSError, match="changed at recovery boundary"):
_transaction.write_changes(
[
_change(created, None, "generated\n"),
_change(second, "second-old\n", "second-new\n"),
],
"creation recovery capture",
)
assert injected
assert created.read_text(encoding="utf-8") == "concurrent replacement\n"
assert Path(_transaction.active_journal_path()).is_file()
_transaction.clear_journal()
@pytest.mark.skipif(os.name == "nt", reason="POSIX mode contract")
def test_mode_repair_uses_private_rollback_and_restores_original_mode(
monkeypatch: pytest.MonkeyPatch,
tmp_path: Path,
) -> None:
secret = tmp_path / "private.key"
second = tmp_path / "config.json"
_write(secret, "secret\n")
_write(second, "old\n")
secret.chmod(0o644)
real_replace = _transaction._replace_physical
rollback_modes: list[int] = []
def inspect_then_fail_second(
source: str,
destination: str,
directory: _transaction.PinnedDirectory,
) -> None:
if os.path.abspath(destination) == os.path.abspath(str(secret)):
rollback = next(tmp_path.glob(".private.key.*.rollback"))
rollback_modes.append(rollback.stat().st_mode & 0o777)
if os.path.abspath(destination) == os.path.abspath(str(second)):
raise OSError("injected second commit failure")
real_replace(source, destination, directory)
monkeypatch.setattr(_transaction, "_replace_physical", inspect_then_fail_second)
with pytest.raises(OSError, match="injected second commit failure"):
_transaction.write_changes(
[
_change(secret, "secret\n", "secret\n", enforce_mode=True),
_change(second, "old\n", "new\n"),
],
"mode repair",
)
assert rollback_modes
assert set(rollback_modes) == {0o600}
assert secret.read_text(encoding="utf-8") == "secret\n"
assert secret.stat().st_mode & 0o777 == 0o644
assert second.read_text(encoding="utf-8") == "old\n"
assert list(tmp_path.glob(".*.rollback")) == []
@pytest.mark.skipif(os.name == "nt", reason="POSIX mode contract")
def test_deletion_uses_private_rollback_and_restores_original_mode(
monkeypatch: pytest.MonkeyPatch,
tmp_path: Path,
) -> None:
removed = tmp_path / "removed.txt"
second = tmp_path / "second.txt"
_write(removed, "remove me\n")
_write(second, "old\n")
removed.chmod(0o644)
real_replace = _transaction._replace_physical
rollback_modes: list[int] = []
def inspect_then_fail_second(
source: str,
destination: str,
directory: _transaction.PinnedDirectory,
) -> None:
if os.path.abspath(destination) == os.path.abspath(str(second)):
rollback = next(tmp_path.glob(".removed.txt.*.rollback"))
rollback_modes.append(rollback.stat().st_mode & 0o777)
raise OSError("injected second commit failure")
real_replace(source, destination, directory)
monkeypatch.setattr(_transaction, "_replace_physical", inspect_then_fail_second)
with pytest.raises(OSError, match="injected second commit failure"):
_transaction.write_changes(
[
_change(removed, "remove me\n", None),
_change(second, "old\n", "new\n"),
],
"deletion rollback mode",
)
assert rollback_modes == [0o600]
assert removed.read_text(encoding="utf-8") == "remove me\n"
assert removed.stat().st_mode & 0o777 == 0o644
assert second.read_text(encoding="utf-8") == "old\n"
assert list(tmp_path.glob(".*.rollback")) == []
def _swap_directory(parent: Path, backup: Path, redirected: Path) -> None:
parent.rename(backup)
redirected.rename(parent)
@pytest.mark.skipif(os.name == "nt", reason="Windows handle blocks directory rename")
def test_staging_directory_swap_never_writes_secret_to_redirected_path(
monkeypatch: pytest.MonkeyPatch,
tmp_path: Path,
) -> None:
parent = tmp_path / "runtime"
backup = tmp_path / "runtime-original"
redirected = tmp_path / "runtime-redirected"
parent.mkdir()
redirected.mkdir()
target = parent / "private.key"
real_write = _transaction_staging.write_staged
swapped = False
def swap_then_stage(*args: object, **kwargs: object):
nonlocal swapped
if not swapped:
swapped = True
_swap_directory(parent, backup, redirected)
return real_write(*args, **kwargs)
monkeypatch.setattr(_transaction_staging, "write_staged", swap_then_stage)
with pytest.raises(OSError, match="changed during transaction"):
_transaction.write_changes(
[_change(target, None, "secret\n")],
"staging swap",
)
assert not (parent / "private.key").exists()
assert not (backup / "private.key").exists()
assert list(parent.iterdir()) == []
assert list(backup.iterdir()) == []
@pytest.mark.skipif(os.name == "nt", reason="Windows handle blocks directory rename")
def test_commit_directory_swap_rolls_back_inside_pinned_directory(
monkeypatch: pytest.MonkeyPatch,
tmp_path: Path,
) -> None:
parent = tmp_path / "runtime"
backup = tmp_path / "runtime-original"
redirected = tmp_path / "runtime-redirected"
parent.mkdir()
redirected.mkdir()
target = parent / "private.key"
real_replace = _transaction._replace_physical
swapped = False
def swap_then_replace(
source: str,
destination: str,
directory: _transaction.PinnedDirectory,
) -> None:
nonlocal swapped
if not swapped:
swapped = True
_swap_directory(parent, backup, redirected)
real_replace(source, destination, directory)
monkeypatch.setattr(_transaction, "_replace_physical", swap_then_replace)
with pytest.raises(OSError, match="changed during transaction"):
_transaction.write_changes(
[_change(target, None, "secret\n")],
"commit swap",
)
assert not (parent / "private.key").exists()
assert not (backup / "private.key").exists()
assert list(parent.iterdir()) == []
assert list(backup.iterdir()) == []
def test_failed_transaction_removes_only_created_empty_directories(
monkeypatch: pytest.MonkeyPatch,
tmp_path: Path,
) -> None:
kiro = tmp_path / ".kiro"
hooks = kiro / "hooks"
hooks.mkdir(parents=True)
kiro_snapshot = read_physical_directory(str(kiro), "Kiro directory")
hooks_snapshot = read_physical_directory(str(hooks), "hooks directory")
target = hooks / "elydora-audit.json"
def fail_replace(
_source: str,
_destination: str,
_directory: _transaction.PinnedDirectory,
) -> None:
raise OSError("injected commit failure")
monkeypatch.setattr(_transaction, "_replace_physical", fail_replace)
with pytest.raises(OSError, match="injected commit failure"):
_transaction.write_changes(
[_change(target, None, "{}\n")],
"created directory recovery",
directory_preconditions=[
_transaction.DirectoryPrecondition(
str(kiro), "Kiro directory", kiro_snapshot, created=True
),
_transaction.DirectoryPrecondition(
str(hooks), "hooks directory", hooks_snapshot, created=True
),
],
)
assert not hooks.exists()
assert not kiro.exists()
def test_created_directory_recovery_keeps_parent_and_child_pinned(
monkeypatch: pytest.MonkeyPatch,
tmp_path: Path,
) -> None:
kiro = tmp_path / ".kiro"
hooks = kiro / "hooks"
hooks.mkdir(parents=True)
kiro_snapshot = read_physical_directory(str(kiro), "Kiro directory")
hooks_snapshot = read_physical_directory(str(hooks), "hooks directory")
target = hooks / "elydora-audit.json"
observed: list[tuple[Path, Path]] = []
real_remove = _transaction_staging.remove_pinned_empty_directory
def observe_pins(
parent: object,
child: object,
*,
require_empty: bool = True,
) -> None:
assert isinstance(parent, _transaction.PinnedDirectory)
assert isinstance(child, _transaction.PinnedDirectory)
assert parent.descriptor is not None
assert child.descriptor is not None
assert os.fstat(child.descriptor).st_ino == child.snapshot.inode
observed.append((Path(parent.path), Path(child.path)))
real_remove(parent, child, require_empty=require_empty)
def fail_replace(
_source: str,
_destination: str,
_directory: _transaction.PinnedDirectory,
) -> None:
raise OSError("injected pinned cleanup failure")
monkeypatch.setattr(
_transaction_staging,
"remove_pinned_empty_directory",
observe_pins,
)
monkeypatch.setattr(_transaction, "_replace_physical", fail_replace)
with pytest.raises(OSError, match="injected pinned cleanup failure"):
_transaction.write_changes(
[_change(target, None, "{}\n")],
"pinned directory recovery",
directory_preconditions=[
_transaction.DirectoryPrecondition(
str(kiro), "Kiro directory", kiro_snapshot, created=True
),
_transaction.DirectoryPrecondition(
str(hooks), "hooks directory", hooks_snapshot, created=True
),
],
)
expected = [(kiro, hooks), (tmp_path, kiro)]
assert observed == expected
assert not kiro.exists()
@pytest.mark.skipif(os.name == "nt", reason="Windows handle blocks directory rename")
def test_created_directory_recovery_preserves_swapped_directory(
monkeypatch: pytest.MonkeyPatch,
tmp_path: Path,
) -> None:
created = tmp_path / "created"
created.mkdir()
created_snapshot = read_physical_directory(str(created), "created directory")
target = created / "managed.json"
replacement = tmp_path / "replacement"
replacement.mkdir()
marker = replacement / "user-owned"
marker.write_text("preserve", encoding="utf-8")
original = tmp_path / "created-original"
real_remove = _transaction_staging.remove_pinned_empty_directory
def swap_before_removal(
parent: _transaction.PinnedDirectory,
child: _transaction.PinnedDirectory,
*,
require_empty: bool = True,
) -> None:
created.rename(original)
replacement.rename(created)
real_remove(parent, child, require_empty=require_empty)
def fail_replace(
_source: str,
_destination: str,
_directory: _transaction.PinnedDirectory,
) -> None:
raise OSError("injected swap cleanup failure")
monkeypatch.setattr(
_transaction_staging,
"remove_pinned_empty_directory",
swap_before_removal,
)
monkeypatch.setattr(_transaction, "_replace_physical", fail_replace)
with pytest.raises(OSError, match="recovery failed"):
_transaction.write_changes(
[_change(target, None, "{}\n")],
"swapped directory recovery",
directory_preconditions=[
_transaction.DirectoryPrecondition(
str(created),
"created directory",
created_snapshot,
created=True,
)
],
)
assert (created / marker.name).read_text(encoding="utf-8") == "preserve"
assert original.is_dir()
assert Path(_transaction.active_journal_path()).is_file()
_transaction.clear_journal()