test: align file-identity guard tests with post-18ac3c4 FTS recovery contract
Main removed the live-path runtime FTS rebuild (18ac3c4fb6) and #99652
narrowed the corruption classifier, after the salvaged PR #89364 branched.
Update its tests to assert the current contract: no fail-open detach on a
replaced file (fts_enabled stays True, no stale marker), fail-open (not
rebuild) for genuine same-file FTS corruption, and FTS-provenance errors
for the guard probe since generic malformed no longer reaches fail-open.
This commit is contained in:
@@ -55,7 +55,10 @@ def test_replaced_state_db_diverts_pending_without_fts_rebuild(tmp_path, monkeyp
|
||||
)
|
||||
|
||||
assert store._db._db_replaced is True
|
||||
assert store._db._fts_runtime_rebuild_attempted is False
|
||||
# No FTS surgery ran on either layer: state-level fail-open never
|
||||
# detached, and the gateway one-shot rebuild was not consumed.
|
||||
assert store._db._fts_enabled is True
|
||||
assert store._db._fts_stale is False
|
||||
assert store._fts_rebuild_attempted is False
|
||||
_assert_diverted(tmp_path, sid, "after-replace")
|
||||
store.close_all_db_handles()
|
||||
@@ -91,7 +94,10 @@ def test_copyfile_replaced_state_db_diverts_pending_without_fts_rebuild(
|
||||
)
|
||||
|
||||
assert store._db._db_replaced is True
|
||||
assert store._db._fts_runtime_rebuild_attempted is False
|
||||
# No FTS surgery ran on either layer: state-level fail-open never
|
||||
# detached, and the gateway one-shot rebuild was not consumed.
|
||||
assert store._db._fts_enabled is True
|
||||
assert store._db._fts_stale is False
|
||||
assert store._fts_rebuild_attempted is False
|
||||
_assert_diverted(tmp_path, sid, "after-cp")
|
||||
store.close_all_db_handles()
|
||||
|
||||
@@ -50,8 +50,9 @@ def test_replace_with_new_inode_fails_loudly_without_fts_repair(tmp_path):
|
||||
db.append_message("live-sess", role="user", content="after-replace")
|
||||
|
||||
assert db._db_replaced is True
|
||||
assert db._fts_runtime_rebuild_attempted is False
|
||||
# No FTS surgery ran: fail-open never detached the indexes.
|
||||
assert db._fts_enabled is True
|
||||
assert db._fts_stale is False
|
||||
db.close()
|
||||
|
||||
|
||||
@@ -67,12 +68,18 @@ def test_second_write_after_halt_does_not_attempt_repair(tmp_path):
|
||||
db.append_message("s", role="user", content="first")
|
||||
with pytest.raises(StateDbReplacedError):
|
||||
db.append_message("s", role="user", content="second")
|
||||
assert db._fts_runtime_rebuild_attempted is False
|
||||
assert db._fts_enabled is True
|
||||
assert db._fts_stale is False
|
||||
db.close()
|
||||
|
||||
|
||||
def test_same_file_fts_corruption_still_rebuilds(tmp_path):
|
||||
"""Identity guard must not disable genuine in-file FTS recovery."""
|
||||
def test_same_file_fts_corruption_still_fails_open(tmp_path):
|
||||
"""Identity guard must not disable genuine in-file FTS recovery.
|
||||
|
||||
Since 18ac3c4fb6 the live write path never rebuilds FTS in place; the
|
||||
recovery contract is the fail-open detach (stale marker + triggers
|
||||
dropped) followed by a successful canonical retry.
|
||||
"""
|
||||
db = _make_db(tmp_path / "state.db", "s1", "hello world")
|
||||
_require_identity(db)
|
||||
identity = db._db_file_identity
|
||||
@@ -85,7 +92,9 @@ def test_same_file_fts_corruption_still_rebuilds(tmp_path):
|
||||
db.append_message("s1", role="user", content="healed append")
|
||||
assert db._db_file_identity == identity
|
||||
assert db._db_replaced is False
|
||||
assert db._fts_runtime_rebuild_attempted is True
|
||||
# Fail-open detach ran: canonical write landed, FTS marked stale.
|
||||
assert db._fts_stale is True
|
||||
assert db._fts_enabled is False
|
||||
db.close()
|
||||
|
||||
|
||||
@@ -116,8 +125,10 @@ def test_new_sessiondb_on_replaced_path_records_new_identity(tmp_path):
|
||||
reopened.close()
|
||||
|
||||
|
||||
def test_malformed_error_on_replaced_file_skips_fts_rebuild(tmp_path):
|
||||
"""Even if SQLite surfaces malformed, identity mismatch blocks repair."""
|
||||
def test_fts_scoped_error_on_replaced_file_skips_fts_fail_open(tmp_path):
|
||||
"""Even FTS-provenance corruption must not authorize surgery on a
|
||||
replaced file. (A generic malformed error never reaches fail-open at
|
||||
all since the provenance classifier of #99652 rejects it earlier.)"""
|
||||
live = tmp_path / "state.db"
|
||||
other = tmp_path / "other.db"
|
||||
db = _make_db(live, "s", "a")
|
||||
@@ -127,10 +138,13 @@ def test_malformed_error_on_replaced_file_skips_fts_rebuild(tmp_path):
|
||||
os.replace(other, live)
|
||||
|
||||
with pytest.raises(StateDbReplacedError):
|
||||
db._try_runtime_fts_rebuild(
|
||||
sqlite3.DatabaseError("database disk image is malformed")
|
||||
db._enter_fts_fail_open(
|
||||
sqlite3.DatabaseError(
|
||||
'fts5: corrupt structure record for table "messages_fts"'
|
||||
)
|
||||
)
|
||||
assert db._fts_runtime_rebuild_attempted is False
|
||||
assert db._fts_enabled is True
|
||||
assert db._fts_stale is False
|
||||
db.close()
|
||||
|
||||
|
||||
@@ -156,7 +170,8 @@ def test_copyfile_same_inode_fails_loudly_without_fts_repair(tmp_path):
|
||||
with pytest.raises(StateDbReplacedError, match="replaced underneath"):
|
||||
db.append_message("live-sess", role="user", content="after-cp")
|
||||
assert db._db_replaced is True
|
||||
assert db._fts_runtime_rebuild_attempted is False
|
||||
assert db._fts_enabled is True
|
||||
assert db._fts_stale is False
|
||||
db.close()
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user