@@ -278,6 +278,98 @@ async def test_from_conn_string_cleanup(redis_url: str) -> None:
278278 assert await ext_client .ping () # Should still work
279279 finally :
280280 await ext_client .aclose () # type: ignore[attr-defined]
281+
282+
283+ @pytest .mark .asyncio
284+ async def test_async_client_info_setting (redis_url : str , monkeypatch ) -> None :
285+ """Test that async client_setinfo is called with correct library information."""
286+ from langgraph .checkpoint .redis .version import __full_lib_name__
287+
288+ # Track if client_setinfo was called with the right parameters
289+ client_info_called = False
290+
291+ # Store the original method
292+ original_client_setinfo = Redis .client_setinfo
293+
294+ # Create a mock function for client_setinfo
295+ async def mock_client_setinfo (self , key , value ):
296+ nonlocal client_info_called
297+ # Note: RedisVL might call this with its own lib name first
298+ # We only track calls with our full lib name
299+ if key == "LIB-NAME" and __full_lib_name__ in value :
300+ client_info_called = True
301+ # Call original method to ensure normal function
302+ return await original_client_setinfo (self , key , value )
303+
304+ # Apply the mock
305+ monkeypatch .setattr (Redis , "client_setinfo" , mock_client_setinfo )
306+
307+ # Test client info setting when creating a new saver with async context manager
308+ async with AsyncRedisSaver .from_conn_string (redis_url ) as saver :
309+ await saver .asetup ()
310+ # __aenter__ should have called aset_client_info
311+
312+ # Verify client_setinfo was called with our library info
313+ assert client_info_called , "client_setinfo was not called with our library name"
314+
315+
316+ @pytest .mark .asyncio
317+ async def test_async_client_info_fallback_to_echo (redis_url : str , monkeypatch ) -> None :
318+ """Test that async client_setinfo falls back to echo when not available."""
319+ from langgraph .checkpoint .redis .version import __full_lib_name__
320+ from redis .exceptions import ResponseError
321+
322+ # Remove client_setinfo to simulate older Redis version
323+ async def mock_client_setinfo (self , key , value ):
324+ raise ResponseError ("ERR unknown command" )
325+
326+ # Track if echo was called as fallback
327+ echo_called = False
328+ original_echo = Redis .echo
329+
330+ # Create mock for echo
331+ async def mock_echo (self , message ):
332+ nonlocal echo_called
333+ echo_called = True
334+ assert message == __full_lib_name__
335+ return await original_echo (self , message )
336+
337+ # Apply the mocks
338+ monkeypatch .setattr (Redis , "client_setinfo" , mock_client_setinfo )
339+ monkeypatch .setattr (Redis , "echo" , mock_echo )
340+
341+ # Test client info setting with fallback
342+ async with AsyncRedisSaver .from_conn_string (redis_url ) as saver :
343+ await saver .asetup ()
344+ # __aenter__ should have called aset_client_info with fallback to echo
345+
346+ # Verify echo was called as fallback
347+ assert echo_called , "echo was not called as fallback when async client_setinfo failed"
348+
349+
350+ @pytest .mark .asyncio
351+ async def test_async_client_info_graceful_failure (redis_url : str , monkeypatch ) -> None :
352+ """Test that async client info setting fails gracefully when all methods fail."""
353+ from redis .exceptions import ResponseError
354+
355+ # Simulate failures for both methods
356+ async def mock_client_setinfo (self , key , value ):
357+ raise ResponseError ("ERR unknown command" )
358+
359+ async def mock_echo (self , message ):
360+ raise ResponseError ("ERR connection broken" )
361+
362+ # Apply the mocks
363+ monkeypatch .setattr (Redis , "client_setinfo" , mock_client_setinfo )
364+ monkeypatch .setattr (Redis , "echo" , mock_echo )
365+
366+ # Should not raise any exceptions when both methods fail
367+ try :
368+ async with AsyncRedisSaver .from_conn_string (redis_url ) as saver :
369+ await saver .asetup ()
370+ # __aenter__ should handle failures gracefully
371+ except Exception as e :
372+ assert False , f"aset_client_info did not handle failure gracefully: { e } "
281373
282374
283375@pytest .mark .asyncio
0 commit comments