Skip to content

Commit ec4b411

Browse files
committed
Update test_api_metrics.py
1 parent ca946f2 commit ec4b411

File tree

1 file changed

+63
-53
lines changed

1 file changed

+63
-53
lines changed

tests/unit/automator/test_api_metrics.py

Lines changed: 63 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,25 @@ def setUp(self):
6060
update_interval=0.1
6161
)
6262

63+
# Set up metrics collector mock to avoid real background processes
64+
self.metrics_collector_mock = Mock()
65+
self.metrics_collector_mock.record_api_request_time = Mock()
66+
67+
# Start the patch
68+
self.metrics_collector_patch = patch(
69+
'conductor.client.automator.task_runner_asyncio.MetricsCollector',
70+
return_value=self.metrics_collector_mock
71+
)
72+
self.metrics_collector_patch.start()
73+
6374
def tearDown(self):
6475
"""Clean up test fixtures"""
76+
# Reset the mock for next test
77+
self.metrics_collector_mock.reset_mock()
78+
79+
# Stop the patch
80+
self.metrics_collector_patch.stop()
81+
6582
if os.path.exists(self.metrics_dir):
6683
shutil.rmtree(self.metrics_dir)
6784

@@ -73,9 +90,6 @@ def test_api_timing_successful_poll(self):
7390
metrics_settings=self.metrics_settings
7491
)
7592

76-
# Mock the metrics_collector's record method
77-
runner.metrics_collector.record_api_request_time = Mock()
78-
7993
# Mock successful HTTP response
8094
mock_response = Mock()
8195
mock_response.status_code = 200
@@ -89,8 +103,8 @@ async def run_test():
89103
await runner._poll_tasks_from_server(count=1)
90104

91105
# Verify API timing was recorded
92-
runner.metrics_collector.record_api_request_time.assert_called()
93-
call_args = runner.metrics_collector.record_api_request_time.call_args
106+
self.metrics_collector_mock.record_api_request_time.assert_called()
107+
call_args = self.metrics_collector_mock.record_api_request_time.call_args
94108

95109
# Check parameters
96110
self.assertEqual(call_args.kwargs['method'], 'GET')
@@ -109,9 +123,6 @@ def test_api_timing_failed_poll_with_status_code(self):
109123
metrics_settings=self.metrics_settings
110124
)
111125

112-
# Mock the metrics_collector\'s record method
113-
runner.metrics_collector.record_api_request_time = Mock()
114-
115126
# Mock HTTP error with response
116127
mock_response = Mock()
117128
mock_response.status_code = 500
@@ -128,8 +139,8 @@ async def run_test():
128139
pass
129140

130141
# Verify API timing was recorded with error status
131-
runner.metrics_collector.record_api_request_time.assert_called()
132-
call_args = runner.metrics_collector.record_api_request_time.call_args
142+
self.metrics_collector_mock.record_api_request_time.assert_called()
143+
call_args = self.metrics_collector_mock.record_api_request_time.call_args
133144

134145
self.assertEqual(call_args.kwargs['method'], 'GET')
135146
self.assertEqual(call_args.kwargs['status'], '500')
@@ -145,9 +156,6 @@ def test_api_timing_failed_poll_without_status_code(self):
145156
metrics_settings=self.metrics_settings
146157
)
147158

148-
# Mock the metrics_collector\'s record method
149-
runner.metrics_collector.record_api_request_time = Mock()
150-
151159
# Mock generic network error
152160
error = httpx.ConnectError("Connection refused")
153161

@@ -162,8 +170,8 @@ async def run_test():
162170
pass
163171

164172
# Verify API timing was recorded with "error" status
165-
runner.metrics_collector.record_api_request_time.assert_called()
166-
call_args = runner.metrics_collector.record_api_request_time.call_args
173+
self.metrics_collector_mock.record_api_request_time.assert_called()
174+
call_args = self.metrics_collector_mock.record_api_request_time.call_args
167175

