@umituz/react-native-storage 2.6.0 → 2.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (28) hide show
  1. package/package.json +7 -3
  2. package/src/cache/__tests__/PerformanceAndMemory.test.ts +386 -0
  3. package/src/cache/__tests__/setup.ts +19 -0
  4. package/src/cache/domain/Cache.ts +146 -0
  5. package/src/cache/domain/CacheManager.ts +48 -0
  6. package/src/cache/domain/CacheStatsTracker.ts +49 -0
  7. package/src/cache/domain/ErrorHandler.ts +42 -0
  8. package/src/cache/domain/PatternMatcher.ts +30 -0
  9. package/src/cache/domain/__tests__/Cache.test.ts +292 -0
  10. package/src/cache/domain/__tests__/CacheManager.test.ts +276 -0
  11. package/src/cache/domain/__tests__/ErrorHandler.test.ts +303 -0
  12. package/src/cache/domain/__tests__/PatternMatcher.test.ts +261 -0
  13. package/src/cache/domain/strategies/EvictionStrategy.ts +9 -0
  14. package/src/cache/domain/strategies/FIFOStrategy.ts +12 -0
  15. package/src/cache/domain/strategies/LFUStrategy.ts +22 -0
  16. package/src/cache/domain/strategies/LRUStrategy.ts +22 -0
  17. package/src/cache/domain/strategies/TTLStrategy.ts +23 -0
  18. package/src/cache/domain/strategies/__tests__/EvictionStrategies.test.ts +293 -0
  19. package/src/cache/domain/types/Cache.ts +28 -0
  20. package/src/cache/index.ts +28 -0
  21. package/src/cache/infrastructure/TTLCache.ts +103 -0
  22. package/src/cache/infrastructure/__tests__/TTLCache.test.ts +303 -0
  23. package/src/cache/presentation/__tests__/ReactHooks.test.ts +512 -0
  24. package/src/cache/presentation/useCache.ts +76 -0
  25. package/src/cache/presentation/useCachedValue.ts +88 -0
  26. package/src/cache/types.d.ts +3 -0
  27. package/src/index.ts +28 -0
  28. package/src/types/global.d.ts +2 -0
