Skip to content

Commit a36aa63

Browse files
committed
test: add unit tests for EventletConnection TLS session caching
Add tests for TLS session caching in the eventlet reactor: - Cached session is applied via set_session() - Session is stored after successful handshake - Session reuse is detected and logged - Behavior without cache configured
1 parent 1671681 commit a36aa63

1 file changed

Lines changed: 172 additions & 0 deletions

File tree

tests/unit/io/test_eventletreactor.py

Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,3 +75,175 @@ def _timers(self):
7575

7676
# There is no unpatching because there is not a clear way
7777
# of doing it reliably
78+
79+
80+
try:
81+
from eventlet.green.OpenSSL import SSL as EventletSSL
82+
_HAS_EVENTLET_PYOPENSSL = True
83+
except ImportError:
84+
_HAS_EVENTLET_PYOPENSSL = False
85+
86+
87+
@notpypy
88+
@unittest.skipIf(skip_condition, "Skipping the eventlet tests because it's not installed")
89+
@unittest.skipIf(not _HAS_EVENTLET_PYOPENSSL, "PyOpenSSL not available for eventlet")
90+
class EventletTLSSessionCacheTest(unittest.TestCase):
91+
"""Test TLS session caching for EventletConnection with PyOpenSSL."""
92+
93+
@classmethod
94+
def setUpClass(cls):
95+
if skip_condition:
96+
return
97+
import eventlet
98+
eventlet.sleep()
99+
monkey_patch()
100+
EventletConnection.initialize_reactor()
101+
102+
def test_wrap_socket_applies_cached_session(self):
103+
"""Test that _wrap_socket_from_context applies cached TLS session."""
104+
from unittest.mock import Mock, MagicMock
105+
from cassandra.connection import DefaultEndPoint
106+
107+
# Create mock objects
108+
mock_cache = Mock()
109+
mock_session = Mock()
110+
mock_cache.get_session.return_value = mock_session
111+
112+
mock_ssl_context = MagicMock()
113+
mock_ssl_connection = MagicMock()
114+
115+
endpoint = DefaultEndPoint('127.0.0.1', 9042)
116+
117+
with patch('eventlet.green.socket.socket'):
118+
with patch.object(EventletConnection, '_connect_socket'):
119+
with patch.object(EventletConnection, '_send_options_message'):
120+
conn = EventletConnection(
121+
endpoint,
122+
cql_version='3.0.1',
123+
connect_timeout=5
124+
)
125+
conn.ssl_context = mock_ssl_context
126+
conn.ssl_options = {}
127+
conn.tls_session_cache = mock_cache
128+
conn._socket = Mock()
129+
130+
# Patch SSL.Connection to return our mock
131+
with patch('cassandra.io.eventletreactor.SSL.Connection', return_value=mock_ssl_connection):
132+
conn._wrap_socket_from_context()
133+
134+
# Verify get_session was called with endpoint
135+
mock_cache.get_session.assert_called_once_with(endpoint)
136+
137+
# Verify set_session was called on the SSL connection
138+
mock_ssl_connection.set_session.assert_called_once_with(mock_session)
139+
140+
def test_wrap_socket_no_session_when_cache_empty(self):
141+
"""Test that _wrap_socket_from_context handles empty cache."""
142+
from unittest.mock import Mock, MagicMock
143+
from cassandra.connection import DefaultEndPoint
144+
145+
mock_cache = Mock()
146+
mock_cache.get_session.return_value = None # No cached session
147+
148+
mock_ssl_context = MagicMock()
149+
mock_ssl_connection = MagicMock()
150+
151+
endpoint = DefaultEndPoint('127.0.0.1', 9042)
152+
153+
with patch('eventlet.green.socket.socket'):
154+
with patch.object(EventletConnection, '_connect_socket'):
155+
with patch.object(EventletConnection, '_send_options_message'):
156+
conn = EventletConnection(
157+
endpoint,
158+
cql_version='3.0.1',
159+
connect_timeout=5
160+
)
161+
conn.ssl_context = mock_ssl_context
162+
conn.ssl_options = {}
163+
conn.tls_session_cache = mock_cache
164+
conn._socket = Mock()
165+
166+
with patch('cassandra.io.eventletreactor.SSL.Connection', return_value=mock_ssl_connection):
167+
conn._wrap_socket_from_context()
168+
169+
# Verify get_session was called
170+
mock_cache.get_session.assert_called_once_with(endpoint)
171+
172+
# Verify set_session was NOT called on SSL connection (no cached session)
173+
mock_ssl_connection.set_session.assert_not_called()
174+
175+
def test_initiate_connection_stores_session_after_handshake(self):
176+
"""Test that _initiate_connection stores session after successful handshake."""
177+
from unittest.mock import Mock, MagicMock
178+
from cassandra.connection import DefaultEndPoint
179+
180+
mock_cache = Mock()
181+
mock_session = Mock()
182+
183+
mock_ssl_socket = MagicMock()
184+
mock_ssl_socket.get_session.return_value = mock_session
185+
mock_ssl_socket.session_reused.return_value = False
186+
187+
endpoint = DefaultEndPoint('127.0.0.1', 9042)
188+
189+
with patch('eventlet.green.socket.socket'):
190+
with patch.object(EventletConnection, '_connect_socket'):
191+
with patch.object(EventletConnection, '_send_options_message'):
192+
conn = EventletConnection(
193+
endpoint,
194+
cql_version='3.0.1',
195+
connect_timeout=5
196+
)
197+
conn.ssl_context = Mock()
198+
conn.ssl_options = {}
199+
conn.tls_session_cache = mock_cache
200+
conn._socket = mock_ssl_socket
201+
conn.uses_legacy_ssl_options = False
202+
203+
sockaddr = ('127.0.0.1', 9042)
204+
conn._initiate_connection(sockaddr)
205+
206+
# Verify handshake was called
207+
mock_ssl_socket.do_handshake.assert_called_once()
208+
209+
# Verify session was retrieved and stored
210+
mock_ssl_socket.get_session.assert_called_once()
211+
mock_cache.set_session.assert_called_once_with(endpoint, mock_session)
212+
213+
def test_initiate_connection_logs_session_reuse(self):
214+
"""Test that _initiate_connection logs when session is reused."""
215+
from unittest.mock import Mock, MagicMock
216+
from cassandra.connection import DefaultEndPoint
217+
218+
mock_cache = Mock()
219+
mock_session = Mock()
220+
221+
mock_ssl_socket = MagicMock()
222+
mock_ssl_socket.get_session.return_value = mock_session
223+
mock_ssl_socket.session_reused.return_value = True # Session was reused
224+
225+
endpoint = DefaultEndPoint('127.0.0.1', 9042)
226+
227+
with patch('eventlet.green.socket.socket'):
228+
with patch.object(EventletConnection, '_connect_socket'):
229+
with patch.object(EventletConnection, '_send_options_message'):
230+
conn = EventletConnection(
231+
endpoint,
232+
cql_version='3.0.1',
233+
connect_timeout=5
234+
)
235+
conn.ssl_context = Mock()
236+
conn.ssl_options = {}
237+
conn.tls_session_cache = mock_cache
238+
conn._socket = mock_ssl_socket
239+
conn.uses_legacy_ssl_options = False
240+
241+
with patch('cassandra.io.eventletreactor.log') as mock_log:
242+
sockaddr = ('127.0.0.1', 9042)
243+
conn._initiate_connection(sockaddr)
244+
245+
# Verify session_reused was checked
246+
mock_ssl_socket.session_reused.assert_called_once()
247+
248+
# Verify debug log was called for session reuse
249+
mock_log.debug.assert_called()

0 commit comments

Comments
 (0)