|
7 | 7 |
|
8 | 8 | import asyncio |
9 | 9 | import datetime |
| 10 | +from typing import Literal |
10 | 11 | from unittest import mock |
11 | 12 |
|
12 | 13 | import pytest |
|
18 | 19 | SEMAPHORE_HOLDER_KEY_PREFIX, |
19 | 20 | SEMAPHORE_KEY_PREFIX, |
20 | 21 | ) |
| 22 | +from servicelib.redis._errors import SemaphoreLostError |
21 | 23 | from servicelib.redis._semaphore import ( |
22 | 24 | DistributedSemaphore, |
23 | 25 | SemaphoreAcquisitionError, |
24 | 26 | SemaphoreNotAcquiredError, |
25 | | - with_limited_concurrency, |
26 | 27 | ) |
| 28 | +from servicelib.redis._semaphore_decorator import with_limited_concurrency |
27 | 29 |
|
28 | 30 | pytest_simcore_core_services_selection = [ |
29 | 31 | "redis", |
@@ -318,7 +320,7 @@ async def test_semaphore_auto_renewal_via_decorator( |
318 | 320 | capacity=semaphore_capacity, |
319 | 321 | ttl=short_ttl, |
320 | 322 | ) |
321 | | - async def long_running_work(): |
| 323 | + async def long_running_work() -> Literal["success"]: |
322 | 324 | work_started.set() |
323 | 325 | # Wait longer than TTL to ensure renewal works |
324 | 326 | await asyncio.sleep(short_ttl.total_seconds() * 2) |
@@ -354,52 +356,41 @@ async def test_decorator_auto_renewal_failure_propagation( |
354 | 356 | semaphore_name: str, |
355 | 357 | semaphore_capacity: int, |
356 | 358 | short_ttl: datetime.timedelta, |
357 | | - monkeypatch: pytest.MonkeyPatch, |
358 | 359 | ): |
359 | 360 | """Test that auto-renewal failures properly propagate as exceptions in the decorator""" |
360 | | - from servicelib.redis._semaphore_decorator import _renew_semaphore_entry |
361 | | - |
362 | | - class RenewalFailureError(Exception): |
363 | | - """Custom exception for testing renewal failures""" |
364 | 361 |
|
365 | 362 | work_started = asyncio.Event() |
366 | 363 |
|
367 | | - # Mock the renewal function to fail after first call |
368 | | - call_count = 0 |
369 | | - original_renew = _renew_semaphore_entry |
370 | | - |
371 | | - async def failing_renew_semaphore_entry(semaphore): |
372 | | - nonlocal call_count |
373 | | - call_count += 1 |
374 | | - if call_count <= 1: |
375 | | - # First call succeeds |
376 | | - await original_renew(semaphore) |
377 | | - else: |
378 | | - # Subsequent calls fail |
379 | | - raise RenewalFailureError("Simulated renewal failure") |
380 | | - |
381 | | - monkeypatch.setattr( |
382 | | - "servicelib.redis._semaphore_decorator._renew_semaphore_entry", |
383 | | - failing_renew_semaphore_entry, |
384 | | - ) |
385 | | - |
386 | 364 | @with_limited_concurrency( |
387 | 365 | redis_client_sdk, |
388 | 366 | key=semaphore_name, |
389 | 367 | capacity=semaphore_capacity, |
390 | 368 | ttl=short_ttl, |
391 | 369 | ) |
392 | | - async def work_that_should_fail(): |
| 370 | + async def work_that_should_fail() -> Literal["should not reach here"]: |
393 | 371 | work_started.set() |
394 | 372 | # Wait long enough for renewal to be attempted multiple times |
395 | | - await asyncio.sleep(short_ttl.total_seconds() * 1.5) |
| 373 | + await asyncio.sleep(short_ttl.total_seconds() * 4) |
396 | 374 | return "should not reach here" |
397 | 375 |
|
398 | 376 | # The decorator should propagate the renewal failure |
399 | 377 | task = asyncio.create_task(work_that_should_fail()) |
400 | 378 | await work_started.wait() # Wait for work to start |
401 | 379 |
|
402 | | - with pytest.raises(RenewalFailureError, match="Simulated renewal failure"): |
| 380 | + # Wait for the first renewal interval to pass |
| 381 | + renewal_interval = short_ttl / 3 |
| 382 | + await asyncio.sleep( |
| 383 | + renewal_interval.total_seconds() + 1 |
| 384 | + ) # Wait for renewal to happen |
| 385 | + |
| 386 | + # Find and delete all holder keys for this semaphore |
| 387 | + holder_keys = await redis_client_sdk.redis.keys( |
| 388 | + f"{SEMAPHORE_HOLDER_KEY_PREFIX}{semaphore_name}:*" |
| 389 | + ) |
| 390 | + assert holder_keys, "Holder keys should exist before deletion" |
| 391 | + await redis_client_sdk.redis.delete(*holder_keys) |
| 392 | + |
| 393 | + with pytest.raises(SemaphoreLostError): |
403 | 394 | await task # This should raise the renewal failure exception |
404 | 395 |
|
405 | 396 |
|
|
0 commit comments