sdks/python: mirror Elydora-Python-SDK 2.0.2
Rebuilt from the standalone 54e65eb tree (durable transactions, Kiro IDE adapter, hook retry with a bounded chain lock, version 2.0.2) with the Open-Source API trims re-applied: no token rotation, token issuance, or agent integration updates, and the mirror's registration contract and OpenCode plugin tests kept. Claude-Session: https://claude.ai/code/session_01QoUjdc7HGKTsgj5GhG8HvF
This commit is contained in:
1 parent
b00932d047
commit
1656a94811
56 files changed
+12203
-588
No files matched your search
@@ -0,0 +1,137 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from elydora.plugins import _transaction
|
||||
from elydora.plugins._pinned_directory import PinnedDirectory
|
||||
from elydora.plugins._transaction_journal import active_journal_path
|
||||
|
||||
|
||||
def _change(path: Path, original: str, next_source: str) -> _transaction.FileChange:
|
||||
change = _transaction.source_change(
|
||||
str(path),
|
||||
path.name,
|
||||
original,
|
||||
next_source,
|
||||
0o600,
|
||||
)
|
||||
assert change is not None
|
||||
return change
|
||||
|
||||
|
||||
def _no_op_file_boundary(
|
||||
source: str,
|
||||
destination: str,
|
||||
directory: PinnedDirectory,
|
||||
) -> None:
|
||||
del source, destination, directory
|
||||
|
||||
|
||||
def test_durable_commit_marker_keeps_change_after_post_commit_failure(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
tmp_path: Path,
|
||||
) -> None:
|
||||
target = tmp_path / "managed.json"
|
||||
target.write_bytes(b"old\n")
|
||||
|
||||
def fail_after_marker() -> None:
|
||||
raise OSError("injected post-commit failure")
|
||||
|
||||
monkeypatch.setattr(_transaction, "_after_journal_commit", fail_after_marker)
|
||||
|
||||
with pytest.raises(OSError, match="committed before post-commit failure"):
|
||||
_transaction.write_changes(
|
||||
[_change(target, "old\n", "new\n")],
|
||||
"post-commit boundary",
|
||||
)
|
||||
|
||||
assert target.read_text(encoding="utf-8") == "new\n"
|
||||
assert not Path(active_journal_path()).exists()
|
||||
assert not list(tmp_path.glob(".*.rollback"))
|
||||
|
||||
|
||||
def test_failed_in_process_recovery_preserves_journal_for_restart(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
tmp_path: Path,
|
||||
) -> None:
|
||||
first = tmp_path / "first.json"
|
||||
second = tmp_path / "second.json"
|
||||
first.write_bytes(b"first-old\n")
|
||||
second.write_bytes(b"second-old\n")
|
||||
|
||||
def fail_second_commit(
|
||||
source: str,
|
||||
destination: str,
|
||||
directory: PinnedDirectory,
|
||||
) -> None:
|
||||
del source, directory
|
||||
if Path(destination) == second:
|
||||
raise OSError("injected second commit failure")
|
||||
|
||||
def fail_first_restore(
|
||||
source: str,
|
||||
destination: str,
|
||||
directory: PinnedDirectory,
|
||||
) -> None:
|
||||
del source, destination, directory
|
||||
raise OSError("injected restoration failure")
|
||||
|
||||
monkeypatch.setattr(_transaction, "_replace_physical", fail_second_commit)
|
||||
monkeypatch.setattr(_transaction, "_restore_physical", fail_first_restore)
|
||||
|
||||
with pytest.raises(OSError, match="recovery journal preserved"):
|
||||
_transaction.write_changes(
|
||||
[
|
||||
_change(first, "first-old\n", "first-new\n"),
|
||||
_change(second, "second-old\n", "second-new\n"),
|
||||
],
|
||||
"recoverable in-process failure",
|
||||
)
|
||||
|
||||
assert Path(active_journal_path()).is_file()
|
||||
assert first.read_text(encoding="utf-8") == "first-new\n"
|
||||
assert second.read_text(encoding="utf-8") == "second-old\n"
|
||||
|
||||
monkeypatch.setattr(_transaction, "_restore_physical", _no_op_file_boundary)
|
||||
_transaction.recover_pending_transactions()
|
||||
|
||||
assert first.read_text(encoding="utf-8") == "first-old\n"
|
||||
assert second.read_text(encoding="utf-8") == "second-old\n"
|
||||
assert not Path(active_journal_path()).exists()
|
||||
|
||||
|
||||
def test_committed_cleanup_failure_leaves_restart_record(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
tmp_path: Path,
|
||||
) -> None:
|
||||
target = tmp_path / "managed.json"
|
||||
target.write_bytes(b"old\n")
|
||||
real_cleanup = _transaction.cleanup_staged
|
||||
calls = 0
|
||||
|
||||
def fail_first_cleanup(staged: _transaction.StagedChange) -> None:
|
||||
nonlocal calls
|
||||
calls += 1
|
||||
if calls == 1:
|
||||
raise OSError("injected committed cleanup failure")
|
||||
real_cleanup(staged)
|
||||
|
||||
monkeypatch.setattr(_transaction, "cleanup_staged", fail_first_cleanup)
|
||||
|
||||
with pytest.raises(OSError, match="cleanup pending"):
|
||||
_transaction.write_changes(
|
||||
[_change(target, "old\n", "new\n")],
|
||||
"committed cleanup failure",
|
||||
)
|
||||
|
||||
assert target.read_text(encoding="utf-8") == "new\n"
|
||||
assert Path(active_journal_path()).is_file()
|
||||
|
||||
monkeypatch.setattr(_transaction, "cleanup_staged", real_cleanup)
|
||||
_transaction.recover_pending_transactions()
|
||||
|
||||
assert target.read_text(encoding="utf-8") == "new\n"
|
||||
assert not Path(active_journal_path()).exists()
|
||||
assert not list(tmp_path.glob(".*.rollback"))
|
||||
Reference in new issue
Block a user