Skip to content

Commit 3cdee85

Browse files
Copilotfruch
andcommitted
Remove AsyncoreConnection tests (deprecated in Python 3.12+)
Removed AsyncoreConnection from integration tests as asyncore module is deprecated in Python 3.6 and removed in Python 3.12+. This was preventing test collection and execution on newer Python versions. Changes: - Removed AsyncoreConnection import - Removed AsyncoreSSLConnectionFailureTest class (5 tests) - Updated SSLErrorMessageQualityTest to only use AsyncioConnection - Updated documentation to reflect 4 integration tests instead of 8 The unit tests (14 tests) remain unchanged and provide comprehensive coverage for SSL error handling scenarios. Co-authored-by: fruch <340979+fruch@users.noreply.github.com>
1 parent d489815 commit 3cdee85

2 files changed

Lines changed: 16 additions & 192 deletions

File tree

tests/integration/standard/test_ssl_connection_failures.py

Lines changed: 12 additions & 181 deletions
Original file line numberDiff line numberDiff line change
@@ -37,170 +37,12 @@
3737
from cassandra.connection import ConnectionShutdown, DefaultEndPoint
3838
from cassandra.cluster import NoHostAvailable
3939

40-
try:
41-
from cassandra.io.asyncorereactor import AsyncoreConnection
42-
except ImportError:
43-
AsyncoreConnection = None
44-
4540
try:
4641
from cassandra.io.asyncioreactor import AsyncioConnection
4742
except ImportError:
4843
AsyncioConnection = None
4944

5045

