|
|
@@ -31,13 +31,13 @@
|
|
|
#import "GoogleSignIn/Sources/GIDSignIn_Private.h"
|
|
|
#import "GoogleSignIn/Sources/GIDSignInPreferences.h"
|
|
|
#import "GoogleSignIn/Sources/GIDKeychainHandler/Implementations/Fakes/GIDFakeKeychainHandler.h"
|
|
|
+#import "GoogleSignIn/Sources/GIDHTTPFetcher/Implementations/Fakes/GIDFakeHTTPFetcher.h"
|
|
|
+#import "GoogleSignIn/Sources/GIDHTTPFetcher/Implementations/GIDHTTPFetcher.h"
|
|
|
|
|
|
#if TARGET_OS_IOS && !TARGET_OS_MACCATALYST
|
|
|
#import "GoogleSignIn/Sources/GIDEMMErrorHandler.h"
|
|
|
#endif // TARGET_OS_IOS && !TARGET_OS_MACCATALYST
|
|
|
|
|
|
-#import "GoogleSignIn/Tests/Unit/GIDFakeFetcher.h"
|
|
|
-#import "GoogleSignIn/Tests/Unit/GIDFakeFetcherService.h"
|
|
|
#import "GoogleSignIn/Tests/Unit/GIDFakeMainBundle.h"
|
|
|
#import "GoogleSignIn/Tests/Unit/OIDAuthorizationResponse+Testing.h"
|
|
|
#import "GoogleSignIn/Tests/Unit/OIDTokenResponse+Testing.h"
|
|
|
@@ -194,8 +194,12 @@ static NSString *const kNewScope = @"newScope";
|
|
|
// Mock |GTMAppAuthFetcherAuthorization|.
|
|
|
id _authorization;
|
|
|
|
|
|
+ // Fake for |GIDKeychainHandler|.
|
|
|
GIDFakeKeychainHandler *_keychainHandler;
|
|
|
|
|
|
+ // Fake for |GIDHTTPFetcher|.
|
|
|
+ GIDFakeHTTPFetcher *_httpFetcher;
|
|
|
+
|
|
|
#if TARGET_OS_IOS || TARGET_OS_MACCATALYST
|
|
|
// Mock |UIViewController|.
|
|
|
id _presentingViewController;
|
|
|
@@ -216,9 +220,6 @@ static NSString *const kNewScope = @"newScope";
|
|
|
// Whether callback block has been called.
|
|
|
BOOL _completionCalled;
|
|
|
|
|
|
- // Fake fetcher service to emulate network requests.
|
|
|
- GIDFakeFetcherService *_fetcherService;
|
|
|
-
|
|
|
// Fake [NSBundle mainBundle];
|
|
|
GIDFakeMainBundle *_fakeMainBundle;
|
|
|
|
|
|
@@ -298,7 +299,6 @@ static NSString *const kNewScope = @"newScope";
|
|
|
callback:COPY_TO_ARG_BLOCK(self->_savedTokenCallback)]);
|
|
|
|
|
|
// Fakes
|
|
|
- _fetcherService = [[GIDFakeFetcherService alloc] init];
|
|
|
_fakeMainBundle = [[GIDFakeMainBundle alloc] init];
|
|
|
[_fakeMainBundle startFakingWithClientID:kClientId];
|
|
|
[_fakeMainBundle fakeAllSchemesSupported];
|
|
|
@@ -308,7 +308,11 @@ static NSString *const kNewScope = @"newScope";
|
|
|
forKey:kAppHasRunBeforeKey];
|
|
|
|
|
|
_keychainHandler = [[GIDFakeKeychainHandler alloc] init];
|
|
|
- _signIn = [[GIDSignIn alloc] initWithKeychainHandler:_keychainHandler];
|
|
|
+
|
|
|
+ _httpFetcher = [[GIDFakeHTTPFetcher alloc] init];
|
|
|
+
|
|
|
+ _signIn = [[GIDSignIn alloc] initWithKeychainHandler:_keychainHandler
|
|
|
+ httpFetcher:_httpFetcher];
|
|
|
_hint = nil;
|
|
|
|
|
|
__weak GIDSignInTest *weakSelf = self;
|
|
|
@@ -739,135 +743,166 @@ static NSString *const kNewScope = @"newScope";
|
|
|
#pragma mark - Tests - disconnectWithCallback:
|
|
|
|
|
|
// Verifies disconnect calls callback with no errors if access token is present.
|
|
|
-- (void)testDisconnect_accessToken {
|
|
|
+- (void)testDisconnect_accessTokenIsPresent {
|
|
|
[_keychainHandler saveAuthState:_authState];
|
|
|
- [[[_authState expect] andReturn:_tokenResponse] lastTokenResponse];
|
|
|
- [[[_tokenResponse expect] andReturn:kAccessToken] accessToken];
|
|
|
- [[[_authorization expect] andReturn:_fetcherService] fetcherService];
|
|
|
- XCTestExpectation *expectation =
|
|
|
+ OCMStub([_authState lastTokenResponse]).andReturn(_tokenResponse);
|
|
|
+ OCMStub([_tokenResponse accessToken]).andReturn(kAccessToken);
|
|
|
+
|
|
|
+ XCTestExpectation *fetcherExpectation =
|
|
|
+ [self expectationWithDescription:@"testBlock is invoked."];
|
|
|
+ GIDHTTPFetcherTestBlock testBlock =
|
|
|
+ ^(NSURLRequest *request, GIDHTTPFetcherFakeResponseProviderBlock responseProvider) {
|
|
|
+ [self verifyRevokeRequest:request withToken:kAccessToken];
|
|
|
+ NSData *data = [[NSData alloc] init];
|
|
|
+ responseProvider(data, nil);
|
|
|
+ [fetcherExpectation fulfill];
|
|
|
+ };
|
|
|
+ [_httpFetcher setTestBlock:testBlock];
|
|
|
+
|
|
|
+ XCTestExpectation *completionExpectation =
|
|
|
[self expectationWithDescription:@"Callback called with nil error"];
|
|
|
[_signIn disconnectWithCompletion:^(NSError * _Nullable error) {
|
|
|
- if (error == nil) {
|
|
|
- [expectation fulfill];
|
|
|
- }
|
|
|
+ XCTAssertNil(error);
|
|
|
+ [completionExpectation fulfill];
|
|
|
}];
|
|
|
- [self verifyAndRevokeToken:kAccessToken hasCallback:YES];
|
|
|
- [_authorization verify];
|
|
|
- [_authState verify];
|
|
|
- [_tokenResponse verify];
|
|
|
+ [self waitForExpectationsWithTimeout:1 handler:nil];
|
|
|
XCTAssertNil([_keychainHandler loadAuthState]);
|
|
|
}
|
|
|
|
|
|
// Verifies disconnect if access token is present.
|
|
|
-- (void)testDisconnectNoCallback_accessToken {
|
|
|
+- (void)testDisconnectNoCallback_accessTokenIsPresent {
|
|
|
[_keychainHandler saveAuthState:_authState];
|
|
|
- [[[_authState expect] andReturn:_tokenResponse] lastTokenResponse];
|
|
|
- [[[_tokenResponse expect] andReturn:kAccessToken] accessToken];
|
|
|
- [[[_authorization expect] andReturn:_fetcherService] fetcherService];
|
|
|
+ OCMStub([_authState lastTokenResponse]).andReturn(_tokenResponse);
|
|
|
+ OCMStub([_tokenResponse accessToken]).andReturn(kAccessToken);
|
|
|
+
|
|
|
+ XCTestExpectation *fetcherExpectation =
|
|
|
+ [self expectationWithDescription:@"testBlock is invoked."];
|
|
|
+ GIDHTTPFetcherTestBlock testBlock =
|
|
|
+ ^(NSURLRequest *request, GIDHTTPFetcherFakeResponseProviderBlock responseProvider) {
|
|
|
+ [self verifyRevokeRequest:request withToken:kAccessToken];
|
|
|
+ NSData *data = [[NSData alloc] init];
|
|
|
+ responseProvider(data, nil);
|
|
|
+ [fetcherExpectation fulfill];
|
|
|
+ };
|
|
|
+ [_httpFetcher setTestBlock:testBlock];
|
|
|
+
|
|
|
[_signIn disconnectWithCompletion:nil];
|
|
|
- [self verifyAndRevokeToken:kAccessToken hasCallback:NO];
|
|
|
- [_authorization verify];
|
|
|
- [_authState verify];
|
|
|
- [_tokenResponse verify];
|
|
|
+ [self waitForExpectationsWithTimeout:1 handler:nil];
|
|
|
XCTAssertNil([_keychainHandler loadAuthState]);
|
|
|
}
|
|
|
|
|
|
// Verifies disconnect calls callback with no errors if refresh token is present.
|
|
|
-- (void)testDisconnect_refreshToken {
|
|
|
+- (void)testDisconnect_refreshTokenIsPresent {
|
|
|
[_keychainHandler saveAuthState:_authState];
|
|
|
- [[[_authState expect] andReturn:_tokenResponse] lastTokenResponse];
|
|
|
- [[[_tokenResponse expect] andReturn:nil] accessToken];
|
|
|
- [[[_authState expect] andReturn:_tokenResponse] lastTokenResponse];
|
|
|
- [[[_tokenResponse expect] andReturn:kRefreshToken] refreshToken];
|
|
|
- [[[_authorization expect] andReturn:_fetcherService] fetcherService];
|
|
|
- XCTestExpectation *expectation =
|
|
|
+ OCMStub([_authState lastTokenResponse]).andReturn(_tokenResponse);
|
|
|
+ OCMStub([_tokenResponse accessToken]).andReturn(nil);
|
|
|
+ OCMStub([_tokenResponse refreshToken]).andReturn(kRefreshToken);
|
|
|
+
|
|
|
+ XCTestExpectation *fetcherExpectation =
|
|
|
+ [self expectationWithDescription:@"testBlock is invoked."];
|
|
|
+ GIDHTTPFetcherTestBlock testBlock =
|
|
|
+ ^(NSURLRequest *request, GIDHTTPFetcherFakeResponseProviderBlock responseProvider) {
|
|
|
+ [self verifyRevokeRequest:request withToken:kRefreshToken];
|
|
|
+ NSData *data = [[NSData alloc] init];
|
|
|
+ responseProvider(data, nil);
|
|
|
+ [fetcherExpectation fulfill];
|
|
|
+ };
|
|
|
+ [_httpFetcher setTestBlock:testBlock];
|
|
|
+
|
|
|
+ XCTestExpectation *completionExpectation =
|
|
|
[self expectationWithDescription:@"Callback called with nil error"];
|
|
|
[_signIn disconnectWithCompletion:^(NSError * _Nullable error) {
|
|
|
- if (error == nil) {
|
|
|
- [expectation fulfill];
|
|
|
- }
|
|
|
+ XCTAssertNil(error);
|
|
|
+ [completionExpectation fulfill];
|
|
|
}];
|
|
|
- [self verifyAndRevokeToken:kRefreshToken hasCallback:YES];
|
|
|
- [_authorization verify];
|
|
|
- [_authState verify];
|
|
|
+ [self waitForExpectationsWithTimeout:1 handler:nil];
|
|
|
XCTAssertNil([_keychainHandler loadAuthState]);
|
|
|
}
|
|
|
|
|
|
// Verifies disconnect errors are passed along to the callback.
|
|
|
- (void)testDisconnect_errors {
|
|
|
[_keychainHandler saveAuthState:_authState];
|
|
|
- [[[_authState expect] andReturn:_tokenResponse] lastTokenResponse];
|
|
|
- [[[_tokenResponse expect] andReturn:kAccessToken] accessToken];
|
|
|
- [[[_authorization expect] andReturn:_fetcherService] fetcherService];
|
|
|
- XCTestExpectation *expectation =
|
|
|
+ OCMStub([_authState lastTokenResponse]).andReturn(_tokenResponse);
|
|
|
+ OCMStub([_tokenResponse accessToken]).andReturn(kAccessToken);
|
|
|
+
|
|
|
+ XCTestExpectation *fetcherExpectation =
|
|
|
+ [self expectationWithDescription:@"testBlock is invoked."];
|
|
|
+ GIDHTTPFetcherTestBlock testBlock =
|
|
|
+ ^(NSURLRequest *request, GIDHTTPFetcherFakeResponseProviderBlock responseProvider) {
|
|
|
+ [self verifyRevokeRequest:request withToken:kAccessToken];
|
|
|
+ NSError *error = [self error];
|
|
|
+ responseProvider(nil, error);
|
|
|
+ [fetcherExpectation fulfill];
|
|
|
+ };
|
|
|
+ [_httpFetcher setTestBlock:testBlock];
|
|
|
+
|
|
|
+ XCTestExpectation *completionExpectation =
|
|
|
[self expectationWithDescription:@"Callback called with an error"];
|
|
|
[_signIn disconnectWithCompletion:^(NSError * _Nullable error) {
|
|
|
- if (error != nil) {
|
|
|
- [expectation fulfill];
|
|
|
- }
|
|
|
+ XCTAssertNotNil(error);
|
|
|
+ [completionExpectation fulfill];
|
|
|
}];
|
|
|
- XCTAssertTrue([self isFetcherStarted], @"should start fetching");
|
|
|
- // Emulate result back from server.
|
|
|
- NSError *error = [self error];
|
|
|
- [self didFetch:nil error:error];
|
|
|
[self waitForExpectationsWithTimeout:1 handler:nil];
|
|
|
- [_authorization verify];
|
|
|
- [_authState verify];
|
|
|
- [_tokenResponse verify];
|
|
|
XCTAssertNotNil([_keychainHandler loadAuthState]);
|
|
|
}
|
|
|
|
|
|
// Verifies disconnect with errors
|
|
|
- (void)testDisconnectNoCallback_errors {
|
|
|
[_keychainHandler saveAuthState:_authState];
|
|
|
- [[[_authState expect] andReturn:_tokenResponse] lastTokenResponse];
|
|
|
- [[[_tokenResponse expect] andReturn:kAccessToken] accessToken];
|
|
|
- [[[_authorization expect] andReturn:_fetcherService] fetcherService];
|
|
|
+ OCMStub([_authState lastTokenResponse]).andReturn(_tokenResponse);
|
|
|
+ OCMStub([_tokenResponse accessToken]).andReturn(kAccessToken);
|
|
|
+
|
|
|
+ XCTestExpectation *fetcherExpectation =
|
|
|
+ [self expectationWithDescription:@"testBlock is invoked."];
|
|
|
+ GIDHTTPFetcherTestBlock testBlock =
|
|
|
+ ^(NSURLRequest *request, GIDHTTPFetcherFakeResponseProviderBlock responseProvider) {
|
|
|
+ [self verifyRevokeRequest:request withToken:kAccessToken];
|
|
|
+ NSError *error = [self error];
|
|
|
+ responseProvider(nil, error);
|
|
|
+ [fetcherExpectation fulfill];
|
|
|
+ };
|
|
|
+ [_httpFetcher setTestBlock:testBlock];
|
|
|
+
|
|
|
[_signIn disconnectWithCompletion:nil];
|
|
|
- XCTAssertTrue([self isFetcherStarted], @"should start fetching");
|
|
|
- // Emulate result back from server.
|
|
|
- NSError *error = [self error];
|
|
|
- [self didFetch:nil error:error];
|
|
|
- [_authorization verify];
|
|
|
- [_authState verify];
|
|
|
- [_tokenResponse verify];
|
|
|
+ [self waitForExpectationsWithTimeout:1 handler:nil];
|
|
|
XCTAssertNotNil([_keychainHandler loadAuthState]);
|
|
|
}
|
|
|
|
|
|
// Verifies disconnect calls callback with no errors and clears keychain if no tokens are present.
|
|
|
- (void)testDisconnect_noTokens {
|
|
|
[_keychainHandler saveAuthState:_authState];
|
|
|
- [[[_authState expect] andReturn:_tokenResponse] lastTokenResponse];
|
|
|
- [[[_tokenResponse expect] andReturn:nil] accessToken];
|
|
|
- [[[_authState expect] andReturn:_tokenResponse] lastTokenResponse];
|
|
|
- [[[_tokenResponse expect] andReturn:nil] refreshToken];
|
|
|
+ OCMStub([_authState lastTokenResponse]).andReturn(_tokenResponse);
|
|
|
+ OCMStub([_tokenResponse accessToken]).andReturn(nil);
|
|
|
+ OCMStub([_tokenResponse refreshToken]).andReturn(nil);
|
|
|
+ GIDHTTPFetcherTestBlock testBlock =
|
|
|
+ ^(NSURLRequest *request, GIDHTTPFetcherFakeResponseProviderBlock responseProvider) {
|
|
|
+ XCTFail(@"_httpFetcher should not be invoked.");
|
|
|
+ };
|
|
|
+ [_httpFetcher setTestBlock:testBlock];
|
|
|
+
|
|
|
XCTestExpectation *expectation =
|
|
|
[self expectationWithDescription:@"Callback called with nil error"];
|
|
|
[_signIn disconnectWithCompletion:^(NSError * _Nullable error) {
|
|
|
- if (error == nil) {
|
|
|
- [expectation fulfill];
|
|
|
- }
|
|
|
+ XCTAssertNil(error);
|
|
|
+ [expectation fulfill];
|
|
|
}];
|
|
|
[self waitForExpectationsWithTimeout:1 handler:nil];
|
|
|
- XCTAssertFalse([self isFetcherStarted], @"should not fetch");
|
|
|
- [_authorization verify];
|
|
|
- [_authState verify];
|
|
|
- [_tokenResponse verify];
|
|
|
XCTAssertNil([_keychainHandler loadAuthState]);
|
|
|
}
|
|
|
|
|
|
// Verifies disconnect clears keychain if no tokens are present.
|
|
|
- (void)testDisconnectNoCallback_noTokens {
|
|
|
[_keychainHandler saveAuthState:_authState];
|
|
|
- [[[_authState expect] andReturn:_tokenResponse] lastTokenResponse];
|
|
|
- [[[_tokenResponse expect] andReturn:nil] accessToken];
|
|
|
- [[[_authState expect] andReturn:_tokenResponse] lastTokenResponse];
|
|
|
- [[[_tokenResponse expect] andReturn:nil] refreshToken];
|
|
|
+ OCMStub([_authState lastTokenResponse]).andReturn(_tokenResponse);
|
|
|
+ OCMStub([_tokenResponse accessToken]).andReturn(nil);
|
|
|
+ OCMStub([_tokenResponse refreshToken]).andReturn(nil);
|
|
|
+ GIDHTTPFetcherTestBlock testBlock =
|
|
|
+ ^(NSURLRequest *request, GIDHTTPFetcherFakeResponseProviderBlock responseProvider) {
|
|
|
+ XCTFail(@"_httpFetcher should not be invoked.");
|
|
|
+ };
|
|
|
+ [_httpFetcher setTestBlock:testBlock];
|
|
|
+
|
|
|
[_signIn disconnectWithCompletion:nil];
|
|
|
- XCTAssertFalse([self isFetcherStarted], @"should not fetch");
|
|
|
- [_authorization verify];
|
|
|
- [_authState verify];
|
|
|
- [_tokenResponse verify];
|
|
|
XCTAssertNil([_keychainHandler loadAuthState]);
|
|
|
}
|
|
|
|
|
|
@@ -1085,55 +1120,26 @@ static NSString *const kNewScope = @"newScope";
|
|
|
|
|
|
#pragma mark - Helpers
|
|
|
|
|
|
-// Whether or not a fetcher has been started.
|
|
|
-- (BOOL)isFetcherStarted {
|
|
|
- NSUInteger count = _fetcherService.fetchers.count;
|
|
|
- XCTAssertTrue(count <= 1, @"Only one fetcher is supported");
|
|
|
- return !!count;
|
|
|
-}
|
|
|
-
|
|
|
-// Gets the URL being fetched.
|
|
|
-- (NSURL *)fetchedURL {
|
|
|
- return [_fetcherService.fetchers[0] requestURL];
|
|
|
-}
|
|
|
-
|
|
|
-// Emulates server returning the data as in JSON.
|
|
|
-- (void)didFetch:(id)dataObject error:(NSError *)error {
|
|
|
- NSData *data = nil;
|
|
|
- if (dataObject) {
|
|
|
- NSError *jsonError = nil;
|
|
|
- data = [NSJSONSerialization dataWithJSONObject:dataObject
|
|
|
- options:0
|
|
|
- error:&jsonError];
|
|
|
- XCTAssertNil(jsonError, @"must provide valid data");
|
|
|
- }
|
|
|
- [_fetcherService.fetchers[0] didFinishWithData:data error:error];
|
|
|
-}
|
|
|
-
|
|
|
- (NSError *)error {
|
|
|
return [NSError errorWithDomain:kErrorDomain code:kErrorCode userInfo:nil];
|
|
|
}
|
|
|
|
|
|
-// Verifies a fetcher has started for revoking token and emulates a server response.
|
|
|
-- (void)verifyAndRevokeToken:(NSString *)token hasCallback:(BOOL)hasCallback {
|
|
|
- XCTAssertTrue([self isFetcherStarted], @"should start fetching");
|
|
|
- NSURL *url = [self fetchedURL];
|
|
|
+- (void)verifyRevokeRequest:(NSURLRequest *)request withToken:(NSString *)token {
|
|
|
+ NSURL *url = request.URL;
|
|
|
XCTAssertEqualObjects([url scheme], @"https", @"scheme must match");
|
|
|
XCTAssertEqualObjects([url host], @"accounts.google.com", @"host must match");
|
|
|
XCTAssertEqualObjects([url path], @"/o/oauth2/revoke", @"path must match");
|
|
|
OIDURLQueryComponent *queryComponent = [[OIDURLQueryComponent alloc] initWithURL:url];
|
|
|
NSDictionary<NSString *, NSObject<NSCopying> *> *params = queryComponent.dictionaryValue;
|
|
|
- XCTAssertEqualObjects([params valueForKey:@"token"], token,
|
|
|
- @"token parameter should match");
|
|
|
XCTAssertEqualObjects([params valueForKey:kSDKVersionLoggingParameter], GIDVersion(),
|
|
|
@"SDK version logging parameter should match");
|
|
|
XCTAssertEqualObjects([params valueForKey:kEnvironmentLoggingParameter], GIDEnvironment(),
|
|
|
@"Environment logging parameter should match");
|
|
|
- // Emulate result back from server.
|
|
|
- [self didFetch:nil error:nil];
|
|
|
- if (hasCallback) {
|
|
|
- [self waitForExpectationsWithTimeout:1 handler:nil];
|
|
|
- }
|
|
|
+
|
|
|
+ NSData *body = request.HTTPBody;
|
|
|
+ NSString* bodyString = [[NSString alloc] initWithData:body encoding:NSUTF8StringEncoding];
|
|
|
+ NSArray<NSString *> *strings = [bodyString componentsSeparatedByString:@"="];
|
|
|
+ XCTAssertEqualObjects(strings[1], token);
|
|
|
}
|
|
|
|
|
|
- (void)OAuthLoginWithAddScopesFlow:(BOOL)addScopesFlow
|