@@ -628,3 +628,156 @@ async def test_real_redis_decode_responses_compatibility():
628
628
except Exception :
629
629
pass
630
630
await session .close ()
631
+
632
+
633
+ async def test_get_next_id_method ():
634
+ """Test the _get_next_id atomic counter functionality."""
635
+ session = await _create_test_session ("counter_test" )
636
+
637
+ try :
638
+ await session .clear_session ()
639
+
640
+ # Test atomic counter increment
641
+ id1 = await session ._get_next_id ()
642
+ id2 = await session ._get_next_id ()
643
+ id3 = await session ._get_next_id ()
644
+
645
+ # IDs should be sequential
646
+ assert id1 == 1
647
+ assert id2 == 2
648
+ assert id3 == 3
649
+
650
+ # Test that counter persists across session instances with same session_id
651
+ if USE_FAKE_REDIS :
652
+ session2 = RedisSession (
653
+ session_id = "counter_test" ,
654
+ redis_client = fake_redis ,
655
+ key_prefix = "test:" ,
656
+ )
657
+ else :
658
+ session2 = RedisSession .from_url ("counter_test" , url = REDIS_URL , key_prefix = "test:" )
659
+
660
+ try :
661
+ id4 = await session2 ._get_next_id ()
662
+ assert id4 == 4 # Should continue from previous session's counter
663
+ finally :
664
+ await session2 .close ()
665
+
666
+ finally :
667
+ await session .close ()
668
+
669
+
670
+ async def test_corrupted_data_handling ():
671
+ """Test that corrupted JSON data is handled gracefully."""
672
+ if not USE_FAKE_REDIS :
673
+ pytest .skip ("This test requires fakeredis for direct data manipulation" )
674
+
675
+ session = await _create_test_session ("corruption_test" )
676
+
677
+ try :
678
+ await session .clear_session ()
679
+
680
+ # Add some valid data first
681
+ await session .add_items ([{"role" : "user" , "content" : "valid message" }])
682
+
683
+ # Inject corrupted data directly into Redis
684
+ messages_key = "test:corruption_test:messages"
685
+
686
+ # Add invalid JSON
687
+ await fake_redis .rpush (messages_key , "invalid json data" )
688
+ await fake_redis .rpush (messages_key , "{incomplete json" )
689
+
690
+ # get_items should skip corrupted data and return valid items
691
+ items = await session .get_items ()
692
+ assert len (items ) == 1 # Only the original valid item
693
+
694
+ # Now add a properly formatted valid item using the session's serialization
695
+ valid_item = {"role" : "user" , "content" : "valid after corruption" }
696
+ await session .add_items ([valid_item ])
697
+
698
+ # Should now have 2 valid items (corrupted ones skipped)
699
+ items = await session .get_items ()
700
+ assert len (items ) == 2
701
+ assert items [0 ]["content" ] == "valid message"
702
+ assert items [1 ]["content" ] == "valid after corruption"
703
+
704
+ # Test pop_item with corrupted data at the end
705
+ await fake_redis .rpush (messages_key , "corrupted at end" )
706
+
707
+ # The corrupted item should be handled gracefully
708
+ # Since it's at the end, pop_item will encounter it first and return None
709
+ # But first, let's pop the valid items to get to the corrupted one
710
+ popped1 = await session .pop_item ()
711
+ assert popped1 is not None
712
+ assert popped1 ["content" ] == "valid after corruption"
713
+
714
+ popped2 = await session .pop_item ()
715
+ assert popped2 is not None
716
+ assert popped2 ["content" ] == "valid message"
717
+
718
+ # Now we should hit the corrupted data - this should gracefully handle it
719
+ # by returning None (and removing the corrupted item)
720
+ popped_corrupted = await session .pop_item ()
721
+ assert popped_corrupted is None
722
+
723
+ finally :
724
+ await session .close ()
725
+
726
+
727
+ async def test_ping_connection_failure ():
728
+ """Test ping method when Redis connection fails."""
729
+ if not USE_FAKE_REDIS :
730
+ pytest .skip ("This test requires fakeredis for connection mocking" )
731
+
732
+ import unittest .mock
733
+
734
+ session = await _create_test_session ("ping_failure_test" )
735
+
736
+ try :
737
+ # First verify ping works normally
738
+ assert await session .ping () is True
739
+
740
+ # Mock the ping method to raise an exception
741
+ with unittest .mock .patch .object (
742
+ session ._redis , "ping" , side_effect = Exception ("Connection failed" )
743
+ ):
744
+ # ping should return False when connection fails
745
+ assert await session .ping () is False
746
+
747
+ finally :
748
+ await session .close ()
749
+
750
+
751
+ async def test_close_method_coverage ():
752
+ """Test complete coverage of close() method behavior."""
753
+ if not USE_FAKE_REDIS :
754
+ pytest .skip ("This test requires fakeredis for client state verification" )
755
+
756
+ # Test 1: External client (should NOT be closed)
757
+ external_client = fake_redis
758
+ session1 = RedisSession (
759
+ session_id = "close_test_1" ,
760
+ redis_client = external_client ,
761
+ key_prefix = "test:" ,
762
+ )
763
+
764
+ # Verify _owns_client is False for external client
765
+ assert session1 ._owns_client is False
766
+
767
+ # Close should not close the external client
768
+ await session1 .close ()
769
+
770
+ # Verify external client is still usable
771
+ assert await external_client .ping () is True
772
+
773
+ # Test 2: Internal client (should be closed)
774
+ # Create a session that owns its client
775
+ session2 = RedisSession (
776
+ session_id = "close_test_2" ,
777
+ redis_client = fake_redis ,
778
+ key_prefix = "test:" ,
779
+ )
780
+ session2 ._owns_client = True # Simulate ownership
781
+
782
+ # This should trigger the close path for owned clients
783
+ await session2 .close ()
0 commit comments