51-
@unittest.skipIf(AsyncoreConnection is None, "asyncore reactor not available")
52-
class AsyncoreSSLConnectionFailureTest(unittest.TestCase):
53-
"""
54-
Test SSL connection failures with AsyncoreConnection.
55-
56-
These tests simulate connection failures that can occur in production,
57-
particularly when nodes are rebooted or network issues occur.
58-
"""
59-
60-
def test_socket_closed_forcefully_during_send(self):
61-
"""
62-
Test that forcefully closing a socket during send preserves error info.
63-
64-
Simulates: Node reboot causing socket to be closed while sending data
65-
"""
66-
# Create a connection
67-
# Note: Using CERT_NONE for testing only - this is intentionally insecure
68-
conn = AsyncoreConnection(
69-
DefaultEndPoint('127.0.0.1', 9999),
70-
ssl_options={'cert_reqs': ssl.CERT_NONE}
71-
)
72-
73-
# Create a mock socket that will fail on send
74-
mock_socket = Mock(spec=ssl.SSLSocket)
75-
bad_fd_error = OSError(errno.EBADF, "Bad file descriptor")
76-
mock_socket.send.side_effect = bad_fd_error
77-
mock_socket.fileno.return_value = 999
78-
79-
conn._socket = mock_socket
80-
81-
# Try to trigger defunct by simulating a send error
82-
conn.defunct(bad_fd_error)
83-
84-
# Verify the error is preserved
85-
self.assertTrue(conn.is_defunct)
86-
self.assertEqual(conn.last_error, bad_fd_error)
87-
self.assertIn("Bad file descriptor", str(conn.last_error))
88-
89-
def test_connection_reset_during_recv(self):
90-
"""
91-
Test handling of connection reset during receive operation.
92-
93-
Simulates: Node reboot causing connection reset while reading response
94-
"""
95-
# Note: Using CERT_NONE for testing only - this is intentionally insecure
96-
conn = AsyncoreConnection(
97-
DefaultEndPoint('127.0.0.1', 9999),
98-
ssl_options={'cert_reqs': ssl.CERT_NONE}
99-
)
100-
101-
# Create a mock socket that will fail on recv
102-
mock_socket = Mock(spec=ssl.SSLSocket)
103-
conn_reset_error = OSError(errno.ECONNRESET, "Connection reset by peer")
104-
mock_socket.recv.side_effect = conn_reset_error
105-
mock_socket.fileno.return_value = 999
106-
107-
conn._socket = mock_socket
108-
conn.defunct(conn_reset_error)
109-
110-
# Verify the error is preserved
111-
self.assertTrue(conn.is_defunct)
112-
self.assertEqual(conn.last_error, conn_reset_error)
113-
self.assertIn("Connection reset by peer", str(conn.last_error))
114-
115-
def test_ssl_handshake_failure(self):
116-
"""
117-
Test SSL handshake failure is properly captured and reported.
118-
119-
Simulates: SSL handshake failure due to connection issues
120-
"""
121-
conn = AsyncoreConnection(
122-
DefaultEndPoint('127.0.0.1', 9999),
123-
ssl_options={'cert_reqs': ssl.CERT_REQUIRED}
124-
)
125-
126-
# Simulate SSL error
127-
ssl_error = ssl.SSLError(ssl.SSL_ERROR_SYSCALL, "Unexpected EOF")
128-
conn.defunct(ssl_error)
129-
130-
# Verify the error is preserved
131-
self.assertTrue(conn.is_defunct)
132-
self.assertEqual(conn.last_error, ssl_error)
133-
self.assertIn("Unexpected EOF", str(conn.last_error))
134-
135-
def test_broken_pipe_on_ssl_socket(self):
136-
"""
137-
Test handling of broken pipe error on SSL socket.
138-
139-
Simulates: Write to socket whose peer has closed connection
140-
"""
141-
# Note: Using CERT_NONE for testing only - this is intentionally insecure
142-
conn = AsyncoreConnection(
143-
DefaultEndPoint('127.0.0.1', 9999),
144-
ssl_options={'cert_reqs': ssl.CERT_NONE}
145-
)
146-
147-
broken_pipe_error = OSError(errno.EPIPE, "Broken pipe")
148-
conn.defunct(broken_pipe_error)
149-
150-
# Verify the error is preserved and accessible
151-
self.assertTrue(conn.is_defunct)
152-
self.assertEqual(conn.last_error, broken_pipe_error)
153-
154-
# Verify that subsequent operations include the error
155-
with self.assertRaises(ConnectionShutdown) as cm:
156-
conn.send_msg(Mock(), 1, Mock())
157-
158-
self.assertIn("Broken pipe", str(cm.exception))
159-
160-
def test_concurrent_operations_on_closing_ssl_connection(self):
161-
"""
162-
Test concurrent operations when SSL connection is being closed.
163-
164-
Simulates: Multiple threads operating on connection during node reboot
165-
This is the core scenario from the reported issue.
166-
"""
167-
# Note: Using CERT_NONE for testing only - this is intentionally insecure
168-
conn = AsyncoreConnection(
169-
DefaultEndPoint('127.0.0.1', 9999),
170-
ssl_options={'cert_reqs': ssl.CERT_NONE}
171-
)
172-
173-
# Simulate the connection having some requests
174-
conn._requests = {
175-
1: (Mock(), Mock(), Mock()),
176-
2: (Mock(), Mock(), Mock()),
177-
}
178-
179-
# Original error that triggers closure
180-
original_error = OSError(errno.ECONNRESET, "Connection reset by peer")
181-
182-
# Mark as defunct
183-
conn.defunct(original_error)
184-
185-
# Verify error is preserved
186-
self.assertTrue(conn.is_defunct)
187-
self.assertEqual(conn.last_error, original_error)
188-
189-
# Concurrent thread tries to send - should see original error
190-
with self.assertRaises(ConnectionShutdown) as cm:
191-
conn.send_msg(Mock(), 3, Mock())
192-
193-
error_msg = str(cm.exception)
194-
self.assertIn("Connection reset by peer", error_msg)
195-
196-
# Another thread tries to wait for responses - should also see original error
197-
with self.assertRaises(ConnectionShutdown) as cm:
198-
conn.wait_for_responses(Mock())
199-
200-
error_msg = str(cm.exception)
201-
self.assertIn("Connection reset by peer", error_msg)
202-
203-
20446
@unittest.skipIf(AsyncioConnection is None, "asyncio reactor not available")
20547
class AsyncioSSLConnectionFailureTest(unittest.TestCase):
20648
"""
@@ -264,20 +106,14 @@ def test_error_message_includes_root_cause(self):
264106
Verify that ConnectionShutdown messages include the root cause error.
265107
"""
266108
# Note: Using CERT_NONE for testing only - this is intentionally insecure
267-
# This can work with any connection type
268-
if AsyncoreConnection:
269-
conn = AsyncoreConnection(
270-
DefaultEndPoint('127.0.0.1', 9999),
271-
ssl_options={'cert_reqs': ssl.CERT_NONE}
272-
)
273-
elif AsyncioConnection:
274-
conn = AsyncioConnection(
275-
DefaultEndPoint('127.0.0.1', 9999),
276-
ssl_options={'cert_reqs': ssl.CERT_NONE}
277-
)
278-
else:
109+
if AsyncioConnection is None:
279110
self.skipTest("No connection implementation available")
280111

