1
1
/* eslint-disable sonarjs/no-duplicate-string */
2
- import Cache from "@opennextjs/aws/adapters/cache.js" ;
2
+ import Cache , { SOFT_TAG_PREFIX } from "@opennextjs/aws/adapters/cache.js" ;
3
3
import { vi } from "vitest" ;
4
4
5
5
declare global {
@@ -531,6 +531,8 @@ describe("CacheHandler", () => {
531
531
await cache . revalidateTag ( "tag" ) ;
532
532
533
533
expect ( tagCache . writeTags ) . not . toHaveBeenCalled ( ) ;
534
+ // Reset the config
535
+ globalThis . openNextConfig . dangerous . disableTagCache = false ;
534
536
} ) ;
535
537
536
538
it ( "Should call tagCache.writeTags" , async ( ) => {
@@ -551,13 +553,13 @@ describe("CacheHandler", () => {
551
553
it ( "Should call invalidateCdnHandler.invalidatePaths" , async ( ) => {
552
554
globalThis . tagCache . getByTag . mockResolvedValueOnce ( [ "/path" ] ) ;
553
555
globalThis . tagCache . getByPath . mockResolvedValueOnce ( [ ] ) ;
554
- await cache . revalidateTag ( "_N_T_/ path" ) ;
556
+ await cache . revalidateTag ( ` ${ SOFT_TAG_PREFIX } path` ) ;
555
557
556
558
expect ( tagCache . writeTags ) . toHaveBeenCalledTimes ( 1 ) ;
557
559
expect ( tagCache . writeTags ) . toHaveBeenCalledWith ( [
558
560
{
559
561
path : "/path" ,
560
- tag : "_N_T_/ path" ,
562
+ tag : ` ${ SOFT_TAG_PREFIX } path` ,
561
563
} ,
562
564
] ) ;
563
565
@@ -621,4 +623,199 @@ describe("CacheHandler", () => {
621
623
globalThis . tagCache . getPathsByTags = undefined ;
622
624
} ) ;
623
625
} ) ;
626
+
627
+ describe ( "shouldBypassTagCache" , ( ) => {
628
+ describe ( "fetch cache" , ( ) => {
629
+ it ( "Should bypass tag cache validation when shouldBypassTagCache is true" , async ( ) => {
630
+ incrementalCache . get . mockResolvedValueOnce ( {
631
+ value : {
632
+ kind : "FETCH" ,
633
+ data : {
634
+ headers : { } ,
635
+ body : "{}" ,
636
+ url : "https://example.com" ,
637
+ status : 200 ,
638
+ } ,
639
+ } ,
640
+ lastModified : Date . now ( ) ,
641
+ shouldBypassTagCache : true ,
642
+ } ) ;
643
+
644
+ const result = await cache . get ( "key" , {
645
+ kind : "FETCH" ,
646
+ tags : [ "tag1" ] ,
647
+ } ) ;
648
+
649
+ expect ( getFetchCacheSpy ) . toHaveBeenCalled ( ) ;
650
+ expect ( tagCache . getLastModified ) . not . toHaveBeenCalled ( ) ;
651
+ expect ( tagCache . hasBeenRevalidated ) . not . toHaveBeenCalled ( ) ;
652
+ expect ( result ) . not . toBeNull ( ) ;
653
+ expect ( result ?. value ) . toEqual ( {
654
+ kind : "FETCH" ,
655
+ data : {
656
+ headers : { } ,
657
+ body : "{}" ,
658
+ url : "https://example.com" ,
659
+ status : 200 ,
660
+ } ,
661
+ } ) ;
662
+ } ) ;
663
+
664
+ it ( "Should not bypass tag cache validation when shouldBypassTagCache is false" , async ( ) => {
665
+ globalThis . tagCache . mode = "nextMode" ;
666
+ incrementalCache . get . mockResolvedValueOnce ( {
667
+ value : {
668
+ kind : "FETCH" ,
669
+ data : {
670
+ headers : { } ,
671
+ body : "{}" ,
672
+ url : "https://example.com" ,
673
+ status : 200 ,
674
+ } ,
675
+ } ,
676
+ lastModified : Date . now ( ) ,
677
+ shouldBypassTagCache : false ,
678
+ } ) ;
679
+
680
+ const result = await cache . get ( "key" , {
681
+ kind : "FETCH" ,
682
+ tags : [ "tag1" ] ,
683
+ } ) ;
684
+
685
+ expect ( getFetchCacheSpy ) . toHaveBeenCalled ( ) ;
686
+ expect ( tagCache . hasBeenRevalidated ) . toHaveBeenCalled ( ) ;
687
+ expect ( result ) . not . toBeNull ( ) ;
688
+ } ) ;
689
+
690
+ it ( "Should not bypass tag cache validation when shouldBypassTagCache is undefined" , async ( ) => {
691
+ globalThis . tagCache . mode = "nextMode" ;
692
+ tagCache . hasBeenRevalidated . mockResolvedValueOnce ( false ) ;
693
+ incrementalCache . get . mockResolvedValueOnce ( {
694
+ value : {
695
+ kind : "FETCH" ,
696
+ data : {
697
+ headers : { } ,
698
+ body : "{}" ,
699
+ url : "https://example.com" ,
700
+ status : 200 ,
701
+ } ,
702
+ } ,
703
+ lastModified : Date . now ( ) ,
704
+ // shouldBypassTagCache not set
705
+ } ) ;
706
+
707
+ const result = await cache . get ( "key" , {
708
+ kind : "FETCH" ,
709
+ tags : [ "tag1" ] ,
710
+ } ) ;
711
+
712
+ expect ( getFetchCacheSpy ) . toHaveBeenCalled ( ) ;
713
+ expect ( tagCache . hasBeenRevalidated ) . toHaveBeenCalled ( ) ;
714
+ expect ( result ) . not . toBeNull ( ) ;
715
+ } ) ;
716
+
717
+ it ( "Should bypass path validation when shouldBypassTagCache is true for soft tags" , async ( ) => {
718
+ incrementalCache . get . mockResolvedValueOnce ( {
719
+ value : {
720
+ kind : "FETCH" ,
721
+ data : {
722
+ headers : { } ,
723
+ body : "{}" ,
724
+ url : "https://example.com" ,
725
+ status : 200 ,
726
+ } ,
727
+ } ,
728
+ lastModified : Date . now ( ) ,
729
+ shouldBypassTagCache : true ,
730
+ } ) ;
731
+
732
+ const result = await cache . get ( "key" , {
733
+ kind : "FETCH" ,
734
+ softTags : [ `${ SOFT_TAG_PREFIX } path` ] ,
735
+ } ) ;
736
+
737
+ expect ( getFetchCacheSpy ) . toHaveBeenCalled ( ) ;
738
+ expect ( tagCache . getLastModified ) . not . toHaveBeenCalled ( ) ;
739
+ expect ( tagCache . hasBeenRevalidated ) . not . toHaveBeenCalled ( ) ;
740
+ expect ( result ) . not . toBeNull ( ) ;
741
+ } ) ;
742
+ } ) ;
743
+
744
+ describe ( "incremental cache" , ( ) => {
745
+ it ( "Should bypass tag cache validation when shouldBypassTagCache is true" , async ( ) => {
746
+ incrementalCache . get . mockResolvedValueOnce ( {
747
+ value : {
748
+ type : "route" ,
749
+ body : "{}" ,
750
+ } ,
751
+ lastModified : Date . now ( ) ,
752
+ shouldBypassTagCache : true ,
753
+ } ) ;
754
+
755
+ const result = await cache . get ( "key" , { kindHint : "app" } ) ;
756
+
757
+ expect ( getIncrementalCache ) . toHaveBeenCalled ( ) ;
758
+ expect ( tagCache . getLastModified ) . not . toHaveBeenCalled ( ) ;
759
+ expect ( tagCache . hasBeenRevalidated ) . not . toHaveBeenCalled ( ) ;
760
+ expect ( result ) . not . toBeNull ( ) ;
761
+ expect ( result ?. value ?. kind ) . toEqual ( "ROUTE" ) ;
762
+ } ) ;
763
+
764
+ it ( "Should not bypass tag cache validation when shouldBypassTagCache is false" , async ( ) => {
765
+ globalThis . tagCache . mode = "nextMode" ;
766
+ incrementalCache . get . mockResolvedValueOnce ( {
767
+ value : {
768
+ type : "route" ,
769
+ body : "{}" ,
770
+ } ,
771
+ lastModified : Date . now ( ) ,
772
+ shouldBypassTagCache : false ,
773
+ } ) ;
774
+
775
+ const result = await cache . get ( "key" , { kindHint : "app" } ) ;
776
+
777
+ expect ( getIncrementalCache ) . toHaveBeenCalled ( ) ;
778
+ expect ( tagCache . hasBeenRevalidated ) . toHaveBeenCalled ( ) ;
779
+ expect ( result ) . not . toBeNull ( ) ;
780
+ } ) ;
781
+
782
+ it ( "Should return null when tag cache indicates revalidation and shouldBypassTagCache is false" , async ( ) => {
783
+ globalThis . tagCache . mode = "nextMode" ;
784
+ tagCache . hasBeenRevalidated . mockResolvedValueOnce ( true ) ;
785
+ incrementalCache . get . mockResolvedValueOnce ( {
786
+ value : {
787
+ type : "route" ,
788
+ body : "{}" ,
789
+ } ,
790
+ lastModified : Date . now ( ) ,
791
+ shouldBypassTagCache : false ,
792
+ } ) ;
793
+
794
+ const result = await cache . get ( "key" , { kindHint : "app" } ) ;
795
+
796
+ expect ( getIncrementalCache ) . toHaveBeenCalled ( ) ;
797
+ expect ( tagCache . hasBeenRevalidated ) . toHaveBeenCalled ( ) ;
798
+ expect ( result ) . toBeNull ( ) ;
799
+ } ) ;
800
+
801
+ it ( "Should return value when tag cache indicates revalidation but shouldBypassTagCache is true" , async ( ) => {
802
+ incrementalCache . get . mockResolvedValueOnce ( {
803
+ value : {
804
+ type : "route" ,
805
+ body : "{}" ,
806
+ } ,
807
+ lastModified : Date . now ( ) ,
808
+ shouldBypassTagCache : true ,
809
+ } ) ;
810
+
811
+ const result = await cache . get ( "key" , { kindHint : "app" } ) ;
812
+
813
+ expect ( getIncrementalCache ) . toHaveBeenCalled ( ) ;
814
+ expect ( tagCache . getLastModified ) . not . toHaveBeenCalled ( ) ;
815
+ expect ( tagCache . hasBeenRevalidated ) . not . toHaveBeenCalled ( ) ;
816
+ expect ( result ) . not . toBeNull ( ) ;
817
+ expect ( result ?. value ?. kind ) . toEqual ( "ROUTE" ) ;
818
+ } ) ;
819
+ } ) ;
820
+ } ) ;
624
821
} ) ;
0 commit comments