Skip to content

Commit 9cc105c

Browse files
committed
tests: add unit tests for wait_for_schema_agreement with closed connection
Add two unit tests to ControlConnectionTest that cover the bug fixed in the previous commit (#604): test_wait_for_schema_agreement_recovers_from_closed_control_connection: Simulates a node being stopped while wait_for_schema_agreement() is running in control-connection mode (no explicit connection passed). The mock initial connection raises ConnectionShutdown on its first query and simultaneously installs a replacement connection, mirroring what the driver's reconnect logic does in production. Before the fix this exception propagated to the caller. After the fix the function transparently picks up the new control connection and returns True. test_wait_for_schema_agreement_explicit_connection_raises_on_shutdown: Guards the DDL code path, where the caller passes an explicit connection to ensure schema agreement is verified through the DDL coordinator. In this case ConnectionShutdown must still be re-raised — there is no safe fallback to a different node. This test passes both before and after the fix, confirming that the fix does not weaken the explicit-connection path. Signed-off-by: Nadav Har'El <nyh@scylladb.com>
1 parent dd3cd52 commit 9cc105c

1 file changed

Lines changed: 43 additions & 0 deletions

File tree

tests/unit/test_control_connection.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
from unittest.mock import Mock, ANY, call
1919

2020
from cassandra import OperationTimedOut, SchemaTargetType, SchemaChangeType
21+
from cassandra.connection import ConnectionShutdown
2122
from cassandra.protocol import ResultMessage, RESULT_KIND_ROWS
2223
from cassandra.cluster import ControlConnection, _Scheduler, ProfileManager, EXEC_PROFILE_DEFAULT, ExecutionProfile
2324
from cassandra.pool import Host
@@ -623,6 +624,48 @@ def test_refresh_nodes_and_tokens_add_host_detects_invalid_port(self):
623624
assert self.cluster.added_hosts[0].datacenter == "dc1"
624625
assert self.cluster.added_hosts[0].rack == "rack1"
625626

627+
def test_wait_for_schema_agreement_recovers_from_closed_control_connection(self):
628+
"""
629+
Reproduces https://github.com/scylladb/python-driver/issues/604
630+
631+
When wait_for_schema_agreement() is called without an explicit connection
632+
and the control connection is closed mid-wait (e.g. the node was stopped),
633+
the function should transparently pick up the new control connection and
634+
succeed rather than propagating ConnectionShutdown to the caller.
635+
"""
636+
# Build a second (replacement) connection whose schema queries succeed.
637+
new_connection = MockConnection()
638+
new_connection.endpoint = DefaultEndPoint("192.168.1.1")
639+
640+
# The initial connection raises ConnectionShutdown on the first query,
641+
# simulating the node being stopped while we wait.
642+
def first_call_raises(*args, **kwargs):
643+
# Swap the control connection to the replacement before raising,
644+
# just as the driver's reconnect logic would do.
645+
self.control_connection._connection = new_connection
646+
raise ConnectionShutdown("Connection is already closed")
647+
648+
self.connection.wait_for_responses = Mock(side_effect=first_call_raises)
649+
650+
# Before the fix this raised ConnectionShutdown; after the fix it should
651+
# recover and return True.
652+
result = self.control_connection.wait_for_schema_agreement()
653+
assert result is True
654+
655+
# The replacement connection must have been queried exactly once.
656+
assert new_connection.wait_for_responses.call_count == 1
657+
658+
def test_wait_for_schema_agreement_explicit_connection_raises_on_shutdown(self):
659+
"""
660+
When an explicit connection is passed (DDL path, coordinator must be queried),
661+
ConnectionShutdown should still propagate — there is no safe fallback.
662+
"""
663+
self.connection.wait_for_responses = Mock(
664+
side_effect=ConnectionShutdown("Connection is already closed"))
665+
666+
with self.assertRaises(ConnectionShutdown):
667+
self.control_connection.wait_for_schema_agreement(connection=self.connection)
668+
626669

627670
class EventTimingTest(unittest.TestCase):
628671
"""

0 commit comments

Comments
 (0)