FIRDeviceCheckAPIServiceTests.m 9.6 KB

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