Import AITURK IDE 1.0.0-beta.1 from Hermes 63279301; preserve MIT license
This commit is contained in:
@@ -0,0 +1,141 @@
|
||||
"""Cell 1 — prefix continuation + recovery determinism under real SIGKILL.
|
||||
|
||||
Contract clause (arXiv:2608.03836): every append the store *acknowledged*
|
||||
survives a hard crash; recovery yields a contiguous prefix (no holes, no
|
||||
duplicates); two independent recovery passes see the identical transcript.
|
||||
|
||||
Adapted from the spot-probe in the tracking issue (#80921), which ran ~29.5K
|
||||
messages to a SIGKILL with zero lost-after-return. Scaled down for CI: the
|
||||
parent kills the writer once >= 200 acknowledged appends are journaled — the
|
||||
property assertions are identical, only the exposure window is shorter.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import sqlite3
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from tests.conformance.persistence._harness import (
|
||||
effective_mode_or_skip,
|
||||
integrity_ok,
|
||||
kill9_and_reap,
|
||||
make_hermes_home,
|
||||
spawn_child,
|
||||
wait_for,
|
||||
)
|
||||
|
||||
WRITER = r"""
|
||||
import json, sys
|
||||
from pathlib import Path
|
||||
from hermes_state import SessionDB
|
||||
|
||||
db_path = Path({db_path!r})
|
||||
journal = Path({journal!r})
|
||||
db = SessionDB(db_path=db_path)
|
||||
db.create_session("cell1", source="conformance")
|
||||
i = 0
|
||||
with journal.open("a", buffering=1) as j:
|
||||
while True:
|
||||
rowid = db.append_message("cell1", "user", content=f"m{{i}}")
|
||||
# fsync-journal the acknowledged index AFTER append returns —
|
||||
# exactly the probe's definition of "claimed durable".
|
||||
j.write(json.dumps({{"i": i, "rowid": rowid}}) + "\n")
|
||||
j.flush()
|
||||
i += 1
|
||||
"""
|
||||
|
||||
|
||||
def _acknowledged(journal: Path) -> list[dict]:
|
||||
if not journal.exists():
|
||||
return []
|
||||
out = []
|
||||
for line in journal.read_text().splitlines():
|
||||
try:
|
||||
out.append(json.loads(line))
|
||||
except json.JSONDecodeError:
|
||||
# A torn final line is expected under SIGKILL: the write to the
|
||||
# journal itself was interrupted. That index was never fully
|
||||
# acknowledged to the harness, so it is out of scope.
|
||||
continue
|
||||
return out
|
||||
|
||||
|
||||
def _recover(db_path: Path) -> list[tuple]:
|
||||
"""One independent recovery pass in a fresh connection/process context."""
|
||||
conn = sqlite3.connect(str(db_path))
|
||||
try:
|
||||
return conn.execute(
|
||||
"SELECT id, content FROM messages WHERE session_id='cell1' ORDER BY id"
|
||||
).fetchall()
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
|
||||
@pytest.mark.parametrize("requested_mode", [None, "DELETE", "WAL"])
|
||||
def test_acknowledged_appends_survive_sigkill(tmp_path, requested_mode):
|
||||
db_path = tmp_path / "state.db"
|
||||
journal = tmp_path / "acked.jsonl"
|
||||
|
||||
child_env = {}
|
||||
if requested_mode is not None:
|
||||
# Steer the CHILD's own resolver via an isolated HERMES_HOME config:
|
||||
# pre-seeding the file alone is not enough — SessionDB.__init__ runs
|
||||
# apply_wal_with_fallback(), which upgrades a non-WAL file to WAL
|
||||
# whenever the configured mode says so (the resolver's downgrade
|
||||
# gates may still refuse; effective_mode_or_skip audits post-run).
|
||||
child_env["HERMES_HOME"] = str(
|
||||
make_hermes_home(tmp_path, requested_mode.lower())
|
||||
)
|
||||
|
||||
child = spawn_child(
|
||||
WRITER.format(db_path=str(db_path), journal=str(journal)), env=child_env
|
||||
)
|
||||
try:
|
||||
wait_for(
|
||||
lambda: len(_acknowledged(journal)) >= 200,
|
||||
what=">=200 acknowledged appends",
|
||||
child=child,
|
||||
)
|
||||
# The kill must interrupt a LIVE writer — a child that already
|
||||
# exited would turn this into a clean-shutdown test.
|
||||
assert child.poll() is None, (
|
||||
f"writer exited early (rc={child.returncode}): "
|
||||
f"{(child.stderr.read() if child.stderr else b'').decode(errors='replace')[-1500:]}"
|
||||
)
|
||||
finally:
|
||||
kill9_and_reap(child)
|
||||
|
||||
# A leg that ran in a different mode than requested is no evidence for
|
||||
# the requested mode — skip it rather than silently double-counting WAL.
|
||||
mode = effective_mode_or_skip(db_path, requested_mode)
|
||||
acked = _acknowledged(journal)
|
||||
assert len(acked) >= 200, (
|
||||
f"[journal_mode={mode}] only {len(acked)} acknowledged appends "
|
||||
"journaled before the kill — harness window too small"
|
||||
)
|
||||
|
||||
pass1 = _recover(db_path)
|
||||
pass2 = _recover(db_path)
|
||||
|
||||
# Zero lost-after-return: every acknowledged index is present.
|
||||
recovered_contents = {row[1] for row in pass1}
|
||||
lost = [a for a in acked if f"m{a['i']}" not in recovered_contents]
|
||||
assert not lost, (
|
||||
f"[journal_mode={mode}] {len(lost)} acknowledged appends lost after "
|
||||
f"SIGKILL (first: {lost[:3]})"
|
||||
)
|
||||
|
||||
# Contiguous prefix: indices 0..N-1 with no holes and no duplicates.
|
||||
indices = sorted(int(row[1][1:]) for row in pass1)
|
||||
assert indices == list(range(len(indices))), (
|
||||
f"[journal_mode={mode}] recovered transcript is not a contiguous "
|
||||
"prefix (holes or duplicates present)"
|
||||
)
|
||||
|
||||
# Recovery determinism: two independent passes identical.
|
||||
assert pass1 == pass2, f"[journal_mode={mode}] recovery passes diverge"
|
||||
|
||||
assert integrity_ok(db_path), f"[journal_mode={mode}] integrity_check failed"
|
||||
Reference in New Issue
Block a user