FIRDeviceCheckAPIServiceTests.m 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247
  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 "FBLPromise+Testing.h"
  18. #import "OCMock.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 = [FIRFixtureLoader loadFixtureNamed:@"DeviceCheckResponseSuccess.json"];
  66. XCTAssertNotNil(responseBody);
  67. NSHTTPURLResponse *HTTPResponse = [FIRURLSessionOCMockStub HTTPResponseWithCode:200];
  68. GULURLSessionDataResponse *APIResponse =
  69. [[GULURLSessionDataResponse alloc] initWithResponse:HTTPResponse HTTPBody:responseBody];
  70. OCMExpect([self.mockAPIService sendRequestWithURL:URLValidationArg
  71. HTTPMethod:@"POST"
  72. body:HTTPBodyValidationArg
  73. additionalHeaders:@{@"Content-Type" : @"application/json"}])
  74. .andReturn([FBLPromise resolvedWith:APIResponse]);
  75. // 1.2. Stub response parsing.
  76. OCMExpect([self.mockAPIService appCheckTokenWithAPIResponse:APIResponse])
  77. .andReturn([FBLPromise resolvedWith:expectedResult]);
  78. // 2. Send request.
  79. __auto_type tokenPromise = [self.APIService appCheckTokenWithDeviceToken:deviceTokenData];
  80. // 3. Verify.
  81. XCTAssert(FBLWaitForPromisesWithTimeout(1));
  82. XCTAssertTrue(tokenPromise.isFulfilled);
  83. XCTAssertNil(tokenPromise.error);
  84. XCTAssertEqualObjects(tokenPromise.value.token, expectedResult.token);
  85. XCTAssertEqualObjects(tokenPromise.value.expirationDate, expectedResult.expirationDate);
  86. OCMVerifyAll(self.mockAPIService);
  87. }
  88. - (void)testAppCheckTokenResponseParsingError {
  89. NSData *deviceTokenData = [@"device_token" dataUsingEncoding:NSUTF8StringEncoding];
  90. NSError *parsingError = [NSError errorWithDomain:@"testAppCheckTokenResponseParsingError"
  91. code:-1
  92. userInfo:nil];
  93. // 1. Stub API service.
  94. // 1.1 Stub send request.
  95. NSString *expectedRequestURL =
  96. [NSString stringWithFormat:@"%@%@", [self.mockAPIService baseURL],
  97. @"/projects/project_id/apps/app_id:exchangeDeviceCheckToken"];
  98. id URLValidationArg = [OCMArg checkWithBlock:^BOOL(NSURL *URL) {
  99. XCTAssertEqualObjects(URL.absoluteString, expectedRequestURL);
  100. return YES;
  101. }];
  102. id HTTPBodyValidationArg = [self HTTPBodyValidationArgWithDeviceToken:deviceTokenData];
  103. NSData *responseBody = [FIRFixtureLoader loadFixtureNamed:@"DeviceCheckResponseSuccess.json"];
  104. XCTAssertNotNil(responseBody);
  105. NSHTTPURLResponse *HTTPResponse = [FIRURLSessionOCMockStub HTTPResponseWithCode:200];
  106. GULURLSessionDataResponse *APIResponse =
  107. [[GULURLSessionDataResponse alloc] initWithResponse:HTTPResponse HTTPBody:responseBody];
  108. OCMExpect([self.mockAPIService sendRequestWithURL:URLValidationArg
  109. HTTPMethod:@"POST"
  110. body:HTTPBodyValidationArg
  111. additionalHeaders:@{@"Content-Type" : @"application/json"}])
  112. .andReturn([FBLPromise resolvedWith:APIResponse]);
  113. // 1.2. Stub response parsing.
  114. FBLPromise *rejectedPromise = [FBLPromise pendingPromise];
  115. [rejectedPromise reject:parsingError];
  116. OCMExpect([self.mockAPIService appCheckTokenWithAPIResponse:APIResponse])
  117. .andReturn(rejectedPromise);
  118. // 2. Send request.
  119. __auto_type tokenPromise = [self.APIService appCheckTokenWithDeviceToken:deviceTokenData];
  120. // 3. Verify.
  121. XCTAssert(FBLWaitForPromisesWithTimeout(1));
  122. XCTAssertTrue(tokenPromise.isRejected);
  123. XCTAssertEqualObjects(tokenPromise.error, parsingError);
  124. XCTAssertNil(tokenPromise.value);
  125. OCMVerifyAll(self.mockAPIService);
  126. }
  127. - (void)testAppCheckTokenNetworkError {
  128. NSData *deviceTokenData = [@"device_token" dataUsingEncoding:NSUTF8StringEncoding];
  129. NSError *APIError = [NSError errorWithDomain:@"testAppCheckTokenNetworkError"
  130. code:-1
  131. userInfo:nil];
  132. // 1. Stub API service.
  133. FBLPromise *rejectedPromise = [FBLPromise pendingPromise];
  134. [rejectedPromise reject:APIError];
  135. id HTTPBodyValidationArg = [self HTTPBodyValidationArgWithDeviceToken:deviceTokenData];
  136. OCMExpect([self.mockAPIService sendRequestWithURL:[OCMArg any]
  137. HTTPMethod:@"POST"
  138. body:HTTPBodyValidationArg
  139. additionalHeaders:@{@"Content-Type" : @"application/json"}])
  140. .andReturn(rejectedPromise);
  141. // 2. Send request.
  142. __auto_type tokenPromise = [self.APIService appCheckTokenWithDeviceToken:deviceTokenData];
  143. // 3. Verify.
  144. XCTAssert(FBLWaitForPromisesWithTimeout(1));
  145. XCTAssertTrue(tokenPromise.isRejected);
  146. XCTAssertNil(tokenPromise.value);
  147. XCTAssertEqualObjects(tokenPromise.error, APIError);
  148. OCMVerifyAll(self.mockAPIService);
  149. }
  150. - (void)testAppCheckTokenEmptyDeviceToken {
  151. NSData *deviceTokenData = [NSData data];
  152. // 1. Stub API service.
  153. OCMReject([self.mockAPIService sendRequestWithURL:[OCMArg any]
  154. HTTPMethod:[OCMArg any]
  155. body:[OCMArg any]
  156. additionalHeaders:[OCMArg any]]);
  157. // 2. Send request.
  158. __auto_type tokenPromise = [self.APIService appCheckTokenWithDeviceToken:deviceTokenData];
  159. // 3. Verify.
  160. XCTAssert(FBLWaitForPromisesWithTimeout(1));
  161. XCTAssertTrue(tokenPromise.isRejected);
  162. XCTAssertNil(tokenPromise.value);
  163. XCTAssertNotNil(tokenPromise.error);
  164. XCTAssertEqualObjects(tokenPromise.error.domain, kFIRAppCheckErrorDomain);
  165. XCTAssertEqual(tokenPromise.error.code, FIRAppCheckErrorCodeUnknown);
  166. // Expect response body and HTTP status code to be included in the error.
  167. NSString *failureReason = tokenPromise.error.userInfo[NSLocalizedFailureReasonErrorKey];
  168. XCTAssertEqualObjects(failureReason, @"DeviceCheck token must not be empty.");
  169. OCMVerifyAll(self.mockAPIService);
  170. }
  171. #pragma mark - Helpers
  172. - (id)HTTPBodyValidationArgWithDeviceToken:(NSData *)deviceToken {
  173. return [OCMArg checkWithBlock:^BOOL(NSData *body) {
  174. NSDictionary<NSString *, id> *decodedData = [NSJSONSerialization JSONObjectWithData:body
  175. options:0
  176. error:nil];
  177. XCTAssert([decodedData isKindOfClass:[NSDictionary class]]);
  178. NSString *base64EncodedDeviceToken = decodedData[@"device_token"];
  179. XCTAssertNotNil(base64EncodedDeviceToken);
  180. NSData *decodedToken = [[NSData alloc] initWithBase64EncodedString:base64EncodedDeviceToken
  181. options:0];
  182. XCTAssertEqualObjects(decodedToken, deviceToken);
  183. return YES;
  184. }];
  185. }
  186. @end