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

308 lines
12 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 <XCTest/XCTest.h>
#import "FIRAuthAppCredential.h"
#import "FIRAuthAppCredentialManager.h"
#import "FIRAuthKeychainServices.h"
#import <OCMock/OCMock.h>
#define ANY_ERROR_POINTER ((NSError *__autoreleasing *_Nullable)[OCMArg anyPointer])
#define SAVE_TO(var) [OCMArg checkWithBlock:^BOOL(id arg) { var = arg; return YES; }]
/** @var kReceipt
@brief A fake receipt used for testing.
*/
static NSString *const kReceipt = @"FAKE_RECEIPT";
/** @var kAnotherReceipt
@brief Another fake receipt used for testing.
*/
static NSString *const kAnotherReceipt = @"OTHER_RECEIPT";
/** @var kSecret
@brief A fake secret used for testing.
*/
static NSString *const kSecret = @"FAKE_SECRET";
/** @var kAnotherSecret
@brief Another fake secret used for testing.
*/
static NSString *const kAnotherSecret = @"OTHER_SECRET";
/** @var kVerificationTimeout
@brief The verification timeout used for testing.
*/
static const NSTimeInterval kVerificationTimeout = 1;
/** @var kExpectationTimeout
@brief The test expectation timeout.
@remarks This must be considerably greater than @c kVerificationTimeout .
*/
static const NSTimeInterval kExpectationTimeout = 2;
NS_ASSUME_NONNULL_BEGIN
/** @class FIRAuthAppCredentialManagerTests
@brief Unit tests for @c FIRAuthAppCredentialManager .
*/
@interface FIRAuthAppCredentialManagerTests : XCTestCase
@end
@implementation FIRAuthAppCredentialManagerTests {
/** @var _mockKeychain
@brief The mock keychain for testing.
*/
id _mockKeychain;
}
- (void)setUp {
_mockKeychain = OCMClassMock([FIRAuthKeychainServices class]);
}
/** @fn testCompletion
@brief Tests a successfully completed verification flow.
*/
- (void)testCompletion {
// Initial empty state.
OCMExpect([_mockKeychain dataForKey:OCMOCK_ANY error:ANY_ERROR_POINTER]).andReturn(nil);
FIRAuthAppCredentialManager *manager =
[[FIRAuthAppCredentialManager alloc] initWithKeychain:_mockKeychain];
XCTAssertNil(manager.credential);
OCMVerifyAll(_mockKeychain);
// Start verification.
XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
OCMExpect([_mockKeychain setData:OCMOCK_ANY forKey:OCMOCK_ANY error:ANY_ERROR_POINTER])
.andReturn(YES);
[manager didStartVerificationWithReceipt:kReceipt
timeout:kVerificationTimeout
callback:^(FIRAuthAppCredential *credential) {
XCTAssertEqualObjects(credential.receipt, kReceipt);
XCTAssertEqualObjects(credential.secret, kSecret);
[expectation fulfill];
}];
XCTAssertNil(manager.credential);
OCMVerifyAll(_mockKeychain);
// Mismatched receipt shouldn't finish verification.
XCTAssertFalse([manager canFinishVerificationWithReceipt:kAnotherReceipt secret:kAnotherSecret]);
XCTAssertNil(manager.credential);
// Finish verification.
OCMExpect([_mockKeychain setData:OCMOCK_ANY forKey:OCMOCK_ANY error:ANY_ERROR_POINTER])
.andReturn(YES);
XCTAssertTrue([manager canFinishVerificationWithReceipt:kReceipt secret:kSecret]);
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
XCTAssertNotNil(manager.credential);
XCTAssertEqualObjects(manager.credential.receipt, kReceipt);
XCTAssertEqualObjects(manager.credential.secret, kSecret);
OCMVerifyAll(_mockKeychain);
// Repeated receipt should have no effect.
XCTAssertFalse([manager canFinishVerificationWithReceipt:kReceipt secret:kAnotherSecret]);
XCTAssertEqualObjects(manager.credential.secret, kSecret);
}
/** @fn testTimeout
@brief Tests a verification flow that times out.
*/
- (void)testTimeout {
// Initial empty state.
OCMExpect([_mockKeychain dataForKey:OCMOCK_ANY error:ANY_ERROR_POINTER]).andReturn(nil);
FIRAuthAppCredentialManager *manager =
[[FIRAuthAppCredentialManager alloc] initWithKeychain:_mockKeychain];
XCTAssertNil(manager.credential);
OCMVerifyAll(_mockKeychain);
// Start verification.
XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
OCMExpect([_mockKeychain setData:OCMOCK_ANY forKey:OCMOCK_ANY error:ANY_ERROR_POINTER])
.andReturn(YES);
[manager didStartVerificationWithReceipt:kReceipt
timeout:kVerificationTimeout
callback:^(FIRAuthAppCredential *credential) {
XCTAssertEqualObjects(credential.receipt, kReceipt);
XCTAssertNil(credential.secret);
[expectation fulfill];
}];
XCTAssertNil(manager.credential);
OCMVerifyAll(_mockKeychain);
// Time-out.
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
XCTAssertNil(manager.credential);
// Completion after timeout.
OCMExpect([_mockKeychain setData:OCMOCK_ANY forKey:OCMOCK_ANY error:ANY_ERROR_POINTER])
.andReturn(YES);
XCTAssertTrue([manager canFinishVerificationWithReceipt:kReceipt secret:kSecret]);
XCTAssertNotNil(manager.credential);
XCTAssertEqualObjects(manager.credential.receipt, kReceipt);
XCTAssertEqualObjects(manager.credential.secret, kSecret);
OCMVerifyAll(_mockKeychain);
}
/** @fn testMaximumPendingReceipt
@brief Tests the maximum allowed number of pending receipt.
*/
- (void)testMaximumPendingReceipt {
// Initial empty state.
OCMExpect([_mockKeychain dataForKey:OCMOCK_ANY error:ANY_ERROR_POINTER]).andReturn(nil);
FIRAuthAppCredentialManager *manager =
[[FIRAuthAppCredentialManager alloc] initWithKeychain:_mockKeychain];
XCTAssertNil(manager.credential);
OCMVerifyAll(_mockKeychain);
// Start verification of the target receipt.
XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
OCMExpect([_mockKeychain setData:OCMOCK_ANY forKey:OCMOCK_ANY error:ANY_ERROR_POINTER])
.andReturn(YES);
[manager didStartVerificationWithReceipt:kReceipt
timeout:kVerificationTimeout
callback:^(FIRAuthAppCredential *credential) {
XCTAssertEqualObjects(credential.receipt, kReceipt);
XCTAssertEqualObjects(credential.secret, kSecret);
[expectation fulfill];
}];
XCTAssertNil(manager.credential);
OCMVerifyAll(_mockKeychain);
// Start verification of a number of random receipts without overflowing.
for (NSUInteger i = 1; i < manager.maximumNumberOfPendingReceipts; i++) {
OCMExpect([_mockKeychain setData:OCMOCK_ANY forKey:OCMOCK_ANY error:ANY_ERROR_POINTER])
.andReturn(YES);
NSString *randomReceipt = [NSString stringWithFormat:@"RANDOM_%lu", (unsigned long)i];
XCTestExpectation *randomExpectation = [self expectationWithDescription:randomReceipt];
[manager didStartVerificationWithReceipt:randomReceipt
timeout:kVerificationTimeout
callback:^(FIRAuthAppCredential *credential) {
// They all should get full credential because one is available at this point.
XCTAssertEqualObjects(credential.receipt, kReceipt);
XCTAssertEqualObjects(credential.secret, kSecret);
[randomExpectation fulfill];
}];
}
// Finish verification of target receipt.
OCMExpect([_mockKeychain setData:OCMOCK_ANY forKey:OCMOCK_ANY error:ANY_ERROR_POINTER])
.andReturn(YES);
XCTAssertTrue([manager canFinishVerificationWithReceipt:kReceipt secret:kSecret]);
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
XCTAssertNotNil(manager.credential);
XCTAssertEqualObjects(manager.credential.receipt, kReceipt);
XCTAssertEqualObjects(manager.credential.secret, kSecret);
OCMVerifyAll(_mockKeychain);
// Clear credential to prepare for next round.
[manager clearCredential];
XCTAssertNil(manager.credential);
// Start verification of another target receipt.
expectation = [self expectationWithDescription:@"another callback"];
OCMExpect([_mockKeychain setData:OCMOCK_ANY forKey:OCMOCK_ANY error:ANY_ERROR_POINTER])
.andReturn(YES);
[manager didStartVerificationWithReceipt:kAnotherReceipt
timeout:kVerificationTimeout
callback:^(FIRAuthAppCredential *credential) {
XCTAssertEqualObjects(credential.receipt, kAnotherReceipt);
XCTAssertNil(credential.secret);
[expectation fulfill];
}];
XCTAssertNil(manager.credential);
OCMVerifyAll(_mockKeychain);
// Start verification of a number of random receipts to overflow.
for (NSUInteger i = 0; i < manager.maximumNumberOfPendingReceipts; i++) {
OCMExpect([_mockKeychain setData:OCMOCK_ANY forKey:OCMOCK_ANY error:ANY_ERROR_POINTER])
.andReturn(YES);
NSString *randomReceipt = [NSString stringWithFormat:@"RANDOM_%lu", (unsigned long)i];
XCTestExpectation *randomExpectation = [self expectationWithDescription:randomReceipt];
[manager didStartVerificationWithReceipt:randomReceipt
timeout:kVerificationTimeout
callback:^(FIRAuthAppCredential *credential) {
// They all should get partial credential because verification has never completed.
XCTAssertEqualObjects(credential.receipt, randomReceipt);
XCTAssertNil(credential.secret);
[randomExpectation fulfill];
}];
}
// Finish verification of the other target receipt.
XCTAssertFalse([manager canFinishVerificationWithReceipt:kAnotherReceipt secret:kAnotherSecret]);
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
XCTAssertNil(manager.credential);
}
/** @fn testKeychain
@brief Tests state preservation in the keychain.
*/
- (void)testKeychain {
// Initial empty state.
OCMExpect([_mockKeychain dataForKey:OCMOCK_ANY error:ANY_ERROR_POINTER]).andReturn(nil);
FIRAuthAppCredentialManager *manager =
[[FIRAuthAppCredentialManager alloc] initWithKeychain:_mockKeychain];
XCTAssertNil(manager.credential);
OCMVerifyAll(_mockKeychain);
// Start verification.
XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
__block NSString *key;
__block NSString *data;
OCMExpect([_mockKeychain setData:SAVE_TO(data) forKey:SAVE_TO(key) error:ANY_ERROR_POINTER])
.andReturn(YES);
[manager didStartVerificationWithReceipt:kReceipt
timeout:kVerificationTimeout
callback:^(FIRAuthAppCredential *credential) {
XCTAssertEqualObjects(credential.receipt, kReceipt);
XCTAssertNil(credential.secret);
[expectation fulfill];
}];
XCTAssertNil(manager.credential);
OCMVerifyAll(_mockKeychain);
// Time-out.
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
XCTAssertNil(manager.credential);
// Start a new manager with saved data in keychain.
OCMExpect([_mockKeychain dataForKey:key error:ANY_ERROR_POINTER]).andReturn(data);
manager = [[FIRAuthAppCredentialManager alloc] initWithKeychain:_mockKeychain];
XCTAssertNil(manager.credential);
OCMVerifyAll(_mockKeychain);
// Finish verification.
OCMExpect([_mockKeychain setData:SAVE_TO(data) forKey:SAVE_TO(key) error:ANY_ERROR_POINTER])
.andReturn(YES);
XCTAssertTrue([manager canFinishVerificationWithReceipt:kReceipt secret:kSecret]);
XCTAssertNotNil(manager.credential);
XCTAssertEqualObjects(manager.credential.receipt, kReceipt);
XCTAssertEqualObjects(manager.credential.secret, kSecret);
OCMVerifyAll(_mockKeychain);
// Start yet another new manager with saved data in keychain.
OCMExpect([_mockKeychain dataForKey:key error:ANY_ERROR_POINTER]).andReturn(data);
manager = [[FIRAuthAppCredentialManager alloc] initWithKeychain:_mockKeychain];
XCTAssertNotNil(manager.credential);
XCTAssertEqualObjects(manager.credential.receipt, kReceipt);
XCTAssertEqualObjects(manager.credential.secret, kSecret);
OCMVerifyAll(_mockKeychain);
}
@end
NS_ASSUME_NONNULL_END