ConfigContentTest.swift 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587
  1. // Copyright 2025 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. @testable import FirebaseRemoteConfig
  15. import FirebaseRemoteConfigInterop
  16. import XCTest
  17. class ConfigContentTests: XCTestCase {
  18. var configContent: ConfigContent!
  19. let namespaceGoogleMobilePlatform = "firebase" // Initialize this constant
  20. var namespaceApp1: String!
  21. var namespaceApp2: String!
  22. override func setUp() {
  23. super.setUp()
  24. configContent = ConfigContent(dbManager: ConfigDBManagerFake())
  25. namespaceApp1 = "\(namespaceGoogleMobilePlatform):\(Constants.defaultFirebaseAppName)"
  26. namespaceApp2 = "\(namespaceGoogleMobilePlatform):\(Constants.secondFirebaseAppName)"
  27. }
  28. func testCrashShouldNotHappenWithoutMainBundleID() {
  29. // Given
  30. let bundleID = nil as String?
  31. // When
  32. configContent = ConfigContent(dbManager: ConfigDBManagerFake(), bundleIdentifier: bundleID)
  33. // Then - No crash. Assertion is handled in ConfigContent init now.
  34. }
  35. func testUpdateConfigContentForMultipleApps() {
  36. // Given
  37. // - Update for first namespace.
  38. let config1ToSet: [String: Any] = [
  39. "state": "UPDATE",
  40. "entries": ["key1": "value1", "key2": "value2"],
  41. ]
  42. configContent.updateConfigContent(withResponse: config1ToSet, forNamespace: namespaceApp1)
  43. // - Update for second namespace.
  44. let config2ToSet: [String: Any] = [
  45. "state": "UPDATE",
  46. "entries": ["key11": "value11", "key21": "value21"],
  47. ]
  48. configContent.updateConfigContent(withResponse: config2ToSet, forNamespace: namespaceApp2)
  49. // When
  50. let fetchedConfig1 = configContent.fetchedConfig()
  51. let fetchedConfig2 = configContent.fetchedConfig()
  52. // Then
  53. // - Assertions for first namespace.
  54. XCTAssertEqual(fetchedConfig1[namespaceApp1]?["key1"]?.stringValue, "value1")
  55. XCTAssertEqual(fetchedConfig1[namespaceApp1]?["key2"]?.stringValue, "value2")
  56. // - Assertions for second namespace.
  57. XCTAssertEqual(fetchedConfig2[namespaceApp2]?["key11"]?.stringValue, "value11")
  58. XCTAssertEqual(fetchedConfig2[namespaceApp2]?["key21"]?.stringValue, "value21")
  59. }
  60. func testUpdateConfigContentWithResponse() {
  61. // Given
  62. let configToSet: [String: Any] = [
  63. "state": "UPDATE",
  64. "entries": ["key1": "value1", "key2": "value2"],
  65. ]
  66. configContent.updateConfigContent(
  67. withResponse: configToSet,
  68. forNamespace: namespaceGoogleMobilePlatform
  69. )
  70. // When
  71. let fetchedConfig = configContent.fetchedConfig()
  72. // Then
  73. XCTAssertEqual(fetchedConfig[namespaceGoogleMobilePlatform]?["key1"]?.stringValue, "value1")
  74. XCTAssertEqual(fetchedConfig[namespaceGoogleMobilePlatform]?["key2"]?.stringValue, "value2")
  75. }
  76. func testUpdateConfigContentWithStatusUpdateWithDifferentKeys() {
  77. // Given
  78. configContent.updateConfigContent(
  79. withResponse: ["state": "UPDATE", "entries": ["key1": "value1"]],
  80. forNamespace: namespaceGoogleMobilePlatform
  81. )
  82. configContent.updateConfigContent(
  83. withResponse: ["state": "UPDATE", "entries": ["key2": "value2", "key3": "value3"]],
  84. forNamespace: namespaceGoogleMobilePlatform
  85. )
  86. // When
  87. let fetchedConfig = configContent.fetchedConfig()
  88. // then
  89. XCTAssertNil(fetchedConfig[namespaceGoogleMobilePlatform]?["key1"])
  90. XCTAssertEqual(fetchedConfig[namespaceGoogleMobilePlatform]?["key2"]?.stringValue, "value2")
  91. XCTAssertEqual(fetchedConfig[namespaceGoogleMobilePlatform]?["key3"]?.stringValue, "value3")
  92. }
  93. func testUpdateConfigContentWithStatusUpdateWithDifferentNamespaces() {
  94. // Given
  95. let configToSet1: [String: Any] = ["state": "UPDATE", "entries": ["key1": "value1"]]
  96. let configToSet2: [String: Any] = ["state": "UPDATE", "entries": ["key2": "value2"]]
  97. configContent.updateConfigContent(withResponse: configToSet1, forNamespace: "namespace_1")
  98. configContent.updateConfigContent(withResponse: configToSet2, forNamespace: "namespace_2")
  99. configContent.updateConfigContent(withResponse: configToSet1, forNamespace: "namespace_3")
  100. configContent.updateConfigContent(withResponse: configToSet2, forNamespace: "namespace_4")
  101. // When
  102. let fetchedConfig = configContent.fetchedConfig()
  103. // Then
  104. XCTAssertEqual(fetchedConfig["namespace_1"]?["key1"]?.stringValue, "value1")
  105. XCTAssertEqual(fetchedConfig["namespace_2"]?["key2"]?.stringValue, "value2")
  106. XCTAssertEqual(fetchedConfig["namespace_3"]?["key1"]?.stringValue, "value1")
  107. XCTAssertEqual(fetchedConfig["namespace_4"]?["key2"]?.stringValue, "value2")
  108. }
  109. func skip_testUpdateConfigContentWithStatusNoChange() {
  110. // TODO: Add test case once new eTag based logic is implemented.
  111. }
  112. func skip_testUpdateConfigContentWithRemoveNamespaceStatus() {
  113. // TODO: Add test case once new eTag based logic is implemented.
  114. }
  115. func skip_testUpdateConfigContentWithEmptyConfig() {
  116. // TODO: Add test case once new eTag based logic is implemented.
  117. }
  118. // TODO: Test is broken.
  119. // It has to do with `configConent`'s DB Manager being non-nil.
  120. func testCopyFromDictionaryDoesNotUpdateFetchedConfig() {
  121. configContent.updateConfigContent(
  122. withResponse: ["state": "UPDATE", "entries": ["key1": "value1", "key2": "value2"]],
  123. forNamespace: "dummy_namespace"
  124. )
  125. configContent.copy(
  126. fromDictionary: ["dummy_namespace": ["new_key": "new_value"]],
  127. toSource: .fetched,
  128. forNamespace: "dummy_namespace"
  129. )
  130. XCTAssertEqual(configContent.fetchedConfig()["dummy_namespace"]?.count, 2)
  131. XCTAssertEqual(configContent.activeConfig().count, 0)
  132. XCTAssertEqual(configContent.defaultConfig().count, 0)
  133. }
  134. // func testCopyFromDictionaryUpdatesDefaultConfig() throws {
  135. // let embeddedDictionary = ["default_embedded_key": "default_embedded_Value"]
  136. // let dataValue = try JSONSerialization.data(withJSONObject: embeddedDictionary,
  137. // options: .prettyPrinted)
  138. //
  139. // let now = Date()
  140. // let jsonData = try JSONSerialization.data(withJSONObject: ["key1": "value1"])
  141. // let jsonString = String(data: jsonData, encoding: .utf8)
  142. //
  143. //
  144. // let namespaceToConfig: [String: [String: Any]] = [
  145. // "default_namespace": [
  146. // "new_string_key": "new_string_value",
  147. // "new_number_key": 1234,
  148. // "new_data_key": dataValue,
  149. // "new_date_key": now,
  150. // "new_json_key": jsonString as Any
  151. // ]
  152. // ]
  153. // configContent.copy(fromDictionary: namespaceToConfig, toSource: .default, forNamespace: "default_namespace")
  154. //
  155. // let defaultConfig = configContent.defaultConfig()
  156. // XCTAssertEqual(configContent.fetchedConfig().count, 0)
  157. // XCTAssertEqual(configContent.activeConfig().count, 0)
  158. // XCTAssertNotNil(defaultConfig["default_namespace"])
  159. // XCTAssertEqual(defaultConfig["default_namespace"]?.count, 5)
  160. //
  161. //
  162. // XCTAssertEqual(defaultConfig["default_namespace"]?["new_string_key"]?.stringValue,
  163. // "new_string_value")
  164. // XCTAssertEqual(defaultConfig["default_namespace"]?["new_number_key"]?.numberValue, 1234)
  165. // let sampleJSON = ["key1": "value1"]
  166. // let configJSON = defaultConfig["default_namespace"]?["new_json_key"]?.jsonValue
  167. //
  168. //
  169. // XCTAssertEqual(NSDictionary(dictionary: sampleJSON), configJSON as? NSDictionary)
  170. //
  171. // XCTAssertEqual(defaultConfig["default_namespace"]?["new_data_key"]?.dataValue, dataValue)
  172. //
  173. //
  174. // let dateFormatter = DateFormatter()
  175. // dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
  176. // let strValueForDate = dateFormatter.string(from: now)
  177. //
  178. // XCTAssertEqual(defaultConfig["default_namespace"]?["new_date_key"]?.stringValue,
  179. // strValueForDate)
  180. //
  181. // }
  182. //
  183. //
  184. // func testCopyFromDictionaryUpdatesActiveConfig() throws {
  185. // let embeddedDictionary = ["active_embedded_key": "active_embedded_Value"]
  186. // let dataValue = try JSONSerialization.data(withJSONObject: embeddedDictionary, options:
  187. // .prettyPrinted)
  188. // let rcValue = RemoteConfigValue(data: dataValue, source: .static) //Using .static since
  189. // the source is -1
  190. //
  191. // let namespaceToConfig = ["dummy_namespace": ["new_key": rcValue]]
  192. // configContent.copy(fromDictionary: namespaceToConfig, toSource: .active, forNamespace: "dummy_namespace")
  193. //
  194. // XCTAssertEqual(configContent.activeConfig()["dummy_namespace"]?.count, 1)
  195. // XCTAssertEqual(configContent.fetchedConfig().count, 0)
  196. // XCTAssertEqual(configContent.defaultConfig().count, 0)
  197. // XCTAssertEqual(configContent.activeConfig()["dummy_namespace"]?["new_key"]?.dataValue,
  198. // dataValue)
  199. // }
  200. //
  201. //
  202. //// func testCheckAndWaitForInitialDatabaseLoad() {
  203. //// // This test relies on timing and mocking internal behavior, which is challenging to
  204. /// translate
  205. //// // directly. Re-implement this test to verify the timeout logic in a way that's
  206. /// suitable for your
  207. //// // testing environment and mocking framework.
  208. //// }
  209. //
  210. // func testConfigUpdate_noChange_emptyResponse() {
  211. // let namespace = "test_namespace"
  212. //
  213. // // Populate fetched config
  214. // let fetchResponse = createFetchResponse(config: ["key1": "value1"], p13nMetadata: nil,
  215. // rolloutMetadata: nil)
  216. // configContent.updateConfigContent(withResponse: fetchResponse, forNamespace: namespace)
  217. //
  218. // // Active config same as fetched config
  219. // let value = RemoteConfigValue(data: "value1".data(using: .utf8), source: .remote)
  220. // let namespaceToConfig = [namespace: ["key1": value]]
  221. //
  222. // configContent.copy(fromDictionary: namespaceToConfig, toSource: .active, forNamespace: namespace)
  223. //
  224. //
  225. // let update = configContent.getConfigUpdate(forNamespace: namespace)
  226. // XCTAssertEqual(update?.updatedKeys.count, 0) // No update expected.
  227. // }
  228. //
  229. // func testConfigUpdate_paramAdded_returnsNewKey() {
  230. // let namespace = "test_namespace"
  231. // let newParam = "key2"
  232. //
  233. // // Populate active config
  234. // let value = RemoteConfigValue(data: "value1".data(using: .utf8), source: .remote)
  235. // let namespaceToConfig = [namespace: ["key1": value]]
  236. //
  237. // configContent.copy(fromDictionary: namespaceToConfig, toSource: .active, forNamespace: namespace)
  238. //
  239. // // Fetched response has new param.
  240. // let fetchResponse = createFetchResponse(config: ["key1": "value1", newParam: "value2"],
  241. // p13nMetadata: nil, rolloutMetadata: nil)
  242. // configContent.updateConfigContent(withResponse: fetchResponse, forNamespace: namespace)
  243. //
  244. // let update = configContent.getConfigUpdate(forNamespace: namespace)
  245. //
  246. // XCTAssertEqual(update?.updatedKeys.count, 1)
  247. // XCTAssertTrue(update?.updatedKeys.contains(newParam) ?? false) // Use nil-coalescing and
  248. // optional chaining.
  249. //
  250. //
  251. // }
  252. //
  253. // func testConfigUpdate_paramValueChanged_returnsUpdatedKey() {
  254. // let namespace = "test_namespace"
  255. // let existingParam = "key1"
  256. // let oldValue = "value1"
  257. // let updatedValue = "value2"
  258. //
  259. // // Active config with old value
  260. // let value = RemoteConfigValue(data: oldValue.data(using: .utf8), source: .remote)
  261. //
  262. // configContent.copy(fromDictionary: [namespace: [existingParam: value]], toSource: .active, forNamespace: namespace)
  263. //
  264. // let fetchResponse = createFetchResponse(config: [existingParam: updatedValue],
  265. // p13nMetadata: nil, rolloutMetadata: nil)
  266. // configContent.updateConfigContent(withResponse: fetchResponse, forNamespace: namespace)
  267. //
  268. // let update = configContent.getConfigUpdate(forNamespace: namespace)
  269. //
  270. // XCTAssertEqual(update?.updatedKeys.count, 1)
  271. // XCTAssertTrue(update?.updatedKeys.contains(existingParam) ?? false)
  272. // }
  273. //
  274. //
  275. // func testConfigUpdate_paramDeleted_returnsDeletedKey() {
  276. // let namespace = "test_namespace"
  277. // let existingParam = "key1"
  278. // let newParam = "key2"
  279. // let value1 = "value1"
  280. //
  281. //
  282. // let value = RemoteConfigValue(data: value1.data(using: .utf8), source: .remote)
  283. //
  284. // configContent.copy(fromDictionary: [namespace: [existingParam: value]], toSource: .active, forNamespace: namespace)
  285. //
  286. //
  287. // let fetchResponse = createFetchResponse(config: [newParam: value1], p13nMetadata: nil,
  288. // rolloutMetadata: nil)
  289. // configContent.updateConfigContent(withResponse: fetchResponse, forNamespace: namespace)
  290. //
  291. // let update = configContent.getConfigUpdate(forNamespace: namespace)
  292. //
  293. // XCTAssertEqual(update?.updatedKeys.count, 2) // Should contain both keys
  294. // XCTAssertTrue(update?.updatedKeys.contains(existingParam) ?? false)
  295. // XCTAssertTrue(update?.updatedKeys.contains(newParam) ?? false)
  296. //
  297. // }
  298. //
  299. //
  300. //
  301. //
  302. // func testConfigUpdate_p13nMetadataUpdated_returnsKey() {
  303. // let namespace = "test_namespace"
  304. // let existingParam = "key1"
  305. // let value1 = "value1"
  306. // let oldMetadata = ["arm_index": "1"] //as [String: String] //Swift dictionaries are not
  307. // AnyObject
  308. // let updatedMetadata = ["arm_index": "2"] //as [String: String] //Swift dictionaries are
  309. // not AnyObject
  310. //
  311. // // Populate fetched config
  312. // let fetchResponse = createFetchResponse(config: [existingParam: value1], p13nMetadata:
  313. // [existingParam: oldMetadata], rolloutMetadata: nil)
  314. //
  315. // configContent.updateConfigContent(withResponse: fetchResponse, forNamespace: namespace)
  316. // configContent.activatePersonalization()
  317. //
  318. // let value = RemoteConfigValue(data: value1.data(using: .utf8), source: .remote)
  319. // configContent.copy(fromDictionary: [namespace: [existingParam: value]], toSource: .active, forNamespace: namespace)
  320. //
  321. // var updatedFetchResponse = fetchResponse
  322. // updatedFetchResponse[ConfigConstants.fetchResponseKeyPersonalizationMetadata] =
  323. // [existingParam: updatedMetadata]
  324. // configContent.updateConfigContent(withResponse: updatedFetchResponse, forNamespace: namespace)
  325. //
  326. //
  327. // let update = configContent.getConfigUpdate(forNamespace: namespace)
  328. //
  329. // XCTAssertEqual(update?.updatedKeys.count, 1)
  330. // XCTAssertTrue(update?.updatedKeys.contains(existingParam) ?? false)
  331. //
  332. // }
  333. //
  334. //
  335. //
  336. // func testConfigUpdate_rolloutMetadataUpdated_returnsKey() {
  337. // let namespace = "test_namespace"
  338. // let key1 = "key1"
  339. // let key2 = "key2"
  340. // let value = "value"
  341. // let rolloutId1 = "1"
  342. // let rolloutId2 = "2"
  343. // let variantId1 = "A"
  344. // let variantId2 = "B"
  345. //
  346. // let rolloutMetadata: [[String: Any]] = [[
  347. // ConfigConstants.fetchResponseKeyRolloutID: rolloutId1,
  348. // ConfigConstants.fetchResponseKeyVariantID: variantId1,
  349. // ConfigConstants.fetchResponseKeyAffectedParameterKeys: [key1]
  350. // ]]
  351. //
  352. // let updatedRolloutMetadata: [[String: Any]] = [
  353. // [
  354. // ConfigConstants.fetchResponseKeyRolloutID: rolloutId1,
  355. // ConfigConstants.fetchResponseKeyVariantID: variantId2,
  356. // ConfigConstants.fetchResponseKeyAffectedParameterKeys: [key1]
  357. // ],
  358. // [
  359. // ConfigConstants.fetchResponseKeyRolloutID: rolloutId2,
  360. // ConfigConstants.fetchResponseKeyVariantID: variantId1,
  361. // ConfigConstants.fetchResponseKeyAffectedParameterKeys: [key2]
  362. // ]
  363. // ]
  364. //
  365. // let fetchResponse = createFetchResponse(config: [key1: value], p13nMetadata: nil,
  366. // rolloutMetadata: rolloutMetadata)
  367. // configContent.updateConfigContent(withResponse: fetchResponse, forNamespace: namespace)
  368. // configContent.activateRolloutMetadata( { _ in})
  369. // let rcValue = RemoteConfigValue(data: value.data(using: .utf8), source: .remote)
  370. // configContent.copy(fromDictionary: [namespace: [key1: rcValue]], toSource: .active, forNamespace: namespace)
  371. // var updatedFetchResponse = fetchResponse
  372. // updatedFetchResponse[ConfigConstants.fetchResponseKeyRolloutMetadata] =
  373. // updatedRolloutMetadata
  374. //
  375. // configContent.updateConfigContent(withResponse: updatedFetchResponse, forNamespace: namespace)
  376. //
  377. // let update = configContent.getConfigUpdate(forNamespace: namespace)
  378. // XCTAssertEqual(update?.updatedKeys.count, 2)
  379. // XCTAssertTrue(update?.updatedKeys.contains(key1) ?? false)
  380. // XCTAssertTrue(update?.updatedKeys.contains(key2) ?? false)
  381. //
  382. // }
  383. //
  384. //
  385. // func testConfigUpdate_rolloutMetadataDeleted_returnsKey() {
  386. // let namespace = "test_namespace"
  387. // let key1 = "key1"
  388. // let key2 = "key2"
  389. // let value = "value"
  390. // let rolloutId1 = "1"
  391. // let variantId1 = "A"
  392. //
  393. // let rolloutMetadata = [[
  394. // ConfigConstants.fetchResponseKeyRolloutID: rolloutId1,
  395. // ConfigConstants.fetchResponseKeyVariantID: variantId1,
  396. // ConfigConstants.fetchResponseKeyAffectedParameterKeys: [key1, key2]
  397. // ]]
  398. //
  399. // let updatedRolloutMetadata = [[
  400. // ConfigConstants.fetchResponseKeyRolloutID: rolloutId1,
  401. // ConfigConstants.fetchResponseKeyVariantID: variantId1,
  402. // ConfigConstants.fetchResponseKeyAffectedParameterKeys: [key1]
  403. // ]]
  404. //
  405. // let fetchResponse = createFetchResponse(config: [key1: value, key2: value], p13nMetadata:
  406. // nil, rolloutMetadata: rolloutMetadata)
  407. // configContent.updateConfigContent(withResponse: fetchResponse, forNamespace: namespace)
  408. //
  409. // configContent.activateRolloutMetadata( { _ in})
  410. //
  411. // let rcValue = RemoteConfigValue(data: value.data(using: .utf8), source: .remote)
  412. // configContent.copy(fromDictionary: [namespace: [key1: rcValue, key2: rcValue]], toSource: .active, forNamespace: namespace)
  413. //
  414. // var updatedFetchResponse = fetchResponse
  415. // updatedFetchResponse[ConfigConstants.fetchResponseKeyRolloutMetadata] =
  416. // updatedRolloutMetadata
  417. // configContent.updateConfigContent(withResponse: updatedFetchResponse, forNamespace: namespace)
  418. //
  419. // let update = configContent.getConfigUpdate(forNamespace: namespace)
  420. // XCTAssertEqual(update?.updatedKeys.count, 1)
  421. //
  422. // XCTAssertTrue(update?.updatedKeys.contains(key2) ?? false)
  423. //
  424. // }
  425. //
  426. // func testConfigUpdate_rolloutMetadataDeletedAll_returnsKey() {
  427. // let namespace = "test_namespace"
  428. // let key = "key"
  429. // let value = "value"
  430. // let rolloutId1 = "1"
  431. // let variantId1 = "A"
  432. //
  433. // let rolloutMetadata = [[
  434. // ConfigConstants.fetchResponseKeyRolloutID: rolloutId1,
  435. // ConfigConstants.fetchResponseKeyVariantID: variantId1,
  436. // ConfigConstants.fetchResponseKeyAffectedParameterKeys: [key]
  437. // ]]
  438. //
  439. // let fetchResponse = createFetchResponse(config: [key: value], p13nMetadata: nil,
  440. // rolloutMetadata: rolloutMetadata)
  441. // configContent.updateConfigContent(withResponse: fetchResponse, forNamespace: namespace)
  442. // configContent.activateRolloutMetadata({ _ in })
  443. //
  444. //
  445. // let rcValue = RemoteConfigValue(data: value.data(using: .utf8), source: .remote)
  446. //
  447. // configContent.copy(fromDictionary: [namespace: [key: rcValue]], toSource: .active, forNamespace: namespace)
  448. //
  449. //
  450. // let updatedFetchResponse = createFetchResponse(config: [key: value], p13nMetadata: nil,
  451. // rolloutMetadata: nil)
  452. // configContent.updateConfigContent(withResponse: updatedFetchResponse, forNamespace: namespace)
  453. // let update = configContent.getConfigUpdate(forNamespace: namespace)
  454. // configContent.activateRolloutMetadata({ _ in })
  455. //
  456. // XCTAssertEqual(update?.updatedKeys.count, 1)
  457. // XCTAssertTrue(update?.updatedKeys.contains(key) ?? false)
  458. // XCTAssertTrue(configContent.activeRolloutMetadata().isEmpty)
  459. //
  460. // }
  461. //
  462. //
  463. // func testConfigUpdate_valueSourceChanged_returnsKey() {
  464. // let namespace = "test_namespace"
  465. // let existingParam = "key1"
  466. // let value1 = "value1"
  467. //
  468. // let value = RemoteConfigValue(data: value1.data(using: .utf8), source: .default)
  469. // configContent.copy(fromDictionary: [namespace: [existingParam: value]], toSource: .default, forNamespace: namespace)
  470. //
  471. // let fetchResponse = createFetchResponse(config: [existingParam: value1], p13nMetadata:
  472. // nil, rolloutMetadata: nil)
  473. //
  474. // configContent.updateConfigContent(withResponse: fetchResponse, forNamespace: namespace)
  475. //
  476. // let update = configContent.getConfigUpdate(forNamespace: namespace)
  477. // XCTAssertEqual(update?.updatedKeys.count, 1)
  478. //
  479. // XCTAssertTrue(update?.updatedKeys.contains(existingParam) ?? false)
  480. //
  481. //
  482. // }
  483. //
  484. // // Helper functions (adapt to your set up)
  485. // private func createFetchResponse(config: [String: String]?,
  486. // p13nMetadata: [String: Any]?,
  487. // rolloutMetadata: [[String: Any]]?) -> [String: Any] {
  488. //
  489. // var fetchResponse: [String: Any] = ["state": ConfigConstants.fetchResponseKeyStateUpdate]
  490. // if let config {
  491. // fetchResponse[ConfigConstants.fetchResponseKeyEntries] = config
  492. // }
  493. // if let p13nMetadata {
  494. // fetchResponse[ConfigConstants.fetchResponseKeyPersonalizationMetadata] = p13nMetadata
  495. // }
  496. //
  497. // if let rolloutMetadata {
  498. // fetchResponse[ConfigConstants.fetchResponseKeyRolloutMetadata] = rolloutMetadata
  499. // }
  500. //
  501. // return fetchResponse
  502. // }
  503. }
  504. private class ConfigDBManagerFake: ConfigDBManager {
  505. // var mockExperimentTable: [String: Any] = [:]
  506. //
  507. //
  508. // init(bundle: Bundle) {
  509. // super.init()
  510. // }
  511. //
  512. // override func loadMain(withBundleIdentifier bundleIdentifier: String,
  513. // completionHandler: ((Bool, [String: [String: RemoteConfigValue]]?,
  514. // [String: [String: RemoteConfigValue]]?,
  515. // [String: [String: RemoteConfigValue]]?,
  516. // [String: [[String: Any]]]) -> Void)?) {
  517. // completionHandler?(
  518. // true, [:], [:], [:], [
  519. // ConfigConstants.rolloutTableKeyFetchedMetadata: [],
  520. // ConfigConstants.rolloutTableKeyActiveMetadata: [],
  521. // ]
  522. // )
  523. // }
  524. //
  525. // override func loadExperiment(completionHandler: ((Bool, [String: Any]?) -> Void)?) {
  526. // completionHandler?(true, mockExperimentTable)
  527. // }
  528. //
  529. // override func loadPersonalization(
  530. // completionHandler: ((Bool, [String: Any], [String: Any], [String: Any]?, [String: Any]?)
  531. // -> Void)?
  532. // ) {
  533. // completionHandler?(true, [:], [:])
  534. // }
  535. // override func insertMainTable(withValues values: [Any], fromSource source: DBSource) async ->
  536. // Bool { true }
  537. // override func insertMetadataTable(withValues values: [String: Any]) async -> Bool { true }
  538. // override func deleteRecord(fromMainTableWithNamespace namespace: String,
  539. // bundleIdentifier: String,
  540. // fromSource source: DBSource) { }
  541. // override func insertExperimentTable(withKey key: String, value: Data) async -> Bool { true }
  542. //
  543. // override func deleteExperimentTable(forKey key: String) {}
  544. //
  545. //
  546. //
  547. // override func insertOrUpdateRolloutTable(withKey key: String, value metadataList: [[String :
  548. // Any]]) async -> Bool { true }
  549. //
  550. // override func insertOrUpdateRolloutTable(withKey key: String,
  551. // value metadataList: [[String: Any]],
  552. // completionHandler handler: ((Bool, [String :
  553. // AnyHashable]?) -> Void)?) {
  554. // handler?(true, nil)
  555. // }
  556. // override func insertOrUpdatePersonalizationConfig(_ dataValue: [String : Any], fromSource
  557. // source: DBSource) async {
  558. // }
  559. }