5
5
6
6
import { Logger } from '@graphprotocol/common-ts'
7
7
import { NetworkDataCache , CacheOptions } from '../network-cache'
8
- import { CacheError , CacheEvictionError } from '../errors'
8
+ import { CacheError } from '../errors'
9
9
10
10
const createMockLogger = ( ) : Logger =>
11
11
( {
@@ -39,10 +39,10 @@ describe('NetworkDataCache', () => {
39
39
describe ( 'Basic Cache Operations' , ( ) => {
40
40
it ( 'should store and retrieve values' , async ( ) => {
41
41
const testValue = { data : 'test-data' }
42
-
42
+
43
43
cache . set ( 'test-key' , testValue )
44
44
const retrieved = await cache . get ( 'test-key' )
45
-
45
+
46
46
expect ( retrieved ) . toEqual ( testValue )
47
47
} )
48
48
@@ -54,29 +54,27 @@ describe('NetworkDataCache', () => {
54
54
it ( 'should handle multiple concurrent gets of same key' , async ( ) => {
55
55
const testValue = { data : 'concurrent-test' }
56
56
cache . set ( 'concurrent-key' , testValue )
57
-
58
- const promises = Array . from ( { length : 10 } , ( ) =>
59
- cache . get ( 'concurrent-key' )
60
- )
61
-
57
+
58
+ const promises = Array . from ( { length : 10 } , ( ) => cache . get ( 'concurrent-key' ) )
59
+
62
60
const results = await Promise . all ( promises )
63
- results . forEach ( result => {
61
+ results . forEach ( ( result ) => {
64
62
expect ( result ) . toEqual ( testValue )
65
63
} )
66
64
} )
67
65
68
66
it ( 'should handle cache hits and misses correctly' , async ( ) => {
69
67
const testValue = { id : 'test' , value : 'data' }
70
68
cache . set ( 'hit-key' , testValue )
71
-
69
+
72
70
// Cache hit
73
71
const hit = await cache . get ( 'hit-key' )
74
72
expect ( hit ) . toEqual ( testValue )
75
-
73
+
76
74
// Cache miss
77
75
const miss = await cache . get ( 'miss-key' )
78
76
expect ( miss ) . toBeUndefined ( )
79
-
77
+
80
78
const metrics = cache . getMetrics ( )
81
79
expect ( metrics . hits ) . toBe ( 1 )
82
80
expect ( metrics . misses ) . toBe ( 1 )
@@ -89,13 +87,13 @@ describe('NetworkDataCache', () => {
89
87
cache . set ( 'key1' , { data : 'value1' } )
90
88
cache . set ( 'key2' , { data : 'value2' } )
91
89
cache . set ( 'key3' , { data : 'value3' } )
92
-
90
+
93
91
// Access key1 to make it more recently used
94
92
await cache . get ( 'key1' )
95
-
93
+
96
94
// Add new item, should evict key2 (least recently used)
97
95
cache . set ( 'key4' , { data : 'value4' } )
98
-
96
+
99
97
expect ( await cache . get ( 'key1' ) ) . toBeDefined ( ) // Most recently used
100
98
expect ( await cache . get ( 'key2' ) ) . toBeUndefined ( ) // Should be evicted
101
99
expect ( await cache . get ( 'key3' ) ) . toBeDefined ( ) // Still present
@@ -107,7 +105,7 @@ describe('NetworkDataCache', () => {
107
105
for ( let i = 0 ; i < 5 ; i ++ ) {
108
106
cache . set ( `key${ i } ` , { data : `value${ i } ` } )
109
107
}
110
-
108
+
111
109
const metrics = cache . getMetrics ( )
112
110
expect ( metrics . evictions ) . toBe ( 2 ) // 5 items - 3 max size = 2 evictions
113
111
} )
@@ -116,13 +114,13 @@ describe('NetworkDataCache', () => {
116
114
// Mock eviction failure
117
115
const originalDelete = cache [ 'cache' ] . delete
118
116
cache [ 'cache' ] . delete = jest . fn ( ) . mockReturnValue ( false )
119
-
117
+
120
118
expect ( ( ) => {
121
119
for ( let i = 0 ; i < 5 ; i ++ ) {
122
120
cache . set ( `key${ i } ` , { data : `value${ i } ` } )
123
121
}
124
122
} ) . not . toThrow ( )
125
-
123
+
126
124
// Restore original method
127
125
cache [ 'cache' ] . delete = originalDelete
128
126
} )
@@ -134,18 +132,18 @@ describe('NetworkDataCache', () => {
134
132
ttl : 50 , // 50ms TTL
135
133
maxSize : 10 ,
136
134
} )
137
-
135
+
138
136
shortTTLCache . set ( 'expire-key' , { data : 'will-expire' } )
139
-
137
+
140
138
// Should be available immediately
141
139
expect ( await shortTTLCache . get ( 'expire-key' ) ) . toBeDefined ( )
142
-
140
+
143
141
// Wait for expiration
144
- await new Promise ( resolve => setTimeout ( resolve , 100 ) )
145
-
142
+ await new Promise ( ( resolve ) => setTimeout ( resolve , 100 ) )
143
+
146
144
// Should be expired
147
145
expect ( await shortTTLCache . get ( 'expire-key' ) ) . toBeUndefined ( )
148
-
146
+
149
147
shortTTLCache . dispose ( )
150
148
} )
151
149
@@ -155,25 +153,25 @@ describe('NetworkDataCache', () => {
155
153
maxSize : 10 ,
156
154
cleanupInterval : 25 , // Clean every 25ms
157
155
} )
158
-
156
+
159
157
cleanupCache . set ( 'cleanup-key' , { data : 'test' } )
160
-
158
+
161
159
// Wait for cleanup cycle
162
- await new Promise ( resolve => setTimeout ( resolve , 100 ) )
163
-
160
+ await new Promise ( ( resolve ) => setTimeout ( resolve , 100 ) )
161
+
164
162
const metrics = cleanupCache . getMetrics ( )
165
163
expect ( metrics . cleanupRuns ) . toBeGreaterThan ( 0 )
166
-
164
+
167
165
cleanupCache . dispose ( )
168
166
} )
169
167
170
168
it ( 'should update TTL on cache hit' , async ( ) => {
171
169
const testValue = { data : 'refresh-ttl' }
172
170
cache . set ( 'refresh-key' , testValue )
173
-
171
+
174
172
// Access the key to refresh TTL
175
173
await cache . get ( 'refresh-key' )
176
-
174
+
177
175
// Value should still be available
178
176
expect ( await cache . get ( 'refresh-key' ) ) . toEqual ( testValue )
179
177
} )
@@ -183,12 +181,12 @@ describe('NetworkDataCache', () => {
183
181
it ( 'should fetch and cache on miss' , async ( ) => {
184
182
const fetchedValue = { data : 'fetched-data' }
185
183
const fetcher = jest . fn ( ) . mockResolvedValue ( fetchedValue )
186
-
184
+
187
185
const result = await cache . getCachedOrFetch ( 'fetch-key' , fetcher )
188
-
186
+
189
187
expect ( result ) . toEqual ( fetchedValue )
190
188
expect ( fetcher ) . toHaveBeenCalledTimes ( 1 )
191
-
189
+
192
190
// Second call should use cache
193
191
const cachedResult = await cache . getCachedOrFetch ( 'fetch-key' , fetcher )
194
192
expect ( cachedResult ) . toEqual ( fetchedValue )
@@ -198,24 +196,25 @@ describe('NetworkDataCache', () => {
198
196
it ( 'should handle fetcher errors' , async ( ) => {
199
197
const fetchError = new Error ( 'Fetch failed' )
200
198
const fetcher = jest . fn ( ) . mockRejectedValue ( fetchError )
201
-
202
- await expect ( cache . getCachedOrFetch ( 'error-key' , fetcher ) )
203
- . rejects . toThrow ( CacheError )
199
+
200
+ await expect ( cache . getCachedOrFetch ( 'error-key' , fetcher ) ) . rejects . toThrow (
201
+ CacheError ,
202
+ )
204
203
} )
205
204
206
205
it ( 'should not cache failed fetch results' , async ( ) => {
207
- const fetcher = jest . fn ( )
206
+ const fetcher = jest
207
+ . fn ( )
208
208
. mockRejectedValueOnce ( new Error ( 'First failure' ) )
209
209
. mockResolvedValueOnce ( { data : 'success' } )
210
-
210
+
211
211
// First call fails
212
- await expect ( cache . getCachedOrFetch ( 'retry-key' , fetcher ) )
213
- . rejects . toThrow ( )
214
-
212
+ await expect ( cache . getCachedOrFetch ( 'retry-key' , fetcher ) ) . rejects . toThrow ( )
213
+
215
214
// Second call succeeds and caches
216
215
const result = await cache . getCachedOrFetch ( 'retry-key' , fetcher )
217
216
expect ( result ) . toEqual ( { data : 'success' } )
218
-
217
+
219
218
expect ( fetcher ) . toHaveBeenCalledTimes ( 2 )
220
219
} )
221
220
} )
@@ -225,16 +224,16 @@ describe('NetworkDataCache', () => {
225
224
// Generate various cache operations
226
225
cache . set ( 'metrics1' , { data : 'value1' } )
227
226
cache . set ( 'metrics2' , { data : 'value2' } )
228
-
227
+
229
228
await cache . get ( 'metrics1' ) // Hit
230
229
await cache . get ( 'nonexistent' ) // Miss
231
-
230
+
232
231
// Trigger eviction
233
232
cache . set ( 'metrics3' , { data : 'value3' } )
234
233
cache . set ( 'metrics4' , { data : 'value4' } ) // Should evict metrics2
235
-
234
+
236
235
const metrics = cache . getMetrics ( )
237
-
236
+
238
237
expect ( metrics . hits ) . toBe ( 1 )
239
238
expect ( metrics . misses ) . toBe ( 1 )
240
239
expect ( metrics . sets ) . toBe ( 4 )
@@ -246,12 +245,12 @@ describe('NetworkDataCache', () => {
246
245
it ( 'should reset metrics on demand' , async ( ) => {
247
246
cache . set ( 'reset-key' , { data : 'test' } )
248
247
await cache . get ( 'reset-key' )
249
-
248
+
250
249
let metrics = cache . getMetrics ( )
251
250
expect ( metrics . hits ) . toBe ( 1 )
252
-
251
+
253
252
cache . resetMetrics ( )
254
-
253
+
255
254
metrics = cache . getMetrics ( )
256
255
expect ( metrics . hits ) . toBe ( 0 )
257
256
expect ( metrics . misses ) . toBe ( 0 )
@@ -262,14 +261,14 @@ describe('NetworkDataCache', () => {
262
261
// Create known hit/miss pattern
263
262
cache . set ( 'hit1' , { data : 'value1' } )
264
263
cache . set ( 'hit2' , { data : 'value2' } )
265
-
264
+
266
265
// 2 hits, 3 misses = 2/5 = 0.4 hit rate
267
266
await cache . get ( 'hit1' ) // Hit
268
267
await cache . get ( 'hit2' ) // Hit
269
268
await cache . get ( 'miss1' ) // Miss
270
269
await cache . get ( 'miss2' ) // Miss
271
270
await cache . get ( 'miss3' ) // Miss
272
-
271
+
273
272
const metrics = cache . getMetrics ( )
274
273
expect ( metrics . hitRate ) . toBeCloseTo ( 0.4 )
275
274
} )
@@ -279,11 +278,11 @@ describe('NetworkDataCache', () => {
279
278
it ( 'should clear all entries' , async ( ) => {
280
279
cache . set ( 'clear1' , { data : 'value1' } )
281
280
cache . set ( 'clear2' , { data : 'value2' } )
282
-
281
+
283
282
expect ( cache . getMetrics ( ) . size ) . toBe ( 2 )
284
-
283
+
285
284
cache . clear ( )
286
-
285
+
287
286
expect ( cache . getMetrics ( ) . size ) . toBe ( 0 )
288
287
expect ( await cache . get ( 'clear1' ) ) . toBeUndefined ( )
289
288
expect ( await cache . get ( 'clear2' ) ) . toBeUndefined ( )
@@ -292,21 +291,21 @@ describe('NetworkDataCache', () => {
292
291
it ( 'should delete specific entries' , async ( ) => {
293
292
cache . set ( 'delete-key' , { data : 'to-delete' } )
294
293
cache . set ( 'keep-key' , { data : 'to-keep' } )
295
-
294
+
296
295
const deleted = cache . delete ( 'delete-key' )
297
296
expect ( deleted ) . toBe ( true )
298
-
297
+
299
298
expect ( await cache . get ( 'delete-key' ) ) . toBeUndefined ( )
300
299
expect ( await cache . get ( 'keep-key' ) ) . toBeDefined ( )
301
-
300
+
302
301
// Deleting non-existent key
303
302
const notDeleted = cache . delete ( 'non-existent' )
304
303
expect ( notDeleted ) . toBe ( false )
305
304
} )
306
305
307
306
it ( 'should check key existence' , ( ) => {
308
307
cache . set ( 'exists-key' , { data : 'exists' } )
309
-
308
+
310
309
expect ( cache . has ( 'exists-key' ) ) . toBe ( true )
311
310
expect ( cache . has ( 'non-existent' ) ) . toBe ( false )
312
311
} )
@@ -316,7 +315,7 @@ describe('NetworkDataCache', () => {
316
315
it ( 'should handle memory pressure gracefully' , ( ) => {
317
316
// Simulate memory-intensive cache operations
318
317
const largeData = { data : 'x' . repeat ( 10000 ) }
319
-
318
+
320
319
expect ( ( ) => {
321
320
for ( let i = 0 ; i < 100 ; i ++ ) {
322
321
cache . set ( `large-${ i } ` , { ...largeData , id : i } )
@@ -326,30 +325,30 @@ describe('NetworkDataCache', () => {
326
325
327
326
it ( 'should handle concurrent modifications safely' , async ( ) => {
328
327
const operations = [ ]
329
-
328
+
330
329
// Concurrent sets
331
330
for ( let i = 0 ; i < 50 ; i ++ ) {
332
331
operations . push (
333
- new Promise < void > ( resolve => {
332
+ new Promise < void > ( ( resolve ) => {
334
333
cache . set ( `concurrent-${ i } ` , { data : `value-${ i } ` } )
335
334
resolve ( )
336
- } )
335
+ } ) ,
337
336
)
338
337
}
339
-
338
+
340
339
// Concurrent gets
341
340
for ( let i = 0 ; i < 50 ; i ++ ) {
342
341
operations . push ( cache . get ( `concurrent-${ i } ` ) )
343
342
}
344
-
343
+
345
344
await expect ( Promise . all ( operations ) ) . resolves . not . toThrow ( )
346
345
} )
347
346
348
347
it ( 'should dispose resources properly' , ( ) => {
349
348
const disposeSpy = jest . spyOn ( cache , 'dispose' )
350
-
349
+
351
350
cache . dispose ( )
352
-
351
+
353
352
expect ( disposeSpy ) . toHaveBeenCalled ( )
354
353
expect ( logger . info ) . toHaveBeenCalledWith ( 'NetworkDataCache disposed' )
355
354
} )
@@ -361,12 +360,12 @@ describe('NetworkDataCache', () => {
361
360
ttl : 100 ,
362
361
maxSize : 5 ,
363
362
} )
364
-
363
+
365
364
customCache . set ( 'ttl-key' , { data : 'test' } )
366
-
365
+
367
366
// Should be available within TTL
368
367
expect ( await customCache . get ( 'ttl-key' ) ) . toBeDefined ( )
369
-
368
+
370
369
customCache . dispose ( )
371
370
} )
372
371
@@ -376,13 +375,13 @@ describe('NetworkDataCache', () => {
376
375
maxSize : 10 ,
377
376
enableMetrics : false ,
378
377
} )
379
-
378
+
380
379
noMetricsCache . set ( 'no-metrics' , { data : 'test' } )
381
-
380
+
382
381
const metrics = noMetricsCache . getMetrics ( )
383
382
expect ( typeof metrics ) . toBe ( 'object' )
384
-
383
+
385
384
noMetricsCache . dispose ( )
386
385
} )
387
386
} )
388
- } )
387
+ } )
0 commit comments