mirror of
https://github.com/firebase/firebase-ios-sdk
synced 2026-11-11 22:55:59 +05:45
1868 lines
78 KiB
Swift
1868 lines
78 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
|
|
|
|
class UserTests: RPCBaseTests {
|
|
static let kFakeAPIKey = "FAKE_API_KEY"
|
|
let kGoogleIDToken = "GOOGLE_ID_TOKEN"
|
|
let kGoogleAccessToken = "GOOGLE_ACCESS_TOKEN"
|
|
let kGoogleID = "GOOGLE_ID"
|
|
let kGoogleEmail = "usergmail.com"
|
|
let kGoogleDisplayName = "Google Doe"
|
|
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 kUserName = "User Doe"
|
|
let kGoogleProfile = ["email": "usergmail.com", "given_name": "MyFirst", "family_name": "MyLast"]
|
|
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
|
|
let keychainStorageProvider = FakeAuthKeychainServices.self
|
|
#else
|
|
let keychainStorageProvider = AuthKeychainServices.self
|
|
#endif // os(macOS) && !FIREBASE_AUTH_TESTING_USE_MACOS_KEYCHAIN
|
|
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 kUserArchiverKey = "userArchiverKey"
|
|
let kEnrollmentID = "fakeEnrollment"
|
|
|
|
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,
|
|
]]
|
|
|
|
#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),
|
|
]]
|
|
|
|
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)
|
|
|
|
#if os(iOS)
|
|
// Verify FIRUserInfo properties from the phone auth provider.
|
|
let phoneUserInfo = try XCTUnwrap(providerMap[PhoneAuthProvider.id])
|
|
XCTAssertEqual(phoneUserInfo.phoneNumber, self.kPhoneNumber)
|
|
#endif
|
|
|
|
// Test NSSecureCoding
|
|
XCTAssertTrue(User.supportsSecureCoding)
|
|
|
|
let data = NSMutableData()
|
|
let archiver = NSKeyedArchiver(forWritingWith: data)
|
|
archiver.encode(user, forKey: kUserArchiverKey)
|
|
archiver.finishEncoding()
|
|
|
|
let unarchiver = try NSKeyedUnarchiver(forReadingFrom: data as Data)
|
|
// TODO: The unarchive will fail without this, because of FIRUser not being in the allowed classes.
|
|
// Meanwhile the unarchive in FIRAuth.m getUser method.
|
|
unarchiver.requiresSecureCoding = false
|
|
let unarchivedUser = try XCTUnwrap(unarchiver
|
|
.decodeObject(forKey: kUserArchiverKey) 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 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(iOS)
|
|
// Verify FIRUserInfo properties from the phone auth provider.
|
|
let unarchivedPhoneUserInfo = try XCTUnwrap(unarchivedProviderMap[PhoneAuthProvider.id])
|
|
XCTAssertEqual(unarchivedPhoneUserInfo.phoneNumber, phoneUserInfo.phoneNumber)
|
|
|
|
// TODO: Finish multifactor
|
|
// Verify FIRMultiFactorInfo properties.
|
|
// XCTAssertEqual(user.multiFactor.enrolledFactors[0].factorID, PhoneMultiFactorID)
|
|
// XCTAssertEqual(user.multiFactor.enrolledFactors[0].UID, kEnrollmentID)
|
|
// XCTAssertEqual(user.multiFactor.enrolledFactors[0].displayName, self.kDisplayName)
|
|
// NSDateFormatter *dateFormatter =
|
|
// [[NSDateFormatter alloc] init];
|
|
// [dateFormatter
|
|
// setDateFormat:@"yyyy-MM-dd'T'HH:mm:ss.SSSZ"];
|
|
// NSDate *date =
|
|
// [dateFormatter dateFromString:kEnrolledAt];
|
|
// XCTAssertEqual(
|
|
// user.multiFactor.enrolledFactors[0].enrollmentDate,
|
|
// date);
|
|
#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, expectation: expectation)
|
|
}
|
|
waitForExpectations(timeout: 5)
|
|
}
|
|
|
|
// TODO: revisit after Auth.swift
|
|
/** @fn testUpdateEmailWithAuthLinkAccountSuccess
|
|
@brief Tests a successful @c updateEmail:completion: call updates provider info.
|
|
*/
|
|
// func testUpdateEmailWithAuthLinkAccountSuccess() {
|
|
// setFakeGetAccountProvider()
|
|
// let expectation = self.expectation(description: #function)
|
|
// self.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 {
|
|
let group = self.createGroup()
|
|
|
|
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()
|
|
}
|
|
group.wait()
|
|
|
|
try self.rpcIssuer?.respond(serverErrorMessage: "INVALID_EMAIL")
|
|
|
|
} catch {
|
|
XCTFail("Caught an error in \(#function): \(error)")
|
|
}
|
|
}
|
|
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 {
|
|
let group = self.createGroup()
|
|
|
|
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()
|
|
}
|
|
group.wait()
|
|
|
|
try self.rpcIssuer?.respond(serverErrorMessage: "INVALID_ID_TOKEN")
|
|
|
|
} catch {
|
|
XCTFail("Caught an error in \(#function): \(error)")
|
|
}
|
|
}
|
|
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 {
|
|
let group = self.createGroup()
|
|
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()
|
|
}
|
|
group.wait()
|
|
try self.rpcIssuer?.respond(withJSON: ["idToken": RPCBaseTests.kFakeAccessToken,
|
|
"refreshToken": self.kRefreshToken])
|
|
} catch {
|
|
XCTFail("Caught an error in \(#function): \(error)")
|
|
}
|
|
}
|
|
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 {
|
|
let group = self.createGroup()
|
|
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()
|
|
}
|
|
group.wait()
|
|
try self.rpcIssuer?.respond(serverErrorMessage: "INVALID_PHONE_NUMBER")
|
|
} catch {
|
|
XCTFail("Caught an error in \(#function): \(error)")
|
|
}
|
|
}
|
|
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 {
|
|
let group = self.createGroup()
|
|
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()
|
|
}
|
|
group.wait()
|
|
try self.rpcIssuer?.respond(serverErrorMessage: "TOKEN_EXPIRED")
|
|
} catch {
|
|
XCTFail("Caught an error in \(#function): \(error)")
|
|
}
|
|
}
|
|
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, changePassword: true, 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 {
|
|
let group = self.createGroup()
|
|
|
|
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()
|
|
}
|
|
group.wait()
|
|
|
|
try self.rpcIssuer?.respond(serverErrorMessage: "CREDENTIAL_TOO_OLD_LOGIN_AGAIN")
|
|
|
|
} catch {
|
|
XCTFail("Caught an error in \(#function): \(error)")
|
|
}
|
|
}
|
|
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 {
|
|
let group = self.createGroup()
|
|
|
|
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()
|
|
}
|
|
group.wait()
|
|
|
|
try self.rpcIssuer?.respond(serverErrorMessage: "WEAK_PASSWORD")
|
|
|
|
} catch {
|
|
XCTFail("Caught an error in \(#function): \(error)")
|
|
}
|
|
}
|
|
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 {
|
|
let group = self.createGroup()
|
|
|
|
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()
|
|
}
|
|
group.wait()
|
|
|
|
try self.rpcIssuer?.respond(serverErrorMessage: "USER_DISABLED")
|
|
|
|
} catch {
|
|
XCTFail("Caught an error in \(#function): \(error)")
|
|
}
|
|
}
|
|
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 {
|
|
let group = self.createGroup()
|
|
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()
|
|
}
|
|
group.wait()
|
|
|
|
try self.rpcIssuer?.respond(withJSON: ["idToken": RPCBaseTests.kFakeAccessToken,
|
|
"refreshToken": self.kRefreshToken])
|
|
} catch {
|
|
XCTFail("Caught an error in \(#function): \(error)")
|
|
}
|
|
}
|
|
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 {
|
|
let group = self.createGroup()
|
|
|
|
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()
|
|
}
|
|
group.wait()
|
|
|
|
try self.rpcIssuer?.respond(serverErrorMessage: "TOO_MANY_ATTEMPTS_TRY_LATER")
|
|
|
|
} catch {
|
|
XCTFail("Caught an error in \(#function): \(error)")
|
|
}
|
|
}
|
|
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 {
|
|
let group = self.createGroup()
|
|
|
|
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()
|
|
}
|
|
group.wait()
|
|
|
|
try self.rpcIssuer?.respond(serverErrorMessage: "USER_NOT_FOUND")
|
|
|
|
} catch {
|
|
XCTFail("Caught an error in \(#function): \(error)")
|
|
}
|
|
}
|
|
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
|
|
self.rpcIssuer?.secureTokenNetworkError = true
|
|
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 {
|
|
let group = self.createGroup()
|
|
|
|
// 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()
|
|
}
|
|
group.wait()
|
|
|
|
try self.rpcIssuer?.respond(serverErrorMessage: "QUOTA_EXCEEDED")
|
|
|
|
} catch {
|
|
XCTFail("Caught an error in \(#function): \(error)")
|
|
}
|
|
}
|
|
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 {
|
|
let group = self.createGroup()
|
|
|
|
// 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()
|
|
}
|
|
group.wait()
|
|
|
|
try self.rpcIssuer?.respond(serverErrorMessage: "TOKEN_EXPIRED")
|
|
|
|
} catch {
|
|
XCTFail("Caught an error in \(#function): \(error)")
|
|
}
|
|
}
|
|
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 {
|
|
let group = self.createGroup()
|
|
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?.newUser, false)
|
|
// User is still signed in.
|
|
XCTAssertEqual(UserTests.auth?.currentUser, user)
|
|
expectation.fulfill()
|
|
}
|
|
group.wait()
|
|
try self.rpcIssuer?.respond(withJSON: ["idToken": RPCBaseTests.kFakeAccessToken,
|
|
"refreshToken": self.kRefreshToken])
|
|
} catch {
|
|
XCTFail("Caught an error in \(#function): \(error)")
|
|
}
|
|
}
|
|
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 {
|
|
let group = self.createGroup()
|
|
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()
|
|
}
|
|
group.wait()
|
|
try self.rpcIssuer?.respond(withJSON: ["idToken": RPCBaseTests.kFakeAccessToken,
|
|
"refreshToken": self.kRefreshToken,
|
|
"federatedId": self.kGoogleID,
|
|
"providerId": GoogleAuthProvider.id,
|
|
"localId": self.kLocalID,
|
|
"displayName": self.kDisplayName,
|
|
"rawUserInfo": self.kGoogleProfile,
|
|
"username": self.kUserName])
|
|
} catch {
|
|
XCTFail("Caught an error in \(#function): \(error)")
|
|
}
|
|
}
|
|
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 {
|
|
let group = self.createGroup()
|
|
|
|
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()
|
|
}
|
|
group.wait()
|
|
try self.rpcIssuer?.respond(withJSON: ["idToken": RPCBaseTests.kFakeAccessToken,
|
|
"refreshToken": self.kRefreshToken])
|
|
} catch {
|
|
XCTFail("Caught an error in \(#function): \(error)")
|
|
}
|
|
}
|
|
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 {
|
|
let group = self.createGroup()
|
|
|
|
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()
|
|
}
|
|
group.wait()
|
|
try self.rpcIssuer?.respond(serverErrorMessage: "USER_NOT_FOUND")
|
|
} catch {
|
|
XCTFail("Caught an error in \(#function): \(error)")
|
|
}
|
|
}
|
|
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.setFakeGetAccountProvider(withProviderID: GoogleAuthProvider.id,
|
|
withFederatedID: self.kGoogleID,
|
|
withEmail: self.kGoogleEmail)
|
|
let group = self.createGroup()
|
|
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()
|
|
}
|
|
group.wait()
|
|
try self.rpcIssuer?.respond(withJSON: ["idToken": RPCBaseTests.kFakeAccessToken,
|
|
"refreshToken": self.kRefreshToken,
|
|
"federatedId": self.kGoogleID,
|
|
"providerId": GoogleAuthProvider.id,
|
|
"localId": self.kLocalID,
|
|
"displayName": self.kDisplayName,
|
|
"rawUserInfo": self.kGoogleProfile,
|
|
"username": self.kUserName])
|
|
} catch {
|
|
XCTFail("Caught an error in \(#function): \(error)")
|
|
}
|
|
}
|
|
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()
|
|
let group = self.createGroup()
|
|
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()
|
|
}
|
|
group.wait()
|
|
try self.rpcIssuer?.respond(serverErrorMessage: "CREDENTIAL_TOO_OLD_LOGIN_AGAIN")
|
|
} catch {
|
|
XCTFail("Caught an error in \(#function): \(error)")
|
|
}
|
|
}
|
|
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()
|
|
let group = self.createGroup()
|
|
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()
|
|
}
|
|
group.wait()
|
|
try self.rpcIssuer?.respond(serverErrorMessage: "USER_DISABLED")
|
|
} catch {
|
|
XCTFail("Caught an error in \(#function): \(error)")
|
|
}
|
|
}
|
|
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.setFakeGetAccountProvider(withProviderID: EmailAuthProvider.id)
|
|
let group = self.createGroup()
|
|
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()
|
|
}
|
|
group.wait()
|
|
try self.rpcIssuer?.respond(withJSON: ["idToken": RPCBaseTests.kFakeAccessToken,
|
|
"refreshToken": self.kRefreshToken])
|
|
} catch {
|
|
XCTFail("Caught an error in \(#function): \(error)")
|
|
}
|
|
}
|
|
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.setFakeGetAccountProvider(withProviderID: EmailAuthProvider.id)
|
|
let group = self.createGroup()
|
|
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()
|
|
}
|
|
}
|
|
group.wait()
|
|
try self.rpcIssuer?.respond(withJSON: ["idToken": RPCBaseTests.kFakeAccessToken,
|
|
"refreshToken": self.kRefreshToken])
|
|
} catch {
|
|
XCTFail("Caught an error in \(#function): \(error)")
|
|
}
|
|
}
|
|
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.setFakeGetAccountProvider(withProviderID: EmailAuthProvider.id)
|
|
let group = self.createGroup()
|
|
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()
|
|
}
|
|
group.wait()
|
|
try self.rpcIssuer?.respond(serverErrorMessage: "TOO_MANY_ATTEMPTS_TRY_LATER")
|
|
} catch {
|
|
XCTFail("Caught an error in \(#function): \(error)")
|
|
}
|
|
}
|
|
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.setFakeGetAccountProvider(withProviderID: EmailAuthProvider.id)
|
|
let group = self.createGroup()
|
|
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()
|
|
}
|
|
group.wait()
|
|
try self.rpcIssuer?.respond(serverErrorMessage: "TOKEN_EXPIRED")
|
|
} catch {
|
|
XCTFail("Caught an error in \(#function): \(error)")
|
|
}
|
|
}
|
|
waitForExpectations(timeout: 5)
|
|
}
|
|
|
|
#if os(iOS)
|
|
private class FakeOAuthProvider: OAuthProvider {
|
|
static let kOAuthSessionID = "sessionID"
|
|
static let kOAuthRequestURI = "requestURI"
|
|
override func getCredentialWith(_ UIDelegate: AuthUIDelegate?,
|
|
completion: ((AuthCredential?, Error?) -> Void)? = nil) {
|
|
if let completion {
|
|
let credential = OAuthCredential(
|
|
withProviderID: GoogleAuthProvider.id,
|
|
sessionID: UserTests.FakeOAuthProvider.kOAuthSessionID,
|
|
OAuthResponseURLString: UserTests.FakeOAuthProvider.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()
|
|
let group = self.createGroup()
|
|
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()
|
|
}
|
|
group.wait()
|
|
try self.rpcIssuer?.respond(serverErrorMessage: "TOKEN_EXPIRED")
|
|
} catch {
|
|
XCTFail("Caught an error in \(#function): \(error)")
|
|
}
|
|
}
|
|
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()
|
|
let group = self.createGroup()
|
|
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()
|
|
}
|
|
group.wait()
|
|
try self.rpcIssuer?.respond(serverErrorMessage: "TOKEN_EXPIRED")
|
|
} catch {
|
|
XCTFail("Caught an error in \(#function): \(error)")
|
|
}
|
|
}
|
|
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)
|
|
let group = self.createGroup()
|
|
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()
|
|
}
|
|
group.wait()
|
|
try self.rpcIssuer?.respond(withJSON: ["idToken": RPCBaseTests.kFakeAccessToken,
|
|
"refreshToken": self.kRefreshToken])
|
|
} catch {
|
|
XCTFail("Caught an error in \(#function): \(error)")
|
|
}
|
|
}
|
|
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)
|
|
let group = self.createGroup()
|
|
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.
|
|
let unlinkGroup = self.createGroup()
|
|
user.unlink(fromProvider: PhoneAuthProvider.id) { user, error in
|
|
XCTAssertNil(error)
|
|
XCTAssertEqual(auth.currentUser, user)
|
|
XCTAssertNil(auth.currentUser?.phoneNumber)
|
|
expectation.fulfill()
|
|
}
|
|
unlinkGroup.wait()
|
|
|
|
do {
|
|
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])
|
|
} catch {
|
|
XCTFail("Caught an error responding for unlink's SetAccountInfoRequest: \(error)")
|
|
}
|
|
}
|
|
group.wait()
|
|
try self.rpcIssuer?.respond(withJSON: ["idToken": RPCBaseTests.kFakeAccessToken,
|
|
"refreshToken": self.kRefreshToken])
|
|
} catch {
|
|
XCTFail("Caught an error in \(#function): \(error)")
|
|
}
|
|
}
|
|
waitForExpectations(timeout: 555)
|
|
}
|
|
|
|
/** @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 exisits error. In this case we
|
|
should get a FIRAuthCredential 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)
|
|
let group = self.createGroup()
|
|
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)
|
|
XCTAssertEqual(credential.phoneNumber, self.kTestPhoneNumber)
|
|
XCTAssertEqual(credential.temporaryProof, "Fake Temporary Proof")
|
|
} catch {
|
|
XCTFail("Did not throw expected error \(error)")
|
|
}
|
|
expectation.fulfill()
|
|
}
|
|
group.wait()
|
|
try self.rpcIssuer?.respond(withJSON: ["idToken": RPCBaseTests.kFakeAccessToken,
|
|
"refreshToken": self.kRefreshToken,
|
|
"phoneNumber": self.kTestPhoneNumber,
|
|
"temporaryProof": "Fake Temporary Proof"])
|
|
} catch {
|
|
XCTFail("Caught an error in \(#function): \(error)")
|
|
}
|
|
}
|
|
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)
|
|
|
|
// TODO: is this right? The ObjC signInSecondFactor property was non-nil,
|
|
// but the test token is nil:
|
|
XCTAssertNil(tokenResult.signInSecondFactor)
|
|
expectation.fulfill()
|
|
}
|
|
}
|
|
waitForExpectations(timeout: 5)
|
|
}
|
|
|
|
private func changeUserEmail(user: User, changePassword: 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)
|
|
|
|
let group = createGroup()
|
|
if changePassword {
|
|
user.updatePassword(to: kNewPassword) { error in
|
|
XCTAssertNil(error)
|
|
XCTAssertEqual(user.displayName, self.kNewDisplayName)
|
|
XCTAssertFalse(user.isAnonymous)
|
|
expectation.fulfill()
|
|
}
|
|
} else {
|
|
user.updateEmail(to: kNewEmail) { error in
|
|
XCTAssertNil(error)
|
|
XCTAssertEqual(user.email, self.kNewEmail)
|
|
XCTAssertEqual(user.displayName, self.kNewDisplayName)
|
|
XCTAssertFalse(user.isAnonymous)
|
|
expectation.fulfill()
|
|
}
|
|
}
|
|
group.wait()
|
|
|
|
let request = try XCTUnwrap(rpcIssuer?.request as? SetAccountInfoRequest)
|
|
XCTAssertEqual(request.apiKey, UserTests.kFakeAPIKey)
|
|
XCTAssertEqual(request.accessToken, RPCBaseTests.kFakeAccessToken)
|
|
if changePassword {
|
|
XCTAssertEqual(request.password, kNewPassword)
|
|
XCTAssertNil(request.email)
|
|
} else {
|
|
XCTAssertEqual(request.email, kNewEmail)
|
|
XCTAssertNil(request.password)
|
|
}
|
|
XCTAssertNil(request.localID)
|
|
XCTAssertNil(request.displayName)
|
|
XCTAssertNil(request.photoURL)
|
|
XCTAssertNil(request.providers)
|
|
XCTAssertNil(request.deleteAttributes)
|
|
XCTAssertNil(request.deleteProviders)
|
|
|
|
try rpcIssuer?.respond(withJSON: ["idToken": RPCBaseTests.kFakeAccessToken,
|
|
"email": kNewEmail,
|
|
"refreshToken": kRefreshToken])
|
|
|
|
} catch {
|
|
XCTFail("Caught an error in \(#function): \(error)")
|
|
}
|
|
}
|
|
|
|
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, AuthTests.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)
|
|
setFakeGetAccountProvider(withProviderID: GoogleAuthProvider.id,
|
|
withFederatedID: kGoogleID,
|
|
withEmail: kGoogleEmail)
|
|
|
|
// 1. Create a group to synchronize request creation by the fake rpcIssuer.
|
|
let group = createGroup()
|
|
|
|
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)
|
|
}
|
|
group.wait()
|
|
|
|
// 2. After the fake rpcIssuer leaves the group, validate the created Request instance.
|
|
verifyGoogleAssertionRequest(try XCTUnwrap(rpcIssuer?.request as? VerifyAssertionRequest))
|
|
|
|
// 3. Send the response from the fake backend.
|
|
try rpcIssuer?.respond(withJSON: ["idToken": RPCBaseTests.kFakeAccessToken,
|
|
"providerId": GoogleAuthProvider.id,
|
|
"refreshToken": kRefreshToken,
|
|
"localId": kLocalID,
|
|
"displayName": kDisplayName,
|
|
"rawUserInfo": kGoogleProfile,
|
|
"username": kUserName])
|
|
|
|
} 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, AuthTests.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)
|
|
|
|
// 1. Create a group to synchronize request creation by the fake rpcIssuer.
|
|
let group = createGroup()
|
|
|
|
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)
|
|
}
|
|
group.wait()
|
|
|
|
// 2. After the fake rpcIssuer leaves the group, validate the created Request instance.
|
|
let request = try XCTUnwrap(rpcIssuer?.request as? VerifyAssertionRequest)
|
|
XCTAssertEqual(request.providerID, FacebookAuthProvider.id)
|
|
XCTAssertEqual(request.providerIDToken, kFacebookIDToken)
|
|
XCTAssertEqual(request.providerAccessToken, kFacebookAccessToken)
|
|
XCTAssertTrue(request.returnSecureToken)
|
|
XCTAssertEqual(request.apiKey, AuthTests.kFakeAPIKey)
|
|
XCTAssertTrue(request.returnSecureToken)
|
|
|
|
// 3. Send the response from the fake backend.
|
|
try rpcIssuer?.respond(withJSON: ["idToken": RPCBaseTests.kFakeAccessToken,
|
|
"refreshToken": kRefreshToken,
|
|
"federatedId": kFacebookID,
|
|
"providerId": FacebookAuthProvider.id,
|
|
"localId": kLocalID,
|
|
"displayName": kDisplayName,
|
|
"rawUserInfo": kGoogleProfile,
|
|
"username": kUserName])
|
|
|
|
} catch {
|
|
XCTFail("Throw in \(#function): \(error)")
|
|
}
|
|
}
|
|
|
|
private func assertUserGoogle(_ user: User?) throws {
|
|
let user = try XCTUnwrap(user)
|
|
XCTAssertEqual(user.uid, kLocalID)
|
|
XCTAssertEqual(user.displayName, kGoogleDisplayName)
|
|
XCTAssertEqual(user.providerData.count, 1)
|
|
let googleUserInfo = user.providerData[0]
|
|
XCTAssertEqual(googleUserInfo.providerID, GoogleAuthProvider.id)
|
|
XCTAssertEqual(googleUserInfo.uid, kGoogleID)
|
|
XCTAssertEqual(googleUserInfo.displayName, kGoogleDisplayName)
|
|
XCTAssertEqual(googleUserInfo.email, kGoogleEmail)
|
|
}
|
|
|
|
// TODO: For testUpdateEmailWithAuthLinkAccountSuccess. Revisit after auth.swift. Should be able to
|
|
// TODO: parameterize with signInWithEmailPasswordReturnFakeUser
|
|
// private func signInWithEmailPasswordReturnFakeUserLink(completion: @escaping (User) -> Void) {
|
|
// let kRefreshToken = "fakeRefreshToken"
|
|
// setFakeSecureTokenService()
|
|
//
|
|
// // 1. Create a group to synchronize request creation by the fake rpcIssuer.
|
|
// let group = createGroup()
|
|
//
|
|
// 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
|
|
// }
|
|
// XCTAssertFalse(additionalUserInfo.isNewUser)
|
|
// XCTAssertEqual(additionalUserInfo.providerID, EmailAuthProvider.id)
|
|
// XCTAssertNil(error)
|
|
// completion(user)
|
|
// }
|
|
// group.wait()
|
|
//
|
|
// // 2. After the fake rpcIssuer leaves the group, validate the created Request instance.
|
|
//// let request = try XCTUnwrap(rpcIssuer?.request as? VerifyPasswordRequest)
|
|
//// XCTAssertEqual(request.email, kEmail)
|
|
//// XCTAssertEqual(request.password, kFakePassword)
|
|
//// XCTAssertEqual(request.apiKey, AuthTests.kFakeAPIKey)
|
|
//// XCTAssertTrue(request.returnSecureToken)
|
|
////
|
|
//// // 3. Send the response from the fake backend.
|
|
//// try rpcIssuer?.respond(withJSON: ["idToken": RPCBaseTests.kFakeAccessToken,
|
|
//// "isNewUser": true,
|
|
//// "refreshToken": kRefreshToken])
|
|
//
|
|
// // waitForExpectations(timeout: 10)
|
|
// // assertUser(AuthTests.auth?.currentUser)
|
|
// } catch {
|
|
// XCTFail("Throw in \(#function): \(error)")
|
|
// }
|
|
// }
|
|
}
|