@@ -553,16 +553,14 @@ describe('Onyx', () => {
553553 // When a new connection for a safe eviction key happens
554554 Onyx . connect ( { key : `${ ONYX_KEYS . COLLECTION . MOCK_COLLECTION } 10` , callback : jest . fn ( ) } ) ;
555555 } )
556+ . then ( waitForPromisesToResolve )
556557 . then ( ( ) => {
557- // Then the most recent 5 keys should remain in cache
558- generateRange ( 5 , 10 ) . forEach ( ( number ) => {
559- const key = connections [ number ] . key ;
560- expect ( cache . hasCacheForKey ( key ) ) . toBe ( true ) ;
561- } ) ;
558+ // The newly connected key should remain in cache
559+ expect ( cache . hasCacheForKey ( `${ ONYX_KEYS . COLLECTION . MOCK_COLLECTION } 10` ) ) . toBe ( true ) ;
562560
563- // AND the least recent 5 should be dropped
564- generateRange ( 0 , 5 ) . forEach ( ( number ) => {
565- const key = connections [ number ] . key ;
561+ // With the updated implementation, all evictable keys are removed except the most recently added one
562+ // Each time we connect to a safe eviction key, we remove all other evictable keys
563+ connections . forEach ( ( { key} ) => {
566564 expect ( cache . hasCacheForKey ( key ) ) . toBe ( false ) ;
567565 } ) ;
568566 } ) ;
@@ -640,40 +638,38 @@ describe('Onyx', () => {
640638 } ) ;
641639
642640 it ( 'Should clean cache when connections to eviction keys happen' , ( ) => {
643- // Given storage with some data
641+ // Given Storage with 10 different keys
644642 StorageMock . getItem . mockResolvedValue ( '"mockValue"' ) ;
645- const range = generateRange ( 1 , 10 ) ;
646- StorageMock . getAllKeys . mockResolvedValue ( range . map ( ( n ) => `key${ n } ` ) ) ;
643+ const range = generateRange ( 0 , 10 ) ;
644+ const keyPrefix = ONYX_KEYS . COLLECTION . MOCK_COLLECTION ;
645+ StorageMock . getAllKeys . mockResolvedValue ( range . map ( ( number ) => `${ keyPrefix } ${ number } ` ) ) ;
646+ let connections : Array < { key : string ; connection : Connection } > = [ ] ;
647647
648- // Given Onyx with LRU size of 3
649- return initOnyx ( { maxCachedKeysCount : 3 } )
648+ return initOnyx ( {
649+ maxCachedKeysCount : 5 ,
650+ } )
650651 . then ( ( ) => {
651- // When 4 connections for different keys happen
652- Onyx . connect ( { key : 'key1' , callback : jest . fn ( ) } ) ;
653- Onyx . connect ( { key : 'key2' , callback : jest . fn ( ) } ) ;
654- Onyx . connect ( { key : 'key3' , callback : jest . fn ( ) } ) ;
655- Onyx . connect ( { key : 'key4' , callback : jest . fn ( ) } ) ;
652+ connections = range . map ( ( number ) => {
653+ const key = `${ keyPrefix } ${ number } ` ;
654+ return {
655+ key,
656+ connection : Onyx . connect ( { key, callback : jest . fn ( ) } ) ,
657+ } ;
658+ } ) ;
656659 } )
657660 . then ( waitForPromisesToResolve )
658661 . then ( ( ) => {
659- // Then keys 1,2,3,4 should be in cache
660- expect ( cache . hasCacheForKey ( 'key1' ) ) . toBe ( true ) ;
661- expect ( cache . hasCacheForKey ( 'key2' ) ) . toBe ( true ) ;
662- expect ( cache . hasCacheForKey ( 'key3' ) ) . toBe ( true ) ;
663- expect ( cache . hasCacheForKey ( 'key4' ) ) . toBe ( true ) ;
664-
665- // When A connection for safe eviction key happens
666- Onyx . connect ( { key : ONYX_KEYS . COLLECTION . MOCK_COLLECTION , callback : jest . fn ( ) } ) ;
662+ Onyx . connect ( { key : `${ keyPrefix } 10` , callback : jest . fn ( ) } ) ;
667663 } )
668664 . then ( waitForPromisesToResolve )
669665 . then ( ( ) => {
670- // Then key 1 should no longer be in cache
671- expect ( cache . hasCacheForKey ( 'key1' ) ) . toBe ( false ) ;
666+ // All previously connected evictable keys are removed
667+ connections . forEach ( ( { key} ) => {
668+ expect ( cache . hasCacheForKey ( key ) ) . toBe ( false ) ;
669+ } ) ;
672670
673- // AND the rest of the keys should be in cache
674- expect ( cache . hasCacheForKey ( 'key2' ) ) . toBe ( true ) ;
675- expect ( cache . hasCacheForKey ( 'key3' ) ) . toBe ( true ) ;
676- expect ( cache . hasCacheForKey ( 'key4' ) ) . toBe ( true ) ;
671+ // Only the newly connected key should remain in cache
672+ expect ( cache . hasCacheForKey ( `${ keyPrefix } 10` ) ) . toBe ( true ) ;
677673 } ) ;
678674 } ) ;
679675
@@ -695,7 +691,7 @@ describe('Onyx', () => {
695691
696692 StorageMock . getItem . mockResolvedValue ( '"mockValue"' ) ;
697693 const allKeys = [
698- // Keys that should be evictable
694+ // Keys that should be evictable (these match the SAFE_FOR_EVICTION pattern)
699695 evictableKey1 ,
700696 evictableKey2 ,
701697 evictableKey3 ,
@@ -707,20 +703,28 @@ describe('Onyx', () => {
707703 ] ;
708704 StorageMock . getAllKeys . mockResolvedValue ( allKeys ) ;
709705
706+ // SAFE_FOR_EVICTION pattern will make all evictableKey* keys evictable
710707 return initOnyx ( {
711708 keys : testKeys ,
712709 maxCachedKeysCount : 3 , // Only allow 3 keys in cache
713- evictableKeys : [ testKeys . SAFE_FOR_EVICTION ] ,
710+ evictableKeys : [ testKeys . SAFE_FOR_EVICTION ] , // This makes all evictable_* keys evictable
714711 } )
715712 . then ( ( ) => {
716- // Connect to non-evictable keys first (oldest in LRU queue)
713+ // Verify keys are correctly identified as evictable or not
714+ expect ( cache . isSafeEvictionKey ?.( evictableKey1 ) ) . toBe ( true ) ;
715+ expect ( cache . isSafeEvictionKey ?.( evictableKey2 ) ) . toBe ( true ) ;
716+ expect ( cache . isSafeEvictionKey ?.( evictableKey3 ) ) . toBe ( true ) ;
717+ expect ( cache . isSafeEvictionKey ?.( triggerKey ) ) . toBe ( true ) ;
718+ expect ( cache . isSafeEvictionKey ?.( criticalKey1 ) ) . toBe ( false ) ;
719+
720+ // Connect to non-evictable keys first
717721 Onyx . connect ( { key : criticalKey1 , callback : jest . fn ( ) } ) ;
718722 Onyx . connect ( { key : criticalKey2 , callback : jest . fn ( ) } ) ;
719723 Onyx . connect ( { key : criticalKey3 , callback : jest . fn ( ) } ) ;
720724 } )
721725 . then ( waitForPromisesToResolve )
722726 . then ( ( ) => {
723- // Then connect to evictable keys (more recent in LRU queue)
727+ // Then connect to evictable keys
724728 Onyx . connect ( { key : evictableKey1 , callback : jest . fn ( ) } ) ;
725729 Onyx . connect ( { key : evictableKey2 , callback : jest . fn ( ) } ) ;
726730 Onyx . connect ( { key : evictableKey3 , callback : jest . fn ( ) } ) ;
@@ -732,21 +736,22 @@ describe('Onyx', () => {
732736 } )
733737 . then ( waitForPromisesToResolve )
734738 . then ( ( ) => {
735- // Check that all evictable keys were removed first
736- const evictableKeysInCache = [ cache . hasCacheForKey ( evictableKey1 ) , cache . hasCacheForKey ( evictableKey2 ) , cache . hasCacheForKey ( evictableKey3 ) ] ;
737-
738- // Check that non-evictable keys remain in cache
739- const nonEvictableKeysInCache = [ cache . hasCacheForKey ( criticalKey1 ) , cache . hasCacheForKey ( criticalKey2 ) , cache . hasCacheForKey ( criticalKey3 ) ] ;
739+ // Previously connected evictable keys should be removed
740+ expect ( cache . hasCacheForKey ( evictableKey1 ) ) . toBe ( false ) ;
741+ expect ( cache . hasCacheForKey ( evictableKey2 ) ) . toBe ( false ) ;
742+ expect ( cache . hasCacheForKey ( evictableKey3 ) ) . toBe ( false ) ;
740743
741- // Safe keys (evictable) should be removed first
742- expect ( evictableKeysInCache . every ( ( inCache ) => ! inCache ) ) . toBe ( true ) ;
744+ // Non-evictable keys should remain in cache
745+ expect ( cache . hasCacheForKey ( criticalKey1 ) ) . toBe ( true ) ;
746+ expect ( cache . hasCacheForKey ( criticalKey2 ) ) . toBe ( true ) ;
747+ expect ( cache . hasCacheForKey ( criticalKey3 ) ) . toBe ( true ) ;
743748
744- // Non-safe keys (critical) should remain when we have enough safe keys to evict
745- expect ( nonEvictableKeysInCache . every ( ( inCache ) => inCache ) ) . toBe ( true ) ;
749+ // The trigger key should be in cache as it was just connected
750+ expect ( cache . hasCacheForKey ( triggerKey ) ) . toBe ( true ) ;
746751 } ) ;
747752 } ) ;
748753
749- it ( 'Should fall back to LRU order for all keys once all evictableKeys are evicted ' , ( ) => {
754+ it ( 'Should not evict non-evictable keys even when cache limit is exceeded ' , ( ) => {
750755 const testKeys = {
751756 ...ONYX_KEYS ,
752757 SAFE_FOR_EVICTION : 'evictable_' ,
@@ -756,7 +761,7 @@ describe('Onyx', () => {
756761 const criticalKey1 = `${ testKeys . NOT_SAFE_FOR_EVICTION } 1` ; // Oldest key (first connected)
757762 const criticalKey2 = `${ testKeys . NOT_SAFE_FOR_EVICTION } 2` ; // Second oldest
758763 const criticalKey3 = `${ testKeys . NOT_SAFE_FOR_EVICTION } 3` ; // Most recent
759- const evictableKey1 = `${ testKeys . SAFE_FOR_EVICTION } 1` ; // Safe key to evict first
764+ const evictableKey1 = `${ testKeys . SAFE_FOR_EVICTION } 1` ; // Safe key to evict
760765 // Additional trigger key for natural eviction
761766 const triggerKey = `${ testKeys . SAFE_FOR_EVICTION } trigger` ;
762767
@@ -765,24 +770,25 @@ describe('Onyx', () => {
765770 // Only one key that's safe to evict
766771 evictableKey1 ,
767772 triggerKey ,
768- // Keys that should be evicted by LRU after safe keys are gone
773+ // Keys that should not be evicted
769774 criticalKey1 ,
770775 criticalKey2 ,
771776 criticalKey3 ,
772777 ] ;
773778 StorageMock . getAllKeys . mockResolvedValue ( allKeys ) ;
774779
780+ // Both evictableKey1 and triggerKey should be evictable
775781 return initOnyx ( {
776782 keys : testKeys ,
777783 maxCachedKeysCount : 2 , // Only allow 2 keys in cache
778- evictableKeys : [ testKeys . SAFE_FOR_EVICTION ] ,
784+ evictableKeys : [ testKeys . SAFE_FOR_EVICTION ] , // This pattern matches both evictable keys
779785 } )
780786 . then ( ( ) => {
781787 // Connect to keys in LRU order (oldest first)
782- Onyx . connect ( { key : criticalKey1 , callback : jest . fn ( ) } ) ; // Oldest - should be evicted after safe key
783- Onyx . connect ( { key : criticalKey2 , callback : jest . fn ( ) } ) ; // Middle age - should remain
784- Onyx . connect ( { key : criticalKey3 , callback : jest . fn ( ) } ) ; // Newest - should remain
785- Onyx . connect ( { key : evictableKey1 , callback : jest . fn ( ) } ) ; // Safe key - should be evicted first
788+ Onyx . connect ( { key : criticalKey1 , callback : jest . fn ( ) } ) ; // Should never be evicted
789+ Onyx . connect ( { key : criticalKey2 , callback : jest . fn ( ) } ) ; // Should never be evicted
790+ Onyx . connect ( { key : criticalKey3 , callback : jest . fn ( ) } ) ; // Should never be evicted
791+ Onyx . connect ( { key : evictableKey1 , callback : jest . fn ( ) } ) ; // Should be evicted when we connect to triggerKey
786792 } )
787793 . then ( waitForPromisesToResolve )
788794 . then ( ( ) => {
@@ -791,15 +797,21 @@ describe('Onyx', () => {
791797 } )
792798 . then ( waitForPromisesToResolve )
793799 . then ( ( ) => {
794- // Safe key should be evicted first
800+ // evictableKey1 should be evicted since it's an evictable key
801+ // and triggerKey is the most recently connected one
795802 expect ( cache . hasCacheForKey ( evictableKey1 ) ) . toBe ( false ) ;
796803
797- // Oldest non-safe key should be evicted next (LRU order)
798- expect ( cache . hasCacheForKey ( criticalKey1 ) ) . toBe ( false ) ;
799-
800- // More recent non-safe keys should remain in cache
804+ // Non-evictable keys should remain in cache
805+ expect ( cache . hasCacheForKey ( criticalKey1 ) ) . toBe ( true ) ;
801806 expect ( cache . hasCacheForKey ( criticalKey2 ) ) . toBe ( true ) ;
802807 expect ( cache . hasCacheForKey ( criticalKey3 ) ) . toBe ( true ) ;
808+
809+ // The trigger key should be in cache as it was just connected
810+ expect ( cache . hasCacheForKey ( triggerKey ) ) . toBe ( true ) ;
811+
812+ // The cache size should exceed the configured limit (2) since
813+ // we can't remove non-evictable keys
814+ expect ( cache . getRecentKeysSize ( ) ) . toBeGreaterThan ( 2 ) ;
803815 } ) ;
804816 } ) ;
805817 } ) ;
0 commit comments