@umituz/react-native-design-system 2.8.7 → 2.8.8

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 (128) hide show
  1. package/package.json +5 -6
  2. package/src/device/infrastructure/repositories/LegacyDeviceIdRepository.ts +1 -1
  3. package/src/device/infrastructure/services/DeviceFeatureService.ts +1 -1
  4. package/src/exception/infrastructure/services/ExceptionLogger.ts +1 -1
  5. package/src/exception/infrastructure/storage/ExceptionStore.ts +1 -1
  6. package/src/exports/filesystem.ts +1 -0
  7. package/src/exports/storage.ts +1 -0
  8. package/src/filesystem/domain/constants/FileConstants.ts +20 -0
  9. package/src/filesystem/domain/entities/File.ts +20 -0
  10. package/src/filesystem/domain/types/FileTypes.ts +43 -0
  11. package/src/filesystem/domain/utils/FileUtils.ts +86 -0
  12. package/src/filesystem/index.ts +23 -0
  13. package/src/filesystem/infrastructure/services/FileSystemService.ts +45 -0
  14. package/src/filesystem/infrastructure/services/cache.service.ts +48 -0
  15. package/src/filesystem/infrastructure/services/directory.service.ts +66 -0
  16. package/src/filesystem/infrastructure/services/download.constants.ts +6 -0
  17. package/src/filesystem/infrastructure/services/download.service.ts +74 -0
  18. package/src/filesystem/infrastructure/services/download.types.ts +7 -0
  19. package/src/filesystem/infrastructure/services/encoding.service.ts +25 -0
  20. package/src/filesystem/infrastructure/services/file-info.service.ts +52 -0
  21. package/src/filesystem/infrastructure/services/file-manager.service.ts +81 -0
  22. package/src/filesystem/infrastructure/services/file-path.service.ts +22 -0
  23. package/src/filesystem/infrastructure/services/file-reader.service.ts +52 -0
  24. package/src/filesystem/infrastructure/services/file-writer.service.ts +32 -0
  25. package/src/filesystem/infrastructure/utils/blob.utils.ts +20 -0
  26. package/src/image/infrastructure/services/ImageStorageService.ts +1 -1
  27. package/src/index.ts +9 -0
  28. package/src/molecules/alerts/AlertStore.ts +1 -1
  29. package/src/molecules/calendar/infrastructure/storage/EventActions.ts +1 -1
  30. package/src/molecules/calendar/infrastructure/stores/storageAdapter.ts +1 -1
  31. package/src/offline/infrastructure/storage/OfflineStore.ts +1 -1
  32. package/src/onboarding/infrastructure/storage/OnboardingStore.ts +2 -2
  33. package/src/onboarding/infrastructure/storage/__tests__/OnboardingStore.test.ts +1 -1
  34. package/src/onboarding/infrastructure/storage/actions/answerActions.ts +1 -1
  35. package/src/onboarding/infrastructure/storage/actions/storageHelpers.ts +1 -1
  36. package/src/storage/README.md +185 -0
  37. package/src/storage/__tests__/integration.test.ts +391 -0
  38. package/src/storage/__tests__/mocks/asyncStorage.mock.ts +52 -0
  39. package/src/storage/__tests__/performance.test.tsx +352 -0
  40. package/src/storage/__tests__/setup.ts +63 -0
  41. package/src/storage/application/README.md +158 -0
  42. package/src/storage/application/ports/IStorageRepository.ts +61 -0
  43. package/src/storage/application/ports/README.md +127 -0
  44. package/src/storage/cache/README.md +154 -0
  45. package/src/storage/cache/__tests__/PerformanceAndMemory.test.ts +387 -0
  46. package/src/storage/cache/__tests__/setup.ts +19 -0
  47. package/src/storage/cache/domain/Cache.ts +146 -0
  48. package/src/storage/cache/domain/CacheManager.md +83 -0
  49. package/src/storage/cache/domain/CacheManager.ts +48 -0
  50. package/src/storage/cache/domain/CacheStatsTracker.md +169 -0
  51. package/src/storage/cache/domain/CacheStatsTracker.ts +49 -0
  52. package/src/storage/cache/domain/CachedValue.md +97 -0
  53. package/src/storage/cache/domain/ErrorHandler.md +99 -0
  54. package/src/storage/cache/domain/ErrorHandler.ts +42 -0
  55. package/src/storage/cache/domain/PatternMatcher.md +122 -0
  56. package/src/storage/cache/domain/PatternMatcher.ts +30 -0
  57. package/src/storage/cache/domain/README.md +118 -0
  58. package/src/storage/cache/domain/__tests__/Cache.test.ts +293 -0
  59. package/src/storage/cache/domain/__tests__/CacheManager.test.ts +276 -0
  60. package/src/storage/cache/domain/__tests__/ErrorHandler.test.ts +303 -0
  61. package/src/storage/cache/domain/__tests__/PatternMatcher.test.ts +261 -0
  62. package/src/storage/cache/domain/strategies/EvictionStrategy.ts +9 -0
  63. package/src/storage/cache/domain/strategies/FIFOStrategy.ts +12 -0
  64. package/src/storage/cache/domain/strategies/LFUStrategy.ts +22 -0
  65. package/src/storage/cache/domain/strategies/LRUStrategy.ts +22 -0
  66. package/src/storage/cache/domain/strategies/README.md +117 -0
  67. package/src/storage/cache/domain/strategies/TTLStrategy.ts +23 -0
  68. package/src/storage/cache/domain/strategies/__tests__/EvictionStrategies.test.ts +293 -0
  69. package/src/storage/cache/domain/types/Cache.ts +28 -0
  70. package/src/storage/cache/domain/types/README.md +107 -0
  71. package/src/storage/cache/index.ts +28 -0
  72. package/src/storage/cache/infrastructure/README.md +126 -0
  73. package/src/storage/cache/infrastructure/TTLCache.ts +103 -0
  74. package/src/storage/cache/infrastructure/__tests__/TTLCache.test.ts +303 -0
  75. package/src/storage/cache/presentation/README.md +123 -0
  76. package/src/storage/cache/presentation/__tests__/ReactHooks.test.ts +514 -0
  77. package/src/storage/cache/presentation/useCache.ts +76 -0
  78. package/src/storage/cache/presentation/useCachedValue.ts +88 -0
  79. package/src/storage/cache/types.d.ts +3 -0
  80. package/src/storage/domain/README.md +128 -0
  81. package/src/storage/domain/constants/CacheDefaults.ts +64 -0
  82. package/src/storage/domain/constants/README.md +105 -0
  83. package/src/storage/domain/entities/CachedValue.ts +86 -0
  84. package/src/storage/domain/entities/README.md +109 -0
  85. package/src/storage/domain/entities/StorageResult.ts +75 -0
  86. package/src/storage/domain/entities/__tests__/CachedValue.test.ts +149 -0
  87. package/src/storage/domain/entities/__tests__/StorageResult.test.ts +122 -0
  88. package/src/storage/domain/errors/README.md +126 -0
  89. package/src/storage/domain/errors/StorageError.ts +81 -0
  90. package/src/storage/domain/errors/__tests__/StorageError.test.ts +127 -0
  91. package/src/storage/domain/factories/README.md +138 -0
  92. package/src/storage/domain/factories/StoreFactory.ts +59 -0
  93. package/src/storage/domain/types/README.md +522 -0
  94. package/src/storage/domain/types/Store.ts +44 -0
  95. package/src/storage/domain/utils/CacheKeyGenerator.ts +66 -0
  96. package/src/storage/domain/utils/README.md +127 -0
  97. package/src/storage/domain/utils/__tests__/devUtils.test.ts +97 -0
  98. package/src/storage/domain/utils/devUtils.ts +37 -0
  99. package/src/storage/domain/value-objects/README.md +120 -0
  100. package/src/storage/domain/value-objects/StorageKey.ts +60 -0
  101. package/src/storage/index.ts +175 -0
  102. package/src/storage/infrastructure/README.md +165 -0
  103. package/src/storage/infrastructure/adapters/README.md +175 -0
  104. package/src/storage/infrastructure/adapters/StorageService.md +103 -0
  105. package/src/storage/infrastructure/adapters/StorageService.ts +49 -0
  106. package/src/storage/infrastructure/repositories/AsyncStorageRepository.ts +98 -0
  107. package/src/storage/infrastructure/repositories/BaseStorageOperations.ts +100 -0
  108. package/src/storage/infrastructure/repositories/BatchStorageOperations.ts +42 -0
  109. package/src/storage/infrastructure/repositories/README.md +121 -0
  110. package/src/storage/infrastructure/repositories/StringStorageOperations.ts +44 -0
  111. package/src/storage/infrastructure/repositories/__tests__/AsyncStorageRepository.test.ts +170 -0
  112. package/src/storage/infrastructure/repositories/__tests__/BaseStorageOperations.test.ts +201 -0
  113. package/src/storage/presentation/README.md +181 -0
  114. package/src/storage/presentation/hooks/CacheStorageOperations.ts +94 -0
  115. package/src/storage/presentation/hooks/README.md +128 -0
  116. package/src/storage/presentation/hooks/__tests__/usePersistentCache.test.ts +405 -0
  117. package/src/storage/presentation/hooks/__tests__/useStorage.test.ts +247 -0
  118. package/src/storage/presentation/hooks/__tests__/useStorageState.test.ts +293 -0
  119. package/src/storage/presentation/hooks/useCacheState.ts +53 -0
  120. package/src/storage/presentation/hooks/usePersistentCache.ts +154 -0
  121. package/src/storage/presentation/hooks/useStorage.ts +102 -0
  122. package/src/storage/presentation/hooks/useStorageState.ts +71 -0
  123. package/src/storage/presentation/hooks/useStore.ts +15 -0
  124. package/src/storage/types/README.md +103 -0
  125. package/src/theme/infrastructure/globalThemeStore.ts +1 -1
  126. package/src/theme/infrastructure/storage/ThemeStorage.ts +1 -1
  127. package/src/theme/infrastructure/stores/themeStore.ts +1 -1
  128. package/src/utilities/sharing/infrastructure/services/SharingService.ts +1 -1