168176
self.assertEqual(call_args.kwargs['method'], 'GET')
169177
self.assertEqual(call_args.kwargs['status'], 'error')
@@ -178,8 +186,6 @@ def test_api_timing_successful_update(self):
178186
metrics_settings=self.metrics_settings
179187
)
180188

181-
# Mock the metrics_collector's record method
182-
runner.metrics_collector.record_api_request_time = Mock()
183189

184190
# Create task result
185191
task_result = TaskResult(
@@ -202,8 +208,8 @@ async def run_test():
202208
await runner._update_task(task_result)
203209

204210
# Verify API timing was recorded
205-
runner.metrics_collector.record_api_request_time.assert_called()
206-
call_args = runner.metrics_collector.record_api_request_time.call_args
211+
self.metrics_collector_mock.record_api_request_time.assert_called()
212+
call_args = self.metrics_collector_mock.record_api_request_time.call_args
207213

208214
self.assertEqual(call_args.kwargs['method'], 'POST')
209215
self.assertIn('/tasks/update', call_args.kwargs['uri'])
@@ -220,37 +226,40 @@ def test_api_timing_failed_update(self):
220226
metrics_settings=self.metrics_settings
221227
)
222228

223-
# Mock the metrics_collector's record method
224-
runner.metrics_collector.record_api_request_time = Mock()
225-
226229
# Create task result with required fields
227230
task_result = TaskResult(
228231
task_id='task1',
229232
workflow_instance_id='wf1',
230233
status=TaskResultStatus.COMPLETED
231234
)
232235

233-
# Mock HTTP error
234-
mock_response = Mock()
235-
mock_response.status_code = 503
236-
error = httpx.HTTPStatusError("Service unavailable", request=Mock(), response=mock_response)
236+
# Mock HTTP error for first call, then success to avoid retries
237+
mock_error_response = Mock()
238+
mock_error_response.status_code = 503
239+
error = httpx.HTTPStatusError("Service unavailable", request=Mock(), response=mock_error_response)
240+
241+
mock_success_response = Mock()
242+
mock_success_response.status_code = 200
243+
mock_success_response.text = ''
237244

238245
async def run_test():
239246
runner.http_client = AsyncMock()
240-
runner.http_client.post = AsyncMock(side_effect=error)
247+
# First call fails with 503, second call succeeds (to avoid 14s of retries)
248+
runner.http_client.post = AsyncMock(side_effect=[error, mock_success_response])
241249

242-
# Call update (only needs task_result)
243-
try:
250+
# Mock asyncio.sleep to avoid waiting during retry
251+
with patch('asyncio.sleep', new_callable=AsyncMock):
252+
# Call update - will fail once then succeed on retry
244253
await runner._update_task(task_result)
245-
except:
246-
pass
247254

248-
# Verify API timing was recorded
249-
runner.metrics_collector.record_api_request_time.assert_called()
250-
call_args = runner.metrics_collector.record_api_request_time.call_args
255+
# Verify API timing was recorded for the failed request
256+
# The first call should have recorded the 503 error
257+
self.metrics_collector_mock.record_api_request_time.assert_called()
251258

252-
self.assertEqual(call_args.kwargs['method'], 'POST')
253-
self.assertEqual(call_args.kwargs['status'], '503')
259+
# Check the first call (which failed)
260+
first_call = self.metrics_collector_mock.record_api_request_time.call_args_list[0]
261+
self.assertEqual(first_call.kwargs['method'], 'POST')
262+
self.assertEqual(first_call.kwargs['status'], '503')
254263

255264
asyncio.run(run_test())
256265

@@ -262,8 +271,6 @@ def test_api_timing_multiple_requests(self):
262271
metrics_settings=self.metrics_settings
263272
)
264273

265-
# Mock the metrics_collector's record method
266-
runner.metrics_collector.record_api_request_time = Mock()
267274

268275
mock_response = Mock()
269276
mock_response.status_code = 200
@@ -279,10 +286,10 @@ async def run_test():
279286
await runner._poll_tasks_from_server(count=1)
280287

