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,169 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
from pathlib import Path
|
||||
import stat
|
||||
import subprocess # nosec B404
|
||||
import sys
|
||||
import time
|
||||
|
||||
import pytest
|
||||
|
||||
from elydora.plugins import _transaction_lock
|
||||
|
||||
|
||||
if os.name == "nt":
|
||||
from elydora.plugins._windows_security import verify_private_path
|
||||
|
||||
|
||||
def _use_isolated_state(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
root: Path,
|
||||
) -> None:
|
||||
monkeypatch.setenv("HOME", str(root))
|
||||
monkeypatch.setenv("USERPROFILE", str(root))
|
||||
|
||||
|
||||
def test_transaction_state_directory_has_owner_boundary(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
tmp_path: Path,
|
||||
) -> None:
|
||||
_use_isolated_state(monkeypatch, tmp_path)
|
||||
|
||||
state = Path(_transaction_lock.transaction_state_directory())
|
||||
|
||||
assert state.is_dir()
|
||||
if os.name == "nt":
|
||||
verify_private_path(str(state), directory=True)
|
||||
else:
|
||||
metadata = state.stat()
|
||||
assert metadata.st_uid == getattr(os, "geteuid")()
|
||||
assert stat.S_IMODE(metadata.st_mode) == 0o700
|
||||
|
||||
|
||||
@pytest.mark.skipif(os.name != "posix", reason="POSIX owner contract")
|
||||
def test_transaction_state_directory_rejects_foreign_owner(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
tmp_path: Path,
|
||||
) -> None:
|
||||
_use_isolated_state(monkeypatch, tmp_path)
|
||||
state = Path(_transaction_lock.transaction_state_directory())
|
||||
owner = state.stat().st_uid
|
||||
monkeypatch.setattr(os, "geteuid", lambda: owner + 1)
|
||||
|
||||
with pytest.raises(PermissionError, match="owned by the current user"):
|
||||
_transaction_lock.transaction_state_directory()
|
||||
|
||||
|
||||
@pytest.mark.skipif(os.name != "posix", reason="POSIX owner contract")
|
||||
def test_posix_lock_rejects_foreign_owner(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
tmp_path: Path,
|
||||
) -> None:
|
||||
lock_path = tmp_path / "transaction.lock"
|
||||
lock_path.write_bytes(b"")
|
||||
owner = lock_path.stat().st_uid
|
||||
monkeypatch.setattr(
|
||||
_transaction_lock,
|
||||
"transaction_state_directory",
|
||||
lambda: str(tmp_path),
|
||||
)
|
||||
monkeypatch.setattr(os, "geteuid", lambda: owner + 1)
|
||||
|
||||
with pytest.raises(PermissionError, match="owned by the current user"):
|
||||
_transaction_lock._PosixLock().__enter__()
|
||||
|
||||
|
||||
def test_transaction_lock_rejects_reparse_path(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
tmp_path: Path,
|
||||
) -> None:
|
||||
_use_isolated_state(monkeypatch, tmp_path)
|
||||
state = Path(_transaction_lock.transaction_state_directory())
|
||||
target = tmp_path / "target.lock"
|
||||
target.write_bytes(b"")
|
||||
lock_path = state / "transaction.lock"
|
||||
try:
|
||||
lock_path.symlink_to(target)
|
||||
except OSError as error:
|
||||
pytest.skip(f"Symbolic links unavailable: {error}")
|
||||
|
||||
with pytest.raises(OSError, match="physical file"):
|
||||
with _transaction_lock.serialized_transactions():
|
||||
pass
|
||||
|
||||
|
||||
@pytest.mark.skipif(os.name != "nt", reason="Windows DACL contract")
|
||||
def test_windows_lock_file_has_owner_only_protected_dacl(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
tmp_path: Path,
|
||||
) -> None:
|
||||
_use_isolated_state(monkeypatch, tmp_path)
|
||||
state = Path(_transaction_lock.transaction_state_directory())
|
||||
|
||||
with _transaction_lock.serialized_transactions():
|
||||
lock_path = state / "transaction.lock"
|
||||
assert lock_path.is_file()
|
||||
verify_private_path(str(lock_path), directory=False)
|
||||
|
||||
|
||||
def test_transaction_lock_serializes_independent_processes(tmp_path: Path) -> None:
|
||||
state_root = tmp_path / "state-root"
|
||||
state_root.mkdir()
|
||||
entered = tmp_path / "entered"
|
||||
release = tmp_path / "release"
|
||||
second_entered = tmp_path / "second-entered"
|
||||
script = """
|
||||
import pathlib
|
||||
import sys
|
||||
import time
|
||||
from elydora.plugins._transaction_lock import serialized_transactions
|
||||
|
||||
entered, release = map(pathlib.Path, sys.argv[1:3])
|
||||
with serialized_transactions():
|
||||
entered.write_text("entered", encoding="utf-8")
|
||||
if len(sys.argv) == 4:
|
||||
deadline = time.monotonic() + 10
|
||||
while not release.exists():
|
||||
if time.monotonic() >= deadline:
|
||||
raise TimeoutError("release gate timed out")
|
||||
time.sleep(0.01)
|
||||
"""
|
||||
environment = os.environ.copy()
|
||||
environment["HOME"] = str(state_root)
|
||||
environment["USERPROFILE"] = str(state_root)
|
||||
repository = Path(__file__).resolve().parents[1]
|
||||
first = subprocess.Popen( # nosec B603
|
||||
[sys.executable, "-c", script, str(entered), str(release), "wait"],
|
||||
cwd=repository,
|
||||
env=environment,
|
||||
)
|
||||
second: subprocess.Popen[bytes] | None = None
|
||||
try:
|
||||
deadline = time.monotonic() + 10
|
||||
while not entered.exists() and first.poll() is None:
|
||||
if time.monotonic() >= deadline:
|
||||
raise TimeoutError("first process did not acquire the lock")
|
||||
time.sleep(0.01)
|
||||
assert first.poll() is None
|
||||
|
||||
second = subprocess.Popen( # nosec B603
|
||||
[sys.executable, "-c", script, str(second_entered), str(release)],
|
||||
cwd=repository,
|
||||
env=environment,
|
||||
)
|
||||
time.sleep(0.25)
|
||||
assert second.poll() is None
|
||||
assert not second_entered.exists()
|
||||
|
||||
release.write_text("release", encoding="utf-8")
|
||||
assert first.wait(timeout=10) == 0
|
||||
assert second.wait(timeout=10) == 0
|
||||
assert second_entered.read_text(encoding="utf-8") == "entered"
|
||||
finally:
|
||||
if first.poll() is None:
|
||||
first.kill()
|
||||
first.wait()
|
||||
if second is not None and second.poll() is None:
|
||||
second.kill()
|
||||
second.wait()
|
||||
Reference in new issue
Block a user