@umituz/react-native-storage 2.0.0 → 2.3.2
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 +23 -7
- package/src/__tests__/integration.test.ts +391 -0
- package/src/__tests__/mocks/asyncStorage.mock.ts +52 -0
- package/src/__tests__/performance.test.ts +351 -0
- package/src/__tests__/setup.ts +63 -0
- package/src/application/ports/IStorageRepository.ts +0 -12
- package/src/domain/entities/StorageResult.ts +1 -3
- package/src/domain/entities/__tests__/CachedValue.test.ts +149 -0
- package/src/domain/entities/__tests__/StorageResult.test.ts +122 -0
- package/src/domain/errors/StorageError.ts +0 -2
- package/src/domain/errors/__tests__/StorageError.test.ts +127 -0
- package/src/domain/utils/__tests__/devUtils.test.ts +97 -0
- package/src/domain/utils/devUtils.ts +37 -0
- package/src/domain/value-objects/StorageKey.ts +27 -29
- package/src/index.ts +9 -1
- package/src/infrastructure/adapters/StorageService.ts +8 -6
- package/src/infrastructure/repositories/AsyncStorageRepository.ts +27 -108
- package/src/infrastructure/repositories/BaseStorageOperations.ts +101 -0
- package/src/infrastructure/repositories/BatchStorageOperations.ts +42 -0
- package/src/infrastructure/repositories/StringStorageOperations.ts +44 -0
- package/src/infrastructure/repositories/__tests__/AsyncStorageRepository.test.ts +169 -0
- package/src/infrastructure/repositories/__tests__/BaseStorageOperations.test.ts +200 -0
- package/src/presentation/hooks/CacheStorageOperations.ts +95 -0
- package/src/presentation/hooks/__tests__/usePersistentCache.test.ts +404 -0
- package/src/presentation/hooks/__tests__/useStorage.test.ts +246 -0
- package/src/presentation/hooks/__tests__/useStorageState.test.ts +292 -0
- package/src/presentation/hooks/useCacheState.ts +55 -0
- package/src/presentation/hooks/usePersistentCache.ts +30 -39
- package/src/presentation/hooks/useStorage.ts +4 -3
- package/src/presentation/hooks/useStorageState.ts +24 -8
- package/src/presentation/hooks/useStore.ts +3 -1
- package/src/types/global.d.ts +40 -0
- package/LICENSE +0 -22
- package/src/presentation/hooks/usePersistedState.ts +0 -34
|
@@ -2,178 +2,97 @@
|
|
|
2
2
|
* AsyncStorage Repository
|
|
3
3
|
*
|
|
4
4
|
* Domain-Driven Design: Infrastructure implementation of IStorageRepository
|
|
5
|
-
* Adapts React Native AsyncStorage to domain interface
|
|
5
|
+
* Adapts React Native AsyncStorage to domain interface using composition
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import AsyncStorage from '@react-native-async-storage/async-storage';
|
|
9
8
|
import type { IStorageRepository } from '../../application/ports/IStorageRepository';
|
|
10
9
|
import type { StorageResult } from '../../domain/entities/StorageResult';
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
|
|
14
|
-
StorageWriteError,
|
|
15
|
-
StorageDeleteError,
|
|
16
|
-
StorageSerializationError,
|
|
17
|
-
StorageDeserializationError,
|
|
18
|
-
} from '../../domain/errors/StorageError';
|
|
10
|
+
import { BaseStorageOperations } from './BaseStorageOperations';
|
|
11
|
+
import { StringStorageOperations } from './StringStorageOperations';
|
|
12
|
+
import { BatchStorageOperations } from './BatchStorageOperations';
|
|
19
13
|
|
|
20
14
|
/**
|
|
21
15
|
* AsyncStorage Repository Implementation
|
|
16
|
+
* Uses composition to follow Single Responsibility Principle
|
|
22
17
|
*/
|
|
23
18
|
export class AsyncStorageRepository implements IStorageRepository {
|
|
19
|
+
private baseOps: BaseStorageOperations;
|
|
20
|
+
private stringOps: StringStorageOperations;
|
|
21
|
+
private batchOps: BatchStorageOperations;
|
|
22
|
+
|
|
23
|
+
constructor() {
|
|
24
|
+
this.baseOps = new BaseStorageOperations();
|
|
25
|
+
this.stringOps = new StringStorageOperations();
|
|
26
|
+
this.batchOps = new BatchStorageOperations();
|
|
27
|
+
}
|
|
28
|
+
|
|
24
29
|
/**
|
|
25
30
|
* Get item from AsyncStorage with type safety
|
|
26
31
|
*/
|
|
27
32
|
async getItem<T>(key: string, defaultValue: T): Promise<StorageResult<T>> {
|
|
28
|
-
|
|
29
|
-
const value = await AsyncStorage.getItem(key);
|
|
30
|
-
|
|
31
|
-
if (value === null) {
|
|
32
|
-
// Missing keys on first app launch are NORMAL, not errors
|
|
33
|
-
return success(defaultValue);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
try {
|
|
37
|
-
const parsed = JSON.parse(value) as T;
|
|
38
|
-
return success(parsed);
|
|
39
|
-
} catch (parseError) {
|
|
40
|
-
return failure(new StorageDeserializationError(key, parseError), defaultValue);
|
|
41
|
-
}
|
|
42
|
-
} catch (error) {
|
|
43
|
-
return failure(new StorageReadError(key, error), defaultValue);
|
|
44
|
-
}
|
|
33
|
+
return this.baseOps.getItem(key, defaultValue);
|
|
45
34
|
}
|
|
46
35
|
|
|
47
36
|
/**
|
|
48
37
|
* Set item in AsyncStorage with automatic JSON serialization
|
|
49
38
|
*/
|
|
50
39
|
async setItem<T>(key: string, value: T): Promise<StorageResult<T>> {
|
|
51
|
-
|
|
52
|
-
let serialized: string;
|
|
53
|
-
try {
|
|
54
|
-
serialized = JSON.stringify(value);
|
|
55
|
-
} catch (serializeError) {
|
|
56
|
-
return failure(new StorageSerializationError(key, serializeError));
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
await AsyncStorage.setItem(key, serialized);
|
|
60
|
-
return success(value);
|
|
61
|
-
} catch (error) {
|
|
62
|
-
return failure(new StorageWriteError(key, error));
|
|
63
|
-
}
|
|
40
|
+
return this.baseOps.setItem(key, value);
|
|
64
41
|
}
|
|
65
42
|
|
|
66
43
|
/**
|
|
67
44
|
* Get string value (no JSON parsing)
|
|
68
45
|
*/
|
|
69
46
|
async getString(key: string, defaultValue: string): Promise<StorageResult<string>> {
|
|
70
|
-
|
|
71
|
-
const value = await AsyncStorage.getItem(key);
|
|
72
|
-
|
|
73
|
-
if (value === null) {
|
|
74
|
-
// Missing keys on first app launch are NORMAL, not errors
|
|
75
|
-
return success(defaultValue);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
return success(value);
|
|
79
|
-
} catch (error) {
|
|
80
|
-
return failure(new StorageReadError(key, error), defaultValue);
|
|
81
|
-
}
|
|
47
|
+
return this.stringOps.getString(key, defaultValue);
|
|
82
48
|
}
|
|
83
49
|
|
|
84
50
|
/**
|
|
85
51
|
* Set string value (no JSON serialization)
|
|
86
52
|
*/
|
|
87
53
|
async setString(key: string, value: string): Promise<StorageResult<string>> {
|
|
88
|
-
|
|
89
|
-
await AsyncStorage.setItem(key, value);
|
|
90
|
-
return success(value);
|
|
91
|
-
} catch (error) {
|
|
92
|
-
return failure(new StorageWriteError(key, error));
|
|
93
|
-
}
|
|
54
|
+
return this.stringOps.setString(key, value);
|
|
94
55
|
}
|
|
95
56
|
|
|
96
57
|
/**
|
|
97
58
|
* Remove item from AsyncStorage
|
|
98
59
|
*/
|
|
99
60
|
async removeItem(key: string): Promise<StorageResult<void>> {
|
|
100
|
-
|
|
101
|
-
await AsyncStorage.removeItem(key);
|
|
102
|
-
return success(undefined);
|
|
103
|
-
} catch (error) {
|
|
104
|
-
return failure(new StorageDeleteError(key, error));
|
|
105
|
-
}
|
|
61
|
+
return this.baseOps.removeItem(key);
|
|
106
62
|
}
|
|
107
63
|
|
|
108
64
|
/**
|
|
109
65
|
* Check if key exists in storage
|
|
110
66
|
*/
|
|
111
67
|
async hasItem(key: string): Promise<boolean> {
|
|
112
|
-
|
|
113
|
-
const value = await AsyncStorage.getItem(key);
|
|
114
|
-
return value !== null;
|
|
115
|
-
} catch {
|
|
116
|
-
return false;
|
|
117
|
-
}
|
|
68
|
+
return this.baseOps.hasItem(key);
|
|
118
69
|
}
|
|
119
70
|
|
|
120
71
|
/**
|
|
121
|
-
* Clear all AsyncStorage data
|
|
72
|
+
* Clear all AsyncStorage data
|
|
122
73
|
*/
|
|
123
74
|
async clearAll(): Promise<StorageResult<void>> {
|
|
124
|
-
|
|
125
|
-
await AsyncStorage.clear();
|
|
126
|
-
return success(undefined);
|
|
127
|
-
} catch (error) {
|
|
128
|
-
return failure(new StorageDeleteError('ALL_KEYS', error));
|
|
129
|
-
}
|
|
75
|
+
return this.baseOps.clearAll();
|
|
130
76
|
}
|
|
131
77
|
|
|
132
78
|
/**
|
|
133
|
-
* Get multiple items at once
|
|
79
|
+
* Get multiple items at once
|
|
134
80
|
*/
|
|
135
81
|
async getMultiple(
|
|
136
82
|
keys: string[]
|
|
137
83
|
): Promise<StorageResult<Record<string, string | null>>> {
|
|
138
|
-
|
|
139
|
-
const pairs = await AsyncStorage.multiGet(keys);
|
|
140
|
-
const result = Object.fromEntries(pairs);
|
|
141
|
-
return success(result);
|
|
142
|
-
} catch (error) {
|
|
143
|
-
return failure(new StorageReadError('MULTIPLE_KEYS', error));
|
|
144
|
-
}
|
|
84
|
+
return this.batchOps.getMultiple(keys);
|
|
145
85
|
}
|
|
146
86
|
|
|
147
87
|
/**
|
|
148
88
|
* Get all keys from storage
|
|
149
89
|
*/
|
|
150
90
|
async getAllKeys(): Promise<StorageResult<string[]>> {
|
|
151
|
-
|
|
152
|
-
const keys = await AsyncStorage.getAllKeys();
|
|
153
|
-
return success([...keys]);
|
|
154
|
-
} catch (error) {
|
|
155
|
-
return failure(new StorageReadError('ALL_KEYS', error));
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
/**
|
|
160
|
-
* Get object from storage (alias for getItem for backward compatibility)
|
|
161
|
-
* @deprecated Use getItem instead
|
|
162
|
-
*/
|
|
163
|
-
async getObject<T>(key: string, defaultValue: T): Promise<StorageResult<T>> {
|
|
164
|
-
return this.getItem(key, defaultValue);
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
/**
|
|
168
|
-
* Set object in storage (alias for setItem for backward compatibility)
|
|
169
|
-
* @deprecated Use setItem instead
|
|
170
|
-
*/
|
|
171
|
-
async setObject<T>(key: string, value: T): Promise<StorageResult<T>> {
|
|
172
|
-
return this.setItem(key, value);
|
|
91
|
+
return this.batchOps.getAllKeys();
|
|
173
92
|
}
|
|
174
93
|
}
|
|
175
94
|
|
|
176
95
|
/**
|
|
177
96
|
* Singleton instance
|
|
178
97
|
*/
|
|
179
|
-
export const storageRepository = new AsyncStorageRepository();
|
|
98
|
+
export const storageRepository = new AsyncStorageRepository();
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Base Storage Operations
|
|
3
|
+
*
|
|
4
|
+
* Core storage operations following Single Responsibility Principle
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import AsyncStorage from '@react-native-async-storage/async-storage';
|
|
8
|
+
import type { IStorageRepository } from '../../application/ports/IStorageRepository';
|
|
9
|
+
import type { StorageResult } from '../../domain/entities/StorageResult';
|
|
10
|
+
import { success, failure } from '../../domain/entities/StorageResult';
|
|
11
|
+
import {
|
|
12
|
+
StorageReadError,
|
|
13
|
+
StorageWriteError,
|
|
14
|
+
StorageDeleteError,
|
|
15
|
+
StorageSerializationError,
|
|
16
|
+
StorageDeserializationError,
|
|
17
|
+
} from '../../domain/errors/StorageError';
|
|
18
|
+
import { devWarn } from '../../domain/utils/devUtils';
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Base storage operations implementation
|
|
22
|
+
*/
|
|
23
|
+
export class BaseStorageOperations {
|
|
24
|
+
/**
|
|
25
|
+
* Get item from AsyncStorage with type safety
|
|
26
|
+
*/
|
|
27
|
+
async getItem<T>(key: string, defaultValue: T): Promise<StorageResult<T>> {
|
|
28
|
+
try {
|
|
29
|
+
const value = await AsyncStorage.getItem(key);
|
|
30
|
+
|
|
31
|
+
if (value === null) {
|
|
32
|
+
return success(defaultValue);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
try {
|
|
36
|
+
const parsed = JSON.parse(value) as T;
|
|
37
|
+
return success(parsed);
|
|
38
|
+
} catch (parseError) {
|
|
39
|
+
return failure(new StorageDeserializationError(key, parseError), defaultValue);
|
|
40
|
+
}
|
|
41
|
+
} catch (error) {
|
|
42
|
+
return failure(new StorageReadError(key, error), defaultValue);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Set item in AsyncStorage with automatic JSON serialization
|
|
48
|
+
*/
|
|
49
|
+
async setItem<T>(key: string, value: T): Promise<StorageResult<T>> {
|
|
50
|
+
try {
|
|
51
|
+
let serialized: string;
|
|
52
|
+
try {
|
|
53
|
+
serialized = JSON.stringify(value);
|
|
54
|
+
} catch (serializeError) {
|
|
55
|
+
return failure(new StorageSerializationError(key, serializeError));
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
await AsyncStorage.setItem(key, serialized);
|
|
59
|
+
return success(value);
|
|
60
|
+
} catch (error) {
|
|
61
|
+
return failure(new StorageWriteError(key, error));
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Remove item from AsyncStorage
|
|
67
|
+
*/
|
|
68
|
+
async removeItem(key: string): Promise<StorageResult<void>> {
|
|
69
|
+
try {
|
|
70
|
+
await AsyncStorage.removeItem(key);
|
|
71
|
+
return success(undefined);
|
|
72
|
+
} catch (error) {
|
|
73
|
+
return failure(new StorageDeleteError(key, error));
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Check if key exists in storage
|
|
79
|
+
*/
|
|
80
|
+
async hasItem(key: string): Promise<boolean> {
|
|
81
|
+
try {
|
|
82
|
+
const value = await AsyncStorage.getItem(key);
|
|
83
|
+
return value !== null;
|
|
84
|
+
} catch (error) {
|
|
85
|
+
devWarn(`BaseStorageOperations: Failed to check if key "${key}" exists`, error);
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Clear all AsyncStorage data
|
|
92
|
+
*/
|
|
93
|
+
async clearAll(): Promise<StorageResult<void>> {
|
|
94
|
+
try {
|
|
95
|
+
await AsyncStorage.clear();
|
|
96
|
+
return success(undefined);
|
|
97
|
+
} catch (error) {
|
|
98
|
+
return failure(new StorageDeleteError('ALL_KEYS', error));
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Batch Storage Operations
|
|
3
|
+
*
|
|
4
|
+
* Batch operations following Single Responsibility Principle
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import AsyncStorage from '@react-native-async-storage/async-storage';
|
|
8
|
+
import type { StorageResult } from '../../domain/entities/StorageResult';
|
|
9
|
+
import { success, failure } from '../../domain/entities/StorageResult';
|
|
10
|
+
import { StorageReadError } from '../../domain/errors/StorageError';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Batch storage operations for efficiency
|
|
14
|
+
*/
|
|
15
|
+
export class BatchStorageOperations {
|
|
16
|
+
/**
|
|
17
|
+
* Get multiple items at once
|
|
18
|
+
*/
|
|
19
|
+
async getMultiple(
|
|
20
|
+
keys: string[]
|
|
21
|
+
): Promise<StorageResult<Record<string, string | null>>> {
|
|
22
|
+
try {
|
|
23
|
+
const pairs = await AsyncStorage.multiGet(keys);
|
|
24
|
+
const result = Object.fromEntries(pairs);
|
|
25
|
+
return success(result);
|
|
26
|
+
} catch (error) {
|
|
27
|
+
return failure(new StorageReadError('MULTIPLE_KEYS', error));
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Get all keys from storage
|
|
33
|
+
*/
|
|
34
|
+
async getAllKeys(): Promise<StorageResult<string[]>> {
|
|
35
|
+
try {
|
|
36
|
+
const keys = await AsyncStorage.getAllKeys();
|
|
37
|
+
return success([...keys]);
|
|
38
|
+
} catch (error) {
|
|
39
|
+
return failure(new StorageReadError('ALL_KEYS', error));
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* String Storage Operations
|
|
3
|
+
*
|
|
4
|
+
* Specialized string operations following Single Responsibility Principle
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import AsyncStorage from '@react-native-async-storage/async-storage';
|
|
8
|
+
import type { StorageResult } from '../../domain/entities/StorageResult';
|
|
9
|
+
import { success, failure } from '../../domain/entities/StorageResult';
|
|
10
|
+
import { StorageReadError, StorageWriteError } from '../../domain/errors/StorageError';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* String-specific storage operations
|
|
14
|
+
*/
|
|
15
|
+
export class StringStorageOperations {
|
|
16
|
+
/**
|
|
17
|
+
* Get string value (no JSON parsing)
|
|
18
|
+
*/
|
|
19
|
+
async getString(key: string, defaultValue: string): Promise<StorageResult<string>> {
|
|
20
|
+
try {
|
|
21
|
+
const value = await AsyncStorage.getItem(key);
|
|
22
|
+
|
|
23
|
+
if (value === null) {
|
|
24
|
+
return success(defaultValue);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return success(value);
|
|
28
|
+
} catch (error) {
|
|
29
|
+
return failure(new StorageReadError(key, error), defaultValue);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Set string value (no JSON serialization)
|
|
35
|
+
*/
|
|
36
|
+
async setString(key: string, value: string): Promise<StorageResult<string>> {
|
|
37
|
+
try {
|
|
38
|
+
await AsyncStorage.setItem(key, value);
|
|
39
|
+
return success(value);
|
|
40
|
+
} catch (error) {
|
|
41
|
+
return failure(new StorageWriteError(key, error));
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AsyncStorageRepository Tests
|
|
3
|
+
*
|
|
4
|
+
* Unit tests for AsyncStorageRepository
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { AsyncStorageRepository } from '../AsyncStorageRepository';
|
|
8
|
+
import { AsyncStorage } from '../../__tests__/mocks/asyncStorage.mock';
|
|
9
|
+
|
|
10
|
+
describe('AsyncStorageRepository', () => {
|
|
11
|
+
let repository: AsyncStorageRepository;
|
|
12
|
+
|
|
13
|
+
beforeEach(() => {
|
|
14
|
+
repository = new AsyncStorageRepository();
|
|
15
|
+
(AsyncStorage as any).__clear();
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
describe('Composition', () => {
|
|
19
|
+
it('should use composition pattern', () => {
|
|
20
|
+
expect(repository).toBeInstanceOf(AsyncStorageRepository);
|
|
21
|
+
|
|
22
|
+
// Test that all methods are available
|
|
23
|
+
expect(typeof repository.getItem).toBe('function');
|
|
24
|
+
expect(typeof repository.setItem).toBe('function');
|
|
25
|
+
expect(typeof repository.getString).toBe('function');
|
|
26
|
+
expect(typeof repository.setString).toBe('function');
|
|
27
|
+
expect(typeof repository.removeItem).toBe('function');
|
|
28
|
+
expect(typeof repository.hasItem).toBe('function');
|
|
29
|
+
expect(typeof repository.clearAll).toBe('function');
|
|
30
|
+
expect(typeof repository.getMultiple).toBe('function');
|
|
31
|
+
expect(typeof repository.getAllKeys).toBe('function');
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
describe('Delegation', () => {
|
|
36
|
+
it('should delegate getItem to BaseStorageOperations', async () => {
|
|
37
|
+
const key = 'test-key';
|
|
38
|
+
const defaultValue = 'default';
|
|
39
|
+
const expectedValue = 'test-value';
|
|
40
|
+
|
|
41
|
+
await AsyncStorage.setItem(key, JSON.stringify(expectedValue));
|
|
42
|
+
|
|
43
|
+
const result = await repository.getItem(key, defaultValue);
|
|
44
|
+
|
|
45
|
+
expect(result.success).toBe(true);
|
|
46
|
+
expect(result.data).toBe(expectedValue);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('should delegate setItem to BaseStorageOperations', async () => {
|
|
50
|
+
const key = 'test-key';
|
|
51
|
+
const value = 'test-value';
|
|
52
|
+
|
|
53
|
+
const result = await repository.setItem(key, value);
|
|
54
|
+
|
|
55
|
+
expect(result.success).toBe(true);
|
|
56
|
+
expect(result.data).toBe(value);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('should delegate getString to StringStorageOperations', async () => {
|
|
60
|
+
const key = 'test-key';
|
|
61
|
+
const defaultValue = 'default';
|
|
62
|
+
const expectedValue = 'test-string';
|
|
63
|
+
|
|
64
|
+
await AsyncStorage.setItem(key, expectedValue);
|
|
65
|
+
|
|
66
|
+
const result = await repository.getString(key, defaultValue);
|
|
67
|
+
|
|
68
|
+
expect(result.success).toBe(true);
|
|
69
|
+
expect(result.data).toBe(expectedValue);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it('should delegate setString to StringStorageOperations', async () => {
|
|
73
|
+
const key = 'test-key';
|
|
74
|
+
const value = 'test-string';
|
|
75
|
+
|
|
76
|
+
const result = await repository.setString(key, value);
|
|
77
|
+
|
|
78
|
+
expect(result.success).toBe(true);
|
|
79
|
+
expect(result.data).toBe(value);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it('should delegate removeItem to BaseStorageOperations', async () => {
|
|
83
|
+
const key = 'test-key';
|
|
84
|
+
|
|
85
|
+
await AsyncStorage.setItem(key, 'test-value');
|
|
86
|
+
|
|
87
|
+
const result = await repository.removeItem(key);
|
|
88
|
+
|
|
89
|
+
expect(result.success).toBe(true);
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it('should delegate hasItem to BaseStorageOperations', async () => {
|
|
93
|
+
const key = 'test-key';
|
|
94
|
+
|
|
95
|
+
await AsyncStorage.setItem(key, 'test-value');
|
|
96
|
+
|
|
97
|
+
const exists = await repository.hasItem(key);
|
|
98
|
+
|
|
99
|
+
expect(exists).toBe(true);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it('should delegate clearAll to BaseStorageOperations', async () => {
|
|
103
|
+
await AsyncStorage.setItem('key1', 'value1');
|
|
104
|
+
await AsyncStorage.setItem('key2', 'value2');
|
|
105
|
+
|
|
106
|
+
const result = await repository.clearAll();
|
|
107
|
+
|
|
108
|
+
expect(result.success).toBe(true);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it('should delegate getMultiple to BatchStorageOperations', async () => {
|
|
112
|
+
const keys = ['key1', 'key2'];
|
|
113
|
+
|
|
114
|
+
await AsyncStorage.setItem('key1', 'value1');
|
|
115
|
+
await AsyncStorage.setItem('key2', 'value2');
|
|
116
|
+
|
|
117
|
+
const result = await repository.getMultiple(keys);
|
|
118
|
+
|
|
119
|
+
expect(result.success).toBe(true);
|
|
120
|
+
expect(result.data).toEqual({
|
|
121
|
+
key1: 'value1',
|
|
122
|
+
key2: 'value2'
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
it('should delegate getAllKeys to BatchStorageOperations', async () => {
|
|
127
|
+
await AsyncStorage.setItem('key1', 'value1');
|
|
128
|
+
await AsyncStorage.setItem('key2', 'value2');
|
|
129
|
+
|
|
130
|
+
const result = await repository.getAllKeys();
|
|
131
|
+
|
|
132
|
+
expect(result.success).toBe(true);
|
|
133
|
+
expect(result.data).toEqual(expect.arrayContaining(['key1', 'key2']));
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
describe('Error Handling', () => {
|
|
138
|
+
it('should handle errors from delegated operations', async () => {
|
|
139
|
+
const key = 'test-key';
|
|
140
|
+
|
|
141
|
+
// Mock error in AsyncStorage
|
|
142
|
+
(AsyncStorage.getItem as jest.Mock).mockRejectedValue(new Error('Storage error'));
|
|
143
|
+
|
|
144
|
+
const result = await repository.getItem(key, 'default');
|
|
145
|
+
|
|
146
|
+
expect(result.success).toBe(false);
|
|
147
|
+
expect(result.error).toBeDefined();
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
describe('Type Safety', () => {
|
|
152
|
+
it('should maintain type safety for generic methods', async () => {
|
|
153
|
+
interface TestData {
|
|
154
|
+
id: number;
|
|
155
|
+
name: string;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const key = 'typed-key';
|
|
159
|
+
const data: TestData = { id: 1, name: 'test' };
|
|
160
|
+
|
|
161
|
+
const setResult = await repository.setItem<TestData>(key, data);
|
|
162
|
+
const getResult = await repository.getItem<TestData>(key, { id: 0, name: '' });
|
|
163
|
+
|
|
164
|
+
expect(setResult.success).toBe(true);
|
|
165
|
+
expect(getResult.success).toBe(true);
|
|
166
|
+
expect(getResult.data).toEqual(data);
|
|
167
|
+
});
|
|
168
|
+
});
|
|
169
|
+
});
|