8
8
NoopTelemetryClient ,
9
9
TelemetryClientFactory ,
10
10
TelemetryHelper ,
11
- BaseTelemetryClient
11
+ BaseTelemetryClient ,
12
12
)
13
13
from databricks .sql .telemetry .models .enums import AuthMech , AuthFlow
14
14
from databricks .sql .auth .authenticators import (
@@ -24,7 +24,7 @@ def mock_telemetry_client():
24
24
session_id = str (uuid .uuid4 ())
25
25
auth_provider = AccessTokenAuthProvider ("test-token" )
26
26
executor = MagicMock ()
27
-
27
+
28
28
return TelemetryClient (
29
29
telemetry_enabled = True ,
30
30
session_id_hex = session_id ,
@@ -43,7 +43,7 @@ def test_noop_client_behavior(self):
43
43
client1 = NoopTelemetryClient ()
44
44
client2 = NoopTelemetryClient ()
45
45
assert client1 is client2
46
-
46
+
47
47
# Test that all methods can be called without exceptions
48
48
client1 .export_initial_telemetry_log (MagicMock (), "test-agent" )
49
49
client1 .export_failure_log ("TestError" , "Test message" )
@@ -58,76 +58,76 @@ def test_event_batching_and_flushing_flow(self, mock_telemetry_client):
58
58
"""Test the complete event batching and flushing flow."""
59
59
client = mock_telemetry_client
60
60
client ._batch_size = 3 # Small batch for testing
61
-
61
+
62
62
# Mock the network call
63
- with patch .object (client , ' _send_telemetry' ) as mock_send :
63
+ with patch .object (client , " _send_telemetry" ) as mock_send :
64
64
# Add events one by one - should not flush yet
65
65
client ._export_event ("event1" )
66
66
client ._export_event ("event2" )
67
67
mock_send .assert_not_called ()
68
68
assert len (client ._events_batch ) == 2
69
-
69
+
70
70
# Third event should trigger flush
71
71
client ._export_event ("event3" )
72
72
mock_send .assert_called_once ()
73
73
assert len (client ._events_batch ) == 0 # Batch cleared after flush
74
-
75
- @patch (' requests.post' )
74
+
75
+ @patch (" requests.post" )
76
76
def test_network_request_flow (self , mock_post , mock_telemetry_client ):
77
77
"""Test the complete network request flow with authentication."""
78
78
mock_post .return_value .status_code = 200
79
79
client = mock_telemetry_client
80
-
80
+
81
81
# Create mock events
82
82
mock_events = [MagicMock () for _ in range (2 )]
83
83
for i , event in enumerate (mock_events ):
84
84
event .to_json .return_value = f'{{"event": "{ i } "}}'
85
-
85
+
86
86
# Send telemetry
87
87
client ._send_telemetry (mock_events )
88
-
88
+
89
89
# Verify request was submitted to executor
90
90
client ._executor .submit .assert_called_once ()
91
91
args , kwargs = client ._executor .submit .call_args
92
-
92
+
93
93
# Verify correct function and URL
94
94
assert args [0 ] == requests .post
95
- assert args [1 ] == ' https://test-host.com/telemetry-ext'
96
- assert kwargs [' headers' ][ ' Authorization' ] == ' Bearer test-token'
97
-
95
+ assert args [1 ] == " https://test-host.com/telemetry-ext"
96
+ assert kwargs [" headers" ][ " Authorization" ] == " Bearer test-token"
97
+
98
98
# Verify request body structure
99
- request_data = kwargs [' data' ]
99
+ request_data = kwargs [" data" ]
100
100
assert '"uploadTime"' in request_data
101
101
assert '"protoLogs"' in request_data
102
102
103
103
def test_telemetry_logging_flows (self , mock_telemetry_client ):
104
104
"""Test all telemetry logging methods work end-to-end."""
105
105
client = mock_telemetry_client
106
-
107
- with patch .object (client , ' _export_event' ) as mock_export :
106
+
107
+ with patch .object (client , " _export_event" ) as mock_export :
108
108
# Test initial log
109
109
client .export_initial_telemetry_log (MagicMock (), "test-agent" )
110
110
assert mock_export .call_count == 1
111
-
111
+
112
112
# Test failure log
113
113
client .export_failure_log ("TestError" , "Error message" )
114
114
assert mock_export .call_count == 2
115
-
115
+
116
116
# Test latency log
117
117
client .export_latency_log (150 , "EXECUTE_STATEMENT" , "stmt-123" )
118
118
assert mock_export .call_count == 3
119
119
120
120
def test_error_handling_resilience (self , mock_telemetry_client ):
121
121
"""Test that telemetry errors don't break the client."""
122
122
client = mock_telemetry_client
123
-
123
+
124
124
# Test that exceptions in telemetry don't propagate
125
- with patch .object (client , ' _export_event' , side_effect = Exception ("Test error" )):
125
+ with patch .object (client , " _export_event" , side_effect = Exception ("Test error" )):
126
126
# These should not raise exceptions
127
127
client .export_initial_telemetry_log (MagicMock (), "test-agent" )
128
128
client .export_failure_log ("TestError" , "Error message" )
129
129
client .export_latency_log (100 , "EXECUTE_STATEMENT" , "stmt-123" )
130
-
130
+
131
131
# Test executor submission failure
132
132
client ._executor .submit .side_effect = Exception ("Thread pool error" )
133
133
client ._send_telemetry ([MagicMock ()]) # Should not raise
@@ -140,7 +140,7 @@ def test_system_configuration_caching(self):
140
140
"""Test that system configuration is cached and contains expected data."""
141
141
config1 = TelemetryHelper .get_driver_system_configuration ()
142
142
config2 = TelemetryHelper .get_driver_system_configuration ()
143
-
143
+
144
144
# Should be cached (same instance)
145
145
assert config1 is config2
146
146
@@ -153,7 +153,7 @@ def test_auth_mechanism_detection(self):
153
153
(MagicMock (), AuthMech .OTHER ), # Unknown provider
154
154
(None , None ),
155
155
]
156
-
156
+
157
157
for provider , expected in test_cases :
158
158
assert TelemetryHelper .get_auth_mechanism (provider ) == expected
159
159
@@ -163,19 +163,25 @@ def test_auth_flow_detection(self):
163
163
oauth_with_tokens = MagicMock (spec = DatabricksOAuthProvider )
164
164
oauth_with_tokens ._access_token = "test-access-token"
165
165
oauth_with_tokens ._refresh_token = "test-refresh-token"
166
- assert TelemetryHelper .get_auth_flow (oauth_with_tokens ) == AuthFlow .TOKEN_PASSTHROUGH
167
-
166
+ assert (
167
+ TelemetryHelper .get_auth_flow (oauth_with_tokens )
168
+ == AuthFlow .TOKEN_PASSTHROUGH
169
+ )
170
+
168
171
# Test OAuth with browser-based auth
169
172
oauth_with_browser = MagicMock (spec = DatabricksOAuthProvider )
170
173
oauth_with_browser ._access_token = None
171
174
oauth_with_browser ._refresh_token = None
172
175
oauth_with_browser .oauth_manager = MagicMock ()
173
- assert TelemetryHelper .get_auth_flow (oauth_with_browser ) == AuthFlow .BROWSER_BASED_AUTHENTICATION
174
-
176
+ assert (
177
+ TelemetryHelper .get_auth_flow (oauth_with_browser )
178
+ == AuthFlow .BROWSER_BASED_AUTHENTICATION
179
+ )
180
+
175
181
# Test non-OAuth provider
176
182
pat_auth = AccessTokenAuthProvider ("test-token" )
177
183
assert TelemetryHelper .get_auth_flow (pat_auth ) is None
178
-
184
+
179
185
# Test None auth provider
180
186
assert TelemetryHelper .get_auth_flow (None ) is None
181
187
@@ -202,56 +208,58 @@ def test_client_lifecycle_flow(self):
202
208
"""Test complete client lifecycle: initialize -> use -> close."""
203
209
session_id_hex = "test-session"
204
210
auth_provider = AccessTokenAuthProvider ("token" )
205
-
211
+
206
212
# Initialize enabled client
207
213
TelemetryClientFactory .initialize_telemetry_client (
208
214
telemetry_enabled = True ,
209
215
session_id_hex = session_id_hex ,
210
216
auth_provider = auth_provider ,
211
- host_url = "test-host.com"
217
+ host_url = "test-host.com" ,
212
218
)
213
-
219
+
214
220
client = TelemetryClientFactory .get_telemetry_client (session_id_hex )
215
221
assert isinstance (client , TelemetryClient )
216
222
assert client ._session_id_hex == session_id_hex
217
-
223
+
218
224
# Close client
219
- with patch .object (client , ' close' ) as mock_close :
225
+ with patch .object (client , " close" ) as mock_close :
220
226
TelemetryClientFactory .close (session_id_hex )
221
227
mock_close .assert_called_once ()
222
-
228
+
223
229
# Should get NoopTelemetryClient after close
224
230
client = TelemetryClientFactory .get_telemetry_client (session_id_hex )
225
231
assert isinstance (client , NoopTelemetryClient )
226
232
227
233
def test_disabled_telemetry_flow (self ):
228
234
"""Test that disabled telemetry uses NoopTelemetryClient."""
229
235
session_id_hex = "test-session"
230
-
236
+
231
237
TelemetryClientFactory .initialize_telemetry_client (
232
238
telemetry_enabled = False ,
233
239
session_id_hex = session_id_hex ,
234
240
auth_provider = None ,
235
- host_url = "test-host.com"
241
+ host_url = "test-host.com" ,
236
242
)
237
-
243
+
238
244
client = TelemetryClientFactory .get_telemetry_client (session_id_hex )
239
245
assert isinstance (client , NoopTelemetryClient )
240
246
241
247
def test_factory_error_handling (self ):
242
248
"""Test that factory errors fall back to NoopTelemetryClient."""
243
249
session_id = "test-session"
244
-
250
+
245
251
# Simulate initialization error
246
- with patch ('databricks.sql.telemetry.telemetry_client.TelemetryClient' ,
247
- side_effect = Exception ("Init error" )):
252
+ with patch (
253
+ "databricks.sql.telemetry.telemetry_client.TelemetryClient" ,
254
+ side_effect = Exception ("Init error" ),
255
+ ):
248
256
TelemetryClientFactory .initialize_telemetry_client (
249
257
telemetry_enabled = True ,
250
258
session_id_hex = session_id ,
251
259
auth_provider = AccessTokenAuthProvider ("token" ),
252
- host_url = "test-host.com"
260
+ host_url = "test-host.com" ,
253
261
)
254
-
262
+
255
263
# Should fall back to NoopTelemetryClient
256
264
client = TelemetryClientFactory .get_telemetry_client (session_id )
257
265
assert isinstance (client , NoopTelemetryClient )
@@ -260,25 +268,25 @@ def test_factory_shutdown_flow(self):
260
268
"""Test factory shutdown when last client is removed."""
261
269
session1 = "session-1"
262
270
session2 = "session-2"
263
-
271
+
264
272
# Initialize multiple clients
265
273
for session in [session1 , session2 ]:
266
274
TelemetryClientFactory .initialize_telemetry_client (
267
275
telemetry_enabled = True ,
268
276
session_id_hex = session ,
269
277
auth_provider = AccessTokenAuthProvider ("token" ),
270
- host_url = "test-host.com"
278
+ host_url = "test-host.com" ,
271
279
)
272
-
280
+
273
281
# Factory should be initialized
274
282
assert TelemetryClientFactory ._initialized is True
275
283
assert TelemetryClientFactory ._executor is not None
276
-
284
+
277
285
# Close first client - factory should stay initialized
278
286
TelemetryClientFactory .close (session1 )
279
287
assert TelemetryClientFactory ._initialized is True
280
-
288
+
281
289
# Close second client - factory should shut down
282
290
TelemetryClientFactory .close (session2 )
283
291
assert TelemetryClientFactory ._initialized is False
284
- assert TelemetryClientFactory ._executor is None
292
+ assert TelemetryClientFactory ._executor is None
0 commit comments