aboutsummaryrefslogtreecommitdiffhomepage
path: root/Example/Auth/Tests/FIRUserTests.m
diff options
context:
space:
mode:
authorGravatar Xiangtian Dai <xiangtian@google.com>2017-10-02 14:15:05 -0700
committerGravatar GitHub <noreply@github.com>2017-10-02 14:15:05 -0700
commitbf550507ffa8beee149383a5bf1e2363bccefbb4 (patch)
tree234ac2ff5df20d8c54c7cd89b590791f3429a25b /Example/Auth/Tests/FIRUserTests.m
parent7fa0b0de42141d90b8ba3da67cad037becf6f065 (diff)
Automatically signs user out if the token is no longer valid. (#323)
Diffstat (limited to 'Example/Auth/Tests/FIRUserTests.m')
-rw-r--r--Example/Auth/Tests/FIRUserTests.m279
1 files changed, 275 insertions, 4 deletions
diff --git a/Example/Auth/Tests/FIRUserTests.m b/Example/Auth/Tests/FIRUserTests.m
index 3384885..820ade1 100644
--- a/Example/Auth/Tests/FIRUserTests.m
+++ b/Example/Auth/Tests/FIRUserTests.m
@@ -506,9 +506,41 @@ static const NSTimeInterval kExpectationTimeout = 1;
OCMExpect([_mockBackend setAccountInfo:[OCMArg any] callback:[OCMArg any]])
.andDispatchError2([FIRAuthErrorUtils invalidEmailErrorWithMessage:nil]);
[user updateEmail:kNewEmail completion:^(NSError *_Nullable error) {
+ XCTAssertTrue([NSThread isMainThread]);
XCTAssertEqual(error.code, FIRAuthErrorCodeInvalidEmail);
// Email should not have changed on the client side.
XCTAssertEqualObjects(user.email, kEmail);
+ // User is still signed in.
+ XCTAssertEqual([FIRAuth auth].currentUser, user);
+ [expectation fulfill];
+ }];
+ }];
+ [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
+ OCMVerifyAll(_mockBackend);
+}
+
+/** @fn testUpdateEmailAutoSignOut
+ @brief Tests the flow of a failed @c updateEmail:completion: call that automatically signs out.
+ */
+- (void)testUpdateEmailAutoSignOut {
+ id mockGetAccountInfoResponseUser = OCMClassMock([FIRGetAccountInfoResponseUser class]);
+ OCMStub([mockGetAccountInfoResponseUser localID]).andReturn(kLocalID);
+ OCMStub([mockGetAccountInfoResponseUser email]).andReturn(kEmail);
+ OCMStub([mockGetAccountInfoResponseUser displayName]).andReturn(kGoogleDisplayName);
+ OCMStub([mockGetAccountInfoResponseUser passwordHash]).andReturn(kPasswordHash);
+ XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
+ [self signInWithEmailPasswordWithMockUserInfoResponse:mockGetAccountInfoResponseUser
+ completion:^(FIRUser *user) {
+ [self expectGetAccountInfoWithMockUserInfoResponse:mockGetAccountInfoResponseUser];
+ OCMExpect([_mockBackend setAccountInfo:[OCMArg any] callback:[OCMArg any]])
+ .andDispatchError2([FIRAuthErrorUtils invalidUserTokenErrorWithMessage:nil]);
+ [user updateEmail:kNewEmail completion:^(NSError *_Nullable error) {
+ XCTAssertTrue([NSThread isMainThread]);
+ XCTAssertEqual(error.code, FIRAuthErrorCodeInvalidUserToken);
+ // Email should not have changed on the client side.
+ XCTAssertEqualObjects(user.email, kEmail);
+ // User is no longer signed in.
+ XCTAssertNil([FIRAuth auth].currentUser);
[expectation fulfill];
}];
}];
@@ -545,6 +577,7 @@ static const NSTimeInterval kExpectationTimeout = 1;
verificationCode:kVerificationCode];
[user updatePhoneNumberCredential:credential
completion:^(NSError * _Nullable error) {
+ XCTAssertTrue([NSThread isMainThread]);
XCTAssertNil(error);
XCTAssertEqualObjects([FIRAuth auth].currentUser.phoneNumber, kPhoneNumber);
[expectation fulfill];
@@ -572,7 +605,38 @@ static const NSTimeInterval kExpectationTimeout = 1;
[[FIRPhoneAuthProvider provider] credentialWithVerificationID:kVerificationID
verificationCode:kVerificationCode];
[user updatePhoneNumberCredential:credential completion:^(NSError *_Nullable error) {
+ XCTAssertTrue([NSThread isMainThread]);
XCTAssertEqual(error.code, FIRAuthErrorCodeInvalidPhoneNumber);
+ XCTAssertEqual([FIRAuth auth].currentUser, user);
+ [expectation fulfill];
+ }];
+ }];
+ [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
+ OCMVerifyAll(_mockBackend);
+}
+
+/** @fn testUpdatePhoneNumberFailureAutoSignOut
+ @brief Tests the flow of a failed @c updatePhoneNumberCredential:completion: call that
+ automatically signs out.
+ */
+- (void)testUpdatePhoneNumberFailureAutoSignOut {
+ id mockGetAccountInfoResponseUser = OCMClassMock([FIRGetAccountInfoResponseUser class]);
+ OCMStub([mockGetAccountInfoResponseUser localID]).andReturn(kLocalID);
+ OCMStub([mockGetAccountInfoResponseUser email]).andReturn(kEmail);
+ OCMStub([mockGetAccountInfoResponseUser displayName]).andReturn(kGoogleDisplayName);
+ OCMStub([mockGetAccountInfoResponseUser passwordHash]).andReturn(kPasswordHash);
+ XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
+ [self signInWithEmailPasswordWithMockUserInfoResponse:mockGetAccountInfoResponseUser
+ completion:^(FIRUser *user) {
+ OCMExpect([_mockBackend verifyPhoneNumber:[OCMArg any] callback:[OCMArg any]])
+ .andDispatchError2([FIRAuthErrorUtils userTokenExpiredErrorWithMessage:nil]);
+ FIRPhoneAuthCredential *credential =
+ [[FIRPhoneAuthProvider provider] credentialWithVerificationID:kVerificationID
+ verificationCode:kVerificationCode];
+ [user updatePhoneNumberCredential:credential completion:^(NSError *_Nullable error) {
+ XCTAssertTrue([NSThread isMainThread]);
+ XCTAssertEqual(error.code, FIRAuthErrorCodeUserTokenExpired);
+ XCTAssertNil([FIRAuth auth].currentUser);
[expectation fulfill];
}];
}];
@@ -614,6 +678,7 @@ static const NSTimeInterval kExpectationTimeout = 1;
});
});
[user updatePassword:kNewPassword completion:^(NSError *_Nullable error) {
+ XCTAssertTrue([NSThread isMainThread]);
XCTAssertNil(error);
XCTAssertFalse(user.isAnonymous);
[expectation fulfill];
@@ -637,9 +702,11 @@ static const NSTimeInterval kExpectationTimeout = 1;
completion:^(FIRUser *user) {
[self expectGetAccountInfoWithMockUserInfoResponse:mockGetAccountInfoResponseUser];
OCMExpect([_mockBackend setAccountInfo:[OCMArg any] callback:[OCMArg any]])
- .andDispatchError2([FIRAuthErrorUtils userDisabledErrorWithMessage:nil]);
+ .andDispatchError2([FIRAuthErrorUtils requiresRecentLoginErrorWithMessage:nil]);
[user updatePassword:kNewPassword completion:^(NSError *_Nullable error) {
- XCTAssertEqual(error.code, FIRAuthErrorCodeUserDisabled);
+ XCTAssertTrue([NSThread isMainThread]);
+ XCTAssertEqual(error.code, FIRAuthErrorCodeRequiresRecentLogin);
+ XCTAssertEqual([FIRAuth auth].currentUser, user);
[expectation fulfill];
}];
}];
@@ -660,6 +727,7 @@ static const NSTimeInterval kExpectationTimeout = 1;
[self signInWithEmailPasswordWithMockUserInfoResponse:mockGetAccountInfoResponseUser
completion:^(FIRUser *user) {
[user updatePassword:@"" completion:^(NSError *_Nullable error) {
+ XCTAssertTrue([NSThread isMainThread]);
XCTAssertEqual(error.code, FIRAuthErrorCodeWeakPassword);
[expectation fulfill];
}];
@@ -667,6 +735,33 @@ static const NSTimeInterval kExpectationTimeout = 1;
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
}
+/** @fn testUpdatePasswordFailureAutoSignOut
+ @brief Tests the flow of a failed @c updatePassword:completion: call that automatically signs
+ out.
+ */
+- (void)testUpdatePasswordFailureAutoSignOut {
+ id mockGetAccountInfoResponseUser = OCMClassMock([FIRGetAccountInfoResponseUser class]);
+ OCMStub([mockGetAccountInfoResponseUser localID]).andReturn(kLocalID);
+ OCMStub([mockGetAccountInfoResponseUser email]).andReturn(kEmail);
+ OCMStub([mockGetAccountInfoResponseUser displayName]).andReturn(kGoogleDisplayName);
+ OCMStub([mockGetAccountInfoResponseUser passwordHash]).andReturn(kPasswordHash);
+ XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
+ [self signInWithEmailPasswordWithMockUserInfoResponse:mockGetAccountInfoResponseUser
+ completion:^(FIRUser *user) {
+ [self expectGetAccountInfoWithMockUserInfoResponse:mockGetAccountInfoResponseUser];
+ OCMExpect([_mockBackend setAccountInfo:[OCMArg any] callback:[OCMArg any]])
+ .andDispatchError2([FIRAuthErrorUtils userDisabledErrorWithMessage:nil]);
+ [user updatePassword:kNewPassword completion:^(NSError *_Nullable error) {
+ XCTAssertTrue([NSThread isMainThread]);
+ XCTAssertEqual(error.code, FIRAuthErrorCodeUserDisabled);
+ XCTAssertNil([FIRAuth auth].currentUser);
+ [expectation fulfill];
+ }];
+ }];
+ [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
+ OCMVerifyAll(_mockBackend);
+}
+
/** @fn testChangeProfileSuccess
@brief Tests a successful user profile change flow.
*/
@@ -704,6 +799,7 @@ static const NSTimeInterval kExpectationTimeout = 1;
profileChange.photoURL = [NSURL URLWithString:kNewPhotoURL];
profileChange.displayName = kNewDisplayName;
[profileChange commitChangesWithCompletion:^(NSError *_Nullable error) {
+ XCTAssertTrue([NSThread isMainThread]);
XCTAssertNil(error);
XCTAssertEqualObjects(user.displayName, kNewDisplayName);
XCTAssertEqualObjects(user.photoURL, [NSURL URLWithString:kNewPhotoURL]);
@@ -732,8 +828,39 @@ static const NSTimeInterval kExpectationTimeout = 1;
FIRUserProfileChangeRequest *profileChange = [user profileChangeRequest];
profileChange.displayName = kNewDisplayName;
[profileChange commitChangesWithCompletion:^(NSError *_Nullable error) {
+ XCTAssertTrue([NSThread isMainThread]);
XCTAssertEqual(error.code, FIRAuthErrorCodeTooManyRequests);
XCTAssertEqualObjects(user.displayName, kGoogleDisplayName);
+ XCTAssertEqual([FIRAuth auth].currentUser, user);
+ [expectation fulfill];
+ }];
+ }];
+ [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
+ OCMVerifyAll(_mockBackend);
+}
+
+/** @fn testChangeProfileFailureAutoSignOut
+ @brief Tests a failed user profile change flow that automatically signs out.
+ */
+- (void)testChangeProfileFailureAutoSignOut {
+ id mockGetAccountInfoResponseUser = OCMClassMock([FIRGetAccountInfoResponseUser class]);
+ OCMStub([mockGetAccountInfoResponseUser localID]).andReturn(kLocalID);
+ OCMStub([mockGetAccountInfoResponseUser email]).andReturn(kEmail);
+ OCMStub([mockGetAccountInfoResponseUser displayName]).andReturn(kGoogleDisplayName);
+ OCMStub([mockGetAccountInfoResponseUser passwordHash]).andReturn(kPasswordHash);
+ XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
+ [self signInWithEmailPasswordWithMockUserInfoResponse:mockGetAccountInfoResponseUser
+ completion:^(FIRUser *user) {
+ [self expectGetAccountInfoWithMockUserInfoResponse:mockGetAccountInfoResponseUser];
+ OCMExpect([_mockBackend setAccountInfo:[OCMArg any] callback:[OCMArg any]])
+ .andDispatchError2([FIRAuthErrorUtils userNotFoundErrorWithMessage:nil]);
+ FIRUserProfileChangeRequest *profileChange = [user profileChangeRequest];
+ profileChange.displayName = kNewDisplayName;
+ [profileChange commitChangesWithCompletion:^(NSError *_Nullable error) {
+ XCTAssertTrue([NSThread isMainThread]);
+ XCTAssertEqual(error.code, FIRAuthErrorCodeUserNotFound);
+ XCTAssertEqualObjects(user.displayName, kGoogleDisplayName);
+ XCTAssertNil([FIRAuth auth].currentUser);
[expectation fulfill];
}];
}];
@@ -760,6 +887,7 @@ static const NSTimeInterval kExpectationTimeout = 1;
OCMStub([mockGetAccountInfoResponseUserNew passwordHash]).andReturn(kPasswordHash);
[self expectGetAccountInfoWithMockUserInfoResponse:mockGetAccountInfoResponseUserNew];
[user reloadWithCompletion:^(NSError *_Nullable error) {
+ XCTAssertTrue([NSThread isMainThread]);
XCTAssertNil(error);
XCTAssertEqualObjects(user.email, kNewEmail);
XCTAssertEqualObjects(user.displayName, kNewDisplayName);
@@ -782,9 +910,35 @@ static const NSTimeInterval kExpectationTimeout = 1;
[self signInWithEmailPasswordWithMockUserInfoResponse:mockGetAccountInfoResponseUser
completion:^(FIRUser *user) {
OCMExpect([_mockBackend getAccountInfo:[OCMArg any] callback:[OCMArg any]])
+ .andDispatchError2([FIRAuthErrorUtils quotaExceededErrorWithMessage:nil]);
+ [user reloadWithCompletion:^(NSError *_Nullable error) {
+ XCTAssertTrue([NSThread isMainThread]);
+ XCTAssertEqual(error.code, FIRAuthErrorCodeQuotaExceeded);
+ XCTAssertEqual([FIRAuth auth].currentUser, user);
+ [expectation fulfill];
+ }];
+ }];
+ [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
+ OCMVerifyAll(_mockBackend);
+}
+
+/** @fn testReloadFailureAutoSignOut
+ @brief Tests the flow of a failed @c reloadWithCompletion: call that automtatically signs out.
+ */
+- (void)testReloadFailureAutoSignOut {
+ id mockGetAccountInfoResponseUser = OCMClassMock([FIRGetAccountInfoResponseUser class]);
+ OCMStub([mockGetAccountInfoResponseUser localID]).andReturn(kLocalID);
+ OCMStub([mockGetAccountInfoResponseUser email]).andReturn(kEmail);
+ OCMStub([mockGetAccountInfoResponseUser passwordHash]).andReturn(kPasswordHash);
+ XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
+ [self signInWithEmailPasswordWithMockUserInfoResponse:mockGetAccountInfoResponseUser
+ completion:^(FIRUser *user) {
+ OCMExpect([_mockBackend getAccountInfo:[OCMArg any] callback:[OCMArg any]])
.andDispatchError2([FIRAuthErrorUtils userTokenExpiredErrorWithMessage:nil]);
[user reloadWithCompletion:^(NSError *_Nullable error) {
+ XCTAssertTrue([NSThread isMainThread]);
XCTAssertEqual(error.code, FIRAuthErrorCodeUserTokenExpired);
+ XCTAssertNil([FIRAuth auth].currentUser);
[expectation fulfill];
}];
}];
@@ -832,6 +986,7 @@ static const NSTimeInterval kExpectationTimeout = 1;
FIRAuthCredential *emailCredential =
[FIREmailAuthProvider credentialWithEmail:kEmail password:kFakePassword];
[user reauthenticateWithCredential:emailCredential completion:^(NSError *_Nullable error) {
+ XCTAssertTrue([NSThread isMainThread]);
XCTAssertNil(error);
// Verify that the current user is unchanged.
XCTAssertEqual([FIRAuth auth].currentUser, user);
@@ -882,6 +1037,7 @@ static const NSTimeInterval kExpectationTimeout = 1;
completion:^(FIRAuthDataResult *_Nullable
reauthenticateAuthResult,
NSError *_Nullable error) {
+ XCTAssertTrue([NSThread isMainThread]);
XCTAssertNil(error);
// Verify that the current user is unchanged.
XCTAssertEqual([FIRAuth auth].currentUser, authResult.user);
@@ -946,6 +1102,7 @@ static const NSTimeInterval kExpectationTimeout = 1;
FIRAuthCredential *emailCredential =
[FIREmailAuthProvider credentialWithEmail:kEmail password:kFakePassword];
[user reauthenticateWithCredential:emailCredential completion:^(NSError *_Nullable error) {
+ XCTAssertTrue([NSThread isMainThread]);
// Verify user mismatch error.
XCTAssertEqual(error.code, FIRAuthErrorCodeUserMismatch);
// Verify that the current user is unchanged.
@@ -980,6 +1137,7 @@ static const NSTimeInterval kExpectationTimeout = 1;
FIRAuthCredential *googleCredential =
[FIRGoogleAuthProvider credentialWithIDToken:kGoogleIDToken accessToken:kGoogleAccessToken];
[user reauthenticateWithCredential:googleCredential completion:^(NSError *_Nullable error) {
+ XCTAssertTrue([NSThread isMainThread]);
// Verify user mismatch error.
XCTAssertEqual(error.code, FIRAuthErrorCodeUserMismatch);
// Verify that the current user is unchanged.
@@ -1030,6 +1188,7 @@ static const NSTimeInterval kExpectationTimeout = 1;
completion:^(FIRAuthDataResult *_Nullable
linkAuthResult,
NSError *_Nullable error) {
+ XCTAssertTrue([NSThread isMainThread]);
XCTAssertNil(error);
// Verify that the current user is unchanged.
XCTAssertEqual([FIRAuth auth].currentUser, authResult.user);
@@ -1081,7 +1240,8 @@ static const NSTimeInterval kExpectationTimeout = 1;
.andCallBlock2(^(FIRVerifyAssertionRequest *_Nullable request,
FIRVerifyAssertionResponseCallback callback) {
dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
- callback(nil, [FIRAuthErrorUtils userDisabledErrorWithMessage:nil]);
+ callback(nil,
+ [FIRAuthErrorUtils accountExistsWithDifferentCredentialErrorWithEmail:kEmail]);
});
});
@@ -1091,8 +1251,11 @@ static const NSTimeInterval kExpectationTimeout = 1;
completion:^(FIRAuthDataResult *_Nullable
linkAuthResult,
NSError *_Nullable error) {
+ XCTAssertTrue([NSThread isMainThread]);
XCTAssertNil(linkAuthResult);
- XCTAssertEqual(error.code, FIRAuthErrorCodeUserDisabled);
+ XCTAssertEqual(error.code, FIRAuthErrorCodeAccountExistsWithDifferentCredential);
+ XCTAssertEqual(error.userInfo[FIRAuthErrorUserInfoEmailKey], kEmail);
+ XCTAssertEqual([FIRAuth auth].currentUser, authResult.user);
[expectation fulfill];
}];
}];
@@ -1131,8 +1294,60 @@ static const NSTimeInterval kExpectationTimeout = 1;
completion:^(FIRAuthDataResult *_Nullable
linkAuthResult,
NSError *_Nullable error) {
+ XCTAssertTrue([NSThread isMainThread]);
XCTAssertNil(linkAuthResult);
XCTAssertEqual(error.code, FIRAuthErrorCodeProviderAlreadyLinked);
+ XCTAssertEqual([FIRAuth auth].currentUser, authResult.user);
+ [expectation fulfill];
+ }];
+ }];
+ [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
+ OCMVerifyAll(_mockBackend);
+}
+
+/** @fn testlinkAndRetrieveDataErrorAutoSignOut
+ @brief Tests the flow of an unsuccessful @c linkAndRetrieveDataWithCredential:completion:
+ call that automatically signs out.
+ */
+- (void)testlinkAndRetrieveDataErrorAutoSignOut {
+ [self expectVerifyAssertionRequest:FIRFacebookAuthProviderID
+ federatedID:kFacebookID
+ displayName:kFacebookDisplayName
+ profile:[[self class] googleProfile]
+ providerIDToken:kFacebookIDToken
+ providerAccessToken:kFacebookAccessToken];
+ XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
+ [[FIRAuth auth] signOut:NULL];
+ FIRAuthCredential *facebookCredential =
+ [FIRFacebookAuthProvider credentialWithAccessToken:kFacebookAccessToken];
+ [[FIRAuth auth] signInAndRetrieveDataWithCredential:facebookCredential
+ completion:^(FIRAuthDataResult *_Nullable authResult,
+ NSError *_Nullable error) {
+ XCTAssertTrue([NSThread isMainThread]);
+ [self assertUserFacebook:authResult.user];
+ XCTAssertEqualObjects(authResult.additionalUserInfo.profile, [[self class] googleProfile]);
+ XCTAssertEqualObjects(authResult.additionalUserInfo.username, kUserName);
+ XCTAssertEqualObjects(authResult.additionalUserInfo.providerID, FIRFacebookAuthProviderID);
+ XCTAssertNil(error);
+
+ OCMExpect([_mockBackend verifyAssertion:[OCMArg any] callback:[OCMArg any]])
+ .andCallBlock2(^(FIRVerifyAssertionRequest *_Nullable request,
+ FIRVerifyAssertionResponseCallback callback) {
+ dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
+ callback(nil, [FIRAuthErrorUtils userDisabledErrorWithMessage:nil]);
+ });
+ });
+
+ FIRAuthCredential *linkGoogleCredential =
+ [FIRGoogleAuthProvider credentialWithIDToken:kGoogleIDToken accessToken:kGoogleAccessToken];
+ [authResult.user linkAndRetrieveDataWithCredential:linkGoogleCredential
+ completion:^(FIRAuthDataResult *_Nullable
+ linkAuthResult,
+ NSError *_Nullable error) {
+ XCTAssertTrue([NSThread isMainThread]);
+ XCTAssertNil(linkAuthResult);
+ XCTAssertEqual(error.code, FIRAuthErrorCodeUserDisabled);
+ XCTAssertNil([FIRAuth auth].currentUser);
[expectation fulfill];
}];
}];
@@ -1195,6 +1410,7 @@ static const NSTimeInterval kExpectationTimeout = 1;
completion:^(FIRAuthDataResult *_Nullable
linkAuthResult,
NSError *_Nullable error) {
+ XCTAssertTrue([NSThread isMainThread]);
XCTAssertNil(error);
XCTAssertEqualObjects(linkAuthResult.user.email, kEmail);
XCTAssertEqualObjects(linkAuthResult.user.displayName, kEmailDisplayName);
@@ -1271,6 +1487,7 @@ static const NSTimeInterval kExpectationTimeout = 1;
completion:^(FIRAuthDataResult *_Nullable
linkAuthResult,
NSError *_Nullable error) {
+ XCTAssertTrue([NSThread isMainThread]);
XCTAssertNil(linkAuthResult);
XCTAssertEqual(error.code, FIRAuthErrorCodeProviderAlreadyLinked);
[expectation fulfill];
@@ -1327,6 +1544,60 @@ static const NSTimeInterval kExpectationTimeout = 1;
XCTAssertTrue([NSThread isMainThread]);
XCTAssertNil(linkAuthResult);
XCTAssertEqual(error.code, FIRAuthErrorCodeTooManyRequests);
+ XCTAssertEqual([FIRAuth auth].currentUser, authResult.user);
+ [expectation fulfill];
+ }];
+ }];
+ [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
+ OCMVerifyAll(_mockBackend);
+}
+
+/** @fn testlinkEmailAndRetrieveDataError
+ @brief Tests the flow of an unsuccessful @c linkAndRetrieveDataWithCredential:completion:
+ invocation that automatically signs out.
+ */
+- (void)testlinkEmailAndRetrieveDataErrorAutoSignOut {
+ [self expectVerifyAssertionRequest:FIRFacebookAuthProviderID
+ federatedID:kFacebookID
+ displayName:kFacebookDisplayName
+ profile:[[self class] googleProfile]
+ providerIDToken:kFacebookIDToken
+ providerAccessToken:kFacebookAccessToken];
+
+ XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
+ [[FIRAuth auth] signOut:NULL];
+ FIRAuthCredential *facebookCredential =
+ [FIRFacebookAuthProvider credentialWithAccessToken:kFacebookAccessToken];
+ [[FIRAuth auth] signInAndRetrieveDataWithCredential:facebookCredential
+ completion:^(FIRAuthDataResult *_Nullable authResult,
+ NSError *_Nullable error) {
+ XCTAssertTrue([NSThread isMainThread]);
+ [self assertUserFacebook:authResult.user];
+ XCTAssertEqualObjects(authResult.additionalUserInfo.profile, [[self class] googleProfile]);
+ XCTAssertEqualObjects(authResult.additionalUserInfo.username, kUserName);
+ XCTAssertEqualObjects(authResult.additionalUserInfo.providerID, FIRFacebookAuthProviderID);
+ XCTAssertNil(error);
+
+ OCMExpect([_mockBackend getAccountInfo:[OCMArg any] callback:[OCMArg any]])
+ .andCallBlock2(^(FIRGetAccountInfoRequest *_Nullable request,
+ FIRGetAccountInfoResponseCallback callback) {
+ XCTAssertEqualObjects(request.APIKey, kAPIKey);
+ XCTAssertEqualObjects(request.accessToken, kAccessToken);
+ dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
+ callback(nil, [FIRAuthErrorUtils userTokenExpiredErrorWithMessage:nil]);
+ });
+ });
+
+ FIRAuthCredential *linkEmailCredential =
+ [FIREmailAuthProvider credentialWithEmail:kEmail password:kFakePassword];
+ [authResult.user linkAndRetrieveDataWithCredential:linkEmailCredential
+ completion:^(FIRAuthDataResult *_Nullable
+ linkAuthResult,
+ NSError *_Nullable error) {
+ XCTAssertTrue([NSThread isMainThread]);
+ XCTAssertNil(linkAuthResult);
+ XCTAssertEqual(error.code, FIRAuthErrorCodeUserTokenExpired);
+ XCTAssertNil([FIRAuth auth].currentUser);
[expectation fulfill];
}];
}];