bv-ui-core 2.9.1 → 2.9.2
Sign up to get free protection for your applications and to get access to all the features.
- package/lib/bvFetch/README.md +48 -3
- package/lib/bvFetch/index.js +235 -73
- package/package.json +1 -1
- package/test/unit/bvFetch/index.spec.js +68 -11
package/lib/bvFetch/README.md
CHANGED
@@ -7,23 +7,68 @@ The BvFetch module provides methods to cache duplicate API calls and interact wi
|
|
7
7
|
|
8
8
|
## BvFetch Parameters
|
9
9
|
`shouldCache (Function):` A function that takes the API response JSON as input and returns a boolean indicating whether to cache the response or not. This allows you to implement custom logic based on the response content. If caching is desired, the function should return true; otherwise, false.
|
10
|
-
|
11
10
|
`cacheName (String):` Optional. Specifies the name of the cache to be used. If not provided, the default cache name 'bvCache' will be used.
|
11
|
+
`cacheLimit (Integer)`: Optional. Specifies the cache size limit for the cache storage. Its value should be in MB. Default value is 10 MB.
|
12
12
|
|
13
13
|
## bvFetchFunc Method Parameters
|
14
14
|
`url (String):` The URL of the API endpoint to fetch data from.
|
15
|
-
|
16
15
|
`options (Object):` Optional request options such as headers, method, etc., as supported by the Fetch API.
|
17
16
|
|
18
17
|
## bvFetchFunc Return Value
|
19
18
|
`Promise<Response>:` A promise that resolves to the API response. If the response is cached, it returns the cached response. Otherwise, it fetches data from the API endpoint, caches the response according to the caching logic, and returns the fetched response.
|
20
19
|
|
21
|
-
##
|
20
|
+
## generateCacheKey Method Parameters:
|
21
|
+
`url (String):` The URL of the API endpoint.
|
22
|
+
`options (Object):` Optional request options.
|
23
|
+
## generateCacheKey Return Value:
|
24
|
+
`string:` The generated cache key.
|
25
|
+
|
26
|
+
## retrieveCachedUrls Method
|
27
|
+
Retrieves cached URLs from the cache storage associated with the provided cache name.
|
28
|
+
## retrieveCachedUrls Parameters
|
22
29
|
This method takes no parameters.
|
30
|
+
## retrieveCachedUrls Return Value
|
31
|
+
`void:` This method does not return anything.
|
32
|
+
|
33
|
+
## fetchDataAndCache Method
|
34
|
+
Fetches data from the specified URL, caches the response, and returns the response.
|
35
|
+
## Parameters
|
36
|
+
`url (String):` The URL from which to fetch data.
|
37
|
+
`options (Object):` Optional request options such as headers, method, etc., as supported by the
|
38
|
+
Fetch API.
|
39
|
+
`cacheKey (String):`
|
40
|
+
The cache key associated with the fetched data.
|
41
|
+
## Return Value
|
42
|
+
`Promise<Response>:` A promise that resolves to the fetched response.
|
43
|
+
|
44
|
+
## fetchFromCache Method
|
45
|
+
Function to fetch data from cache.
|
46
|
+
## Parameters
|
47
|
+
`cacheKey (String):` The cache key to fetch data from the cache.
|
48
|
+
## Return Value
|
49
|
+
Promise<Response|null>: A Promise that resolves with a Response object if the data is found in cache, or null if the data is not cached or expired.
|
23
50
|
|
51
|
+
## cacheData Method
|
52
|
+
Caches the provided response with the specified cache key if it meets the criteria for caching.
|
53
|
+
## Parameters
|
54
|
+
`response (Response):` The response object to be cached.
|
55
|
+
`cacheKey (String):` The cache key associated with the response.
|
56
|
+
## Return Value
|
57
|
+
`void:` This method does not return anything.
|
58
|
+
|
59
|
+
|
60
|
+
## flushCache Method Parameters
|
61
|
+
This method takes no parameters.
|
24
62
|
## flushCache Return Value
|
25
63
|
`Promise<void>:` A promise indicating the completion of cache flush operation.
|
26
64
|
|
65
|
+
## manageCache Method
|
66
|
+
Manages the cache by deleting expired cache entries and maintaining the cache size limit.
|
67
|
+
## Parameters
|
68
|
+
This method takes no parameters.
|
69
|
+
## Return Value
|
70
|
+
`void:` This method does not return anything.
|
71
|
+
|
27
72
|
|
28
73
|
## Usage with of `BvFetch`:
|
29
74
|
|
package/lib/bvFetch/index.js
CHANGED
@@ -6,10 +6,12 @@
|
|
6
6
|
|
7
7
|
const { fetch } = require('../polyfills/fetch')
|
8
8
|
|
9
|
-
module.exports = function BvFetch ({ shouldCache, cacheName }) {
|
9
|
+
module.exports = function BvFetch ({ shouldCache, cacheName, cacheLimit }) {
|
10
10
|
this.shouldCache = shouldCache;
|
11
11
|
this.cacheName = cacheName || 'bvCache';
|
12
|
+
this.cacheLimit = cacheLimit * 1024 * 1024 || 10 * 1024 * 1024;
|
12
13
|
this.fetchPromises = new Map();
|
14
|
+
this.cachedUrls = new Set();
|
13
15
|
|
14
16
|
/**
|
15
17
|
* Generates a unique cache key for the given URL and options.
|
@@ -24,6 +26,128 @@ module.exports = function BvFetch ({ shouldCache, cacheName }) {
|
|
24
26
|
return key;
|
25
27
|
};
|
26
28
|
|
29
|
+
/**
|
30
|
+
* Retrieves cached URLs from the cache storage associated with the provided cache name.
|
31
|
+
* @returns {void}
|
32
|
+
*/
|
33
|
+
|
34
|
+
this.retrieveCachedUrls = () => {
|
35
|
+
// Open the Cache Storage
|
36
|
+
caches.open(this.cacheName).then(cache => {
|
37
|
+
// Get all cache keys
|
38
|
+
cache.keys().then(keys => {
|
39
|
+
keys.forEach(request => {
|
40
|
+
this.cachedUrls.add(request.url);
|
41
|
+
});
|
42
|
+
});
|
43
|
+
});
|
44
|
+
|
45
|
+
}
|
46
|
+
|
47
|
+
//callretrieveCachedUrls function to set the cache URL set with the cached URLS
|
48
|
+
this.retrieveCachedUrls();
|
49
|
+
|
50
|
+
/**
|
51
|
+
* Fetches data from the specified URL, caches the response, and returns the response.
|
52
|
+
* @param {string} url - The URL from which to fetch data.
|
53
|
+
* @param {string} cacheKey - The cache key associated with the fetched data.
|
54
|
+
* @returns {Promise<Response>} A Promise that resolves with the fetched response.
|
55
|
+
* @throws {Error} Throws an error if there's any problem fetching the data.
|
56
|
+
*/
|
57
|
+
this.fetchDataAndCache = (url, options = {}, cacheKey) => {
|
58
|
+
return fetch(url,options)
|
59
|
+
.then((response) => {
|
60
|
+
// initiate caching of response and return the response
|
61
|
+
this.cacheData(response, cacheKey);
|
62
|
+
return response.clone();
|
63
|
+
})
|
64
|
+
.catch(function (error) {
|
65
|
+
throw new Error('Error fetching data: ' + error);
|
66
|
+
});
|
67
|
+
}
|
68
|
+
|
69
|
+
/**
|
70
|
+
* Caches the provided response with the specified cache key if it meets the criteria for caching.
|
71
|
+
* @param {Response} response - The response object to be cached.
|
72
|
+
* @param {string} cacheKey - The cache key associated with the response.
|
73
|
+
* @returns {void}
|
74
|
+
*/
|
75
|
+
|
76
|
+
this.cacheData = (response, cacheKey) => {
|
77
|
+
const errJson = response.clone();
|
78
|
+
let canBeCached = true;
|
79
|
+
// Check for error in response obj
|
80
|
+
errJson.json().then(json => {
|
81
|
+
if (typeof this.shouldCache === 'function') {
|
82
|
+
canBeCached = this.shouldCache(json);
|
83
|
+
}
|
84
|
+
}).then(() => {
|
85
|
+
if (canBeCached) {
|
86
|
+
const clonedResponse = response.clone()
|
87
|
+
const newHeaders = new Headers();
|
88
|
+
clonedResponse.headers.forEach((value, key) => {
|
89
|
+
newHeaders.append(key, value);
|
90
|
+
});
|
91
|
+
newHeaders.append('X-Bazaarvoice-Cached-Time', Date.now())
|
92
|
+
// Get response text to calculate its size
|
93
|
+
clonedResponse.text().then(text => {
|
94
|
+
// Calculate size of response text in bytes
|
95
|
+
const sizeInBytes = new Blob([text]).size;
|
96
|
+
|
97
|
+
// Append response size to headers
|
98
|
+
newHeaders.append('X-Bazaarvoice-Response-Size', sizeInBytes);
|
99
|
+
|
100
|
+
// Create new Response object with modified headers
|
101
|
+
const newResponse = new Response(clonedResponse._bodyBlob || clonedResponse.body, {
|
102
|
+
status: clonedResponse.status,
|
103
|
+
statusText: clonedResponse.statusText,
|
104
|
+
headers: newHeaders
|
105
|
+
});
|
106
|
+
// Cache the response
|
107
|
+
caches.open(this.cacheName).then(cache => {
|
108
|
+
cache.put(cacheKey, newResponse);
|
109
|
+
//add key to cachedUrls set
|
110
|
+
this.cachedUrls.add(cacheKey);
|
111
|
+
});
|
112
|
+
});
|
113
|
+
}
|
114
|
+
})
|
115
|
+
}
|
116
|
+
|
117
|
+
/**
|
118
|
+
* Function to fetch data from cache.
|
119
|
+
* @param {string} cacheKey - The cache key to fetch data from the cache.
|
120
|
+
* @returns {Promise<Response|null>} A Promise that resolves with a Response object if the data is found in cache,
|
121
|
+
* or null if the data is not cached or expired.
|
122
|
+
* @throws {Error} Throws an error if there's any problem fetching from cache.
|
123
|
+
*/
|
124
|
+
this.fetchFromCache = (cacheKey) => {
|
125
|
+
// Check if the URL is in the set of cached URLs
|
126
|
+
if (!this.cachedUrls.has(cacheKey)) {
|
127
|
+
return Promise.resolve(null);
|
128
|
+
}
|
129
|
+
|
130
|
+
// Open the cache and try to match the URL
|
131
|
+
return caches.open(this.cacheName)
|
132
|
+
.then((cache) => {
|
133
|
+
return cache.match(cacheKey)
|
134
|
+
.then((cachedResponse) => {
|
135
|
+
|
136
|
+
const cachedTime = cachedResponse.headers.get('X-Bazaarvoice-Cached-Time');
|
137
|
+
const ttl = cachedResponse.headers.get('Cache-Control').match(/max-age=(\d+)/)[1];
|
138
|
+
const currentTimestamp = Date.now();
|
139
|
+
const cacheAge = (currentTimestamp - cachedTime) / 1000;
|
140
|
+
if (cacheAge < ttl) {
|
141
|
+
// Cached response found
|
142
|
+
return cachedResponse.clone();
|
143
|
+
}
|
144
|
+
})
|
145
|
+
})
|
146
|
+
.catch((error) => {
|
147
|
+
throw new Error('Error fetching from cache: ' + error);
|
148
|
+
});
|
149
|
+
}
|
150
|
+
|
27
151
|
/**
|
28
152
|
* Fetches data from the API endpoint, caches responses, and handles caching logic.
|
29
153
|
* @param {string} url - The URL of the API endpoint.
|
@@ -32,84 +156,38 @@ module.exports = function BvFetch ({ shouldCache, cacheName }) {
|
|
32
156
|
*/
|
33
157
|
|
34
158
|
this.bvFetchFunc = (url, options = {}) => {
|
35
|
-
|
159
|
+
|
36
160
|
const cacheKey = this.generateCacheKey(url, options);
|
161
|
+
// If an ongoing fetch promise exists for the URL, return it
|
162
|
+
if (this.fetchPromises.has(cacheKey)) {
|
163
|
+
return this.fetchPromises.get(cacheKey).then(res => res.clone());
|
164
|
+
}
|
37
165
|
|
38
|
-
|
39
|
-
|
40
|
-
.then(
|
41
|
-
|
166
|
+
// Check if response is available in cache
|
167
|
+
const newPromise = this.fetchFromCache(cacheKey)
|
168
|
+
.then((cachedResponse) => {
|
169
|
+
// If response found in cache, return it
|
42
170
|
if (cachedResponse) {
|
43
|
-
|
44
|
-
const ttl = cachedResponse.headers.get('Cache-Control').match(/max-age=(\d+)/)[1];
|
45
|
-
const currentTimestamp = Date.now();
|
46
|
-
const cacheAge = (currentTimestamp - cachedTime) / 1000;
|
47
|
-
|
48
|
-
if (cacheAge < ttl) {
|
49
|
-
// Cached response found
|
50
|
-
return cachedResponse.clone();
|
51
|
-
}
|
171
|
+
return cachedResponse;
|
52
172
|
}
|
173
|
+
// If response not found in cache, fetch from API and cache it
|
174
|
+
return this.fetchDataAndCache(url, options, cacheKey);
|
175
|
+
});
|
53
176
|
|
54
|
-
|
55
|
-
|
56
|
-
return this.fetchPromises.get(cacheKey).then(res => res.clone());
|
57
|
-
}
|
177
|
+
// Store the ongoing fetch promise
|
178
|
+
this.fetchPromises.set(cacheKey, newPromise);
|
58
179
|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
let canBeCached = true;
|
70
|
-
return errJson.json().then(json => {
|
71
|
-
if (typeof this.shouldCache === 'function') {
|
72
|
-
canBeCached = this.shouldCache(json);
|
73
|
-
}
|
74
|
-
return response
|
75
|
-
}).then(res => {
|
76
|
-
if (canBeCached) {
|
77
|
-
const newHeaders = new Headers();
|
78
|
-
clonedResponse.headers.forEach((value, key) => {
|
79
|
-
newHeaders.append(key, value);
|
80
|
-
});
|
81
|
-
newHeaders.append('X-Cached-Time', Date.now());
|
82
|
-
|
83
|
-
const newResponse = new Response(clonedResponse._bodyBlob, {
|
84
|
-
status: clonedResponse.status,
|
85
|
-
statusText: clonedResponse.statusText,
|
86
|
-
headers: newHeaders
|
87
|
-
});
|
88
|
-
//Delete promise from promise map once its resolved
|
89
|
-
this.fetchPromises.delete(cacheKey);
|
90
|
-
|
91
|
-
return caches.open(this.cacheName)
|
92
|
-
.then(currentCache =>
|
93
|
-
currentCache.put(cacheKey, newResponse)
|
94
|
-
)
|
95
|
-
.then(() => res);
|
96
|
-
}
|
97
|
-
else {
|
98
|
-
//Delete promise from promise map if error exists
|
99
|
-
this.fetchPromises.delete(cacheKey);
|
100
|
-
|
101
|
-
return res
|
102
|
-
}
|
180
|
+
//initiate cache cleanUp
|
181
|
+
this.debounceCleanupExpiredCache();
|
182
|
+
|
183
|
+
// When fetch completes or fails, remove the promise from the store
|
184
|
+
newPromise.finally(() => {
|
185
|
+
this.fetchPromises.delete(cacheKey);
|
186
|
+
});
|
187
|
+
|
188
|
+
return newPromise.then(res => res.clone());
|
189
|
+
}
|
103
190
|
|
104
|
-
});
|
105
|
-
})
|
106
|
-
})
|
107
|
-
.catch(err => {
|
108
|
-
// Remove the promise that was pushed earlier
|
109
|
-
this.fetchPromises.delete(cacheKey);
|
110
|
-
throw err;
|
111
|
-
});
|
112
|
-
};
|
113
191
|
|
114
192
|
/**
|
115
193
|
* Clears all cache entries stored in the cache storage.
|
@@ -124,5 +202,89 @@ module.exports = function BvFetch ({ shouldCache, cacheName }) {
|
|
124
202
|
});
|
125
203
|
});
|
126
204
|
};
|
205
|
+
|
206
|
+
this.manageCache = () => {
|
207
|
+
// Delete expired cache entries
|
208
|
+
caches.open(this.cacheName).then(cache => {
|
209
|
+
cache.keys().then(keys => {
|
210
|
+
keys.forEach(key => {
|
211
|
+
cache.match(key).then(response => {
|
212
|
+
const cachedTime = response.headers.get('X-Bazaarvoice-Cached-Time');
|
213
|
+
const ttl = response.headers.get('Cache-Control').match(/max-age=(\d+)/)[1];
|
214
|
+
const currentTimestamp = Date.now();
|
215
|
+
const cacheAge = (currentTimestamp - cachedTime) / 1000;
|
216
|
+
if (cacheAge >= ttl) {
|
217
|
+
cache.delete(key);
|
218
|
+
this.cachedUrls.delete(key);
|
219
|
+
}
|
220
|
+
});
|
221
|
+
});
|
222
|
+
});
|
223
|
+
});
|
224
|
+
|
225
|
+
// Calculate total size of cached responses
|
226
|
+
let totalSize = 0;
|
227
|
+
caches.open(this.cacheName).then(cache => {
|
228
|
+
cache.keys().then(keys => {
|
229
|
+
// Create an array of promises for cache match operations
|
230
|
+
const matchPromises = keys.map(key =>
|
231
|
+
cache.match(key).then(response => {
|
232
|
+
const sizeHeader = response.headers.get('X-Bazaarvoice-Response-Size');
|
233
|
+
return parseInt(sizeHeader, 10);
|
234
|
+
})
|
235
|
+
);
|
236
|
+
|
237
|
+
// wait for all match promises to resolve
|
238
|
+
return Promise.all(matchPromises)
|
239
|
+
.then(sizes => sizes.reduce((acc, size) => acc + size, 0));
|
240
|
+
}).then(size => {
|
241
|
+
totalSize = size;
|
242
|
+
// If total size exceeds 10 MB, delete old cache entries
|
243
|
+
if (totalSize > this.cacheLimit) {
|
244
|
+
|
245
|
+
// create an array of cached responses
|
246
|
+
const cacheEntries = [];
|
247
|
+
return cache.keys().then(keys => {
|
248
|
+
const cachesResEntries = keys.map(key =>
|
249
|
+
cache.match(key).then(response => {
|
250
|
+
const sizeHeader = response.headers.get('X-Bazaarvoice-Response-Size');
|
251
|
+
const lastAccessedTime = response.headers.get('X-Bazaarvoice-Cached-Time');
|
252
|
+
cacheEntries.push({ key, size: parseInt(sizeHeader, 10), lastAccessedTime });
|
253
|
+
})
|
254
|
+
);
|
255
|
+
|
256
|
+
return Promise.all(cachesResEntries)
|
257
|
+
.then(() => {
|
258
|
+
// Sort cache entries by last accessed time in ascending order
|
259
|
+
cacheEntries.sort((a, b) => a.lastAccessedTime - b.lastAccessedTime);
|
260
|
+
|
261
|
+
// Delete older cache entries until total size is under 10 MB
|
262
|
+
let currentSize = totalSize;
|
263
|
+
cacheEntries.forEach(entry => {
|
264
|
+
if (currentSize > this.cacheLimit) {
|
265
|
+
cache.delete(entry.key);
|
266
|
+
this.cachedUrls.delete(entry.key);
|
267
|
+
currentSize -= entry.size;
|
268
|
+
}
|
269
|
+
});
|
270
|
+
});
|
271
|
+
});
|
272
|
+
}
|
273
|
+
});
|
274
|
+
});
|
275
|
+
};
|
276
|
+
|
277
|
+
|
278
|
+
function debounce (func, delay) {
|
279
|
+
let timer;
|
280
|
+
return function () {
|
281
|
+
clearTimeout(timer);
|
282
|
+
timer = setTimeout(() => {
|
283
|
+
func.apply(this, arguments);
|
284
|
+
}, delay);
|
285
|
+
};
|
286
|
+
}
|
287
|
+
|
288
|
+
this.debounceCleanupExpiredCache = debounce(this.manageCache, 8000);
|
127
289
|
|
128
|
-
}
|
290
|
+
}
|
package/package.json
CHANGED
@@ -65,7 +65,7 @@ describe('BvFetch', function () {
|
|
65
65
|
caches.open.resolves({
|
66
66
|
match: (key) => {
|
67
67
|
expect(key).to.equal(cacheKey);
|
68
|
-
Promise.resolve(mockResponse)
|
68
|
+
return Promise.resolve(mockResponse)
|
69
69
|
},
|
70
70
|
put: (key, response) => {
|
71
71
|
cacheStorage.set(key, response);
|
@@ -73,6 +73,10 @@ describe('BvFetch', function () {
|
|
73
73
|
}
|
74
74
|
});
|
75
75
|
|
76
|
+
// Simulate that the response is cached
|
77
|
+
bvFetchInstance.cachedUrls.add(cacheKey);
|
78
|
+
|
79
|
+
// Call the function under test
|
76
80
|
bvFetchInstance.bvFetchFunc(url, options)
|
77
81
|
.then(response => {
|
78
82
|
// Check if response is fetched from cache
|
@@ -91,19 +95,18 @@ describe('BvFetch', function () {
|
|
91
95
|
done(error); // Call done with error if any
|
92
96
|
})
|
93
97
|
});
|
94
|
-
|
95
98
|
|
99
|
+
|
96
100
|
it('should fetch from network when response is not cached', function (done) {
|
97
101
|
const url = 'https://jsonplaceholder.typicode.com/todos';
|
98
102
|
const options = {};
|
99
103
|
|
100
|
-
const
|
101
|
-
|
104
|
+
const matchSpy = sinon.spy((key) => {
|
105
|
+
expect(key).to.equal(cacheKey);
|
106
|
+
Promise.resolve(null)
|
107
|
+
});
|
102
108
|
caches.open.resolves({
|
103
|
-
match:
|
104
|
-
expect(key).to.equal(cacheKey);
|
105
|
-
Promise.resolve(null)
|
106
|
-
},
|
109
|
+
match: matchSpy,
|
107
110
|
put: (key, response) => {
|
108
111
|
cacheStorage.set(key, response);
|
109
112
|
return Promise.resolve();
|
@@ -118,7 +121,7 @@ describe('BvFetch', function () {
|
|
118
121
|
console.log(response.body)
|
119
122
|
|
120
123
|
// Check if caches.match was called
|
121
|
-
expect(
|
124
|
+
expect(matchSpy.called).to.be.false;
|
122
125
|
|
123
126
|
done();
|
124
127
|
})
|
@@ -133,7 +136,7 @@ describe('BvFetch', function () {
|
|
133
136
|
bvFetchInstance.shouldCache = (res) => {
|
134
137
|
return false
|
135
138
|
};
|
136
|
-
|
139
|
+
|
137
140
|
bvFetchInstance.bvFetchFunc(url, options)
|
138
141
|
.then(response => {
|
139
142
|
// Check if response is fetched from network
|
@@ -141,7 +144,7 @@ describe('BvFetch', function () {
|
|
141
144
|
console.log(response.body)
|
142
145
|
|
143
146
|
// Check if caches.match was called
|
144
|
-
expect(cacheStub.calledOnce).to.be.
|
147
|
+
expect(cacheStub.calledOnce).to.be.false;
|
145
148
|
|
146
149
|
// Check if response is not cached
|
147
150
|
const cachedResponse = cacheStorage.get(url);
|
@@ -152,5 +155,59 @@ describe('BvFetch', function () {
|
|
152
155
|
.catch(done);
|
153
156
|
});
|
154
157
|
|
158
|
+
it('should delete cache when size is greater than 10 MB', function (done) {
|
159
|
+
// Mock cache entries exceeding 10 MB
|
160
|
+
const mockCacheEntries = [
|
161
|
+
{ key: 'key1', size: 6000000 }, // 6 MB
|
162
|
+
{ key: 'key2', size: 6000000 } // 6 MB
|
163
|
+
// Add more entries as needed to exceed 10 MB
|
164
|
+
];
|
165
|
+
|
166
|
+
// Stub cache operations
|
167
|
+
const deleteSpy = sinon.spy(
|
168
|
+
(key) => {
|
169
|
+
const index = mockCacheEntries.findIndex(entry => entry.key === key);
|
170
|
+
if (index !== -1) {
|
171
|
+
mockCacheEntries.splice(index, 1); // Delete entry from mock cache entries
|
172
|
+
}
|
173
|
+
return Promise.resolve(true);
|
174
|
+
}
|
175
|
+
)
|
176
|
+
caches.open.resolves({
|
177
|
+
keys: () => Promise.resolve(mockCacheEntries.map(entry => entry.key)),
|
178
|
+
match: (key) => {
|
179
|
+
const entry = mockCacheEntries.find(entry => entry.key === key);
|
180
|
+
if (entry) {
|
181
|
+
return Promise.resolve({
|
182
|
+
headers: new Headers({
|
183
|
+
'X-Bazaarvoice-Response-Size': entry.size.toString(),
|
184
|
+
'X-Bazaarvoice-Cached-Time': Date.now(),
|
185
|
+
'Cache-Control': 'max-age=3600'
|
186
|
+
})
|
187
|
+
});
|
188
|
+
}
|
189
|
+
else {
|
190
|
+
return Promise.resolve(null);
|
191
|
+
}
|
192
|
+
},
|
193
|
+
delete: deleteSpy
|
194
|
+
});
|
195
|
+
|
196
|
+
// Create a new instance of BvFetch
|
197
|
+
const bvFetchInstance = new BvFetch({ shouldCache: true });
|
198
|
+
|
199
|
+
// Call manageCache function
|
200
|
+
bvFetchInstance.manageCache()
|
201
|
+
setTimeout(() => {
|
202
|
+
// Ensure cache deletion occurred until the total size is under 10 MB
|
203
|
+
const totalSizeAfterDeletion = mockCacheEntries.reduce((acc, entry) => acc + entry.size, 0);
|
204
|
+
expect(totalSizeAfterDeletion).to.be.at.most(10 * 1024 * 1024); // Total size should be under 10 MB
|
205
|
+
// Ensure cache.delete was called for each deleted entry
|
206
|
+
expect(deleteSpy.called).to.be.true;
|
207
|
+
expect(deleteSpy.callCount).to.equal(mockCacheEntries.length);
|
208
|
+
done();
|
209
|
+
}, 500);
|
210
|
+
|
211
|
+
});
|
155
212
|
|
156
213
|
});
|