Skip to content

Commit 6d97a52

Browse files
Pin is_disconnect leader-code matrix and do_ping error propagation
Two regression fences on the sync dialect: 1. is_disconnect: existing coverage tested only NOT_LEADER (10250); LEADERSHIP_LOST (10506) was in the LEADER_ERROR_CODES tuple but untested. Iterate the full tuple and assert each member is classified as a disconnect. Add a negative parametrised test across common non-leader SQLite codes (1 / 5 / 14 / 19) to guard against a broadened check misclassifying them. 2. do_ping: the narrow except tuple was tested for RuntimeError only; programming-error categories (AttributeError, TypeError, AssertionError) that signal real refactor bugs should propagate too, not silently return False. Parametrised test fences each. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 10f6776 commit 6d97a52

1 file changed

Lines changed: 50 additions & 0 deletions

File tree

tests/test_dialect_dialect_config.py

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,28 @@ def test_propagates_unexpected_exception(self) -> None:
250250
with pytest.raises(RuntimeError, match="bug"):
251251
dialect.do_ping(conn)
252252

253+
@pytest.mark.parametrize(
254+
"exc_cls,msg",
255+
[
256+
(AttributeError, "stale attribute"),
257+
(TypeError, "bad signature"),
258+
(AssertionError, "invariant violated"),
259+
],
260+
)
261+
def test_propagates_programming_errors(self, exc_cls: type[Exception], msg: str) -> None:
262+
"""A broadened except in do_ping would swallow refactor bugs
263+
(AttributeError from a renamed attribute, TypeError from a
264+
stale signature, AssertionError from a violated invariant) and
265+
silently return False — pool health checks would pass while
266+
the real fault rotted. Pin propagation for each category."""
267+
dialect = DqliteDialect()
268+
conn = MagicMock()
269+
cursor = MagicMock()
270+
conn.cursor.return_value = cursor
271+
cursor.execute.side_effect = exc_cls(msg)
272+
with pytest.raises(exc_cls, match=msg):
273+
dialect.do_ping(conn)
274+
253275

254276
class TestIsDisconnectTypeDispatch:
255277
def test_dqlite_connection_error_is_disconnect(self) -> None:
@@ -275,3 +297,31 @@ def test_unrelated_operational_error_is_not_disconnect(self) -> None:
275297
dialect = DqliteDialect()
276298
e = dqlitedbapi.exceptions.OperationalError("no such table")
277299
assert dialect.is_disconnect(e, None, None) is False
300+
301+
def test_is_disconnect_true_for_every_leader_error_code(self) -> None:
302+
"""Pin the full LEADER_ERROR_CODES tuple — if a future refactor
303+
drops SQLITE_IOERR_LEADERSHIP_LOST from the constant, a real
304+
leadership transfer stops triggering the reconnect path and the
305+
only signal is application latency, not a test failure."""
306+
from dqlitewire import LEADER_ERROR_CODES
307+
308+
dialect = DqliteDialect()
309+
for code in LEADER_ERROR_CODES:
310+
e = dqliteclient.exceptions.OperationalError(code, "leader gone")
311+
assert dialect.is_disconnect(e, None, None) is True, (
312+
f"LEADER_ERROR_CODES member {code} must be classified "
313+
f"as a disconnect — is_disconnect returned False."
314+
)
315+
316+
@pytest.mark.parametrize("code", [1, 5, 19, 14])
317+
def test_is_disconnect_false_for_non_leader_codes(self, code: int) -> None:
318+
"""Defensive fence against future reclassification drift: a stray
319+
OperationalError carrying a common SQLite code (generic error,
320+
SQLITE_BUSY, SQLITE_CONSTRAINT, SQLITE_CANTOPEN) must not be
321+
misread as a disconnect. Constraint codes in particular now
322+
surface as IntegrityError after ISSUE-209, but this test pins
323+
the fallback behaviour for any stray OperationalError that
324+
slips through."""
325+
dialect = DqliteDialect()
326+
e = dqliteclient.exceptions.OperationalError(code, "application error")
327+
assert dialect.is_disconnect(e, None, None) is False

0 commit comments

Comments
 (0)