Skip to content

Commit 2ceb9d6

Browse files
authored
Remote Config Console API for Testing (#6009)
* Added Remote Config Console API and some tests * Added workflow scripts * Update path in remote config workflow * Update path in remote config workflow 2.0 * Adding print statements to debug * Update generate_access_token.sh * Style and script cleanup * Updated README and script * reordering workflow tests for faster dev * Updated generate_access_token.sh * Removed unneeded part of path * Set env var in GHA rather than script * Fix env var in GHA * Fix env var in GHA 2.0 * Refactored actions into one script * removed back slash * Removed unneeded workflow step * Refactored script and added comments * Minor tweaks * Added new line * well nvm that tweak * Reordered workflow steps back to original order * Only generate access token for ios runs * Fixed readme, organized tests, improved RemoteConfigConsole * Fixed test * reordering tests for dev * Removed README.md * Added local dev mode for script * Reset the ordering of tests to original order * Update README * Update README again * Trailing space * trailing space..again * Cleanup up cloned repo in script * Resolved feedback * Style * Gotta love trailing spaces * Resolved feedback 2.0 * Resolved feedback 3.0 * Changed param type for update APIs * Fixed type in updateRemoteConfigValue method
1 parent e0a0f00 commit 2ceb9d6

File tree

8 files changed

+580
-9
lines changed

8 files changed

+580
-9
lines changed

.github/workflows/remoteconfig.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@ jobs:
3030
- name: Install Secret GoogleService-Info.plist
3131
run: scripts/decrypt_gha_secret.sh scripts/gha-encrypted/RemoteConfigSwiftAPI/GoogleService-Info.plist.gpg \
3232
FirebaseRemoteConfig/Tests/SwiftAPI/GoogleService-Info.plist "$plist_secret"
33+
- name: Generate Access Token for RemoteConfigConsoleAPI in IntegrationTests
34+
if: matrix.target == 'iOS'
35+
run: scripts/generate_access_token.sh "$plist_secret" scripts/gha-encrypted/RemoteConfigSwiftAPI/ServiceAccount.json.gpg
36+
FirebaseRemoteConfig/Tests/SwiftAPI/AccessToken.json
3337
- name: BuildAndUnitTest # can be replaced with pod lib lint with CocoaPods 1.10
3438
run: scripts/third_party/travis/retry.sh scripts/build.sh RemoteConfig ${{ matrix.target }} unit
3539
- name: Fake Console API Tests

FirebaseRemoteConfig.podspec

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,8 @@ app update.
8787
swift_api.pod_target_xcconfig = {
8888
'SWIFT_OBJC_BRIDGING_HEADER' => '$(PODS_TARGET_SRCROOT)/FirebaseRemoteConfig/Tests/FakeUtils/Bridging-Header.h'
8989
}
90-
swift_api.resources = 'FirebaseRemoteConfig/Tests/SwiftAPI/GoogleService-Info.plist'
90+
swift_api.resources = 'FirebaseRemoteConfig/Tests/SwiftAPI/GoogleService-Info.plist',
91+
'FirebaseRemoteConfig/Tests/SwiftAPI/AccessToken.json'
9192
swift_api.dependency 'OCMock'
9293
end
9394

FirebaseRemoteConfig/Tests/SwiftAPI/APITests.swift

Lines changed: 182 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,37 @@ import FirebaseCore
1717

1818
import XCTest
1919

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+
2031
class APITests: APITestBase {
32+
var console: RemoteConfigConsole!
33+
2134
override func setUp() {
2235
super.setUp()
2336
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)
2551
}
2652
}
2753

@@ -34,7 +60,7 @@ class APITests: APITestBase {
3460
XCTAssertEqual(status, RemoteConfigFetchStatus.success)
3561
self.config.activate { _, error in
3662
XCTAssertNil(error)
37-
XCTAssertEqual(self.config["Key1"].stringValue, "Value1")
63+
XCTAssertEqual(self.config[Constants.key1].stringValue, Constants.value1)
3864
expectation.fulfill()
3965
}
4066
}
@@ -50,7 +76,7 @@ class APITests: APITestBase {
5076
XCTAssertEqual(status, RemoteConfigFetchStatus.success)
5177
self.config.activate { _, error in
5278
XCTAssertNil(error)
53-
XCTAssertEqual(self.config["Key1"].stringValue, "Value1")
79+
XCTAssertEqual(self.config[Constants.key1].stringValue, Constants.value1)
5480
expectation.fulfill()
5581
}
5682
}
@@ -63,7 +89,7 @@ class APITests: APITestBase {
6389
if let error = error {
6490
XCTFail("Fetch and Activate Error \(error)")
6591
}
66-
XCTAssertEqual(self.config["Key1"].stringValue, "Value1")
92+
XCTAssertEqual(self.config[Constants.key1].stringValue, Constants.value1)
6793
expectation.fulfill()
6894
}
6995
waitForExpectations()
@@ -82,7 +108,7 @@ class APITests: APITestBase {
82108
if let error = error {
83109
print("Activate Error \(error)")
84110
}
85-
XCTAssertEqual(self.config["Key1"].stringValue, "Value1")
111+
XCTAssertEqual(self.config[Constants.key1].stringValue, Constants.value1)
86112
expectation.fulfill()
87113
}
88114
}
@@ -98,7 +124,7 @@ class APITests: APITestBase {
98124
if let error = error {
99125
XCTAssertEqual((error as NSError).code, RemoteConfigError.internalError.rawValue)
100126
}
101-
XCTAssertEqual(self.config["Key1"].stringValue, "Value1")
127+
XCTAssertEqual(self.config[Constants.key1].stringValue, Constants.value1)
102128
expectation2.fulfill()
103129
}
104130
}
@@ -117,7 +143,7 @@ class APITests: APITestBase {
117143
self.config.activate { changed, error in
118144
XCTAssertTrue(!APITests.useFakeConfig || changed)
119145
XCTAssertNil(error)
120-
XCTAssertEqual(self.config["Key1"].stringValue, "Value1")
146+
XCTAssertEqual(self.config[Constants.key1].stringValue, Constants.value1)
121147
expectation.fulfill()
122148
}
123149
}
@@ -131,13 +157,161 @@ class APITests: APITestBase {
131157
self.config.activate { changed, error in
132158
XCTAssertFalse(changed)
133159
XCTAssertNil(error)
134-
XCTAssertEqual(self.config["Key1"].stringValue, "Value1")
160+
XCTAssertEqual(self.config[Constants.key1].stringValue, Constants.value1)
135161
expectation2.fulfill()
136162
}
137163
}
138164
waitForExpectations()
139165
}
140166

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+
141315
private func waitForExpectations() {
142316
let kFIRStorageIntegrationTestTimeout = 10.0
143317
waitForExpectations(timeout: kFIRStorageIntegrationTestTimeout,
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"token_type":"Bearer",
3+
"expires_in":3599,
4+
"access_token":"Generated by ./scripts/generate_access_token.sh"
5+
}

0 commit comments

Comments
 (0)