@@ -689,4 +689,162 @@ describe('cache function', () => {
689
689
expect ( onCacheMock ) . not . toHaveBeenCalled ( ) ;
690
690
} ) ;
691
691
} ) ;
692
+
693
+ describe ( 'unstable_shouldDisable' , ( ) => {
694
+ beforeEach ( ( ) => {
695
+ jest . useFakeTimers ( ) ;
696
+ clearStore ( ) ;
697
+ } ) ;
698
+
699
+ afterEach ( ( ) => {
700
+ jest . useRealTimers ( ) ;
701
+ configureCache ( { maxSize : CacheSize . GB } ) ;
702
+ } ) ;
703
+
704
+ it ( 'should bypass cache when unstable_shouldDisable returns true' , async ( ) => {
705
+ const mockFn = jest . fn ( ) . mockResolvedValue ( 'test data' ) ;
706
+ const cachedFn = cache ( mockFn , { maxAge : CacheTime . MINUTE } ) ;
707
+
708
+ configureCache ( {
709
+ maxSize : CacheSize . GB ,
710
+ unstable_shouldDisable : ( ) => true ,
711
+ } ) ;
712
+
713
+ const handler = withRequestCache ( async ( req : Request ) => {
714
+ const result1 = await cachedFn ( 'param1' ) ;
715
+ const result2 = await cachedFn ( 'param1' ) ;
716
+ return { result1, result2 } ;
717
+ } ) ;
718
+
719
+ await handler ( new MockRequest ( ) as unknown as Request ) ;
720
+
721
+ expect ( mockFn ) . toHaveBeenCalledTimes ( 2 ) ;
722
+ } ) ;
723
+
724
+ it ( 'should use cache when unstable_shouldDisable returns false' , async ( ) => {
725
+ const mockFn = jest . fn ( ) . mockResolvedValue ( 'test data' ) ;
726
+ const cachedFn = cache ( mockFn , { maxAge : CacheTime . MINUTE } ) ;
727
+
728
+ configureCache ( {
729
+ maxSize : CacheSize . GB ,
730
+ unstable_shouldDisable : ( ) => false ,
731
+ } ) ;
732
+
733
+ const handler = withRequestCache ( async ( req : Request ) => {
734
+ const result1 = await cachedFn ( 'param1' ) ;
735
+ const result2 = await cachedFn ( 'param1' ) ;
736
+ return { result1, result2 } ;
737
+ } ) ;
738
+
739
+ await handler ( new MockRequest ( ) as unknown as Request ) ;
740
+
741
+ expect ( mockFn ) . toHaveBeenCalledTimes ( 1 ) ;
742
+ } ) ;
743
+
744
+ it ( 'should support dynamic decision based on request' , async ( ) => {
745
+ const mockFn = jest . fn ( ) . mockResolvedValue ( 'test data' ) ;
746
+ const cachedFn = cache ( mockFn , { tag : 'testTag' } ) ;
747
+
748
+ configureCache ( {
749
+ maxSize : CacheSize . GB ,
750
+ unstable_shouldDisable : ( { request } ) => {
751
+ return request . url . includes ( 'no-cache' ) ;
752
+ } ,
753
+ } ) ;
754
+
755
+ const handlerWithCache = withRequestCache ( async ( req : Request ) => {
756
+ const result1 = await cachedFn ( 'param1' ) ;
757
+ const result2 = await cachedFn ( 'param1' ) ;
758
+ return { result1, result2 } ;
759
+ } ) ;
760
+
761
+ const handlerWithoutCache = withRequestCache ( async ( req : Request ) => {
762
+ const result1 = await cachedFn ( 'param1' ) ;
763
+ const result2 = await cachedFn ( 'param1' ) ;
764
+ return { result1, result2 } ;
765
+ } ) ;
766
+
767
+ await handlerWithCache (
768
+ new MockRequest ( 'http://example.com' ) as unknown as Request ,
769
+ ) ;
770
+
771
+ await handlerWithoutCache (
772
+ new MockRequest ( 'http://example.com/no-cache' ) as unknown as Request ,
773
+ ) ;
774
+
775
+ expect ( mockFn ) . toHaveBeenCalledTimes ( 3 ) ;
776
+ } ) ;
777
+
778
+ it ( 'should affect no-options cache as well' , async ( ) => {
779
+ const mockFn = jest . fn ( ) . mockResolvedValue ( 'test data' ) ;
780
+ const cachedFn = cache ( mockFn ) ;
781
+ configureCache ( {
782
+ maxSize : CacheSize . GB ,
783
+ unstable_shouldDisable : ( ) => true ,
784
+ } ) ;
785
+
786
+ const handler = withRequestCache ( async ( req : Request ) => {
787
+ const result1 = await cachedFn ( 'param1' ) ;
788
+ const result2 = await cachedFn ( 'param1' ) ;
789
+ return { result1, result2 } ;
790
+ } ) ;
791
+
792
+ await handler ( new MockRequest ( ) as unknown as Request ) ;
793
+
794
+ expect ( mockFn ) . toHaveBeenCalledTimes ( 2 ) ;
795
+ } ) ;
796
+
797
+ it ( 'should support async decision function' , async ( ) => {
798
+ const mockFn = jest . fn ( ) . mockResolvedValue ( 'test data' ) ;
799
+ const cachedFn = cache ( mockFn , { maxAge : CacheTime . MINUTE } ) ;
800
+
801
+ configureCache ( {
802
+ maxSize : CacheSize . GB ,
803
+ unstable_shouldDisable : async ( ) => {
804
+ return Promise . resolve ( true ) ;
805
+ } ,
806
+ } ) ;
807
+
808
+ const handler = withRequestCache ( async ( req : Request ) => {
809
+ const result1 = await cachedFn ( 'param1' ) ;
810
+ const result2 = await cachedFn ( 'param1' ) ;
811
+ return { result1, result2 } ;
812
+ } ) ;
813
+
814
+ await handler ( new MockRequest ( ) as unknown as Request ) ;
815
+
816
+ expect ( mockFn ) . toHaveBeenCalledTimes ( 2 ) ;
817
+ } ) ;
818
+
819
+ it ( 'should still trigger onCache callback even when cache is disabled' , async ( ) => {
820
+ const mockFn = jest . fn ( ) . mockResolvedValue ( 'test data' ) ;
821
+ const onCacheMock = jest . fn ( ) ;
822
+
823
+ const cachedFn = cache ( mockFn , {
824
+ maxAge : CacheTime . MINUTE ,
825
+ onCache : onCacheMock ,
826
+ } ) ;
827
+
828
+ configureCache ( {
829
+ maxSize : CacheSize . GB ,
830
+ unstable_shouldDisable : ( ) => true ,
831
+ } ) ;
832
+
833
+ const handler = withRequestCache ( async ( req : Request ) => {
834
+ const result1 = await cachedFn ( 'param1' ) ;
835
+ const result2 = await cachedFn ( 'param1' ) ;
836
+ return { result1, result2 } ;
837
+ } ) ;
838
+
839
+ await handler ( new MockRequest ( ) as unknown as Request ) ;
840
+
841
+ expect ( mockFn ) . toHaveBeenCalledTimes ( 2 ) ;
842
+ expect ( onCacheMock ) . toHaveBeenCalledTimes ( 2 ) ;
843
+ expect ( onCacheMock ) . toHaveBeenCalledWith (
844
+ expect . objectContaining ( {
845
+ status : 'miss' ,
846
+ } ) ,
847
+ ) ;
848
+ } ) ;
849
+ } ) ;
692
850
} ) ;
0 commit comments