|
8 | 8 | """ |
9 | 9 |
|
10 | 10 | from datetime import datetime, timedelta, timezone |
| 11 | +from unittest.mock import AsyncMock, MagicMock, patch |
11 | 12 |
|
12 | 13 | import pytest |
13 | 14 | from grpc.aio import AioRpcError |
@@ -291,6 +292,106 @@ async def test_retry_finished_job_no_effect(self, jobs_api_async): |
291 | 292 | with pytest.raises(AioRpcError, match="job cannot be retried"): |
292 | 293 | await jobs_api_async.retry(job) |
293 | 294 |
|
| 295 | + class TestWaitUntilComplete: |
| 296 | + """Tests for the async wait_until_complete method.""" |
| 297 | + |
| 298 | + @pytest.mark.asyncio |
| 299 | + async def test_returns_immediately_when_job_already_complete(self, jobs_api_async): |
| 300 | + """When get returns a completed job on first call, wait returns immediately.""" |
| 301 | + job_id = "test-job-id" |
| 302 | + mock_job = MagicMock() |
| 303 | + mock_job.job_status = JobStatus.FINISHED |
| 304 | + |
| 305 | + with patch( |
| 306 | + "sift_client.resources.jobs.JobsAPIAsync.get", |
| 307 | + new_callable=AsyncMock, |
| 308 | + return_value=mock_job, |
| 309 | + ) as mock_get: |
| 310 | + result = await jobs_api_async.wait_until_complete(job=job_id) |
| 311 | + |
| 312 | + assert result is mock_job |
| 313 | + assert result.job_status == JobStatus.FINISHED |
| 314 | + mock_get.assert_called_once_with(job_id) |
| 315 | + |
| 316 | + @pytest.mark.asyncio |
| 317 | + async def test_returns_immediately_when_job_already_failed(self, jobs_api_async): |
| 318 | + """When get returns a failed job on first call, wait returns immediately.""" |
| 319 | + job_id = "test-job-id" |
| 320 | + mock_job = MagicMock() |
| 321 | + mock_job.job_status = JobStatus.FAILED |
| 322 | + |
| 323 | + with patch( |
| 324 | + "sift_client.resources.jobs.JobsAPIAsync.get", |
| 325 | + new_callable=AsyncMock, |
| 326 | + return_value=mock_job, |
| 327 | + ) as mock_get: |
| 328 | + result = await jobs_api_async.wait_until_complete(job=job_id) |
| 329 | + |
| 330 | + assert result is mock_job |
| 331 | + assert result.job_status == JobStatus.FAILED |
| 332 | + mock_get.assert_called_once_with(job_id) |
| 333 | + |
| 334 | + @pytest.mark.asyncio |
| 335 | + async def test_returns_immediately_when_job_already_cancelled(self, jobs_api_async): |
| 336 | + """When get returns a cancelled job on first call, wait returns immediately.""" |
| 337 | + job_id = "test-job-id" |
| 338 | + mock_job = MagicMock() |
| 339 | + mock_job.job_status = JobStatus.CANCELLED |
| 340 | + |
| 341 | + with patch( |
| 342 | + "sift_client.resources.jobs.JobsAPIAsync.get", |
| 343 | + new_callable=AsyncMock, |
| 344 | + return_value=mock_job, |
| 345 | + ) as mock_get: |
| 346 | + result = await jobs_api_async.wait_until_complete(job=job_id) |
| 347 | + |
| 348 | + assert result is mock_job |
| 349 | + assert result.job_status == JobStatus.CANCELLED |
| 350 | + mock_get.assert_called_once_with(job_id) |
| 351 | + |
| 352 | + @pytest.mark.asyncio |
| 353 | + async def test_polls_until_complete(self, jobs_api_async): |
| 354 | + """When get returns running then finished, wait returns after second poll.""" |
| 355 | + job_id = "test-job-id" |
| 356 | + running_job = MagicMock() |
| 357 | + running_job.job_status = JobStatus.RUNNING |
| 358 | + finished_job = MagicMock() |
| 359 | + finished_job.job_status = JobStatus.FINISHED |
| 360 | + |
| 361 | + with patch( |
| 362 | + "sift_client.resources.jobs.JobsAPIAsync.get", |
| 363 | + new_callable=AsyncMock, |
| 364 | + side_effect=[running_job, finished_job], |
| 365 | + ) as mock_get: |
| 366 | + result = await jobs_api_async.wait_until_complete( |
| 367 | + job=job_id, |
| 368 | + polling_interval_secs=0.01, |
| 369 | + timeout_secs=10.0, |
| 370 | + ) |
| 371 | + |
| 372 | + assert result is finished_job |
| 373 | + assert result.job_status == JobStatus.FINISHED |
| 374 | + assert mock_get.call_count == 2 |
| 375 | + |
| 376 | + @pytest.mark.asyncio |
| 377 | + async def test_raises_timeout_error_when_not_complete_in_time(self, jobs_api_async): |
| 378 | + """When job never reaches a completed state, TimeoutError is raised.""" |
| 379 | + job_id = "test-job-id" |
| 380 | + running_job = MagicMock() |
| 381 | + running_job.job_status = JobStatus.RUNNING |
| 382 | + |
| 383 | + with patch( |
| 384 | + "sift_client.resources.jobs.JobsAPIAsync.get", |
| 385 | + new_callable=AsyncMock, |
| 386 | + return_value=running_job, |
| 387 | + ): |
| 388 | + with pytest.raises(TimeoutError): |
| 389 | + await jobs_api_async.wait_until_complete( |
| 390 | + job=job_id, |
| 391 | + polling_interval_secs=0.05, |
| 392 | + timeout_secs=0.1, |
| 393 | + ) |
| 394 | + |
294 | 395 | class TestJobProperties: |
295 | 396 | """Tests for job property methods.""" |
296 | 397 |
|
|
0 commit comments