@@ -0,0 +1,303 @@
1
+ /**
2
+ * TTLCache Tests
3
+ */
4
+
5
+ import { TTLCache } from '../TTLCache';
6
+ import type { CacheConfig } from '../domain/types/Cache';
7
+
8
+ describe('TTLCache', () => {
9
+ let cache: TTLCache<string>;
10
+
11
+ beforeEach(() => {
12
+ jest.useFakeTimers();
13
+ cache = new TTLCache<string>({
14
+ maxSize: 5,
15
+ defaultTTL: 1000,
16
+ cleanupIntervalMs: 500
17
+ });
18
+ });
19
+
20
+ afterEach(() => {
21
+ jest.useRealTimers();
22
+ cache.destroy();
23
+ });
24
+
25
+ describe('Basic Functionality', () => {
26
+ test('should inherit from Cache', () => {
27
+ expect(cache).toHaveProperty('set');
28
+ expect(cache).toHaveProperty('get');
29
+ expect(cache).toHaveProperty('has');
30
+ expect(cache).toHaveProperty('delete');
31
+ expect(cache).toHaveProperty('clear');
32
+ });
33
+
34
+ test('should set and get values', () => {
35
+ cache.set('key1', 'value1');
36
+ expect(cache.get('key1')).toBe('value1');
37
+ });
38
+
39
+ test('should use default TTL', () => {
40
+ cache.set('key1', 'value1');
41
+ expect(cache.get('key1')).toBe('value1');
42
+
43
+ jest.advanceTimersByTime(1001);
44
+ expect(cache.get('key1')).toBeUndefined();
45
+ });
46
+
47
+ test('should use custom TTL', () => {
48
+ cache.set('key1', 'value1', 2000);
49
+ expect(cache.get('key1')).toBe('value1');
50
+
51
+ jest.advanceTimersByTime(1001);
52
+ expect(cache.get('key1')).toBe('value1'); // Should still exist
53
+
54
+ jest.advanceTimersByTime(1000);
55
+ expect(cache.get('key1')).toBeUndefined(); // Should be expired
56
+ });
57
+ });
58
+
59
+ describe('Automatic Cleanup', () => {
60
+ test('should start cleanup interval on creation', () => {
61
+ const setIntervalSpy = jest.spyOn(global, 'setInterval');
62
+ new TTLCache<string>({ cleanupIntervalMs: 1000 });
63
+
64
+ expect(setIntervalSpy).toHaveBeenCalledWith(expect.any(Function), 1000);
65
+ setIntervalSpy.mockRestore();
66
+ });
67
+
68
+ test('should cleanup expired entries automatically', () => {
69
+ const consoleSpy = jest.spyOn(console, 'log').mockImplementation();
70
+
71
+ cache.set('key1', 'value1', 500);
72
+ cache.set('key2', 'value2', 1500);
73
+
74
+ // Fast forward to trigger first cleanup
75
+ jest.advanceTimersByTime(500);
76
+
77
+ // key1 should be cleaned up, key2 should remain
78
+ expect(cache.get('key1')).toBeUndefined();
79
+ expect(cache.get('key2')).toBe('value2');
80
+
81
+ expect(consoleSpy).toHaveBeenCalledWith('TTLCache: Cleaned up 1 expired entries');
82
+
83
+ consoleSpy.mockRestore();
84
+ });
85
+
86
+ test('should not log when no entries are cleaned up', () => {
87
+ const consoleSpy = jest.spyOn(console, 'log').mockImplementation();
88
+
89
+ cache.set('key1', 'value1', 2000);
90
+
91
+ // Fast forward, but no entries should be expired yet
92
+ jest.advanceTimersByTime(500);
93
+
94
+ expect(consoleSpy).not.toHaveBeenCalled();
95
+
96
+ consoleSpy.mockRestore();
97
+ });
98
+
99
+ test('should handle multiple cleanup cycles', () => {
100
+ const consoleSpy = jest.spyOn(console, 'log').mockImplementation();
101
+
102
+ cache.set('key1', 'value1', 300);
103
+ cache.set('key2', 'value2', 800);
104
+ cache.set('key3', 'value3', 1300);
105
+
106
+ // First cleanup - key1 expires
107
+ jest.advanceTimersByTime(500);
108
+ expect(cache.get('key1')).toBeUndefined();
109
+ expect(cache.get('key2')).toBe('value2');
110
+ expect(cache.get('key3')).toBe('value3');
111
+
112
+ // Second cleanup - key2 expires
113
+ jest.advanceTimersByTime(500);
114
+ expect(cache.get('key2')).toBeUndefined();
115
+ expect(cache.get('key3')).toBe('value3');
116
+
117
+ // Third cleanup - key3 expires
118
+ jest.advanceTimersByTime(500);
119
+ expect(cache.get('key3')).toBeUndefined();
120
+
121
+ expect(consoleSpy).toHaveBeenCalledTimes(3);
122
+
123
+ consoleSpy.mockRestore();
124
+ });
125
+ });
126
+
127
+ describe('Destroy Method', () => {
128
+ test('should clear cleanup interval on destroy', () => {
129
+ const clearIntervalSpy = jest.spyOn(global, 'clearInterval');
130
+
131
+ cache.destroy();
132
+
133
+ expect(clearIntervalSpy).toHaveBeenCalled();
134
+ clearIntervalSpy.mockRestore();
135
+ });
136
+
137
+ test('should clear all data on destroy', () => {
138
+ cache.set('key1', 'value1');
139
+ cache.set('key2', 'value2');
140
+
141
+ expect(cache.getStats().size).toBe(2);
142
+
143
+ cache.destroy();
144
+
145
+ expect(cache.getStats().size).toBe(0);
146
+ });
147
+
148
+ test('should handle multiple destroy calls', () => {
149
+ const clearIntervalSpy = jest.spyOn(global, 'clearInterval');
150
+
151
+ cache.destroy();
152
+ cache.destroy();
153
+ cache.destroy();
154
+
155
+ // Should only call clearInterval once
156
+ expect(clearIntervalSpy).toHaveBeenCalledTimes(1);
157
+
158
+ clearIntervalSpy.mockRestore();
159
+ });
160
+
161
+ test('should not cleanup after destroy', () => {
162
+ const consoleSpy = jest.spyOn(console, 'log').mockImplementation();
163
+
164
+ cache.set('key1', 'value1', 300);
165
+
166
+ cache.destroy();
167
+
168
+ // Advance time - should not trigger cleanup
169
+ jest.advanceTimersByTime(1000);
170
+
171
+ expect(consoleSpy).not.toHaveBeenCalled();
172
+
173
+ consoleSpy.mockRestore();
174
+ });
175
+ });
176
+
177
+ describe('Destroyed State Handling', () => {
178
+ beforeEach(() => {
179
+ cache.destroy();
180
+ });
181
+
182
+ test('should warn on set after destroy', () => {
183
+ const consoleSpy = jest.spyOn(console, 'warn').mockImplementation();
184
+
185
+ cache.set('key1', 'value1');
186
+
187
+ expect(consoleSpy).toHaveBeenCalledWith('TTLCache: Attempted to set value on destroyed cache');
188
+ expect(cache.get('key1')).toBeUndefined();
189
+
190
+ consoleSpy.mockRestore();
191
+ });
192
+
193
+ test('should warn on get after destroy', () => {
194
+ const consoleSpy = jest.spyOn(console, 'warn').mockImplementation();
195
+
196
+ const result = cache.get('key1');
197
+
198
+ expect(consoleSpy).toHaveBeenCalledWith('TTLCache: Attempted to get value from destroyed cache');
199
+ expect(result).toBeUndefined();
200
+
201
+ consoleSpy.mockRestore();
202
+ });
203
+
204
+ test('should return false for has after destroy', () => {
205
+ expect(cache.has('key1')).toBe(false);
206
+ });
207
+
208
+ test('should return false for delete after destroy', () => {
209
+ expect(cache.delete('key1')).toBe(false);
210
+ });
211
+
212
+ test('should not throw on clear after destroy', () => {
213
+ expect(() => cache.clear()).not.toThrow();
214
+ });
215
+ });
216
+
217
+ describe('Configuration', () => {
218
+ test('should use custom cleanup interval', () => {
219
+ const setIntervalSpy = jest.spyOn(global, 'setInterval');
220
+
221
+ new TTLCache<string>({ cleanupIntervalMs: 2000 });
222
+
223
+ expect(setIntervalSpy).toHaveBeenCalledWith(expect.any(Function), 2000);
224
+ setIntervalSpy.mockRestore();
225
+ });
226
+
227
+ test('should use default cleanup interval when not specified', () => {
228
+ const setIntervalSpy = jest.spyOn(global, 'setInterval');
229
+
230
+ new TTLCache<string>();
231
+
232
+ expect(setIntervalSpy).toHaveBeenCalledWith(expect.any(Function), 60000);
233
+ setIntervalSpy.mockRestore();
234
+ });
235
+
236
+ test('should pass configuration to parent Cache', () => {
237
+ const config: CacheConfig = {
238
+ maxSize: 10,
239
+ defaultTTL: 5000,
240
+ };
241
+
242
+ const customCache = new TTLCache<string>(config);
243
+
244
+ customCache.set('key1', 'value1');
245
+ expect(customCache.get('key1')).toBe('value1');
246
+
247
+ // Should use custom default TTL
248
+ jest.advanceTimersByTime(5001);
249
+ expect(customCache.get('key1')).toBeUndefined();
250
+
251
+ customCache.destroy();
252
+ });
253
+ });
254
+
255
+ describe('Memory Management', () => {
256
+ test('should not memory leak with many entries', () => {
257
+ const entryCount = 1000;
258
+
259
+ // Create many entries with short TTL
260
+ for (let i = 0; i < entryCount; i++) {
261
+ cache.set(`key${i}`, `value${i}`, 100);
262
+ }
263
+
264
+ expect(cache.getStats().size).toBe(entryCount);
265
+
266
+ // Let all entries expire and cleanup
267
+ jest.advanceTimersByTime(500);
268
+
269
+ expect(cache.getStats().size).toBe(0);
270
+ });
271
+
272
+ test('should handle rapid create/destroy cycles', () => {
273
+ for (let i = 0; i < 10; i++) {
274
+ const tempCache = new TTLCache<string>({ cleanupIntervalMs: 100 });
275
+ tempCache.set('key', 'value');
276
+ tempCache.destroy();
277
+ }
278
+
279
+ // Should not throw or cause issues
280
+ expect(true).toBe(true);
281
+ });
282
+ });
283
+
284
+ describe('Edge Cases', () => {
285
+ test('should handle zero cleanup interval', () => {
286
+ expect(() => {
287
+ new TTLCache<string>({ cleanupIntervalMs: 0 });
288
+ }).not.toThrow();
289
+ });
290
+
291
+ test('should handle negative cleanup interval', () => {
292
+ expect(() => {
293
+ new TTLCache<string>({ cleanupIntervalMs: -100 });
294
+ }).not.toThrow();
295
+ });
296
+
297
+ test('should handle very large cleanup interval', () => {
298
+ expect(() => {
299
+ new TTLCache<string>({ cleanupIntervalMs: Number.MAX_SAFE_INTEGER });
300
+ }).not.toThrow();
301
+ });
302
+ });
303
+ });