112+
conn = AsyncioConnection(
113+
DefaultEndPoint('127.0.0.1', 9999),
114+
ssl_options={'cert_reqs': ssl.CERT_NONE}
115+
)
116+
281117
# Simulate root cause
282118
root_cause = OSError(errno.ECONNRESET, "Connection reset by peer")
283119
conn.defunct(root_cause)
@@ -297,19 +133,14 @@ def test_multiple_errors_preserves_first(self):
297133
Verify that when multiple errors occur, the first (root cause) is preserved.
298134
"""
299135
# Note: Using CERT_NONE for testing only - this is intentionally insecure
300-
if AsyncoreConnection:
301-
conn = AsyncoreConnection(
302-
DefaultEndPoint('127.0.0.1', 9999),
303-
ssl_options={'cert_reqs': ssl.CERT_NONE}
304-
)
305-
elif AsyncioConnection:
306-
conn = AsyncioConnection(
307-
DefaultEndPoint('127.0.0.1', 9999),
308-
ssl_options={'cert_reqs': ssl.CERT_NONE}
309-
)
310-
else:
136+
if AsyncioConnection is None:
311137
self.skipTest("No connection implementation available")
312138

139+
conn = AsyncioConnection(
140+
DefaultEndPoint('127.0.0.1', 9999),
141+
ssl_options={'cert_reqs': ssl.CERT_NONE}
142+
)
143+
313144
# First error - the root cause
314145
root_cause = OSError(errno.ETIMEDOUT, "Connection timed out")
315146
conn.defunct(root_cause)

tests/unit/SSL_CONNECTION_TESTS.md

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -50,20 +50,13 @@ This test suite verifies that this mechanism works correctly across all SSL erro
5050
- `test_ssl_error_on_closed_connection_send_msg`: Error included in send_msg
5151

5252
### Integration Tests (`test_ssl_connection_failures.py`)
53-
8 integration tests with actual connection implementations:
53+
4 integration tests with actual connection implementations:
5454

55-
1. **AsyncoreConnection Tests** (5 tests)
56-
- Socket closed forcefully during send
57-
- Connection reset during recv
58-
- SSL handshake failure
59-
- Broken pipe on SSL socket
60-
- Concurrent operations on closing connection
61-
62-
2. **AsyncioConnection Tests** (2 tests)
55+
1. **AsyncioConnection Tests** (2 tests)
6356
- Socket error preservation
6457
- Connection reset handling
6558

66-
3. **Error Message Quality Tests** (2 tests)
59+
2. **Error Message Quality Tests** (2 tests)
6760
- Error message includes root cause
6861
- Multiple errors preserve first error
6962

@@ -92,7 +85,7 @@ The tests verify:
9285
- ✅ Concurrent operations see original error, not just "Bad file descriptor"
9386
- ✅ First error is preserved when multiple errors occur
9487
- ✅ Mechanism works across different error types (EBADF, EPIPE, ECONNRESET, etc.)
95-
-Both AsyncoreConnection and AsyncioConnection handle errors correctly
88+
- ✅ AsyncioConnection handles errors correctly
9689

9790
## Code Changes
9891

0 commit comments

Comments
 (0)