ContextProviderTests.swift 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347
  1. // Copyright 2022 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 Foundation
  15. import FirebaseCore
  16. @testable import FirebaseFunctions
  17. import FirebaseAppCheckInterop
  18. import FirebaseAuthInterop
  19. import FirebaseMessagingInterop
  20. import SharedTestUtilities
  21. import XCTest
  22. class ContextProviderTests: XCTestCase {
  23. let appCheckFake = FIRAppCheckFake()
  24. let appCheckTokenError = FIRAppCheckTokenResultFake(token: "dummy token",
  25. error: NSError(
  26. domain: "testAppCheckError",
  27. code: -1,
  28. userInfo: nil
  29. ))
  30. let appCheckLimitedUseTokenError = FIRAppCheckTokenResultFake(token: "limited use token",
  31. error: NSError(
  32. domain: "testAppCheckError",
  33. code: -1,
  34. userInfo: nil
  35. ))
  36. let appCheckTokenSuccess = FIRAppCheckTokenResultFake(token: "valid_token", error: nil)
  37. let messagingFake = FIRMessagingInteropFake()
  38. func testAsyncContextWithAuth() async throws {
  39. let auth = FIRAuthInteropFake(token: "token", userID: "userID", error: nil)
  40. let provider = FunctionsContextProvider(auth: auth, messaging: messagingFake, appCheck: nil)
  41. let context = try await provider.context(options: nil)
  42. XCTAssertNotNil(context)
  43. XCTAssertEqual(context.authToken, "token")
  44. XCTAssertEqual(context.fcmToken, messagingFake.fcmToken)
  45. }
  46. func testContextWithAuth() {
  47. let auth = FIRAuthInteropFake(token: "token", userID: "userID", error: nil)
  48. let provider = FunctionsContextProvider(auth: auth, messaging: messagingFake, appCheck: nil)
  49. let expectation = expectation(description: "Context should have auth keys.")
  50. provider.getContext { context, error in
  51. XCTAssertNotNil(context)
  52. XCTAssertEqual(context.authToken, "token")
  53. XCTAssertEqual(context.fcmToken, self.messagingFake.fcmToken)
  54. XCTAssertNil(error)
  55. expectation.fulfill()
  56. }
  57. waitForExpectations(timeout: 0.1)
  58. }
  59. func testAsyncContextWithAuthError() async {
  60. let authError = NSError(domain: "com.functions.tests", code: 4, userInfo: nil)
  61. let auth = FIRAuthInteropFake(token: nil, userID: "userID", error: authError)
  62. let provider = FunctionsContextProvider(auth: auth, messaging: messagingFake, appCheck: nil)
  63. do {
  64. _ = try await provider.context(options: nil)
  65. XCTFail("Expected an error")
  66. } catch {
  67. XCTAssertEqual(error as NSError, authError)
  68. }
  69. }
  70. func testContextWithAuthError() {
  71. let authError = NSError(domain: "com.functions.tests", code: 4, userInfo: nil)
  72. let auth = FIRAuthInteropFake(token: nil, userID: "userID", error: authError)
  73. let provider = FunctionsContextProvider(auth: auth, messaging: messagingFake, appCheck: nil)
  74. let expectation = expectation(description: "Completion handler should fail with Auth error.")
  75. provider.getContext { context, error in
  76. XCTAssertNotNil(context)
  77. XCTAssertNil(context.authToken)
  78. XCTAssertEqual(error as NSError?, authError)
  79. expectation.fulfill()
  80. }
  81. waitForExpectations(timeout: 0.1)
  82. }
  83. func testAsyncContextWithoutAuth() async throws {
  84. let provider = FunctionsContextProvider(auth: nil, messaging: nil, appCheck: nil)
  85. let context = try await provider.context(options: nil)
  86. XCTAssertNil(context.authToken)
  87. XCTAssertNil(context.fcmToken)
  88. }
  89. func testContextWithoutAuth() {
  90. let provider = FunctionsContextProvider(auth: nil, messaging: nil, appCheck: nil)
  91. let expectation = expectation(description: "Completion handler should succeed without Auth.")
  92. provider.getContext { context, error in
  93. XCTAssertNotNil(context)
  94. XCTAssertNil(error)
  95. XCTAssertNil(context.authToken)
  96. XCTAssertNil(context.fcmToken)
  97. expectation.fulfill()
  98. }
  99. waitForExpectations(timeout: 0.1)
  100. }
  101. func testAsyncContextWithAppCheckOnlySuccess() async throws {
  102. appCheckFake.tokenResult = appCheckTokenSuccess
  103. let provider = FunctionsContextProvider(auth: nil, messaging: nil, appCheck: appCheckFake)
  104. let context = try await provider.context(options: nil)
  105. XCTAssertNil(context.authToken)
  106. XCTAssertNil(context.fcmToken)
  107. XCTAssertEqual(context.appCheckToken, appCheckTokenSuccess.token)
  108. }
  109. func testContextWithAppCheckOnlySuccess() {
  110. appCheckFake.tokenResult = appCheckTokenSuccess
  111. let provider = FunctionsContextProvider(auth: nil, messaging: nil, appCheck: appCheckFake)
  112. let expectation = expectation(description: "Verify app check.")
  113. provider.getContext { context, error in
  114. XCTAssertNotNil(context)
  115. XCTAssertNil(error)
  116. XCTAssertNil(context.authToken)
  117. XCTAssertNil(context.fcmToken)
  118. XCTAssertEqual(context.appCheckToken, self.appCheckTokenSuccess.token)
  119. expectation.fulfill()
  120. }
  121. waitForExpectations(timeout: 0.1)
  122. }
  123. func testAsyncContextWithAppCheckOnlyError() async throws {
  124. appCheckFake.tokenResult = appCheckTokenError
  125. let provider = FunctionsContextProvider(auth: nil, messaging: nil, appCheck: appCheckFake)
  126. let context = try await provider.context(options: nil)
  127. XCTAssertNil(context.authToken)
  128. XCTAssertNil(context.fcmToken)
  129. // Expect placeholder token in the case of App Check error.
  130. XCTAssertEqual(context.appCheckToken, appCheckFake.tokenResult.token)
  131. }
  132. func testAsyncContextWithAppCheckOnlyError_LimitedUseToken() async throws {
  133. appCheckFake.limitedUseTokenResult = appCheckLimitedUseTokenError
  134. let provider = FunctionsContextProvider(auth: nil, messaging: nil, appCheck: appCheckFake)
  135. let context = try await provider.context(options: .init(requireLimitedUseAppCheckTokens: true))
  136. XCTAssertNil(context.authToken)
  137. XCTAssertNil(context.fcmToken)
  138. // Expect placeholder token in the case of App Check error.
  139. XCTAssertEqual(context.limitedUseAppCheckToken, appCheckFake.limitedUseTokenResult.token)
  140. }
  141. func testContextWithAppCheckOnlyError() {
  142. appCheckFake.tokenResult = appCheckTokenError
  143. let provider = FunctionsContextProvider(auth: nil, messaging: nil, appCheck: appCheckFake)
  144. let expectation = expectation(description: "Verify bad app check token")
  145. provider.getContext { context, error in
  146. XCTAssertNotNil(context)
  147. XCTAssertNil(error)
  148. XCTAssertNil(context.authToken)
  149. XCTAssertNil(context.fcmToken)
  150. // Expect placeholder token in the case of App Check error.
  151. XCTAssertEqual(context.appCheckToken, self.appCheckFake.tokenResult.token)
  152. expectation.fulfill()
  153. }
  154. waitForExpectations(timeout: 0.1)
  155. }
  156. func testContextWithAppCheckOnlyError_LimitedUseToken() {
  157. appCheckFake.limitedUseTokenResult = appCheckLimitedUseTokenError
  158. let provider = FunctionsContextProvider(auth: nil, messaging: nil, appCheck: appCheckFake)
  159. let expectation = expectation(description: "Verify bad app check token")
  160. provider.getContext(options: .init(requireLimitedUseAppCheckTokens: true)) { context, error in
  161. XCTAssertNotNil(context)
  162. XCTAssertNil(error)
  163. XCTAssertNil(context.authToken)
  164. XCTAssertNil(context.fcmToken)
  165. // Expect placeholder token in the case of App Check error.
  166. XCTAssertEqual(context.limitedUseAppCheckToken, self.appCheckFake.limitedUseTokenResult.token)
  167. expectation.fulfill()
  168. }
  169. waitForExpectations(timeout: 0.1)
  170. }
  171. func testAsyncContextWithAppCheckWithoutOptionalMethods() async throws {
  172. let appCheck = AppCheckFakeWithoutOptionalMethods(tokenResult: appCheckTokenSuccess)
  173. let provider = FunctionsContextProvider(auth: nil, messaging: nil, appCheck: appCheck)
  174. let context = try await provider.context(options: .init(requireLimitedUseAppCheckTokens: true))
  175. XCTAssertNil(context.authToken)
  176. XCTAssertNil(context.fcmToken)
  177. XCTAssertNil(context.appCheckToken)
  178. // If the method for limited-use tokens is not implemented, the value should be `nil`:
  179. XCTAssertNil(context.limitedUseAppCheckToken)
  180. }
  181. func testContextWithAppCheckWithoutOptionalMethods() {
  182. let appCheck = AppCheckFakeWithoutOptionalMethods(tokenResult: appCheckTokenSuccess)
  183. let provider = FunctionsContextProvider(auth: nil, messaging: nil, appCheck: appCheck)
  184. let expectation =
  185. expectation(description: "Verify non-implemented method for limited-use tokens")
  186. provider.getContext(options: .init(requireLimitedUseAppCheckTokens: true)) { context, error in
  187. XCTAssertNotNil(context)
  188. XCTAssertNil(error)
  189. XCTAssertNil(context.authToken)
  190. XCTAssertNil(context.fcmToken)
  191. XCTAssertNil(context.appCheckToken)
  192. // If the method for limited-use tokens is not implemented, the value should be `nil`:
  193. XCTAssertNil(context.limitedUseAppCheckToken)
  194. expectation.fulfill()
  195. }
  196. // Importantly, `getContext(options:_:)` must still finish in a timely manner:
  197. waitForExpectations(timeout: 0.1)
  198. }
  199. func testAsyncAllContextsAvailableSuccess() async throws {
  200. appCheckFake.tokenResult = appCheckTokenSuccess
  201. let auth = FIRAuthInteropFake(token: "token", userID: "userID", error: nil)
  202. let provider = FunctionsContextProvider(
  203. auth: auth,
  204. messaging: messagingFake,
  205. appCheck: appCheckFake
  206. )
  207. let context = try await provider.context(options: nil)
  208. XCTAssertEqual(context.authToken, "token")
  209. XCTAssertEqual(context.fcmToken, messagingFake.fcmToken)
  210. XCTAssertEqual(context.appCheckToken, appCheckTokenSuccess.token)
  211. }
  212. func testAllContextsAvailableSuccess() {
  213. appCheckFake.tokenResult = appCheckTokenSuccess
  214. let auth = FIRAuthInteropFake(token: "token", userID: "userID", error: nil)
  215. let provider = FunctionsContextProvider(
  216. auth: auth,
  217. messaging: messagingFake,
  218. appCheck: appCheckFake
  219. )
  220. let expectation = expectation(description: "All contexts available")
  221. provider.getContext { context, error in
  222. XCTAssertNotNil(context)
  223. XCTAssertNil(error)
  224. XCTAssertEqual(context.authToken, "token")
  225. XCTAssertEqual(context.fcmToken, self.messagingFake.fcmToken)
  226. XCTAssertEqual(context.appCheckToken, self.appCheckTokenSuccess.token)
  227. expectation.fulfill()
  228. }
  229. waitForExpectations(timeout: 0.1)
  230. }
  231. func testAsyncAllContextsAuthAndAppCheckError() async {
  232. appCheckFake.tokenResult = appCheckTokenError
  233. let authError = NSError(domain: "com.functions.tests", code: 4, userInfo: nil)
  234. let auth = FIRAuthInteropFake(token: nil, userID: "userID", error: authError)
  235. let provider = FunctionsContextProvider(
  236. auth: auth,
  237. messaging: messagingFake,
  238. appCheck: appCheckFake
  239. )
  240. do {
  241. _ = try await provider.context(options: nil)
  242. XCTFail("Expected an error")
  243. } catch {
  244. XCTAssertEqual(error as NSError, authError)
  245. }
  246. }
  247. func testAllContextsAuthAndAppCheckError() {
  248. appCheckFake.tokenResult = appCheckTokenError
  249. let authError = NSError(domain: "com.functions.tests", code: 4, userInfo: nil)
  250. let auth = FIRAuthInteropFake(token: nil, userID: "userID", error: authError)
  251. let provider = FunctionsContextProvider(
  252. auth: auth,
  253. messaging: messagingFake,
  254. appCheck: appCheckFake
  255. )
  256. let expectation = expectation(description: "All contexts with errors")
  257. provider.getContext { context, error in
  258. XCTAssertNotNil(context)
  259. XCTAssertEqual(error as NSError?, authError)
  260. XCTAssertNil(context.authToken)
  261. XCTAssertEqual(context.fcmToken, self.messagingFake.fcmToken)
  262. // Expect placeholder token in the case of App Check error.
  263. XCTAssertEqual(context.appCheckToken, self.appCheckFake.tokenResult.token)
  264. expectation.fulfill()
  265. }
  266. waitForExpectations(timeout: 0.1)
  267. }
  268. func testAllContextsAuthAndAppCheckError_LimitedUseToken() {
  269. appCheckFake.limitedUseTokenResult = appCheckLimitedUseTokenError
  270. let authError = NSError(domain: "com.functions.tests", code: 4, userInfo: nil)
  271. let auth = FIRAuthInteropFake(token: nil, userID: "userID", error: authError)
  272. let provider = FunctionsContextProvider(
  273. auth: auth,
  274. messaging: messagingFake,
  275. appCheck: appCheckFake
  276. )
  277. let expectation = expectation(description: "All contexts with errors")
  278. provider.getContext(options: .init(requireLimitedUseAppCheckTokens: true)) { context, error in
  279. XCTAssertNotNil(context)
  280. XCTAssertEqual(error as NSError?, authError)
  281. XCTAssertNil(context.authToken)
  282. XCTAssertEqual(context.fcmToken, self.messagingFake.fcmToken)
  283. // Expect placeholder token in the case of App Check error.
  284. XCTAssertEqual(context.limitedUseAppCheckToken, self.appCheckFake.limitedUseTokenResult.token)
  285. expectation.fulfill()
  286. }
  287. waitForExpectations(timeout: 0.1)
  288. }
  289. }
  290. // MARK: - Utilities
  291. private class AppCheckFakeWithoutOptionalMethods: NSObject, AppCheckInterop {
  292. let tokenResult: FIRAppCheckTokenResultInterop
  293. init(tokenResult: FIRAppCheckTokenResultInterop) {
  294. self.tokenResult = tokenResult
  295. }
  296. func getToken(forcingRefresh: Bool, completion handler: @escaping AppCheckTokenHandlerInterop) {
  297. handler(tokenResult)
  298. }
  299. func tokenDidChangeNotificationName() -> String { "AppCheckFakeTokenDidChangeNotification" }
  300. func notificationTokenKey() -> String { "AppCheckFakeTokenNotificationKey" }
  301. func notificationAppNameKey() -> String { "AppCheckFakeAppNameNotificationKey" }
  302. }