|
15 | 15 | from servicelib.redis._constants import ( |
16 | 16 | SEMAPHORE_HOLDER_KEY_PREFIX, |
17 | 17 | ) |
18 | | -from servicelib.redis._errors import SemaphoreLostError |
| 18 | +from servicelib.redis._errors import SemaphoreLostError, SemaphoreNotAcquiredError |
19 | 19 | from servicelib.redis._semaphore import ( |
20 | 20 | DistributedSemaphore, |
21 | 21 | SemaphoreAcquisitionError, |
@@ -348,3 +348,47 @@ async def function_raising_cancelled_error(): |
348 | 348 | # Verify CancelledError is preserved |
349 | 349 | with pytest.raises(asyncio.CancelledError): |
350 | 350 | await function_raising_cancelled_error() |
| 351 | + |
| 352 | + |
| 353 | +async def test_release_failure( |
| 354 | + redis_client_sdk: RedisClientSDK, |
| 355 | + semaphore_name: str, |
| 356 | + semaphore_capacity: int, |
| 357 | + short_ttl: datetime.timedelta, |
| 358 | + mocker: MockerFixture, |
| 359 | + caplog: pytest.LogCaptureFixture, |
| 360 | +): |
| 361 | + """Test that semaphore release failures are properly logged without raising""" |
| 362 | + |
| 363 | + # Mock the semaphore.release() method to raise SemaphoreNotAcquiredError |
| 364 | + mock_release = mocker.AsyncMock( |
| 365 | + side_effect=SemaphoreNotAcquiredError(name="test-key") |
| 366 | + ) |
| 367 | + |
| 368 | + work_completed = asyncio.Event() |
| 369 | + |
| 370 | + @with_limited_concurrency( |
| 371 | + redis_client_sdk, |
| 372 | + key=semaphore_name, |
| 373 | + capacity=semaphore_capacity, |
| 374 | + ttl=short_ttl, |
| 375 | + ) |
| 376 | + async def work_function() -> str: |
| 377 | + work_completed.set() |
| 378 | + return "success" |
| 379 | + |
| 380 | + # Patch the release method after semaphore creation |
| 381 | + mocker.patch.object(DistributedSemaphore, "release", mock_release) |
| 382 | + # The decorator should complete successfully despite release failure |
| 383 | + result = await work_function() |
| 384 | + assert result == "success" |
| 385 | + |
| 386 | + # Wait for work to complete |
| 387 | + await work_completed.wait() |
| 388 | + |
| 389 | + # Verify the release was attempted |
| 390 | + mock_release.assert_called_once() |
| 391 | + |
| 392 | + # Verify the exception was logged (not raised) |
| 393 | + assert "Unexpected error while releasing semaphore" in caplog.text |
| 394 | + assert "SemaphoreNotAcquiredError" in caplog.text |
0 commit comments