RemoteConfigComponentOrigTest.swift 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  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. import FirebaseCore
  18. import FirebaseCoreExtension
  19. class RemoteConfigComponentTests: XCTestCase {
  20. var app: FirebaseApp?
  21. override func tearDown() {
  22. // Clear out any apps that were called with `configure`.
  23. FirebaseApp.resetApps()
  24. RemoteConfigComponent.clearAllComponentInstances()
  25. super.tearDown()
  26. }
  27. func testRemoteConfigInstanceCreationAndCaching() {
  28. // Create the provider to vend Remote Config instances.
  29. let provider = providerForTest()
  30. // Create a Remote Config instance from the provider.
  31. let sharedNamespace = "some_namespace"
  32. let config = provider.remoteConfig(forNamespace: sharedNamespace)
  33. XCTAssertNotNil(config)
  34. // Fetch an instance with the same namespace - should be the same instance.
  35. let sameConfig = provider.remoteConfig(forNamespace: sharedNamespace)
  36. XCTAssertNotNil(sameConfig)
  37. XCTAssertEqual(config, sameConfig)
  38. }
  39. func testSeparateInstancesForDifferentNamespaces() {
  40. // Create the provider to vend Remote Config instances.
  41. let provider = providerForTest()
  42. // Create a Remote Config instance from the provider.
  43. let config = provider.remoteConfig(forNamespace: "namespace1")
  44. XCTAssertNotNil(config)
  45. // Fetch another instance with a different namespace.
  46. let config2 = provider.remoteConfig(forNamespace: "namespace2")
  47. XCTAssertNotNil(config2)
  48. XCTAssertNotEqual(config, config2)
  49. }
  50. func testSeparateInstancesForDifferentApps() throws {
  51. let provider = providerForTest()
  52. // Create a Remote Config instance from the provider.
  53. let sharedNamespace = "some_namespace"
  54. let config = provider.remoteConfig(forNamespace: sharedNamespace)
  55. XCTAssertNotNil(config)
  56. // Use a new app and new povider, ensure the instances with the same
  57. // namespace are different.
  58. let secondAppName = try XCTUnwrap(provider.app?.name.appending("2"))
  59. let secondApp = FirebaseApp(instanceWithName: secondAppName, options: fakeOptions())
  60. let separateProvider = RemoteConfigComponent(app: secondApp)
  61. let separateConfig = separateProvider.remoteConfig(forNamespace: sharedNamespace)
  62. XCTAssertNotNil(separateConfig)
  63. XCTAssertNotEqual(config, separateConfig)
  64. }
  65. func testInitialization() {
  66. // Explicitly instantiate the component here in case the `providerForTest`
  67. // ever changes to mock something.
  68. let appName = generatedTestAppName()
  69. let app = FirebaseApp(instanceWithName: appName, options: fakeOptions())
  70. let provider = RemoteConfigComponent(app: app)
  71. XCTAssertNotNil(provider)
  72. XCTAssertNotNil(provider.app)
  73. }
  74. func testRegistersAsLibrary() throws {
  75. // Now component has two register, one is provider and another one is Interop
  76. XCTAssertEqual(RemoteConfigComponent.componentsToRegister().count, 2)
  77. // Configure a test app to fetch instances of provider and interop
  78. let appName = generatedTestAppName()
  79. FirebaseApp.configure(name: appName, options: fakeOptions())
  80. let app = try XCTUnwrap(FirebaseApp.app(name: appName))
  81. // Attempt to fetch the component and verify it's a valid instance.
  82. let provider = app.container.instance(for: RemoteConfigProvider.self) as AnyObject
  83. let interop = app.container.instance(for: RemoteConfigInterop.self) as AnyObject
  84. XCTAssertNotNil(provider)
  85. XCTAssertNotNil(interop)
  86. // Ensure that the instance that comes from the container is cached.
  87. let sameProvider = app.container.instance(for: RemoteConfigProvider.self) as AnyObject
  88. let sameInterop = app.container.instance(for: RemoteConfigInterop.self) as AnyObject
  89. XCTAssertNotNil(sameProvider)
  90. XCTAssertNotNil(sameInterop)
  91. XCTAssertTrue(provider === sameProvider)
  92. XCTAssertTrue(interop === sameInterop)
  93. XCTAssertTrue(provider === interop)
  94. }
  95. func testTwoAppsCreateTwoComponents() throws {
  96. let appName = generatedTestAppName()
  97. FirebaseApp.configure(name: appName, options: fakeOptions())
  98. let app = try XCTUnwrap(FirebaseApp.app(name: appName))
  99. FirebaseApp.configure(options: fakeOptions())
  100. let defaultApp = try XCTUnwrap(FirebaseApp.app())
  101. XCTAssertNotEqual(app, defaultApp)
  102. let provider = app.container.instance(for: RemoteConfigProvider.self) as AnyObject
  103. let interop = app.container.instance(for: RemoteConfigInterop.self) as AnyObject
  104. let defaultProvider = defaultApp.container.instance(for: RemoteConfigProvider.self) as AnyObject
  105. let defaultAppInterop = defaultApp.container
  106. .instance(for: RemoteConfigInterop.self) as AnyObject
  107. XCTAssertTrue(provider === interop)
  108. XCTAssertTrue(defaultProvider === defaultAppInterop)
  109. // Check two apps get their own component to register
  110. XCTAssertFalse(interop === defaultAppInterop)
  111. }
  112. // TODO: Consider either using the shared exception catcher or removing
  113. // exception from implementation (preferred).
  114. func testThrowsWithEmptyGoogleAppID() {
  115. let options = fakeOptions()
  116. options.googleAppID = ""
  117. // Create the provider to vend Remote Config instances.
  118. let appName = generatedTestAppName()
  119. let app = FirebaseApp(instanceWithName: appName, options: options)
  120. /* component */ _ = RemoteConfigComponent(app: app)
  121. // Creating a Remote Config instance should fail since the projectID is empty.
  122. // XCTAssertThrowsError(component.remoteConfig(forNamespace: "some_namespace"))
  123. }
  124. // TODO: Consider either using the shared exception catcher or removing
  125. // exception from implementation (preferred).
  126. func testThrowsWithNilGCMSenderID() {
  127. let options = fakeOptions()
  128. options.gcmSenderID = ""
  129. // Create the provider to vend Remote Config instances.
  130. let appName = generatedTestAppName()
  131. let app = FirebaseApp(instanceWithName: appName, options: options)
  132. /* component */ _ = RemoteConfigComponent(app: app)
  133. // Creating a Remote Config instance should fail since the projectID is empty.
  134. // XCTAssertThrowsError(component.remoteConfig(forNamespace: "some_namespace"))
  135. }
  136. // TODO: Consider either using the shared exception catcher or removing
  137. // exception from implementation (preferred).
  138. func testThrowsWithEmptyProjectID() {
  139. let options = fakeOptions()
  140. options.projectID = ""
  141. // Create the provider to vend Remote Config instances.
  142. let appName = generatedTestAppName()
  143. let app = FirebaseApp(instanceWithName: appName, options: options)
  144. /* component */ _ = RemoteConfigComponent(app: app)
  145. // Creating a Remote Config instance should fail since the projectID is empty.
  146. // XCTAssertThrowsError(component.remoteConfig(forNamespace: "some_namespace"))
  147. }
  148. // TODO: Consider either using the shared exception catcher or removing
  149. // exception from implementation (preferred).
  150. func testThrowsWithNilProjectID() {
  151. let options = fakeOptions()
  152. options.projectID = nil
  153. // Create the provider to vend Remote Config instances.
  154. let appName = generatedTestAppName()
  155. let app = FirebaseApp(instanceWithName: appName, options: options)
  156. /* component */ _ = RemoteConfigComponent(app: app)
  157. // Creating a Remote Config instance should fail since the projectID is empty.
  158. // XCTAssertThrowsError(component.remoteConfig(forNamespace: "some_namespace"))
  159. }
  160. // MARK: - Helpers
  161. // Helper function to create fake options
  162. func fakeOptions() -> FirebaseOptions {
  163. let options = FirebaseOptions(googleAppID: "1:123:ios:123abc",
  164. gcmSenderID: "correct_gcm_sender_id")
  165. options.apiKey = "AIzaSy-ApiKeyWithValidFormat_0123456789"
  166. options.projectID = "project-id"
  167. return options
  168. }
  169. private func generatedTestAppName() -> String {
  170. TestUtilities.generatedTestAppName(for: name)
  171. }
  172. private func providerForTest() -> RemoteConfigComponent {
  173. // Create the provider to vend Remote Config instances.
  174. let appName = generatedTestAppName()
  175. let options = fakeOptions().copy() as! FirebaseOptions
  176. // The app is weakly retained by `RemoteConfigComponent` so strongly
  177. // retain it by the class instance to keep it from deinitializing.
  178. app = FirebaseApp(instanceWithName: appName, options: options)
  179. let provider = RemoteConfigComponent(app: app!)
  180. XCTAssertNotNil(provider)
  181. XCTAssertFalse(provider.app?.options.googleAppID.isEmpty ?? true)
  182. XCTAssertFalse(provider.app?.options.gcmSenderID.isEmpty ?? true)
  183. return provider
  184. }
  185. }