@@ -0,0 +1,276 @@
1
+ /**
2
+ * CacheManager Tests
3
+ */
4
+
5
+ import { CacheManager, cacheManager } from '../CacheManager';
6
+ import type { CacheConfig } from '../types/Cache';
7
+
8
+ describe('CacheManager', () => {
9
+ let manager: CacheManager;
10
+
11
+ beforeEach(() => {
12
+ // Reset singleton for testing
13
+ (CacheManager as any).instance = null;
14
+ manager = CacheManager.getInstance();
15
+ });
16
+
17
+ afterEach(() => {
18
+ manager.clearAll();
19
+ (CacheManager as any).instance = null;
20
+ });
21
+
22
+ describe('Singleton Pattern', () => {
23
+ test('should return the same instance', () => {
24
+ const instance1 = CacheManager.getInstance();
25
+ const instance2 = CacheManager.getInstance();
26
+
27
+ expect(instance1).toBe(instance2);
28
+ });
29
+
30
+ test('should create only one instance', () => {
31
+ const instance1 = CacheManager.getInstance();
32
+ const instance2 = new (CacheManager as any)();
33
+
34
+ expect(instance1).not.toBe(instance2);
35
+ expect(instance1).toBe(CacheManager.getInstance());
36
+ });
37
+ });
38
+
39
+ describe('Cache Management', () => {
40
+ test('should create and retrieve cache instances', () => {
41
+ const cache1 = manager.getCache<string>('cache1');
42
+ const cache2 = manager.getCache<number>('cache2');
43
+
44
+ expect(cache1).toBeDefined();
45
+ expect(cache2).toBeDefined();
46
+ expect(cache1).not.toBe(cache2);
47
+ });
48
+
49
+ test('should return same cache instance for same name', () => {
50
+ const cache1 = manager.getCache<string>('cache1');
51
+ const cache2 = manager.getCache<string>('cache1');
52
+
53
+ expect(cache1).toBe(cache2);
54
+ });
55
+
56
+ test('should create caches with different types', () => {
57
+ const stringCache = manager.getCache<string>('strings');
58
+ const numberCache = manager.getCache<number>('numbers');
59
+ const objectCache = manager.getCache<{ id: string }>('objects');
60
+
61
+ stringCache.set('key', 'value');
62
+ numberCache.set('key', 42);
63
+ objectCache.set('key', { id: 'test' });
64
+
65
+ expect(stringCache.get('key')).toBe('value');
66
+ expect(numberCache.get('key')).toBe(42);
67
+ expect(objectCache.get('key')).toEqual({ id: 'test' });
68
+ });
69
+
70
+ test('should apply configuration to cache', () => {
71
+ const config: CacheConfig = {
72
+ maxSize: 50,
73
+ defaultTTL: 10000,
74
+ };
75
+
76
+ const cache = manager.getCache<string>('configured-cache', config);
77
+
78
+ cache.set('key', 'value');
79
+ expect(cache.get('key')).toBe('value');
80
+
81
+ // Test configuration is applied (basic check)
82
+ const stats = cache.getStats();
83
+ expect(stats.size).toBe(1);
84
+ });
85
+ });
86
+
87
+ describe('Cache Deletion', () => {
88
+ test('should delete specific cache', () => {
89
+ manager.getCache<string>('cache1');
90
+ manager.getCache<string>('cache2');
91
+
92
+ expect(manager.getCacheNames()).toContain('cache1');
93
+ expect(manager.getCacheNames()).toContain('cache2');
94
+
95
+ const deleted = manager.deleteCache('cache1');
96
+
97
+ expect(deleted).toBe(true);
98
+ expect(manager.getCacheNames()).not.toContain('cache1');
99
+ expect(manager.getCacheNames()).toContain('cache2');
100
+ });
101
+
102
+ test('should return false when deleting non-existent cache', () => {
103
+ const deleted = manager.deleteCache('nonexistent');
104
+
105
+ expect(deleted).toBe(false);
106
+ });
107
+
108
+ test('should clear cache before deletion', () => {
109
+ const cache = manager.getCache<string>('cache1');
110
+ cache.set('key1', 'value1');
111
+ cache.set('key2', 'value2');
112
+
113
+ expect(cache.getStats().size).toBe(2);
114
+
115
+ manager.deleteCache('cache1');
116
+
117
+ // Get new instance to verify old one was cleared
118
+ const newCache = manager.getCache<string>('cache1');
119
+ expect(newCache.getStats().size).toBe(0);
120
+ });
121
+ });
122
+
123
+ describe('Clear All', () => {
124
+ test('should clear all caches', () => {
125
+ const cache1 = manager.getCache<string>('cache1');
126
+ const cache2 = manager.getCache<string>('cache2');
127
+ const cache3 = manager.getCache<string>('cache3');
128
+
129
+ cache1.set('key1', 'value1');
130
+ cache2.set('key2', 'value2');
131
+ cache3.set('key3', 'value3');
132
+
133
+ expect(cache1.getStats().size).toBe(1);
134
+ expect(cache2.getStats().size).toBe(1);
135
+ expect(cache3.getStats().size).toBe(1);
136
+ expect(manager.getCacheNames()).toHaveLength(3);
137
+
138
+ manager.clearAll();
139
+
140
+ expect(cache1.getStats().size).toBe(0);
141
+ expect(cache2.getStats().size).toBe(0);
142
+ expect(cache3.getStats().size).toBe(0);
143
+ expect(manager.getCacheNames()).toHaveLength(0);
144
+ });
145
+
146
+ test('should handle clear all when no caches exist', () => {
147
+ expect(() => manager.clearAll()).not.toThrow();
148
+ expect(manager.getCacheNames()).toHaveLength(0);
149
+ });
150
+ });
151
+
152
+ describe('Cache Names', () => {
153
+ test('should return list of cache names', () => {
154
+ expect(manager.getCacheNames()).toHaveLength(0);
155
+
156
+ manager.getCache<string>('cache1');
157
+ manager.getCache<string>('cache2');
158
+ manager.getCache<string>('cache3');
159
+
160
+ const names = manager.getCacheNames();
161
+ expect(names).toHaveLength(3);
162
+ expect(names).toContain('cache1');
163
+ expect(names).toContain('cache2');
164
+ expect(names).toContain('cache3');
165
+ });
166
+
167
+ test('should not duplicate cache names', () => {
168
+ manager.getCache<string>('cache1');
169
+ manager.getCache<string>('cache1'); // Same name
170
+ manager.getCache<string>('cache2');
171
+
172
+ const names = manager.getCacheNames();
173
+ expect(names).toHaveLength(2);
174
+ expect(names.filter(name => name === 'cache1')).toHaveLength(1);
175
+ });
176
+ });
177
+
178
+ describe('Memory Management', () => {
179
+ test('should handle large number of caches', () => {
180
+ const cacheCount = 100;
181
+
182
+ for (let i = 0; i < cacheCount; i++) {
183
+ const cache = manager.getCache<string>(`cache${i}`);
184
+ cache.set(`key${i}`, `value${i}`);
185
+ }
186
+
187
+ expect(manager.getCacheNames()).toHaveLength(cacheCount);
188
+
189
+ // Verify all caches have their data
190
+ for (let i = 0; i < cacheCount; i++) {
191
+ const cache = manager.getCache<string>(`cache${i}`);
192
+ expect(cache.get(`key${i}`)).toBe(`value${i}`);
193
+ }
194
+ });
195
+
196
+ test('should handle cache deletion in large scale', () => {
197
+ const cacheCount = 50;
198
+
199
+ // Create caches
200
+ for (let i = 0; i < cacheCount; i++) {
201
+ manager.getCache<string>(`cache${i}`);
202
+ }
203
+
204
+ expect(manager.getCacheNames()).toHaveLength(cacheCount);
205
+
206
+ // Delete every other cache
207
+ for (let i = 0; i < cacheCount; i += 2) {
208
+ manager.deleteCache(`cache${i}`);
209
+ }
210
+
211
+ expect(manager.getCacheNames()).toHaveLength(Math.floor(cacheCount / 2));
212
+
213
+ // Verify remaining caches still work
214
+ for (let i = 1; i < cacheCount; i += 2) {
215
+ const cache = manager.getCache<string>(`cache${i}`);
216
+ cache.set('test', 'value');
217
+ expect(cache.get('test')).toBe('value');
218
+ }
219
+ });
220
+ });
221
+
222
+ describe('Edge Cases', () => {
223
+ test('should handle empty string cache name', () => {
224
+ const cache = manager.getCache<string>('');
225
+ expect(cache).toBeDefined();
226
+ expect(manager.getCacheNames()).toContain('');
227
+ });
228
+
229
+ test('should handle special characters in cache names', () => {
230
+ const specialNames = ['cache-with-dash', 'cache_with_underscore', 'cache.with.dots', 'cache with spaces'];
231
+
232
+ specialNames.forEach(name => {
233
+ const cache = manager.getCache<string>(name);
234
+ expect(cache).toBeDefined();
235
+ expect(manager.getCacheNames()).toContain(name);
236
+ });
237
+ });
238
+
239
+ test('should handle very long cache names', () => {
240
+ const longName = 'a'.repeat(1000);
241
+ const cache = manager.getCache<string>(longName);
242
+
243
+ expect(cache).toBeDefined();
244
+ expect(manager.getCacheNames()).toContain(longName);
245
+ });
246
+ });
247
+ });
248
+
249
+ describe('cacheManager export', () => {
250
+ beforeEach(() => {
251
+ (CacheManager as any).instance = null;
252
+ });
253
+
254
+ afterEach(() => {
255
+ cacheManager.clearAll();
256
+ (CacheManager as any).instance = null;
257
+ });
258
+
259
+ test('should export singleton instance', () => {
260
+ expect(cacheManager).toBeDefined();
261
+ expect(cacheManager).toBeInstanceOf(CacheManager);
262
+ });
263
+
264
+ test('should be the same instance as CacheManager.getInstance()', () => {
265
+ const instance = CacheManager.getInstance();
266
+ expect(cacheManager).toBe(instance);
267
+ });
268
+
269
+ test('should work with exported instance', () => {
270
+ const cache = cacheManager.getCache<string>('test-cache');
271
+ cache.set('key', 'value');
272
+
273
+ expect(cache.get('key')).toBe('value');
274
+ expect(cacheManager.getCacheNames()).toContain('test-cache');
275
+ });
276
+ });
@@ -0,0 +1,303 @@
1
+ /**
2
+ * ErrorHandler Tests
3
+ */
4
+
5
+ import { ErrorHandler, CacheError } from '../ErrorHandler';
6
+
7
+ describe('ErrorHandler', () => {
8
+ describe('CacheError', () => {
9
+ test('should create CacheError with message and code', () => {
10
+ const error = new CacheError('Test message', 'TEST_CODE');
11
+
12
+ expect(error).toBeInstanceOf(Error);
13
+ expect(error).toBeInstanceOf(CacheError);
14
+ expect(error.message).toBe('Test message');
15
+ expect(error.code).toBe('TEST_CODE');
16
+ expect(error.name).toBe('CacheError');
17
+ });
18
+
19
+ test('should have stack trace', () => {
20
+ const error = new CacheError('Test message', 'TEST_CODE');
21
+
22
+ expect(error.stack).toBeDefined();
23
+ expect(typeof error.stack).toBe('string');
24
+ });
25
+
26
+ test('should be serializable', () => {
27
+ const error = new CacheError('Test message', 'TEST_CODE');
28
+ const serialized = JSON.stringify(error);
29
+ const parsed = JSON.parse(serialized);
30
+
31
+ expect(parsed.message).toBe('Test message');
32
+ expect(parsed.code).toBe('TEST_CODE');
33
+ expect(parsed.name).toBe('CacheError');
34
+ });
35
+ });
36
+
37
+ describe('handle', () => {
38
+ test('should throw CacheError as-is', () => {
39
+ const originalError = new CacheError('Original error', 'ORIGINAL');
40
+
41
+ expect(() => {
42
+ ErrorHandler.handle(originalError, 'test context');
43
+ }).toThrow('Original error');
44
+ });
45
+
46
+ test('should wrap regular Error in CacheError', () => {
47
+ const originalError = new Error('Regular error');
48
+
49
+ expect(() => {
50
+ ErrorHandler.handle(originalError, 'test context');
51
+ }).toThrow(CacheError);
52
+ });
53
+
54
+ test('should include context in wrapped error message', () => {
55
+ const originalError = new Error('Regular error');
56
+
57
+ try {
58
+ ErrorHandler.handle(originalError, 'test context');
59
+ } catch (error) {
60
+ expect((error as CacheError).message).toBe('test context: Regular error');
61
+ expect((error as CacheError).code).toBe('CACHE_ERROR');
62
+ }
63
+ });
64
+
65
+ test('should handle unknown error type', () => {
66
+ const unknownError = 'string error';
67
+
68
+ try {
69
+ ErrorHandler.handle(unknownError, 'test context');
70
+ } catch (error) {
71
+ expect((error as CacheError).message).toBe('test context: Unknown error');
72
+ expect((error as CacheError).code).toBe('UNKNOWN_ERROR');
73
+ }
74
+ });
75
+
76
+ test('should handle null error', () => {
77
+ try {
78
+ ErrorHandler.handle(null, 'test context');
79
+ } catch (error) {
80
+ expect((error as CacheError).message).toBe('test context: Unknown error');
81
+ expect((error as CacheError).code).toBe('UNKNOWN_ERROR');
82
+ }
83
+ });
84
+
85
+ test('should handle undefined error', () => {
86
+ try {
87
+ ErrorHandler.handle(undefined, 'test context');
88
+ } catch (error) {
89
+ expect((error as CacheError).message).toBe('test context: Unknown error');
90
+ expect((error as CacheError).code).toBe('UNKNOWN_ERROR');
91
+ }
92
+ });
93
+
94
+ test('should preserve original error properties when possible', () => {
95
+ const originalError = new Error('Original error');
96
+ (originalError as any).customProperty = 'custom value';
97
+
98
+ try {
99
+ ErrorHandler.handle(originalError, 'test context');
100
+ } catch (error) {
101
+ const cacheError = error as CacheError;
102
+ expect(cacheError.message).toBe('test context: Original error');
103
+ expect(cacheError.code).toBe('CACHE_ERROR');
104
+ // Note: original properties are not copied to maintain clean error structure
105
+ }
106
+ });
107
+ });
108
+
109
+ describe('withTimeout', () => {
110
+ beforeEach(() => {
111
+ jest.useFakeTimers();
112
+ });
113
+
114
+ afterEach(() => {
115
+ jest.useRealTimers();
116
+ });
117
+
118
+ test('should resolve promise before timeout', async () => {
119
+ const promise = Promise.resolve('success');
120
+ const result = await ErrorHandler.withTimeout(promise, 1000, 'test context');
121
+
122
+ expect(result).toBe('success');
123
+ });
124
+
125
+ test('should timeout when promise takes too long', async () => {
126
+ const promise = new Promise(resolve => setTimeout(() => resolve('late'), 2000));
127
+
128
+ await expect(
129
+ ErrorHandler.withTimeout(promise, 1000, 'test context')
130
+ ).rejects.toThrow(CacheError);
131
+ });
132
+
133
+ test('should include timeout in error message', async () => {
134
+ const promise = new Promise(resolve => setTimeout(() => resolve('late'), 2000));
135
+
136
+ try {
137
+ await ErrorHandler.withTimeout(promise, 1000, 'test context');
138
+ } catch (error) {
139
+ expect((error as CacheError).message).toBe('test context: Operation timed out after 1000ms');
140
+ expect((error as CacheError).code).toBe('TIMEOUT');
141
+ }
142
+ });
143
+
144
+ test('should handle promise rejection before timeout', async () => {
145
+ const promise = Promise.reject(new Error('Promise rejected'));
146
+
147
+ await expect(
148
+ ErrorHandler.withTimeout(promise, 1000, 'test context')
149
+ ).rejects.toThrow(CacheError);
150
+ });
151
+
152
+ test('should include context in promise rejection error', async () => {
153
+ const promise = Promise.reject(new Error('Promise rejected'));
154
+
155
+ try {
156
+ await ErrorHandler.withTimeout(promise, 1000, 'test context');
157
+ } catch (error) {
158
+ expect((error as CacheError).message).toBe('test context: Promise rejected');
159
+ expect((error as CacheError).code).toBe('CACHE_ERROR');
160
+ }
161
+ });
162
+
163
+ test('should handle zero timeout', async () => {
164
+ const promise = new Promise(resolve => setTimeout(() => resolve('success'), 100));
165
+
166
+ await expect(
167
+ ErrorHandler.withTimeout(promise, 0, 'test context')
168
+ ).rejects.toThrow(CacheError);
169
+ });
170
+
171
+ test('should handle negative timeout', async () => {
172
+ const promise = Promise.resolve('success');
173
+
174
+ // Negative timeout should trigger immediate timeout
175
+ await expect(
176
+ ErrorHandler.withTimeout(promise, -1000, 'test context')
177
+ ).rejects.toThrow(CacheError);
178
+ });
179
+
180
+ test('should cleanup timeout when promise resolves', async () => {
181
+ const clearTimeoutSpy = jest.spyOn(global, 'clearTimeout');
182
+ const promise = Promise.resolve('success');
183
+
184
+ await ErrorHandler.withTimeout(promise, 1000, 'test context');
185
+
186
+ // Should clear timeout after promise resolves
187
+ expect(clearTimeoutSpy).toHaveBeenCalled();
188
+
189
+ clearTimeoutSpy.mockRestore();
190
+ });
191
+
192
+ test('should handle multiple concurrent timeouts', async () => {
193
+ const promise1 = Promise.resolve('success1');
194
+ const promise2 = Promise.resolve('success2');
195
+ const promise3 = new Promise(resolve => setTimeout(() => resolve('success3'), 2000));
196
+
197
+ const results = await Promise.allSettled([
198
+ ErrorHandler.withTimeout(promise1, 1000, 'context1'),
199
+ ErrorHandler.withTimeout(promise2, 1000, 'context2'),
200
+ ErrorHandler.withTimeout(promise3, 1000, 'context3'),
201
+ ]);
202
+
203
+ expect(results[0].status).toBe('fulfilled');
204
+ expect(results[1].status).toBe('fulfilled');
205
+ expect(results[2].status).toBe('rejected');
206
+ });
207
+ });
208
+
209
+ describe('Integration with Cache Operations', () => {
210
+ test('should handle cache operation errors', () => {
211
+ const mockOperation = () => {
212
+ throw new Error('Cache operation failed');
213
+ };
214
+
215
+ expect(() => {
216
+ ErrorHandler.handle(mockOperation(), 'cache.set');
217
+ }).toThrow('cache.set: Cache operation failed');
218
+ });
219
+
220
+ test('should handle async cache operation errors', async () => {
221
+ const mockAsyncOperation = async () => {
222
+ throw new Error('Async cache operation failed');
223
+ };
224
+
225
+ await expect(
226
+ ErrorHandler.withTimeout(mockAsyncOperation(), 1000, 'cache.get')
227
+ ).rejects.toThrow('cache.get: Async cache operation failed');
228
+ });
229
+
230
+ test('should handle network timeout scenarios', async () => {
231
+ const mockNetworkCall = new Promise(resolve =>
232
+ setTimeout(() => resolve({ data: 'response' }), 5000)
233
+ );
234
+
235
+ await expect(
236
+ ErrorHandler.withTimeout(mockNetworkCall, 1000, 'api.fetch')
237
+ ).rejects.toThrow('api.fetch: Operation timed out after 1000ms');
238
+ });
239
+ });
240
+
241
+ describe('Error Code Constants', () => {
242
+ test('should use consistent error codes', () => {
243
+ const scenarios = [
244
+ { error: new Error('test'), expectedCode: 'CACHE_ERROR' },
245
+ { error: 'string', expectedCode: 'UNKNOWN_ERROR' },
246
+ { error: null, expectedCode: 'UNKNOWN_ERROR' },
247
+ { error: undefined, expectedCode: 'UNKNOWN_ERROR' },
248
+ ];
249
+
250
+ scenarios.forEach(({ error, expectedCode }) => {
251
+ try {
252
+ ErrorHandler.handle(error, 'test');
253
+ } catch (caught) {
254
+ expect((caught as CacheError).code).toBe(expectedCode);
255
+ }
256
+ });
257
+ });
258
+
259
+ test('should use TIMEOUT code for timeout errors', async () => {
260
+ const promise = new Promise(resolve => setTimeout(() => resolve('late'), 2000));
261
+
262
+ try {
263
+ await ErrorHandler.withTimeout(promise, 1000, 'test');
264
+ } catch (error) {
265
+ expect((error as CacheError).code).toBe('TIMEOUT');
266
+ }
267
+ });
268
+ });
269
+
270
+ describe('Edge Cases', () => {
271
+ test('should handle empty context string', () => {
272
+ const error = new Error('test error');
273
+
274
+ try {
275
+ ErrorHandler.handle(error, '');
276
+ } catch (caught) {
277
+ expect((caught as CacheError).message).toBe(': test error');
278
+ }
279
+ });
280
+
281
+ test('should handle very long context string', () => {
282
+ const longContext = 'context'.repeat(1000);
283
+ const error = new Error('test error');
284
+
285
+ try {
286
+ ErrorHandler.handle(error, longContext);
287
+ } catch (caught) {
288
+ expect((caught as CacheError).message).toBe(`${longContext}: test error`);
289
+ }
290
+ });
291
+
292
+ test('should handle special characters in context', () => {
293
+ const context = 'context-with-special-chars-!@#$%^&*()';
294
+ const error = new Error('test error');
295
+
296
+ try {
297
+ ErrorHandler.handle(error, context);
298
+ } catch (caught) {
299
+ expect((caught as CacheError).message).toBe(`${context}: test error`);
300
+ }
301
+ });
302
+ });
303
+ });