42 lines
1.2 KiB
Python
42 lines
1.2 KiB
Python
"""Atomic hot-state writes: crash never corrupts; temp never leaks."""
|
|
|
|
from pathlib import Path
|
|
|
|
from rune.orchestration.atomic import AtomicStore, atomic_write
|
|
|
|
|
|
def test_write_then_read_roundtrips(tmp_path: Path):
|
|
store = AtomicStore(tmp_path / "state")
|
|
store.write("cursor.json", '{"offset": 7}')
|
|
assert store.read("cursor.json") == '{"offset": 7}'
|
|
|
|
|
|
def test_no_temp_file_left_behind_after_success(tmp_path: Path):
|
|
store = AtomicStore(tmp_path / "state")
|
|
store.write("cursor.json", "x")
|
|
leftovers = [p for p in (tmp_path / "state").iterdir() if p.name != "cursor.json"]
|
|
assert leftovers == []
|
|
|
|
|
|
def test_failed_writer_leaves_original_intact(tmp_path: Path):
|
|
path = tmp_path / "f.json"
|
|
atomic_write(path, lambda fh: fh.write("original"))
|
|
|
|
def boom(fh):
|
|
fh.write("corrupt")
|
|
raise RuntimeError("boom")
|
|
|
|
try:
|
|
atomic_write(path, boom)
|
|
raise AssertionError("expected RuntimeError")
|
|
except RuntimeError:
|
|
pass
|
|
assert path.read_text() == "original"
|
|
# no temp file remains
|
|
assert [p.name for p in tmp_path.iterdir()] == ["f.json"]
|
|
|
|
|
|
def test_read_missing_returns_none(tmp_path: Path):
|
|
store = AtomicStore(tmp_path / "state")
|
|
assert store.read("missing.json") is None
|