281288
# Should have 3 API timing records
282-
self.assertEqual(runner.metrics_collector.record_api_request_time.call_count, 3)
289+
self.assertEqual(self.metrics_collector_mock.record_api_request_time.call_count,3)
283290

284291
# All should be successful
285-
for call in runner.metrics_collector.record_api_request_time.call_args_list:
292+
for call in self.metrics_collector_mock.record_api_request_time.call_args_list:
286293
self.assertEqual(call.kwargs['status'], '200')
287294

288295
asyncio.run(run_test())
@@ -318,9 +325,6 @@ def test_api_timing_precision(self):
318325
metrics_settings=self.metrics_settings
319326
)
320327

321-
# Mock the metrics_collector\'s record method
322-
runner.metrics_collector.record_api_request_time = Mock()
323-
324328
# Mock fast response
325329
mock_response = Mock()
326330
mock_response.status_code = 200
@@ -339,7 +343,7 @@ async def mock_get(*args, **kwargs):
339343
await runner._poll_tasks_from_server(count=1)
340344

341345
# Verify timing captured sub-second precision
342-
call_args = runner.metrics_collector.record_api_request_time.call_args
346+
call_args = self.metrics_collector_mock.record_api_request_time.call_args
343347
time_spent = call_args.kwargs['time_spent']
344348

345349
# Should be at least 1ms, but less than 100ms
@@ -356,8 +360,6 @@ def test_api_timing_auth_error_401(self):
356360
metrics_settings=self.metrics_settings
357361
)
358362

359-
# Mock the metrics_collector's record method
360-
runner.metrics_collector.record_api_request_time = Mock()
361363

362364
mock_response = Mock()
363365
mock_response.status_code = 401
@@ -373,7 +375,7 @@ async def run_test():
373375
pass
374376

375377
# Verify 401 status captured
376-
call_args = runner.metrics_collector.record_api_request_time.call_args
378+
call_args = self.metrics_collector_mock.record_api_request_time.call_args
377379
self.assertEqual(call_args.kwargs['status'], '401')
378380

379381
asyncio.run(run_test())
@@ -386,8 +388,6 @@ def test_api_timing_timeout_error(self):
386388
metrics_settings=self.metrics_settings
387389
)
388390

389-
# Mock the metrics_collector's record method
390-
runner.metrics_collector.record_api_request_time = Mock()
391391

392392
error = httpx.TimeoutException("Request timeout")
393393

@@ -401,7 +401,7 @@ async def run_test():
401401
pass
402402

403403
# Verify "error" status for timeout
404-
call_args = runner.metrics_collector.record_api_request_time.call_args
404+
call_args = self.metrics_collector_mock.record_api_request_time.call_args
405405
self.assertEqual(call_args.kwargs['status'], 'error')
406406

407407
asyncio.run(run_test())
@@ -414,8 +414,6 @@ def test_api_timing_concurrent_requests(self):
414414
metrics_settings=self.metrics_settings
415415
)
416416

417-
# Mock the metrics_collector's record method
418-
runner.metrics_collector.record_api_request_time = Mock()
419417

420418
mock_response = Mock()
421419
mock_response.status_code = 200
@@ -431,10 +429,22 @@ async def run_test():
431429
])
432430

433431
# Should have 5 timing records
434-
self.assertEqual(runner.metrics_collector.record_api_request_time.call_count, 5)
432+
self.assertEqual(self.metrics_collector_mock.record_api_request_time.call_count,5)
435433

436434
asyncio.run(run_test())
437435

438436

437+
def tearDownModule():
438+
"""Module-level teardown to clean up any lingering resources"""
439+
import gc
440+
import time
441+
442+
# Force garbage collection
443+
gc.collect()
444+
445+
# Small delay to let async resources clean up
446+
time.sleep(0.1)
447+
448+
439449
if __name__ == '__main__':
440450
unittest.main()

0 commit comments

Comments
 (0)