@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.
- package/package.json +5 -6
- package/src/device/infrastructure/repositories/LegacyDeviceIdRepository.ts +1 -1
- package/src/device/infrastructure/services/DeviceFeatureService.ts +1 -1
- package/src/exception/infrastructure/services/ExceptionLogger.ts +1 -1
- package/src/exception/infrastructure/storage/ExceptionStore.ts +1 -1
- package/src/exports/filesystem.ts +1 -0
- package/src/exports/storage.ts +1 -0
- package/src/filesystem/domain/constants/FileConstants.ts +20 -0
- package/src/filesystem/domain/entities/File.ts +20 -0
- package/src/filesystem/domain/types/FileTypes.ts +43 -0
- package/src/filesystem/domain/utils/FileUtils.ts +86 -0
- package/src/filesystem/index.ts +23 -0
- package/src/filesystem/infrastructure/services/FileSystemService.ts +45 -0
- package/src/filesystem/infrastructure/services/cache.service.ts +48 -0
- package/src/filesystem/infrastructure/services/directory.service.ts +66 -0
- package/src/filesystem/infrastructure/services/download.constants.ts +6 -0
- package/src/filesystem/infrastructure/services/download.service.ts +74 -0
- package/src/filesystem/infrastructure/services/download.types.ts +7 -0
- package/src/filesystem/infrastructure/services/encoding.service.ts +25 -0
- package/src/filesystem/infrastructure/services/file-info.service.ts +52 -0
- package/src/filesystem/infrastructure/services/file-manager.service.ts +81 -0
- package/src/filesystem/infrastructure/services/file-path.service.ts +22 -0
- package/src/filesystem/infrastructure/services/file-reader.service.ts +52 -0
- package/src/filesystem/infrastructure/services/file-writer.service.ts +32 -0
- package/src/filesystem/infrastructure/utils/blob.utils.ts +20 -0
- package/src/image/infrastructure/services/ImageStorageService.ts +1 -1
- package/src/index.ts +9 -0
- package/src/molecules/alerts/AlertStore.ts +1 -1
- package/src/molecules/calendar/infrastructure/storage/EventActions.ts +1 -1
- package/src/molecules/calendar/infrastructure/stores/storageAdapter.ts +1 -1
- package/src/offline/infrastructure/storage/OfflineStore.ts +1 -1
- package/src/onboarding/infrastructure/storage/OnboardingStore.ts +2 -2
- package/src/onboarding/infrastructure/storage/__tests__/OnboardingStore.test.ts +1 -1
- package/src/onboarding/infrastructure/storage/actions/answerActions.ts +1 -1
- package/src/onboarding/infrastructure/storage/actions/storageHelpers.ts +1 -1
- package/src/storage/README.md +185 -0
- package/src/storage/__tests__/integration.test.ts +391 -0
- package/src/storage/__tests__/mocks/asyncStorage.mock.ts +52 -0
- package/src/storage/__tests__/performance.test.tsx +352 -0
- package/src/storage/__tests__/setup.ts +63 -0
- package/src/storage/application/README.md +158 -0
- package/src/storage/application/ports/IStorageRepository.ts +61 -0
- package/src/storage/application/ports/README.md +127 -0
- package/src/storage/cache/README.md +154 -0
- package/src/storage/cache/__tests__/PerformanceAndMemory.test.ts +387 -0
- package/src/storage/cache/__tests__/setup.ts +19 -0
- package/src/storage/cache/domain/Cache.ts +146 -0
- package/src/storage/cache/domain/CacheManager.md +83 -0
- package/src/storage/cache/domain/CacheManager.ts +48 -0
- package/src/storage/cache/domain/CacheStatsTracker.md +169 -0
- package/src/storage/cache/domain/CacheStatsTracker.ts +49 -0
- package/src/storage/cache/domain/CachedValue.md +97 -0
- package/src/storage/cache/domain/ErrorHandler.md +99 -0
- package/src/storage/cache/domain/ErrorHandler.ts +42 -0
- package/src/storage/cache/domain/PatternMatcher.md +122 -0
- package/src/storage/cache/domain/PatternMatcher.ts +30 -0
- package/src/storage/cache/domain/README.md +118 -0
- package/src/storage/cache/domain/__tests__/Cache.test.ts +293 -0
- package/src/storage/cache/domain/__tests__/CacheManager.test.ts +276 -0
- package/src/storage/cache/domain/__tests__/ErrorHandler.test.ts +303 -0
- package/src/storage/cache/domain/__tests__/PatternMatcher.test.ts +261 -0
- package/src/storage/cache/domain/strategies/EvictionStrategy.ts +9 -0
- package/src/storage/cache/domain/strategies/FIFOStrategy.ts +12 -0
- package/src/storage/cache/domain/strategies/LFUStrategy.ts +22 -0
- package/src/storage/cache/domain/strategies/LRUStrategy.ts +22 -0
- package/src/storage/cache/domain/strategies/README.md +117 -0
- package/src/storage/cache/domain/strategies/TTLStrategy.ts +23 -0
- package/src/storage/cache/domain/strategies/__tests__/EvictionStrategies.test.ts +293 -0
- package/src/storage/cache/domain/types/Cache.ts +28 -0
- package/src/storage/cache/domain/types/README.md +107 -0
- package/src/storage/cache/index.ts +28 -0
- package/src/storage/cache/infrastructure/README.md +126 -0
- package/src/storage/cache/infrastructure/TTLCache.ts +103 -0
- package/src/storage/cache/infrastructure/__tests__/TTLCache.test.ts +303 -0
- package/src/storage/cache/presentation/README.md +123 -0
- package/src/storage/cache/presentation/__tests__/ReactHooks.test.ts +514 -0
- package/src/storage/cache/presentation/useCache.ts +76 -0
- package/src/storage/cache/presentation/useCachedValue.ts +88 -0
- package/src/storage/cache/types.d.ts +3 -0
- package/src/storage/domain/README.md +128 -0
- package/src/storage/domain/constants/CacheDefaults.ts +64 -0
- package/src/storage/domain/constants/README.md +105 -0
- package/src/storage/domain/entities/CachedValue.ts +86 -0
- package/src/storage/domain/entities/README.md +109 -0
- package/src/storage/domain/entities/StorageResult.ts +75 -0
- package/src/storage/domain/entities/__tests__/CachedValue.test.ts +149 -0
- package/src/storage/domain/entities/__tests__/StorageResult.test.ts +122 -0
- package/src/storage/domain/errors/README.md +126 -0
- package/src/storage/domain/errors/StorageError.ts +81 -0
- package/src/storage/domain/errors/__tests__/StorageError.test.ts +127 -0
- package/src/storage/domain/factories/README.md +138 -0
- package/src/storage/domain/factories/StoreFactory.ts +59 -0
- package/src/storage/domain/types/README.md +522 -0
- package/src/storage/domain/types/Store.ts +44 -0
- package/src/storage/domain/utils/CacheKeyGenerator.ts +66 -0
- package/src/storage/domain/utils/README.md +127 -0
- package/src/storage/domain/utils/__tests__/devUtils.test.ts +97 -0
- package/src/storage/domain/utils/devUtils.ts +37 -0
- package/src/storage/domain/value-objects/README.md +120 -0
- package/src/storage/domain/value-objects/StorageKey.ts +60 -0
- package/src/storage/index.ts +175 -0
- package/src/storage/infrastructure/README.md +165 -0
- package/src/storage/infrastructure/adapters/README.md +175 -0
- package/src/storage/infrastructure/adapters/StorageService.md +103 -0
- package/src/storage/infrastructure/adapters/StorageService.ts +49 -0
- package/src/storage/infrastructure/repositories/AsyncStorageRepository.ts +98 -0
- package/src/storage/infrastructure/repositories/BaseStorageOperations.ts +100 -0
- package/src/storage/infrastructure/repositories/BatchStorageOperations.ts +42 -0
- package/src/storage/infrastructure/repositories/README.md +121 -0
- package/src/storage/infrastructure/repositories/StringStorageOperations.ts +44 -0
- package/src/storage/infrastructure/repositories/__tests__/AsyncStorageRepository.test.ts +170 -0
- package/src/storage/infrastructure/repositories/__tests__/BaseStorageOperations.test.ts +201 -0
- package/src/storage/presentation/README.md +181 -0
- package/src/storage/presentation/hooks/CacheStorageOperations.ts +94 -0
- package/src/storage/presentation/hooks/README.md +128 -0
- package/src/storage/presentation/hooks/__tests__/usePersistentCache.test.ts +405 -0
- package/src/storage/presentation/hooks/__tests__/useStorage.test.ts +247 -0
- package/src/storage/presentation/hooks/__tests__/useStorageState.test.ts +293 -0
- package/src/storage/presentation/hooks/useCacheState.ts +53 -0
- package/src/storage/presentation/hooks/usePersistentCache.ts +154 -0
- package/src/storage/presentation/hooks/useStorage.ts +102 -0
- package/src/storage/presentation/hooks/useStorageState.ts +71 -0
- package/src/storage/presentation/hooks/useStore.ts +15 -0
- package/src/storage/types/README.md +103 -0
- package/src/theme/infrastructure/globalThemeStore.ts +1 -1
- package/src/theme/infrastructure/storage/ThemeStorage.ts +1 -1
- package/src/theme/infrastructure/stores/themeStore.ts +1 -1
- package/src/utilities/sharing/infrastructure/services/SharingService.ts +1 -1
|
@@ -0,0 +1,352 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
/**
|
|
3
|
+
* Performance Tests
|
|
4
|
+
*
|
|
5
|
+
* Performance and memory leak tests
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { renderHook, act } from '@testing-library/react-hooks';
|
|
9
|
+
import { useStorage, useStorageState, usePersistentCache } from '../../index';
|
|
10
|
+
import { AsyncStorage } from '../mocks/asyncStorage.mock';
|
|
11
|
+
import { mockPerformance, trackMemoryUsage } from '../setup';
|
|
12
|
+
|
|
13
|
+
describe('Performance Tests', () => {
|
|
14
|
+
beforeEach(() => {
|
|
15
|
+
(AsyncStorage as any).__clear();
|
|
16
|
+
jest.clearAllMocks();
|
|
17
|
+
mockPerformance();
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
describe('Memory Leak Prevention', () => {
|
|
21
|
+
it('should not leak memory with multiple hook instances', () => {
|
|
22
|
+
const memoryTracker = trackMemoryUsage();
|
|
23
|
+
|
|
24
|
+
// Create many hook instances
|
|
25
|
+
const hooks = Array.from({ length: 100 }, (_, i) =>
|
|
26
|
+
renderHook(() => useStorage())
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
// Add listeners
|
|
30
|
+
hooks.forEach(hook => {
|
|
31
|
+
hook.current.getItem(`key-${i}`, 'default');
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
const listenerCount = memoryTracker.getListenerCount();
|
|
35
|
+
expect(listenerCount).toBeLessThan(50); // Should be much less than 100
|
|
36
|
+
|
|
37
|
+
// Cleanup
|
|
38
|
+
hooks.forEach(hook => hook.unmount());
|
|
39
|
+
memoryTracker.cleanup();
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('should cleanup on unmount', () => {
|
|
43
|
+
const memoryTracker = trackMemoryUsage();
|
|
44
|
+
|
|
45
|
+
const { result, unmount } = renderHook(() => useStorageState('test', 'default'));
|
|
46
|
+
|
|
47
|
+
// Add some async operations
|
|
48
|
+
const promise = result.current[1]('test-value');
|
|
49
|
+
|
|
50
|
+
// Unmount before promise resolves
|
|
51
|
+
unmount();
|
|
52
|
+
|
|
53
|
+
const listenerCount = memoryTracker.getListenerCount();
|
|
54
|
+
expect(listenerCount).toBe(0);
|
|
55
|
+
|
|
56
|
+
memoryTracker.cleanup();
|
|
57
|
+
return promise;
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it('should not create multiple CacheStorageOperations instances', () => {
|
|
61
|
+
const { result: hook1 } = renderHook(() =>
|
|
62
|
+
usePersistentCache('key1')
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
const { result: hook2 } = renderHook(() =>
|
|
66
|
+
usePersistentCache('key2')
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
const { result: hook3 } = renderHook(() =>
|
|
70
|
+
usePersistentCache('key3')
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
// All should use the same instance (singleton pattern)
|
|
74
|
+
expect(hook1.current.setData).toBe(hook2.current.setData);
|
|
75
|
+
expect(hook2.current.setData).toBe(hook3.current.setData);
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
describe('Performance Benchmarks', () => {
|
|
80
|
+
it('should handle large datasets efficiently', async () => {
|
|
81
|
+
const { result: storageHook } = renderHook(() => useStorage());
|
|
82
|
+
|
|
83
|
+
// Create large dataset (10,000 items)
|
|
84
|
+
const largeData = Array.from({ length: 10000 }, (_, i) => ({
|
|
85
|
+
id: i,
|
|
86
|
+
name: `Item ${i}`,
|
|
87
|
+
description: `Description for item ${i}`,
|
|
88
|
+
metadata: {
|
|
89
|
+
created: Date.now(),
|
|
90
|
+
tags: [`tag${i % 10}`, `category${i % 5}`],
|
|
91
|
+
},
|
|
92
|
+
}));
|
|
93
|
+
|
|
94
|
+
const startTime = performance.now();
|
|
95
|
+
|
|
96
|
+
// Set large data
|
|
97
|
+
const setSuccess = await storageHook.current.setItem('large-data', largeData);
|
|
98
|
+
expect(setSuccess).toBe(true);
|
|
99
|
+
|
|
100
|
+
// Get large data
|
|
101
|
+
const retrievedData = await storageHook.current.getItem('large-data', []);
|
|
102
|
+
expect(retrievedData).toEqual(largeData);
|
|
103
|
+
|
|
104
|
+
const endTime = performance.now();
|
|
105
|
+
const duration = endTime - startTime;
|
|
106
|
+
|
|
107
|
+
// Should complete within reasonable time (less than 1 second)
|
|
108
|
+
expect(duration).toBeLessThan(1000);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it('should handle rapid operations efficiently', async () => {
|
|
112
|
+
const { result: storageHook } = renderHook(() => useStorage());
|
|
113
|
+
|
|
114
|
+
const iterations = 1000;
|
|
115
|
+
const startTime = performance.now();
|
|
116
|
+
|
|
117
|
+
// Rapid set/get operations
|
|
118
|
+
for (let i = 0; i < iterations; i++) {
|
|
119
|
+
const key = `rapid-key-${i}`;
|
|
120
|
+
const value = `rapid-value-${i}`;
|
|
121
|
+
|
|
122
|
+
await storageHook.current.setItem(key, value);
|
|
123
|
+
const retrieved = await storageHook.current.getItem(key, '');
|
|
124
|
+
expect(retrieved).toBe(value);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const endTime = performance.now();
|
|
128
|
+
const duration = endTime - startTime;
|
|
129
|
+
const avgTime = duration / iterations;
|
|
130
|
+
|
|
131
|
+
// Average operation should be fast (less than 1ms per operation)
|
|
132
|
+
expect(avgTime).toBeLessThan(1);
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
it('should handle concurrent operations efficiently', async () => {
|
|
136
|
+
const { result: storageHook } = renderHook(() => useStorage());
|
|
137
|
+
|
|
138
|
+
const concurrentOperations = 100;
|
|
139
|
+
const operations = Array.from({ length: concurrentOperations }, (_, i) =>
|
|
140
|
+
storageHook.current.setItem(`concurrent-key-${i}`, `concurrent-value-${i}`)
|
|
141
|
+
);
|
|
142
|
+
|
|
143
|
+
const startTime = performance.now();
|
|
144
|
+
|
|
145
|
+
// Execute all operations concurrently
|
|
146
|
+
const results = await Promise.all(operations);
|
|
147
|
+
|
|
148
|
+
const endTime = performance.now();
|
|
149
|
+
const duration = endTime - startTime;
|
|
150
|
+
|
|
151
|
+
// All operations should succeed
|
|
152
|
+
expect(results.every(success => success)).toBe(true);
|
|
153
|
+
|
|
154
|
+
// Concurrent operations should be faster than sequential
|
|
155
|
+
expect(duration).toBeLessThan(concurrentOperations * 10); // Less than 10ms per operation
|
|
156
|
+
});
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
describe('Cache Performance', () => {
|
|
160
|
+
it('should handle cache operations efficiently', async () => {
|
|
161
|
+
const { result: cacheHook } = renderHook(() =>
|
|
162
|
+
usePersistentCache('performance-cache')
|
|
163
|
+
);
|
|
164
|
+
|
|
165
|
+
const cacheOperations = 1000;
|
|
166
|
+
const startTime = performance.now();
|
|
167
|
+
|
|
168
|
+
// Rapid cache operations
|
|
169
|
+
for (let i = 0; i < cacheOperations; i++) {
|
|
170
|
+
const data = { id: i, value: `cache-value-${i}` };
|
|
171
|
+
|
|
172
|
+
await cacheHook.current.setData(data);
|
|
173
|
+
expect(cacheHook.current.data).toEqual(data);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const endTime = performance.now();
|
|
177
|
+
const duration = endTime - startTime;
|
|
178
|
+
const avgTime = duration / cacheOperations;
|
|
179
|
+
|
|
180
|
+
// Cache operations should be very fast
|
|
181
|
+
expect(avgTime).toBeLessThan(0.1); // Less than 0.1ms per operation
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
it('should handle TTL checks efficiently', async () => {
|
|
185
|
+
const { result: cacheHook } = renderHook(() =>
|
|
186
|
+
usePersistentCache('ttl-cache', { ttl: 60000 })
|
|
187
|
+
);
|
|
188
|
+
|
|
189
|
+
const data = { test: 'value' };
|
|
190
|
+
await cacheHook.current.setData(data);
|
|
191
|
+
|
|
192
|
+
const startTime = performance.now();
|
|
193
|
+
|
|
194
|
+
// Check TTL many times (should be cached)
|
|
195
|
+
for (let i = 0; i < 10000; i++) {
|
|
196
|
+
// Access isExpired property
|
|
197
|
+
const isExpired = cacheHook.current.isExpired;
|
|
198
|
+
expect(isExpired).toBe(false);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const endTime = performance.now();
|
|
202
|
+
const duration = endTime - startTime;
|
|
203
|
+
|
|
204
|
+
// TTL checks should be extremely fast
|
|
205
|
+
expect(duration).toBeLessThan(10); // Less than 10ms for 10,000 checks
|
|
206
|
+
});
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
describe('State Performance', () => {
|
|
210
|
+
it('should handle state updates efficiently', async () => {
|
|
211
|
+
const { result: stateHook, waitForNextUpdate } = renderHook(() =>
|
|
212
|
+
useStorageState('performance-state', 'initial')
|
|
213
|
+
);
|
|
214
|
+
|
|
215
|
+
await act(async () => {
|
|
216
|
+
await waitForNextUpdate();
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
const stateUpdates = 1000;
|
|
220
|
+
const startTime = performance.now();
|
|
221
|
+
|
|
222
|
+
// Rapid state updates
|
|
223
|
+
for (let i = 0; i < stateUpdates; i++) {
|
|
224
|
+
await act(async () => {
|
|
225
|
+
await stateHook.current[1](`update-${i}`);
|
|
226
|
+
});
|
|
227
|
+
expect(stateHook.current[0]).toBe(`update-${i}`);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
const endTime = performance.now();
|
|
231
|
+
const duration = endTime - startTime;
|
|
232
|
+
const avgTime = duration / stateUpdates;
|
|
233
|
+
|
|
234
|
+
// State updates should be fast
|
|
235
|
+
expect(avgTime).toBeLessThan(1); // Less than 1ms per update
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
it('should not re-render unnecessarily', async () => {
|
|
239
|
+
let renderCount = 0;
|
|
240
|
+
|
|
241
|
+
const TestComponent = () => {
|
|
242
|
+
renderCount++;
|
|
243
|
+
const [state] = useStorageState('render-test', 'default');
|
|
244
|
+
return null; // Don't render anything
|
|
245
|
+
};
|
|
246
|
+
|
|
247
|
+
const { rerender } = renderHook(() => <TestComponent />);
|
|
248
|
+
|
|
249
|
+
const initialRenders = renderCount;
|
|
250
|
+
|
|
251
|
+
// Rerender with same props
|
|
252
|
+
rerender(<TestComponent />);
|
|
253
|
+
|
|
254
|
+
// Should not cause additional renders
|
|
255
|
+
expect(renderCount).toBe(initialRenders);
|
|
256
|
+
});
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
describe('Memory Usage', () => {
|
|
260
|
+
it('should maintain stable memory usage', async () => {
|
|
261
|
+
const { result: storageHook } = renderHook(() => useStorage());
|
|
262
|
+
|
|
263
|
+
const initialMemory = (performance as any).memory?.usedJSHeapSize || 0;
|
|
264
|
+
|
|
265
|
+
// Create and destroy many objects
|
|
266
|
+
for (let i = 0; i < 1000; i++) {
|
|
267
|
+
const largeObject = {
|
|
268
|
+
id: i,
|
|
269
|
+
data: new Array(1000).fill(`data-${i}`),
|
|
270
|
+
nested: {
|
|
271
|
+
level1: { level2: { level3: new Array(100).fill(`nested-${i}`) } },
|
|
272
|
+
},
|
|
273
|
+
};
|
|
274
|
+
|
|
275
|
+
await storageHook.current.setItem(`temp-${i}`, largeObject);
|
|
276
|
+
await storageHook.current.removeItem(`temp-${i}`);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// Force garbage collection if available
|
|
280
|
+
if (global.gc) {
|
|
281
|
+
global.gc();
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
const finalMemory = (performance as any).memory?.usedJSHeapSize || 0;
|
|
285
|
+
const memoryIncrease = finalMemory - initialMemory;
|
|
286
|
+
|
|
287
|
+
// Memory increase should be minimal
|
|
288
|
+
expect(memoryIncrease).toBeLessThan(1024 * 1024); // Less than 1MB increase
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
it('should cleanup event listeners', () => {
|
|
292
|
+
const memoryTracker = trackMemoryUsage();
|
|
293
|
+
|
|
294
|
+
// Create and destroy many hooks
|
|
295
|
+
for (let i = 0; i < 100; i++) {
|
|
296
|
+
const { unmount } = renderHook(() => useStorageState(`cleanup-${i}`, 'default'));
|
|
297
|
+
unmount();
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
const listenerCount = memoryTracker.getListenerCount();
|
|
301
|
+
expect(listenerCount).toBe(0);
|
|
302
|
+
|
|
303
|
+
memoryTracker.cleanup();
|
|
304
|
+
});
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
describe('Scalability Tests', () => {
|
|
308
|
+
it('should handle many keys efficiently', async () => {
|
|
309
|
+
const { result: storageHook } = renderHook(() => useStorage());
|
|
310
|
+
|
|
311
|
+
const keyCount = 10000;
|
|
312
|
+
const startTime = performance.now();
|
|
313
|
+
|
|
314
|
+
// Create many keys
|
|
315
|
+
for (let i = 0; i < keyCount; i++) {
|
|
316
|
+
await storageHook.current.setItem(`scale-key-${i}`, `scale-value-${i}`);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
const endTime = performance.now();
|
|
320
|
+
const duration = endTime - startTime;
|
|
321
|
+
|
|
322
|
+
// Should handle many keys efficiently
|
|
323
|
+
expect(duration).toBeLessThan(keyCount * 0.1); // Less than 0.1ms per key
|
|
324
|
+
|
|
325
|
+
// Verify all keys exist
|
|
326
|
+
for (let i = 0; i < keyCount; i++) {
|
|
327
|
+
const exists = await storageHook.current.hasItem(`scale-key-${i}`);
|
|
328
|
+
expect(exists).toBe(true);
|
|
329
|
+
}
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
it('should handle large values efficiently', async () => {
|
|
333
|
+
const { result: storageHook } = renderHook(() => useStorage());
|
|
334
|
+
|
|
335
|
+
// Create very large value (1MB string)
|
|
336
|
+
const largeValue = 'x'.repeat(1024 * 1024);
|
|
337
|
+
const startTime = performance.now();
|
|
338
|
+
|
|
339
|
+
const setSuccess = await storageHook.current.setItem('large-value', largeValue);
|
|
340
|
+
expect(setSuccess).toBe(true);
|
|
341
|
+
|
|
342
|
+
const retrievedValue = await storageHook.current.getString('large-value', '');
|
|
343
|
+
expect(retrievedValue).toBe(largeValue);
|
|
344
|
+
|
|
345
|
+
const endTime = performance.now();
|
|
346
|
+
const duration = endTime - startTime;
|
|
347
|
+
|
|
348
|
+
// Large values should be handled efficiently
|
|
349
|
+
expect(duration).toBeLessThan(100); // Less than 100ms for 1MB
|
|
350
|
+
});
|
|
351
|
+
});
|
|
352
|
+
});
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test Setup
|
|
3
|
+
*
|
|
4
|
+
* Global test configuration and mocks
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import 'jest-environment-jsdom';
|
|
8
|
+
|
|
9
|
+
// Mock __DEV__ global
|
|
10
|
+
Object.defineProperty(globalThis, '__DEV__', {
|
|
11
|
+
value: true,
|
|
12
|
+
writable: true,
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
// Mock AsyncStorage
|
|
16
|
+
jest.mock('@react-native-async-storage/async-storage', () => ({
|
|
17
|
+
default: {
|
|
18
|
+
getItem: jest.fn(),
|
|
19
|
+
setItem: jest.fn(),
|
|
20
|
+
removeItem: jest.fn(),
|
|
21
|
+
clear: jest.fn(),
|
|
22
|
+
getAllKeys: jest.fn(),
|
|
23
|
+
multiGet: jest.fn(),
|
|
24
|
+
},
|
|
25
|
+
}));
|
|
26
|
+
|
|
27
|
+
// Mock console for __DEV__ checks
|
|
28
|
+
const originalConsole = { ...console };
|
|
29
|
+
|
|
30
|
+
beforeEach(() => {
|
|
31
|
+
jest.clearAllMocks();
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
afterEach(() => {
|
|
35
|
+
// Restore console after each test
|
|
36
|
+
Object.assign(console, originalConsole);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
// Performance test utilities
|
|
40
|
+
export const mockPerformance = () => {
|
|
41
|
+
const mockPerformance = {
|
|
42
|
+
now: jest.fn(() => Date.now()),
|
|
43
|
+
mark: jest.fn(),
|
|
44
|
+
measure: jest.fn(),
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
globalThis.performance = mockPerformance as any;
|
|
48
|
+
return mockPerformance;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
// Memory leak test utilities
|
|
52
|
+
export const trackMemoryUsage = () => {
|
|
53
|
+
const listeners = new Set<() => void>();
|
|
54
|
+
|
|
55
|
+
return {
|
|
56
|
+
addListener: (listener: () => void) => {
|
|
57
|
+
listeners.add(listener);
|
|
58
|
+
return () => listeners.delete(listener);
|
|
59
|
+
},
|
|
60
|
+
getListenerCount: () => listeners.size,
|
|
61
|
+
cleanup: () => listeners.clear(),
|
|
62
|
+
};
|
|
63
|
+
};
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
# Application Layer
|
|
2
|
+
|
|
3
|
+
Ports and interfaces for dependency inversion between domain and infrastructure layers.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
Application layer containing interfaces (ports) for storage operations. Located at `src/application/`.
|
|
8
|
+
|
|
9
|
+
## Directory Structure
|
|
10
|
+
|
|
11
|
+
- `ports/` - Repository interfaces defining storage contracts
|
|
12
|
+
|
|
13
|
+
## Strategies
|
|
14
|
+
|
|
15
|
+
### Dependency Inversion
|
|
16
|
+
- Use IStorageRepository interface for storage operations
|
|
17
|
+
- Define contracts in application layer
|
|
18
|
+
- Implement interfaces in infrastructure layer
|
|
19
|
+
- Enable dependency injection for testing
|
|
20
|
+
|
|
21
|
+
### Port Definition
|
|
22
|
+
- Use IStorageRepository as primary storage interface
|
|
23
|
+
- Define all storage operations as methods
|
|
24
|
+
- Support Result pattern for error handling
|
|
25
|
+
- Enable generic type parameters for type safety
|
|
26
|
+
|
|
27
|
+
### Use Case Pattern
|
|
28
|
+
- Create use cases for complex business logic
|
|
29
|
+
- Inject IStorageRepository through constructor
|
|
30
|
+
- Encapsulate business rules in use case methods
|
|
31
|
+
- Enable testing with mock implementations
|
|
32
|
+
|
|
33
|
+
### Interface Segregation
|
|
34
|
+
- Define cohesive interfaces for specific purposes
|
|
35
|
+
- Support granular method dependencies
|
|
36
|
+
- Enable single responsibility principle
|
|
37
|
+
- Facilitate testing with focused mocks
|
|
38
|
+
|
|
39
|
+
## Restrictions
|
|
40
|
+
|
|
41
|
+
### Interface Definition
|
|
42
|
+
- DO NOT define implementation details in interfaces
|
|
43
|
+
- DO NOT create interfaces without clear purpose
|
|
44
|
+
- DO NOT mix different concerns in single interface
|
|
45
|
+
- DO NOT include framework-specific types
|
|
46
|
+
|
|
47
|
+
### Dependency Injection
|
|
48
|
+
- DO NOT create concrete instances in use cases
|
|
49
|
+
- DO NOT use hardcoded dependencies
|
|
50
|
+
- DO NOT skip constructor injection
|
|
51
|
+
- DO NOT create circular dependencies
|
|
52
|
+
|
|
53
|
+
### Use Case Implementation
|
|
54
|
+
- DO NOT implement business logic in components
|
|
55
|
+
- DO NOT mix storage operations with UI logic
|
|
56
|
+
- DO NOT create use cases without clear purpose
|
|
57
|
+
- DO NOT bypass error handling in use cases
|
|
58
|
+
|
|
59
|
+
### Testing
|
|
60
|
+
- DO NOT test with real storage implementations
|
|
61
|
+
- DO NOT create complex mock implementations
|
|
62
|
+
- DO NOT share state between tests
|
|
63
|
+
- DO NOT forget to reset mocks between tests
|
|
64
|
+
|
|
65
|
+
## Rules
|
|
66
|
+
|
|
67
|
+
### IStorageRepository Interface
|
|
68
|
+
- MUST define getItem<T>(key, defaultValue): Promise<StorageResult<T>>
|
|
69
|
+
- MUST define setItem<T>(key, value): Promise<StorageResult<void>>
|
|
70
|
+
- MUST define getString(key, defaultValue): Promise<StorageResult<string>>
|
|
71
|
+
- MUST define setString(key, value): Promise<StorageResult<void>>
|
|
72
|
+
- MUST define removeItem(key): Promise<StorageResult<void>>
|
|
73
|
+
- MUST define hasItem(key): Promise<boolean>
|
|
74
|
+
- MUST define clearAll(): Promise<StorageResult<void>>
|
|
75
|
+
|
|
76
|
+
### Interface Design
|
|
77
|
+
- MUST use generic type parameters for value operations
|
|
78
|
+
- MUST return Result type for all mutable operations
|
|
79
|
+
- MUST support type inference from parameters
|
|
80
|
+
- MUST be framework-agnostic
|
|
81
|
+
- MUST define contracts only (no implementation)
|
|
82
|
+
|
|
83
|
+
### Method Signatures
|
|
84
|
+
- MUST accept key parameter for all operations
|
|
85
|
+
- MUST accept defaultValue parameter for get operations
|
|
86
|
+
- MUST accept value parameter for set operations
|
|
87
|
+
- MUST return Promise for all async operations
|
|
88
|
+
- MUST include StorageResult for typed results
|
|
89
|
+
|
|
90
|
+
### Use Case Implementation
|
|
91
|
+
- MUST accept IStorageRepository in constructor
|
|
92
|
+
- MUST define single responsibility per use case
|
|
93
|
+
- MUST handle Result pattern in all operations
|
|
94
|
+
- MUST return domain types from use case methods
|
|
95
|
+
- MUST not expose storage implementation details
|
|
96
|
+
|
|
97
|
+
### Constructor Injection
|
|
98
|
+
- MUST inject dependencies through constructor
|
|
99
|
+
- MUST store dependencies as private fields
|
|
100
|
+
- MUST use interface types for dependencies
|
|
101
|
+
- MUST enable dependency injection for testing
|
|
102
|
+
- MUST not create instances inside use cases
|
|
103
|
+
|
|
104
|
+
### Error Handling
|
|
105
|
+
- MUST check result.success before accessing data
|
|
106
|
+
- MUST handle result.error appropriately
|
|
107
|
+
- MUST propagate errors to callers when needed
|
|
108
|
+
- MUST not throw exceptions from use cases
|
|
109
|
+
- MUST preserve error context
|
|
110
|
+
|
|
111
|
+
### Testing Requirements
|
|
112
|
+
- MUST enable mocking of IStorageRepository
|
|
113
|
+
- MUST support test double implementations
|
|
114
|
+
- MUST isolate tests from real storage
|
|
115
|
+
- MUST clear state between test runs
|
|
116
|
+
- MUST not depend on external services
|
|
117
|
+
|
|
118
|
+
### Mock Implementation
|
|
119
|
+
- MUST implement full IStorageRepository interface
|
|
120
|
+
- MUST return appropriate Result types
|
|
121
|
+
- MUST simulate storage behavior accurately
|
|
122
|
+
- MUST support async operations with Promise
|
|
123
|
+
- MUST be simple and predictable
|
|
124
|
+
|
|
125
|
+
### Custom Repository Implementation
|
|
126
|
+
- MUST implement IStorageRepository interface
|
|
127
|
+
- MUST use Result pattern for all methods
|
|
128
|
+
- MUST handle errors appropriately
|
|
129
|
+
- MUST support generic type parameters
|
|
130
|
+
- MUST be compatible with existing code
|
|
131
|
+
|
|
132
|
+
### Layer Separation
|
|
133
|
+
- MUST not depend on infrastructure implementations
|
|
134
|
+
- MUST not depend on presentation components
|
|
135
|
+
- MUST define contracts for domain use
|
|
136
|
+
- MUST enable layer independence
|
|
137
|
+
- MUST support testing at each layer
|
|
138
|
+
|
|
139
|
+
### Export Rules
|
|
140
|
+
- MUST export IStorageRepository interface
|
|
141
|
+
- MUST export all port interfaces
|
|
142
|
+
- MUST not export implementations
|
|
143
|
+
- MUST provide TypeScript types
|
|
144
|
+
- MUST document interface contracts
|
|
145
|
+
|
|
146
|
+
### Architecture Compliance
|
|
147
|
+
- MUST follow dependency inversion principle
|
|
148
|
+
- MUST depend on abstractions not concretions
|
|
149
|
+
- MUST enable independent layer evolution
|
|
150
|
+
- MUST support multiple implementations
|
|
151
|
+
- MUST maintain clear separation of concerns
|
|
152
|
+
|
|
153
|
+
### Documentation
|
|
154
|
+
- MUST document all interface methods
|
|
155
|
+
- MUST specify method contracts clearly
|
|
156
|
+
- MUST provide usage guidance
|
|
157
|
+
- MUST explain Result pattern usage
|
|
158
|
+
- MUST not include implementation examples
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Storage Repository Interface (Port)
|
|
3
|
+
*
|
|
4
|
+
* Domain-Driven Design: Application port for storage operations
|
|
5
|
+
* Infrastructure layer implements this interface
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { StorageResult } from '../../domain/entities/StorageResult';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Storage Repository Interface
|
|
12
|
+
* Defines contract for storage operations
|
|
13
|
+
*/
|
|
14
|
+
export interface IStorageRepository {
|
|
15
|
+
/**
|
|
16
|
+
* Get item from storage with JSON parsing
|
|
17
|
+
*/
|
|
18
|
+
getItem<T>(key: string, defaultValue: T): Promise<StorageResult<T>>;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Set item in storage with JSON serialization
|
|
22
|
+
*/
|
|
23
|
+
setItem<T>(key: string, value: T): Promise<StorageResult<T>>;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Get string value (no JSON parsing)
|
|
27
|
+
*/
|
|
28
|
+
getString(key: string, defaultValue: string): Promise<StorageResult<string>>;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Set string value (no JSON serialization)
|
|
32
|
+
*/
|
|
33
|
+
setString(key: string, value: string): Promise<StorageResult<string>>;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Remove item from storage
|
|
37
|
+
*/
|
|
38
|
+
removeItem(key: string): Promise<StorageResult<void>>;
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Check if key exists in storage
|
|
42
|
+
*/
|
|
43
|
+
hasItem(key: string): Promise<boolean>;
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Clear all storage data
|
|
47
|
+
*/
|
|
48
|
+
clearAll(): Promise<StorageResult<void>>;
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Get multiple items at once
|
|
52
|
+
*/
|
|
53
|
+
getMultiple(keys: string[]): Promise<StorageResult<Record<string, string | null>>>;
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Get all keys from storage
|
|
57
|
+
*/
|
|
58
|
+
getAllKeys(): Promise<StorageResult<string[]>>;
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
}
|