FIRDeviceCheckAPIServiceTests.m 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  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 <FirebaseAppCheck/FirebaseAppCheck.h>
  20. #import <GoogleUtilities/GULURLSessionDataResponse.h>
  21. #import "FirebaseAppCheck/Sources/Core/APIService/FIRAppCheckAPIService.h"
  22. #import "FirebaseAppCheck/Sources/Core/Errors/FIRAppCheckErrorUtil.h"
  23. #import "FirebaseAppCheck/Sources/DeviceCheckProvider/API/FIRDeviceCheckAPIService.h"
  24. #import "FirebaseAppCheck/Tests/Unit/Utils/FIRFixtureLoader.h"
  25. #import "SharedTestUtilities/URLSession/FIRURLSessionOCMockStub.h"
  26. #import "FirebaseCore/Sources/Private/FirebaseCoreInternal.h"
  27. typedef BOOL (^FIRRequestValidationBlock)(NSURLRequest *request);
  28. @interface FIRDeviceCheckAPIServiceTests : XCTestCase
  29. @property(nonatomic) FIRDeviceCheckAPIService *APIService;
  30. @property(nonatomic) id mockAPIService;
  31. @property(nonatomic) NSString *projectID;
  32. @property(nonatomic) NSString *appID;
  33. @end
  34. @implementation FIRDeviceCheckAPIServiceTests
  35. - (void)setUp {
  36. [super setUp];
  37. self.projectID = @"project_id";
  38. self.appID = @"app_id";
  39. self.mockAPIService = OCMProtocolMock(@protocol(FIRAppCheckAPIServiceProtocol));
  40. OCMStub([self.mockAPIService baseURL]).andReturn(@"https://test.appcheck.url.com/alpha");
  41. self.APIService = [[FIRDeviceCheckAPIService alloc] initWithAPIService:self.mockAPIService
  42. projectID:self.projectID
  43. appID:self.appID];
  44. }
  45. - (void)tearDown {
  46. self.APIService = nil;
  47. [self.mockAPIService stopMocking];
  48. self.mockAPIService = nil;
  49. [super tearDown];
  50. }
  51. - (void)testAppCheckTokenSuccess {
  52. NSData *deviceTokenData = [@"device_token" dataUsingEncoding:NSUTF8StringEncoding];
  53. FIRAppCheckToken *expectedResult = [[FIRAppCheckToken alloc] initWithToken:@"app_check_token"
  54. expirationDate:[NSDate date]];
  55. // 1. Stub API service.
  56. // 1.1 Stub send request.
  57. NSString *expectedRequestURL =
  58. [NSString stringWithFormat:@"%@%@", [self.mockAPIService baseURL],
  59. @"/projects/project_id/apps/app_id:exchangeDeviceCheckToken"];
  60. id URLValidationArg = [OCMArg checkWithBlock:^BOOL(NSURL *URL) {
  61. XCTAssertEqualObjects(URL.absoluteString, expectedRequestURL);
  62. return YES;
  63. }];
  64. id HTTPBodyValidationArg = [self HTTPBodyValidationArgWithDeviceToken:deviceTokenData];
  65. NSData *responseBody =
  66. [FIRFixtureLoader loadFixtureNamed:@"FACTokenExchangeResponseSuccess.json"];
  67. XCTAssertNotNil(responseBody);
  68. NSHTTPURLResponse *HTTPResponse = [FIRURLSessionOCMockStub HTTPResponseWithCode:200];
  69. GULURLSessionDataResponse *APIResponse =
  70. [[GULURLSessionDataResponse alloc] initWithResponse:HTTPResponse HTTPBody:responseBody];
  71. OCMExpect([self.mockAPIService sendRequestWithURL:URLValidationArg
  72. HTTPMethod:@"POST"
  73. body:HTTPBodyValidationArg
  74. additionalHeaders:@{@"Content-Type" : @"application/json"}])
  75. .andReturn([FBLPromise resolvedWith:APIResponse]);
  76. // 1.2. Stub response parsing.
  77. OCMExpect([self.mockAPIService appCheckTokenWithAPIResponse:APIResponse])
  78. .andReturn([FBLPromise resolvedWith:expectedResult]);
  79. // 2. Send request.
  80. __auto_type tokenPromise = [self.APIService appCheckTokenWithDeviceToken:deviceTokenData];
  81. // 3. Verify.
  82. XCTAssert(FBLWaitForPromisesWithTimeout(1));
  83. XCTAssertTrue(tokenPromise.isFulfilled);
  84. XCTAssertNil(tokenPromise.error);
  85. XCTAssertEqualObjects(tokenPromise.value.token, expectedResult.token);
  86. XCTAssertEqualObjects(tokenPromise.value.expirationDate, expectedResult.expirationDate);
  87. OCMVerifyAll(self.mockAPIService);
  88. }
  89. - (void)testAppCheckTokenResponseParsingError {
  90. NSData *deviceTokenData = [@"device_token" dataUsingEncoding:NSUTF8StringEncoding];
  91. NSError *parsingError = [NSError errorWithDomain:@"testAppCheckTokenResponseParsingError"
  92. code:-1
  93. userInfo:nil];
  94. // 1. Stub API service.
  95. // 1.1 Stub send request.
  96. NSString *expectedRequestURL =
  97. [NSString stringWithFormat:@"%@%@", [self.mockAPIService baseURL],
  98. @"/projects/project_id/apps/app_id:exchangeDeviceCheckToken"];
  99. id URLValidationArg = [OCMArg checkWithBlock:^BOOL(NSURL *URL) {
  100. XCTAssertEqualObjects(URL.absoluteString, expectedRequestURL);
  101. return YES;
  102. }];
  103. id HTTPBodyValidationArg = [self HTTPBodyValidationArgWithDeviceToken:deviceTokenData];
  104. NSData *responseBody =
  105. [FIRFixtureLoader loadFixtureNamed:@"FACTokenExchangeResponseSuccess.json"];
  106. XCTAssertNotNil(responseBody);
  107. NSHTTPURLResponse *HTTPResponse = [FIRURLSessionOCMockStub HTTPResponseWithCode:200];
  108. GULURLSessionDataResponse *APIResponse =
  109. [[GULURLSessionDataResponse alloc] initWithResponse:HTTPResponse HTTPBody:responseBody];
  110. OCMExpect([self.mockAPIService sendRequestWithURL:URLValidationArg
  111. HTTPMethod:@"POST"
  112. body:HTTPBodyValidationArg
  113. additionalHeaders:@{@"Content-Type" : @"application/json"}])
  114. .andReturn([FBLPromise resolvedWith:APIResponse]);
  115. // 1.2. Stub response parsing.
  116. FBLPromise *rejectedPromise = [FBLPromise pendingPromise];
  117. [rejectedPromise reject:parsingError];
  118. OCMExpect([self.mockAPIService appCheckTokenWithAPIResponse:APIResponse])
  119. .andReturn(rejectedPromise);
  120. // 2. Send request.
  121. __auto_type tokenPromise = [self.APIService appCheckTokenWithDeviceToken:deviceTokenData];
  122. // 3. Verify.
  123. XCTAssert(FBLWaitForPromisesWithTimeout(1));
  124. XCTAssertTrue(tokenPromise.isRejected);
  125. XCTAssertEqualObjects(tokenPromise.error, parsingError);
  126. XCTAssertNil(tokenPromise.value);
  127. OCMVerifyAll(self.mockAPIService);
  128. }
  129. - (void)testAppCheckTokenNetworkError {
  130. NSData *deviceTokenData = [@"device_token" dataUsingEncoding:NSUTF8StringEncoding];
  131. NSError *APIError = [NSError errorWithDomain:@"testAppCheckTokenNetworkError"
  132. code:-1
  133. userInfo:nil];
  134. // 1. Stub API service.
  135. FBLPromise *rejectedPromise = [FBLPromise pendingPromise];
  136. [rejectedPromise reject:APIError];
  137. id HTTPBodyValidationArg = [self HTTPBodyValidationArgWithDeviceToken:deviceTokenData];
  138. OCMExpect([self.mockAPIService sendRequestWithURL:[OCMArg any]
  139. HTTPMethod:@"POST"
  140. body:HTTPBodyValidationArg
  141. additionalHeaders:@{@"Content-Type" : @"application/json"}])
  142. .andReturn(rejectedPromise);
  143. // 2. Send request.
  144. __auto_type tokenPromise = [self.APIService appCheckTokenWithDeviceToken:deviceTokenData];
  145. // 3. Verify.
  146. XCTAssert(FBLWaitForPromisesWithTimeout(1));
  147. XCTAssertTrue(tokenPromise.isRejected);
  148. XCTAssertNil(tokenPromise.value);
  149. XCTAssertEqualObjects(tokenPromise.error, APIError);
  150. OCMVerifyAll(self.mockAPIService);
  151. }
  152. - (void)testAppCheckTokenEmptyDeviceToken {
  153. NSData *deviceTokenData = [NSData data];
  154. // 1. Stub API service.
  155. OCMReject([self.mockAPIService sendRequestWithURL:[OCMArg any]
  156. HTTPMethod:[OCMArg any]
  157. body:[OCMArg any]
  158. additionalHeaders:[OCMArg any]]);
  159. // 2. Send request.
  160. __auto_type tokenPromise = [self.APIService appCheckTokenWithDeviceToken:deviceTokenData];
  161. // 3. Verify.
  162. XCTAssert(FBLWaitForPromisesWithTimeout(1));
  163. XCTAssertTrue(tokenPromise.isRejected);
  164. XCTAssertNil(tokenPromise.value);
  165. XCTAssertNotNil(tokenPromise.error);
  166. XCTAssertEqualObjects(tokenPromise.error.domain, kFIRAppCheckErrorDomain);
  167. XCTAssertEqual(tokenPromise.error.code, FIRAppCheckErrorCodeUnknown);
  168. // Expect response body and HTTP status code to be included in the error.
  169. NSString *failureReason = tokenPromise.error.userInfo[NSLocalizedFailureReasonErrorKey];
  170. XCTAssertEqualObjects(failureReason, @"DeviceCheck token must not be empty.");
  171. OCMVerifyAll(self.mockAPIService);
  172. }
  173. #pragma mark - Helpers
  174. - (id)HTTPBodyValidationArgWithDeviceToken:(NSData *)deviceToken {
  175. return [OCMArg checkWithBlock:^BOOL(NSData *body) {
  176. NSDictionary<NSString *, id> *decodedData = [NSJSONSerialization JSONObjectWithData:body
  177. options:0
  178. error:nil];
  179. XCTAssert([decodedData isKindOfClass:[NSDictionary class]]);
  180. NSString *base64EncodedDeviceToken = decodedData[@"device_token"];
  181. XCTAssertNotNil(base64EncodedDeviceToken);
  182. NSData *decodedToken = [[NSData alloc] initWithBase64EncodedString:base64EncodedDeviceToken
  183. options:0];
  184. XCTAssertEqualObjects(decodedToken, deviceToken);
  185. return YES;
  186. }];
  187. }
  188. @end