test(wal): cover crash-safety, idempotent setup, and redaction edge paths

The write-ahead log gained its own module in v3.5.0 but sat at 82% coverage;
the uncovered lines were exactly the failure/guard branches that uphold its
contracts: the cache-hit early return, the restricted-FS chmod/mkdir swallow
paths, and the promise that a WAL write failure is logged and never crashes
the calling tool. Add five tests covering those branches plus the non-string
redaction marker, bringing mempalace/wal.py to 100% and locking the
crash-safety guarantees against regression. Test-only; no production change.
This commit is contained in:
Arnold Wender 2026-06-24 22:58:08 +02:00
parent 6ea60c6043
commit c80f6537ec
1 changed files with 128 additions and 0 deletions

View File

@ -40,3 +40,131 @@ def test_wal_log_redacts_and_writes(tmp_path, monkeypatch):
assert entry["operation"] == "op"
assert entry["params"]["entry"].startswith("[REDACTED")
assert entry["params"]["safe"] == "ok"
def test_wal_ensure_is_idempotent_and_cached(tmp_path, monkeypatch):
"""_ensure_wal hardens the dir once, then short-circuits on the cached path.
Covers the cache-hit early return (wal.py:58): once _WAL_INITIALIZED_DIR
matches the WAL dir, a second call must not touch the filesystem again. This
is what stops a persistent chmod failure on a restricted FS from being
retried on every single write.
"""
from pathlib import Path
from mempalace import wal
wal_dir = tmp_path / "wal"
wal_dir.mkdir()
monkeypatch.setattr(wal, "_WAL_FILE", wal_dir / "write_log.jsonl")
monkeypatch.setattr(wal, "_WAL_INITIALIZED_DIR", None)
wal._ensure_wal()
assert wal._WAL_INITIALIZED_DIR == wal_dir
# After caching, a second call must return before reaching any chmod/mkdir.
def _boom(self, *args, **kwargs):
raise AssertionError("filesystem touched again after dir was cached")
monkeypatch.setattr(Path, "chmod", _boom)
monkeypatch.setattr(Path, "mkdir", _boom)
wal._ensure_wal() # must hit the cached early-return, not raise
def test_wal_log_never_raises_when_write_fails(tmp_path, monkeypatch, caplog):
"""A WAL write failure is logged and swallowed, never crashing the caller.
Covers wal.py:96-97 the module docstring and _wal_log both promise that
any WAL failure is non-fatal, so a tool call is never broken by audit-log
I/O (e.g. a full disk or a read-only filesystem).
"""
import logging
from mempalace import wal
monkeypatch.setattr(wal, "_WAL_FILE", tmp_path / "wal" / "write_log.jsonl")
monkeypatch.setattr(wal, "_WAL_INITIALIZED_DIR", None)
def _boom(*args, **kwargs):
raise OSError("no space left on device")
monkeypatch.setattr(wal.os, "open", _boom)
with caplog.at_level(logging.ERROR, logger="mempalace.wal"):
wal._wal_log("add_drawer", {"safe": "ok"}) # must not raise
assert any("WAL write failed" in r.getMessage() for r in caplog.records)
def test_wal_ensure_swallows_chmod_failure_on_existing_dir(tmp_path, monkeypatch):
"""A denied chmod on an existing dir is swallowed and the dir still caches.
Covers wal.py:67-68 the WAL dir already exists (so no FileNotFoundError),
but chmod is denied (restricted FS). _ensure_wal must not raise and must
cache the dir so the failing chmod is not retried on every write.
"""
from pathlib import Path
from mempalace import wal
wal_dir = tmp_path / "wal"
wal_dir.mkdir()
monkeypatch.setattr(wal, "_WAL_FILE", wal_dir / "write_log.jsonl")
monkeypatch.setattr(wal, "_WAL_INITIALIZED_DIR", None)
def _denied(self, *args, **kwargs):
raise OSError("operation not permitted")
monkeypatch.setattr(Path, "chmod", _denied)
wal._ensure_wal() # must not raise
assert wal._WAL_INITIALIZED_DIR == wal_dir
def test_wal_ensure_swallows_mkdir_failure(tmp_path, monkeypatch):
"""A failed fallback mkdir is swallowed and the dir still caches.
Covers wal.py:65-66 chmod raises FileNotFoundError (dir absent), the
fallback mkdir then fails too (read-only parent). _ensure_wal must not raise.
"""
from pathlib import Path
from mempalace import wal
wal_dir = tmp_path / "missing" / "wal"
monkeypatch.setattr(wal, "_WAL_FILE", wal_dir / "write_log.jsonl")
monkeypatch.setattr(wal, "_WAL_INITIALIZED_DIR", None)
def _not_found(self, *args, **kwargs):
raise FileNotFoundError
def _mkdir_denied(self, *args, **kwargs):
raise OSError("read-only file system")
monkeypatch.setattr(Path, "chmod", _not_found)
monkeypatch.setattr(Path, "mkdir", _mkdir_denied)
wal._ensure_wal() # must not raise
assert wal._WAL_INITIALIZED_DIR == wal_dir
def test_wal_log_redacts_non_string_values(tmp_path, monkeypatch):
"""Non-string values under a redact key use the plain [REDACTED] marker.
Covers the else-branch of the redaction ternary (wal.py:80): only str values
get the "[REDACTED N chars]" form; any other type is fully redacted without
calling len() on it.
"""
import json
from mempalace import wal
wal_file = tmp_path / "wal" / "write_log.jsonl"
monkeypatch.setattr(wal, "_WAL_FILE", wal_file)
monkeypatch.setattr(wal, "_WAL_INITIALIZED_DIR", None)
wal._wal_log("kg_add", {"document": [1, 2, 3], "safe": "ok"})
entry = json.loads(wal_file.read_text().strip())
assert entry["params"]["document"] == "[REDACTED]"
assert entry["params"]["safe"] == "ok"