Codable.swift 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. // Copyright 2021 Google LLC
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. import FirebaseRemoteConfig
  15. import FirebaseRemoteConfigSwift
  16. import XCTest
  17. /// String constants used for testing.
  18. private enum Constants {
  19. static let jsonKey = "Recipe"
  20. static let jsonValue = ["recipeName": "PB&J",
  21. "ingredients": ["bread", "peanut butter", "jelly"],
  22. "cookTime": 7] as [String: AnyHashable]
  23. static let nonJsonKey = "notJSON"
  24. static let nonJsonValue = "notJSON"
  25. }
  26. #if compiler(>=5.5) && canImport(_Concurrency)
  27. @available(iOS 15, tvOS 15, macOS 12, watchOS 8, *)
  28. class CodableTests: APITestBase {
  29. var console: RemoteConfigConsole!
  30. override func setUpWithError() throws {
  31. try super.setUpWithError()
  32. let jsonData = try JSONSerialization.data(
  33. withJSONObject: Constants.jsonValue,
  34. options: .prettyPrinted
  35. )
  36. guard let jsonValue = String(data: jsonData, encoding: .ascii) else {
  37. fatalError("Failed to make json Value from jsonData")
  38. }
  39. if APITests.useFakeConfig {
  40. fakeConsole.config = [Constants.jsonKey: jsonValue,
  41. Constants.nonJsonKey: Constants.nonJsonValue]
  42. } else {
  43. console = RemoteConfigConsole()
  44. console.updateRemoteConfigValue(jsonValue, forKey: Constants.jsonKey)
  45. console.updateRemoteConfigValue(Constants.nonJsonKey, forKey: Constants.nonJsonValue)
  46. }
  47. }
  48. override func tearDown() {
  49. super.tearDown()
  50. // If using RemoteConfigConsole, reset remote config values.
  51. if !APITests.useFakeConfig {
  52. console.removeRemoteConfigValue(forKey: Constants.jsonKey)
  53. console.removeRemoteConfigValue(forKey: Constants.nonJsonKey)
  54. }
  55. }
  56. // Contrast this test with the subsequent one to see the value of the Codable API.
  57. func testFetchAndActivateWithoutCodable() async throws {
  58. let status = try await config.fetchAndActivate()
  59. XCTAssertEqual(status, .successFetchedFromRemote)
  60. let dict = try XCTUnwrap(config[Constants.jsonKey].jsonValue as? [String: AnyHashable])
  61. XCTAssertEqual(dict["recipeName"], "PB&J")
  62. XCTAssertEqual(dict["ingredients"], ["bread", "peanut butter", "jelly"])
  63. XCTAssertEqual(dict["cookTime"], 7)
  64. XCTAssertEqual(
  65. config[Constants.jsonKey].jsonValue as! [String: AnyHashable],
  66. Constants.jsonValue
  67. )
  68. }
  69. struct Recipe: Decodable {
  70. var recipeName: String
  71. var ingredients: [String]
  72. var cookTime: Int
  73. }
  74. func testFetchAndActivateWithCodable() async throws {
  75. let status = try await config.fetchAndActivate()
  76. XCTAssertEqual(status, .successFetchedFromRemote)
  77. let recipe = try XCTUnwrap(config[Constants.jsonKey].decoded(asType: Recipe.self))
  78. XCTAssertEqual(recipe.recipeName, "PB&J")
  79. XCTAssertEqual(recipe.ingredients, ["bread", "peanut butter", "jelly"])
  80. XCTAssertEqual(recipe.cookTime, 7)
  81. }
  82. func testFetchAndActivateWithCodableAlternativeAPI() async throws {
  83. let status = try await config.fetchAndActivate()
  84. XCTAssertEqual(status, .successFetchedFromRemote)
  85. let recipe: Recipe = try XCTUnwrap(config[Constants.jsonKey].decoded())
  86. XCTAssertEqual(recipe.recipeName, "PB&J")
  87. XCTAssertEqual(recipe.ingredients, ["bread", "peanut butter", "jelly"])
  88. XCTAssertEqual(recipe.cookTime, 7)
  89. }
  90. func testFetchAndActivateWithCodableBadJson() async throws {
  91. let status = try await config.fetchAndActivate()
  92. XCTAssertEqual(status, .successFetchedFromRemote)
  93. do {
  94. _ = try config[Constants.nonJsonKey].decoded(asType: String?.self)
  95. } catch RemoteConfigCodableError.jsonValueError {
  96. return
  97. }
  98. XCTFail("Failed to catch trying to decode non-JSON key as JSON")
  99. }
  100. struct DataTestDefaults: Encodable {
  101. var bool: Bool
  102. var int: Int32
  103. var long: Int64
  104. var string: String
  105. }
  106. func testSetEncodeableDefaults() throws {
  107. let data = DataTestDefaults(
  108. bool: true,
  109. int: 2,
  110. long: 9_876_543_210,
  111. string: "four"
  112. )
  113. try config.setDefaults(from: data)
  114. let boolValue = try XCTUnwrap(config.defaultValue(forKey: "bool")).numberValue.boolValue
  115. XCTAssertTrue(boolValue)
  116. let intValue = try XCTUnwrap(config.defaultValue(forKey: "int")).numberValue.intValue
  117. XCTAssertEqual(intValue, 2)
  118. let longValue = try XCTUnwrap(config.defaultValue(forKey: "long")).numberValue.int64Value
  119. XCTAssertEqual(longValue, 9_876_543_210)
  120. let stringValue = try XCTUnwrap(config.defaultValue(forKey: "string")).stringValue
  121. XCTAssertEqual(stringValue, "four")
  122. }
  123. }
  124. #endif