GACDeviceCheckAPIServiceTests.m 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  1. /*
  2. * Copyright 2020 Google LLC
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. #import <XCTest/XCTest.h>
  17. #import <OCMock/OCMock.h>
  18. #import "FBLPromise+Testing.h"
  19. #import <GoogleUtilities/GULURLSessionDataResponse.h>
  20. #import "AppCheckCore/Sources/Core/APIService/GACAppCheckAPIService.h"
  21. #import "AppCheckCore/Sources/Core/Errors/GACAppCheckErrorUtil.h"
  22. #import "AppCheckCore/Sources/DeviceCheckProvider/API/GACDeviceCheckAPIService.h"
  23. #import "AppCheckCore/Sources/Public/AppCheckCore/GACAppCheckErrors.h"
  24. #import "AppCheckCore/Sources/Public/AppCheckCore/GACAppCheckToken.h"
  25. #import "AppCheckCore/Tests/Unit/Utils/GACFixtureLoader.h"
  26. #import "AppCheckCore/Tests/Utils/URLSession/GACURLSessionOCMockStub.h"
  27. #import "FirebaseCore/Extension/FirebaseCoreInternal.h"
  28. static NSString *const kResourceName = @"projects/project_id/apps/app_id";
  29. typedef BOOL (^FIRRequestValidationBlock)(NSURLRequest *request);
  30. @interface GACDeviceCheckAPIServiceTests : XCTestCase
  31. @property(nonatomic) GACDeviceCheckAPIService *APIService;
  32. @property(nonatomic) id mockAPIService;
  33. @end
  34. @implementation GACDeviceCheckAPIServiceTests
  35. - (void)setUp {
  36. [super setUp];
  37. self.mockAPIService = OCMProtocolMock(@protocol(GACAppCheckAPIServiceProtocol));
  38. OCMStub([self.mockAPIService baseURL]).andReturn(@"https://test.appcheck.url.com/alpha");
  39. self.APIService = [[GACDeviceCheckAPIService alloc] initWithAPIService:self.mockAPIService
  40. resourceName:kResourceName];
  41. }
  42. - (void)tearDown {
  43. self.APIService = nil;
  44. [self.mockAPIService stopMocking];
  45. self.mockAPIService = nil;
  46. [super tearDown];
  47. }
  48. - (void)testAppCheckTokenSuccess {
  49. NSData *deviceTokenData = [@"device_token" dataUsingEncoding:NSUTF8StringEncoding];
  50. GACAppCheckToken *expectedResult = [[GACAppCheckToken alloc] initWithToken:@"app_check_token"
  51. expirationDate:[NSDate date]];
  52. // 1. Stub API service.
  53. // 1.1 Stub send request.
  54. NSString *expectedRequestURL =
  55. [NSString stringWithFormat:@"%@%@", [self.mockAPIService baseURL],
  56. @"/projects/project_id/apps/app_id:exchangeDeviceCheckToken"];
  57. id URLValidationArg = [OCMArg checkWithBlock:^BOOL(NSURL *URL) {
  58. XCTAssertEqualObjects(URL.absoluteString, expectedRequestURL);
  59. return YES;
  60. }];
  61. id HTTPBodyValidationArg = [self HTTPBodyValidationArgWithDeviceToken:deviceTokenData];
  62. NSData *responseBody =
  63. [GACFixtureLoader loadFixtureNamed:@"FACTokenExchangeResponseSuccess.json"];
  64. XCTAssertNotNil(responseBody);
  65. NSHTTPURLResponse *HTTPResponse = [GACURLSessionOCMockStub HTTPResponseWithCode:200];
  66. GULURLSessionDataResponse *APIResponse =
  67. [[GULURLSessionDataResponse alloc] initWithResponse:HTTPResponse HTTPBody:responseBody];
  68. OCMExpect([self.mockAPIService sendRequestWithURL:URLValidationArg
  69. HTTPMethod:@"POST"
  70. body:HTTPBodyValidationArg
  71. additionalHeaders:@{@"Content-Type" : @"application/json"}])
  72. .andReturn([FBLPromise resolvedWith:APIResponse]);
  73. // 1.2. Stub response parsing.
  74. OCMExpect([self.mockAPIService appCheckTokenWithAPIResponse:APIResponse])
  75. .andReturn([FBLPromise resolvedWith:expectedResult]);
  76. // 2. Send request.
  77. __auto_type tokenPromise = [self.APIService appCheckTokenWithDeviceToken:deviceTokenData];
  78. // 3. Verify.
  79. XCTAssert(FBLWaitForPromisesWithTimeout(1));
  80. XCTAssertTrue(tokenPromise.isFulfilled);
  81. XCTAssertNil(tokenPromise.error);
  82. XCTAssertEqualObjects(tokenPromise.value.token, expectedResult.token);
  83. XCTAssertEqualObjects(tokenPromise.value.expirationDate, expectedResult.expirationDate);
  84. OCMVerifyAll(self.mockAPIService);
  85. }
  86. - (void)testAppCheckTokenResponseParsingError {
  87. NSData *deviceTokenData = [@"device_token" dataUsingEncoding:NSUTF8StringEncoding];
  88. NSError *parsingError = [NSError errorWithDomain:@"testAppCheckTokenResponseParsingError"
  89. code:-1
  90. userInfo:nil];
  91. // 1. Stub API service.
  92. // 1.1 Stub send request.
  93. NSString *expectedRequestURL =
  94. [NSString stringWithFormat:@"%@%@", [self.mockAPIService baseURL],
  95. @"/projects/project_id/apps/app_id:exchangeDeviceCheckToken"];
  96. id URLValidationArg = [OCMArg checkWithBlock:^BOOL(NSURL *URL) {
  97. XCTAssertEqualObjects(URL.absoluteString, expectedRequestURL);
  98. return YES;
  99. }];
  100. id HTTPBodyValidationArg = [self HTTPBodyValidationArgWithDeviceToken:deviceTokenData];
  101. NSData *responseBody =
  102. [GACFixtureLoader loadFixtureNamed:@"FACTokenExchangeResponseSuccess.json"];
  103. XCTAssertNotNil(responseBody);
  104. NSHTTPURLResponse *HTTPResponse = [GACURLSessionOCMockStub HTTPResponseWithCode:200];
  105. GULURLSessionDataResponse *APIResponse =
  106. [[GULURLSessionDataResponse alloc] initWithResponse:HTTPResponse HTTPBody:responseBody];
  107. OCMExpect([self.mockAPIService sendRequestWithURL:URLValidationArg
  108. HTTPMethod:@"POST"
  109. body:HTTPBodyValidationArg
  110. additionalHeaders:@{@"Content-Type" : @"application/json"}])
  111. .andReturn([FBLPromise resolvedWith:APIResponse]);
  112. // 1.2. Stub response parsing.
  113. FBLPromise *rejectedPromise = [FBLPromise pendingPromise];
  114. [rejectedPromise reject:parsingError];
  115. OCMExpect([self.mockAPIService appCheckTokenWithAPIResponse:APIResponse])
  116. .andReturn(rejectedPromise);
  117. // 2. Send request.
  118. __auto_type tokenPromise = [self.APIService appCheckTokenWithDeviceToken:deviceTokenData];
  119. // 3. Verify.
  120. XCTAssert(FBLWaitForPromisesWithTimeout(1));
  121. XCTAssertTrue(tokenPromise.isRejected);
  122. XCTAssertEqualObjects(tokenPromise.error, parsingError);
  123. XCTAssertNil(tokenPromise.value);
  124. OCMVerifyAll(self.mockAPIService);
  125. }
  126. - (void)testAppCheckTokenNetworkError {
  127. NSData *deviceTokenData = [@"device_token" dataUsingEncoding:NSUTF8StringEncoding];
  128. NSError *APIError = [NSError errorWithDomain:@"testAppCheckTokenNetworkError"
  129. code:-1
  130. userInfo:nil];
  131. // 1. Stub API service.
  132. FBLPromise *rejectedPromise = [FBLPromise pendingPromise];
  133. [rejectedPromise reject:APIError];
  134. id HTTPBodyValidationArg = [self HTTPBodyValidationArgWithDeviceToken:deviceTokenData];
  135. OCMExpect([self.mockAPIService sendRequestWithURL:[OCMArg any]
  136. HTTPMethod:@"POST"
  137. body:HTTPBodyValidationArg
  138. additionalHeaders:@{@"Content-Type" : @"application/json"}])
  139. .andReturn(rejectedPromise);
  140. // 2. Send request.
  141. __auto_type tokenPromise = [self.APIService appCheckTokenWithDeviceToken:deviceTokenData];
  142. // 3. Verify.
  143. XCTAssert(FBLWaitForPromisesWithTimeout(1));
  144. XCTAssertTrue(tokenPromise.isRejected);
  145. XCTAssertNil(tokenPromise.value);
  146. XCTAssertEqualObjects(tokenPromise.error, APIError);
  147. OCMVerifyAll(self.mockAPIService);
  148. }
  149. - (void)testAppCheckTokenEmptyDeviceToken {
  150. NSData *deviceTokenData = [NSData data];
  151. // 1. Stub API service.
  152. OCMReject([self.mockAPIService sendRequestWithURL:[OCMArg any]
  153. HTTPMethod:[OCMArg any]
  154. body:[OCMArg any]
  155. additionalHeaders:[OCMArg any]]);
  156. // 2. Send request.
  157. __auto_type tokenPromise = [self.APIService appCheckTokenWithDeviceToken:deviceTokenData];
  158. // 3. Verify.
  159. XCTAssert(FBLWaitForPromisesWithTimeout(1));
  160. XCTAssertTrue(tokenPromise.isRejected);
  161. XCTAssertNil(tokenPromise.value);
  162. XCTAssertNotNil(tokenPromise.error);
  163. XCTAssertEqualObjects(tokenPromise.error.domain, GACAppCheckErrorDomain);
  164. XCTAssertEqual(tokenPromise.error.code, GACAppCheckErrorCodeUnknown);
  165. // Expect response body and HTTP status code to be included in the error.
  166. NSString *failureReason = tokenPromise.error.userInfo[NSLocalizedFailureReasonErrorKey];
  167. XCTAssertEqualObjects(failureReason, @"DeviceCheck token must not be empty.");
  168. OCMVerifyAll(self.mockAPIService);
  169. }
  170. #pragma mark - Helpers
  171. - (id)HTTPBodyValidationArgWithDeviceToken:(NSData *)deviceToken {
  172. return [OCMArg checkWithBlock:^BOOL(NSData *body) {
  173. NSDictionary<NSString *, id> *decodedData = [NSJSONSerialization JSONObjectWithData:body
  174. options:0
  175. error:nil];
  176. XCTAssert([decodedData isKindOfClass:[NSDictionary class]]);
  177. NSString *base64EncodedDeviceToken = decodedData[@"device_token"];
  178. XCTAssertNotNil(base64EncodedDeviceToken);
  179. NSData *decodedToken = [[NSData alloc] initWithBase64EncodedString:base64EncodedDeviceToken
  180. options:0];
  181. XCTAssertEqualObjects(decodedToken, deviceToken);
  182. return YES;
  183. }];
  184. }
  185. @end