Import AITURK IDE 1.0.0-beta.1 from Hermes 63279301; preserve MIT license
This commit is contained in:
@@ -0,0 +1,330 @@
|
||||
"""Behavioral coverage for #68545's centralized journal-mode setting."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import sqlite3
|
||||
|
||||
import pytest
|
||||
import yaml
|
||||
|
||||
|
||||
def _write_config(monkeypatch: pytest.MonkeyPatch, tmp_path, config: object) -> None:
|
||||
home = tmp_path / "hermes-home"
|
||||
home.mkdir(exist_ok=True)
|
||||
monkeypatch.setenv("HERMES_HOME", str(home))
|
||||
(home / "config.yaml").write_text(
|
||||
yaml.safe_dump(config),
|
||||
encoding="utf-8",
|
||||
)
|
||||
|
||||
|
||||
def _configure_mode(monkeypatch: pytest.MonkeyPatch, tmp_path, mode: object) -> None:
|
||||
_write_config(monkeypatch, tmp_path, {"database": {"journal_mode": mode}})
|
||||
|
||||
|
||||
def _disable_vulnerable_gate(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
monkeypatch.setattr(
|
||||
"hermes_state.is_sqlite_wal_reset_vulnerable",
|
||||
lambda **kwargs: False,
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def _reset_configured_delete_override_warned_paths():
|
||||
"""Reset the configured-delete-override warned-paths set so the
|
||||
once-per-process-per-db_label dedup doesn't leak between tests."""
|
||||
import hermes_state
|
||||
|
||||
hermes_state._delete_overridden_warned_paths.clear()
|
||||
yield
|
||||
hermes_state._delete_overridden_warned_paths.clear()
|
||||
|
||||
|
||||
def test_database_journal_mode_has_a_canonical_default():
|
||||
from hermes_cli.config import DEFAULT_CONFIG
|
||||
|
||||
assert DEFAULT_CONFIG["database"]["journal_mode"] == "wal"
|
||||
|
||||
|
||||
def test_resolve_journal_mode_uses_real_database_config(monkeypatch, tmp_path):
|
||||
from hermes_state import resolve_journal_mode
|
||||
|
||||
_configure_mode(monkeypatch, tmp_path, "DELETE")
|
||||
assert resolve_journal_mode() == "delete"
|
||||
|
||||
|
||||
def test_new_nonsecret_hermes_env_override_is_not_exposed(monkeypatch, tmp_path):
|
||||
from hermes_state import resolve_journal_mode
|
||||
|
||||
_configure_mode(monkeypatch, tmp_path, "wal")
|
||||
monkeypatch.setenv("HERMES_JOURNAL_MODE", "delete")
|
||||
assert resolve_journal_mode() == "wal"
|
||||
|
||||
|
||||
@pytest.mark.parametrize("value", ["bogus", "truncate", None, 42, {"bad": "shape"}])
|
||||
def test_invalid_config_value_falls_back_to_wal(monkeypatch, tmp_path, value):
|
||||
from hermes_state import resolve_journal_mode
|
||||
|
||||
_configure_mode(monkeypatch, tmp_path, value)
|
||||
assert resolve_journal_mode() == "wal"
|
||||
|
||||
|
||||
@pytest.mark.parametrize("database", [[], "delete", 42, None])
|
||||
def test_malformed_database_section_falls_back_to_wal(
|
||||
monkeypatch, tmp_path, database
|
||||
):
|
||||
from hermes_state import resolve_journal_mode
|
||||
|
||||
_write_config(monkeypatch, tmp_path, {"database": database})
|
||||
assert resolve_journal_mode() == "wal"
|
||||
|
||||
|
||||
def test_apply_wal_with_fallback_honors_delete_config(monkeypatch, tmp_path):
|
||||
from hermes_state import apply_wal_with_fallback
|
||||
|
||||
_configure_mode(monkeypatch, tmp_path, "delete")
|
||||
_disable_vulnerable_gate(monkeypatch)
|
||||
conn = sqlite3.connect(tmp_path / "configured.db")
|
||||
try:
|
||||
assert apply_wal_with_fallback(conn, db_label="configured.db") == "delete"
|
||||
assert conn.execute("PRAGMA journal_mode").fetchone()[0].lower() == "delete"
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
|
||||
def test_apply_wal_with_fallback_defaults_to_wal(monkeypatch, tmp_path):
|
||||
from hermes_state import apply_wal_with_fallback
|
||||
|
||||
_configure_mode(monkeypatch, tmp_path, "wal")
|
||||
_disable_vulnerable_gate(monkeypatch)
|
||||
conn = sqlite3.connect(tmp_path / "default.db")
|
||||
try:
|
||||
assert apply_wal_with_fallback(conn, db_label="default.db") == "wal"
|
||||
assert conn.execute("PRAGMA journal_mode").fetchone()[0].lower() == "wal"
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
|
||||
def test_configured_delete_validates_vulnerable_sqlite_result(monkeypatch, tmp_path):
|
||||
"""The safety gate must not report DELETE when SQLite returns MEMORY."""
|
||||
from hermes_state import apply_wal_with_fallback
|
||||
|
||||
_configure_mode(monkeypatch, tmp_path, "delete")
|
||||
monkeypatch.setattr(
|
||||
"hermes_state.is_sqlite_wal_reset_vulnerable",
|
||||
lambda **kwargs: True,
|
||||
)
|
||||
conn = sqlite3.connect(":memory:")
|
||||
try:
|
||||
with pytest.raises(sqlite3.OperationalError, match="configured.*delete"):
|
||||
apply_wal_with_fallback(conn, db_label="memory-configured.db")
|
||||
assert conn.execute("PRAGMA journal_mode").fetchone()[0].lower() == "memory"
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
|
||||
def test_configured_delete_never_live_downgrades_existing_wal(monkeypatch, tmp_path, caplog):
|
||||
"""Keeping WAL is correct, but the operator must be told their configured
|
||||
delete had no effect (otherwise the DB silently stays WAL and the protection
|
||||
they configured never applies)."""
|
||||
from hermes_state import apply_wal_with_fallback
|
||||
|
||||
_configure_mode(monkeypatch, tmp_path, "delete")
|
||||
db_path = tmp_path / "existing-wal.db"
|
||||
conn = sqlite3.connect(db_path)
|
||||
try:
|
||||
assert conn.execute("PRAGMA journal_mode=WAL").fetchone()[0].lower() == "wal"
|
||||
monkeypatch.setattr(
|
||||
"hermes_state.is_sqlite_wal_reset_vulnerable",
|
||||
lambda **kwargs: True,
|
||||
)
|
||||
with caplog.at_level("ERROR", logger="hermes_state"):
|
||||
assert apply_wal_with_fallback(conn, db_label="existing-wal.db") == "wal"
|
||||
assert conn.execute("PRAGMA journal_mode").fetchone()[0].lower() == "wal"
|
||||
assert any(
|
||||
"database.journal_mode=delete is configured" in r.message
|
||||
and "on-disk" in r.message
|
||||
for r in caplog.records
|
||||
), "expected a warning that configured delete was overridden by on-disk WAL"
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
|
||||
def test_configured_delete_overridden_warns_on_non_vulnerable_runtime_too(monkeypatch, tmp_path, caplog):
|
||||
"""When the SQLite runtime is NOT WAL-reset-vulnerable (e.g. after a
|
||||
3.51.3+ upgrade), the on-disk WAL + configured-delete case reaches the
|
||||
read-only probe path instead of the vulnerability path. That path used to
|
||||
return WAL with no signal at all; it must emit the same override warning."""
|
||||
from hermes_state import apply_wal_with_fallback
|
||||
|
||||
_configure_mode(monkeypatch, tmp_path, "delete")
|
||||
_disable_vulnerable_gate(monkeypatch)
|
||||
db_path = tmp_path / "existing-wal.db"
|
||||
conn = sqlite3.connect(db_path)
|
||||
try:
|
||||
assert conn.execute("PRAGMA journal_mode=WAL").fetchone()[0].lower() == "wal"
|
||||
with caplog.at_level("ERROR", logger="hermes_state"):
|
||||
assert apply_wal_with_fallback(conn, db_label="existing-wal.db") == "wal"
|
||||
assert apply_wal_with_fallback(conn, db_label="existing-wal.db") == "wal"
|
||||
assert conn.execute("PRAGMA journal_mode").fetchone()[0].lower() == "wal"
|
||||
warnings = [
|
||||
r for r in caplog.records
|
||||
if "database.journal_mode=delete is configured" in r.message
|
||||
]
|
||||
assert len(warnings) == 1, (
|
||||
"probe path must warn when configured delete is overridden by on-disk WAL, "
|
||||
"and dedup to one per process per db_label"
|
||||
)
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
|
||||
def test_configured_delete_overridden_warning_fires_once_per_db(monkeypatch, tmp_path, caplog):
|
||||
"""The override warning is deduped per process per db_label (same discipline
|
||||
as the WAL-fallback warning), so repeated connections don't flood the log."""
|
||||
from hermes_state import apply_wal_with_fallback
|
||||
|
||||
_configure_mode(monkeypatch, tmp_path, "delete")
|
||||
monkeypatch.setattr(
|
||||
"hermes_state.is_sqlite_wal_reset_vulnerable",
|
||||
lambda **kwargs: True,
|
||||
)
|
||||
db_path = tmp_path / "existing-wal.db"
|
||||
conn = sqlite3.connect(db_path)
|
||||
try:
|
||||
conn.execute("PRAGMA journal_mode=WAL").fetchone()
|
||||
with caplog.at_level("ERROR", logger="hermes_state"):
|
||||
assert apply_wal_with_fallback(conn, db_label="once.db") == "wal"
|
||||
assert apply_wal_with_fallback(conn, db_label="once.db") == "wal"
|
||||
assert apply_wal_with_fallback(conn, db_label="once.db") == "wal"
|
||||
warnings = [
|
||||
r for r in caplog.records
|
||||
if "database.journal_mode=delete is configured" in r.message
|
||||
]
|
||||
assert len(warnings) == 1, "override warning must fire once per process per db_label"
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
|
||||
def test_configured_delete_with_require_wal_and_existing_wal_returns_wal(monkeypatch, tmp_path, caplog):
|
||||
"""Pin the require_wal=True + configured=delete + on-disk WAL edge case: the
|
||||
existing-WAL probe branch returns "wal" unconditionally (require_wal only
|
||||
governs the WAL-refusal fallback paths), so the override warning fires and no
|
||||
WalUnsupportedError is raised."""
|
||||
from hermes_state import apply_wal_with_fallback
|
||||
|
||||
_configure_mode(monkeypatch, tmp_path, "delete")
|
||||
_disable_vulnerable_gate(monkeypatch)
|
||||
db_path = tmp_path / "existing-wal.db"
|
||||
conn = sqlite3.connect(db_path)
|
||||
try:
|
||||
assert conn.execute("PRAGMA journal_mode=WAL").fetchone()[0].lower() == "wal"
|
||||
with caplog.at_level("ERROR", logger="hermes_state"):
|
||||
result = apply_wal_with_fallback(
|
||||
conn, db_label="existing-wal.db", require_wal=True
|
||||
)
|
||||
assert result == "wal"
|
||||
assert any(
|
||||
"database.journal_mode=delete is configured" in r.message
|
||||
for r in caplog.records
|
||||
)
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
|
||||
def test_real_db_openers_honor_configured_delete(monkeypatch, tmp_path):
|
||||
"""All helper-routed file-backed openers must behaviorally use DELETE."""
|
||||
_configure_mode(monkeypatch, tmp_path, "delete")
|
||||
_disable_vulnerable_gate(monkeypatch)
|
||||
|
||||
from agent import verification_evidence
|
||||
from cron import executions
|
||||
from gateway import delivery_ledger
|
||||
from gateway.platforms.api_server import ResponseStore
|
||||
from hermes_cli import kanban_db, projects_db
|
||||
from hermes_state import SessionDB
|
||||
from plugins.memory.holographic.store import MemoryStore
|
||||
from plugins.platforms.discord.recovery import DiscordRecoveryStore
|
||||
from tools import async_delegation
|
||||
|
||||
observed: dict[str, str] = {}
|
||||
|
||||
for name, connect in (
|
||||
("async_delegation", async_delegation._connect),
|
||||
("delivery_ledger", delivery_ledger._connect),
|
||||
("verification_evidence", verification_evidence._connect),
|
||||
):
|
||||
conn = connect()
|
||||
try:
|
||||
observed[name] = conn.execute("PRAGMA journal_mode").fetchone()[0].lower()
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
monkeypatch.setattr(executions, "EXECUTIONS_FILE", tmp_path / "cron" / "executions.db")
|
||||
executions.EXECUTIONS_FILE.parent.mkdir(parents=True, exist_ok=True)
|
||||
cron_conn = executions._connect()
|
||||
try:
|
||||
executions._initialize_schema(cron_conn)
|
||||
observed["cron_executions"] = cron_conn.execute(
|
||||
"PRAGMA journal_mode"
|
||||
).fetchone()[0].lower()
|
||||
finally:
|
||||
cron_conn.close()
|
||||
|
||||
discord = DiscordRecoveryStore(hermes_home=tmp_path)
|
||||
observed["discord_recovery"] = discord.call(
|
||||
lambda conn: conn.execute("PRAGMA journal_mode").fetchone()[0].lower()
|
||||
)
|
||||
|
||||
session_db = SessionDB(db_path=tmp_path / "state.db")
|
||||
try:
|
||||
observed["session_db"] = session_db._conn.execute(
|
||||
"PRAGMA journal_mode"
|
||||
).fetchone()[0].lower()
|
||||
finally:
|
||||
session_db.close()
|
||||
|
||||
kanban_conn = kanban_db.connect(db_path=tmp_path / "kanban.db")
|
||||
try:
|
||||
observed["kanban"] = kanban_conn.execute(
|
||||
"PRAGMA journal_mode"
|
||||
).fetchone()[0].lower()
|
||||
finally:
|
||||
kanban_conn.close()
|
||||
|
||||
projects_conn = projects_db.connect(db_path=tmp_path / "projects.db")
|
||||
try:
|
||||
observed["projects"] = projects_conn.execute(
|
||||
"PRAGMA journal_mode"
|
||||
).fetchone()[0].lower()
|
||||
finally:
|
||||
projects_conn.close()
|
||||
|
||||
holographic = MemoryStore(db_path=tmp_path / "memory_store.db")
|
||||
try:
|
||||
observed["holographic"] = holographic._conn.execute(
|
||||
"PRAGMA journal_mode"
|
||||
).fetchone()[0].lower()
|
||||
finally:
|
||||
holographic.close()
|
||||
|
||||
response_store = ResponseStore(db_path=str(tmp_path / "response_store.db"))
|
||||
try:
|
||||
observed["response_store"] = response_store._conn.execute(
|
||||
"PRAGMA journal_mode"
|
||||
).fetchone()[0].lower()
|
||||
finally:
|
||||
response_store.close()
|
||||
|
||||
assert observed == {
|
||||
"async_delegation": "delete",
|
||||
"delivery_ledger": "delete",
|
||||
"verification_evidence": "delete",
|
||||
"cron_executions": "delete",
|
||||
"discord_recovery": "delete",
|
||||
"session_db": "delete",
|
||||
"kanban": "delete",
|
||||
"projects": "delete",
|
||||
"holographic": "delete",
|
||||
"response_store": "delete",
|
||||
}
|
||||
Reference in New Issue
Block a user