|
18 | 18 | from openeo.rest.job import BatchJob, ResultAsset |
19 | 19 | from openeo.rest.models.general import Link |
20 | 20 | from openeo.rest.models.logs import LogEntry |
| 21 | +from openeo.utils.http import ( |
| 22 | + HTTP_402_PAYMENT_REQUIRED, |
| 23 | + HTTP_429_TOO_MANY_REQUESTS, |
| 24 | + HTTP_500_INTERNAL_SERVER_ERROR, |
| 25 | + HTTP_502_BAD_GATEWAY, |
| 26 | + HTTP_503_SERVICE_UNAVAILABLE, |
| 27 | +) |
21 | 28 |
|
22 | 29 | API_URL = "https://oeo.test" |
23 | 30 |
|
@@ -320,28 +327,35 @@ def test_execute_batch_with_excessive_soft_errors(con100, requests_mock, tmpdir, |
320 | 327 | [ |
321 | 328 | ( # Default retry settings |
322 | 329 | None, |
323 | | - [ |
324 | | - httpretty.Response(status=502, body="Bad Gateway"), |
325 | | - httpretty.Response(status=504, body="Service Unavailable"), |
326 | | - ], |
| 330 | + [], |
327 | 331 | contextlib.nullcontext(), |
328 | | - [0.1, 23, 34], |
| 332 | + [23, 34], |
329 | 333 | ), |
330 | | - ( |
331 | | - # Only retry on 429 (and fail on 500) |
332 | | - {"status_forcelist": [429]}, |
333 | | - [ |
334 | | - httpretty.Response(status=500, body="Internal Server Error"), |
335 | | - ], |
| 334 | + ( # Default config with a generic 500 error |
| 335 | + None, |
| 336 | + [httpretty.Response(status=HTTP_500_INTERNAL_SERVER_ERROR, body="Internal Server Error")], |
336 | 337 | pytest.raises(OpenEoApiPlainError, match=re.escape("[500] Internal Server Error")), |
337 | | - [0.1, 23], |
| 338 | + [23], |
| 339 | + ), |
| 340 | + ( # Default config with a 503 error (skipped by soft error feature of execute_batch poll loop) |
| 341 | + None, |
| 342 | + [httpretty.Response(status=HTTP_503_SERVICE_UNAVAILABLE, body="Service Unavailable")], |
| 343 | + contextlib.nullcontext(), |
| 344 | + [23, 12.34, 34], |
338 | 345 | ), |
339 | 346 | ( |
340 | | - # No retry setup |
| 347 | + # Explicit status_forcelist with custom status code to retry |
| 348 | + {"status_forcelist": [HTTP_429_TOO_MANY_REQUESTS, HTTP_402_PAYMENT_REQUIRED]}, |
| 349 | + [httpretty.Response(status=HTTP_402_PAYMENT_REQUIRED, body="Payment Required")], |
| 350 | + contextlib.nullcontext(), |
| 351 | + [23, 34], |
| 352 | + ), |
| 353 | + ( |
| 354 | + # No retry setup: also fail on 429 |
341 | 355 | False, |
342 | 356 | [], |
343 | 357 | pytest.raises(OpenEoApiPlainError, match=re.escape("[429] Too Many Requests")), |
344 | | - [0.1], |
| 358 | + [], |
345 | 359 | ), |
346 | 360 | ], |
347 | 361 | ) |
@@ -401,12 +415,17 @@ def test_execute_batch_retry_after_429_too_many_requests( |
401 | 415 |
|
402 | 416 | con = openeo.connect(API_URL, retry=retry_config) |
403 | 417 |
|
| 418 | + max_poll_interval = 0.1 |
| 419 | + connection_retry_interval = 12.34 |
404 | 420 | with mock.patch("time.sleep") as sleep_mock: |
405 | 421 | job = con.load_collection("SENTINEL2").create_job() |
406 | 422 | with expectation_context: |
407 | | - job.start_and_wait(max_poll_interval=0.1) |
| 423 | + job.start_and_wait(max_poll_interval=max_poll_interval, connection_retry_interval=connection_retry_interval) |
408 | 424 |
|
409 | | - assert sleep_mock.call_args_list == dirty_equals.Contains(*(mock.call(s) for s in expected_sleeps)) |
| 425 | + # Check retry related sleeps |
| 426 | + actual_sleeps = [args[0] for args, kwargs in sleep_mock.call_args_list] |
| 427 | + actual_sleeps = [s for s in actual_sleeps if s != max_poll_interval] |
| 428 | + assert actual_sleeps == expected_sleeps |
410 | 429 |
|
411 | 430 |
|
412 | 431 | class LogGenerator: |
|
0 commit comments