Skip to content

Commit f6aaa43

Browse files
Update PRAGMA deny-list integration tests for busy_timeout interception
The pre-existing pin asserted PRAGMA busy_timeout (both setter and getter forms) raises DatabaseError("not authorized") from the server's VFS authorizer. With the dbapi-layer interception now in place, the PRAGMA is intercepted at the cursor's execute path BEFORE the wire send — the server never sees it, so the "not authorized" error never fires. Drop the busy_timeout entries from _DENIED_PRAGMAS_SYNC and add positive pins for the new behaviour: the setter form updates connection._busy_timeout and emits the value as a row; the getter form emits the current value. Tests run against the live cluster to verify the user-observable behaviour end-to-end (the unit-test test_pragma_busy_timeout_intercept.py covers the interception logic against a fake cursor). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 7358a97 commit f6aaa43

1 file changed

Lines changed: 64 additions & 2 deletions

File tree

tests/integration/test_pragma_deny_list.py

Lines changed: 64 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,18 @@
3030
# Both-form denials (read + write)
3131
"PRAGMA wal_checkpoint",
3232
"PRAGMA wal_checkpoint(FULL)",
33-
"PRAGMA busy_timeout = 1000",
34-
"PRAGMA busy_timeout",
33+
# ``PRAGMA busy_timeout`` was historically in this deny list
34+
# (C VFS rejects it server-side via SQLITE_AUTH), but the
35+
# dbapi-layer now intercepts the PRAGMA at the cursor's
36+
# execute path BEFORE the wire send — the interception updates
37+
# the connection's busy_timeout (stdlib parity) without ever
38+
# reaching the server. See
39+
# ``test_pragma_busy_timeout_intercept.py`` for the
40+
# interception pin and ``_pragma_intercept.py`` for the
41+
# implementation. The busy_timeout entries are removed from
42+
# this deny-list-pin because the user-observable behaviour is
43+
# no longer ``DatabaseError("not authorized")`` — it's a
44+
# successful PRAGMA that returns the current value as a row.
3545
"PRAGMA read_uncommitted = 1",
3646
"PRAGMA read_uncommitted",
3747
"PRAGMA locking_mode = EXCLUSIVE",
@@ -58,3 +68,55 @@ async def test_denied_pragma_raises_database_error_async(cluster_address: str, p
5868
await cur.execute(pragma)
5969
finally:
6070
await conn.close()
71+
72+
73+
# Pre-existing pin was "PRAGMA busy_timeout raises DatabaseError(not
74+
# authorized)" — that pin pre-dated the dbapi-layer interception.
75+
# The busy_timeout entries are now intercepted client-side: the
76+
# setter updates the connection's busy_timeout and emits the value
77+
# as a row; the getter emits the current value. These tests pin the
78+
# new user-observable behaviour against the live cluster (the
79+
# unit-test ``test_pragma_busy_timeout_intercept.py`` pins it
80+
# against a fake cursor).
81+
82+
83+
@pytest.mark.integration
84+
@pytest.mark.parametrize("pragma", ["PRAGMA busy_timeout = 1000", "PRAGMA busy_timeout"])
85+
def test_pragma_busy_timeout_intercepted_sync(cluster_address: str, pragma: str) -> None:
86+
"""``PRAGMA busy_timeout`` is intercepted at the cursor layer
87+
BEFORE the wire send — the server never sees it. The setter
88+
updates the connection's busy_timeout; both forms emit the
89+
(possibly updated) value as a row."""
90+
with dqlitedbapi.connect(cluster_address, timeout=2.0) as conn:
91+
cur = conn.cursor()
92+
cur.execute(pragma)
93+
row = cur.fetchone()
94+
assert row is not None
95+
# Setter sets to 1000ms (1.0s); getter returns whatever the
96+
# connection's default is (5000ms from the kwarg default).
97+
if "=" in pragma:
98+
assert row[0] == 1000
99+
# Verify the connection state was actually updated.
100+
assert conn._busy_timeout == 1.0
101+
else:
102+
# Getter — returns the current value (default 5000ms).
103+
assert row[0] == 5000
104+
105+
106+
@pytest.mark.integration
107+
@pytest.mark.parametrize("pragma", ["PRAGMA busy_timeout = 1000", "PRAGMA busy_timeout"])
108+
async def test_pragma_busy_timeout_intercepted_async(cluster_address: str, pragma: str) -> None:
109+
"""Async sibling — same interception behaviour."""
110+
conn = await aconnect(cluster_address)
111+
try:
112+
cur = conn.cursor()
113+
await cur.execute(pragma)
114+
row = await cur.fetchone()
115+
assert row is not None
116+
if "=" in pragma:
117+
assert row[0] == 1000
118+
assert conn._busy_timeout == 1.0
119+
else:
120+
assert row[0] == 5000
121+
finally:
122+
await conn.close()

0 commit comments

Comments
 (0)