Skip to content

Commit 0052163

Browse files
Align ClusterClient max_attempts validation wording with ConnectionPool
ConnectionPool's validator emits "must be at least 1 if provided, got X"; ClusterClient.connect's validator emitted "must be >= 1, got X". Same constraint, two different operator-facing messages. Standardize on the pool's wording (which documents the None case). Pin parity with a new test that asserts both validators produce matching substrings ("must be at least 1", "got X"), and update the existing cluster-side test that pinned the old wording. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 0db4361 commit 0052163

3 files changed

Lines changed: 62 additions & 2 deletions

File tree

src/dqliteclient/cluster.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -479,7 +479,10 @@ async def connect(
479479
"""
480480
attempts_cap = max_attempts if max_attempts is not None else _DEFAULT_CONNECT_MAX_ATTEMPTS
481481
if attempts_cap < 1:
482-
raise ValueError(f"max_attempts must be >= 1, got {attempts_cap}")
482+
# Wording mirrors ``ConnectionPool.__init__``'s validator
483+
# so operator-facing error parsing matches whichever layer
484+
# validated first.
485+
raise ValueError(f"max_attempts must be at least 1 if provided, got {attempts_cap}")
483486

484487
attempt_counter = [0]
485488

tests/test_cluster.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -601,7 +601,7 @@ async def fake_find_leader(**_kwargs: object) -> str:
601601
async def test_max_attempts_zero_rejected(self) -> None:
602602
store = MemoryNodeStore(["localhost:1"])
603603
client = ClusterClient(store, timeout=0.1)
604-
with pytest.raises(ValueError, match=">= 1"):
604+
with pytest.raises(ValueError, match="must be at least 1"):
605605
await client.connect(max_attempts=0)
606606

607607

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
"""Pin: the ``max_attempts`` validation error wording is identical
2+
in ``ConnectionPool.__init__`` and ``ClusterClient.connect``.
3+
4+
Operators triaging via grep should not have to remember which layer
5+
validated first; the same constraint must produce the same error
6+
message.
7+
"""
8+
9+
from __future__ import annotations
10+
11+
import pytest
12+
13+
from dqliteclient.cluster import ClusterClient
14+
from dqliteclient.node_store import MemoryNodeStore
15+
from dqliteclient.pool import ConnectionPool
16+
17+
18+
@pytest.mark.parametrize("bad_value", [0, -1, -100])
19+
def test_pool_max_attempts_validation_message(bad_value: int) -> None:
20+
with pytest.raises(ValueError, match="must be at least 1") as exc_info:
21+
ConnectionPool(["a:9001"], max_attempts=bad_value)
22+
assert f"got {bad_value}" in str(exc_info.value)
23+
24+
25+
@pytest.mark.asyncio
26+
@pytest.mark.parametrize("bad_value", [0, -1, -100])
27+
async def test_cluster_max_attempts_validation_message(bad_value: int) -> None:
28+
cluster = ClusterClient(MemoryNodeStore(["a:9001"]), timeout=1.0)
29+
with pytest.raises(ValueError, match="must be at least 1") as exc_info:
30+
await cluster.connect(database="x", max_attempts=bad_value)
31+
assert f"got {bad_value}" in str(exc_info.value)
32+
33+
34+
@pytest.mark.asyncio
35+
async def test_pool_and_cluster_max_attempts_messages_share_wording() -> None:
36+
"""Both validators emit the same ``"must be at least 1"`` substring
37+
plus the same ``"got X"`` shape, so a regex like
38+
``r"must be at least 1.*got"`` matches both."""
39+
pool_msg: str | None = None
40+
cluster_msg: str | None = None
41+
42+
try:
43+
ConnectionPool(["a:9001"], max_attempts=0)
44+
except ValueError as e:
45+
pool_msg = str(e)
46+
47+
cluster = ClusterClient(MemoryNodeStore(["a:9001"]), timeout=1.0)
48+
try:
49+
await cluster.connect(database="x", max_attempts=0)
50+
except ValueError as e:
51+
cluster_msg = str(e)
52+
53+
assert pool_msg is not None and cluster_msg is not None
54+
# Same substring shape — operators can grep one regex for both.
55+
for msg in (pool_msg, cluster_msg):
56+
assert "must be at least 1" in msg
57+
assert "got 0" in msg

0 commit comments

Comments
 (0)