IDTokenDidChangePublisherTests.swift 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. // Copyright 2020 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. import FirebaseAuth
  17. import FirebaseCombineSwift
  18. import Combine
  19. import XCTest
  20. class IDTokenDidChangePublisherTests: XCTestCase {
  21. static let apiKey = Credentials.apiKey
  22. static let accessTokenTimeToLive: TimeInterval = 60 * 60
  23. static let refreshToken = "REFRESH_TOKEN"
  24. static let accessToken = "ACCESS_TOKEN"
  25. static let email = "johnnyappleseed@apple.com"
  26. static let password = "secret"
  27. static let localID = "LOCAL_ID"
  28. static let displayName = "Johnny Appleseed"
  29. class MockGetAccountInfoResponseUser: FIRGetAccountInfoResponseUser {
  30. override var localID: String { return IDTokenDidChangePublisherTests.localID }
  31. override var email: String { return IDTokenDidChangePublisherTests.email }
  32. override var displayName: String { return IDTokenDidChangePublisherTests.displayName }
  33. }
  34. class MockGetAccountInfoResponse: FIRGetAccountInfoResponse {
  35. override var users: [FIRGetAccountInfoResponseUser] {
  36. return [MockGetAccountInfoResponseUser(dictionary: [:])]
  37. }
  38. }
  39. class MockSignUpNewUserResponse: FIRSignUpNewUserResponse {
  40. override var idToken: String { return IDTokenDidChangePublisherTests.accessToken }
  41. override var refreshToken: String { return IDTokenDidChangePublisherTests.refreshToken }
  42. override var approximateExpirationDate: Date {
  43. Date(timeIntervalSinceNow: IDTokenDidChangePublisherTests.accessTokenTimeToLive)
  44. }
  45. }
  46. class MockAuthBackend: AuthBackendImplementationMock {
  47. override func signUpNewUser(_ request: FIRSignUpNewUserRequest,
  48. callback: @escaping FIRSignupNewUserCallback) {
  49. XCTAssertEqual(request.apiKey, AnonymousAuthTests.apiKey)
  50. XCTAssertNil(request.email)
  51. XCTAssertNil(request.password)
  52. XCTAssertTrue(request.returnSecureToken)
  53. let response = MockSignUpNewUserResponse()
  54. callback(response, nil)
  55. }
  56. override func getAccountInfo(_ request: FIRGetAccountInfoRequest,
  57. callback: @escaping FIRGetAccountInfoResponseCallback) {
  58. XCTAssertEqual(request.apiKey, IDTokenDidChangePublisherTests.apiKey)
  59. XCTAssertEqual(request.accessToken, IDTokenDidChangePublisherTests.accessToken)
  60. let response = MockGetAccountInfoResponse()
  61. callback(response, nil)
  62. }
  63. }
  64. override class func setUp() {
  65. FirebaseApp.configureForTests()
  66. }
  67. override class func tearDown() {
  68. FirebaseApp.app()?.delete { success in
  69. if success {
  70. print("Shut down app successfully.")
  71. } else {
  72. print("💥 There was a problem when shutting down the app..")
  73. }
  74. }
  75. }
  76. override func setUp() {
  77. do {
  78. print(#function)
  79. try Auth.auth().signOut()
  80. } catch {}
  81. }
  82. func testIDTokenChanges() {
  83. // given
  84. FIRAuthBackend.setBackendImplementation(MockAuthBackend())
  85. //
  86. // 1) Publisher should emit as soon as it is registered
  87. var expect = expectation(description: "Publisher emits value as soon as it is subscribed")
  88. var cancellable = Auth.auth()
  89. .idTokenDidChangePublisher()
  90. .sink { user in
  91. XCTAssertNil(user)
  92. expect.fulfill()
  93. }
  94. wait(for: [expect], timeout: expectationTimeout)
  95. cancellable.cancel()
  96. //
  97. // 2) Publisher should emit when user is signed in
  98. expect = expectation(description: "Publisher emits value when user is signed in")
  99. cancellable = Auth.auth()
  100. .idTokenDidChangePublisher()
  101. .sink { user in
  102. if let user = user, user.isAnonymous {
  103. expect.fulfill()
  104. }
  105. }
  106. Auth.auth().signInAnonymously()
  107. wait(for: [expect], timeout: expectationTimeout)
  108. cancellable.cancel()
  109. //
  110. // 3) Publisher should not fire for signing in again
  111. expect = expectation(description: "Publisher emits value when user is signed in")
  112. cancellable = Auth.auth()
  113. .idTokenDidChangePublisher()
  114. .sink { user in
  115. if let user = user, user.isAnonymous {
  116. print(#function)
  117. expect.fulfill()
  118. }
  119. }
  120. // Sign in, expect the publisher to emit
  121. Auth.auth().signInAnonymously()
  122. wait(for: [expect], timeout: expectationTimeout)
  123. // Sign in again, expect the publisher NOT to emit
  124. expect = expectation(description: "Publisher does not emit when user sign in again")
  125. expect.isInverted = true
  126. Auth.auth().signInAnonymously()
  127. wait(for: [expect], timeout: expectationTimeout)
  128. cancellable.cancel()
  129. //
  130. // 4) Listener should fire for signing out.
  131. expect = expectation(description: "Publisher emits value when user is signed in")
  132. var shouldUserBeNil = false
  133. cancellable = Auth.auth()
  134. .idTokenDidChangePublisher()
  135. .sink { user in
  136. if shouldUserBeNil {
  137. if user == nil {
  138. expect.fulfill()
  139. }
  140. } else {
  141. if let user = user, user.isAnonymous {
  142. expect.fulfill()
  143. }
  144. }
  145. }
  146. // sign in first
  147. Auth.auth().signInAnonymously()
  148. wait(for: [expect], timeout: expectationTimeout)
  149. // now sign out
  150. expect = expectation(description: "Publisher emits value when user signs out")
  151. shouldUserBeNil = true
  152. do {
  153. try Auth.auth().signOut()
  154. } catch {}
  155. wait(for: [expect], timeout: expectationTimeout)
  156. cancellable.cancel()
  157. //
  158. // Listener should no longer fire once detached.
  159. expect = expectation(description: "Publisher emits value when user is signed in")
  160. shouldUserBeNil = false
  161. cancellable = Auth.auth()
  162. .idTokenDidChangePublisher()
  163. .sink { user in
  164. if shouldUserBeNil {
  165. if user == nil {
  166. expect.fulfill()
  167. }
  168. } else {
  169. if let user = user, user.isAnonymous {
  170. expect.fulfill()
  171. }
  172. }
  173. }
  174. // sign in first
  175. Auth.auth().signInAnonymously()
  176. wait(for: [expect], timeout: expectationTimeout)
  177. // detach the publisher
  178. expect = expectation(description: "Publisher no longer emits once detached")
  179. expect.isInverted = true
  180. cancellable.cancel()
  181. shouldUserBeNil = true
  182. do {
  183. try Auth.auth().signOut()
  184. } catch {}
  185. wait(for: [expect], timeout: expectationTimeout)
  186. }
  187. }