@@ -17,11 +17,37 @@ import FirebaseCore
17
17
18
18
import XCTest
19
19
20
+ /// String constants used for testing.
21
+ private enum Constants {
22
+ static let key1 = " Key1 "
23
+ static let jedi = " Jedi "
24
+ static let sith = " Sith_Lord "
25
+ static let value1 = " Value1 "
26
+ static let obiwan = " Obi-Wan "
27
+ static let yoda = " Yoda "
28
+ static let darthSidious = " Darth Sidious "
29
+ }
30
+
20
31
class APITests : APITestBase {
32
+ var console : RemoteConfigConsole !
33
+
21
34
override func setUp( ) {
22
35
super. setUp ( )
23
36
if APITests . useFakeConfig {
24
- fakeConsole. config = [ " Key1 " : " Value1 " ]
37
+ fakeConsole. config = [ Constants . key1: Constants . value1]
38
+ } else {
39
+ console = RemoteConfigConsole ( )
40
+ console. updateRemoteConfigValue ( Constants . obiwan, forKey: Constants . jedi)
41
+ }
42
+ }
43
+
44
+ override func tearDown( ) {
45
+ super. tearDown ( )
46
+
47
+ // If using RemoteConfigConsole, reset remote config values.
48
+ if !APITests. useFakeConfig {
49
+ console. removeRemoteConfigValue ( forKey: Constants . sith)
50
+ console. removeRemoteConfigValue ( forKey: Constants . jedi)
25
51
}
26
52
}
27
53
@@ -34,7 +60,7 @@ class APITests: APITestBase {
34
60
XCTAssertEqual ( status, RemoteConfigFetchStatus . success)
35
61
self . config. activate { _, error in
36
62
XCTAssertNil ( error)
37
- XCTAssertEqual ( self . config [ " Key1 " ] . stringValue, " Value1 " )
63
+ XCTAssertEqual ( self . config [ Constants . key1 ] . stringValue, Constants . value1 )
38
64
expectation. fulfill ( )
39
65
}
40
66
}
@@ -50,7 +76,7 @@ class APITests: APITestBase {
50
76
XCTAssertEqual ( status, RemoteConfigFetchStatus . success)
51
77
self . config. activate { _, error in
52
78
XCTAssertNil ( error)
53
- XCTAssertEqual ( self . config [ " Key1 " ] . stringValue, " Value1 " )
79
+ XCTAssertEqual ( self . config [ Constants . key1 ] . stringValue, Constants . value1 )
54
80
expectation. fulfill ( )
55
81
}
56
82
}
@@ -63,7 +89,7 @@ class APITests: APITestBase {
63
89
if let error = error {
64
90
XCTFail ( " Fetch and Activate Error \( error) " )
65
91
}
66
- XCTAssertEqual ( self . config [ " Key1 " ] . stringValue, " Value1 " )
92
+ XCTAssertEqual ( self . config [ Constants . key1 ] . stringValue, Constants . value1 )
67
93
expectation. fulfill ( )
68
94
}
69
95
waitForExpectations ( )
@@ -82,7 +108,7 @@ class APITests: APITestBase {
82
108
if let error = error {
83
109
print ( " Activate Error \( error) " )
84
110
}
85
- XCTAssertEqual ( self . config [ " Key1 " ] . stringValue, " Value1 " )
111
+ XCTAssertEqual ( self . config [ Constants . key1 ] . stringValue, Constants . value1 )
86
112
expectation. fulfill ( )
87
113
}
88
114
}
@@ -98,7 +124,7 @@ class APITests: APITestBase {
98
124
if let error = error {
99
125
XCTAssertEqual ( ( error as NSError ) . code, RemoteConfigError . internalError. rawValue)
100
126
}
101
- XCTAssertEqual ( self . config [ " Key1 " ] . stringValue, " Value1 " )
127
+ XCTAssertEqual ( self . config [ Constants . key1 ] . stringValue, Constants . value1 )
102
128
expectation2. fulfill ( )
103
129
}
104
130
}
@@ -117,7 +143,7 @@ class APITests: APITestBase {
117
143
self . config. activate { changed, error in
118
144
XCTAssertTrue ( !APITests. useFakeConfig || changed)
119
145
XCTAssertNil ( error)
120
- XCTAssertEqual ( self . config [ " Key1 " ] . stringValue, " Value1 " )
146
+ XCTAssertEqual ( self . config [ Constants . key1 ] . stringValue, Constants . value1 )
121
147
expectation. fulfill ( )
122
148
}
123
149
}
@@ -131,13 +157,161 @@ class APITests: APITestBase {
131
157
self . config. activate { changed, error in
132
158
XCTAssertFalse ( changed)
133
159
XCTAssertNil ( error)
134
- XCTAssertEqual ( self . config [ " Key1 " ] . stringValue, " Value1 " )
160
+ XCTAssertEqual ( self . config [ Constants . key1 ] . stringValue, Constants . value1 )
135
161
expectation2. fulfill ( )
136
162
}
137
163
}
138
164
waitForExpectations ( )
139
165
}
140
166
167
+ func testFetchAndActivateUnchangedConfig( ) throws {
168
+ guard APITests . useFakeConfig == false else { return }
169
+
170
+ let expectation = self . expectation ( description: #function)
171
+
172
+ XCTAssertEqual ( config. settings. minimumFetchInterval, 0 )
173
+
174
+ let serialQueue = DispatchQueue ( label: " \( #function) Queue " )
175
+ let group = DispatchGroup ( )
176
+ group. enter ( )
177
+ serialQueue. async {
178
+ // Represents pre-fetch occuring sometime in past.
179
+ self . config. fetch { status, error in
180
+ XCTAssertNil ( error, " Fetch Error \( error!) " )
181
+ XCTAssertEqual ( status, . success)
182
+ group. leave ( )
183
+ }
184
+ }
185
+
186
+ serialQueue. async {
187
+ group. wait ( )
188
+ group. enter ( )
189
+ // Represents a `fetchAndActivate` being made to pull latest changes from Remote Config.
190
+ self . config. fetchAndActivate { status, error in
191
+ XCTAssertNil ( error, " Fetch & Activate Error \( error!) " )
192
+ // Since no updates to remote config have occurred we use the `.successUsingPreFetchedData`.
193
+ XCTAssertEqual ( status, . successUsingPreFetchedData)
194
+ // The `lastETagUpdateTime` should either be older or the same time as `lastFetchTime`.
195
+ if let lastFetchTime = try ? XCTUnwrap ( self . config. lastFetchTime) {
196
+ XCTAssertLessThanOrEqual ( Double ( self . config. settings. lastETagUpdateTime) ,
197
+ Double ( lastFetchTime. timeIntervalSince1970) )
198
+ } else {
199
+ XCTFail ( " Could not unwrap lastFetchTime. " )
200
+ }
201
+
202
+ expectation. fulfill ( )
203
+ }
204
+ }
205
+
206
+ waitForExpectations ( )
207
+ }
208
+
209
+ // MARK: - RemoteConfigConsole Tests
210
+
211
+ func testFetchConfigThenUpdateConsoleThenFetchAgain( ) {
212
+ guard APITests . useFakeConfig == false else { return }
213
+
214
+ let expectation = self . expectation ( description: #function)
215
+
216
+ config. fetchAndActivate { status, error in
217
+ XCTAssertNil ( error, " Fetch & Activate Error \( error!) " )
218
+
219
+ if let configValue = self . config. configValue ( forKey: Constants . jedi) . stringValue {
220
+ XCTAssertEqual ( configValue, Constants . obiwan)
221
+ } else {
222
+ XCTFail ( " Could not unwrap config value for key: \( Constants . jedi) " )
223
+ }
224
+ expectation. fulfill ( )
225
+ }
226
+ waitForExpectations ( )
227
+
228
+ // Synchronously update the console.
229
+ console. updateRemoteConfigValue ( Constants . yoda, forKey: Constants . jedi)
230
+
231
+ let expectation2 = self . expectation ( description: #function + " 2 " )
232
+ config. fetchAndActivate { status, error in
233
+ XCTAssertNil ( error, " Fetch & Activate Error \( error!) " )
234
+
235
+ if let configValue = self . config. configValue ( forKey: Constants . jedi) . stringValue {
236
+ XCTAssertEqual ( configValue, Constants . yoda)
237
+ } else {
238
+ XCTFail ( " Could not unwrap config value for key: \( Constants . jedi) " )
239
+ }
240
+
241
+ expectation2. fulfill ( )
242
+ }
243
+ waitForExpectations ( )
244
+ }
245
+
246
+ func testFetchConfigThenAddValueOnConsoleThenFetchAgain( ) {
247
+ guard APITests . useFakeConfig == false else { return }
248
+
249
+ // Ensure no Sith Lord has been written to Remote Config yet.
250
+ let expectation = self . expectation ( description: #function)
251
+
252
+ config. fetchAndActivate { status, error in
253
+ XCTAssertNil ( error, " Fetch & Activate Error \( error!) " )
254
+
255
+ XCTAssertTrue ( self . config. configValue ( forKey: Constants . sith) . dataValue. isEmpty)
256
+
257
+ expectation. fulfill ( )
258
+ }
259
+ waitForExpectations ( )
260
+
261
+ // Synchronously update the console
262
+ console. updateRemoteConfigValue ( Constants . darthSidious, forKey: Constants . sith)
263
+
264
+ // Verify the Sith Lord can now be fetched from Remote Config.
265
+ let expectation2 = self . expectation ( description: #function + " 2 " )
266
+
267
+ config. fetchAndActivate { status, error in
268
+ XCTAssertNil ( error, " Fetch & Activate Error \( error!) " )
269
+
270
+ if let configValue = self . config. configValue ( forKey: Constants . sith) . stringValue {
271
+ XCTAssertEqual ( configValue, Constants . darthSidious)
272
+ } else {
273
+ XCTFail ( " Could not unwrap config value for key: \( Constants . sith) " )
274
+ }
275
+
276
+ expectation2. fulfill ( )
277
+ }
278
+ waitForExpectations ( )
279
+ }
280
+
281
+ func testFetchConfigThenDeleteValueOnConsoleThenFetchAgain( ) {
282
+ guard APITests . useFakeConfig == false else { return }
283
+
284
+ let expectation = self . expectation ( description: #function)
285
+
286
+ config. fetchAndActivate { status, error in
287
+ XCTAssertNil ( error, " Fetch & Activate Error \( error!) " )
288
+
289
+ if let configValue = self . config. configValue ( forKey: Constants . jedi) . stringValue {
290
+ XCTAssertEqual ( configValue, Constants . obiwan)
291
+ } else {
292
+ XCTFail ( " Could not unwrap config value for key: \( Constants . jedi) " )
293
+ }
294
+ expectation. fulfill ( )
295
+ }
296
+ waitForExpectations ( )
297
+
298
+ // Synchronously delete value on the console.
299
+ console. removeRemoteConfigValue ( forKey: Constants . jedi)
300
+
301
+ let expectation2 = self . expectation ( description: #function + " 2 " )
302
+ config. fetchAndActivate { status, error in
303
+ XCTAssertNil ( error, " Fetch & Activate Error \( error!) " )
304
+
305
+ XCTAssertTrue ( self . config. configValue ( forKey: Constants . jedi) . dataValue. isEmpty,
306
+ " Remote config should have been deleted. " )
307
+
308
+ expectation2. fulfill ( )
309
+ }
310
+ waitForExpectations ( )
311
+ }
312
+
313
+ // MARK: - Private Helpers
314
+
141
315
private func waitForExpectations( ) {
142
316
let kFIRStorageIntegrationTestTimeout = 10.0
143
317
waitForExpectations ( timeout: kFIRStorageIntegrationTestTimeout,
0 commit comments