Files
firebase-ios-sdk/Example/Auth/Tests/FIRAuthTests.m
T

2031 lines
86 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 <FirebaseCore/FIRAppInternal.h>
#import <FirebaseAuth/FIREmailAuthProvider.h>
#import <FirebaseAuth/FIRGoogleAuthProvider.h>
#import <FirebaseAuth/FIRAdditionalUserInfo.h>
#import "FIRAuth_Internal.h"
#import "FIRAuthOperationType.h"
#import "FIRAuthErrorUtils.h"
#import "FIRAuthDispatcher.h"
#import "FIRAuthGlobalWorkQueue.h"
#import "FIRUser_Internal.h"
#import "FIRAuthBackend.h"
#import "FIRCreateAuthURIRequest.h"
#import "FIRCreateAuthURIResponse.h"
#import "FIRGetAccountInfoRequest.h"
#import "FIRGetAccountInfoResponse.h"
#import "FIRGetOOBConfirmationCodeRequest.h"
#import "FIRGetOOBConfirmationCodeResponse.h"
#import "FIRSecureTokenRequest.h"
#import "FIRSecureTokenResponse.h"
#import "FIRResetPasswordRequest.h"
#import "FIRResetPasswordResponse.h"
#import "FIRSetAccountInfoRequest.h"
#import "FIRSetAccountInfoResponse.h"
#import "FIRSignUpNewUserRequest.h"
#import "FIRSignUpNewUserResponse.h"
#import "FIRVerifyCustomTokenRequest.h"
#import "FIRVerifyCustomTokenResponse.h"
#import "FIRVerifyAssertionRequest.h"
#import "FIRVerifyAssertionResponse.h"
#import "FIRVerifyPasswordRequest.h"
#import "FIRVerifyPasswordResponse.h"
#import "FIRVerifyPhoneNumberRequest.h"
#import "FIRVerifyPhoneNumberResponse.h"
#import "FIRApp+FIRAuthUnitTests.h"
#import "OCMStubRecorder+FIRAuthUnitTests.h"
#import <OCMock/OCMock.h>
#if TARGET_OS_IOS
#import "FIRPhoneAuthCredential.h"
#import "FIRPhoneAuthProvider.h"
#endif
/** @var kFirebaseAppName1
@brief A fake Firebase app name.
*/
static NSString *const kFirebaseAppName1 = @"FIREBASE_APP_NAME_1";
/** @var kFirebaseAppName2
@brief Another fake Firebase app name.
*/
static NSString *const kFirebaseAppName2 = @"FIREBASE_APP_NAME_2";
/** @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 = @"ACCESS_TOKEN";
/** @var kNewAccessToken
@brief Another fake access token used to simulate token refreshed via automatic token refresh.
*/
NSString *kNewAccessToken = @"NewAccessToken";
/** @var kAccessTokenValidInterval
@brief The time to live for the fake access token.
*/
static const NSTimeInterval kAccessTokenTimeToLive = 60 * 60;
/** @var kTestTokenExpirationTimeInterval
@brief The fake time interval that it takes a token to expire.
*/
static const NSTimeInterval kTestTokenExpirationTimeInterval = 55 * 60;
/** @var kRefreshToken
@brief The fake refresh token.
*/
static NSString *const kRefreshToken = @"REFRESH_TOKEN";
/** @var kEmail
@brief The fake user email.
*/
static NSString *const kEmail = @"user@company.com";
/** @var kFakePassword
@brief The fake user password.
*/
static NSString *const kFakePassword = @"!@#$%^";
/** @var kPasswordHash
@brief The fake user password hash.
*/
static NSString *const kPasswordHash = @"UkVEQUNURUQ=";
/** @var kLocalID
@brief The fake local user ID.
*/
static NSString *const kLocalID = @"LOCAL_ID";
/** @var kDisplayName
@brief The fake user display name.
*/
static NSString *const kDisplayName = @"User Doe";
/** @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 kGoogleAccessToken
@brief The fake access token from Google Sign-In.
*/
static NSString *const kGoogleAccessToken = @"GOOGLE_ACCESS_TOKEN";
/** @var kGoogleIDToken
@brief The fake ID token from Google Sign-In.
*/
static NSString *const kGoogleIDToken = @"GOOGLE_ID_TOKEN";
/** @var kCustomToken
@brief The fake custom token to sign in.
*/
static NSString *const kCustomToken = @"CUSTOM_TOKEN";
/** @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 kExpectationTimeout
@brief The maximum time waiting for expectations to fulfill.
*/
static const NSTimeInterval kExpectationTimeout = 2;
/** @var kWaitInterval
@brief The time waiting for background tasks to finish before continue when necessary.
*/
static const NSTimeInterval kWaitInterval = .5;
/** @class FIRAuthTests
@brief Tests for @c FIRAuth.
*/
@interface FIRAuthTests : XCTestCase
@end
@implementation FIRAuthTests {
/** @var _mockBackend
@brief The mock @c FIRAuthBackendImplementation .
*/
id _mockBackend;
/** @var _FIRAuthDispatcherCallback
@brief Used to save a task from FIRAuthDispatcher to be executed later.
*/
__block void (^_Nonnull _FIRAuthDispatcherCallback)(void);
}
/** @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 = @{
@"iss": @"https://accounts.google.com\\",
@"email": kGoogleEmail,
@"given_name": @"User",
@"family_name": @"Doe"
};
});
return kGoogleProfile;
}
- (void)setUp {
[super setUp];
_mockBackend = OCMProtocolMock(@protocol(FIRAuthBackendImplementation));
[FIRAuthBackend setBackendImplementation:_mockBackend];
[FIRApp resetAppForAuthUnitTests];
// Set FIRAuthDispatcher implementation in order to save the token refresh task for later
// execution.
[[FIRAuthDispatcher sharedInstance]
setDispatchAfterImplementation:^(NSTimeInterval delay,
dispatch_queue_t _Nonnull queue,
void (^task)(void)) {
XCTAssertNotNil(task);
XCTAssert(delay > 0);
XCTAssertEqualObjects(FIRAuthGlobalWorkQueue(), queue);
_FIRAuthDispatcherCallback = task;
}];
}
- (void)tearDown {
[FIRAuthBackend setDefaultBackendImplementationWithRPCIssuer:nil];
[[FIRAuthDispatcher sharedInstance] setDispatchAfterImplementation:nil];
[super tearDown];
}
#pragma mark - Life Cycle Tests
/** @fn testSingleton
@brief Verifies the @c auth method behaves like a singleton.
*/
- (void)testSingleton {
FIRAuth *auth1 = [FIRAuth auth];
XCTAssertNotNil(auth1);
FIRAuth *auth2 = [FIRAuth auth];
XCTAssertEqual(auth1, auth2);
}
/** @fn testDefaultAuth
@brief Verifies the @c auth method associates with the default Firebase app.
*/
- (void)testDefaultAuth {
FIRAuth *auth1 = [FIRAuth auth];
FIRAuth *auth2 = [FIRAuth authWithApp:[FIRApp defaultApp]];
XCTAssertEqual(auth1, auth2);
XCTAssertEqual(auth1.app, [FIRApp defaultApp]);
}
/** @fn testNilAppException
@brief Verifies the @c auth method raises an exception if the default FIRApp is not configured.
*/
- (void)testNilAppException {
[FIRApp resetApps];
XCTAssertThrows([FIRAuth auth]);
}
/** @fn testAppAPIkey
@brief Verifies the API key is correctly copied from @c FIRApp to @c FIRAuth .
*/
- (void)testAppAPIkey {
FIRAuth *auth = [FIRAuth auth];
XCTAssertEqualObjects(auth.requestConfiguration.APIKey, kAPIKey);
}
/** @fn testAppAssociation
@brief Verifies each @c FIRApp instance associates with a @c FIRAuth .
*/
- (void)testAppAssociation {
FIRApp *app1 = [self app1];
FIRAuth *auth1 = [FIRAuth authWithApp:app1];
XCTAssertNotNil(auth1);
XCTAssertEqual(auth1.app, app1);
FIRApp *app2 = [self app2];
FIRAuth *auth2 = [FIRAuth authWithApp:app2];
XCTAssertNotNil(auth2);
XCTAssertEqual(auth2.app, app2);
XCTAssertNotEqual(auth1, auth2);
}
/** @fn testLifeCycle
@brief Verifies the life cycle of @c FIRAuth is the same as its associated @c FIRApp .
*/
- (void)testLifeCycle {
__weak FIRApp *app;
__weak FIRAuth *auth;
@autoreleasepool {
FIRApp *app1 = [self app1];
app = app1;
auth = [FIRAuth authWithApp:app1];
// Verify that neither the app nor the auth is released yet, i.e., the app owns the auth
// because nothing else retains the auth.
XCTAssertNotNil(app);
XCTAssertNotNil(auth);
}
[self waitForTimeIntervel:kWaitInterval];
// Verify that both the app and the auth are released upon exit of the autorelease pool,
// i.e., the app is the sole owner of the auth.
XCTAssertNil(app);
XCTAssertNil(auth);
}
/** @fn testGetUID
@brief Verifies that FIRApp's getUIDImplementation is correctly set by FIRAuth.
*/
- (void)testGetUID {
FIRApp *app = [FIRApp defaultApp];
XCTAssertNotNil(app.getUIDImplementation);
[[FIRAuth auth] signOut:NULL];
XCTAssertNil(app.getUIDImplementation());
[self waitForSignIn];
XCTAssertEqualObjects(app.getUIDImplementation(), kLocalID);
}
#pragma mark - Server API Tests
/** @fn testFetchProvidersForEmailSuccess
@brief Tests the flow of a successful @c fetchProvidersForEmail:completion: call.
*/
- (void)testFetchProvidersForEmailSuccess {
NSArray<NSString *> *allProviders =
@[ FIRGoogleAuthProviderID, FIREmailAuthProviderID ];
OCMExpect([_mockBackend createAuthURI:[OCMArg any]
callback:[OCMArg any]])
.andCallBlock2(^(FIRCreateAuthURIRequest *_Nullable request,
FIRCreateAuthURIResponseCallback callback) {
XCTAssertEqualObjects(request.identifier, kEmail);
XCTAssertNotNil(request.endpoint);
XCTAssertEqualObjects(request.APIKey, kAPIKey);
dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
id mockCreateAuthURIResponse = OCMClassMock([FIRCreateAuthURIResponse class]);
OCMStub([mockCreateAuthURIResponse allProviders]).andReturn(allProviders);
callback(mockCreateAuthURIResponse, nil);
});
});
XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
[[FIRAuth auth] fetchProvidersForEmail:kEmail
completion:^(NSArray<NSString *> *_Nullable providers,
NSError *_Nullable error) {
XCTAssertTrue([NSThread isMainThread]);
XCTAssertEqualObjects(providers, allProviders);
XCTAssertNil(error);
[expectation fulfill];
}];
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
OCMVerifyAll(_mockBackend);
}
/** @fn testFetchProvidersForEmailSuccessDeprecatedProviderID
@brief Tests the flow of a successful @c fetchProvidersForEmail:completion: call using the
deprecated FIREmailPasswordAuthProviderID.
*/
- (void)testFetchProvidersForEmailSuccessDeprecatedProviderID {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
NSArray<NSString *> *allProviders =
@[ FIRGoogleAuthProviderID, FIREmailPasswordAuthProviderID ];
#pragma clang diagnostic pop
OCMExpect([_mockBackend createAuthURI:[OCMArg any]
callback:[OCMArg any]])
.andCallBlock2(^(FIRCreateAuthURIRequest *_Nullable request,
FIRCreateAuthURIResponseCallback callback) {
XCTAssertEqualObjects(request.identifier, kEmail);
XCTAssertNotNil(request.endpoint);
XCTAssertEqualObjects(request.APIKey, kAPIKey);
dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
id mockCreateAuthURIResponse = OCMClassMock([FIRCreateAuthURIResponse class]);
OCMStub([mockCreateAuthURIResponse allProviders]).andReturn(allProviders);
callback(mockCreateAuthURIResponse, nil);
});
});
XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
[[FIRAuth auth] fetchProvidersForEmail:kEmail
completion:^(NSArray<NSString *> *_Nullable providers,
NSError *_Nullable error) {
XCTAssertTrue([NSThread isMainThread]);
XCTAssertEqualObjects(providers, allProviders);
XCTAssertNil(error);
[expectation fulfill];
}];
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
OCMVerifyAll(_mockBackend);
}
/** @fn testFetchProvidersForEmailFailure
@brief Tests the flow of a failed @c fetchProvidersForEmail:completion: call.
*/
- (void)testFetchProvidersForEmailFailure {
OCMExpect([_mockBackend createAuthURI:[OCMArg any] callback:[OCMArg any]])
.andDispatchError2([FIRAuthErrorUtils tooManyRequestsErrorWithMessage:nil]);
XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
[[FIRAuth auth] fetchProvidersForEmail:kEmail
completion:^(NSArray<NSString *> *_Nullable providers,
NSError *_Nullable error) {
XCTAssertTrue([NSThread isMainThread]);
XCTAssertNil(providers);
XCTAssertEqual(error.code, FIRAuthErrorCodeTooManyRequests);
XCTAssertNotNil(error.userInfo[NSLocalizedDescriptionKey]);
[expectation fulfill];
}];
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
OCMVerifyAll(_mockBackend);
}
#if TARGET_OS_IOS
/** @fn testPhoneAuthSuccess
@brief Tests the flow of a successful @c signInWithCredential:completion for phone auth.
*/
- (void)testPhoneAuthSuccess {
OCMExpect([_mockBackend verifyPhoneNumber:[OCMArg any] callback:[OCMArg any]])
.andCallBlock2(^(FIRVerifyPhoneNumberRequest *_Nullable request,
FIRVerifyPhoneNumberResponseCallback callback) {
XCTAssertEqualObjects(request.verificationCode, kVerificationCode);
XCTAssertEqualObjects(request.verificationID, kVerificationID);
XCTAssertEqual(request.operation, FIRAuthOperationTypeSignUpOrSignIn);
dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
id mockVerifyPhoneResponse = OCMClassMock([FIRVerifyPhoneNumberResponse class]);
[self stubTokensWithMockResponse:mockVerifyPhoneResponse];
// Stub isNewUser flag in the response.
OCMStub([mockVerifyPhoneResponse isNewUser]).andReturn(YES);
callback(mockVerifyPhoneResponse, nil);
});
});
[self expectGetAccountInfo];
XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
[[FIRAuth auth] signOut:NULL];
FIRAuthCredential *credential =
[[FIRPhoneAuthProvider provider] credentialWithVerificationID:kVerificationID
verificationCode:kVerificationCode];
[[FIRAuth auth] signInAndRetrieveDataWithCredential:credential
completion:^(FIRAuthDataResult *_Nullable authDataResult,
NSError *_Nullable error) {
XCTAssertTrue([NSThread isMainThread]);
[self assertUser:authDataResult.user];
XCTAssertTrue(authDataResult.additionalUserInfo.isNewUser);
XCTAssertNil(error);
[expectation fulfill];
}];
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
[self assertUser:[FIRAuth auth].currentUser];
OCMVerifyAll(_mockBackend);
}
/** @fn testPhoneAuthMissingVerificationCode
@brief Tests the flow of an unsuccessful @c signInWithCredential:completion for phone auth due
to an empty verification code
*/
- (void)testPhoneAuthMissingVerificationCode {
[self expectGetAccountInfo];
XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
[[FIRAuth auth] signOut:NULL];
FIRAuthCredential *credential =
[[FIRPhoneAuthProvider provider] credentialWithVerificationID:kVerificationID
verificationCode:@""];
[[FIRAuth auth] signInWithCredential:credential completion:^(FIRUser *_Nullable user,
NSError *_Nullable error) {
XCTAssertTrue([NSThread isMainThread]);
XCTAssertNil(user);
XCTAssertEqual(error.code, FIRAuthErrorCodeMissingVerificationCode);
[expectation fulfill];
}];
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
}
/** @fn testPhoneAuthMissingVerificationID
@brief Tests the flow of an unsuccessful @c signInWithCredential:completion for phone auth due
to an empty verification ID.
*/
- (void)testPhoneAuthMissingVerificationID {
[self expectGetAccountInfo];
XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
[[FIRAuth auth] signOut:NULL];
FIRAuthCredential *credential =
[[FIRPhoneAuthProvider provider] credentialWithVerificationID:@""
verificationCode:kVerificationCode];
[[FIRAuth auth] signInWithCredential:credential completion:^(FIRUser *_Nullable user,
NSError *_Nullable error) {
XCTAssertTrue([NSThread isMainThread]);
XCTAssertNil(user);
XCTAssertEqual(error.code, FIRAuthErrorCodeMissingVerificationID);
[expectation fulfill];
}];
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
}
#endif
/** @fn testSignInWithEmailPasswordSuccess
@brief Tests the flow of a successful @c signInWithEmail:password:completion: call.
*/
- (void)testSignInWithEmailPasswordSuccess {
OCMExpect([_mockBackend verifyPassword:[OCMArg any] callback:[OCMArg any]])
.andCallBlock2(^(FIRVerifyPasswordRequest *_Nullable request,
FIRVerifyPasswordResponseCallback callback) {
XCTAssertEqualObjects(request.APIKey, kAPIKey);
XCTAssertEqualObjects(request.email, kEmail);
XCTAssertEqualObjects(request.password, kFakePassword);
XCTAssertTrue(request.returnSecureToken);
dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
id mockVerifyPasswordResponse = OCMClassMock([FIRVerifyPasswordResponse class]);
[self stubTokensWithMockResponse:mockVerifyPasswordResponse];
callback(mockVerifyPasswordResponse, nil);
});
});
[self expectGetAccountInfo];
XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
[[FIRAuth auth] signOut:NULL];
[[FIRAuth auth] signInWithEmail:kEmail password:kFakePassword completion:^(FIRUser *_Nullable user,
NSError *_Nullable error) {
XCTAssertTrue([NSThread isMainThread]);
[self assertUser:user];
XCTAssertNil(error);
[expectation fulfill];
}];
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
[self assertUser:[FIRAuth auth].currentUser];
OCMVerifyAll(_mockBackend);
}
/** @fn testSignInWithEmailPasswordFailure
@brief Tests the flow of a failed @c signInWithEmail:password:completion: call.
*/
- (void)testSignInWithEmailPasswordFailure {
OCMExpect([_mockBackend verifyPassword:[OCMArg any] callback:[OCMArg any]])
.andDispatchError2([FIRAuthErrorUtils wrongPasswordErrorWithMessage:nil]);
XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
[[FIRAuth auth] signOut:NULL];
[[FIRAuth auth] signInWithEmail:kEmail password:kFakePassword completion:^(FIRUser *_Nullable user,
NSError *_Nullable error) {
XCTAssertTrue([NSThread isMainThread]);
XCTAssertNil(user);
XCTAssertEqual(error.code, FIRAuthErrorCodeWrongPassword);
XCTAssertNotNil(error.userInfo[NSLocalizedDescriptionKey]);
[expectation fulfill];
}];
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
XCTAssertNil([FIRAuth auth].currentUser);
OCMVerifyAll(_mockBackend);
}
/** @fn testSignInAndRetrieveDataWithEmailPasswordSuccess
@brief Tests the flow of a successful @c signInAndRetrieveDataWithEmail:password:completion:
call.
*/
- (void)testSignInAndRetrieveDataWithEmailPasswordSuccess {
OCMExpect([_mockBackend verifyPassword:[OCMArg any] callback:[OCMArg any]])
.andCallBlock2(^(FIRVerifyPasswordRequest *_Nullable request,
FIRVerifyPasswordResponseCallback callback) {
XCTAssertEqualObjects(request.APIKey, kAPIKey);
XCTAssertEqualObjects(request.email, kEmail);
XCTAssertEqualObjects(request.password, kFakePassword);
XCTAssertTrue(request.returnSecureToken);
dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
id mockVerifyPasswordResponse = OCMClassMock([FIRVerifyPasswordResponse class]);
[self stubTokensWithMockResponse:mockVerifyPasswordResponse];
callback(mockVerifyPasswordResponse, nil);
});
});
[self expectGetAccountInfo];
XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
[[FIRAuth auth] signOut:NULL];
[[FIRAuth auth] signInAndRetrieveDataWithEmail:kEmail
password:kFakePassword
completion:^(FIRAuthDataResult *_Nullable result,
NSError *_Nullable error) {
XCTAssertTrue([NSThread isMainThread]);
[self assertUser:result.user];
XCTAssertFalse(result.additionalUserInfo.isNewUser);
XCTAssertEqualObjects(result.additionalUserInfo.providerID, FIREmailAuthProviderID);
XCTAssertNil(error);
[expectation fulfill];
}];
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
[self assertUser:[FIRAuth auth].currentUser];
OCMVerifyAll(_mockBackend);
}
/** @fn testSignInAndRetrieveDataWithEmailPasswordFailure
@brief Tests the flow of a failed @c signInAndRetrieveDataWithEmail:password:completion: call.
*/
- (void)testSignInAndRetrieveDataWithEmailPasswordFailure {
OCMExpect([_mockBackend verifyPassword:[OCMArg any] callback:[OCMArg any]])
.andDispatchError2([FIRAuthErrorUtils wrongPasswordErrorWithMessage:nil]);
XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
[[FIRAuth auth] signOut:NULL];
[[FIRAuth auth] signInAndRetrieveDataWithEmail:kEmail
password:kFakePassword
completion:^(FIRAuthDataResult *_Nullable result,
NSError *_Nullable error) {
XCTAssertTrue([NSThread isMainThread]);
XCTAssertNil(result);
XCTAssertEqual(error.code, FIRAuthErrorCodeWrongPassword);
XCTAssertNotNil(error.userInfo[NSLocalizedDescriptionKey]);
[expectation fulfill];
}];
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
XCTAssertNil([FIRAuth auth].currentUser);
OCMVerifyAll(_mockBackend);
}
/** @fn testResetPasswordSuccess
@brief Tests the flow of a successful @c confirmPasswordResetWithCode:newPassword:completion:
call.
*/
- (void)testResetPasswordSuccess {
NSString *fakeEmail = @"fakeEmail";
NSString *fakeCode = @"fakeCode";
NSString *fakeNewPassword = @"fakeNewPassword";
OCMExpect([_mockBackend resetPassword:[OCMArg any] callback:[OCMArg any]])
.andCallBlock2(^(FIRResetPasswordRequest *_Nullable request,
FIRResetPasswordCallback callback) {
XCTAssertEqualObjects(request.APIKey, kAPIKey);
XCTAssertEqualObjects(request.oobCode, fakeCode);
XCTAssertEqualObjects(request.updatedPassword, fakeNewPassword);
dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
id mockResetPasswordResponse = OCMClassMock([FIRResetPasswordResponse class]);
OCMStub([mockResetPasswordResponse email]).andReturn(fakeEmail);
callback(mockResetPasswordResponse, nil);
});
});
XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
[[FIRAuth auth] signOut:NULL];
[[FIRAuth auth] confirmPasswordResetWithCode:fakeCode
newPassword:fakeNewPassword
completion:^(NSError *_Nullable error) {
XCTAssertTrue([NSThread isMainThread]);
XCTAssertNil(error);
[expectation fulfill];
}];
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
OCMVerifyAll(_mockBackend);
}
/** @fn testResetPasswordFailure
@brief Tests the flow of a failed @c confirmPasswordResetWithCode:newPassword:completion:
call.
*/
- (void)testResetPasswordFailure {
NSString *fakeCode = @"fakeCode";
NSString *fakeNewPassword = @"fakeNewPassword";
OCMExpect([_mockBackend resetPassword:[OCMArg any] callback:[OCMArg any]])
._andDispatchError2([FIRAuthErrorUtils invalidActionCodeErrorWithMessage:nil]);
XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
[[FIRAuth auth] signOut:NULL];
[[FIRAuth auth] confirmPasswordResetWithCode:fakeCode
newPassword:fakeNewPassword
completion:^(NSError *_Nullable error) {
XCTAssertTrue([NSThread isMainThread]);
XCTAssertEqual(error.code, FIRAuthErrorCodeInvalidActionCode);
[expectation fulfill];
}];
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
OCMVerifyAll(_mockBackend);
}
/** @fn testCheckActionCodeSuccess
@brief Tests the flow of a successful @c checkActionCode:completion call.
*/
- (void)testCheckActionCodeSuccess {
NSString *verifyEmailRequestType = @"VERIFY_EMAIL";
NSString *fakeEmail = @"fakeEmail";
NSString *fakeNewEmail = @"fakeNewEmail";
NSString *fakeCode = @"fakeCode";
OCMExpect([_mockBackend resetPassword:[OCMArg any] callback:[OCMArg any]])
.andCallBlock2(^(FIRResetPasswordRequest *_Nullable request,
FIRResetPasswordCallback callback) {
XCTAssertEqualObjects(request.APIKey, kAPIKey);
XCTAssertEqualObjects(request.oobCode, fakeCode);
dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
id mockResetPasswordResponse = OCMClassMock([FIRResetPasswordResponse class]);
OCMStub([mockResetPasswordResponse email]).andReturn(fakeEmail);
OCMStub([mockResetPasswordResponse verifiedEmail]).andReturn(fakeNewEmail);
OCMStubRecorder *stub =
OCMStub([(FIRResetPasswordResponse *) mockResetPasswordResponse requestType]);
stub.andReturn(verifyEmailRequestType);
callback(mockResetPasswordResponse, nil);
});
});
XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
[[FIRAuth auth] checkActionCode:fakeCode completion:^(FIRActionCodeInfo *_Nullable info,
NSError *_Nullable error) {
XCTAssertTrue([NSThread isMainThread]);
XCTAssertNil(error);
XCTAssertEqual(info.operation, FIRActionCodeOperationVerifyEmail);
XCTAssert([fakeNewEmail isEqualToString:[info dataForKey:FIRActionCodeEmailKey]]);
[expectation fulfill];
}];
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
OCMVerifyAll(_mockBackend);
}
/** @fn testCheckActionCodeFailure
@brief Tests the flow of a failed @c checkActionCode:completion call.
*/
- (void)testCheckActionCodeFailure {
NSString *fakeCode = @"fakeCode";
OCMExpect([_mockBackend resetPassword:[OCMArg any] callback:[OCMArg any]])
._andDispatchError2([FIRAuthErrorUtils expiredActionCodeErrorWithMessage:nil]);
XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
[[FIRAuth auth] signOut:NULL];
[[FIRAuth auth] checkActionCode:fakeCode completion:^(FIRActionCodeInfo *_Nullable info,
NSError *_Nullable error) {
XCTAssertTrue([NSThread isMainThread]);
XCTAssertNotNil(error);
XCTAssertEqual(error.code, FIRAuthErrorCodeExpiredActionCode);
[expectation fulfill];
}];
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
OCMVerifyAll(_mockBackend);
}
/** @fn testApplyActionCodeSuccess
@brief Tests the flow of a successful @c applyActionCode:completion call.
*/
- (void)testApplyActionCodeSuccess {
NSString *fakeCode = @"fakeCode";
OCMExpect([_mockBackend setAccountInfo:[OCMArg any] callback:[OCMArg any]])
.andCallBlock2(^(FIRSetAccountInfoRequest *_Nullable request,
FIRSetAccountInfoResponseCallback callback) {
XCTAssertEqualObjects(request.APIKey, kAPIKey);
XCTAssertEqualObjects(request.OOBCode, fakeCode);
dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
id mockSetAccountInfoResponse = OCMClassMock([FIRSetAccountInfoResponse class]);
callback(mockSetAccountInfoResponse, nil);
});
});
XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
[[FIRAuth auth] applyActionCode:fakeCode completion:^(NSError *_Nullable error) {
XCTAssertTrue([NSThread isMainThread]);
XCTAssertNil(error);
[expectation fulfill];
}];
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
OCMVerifyAll(_mockBackend);
}
/** @fn testApplyActionCodeFailure
@brief Tests the flow of a failed @c checkActionCode:completion call.
*/
- (void)testApplyActionCodeFailure {
NSString *fakeCode = @"fakeCode";
OCMExpect([_mockBackend setAccountInfo:[OCMArg any] callback:[OCMArg any]])
._andDispatchError2([FIRAuthErrorUtils invalidActionCodeErrorWithMessage:nil]);
XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
[[FIRAuth auth] signOut:NULL];
[[FIRAuth auth] applyActionCode:fakeCode completion:^(NSError *_Nullable error) {
XCTAssertTrue([NSThread isMainThread]);
XCTAssertNotNil(error);
XCTAssertEqual(error.code, FIRAuthErrorCodeInvalidActionCode);
[expectation fulfill];
}];
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
OCMVerifyAll(_mockBackend);
}
/** @fn testVerifyPasswordResetCodeSuccess
@brief Tests the flow of a successful @c verifyPasswordResetCode:completion call.
*/
- (void)testVerifyPasswordResetCodeSuccess {
NSString *passwordResetRequestType = @"PASSWORD_RESET";
NSString *fakeEmail = @"fakeEmail";
NSString *fakeCode = @"fakeCode";
OCMExpect([_mockBackend resetPassword:[OCMArg any] callback:[OCMArg any]])
.andCallBlock2(^(FIRResetPasswordRequest *_Nullable request,
FIRResetPasswordCallback callback) {
XCTAssertEqualObjects(request.APIKey, kAPIKey);
XCTAssertEqualObjects(request.oobCode, fakeCode);
dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
id mockResetPasswordResponse = OCMClassMock([FIRResetPasswordResponse class]);
OCMStub([mockResetPasswordResponse email]).andReturn(fakeEmail);
OCMStubRecorder *stub =
OCMStub([(FIRResetPasswordResponse *) mockResetPasswordResponse requestType]);
stub.andReturn(passwordResetRequestType);
callback(mockResetPasswordResponse, nil);
});
});
XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
[[FIRAuth auth] verifyPasswordResetCode:fakeCode completion:^(NSString *_Nullable email,
NSError *_Nullable error) {
XCTAssertTrue([NSThread isMainThread]);
XCTAssertNil(error);
XCTAssertEqual(email, fakeEmail);
[expectation fulfill];
}];
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
OCMVerifyAll(_mockBackend);
}
/** @fn testVerifyPasswordResetCodeFailure
@brief Tests the flow of a failed @c verifyPasswordResetCode:completion call.
*/
- (void)testVeridyPasswordResetCodeFailure {
NSString *fakeCode = @"fakeCode";
OCMExpect([_mockBackend resetPassword:[OCMArg any] callback:[OCMArg any]])
._andDispatchError2([FIRAuthErrorUtils invalidActionCodeErrorWithMessage:nil]);
XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
[[FIRAuth auth] signOut:NULL];
[[FIRAuth auth] verifyPasswordResetCode:fakeCode completion:^(NSString *_Nullable email,
NSError *_Nullable error) {
XCTAssertTrue([NSThread isMainThread]);
XCTAssertNotNil(error);
XCTAssertEqual(error.code, FIRAuthErrorCodeInvalidActionCode);
[expectation fulfill];
}];
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
OCMVerifyAll(_mockBackend);
}
/** @fn testSignInWithEmailCredentialSuccess
@brief Tests the flow of a successfully @c signInWithCredential:completion: call with an
email-password credential.
*/
- (void)testSignInWithEmailCredentialSuccess {
OCMExpect([_mockBackend verifyPassword:[OCMArg any] callback:[OCMArg any]])
.andCallBlock2(^(FIRVerifyPasswordRequest *_Nullable request,
FIRVerifyPasswordResponseCallback callback) {
XCTAssertEqualObjects(request.APIKey, kAPIKey);
XCTAssertEqualObjects(request.email, kEmail);
XCTAssertEqualObjects(request.password, kFakePassword);
XCTAssertTrue(request.returnSecureToken);
dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
id mockVeriyPasswordResponse = OCMClassMock([FIRVerifyPasswordResponse class]);
[self stubTokensWithMockResponse:mockVeriyPasswordResponse];
callback(mockVeriyPasswordResponse, nil);
});
});
[self expectGetAccountInfo];
XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
[[FIRAuth auth] signOut:NULL];
FIRAuthCredential *emailCredential =
[FIREmailAuthProvider credentialWithEmail:kEmail password:kFakePassword];
[[FIRAuth auth] signInWithCredential:emailCredential completion:^(FIRUser *_Nullable user,
NSError *_Nullable error) {
XCTAssertTrue([NSThread isMainThread]);
[self assertUser:user];
XCTAssertNil(error);
[expectation fulfill];
}];
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
[self assertUser:[FIRAuth auth].currentUser];
OCMVerifyAll(_mockBackend);
}
/** @fn testSignInWithEmailCredentialSuccess
@brief Tests the flow of a successfully @c signInWithCredential:completion: call with an
email-password credential using the deprecated FIREmailPasswordAuthProvider.
*/
- (void)testSignInWithEmailCredentialSuccessWithDepricatedProvider {
OCMExpect([_mockBackend verifyPassword:[OCMArg any] callback:[OCMArg any]])
.andCallBlock2(^(FIRVerifyPasswordRequest *_Nullable request,
FIRVerifyPasswordResponseCallback callback) {
XCTAssertEqualObjects(request.APIKey, kAPIKey);
XCTAssertEqualObjects(request.email, kEmail);
XCTAssertEqualObjects(request.password, kFakePassword);
XCTAssertTrue(request.returnSecureToken);
dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
id mockVeriyPasswordResponse = OCMClassMock([FIRVerifyPasswordResponse class]);
[self stubTokensWithMockResponse:mockVeriyPasswordResponse];
callback(mockVeriyPasswordResponse, nil);
});
});
[self expectGetAccountInfo];
XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
[[FIRAuth auth] signOut:NULL];
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
FIRAuthCredential *emailCredential =
[FIREmailPasswordAuthProvider credentialWithEmail:kEmail password:kFakePassword];
#pragma clang diagnostic pop
[[FIRAuth auth] signInWithCredential:emailCredential completion:^(FIRUser *_Nullable user,
NSError *_Nullable error) {
XCTAssertTrue([NSThread isMainThread]);
[self assertUser:user];
XCTAssertNil(error);
[expectation fulfill];
}];
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
[self assertUser:[FIRAuth auth].currentUser];
OCMVerifyAll(_mockBackend);
}
/** @fn testSignInWithEmailCredentialFailure
@brief Tests the flow of a failed @c signInWithCredential:completion: call with an
email-password credential.
*/
- (void)testSignInWithEmailCredentialFailure {
OCMExpect([_mockBackend verifyPassword:[OCMArg any] callback:[OCMArg any]])
.andDispatchError2([FIRAuthErrorUtils userDisabledErrorWithMessage:nil]);
XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
[[FIRAuth auth] signOut:NULL];
FIRAuthCredential *emailCredential =
[FIREmailAuthProvider credentialWithEmail:kEmail password:kFakePassword];
[[FIRAuth auth] signInWithCredential:emailCredential completion:^(FIRUser *_Nullable user,
NSError *_Nullable error) {
XCTAssertTrue([NSThread isMainThread]);
XCTAssertNil(user);
XCTAssertEqual(error.code, FIRAuthErrorCodeUserDisabled);
XCTAssertNotNil(error.userInfo[NSLocalizedDescriptionKey]);
[expectation fulfill];
}];
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
XCTAssertNil([FIRAuth auth].currentUser);
OCMVerifyAll(_mockBackend);
}
/** @fn testSignInWithEmailCredentialEmptyPassword
@brief Tests the flow of a failed @c signInWithCredential:completion: call with an
email-password credential using an empty password. This error occurs on the client side,
so there is no need to fake an RPC response.
*/
- (void)testSignInWithEmailCredentialEmptyPassword {
NSString *emptyString = @"";
[self expectGetAccountInfo];
XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
[[FIRAuth auth] signOut:NULL];
FIRAuthCredential *emailCredential =
[FIREmailAuthProvider credentialWithEmail:kEmail password:emptyString];
[[FIRAuth auth] signInWithCredential:emailCredential completion:^(FIRUser *_Nullable user,
NSError *_Nullable error) {
XCTAssertTrue([NSThread isMainThread]);
XCTAssertEqual(error.code, FIRAuthErrorCodeWrongPassword);
[expectation fulfill];
}];
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
}
/** @fn testSignInWithGoogleAccountExistsError
@brief Tests the flow of a failed @c signInWithCredential:completion: with a Google credential
where the backend returns a needs @needConfirmation equal to true. An
FIRAuthErrorCodeAccountExistsWithDifferentCredential error should be thrown.
*/
- (void)testSignInWithGoogleAccountExistsError {
OCMExpect([_mockBackend verifyAssertion:[OCMArg any] callback:[OCMArg any]])
.andCallBlock2(^(FIRVerifyAssertionRequest *_Nullable request,
FIRVerifyAssertionResponseCallback callback) {
XCTAssertEqualObjects(request.APIKey, kAPIKey);
XCTAssertEqualObjects(request.providerID, FIRGoogleAuthProviderID);
XCTAssertEqualObjects(request.providerIDToken, kGoogleIDToken);
XCTAssertEqualObjects(request.providerAccessToken, kGoogleAccessToken);
XCTAssertTrue(request.returnSecureToken);
dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
id mockVeriyAssertionResponse = OCMClassMock([FIRVerifyAssertionResponse class]);
OCMStub([mockVeriyAssertionResponse needConfirmation]).andReturn(YES);
OCMStub([mockVeriyAssertionResponse email]).andReturn(kEmail);
[self stubTokensWithMockResponse:mockVeriyAssertionResponse];
callback(mockVeriyAssertionResponse, nil);
});
});
XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
[[FIRAuth auth] signOut:NULL];
FIRAuthCredential *googleCredential =
[FIRGoogleAuthProvider credentialWithIDToken:kGoogleIDToken accessToken:kGoogleAccessToken];
[[FIRAuth auth] signInWithCredential:googleCredential completion:^(FIRUser *_Nullable user,
NSError *_Nullable error) {
XCTAssertTrue([NSThread isMainThread]);
XCTAssertEqual(error.code, FIRAuthErrorCodeAccountExistsWithDifferentCredential);
XCTAssertEqualObjects(error.userInfo[FIRAuthErrorUserInfoEmailKey], kEmail);
[expectation fulfill];
}];
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
OCMVerifyAll(_mockBackend);
}
/** @fn testSignInWithGoogleCredentialSuccess
@brief Tests the flow of a successful @c signInWithCredential:completion: call with an
Google Sign-In credential.
*/
- (void)testSignInWithGoogleCredentialSuccess {
OCMExpect([_mockBackend verifyAssertion:[OCMArg any] callback:[OCMArg any]])
.andCallBlock2(^(FIRVerifyAssertionRequest *_Nullable request,
FIRVerifyAssertionResponseCallback callback) {
XCTAssertEqualObjects(request.APIKey, kAPIKey);
XCTAssertEqualObjects(request.providerID, FIRGoogleAuthProviderID);
XCTAssertEqualObjects(request.providerIDToken, kGoogleIDToken);
XCTAssertEqualObjects(request.providerAccessToken, kGoogleAccessToken);
XCTAssertTrue(request.returnSecureToken);
dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
id mockVeriyAssertionResponse = OCMClassMock([FIRVerifyAssertionResponse class]);
OCMStub([mockVeriyAssertionResponse federatedID]).andReturn(kGoogleID);
OCMStub([mockVeriyAssertionResponse providerID]).andReturn(FIRGoogleAuthProviderID);
OCMStub([mockVeriyAssertionResponse localID]).andReturn(kLocalID);
OCMStub([mockVeriyAssertionResponse displayName]).andReturn(kGoogleDisplayName);
[self stubTokensWithMockResponse:mockVeriyAssertionResponse];
callback(mockVeriyAssertionResponse, nil);
});
});
[self expectGetAccountInfoGoogle];
XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
[[FIRAuth auth] signOut:NULL];
FIRAuthCredential *googleCredential =
[FIRGoogleAuthProvider credentialWithIDToken:kGoogleIDToken accessToken:kGoogleAccessToken];
[[FIRAuth auth] signInWithCredential:googleCredential completion:^(FIRUser *_Nullable user,
NSError *_Nullable error) {
XCTAssertTrue([NSThread isMainThread]);
[self assertUserGoogle:user];
XCTAssertNil(error);
[expectation fulfill];
}];
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
[self assertUserGoogle:[FIRAuth auth].currentUser];
OCMVerifyAll(_mockBackend);
}
/** @fn testSignInAndRetrieveDataWithCredentialSuccess
@brief Tests the flow of a successful @c signInAndRetrieveDataWithCredential:completion: call
with an Google Sign-In credential.
*/
- (void)testSignInAndRetrieveDataWithCredentialSuccess {
OCMExpect([_mockBackend verifyAssertion:[OCMArg any] callback:[OCMArg any]])
.andCallBlock2(^(FIRVerifyAssertionRequest *_Nullable request,
FIRVerifyAssertionResponseCallback callback) {
XCTAssertEqualObjects(request.APIKey, kAPIKey);
XCTAssertEqualObjects(request.providerID, FIRGoogleAuthProviderID);
XCTAssertEqualObjects(request.providerIDToken, kGoogleIDToken);
XCTAssertEqualObjects(request.providerAccessToken, kGoogleAccessToken);
XCTAssertTrue(request.returnSecureToken);
dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
id mockVeriyAssertionResponse = OCMClassMock([FIRVerifyAssertionResponse class]);
OCMStub([mockVeriyAssertionResponse federatedID]).andReturn(kGoogleID);
OCMStub([mockVeriyAssertionResponse providerID]).andReturn(FIRGoogleAuthProviderID);
OCMStub([mockVeriyAssertionResponse localID]).andReturn(kLocalID);
OCMStub([mockVeriyAssertionResponse displayName]).andReturn(kGoogleDisplayName);
OCMStub([mockVeriyAssertionResponse profile]).andReturn([[self class] googleProfile]);
OCMStub([mockVeriyAssertionResponse username]).andReturn(kDisplayName);
[self stubTokensWithMockResponse:mockVeriyAssertionResponse];
callback(mockVeriyAssertionResponse, nil);
});
});
[self expectGetAccountInfoGoogle];
XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
[[FIRAuth auth] signOut:NULL];
FIRAuthCredential *googleCredential =
[FIRGoogleAuthProvider credentialWithIDToken:kGoogleIDToken accessToken:kGoogleAccessToken];
[[FIRAuth auth] signInAndRetrieveDataWithCredential: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, kDisplayName);
XCTAssertEqualObjects(authResult.additionalUserInfo.providerID, FIRGoogleAuthProviderID);
XCTAssertNil(error);
[expectation fulfill];
}];
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
[self assertUserGoogle:[FIRAuth auth].currentUser];
OCMVerifyAll(_mockBackend);
}
/** @fn testSignInWithGoogleCredentialFailure
@brief Tests the flow of a failed @c signInWithCredential:completion: call with an
Google Sign-In credential.
*/
- (void)testSignInWithGoogleCredentialFailure {
OCMExpect([_mockBackend verifyAssertion:[OCMArg any] callback:[OCMArg any]])
.andDispatchError2([FIRAuthErrorUtils emailAlreadyInUseErrorWithEmail:kGoogleEmail]);
XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
[[FIRAuth auth] signOut:NULL];
FIRAuthCredential *googleCredential =
[FIRGoogleAuthProvider credentialWithIDToken:kGoogleIDToken accessToken:kGoogleAccessToken];
[[FIRAuth auth] signInWithCredential:googleCredential completion:^(FIRUser *_Nullable user,
NSError *_Nullable error) {
XCTAssertTrue([NSThread isMainThread]);
XCTAssertNil(user);
XCTAssertEqual(error.code, FIRAuthErrorCodeEmailAlreadyInUse);
XCTAssertNotNil(error.userInfo[NSLocalizedDescriptionKey]);
[expectation fulfill];
}];
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
XCTAssertNil([FIRAuth auth].currentUser);
OCMVerifyAll(_mockBackend);
}
/** @fn testSignInAnonymouslySuccess
@brief Tests the flow of a successful @c signInAnonymouslyWithCompletion: call.
*/
- (void)testSignInAnonymouslySuccess {
OCMExpect([_mockBackend signUpNewUser:[OCMArg any] callback:[OCMArg any]])
.andCallBlock2(^(FIRSignUpNewUserRequest *_Nullable request,
FIRSignupNewUserCallback callback) {
XCTAssertEqualObjects(request.APIKey, kAPIKey);
XCTAssertNil(request.email);
XCTAssertNil(request.password);
XCTAssertTrue(request.returnSecureToken);
dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
id mockSignUpNewUserResponse = OCMClassMock([FIRSignUpNewUserResponse class]);
[self stubTokensWithMockResponse:mockSignUpNewUserResponse];
callback(mockSignUpNewUserResponse, nil);
});
});
[self expectGetAccountInfoAnonymous];
XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
[[FIRAuth auth] signOut:NULL];
[[FIRAuth auth] signInAnonymouslyWithCompletion:^(FIRUser *_Nullable user,
NSError *_Nullable error) {
XCTAssertTrue([NSThread isMainThread]);
[self assertUserAnonymous:user];
XCTAssertNil(error);
[expectation fulfill];
}];
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
[self assertUserAnonymous:[FIRAuth auth].currentUser];
OCMVerifyAll(_mockBackend);
}
/** @fn testSignInAnonymouslyFailure
@brief Tests the flow of a failed @c signInAnonymouslyWithCompletion: call.
*/
- (void)testSignInAnonymouslyFailure {
OCMExpect([_mockBackend signUpNewUser:[OCMArg any] callback:[OCMArg any]])
.andDispatchError2([FIRAuthErrorUtils operationNotAllowedErrorWithMessage:nil]);
XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
[[FIRAuth auth] signOut:NULL];
[[FIRAuth auth] signInAnonymouslyWithCompletion:^(FIRUser *_Nullable user,
NSError *_Nullable error) {
XCTAssertTrue([NSThread isMainThread]);
XCTAssertNil(user);
XCTAssertEqual(error.code, FIRAuthErrorCodeOperationNotAllowed);
XCTAssertNotNil(error.userInfo[NSLocalizedDescriptionKey]);
[expectation fulfill];
}];
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
XCTAssertNil([FIRAuth auth].currentUser);
OCMVerifyAll(_mockBackend);
}
/** @fn testSignInAnonymouslyAndRetrieveDataSuccess
@brief Tests the flow of a successful @c signInAnonymouslyAndRetrieveDataWithCompletion: call.
*/
- (void)testSignInAnonymouslyAndRetrieveDataSuccess {
OCMExpect([_mockBackend signUpNewUser:[OCMArg any] callback:[OCMArg any]])
.andCallBlock2(^(FIRSignUpNewUserRequest *_Nullable request,
FIRSignupNewUserCallback callback) {
XCTAssertEqualObjects(request.APIKey, kAPIKey);
XCTAssertNil(request.email);
XCTAssertNil(request.password);
XCTAssertTrue(request.returnSecureToken);
dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
id mockSignUpNewUserResponse = OCMClassMock([FIRSignUpNewUserResponse class]);
[self stubTokensWithMockResponse:mockSignUpNewUserResponse];
callback(mockSignUpNewUserResponse, nil);
});
});
[self expectGetAccountInfoAnonymous];
XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
[[FIRAuth auth] signOut:NULL];
[[FIRAuth auth] signInAnonymouslyAndRetrieveDataWithCompletion:
^(FIRAuthDataResult *_Nullable result, NSError *_Nullable error) {
XCTAssertTrue([NSThread isMainThread]);
[self assertUserAnonymous:result.user];
XCTAssertNil(error);
[expectation fulfill];
}];
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
[self assertUserAnonymous:[FIRAuth auth].currentUser];
OCMVerifyAll(_mockBackend);
}
/** @fn testSignInAnonymouslyAndRetrieveDataFailure
@brief Tests the flow of a failed @c signInAnonymouslyAndRetrieveDataWithCompletion: call.
*/
- (void)testSignInAnonymouslyAndRetrieveDataFailure {
OCMExpect([_mockBackend signUpNewUser:[OCMArg any] callback:[OCMArg any]])
.andDispatchError2([FIRAuthErrorUtils operationNotAllowedErrorWithMessage:nil]);
XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
[[FIRAuth auth] signOut:NULL];
[[FIRAuth auth] signInAnonymouslyAndRetrieveDataWithCompletion:
^(FIRAuthDataResult *_Nullable result, NSError *_Nullable error) {
XCTAssertTrue([NSThread isMainThread]);
XCTAssertNil(result);
XCTAssertEqual(error.code, FIRAuthErrorCodeOperationNotAllowed);
XCTAssertNotNil(error.userInfo[NSLocalizedDescriptionKey]);
[expectation fulfill];
}];
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
XCTAssertNil([FIRAuth auth].currentUser);
OCMVerifyAll(_mockBackend);
}
/** @fn testSignInWithCustomTokenSuccess
@brief Tests the flow of a successful @c signInWithCustomToken:completion: call.
*/
- (void)testSignInWithCustomTokenSuccess {
OCMExpect([_mockBackend verifyCustomToken:[OCMArg any] callback:[OCMArg any]])
.andCallBlock2(^(FIRVerifyCustomTokenRequest *_Nullable request,
FIRVerifyCustomTokenResponseCallback callback) {
XCTAssertEqualObjects(request.APIKey, kAPIKey);
XCTAssertEqualObjects(request.token, kCustomToken);
XCTAssertTrue(request.returnSecureToken);
dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
id mockVeriyCustomTokenResponse = OCMClassMock([FIRVerifyCustomTokenResponse class]);
[self stubTokensWithMockResponse:mockVeriyCustomTokenResponse];
callback(mockVeriyCustomTokenResponse, nil);
});
});
[self expectGetAccountInfo];
XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
[[FIRAuth auth] signOut:NULL];
[[FIRAuth auth] signInWithCustomToken:kCustomToken completion:^(FIRUser *_Nullable user,
NSError *_Nullable error) {
XCTAssertTrue([NSThread isMainThread]);
[self assertUser:user];
XCTAssertNil(error);
[expectation fulfill];
}];
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
[self assertUser:[FIRAuth auth].currentUser];
OCMVerifyAll(_mockBackend);
}
/** @fn testSignInWithCustomTokenFailure
@brief Tests the flow of a failed @c signInWithCustomToken:completion: call.
*/
- (void)testSignInWithCustomTokenFailure {
OCMExpect([_mockBackend verifyCustomToken:[OCMArg any] callback:[OCMArg any]])
.andDispatchError2([FIRAuthErrorUtils invalidCustomTokenErrorWithMessage:nil]);
XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
[[FIRAuth auth] signOut:NULL];
[[FIRAuth auth] signInWithCustomToken:kCustomToken completion:^(FIRUser *_Nullable user,
NSError *_Nullable error) {
XCTAssertTrue([NSThread isMainThread]);
XCTAssertNil(user);
XCTAssertEqual(error.code, FIRAuthErrorCodeInvalidCustomToken);
XCTAssertNotNil(error.userInfo[NSLocalizedDescriptionKey]);
[expectation fulfill];
}];
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
XCTAssertNil([FIRAuth auth].currentUser);
OCMVerifyAll(_mockBackend);
}
/** @fn testSignInAndRetrieveDataWithCustomTokenSuccess
@brief Tests the flow of a successful @c signInAndRetrieveDataWithCustomToken:completion: call.
*/
- (void)testSignInAndRetrieveDataWithCustomTokenSuccess {
OCMExpect([_mockBackend verifyCustomToken:[OCMArg any] callback:[OCMArg any]])
.andCallBlock2(^(FIRVerifyCustomTokenRequest *_Nullable request,
FIRVerifyCustomTokenResponseCallback callback) {
XCTAssertEqualObjects(request.APIKey, kAPIKey);
XCTAssertEqualObjects(request.token, kCustomToken);
XCTAssertTrue(request.returnSecureToken);
dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
id mockVeriyCustomTokenResponse = OCMClassMock([FIRVerifyCustomTokenResponse class]);
[self stubTokensWithMockResponse:mockVeriyCustomTokenResponse];
callback(mockVeriyCustomTokenResponse, nil);
});
});
[self expectGetAccountInfo];
XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
[[FIRAuth auth] signOut:NULL];
[[FIRAuth auth] signInAndRetrieveDataWithCustomToken:kCustomToken
completion:^(FIRAuthDataResult *_Nullable result,
NSError *_Nullable error) {
XCTAssertTrue([NSThread isMainThread]);
[self assertUser:result.user];
XCTAssertFalse(result.additionalUserInfo.isNewUser);
XCTAssertNil(error);
[expectation fulfill];
}];
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
[self assertUser:[FIRAuth auth].currentUser];
OCMVerifyAll(_mockBackend);
}
/** @fn testSignInAndRetrieveDataWithCustomTokenFailure
@brief Tests the flow of a failed @c signInAndRetrieveDataWithCustomToken:completion: call.
*/
- (void)testSignInAndRetrieveDataWithCustomTokenFailure {
OCMExpect([_mockBackend verifyCustomToken:[OCMArg any] callback:[OCMArg any]])
.andDispatchError2([FIRAuthErrorUtils invalidCustomTokenErrorWithMessage:nil]);
XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
[[FIRAuth auth] signOut:NULL];
[[FIRAuth auth] signInAndRetrieveDataWithCustomToken:kCustomToken
completion:^(FIRAuthDataResult *_Nullable result,
NSError *_Nullable error) {
XCTAssertTrue([NSThread isMainThread]);
XCTAssertNil(result);
XCTAssertEqual(error.code, FIRAuthErrorCodeInvalidCustomToken);
XCTAssertNotNil(error.userInfo[NSLocalizedDescriptionKey]);
[expectation fulfill];
}];
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
XCTAssertNil([FIRAuth auth].currentUser);
OCMVerifyAll(_mockBackend);
}
/** @fn testCreateUserWithEmailPasswordSuccess
@brief Tests the flow of a successful @c createUserWithEmail:password:completion: call.
*/
- (void)testCreateUserWithEmailPasswordSuccess {
OCMExpect([_mockBackend signUpNewUser:[OCMArg any] callback:[OCMArg any]])
.andCallBlock2(^(FIRSignUpNewUserRequest *_Nullable request,
FIRSignupNewUserCallback callback) {
XCTAssertEqualObjects(request.APIKey, kAPIKey);
XCTAssertEqualObjects(request.email, kEmail);
XCTAssertEqualObjects(request.password, kFakePassword);
XCTAssertTrue(request.returnSecureToken);
dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
id mockSignUpNewUserResponse = OCMClassMock([FIRSignUpNewUserResponse class]);
[self stubTokensWithMockResponse:mockSignUpNewUserResponse];
callback(mockSignUpNewUserResponse, nil);
});
});
[self expectGetAccountInfo];
XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
[[FIRAuth auth] signOut:NULL];
[[FIRAuth auth] createUserWithEmail:kEmail
password:kFakePassword
completion:^(FIRUser *_Nullable user, NSError *_Nullable error) {
XCTAssertTrue([NSThread isMainThread]);
[self assertUser:user];
XCTAssertNil(error);
[expectation fulfill];
}];
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
[self assertUser:[FIRAuth auth].currentUser];
OCMVerifyAll(_mockBackend);
}
/** @fn testCreateUserWithEmailPasswordFailure
@brief Tests the flow of a failed @c createUserWithEmail:password:completion: call.
*/
- (void)testCreateUserWithEmailPasswordFailure {
NSString *reason = @"Password shouldn't be a common word.";
OCMExpect([_mockBackend signUpNewUser:[OCMArg any] callback:[OCMArg any]])
.andDispatchError2([FIRAuthErrorUtils weakPasswordErrorWithServerResponseReason:reason]);
XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
[[FIRAuth auth] signOut:NULL];
[[FIRAuth auth] createUserWithEmail:kEmail
password:kFakePassword
completion:^(FIRUser *_Nullable user, NSError *_Nullable error) {
XCTAssertTrue([NSThread isMainThread]);
XCTAssertNil(user);
XCTAssertEqual(error.code, FIRAuthErrorCodeWeakPassword);
XCTAssertNotNil(error.userInfo[NSLocalizedDescriptionKey]);
XCTAssertEqualObjects(error.userInfo[NSLocalizedFailureReasonErrorKey], reason);
[expectation fulfill];
}];
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
XCTAssertNil([FIRAuth auth].currentUser);
OCMVerifyAll(_mockBackend);
}
/** @fn testCreateUserAndRetrieveDataWithEmailPasswordSuccess
@brief Tests the flow of a successful @c createUserAndRetrieveDataWithEmail:password:completion:
call.
*/
- (void)testCreateUserAndRetrieveDataWithEmailPasswordSuccess {
OCMExpect([_mockBackend signUpNewUser:[OCMArg any] callback:[OCMArg any]])
.andCallBlock2(^(FIRSignUpNewUserRequest *_Nullable request,
FIRSignupNewUserCallback callback) {
XCTAssertEqualObjects(request.APIKey, kAPIKey);
XCTAssertEqualObjects(request.email, kEmail);
XCTAssertEqualObjects(request.password, kFakePassword);
XCTAssertTrue(request.returnSecureToken);
dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
id mockSignUpNewUserResponse = OCMClassMock([FIRSignUpNewUserResponse class]);
[self stubTokensWithMockResponse:mockSignUpNewUserResponse];
callback(mockSignUpNewUserResponse, nil);
});
});
[self expectGetAccountInfo];
XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
[[FIRAuth auth] signOut:NULL];
[[FIRAuth auth] createUserAndRetrieveDataWithEmail:kEmail
password:kFakePassword
completion:^(FIRAuthDataResult *_Nullable result,
NSError *_Nullable error) {
XCTAssertTrue([NSThread isMainThread]);
[self assertUser:result.user];
XCTAssertTrue(result.additionalUserInfo.isNewUser);
XCTAssertNil(error);
[expectation fulfill];
}];
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
[self assertUser:[FIRAuth auth].currentUser];
OCMVerifyAll(_mockBackend);
}
/** @fn testCreateUserAndRetrieveDataWithEmailPasswordFailure
@brief Tests the flow of a failed @c createUserAndRetrieveDataWithEmail:password:completion:
call.
*/
- (void)testCreateUserAndRetrieveDataWithEmailPasswordFailure {
NSString *reason = @"Password shouldn't be a common word.";
OCMExpect([_mockBackend signUpNewUser:[OCMArg any] callback:[OCMArg any]])
.andDispatchError2([FIRAuthErrorUtils weakPasswordErrorWithServerResponseReason:reason]);
XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
[[FIRAuth auth] signOut:NULL];
[[FIRAuth auth] createUserAndRetrieveDataWithEmail:kEmail
password:kFakePassword
completion:^(FIRAuthDataResult *_Nullable result,
NSError *_Nullable error) {
XCTAssertTrue([NSThread isMainThread]);
XCTAssertNil(result);
XCTAssertEqual(error.code, FIRAuthErrorCodeWeakPassword);
XCTAssertNotNil(error.userInfo[NSLocalizedDescriptionKey]);
XCTAssertEqualObjects(error.userInfo[NSLocalizedFailureReasonErrorKey], reason);
[expectation fulfill];
}];
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
XCTAssertNil([FIRAuth auth].currentUser);
OCMVerifyAll(_mockBackend);
}
/** @fn testCreateUserEmptyPasswordFailure
@brief Tests the flow of a failed @c createUserWithEmail:password:completion: call due to an
empty password. This error occurs on the client side, so there is no need to fake an RPC
response.
*/
- (void)testCreateUserEmptyPasswordFailure {
[self expectGetAccountInfo];
XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
[[FIRAuth auth] signOut:NULL];
[[FIRAuth auth] createUserWithEmail:kEmail
password:@""
completion:^(FIRUser *_Nullable user, NSError *_Nullable error) {
XCTAssertTrue([NSThread isMainThread]);
XCTAssertEqual(error.code, FIRAuthErrorCodeWeakPassword);
[expectation fulfill];
}];
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
}
/** @fn testCreateUserEmptyEmailFailure
@brief Tests the flow of a failed @c createUserWithEmail:password:completion: call due to an
empty email adress. This error occurs on the client side, so there is no need to fake an RPC
response.
*/
- (void)testCreateUserEmptyEmailFailure {
[self expectGetAccountInfo];
XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
[[FIRAuth auth] signOut:NULL];
[[FIRAuth auth] createUserWithEmail:@""
password:kFakePassword
completion:^(FIRUser *_Nullable user, NSError *_Nullable error) {
XCTAssertTrue([NSThread isMainThread]);
XCTAssertEqual(error.code, FIRAuthErrorCodeMissingEmail);
[expectation fulfill];
}];
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
}
/** @fn testSendPasswordResetEmailSuccess
@brief Tests the flow of a successful @c sendPasswordResetWithEmail:completion: call.
*/
- (void)testSendPasswordResetEmailSuccess {
OCMExpect([_mockBackend getOOBConfirmationCode:[OCMArg any] callback:[OCMArg any]])
.andCallBlock2(^(FIRGetOOBConfirmationCodeRequest *_Nullable request,
FIRGetOOBConfirmationCodeResponseCallback callback) {
XCTAssertEqualObjects(request.APIKey, kAPIKey);
XCTAssertEqualObjects(request.email, kEmail);
dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
callback([[FIRGetOOBConfirmationCodeResponse alloc] init], nil);
});
});
XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
[[FIRAuth auth] sendPasswordResetWithEmail:kEmail completion:^(NSError *_Nullable error) {
XCTAssertTrue([NSThread isMainThread]);
XCTAssertNil(error);
[expectation fulfill];
}];
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
OCMVerifyAll(_mockBackend);
}
/** @fn testSendPasswordResetEmailFailure
@brief Tests the flow of a failed @c sendPasswordResetWithEmail:completion: call.
*/
- (void)testSendPasswordResetEmailFailure {
OCMExpect([_mockBackend getOOBConfirmationCode:[OCMArg any] callback:[OCMArg any]])
.andDispatchError2([FIRAuthErrorUtils appNotAuthorizedError]);
XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
[[FIRAuth auth] sendPasswordResetWithEmail:kEmail completion:^(NSError *_Nullable error) {
XCTAssertTrue([NSThread isMainThread]);
XCTAssertEqual(error.code, FIRAuthErrorCodeAppNotAuthorized);
XCTAssertNotNil(error.userInfo[NSLocalizedDescriptionKey]);
[expectation fulfill];
}];
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
OCMVerifyAll(_mockBackend);
}
/** @fn testSignOut
@brief Tests the @c signOut: method.
*/
- (void)testSignOut {
[self waitForSignIn];
// Verify signing out succeeds and clears the current user.
NSError *error;
XCTAssertTrue([[FIRAuth auth] signOut:&error]);
XCTAssertNil([FIRAuth auth].currentUser);
}
/** @fn testAuthStateChanges
@brief Tests @c addAuthStateDidChangeListener: and @c removeAuthStateDidChangeListener: methods.
*/
- (void)testAuthStateChanges {
// Set up listener.
__block XCTestExpectation *expectation;
__block BOOL shouldHaveUser;
FIRAuthStateDidChangeListenerBlock listener = ^(FIRAuth *auth, FIRUser *_Nullable user) {
XCTAssertTrue([NSThread isMainThread]);
XCTAssertEqual(auth, [FIRAuth auth]);
XCTAssertEqual(user, [FIRAuth auth].currentUser);
if (shouldHaveUser) {
XCTAssertNotNil(user);
} else {
XCTAssertNil(user);
}
// `expectation` being nil means the listener is not expected to be fired at this moment.
XCTAssertNotNil(expectation);
[expectation fulfill];
};
[[FIRAuth auth] signOut:NULL];
[self waitForTimeIntervel:kWaitInterval]; // Wait until dust settled from previous tests.
// Listener should fire immediately when attached.
expectation = [self expectationWithDescription:@"initial"];
shouldHaveUser = NO;
FIRAuthStateDidChangeListenerHandle handle =
[[FIRAuth auth] addAuthStateDidChangeListener:listener];
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
// Listener should fire for signing in.
expectation = [self expectationWithDescription:@"sign-in"];
shouldHaveUser = YES;
[self waitForSignIn];
// Listener should not fire for signing in again.
shouldHaveUser = YES;
[self waitForSignIn];
[self waitForTimeIntervel:kWaitInterval]; // make sure listener is not called
// Listener should fire for signing out.
expectation = [self expectationWithDescription:@"sign-out"];
shouldHaveUser = NO;
[[FIRAuth auth] signOut:NULL];
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
// Listener should no longer fire once detached.
expectation = nil;
[[FIRAuth auth] removeAuthStateDidChangeListener:handle];
[self waitForSignIn];
[self waitForTimeIntervel:kWaitInterval]; // make sure listener is no longer called
}
/** @fn testIDTokenChanges
@brief Tests @c addIDTokenDidChangeListener: and @c removeIDTokenDidChangeListener: methods.
*/
- (void)testIDTokenChanges {
// Set up listener.
__block XCTestExpectation *expectation;
__block BOOL shouldHaveUser;
FIRIDTokenDidChangeListenerBlock listener = ^(FIRAuth *auth, FIRUser *_Nullable user) {
XCTAssertTrue([NSThread isMainThread]);
XCTAssertEqual(auth, [FIRAuth auth]);
XCTAssertEqual(user, [FIRAuth auth].currentUser);
if (shouldHaveUser) {
XCTAssertNotNil(user);
} else {
XCTAssertNil(user);
}
// `expectation` being nil means the listener is not expected to be fired at this moment.
XCTAssertNotNil(expectation);
[expectation fulfill];
};
[[FIRAuth auth] signOut:NULL];
[self waitForTimeIntervel:kWaitInterval]; // Wait until dust settled from previous tests.
// Listener should fire immediately when attached.
expectation = [self expectationWithDescription:@"initial"];
shouldHaveUser = NO;
FIRIDTokenDidChangeListenerHandle handle =
[[FIRAuth auth] addIDTokenDidChangeListener:listener];
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
// Listener should fire for signing in.
expectation = [self expectationWithDescription:@"sign-in"];
shouldHaveUser = YES;
[self waitForSignIn];
// Listener should fire for signing in again as the same user with another access token.
expectation = [self expectationWithDescription:@"sign-in again"];
shouldHaveUser = YES;
[self waitForSignInWithAccessToken:kNewAccessToken];
// Listener should fire for signing out.
expectation = [self expectationWithDescription:@"sign-out"];
shouldHaveUser = NO;
[[FIRAuth auth] signOut:NULL];
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
// Listener should no longer fire once detached.
expectation = nil;
[[FIRAuth auth] removeIDTokenDidChangeListener:handle];
[self waitForSignIn];
[self waitForTimeIntervel:kWaitInterval]; // make sure listener is no longer called
}
#pragma mark - Automatic Token Refresh Tests.
/** @fn testAutomaticTokenRefresh
@brief Tests a successful flow to automatically refresh tokens for a signed in user.
*/
- (void)testAutomaticTokenRefresh {
[[FIRAuth auth] signOut:NULL];
// Enable auto refresh
[self enableAutoTokenRefresh];
// Sign in a user.
[self waitForSignIn];
// Set up expectation for secureToken RPC made by token refresh task.
[self mockSecureTokenResponseWithError:nil];
// Verify that the current user's access token is the "old" access token before automatic token
// refresh.
XCTAssertEqualObjects(kAccessToken, [FIRAuth auth].currentUser.rawAccessToken);
// Execute saved token refresh task.
XCTestExpectation *dispatchAfterExpectation =
[self expectationWithDescription:@"dispatchAfterExpectation"];
dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
XCTAssertNotNil(_FIRAuthDispatcherCallback);
_FIRAuthDispatcherCallback();
[dispatchAfterExpectation fulfill];
});
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
// Verify that current user's access token is the "new" access token provided in the mock secure
// token response during automatic token refresh.
XCTAssertEqualObjects(kNewAccessToken, [FIRAuth auth].currentUser.rawAccessToken);
OCMVerifyAll(_mockBackend);
}
/** @fn testAutomaticTokenRefreshInvalidTokenFailure
@brief Tests an unsuccessful flow to auto refresh tokens with an "invalid token" error.
This error should cause the user to be signed out.
*/
- (void)testAutomaticTokenRefreshInvalidTokenFailure {
[[FIRAuth auth] signOut:NULL];
// Enable auto refresh
[self enableAutoTokenRefresh];
// Sign in a user.
[self waitForSignIn];
// Set up expectation for secureToken RPC made by a failed attempt to refresh tokens.
[self mockSecureTokenResponseWithError:[FIRAuthErrorUtils invalidUserTokenErrorWithMessage:nil]];
// Verify that current user is still valid.
XCTAssertEqualObjects(kAccessToken, [FIRAuth auth].currentUser.rawAccessToken);
// Execute saved token refresh task.
XCTestExpectation *dispatchAfterExpectation =
[self expectationWithDescription:@"dispatchAfterExpectation"];
dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
XCTAssertNotNil(_FIRAuthDispatcherCallback);
_FIRAuthDispatcherCallback();
[dispatchAfterExpectation fulfill];
});
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
// Verify that the user is nil after failed attempt to refresh tokens caused signed out.
XCTAssertNil([FIRAuth auth].currentUser);
OCMVerifyAll(_mockBackend);
}
/** @fn testAutomaticTokenRefreshRetry
@brief Tests that a retry is attempted for a automatic token refresh task (which is not due to
invalid tokens). The initial attempt to refresh the access token fails, but the second
attempt is successful.
*/
- (void)testAutomaticTokenRefreshRetry {
[[FIRAuth auth] signOut:NULL];
// Enable auto refresh
[self enableAutoTokenRefresh];
// Sign in a user.
[self waitForSignIn];
// Set up expectation for secureToken RPC made by a failed attempt to refresh tokens.
[self mockSecureTokenResponseWithError:[NSError errorWithDomain:@"ERROR" code:-1 userInfo:nil]];
// Execute saved token refresh task.
XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
XCTAssertNotNil(_FIRAuthDispatcherCallback);
_FIRAuthDispatcherCallback();
_FIRAuthDispatcherCallback = nil;
[expectation fulfill];
});
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
// The old access token should still be the current user's access token and not the new access
// token (kNewAccessToken).
XCTAssertEqualObjects(kAccessToken, [FIRAuth auth].currentUser.rawAccessToken);
// Set up expectation for secureToken RPC made by a successful attempt to refresh tokens.
[self mockSecureTokenResponseWithError:nil];
// Execute saved token refresh task.
XCTestExpectation *dispatchAfterExpectation =
[self expectationWithDescription:@"dispatchAfterExpectation"];
dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
XCTAssertNotNil(_FIRAuthDispatcherCallback);
_FIRAuthDispatcherCallback();
[dispatchAfterExpectation fulfill];
});
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
// Verify that current user's access token is the "new" access token provided in the mock secure
// token response during automatic token refresh.
XCTAssertEqualObjects([FIRAuth auth].currentUser.rawAccessToken, kNewAccessToken);
OCMVerifyAll(_mockBackend);
}
#if TARGET_OS_IOS
/** @fn testAutomaticTokenRefreshInvalidTokenFailure
@brief Tests that app foreground notification triggers the scheduling of an automatic token
refresh task.
*/
- (void)testAutoRefreshAppForegroundedNotification {
[[FIRAuth auth] signOut:NULL];
// Enable auto refresh
[self enableAutoTokenRefresh];
// Sign in a user.
[self waitForSignIn];
// Post "UIApplicationDidBecomeActiveNotification" to trigger scheduling token refresh task.
[[NSNotificationCenter defaultCenter]
postNotificationName:UIApplicationDidBecomeActiveNotification object:nil];
// Verify that current user is still valid with old access token.
XCTAssertEqualObjects(kAccessToken, [FIRAuth auth].currentUser.rawAccessToken);
// Set up expectation for secureToken RPC made by a successful attempt to refresh tokens.
[self mockSecureTokenResponseWithError:nil];
// Execute saved token refresh task.
XCTestExpectation *dispatchAfterExpectation =
[self expectationWithDescription:@"dispatchAfterExpectation"];
dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
XCTAssertNotNil(_FIRAuthDispatcherCallback);
_FIRAuthDispatcherCallback();
[dispatchAfterExpectation fulfill];
});
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
// Verify that current user is still valid with new access token.
XCTAssertEqualObjects(kNewAccessToken, [FIRAuth auth].currentUser.rawAccessToken);
OCMVerifyAll(_mockBackend);
}
#endif
#pragma mark - Helpers
/** @fn mockSecureTokenResponseWithError:
@brief Set up expectation for secureToken RPC.
@param error The error that the mock should return if any.
*/
- (void)mockSecureTokenResponseWithError:(nullable NSError *)error {
// Set up expectation for secureToken RPC made by a successful attempt to refresh tokens.
XCTestExpectation *secureTokenResponseExpectation =
[self expectationWithDescription:@"secureTokenResponseExpectation"];
OCMExpect([_mockBackend secureToken:[OCMArg any] callback:[OCMArg any]])
.andCallBlock2(^(FIRSecureTokenRequest *_Nullable request,
FIRSecureTokenResponseCallback callback) {
XCTAssertEqualObjects(request.APIKey, kAPIKey);
XCTAssertEqualObjects(request.refreshToken, kRefreshToken);
dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
if (error) {
callback(nil, error);
[secureTokenResponseExpectation fulfill];
return;
}
id mockSecureTokenResponse = OCMClassMock([FIRSecureTokenResponse class]);
OCMStub([mockSecureTokenResponse accessToken]).andReturn(kNewAccessToken);
NSDate *futureDate =
[[NSDate date] dateByAddingTimeInterval:kTestTokenExpirationTimeInterval];
OCMStub([mockSecureTokenResponse approximateExpirationDate]).andReturn(futureDate);
callback(mockSecureTokenResponse, nil);
[secureTokenResponseExpectation fulfill];
});
});
}
/** @fn enableAutoTokenRefresh
@brief Enables automatic token refresh by invoking FIRAuth's implementation of FIRApp's
|getTokenWithImplementation|.
*/
- (void)enableAutoTokenRefresh {
XCTestExpectation *expectation = [self expectationWithDescription:@"autoTokenRefreshcallback"];
[[FIRAuth auth].app getTokenForcingRefresh:NO withCallback:^(NSString *_Nullable token,
NSError *_Nullable error) {
[expectation fulfill];
}];
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
}
/** @fn app1
@brief Creates a Firebase app.
@return A @c FIRApp with some name.
*/
- (FIRApp *)app1 {
return [FIRApp appForAuthUnitTestsWithName:kFirebaseAppName1];
}
/** @fn app2
@brief Creates another Firebase app.
@return A @c FIRApp with some other name.
*/
- (FIRApp *)app2 {
return [FIRApp appForAuthUnitTestsWithName:kFirebaseAppName2];
}
/** @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 expectGetAccountInfo
@brief Expects a GetAccountInfo request on the mock backend and calls back with fake account
data.
*/
- (void)expectGetAccountInfo {
[self expectGetAccountInfoWithAccessToken:kAccessToken];
}
/** @fn expectGetAccountInfoWithAccessToken
@param accessToken The access token for the user to check against.
@brief Expects a GetAccountInfo request on the mock backend and calls back with fake account
data.
*/
- (void)expectGetAccountInfoWithAccessToken:(NSString *)accessToken {
OCMExpect([_mockBackend getAccountInfo:[OCMArg any] callback:[OCMArg any]])
.andCallBlock2(^(FIRGetAccountInfoRequest *_Nullable request,
FIRGetAccountInfoResponseCallback callback) {
XCTAssertEqualObjects(request.APIKey, kAPIKey);
XCTAssertEqualObjects(request.accessToken, accessToken);
dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
id mockGetAccountInfoResponseUser = OCMClassMock([FIRGetAccountInfoResponseUser class]);
OCMStub([mockGetAccountInfoResponseUser localID]).andReturn(kLocalID);
OCMStub([mockGetAccountInfoResponseUser displayName]).andReturn(kDisplayName);
OCMStub([mockGetAccountInfoResponseUser email]).andReturn(kEmail);
OCMStub([mockGetAccountInfoResponseUser passwordHash]).andReturn(kPasswordHash);
id mockGetAccountInfoResponse = OCMClassMock([FIRGetAccountInfoResponse class]);
OCMStub([mockGetAccountInfoResponse users]).andReturn(@[ mockGetAccountInfoResponseUser ]);
callback(mockGetAccountInfoResponse, nil);
});
});
}
/** @fn assertUser
@brief Asserts the given FIRUser matching the fake data returned by @c expectGetAccountInfo.
@param user The user object to be verified.
*/
- (void)assertUser:(FIRUser *)user {
XCTAssertNotNil(user);
XCTAssertEqualObjects(user.uid, kLocalID);
XCTAssertEqualObjects(user.displayName, kDisplayName);
XCTAssertEqualObjects(user.email, kEmail);
XCTAssertFalse(user.anonymous);
XCTAssertEqual(user.providerData.count, 0u);
}
/** @fn expectGetAccountInfoGoogle
@brief Expects a GetAccountInfo request on the mock backend and calls back with fake account
data for a Google Sign-In user.
*/
- (void)expectGetAccountInfoGoogle {
OCMExpect([_mockBackend getAccountInfo:[OCMArg any] callback:[OCMArg any]])
.andCallBlock2(^(FIRGetAccountInfoRequest *_Nullable request,
FIRGetAccountInfoResponseCallback callback) {
XCTAssertEqualObjects(request.APIKey, kAPIKey);
XCTAssertEqualObjects(request.accessToken, kAccessToken);
dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
id mockGoogleUserInfo = OCMClassMock([FIRGetAccountInfoResponseProviderUserInfo class]);
OCMStub([mockGoogleUserInfo providerID]).andReturn(FIRGoogleAuthProviderID);
OCMStub([mockGoogleUserInfo displayName]).andReturn(kGoogleDisplayName);
OCMStub([mockGoogleUserInfo federatedID]).andReturn(kGoogleID);
OCMStub([mockGoogleUserInfo email]).andReturn(kGoogleEmail);
id mockGetAccountInfoResponseUser = OCMClassMock([FIRGetAccountInfoResponseUser class]);
OCMStub([mockGetAccountInfoResponseUser localID]).andReturn(kLocalID);
OCMStub([mockGetAccountInfoResponseUser displayName]).andReturn(kDisplayName);
OCMStub([mockGetAccountInfoResponseUser providerUserInfo])
.andReturn((@[ mockGoogleUserInfo ]));
id mockGetAccountInfoResponse = OCMClassMock([FIRGetAccountInfoResponse class]);
OCMStub([mockGetAccountInfoResponse users]).andReturn(@[ mockGetAccountInfoResponseUser ]);
callback(mockGetAccountInfoResponse, nil);
});
});
}
/** @fn assertUserGoogle
@brief Asserts the given FIRUser matching the fake data returned by
@c expectGetAccountInfoGoogle.
@param user The user object to be verified.
*/
- (void)assertUserGoogle:(FIRUser *)user {
XCTAssertNotNil(user);
XCTAssertEqualObjects(user.uid, kLocalID);
XCTAssertEqualObjects(user.displayName, kDisplayName);
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 expectGetAccountInfoAnonymous
@brief Expects a GetAccountInfo request on the mock backend and calls back with fake anonymous
account data.
*/
- (void)expectGetAccountInfoAnonymous {
OCMExpect([_mockBackend getAccountInfo:[OCMArg any] callback:[OCMArg any]])
.andCallBlock2(^(FIRGetAccountInfoRequest *_Nullable request,
FIRGetAccountInfoResponseCallback callback) {
XCTAssertEqualObjects(request.APIKey, kAPIKey);
XCTAssertEqualObjects(request.accessToken, kAccessToken);
dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
id mockGetAccountInfoResponseUser = OCMClassMock([FIRGetAccountInfoResponseUser class]);
OCMStub([mockGetAccountInfoResponseUser localID]).andReturn(kLocalID);
id mockGetAccountInfoResponse = OCMClassMock([FIRGetAccountInfoResponse class]);
OCMStub([mockGetAccountInfoResponse users]).andReturn(@[ mockGetAccountInfoResponseUser ]);
callback(mockGetAccountInfoResponse, nil);
});
});
}
/** @fn assertUserAnonymous
@brief Asserts the given FIRUser matching the fake data returned by
@c expectGetAccountInfoAnonymous.
@param user The user object to be verified.
*/
- (void)assertUserAnonymous:(FIRUser *)user {
XCTAssertNotNil(user);
XCTAssertEqualObjects(user.uid, kLocalID);
XCTAssertNil(user.displayName);
XCTAssertTrue(user.anonymous);
XCTAssertEqual(user.providerData.count, 0u);
}
/** @fn waitForSignIn
@brief Signs in a user to prepare for tests.
@remarks This method also waits for all other pending @c XCTestExpectation instances.
*/
- (void)waitForSignIn {
[self waitForSignInWithAccessToken:kAccessToken];
}
/** @fn waitForSignInWithAccessToken:
@brief Signs in a user to prepare for tests.
@param accessToken The access token for the user to have.
@remarks This method also waits for all other pending @c XCTestExpectation instances.
*/
- (void)waitForSignInWithAccessToken:(NSString *)accessToken {
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(accessToken);
OCMStub([mockVeriyPasswordResponse approximateExpirationDate])
.andReturn([NSDate dateWithTimeIntervalSinceNow:kAccessTokenTimeToLive]);
OCMStub([mockVeriyPasswordResponse refreshToken]).andReturn(kRefreshToken);
callback(mockVeriyPasswordResponse, nil);
});
});
[self expectGetAccountInfoWithAccessToken:accessToken];
XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
[[FIRAuth auth] signInWithEmail:kEmail password:kFakePassword completion:^(FIRUser *_Nullable user,
NSError *_Nullable error) {
[expectation fulfill];
}];
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
OCMVerifyAll(_mockBackend);
XCTAssertNotNil([FIRAuth auth].currentUser);
}
/** @fn waitForTimeInterval:
@brief Wait for a particular time interval.
@remarks This method also waits for all other pending @c XCTestExpectation instances.
*/
- (void)waitForTimeIntervel:(NSTimeInterval)timeInterval {
static dispatch_queue_t queue;
static dispatch_once_t onceToken;
XCTestExpectation *expectation = [self expectationWithDescription:@"waitForTimeIntervel:"];
dispatch_once(&onceToken, ^{
queue = dispatch_queue_create("com.google.FIRAuthUnitTests.waitForTimeIntervel", NULL);
});
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, timeInterval * NSEC_PER_SEC), queue, ^() {
[expectation fulfill];
});
[self waitForExpectationsWithTimeout:timeInterval + kExpectationTimeout handler:nil];
}
@end