@react-native-firebase/storage 26.3.3 → 27.0.0
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/CHANGELOG.md +21 -0
- package/RNFBStorage.podspec +9 -4
- package/android/build.gradle +6 -0
- package/android/src/main/java/io/invertase/firebase/storage/NativeRNFBTurboStorage.java +24 -4
- package/android/src/main/java/io/invertase/firebase/storage/RNFBStorageTaskRegistry.kt +88 -0
- package/android/src/main/java/io/invertase/firebase/storage/ReactNativeFirebaseStorageTask.java +25 -46
- package/android/src/main/java/io/invertase/firebase/storage/StoragePendingHandle.java +30 -0
- package/android/src/test/java/io/invertase/firebase/storage/RNFBStorageTaskRegistryTest.java +237 -0
- package/android/src/test/java/io/invertase/firebase/storage/ReactNativeFirebaseStorageTaskTest.java +285 -0
- package/dist/module/index.js +1 -2
- package/dist/module/index.js.map +1 -1
- package/dist/module/version.js +1 -1
- package/dist/typescript/lib/index.d.ts +1 -1
- package/dist/typescript/lib/types/internal.d.ts +2 -3
- package/dist/typescript/lib/types/internal.d.ts.map +1 -1
- package/dist/typescript/lib/version.d.ts +1 -1
- package/ios/RNFBStorage/RNFBStorageCommon.m +17 -98
- package/ios/RNFBStorage/RNFBStorageErrorCodeMapper.swift +122 -0
- package/ios/RNFBStorage/RNFBStorageErrorSnapshotDictBuilder.swift +70 -0
- package/ios/RNFBStorage/RNFBStorageHelper.m +38 -13
- package/ios/RNFBStorage/RNFBStorageTaskRegistry.h +42 -0
- package/ios/RNFBStorage/RNFBStorageTaskRegistry.m +87 -0
- package/ios/RNFBStorage/RNFBStorageTaskRegistryStorage.swift +87 -0
- package/ios/RNFBStorage/RNFBStorageTaskStatusMapper.swift +57 -0
- package/ios/RNFBStorage.xcodeproj/project.pbxproj +26 -0
- package/ios/RNFBStorageUnitTests/RNFBStorageErrorCodeMapperFacadeTests.m +91 -0
- package/ios/RNFBStorageUnitTests/RNFBStorageErrorCodeMapperTests.swift +211 -0
- package/ios/RNFBStorageUnitTests/RNFBStorageErrorSnapshotDictBuilderFacadeTests.m +125 -0
- package/ios/RNFBStorageUnitTests/RNFBStorageTaskRegistryStorageTests.swift +154 -0
- package/ios/RNFBStorageUnitTests/RNFBStorageTaskRegistryTests.m +225 -0
- package/ios/RNFBStorageUnitTests/RNFBStorageTaskStatusMapperFacadeTests.m +52 -0
- package/ios/RNFBStorageUnitTests/RNFBStorageTaskStatusMapperTests.swift +58 -0
- package/ios/RNFBStorageUnitTests/RNFBStorageUnitTests.xcodeproj/project.pbxproj +301 -0
- package/ios/RNFBStorageUnitTests/RNFBStorageUnitTests.xcodeproj/xcshareddata/xcschemes/RNFBStorageUnitTests.xcscheme +69 -0
- package/lib/index.ts +1 -1
- package/lib/types/internal.ts +5 -4
- package/lib/version.ts +1 -1
- package/package.json +5 -4
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2016-present Invertase Limited & Contributors
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this library except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
#import <XCTest/XCTest.h>
|
|
18
|
+
|
|
19
|
+
#import "RNFBHandleMapStorage-Swift.inc"
|
|
20
|
+
|
|
21
|
+
@interface RNFBStorageErrorCodeMapperFacadeTests : XCTestCase
|
|
22
|
+
@end
|
|
23
|
+
|
|
24
|
+
@implementation RNFBStorageErrorCodeMapperFacadeTests
|
|
25
|
+
|
|
26
|
+
- (NSError *)errorWithCode:(NSInteger)code description:(NSString *)description {
|
|
27
|
+
return [NSError errorWithDomain:@"FIRStorageErrorDomain"
|
|
28
|
+
code:code
|
|
29
|
+
userInfo:@{NSLocalizedDescriptionKey : description}];
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
- (void)testObjCSelectorNilAndKnownCodes {
|
|
33
|
+
NSArray *nilPair = [RNFBStorageErrorCodeMapper codeAndMessageForError:nil];
|
|
34
|
+
XCTAssertEqualObjects(nilPair[0], @"unknown");
|
|
35
|
+
XCTAssertEqualObjects(nilPair[1], @"An unknown error has occurred.");
|
|
36
|
+
|
|
37
|
+
// FIRStorageErrorCodeObjectNotFound
|
|
38
|
+
NSArray *notFound = [RNFBStorageErrorCodeMapper codeAndMessageForError:[self errorWithCode:-13010
|
|
39
|
+
description:@"x"]];
|
|
40
|
+
XCTAssertEqualObjects(notFound[0], @"object-not-found");
|
|
41
|
+
XCTAssertEqualObjects(notFound[1], @"No object exists at the desired reference.");
|
|
42
|
+
|
|
43
|
+
// FIRStorageErrorCodeCancelled
|
|
44
|
+
NSArray *cancelled = [RNFBStorageErrorCodeMapper
|
|
45
|
+
codeAndMessageForError:[self errorWithCode:-13040 description:@"x"]];
|
|
46
|
+
XCTAssertEqualObjects(cancelled[0], @"cancelled");
|
|
47
|
+
XCTAssertEqualObjects(cancelled[1], @"User cancelled the operation.");
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
- (void)testObjCSelectorUnknownPermissionAndResponseBody {
|
|
51
|
+
// Permission-denied underlying → invalid-device-file-path
|
|
52
|
+
NSError *underlying = [NSError
|
|
53
|
+
errorWithDomain:NSPOSIXErrorDomain
|
|
54
|
+
code:1
|
|
55
|
+
userInfo:@{
|
|
56
|
+
NSLocalizedDescriptionKey : @"The operation couldn’t be completed. Permission denied"
|
|
57
|
+
}];
|
|
58
|
+
NSError *permissionError = [NSError errorWithDomain:@"FIRStorageErrorDomain"
|
|
59
|
+
code:-13000
|
|
60
|
+
userInfo:@{NSUnderlyingErrorKey : underlying}];
|
|
61
|
+
NSArray *permissionPair = [RNFBStorageErrorCodeMapper codeAndMessageForError:permissionError];
|
|
62
|
+
XCTAssertEqualObjects(permissionPair[0], @"invalid-device-file-path");
|
|
63
|
+
XCTAssertEqualObjects(permissionPair[1],
|
|
64
|
+
@"The specified device file path is invalid or is restricted.");
|
|
65
|
+
|
|
66
|
+
// ResponseBody → formatted unknown message
|
|
67
|
+
NSError *bodyError = [NSError errorWithDomain:@"FIRStorageErrorDomain"
|
|
68
|
+
code:-13000
|
|
69
|
+
userInfo:@{@"ResponseBody" : @"backend detail"}];
|
|
70
|
+
NSArray *bodyPair = [RNFBStorageErrorCodeMapper codeAndMessageForError:bodyError];
|
|
71
|
+
XCTAssertEqualObjects(bodyPair[0], @"unknown");
|
|
72
|
+
XCTAssertEqualObjects(bodyPair[1],
|
|
73
|
+
@"An unknown error has occurred. (underlying reason 'backend detail')");
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
- (void)testObjCSelectorDefaultKeepsLocalizedDescription {
|
|
77
|
+
// FIRStorageErrorCodeInvalidArgument
|
|
78
|
+
NSArray *pair = [RNFBStorageErrorCodeMapper
|
|
79
|
+
codeAndMessageForError:[self errorWithCode:-13050 description:@"keep me"]];
|
|
80
|
+
XCTAssertEqualObjects(pair[0], @"unknown");
|
|
81
|
+
XCTAssertEqualObjects(pair[1], @"keep me");
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
- (void)testFacadePublicSelectorUnchanged {
|
|
85
|
+
XCTAssertTrue([RNFBStorageErrorCodeMapper respondsToSelector:@selector(codeAndMessageForError:)]);
|
|
86
|
+
NSMethodSignature *signature =
|
|
87
|
+
[RNFBStorageErrorCodeMapper methodSignatureForSelector:@selector(codeAndMessageForError:)];
|
|
88
|
+
XCTAssertEqual(strcmp(signature.methodReturnType, @encode(id)), 0);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
@end
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2016-present Invertase Limited & Contributors
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this library except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import XCTest
|
|
18
|
+
|
|
19
|
+
final class RNFBStorageErrorCodeMapperTests: XCTestCase {
|
|
20
|
+
/// `FIRStorageErrorCodeUnknown`
|
|
21
|
+
private let codeUnknown = -13000
|
|
22
|
+
/// `FIRStorageErrorCodeObjectNotFound`
|
|
23
|
+
private let codeObjectNotFound = -13010
|
|
24
|
+
/// `FIRStorageErrorCodeBucketNotFound`
|
|
25
|
+
private let codeBucketNotFound = -13011
|
|
26
|
+
/// `FIRStorageErrorCodeProjectNotFound`
|
|
27
|
+
private let codeProjectNotFound = -13012
|
|
28
|
+
/// `FIRStorageErrorCodeQuotaExceeded`
|
|
29
|
+
private let codeQuotaExceeded = -13013
|
|
30
|
+
/// `FIRStorageErrorCodeUnauthenticated`
|
|
31
|
+
private let codeUnauthenticated = -13020
|
|
32
|
+
/// `FIRStorageErrorCodeUnauthorized`
|
|
33
|
+
private let codeUnauthorized = -13021
|
|
34
|
+
/// `FIRStorageErrorCodeRetryLimitExceeded`
|
|
35
|
+
private let codeRetryLimitExceeded = -13030
|
|
36
|
+
/// `FIRStorageErrorCodeNonMatchingChecksum`
|
|
37
|
+
private let codeNonMatchingChecksum = -13031
|
|
38
|
+
/// `FIRStorageErrorCodeDownloadSizeExceeded`
|
|
39
|
+
private let codeDownloadSizeExceeded = -13032
|
|
40
|
+
/// `FIRStorageErrorCodeCancelled`
|
|
41
|
+
private let codeCancelled = -13040
|
|
42
|
+
|
|
43
|
+
private func makeError(code: Int, description: String = "native", userInfo: [String: Any] = [:])
|
|
44
|
+
-> NSError
|
|
45
|
+
{
|
|
46
|
+
var info = userInfo
|
|
47
|
+
if info[NSLocalizedDescriptionKey] == nil {
|
|
48
|
+
info[NSLocalizedDescriptionKey] = description
|
|
49
|
+
}
|
|
50
|
+
return NSError(domain: "FIRStorageErrorDomain", code: code, userInfo: info)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
func testNilErrorReturnsUnknownPair() {
|
|
54
|
+
let result = RNFBStorageErrorCodeMapper.codeAndMessage(for: nil)
|
|
55
|
+
XCTAssertEqual(result, ["unknown", "An unknown error has occurred."])
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
func testObjectNotFound() {
|
|
59
|
+
let result = RNFBStorageErrorCodeMapper.codeAndMessage(
|
|
60
|
+
for: makeError(code: codeObjectNotFound)
|
|
61
|
+
)
|
|
62
|
+
XCTAssertEqual(result, ["object-not-found", "No object exists at the desired reference."])
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
func testBucketNotFound() {
|
|
66
|
+
let result = RNFBStorageErrorCodeMapper.codeAndMessage(
|
|
67
|
+
for: makeError(code: codeBucketNotFound)
|
|
68
|
+
)
|
|
69
|
+
XCTAssertEqual(
|
|
70
|
+
result,
|
|
71
|
+
["bucket-not-found", "No bucket is configured for Firebase Storage."]
|
|
72
|
+
)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
func testProjectNotFound() {
|
|
76
|
+
let result = RNFBStorageErrorCodeMapper.codeAndMessage(
|
|
77
|
+
for: makeError(code: codeProjectNotFound)
|
|
78
|
+
)
|
|
79
|
+
XCTAssertEqual(
|
|
80
|
+
result,
|
|
81
|
+
["project-not-found", "No project is configured for Firebase Storage."]
|
|
82
|
+
)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
func testQuotaExceeded() {
|
|
86
|
+
let result = RNFBStorageErrorCodeMapper.codeAndMessage(
|
|
87
|
+
for: makeError(code: codeQuotaExceeded)
|
|
88
|
+
)
|
|
89
|
+
XCTAssertEqual(
|
|
90
|
+
result,
|
|
91
|
+
["quota-exceeded", "Quota on your Firebase Storage bucket has been exceeded."]
|
|
92
|
+
)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
func testUnauthenticated() {
|
|
96
|
+
let result = RNFBStorageErrorCodeMapper.codeAndMessage(
|
|
97
|
+
for: makeError(code: codeUnauthenticated)
|
|
98
|
+
)
|
|
99
|
+
XCTAssertEqual(
|
|
100
|
+
result,
|
|
101
|
+
["unauthenticated", "User is unauthenticated. Authenticate and try again."]
|
|
102
|
+
)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
func testUnauthorized() {
|
|
106
|
+
let result = RNFBStorageErrorCodeMapper.codeAndMessage(
|
|
107
|
+
for: makeError(code: codeUnauthorized)
|
|
108
|
+
)
|
|
109
|
+
XCTAssertEqual(
|
|
110
|
+
result,
|
|
111
|
+
["unauthorized", "User is not authorized to perform the desired action."]
|
|
112
|
+
)
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
func testRetryLimitExceeded() {
|
|
116
|
+
let result = RNFBStorageErrorCodeMapper.codeAndMessage(
|
|
117
|
+
for: makeError(code: codeRetryLimitExceeded)
|
|
118
|
+
)
|
|
119
|
+
XCTAssertEqual(
|
|
120
|
+
result,
|
|
121
|
+
[
|
|
122
|
+
"retry-limit-exceeded",
|
|
123
|
+
"The maximum time limit on an operation (upload, download, delete, etc.) has been exceeded.",
|
|
124
|
+
]
|
|
125
|
+
)
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
func testNonMatchingChecksum() {
|
|
129
|
+
let result = RNFBStorageErrorCodeMapper.codeAndMessage(
|
|
130
|
+
for: makeError(code: codeNonMatchingChecksum)
|
|
131
|
+
)
|
|
132
|
+
XCTAssertEqual(
|
|
133
|
+
result,
|
|
134
|
+
[
|
|
135
|
+
"non-matching-checksum",
|
|
136
|
+
"File on the client does not match the checksum of the file received by the server.",
|
|
137
|
+
]
|
|
138
|
+
)
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
func testDownloadSizeExceeded() {
|
|
142
|
+
let result = RNFBStorageErrorCodeMapper.codeAndMessage(
|
|
143
|
+
for: makeError(code: codeDownloadSizeExceeded)
|
|
144
|
+
)
|
|
145
|
+
XCTAssertEqual(
|
|
146
|
+
result,
|
|
147
|
+
[
|
|
148
|
+
"download-size-exceeded",
|
|
149
|
+
"Size of the downloaded file exceeds the amount of memory allocated for the download.",
|
|
150
|
+
]
|
|
151
|
+
)
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
func testCancelled() {
|
|
155
|
+
let result = RNFBStorageErrorCodeMapper.codeAndMessage(
|
|
156
|
+
for: makeError(code: codeCancelled)
|
|
157
|
+
)
|
|
158
|
+
XCTAssertEqual(result, ["cancelled", "User cancelled the operation."])
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
func testUnknownWithoutExtras() {
|
|
162
|
+
let result = RNFBStorageErrorCodeMapper.codeAndMessage(
|
|
163
|
+
for: makeError(code: codeUnknown, description: "should be replaced")
|
|
164
|
+
)
|
|
165
|
+
XCTAssertEqual(result, ["unknown", "An unknown error has occurred."])
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
func testUnknownPermissionDeniedOverride() {
|
|
169
|
+
let underlying = NSError(
|
|
170
|
+
domain: NSPOSIXErrorDomain,
|
|
171
|
+
code: 1,
|
|
172
|
+
userInfo: [
|
|
173
|
+
NSLocalizedDescriptionKey: "The operation couldn’t be completed. Permission denied",
|
|
174
|
+
]
|
|
175
|
+
)
|
|
176
|
+
let result = RNFBStorageErrorCodeMapper.codeAndMessage(
|
|
177
|
+
for: makeError(
|
|
178
|
+
code: codeUnknown,
|
|
179
|
+
userInfo: [NSUnderlyingErrorKey: underlying]
|
|
180
|
+
)
|
|
181
|
+
)
|
|
182
|
+
XCTAssertEqual(
|
|
183
|
+
result,
|
|
184
|
+
[
|
|
185
|
+
"invalid-device-file-path",
|
|
186
|
+
"The specified device file path is invalid or is restricted.",
|
|
187
|
+
]
|
|
188
|
+
)
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
func testUnknownResponseBodyMessage() {
|
|
192
|
+
let result = RNFBStorageErrorCodeMapper.codeAndMessage(
|
|
193
|
+
for: makeError(
|
|
194
|
+
code: codeUnknown,
|
|
195
|
+
userInfo: ["ResponseBody": "quota soft limit"]
|
|
196
|
+
)
|
|
197
|
+
)
|
|
198
|
+
XCTAssertEqual(
|
|
199
|
+
result,
|
|
200
|
+
["unknown", "An unknown error has occurred. (underlying reason 'quota soft limit')"]
|
|
201
|
+
)
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
func testDefaultKeepsLocalizedDescription() {
|
|
205
|
+
// FIRStorageErrorCodeInvalidArgument (-13050) and any unmapped code
|
|
206
|
+
let result = RNFBStorageErrorCodeMapper.codeAndMessage(
|
|
207
|
+
for: makeError(code: -13050, description: "Invalid argument from SDK")
|
|
208
|
+
)
|
|
209
|
+
XCTAssertEqual(result, ["unknown", "Invalid argument from SDK"])
|
|
210
|
+
}
|
|
211
|
+
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2016-present Invertase Limited & Contributors
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this library except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
#import <XCTest/XCTest.h>
|
|
18
|
+
|
|
19
|
+
#import "RNFBHandleMapStorage-Swift.inc"
|
|
20
|
+
|
|
21
|
+
@interface RNFBStorageErrorSnapshotDictBuilderFacadeTests : XCTestCase
|
|
22
|
+
@end
|
|
23
|
+
|
|
24
|
+
@implementation RNFBStorageErrorSnapshotDictBuilderFacadeTests
|
|
25
|
+
|
|
26
|
+
- (NSError *)errorWithCode:(NSInteger)code description:(NSString *)description {
|
|
27
|
+
return [NSError errorWithDomain:@"FIRStorageErrorDomain"
|
|
28
|
+
code:code
|
|
29
|
+
userInfo:@{NSLocalizedDescriptionKey : description}];
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
- (void)testFullBuilderSetsCodeMessageAndNativeErrorMessage {
|
|
33
|
+
NSMutableDictionary *snapshot = [@{@"status" : @"error"} mutableCopy];
|
|
34
|
+
// FIRStorageErrorCodeObjectNotFound
|
|
35
|
+
NSError *error = [self errorWithCode:-13010 description:@"native detail"];
|
|
36
|
+
|
|
37
|
+
NSDictionary *result = [RNFBStorageErrorSnapshotDictBuilder buildErrorSnapshotDict:error
|
|
38
|
+
taskSnapshotDict:snapshot];
|
|
39
|
+
|
|
40
|
+
XCTAssertTrue(result == snapshot, @"must mutate in place and return same dict");
|
|
41
|
+
NSDictionary *errorDict = snapshot[@"error"];
|
|
42
|
+
XCTAssertEqualObjects(errorDict[@"code"], @"object-not-found");
|
|
43
|
+
XCTAssertEqualObjects(errorDict[@"message"], @"No object exists at the desired reference.");
|
|
44
|
+
XCTAssertEqualObjects(errorDict[@"nativeErrorMessage"], @"native detail");
|
|
45
|
+
XCTAssertEqual(errorDict.count, 3u);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
- (void)testFromCodeAndMessageSetsCodeAndMessageOnly {
|
|
49
|
+
NSMutableDictionary *snapshot = [@{@"status" : @"error"} mutableCopy];
|
|
50
|
+
NSArray *pair = @[ @"unauthorized", @"User is not authorized to perform the desired action." ];
|
|
51
|
+
|
|
52
|
+
NSDictionary *result =
|
|
53
|
+
[RNFBStorageErrorSnapshotDictBuilder buildErrorSnapshotDictFromCodeAndMessage:pair
|
|
54
|
+
taskSnapshotDict:snapshot];
|
|
55
|
+
|
|
56
|
+
XCTAssertTrue(result == snapshot, @"must mutate in place and return same dict");
|
|
57
|
+
NSDictionary *errorDict = snapshot[@"error"];
|
|
58
|
+
XCTAssertEqualObjects(errorDict[@"code"], @"unauthorized");
|
|
59
|
+
XCTAssertEqualObjects(errorDict[@"message"],
|
|
60
|
+
@"User is not authorized to perform the desired action.");
|
|
61
|
+
XCTAssertNil(errorDict[@"nativeErrorMessage"]);
|
|
62
|
+
XCTAssertEqual(errorDict.count, 2u);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
- (void)testNilSnapshotDictReturnsNil {
|
|
66
|
+
NSError *error = [self errorWithCode:-13040 description:@"cancelled native"];
|
|
67
|
+
NSDictionary *fullNil = [RNFBStorageErrorSnapshotDictBuilder buildErrorSnapshotDict:error
|
|
68
|
+
taskSnapshotDict:nil];
|
|
69
|
+
XCTAssertNil(fullNil);
|
|
70
|
+
|
|
71
|
+
NSArray *pair = @[ @"cancelled", @"User cancelled the operation." ];
|
|
72
|
+
NSDictionary *codeNil =
|
|
73
|
+
[RNFBStorageErrorSnapshotDictBuilder buildErrorSnapshotDictFromCodeAndMessage:pair
|
|
74
|
+
taskSnapshotDict:nil];
|
|
75
|
+
XCTAssertNil(codeNil);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
- (void)testFullBuilderNilErrorUsesNullNativeMessage {
|
|
79
|
+
NSMutableDictionary *snapshot = [NSMutableDictionary dictionary];
|
|
80
|
+
NSDictionary *result = [RNFBStorageErrorSnapshotDictBuilder buildErrorSnapshotDict:nil
|
|
81
|
+
taskSnapshotDict:snapshot];
|
|
82
|
+
XCTAssertTrue(result == snapshot);
|
|
83
|
+
NSDictionary *errorDict = snapshot[@"error"];
|
|
84
|
+
XCTAssertEqualObjects(errorDict[@"code"], @"unknown");
|
|
85
|
+
XCTAssertEqualObjects(errorDict[@"message"], @"An unknown error has occurred.");
|
|
86
|
+
XCTAssertEqualObjects(errorDict[@"nativeErrorMessage"], [NSNull null]);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
- (void)testEmptySnapshotDictMutatedInPlace {
|
|
90
|
+
NSMutableDictionary *empty = [NSMutableDictionary dictionary];
|
|
91
|
+
NSError *error = [self errorWithCode:-13040 description:@"cancelled native"];
|
|
92
|
+
|
|
93
|
+
NSDictionary *fullResult = [RNFBStorageErrorSnapshotDictBuilder buildErrorSnapshotDict:error
|
|
94
|
+
taskSnapshotDict:empty];
|
|
95
|
+
XCTAssertTrue(fullResult == empty);
|
|
96
|
+
XCTAssertEqualObjects(empty[@"error"][@"code"], @"cancelled");
|
|
97
|
+
XCTAssertEqualObjects(empty[@"error"][@"nativeErrorMessage"], @"cancelled native");
|
|
98
|
+
|
|
99
|
+
NSMutableDictionary *empty2 = [NSMutableDictionary dictionary];
|
|
100
|
+
NSArray *pair = @[ @"unknown", @"An unknown error has occurred." ];
|
|
101
|
+
NSDictionary *codeResult =
|
|
102
|
+
[RNFBStorageErrorSnapshotDictBuilder buildErrorSnapshotDictFromCodeAndMessage:pair
|
|
103
|
+
taskSnapshotDict:empty2];
|
|
104
|
+
XCTAssertTrue(codeResult == empty2);
|
|
105
|
+
XCTAssertEqualObjects(empty2[@"error"][@"code"], @"unknown");
|
|
106
|
+
XCTAssertNil(empty2[@"error"][@"nativeErrorMessage"]);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
- (void)testFacadePublicSelectorsUnchanged {
|
|
110
|
+
SEL fullSel = @selector(buildErrorSnapshotDict:taskSnapshotDict:);
|
|
111
|
+
SEL fromCodeSel = @selector(buildErrorSnapshotDictFromCodeAndMessage:taskSnapshotDict:);
|
|
112
|
+
|
|
113
|
+
XCTAssertTrue([RNFBStorageErrorSnapshotDictBuilder respondsToSelector:fullSel]);
|
|
114
|
+
XCTAssertTrue([RNFBStorageErrorSnapshotDictBuilder respondsToSelector:fromCodeSel]);
|
|
115
|
+
|
|
116
|
+
NSMethodSignature *fullSig =
|
|
117
|
+
[RNFBStorageErrorSnapshotDictBuilder methodSignatureForSelector:fullSel];
|
|
118
|
+
XCTAssertEqual(strcmp(fullSig.methodReturnType, @encode(id)), 0);
|
|
119
|
+
|
|
120
|
+
NSMethodSignature *codeSig =
|
|
121
|
+
[RNFBStorageErrorSnapshotDictBuilder methodSignatureForSelector:fromCodeSel];
|
|
122
|
+
XCTAssertEqual(strcmp(codeSig.methodReturnType, @encode(id)), 0);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
@end
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2016-present Invertase Limited & Contributors
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this library except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import XCTest
|
|
18
|
+
|
|
19
|
+
private final class FakeCancellableHandle: NSObject {
|
|
20
|
+
private(set) var cancelCount = 0
|
|
21
|
+
var onCancel: (() -> Void)?
|
|
22
|
+
|
|
23
|
+
@objc func cancel() {
|
|
24
|
+
cancelCount += 1
|
|
25
|
+
onCancel?()
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
final class RNFBStorageTaskRegistryStorageTests: XCTestCase {
|
|
30
|
+
private var storage: RNFBStorageTaskRegistryStorage!
|
|
31
|
+
|
|
32
|
+
override func setUp() {
|
|
33
|
+
super.setUp()
|
|
34
|
+
storage = RNFBStorageTaskRegistryStorage()
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
func testPutIfAbsent_storesWhenFree() {
|
|
38
|
+
let handle = FakeCancellableHandle()
|
|
39
|
+
XCTAssertTrue(storage.putIfAbsent(1 as NSNumber, value: handle))
|
|
40
|
+
XCTAssertTrue(storage.get(1 as NSNumber) === handle)
|
|
41
|
+
XCTAssertEqual(handle.cancelCount, 0)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
func testPutIfAbsent_collision_keepsExistingWithoutCancel() {
|
|
45
|
+
let first = FakeCancellableHandle()
|
|
46
|
+
let second = FakeCancellableHandle()
|
|
47
|
+
XCTAssertTrue(storage.putIfAbsent(1 as NSNumber, value: first))
|
|
48
|
+
XCTAssertFalse(storage.putIfAbsent(1 as NSNumber, value: second))
|
|
49
|
+
XCTAssertTrue(storage.get(1 as NSNumber) === first)
|
|
50
|
+
XCTAssertEqual(first.cancelCount, 0)
|
|
51
|
+
XCTAssertEqual(second.cancelCount, 0)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
func testPutOrDiscard_storesWhenFree() {
|
|
55
|
+
let handle = FakeCancellableHandle()
|
|
56
|
+
XCTAssertTrue(storage.putOrDiscard(1 as NSNumber, value: handle))
|
|
57
|
+
XCTAssertTrue(storage.get(1 as NSNumber) === handle)
|
|
58
|
+
XCTAssertEqual(handle.cancelCount, 0)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
func testPutOrDiscard_collision_cancelsIncoming() {
|
|
62
|
+
let first = FakeCancellableHandle()
|
|
63
|
+
let duplicate = FakeCancellableHandle()
|
|
64
|
+
XCTAssertTrue(storage.putOrDiscard(1 as NSNumber, value: first))
|
|
65
|
+
XCTAssertFalse(storage.putOrDiscard(1 as NSNumber, value: duplicate))
|
|
66
|
+
XCTAssertEqual(duplicate.cancelCount, 1)
|
|
67
|
+
XCTAssertEqual(first.cancelCount, 0)
|
|
68
|
+
XCTAssertTrue(storage.get(1 as NSNumber) === first)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
func testGet_whenFree_isNil() {
|
|
72
|
+
XCTAssertNil(storage.get(99 as NSNumber))
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
func testTake_removesAndReturnsWithoutCallingCancel() {
|
|
76
|
+
let handle = FakeCancellableHandle()
|
|
77
|
+
XCTAssertTrue(storage.putIfAbsent(1 as NSNumber, value: handle))
|
|
78
|
+
XCTAssertTrue(storage.take(1 as NSNumber) === handle)
|
|
79
|
+
XCTAssertNil(storage.get(1 as NSNumber))
|
|
80
|
+
XCTAssertEqual(handle.cancelCount, 0)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
func testTake_whenFree_isNil() {
|
|
84
|
+
XCTAssertNil(storage.take(99 as NSNumber))
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
func testTakeIf_whenIdentityMatches_takes() {
|
|
88
|
+
let handle = FakeCancellableHandle()
|
|
89
|
+
XCTAssertTrue(storage.putIfAbsent(1 as NSNumber, value: handle))
|
|
90
|
+
XCTAssertTrue(storage.takeIf(1 as NSNumber, when: { $0 === handle }) === handle)
|
|
91
|
+
XCTAssertNil(storage.get(1 as NSNumber))
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
func testTakeIf_whenIdentityMismatch_leavesMapping() {
|
|
95
|
+
let mapped = FakeCancellableHandle()
|
|
96
|
+
let other = FakeCancellableHandle()
|
|
97
|
+
XCTAssertTrue(storage.putIfAbsent(1 as NSNumber, value: mapped))
|
|
98
|
+
XCTAssertNil(storage.takeIf(1 as NSNumber, when: { $0 === other }))
|
|
99
|
+
XCTAssertTrue(storage.get(1 as NSNumber) === mapped)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
func testTakeAndCancel_cancelsAndRemoves() {
|
|
103
|
+
let handle = FakeCancellableHandle()
|
|
104
|
+
XCTAssertTrue(storage.putIfAbsent(1 as NSNumber, value: handle))
|
|
105
|
+
XCTAssertTrue(storage.takeAndCancel(1 as NSNumber))
|
|
106
|
+
XCTAssertEqual(handle.cancelCount, 1)
|
|
107
|
+
XCTAssertNil(storage.get(1 as NSNumber))
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
func testTakeAndCancel_missingKey_returnsFalse() {
|
|
111
|
+
XCTAssertFalse(storage.takeAndCancel(99 as NSNumber))
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
func testTakeAndCancel_replacementDuringCancel_leavesReplacement() {
|
|
115
|
+
let original = FakeCancellableHandle()
|
|
116
|
+
let replacement = FakeCancellableHandle()
|
|
117
|
+
original.onCancel = { [weak self] in
|
|
118
|
+
guard let self = self else { return }
|
|
119
|
+
_ = self.storage.take(11 as NSNumber)
|
|
120
|
+
_ = self.storage.putIfAbsent(11 as NSNumber, value: replacement)
|
|
121
|
+
}
|
|
122
|
+
XCTAssertTrue(storage.putIfAbsent(11 as NSNumber, value: original))
|
|
123
|
+
XCTAssertTrue(storage.takeAndCancel(11 as NSNumber))
|
|
124
|
+
XCTAssertEqual(original.cancelCount, 1)
|
|
125
|
+
XCTAssertTrue(storage.get(11 as NSNumber) === replacement)
|
|
126
|
+
XCTAssertEqual(replacement.cancelCount, 0)
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
func testTakeAndCancel_objectWithoutCancel_doesNotCrash() {
|
|
130
|
+
let plain = NSObject()
|
|
131
|
+
XCTAssertTrue(storage.putIfAbsent(1 as NSNumber, value: plain))
|
|
132
|
+
XCTAssertTrue(storage.takeAndCancel(1 as NSNumber))
|
|
133
|
+
XCTAssertNil(storage.get(1 as NSNumber))
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
func testCancelAll_cancelsSnapshotAndLeavesEmpty() {
|
|
137
|
+
let a = FakeCancellableHandle()
|
|
138
|
+
let b = FakeCancellableHandle()
|
|
139
|
+
XCTAssertTrue(storage.putIfAbsent(1 as NSNumber, value: a))
|
|
140
|
+
XCTAssertTrue(storage.putIfAbsent(2 as NSNumber, value: b))
|
|
141
|
+
storage.cancelAll()
|
|
142
|
+
XCTAssertEqual(a.cancelCount, 1)
|
|
143
|
+
XCTAssertEqual(b.cancelCount, 1)
|
|
144
|
+
XCTAssertNil(storage.get(1 as NSNumber))
|
|
145
|
+
XCTAssertNil(storage.get(2 as NSNumber))
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
func testCancelAll_objectWithoutCancel_doesNotCrash() {
|
|
149
|
+
let plain = NSObject()
|
|
150
|
+
XCTAssertTrue(storage.putIfAbsent(1 as NSNumber, value: plain))
|
|
151
|
+
storage.cancelAll()
|
|
152
|
+
XCTAssertNil(storage.get(1 as NSNumber))
|
|
153
|
+
}
|
|
154
|
+
}
|