1+ import { describe , expect , it , vi } from "vitest" ;
2+
3+ import * as internal from "../overrides/internal" ;
4+ import { BucketCachePurge } from "./bucket-cache-purge" ;
5+
6+ vi . mock ( "cloudflare:workers" , ( ) => ( {
7+ DurableObject : class {
8+ constructor (
9+ public ctx : DurableObjectState ,
10+ public env : CloudflareEnv
11+ ) { }
12+ } ,
13+ } ) ) ;
14+
15+
16+ const createBucketCachePurge = ( ) => {
17+ const mockState = {
18+ waitUntil : vi . fn ( ) ,
19+ blockConcurrencyWhile : vi . fn ( ) . mockImplementation ( async ( fn ) => fn ( ) ) ,
20+ storage : {
21+ setAlarm : vi . fn ( ) ,
22+ getAlarm : vi . fn ( ) ,
23+ sql : {
24+ exec : vi . fn ( ) . mockImplementation ( ( ) => ( {
25+ one : vi . fn ( ) ,
26+ toArray : vi . fn ( ) . mockReturnValue ( [ ] ) ,
27+ } ) ) ,
28+ } ,
29+ } ,
30+ } ;
31+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
32+ return new BucketCachePurge ( mockState as any , { } ) ;
33+ } ;
34+
35+ describe ( "BucketCachePurge" , ( ) => {
36+ it ( "should block concurrency while creating the table" , async ( ) => {
37+ const cache = createBucketCachePurge ( ) ;
38+ // @ts -expect-error - testing private method
39+ expect ( cache . ctx . blockConcurrencyWhile ) . toHaveBeenCalled ( ) ;
40+ // @ts -expect-error - testing private method
41+ expect ( cache . ctx . storage . sql . exec ) . toHaveBeenCalledWith (
42+ expect . stringContaining ( "CREATE TABLE IF NOT EXISTS cache_purge" ) ,
43+ ) ;
44+ } ) ;
45+
46+ describe ( "purgeCacheByTags" , ( ) => {
47+ it ( "should insert tags into the sql table" , async ( ) => {
48+ const cache = createBucketCachePurge ( ) ;
49+ const tags = [ "tag1" , "tag2" ] ;
50+ await cache . purgeCacheByTags ( tags ) ;
51+ // @ts -expect-error - testing private method
52+ expect ( cache . ctx . storage . sql . exec ) . toHaveBeenCalledWith (
53+ expect . stringContaining ( "INSERT OR REPLACE INTO cache_purge" ) ,
54+ [ tags [ 0 ] ] ,
55+ ) ;
56+ // @ts -expect-error - testing private method
57+ expect ( cache . ctx . storage . sql . exec ) . toHaveBeenCalledWith (
58+ expect . stringContaining ( "INSERT OR REPLACE INTO cache_purge" ) ,
59+ [ tags [ 1 ] ] ,
60+ ) ;
61+ } ) ;
62+
63+ it ( "should set an alarm if no alarm is set" , async ( ) => {
64+ const cache = createBucketCachePurge ( ) ;
65+ // @ts -expect-error - testing private method
66+ cache . ctx . storage . getAlarm . mockResolvedValueOnce ( null ) ;
67+ await cache . purgeCacheByTags ( [ "tag" ] ) ;
68+ // @ts -expect-error - testing private method
69+ expect ( cache . ctx . storage . setAlarm ) . toHaveBeenCalled ( ) ;
70+ } ) ;
71+
72+ it ( "should not set an alarm if one is already set" , async ( ) => {
73+ const cache = createBucketCachePurge ( ) ;
74+ // @ts -expect-error - testing private method
75+ cache . ctx . storage . getAlarm . mockResolvedValueOnce ( true ) ;
76+ await cache . purgeCacheByTags ( [ "tag" ] ) ;
77+ // @ts -expect-error - testing private method
78+ expect ( cache . ctx . storage . setAlarm ) . not . toHaveBeenCalled ( ) ;
79+ } ) ;
80+ } )
81+
82+ describe ( "alarm" , ( ) => {
83+ it ( "should purge cache by tags and delete them from the sql table" , async ( ) => {
84+ const cache = createBucketCachePurge ( ) ;
85+ // @ts -expect-error - testing private method
86+ cache . ctx . storage . sql . exec . mockReturnValueOnce ( {
87+ toArray : ( ) => [ { tag : "tag1" } , { tag : "tag2" } ] ,
88+ } ) ;
89+ await cache . alarm ( ) ;
90+ // @ts -expect-error - testing private method
91+ expect ( cache . ctx . storage . sql . exec ) . toHaveBeenCalledWith (
92+ expect . stringContaining ( "DELETE FROM cache_purge" ) ,
93+ [ "tag1" , "tag2" ] ,
94+ ) ;
95+ } ) ;
96+ it ( "should not purge cache if no tags are found" , async ( ) => {
97+ const cache = createBucketCachePurge ( ) ;
98+ // @ts -expect-error - testing private method
99+ cache . ctx . storage . sql . exec . mockReturnValueOnce ( {
100+ toArray : ( ) => [ ] ,
101+ } ) ;
102+ await cache . alarm ( ) ;
103+ // @ts -expect-error - testing private method
104+ expect ( cache . ctx . storage . sql . exec ) . not . toHaveBeenCalledWith (
105+ expect . stringContaining ( "DELETE FROM cache_purge" ) ,
106+ [ ] ,
107+ ) ;
108+ } ) ;
109+
110+ it ( "should call internalPurgeCacheByTags with the correct tags" , async ( ) => {
111+ const cache = createBucketCachePurge ( ) ;
112+ const tags = [ "tag1" , "tag2" ] ;
113+ // @ts -expect-error - testing private method
114+ cache . ctx . storage . sql . exec . mockReturnValueOnce ( {
115+ toArray : ( ) => tags . map ( ( tag ) => ( { tag } ) ) ,
116+ } ) ;
117+ const internalPurgeCacheByTagsSpy = vi . spyOn ( internal , "internalPurgeCacheByTags" ) ;
118+ await cache . alarm ( ) ;
119+ expect ( internalPurgeCacheByTagsSpy ) . toHaveBeenCalledWith (
120+ // @ts -expect-error - testing private method
121+ cache . env ,
122+ tags ,
123+ ) ;
124+ // @ts -expect-error - testing private method 1st is constructor, 2nd is to get the tags and 3rd is to delete them
125+ expect ( cache . ctx . storage . sql . exec ) . toHaveBeenCalledTimes ( 3 ) ;
126+ } ) ;
127+
128+ it ( "should continue until all tags are purged" , async ( ) => {
129+ const cache = createBucketCachePurge ( ) ;
130+ const tags = Array . from ( { length : 100 } , ( _ , i ) => `tag${ i } ` ) ;
131+ // @ts -expect-error - testing private method
132+ cache . ctx . storage . sql . exec . mockReturnValueOnce ( {
133+ toArray : ( ) => tags . map ( ( tag ) => ( { tag } ) ) ,
134+ } ) ;
135+ const internalPurgeCacheByTagsSpy = vi . spyOn ( internal , "internalPurgeCacheByTags" ) ;
136+ await cache . alarm ( ) ;
137+ expect ( internalPurgeCacheByTagsSpy ) . toHaveBeenCalledWith (
138+ // @ts -expect-error - testing private method
139+ cache . env ,
140+ tags ,
141+ ) ;
142+ // @ts -expect-error - testing private method 1st is constructor, 2nd is to get the tags and 3rd is to delete them, 4th is to get the next 100 tags
143+ expect ( cache . ctx . storage . sql . exec ) . toHaveBeenCalledTimes ( 4 ) ;
144+ // @ts -expect-error - testing private method
145+ expect ( cache . ctx . storage . sql . exec ) . toHaveBeenLastCalledWith (
146+ expect . stringContaining ( "SELECT * FROM cache_purge LIMIT 100" ) ,
147+ ) ;
148+ } ) ;
149+
150+
151+ } )
152+ } ) ;
0 commit comments