@@ -917,3 +917,94 @@ async def async_func(x):
917917 assert call_count >= 2
918918
919919 async_func .clear_cache ()
920+
921+
922+ # =============================================================================
923+ # Exception Handling and Edge Cases
924+ # =============================================================================
925+
926+
927+ class TestAsyncExceptionHandling :
928+ """Tests for exception handling in async background tasks."""
929+
930+ @pytest .mark .memory
931+ @pytest .mark .asyncio
932+ async def test_function_thread_async_exception_handling (self ):
933+ """Test that exceptions in background async tasks are caught and handled."""
934+
935+ @cachier (backend = "memory" , stale_after = timedelta (seconds = 1 ), next_time = True )
936+ async def async_func_that_fails (x ):
937+ await asyncio .sleep (0.2 )
938+ if x == 99 :
939+ raise ValueError ("Intentional test error" )
940+ return x * 2
941+
942+ async_func_that_fails .clear_cache ()
943+
944+ # First call with valid value
945+ result1 = await async_func_that_fails (5 )
946+ assert result1 == 10
947+
948+ # Wait for stale
949+ await asyncio .sleep (1.5 )
950+
951+ # Call with value that will fail in background - should return stale
952+ result2 = await async_func_that_fails (5 )
953+ assert result2 == 10 # Returns stale value
954+
955+ # Wait for background task to complete and fail
956+ await asyncio .sleep (0.5 )
957+
958+ # The error should be caught and handled silently in background
959+ # (no exception should propagate to this test)
960+
961+ async_func_that_fails .clear_cache ()
962+
963+
964+ class TestAsyncStaleProcessing :
965+ """Tests for stale entry processing with next_time."""
966+
967+ @pytest .mark .memory
968+ @pytest .mark .asyncio
969+ async def test_stale_entry_processing_returns_stale_with_next_time (self ):
970+ """Test that stale entry being processed returns stale value when next_time=True."""
971+ call_count = 0
972+
973+ @cachier (backend = "memory" , stale_after = timedelta (seconds = 1 ), next_time = True )
974+ async def slow_async_func (x ):
975+ nonlocal call_count
976+ call_count += 1
977+ await asyncio .sleep (0.8 ) # Long enough to be "processing"
978+ return call_count * 10
979+
980+ slow_async_func .clear_cache ()
981+ call_count = 0
982+
983+ # First call - populate cache
984+ result1 = await slow_async_func (5 )
985+ assert result1 == 10
986+ assert call_count == 1
987+
988+ # Wait for stale
989+ await asyncio .sleep (1.5 )
990+
991+ # Launch two concurrent calls when stale
992+ # First will trigger background update, both should return stale
993+ results = await asyncio .gather (
994+ slow_async_func (5 ),
995+ slow_async_func (5 ),
996+ )
997+
998+ # Both should get the stale value (10)
999+ assert results [0 ] == 10
1000+ assert results [1 ] == 10
1001+
1002+ # Wait for background update to complete
1003+ await asyncio .sleep (1.5 )
1004+
1005+ # Next call should get updated value
1006+ result_new = await slow_async_func (5 )
1007+ assert result_new > 10 # Updated in background
1008+
1009+ slow_async_func .clear_cache ()
1010+
0 commit comments