Files
firebase-ios-sdk/FirebaseAuth/Tests/Unit/FIRUserTests.m
T
Rosalyn Tan 14764b8d60 Remove deprecated auth APIs (#6607)
* Remove deprecated APIs from public header.

* Remove deprecated API implementations.

* Also remove deprecated APIs from FIRUser.

* Update unit tests.

* Update sample app.

* Update changelog.
2020-09-30 15:51:09 -07:00

3138 lines
193 KiB
Objective-C

/*
* Copyright 2017 Google
*
* 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/Foundation.h>
#import <XCTest/XCTest.h>
#import "OCMock.h"
#import "FirebaseAuth/Sources/Public/FirebaseAuth/FIRAuthTokenResult.h"
#import "FirebaseAuth/Sources/Public/FirebaseAuth/FIREmailAuthProvider.h"
#import "FirebaseAuth/Sources/Public/FirebaseAuth/FIRFacebookAuthProvider.h"
#import "FirebaseAuth/Sources/Public/FirebaseAuth/FIRGoogleAuthProvider.h"
#import "FirebaseAuth/Sources/Public/FirebaseAuth/FIRUserInfo.h"
#import "FirebaseAuth/Sources/Public/FirebaseAuth/FIRUserMetadata.h"
#import "FirebaseAuth/Sources/Auth/FIRAuthGlobalWorkQueue.h"
#import "FirebaseAuth/Sources/Auth/FIRAuthOperationType.h"
#import "FirebaseAuth/Sources/Auth/FIRAuth_Internal.h"
#import "FirebaseAuth/Sources/Backend/FIRAuthBackend.h"
#import "FirebaseAuth/Sources/Backend/RPC/FIREmailLinkSignInResponse.h"
#import "FirebaseAuth/Sources/Backend/RPC/FIRGetAccountInfoRequest.h"
#import "FirebaseAuth/Sources/Backend/RPC/FIRGetAccountInfoResponse.h"
#import "FirebaseAuth/Sources/Backend/RPC/FIRSecureTokenRequest.h"
#import "FirebaseAuth/Sources/Backend/RPC/FIRSecureTokenResponse.h"
#import "FirebaseAuth/Sources/Backend/RPC/FIRSetAccountInfoRequest.h"
#import "FirebaseAuth/Sources/Backend/RPC/FIRSetAccountInfoResponse.h"
#import "FirebaseAuth/Sources/Backend/RPC/FIRSignUpNewUserResponse.h"
#import "FirebaseAuth/Sources/Backend/RPC/FIRVerifyAssertionRequest.h"
#import "FirebaseAuth/Sources/Backend/RPC/FIRVerifyAssertionResponse.h"
#import "FirebaseAuth/Sources/Backend/RPC/FIRVerifyPasswordRequest.h"
#import "FirebaseAuth/Sources/Backend/RPC/FIRVerifyPasswordResponse.h"
#import "FirebaseAuth/Sources/Backend/RPC/FIRVerifyPhoneNumberRequest.h"
#import "FirebaseAuth/Sources/Backend/RPC/FIRVerifyPhoneNumberResponse.h"
#import "FirebaseAuth/Sources/SystemService/FIRSecureTokenService.h"
#import "FirebaseAuth/Sources/User/FIRAdditionalUserInfo_Internal.h"
#import "FirebaseAuth/Sources/User/FIRUser_Internal.h"
#import "FirebaseAuth/Sources/Utilities/FIRAuthErrorUtils.h"
#import "FirebaseAuth/Tests/Unit/FIRApp+FIRAuthUnitTests.h"
#import "FirebaseAuth/Tests/Unit/OCMStubRecorder+FIRAuthUnitTests.h"
#if TARGET_OS_IOS
#import "FirebaseAuth/Sources/Public/FirebaseAuth/FIRPhoneAuthProvider.h"
#import "FirebaseAuth/Sources/AuthProvider/Phone/FIRPhoneAuthCredential_Internal.h"
#endif
NS_ASSUME_NONNULL_BEGIN
/** @var kAPIKey
@brief The fake API key.
*/
static NSString *const kAPIKey = @"FAKE_API_KEY";
/** @var kAccessToken
@brief The fake access token.
*/
static NSString *const kAccessToken =
@"eyJhbGciOimnuzI1NiIsImtpZCI6ImY1YjE4Mjc2YTQ4NjYxZDBhODBiYzh"
"jM2U5NDM0OTc0ZDFmMWRiNTEifQ."
"eyJpc3MiOiJodHRwczovL3NlY3VyZXRva2VuLmdvb2dsZS5jb20vZmItc2EtdXBncm"
"FkZWQiLCJhdWQiOiJ0ZXN0X2F1ZCIsImF1dGhfdGltZSI6MTUyMjM2MDU0OSwidXNlcl9pZCI6InRlc3RfdXNlcl9pZCI"
"s"
"InN1YiI6InRlc3Rfc3ViIiwiaWF0IjoxNTIyMzYwNTU3LCJleHAiOjE1MjIzNjQxNTcsImVtYWlsIjoiYXVuaXRlc3R1c"
"2"
"VyQGdtYWlsLmNvbSIsImVtYWlsX3ZlcmlmaWVkIjpmYWxzZSwiZmlyZWJhc2UiOnsiaWRlbnRpdGllcyI6eyJlbWFpbCI"
"6"
"WyJhdW5pdGVzdHVzZXJAZ21haWwuY29tIl19LCJzaWduX2luX3Byb3ZpZGVyIjoicGFzc3dvcmQifX0=."
"WFQqSrpVnxx7m"
"UrdKZA517Sp4ZBt-l2xQzGKNMVE90JB3vuNa-NyWZC-aTYMvND3-4aS3qRnN2kvk9KJAaF3eI_"
"BKkcbZuq8O7iDVpOvqKC"
"3QcW0PnwqSPChL3XqoDF322FcBEgemwwgaEVZMuo7GhJvHw-"
"XtBt1KRXOoGHcr3P6RsvoulUouKQmqt6TP27eZtrgH7jjN"
"hHm7gjX_WaRmgTOvYsuDbBBGdE15yIVZ3acI4cFUgwMRhaW-"
"dDV7jTOqZGYJlTsI5oRMehphoVnYnEedJga28r4mqVkPbW"
"lddL4dVVm85FYmQcRc0b2CLMnSevBDlwu754ZUZmRgnuvDA";
/** @var kAccessTokenWithBase64URLCharacters
@brief The fake access where the AUD value is "??????????>>>>>>>>>>" and the email value is
">>>>>>>>????????@gmail.com".
*/
static NSString *const kAccessTokenWithBase64URLCharacter =
@"ey?hbGciOimnuzI1NiIsImtpZCI6ImY1YjE4M"
"jc2YTQ4NjYxZDBhODBiYzhjM2U5NDM0OTc0ZDFmMWRiNTEifQ."
"eyJpc3MiOiJodHRwczovL3NlY3VyZXRva2VuLmdvb2ds"
"ZS5jb20vZmItc2EtdXBncmFkZWQiLCJhdWQiOiI_Pz8_Pz8_Pz8_Pj4-Pj4-Pj4-"
"PiIsImF1dGhfdGltZSI6MTUyMjM2MD"
"U0OSwidXNlcl9pZCI6InRlc3RfdXNlcl9pZCIsInN1YiI6InRlc3Rfc3ViIiwiaWF0IjoxNTIyMzYwNTU3LCJleHAiOjE"
"1"
"MjIzNjQxNTcsImVtYWlsIjoiPj4-Pj4-Pj4_Pz8_Pz8_"
"P0BnbWFpbC5jb20iLCJlbWFpbF92ZXJpZmllZCI6ZmFsc2UsIm"
"ZpcmViYXNlIjp7ImlkZW50aXRpZXMiOnsiZW1haWwiOlsiYXVuaXRlc3R1c2VyQGdtYWlsLmNvbSJdfSwic2lnbl9pbl9"
"w"
"cm92aWRlciI6IlBhc3N3b3JkIn19.WFQqSrpVnxx7mUrdKZA517Sp4ZBt-l2xQzGKNMVE90JB3vuNa-NyWZC-"
"aTYMvND3-"
"4aS3qRnN2kvk9KJAaF3eI_BKkcbZuq8O7iDVpOvqKC3QcW0PnwqSPChL3XqoDF322FcBEgemwwgaEVZMuo7GhJvHw-"
"XtBt"
"1KRXOoGHcr3P6RsvoulUouKQmqt6TP27eZtrgH7jjNhHm7gjX_WaRmgTOvYsuDbBBGdE15yIVZ3acI4cFUgwMRhaW-"
"dDV7"
"jTOqZGYJlTsI5oRMehphoVnYnEedJga28r4mqVkPbWlddL4dVVm85FYmQcRc0b2CLMnSevBDlwu754ZUZmRgnuvDA";
/** @var kbase64URLEncodedEmail
@brief The fake email address with a value containing non-valid base64 encoded characters.
@remarks This is used to ensure that the token parser is able to handle base64 URL encoded
strings. Note that consecutive question marks in a string without being escaped is not
legal C99.
*/
static NSString *const kbase64URLEncodedEmail = @">>>>>>>>\?\?\?\?\?\?\?\?@gmail.com";
/** @var kbase64URLEncodedAUD
@brief The fake AUD with a value containing non-valid base64 encoded characters.
@remarks This is used to ensure that the token parser is able to handle base64 URL encoded
strings. Note that consecutive question marks in a string without being escaped is not
legal C99.
*/
static NSString *const kbase64URLEncodedAUD = @"\?\?\?\?\?\?\?\?\?\?>>>>>>>>>>";
/** @var kAccessTokenLength415
@brief The fake access token with 415 characters in the claims potion of the token.
*/
static NSString *const kAccessTokenLength415 =
@"eyJhbGciOimnuzI1NiIsImtpZCI6ImY1YjE4Mjc2YTQ4NjYxZD"
"BhODBiYzhjM2U5NDM0OTc0ZDFmMWRiNTEifQ."
"eyJpc3MiOiJodHRwczovL3NlY3VyZXRva2VuLmdvb2dsZS5jb20vdGVzd"
"CIsImF1ZCI6InRlc3RfYXVkIiwiYXV0aF90aW1lIjoxNTIyMzYwNTQ5LCJ1c2VyX2lkIjoidGVzdF91c2VyX2lkIiwic3"
"V"
"iIjoidGVzdF9zdWIiLCJpYXQiOjE1MjIzNjA1NTcsImV4cCI6MTUyMjM2NDE1NywiZW1haWwiOiJhdW5pdGVzdHVzZXJA"
"Z"
"21haWwuY29tIiwiZW1haWxfdmVyaWZpZWQiOmZhbHNlLCJmaXJlYmFzZSI6eyJpZGVudGl0aWVzIjp7ImVtYWlsIjpbIm"
"F"
"1bml0ZXN0dXNlckBnbWFpbC5jb20iXX0sInNpZ25faW5fcHJvdmlkZXIiOiJwYXNzd29yZCJ9fQ=.WFQqSrpVnxx7m"
"UrdKZA517Sp4ZBt-l2xQzGKNMVE90JB3vuNa-NyWZC-aTYMvND3-4aS3qRnN2kvk9KJAaF3eI_"
"BKkcbZuq8O7iDVpOvqKC"
"3QcW0PnwqSPChL3XqoDF322FcBEgemwwgaEVZMuo7GhJvHw-"
"XtBt1KRXOoGHcr3P6RsvoulUouKQmqt6TP27eZtrgH7jjN"
"hHm7gjX_WaRmgTOvYsuDbBBGdE15yIVZ3acI4cFUgwMRhaW-"
"dDV7jTOqZGYJlTsI5oRMehphoVnYnEedJga28r4mqVkPbW"
"lddL4dVVm85FYmQcRc0b2CLMnSevBDlwu754ZUZmRgnuvDA";
/** @var kAccessTokenLength416
@brief The fake access token with 416 characters in the claims potion of the token.
*/
static NSString *const kAccessTokenLength416 =
@"eyJhbGciOimnuzI1NiIsImtpZCI6ImY1YjE4Mjc2YTQ4NjYxZD"
"BhODBiYzhjM2U5NDM0OTc0ZDFmMWRiNTEifQ."
"eyJpc3MiOiJodHRwczovL3NlY3VyZXRva2VuLmdvb2dsZS5jb20vdGVzd"
"DIiLCJhdWQiOiJ0ZXN0X2F1ZCIsImF1dGhfdGltZSI6MTUyMjM2MDU0OSwidXNlcl9pZCI6InRlc3RfdXNlcl9pZCIsIn"
"N"
"1YiI6InRlc3Rfc3ViIiwiaWF0IjoxNTIyMzYwNTU3LCJleHAiOjE1MjIzNjQxNTcsImVtYWlsIjoiYXVuaXRlc3R1c2Vy"
"Q"
"GdtYWlsLmNvbSIsImVtYWlsX3ZlcmlmaWVkIjpmYWxzZSwiZmlyZWJhc2UiOnsiaWRlbnRpdGllcyI6eyJlbWFpbCI6Wy"
"J"
"hdW5pdGVzdHVzZXJAZ21haWwuY29tIl19LCJzaWduX2luX3Byb3ZpZGVyIjoicGFzc3dvcmQifX0=.WFQqSrpVnxx7m"
"UrdKZA517Sp4ZBt-l2xQzGKNMVE90JB3vuNa-NyWZC-aTYMvND3-4aS3qRnN2kvk9KJAaF3eI_"
"BKkcbZuq8O7iDVpOvqKC"
"3QcW0PnwqSPChL3XqoDF322FcBEgemwwgaEVZMuo7GhJvHw-"
"XtBt1KRXOoGHcr3P6RsvoulUouKQmqt6TP27eZtrgH7jjN"
"hHm7gjX_WaRmgTOvYsuDbBBGdE15yIVZ3acI4cFUgwMRhaW-"
"dDV7jTOqZGYJlTsI5oRMehphoVnYnEedJga28r4mqVkPbW"
"lddL4dVVm85FYmQcRc0b2CLMnSevBDlwu754ZUZmRgnuvDA";
/** @var kAccessTokenLength4523
@brief The fake access token with 523 characters in the claims potion of the token.
*/
static NSString *const kAccessTokenLength523 =
@"eyJhbGciOimnuzI1NiIsImtpZCI6ImY1YjE4Mjc2YTQ4NjYxZD"
"BhODBiYzhjM2U5NDM0OTc0ZDFmMWRiNTEifQ."
"eyJpc3MiOiJodHRwczovL3NlY3VyZXRva2VuLmdvb2dsZS5jb20vdGVzd"
"DQiLCJhdWQiOiJ0ZXN0X2F1ZCIsImF1dGhfdGltZSI6MTUyMjM2MDU0OSwidXNlcl9pZCI6InRlc3RfdXNlcl9pZF81ND"
"M"
"yIiwic3ViIjoidGVzdF9zdWIiLCJpYXQiOjE1MjIzNjA1NTcsImV4cCI6MTUyMjM2NDE1OSwiZW1haWwiOiJhdW5pdGVz"
"d"
"HVzZXI0QGdtYWlsLmNvbSIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJmaXJlYmFzZSI6eyJpZGVudGl0aWVzIjp7ImVtYW"
"l"
"sIjpbImF1bml0ZXN0dXNlckBnbWFpbC5jb20iXX0sInNpZ25faW5fcHJvdmlkZXIiOiJwYXNzd29yZCJ9fQ=."
"WFQqSrpVn"
"xx7mUrdKZA517Sp4ZBt-l2xQzGKNMVE90JB3vuNa-NyWZC-aTYMvND3-4aS3qRnN2kvk9KJAaF3eI_"
"BKkcbZuq8O7iDVpO"
"vqKC3QcW0PnwqSPChL3XqoDF322FcBEgemwwgaEVZMuo7GhJvHw-"
"XtBt1KRXOoGHcr3P6RsvoulUouKQmqt6TP27eZtrgH"
"7jjNhHm7gjX_WaRmgTOvYsuDbBBGdE15yIVZ3acI4cFUgwMRhaW-"
"dDV7jTOqZGYJlTsI5oRMehphoVnYnEedJga28r4mqV"
"kPbWlddL4dVVm85FYmQcRc0b2CLMnSevBDlwu754ZUZmRgnuvDA";
/** @var kNewAccessToken
@brief A new value for the fake access token.
*/
static NSString *const kNewAccessToken = @"NEW_ACCESS_TOKEN";
/** @var kAccessTokenValidInterval
@brief The time to live for the fake access token.
*/
static const NSTimeInterval kAccessTokenTimeToLive = 60 * 60;
/** @var kRefreshToken
@brief The fake refresh token.
*/
static NSString *const kRefreshToken = @"REFRESH_TOKEN";
/** @var kLocalID
@brief The fake local user ID.
*/
static NSString *const kLocalID = @"LOCAL_ID";
/** @var kAnotherLocalID
@brief The fake local ID of another user.
*/
static NSString *const kAnotherLocalID = @"ANOTHER_LOCAL_ID";
/** @var kGoogleIDToken
@brief The fake ID token from Google Sign-In.
*/
static NSString *const kGoogleIDToken = @"GOOGLE_ID_TOKEN";
/** @var kFacebookIDToken
@brief The fake ID token from Facebook Sign-In. Facebook provider ID token is always nil.
*/
static NSString *const kFacebookIDToken = nil;
/** @var kGoogleAccessToken
@brief The fake access token from Google Sign-In.
*/
static NSString *const kGoogleAccessToken = @"GOOGLE_ACCESS_TOKEN";
/** @var kFacebookAccessToken
@brief The fake access token from Facebook Sign-In.
*/
static NSString *const kFacebookAccessToken = @"FACEBOOK_ACCESS_TOKEN";
/** @var kEmail
@brief The fake user email.
*/
static NSString *const kEmail = @"user@company.com";
/** @var kPhoneNumber
@brief The fake user phone number.
*/
static NSString *const kPhoneNumber = @"12345658";
/** @var kTemporaryProof
@brief The fake temporary proof.
*/
static NSString *const kTemporaryProof = @"12345658";
/** @var kNewEmail
@brief A new value for the fake user email.
*/
static NSString *const kNewEmail = @"newuser@company.com";
/** @var kUserName
@brief The fake user name.
*/
static NSString *const kUserName = @"User Doe";
/** @var kNewDisplayName
@brief A new value for the fake user display name.
*/
static NSString *const kNewDisplayName = @"New User Doe";
/** @var kPhotoURL
@brief The fake user profile image URL string.
*/
static NSString *const kPhotoURL = @"https://host.domain/image";
/** @var kNewPhotoURL
@brief A new value for the fake user profile image URL string..
*/
static NSString *const kNewPhotoURL = @"https://host.domain/new/image";
/** @var kFakePassword
@brief The fake user password.
*/
static NSString *const kFakePassword = @"123456";
/** @var kNewPassword
@brief The fake new user password.
*/
static NSString *const kNewPassword = @"1234567";
/** @var kPasswordHash
@brief The fake user password hash.
*/
static NSString *const kPasswordHash = @"UkVEQUNURUQ=";
/** @var kGoogleUD
@brief The fake user ID under Google Sign-In.
*/
static NSString *const kGoogleID = @"GOOGLE_ID";
/** @var kGoogleEmail
@brief The fake user email under Google Sign-In.
*/
static NSString *const kGoogleEmail = @"user@gmail.com";
/** @var kGoogleDisplayName
@brief The fake user display name under Google Sign-In.
*/
static NSString *const kGoogleDisplayName = @"Google Doe";
/** @var kEmailDisplayName
@brief The fake user display name for email password user.
*/
static NSString *const kEmailDisplayName = @"Email Doe";
/** @var kFacebookDisplayName
@brief The fake user display name under Facebook Sign-In.
*/
static NSString *const kFacebookDisplayName = @"Facebook Doe";
/** @var kGooglePhotoURL
@brief The fake user profile image URL string under Google Sign-In.
*/
static NSString *const kGooglePhotoURL = @"https://googleusercontents.com/user/profile";
/** @var kFacebookID
@brief The fake user ID under Facebook Login.
*/
static NSString *const kFacebookID = @"FACEBOOK_ID";
/** @var kFacebookEmail
@brief The fake user email under Facebook Login.
*/
static NSString *const kFacebookEmail = @"user@facebook.com";
/** @var kVerificationCode
@brief Fake verification code used for testing.
*/
static NSString *const kVerificationCode = @"12345678";
/** @var kVerificationID
@brief Fake verification ID for testing.
*/
static NSString *const kVerificationID = @"55432";
/** @var kUserArchiverKey
@brief The key used to archive and unarchive the user object for test NSSecureCoding.
*/
static NSString *const kUserArchiverKey = @"userArchiverKey";
/** @var kCreationDateInSeconds
@brief The fake creation date.
*/
static NSTimeInterval const kCreationDateTimeIntervalInSeconds = 1505858500;
/** @var kLastSignInDateTimeIntervalInSeconds
@brief The fake last sign in date date.
*/
static NSTimeInterval const kLastSignInDateTimeIntervalInSeconds = 1505858583;
/** @var kExpectationTimeout
@brief The maximum time waiting for expectations to fulfill.
*/
static const NSTimeInterval kExpectationTimeout = 2;
/** @extention FIRSecureTokenService
@brief Extends the FIRSecureTokenService class to expose one private method for testing only.
*/
@interface FIRSecureTokenService ()
/** @fn hasValidAccessToken
@brief private method exposed so it can be mocked to prevent the fake expiration date from
affecting the the unit tests.
*/
- (BOOL)hasValidAccessToken;
@end
/** @class FIRUserTests
@brief Tests for @c FIRUser .
*/
@interface FIRUserTests : XCTestCase
@end
@implementation FIRUserTests {
/** @var _mockBackend
@brief The mock @c FIRAuthBackendImplementation .
*/
id _mockBackend;
}
/** @fn googleProfile
@brief The fake user profile under additional user data in @c FIRVerifyAssertionResponse.
*/
+ (NSDictionary *)googleProfile {
static NSDictionary *kGoogleProfile = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
kGoogleProfile = @{@"email" : kGoogleEmail, @"given_name" : @"User", @"family_name" : @"Doe"};
});
return kGoogleProfile;
}
- (void)setUp {
[super setUp];
_mockBackend = OCMProtocolMock(@protocol(FIRAuthBackendImplementation));
[FIRAuthBackend setBackendImplementation:_mockBackend];
[FIRApp resetAppForAuthUnitTests];
}
- (void)tearDown {
[FIRAuthBackend setDefaultBackendImplementationWithRPCIssuer:nil];
[super tearDown];
}
#pragma mark - Tests
/** @fn testUserPropertiesAndNSSecureCoding
@brief Tests properties of the @c FIRUser instance before and after being
serialized/deserialized.
*/
- (void)testUserPropertiesAndNSSecureCoding {
// Mock auth provider user info for email/password for GetAccountInfo.
id mockPasswordUserInfo = OCMClassMock([FIRGetAccountInfoResponseProviderUserInfo class]);
OCMStub([mockPasswordUserInfo providerID]).andReturn(FIREmailAuthProviderID);
OCMStub([mockPasswordUserInfo federatedID]).andReturn(kEmail);
OCMStub([mockPasswordUserInfo email]).andReturn(kEmail);
// Mock auth provider user info from Google for GetAccountInfo.
id mockGoogleUserInfo = OCMClassMock([FIRGetAccountInfoResponseProviderUserInfo class]);
OCMStub([mockGoogleUserInfo providerID]).andReturn(FIRGoogleAuthProviderID);
OCMStub([mockGoogleUserInfo displayName]).andReturn(kGoogleDisplayName);
OCMStub([mockGoogleUserInfo photoURL]).andReturn([NSURL URLWithString:kGooglePhotoURL]);
OCMStub([mockGoogleUserInfo federatedID]).andReturn(kGoogleID);
OCMStub([mockGoogleUserInfo email]).andReturn(kGoogleEmail);
// Mock auth provider user info from Facebook for GetAccountInfo.
id mockFacebookUserInfo = OCMClassMock([FIRGetAccountInfoResponseProviderUserInfo class]);
OCMStub([mockFacebookUserInfo providerID]).andReturn(FIRFacebookAuthProviderID);
OCMStub([mockFacebookUserInfo federatedID]).andReturn(kFacebookID);
OCMStub([mockFacebookUserInfo email]).andReturn(kFacebookEmail);
#if TARGET_OS_IOS
// Mock auth provider user info from Phone auth provider for GetAccountInfo.
id mockPhoneUserInfo = OCMClassMock([FIRGetAccountInfoResponseProviderUserInfo class]);
OCMStub([mockPhoneUserInfo providerID]).andReturn(FIRPhoneAuthProviderID);
OCMStub([mockPhoneUserInfo phoneNumber]).andReturn(kPhoneNumber);
#endif
// Mock the root user info object for GetAccountInfo.
id mockGetAccountInfoResponseUser = OCMClassMock([FIRGetAccountInfoResponseUser class]);
OCMStub([mockGetAccountInfoResponseUser localID]).andReturn(kLocalID);
OCMStub([mockGetAccountInfoResponseUser email]).andReturn(kEmail);
OCMStub([mockGetAccountInfoResponseUser emailVerified]).andReturn(YES);
OCMStub([mockGetAccountInfoResponseUser displayName]).andReturn(kGoogleDisplayName);
OCMStub([mockGetAccountInfoResponseUser photoURL]).andReturn([NSURL URLWithString:kPhotoURL]);
OCMStub([mockGetAccountInfoResponseUser creationDate])
.andReturn([NSDate dateWithTimeIntervalSince1970:kCreationDateTimeIntervalInSeconds]);
OCMStub([mockGetAccountInfoResponseUser lastLoginDate])
.andReturn([NSDate dateWithTimeIntervalSince1970:kLastSignInDateTimeIntervalInSeconds]);
NSArray *providerUserInfos = @[
#if TARGET_OS_IOS
mockPhoneUserInfo,
#endif
mockPasswordUserInfo, mockGoogleUserInfo, mockFacebookUserInfo
];
OCMStub([mockGetAccountInfoResponseUser providerUserInfo]).andReturn(providerUserInfos);
OCMStub([mockGetAccountInfoResponseUser passwordHash]).andReturn(kPasswordHash);
XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
[self
signInWithEmailPasswordWithMockUserInfoResponse:mockGetAccountInfoResponseUser
completion:^(FIRUser *user) {
// Verify FIRUserInfo properties on FIRUser itself.
XCTAssertEqualObjects(user.providerID, @"Firebase");
XCTAssertEqualObjects(user.uid, kLocalID);
XCTAssertEqualObjects(user.displayName,
kGoogleDisplayName);
XCTAssertEqualObjects(user.photoURL,
[NSURL URLWithString:kPhotoURL]);
XCTAssertEqualObjects(user.email, kEmail);
XCTAssertEqualObjects(
user.metadata.creationDate,
[NSDate dateWithTimeIntervalSince1970:
kCreationDateTimeIntervalInSeconds]);
XCTAssertEqualObjects(
user.metadata.lastSignInDate,
[NSDate dateWithTimeIntervalSince1970:
kLastSignInDateTimeIntervalInSeconds]);
// Verify FIRUser properties besides providerData
// contents.
XCTAssertFalse(user.anonymous);
XCTAssertTrue(user.emailVerified);
XCTAssertEqualObjects(user.refreshToken,
kRefreshToken);
XCTAssertEqual(user.providerData.count,
providerUserInfos.count);
NSDictionary<NSString *, id<FIRUserInfo>>
*providerMap = [self
dictionaryWithUserInfoArray:user.providerData];
// Verify FIRUserInfo properties from email/password.
id<FIRUserInfo> passwordUserInfo =
providerMap[FIREmailAuthProviderID];
XCTAssertEqualObjects(passwordUserInfo.uid, kEmail);
XCTAssertNil(passwordUserInfo.displayName);
XCTAssertNil(passwordUserInfo.photoURL);
XCTAssertEqualObjects(passwordUserInfo.email, kEmail);
// Verify FIRUserInfo properties from the Google auth
// provider.
id<FIRUserInfo> googleUserInfo =
providerMap[FIRGoogleAuthProviderID];
XCTAssertEqualObjects(googleUserInfo.uid, kGoogleID);
XCTAssertEqualObjects(googleUserInfo.displayName,
kGoogleDisplayName);
XCTAssertEqualObjects(
googleUserInfo.photoURL,
[NSURL URLWithString:kGooglePhotoURL]);
XCTAssertEqualObjects(googleUserInfo.email,
kGoogleEmail);
// Verify FIRUserInfo properties from the Facebook auth
// provider.
id<FIRUserInfo> facebookUserInfo =
providerMap[FIRFacebookAuthProviderID];
XCTAssertEqualObjects(facebookUserInfo.uid,
kFacebookID);
XCTAssertNil(facebookUserInfo.displayName);
XCTAssertNil(facebookUserInfo.photoURL);
XCTAssertEqualObjects(facebookUserInfo.email,
kFacebookEmail);
#if TARGET_OS_IOS
// Verify FIRUserInfo properties from the phone auth
// provider.
id<FIRUserInfo> phoneUserInfo =
providerMap[FIRPhoneAuthProviderID];
XCTAssertEqualObjects(phoneUserInfo.phoneNumber,
kPhoneNumber);
#endif
// Test NSSecureCoding
XCTAssertTrue([FIRUser supportsSecureCoding]);
NSMutableData *data = [NSMutableData data];
NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc]
initForWritingWithMutableData:data];
[archiver encodeObject:user forKey:kUserArchiverKey];
[archiver finishEncoding];
NSKeyedUnarchiver *unarchiver =
[[NSKeyedUnarchiver alloc]
initForReadingWithData:data];
FIRUser *unarchivedUser =
[unarchiver decodeObjectForKey:kUserArchiverKey];
// Verify NSSecureCoding for FIRUser
XCTAssertEqualObjects(unarchivedUser.providerID,
user.providerID);
XCTAssertEqualObjects(unarchivedUser.uid, user.uid);
XCTAssertEqualObjects(unarchivedUser.email,
user.email);
XCTAssertEqualObjects(unarchivedUser.photoURL,
user.photoURL);
XCTAssertEqualObjects(unarchivedUser.displayName,
user.displayName);
// Verify NSSecureCoding properties besides
// providerData contents.
XCTAssertEqual(unarchivedUser.anonymous,
user.anonymous);
XCTAssertEqual(unarchivedUser.emailVerified,
user.emailVerified);
XCTAssertEqualObjects(unarchivedUser.refreshToken,
user.refreshToken);
XCTAssertEqualObjects(
unarchivedUser.metadata.creationDate,
user.metadata.creationDate);
XCTAssertEqualObjects(
unarchivedUser.metadata.lastSignInDate,
user.metadata.lastSignInDate);
XCTAssertEqual(unarchivedUser.providerData.count,
user.providerData.count);
NSDictionary<NSString *, id<FIRUserInfo>>
*unarchivedProviderMap = [self
dictionaryWithUserInfoArray:unarchivedUser
.providerData];
// Verify NSSecureCoding properties from
// email/password.
id<FIRUserInfo> unarchivedPasswordUserInfo =
unarchivedProviderMap[FIREmailAuthProviderID];
XCTAssertEqualObjects(unarchivedPasswordUserInfo.uid,
passwordUserInfo.uid);
XCTAssertEqualObjects(
unarchivedPasswordUserInfo.displayName,
passwordUserInfo.displayName);
XCTAssertEqualObjects(
unarchivedPasswordUserInfo.photoURL,
passwordUserInfo.photoURL);
XCTAssertEqualObjects(unarchivedPasswordUserInfo.email,
passwordUserInfo.email);
// Verify NSSecureCoding properties from the Google
// auth provider.
id<FIRUserInfo> unarchivedGoogleUserInfo =
unarchivedProviderMap[FIRGoogleAuthProviderID];
XCTAssertEqualObjects(unarchivedGoogleUserInfo.uid,
googleUserInfo.uid);
XCTAssertEqualObjects(
unarchivedGoogleUserInfo.displayName,
googleUserInfo.displayName);
XCTAssertEqualObjects(
unarchivedGoogleUserInfo.photoURL,
googleUserInfo.photoURL);
XCTAssertEqualObjects(unarchivedGoogleUserInfo.email,
googleUserInfo.email);
// Verify NSSecureCoding properties from the Facebook
// auth provider.
id<FIRUserInfo> unarchivedFacebookUserInfo =
unarchivedProviderMap[FIRFacebookAuthProviderID];
XCTAssertEqualObjects(unarchivedFacebookUserInfo.uid,
facebookUserInfo.uid);
XCTAssertEqualObjects(
unarchivedFacebookUserInfo.displayName,
facebookUserInfo.displayName);
XCTAssertEqualObjects(
unarchivedFacebookUserInfo.photoURL,
facebookUserInfo.photoURL);
XCTAssertEqualObjects(unarchivedFacebookUserInfo.email,
facebookUserInfo.email);
#if TARGET_OS_IOS
// Verify FIRUserInfo properties from the phone auth
// provider.
id<FIRUserInfo> unarchivedPhoneUserInfo =
unarchivedProviderMap[FIRPhoneAuthProviderID];
XCTAssertEqualObjects(
unarchivedPhoneUserInfo.phoneNumber,
phoneUserInfo.phoneNumber);
#endif
[expectation fulfill];
}];
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
OCMVerifyAll(_mockBackend);
}
/** @fn testUpdateEmailSuccess
@brief Tests the flow of a successful @c updateEmail:completion: call.
*/
- (void)testUpdateEmailSuccess {
id (^mockUserInfoWithDisplayName)(NSString *) = ^(NSString *displayName) {
id mockGetAccountInfoResponseUser = OCMClassMock([FIRGetAccountInfoResponseUser class]);
OCMStub([mockGetAccountInfoResponseUser localID]).andReturn(kLocalID);
OCMStub([mockGetAccountInfoResponseUser email]).andReturn(kEmail);
OCMStub([mockGetAccountInfoResponseUser displayName]).andReturn(displayName);
OCMStub([mockGetAccountInfoResponseUser passwordHash]).andReturn(kPasswordHash);
return mockGetAccountInfoResponseUser;
};
XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
id userInfoResponse = mockUserInfoWithDisplayName(kGoogleDisplayName);
[self
signInWithEmailPasswordWithMockUserInfoResponse:userInfoResponse
completion:^(FIRUser *user) {
// Pretend that the display name on the server has been
// changed since last request.
[self
expectGetAccountInfoWithMockUserInfoResponse:
mockUserInfoWithDisplayName(kNewDisplayName)];
OCMExpect([self->_mockBackend
setAccountInfo:[OCMArg any]
callback:[OCMArg any]])
.andCallBlock2(^(
FIRSetAccountInfoRequest *_Nullable request,
FIRSetAccountInfoResponseCallback callback) {
XCTAssertEqualObjects(request.APIKey, kAPIKey);
XCTAssertEqualObjects(request.accessToken,
kAccessToken);
XCTAssertEqualObjects(request.email, kNewEmail);
XCTAssertNil(request.localID);
XCTAssertNil(request.displayName);
XCTAssertNil(request.photoURL);
XCTAssertNil(request.password);
XCTAssertNil(request.providers);
XCTAssertNil(request.deleteAttributes);
XCTAssertNil(request.deleteProviders);
dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
id mockSetAccountInfoResponse = OCMClassMock(
[FIRSetAccountInfoResponse class]);
OCMStub([mockSetAccountInfoResponse email])
.andReturn(kNewEmail);
OCMStub(
[mockSetAccountInfoResponse displayName])
.andReturn(kNewDisplayName);
callback(mockSetAccountInfoResponse, nil);
});
});
[user updateEmail:kNewEmail
completion:^(NSError *_Nullable error) {
XCTAssertNil(error);
XCTAssertEqualObjects(user.email, kNewEmail);
XCTAssertEqualObjects(user.displayName,
kNewDisplayName);
[expectation fulfill];
}];
}];
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
OCMVerifyAll(_mockBackend);
}
/** @fn testUpdateEmailWithAuthLinkAccountSuccess
@brief Tests a successful @c updateEmail:completion: call updates provider info.
*/
- (void)testUpdateEmailWithAuthLinkAccountSuccess {
id (^mockUserInfoWithDisplayName)(NSString *) = ^(NSString *displayName) {
id mockGetAccountInfoResponseUser = OCMClassMock([FIRGetAccountInfoResponseUser class]);
OCMStub([mockGetAccountInfoResponseUser localID]).andReturn(kLocalID);
OCMStub([mockGetAccountInfoResponseUser email]).andReturn(kEmail);
OCMStub([mockGetAccountInfoResponseUser displayName]).andReturn(displayName);
OCMStub([mockGetAccountInfoResponseUser passwordHash]).andReturn(kPasswordHash);
return mockGetAccountInfoResponseUser;
};
XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
id userInfoResponse = mockUserInfoWithDisplayName(kGoogleDisplayName);
[self
signInWithEmailLinkWithMockUserInfoResponse:userInfoResponse
completion:^(FIRUser *user) {
// Pretend that the display name on the server has been
// changed since last request.
[self expectGetAccountInfoWithMockUserInfoResponse:
mockUserInfoWithDisplayName(kNewDisplayName)];
OCMExpect([self->_mockBackend setAccountInfo:[OCMArg any]
callback:[OCMArg any]])
.andCallBlock2(^(
FIRSetAccountInfoRequest *_Nullable request,
FIRSetAccountInfoResponseCallback callback) {
XCTAssertEqualObjects(request.APIKey, kAPIKey);
XCTAssertEqualObjects(request.accessToken,
kAccessToken);
XCTAssertEqualObjects(request.email, kNewEmail);
XCTAssertNil(request.localID);
XCTAssertNil(request.displayName);
XCTAssertNil(request.photoURL);
XCTAssertNil(request.password);
XCTAssertNil(request.providers);
XCTAssertNil(request.deleteAttributes);
XCTAssertNil(request.deleteProviders);
dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
id mockSetAccountInfoResponse = OCMClassMock(
[FIRSetAccountInfoResponse class]);
OCMStub([mockSetAccountInfoResponse email])
.andReturn(kNewEmail);
OCMStub([mockSetAccountInfoResponse displayName])
.andReturn(kNewDisplayName);
callback(mockSetAccountInfoResponse, nil);
});
});
[user updateEmail:kNewEmail
completion:^(NSError *_Nullable error) {
XCTAssertNil(error);
XCTAssertEqualObjects(user.email, kNewEmail);
XCTAssertEqualObjects(user.displayName,
kNewDisplayName);
XCTAssertFalse(user.isAnonymous);
[expectation fulfill];
}];
}];
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
OCMVerifyAll(_mockBackend);
}
/** @fn testUpdateEmailFailure
@brief Tests the flow of a failed @c updateEmail:completion: call.
*/
- (void)testUpdateEmailFailure {
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([self->_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([self->_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];
}];
}];
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
OCMVerifyAll(_mockBackend);
}
#if TARGET_OS_IOS
/** @fn testUpdatePhoneSuccess
@brief Tests the flow of a successful @c updatePhoneNumberCredential:completion: call.
*/
- (void)testUpdatePhoneSuccess {
id (^mockUserInfoWithPhoneNumber)(NSString *) = ^(NSString *phoneNumber) {
id mockGetAccountInfoResponseUser = OCMClassMock([FIRGetAccountInfoResponseUser class]);
OCMStub([mockGetAccountInfoResponseUser localID]).andReturn(kLocalID);
OCMStub([mockGetAccountInfoResponseUser email]).andReturn(kEmail);
OCMStub([mockGetAccountInfoResponseUser passwordHash]).andReturn(kPasswordHash);
if (phoneNumber.length) {
OCMStub([mockGetAccountInfoResponseUser phoneNumber]).andReturn(phoneNumber);
}
return mockGetAccountInfoResponseUser;
};
XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
id userInfoResponse = mockUserInfoWithPhoneNumber(nil);
[self
signInWithEmailPasswordWithMockUserInfoResponse:userInfoResponse
completion:^(FIRUser *user) {
[self
expectVerifyPhoneNumberRequestWithPhoneNumber:
kPhoneNumber
isLinkOperation:NO
error:nil];
id userInfoResponseUpdate =
mockUserInfoWithPhoneNumber(kPhoneNumber);
[self expectGetAccountInfoWithMockUserInfoResponse:
userInfoResponseUpdate];
FIRPhoneAuthCredential *credential =
[[FIRPhoneAuthProvider provider]
credentialWithVerificationID:kVerificationID
verificationCode:
kVerificationCode];
[user updatePhoneNumberCredential:credential
completion:^(
NSError *_Nullable error) {
XCTAssertTrue(
[NSThread isMainThread]);
XCTAssertNil(error);
XCTAssertEqualObjects(
[FIRAuth auth]
.currentUser
.phoneNumber,
kPhoneNumber);
[expectation fulfill];
}];
}];
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
OCMVerifyAll(_mockBackend);
}
/** @fn testUpdatePhoneNumberFailure
@brief Tests the flow of a failed @c updatePhoneNumberCredential:completion: call.
*/
- (void)testUpdatePhoneNumberFailure {
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([self->_mockBackend
verifyPhoneNumber:[OCMArg any]
callback:[OCMArg any]])
.andDispatchError2([FIRAuthErrorUtils
invalidPhoneNumberErrorWithMessage:nil]);
FIRPhoneAuthCredential *credential =
[[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([self->_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];
}];
}];
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
OCMVerifyAll(_mockBackend);
}
#endif
/** @fn testUpdatePasswordSuccess
@brief Tests the flow of a successful @c updatePassword:completion: call.
*/
- (void)testUpdatePasswordSuccess {
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([self->_mockBackend
setAccountInfo:[OCMArg any]
callback:[OCMArg any]])
.andCallBlock2(^(
FIRSetAccountInfoRequest *_Nullable request,
FIRSetAccountInfoResponseCallback callback) {
XCTAssertEqualObjects(request.APIKey, kAPIKey);
XCTAssertEqualObjects(request.accessToken,
kAccessToken);
XCTAssertEqualObjects(request.password,
kNewPassword);
XCTAssertNil(request.localID);
XCTAssertNil(request.displayName);
XCTAssertNil(request.photoURL);
XCTAssertNil(request.email);
XCTAssertNil(request.providers);
XCTAssertNil(request.deleteAttributes);
XCTAssertNil(request.deleteProviders);
dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
id mockSetAccountInfoResponse = OCMClassMock(
[FIRSetAccountInfoResponse class]);
OCMStub(
[mockSetAccountInfoResponse displayName])
.andReturn(kNewDisplayName);
callback(mockSetAccountInfoResponse, nil);
});
});
[user updatePassword:kNewPassword
completion:^(NSError *_Nullable error) {
XCTAssertTrue([NSThread isMainThread]);
XCTAssertNil(error);
XCTAssertFalse(user.isAnonymous);
[expectation fulfill];
}];
}];
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
OCMVerifyAll(_mockBackend);
}
/** @fn testUpdatePasswordFailure
@brief Tests the flow of a failed @c updatePassword:completion: call.
*/
- (void)testUpdatePasswordFailure {
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([self->_mockBackend
setAccountInfo:[OCMArg any]
callback:[OCMArg any]])
.andDispatchError2([FIRAuthErrorUtils
requiresRecentLoginErrorWithMessage:nil]);
[user updatePassword:kNewPassword
completion:^(NSError *_Nullable error) {
XCTAssertTrue([NSThread isMainThread]);
XCTAssertEqual(
error.code,
FIRAuthErrorCodeRequiresRecentLogin);
XCTAssertEqual(
[FIRAuth auth].currentUser, user);
[expectation fulfill];
}];
}];
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
OCMVerifyAll(_mockBackend);
}
/** @fn testUpdateEmptyPasswordFailure
@brief Tests the flow of a failed @c updatePassword:completion: call due to an empty password.
*/
- (void)testUpdateEmptyPasswordFailure {
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) {
[user updatePassword:@""
completion:^(NSError *_Nullable error) {
XCTAssertTrue([NSThread isMainThread]);
XCTAssertEqual(
error.code,
FIRAuthErrorCodeWeakPassword);
[expectation fulfill];
}];
}];
[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([self->_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.
*/
- (void)testChangeProfileSuccess {
id mockGetAccountInfoResponseUser = OCMClassMock([FIRGetAccountInfoResponseUser class]);
OCMStub([mockGetAccountInfoResponseUser localID]).andReturn(kLocalID);
OCMStub([mockGetAccountInfoResponseUser email]).andReturn(kEmail);
OCMStub([mockGetAccountInfoResponseUser displayName]).andReturn(kGoogleDisplayName);
OCMStub([mockGetAccountInfoResponseUser photoURL]).andReturn(kPhotoURL);
OCMStub([mockGetAccountInfoResponseUser passwordHash]).andReturn(kPasswordHash);
XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
[self signInWithEmailPasswordWithMockUserInfoResponse:mockGetAccountInfoResponseUser
completion:^(FIRUser *user) {
[self expectGetAccountInfoWithMockUserInfoResponse:
mockGetAccountInfoResponseUser];
OCMExpect([self->_mockBackend
setAccountInfo:[OCMArg any]
callback:[OCMArg any]])
.andCallBlock2(^(
FIRSetAccountInfoRequest *_Nullable request,
FIRSetAccountInfoResponseCallback callback) {
XCTAssertEqualObjects(request.APIKey, kAPIKey);
XCTAssertEqualObjects(request.accessToken,
kAccessToken);
XCTAssertEqualObjects(request.displayName,
kNewDisplayName);
XCTAssertEqualObjects(
request.photoURL,
[NSURL URLWithString:kNewPhotoURL]);
XCTAssertNil(request.localID);
XCTAssertNil(request.email);
XCTAssertNil(request.password);
XCTAssertNil(request.providers);
XCTAssertNil(request.deleteAttributes);
XCTAssertNil(request.deleteProviders);
dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
id mockSetAccountInfoResponse = OCMClassMock(
[FIRSetAccountInfoResponse class]);
OCMStub(
[mockSetAccountInfoResponse displayName])
.andReturn(kNewDisplayName);
callback(mockSetAccountInfoResponse, nil);
});
});
FIRUserProfileChangeRequest *profileChange =
[user profileChangeRequest];
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]);
[expectation fulfill];
}];
}];
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
OCMVerifyAll(_mockBackend);
}
/** @fn testChangeProfileFailure
@brief Tests a failed user profile change flow.
*/
- (void)testChangeProfileFailure {
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([self->_mockBackend
setAccountInfo:[OCMArg any]
callback:[OCMArg any]])
.andDispatchError2([FIRAuthErrorUtils
tooManyRequestsErrorWithMessage:nil]);
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([self->_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];
}];
}];
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
OCMVerifyAll(_mockBackend);
}
/** @fn testReloadSuccess
@brief Tests the flow of a successful @c reloadWithCompletion: call.
*/
- (void)testReloadSuccess {
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) {
id mockGetAccountInfoResponseUserNew = OCMClassMock(
[FIRGetAccountInfoResponseUser class]);
OCMStub([mockGetAccountInfoResponseUserNew localID])
.andReturn(kLocalID);
OCMStub([mockGetAccountInfoResponseUserNew email])
.andReturn(kNewEmail);
OCMStub(
[mockGetAccountInfoResponseUserNew displayName])
.andReturn(kNewDisplayName);
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);
[expectation fulfill];
}];
}];
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
OCMVerifyAll(_mockBackend);
}
/** @fn testGetIDTokenResultSuccess
@brief Tests the flow of a successful @c getIDTokenResultWithCompletion: call.
*/
- (void)testGetIDTokenResultSuccess {
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"];
id mockSecureTokenService = OCMClassMock([FIRSecureTokenService class]);
OCMStub([mockSecureTokenService hasValidAccessToken]).andReturn(YES);
[self signInWithEmailPasswordWithMockUserInfoResponse:mockGetAccountInfoResponseUser
completion:^(FIRUser *user) {
[user getIDTokenResultWithCompletion:^(
FIRAuthTokenResult *_Nullable tokenResult,
NSError *_Nullable error) {
XCTAssertTrue([NSThread isMainThread]);
XCTAssertNil(error);
XCTAssertEqualObjects(tokenResult.token,
kAccessToken);
XCTAssertTrue(tokenResult.issuedAtDate &&
[tokenResult.issuedAtDate
isKindOfClass:[NSDate class]]);
XCTAssertTrue(tokenResult.authDate &&
[tokenResult.authDate
isKindOfClass:[NSDate class]]);
XCTAssertTrue(tokenResult.expirationDate &&
[tokenResult.expirationDate
isKindOfClass:[NSDate class]]);
XCTAssertEqualObjects(tokenResult.signInProvider,
FIREmailAuthProviderID);
XCTAssertTrue(
tokenResult.claims &&
[tokenResult.claims
isKindOfClass:[NSDictionary class]]);
[expectation fulfill];
}];
}];
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
OCMVerifyAll(_mockBackend);
}
/** @fn testGetIDTokenResultForcingRefreshFailure
@brief Tests the flow successful @c getIDTokenResultForcingRefresh:completion: calls.
*/
- (void)testGetIDTokenResultForcingRefreshSuccess {
[self getIDTokenResultForcingRefreshSuccessWithIDToken:kAccessToken];
[self getIDTokenResultForcingRefreshSuccessWithIDToken:kAccessTokenLength415];
[self getIDTokenResultForcingRefreshSuccessWithIDToken:kAccessTokenLength416];
[self getIDTokenResultForcingRefreshSuccessWithIDToken:kAccessTokenLength523];
[self getIDTokenResultForcingRefreshSuccessWithIDToken:kAccessTokenWithBase64URLCharacter];
}
/** @fn testGetIDTokenResultSuccessWithBase64EncodedURL
@brief Tests the flow of a successful @c getIDTokenResultWithCompletion: call using a base64 url
encoded string.
*/
- (void)testGetIDTokenResultSuccessWithBase64EncodedURL {
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([self->_mockBackend
secureToken:[OCMArg any]
callback:[OCMArg any]])
.andCallBlock2(^(
FIRSecureTokenRequest *_Nullable request,
FIRSecureTokenResponseCallback callback) {
XCTAssertEqualObjects(request.APIKey, kAPIKey);
dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
id mockSecureTokenResponse = OCMClassMock(
[FIRSecureTokenResponse class]);
OCMStub([mockSecureTokenResponse accessToken])
.andReturn(
kAccessTokenWithBase64URLCharacter);
callback(mockSecureTokenResponse, nil);
});
});
[user
getIDTokenResultForcingRefresh:YES
completion:^(
FIRAuthTokenResult
*_Nullable tokenResult,
NSError *_Nullable error) {
XCTAssertTrue(
[NSThread isMainThread]);
XCTAssertNil(error);
XCTAssertEqualObjects(
tokenResult.token,
kAccessTokenWithBase64URLCharacter);
XCTAssertTrue(
tokenResult
.issuedAtDate &&
[tokenResult.issuedAtDate
isKindOfClass:
[NSDate class]]);
XCTAssertTrue(
tokenResult.authDate &&
[tokenResult.authDate
isKindOfClass:
[NSDate class]]);
XCTAssertTrue(
tokenResult
.expirationDate &&
[tokenResult
.expirationDate
isKindOfClass:
[NSDate class]]);
XCTAssertTrue(
tokenResult.claims &&
[tokenResult.claims
isKindOfClass:
[NSDictionary
class]]);
NSDictionary *claims =
tokenResult.claims;
XCTAssertEqualObjects(
claims[@"email"],
kbase64URLEncodedEmail);
XCTAssertEqualObjects(
claims[@"aud"],
kbase64URLEncodedAUD);
[expectation fulfill];
}];
}];
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
OCMVerifyAll(_mockBackend);
}
/** @fn testGetIDTokenResultForcingRefreshFailure
@brief Tests the flow of a failed @c getIDTokenResultForcingRefresh:completion: call.
*/
- (void)testGetIDTokenResultForcingRefreshFailure {
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([self->_mockBackend
secureToken:[OCMArg any]
callback:[OCMArg any]])
.andCallBlock2(^(
FIRSecureTokenRequest *_Nullable request,
FIRSecureTokenResponseCallback callback) {
XCTAssertEqualObjects(request.APIKey, kAPIKey);
dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
NSError *underlying =
[NSError errorWithDomain:@"Test Error"
code:1
userInfo:nil];
callback(nil,
[FIRAuthErrorUtils
networkErrorWithUnderlyingError:
underlying]);
});
});
[user
getIDTokenResultForcingRefresh:YES
completion:^(
FIRAuthTokenResult
*_Nullable tokenResult,
NSError *_Nullable error) {
XCTAssertTrue(
[NSThread isMainThread]);
XCTAssertNil(tokenResult);
XCTAssertEqual(
error.code,
FIRAuthErrorCodeNetworkError);
[expectation fulfill];
}];
}];
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
OCMVerifyAll(_mockBackend);
}
/** @fn testReloadFailure
@brief Tests the flow of a failed @c reloadWithCompletion: call.
*/
- (void)testReloadFailure {
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([self->_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([self->_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];
}];
}];
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
OCMVerifyAll(_mockBackend);
}
/** @fn testReauthenticateSuccess
@brief Tests the flow of a successful @c reauthenticateWithCredential:completion: call.
*/
- (void)testReauthenticateSuccess {
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([self->_mockBackend
verifyPassword:[OCMArg any]
callback:[OCMArg any]])
.andCallBlock2(^(
FIRVerifyPasswordRequest *_Nullable request,
FIRVerifyPasswordResponseCallback callback) {
dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
id mockVeriyPasswordResponse = OCMClassMock(
[FIRVerifyPasswordResponse class]);
// New authentication comes back with new
// access token.
OCMStub([mockVeriyPasswordResponse IDToken])
.andReturn(kNewAccessToken);
OCMStub([mockVeriyPasswordResponse
approximateExpirationDate])
.andReturn(
[NSDate dateWithTimeIntervalSinceNow:
kAccessTokenTimeToLive]);
OCMStub(
[mockVeriyPasswordResponse refreshToken])
.andReturn(kRefreshToken);
callback(mockVeriyPasswordResponse, nil);
});
});
OCMExpect([self->_mockBackend
getAccountInfo:[OCMArg any]
callback:[OCMArg any]])
.andCallBlock2(^(
FIRGetAccountInfoRequest *_Nullable request,
FIRGetAccountInfoResponseCallback callback) {
XCTAssertEqualObjects(request.APIKey, kAPIKey);
// Verify that the new access token is being used
// for subsequent requests.
XCTAssertEqualObjects(request.accessToken,
kNewAccessToken);
dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
id mockGetAccountInfoResponse = OCMClassMock(
[FIRGetAccountInfoResponse class]);
OCMStub([mockGetAccountInfoResponse users])
.andReturn(
@[ mockGetAccountInfoResponseUser ]);
callback(mockGetAccountInfoResponse, nil);
});
});
FIRAuthCredential *emailCredential =
[FIREmailAuthProvider
credentialWithEmail:kEmail
password:kFakePassword];
[user
reauthenticateWithCredential:emailCredential
completion:^(
FIRAuthDataResult
*_Nullable result,
NSError *_Nullable error) {
XCTAssertTrue(
[NSThread isMainThread]);
XCTAssertNil(error);
// Verify that the current user
// is unchanged.
XCTAssertEqual(
[FIRAuth auth].currentUser,
user);
[expectation fulfill];
}];
}];
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
OCMVerifyAll(_mockBackend);
}
/** @fn testReauthenticateWithCredentialSuccess
@brief Tests the flow of a successful @c reauthenticateWithCredential:completion:
call.
*/
- (void)testReauthenticateWithCredentialSuccess {
[self expectVerifyAssertionRequest:FIRGoogleAuthProviderID
federatedID:kGoogleID
displayName:kGoogleDisplayName
profile:[[self class] googleProfile]
providerIDToken:kGoogleIDToken
providerAccessToken:kGoogleAccessToken];
XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
[[FIRAuth auth] signOut:NULL];
FIRAuthCredential *googleCredential =
[FIRGoogleAuthProvider credentialWithIDToken:kGoogleIDToken accessToken:kGoogleAccessToken];
[[FIRAuth auth]
signInWithCredential:googleCredential
completion:^(FIRAuthDataResult *_Nullable authResult, NSError *_Nullable error) {
XCTAssertTrue([NSThread isMainThread]);
[self assertUserGoogle:authResult.user];
XCTAssertEqualObjects(authResult.additionalUserInfo.profile,
[[self class] googleProfile]);
XCTAssertEqualObjects(authResult.additionalUserInfo.username, kUserName);
XCTAssertEqualObjects(authResult.additionalUserInfo.providerID,
FIRGoogleAuthProviderID);
XCTAssertNil(error);
[self expectVerifyAssertionRequest:FIRGoogleAuthProviderID
federatedID:kGoogleID
displayName:kGoogleDisplayName
profile:[[self class] googleProfile]
providerIDToken:kGoogleIDToken
providerAccessToken:kGoogleAccessToken];
FIRAuthCredential *reauthenticateGoogleCredential =
[FIRGoogleAuthProvider credentialWithIDToken:kGoogleIDToken
accessToken:kGoogleAccessToken];
[authResult.user
reauthenticateWithCredential:reauthenticateGoogleCredential
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);
// Verify that the current user and reauthenticated user
// are not same pointers.
XCTAssertNotEqualObjects(authResult.user,
reauthenticateAuthResult.user);
// Verify that anyway the current user and reauthenticated
// user have same IDs.
XCTAssertEqualObjects(authResult.user.uid,
reauthenticateAuthResult.user.uid);
XCTAssertEqualObjects(
authResult.user.displayName,
reauthenticateAuthResult.user.displayName);
XCTAssertEqualObjects(
reauthenticateAuthResult.additionalUserInfo.profile,
[[self class] googleProfile]);
XCTAssertEqualObjects(
reauthenticateAuthResult.additionalUserInfo.username,
kUserName);
XCTAssertEqualObjects(reauthenticateAuthResult
.additionalUserInfo.providerID,
FIRGoogleAuthProviderID);
[expectation fulfill];
}];
}];
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
[self assertUserGoogle:[FIRAuth auth].currentUser];
OCMVerifyAll(_mockBackend);
}
/** @fn testReauthenticateFailure
@brief Tests the flow of a failed @c reauthenticateWithCredential:completion: call.
*/
- (void)testReauthenticateFailure {
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([self->_mockBackend
verifyPassword:[OCMArg any]
callback:[OCMArg any]])
.andCallBlock2(^(
FIRVerifyPasswordRequest *_Nullable request,
FIRVerifyPasswordResponseCallback callback) {
dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
id mockVeriyPasswordResponse = OCMClassMock(
[FIRVerifyPasswordResponse class]);
OCMStub([mockVeriyPasswordResponse IDToken])
.andReturn(kNewAccessToken);
OCMStub([mockVeriyPasswordResponse
approximateExpirationDate])
.andReturn(
[NSDate dateWithTimeIntervalSinceNow:
kAccessTokenTimeToLive]);
OCMStub(
[mockVeriyPasswordResponse refreshToken])
.andReturn(kRefreshToken);
callback(mockVeriyPasswordResponse, nil);
});
});
OCMExpect([self->_mockBackend
getAccountInfo:[OCMArg any]
callback:[OCMArg any]])
.andCallBlock2(^(
FIRGetAccountInfoRequest *_Nullable request,
FIRGetAccountInfoResponseCallback callback) {
dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
id mockGetAccountInfoResponseUserNew =
OCMClassMock(
[FIRGetAccountInfoResponseUser class]);
// The newly-signed-in user has a different ID.
OCMStub([mockGetAccountInfoResponseUserNew
localID])
.andReturn(kAnotherLocalID);
OCMStub(
[mockGetAccountInfoResponseUserNew email])
.andReturn(kNewEmail);
OCMStub([mockGetAccountInfoResponseUserNew
displayName])
.andReturn(kNewDisplayName);
OCMStub([mockGetAccountInfoResponseUserNew
passwordHash])
.andReturn(kPasswordHash);
id mockGetAccountInfoResponse = OCMClassMock(
[FIRGetAccountInfoResponse class]);
OCMStub([mockGetAccountInfoResponse users])
.andReturn(@[
mockGetAccountInfoResponseUserNew
]);
callback(mockGetAccountInfoResponse, nil);
});
});
FIRAuthCredential *emailCredential =
[FIREmailAuthProvider
credentialWithEmail:kEmail
password:kFakePassword];
[user
reauthenticateWithCredential:emailCredential
completion:^(
FIRAuthDataResult
*_Nullable result,
NSError *_Nullable error) {
XCTAssertTrue(
[NSThread isMainThread]);
// Verify user mismatch error.
XCTAssertEqual(
error.code,
FIRAuthErrorCodeUserMismatch);
// Verify that the current user
// is unchanged.
XCTAssertEqual(
[FIRAuth auth].currentUser,
user);
[expectation fulfill];
}];
}];
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
OCMVerifyAll(_mockBackend);
}
/** @fn testReauthenticateUserMismatchFailure
@brief Tests the flow of a failed @c reauthenticateWithCredential:completion: call due to trying
to reauthenticate a user that does not exist.
*/
- (void)testReauthenticateUserMismatchFailure {
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([self->_mockBackend
verifyAssertion:[OCMArg any]
callback:[OCMArg any]])
.andCallBlock2(^(
FIRVerifyAssertionRequest *_Nullable request,
FIRVerifyAssertionResponseCallback callback) {
dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
callback(
nil,
[FIRAuthErrorUtils
userNotFoundErrorWithMessage:nil]);
});
});
FIRAuthCredential *googleCredential =
[FIRGoogleAuthProvider
credentialWithIDToken:kGoogleIDToken
accessToken:kGoogleAccessToken];
[user
reauthenticateWithCredential:googleCredential
completion:^(
FIRAuthDataResult
*_Nullable result,
NSError *_Nullable error) {
XCTAssertTrue(
[NSThread isMainThread]);
// Verify user mismatch error.
XCTAssertEqual(
error.code,
FIRAuthErrorCodeUserMismatch);
// Verify that the current user
// is unchanged.
XCTAssertEqual(
[FIRAuth auth].currentUser,
user);
[expectation fulfill];
}];
}];
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
OCMVerifyAll(_mockBackend);
}
/** @fn testlinkAndRetrieveDataSuccess
@brief Tests the flow of a successful @c linkWithCredential:completion:
call.
*/
- (void)testlinkAndRetrieveDataSuccess {
[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]
signInWithCredential: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);
[self expectVerifyAssertionRequest:FIRGoogleAuthProviderID
federatedID:kGoogleID
displayName:kGoogleDisplayName
profile:[[self class] googleProfile]
providerIDToken:kGoogleIDToken
providerAccessToken:kGoogleAccessToken];
FIRAuthCredential *linkGoogleCredential =
[FIRGoogleAuthProvider credentialWithIDToken:kGoogleIDToken
accessToken:kGoogleAccessToken];
[authResult.user
linkWithCredential:linkGoogleCredential
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);
// Verify that the current user and reauthenticated user are same
// pointers.
XCTAssertEqualObjects(authResult.user, linkAuthResult.user);
// Verify that anyway the current user and linked user have same
// IDs.
XCTAssertEqualObjects(authResult.user.uid, linkAuthResult.user.uid);
XCTAssertEqualObjects(authResult.user.displayName,
linkAuthResult.user.displayName);
XCTAssertEqualObjects(linkAuthResult.additionalUserInfo.profile,
[[self class] googleProfile]);
XCTAssertEqualObjects(linkAuthResult.additionalUserInfo.username,
kUserName);
XCTAssertEqualObjects(linkAuthResult.additionalUserInfo.providerID,
FIRGoogleAuthProviderID);
[expectation fulfill];
}];
}];
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
[self assertUserGoogle:[FIRAuth auth].currentUser];
OCMVerifyAll(_mockBackend);
}
/** @fn testlinkAndRetrieveDataError
@brief Tests the flow of an unsuccessful @c linkWithCredential:completion:
call with an error from the backend.
*/
- (void)testlinkAndRetrieveDataError {
[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]
signInWithCredential: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([self->_mockBackend verifyAssertion:[OCMArg any] callback:[OCMArg any]])
.andCallBlock2(^(FIRVerifyAssertionRequest *_Nullable request,
FIRVerifyAssertionResponseCallback callback) {
dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
callback(nil,
[FIRAuthErrorUtils
accountExistsWithDifferentCredentialErrorWithEmail:kEmail
updatedCredential:nil]);
});
});
FIRAuthCredential *linkGoogleCredential =
[FIRGoogleAuthProvider credentialWithIDToken:kGoogleIDToken
accessToken:kGoogleAccessToken];
[authResult.user
linkWithCredential:linkGoogleCredential
completion:^(FIRAuthDataResult *_Nullable linkAuthResult,
NSError *_Nullable error) {
XCTAssertTrue([NSThread isMainThread]);
XCTAssertNil(linkAuthResult);
XCTAssertEqual(
error.code,
FIRAuthErrorCodeAccountExistsWithDifferentCredential);
XCTAssertEqual(error.userInfo[FIRAuthErrorUserInfoEmailKey],
kEmail);
XCTAssertEqual([FIRAuth auth].currentUser, authResult.user);
[expectation fulfill];
}];
}];
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
OCMVerifyAll(_mockBackend);
}
/** @fn testlinkAndRetrieveDataProviderAlreadyLinked
@brief Tests the flow of an unsuccessful @c linkWithCredential:completion:
call with FIRAuthErrorCodeProviderAlreadyLinked, which is a client side error.
*/
- (void)testlinkAndRetrieveDataProviderAlreadyLinked {
[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]
signInWithCredential: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);
FIRAuthCredential *linkFacebookCredential =
[FIRFacebookAuthProvider credentialWithAccessToken:kFacebookAccessToken];
[authResult.user
linkWithCredential:linkFacebookCredential
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 linkWithCredential: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]
signInWithCredential: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([self->_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
linkWithCredential:linkGoogleCredential
completion:^(FIRAuthDataResult *_Nullable linkAuthResult,
NSError *_Nullable error) {
XCTAssertTrue([NSThread isMainThread]);
XCTAssertNil(linkAuthResult);
XCTAssertEqual(error.code, FIRAuthErrorCodeUserDisabled);
XCTAssertNil([FIRAuth auth].currentUser);
[expectation fulfill];
}];
}];
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
OCMVerifyAll(_mockBackend);
}
/** @fn testlinkEmailAndRetrieveDataSuccess
@brief Tests the flow of a successful @c linkWithCredential:completion:
invocation for email credential.
*/
- (void)testlinkEmailAndRetrieveDataSuccess {
[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]
signInWithCredential: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);
id mockGetAccountInfoResponseUser =
OCMClassMock([FIRGetAccountInfoResponseUser class]);
OCMStub([mockGetAccountInfoResponseUser localID]).andReturn(kLocalID);
OCMStub([mockGetAccountInfoResponseUser email]).andReturn(kEmail);
OCMStub([mockGetAccountInfoResponseUser displayName])
.andReturn(kEmailDisplayName);
OCMStub([mockGetAccountInfoResponseUser passwordHash]).andReturn(kPasswordHash);
// Get account info is expected to be invoked twice.
[self
expectGetAccountInfoWithMockUserInfoResponse:mockGetAccountInfoResponseUser];
[self
expectGetAccountInfoWithMockUserInfoResponse:mockGetAccountInfoResponseUser];
OCMExpect([self->_mockBackend setAccountInfo:[OCMArg any] callback:[OCMArg any]])
.andCallBlock2(^(FIRSetAccountInfoRequest *_Nullable request,
FIRSetAccountInfoResponseCallback callback) {
XCTAssertEqualObjects(request.APIKey, kAPIKey);
XCTAssertEqualObjects(request.accessToken, kAccessToken);
XCTAssertEqualObjects(request.password, kFakePassword);
XCTAssertNil(request.localID);
XCTAssertNil(request.displayName);
dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
id mockSetAccountInfoResponse =
OCMClassMock([FIRSetAccountInfoResponse class]);
callback(mockSetAccountInfoResponse, nil);
});
});
FIRAuthCredential *linkEmailCredential =
[FIREmailAuthProvider credentialWithEmail:kEmail password:kFakePassword];
[authResult.user
linkWithCredential:linkEmailCredential
completion:^(FIRAuthDataResult *_Nullable linkAuthResult,
NSError *_Nullable error) {
XCTAssertTrue([NSThread isMainThread]);
XCTAssertNil(error);
XCTAssertEqualObjects(linkAuthResult.user.email, kEmail);
XCTAssertEqualObjects(linkAuthResult.user.displayName,
kEmailDisplayName);
[expectation fulfill];
}];
}];
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
OCMVerifyAll(_mockBackend);
}
/** @fn testlinkEmailProviderAlreadyLinkedError
@brief Tests the flow of an unsuccessful @c linkWithCredential:completion:
invocation for email credential and FIRAuthErrorCodeProviderAlreadyLinked which is a client
side error.
*/
- (void)testlinkEmailProviderAlreadyLinkedError {
[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]
signInWithCredential: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);
id mockGetAccountInfoResponseUser =
OCMClassMock([FIRGetAccountInfoResponseUser class]);
OCMStub([mockGetAccountInfoResponseUser localID]).andReturn(kLocalID);
OCMStub([mockGetAccountInfoResponseUser email]).andReturn(kEmail);
OCMStub([mockGetAccountInfoResponseUser displayName])
.andReturn(kEmailDisplayName);
OCMStub([mockGetAccountInfoResponseUser passwordHash]).andReturn(kPasswordHash);
// Get account info is expected to be invoked twice.
[self
expectGetAccountInfoWithMockUserInfoResponse:mockGetAccountInfoResponseUser];
[self
expectGetAccountInfoWithMockUserInfoResponse:mockGetAccountInfoResponseUser];
OCMExpect([self->_mockBackend setAccountInfo:[OCMArg any] callback:[OCMArg any]])
.andCallBlock2(^(FIRSetAccountInfoRequest *_Nullable request,
FIRSetAccountInfoResponseCallback callback) {
XCTAssertEqualObjects(request.APIKey, kAPIKey);
XCTAssertEqualObjects(request.accessToken, kAccessToken);
XCTAssertEqualObjects(request.password, kFakePassword);
XCTAssertNil(request.localID);
XCTAssertNil(request.displayName);
dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
id mockSetAccountInfoResponse =
OCMClassMock([FIRSetAccountInfoResponse class]);
callback(mockSetAccountInfoResponse, nil);
});
});
FIRAuthCredential *linkEmailCredential =
[FIREmailAuthProvider credentialWithEmail:kEmail password:kFakePassword];
[authResult.user
linkWithCredential:linkEmailCredential
completion:^(FIRAuthDataResult *_Nullable linkAuthResult,
NSError *_Nullable error) {
XCTAssertNil(error);
XCTAssertEqualObjects(linkAuthResult.user.email, kEmail);
XCTAssertEqualObjects(linkAuthResult.user.displayName,
kEmailDisplayName);
// Try linking same credential a second time to trigger client side
// error.
[authResult.user
linkWithCredential:linkEmailCredential
completion:^(
FIRAuthDataResult *_Nullable linkAuthResult,
NSError *_Nullable error) {
XCTAssertTrue([NSThread isMainThread]);
XCTAssertNil(linkAuthResult);
XCTAssertEqual(error.code,
FIRAuthErrorCodeProviderAlreadyLinked);
[expectation fulfill];
}];
}];
}];
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
OCMVerifyAll(_mockBackend);
}
/** @fn testlinkEmailAndRetrieveDataError
@brief Tests the flow of an unsuccessful @c linkWithCredential:completion:
invocation for email credential and an error from the backend.
*/
- (void)testlinkEmailAndRetrieveDataError {
[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]
signInWithCredential: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([self->_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 tooManyRequestsErrorWithMessage:nil]);
});
});
FIRAuthCredential *linkEmailCredential =
[FIREmailAuthProvider credentialWithEmail:kEmail password:kFakePassword];
[authResult.user
linkWithCredential:linkEmailCredential
completion:^(FIRAuthDataResult *_Nullable linkAuthResult,
NSError *_Nullable error) {
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 linkWithCredential: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]
signInWithCredential: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([self->_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
linkWithCredential:linkEmailCredential
completion:^(FIRAuthDataResult *_Nullable linkAuthResult,
NSError *_Nullable error) {
XCTAssertTrue([NSThread isMainThread]);
XCTAssertNil(linkAuthResult);
XCTAssertEqual(error.code, FIRAuthErrorCodeUserTokenExpired);
XCTAssertNil([FIRAuth auth].currentUser);
[expectation fulfill];
}];
}];
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
OCMVerifyAll(_mockBackend);
}
/** @fn testlinkCredentialSuccess
@brief Tests the flow of a successful @c linkWithCredential:completion: call, without additional
IDP data.
*/
- (void)testlinkCredentialSuccess {
[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]
signInWithCredential: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);
[self expectVerifyAssertionRequest:FIRGoogleAuthProviderID
federatedID:kGoogleID
displayName:kGoogleDisplayName
profile:[[self class] googleProfile]
providerIDToken:kGoogleIDToken
providerAccessToken:kGoogleAccessToken];
FIRAuthCredential *linkGoogleCredential =
[FIRGoogleAuthProvider credentialWithIDToken:kGoogleIDToken
accessToken:kGoogleAccessToken];
[authResult.user
linkWithCredential:linkGoogleCredential
completion:^(FIRAuthDataResult *_Nullable result,
NSError *_Nullable error) {
XCTAssertNil(error);
id<FIRUserInfo> userInfo = result.user.providerData.firstObject;
XCTAssertEqual(userInfo.providerID, FIRGoogleAuthProviderID);
XCTAssertEqual([FIRAuth auth].currentUser, authResult.user);
[expectation fulfill];
}];
}];
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
[self assertUserGoogle:[FIRAuth auth].currentUser];
OCMVerifyAll(_mockBackend);
}
/** @fn testlinkCredentialError
@brief Tests the flow of an unsuccessful @c linkWithCredential:completion: call, with an error
from the backend.
*/
- (void)testlinkCredentialError {
[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]
signInWithCredential: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([self->_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
linkWithCredential:linkGoogleCredential
completion:^(FIRAuthDataResult *_Nullable result,
NSError *_Nullable error) {
XCTAssertNil(result.user);
XCTAssertEqual(error.code, FIRAuthErrorCodeUserDisabled);
[expectation fulfill];
}];
}];
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
OCMVerifyAll(_mockBackend);
}
/** @fn testlinkCredentialProviderAlreadyLinkedError
@brief Tests the flow of an unsuccessful @c linkWithCredential:completion: call, with a client
side error.
*/
- (void)testlinkCredentialProviderAlreadyLinkedError {
[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]
signInWithCredential: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);
FIRAuthCredential *linkFacebookCredential =
[FIRFacebookAuthProvider credentialWithAccessToken:kGoogleAccessToken];
[authResult.user
linkWithCredential:linkFacebookCredential
completion:^(FIRAuthDataResult *_Nullable result,
NSError *_Nullable error) {
XCTAssertNil(result.user);
XCTAssertEqual(error.code, FIRAuthErrorCodeProviderAlreadyLinked);
[expectation fulfill];
}];
}];
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
OCMVerifyAll(_mockBackend);
}
#if TARGET_OS_IOS
/** @fn testlinkPhoneAuthCredentialSuccess
@brief Tests the flow of a successful @c linkWithCredential:completion:
call using a phoneAuthCredential.
*/
- (void)testlinkPhoneAuthCredentialSuccess {
id (^mockUserInfoWithPhoneNumber)(NSString *) = ^(NSString *phoneNumber) {
id mockGetAccountInfoResponseUser = OCMClassMock([FIRGetAccountInfoResponseUser class]);
OCMStub([mockGetAccountInfoResponseUser localID]).andReturn(kLocalID);
OCMStub([mockGetAccountInfoResponseUser email]).andReturn(kEmail);
OCMStub([mockGetAccountInfoResponseUser passwordHash]).andReturn(kPasswordHash);
if (phoneNumber.length) {
NSDictionary *userInfoDictionary = @{@"providerId" : FIRPhoneAuthProviderID};
FIRGetAccountInfoResponseProviderUserInfo *userInfo =
[[FIRGetAccountInfoResponseProviderUserInfo alloc] initWithDictionary:userInfoDictionary];
OCMStub([mockGetAccountInfoResponseUser providerUserInfo]).andReturn(@[ userInfo ]);
OCMStub([mockGetAccountInfoResponseUser phoneNumber]).andReturn(phoneNumber);
}
return mockGetAccountInfoResponseUser;
};
XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
id userInfoResponse = mockUserInfoWithPhoneNumber(nil);
[self
signInWithEmailPasswordWithMockUserInfoResponse:userInfoResponse
completion:^(FIRUser *user) {
[self
expectVerifyPhoneNumberRequestWithPhoneNumber:
kPhoneNumber
isLinkOperation:YES
error:nil];
id userInfoResponseUpdate =
mockUserInfoWithPhoneNumber(kPhoneNumber);
[self expectGetAccountInfoWithMockUserInfoResponse:
userInfoResponseUpdate];
FIRPhoneAuthCredential *credential =
[[FIRPhoneAuthProvider provider]
credentialWithVerificationID:kVerificationID
verificationCode:
kVerificationCode];
[user
linkWithCredential:credential
completion:^(FIRAuthDataResult
*_Nullable linkAuthResult,
NSError *_Nullable error) {
XCTAssertNil(error);
XCTAssertEqualObjects(
[FIRAuth auth]
.currentUser.providerData
.firstObject.providerID,
FIRPhoneAuthProviderID);
XCTAssertEqualObjects(
[FIRAuth auth]
.currentUser.phoneNumber,
kPhoneNumber);
[expectation fulfill];
}];
}];
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
OCMVerifyAll(_mockBackend);
}
/** @fn testUnlinkPhoneAuthCredentialSuccess
@brief Tests the flow of a successful @c unlinkFromProvider:completion: call using a
@c FIRPhoneAuthProvider.
*/
- (void)testUnlinkPhoneAuthCredentialSuccess {
id (^mockUserInfoWithPhoneNumber)(NSString *) = ^(NSString *phoneNumber) {
id mockGetAccountInfoResponseUser = OCMClassMock([FIRGetAccountInfoResponseUser class]);
OCMStub([mockGetAccountInfoResponseUser localID]).andReturn(kLocalID);
OCMStub([mockGetAccountInfoResponseUser email]).andReturn(kEmail);
OCMStub([mockGetAccountInfoResponseUser passwordHash]).andReturn(kPasswordHash);
if (phoneNumber.length) {
NSDictionary *userInfoDictionary = @{@"providerId" : FIRPhoneAuthProviderID};
FIRGetAccountInfoResponseProviderUserInfo *userInfo =
[[FIRGetAccountInfoResponseProviderUserInfo alloc] initWithDictionary:userInfoDictionary];
OCMStub([mockGetAccountInfoResponseUser providerUserInfo]).andReturn(@[ userInfo ]);
OCMStub([mockGetAccountInfoResponseUser phoneNumber]).andReturn(phoneNumber);
}
return mockGetAccountInfoResponseUser;
};
OCMExpect([self->_mockBackend setAccountInfo:[OCMArg any] callback:[OCMArg any]])
.andCallBlock2(^(FIRSetAccountInfoRequest *_Nullable request,
FIRSetAccountInfoResponseCallback callback) {
XCTAssertEqualObjects(request.APIKey, kAPIKey);
XCTAssertEqualObjects(request.accessToken, kAccessToken);
XCTAssertNotNil(request.deleteProviders);
XCTAssertNil(request.email);
XCTAssertNil(request.localID);
XCTAssertNil(request.displayName);
XCTAssertNil(request.photoURL);
XCTAssertNil(request.password);
XCTAssertNil(request.providers);
XCTAssertNil(request.deleteAttributes);
dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
id mockSetAccountInfoResponse = OCMClassMock([FIRSetAccountInfoResponse class]);
callback(mockSetAccountInfoResponse, nil);
});
});
XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
id userInfoResponse = mockUserInfoWithPhoneNumber(nil);
[self
signInWithEmailPasswordWithMockUserInfoResponse:userInfoResponse
completion:^(FIRUser *user) {
[self
expectVerifyPhoneNumberRequestWithPhoneNumber:
kPhoneNumber
isLinkOperation:YES
error:nil];
id userInfoResponseUpdate =
mockUserInfoWithPhoneNumber(kPhoneNumber);
[self expectGetAccountInfoWithMockUserInfoResponse:
userInfoResponseUpdate];
FIRPhoneAuthCredential *credential =
[[FIRPhoneAuthProvider provider]
credentialWithVerificationID:kVerificationID
verificationCode:
kVerificationCode];
// Link phone credential.
[user
linkWithCredential:credential
completion:^(FIRAuthDataResult
*_Nullable linkAuthResult,
NSError *_Nullable error) {
XCTAssertNil(error);
XCTAssertEqualObjects(
[FIRAuth auth]
.currentUser.providerData
.firstObject.providerID,
FIRPhoneAuthProviderID);
XCTAssertEqualObjects(
[FIRAuth auth]
.currentUser.phoneNumber,
kPhoneNumber);
// Immediately unlink the phone auth
// provider.
[user
unlinkFromProvider:
FIRPhoneAuthProviderID
completion:^(
FIRUser *_Nullable user,
NSError
*_Nullable error) {
XCTAssertNil(error);
XCTAssertNil(
[FIRAuth auth]
.currentUser
.phoneNumber);
[expectation fulfill];
}];
}];
}];
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
OCMVerifyAll(_mockBackend);
}
/** @fn testlinkPhoneAuthCredentialFailure
@brief Tests the flow of a failed call to @c linkWithCredential:completion: due
to a phone provider already being linked.
*/
- (void)testlinkPhoneAuthCredentialFailure {
id (^mockUserInfoWithPhoneNumber)(NSString *) = ^(NSString *phoneNumber) {
id mockGetAccountInfoResponseUser = OCMClassMock([FIRGetAccountInfoResponseUser class]);
OCMStub([mockGetAccountInfoResponseUser localID]).andReturn(kLocalID);
OCMStub([mockGetAccountInfoResponseUser email]).andReturn(kEmail);
OCMStub([mockGetAccountInfoResponseUser passwordHash]).andReturn(kPasswordHash);
if (phoneNumber.length) {
OCMStub([mockGetAccountInfoResponseUser phoneNumber]).andReturn(phoneNumber);
}
return mockGetAccountInfoResponseUser;
};
XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
id userInfoResponse = mockUserInfoWithPhoneNumber(nil);
[self
signInWithEmailPasswordWithMockUserInfoResponse:userInfoResponse
completion:^(FIRUser *user) {
NSError *error =
[FIRAuthErrorUtils providerAlreadyLinkedError];
[self
expectVerifyPhoneNumberRequestWithPhoneNumber:nil
isLinkOperation:YES
error:
error];
FIRPhoneAuthCredential *credential =
[[FIRPhoneAuthProvider provider]
credentialWithVerificationID:kVerificationID
verificationCode:
kVerificationCode];
[user
linkWithCredential:credential
completion:^(FIRAuthDataResult
*_Nullable linkAuthResult,
NSError *_Nullable error) {
XCTAssertEqual(
error.code,
FIRAuthErrorCodeProviderAlreadyLinked);
[expectation fulfill];
}];
}];
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
OCMVerifyAll(_mockBackend);
}
/** @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.
*/
- (void)testlinkPhoneCredentialAlreadyExistsError {
id (^mockUserInfoWithPhoneNumber)(NSString *) = ^(NSString *phoneNumber) {
id mockGetAccountInfoResponseUser = OCMClassMock([FIRGetAccountInfoResponseUser class]);
OCMStub([mockGetAccountInfoResponseUser localID]).andReturn(kLocalID);
OCMStub([mockGetAccountInfoResponseUser email]).andReturn(kEmail);
OCMStub([mockGetAccountInfoResponseUser passwordHash]).andReturn(kPasswordHash);
if (phoneNumber.length) {
OCMStub([mockGetAccountInfoResponseUser phoneNumber]).andReturn(phoneNumber);
}
return mockGetAccountInfoResponseUser;
};
void (^expectVerifyPhoneNumberRequest)(NSString *) = ^(NSString *phoneNumber) {
OCMExpect([self->_mockBackend verifyPhoneNumber:[OCMArg any] callback:[OCMArg any]])
.andCallBlock2(^(FIRVerifyPhoneNumberRequest *_Nullable request,
FIRVerifyPhoneNumberResponseCallback callback) {
XCTAssertEqualObjects(request.verificationID, kVerificationID);
XCTAssertEqualObjects(request.verificationCode, kVerificationCode);
XCTAssertEqualObjects(request.accessToken, kAccessToken);
dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
FIRPhoneAuthCredential *credential =
[[FIRPhoneAuthCredential alloc] initWithTemporaryProof:kTemporaryProof
phoneNumber:kPhoneNumber
providerID:FIRPhoneAuthProviderID];
callback(nil, [FIRAuthErrorUtils credentialAlreadyInUseErrorWithMessage:nil
credential:credential
email:nil]);
});
});
};
XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
id userInfoResponse = mockUserInfoWithPhoneNumber(nil);
[self
signInWithEmailPasswordWithMockUserInfoResponse:userInfoResponse
completion:^(FIRUser *user) {
expectVerifyPhoneNumberRequest(kPhoneNumber);
FIRPhoneAuthCredential *credential =
[[FIRPhoneAuthProvider provider]
credentialWithVerificationID:kVerificationID
verificationCode:
kVerificationCode];
[user
linkWithCredential:credential
completion:^(FIRAuthDataResult
*_Nullable linkAuthResult,
NSError *_Nullable error) {
XCTAssertNil(linkAuthResult);
XCTAssertEqual(
error.code,
FIRAuthErrorCodeCredentialAlreadyInUse);
FIRPhoneAuthCredential *credential =
error.userInfo
[FIRAuthErrorUserInfoUpdatedCredentialKey];
XCTAssertEqual(credential.temporaryProof,
kTemporaryProof);
XCTAssertEqual(credential.phoneNumber,
kPhoneNumber);
[expectation fulfill];
}];
}];
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
OCMVerifyAll(_mockBackend);
}
#endif
#pragma mark - Helpers
/** @fn getIDTokenResultForcingRefreshSuccess
@brief Helper for testing the flow of a successful @c
getIDTokenResultForcingRefresh:completion: call.
*/
- (void)getIDTokenResultForcingRefreshSuccessWithIDToken:(NSString *)IDToken {
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([self->_mockBackend
secureToken:[OCMArg any]
callback:[OCMArg any]])
.andCallBlock2(^(
FIRSecureTokenRequest *_Nullable request,
FIRSecureTokenResponseCallback callback) {
XCTAssertEqualObjects(request.APIKey, kAPIKey);
dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
id mockSecureTokenResponse = OCMClassMock(
[FIRSecureTokenResponse class]);
OCMStub([mockSecureTokenResponse accessToken])
.andReturn(IDToken);
callback(mockSecureTokenResponse, nil);
});
});
[user
getIDTokenResultForcingRefresh:YES
completion:^(
FIRAuthTokenResult
*_Nullable tokenResult,
NSError *_Nullable error) {
XCTAssertTrue(
[NSThread isMainThread]);
XCTAssertNil(error);
XCTAssertEqualObjects(
tokenResult.token,
IDToken);
XCTAssertTrue(
tokenResult
.issuedAtDate &&
[tokenResult.issuedAtDate
isKindOfClass:
[NSDate class]]);
XCTAssertTrue(
tokenResult.authDate &&
[tokenResult.authDate
isKindOfClass:
[NSDate class]]);
XCTAssertTrue(
tokenResult
.expirationDate &&
[tokenResult
.expirationDate
isKindOfClass:
[NSDate class]]);
XCTAssertTrue(
tokenResult.claims &&
[tokenResult.claims
isKindOfClass:
[NSDictionary
class]]);
[expectation fulfill];
}];
}];
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
OCMVerifyAll(_mockBackend);
}
/** @fn signInAnonymouslyWithMockGetAccountInfoResponse:completion:
@brief Signs in with an anonymous account with mocked backend end calls.
@param mockUserInfoResponse A mocked FIRGetAccountInfoResponseUser object.
@param completion The completion block that takes the newly signed-in user as the only
parameter.
*/
- (void)signInAnonymouslyWithMockGetAccountInfoResponse:(id)mockUserInfoResponse
completion:(void (^)(FIRUser *user))completion {
OCMExpect([_mockBackend signUpNewUser:[OCMArg any] callback:[OCMArg any]])
.andCallBlock2(
^(FIRSignUpNewUserRequest *_Nullable request, FIRSignupNewUserCallback callback) {
id mockSignUpResponse = OCMClassMock([FIRSignUpNewUserResponse class]);
OCMStub([mockSignUpResponse IDToken]).andReturn(kAccessToken);
OCMStub([mockSignUpResponse approximateExpirationDate])
.andReturn([NSDate dateWithTimeIntervalSinceNow:kAccessTokenTimeToLive]);
OCMStub([mockSignUpResponse refreshToken]).andReturn(kRefreshToken);
callback(mockSignUpResponse, nil);
});
[self expectGetAccountInfoWithMockUserInfoResponse:mockUserInfoResponse];
[[FIRAuth auth] signOut:NULL];
[[FIRAuth auth] signInAnonymouslyWithCompletion:^(FIRAuthDataResult *_Nullable result,
NSError *_Nullable error) {
XCTAssertNotNil(result.user);
XCTAssertNil(error);
completion(result.user);
}];
}
/** @fn signInWithEmailPasswordWithMockGetAccountInfoResponse:completion:
@brief Signs in with an email and password account with mocked backend end calls.
@param mockUserInfoResponse A mocked FIRGetAccountInfoResponseUser object.
@param completion The completion block that takes the newly signed-in user as the only
parameter.
*/
- (void)signInWithEmailPasswordWithMockUserInfoResponse:(id)mockUserInfoResponse
completion:(void (^)(FIRUser *user))completion {
OCMExpect([_mockBackend verifyPassword:[OCMArg any] callback:[OCMArg any]])
.andCallBlock2(^(FIRVerifyPasswordRequest *_Nullable request,
FIRVerifyPasswordResponseCallback callback) {
dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
id mockVeriyPasswordResponse = OCMClassMock([FIRVerifyPasswordResponse class]);
OCMStub([mockVeriyPasswordResponse IDToken]).andReturn(kAccessToken);
OCMStub([mockVeriyPasswordResponse approximateExpirationDate])
.andReturn([NSDate dateWithTimeIntervalSinceNow:kAccessTokenTimeToLive]);
OCMStub([mockVeriyPasswordResponse refreshToken]).andReturn(kRefreshToken);
callback(mockVeriyPasswordResponse, nil);
});
});
[self expectGetAccountInfoWithMockUserInfoResponse:mockUserInfoResponse];
[[FIRAuth auth] signOut:NULL];
[[FIRAuth auth] signInWithEmail:kEmail
password:kFakePassword
completion:^(FIRAuthDataResult *_Nullable result, NSError *_Nullable error) {
XCTAssertNotNil(result.user);
XCTAssertNil(error);
completion(result.user);
}];
}
/** @fn signInWithEmailLinkWithMockGetAccountInfoResponse:completion:
@brief Signs in with an email link auth account with mocked backend end calls.
@param mockUserInfoResponse A mocked FIRGetAccountInfoResponseUser object.
@param completion The completion block that takes the newly signed-in user as the only
parameter.
*/
- (void)signInWithEmailLinkWithMockUserInfoResponse:(id)mockUserInfoResponse
completion:(void (^)(FIRUser *user))completion {
OCMExpect([_mockBackend emailLinkSignin:[OCMArg any] callback:[OCMArg any]])
.andCallBlock2(^(FIREmailLinkSignInRequest *_Nullable request,
FIREmailLinkSigninResponseCallback callback) {
id mockVerifyLinkResponse = OCMClassMock([FIREmailLinkSignInResponse class]);
OCMStub([mockVerifyLinkResponse IDToken]).andReturn(kAccessToken);
OCMStub([mockVerifyLinkResponse approximateExpirationDate])
.andReturn([NSDate dateWithTimeIntervalSinceNow:kAccessTokenTimeToLive]);
OCMStub([mockVerifyLinkResponse refreshToken]).andReturn(kRefreshToken);
callback(mockVerifyLinkResponse, nil);
});
[self expectGetAccountInfoWithMockUserInfoResponse:mockUserInfoResponse];
[[FIRAuth auth] signOut:NULL];
[[FIRAuth auth] signInWithEmail:kEmail
link:@"https://www.google.com?oobCode=aCode&mode=signIn"
completion:^(FIRAuthDataResult *_Nullable result, NSError *_Nullable error) {
XCTAssertNotNil(result.user);
XCTAssertNil(error);
completion(result.user);
}];
}
/** @fn expectGetAccountInfoWithMockUserInfoResponse:
@brief Expects a GetAccountInfo request on the mock backend and calls back with provided
fake account data.
@param mockUserInfoResponse A mock @c FIRGetAccountInfoResponseUser object containing user info.
*/
- (void)expectGetAccountInfoWithMockUserInfoResponse:(id)mockUserInfoResponse {
OCMExpect([self->_mockBackend getAccountInfo:[OCMArg any] callback:[OCMArg any]])
.andCallBlock2(^(FIRGetAccountInfoRequest *_Nullable request,
FIRGetAccountInfoResponseCallback callback) {
XCTAssertEqualObjects(request.APIKey, kAPIKey);
XCTAssertEqualObjects(request.accessToken, kAccessToken);
dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
id mockGetAccountInfoResponse = OCMClassMock([FIRGetAccountInfoResponse class]);
OCMStub([mockGetAccountInfoResponse users]).andReturn(@[ mockUserInfoResponse ]);
callback(mockGetAccountInfoResponse, nil);
});
});
}
/** @fn dictionaryWithUserInfoArray:
@brief Converts an array of @c FIRUserInfo into a dictionary that indexed by provider IDs.
@param userInfoArray An array of @c FIRUserInfo objects.
@return A dictionary contains same values as @c userInfoArray does but keyed by their
@c providerID .
*/
- (NSDictionary<NSString *, id<FIRUserInfo>> *)dictionaryWithUserInfoArray:
(NSArray<id<FIRUserInfo>> *)userInfoArray {
NSMutableDictionary<NSString *, id<FIRUserInfo>> *map =
[NSMutableDictionary dictionaryWithCapacity:userInfoArray.count];
for (id<FIRUserInfo> userInfo in userInfoArray) {
XCTAssertNil(map[userInfo.providerID]);
map[userInfo.providerID] = userInfo;
}
return map;
}
/** @fn stubSecureTokensWithMockResponse
@brief Creates stubs on the mock response object with access and refresh tokens
@param mockResponse The mock response object.
*/
- (void)stubTokensWithMockResponse:(id)mockResponse {
OCMStub([mockResponse IDToken]).andReturn(kAccessToken);
OCMStub([mockResponse approximateExpirationDate])
.andReturn([NSDate dateWithTimeIntervalSinceNow:kAccessTokenTimeToLive]);
OCMStub([mockResponse refreshToken]).andReturn(kRefreshToken);
}
/** @fn assertUserGoogle
@brief Asserts the given FIRUser matching the fake data returned by
@c expectGetAccountInfo:federatedID:displayName: .
@param user The user object to be verified.
*/
- (void)assertUserGoogle:(FIRUser *)user {
XCTAssertNotNil(user);
XCTAssertEqualObjects(user.uid, kLocalID);
XCTAssertEqualObjects(user.displayName, kGoogleDisplayName);
XCTAssertEqual(user.providerData.count, 1u);
id<FIRUserInfo> googleUserInfo = user.providerData[0];
XCTAssertEqualObjects(googleUserInfo.providerID, FIRGoogleAuthProviderID);
XCTAssertEqualObjects(googleUserInfo.uid, kGoogleID);
XCTAssertEqualObjects(googleUserInfo.displayName, kGoogleDisplayName);
XCTAssertEqualObjects(googleUserInfo.email, kGoogleEmail);
}
/** @fn assertUserFacebook
@brief Asserts the given FIRUser matching the fake data returned by
@c expectGetAccountInfo:federatedID:displayName: .
@param user The user object to be verified.
*/
- (void)assertUserFacebook:(FIRUser *)user {
XCTAssertNotNil(user);
XCTAssertEqualObjects(user.uid, kLocalID);
XCTAssertEqualObjects(user.displayName, kFacebookDisplayName);
XCTAssertEqual(user.providerData.count, 1u);
id<FIRUserInfo> googleUserInfo = user.providerData[0];
XCTAssertEqualObjects(googleUserInfo.providerID, FIRFacebookAuthProviderID);
XCTAssertEqualObjects(googleUserInfo.uid, kFacebookID);
XCTAssertEqualObjects(googleUserInfo.displayName, kFacebookDisplayName);
XCTAssertEqualObjects(googleUserInfo.email, kGoogleEmail);
}
/** @fn expectGetAccountInfo:federatedID:displayName:
@brief Expects a GetAccountInfo request on the mock backend and calls back with fake account
data for a Google Sign-In user.
*/
- (void)expectGetAccountInfo:(NSString *)providerId
federatedID:(NSString *)federatedID
displayName:(NSString *)displayName {
OCMExpect([self->_mockBackend getAccountInfo:[OCMArg any] callback:[OCMArg any]])
.andCallBlock2(^(FIRGetAccountInfoRequest *_Nullable request,
FIRGetAccountInfoResponseCallback callback) {
XCTAssertEqualObjects(request.APIKey, kAPIKey);
XCTAssertEqualObjects(request.accessToken, kAccessToken);
dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
id mockGoogleUserInfo = OCMClassMock([FIRGetAccountInfoResponseProviderUserInfo class]);
OCMStub([mockGoogleUserInfo providerID]).andReturn(providerId);
OCMStub([mockGoogleUserInfo displayName]).andReturn(displayName);
OCMStub([mockGoogleUserInfo federatedID]).andReturn(federatedID);
OCMStub([mockGoogleUserInfo email]).andReturn(kGoogleEmail);
id mockGetAccountInfoResponseUser = OCMClassMock([FIRGetAccountInfoResponseUser class]);
OCMStub([mockGetAccountInfoResponseUser localID]).andReturn(kLocalID);
OCMStub([mockGetAccountInfoResponseUser displayName]).andReturn(displayName);
OCMStub([mockGetAccountInfoResponseUser providerUserInfo])
.andReturn((@[ mockGoogleUserInfo ]));
id mockGetAccountInfoResponse = OCMClassMock([FIRGetAccountInfoResponse class]);
OCMStub([mockGetAccountInfoResponse users]).andReturn(@[
mockGetAccountInfoResponseUser
]);
callback(mockGetAccountInfoResponse, nil);
});
});
}
/** @fn expectVerifyAssertionRequest:federatedID:displayName:profile:providerAccessToken:
@brief Expects a Verify Assertion request on the mock backend and calls back with fake account
data.
*/
- (void)expectVerifyAssertionRequest:(NSString *)providerId
federatedID:(NSString *)federatedID
displayName:(NSString *)displayName
profile:(NSDictionary *)profile
providerIDToken:(nullable NSString *)providerIDToken
providerAccessToken:(NSString *)providerAccessToken {
OCMExpect([_mockBackend verifyAssertion:[OCMArg any] callback:[OCMArg any]])
.andCallBlock2(^(FIRVerifyAssertionRequest *_Nullable request,
FIRVerifyAssertionResponseCallback callback) {
XCTAssertEqualObjects(request.APIKey, kAPIKey);
XCTAssertEqualObjects(request.providerID, providerId);
XCTAssertEqualObjects(request.providerIDToken, providerIDToken);
XCTAssertEqualObjects(request.providerAccessToken, providerAccessToken);
XCTAssertTrue(request.returnSecureToken);
dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
id mockVeriyAssertionResponse = OCMClassMock([FIRVerifyAssertionResponse class]);
OCMStub([mockVeriyAssertionResponse federatedID]).andReturn(federatedID);
OCMStub([mockVeriyAssertionResponse providerID]).andReturn(providerId);
OCMStub([mockVeriyAssertionResponse localID]).andReturn(kLocalID);
OCMStub([mockVeriyAssertionResponse displayName]).andReturn(displayName);
OCMStub([mockVeriyAssertionResponse profile]).andReturn(profile);
OCMStub([mockVeriyAssertionResponse username]).andReturn(kUserName);
[self stubTokensWithMockResponse:mockVeriyAssertionResponse];
callback(mockVeriyAssertionResponse, nil);
});
});
[self expectGetAccountInfo:providerId federatedID:federatedID displayName:displayName];
}
#if TARGET_OS_IOS
/** @fn expectVerifyPhoneNumberRequestWithPhoneNumber:error:
@brief Expects a verify phone numner request on the mock backend and calls back with fake
account data or an error.
@param phoneNumber Optionally; The phone number to use in the mocked response.
@param isLinkOperation Boolean value that indicates whether or not this method is triggered by
a link operation.
@param error Optionally; The error to return in the mocked response.
*/
- (void)expectVerifyPhoneNumberRequestWithPhoneNumber:(nullable NSString *)phoneNumber
isLinkOperation:(BOOL)isLinkOperation
error:(nullable NSError *)error {
OCMExpect([self->_mockBackend verifyPhoneNumber:[OCMArg any] callback:[OCMArg any]])
.andCallBlock2(^(FIRVerifyPhoneNumberRequest *_Nullable request,
FIRVerifyPhoneNumberResponseCallback callback) {
XCTAssertEqualObjects(request.verificationID, kVerificationID);
XCTAssertEqualObjects(request.verificationCode, kVerificationCode);
if (isLinkOperation) {
XCTAssertEqual(request.operation, FIRAuthOperationTypeLink);
} else {
XCTAssertEqual(request.operation, FIRAuthOperationTypeUpdate);
}
XCTAssertEqualObjects(request.accessToken, kAccessToken);
dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
if (error) {
callback(nil, error);
return;
}
id mockVerifyPhoneNumberResponse = OCMClassMock([FIRVerifyPhoneNumberResponse class]);
OCMStub([mockVerifyPhoneNumberResponse phoneNumber]).andReturn(phoneNumber);
callback(mockVerifyPhoneNumberResponse, nil);
});
});
}
#endif
@end
NS_ASSUME_NONNULL_END