Files
firebase-ios-sdk/FirebaseAuth/Tests/Unit/UserTests.swift
T

1856 lines
80 KiB
Swift

// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License")
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import Foundation
import XCTest
@testable import FirebaseAuth
import FirebaseCore
@available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
class UserTests: RPCBaseTests {
static let kFakeAPIKey = "FAKE_API_KEY"
let kFacebookAccessToken = "FACEBOOK_ACCESS_TOKEN"
let kFacebookID = "FACEBOOK_ID"
let kFacebookEmail = "user@facebook.com"
let kFacebookDisplayName = "Facebook Doe"
let kFacebookIDToken: String? = nil // Facebook id Token is always nil.
let kNewEmail = "newuser@company.com"
let kNewPassword = "newpassword"
let kNewDisplayName = "New User Doe"
let kVerificationCode = "12345678"
let kVerificationID = "55432"
let kPhoneNumber = "555-1234"
static var auth: Auth?
override class func setUp() {
let options = FirebaseOptions(googleAppID: "0:0000000000000:ios:0000000000000000",
gcmSenderID: "00000000000000000-00000000000-000000000")
options.apiKey = kFakeAPIKey
options.projectID = "myUserProjectID"
FirebaseApp.configure(name: "test-UserTests", options: options)
#if (os(macOS) && !FIREBASE_AUTH_TESTING_USE_MACOS_KEYCHAIN) || SWIFT_PACKAGE
let keychainStorageProvider = FakeAuthKeychainStorage()
#else
let keychainStorageProvider = AuthKeychainStorageReal()
#endif // (os(macOS) && !FIREBASE_AUTH_TESTING_USE_MACOS_KEYCHAIN) || SWIFT_PACKAGE
auth = Auth(
app: FirebaseApp.app(name: "test-UserTests")!,
keychainStorageProvider: keychainStorageProvider
)
}
override func tearDown() {
// Verifies that no tasks are left suspended on the AuthSerialTaskQueue.
try? UserTests.auth?.signOut()
}
/** @fn testUserPropertiesAndNSSecureCoding
@brief Tests properties of the @c User instance before and after being
serialized/deserialized.
*/
func testUserPropertiesAndNSSecureCoding() throws {
let kProviderUserInfoKey = "providerUserInfo"
let kPhotoUrlKey = "photoUrl"
let kProviderIDkey = "providerId"
let kDisplayNameKey = "displayName"
let kFederatedIDKey = "federatedId"
let kEmailKey = "email"
let kPasswordHashKey = "passwordHash"
let kTestPasswordHash = "testPasswordHash"
let kEmailVerifiedKey = "emailVerified"
let kLocalIDKey = "localId"
let kGoogleID = "GOOGLE_ID"
let kGoogleDisplayName = "Google Doe"
let kGoogleEmail = "user@gmail.com"
let kGooglePhotoURL = "https://googleusercontents.com/user/profile"
let kFacebookID = "FACEBOOK_ID"
let kFacebookEmail = "user@facebook.com"
let kEnrollmentID = "fakeEnrollment"
let kPhoneInfo = "+15555555555"
let kEnrolledAt = "2022-08-01T18:31:15.426458Z"
let kEnrolledAtMatch = "2022-08-01 18:31:15 +0000"
let kTwitterID = "TwitterID"
let kGitHubID = "GitHubID"
let kGameCenterID = "GameCenterID"
var providerUserInfos = [[
kProviderIDkey: EmailAuthProvider.id,
kFederatedIDKey: kEmail,
kEmailKey: kEmail,
],
[
kProviderIDkey: GoogleAuthProvider.id,
kDisplayNameKey: kGoogleDisplayName,
kPhotoUrlKey: kGooglePhotoURL,
kFederatedIDKey: kGoogleID,
kEmailKey: kGoogleEmail,
],
[
kProviderIDkey: FacebookAuthProvider.id,
kFederatedIDKey: kFacebookID,
kEmailKey: kFacebookEmail,
],
[
kProviderIDkey: GitHubAuthProvider.id,
kFederatedIDKey: kGitHubID,
kEmailKey: kGoogleEmail,
],
[
kProviderIDkey: TwitterAuthProvider.id,
kFederatedIDKey: kTwitterID,
kEmailKey: kFacebookEmail,
]]
#if !os(watchOS)
providerUserInfos.append([
kProviderIDkey: GameCenterAuthProvider.id,
kFederatedIDKey: kGameCenterID,
kEmailKey: kFacebookEmail,
])
#endif
#if os(iOS)
providerUserInfos.append([
kProviderIDkey: PhoneAuthProvider.id,
kFederatedIDKey: kPhoneNumber,
"phoneNumber": kPhoneNumber,
])
#endif
rpcIssuer?.fakeGetAccountProviderJSON = [[
kProviderUserInfoKey: providerUserInfos,
kLocalIDKey: kLocalID,
kDisplayNameKey: kDisplayName,
kEmailKey: kEmail,
kPhotoUrlKey: kTestPhotoURL,
kEmailVerifiedKey: true,
kPasswordHashKey: kTestPasswordHash,
"phoneNumber": kPhoneNumber,
"createdAt": String(Int(kCreationDateTimeIntervalInSeconds) * 1000), // to nanoseconds
"lastLoginAt": String(Int(kLastSignInDateTimeIntervalInSeconds) * 1000),
"mfaInfo": [[
"phoneInfo": kPhoneInfo,
"mfaEnrollmentId": kEnrollmentID,
"displayName": kDisplayName,
"enrolledAt": kEnrolledAt,
]],
]]
let expectation = self.expectation(description: #function)
signInWithEmailPasswordReturnFakeUser { user in
do {
XCTAssertEqual(user.providerID, "Firebase")
XCTAssertEqual(user.uid, self.kLocalID)
XCTAssertEqual(user.displayName, self.kDisplayName)
XCTAssertEqual(user.photoURL, URL(string: self.kTestPhotoURL))
XCTAssertEqual(user.email, self.kEmail)
XCTAssertEqual(user.metadata.creationDate, Date(timeIntervalSince1970:
self.kCreationDateTimeIntervalInSeconds))
XCTAssertEqual(user.metadata.lastSignInDate,
Date(timeIntervalSince1970: self.kLastSignInDateTimeIntervalInSeconds))
// Verify FIRUser properties besides providerData contents.
XCTAssertFalse(user.isAnonymous)
XCTAssertTrue(user.isEmailVerified)
XCTAssertEqual(user.refreshToken, self.kRefreshToken)
XCTAssertEqual(user.providerData.count, providerUserInfos.count)
let providerMap = user.providerData.reduce(into: [String: UserInfo]()) {
$0[$1.providerID] = $1
}
// Verify FIRUserInfo properties from email/password.
let passwordUserInfo = try XCTUnwrap(providerMap[EmailAuthProvider.id])
XCTAssertEqual(passwordUserInfo.uid, self.kEmail)
XCTAssertNil(passwordUserInfo.displayName)
XCTAssertNil(passwordUserInfo.photoURL)
XCTAssertEqual(passwordUserInfo.email, self.kEmail)
// Verify FIRUserInfo properties from the Google auth provider.
let googleUserInfo = try XCTUnwrap(providerMap[GoogleAuthProvider.id])
XCTAssertEqual(googleUserInfo.uid, kGoogleID)
XCTAssertEqual(googleUserInfo.displayName, kGoogleDisplayName)
XCTAssertEqual(googleUserInfo.photoURL, URL(string: kGooglePhotoURL))
XCTAssertEqual(googleUserInfo.email, kGoogleEmail)
// Verify FIRUserInfo properties from the Facebook auth provider.
let facebookUserInfo = try XCTUnwrap(providerMap[FacebookAuthProvider.id])
XCTAssertEqual(facebookUserInfo.uid, kFacebookID)
XCTAssertNil(facebookUserInfo.displayName)
XCTAssertNil(facebookUserInfo.photoURL)
XCTAssertEqual(facebookUserInfo.email, kFacebookEmail)
// Verify FIRUserInfo properties from the GitHub auth provider.
let gitHubUserInfo = try XCTUnwrap(providerMap[GitHubAuthProvider.id])
XCTAssertEqual(gitHubUserInfo.uid, kGitHubID)
XCTAssertNil(gitHubUserInfo.displayName)
XCTAssertNil(gitHubUserInfo.photoURL)
XCTAssertEqual(gitHubUserInfo.email, kGoogleEmail)
// Verify FIRUserInfo properties from the Twitter auth provider.
let twitterUserInfo = try XCTUnwrap(providerMap[TwitterAuthProvider.id])
XCTAssertEqual(twitterUserInfo.uid, kTwitterID)
XCTAssertNil(twitterUserInfo.displayName)
XCTAssertNil(twitterUserInfo.photoURL)
XCTAssertEqual(twitterUserInfo.email, kFacebookEmail)
#if os(iOS)
// Verify UserInfo properties from the phone auth provider.
let phoneUserInfo = try XCTUnwrap(providerMap[PhoneAuthProvider.id])
XCTAssertEqual(phoneUserInfo.phoneNumber, self.kPhoneNumber)
#endif
#if !os(watchOS)
// Verify FIRUserInfo properties from the Game Center auth provider.
let gameCenterUserInfo = try XCTUnwrap(providerMap[GameCenterAuthProvider.id])
XCTAssertEqual(gameCenterUserInfo.uid, kGameCenterID)
XCTAssertNil(gameCenterUserInfo.displayName)
XCTAssertNil(gameCenterUserInfo.photoURL)
XCTAssertEqual(gameCenterUserInfo.email, kFacebookEmail)
#endif
// Test NSSecureCoding
XCTAssertTrue(User.supportsSecureCoding)
let data = try NSKeyedArchiver.archivedData(
withRootObject: user,
requiringSecureCoding: true
)
var encodedClasses = [User.self, NSDictionary.self, NSURL.self, SecureTokenService.self,
UserInfoImpl.self, NSDate.self, UserMetadata.self, NSString.self,
NSArray.self]
#if os(iOS)
encodedClasses.append(MultiFactor.self)
encodedClasses.append(PhoneMultiFactorInfo.self)
#endif
let unarchivedUser = try XCTUnwrap(NSKeyedUnarchiver.unarchivedObject(
ofClasses: encodedClasses, from: data
)
as? User)
// Verify NSSecureCoding for FIRUser
XCTAssertEqual(unarchivedUser.providerID, user.providerID)
XCTAssertEqual(unarchivedUser.uid, user.uid)
XCTAssertEqual(unarchivedUser.email, user.email)
XCTAssertEqual(unarchivedUser.photoURL, user.photoURL)
XCTAssertEqual(unarchivedUser.displayName, user.displayName)
// Verify NSSecureCoding properties besides providerData contents.
XCTAssertEqual(unarchivedUser.isAnonymous, user.isAnonymous)
XCTAssertEqual(unarchivedUser.isEmailVerified, user.isEmailVerified)
XCTAssertEqual(unarchivedUser.refreshToken, user.refreshToken)
XCTAssertEqual(unarchivedUser.metadata.creationDate, user.metadata.creationDate)
XCTAssertEqual(unarchivedUser.metadata.lastSignInDate, user.metadata.lastSignInDate)
XCTAssertEqual(unarchivedUser.providerData.count, user.providerData.count)
let unarchivedProviderMap = unarchivedUser.providerData.reduce(into: [String: UserInfo]()) {
$0[$1.providerID] = $1
}
// Verify NSSecureCoding properties for AuthDataResult
let kFakeProfile = ["email": "user@mail.com", "given_name": "User", "family_name": "Doe"]
let kUserName = "User Doe"
let kProviderID = "PROVIDER_ID"
let userInfo = AdditionalUserInfo(providerID: kProviderID,
profile: kFakeProfile,
username: kUserName,
isNewUser: true)
let authDataResult = AuthDataResult(withUser: user, additionalUserInfo: userInfo)
XCTAssertTrue(AuthDataResult.supportsSecureCoding)
let authDataResultData = try NSKeyedArchiver.archivedData(
withRootObject: authDataResult,
requiringSecureCoding: true
)
encodedClasses.append(AuthDataResult.self)
encodedClasses.append(AdditionalUserInfo.self)
let unarchivedDataResult = try XCTUnwrap(NSKeyedUnarchiver.unarchivedObject(
ofClasses: encodedClasses, from: authDataResultData
) as? AuthDataResult)
XCTAssertEqual(unarchivedDataResult.user.providerID, user.providerID)
XCTAssertEqual(unarchivedDataResult.user.uid, user.uid)
XCTAssertEqual(unarchivedDataResult.user.email, user.email)
XCTAssertEqual(unarchivedDataResult.user.photoURL, user.photoURL)
XCTAssertEqual(unarchivedDataResult.user.displayName, user.displayName)
XCTAssertEqual(unarchivedDataResult.additionalUserInfo?.providerID, kProviderID)
XCTAssertEqual(unarchivedDataResult.additionalUserInfo?.profile as? [String: String],
kFakeProfile)
XCTAssertEqual(unarchivedDataResult.additionalUserInfo?.username, kUserName)
// Verify NSSecureCoding properties from email/password.
let unarchivedPasswordUserInfo = try XCTUnwrap(unarchivedProviderMap[EmailAuthProvider.id])
XCTAssertEqual(unarchivedPasswordUserInfo.uid, passwordUserInfo.uid)
XCTAssertEqual(unarchivedPasswordUserInfo.displayName, passwordUserInfo.displayName)
XCTAssertEqual(unarchivedPasswordUserInfo.photoURL, passwordUserInfo.photoURL)
XCTAssertEqual(unarchivedPasswordUserInfo.email, passwordUserInfo.email)
// Verify NSSecureCoding properties from the Google auth provider.
let unarchivedGoogleUserInfo = try XCTUnwrap(unarchivedProviderMap[GoogleAuthProvider.id])
XCTAssertEqual(unarchivedGoogleUserInfo.uid, googleUserInfo.uid)
XCTAssertEqual(unarchivedGoogleUserInfo.displayName, googleUserInfo.displayName)
XCTAssertEqual(unarchivedGoogleUserInfo.photoURL, googleUserInfo.photoURL)
XCTAssertEqual(unarchivedGoogleUserInfo.email, googleUserInfo.email)
// Verify NSSecureCoding properties from the Facebook auth provider.
let unarchivedFacebookUserInfo =
try XCTUnwrap(unarchivedProviderMap[FacebookAuthProvider.id])
XCTAssertEqual(unarchivedFacebookUserInfo.uid, facebookUserInfo.uid)
XCTAssertEqual(unarchivedFacebookUserInfo.displayName, facebookUserInfo.displayName)
XCTAssertEqual(unarchivedFacebookUserInfo.photoURL, facebookUserInfo.photoURL)
XCTAssertEqual(unarchivedFacebookUserInfo.email, facebookUserInfo.email)
#if !os(watchOS)
// Verify NSSecureCoding properties from the GameCenter auth provider.
let unarchivedGameCenterUserInfo =
try XCTUnwrap(unarchivedProviderMap[GameCenterAuthProvider.id])
XCTAssertEqual(unarchivedGameCenterUserInfo.uid, gameCenterUserInfo.uid)
XCTAssertEqual(unarchivedGameCenterUserInfo.displayName, gameCenterUserInfo.displayName)
XCTAssertEqual(unarchivedGameCenterUserInfo.photoURL, gameCenterUserInfo.photoURL)
XCTAssertEqual(unarchivedGameCenterUserInfo.email, gameCenterUserInfo.email)
#endif
// Verify NSSecureCoding properties from the GitHub auth provider.
let unarchivedGitHubUserInfo =
try XCTUnwrap(unarchivedProviderMap[GitHubAuthProvider.id])
XCTAssertEqual(unarchivedGitHubUserInfo.uid, gitHubUserInfo.uid)
XCTAssertEqual(unarchivedGitHubUserInfo.displayName, gitHubUserInfo.displayName)
XCTAssertEqual(unarchivedGitHubUserInfo.photoURL, gitHubUserInfo.photoURL)
XCTAssertEqual(unarchivedGitHubUserInfo.email, gitHubUserInfo.email)
// Verify NSSecureCoding properties from the Twitter auth provider.
let unarchivedTwitterUserInfo =
try XCTUnwrap(unarchivedProviderMap[TwitterAuthProvider.id])
XCTAssertEqual(unarchivedTwitterUserInfo.uid, twitterUserInfo.uid)
XCTAssertEqual(unarchivedTwitterUserInfo.displayName, twitterUserInfo.displayName)
XCTAssertEqual(unarchivedTwitterUserInfo.photoURL, twitterUserInfo.photoURL)
XCTAssertEqual(unarchivedTwitterUserInfo.email, twitterUserInfo.email)
#if os(iOS)
// Verify NSSecureCoding properties from the phone auth provider.
let unarchivedPhoneUserInfo = try XCTUnwrap(unarchivedProviderMap[PhoneAuthProvider.id])
XCTAssertEqual(unarchivedPhoneUserInfo.phoneNumber, phoneUserInfo.phoneNumber)
// Verify MultiFactorInfo properties.
let enrolledFactors = try XCTUnwrap(user.multiFactor.enrolledFactors)
XCTAssertEqual(enrolledFactors[0].factorID, PhoneMultiFactorInfo.PhoneMultiFactorID)
XCTAssertEqual(enrolledFactors[0].uid, kEnrollmentID)
XCTAssertEqual(enrolledFactors[0].displayName, self.kDisplayName)
let date = try XCTUnwrap(enrolledFactors[0].enrollmentDate)
XCTAssertEqual("\(date)", kEnrolledAtMatch)
#endif
} catch {
XCTFail("Caught an error in \(#function): \(error)")
}
expectation.fulfill()
}
waitForExpectations(timeout: 5)
}
/** @fn testUpdateEmailSuccess
@brief Tests the flow of a successful @c updateEmail:completion: call.
*/
func testUpdateEmailSuccess() {
setFakeGetAccountProvider(withPasswordHash: kFakePassword)
let expectation = self.expectation(description: #function)
signInWithEmailPasswordReturnFakeUser { user in
self.changeUserEmail(user: user, changeEmail: true, expectation: expectation)
}
waitForExpectations(timeout: 5)
}
/** @fn testUpdateEmailWithAuthLinkAccountSuccess
@brief Tests a successful @c updateEmail:completion: call updates provider info.
*/
func testUpdateEmailWithAuthLinkAccountSuccess() {
setFakeGetAccountProvider()
let expectation = self.expectation(description: #function)
signInWithEmailPasswordReturnFakeUserLink { user in
self.changeUserEmail(user: user, expectation: expectation)
}
waitForExpectations(timeout: 5)
}
/** @fn testUpdateEmailFailure
@brief Tests the flow of a failed @c updateEmail:completion: call.
*/
func testUpdateEmailFailure() {
setFakeGetAccountProvider()
let expectation = self.expectation(description: #function)
signInWithEmailPasswordReturnFakeUser { user in
do {
self.rpcIssuer.respondBlock = {
try self.rpcIssuer?.respond(serverErrorMessage: "INVALID_EMAIL")
}
user.updateEmail(to: self.kNewEmail) { rawError in
XCTAssertTrue(Thread.isMainThread)
let error = try! XCTUnwrap(rawError)
XCTAssertEqual((error as NSError).code, AuthErrorCode.invalidEmail.rawValue)
// Email should not have changed on the client side.
XCTAssertEqual(user.email, self.kEmail)
// User is still signed in.
XCTAssertEqual(UserTests.auth?.currentUser, user)
expectation.fulfill()
}
}
}
waitForExpectations(timeout: 5)
}
/** @fn testUpdateEmailAutoSignOut
@brief Tests the flow of a failed @c updateEmail:completion: call that automatically signs out.
*/
func testUpdateEmailAutoSignOut() {
setFakeGetAccountProvider()
let expectation = self.expectation(description: #function)
signInWithEmailPasswordReturnFakeUser { user in
do {
self.rpcIssuer.respondBlock = {
try self.rpcIssuer?.respond(serverErrorMessage: "INVALID_ID_TOKEN")
}
user.updateEmail(to: self.kNewEmail) { rawError in
XCTAssertTrue(Thread.isMainThread)
let error = try! XCTUnwrap(rawError)
XCTAssertEqual((error as NSError).code, AuthErrorCode.invalidUserToken.rawValue)
// Email should not have changed on the client side.
XCTAssertEqual(user.email, self.kEmail)
// User is no longer signed in..
XCTAssertNil(UserTests.auth?.currentUser)
expectation.fulfill()
}
}
}
waitForExpectations(timeout: 5)
}
#if os(iOS)
/** @fn testUpdatePhoneSuccess
@brief Tests the flow of a successful @c updatePhoneNumberCredential:completion: call.
*/
func testUpdatePhoneSuccess() throws {
setFakeGetAccountProvider()
let expectation = self.expectation(description: #function)
let auth = try XCTUnwrap(UserTests.auth)
signInWithEmailPasswordReturnFakeUser { user in
do {
self.rpcIssuer.respondBlock = {
try self.rpcIssuer?.respond(withJSON: ["idToken": RPCBaseTests.kFakeAccessToken,
"refreshToken": self.kRefreshToken])
}
self.expectVerifyPhoneNumberRequest()
self.rpcIssuer?.fakeGetAccountProviderJSON = [[
"phoneNumber": self.kPhoneNumber,
]]
let credential = PhoneAuthProvider.provider(auth: auth).credential(
withVerificationID: self.kVerificationID,
verificationCode: self.kVerificationCode
)
user.updatePhoneNumber(credential) { error in
XCTAssertTrue(Thread.isMainThread)
XCTAssertNil(error)
XCTAssertEqual(auth.currentUser?.phoneNumber, self.kPhoneNumber)
expectation.fulfill()
}
}
}
waitForExpectations(timeout: 5)
}
/** @fn testUpdatePhoneNumberFailure
@brief Tests the flow of a failed @c updatePhoneNumberCredential:completion: call.
*/
func testUpdatePhoneNumberFailure() throws {
setFakeGetAccountProvider()
let expectation = self.expectation(description: #function)
let auth = try XCTUnwrap(UserTests.auth)
signInWithEmailPasswordReturnFakeUser { user in
do {
self.rpcIssuer.respondBlock = {
try self.rpcIssuer?.respond(serverErrorMessage: "INVALID_PHONE_NUMBER")
}
self.expectVerifyPhoneNumberRequest()
let credential = PhoneAuthProvider.provider(auth: auth).credential(
withVerificationID: self.kVerificationID,
verificationCode: self.kVerificationCode
)
user.updatePhoneNumber(credential) { rawError in
XCTAssertTrue(Thread.isMainThread)
let error = try! XCTUnwrap(rawError)
XCTAssertEqual((error as NSError).code, AuthErrorCode.invalidPhoneNumber.rawValue)
XCTAssertEqual(auth.currentUser, user)
expectation.fulfill()
}
}
}
waitForExpectations(timeout: 5)
}
/** @fn testUpdatePhoneNumberFailureAutoSignOut
@brief Tests the flow of a failed @c updatePhoneNumberCredential:completion: call that
automatically signs out.
*/
func testUpdatePhoneNumberFailureAutoSignOut() throws {
setFakeGetAccountProvider()
let expectation = self.expectation(description: #function)
let auth = try XCTUnwrap(UserTests.auth)
signInWithEmailPasswordReturnFakeUser { user in
do {
self.rpcIssuer.respondBlock = {
try self.rpcIssuer?.respond(serverErrorMessage: "TOKEN_EXPIRED")
}
self.expectVerifyPhoneNumberRequest()
let credential = PhoneAuthProvider.provider(auth: auth).credential(
withVerificationID: self.kVerificationID,
verificationCode: self.kVerificationCode
)
user.updatePhoneNumber(credential) { rawError in
XCTAssertTrue(Thread.isMainThread)
let error = try! XCTUnwrap(rawError)
XCTAssertEqual((error as NSError).code, AuthErrorCode.userTokenExpired.rawValue)
// User is no longer signed in.
XCTAssertNil(UserTests.auth?.currentUser)
expectation.fulfill()
}
}
}
waitForExpectations(timeout: 5)
}
#endif
/** @fn testUpdatePasswordSuccess
@brief Tests the flow of a successful @c updatePassword:completion: call.
*/
func testUpdatePasswordSuccess() {
setFakeGetAccountProvider()
let expectation = self.expectation(description: #function)
signInWithEmailPasswordReturnFakeUser { user in
self.changeUserEmail(user: user, expectation: expectation)
}
waitForExpectations(timeout: 5)
}
/** @fn testUpdatePasswordFailure
@brief Tests the flow of a failed @c updatePassword:completion: call.
*/
func testUpdatePasswordFailure() {
setFakeGetAccountProvider()
let expectation = self.expectation(description: #function)
signInWithEmailPasswordReturnFakeUser { user in
do {
self.rpcIssuer.respondBlock = {
try self.rpcIssuer?.respond(serverErrorMessage: "CREDENTIAL_TOO_OLD_LOGIN_AGAIN")
}
user.updatePassword(to: self.kNewPassword) { rawError in
XCTAssertTrue(Thread.isMainThread)
let error = try! XCTUnwrap(rawError)
XCTAssertEqual((error as NSError).code, AuthErrorCode.requiresRecentLogin.rawValue)
// Email should not have changed on the client side.
XCTAssertEqual(user.email, self.kEmail)
// User is still signed in.
XCTAssertEqual(UserTests.auth?.currentUser, user)
expectation.fulfill()
}
}
}
waitForExpectations(timeout: 5)
}
/** @fn testUpdateEmptyPasswordFailure
@brief Tests the flow of a failed @c updatePassword:completion: call due to an empty password.
*/
func testUpdateEmptyPasswordFailure() {
setFakeGetAccountProvider()
let expectation = self.expectation(description: #function)
signInWithEmailPasswordReturnFakeUser { user in
do {
self.rpcIssuer.respondBlock = {
try self.rpcIssuer?.respond(serverErrorMessage: "WEAK_PASSWORD")
}
user.updatePassword(to: self.kNewPassword) { rawError in
XCTAssertTrue(Thread.isMainThread)
let error = try! XCTUnwrap(rawError)
XCTAssertEqual((error as NSError).code, AuthErrorCode.weakPassword.rawValue)
// Email should not have changed on the client side.
XCTAssertEqual(user.email, self.kEmail)
// User is still signed in.
XCTAssertEqual(UserTests.auth?.currentUser, user)
expectation.fulfill()
}
}
}
waitForExpectations(timeout: 5)
}
/** @fn testUpdatePasswordFailureAutoSignOut
@brief Tests the flow of a failed @c updatePassword:completion: call that automatically signs
out.
*/
func testUpdatePasswordFailureAutoSignOut() {
setFakeGetAccountProvider()
let expectation = self.expectation(description: #function)
signInWithEmailPasswordReturnFakeUser { user in
do {
self.rpcIssuer.respondBlock = {
try self.rpcIssuer?.respond(serverErrorMessage: "USER_DISABLED")
}
user.updatePassword(to: self.kNewPassword) { rawError in
XCTAssertTrue(Thread.isMainThread)
let error = try! XCTUnwrap(rawError)
XCTAssertEqual((error as NSError).code, AuthErrorCode.userDisabled.rawValue)
// Email should not have changed on the client side.
XCTAssertEqual(user.email, self.kEmail)
// User is signed out.
XCTAssertNil(UserTests.auth?.currentUser)
expectation.fulfill()
}
}
}
waitForExpectations(timeout: 5)
}
/** @fn testChangeProfileSuccess
@brief Tests a successful user profile change flow.
*/
func testChangeProfileSuccess() {
setFakeGetAccountProvider()
let expectation = self.expectation(description: #function)
signInWithEmailPasswordReturnFakeUser { user in
do {
self.rpcIssuer.respondBlock = {
try self.rpcIssuer?.respond(withJSON: ["idToken": RPCBaseTests.kFakeAccessToken,
"refreshToken": self.kRefreshToken])
}
let profileChange = user.createProfileChangeRequest()
profileChange.photoURL = URL(string: self.kTestPhotoURL)
profileChange.displayName = self.kNewDisplayName
profileChange.commitChanges { error in
XCTAssertTrue(Thread.isMainThread)
XCTAssertNil(error)
XCTAssertEqual(user.displayName, self.kNewDisplayName)
XCTAssertEqual(user.photoURL, URL(string: self.kTestPhotoURL))
expectation.fulfill()
}
}
}
waitForExpectations(timeout: 5)
}
/** @fn testChangeProfileFailure
@brief Tests a failed user profile change flow.
*/
func testChangeProfileFailure() {
setFakeGetAccountProvider()
let expectation = self.expectation(description: #function)
signInWithEmailPasswordReturnFakeUser { user in
do {
self.rpcIssuer.respondBlock = {
try self.rpcIssuer?.respond(serverErrorMessage: "TOO_MANY_ATTEMPTS_TRY_LATER")
}
let profileChange = user.createProfileChangeRequest()
profileChange.displayName = self.kNewDisplayName
profileChange.commitChanges { rawError in
XCTAssertTrue(Thread.isMainThread)
let error = try! XCTUnwrap(rawError)
XCTAssertEqual((error as NSError).code, AuthErrorCode.tooManyRequests.rawValue)
// Email should not have changed on the client side.
XCTAssertEqual(user.email, self.kEmail)
XCTAssertEqual(user.displayName, self.kDisplayName)
// User is still signed in.
XCTAssertEqual(UserTests.auth?.currentUser, user)
expectation.fulfill()
}
}
}
waitForExpectations(timeout: 5)
}
/** @fn testChangeProfileFailureAutoSignOut
@brief Tests a failed user profile change flow that automatically signs out.
*/
func testChangeProfileFailureAutoSignOut() {
setFakeGetAccountProvider()
let expectation = self.expectation(description: #function)
signInWithEmailPasswordReturnFakeUser { user in
do {
self.rpcIssuer.respondBlock = {
try self.rpcIssuer?.respond(serverErrorMessage: "USER_NOT_FOUND")
}
let profileChange = user.createProfileChangeRequest()
profileChange.displayName = self.kNewDisplayName
profileChange.commitChanges { rawError in
XCTAssertTrue(Thread.isMainThread)
let error = try! XCTUnwrap(rawError)
XCTAssertEqual((error as NSError).code, AuthErrorCode.userNotFound.rawValue)
// Email should not have changed on the client side.
XCTAssertEqual(user.email, self.kEmail)
// User is signed out.
XCTAssertNil(UserTests.auth?.currentUser)
expectation.fulfill()
}
}
}
waitForExpectations(timeout: 5)
}
/** @fn testGetIDTokenResultSuccess
@brief Tests the flow of a successful @c getIDTokenResultWithCompletion: call.
*/
func testGetIDTokenResultSuccess() {
internalGetIDTokenResult(token: RPCBaseTests.kFakeAccessToken, forceRefresh: false)
}
/** @fn testGetIDTokenResultForcingRefreshSameAccessTokenSuccess
@brief Tests the flow of a successful @c getIDTokenResultForcingRefresh:completion: call when
the returned access token is the same as the stored access token.
*/
func testGetIDTokenResultForcingRefreshSameAccessTokenSuccess() {
internalGetIDTokenResult(token: RPCBaseTests.kFakeAccessToken)
}
/** @fn testGetIDTokenResultForcingRefreshSuccess
@brief Tests the flow successful @c getIDTokenResultForcingRefresh:completion: calls.
*/
func testGetIDTokenResultForcingRefreshSuccess() {
internalGetIDTokenResult(token: RPCBaseTests.kFakeAccessTokenLength415)
internalGetIDTokenResult(token: RPCBaseTests.kFakeAccessTokenLength416)
internalGetIDTokenResult(token: RPCBaseTests.kFakeAccessTokenLength523,
emailMatch: "aunitestuser4@gmail.com")
}
/** @fn testGetIDTokenResultSuccessWithBase64EncodedURL
@brief Tests the flow of a successful @c getIDTokenResultWithCompletion: call using a base64 url
encoded string.
*/
func testGetIDTokenResultSuccessWithBase64EncodedURL() {
internalGetIDTokenResult(token: RPCBaseTests.kFakeAccessTokenWithBase64,
emailMatch: ">>>>>>>>????????@gmail.com",
audMatch: "??????????>>>>>>>>>>")
}
/** @fn testGetIDTokenResultForcingRefreshFailure
@brief Tests the flow of a failed @c getIDTokenResultForcingRefresh:completion: call.
*/
func testGetIDTokenResultForcingRefreshFailure() {
setFakeGetAccountProvider()
let expectation = self.expectation(description: #function)
signInWithEmailPasswordReturnFakeUser(fakeAccessToken: RPCBaseTests.kFakeAccessToken) { user in
let underlying = NSError(domain: "Test Error", code: 1)
self.rpcIssuer?.secureTokenNetworkError =
AuthErrorUtils.networkError(underlyingError: underlying) as NSError
user.getIDTokenResult(forcingRefresh: true) { tokenResult, rawError in
do {
XCTAssertTrue(Thread.isMainThread)
XCTAssertNil(tokenResult)
let error = try XCTUnwrap(rawError)
XCTAssertEqual((error as NSError).code, AuthErrorCode.networkError.rawValue)
} catch {
XCTFail("Caught an error in \(#function): \(error)")
}
expectation.fulfill()
}
}
waitForExpectations(timeout: 5)
}
/** @fn testReloadSuccess
@brief Tests the flow of a successful @c reloadWithCompletion: call.
*/
func testReloadSuccess() {
setFakeGetAccountProvider()
let expectation = self.expectation(description: #function)
signInWithEmailPasswordReturnFakeUser { user in
user.reload { error in
XCTAssertTrue(Thread.isMainThread)
XCTAssertNil(error)
XCTAssertEqual(user.displayName, self.kDisplayName)
XCTAssertEqual(user.email, self.kEmail)
expectation.fulfill()
}
}
waitForExpectations(timeout: 5)
}
/** @fn testReloadFailure
@brief Tests the flow of a failed @c reloadWithCompletion: call.
*/
func testReloadFailure() {
setFakeGetAccountProvider()
let expectation = self.expectation(description: #function)
signInWithEmailPasswordReturnFakeUser { user in
do {
self.rpcIssuer.respondBlock = {
try self.rpcIssuer?.respond(serverErrorMessage: "QUOTA_EXCEEDED")
}
// Clear fake so we can inject error
self.rpcIssuer?.fakeGetAccountProviderJSON = nil
user.reload { rawError in
XCTAssertTrue(Thread.isMainThread)
let error = try! XCTUnwrap(rawError)
XCTAssertEqual((error as NSError).code, AuthErrorCode.quotaExceeded.rawValue)
// User is still signed in.
XCTAssertEqual(UserTests.auth?.currentUser, user)
expectation.fulfill()
}
}
}
waitForExpectations(timeout: 5)
}
/** @fn testReloadFailureAutoSignOut
@brief Tests the flow of a failed @c reloadWithCompletion: call that automtatically signs out.
*/
func testReloadFailureAutoSignOut() {
setFakeGetAccountProvider()
let expectation = self.expectation(description: #function)
signInWithEmailPasswordReturnFakeUser { user in
do {
self.rpcIssuer.respondBlock = {
try self.rpcIssuer?.respond(serverErrorMessage: "TOKEN_EXPIRED")
}
// Clear fake so we can inject error
self.rpcIssuer?.fakeGetAccountProviderJSON = nil
user.reload { rawError in
XCTAssertTrue(Thread.isMainThread)
let error = try! XCTUnwrap(rawError)
XCTAssertEqual((error as NSError).code, AuthErrorCode.userTokenExpired.rawValue)
// User is no longer signed in.
XCTAssertNil(UserTests.auth?.currentUser)
expectation.fulfill()
}
}
}
waitForExpectations(timeout: 5)
}
/** @fn testReauthenticateSuccess
@brief Tests the flow of a successful @c reauthenticateWithCredential:completion: call.
*/
func testReauthenticateSuccess() {
setFakeGetAccountProvider()
let expectation = self.expectation(description: #function)
signInWithEmailPasswordReturnFakeUser { user in
do {
self.rpcIssuer.respondBlock = {
try self.rpcIssuer?.respond(withJSON: ["idToken": RPCBaseTests.kFakeAccessToken,
"refreshToken": self.kRefreshToken])
}
let emailCredential = EmailAuthProvider.credential(withEmail: self.kEmail,
password: self.kFakePassword)
user.reauthenticate(with: emailCredential) { rawResult, error in
XCTAssertTrue(Thread.isMainThread)
XCTAssertNil(error)
let result = try! XCTUnwrap(rawResult)
XCTAssertEqual(result.user.uid, user.uid)
XCTAssertEqual(result.user.email, user.email)
XCTAssertEqual(result.additionalUserInfo?.isNewUser, false)
// User is still signed in.
XCTAssertEqual(UserTests.auth?.currentUser, user)
expectation.fulfill()
}
}
}
waitForExpectations(timeout: 5)
}
/** @fn testReauthenticateWithCredentialSuccess
@brief Tests the flow of a successful @c reauthenticateWithCredential call.
*/
func testReauthenticateWithCredentialSuccess() throws {
let expectation = self.expectation(description: #function)
signInWithGoogleCredential { user in
do {
self.rpcIssuer.respondBlock = {
try self.rpcIssuer?.respond(withJSON: ["idToken": RPCBaseTests.kFakeAccessToken,
"refreshToken": self.kRefreshToken,
"federatedId": self.kGoogleID,
"providerId": GoogleAuthProvider.id,
"localId": self.kLocalID,
"displayName": self.kGoogleDisplayName,
"rawUserInfo": self.kGoogleProfile,
"username": self.kUserName])
}
let googleCredential = GoogleAuthProvider.credential(withIDToken: self.kGoogleIDToken,
accessToken: self.kGoogleAccessToken)
user.reauthenticate(with: googleCredential) { reauthenticatedAuthResult, error in
XCTAssertTrue(Thread.isMainThread)
do {
try self.assertUserGoogle(reauthenticatedAuthResult?.user)
} catch {
XCTFail("\(error)")
}
XCTAssertNil(error)
// Verify that the current user is unchanged.
XCTAssertEqual(UserTests.auth?.currentUser, user)
// Verify that the current user and reauthenticated user are not same pointers.
XCTAssertNotEqual(user, reauthenticatedAuthResult?.user)
// Verify that anyway the current user and reauthenticated user have same IDs.
XCTAssertEqual(reauthenticatedAuthResult?.user.uid, user.uid)
XCTAssertEqual(reauthenticatedAuthResult?.user.email, user.email)
XCTAssertEqual(reauthenticatedAuthResult?.user.displayName, user.displayName)
XCTAssertEqual(reauthenticatedAuthResult?.additionalUserInfo?.username, self.kUserName)
XCTAssertEqual(reauthenticatedAuthResult?.additionalUserInfo?.providerID,
GoogleAuthProvider.id)
XCTAssertEqual(
reauthenticatedAuthResult?.additionalUserInfo?.profile as? [String: String],
self.kGoogleProfile
)
expectation.fulfill()
}
}
}
waitForExpectations(timeout: 5)
try assertUserGoogle(UserTests.auth?.currentUser)
}
/** @fn testReauthenticateFailure
@brief Tests the flow of a failed @c reauthenticateWithCredential:completion: call.
*/
func testReauthenticateFailure() {
setFakeGetAccountProvider()
let expectation = self.expectation(description: #function)
signInWithEmailPasswordReturnFakeUser { user in
do {
self.rpcIssuer.respondBlock = {
try self.rpcIssuer?.respond(withJSON: ["idToken": RPCBaseTests.kFakeAccessToken,
"refreshToken": self.kRefreshToken])
}
self.setFakeGetAccountProvider(withLocalID: "A different Local ID")
let emailCredential = EmailAuthProvider.credential(withEmail: self.kEmail,
password: self.kFakePassword)
user.reauthenticate(with: emailCredential) { reauthenticatedAuthResult, rawError in
XCTAssertTrue(Thread.isMainThread)
let error = try! XCTUnwrap(rawError)
XCTAssertEqual((error as NSError).code, AuthErrorCode.userMismatch.rawValue)
// Email should not have changed on the client side.
XCTAssertEqual(user.email, self.kEmail)
// User is still signed in.
XCTAssertEqual(UserTests.auth?.currentUser, user)
expectation.fulfill()
}
}
}
waitForExpectations(timeout: 5)
}
/** @fn testReauthenticateUserMismatchFailure
@brief Tests the flow of a failed @c reauthenticateWithCredential:completion: call due to trying
to reauthenticate a user that does not exist.
*/
func testReauthenticateUserMismatchFailure() {
setFakeGetAccountProvider()
let expectation = self.expectation(description: #function)
signInWithEmailPasswordReturnFakeUser { user in
do {
self.rpcIssuer.respondBlock = {
try self.rpcIssuer?.respond(serverErrorMessage: "USER_NOT_FOUND")
}
let googleCredential = GoogleAuthProvider.credential(withIDToken: self.kGoogleIDToken,
accessToken: self.kGoogleAccessToken)
user.reauthenticate(with: googleCredential) { reauthenticatedAuthResult, rawError in
XCTAssertTrue(Thread.isMainThread)
let error = try! XCTUnwrap(rawError)
XCTAssertEqual((error as NSError).code, AuthErrorCode.userMismatch.rawValue)
// Email should not have changed on the client side.
XCTAssertEqual(user.email, self.kEmail)
// User is still signed in.
XCTAssertEqual(UserTests.auth?.currentUser, user)
expectation.fulfill()
}
}
}
waitForExpectations(timeout: 5)
}
/** @fn testLinkAndRetrieveDataSuccess (and old testLinkCredentialSuccess)
@brief Tests the flow of a successful @c linkWithCredential call.
*/
func testLinkAndRetrieveDataSuccess() throws {
setFakeGetAccountProvider()
let expectation = self.expectation(description: #function)
let auth = try XCTUnwrap(UserTests.auth)
signInWithFacebookCredential { user in
XCTAssertNotNil(user)
do {
self.setFakeGoogleGetAccountProvider()
self.rpcIssuer.respondBlock = {
try self.rpcIssuer?.respond(withJSON: ["idToken": RPCBaseTests.kFakeAccessToken,
"refreshToken": self.kRefreshToken,
"federatedId": self.kGoogleID,
"providerId": GoogleAuthProvider.id,
"localId": self.kLocalID,
"displayName": self.kGoogleDisplayName,
"rawUserInfo": self.kGoogleProfile,
"username": self.kUserName])
}
let googleCredential = GoogleAuthProvider.credential(withIDToken: self.kGoogleIDToken,
accessToken: self.kGoogleAccessToken)
user.link(with: googleCredential) { linkAuthResult, error in
XCTAssertTrue(Thread.isMainThread)
XCTAssertNil(error)
// Verify that the current user is unchanged.
XCTAssertEqual(auth.currentUser, user)
// Verify that the current user and reauthenticated user are the same pointers.
XCTAssertEqual(user, linkAuthResult?.user)
// Verify that anyway the current user and reauthenticated user have same IDs.
XCTAssertEqual(linkAuthResult?.user.uid, user.uid)
XCTAssertEqual(linkAuthResult?.user.email, user.email)
XCTAssertEqual(linkAuthResult?.user.displayName, user.displayName)
XCTAssertEqual(linkAuthResult?.additionalUserInfo?.username, self.kUserName)
XCTAssertEqual(linkAuthResult?.additionalUserInfo?.providerID,
GoogleAuthProvider.id)
XCTAssertEqual(
linkAuthResult?.additionalUserInfo?.profile as? [String: String],
self.kGoogleProfile
)
XCTAssertEqual(linkAuthResult?.user.providerData.first?.providerID, GoogleAuthProvider.id)
expectation.fulfill()
}
}
}
waitForExpectations(timeout: 5)
try assertUserGoogle(auth.currentUser)
}
/** @fn testLinkAndRetrieveDataError
@brief Tests the flow of an unsuccessful @c linkWithCredential:completion:
call with an error from the backend.
*/
func testLinkAndRetrieveDataError() throws {
setFakeGetAccountProvider()
let expectation = self.expectation(description: #function)
signInWithFacebookCredential { user in
XCTAssertNotNil(user)
do {
self.setFakeGetAccountProvider()
self.rpcIssuer.respondBlock = {
try self.rpcIssuer?.respond(serverErrorMessage: "CREDENTIAL_TOO_OLD_LOGIN_AGAIN")
}
let googleCredential = GoogleAuthProvider.credential(withIDToken: self.kGoogleIDToken,
accessToken: self.kGoogleAccessToken)
user.link(with: googleCredential) { linkAuthResult, rawError in
XCTAssertTrue(Thread.isMainThread)
XCTAssertNil(linkAuthResult)
let error = try! XCTUnwrap(rawError)
XCTAssertEqual((error as NSError).code, AuthErrorCode.requiresRecentLogin.rawValue)
// Email should not have changed on the client side.
XCTAssertEqual(user.email, self.kFacebookEmail)
// User is still signed in.
XCTAssertEqual(UserTests.auth?.currentUser, user)
expectation.fulfill()
}
}
}
waitForExpectations(timeout: 5)
}
/** @fn testLinkAndRetrieveDataProviderAlreadyLinked and old testLinkCredentialProviderAlreadyLinkedError
@brief Tests the flow of an unsuccessful @c linkWithCredential:completion:
call with FIRAuthErrorCodeProviderAlreadyLinked, which is a client side error.
*/
func testLinkAndRetrieveDataProviderAlreadyLinked() throws {
setFakeGetAccountProvider()
let expectation = self.expectation(description: #function)
signInWithFacebookCredential { user in
XCTAssertNotNil(user)
do {
self.setFakeGetAccountProvider()
let facebookCredential =
FacebookAuthProvider.credential(withAccessToken: self.kFacebookAccessToken)
user.link(with: facebookCredential) { linkAuthResult, rawError in
XCTAssertTrue(Thread.isMainThread)
XCTAssertNil(linkAuthResult)
do {
let error = try XCTUnwrap(rawError)
XCTAssertEqual((error as NSError).code, AuthErrorCode.providerAlreadyLinked.rawValue)
} catch {
XCTFail("Expected to throw providerAlreadyLinked error.")
}
// User is still signed in.
XCTAssertEqual(UserTests.auth?.currentUser, user)
expectation.fulfill()
}
}
}
waitForExpectations(timeout: 5)
}
/** @fn testLinkAndRetrieveDataErrorAutoSignOut (and old testLinkCredentialError)
@brief Tests the flow of an unsuccessful @c linkWithCredential:completion:
call that automatically signs out.
*/
func testLinkAndRetrieveDataErrorAutoSignOut() throws {
setFakeGetAccountProvider()
let expectation = self.expectation(description: #function)
signInWithFacebookCredential { user in
XCTAssertNotNil(user)
do {
self.setFakeGetAccountProvider()
self.rpcIssuer.respondBlock = {
try self.rpcIssuer?.respond(serverErrorMessage: "USER_DISABLED")
}
let googleCredential = GoogleAuthProvider.credential(withIDToken: self.kGoogleIDToken,
accessToken: self.kGoogleAccessToken)
user.link(with: googleCredential) { linkAuthResult, rawError in
XCTAssertTrue(Thread.isMainThread)
XCTAssertNil(linkAuthResult)
let error = try! XCTUnwrap(rawError)
XCTAssertEqual((error as NSError).code, AuthErrorCode.userDisabled.rawValue)
// User is signed out.
XCTAssertNil(UserTests.auth?.currentUser)
expectation.fulfill()
}
}
}
waitForExpectations(timeout: 5)
}
/** @fn testLinkEmailAndRetrieveDataSuccess
@brief Tests the flow of a successful @c linkWithCredential:completion:
invocation for email credential.
*/
func testLinkEmailAndRetrieveDataSuccess() throws {
setFakeGetAccountProvider()
let expectation = self.expectation(description: #function)
let auth = try XCTUnwrap(UserTests.auth)
signInWithFacebookCredential { user in
XCTAssertNotNil(user)
do {
self.rpcIssuer.respondBlock = {
let request = self.rpcIssuer?.request as? SignUpNewUserRequest
XCTAssertNotNil(request)
XCTAssertEqual(request?.email, self.kEmail)
XCTAssertEqual(request?.password, self.kFakePassword)
XCTAssertNil(request?.displayName)
try self.rpcIssuer?.respond(withJSON: ["idToken": RPCBaseTests.kFakeAccessToken,
"refreshToken": self.kRefreshToken])
self.setFakeGetAccountProvider(withProviderID: EmailAuthProvider.id)
}
let emailCredential = EmailAuthProvider.credential(withEmail: self.kEmail,
password: self.kFakePassword)
user.link(with: emailCredential) { linkAuthResult, error in
XCTAssertTrue(Thread.isMainThread)
XCTAssertNil(error)
// Verify that the current user is unchanged.
XCTAssertEqual(auth.currentUser, user)
// Verify that the current user and reauthenticated user are the same pointers.
XCTAssertEqual(user, linkAuthResult?.user)
// Verify that anyway the current user and reauthenticated user have same IDs.
XCTAssertEqual(linkAuthResult?.user.uid, user.uid)
XCTAssertEqual(linkAuthResult?.user.email, user.email)
XCTAssertEqual(linkAuthResult?.user.displayName, user.displayName)
expectation.fulfill()
}
}
}
waitForExpectations(timeout: 5)
}
/** @fn tesLlinkEmailProviderAlreadyLinkedError
@brief Tests the flow of an unsuccessful @c linkWithCredential:completion:
invocation for email credential and FIRAuthErrorCodeProviderAlreadyLinked which is a client
side error.
*/
func testLinkEmailProviderAlreadyLinkedError() throws {
setFakeGetAccountProvider()
let expectation = self.expectation(description: #function)
signInWithFacebookCredential { user in
XCTAssertNotNil(user)
do {
self.rpcIssuer.respondBlock = {
let request = self.rpcIssuer?.request as? SignUpNewUserRequest
XCTAssertNotNil(request)
XCTAssertEqual(request?.email, self.kEmail)
XCTAssertEqual(request?.password, self.kFakePassword)
try self.rpcIssuer?.respond(withJSON: ["idToken": RPCBaseTests.kFakeAccessToken,
"refreshToken": self.kRefreshToken])
self.setFakeGetAccountProvider(withProviderID: EmailAuthProvider.id)
}
let emailCredential = EmailAuthProvider.credential(withEmail: self.kEmail,
password: self.kFakePassword)
user.link(with: emailCredential) { linkAuthResult, error in
XCTAssertEqual(user, linkAuthResult?.user)
linkAuthResult?.user.link(with: emailCredential) { linkLinkAuthResult, rawError in
XCTAssertTrue(Thread.isMainThread)
XCTAssertNil(linkLinkAuthResult)
do {
let error = try XCTUnwrap(rawError)
XCTAssertEqual((error as NSError).code, AuthErrorCode.providerAlreadyLinked.rawValue)
} catch {
XCTFail("Expected to throw providerAlreadyLinked error.")
}
// User is still signed in.
XCTAssertEqual(UserTests.auth?.currentUser, user)
expectation.fulfill()
}
}
}
}
waitForExpectations(timeout: 5)
}
/** @fn testLinkEmailAndRetrieveDataError
@brief Tests the flow of an unsuccessful @c linkWithCredential:completion:
invocation for email credential and an error from the backend.
*/
func testLinkEmailAndRetrieveDataError() throws {
setFakeGetAccountProvider()
let expectation = self.expectation(description: #function)
signInWithFacebookCredential { user in
XCTAssertNotNil(user)
do {
self.rpcIssuer.respondBlock = {
let request = self.rpcIssuer?.request as? SignUpNewUserRequest
XCTAssertNotNil(request)
XCTAssertEqual(request?.email, self.kEmail)
XCTAssertEqual(request?.password, self.kFakePassword)
try self.rpcIssuer?.respond(serverErrorMessage: "TOO_MANY_ATTEMPTS_TRY_LATER")
}
let emailCredential = EmailAuthProvider.credential(withEmail: self.kEmail,
password: self.kFakePassword)
user.link(with: emailCredential) { linkAuthResult, rawError in
XCTAssertTrue(Thread.isMainThread)
XCTAssertNil(linkAuthResult)
let error = try! XCTUnwrap(rawError)
XCTAssertEqual((error as NSError).code, AuthErrorCode.tooManyRequests.rawValue)
// User is still signed in.
XCTAssertEqual(UserTests.auth?.currentUser, user)
expectation.fulfill()
}
}
}
waitForExpectations(timeout: 5)
}
/** @fn testLinkEmailAndRetrieveDataErrorAutoSignOut
@brief Tests the flow of an unsuccessful @c linkWithCredential:completion:
invocation that automatically signs out.
*/
func testLinkEmailAndRetrieveDataErrorAutoSignOut() throws {
setFakeGetAccountProvider()
let expectation = self.expectation(description: #function)
signInWithFacebookCredential { user in
XCTAssertNotNil(user)
do {
self.rpcIssuer.respondBlock = {
XCTAssertNotNil(self.rpcIssuer?.request as? SignUpNewUserRequest)
try self.rpcIssuer?.respond(serverErrorMessage: "TOKEN_EXPIRED")
}
let emailCredential = EmailAuthProvider.credential(withEmail: self.kEmail,
password: self.kFakePassword)
user.link(with: emailCredential) { linkAuthResult, rawError in
XCTAssertTrue(Thread.isMainThread)
XCTAssertNil(linkAuthResult)
let error = try! XCTUnwrap(rawError)
XCTAssertEqual((error as NSError).code, AuthErrorCode.userTokenExpired.rawValue)
// User is signed out.
XCTAssertNil(UserTests.auth?.currentUser)
expectation.fulfill()
}
}
}
waitForExpectations(timeout: 5)
}
#if os(iOS)
private class FakeOAuthProvider: OAuthProvider {
override func getCredentialWith(_ UIDelegate: AuthUIDelegate?,
completion: ((AuthCredential?, Error?) -> Void)? = nil) {
if let completion {
let credential = OAuthCredential(
withProviderID: GoogleAuthProvider.id,
sessionID: UserTests.kOAuthSessionID,
OAuthResponseURLString: UserTests.kOAuthRequestURI
)
completion(credential, nil)
}
}
}
/** @fn testLinkProviderFailure
@brief Tests the flow of a failed @c linkWithProvider:completion:
call.
*/
func testLinkProviderFailure() throws {
setFakeGetAccountProvider()
let expectation = self.expectation(description: #function)
let auth = try XCTUnwrap(UserTests.auth)
signInWithFacebookCredential { user in
XCTAssertNotNil(user)
do {
self.setFakeGetAccountProvider()
self.rpcIssuer.respondBlock = {
try self.rpcIssuer?.respond(serverErrorMessage: "TOKEN_EXPIRED")
}
user.link(with: FakeOAuthProvider(providerID: "foo", auth: auth),
uiDelegate: nil) { linkAuthResult, rawError in
XCTAssertTrue(Thread.isMainThread)
XCTAssertNil(linkAuthResult)
let error = try! XCTUnwrap(rawError)
XCTAssertEqual((error as NSError).code, AuthErrorCode.userTokenExpired.rawValue)
// User is signed out.
XCTAssertNil(UserTests.auth?.currentUser)
expectation.fulfill()
}
}
}
waitForExpectations(timeout: 5)
}
/** @fn testReauthenticateWithProviderFailure
@brief Tests the flow of a failed @c reauthenticateWithProvider:completion: call.
*/
func testReauthenticateWithProviderFailure() throws {
setFakeGetAccountProvider()
let expectation = self.expectation(description: #function)
let auth = try XCTUnwrap(UserTests.auth)
signInWithFacebookCredential { user in
XCTAssertNotNil(user)
do {
self.setFakeGetAccountProvider()
self.rpcIssuer.respondBlock = {
try self.rpcIssuer?.respond(serverErrorMessage: "TOKEN_EXPIRED")
}
user.reauthenticate(with: FakeOAuthProvider(providerID: "foo", auth: auth),
uiDelegate: nil) { linkAuthResult, rawError in
XCTAssertTrue(Thread.isMainThread)
XCTAssertNil(linkAuthResult)
let error = try! XCTUnwrap(rawError)
XCTAssertEqual((error as NSError).code, AuthErrorCode.userTokenExpired.rawValue)
// User is still signed in.
XCTAssertEqual(UserTests.auth?.currentUser, user)
expectation.fulfill()
}
}
}
waitForExpectations(timeout: 5)
}
/** @fn testLinkPhoneAuthCredentialSuccess
@brief Tests the flow of a successful @c linkWithCredential call using a phoneAuthCredential.
*/
func testLinkPhoneAuthCredentialSuccess() throws {
setFakeGetAccountProvider()
let expectation = self.expectation(description: #function)
let auth = try XCTUnwrap(UserTests.auth)
signInWithEmailPasswordReturnFakeUser { user in
XCTAssertNotNil(user)
self.expectVerifyPhoneNumberRequest(isLink: true)
do {
self.setFakeGetAccountProvider(withProviderID: PhoneAuthProvider.id)
self.rpcIssuer.respondBlock = {
try self.rpcIssuer?.respond(withJSON: ["idToken": RPCBaseTests.kFakeAccessToken,
"refreshToken": self.kRefreshToken])
}
let credential = PhoneAuthProvider.provider(auth: auth).credential(
withVerificationID: self.kVerificationID,
verificationCode: self.kVerificationCode
)
user.link(with: credential) { linkAuthResult, error in
XCTAssertTrue(Thread.isMainThread)
XCTAssertNil(error)
// Verify that the current user is unchanged.
XCTAssertEqual(auth.currentUser, user)
// Verify that the current user and reauthenticated user are the same pointers.
XCTAssertEqual(user, linkAuthResult?.user)
// Verify that anyway the current user and reauthenticated user have same IDs.
XCTAssertEqual(linkAuthResult?.user.uid, user.uid)
XCTAssertEqual(linkAuthResult?.user.email, user.email)
XCTAssertEqual(linkAuthResult?.user.displayName, user.displayName)
XCTAssertEqual(auth.currentUser?.providerData.first?.providerID, PhoneAuthProvider.id)
XCTAssertEqual(
linkAuthResult?.user.providerData.first?.providerID,
PhoneAuthProvider.id
)
XCTAssertEqual(auth.currentUser?.phoneNumber, self.kTestPhoneNumber)
expectation.fulfill()
}
}
}
waitForExpectations(timeout: 5)
}
/** @fn testUnlinkPhoneAuthCredentialSuccess
@brief Tests the flow of a successful @c unlinkFromProvider:completion: call using a
@c FIRPhoneAuthProvider.
*/
func testUnlinkPhoneAuthCredentialSuccess() throws {
setFakeGetAccountProvider()
let expectation = self.expectation(description: #function)
let auth = try XCTUnwrap(UserTests.auth)
signInWithEmailPasswordReturnFakeUser { user in
XCTAssertNotNil(user)
self.expectVerifyPhoneNumberRequest(isLink: true)
do {
self.setFakeGetAccountProvider(withProviderID: PhoneAuthProvider.id)
self.rpcIssuer.respondBlock = {
try self.rpcIssuer?.respond(withJSON: ["idToken": RPCBaseTests.kFakeAccessToken,
"refreshToken": self.kRefreshToken])
}
let credential = PhoneAuthProvider.provider(auth: auth).credential(
withVerificationID: self.kVerificationID,
verificationCode: self.kVerificationCode
)
user.link(with: credential) { linkAuthResult, error in
XCTAssertTrue(Thread.isMainThread)
XCTAssertNil(error)
// Verify that the current user is unchanged.
XCTAssertEqual(auth.currentUser, user)
// Verify that the current user and reauthenticated user are the same pointers.
XCTAssertEqual(user, linkAuthResult?.user)
// Verify that anyway the current user and reauthenticated user have same IDs.
XCTAssertEqual(linkAuthResult?.user.uid, user.uid)
XCTAssertEqual(linkAuthResult?.user.email, user.email)
XCTAssertEqual(linkAuthResult?.user.displayName, user.displayName)
XCTAssertEqual(auth.currentUser?.providerData.first?.providerID, PhoneAuthProvider.id)
XCTAssertEqual(
linkAuthResult?.user.providerData.first?.providerID,
PhoneAuthProvider.id
)
XCTAssertEqual(auth.currentUser?.phoneNumber, self.kTestPhoneNumber)
// Immediately unlink the phone auth provider.
self.rpcIssuer.respondBlock = {
let request = try XCTUnwrap(self.rpcIssuer?.request as? SetAccountInfoRequest)
XCTAssertEqual(request.apiKey, UserTests.kFakeAPIKey)
XCTAssertEqual(request.accessToken, RPCBaseTests.kFakeAccessToken)
XCTAssertNil(request.email)
XCTAssertNil(request.password)
XCTAssertNil(request.localID)
XCTAssertNil(request.displayName)
XCTAssertNil(request.photoURL)
XCTAssertNil(request.providers)
XCTAssertNil(request.deleteAttributes)
XCTAssertEqual(try XCTUnwrap(request.deleteProviders?.first), PhoneAuthProvider.id)
try self.rpcIssuer?.respond(withJSON: ["idToken": RPCBaseTests.kFakeAccessToken,
"refreshToken": self.kRefreshToken])
}
user.unlink(fromProvider: PhoneAuthProvider.id) { user, error in
XCTAssertNil(error)
XCTAssertEqual(auth.currentUser, user)
XCTAssertNil(auth.currentUser?.phoneNumber)
expectation.fulfill()
}
}
}
}
waitForExpectations(timeout: 5)
}
/** @fn testlinkPhoneAuthCredentialFailure
@brief Tests the flow of a failed call to @c linkWithCredential:completion: due
to a phone provider already being linked.
*/
func testlinkPhoneAuthCredentialFailure() throws {
setFakeGetAccountProvider(withPasswordHash: kFakePassword)
let expectation = self.expectation(description: #function)
signInWithEmailPasswordReturnFakeUser { user in
XCTAssertNotNil(user)
self.expectVerifyPhoneNumberRequest(isLink: true)
self.setFakeGetAccountProvider(withProviderID: PhoneAuthProvider.id)
let credential = EmailAuthCredential(withEmail: self.kEmail, password: self.kFakePassword)
user.link(with: credential) { linkAuthResult, rawError in
XCTAssertTrue(Thread.isMainThread)
XCTAssertNil(linkAuthResult)
if let error = try? XCTUnwrap(rawError) {
XCTAssertEqual((error as NSError).code, AuthErrorCode.providerAlreadyLinked.rawValue)
} else {
XCTFail("Did not throw expected error")
}
expectation.fulfill()
}
}
waitForExpectations(timeout: 5)
}
/** @fn testlinkPhoneCredentialAlreadyExistsError
@brief Tests the flow of @c linkWithCredential:completion:
call using a phoneAuthCredential and a credential already exists error. In this case we
should get a AuthCredential in the error object.
*/
func testlinkPhoneCredentialAlreadyExistsError() throws {
setFakeGetAccountProvider()
let expectation = self.expectation(description: #function)
let auth = try XCTUnwrap(UserTests.auth)
signInWithEmailPasswordReturnFakeUser { user in
XCTAssertNotNil(user)
self.expectVerifyPhoneNumberRequest(isLink: true)
do {
self.setFakeGetAccountProvider(withProviderID: PhoneAuthProvider.id)
self.rpcIssuer.respondBlock = {
try self.rpcIssuer?.respond(withJSON: ["idToken": RPCBaseTests.kFakeAccessToken,
"refreshToken": self.kRefreshToken,
"phoneNumber": self.kTestPhoneNumber,
"temporaryProof": "Fake Temporary Proof"])
}
let credential = PhoneAuthProvider.provider(auth: auth).credential(
withVerificationID: self.kVerificationID,
verificationCode: self.kVerificationCode
)
user.link(with: credential) { linkAuthResult, rawError in
XCTAssertTrue(Thread.isMainThread)
XCTAssertNil(linkAuthResult)
do {
let error = try XCTUnwrap(rawError)
XCTAssertEqual((error as NSError).code, AuthErrorCode.credentialAlreadyInUse.rawValue)
let credential = try XCTUnwrap((error as NSError)
.userInfo[AuthErrors.userInfoUpdatedCredentialKey] as? PhoneAuthCredential)
switch credential.credentialKind {
case let .phoneNumber(phoneNumber, temporaryProof):
XCTAssertEqual(temporaryProof, "Fake Temporary Proof")
XCTAssertEqual(phoneNumber, self.kTestPhoneNumber)
case .verification: XCTFail("Should be phoneNumber case")
}
} catch {
XCTFail("Did not throw expected error \(error)")
}
expectation.fulfill()
}
}
}
waitForExpectations(timeout: 5)
}
#endif
// MARK: Private helper functions
private func expectVerifyPhoneNumberRequest(isLink: Bool = false) {
rpcIssuer?.verifyPhoneNumberRequester = { request in
XCTAssertEqual(request.verificationID, self.kVerificationID)
XCTAssertEqual(request.verificationCode, self.kVerificationCode)
XCTAssertEqual(request.accessToken, RPCBaseTests.kFakeAccessToken)
if isLink {
XCTAssertEqual(request.operation, AuthOperationType.link)
} else {
XCTAssertEqual(request.operation, AuthOperationType.update)
}
}
}
private func internalGetIDTokenResult(token: String, forceRefresh: Bool = true,
emailMatch: String = "aunitestuser@gmail.com",
audMatch: String = "test_aud") {
setFakeGetAccountProvider()
let expectation = self.expectation(description: #function)
signInWithEmailPasswordReturnFakeUser(fakeAccessToken: token) { user in
user.getIDTokenResult(forcingRefresh: forceRefresh) { rawTokenResult, error in
XCTAssertTrue(Thread.isMainThread)
XCTAssertNil(error)
XCTAssertEqual(user.displayName, self.kDisplayName)
XCTAssertEqual(user.email, self.kEmail)
let tokenResult = try! XCTUnwrap(rawTokenResult)
XCTAssertEqual(tokenResult.token, token)
XCTAssertNotNil(tokenResult.issuedAtDate)
XCTAssertNotNil(tokenResult.authDate)
XCTAssertNotNil(tokenResult.expirationDate)
XCTAssertNotNil(tokenResult.signInProvider)
// The lowercased is for the base64 test which seems to be an erroneously uppercased
// "Password"?
XCTAssertEqual(tokenResult.signInProvider.lowercased(), EmailAuthProvider.id)
XCTAssertEqual(tokenResult.claims["email"] as! String, emailMatch)
XCTAssertEqual(tokenResult.claims["aud"] as! String, audMatch)
XCTAssertEqual(tokenResult.signInSecondFactor, "")
expectation.fulfill()
}
}
waitForExpectations(timeout: 5)
}
private func changeUserEmail(user: User, changeEmail: Bool = false,
expectation: XCTestExpectation) {
do {
XCTAssertEqual(user.providerID, "Firebase")
XCTAssertEqual(user.uid, kLocalID)
XCTAssertEqual(user.displayName, kDisplayName)
XCTAssertEqual(user.photoURL, URL(string: kTestPhotoURL))
XCTAssertEqual(user.email, kEmail)
// Pretend that the display name on the server has been changed since the original signin.
setFakeGetAccountProvider(withNewDisplayName: kNewDisplayName)
rpcIssuer.respondBlock = {
let request = try XCTUnwrap(self.rpcIssuer?.request as? SetAccountInfoRequest)
XCTAssertEqual(request.apiKey, UserTests.kFakeAPIKey)
XCTAssertEqual(request.accessToken, RPCBaseTests.kFakeAccessToken)
if changeEmail {
XCTAssertEqual(request.email, self.kNewEmail)
XCTAssertNil(request.password)
} else {
XCTAssertEqual(request.password, self.kNewPassword)
XCTAssertNil(request.email)
}
XCTAssertNil(request.localID)
XCTAssertNil(request.displayName)
XCTAssertNil(request.photoURL)
XCTAssertNil(request.providers)
XCTAssertNil(request.deleteAttributes)
XCTAssertNil(request.deleteProviders)
try self.rpcIssuer?.respond(withJSON: ["idToken": RPCBaseTests.kFakeAccessToken,
"email": self.kNewEmail,
"refreshToken": self.kRefreshToken])
}
if changeEmail {
user.updateEmail(to: kNewEmail) { error in
XCTAssertNil(error)
XCTAssertEqual(user.email, self.kNewEmail)
XCTAssertEqual(user.displayName, self.kNewDisplayName)
XCTAssertFalse(user.isAnonymous)
expectation.fulfill()
}
} else {
user.updatePassword(to: kNewPassword) { error in
XCTAssertNil(error)
XCTAssertEqual(user.displayName, self.kNewDisplayName)
XCTAssertFalse(user.isAnonymous)
expectation.fulfill()
}
}
}
}
private func signInWithEmailPasswordReturnFakeUser(fakeAccessToken: String = RPCBaseTests
.kFakeAccessToken,
completion: @escaping (User) -> Void) {
let kRefreshToken = "fakeRefreshToken"
setFakeSecureTokenService(fakeAccessToken: fakeAccessToken)
rpcIssuer?.verifyPasswordRequester = { request in
// 2. Validate the created Request instance.
XCTAssertEqual(request.email, self.kEmail)
XCTAssertEqual(request.password, self.kFakePassword)
XCTAssertEqual(request.apiKey, UserTests.kFakeAPIKey)
XCTAssertTrue(request.returnSecureToken)
do {
// 3. Send the response from the fake backend.
try self.rpcIssuer?.respond(withJSON: ["idToken": fakeAccessToken,
"isNewUser": true,
"refreshToken": kRefreshToken])
} catch {
XCTFail("Failure sending response: \(error)")
}
}
// 1. After setting up fakes, sign out and sign in.
do {
try UserTests.auth?.signOut()
} catch {
XCTFail("Sign out failed: \(error)")
return
}
UserTests.auth?.signIn(withEmail: kEmail, password: kFakePassword) { authResult, error in
// 4. After the response triggers the callback, verify the returned result.
XCTAssertTrue(Thread.isMainThread)
guard let user = authResult?.user else {
XCTFail("authResult.user is missing")
return
}
XCTAssertEqual(user.refreshToken, kRefreshToken)
XCTAssertFalse(user.isAnonymous)
XCTAssertEqual(user.email, self.kEmail)
guard let additionalUserInfo = authResult?.additionalUserInfo else {
XCTFail("authResult.additionalUserInfo is missing")
return
}
XCTAssertFalse(additionalUserInfo.isNewUser)
XCTAssertEqual(additionalUserInfo.providerID, EmailAuthProvider.id)
XCTAssertNil(error)
// Clear the password Requester to avoid being called again by reauthenticate tests.
self.rpcIssuer?.verifyPasswordRequester = nil
completion(user)
}
}
private func signInWithGoogleCredential(completion: @escaping (User) -> Void) {
setFakeSecureTokenService(fakeAccessToken: RPCBaseTests.kFakeAccessToken)
setFakeGoogleGetAccountProvider()
rpcIssuer.respondBlock = {
try self.verifyGoogleAssertionRequest(
XCTUnwrap(self.rpcIssuer?.request as? VerifyAssertionRequest)
)
// 3. Send the response from the fake backend.
try self.rpcIssuer?.respond(withJSON: ["idToken": RPCBaseTests.kFakeAccessToken,
"providerId": GoogleAuthProvider.id,
"refreshToken": self.kRefreshToken,
"localId": self.kLocalID,
"displayName": self.kDisplayName,
"rawUserInfo": self.kGoogleProfile,
"username": self.kUserName])
}
do {
try UserTests.auth?.signOut()
let googleCredential = GoogleAuthProvider.credential(withIDToken: kGoogleIDToken,
accessToken: kGoogleAccessToken)
UserTests.auth?.signIn(with: googleCredential) { authResult, error in
// 4. After the response triggers the callback, verify the returned result.
XCTAssertTrue(Thread.isMainThread)
guard let user = authResult?.user else {
XCTFail("authResult.user is missing")
return
}
XCTAssertEqual(user.refreshToken, self.kRefreshToken)
XCTAssertFalse(user.isAnonymous)
XCTAssertEqual(user.email, self.kGoogleEmail)
guard let additionalUserInfo = authResult?.additionalUserInfo,
let profile = additionalUserInfo.profile as? [String: String] else {
XCTFail("authResult.additionalUserInfo and/or profile is missing")
return
}
XCTAssertEqual(profile, self.kGoogleProfile)
XCTAssertFalse(additionalUserInfo.isNewUser)
XCTAssertEqual(additionalUserInfo.providerID, GoogleAuthProvider.id)
XCTAssertEqual(additionalUserInfo.username, self.kUserName)
XCTAssertNil(error)
completion(user)
}
} catch {
XCTFail("Throw in \(#function): \(error)")
}
}
private func verifyGoogleAssertionRequest(_ request: VerifyAssertionRequest) {
XCTAssertEqual(request.providerID, GoogleAuthProvider.id)
XCTAssertEqual(request.providerIDToken, kGoogleIDToken)
XCTAssertEqual(request.providerAccessToken, kGoogleAccessToken)
XCTAssertTrue(request.returnSecureToken)
XCTAssertEqual(request.apiKey, UserTests.kFakeAPIKey)
XCTAssertTrue(request.returnSecureToken)
}
private func signInWithFacebookCredential(completion: @escaping (User) -> Void) {
setFakeSecureTokenService(fakeAccessToken: RPCBaseTests.kFakeAccessToken)
setFakeGetAccountProvider(withNewDisplayName: kFacebookDisplayName,
withProviderID: FacebookAuthProvider.id,
withFederatedID: kFacebookID,
withEmail: kFacebookEmail)
rpcIssuer.respondBlock = {
let request = try XCTUnwrap(self.rpcIssuer?.request as? VerifyAssertionRequest)
XCTAssertEqual(request.providerID, FacebookAuthProvider.id)
XCTAssertEqual(request.providerIDToken, self.kFacebookIDToken)
XCTAssertEqual(request.providerAccessToken, self.kFacebookAccessToken)
XCTAssertTrue(request.returnSecureToken)
XCTAssertEqual(request.apiKey, UserTests.kFakeAPIKey)
XCTAssertTrue(request.returnSecureToken)
// 3. Send the response from the fake backend.
try self.rpcIssuer?.respond(withJSON: ["idToken": RPCBaseTests.kFakeAccessToken,
"refreshToken": self.kRefreshToken,
"federatedId": self.kFacebookID,
"providerId": FacebookAuthProvider.id,
"localId": self.kLocalID,
"displayName": self.kDisplayName,
"rawUserInfo": self.kGoogleProfile,
"username": self.kUserName])
}
do {
try UserTests.auth?.signOut()
let facebookCredential = FacebookAuthProvider
.credential(withAccessToken: kFacebookAccessToken)
UserTests.auth?.signIn(with: facebookCredential) { authResult, error in
// 4. After the response triggers the callback, verify the returned result.
XCTAssertTrue(Thread.isMainThread)
guard let user = authResult?.user else {
XCTFail("authResult.user is missing")
return
}
XCTAssertEqual(user.refreshToken, self.kRefreshToken)
XCTAssertFalse(user.isAnonymous)
XCTAssertEqual(user.email, self.kFacebookEmail)
XCTAssertEqual(user.displayName, self.kFacebookDisplayName)
XCTAssertEqual(user.providerData.count, 1)
guard let additionalUserInfo = authResult?.additionalUserInfo,
let facebookUserInfo = user.providerData.first,
let profile = additionalUserInfo.profile as? [String: String] else {
XCTFail("authResult.additionalUserInfo and/or profile is missing")
return
}
XCTAssertEqual(facebookUserInfo.providerID, FacebookAuthProvider.id)
XCTAssertEqual(facebookUserInfo.uid, self.kFacebookID)
XCTAssertEqual(facebookUserInfo.displayName, self.kFacebookDisplayName)
XCTAssertEqual(facebookUserInfo.email, self.kFacebookEmail)
XCTAssertEqual(profile, self.kGoogleProfile)
XCTAssertFalse(additionalUserInfo.isNewUser)
XCTAssertEqual(additionalUserInfo.providerID, FacebookAuthProvider.id)
XCTAssertEqual(additionalUserInfo.username, self.kUserName)
XCTAssertNil(error)
completion(user)
}
} catch {
XCTFail("Throw in \(#function): \(error)")
}
}
private func signInWithEmailPasswordReturnFakeUserLink(completion: @escaping (User) -> Void) {
let kRefreshToken = "fakeRefreshToken"
setFakeSecureTokenService()
rpcIssuer.respondBlock = {
let request = try XCTUnwrap(self.rpcIssuer?.request as? EmailLinkSignInRequest)
XCTAssertEqual(request.email, self.kEmail)
XCTAssertEqual(request.apiKey, UserTests.kFakeAPIKey)
XCTAssertEqual(request.oobCode, "aCode")
XCTAssertNil(request.idToken)
// Send the response from the fake backend.
try self.rpcIssuer?.respond(withJSON: ["idToken": RPCBaseTests.kFakeAccessToken,
"isNewUser": true,
"refreshToken": kRefreshToken])
}
do {
try UserTests.auth?.signOut()
UserTests.auth?.signIn(
withEmail: kEmail,
link: "https://www.google.com?oobCode=aCode&mode=signIn"
) { authResult, error in
// 4. After the response triggers the callback, verify the returned result.
XCTAssertTrue(Thread.isMainThread)
guard let user = authResult?.user else {
XCTFail("authResult.user is missing")
return
}
XCTAssertEqual(user.refreshToken, kRefreshToken)
XCTAssertFalse(user.isAnonymous)
XCTAssertEqual(user.email, self.kEmail)
guard let additionalUserInfo = authResult?.additionalUserInfo else {
XCTFail("authResult.additionalUserInfo is missing")
return
}
XCTAssertTrue(additionalUserInfo.isNewUser)
XCTAssertEqual(additionalUserInfo.providerID, EmailAuthProvider.id)
XCTAssertNil(error)
completion(user)
}
} catch {
XCTFail("Throw in \(#function): \(error)")
}
}
}