GACAppCheckTests.m 43 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014
  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 "AppCheck/Sources/Public/AppCheck/GACAppCheck.h"
  20. #import "AppCheck/Sources/Public/AppCheck/GACAppCheckErrors.h"
  21. #import "AppCheck/Sources/Public/AppCheck/GACAppCheckProvider.h"
  22. #import "AppCheck/Sources/Public/AppCheck/GACAppCheckSettings.h"
  23. #import "AppCheck/Sources/Core/Errors/GACAppCheckErrorUtil.h"
  24. #import "AppCheck/Sources/Core/GACAppCheckTokenResult.h"
  25. #import "AppCheck/Sources/Core/Storage/GACAppCheckStorage.h"
  26. #import "AppCheck/Sources/Core/TokenRefresh/GACAppCheckTokenRefreshResult.h"
  27. #import "AppCheck/Sources/Core/TokenRefresh/GACAppCheckTokenRefresher.h"
  28. #import "AppCheck/Sources/Public/AppCheck/GACAppCheckToken.h"
  29. #import "FirebaseCore/Extension/FirebaseCoreInternal.h"
  30. // Since DeviceCheck is the default attestation provider for AppCheck, disable
  31. // test cases that may be dependent on DeviceCheck being available.
  32. #if GAC_DEVICE_CHECK_SUPPORTED_TARGETS
  33. // The FAC token value returned when an error occurs.
  34. static NSString *const kDummyToken = @"eyJlcnJvciI6IlVOS05PV05fRVJST1IifQ==";
  35. static NSString *const kResourceName = @"projects/test_project_id/apps/test_app_id";
  36. static NSString *const kAppName = @"GACAppCheckTests";
  37. static NSString *const kAppGroupID = @"app_group_id";
  38. @interface GACAppCheck (Tests) <GACAppCheckInterop>
  39. - (instancetype)initWithInstanceName:(NSString *)instanceName
  40. appCheckProvider:(id<GACAppCheckProvider>)appCheckProvider
  41. storage:(id<GACAppCheckStorageProtocol>)storage
  42. tokenRefresher:(id<GACAppCheckTokenRefresherProtocol>)tokenRefresher
  43. notificationCenter:(NSNotificationCenter *)notificationCenter
  44. settings:(id<GACAppCheckSettingsProtocol>)settings;
  45. @end
  46. @interface GACAppCheckTests : XCTestCase
  47. @property(nonatomic) OCMockObject<GACAppCheckStorageProtocol> *mockStorage;
  48. @property(nonatomic) OCMockObject<GACAppCheckProvider> *mockAppCheckProvider;
  49. @property(nonatomic) OCMockObject<GACAppCheckTokenRefresherProtocol> *mockTokenRefresher;
  50. @property(nonatomic) OCMockObject<GACAppCheckSettingsProtocol> *mockSettings;
  51. @property(nonatomic) NSNotificationCenter *notificationCenter;
  52. @property(nonatomic) GACAppCheck<GACAppCheckInterop> *appCheck;
  53. @property(nonatomic, copy, nullable) GACAppCheckTokenRefreshBlock tokenRefreshHandler;
  54. @end
  55. @implementation GACAppCheckTests
  56. - (void)setUp {
  57. [super setUp];
  58. self.mockStorage = OCMStrictProtocolMock(@protocol(GACAppCheckStorageProtocol));
  59. self.mockAppCheckProvider = OCMStrictProtocolMock(@protocol(GACAppCheckProvider));
  60. self.mockTokenRefresher = OCMStrictProtocolMock(@protocol(GACAppCheckTokenRefresherProtocol));
  61. self.mockSettings = OCMProtocolMock(@protocol(GACAppCheckSettingsProtocol));
  62. self.notificationCenter = [[NSNotificationCenter alloc] init];
  63. [self stubSetTokenRefreshHandler];
  64. self.appCheck = [[GACAppCheck alloc] initWithInstanceName:kAppName
  65. appCheckProvider:self.mockAppCheckProvider
  66. storage:self.mockStorage
  67. tokenRefresher:self.mockTokenRefresher
  68. notificationCenter:self.notificationCenter
  69. settings:self.mockSettings];
  70. }
  71. - (void)tearDown {
  72. self.appCheck = nil;
  73. [self.mockAppCheckProvider stopMocking];
  74. self.mockAppCheckProvider = nil;
  75. [self.mockStorage stopMocking];
  76. self.mockStorage = nil;
  77. [self.mockTokenRefresher stopMocking];
  78. self.mockTokenRefresher = nil;
  79. [super tearDown];
  80. }
  81. #pragma mark - Public Init
  82. - (void)testAppCheckInit {
  83. NSString *tokenKey =
  84. [NSString stringWithFormat:@"app_check_token.%@.%@", kAppName, kResourceName];
  85. // 1. Stub GACAppCheckTokenRefresher and validate usage.
  86. id mockTokenRefresher = OCMClassMock([GACAppCheckTokenRefresher class]);
  87. OCMExpect([mockTokenRefresher alloc]).andReturn(mockTokenRefresher);
  88. id refresherDateValidator =
  89. [OCMArg checkWithBlock:^BOOL(GACAppCheckTokenRefreshResult *refreshResult) {
  90. XCTAssertEqual(refreshResult.status, GACAppCheckTokenRefreshStatusNever);
  91. XCTAssertEqual(refreshResult.tokenExpirationDate, nil);
  92. XCTAssertEqual(refreshResult.tokenReceivedAtDate, nil);
  93. return YES;
  94. }];
  95. id settingsValidator = [OCMArg checkWithBlock:^BOOL(id obj) {
  96. XCTAssert([obj conformsToProtocol:@protocol(GACAppCheckSettingsProtocol)]);
  97. return YES;
  98. }];
  99. OCMExpect([mockTokenRefresher initWithRefreshResult:refresherDateValidator
  100. settings:settingsValidator])
  101. .andReturn(mockTokenRefresher);
  102. OCMExpect([mockTokenRefresher setTokenRefreshHandler:[OCMArg any]]);
  103. // 2. Stub GACAppCheckStorage and validate usage.
  104. id mockStorage = OCMStrictClassMock([GACAppCheckStorage class]);
  105. OCMExpect([mockStorage alloc]).andReturn(mockStorage);
  106. OCMExpect([mockStorage initWithTokenKey:tokenKey accessGroup:kAppGroupID]).andReturn(mockStorage);
  107. // 3. Stub attestation provider.
  108. OCMockObject<GACAppCheckProvider> *mockProvider =
  109. OCMStrictProtocolMock(@protocol(GACAppCheckProvider));
  110. // 4. Stub GACAppCheckSettingsProtocol.
  111. OCMockObject<GACAppCheckSettingsProtocol> *mockSettings =
  112. OCMStrictProtocolMock(@protocol(GACAppCheckSettingsProtocol));
  113. // 5. Call init.
  114. GACAppCheck *appCheck = [[GACAppCheck alloc] initWithInstanceName:kAppName
  115. appCheckProvider:mockProvider
  116. settings:mockSettings
  117. resourceName:kResourceName
  118. keychainAccessGroup:kAppGroupID];
  119. XCTAssert([appCheck isKindOfClass:[GACAppCheck class]]);
  120. // 6. Verify mocks.
  121. OCMVerifyAll(mockTokenRefresher);
  122. OCMVerifyAll(mockStorage);
  123. OCMVerifyAll(mockProvider);
  124. OCMVerifyAll(mockSettings);
  125. // 7. Stop mocking real class mocks.
  126. [mockTokenRefresher stopMocking];
  127. mockTokenRefresher = nil;
  128. [mockStorage stopMocking];
  129. mockStorage = nil;
  130. }
  131. #pragma mark - Public Get Token
  132. - (void)testGetToken_WhenNoCache_Success {
  133. // 1. Create expected token and configure expectations.
  134. GACAppCheckToken *expectedToken = [self validToken];
  135. NSArray * /*[tokenNotification, getToken]*/ expectations =
  136. [self configuredExpectations_GetTokenWhenNoCache_withExpectedToken:expectedToken];
  137. // 2. Request token and verify result.
  138. [self.appCheck
  139. tokenForcingRefresh:NO
  140. completion:^(GACAppCheckToken *_Nullable token, NSError *_Nullable error) {
  141. [expectations.lastObject fulfill];
  142. XCTAssertNotNil(token);
  143. XCTAssertEqualObjects(token.token, expectedToken.token);
  144. XCTAssertNil(error);
  145. }];
  146. // 3. Wait for expectations and validate mocks.
  147. [self waitForExpectations:expectations timeout:0.5];
  148. [self verifyAllMocks];
  149. }
  150. - (void)testGetToken_WhenCachedTokenIsValid_Success {
  151. [self assertGetToken_WhenCachedTokenIsValid_Success];
  152. }
  153. - (void)testGetTokenForcingRefresh_WhenCachedTokenIsValid_Success {
  154. // 1. Create expected token and configure expectations.
  155. GACAppCheckToken *expectedToken = [self validToken];
  156. NSArray * /*[tokenNotification, getToken]*/ expectations =
  157. [self configuredExpectations_GetTokenForcingRefreshWhenCacheIsValid_withExpectedToken:
  158. expectedToken];
  159. // 2. Request token and verify result.
  160. [self.appCheck
  161. tokenForcingRefresh:YES
  162. completion:^(GACAppCheckToken *_Nullable token, NSError *_Nullable error) {
  163. [expectations.lastObject fulfill];
  164. XCTAssertNotNil(token);
  165. XCTAssertEqualObjects(token.token, expectedToken.token);
  166. XCTAssertNil(error);
  167. }];
  168. // 3. Wait for expectations and validate mocks.
  169. [self waitForExpectations:expectations timeout:0.5];
  170. [self verifyAllMocks];
  171. }
  172. - (void)testGetToken_WhenCachedTokenExpired_Success {
  173. // 1. Create expected token and configure expectations.
  174. GACAppCheckToken *expectedToken = [self validToken];
  175. NSArray * /*[tokenNotification, getToken]*/ expectations =
  176. [self configuredExpectations_GetTokenWhenCachedTokenExpired_withExpectedToken:expectedToken];
  177. // 2. Request token and verify result.
  178. [self.appCheck
  179. tokenForcingRefresh:NO
  180. completion:^(GACAppCheckToken *_Nullable token, NSError *_Nullable error) {
  181. [expectations.lastObject fulfill];
  182. XCTAssertNotNil(token);
  183. XCTAssertEqualObjects(token.token, expectedToken.token);
  184. XCTAssertNil(error);
  185. }];
  186. // 3. Wait for expectations and validate mocks.
  187. [self waitForExpectations:expectations timeout:0.5];
  188. [self verifyAllMocks];
  189. }
  190. - (void)testGetToken_AppCheckProviderError {
  191. // 1. Create expected token and error and configure expectations.
  192. GACAppCheckToken *cachedToken = [self soonExpiringToken];
  193. NSError *providerError = [NSError errorWithDomain:@"GACAppCheckTests" code:-1 userInfo:nil];
  194. NSArray * /*[tokenNotification, getToken]*/ expectations =
  195. [self configuredExpectations_GetTokenWhenError_withError:providerError andToken:cachedToken];
  196. // 2. Request token and verify result.
  197. [self.appCheck
  198. tokenForcingRefresh:NO
  199. completion:^(GACAppCheckToken *_Nullable token, NSError *_Nullable error) {
  200. [expectations.lastObject fulfill];
  201. XCTAssertNil(token);
  202. XCTAssertNotNil(error);
  203. XCTAssertNotEqualObjects(error, providerError);
  204. XCTAssertEqualObjects(error.domain, GACAppCheckErrorDomain);
  205. XCTAssertEqualObjects(error.userInfo[NSUnderlyingErrorKey], providerError);
  206. }];
  207. // 3. Wait for expectations and validate mocks.
  208. [self waitForExpectations:expectations timeout:0.5];
  209. [self verifyAllMocks];
  210. }
  211. - (void)testGetToken_ServerUnreachableError {
  212. // 1. Create expected error and configure expectations.
  213. NSError *serverError = [GACAppCheckErrorUtil APIErrorWithNetworkError:[self internalError]];
  214. NSArray * /*[tokenNotification, getToken]*/ expectations =
  215. [self configuredExpectations_GetTokenWhenError_withError:serverError andToken:nil];
  216. // 2. Request token and verify result.
  217. [self.appCheck
  218. tokenForcingRefresh:NO
  219. completion:^(GACAppCheckToken *_Nullable token, NSError *_Nullable error) {
  220. [expectations.lastObject fulfill];
  221. XCTAssertNil(token);
  222. XCTAssertNotNil(error);
  223. XCTAssertEqualObjects(error, serverError);
  224. XCTAssertEqualObjects(error.domain, GACAppCheckErrorDomain);
  225. }];
  226. // 3. Wait for expectations and validate mocks.
  227. [self waitForExpectations:expectations timeout:0.5];
  228. [self verifyAllMocks];
  229. }
  230. - (void)testGetToken_KeychainError {
  231. // 1. Expect token to be requested from storage.
  232. NSError *keychainError = [GACAppCheckErrorUtil keychainErrorWithError:[self internalError]];
  233. OCMExpect([self.mockStorage getToken]).andReturn([FBLPromise resolvedWith:keychainError]);
  234. // 2. Expect token requested from app check provider.
  235. GACAppCheckToken *expectedToken = [self validToken];
  236. id completionArg = [OCMArg invokeBlockWithArgs:expectedToken, [NSNull null], nil];
  237. OCMExpect([self.mockAppCheckProvider getTokenWithCompletion:completionArg]);
  238. // 3. Expect new token to be stored.
  239. OCMExpect([self.mockStorage setToken:expectedToken])
  240. .andReturn([FBLPromise resolvedWith:keychainError]);
  241. // 4. Don't expect token update notification to be sent.
  242. XCTestExpectation *notificationExpectation = [self tokenUpdateNotificationWithExpectedToken:@""
  243. isInverted:YES];
  244. // 5. Request token and verify result.
  245. XCTestExpectation *getTokenExpectation = [self expectationWithDescription:@"getToken"];
  246. [self.appCheck
  247. tokenForcingRefresh:NO
  248. completion:^(GACAppCheckToken *_Nullable token, NSError *_Nullable error) {
  249. [getTokenExpectation fulfill];
  250. XCTAssertNil(token);
  251. XCTAssertNotNil(error);
  252. XCTAssertEqualObjects(error, keychainError);
  253. XCTAssertEqualObjects(error.domain, GACAppCheckErrorDomain);
  254. }];
  255. // 3. Wait for expectations and validate mocks.
  256. [self waitForExpectations:@[ notificationExpectation, getTokenExpectation ] timeout:0.5];
  257. [self verifyAllMocks];
  258. }
  259. - (void)testGetToken_UnsupportedError {
  260. // 1. Create expected error and configure expectations.
  261. NSError *providerError =
  262. [GACAppCheckErrorUtil unsupportedAttestationProvider:@"AppAttestProvider"];
  263. NSArray * /*[tokenNotification, getToken]*/ expectations =
  264. [self configuredExpectations_GetTokenWhenError_withError:providerError andToken:nil];
  265. // 2. Request token and verify result.
  266. [self.appCheck
  267. tokenForcingRefresh:NO
  268. completion:^(GACAppCheckToken *_Nullable token, NSError *_Nullable error) {
  269. [expectations.lastObject fulfill];
  270. XCTAssertNil(token);
  271. XCTAssertNotNil(error);
  272. XCTAssertEqualObjects(error, providerError);
  273. XCTAssertEqualObjects(error.domain, GACAppCheckErrorDomain);
  274. }];
  275. // 3. Wait for expectations and validate mocks.
  276. [self waitForExpectations:expectations timeout:0.5];
  277. [self verifyAllMocks];
  278. }
  279. #pragma mark - GACAppCheckInterop Get Token
  280. - (void)testInteropGetToken_WhenNoCache_Success {
  281. // 1. Create expected token and configure expectations.
  282. GACAppCheckToken *expectedToken = [self validToken];
  283. NSArray * /*[tokenNotification, getToken]*/ expectations =
  284. [self configuredExpectations_GetTokenWhenNoCache_withExpectedToken:expectedToken];
  285. // 2. Request token and verify result.
  286. [self.appCheck getTokenForcingRefresh:NO
  287. completion:^(id<GACAppCheckTokenResultInterop> tokenResult) {
  288. [expectations.lastObject fulfill];
  289. XCTAssertNotNil(tokenResult);
  290. XCTAssertEqualObjects(tokenResult.token, expectedToken.token);
  291. XCTAssertNil(tokenResult.error);
  292. }];
  293. // 3. Wait for expectations and validate mocks.
  294. [self waitForExpectations:expectations timeout:0.5];
  295. [self verifyAllMocks];
  296. }
  297. - (void)testInteropGetToken_WhenCachedTokenIsValid_Success {
  298. [self assertInteropGetToken_WhenCachedTokenIsValid_Success];
  299. }
  300. - (void)testInteropGetTokenForcingRefresh_WhenCachedTokenIsValid_Success {
  301. // 1. Create expected token and configure expectations.
  302. GACAppCheckToken *expectedToken = [self validToken];
  303. NSArray * /*[tokenNotification, getToken]*/ expectations =
  304. [self configuredExpectations_GetTokenForcingRefreshWhenCacheIsValid_withExpectedToken:
  305. expectedToken];
  306. // 2. Request token and verify result.
  307. [self.appCheck getTokenForcingRefresh:YES
  308. completion:^(id<GACAppCheckTokenResultInterop> tokenResult) {
  309. [expectations.lastObject fulfill];
  310. XCTAssertNotNil(tokenResult);
  311. XCTAssertEqualObjects(tokenResult.token, expectedToken.token);
  312. XCTAssertNil(tokenResult.error);
  313. }];
  314. // 3. Wait for expectations and validate mocks.
  315. [self waitForExpectations:expectations timeout:0.5];
  316. [self verifyAllMocks];
  317. }
  318. - (void)testInteropGetToken_WhenCachedTokenExpired_Success {
  319. // 1. Create expected token and configure expectations.
  320. GACAppCheckToken *expectedToken = [self validToken];
  321. NSArray * /*[tokenNotification, getToken]*/ expectations =
  322. [self configuredExpectations_GetTokenWhenCachedTokenExpired_withExpectedToken:expectedToken];
  323. // 2. Request token and verify result.
  324. [self.appCheck getTokenForcingRefresh:NO
  325. completion:^(id<GACAppCheckTokenResultInterop> tokenResult) {
  326. [expectations.lastObject fulfill];
  327. XCTAssertNotNil(tokenResult);
  328. XCTAssertEqualObjects(tokenResult.token, expectedToken.token);
  329. XCTAssertNil(tokenResult.error);
  330. }];
  331. // 3. Wait for expectations and validate mocks.
  332. [self waitForExpectations:expectations timeout:0.5];
  333. [self verifyAllMocks];
  334. }
  335. - (void)testInteropGetToken_AppCheckProviderError {
  336. // 1. Create expected tokens and errors and configure expectations.
  337. GACAppCheckToken *cachedToken = [self soonExpiringToken];
  338. NSError *providerError = [NSError errorWithDomain:@"GACAppCheckTests" code:-1 userInfo:nil];
  339. NSArray * /*[tokenNotification, getToken]*/ expectations =
  340. [self configuredExpectations_GetTokenWhenError_withError:providerError andToken:cachedToken];
  341. // 2. Request token and verify result.
  342. [self.appCheck
  343. getTokenForcingRefresh:NO
  344. completion:^(id<GACAppCheckTokenResultInterop> result) {
  345. [expectations.lastObject fulfill];
  346. XCTAssertNotNil(result);
  347. XCTAssertEqualObjects(result.token, kDummyToken);
  348. XCTAssertEqualObjects(result.error, providerError);
  349. // Interop API does not wrap errors in public domain.
  350. XCTAssertNotEqualObjects(result.error.domain, GACAppCheckErrorDomain);
  351. }];
  352. // 3. Wait for expectations and validate mocks.
  353. [self waitForExpectations:expectations timeout:0.5];
  354. [self verifyAllMocks];
  355. }
  356. #pragma mark - Token refresher
  357. - (void)testTokenRefreshTriggeredAndRefreshSuccess {
  358. // 1. Expect token to be requested from storage.
  359. OCMExpect([self.mockStorage getToken]).andReturn([FBLPromise resolvedWith:nil]);
  360. // 2. Expect token requested from app check provider.
  361. NSDate *expirationDate = [NSDate dateWithTimeIntervalSinceNow:10000];
  362. GACAppCheckToken *tokenToReturn = [[GACAppCheckToken alloc] initWithToken:@"valid"
  363. expirationDate:expirationDate];
  364. id completionArg = [OCMArg invokeBlockWithArgs:tokenToReturn, [NSNull null], nil];
  365. OCMExpect([self.mockAppCheckProvider getTokenWithCompletion:completionArg]);
  366. // 3. Expect new token to be stored.
  367. OCMExpect([self.mockStorage setToken:tokenToReturn])
  368. .andReturn([FBLPromise resolvedWith:tokenToReturn]);
  369. OCMExpect([self.mockTokenRefresher updateWithRefreshResult:[OCMArg any]]);
  370. // 4. Expect token update notification to be sent.
  371. XCTestExpectation *notificationExpectation =
  372. [self tokenUpdateNotificationWithExpectedToken:tokenToReturn.token];
  373. // 5. Trigger refresh and expect the result.
  374. if (self.tokenRefreshHandler == nil) {
  375. XCTFail(@"`tokenRefreshHandler` must be not `nil`.");
  376. return;
  377. }
  378. XCTestExpectation *completionExpectation = [self expectationWithDescription:@"completion"];
  379. self.tokenRefreshHandler(^(GACAppCheckTokenRefreshResult *refreshResult) {
  380. [completionExpectation fulfill];
  381. XCTAssertEqualObjects(refreshResult.tokenExpirationDate, expirationDate);
  382. XCTAssertEqual(refreshResult.status, GACAppCheckTokenRefreshStatusSuccess);
  383. });
  384. [self waitForExpectations:@[ notificationExpectation, completionExpectation ] timeout:0.5];
  385. [self verifyAllMocks];
  386. }
  387. - (void)testTokenRefreshTriggeredAndRefreshError {
  388. // 1. Expect token to be requested from storage.
  389. OCMExpect([self.mockStorage getToken]).andReturn([FBLPromise resolvedWith:nil]);
  390. // 2. Expect token requested from app check provider.
  391. NSError *providerError = [self internalError];
  392. id completionArg = [OCMArg invokeBlockWithArgs:[NSNull null], providerError, nil];
  393. OCMExpect([self.mockAppCheckProvider getTokenWithCompletion:completionArg]);
  394. // 3. Don't expect token requested from app check provider.
  395. OCMReject([self.mockAppCheckProvider getTokenWithCompletion:[OCMArg any]]);
  396. // 4. Don't expect token update notification to be sent.
  397. XCTestExpectation *notificationExpectation = [self tokenUpdateNotificationWithExpectedToken:@""
  398. isInverted:YES];
  399. // 5. Trigger refresh and expect the result.
  400. if (self.tokenRefreshHandler == nil) {
  401. XCTFail(@"`tokenRefreshHandler` must be not `nil`.");
  402. return;
  403. }
  404. XCTestExpectation *completionExpectation = [self expectationWithDescription:@"completion"];
  405. self.tokenRefreshHandler(^(GACAppCheckTokenRefreshResult *refreshResult) {
  406. [completionExpectation fulfill];
  407. XCTAssertEqual(refreshResult.status, GACAppCheckTokenRefreshStatusFailure);
  408. XCTAssertNil(refreshResult.tokenExpirationDate);
  409. XCTAssertNil(refreshResult.tokenReceivedAtDate);
  410. });
  411. [self waitForExpectations:@[ notificationExpectation, completionExpectation ] timeout:0.5];
  412. [self verifyAllMocks];
  413. }
  414. - (void)testLimitedUseTokenWithSuccess {
  415. // 1. Don't expect token to be requested from storage.
  416. OCMReject([self.mockStorage getToken]);
  417. // 2. Expect token requested from app check provider.
  418. GACAppCheckToken *expectedToken = [self validToken];
  419. id completionArg = [OCMArg invokeBlockWithArgs:expectedToken, [NSNull null], nil];
  420. OCMExpect([self.mockAppCheckProvider getTokenWithCompletion:completionArg]);
  421. // 3. Don't expect token requested from storage.
  422. OCMReject([self.mockStorage setToken:expectedToken]);
  423. // 4. Don't expect token update notification to be sent.
  424. XCTestExpectation *notificationExpectation = [self tokenUpdateNotificationWithExpectedToken:@""
  425. isInverted:YES];
  426. // 5. Expect token request to be completed.
  427. XCTestExpectation *getTokenExpectation = [self expectationWithDescription:@"getToken"];
  428. [self.appCheck
  429. limitedUseTokenWithCompletion:^(GACAppCheckToken *_Nullable token, NSError *_Nullable error) {
  430. [getTokenExpectation fulfill];
  431. XCTAssertNotNil(token);
  432. XCTAssertEqualObjects(token.token, expectedToken.token);
  433. XCTAssertNil(error);
  434. }];
  435. [self waitForExpectations:@[ notificationExpectation, getTokenExpectation ] timeout:0.5];
  436. [self verifyAllMocks];
  437. }
  438. - (void)testLimitedUseToken_WhenTokenGenerationErrors {
  439. // 1. Don't expect token to be requested from storage.
  440. OCMReject([self.mockStorage getToken]);
  441. // 2. Expect error when requesting token from app check provider.
  442. NSError *providerError = [GACAppCheckErrorUtil keychainErrorWithError:[self internalError]];
  443. id completionArg = [OCMArg invokeBlockWithArgs:[NSNull null], providerError, nil];
  444. OCMExpect([self.mockAppCheckProvider getTokenWithCompletion:completionArg]);
  445. // 3. Don't expect token requested from app check provider.
  446. OCMReject([self.mockAppCheckProvider getTokenWithCompletion:[OCMArg any]]);
  447. // 4. Don't expect token update notification to be sent.
  448. XCTestExpectation *notificationExpectation = [self tokenUpdateNotificationWithExpectedToken:@""
  449. isInverted:YES];
  450. // 5. Expect token request to be completed.
  451. XCTestExpectation *getTokenExpectation = [self expectationWithDescription:@"getToken"];
  452. [self.appCheck
  453. limitedUseTokenWithCompletion:^(GACAppCheckToken *_Nullable token, NSError *_Nullable error) {
  454. [getTokenExpectation fulfill];
  455. XCTAssertNotNil(error);
  456. XCTAssertNil(token.token);
  457. XCTAssertEqualObjects(error, providerError);
  458. XCTAssertEqualObjects(error.domain, GACAppCheckErrorDomain);
  459. }];
  460. [self waitForExpectations:@[ notificationExpectation, getTokenExpectation ] timeout:0.5];
  461. [self verifyAllMocks];
  462. }
  463. #pragma mark - Token update notifications
  464. - (void)testTokenUpdateNotificationKeys {
  465. XCTAssertEqualObjects([self.appCheck tokenDidChangeNotificationName],
  466. @"GACAppCheckAppCheckTokenDidChangeNotification");
  467. XCTAssertEqualObjects([self.appCheck notificationInstanceNameKey],
  468. @"GACAppCheckInstanceNameNotificationKey");
  469. XCTAssertEqualObjects([self.appCheck notificationTokenKey], @"GACAppCheckTokenNotificationKey");
  470. }
  471. #pragma mark - Merging multiple get token requests
  472. - (void)testGetToken_WhenCalledSeveralTimesSuccess_ThenThereIsOnlyOneOperation {
  473. // 1. Expect a token to be requested and stored.
  474. NSArray * /*[expectedToken, storeTokenPromise]*/ expectedTokenAndPromise =
  475. [self expectTokenRequestFromAppCheckProvider];
  476. GACAppCheckToken *expectedToken = expectedTokenAndPromise.firstObject;
  477. FBLPromise *storeTokenPromise = expectedTokenAndPromise.lastObject;
  478. OCMExpect([self.mockTokenRefresher updateWithRefreshResult:[OCMArg any]]);
  479. // 2. Expect token update notification to be sent.
  480. XCTestExpectation *notificationExpectation =
  481. [self tokenUpdateNotificationWithExpectedToken:expectedToken.token];
  482. // 3. Request token several times.
  483. NSInteger getTokenCallsCount = 10;
  484. NSMutableArray *getTokenCompletionExpectations =
  485. [NSMutableArray arrayWithCapacity:getTokenCallsCount];
  486. for (NSInteger i = 0; i < getTokenCallsCount; i++) {
  487. // 3.1. Expect a completion to be called for each method call.
  488. XCTestExpectation *getTokenExpectation =
  489. [self expectationWithDescription:[NSString stringWithFormat:@"getToken%@", @(i)]];
  490. [getTokenCompletionExpectations addObject:getTokenExpectation];
  491. // 3.2. Request token and verify result.
  492. [self.appCheck
  493. tokenForcingRefresh:NO
  494. completion:^(GACAppCheckToken *_Nullable token, NSError *_Nullable error) {
  495. [getTokenExpectation fulfill];
  496. XCTAssertNotNil(token);
  497. XCTAssertEqualObjects(token.token, expectedToken.token);
  498. XCTAssertNil(error);
  499. }];
  500. }
  501. // 3.3. Fulfill the pending promise to finish the get token operation.
  502. [storeTokenPromise fulfill:expectedToken];
  503. // 4. Wait for expectations and validate mocks.
  504. NSArray *expectations =
  505. [getTokenCompletionExpectations arrayByAddingObject:notificationExpectation];
  506. [self waitForExpectations:expectations timeout:0.5];
  507. [self verifyAllMocks];
  508. // 5. Check a get token call after.
  509. [self assertGetToken_WhenCachedTokenIsValid_Success];
  510. }
  511. - (void)testGetToken_WhenCalledSeveralTimesError_ThenThereIsOnlyOneOperation {
  512. // 1. Expect a token to be requested and stored.
  513. NSArray * /*[expectedToken, storeTokenPromise]*/ expectedTokenAndPromise =
  514. [self expectTokenRequestFromAppCheckProvider];
  515. GACAppCheckToken *expectedToken = expectedTokenAndPromise.firstObject;
  516. FBLPromise *storeTokenPromise = expectedTokenAndPromise.lastObject;
  517. // 1.1. Create an expected error to be rejected with later.
  518. NSError *storageError = [NSError errorWithDomain:self.name code:0 userInfo:nil];
  519. // 2. Don't expect token update notification to be sent.
  520. XCTestExpectation *notificationExpectation =
  521. [self tokenUpdateNotificationWithExpectedToken:expectedToken.token isInverted:YES];
  522. // 3. Request token several times.
  523. NSInteger getTokenCallsCount = 10;
  524. NSMutableArray *getTokenCompletionExpectations =
  525. [NSMutableArray arrayWithCapacity:getTokenCallsCount];
  526. for (NSInteger i = 0; i < getTokenCallsCount; i++) {
  527. // 3.1. Expect a completion to be called for each method call.
  528. XCTestExpectation *getTokenExpectation =
  529. [self expectationWithDescription:[NSString stringWithFormat:@"getToken%@", @(i)]];
  530. [getTokenCompletionExpectations addObject:getTokenExpectation];
  531. // 3.2. Request token and verify result.
  532. [self.appCheck
  533. tokenForcingRefresh:NO
  534. completion:^(GACAppCheckToken *_Nullable token, NSError *_Nullable error) {
  535. [getTokenExpectation fulfill];
  536. XCTAssertNil(token);
  537. XCTAssertNotNil(error);
  538. XCTAssertNotEqualObjects(error, storageError);
  539. XCTAssertEqualObjects(error.domain, GACAppCheckErrorDomain);
  540. XCTAssertEqualObjects(error.userInfo[NSUnderlyingErrorKey], storageError);
  541. }];
  542. }
  543. // 3.3. Reject the pending promise to finish the get token operation.
  544. [storeTokenPromise reject:storageError];
  545. // 4. Wait for expectations and validate mocks.
  546. NSArray *expectations =
  547. [getTokenCompletionExpectations arrayByAddingObject:notificationExpectation];
  548. [self waitForExpectations:expectations timeout:0.5];
  549. [self verifyAllMocks];
  550. // 5. Check a get token call after.
  551. [self assertGetToken_WhenCachedTokenIsValid_Success];
  552. }
  553. - (void)testInteropGetToken_WhenCalledSeveralTimesSuccess_ThenThereIsOnlyOneOperation {
  554. // 1. Expect a token to be requested and stored.
  555. NSArray * /*[expectedToken, storeTokenPromise]*/ expectedTokenAndPromise =
  556. [self expectTokenRequestFromAppCheckProvider];
  557. GACAppCheckToken *expectedToken = expectedTokenAndPromise.firstObject;
  558. FBLPromise *storeTokenPromise = expectedTokenAndPromise.lastObject;
  559. OCMExpect([self.mockTokenRefresher updateWithRefreshResult:[OCMArg any]]);
  560. // 2. Expect token update notification to be sent.
  561. XCTestExpectation *notificationExpectation =
  562. [self tokenUpdateNotificationWithExpectedToken:expectedToken.token];
  563. // 3. Request token several times.
  564. NSInteger getTokenCallsCount = 10;
  565. NSMutableArray *getTokenCompletionExpectations =
  566. [NSMutableArray arrayWithCapacity:getTokenCallsCount];
  567. for (NSInteger i = 0; i < getTokenCallsCount; i++) {
  568. // 3.1. Expect a completion to be called for each method call.
  569. XCTestExpectation *getTokenExpectation =
  570. [self expectationWithDescription:[NSString stringWithFormat:@"getToken%@", @(i)]];
  571. [getTokenCompletionExpectations addObject:getTokenExpectation];
  572. // 3.2. Request token and verify result.
  573. [self.appCheck getTokenForcingRefresh:NO
  574. completion:^(id<GACAppCheckTokenResultInterop> tokenResult) {
  575. [getTokenExpectation fulfill];
  576. XCTAssertNotNil(tokenResult);
  577. XCTAssertEqualObjects(tokenResult.token, expectedToken.token);
  578. XCTAssertNil(tokenResult.error);
  579. }];
  580. }
  581. // 3.3. Fulfill the pending promise to finish the get token operation.
  582. [storeTokenPromise fulfill:expectedToken];
  583. // 4. Wait for expectations and validate mocks.
  584. NSArray *expectations =
  585. [getTokenCompletionExpectations arrayByAddingObject:notificationExpectation];
  586. [self waitForExpectations:expectations timeout:0.5];
  587. [self verifyAllMocks];
  588. // 5. Check a get token call after.
  589. [self assertInteropGetToken_WhenCachedTokenIsValid_Success];
  590. }
  591. - (void)testInteropGetToken_WhenCalledSeveralTimesError_ThenThereIsOnlyOneOperation {
  592. // 1. Expect a token to be requested and stored.
  593. NSArray * /*[expectedToken, storeTokenPromise]*/ expectedTokenAndPromise =
  594. [self expectTokenRequestFromAppCheckProvider];
  595. GACAppCheckToken *expectedToken = expectedTokenAndPromise.firstObject;
  596. FBLPromise *storeTokenPromise = expectedTokenAndPromise.lastObject;
  597. // 1.1. Create an expected error to be reject the store token promise with later.
  598. NSError *storageError = [NSError errorWithDomain:self.name code:0 userInfo:nil];
  599. // 2. Don't expect token update notification to be sent.
  600. XCTestExpectation *notificationExpectation =
  601. [self tokenUpdateNotificationWithExpectedToken:expectedToken.token isInverted:YES];
  602. // 3. Request token several times.
  603. NSInteger getTokenCallsCount = 10;
  604. NSMutableArray *getTokenCompletionExpectations =
  605. [NSMutableArray arrayWithCapacity:getTokenCallsCount];
  606. for (NSInteger i = 0; i < getTokenCallsCount; i++) {
  607. // 3.1. Expect a completion to be called for each method call.
  608. XCTestExpectation *getTokenExpectation =
  609. [self expectationWithDescription:[NSString stringWithFormat:@"getToken%@", @(i)]];
  610. [getTokenCompletionExpectations addObject:getTokenExpectation];
  611. // 3.2. Request token and verify result.
  612. [self.appCheck getTokenForcingRefresh:NO
  613. completion:^(id<GACAppCheckTokenResultInterop> tokenResult) {
  614. [getTokenExpectation fulfill];
  615. XCTAssertNotNil(tokenResult);
  616. XCTAssertEqualObjects(tokenResult.error, storageError);
  617. XCTAssertEqualObjects(tokenResult.token, kDummyToken);
  618. }];
  619. }
  620. // 3.3. Reject the pending promise to finish the get token operation.
  621. [storeTokenPromise reject:storageError];
  622. // 4. Wait for expectations and validate mocks.
  623. NSArray *expectations =
  624. [getTokenCompletionExpectations arrayByAddingObject:notificationExpectation];
  625. [self waitForExpectations:expectations timeout:0.5];
  626. [self verifyAllMocks];
  627. // 5. Check a get token call after.
  628. [self assertInteropGetToken_WhenCachedTokenIsValid_Success];
  629. }
  630. #pragma mark - Helpers
  631. - (NSError *)internalError {
  632. return [NSError errorWithDomain:@"com.internal.error" code:-1 userInfo:nil];
  633. }
  634. - (GACAppCheckToken *)validToken {
  635. return [[GACAppCheckToken alloc] initWithToken:[NSUUID UUID].UUIDString
  636. expirationDate:[NSDate distantFuture]];
  637. }
  638. - (GACAppCheckToken *)soonExpiringToken {
  639. NSDate *soonExpiringTokenDate = [NSDate dateWithTimeIntervalSinceNow:4.5 * 60];
  640. return [[GACAppCheckToken alloc] initWithToken:@"valid" expirationDate:soonExpiringTokenDate];
  641. }
  642. - (void)stubSetTokenRefreshHandler {
  643. id arg = [OCMArg checkWithBlock:^BOOL(id handler) {
  644. self.tokenRefreshHandler = handler;
  645. return YES;
  646. }];
  647. OCMExpect([self.mockTokenRefresher setTokenRefreshHandler:arg]);
  648. }
  649. - (XCTestExpectation *)tokenUpdateNotificationWithExpectedToken:(NSString *)expectedToken {
  650. return [self tokenUpdateNotificationWithExpectedToken:expectedToken isInverted:NO];
  651. }
  652. - (XCTestExpectation *)tokenUpdateNotificationWithExpectedToken:(NSString *)expectedToken
  653. isInverted:(BOOL)isInverted {
  654. XCTestExpectation *expectation = [self
  655. expectationForNotification:[self.appCheck tokenDidChangeNotificationName]
  656. object:nil
  657. notificationCenter:self.notificationCenter
  658. handler:^BOOL(NSNotification *_Nonnull notification) {
  659. XCTAssertEqualObjects(
  660. notification.userInfo[[self.appCheck notificationInstanceNameKey]],
  661. kAppName);
  662. XCTAssertEqualObjects(
  663. notification.userInfo[[self.appCheck notificationTokenKey]],
  664. expectedToken);
  665. XCTAssertEqualObjects(notification.object, self.appCheck);
  666. return YES;
  667. }];
  668. expectation.inverted = isInverted;
  669. return expectation;
  670. }
  671. - (void)assertGetToken_WhenCachedTokenIsValid_Success {
  672. // 1. Create expected token and configure expectations.
  673. GACAppCheckToken *cachedToken = [self validToken];
  674. NSArray * /*[tokenNotification, getToken]*/ expectations =
  675. [self configuredExpectations_GetTokenWhenCacheTokenIsValid_withExpectedToken:cachedToken];
  676. // 2. Request token and verify result.
  677. [self.appCheck
  678. tokenForcingRefresh:NO
  679. completion:^(GACAppCheckToken *_Nullable token, NSError *_Nullable error) {
  680. [expectations.lastObject fulfill];
  681. XCTAssertNotNil(token);
  682. XCTAssertEqualObjects(token.token, cachedToken.token);
  683. XCTAssertNil(error);
  684. }];
  685. // 3. Wait for expectations and validate mocks.
  686. [self waitForExpectations:expectations timeout:0.5];
  687. [self verifyAllMocks];
  688. }
  689. - (void)assertInteropGetToken_WhenCachedTokenIsValid_Success {
  690. // 1. Create expected token and configure expectations.
  691. GACAppCheckToken *cachedToken = [self validToken];
  692. NSArray * /*[tokenNotification, getToken]*/ expectations =
  693. [self configuredExpectations_GetTokenWhenCacheTokenIsValid_withExpectedToken:cachedToken];
  694. // 2. Request token and verify result.
  695. [self.appCheck getTokenForcingRefresh:NO
  696. completion:^(id<GACAppCheckTokenResultInterop> tokenResult) {
  697. [expectations.lastObject fulfill];
  698. XCTAssertNotNil(tokenResult);
  699. XCTAssertEqualObjects(tokenResult.token, cachedToken.token);
  700. XCTAssertNil(tokenResult.error);
  701. }];
  702. // 3. Wait for expectations and validate mocks.
  703. [self waitForExpectations:expectations timeout:0.5];
  704. [self verifyAllMocks];
  705. }
  706. - (NSArray<XCTestExpectation *> *)configuredExpectations_GetTokenWhenNoCache_withExpectedToken:
  707. (GACAppCheckToken *)expectedToken {
  708. // 1. Expect token to be requested from storage.
  709. OCMExpect([self.mockStorage getToken]).andReturn([FBLPromise resolvedWith:nil]);
  710. // 2. Expect token requested from app check provider.
  711. id completionArg = [OCMArg invokeBlockWithArgs:expectedToken, [NSNull null], nil];
  712. OCMExpect([self.mockAppCheckProvider getTokenWithCompletion:completionArg]);
  713. // 3. Expect new token to be stored.
  714. OCMExpect([self.mockStorage setToken:expectedToken])
  715. .andReturn([FBLPromise resolvedWith:expectedToken]);
  716. OCMExpect([self.mockTokenRefresher updateWithRefreshResult:[OCMArg any]]);
  717. // 4. Expect token update notification to be sent.
  718. XCTestExpectation *tokenNotificationExpectation =
  719. [self tokenUpdateNotificationWithExpectedToken:expectedToken.token];
  720. // 5. Expect token request to be completed.
  721. XCTestExpectation *getTokenExpectation = [self expectationWithDescription:@"getToken"];
  722. return @[ tokenNotificationExpectation, getTokenExpectation ];
  723. }
  724. - (NSArray<XCTestExpectation *> *)
  725. configuredExpectations_GetTokenWhenCacheTokenIsValid_withExpectedToken:
  726. (GACAppCheckToken *)expectedToken {
  727. // 1. Expect token to be requested from storage.
  728. OCMExpect([self.mockStorage getToken]).andReturn([FBLPromise resolvedWith:expectedToken]);
  729. // 2. Don't expect token requested from app check provider.
  730. OCMReject([self.mockAppCheckProvider getTokenWithCompletion:[OCMArg any]]);
  731. // 3. Don't expect token update notification to be sent.
  732. XCTestExpectation *tokenNotificationExpectation =
  733. [self tokenUpdateNotificationWithExpectedToken:@"" isInverted:YES];
  734. // 4. Expect token request to be completed.
  735. XCTestExpectation *getTokenExpectation = [self expectationWithDescription:@"getToken"];
  736. return @[ tokenNotificationExpectation, getTokenExpectation ];
  737. }
  738. - (NSArray<XCTestExpectation *> *)
  739. configuredExpectations_GetTokenForcingRefreshWhenCacheIsValid_withExpectedToken:
  740. (GACAppCheckToken *)expectedToken {
  741. // 1. Don't expect token to be requested from storage.
  742. OCMReject([self.mockStorage getToken]);
  743. // 2. Expect token requested from app check provider.
  744. id completionArg = [OCMArg invokeBlockWithArgs:expectedToken, [NSNull null], nil];
  745. OCMExpect([self.mockAppCheckProvider getTokenWithCompletion:completionArg]);
  746. // 3. Expect new token to be stored.
  747. OCMExpect([self.mockStorage setToken:expectedToken])
  748. .andReturn([FBLPromise resolvedWith:expectedToken]);
  749. OCMExpect([self.mockTokenRefresher updateWithRefreshResult:[OCMArg any]]);
  750. // 4. Expect token update notification to be sent.
  751. XCTestExpectation *notificationExpectation =
  752. [self tokenUpdateNotificationWithExpectedToken:expectedToken.token];
  753. // 5. Expect token request to be completed.
  754. XCTestExpectation *getTokenExpectation = [self expectationWithDescription:@"getToken"];
  755. return @[ notificationExpectation, getTokenExpectation ];
  756. }
  757. - (NSArray<XCTestExpectation *> *)
  758. configuredExpectations_GetTokenWhenCachedTokenExpired_withExpectedToken:
  759. (GACAppCheckToken *)expectedToken {
  760. // 1. Expect token to be requested from storage.
  761. GACAppCheckToken *cachedToken = [[GACAppCheckToken alloc] initWithToken:@"expired"
  762. expirationDate:[NSDate date]];
  763. OCMExpect([self.mockStorage getToken]).andReturn([FBLPromise resolvedWith:cachedToken]);
  764. // 2. Expect token requested from app check provider.
  765. id completionArg = [OCMArg invokeBlockWithArgs:expectedToken, [NSNull null], nil];
  766. OCMExpect([self.mockAppCheckProvider getTokenWithCompletion:completionArg]);
  767. // 3. Expect new token to be stored.
  768. OCMExpect([self.mockStorage setToken:expectedToken])
  769. .andReturn([FBLPromise resolvedWith:expectedToken]);
  770. OCMExpect([self.mockTokenRefresher updateWithRefreshResult:[OCMArg any]]);
  771. // 4. Expect token update notification to be sent.
  772. XCTestExpectation *notificationExpectation =
  773. [self tokenUpdateNotificationWithExpectedToken:expectedToken.token];
  774. // 5. Expect token request to be completed.
  775. XCTestExpectation *getTokenExpectation = [self expectationWithDescription:@"getToken"];
  776. return @[ notificationExpectation, getTokenExpectation ];
  777. }
  778. - (NSArray<XCTestExpectation *> *)
  779. configuredExpectations_GetTokenWhenError_withError:(NSError *_Nonnull)error
  780. andToken:(GACAppCheckToken *_Nullable)token {
  781. // 1. Expect token to be requested from storage.
  782. OCMExpect([self.mockStorage getToken]).andReturn([FBLPromise resolvedWith:token]);
  783. // 2. Expect token requested from app check provider.
  784. id completionArg = [OCMArg invokeBlockWithArgs:[NSNull null], error, nil];
  785. OCMExpect([self.mockAppCheckProvider getTokenWithCompletion:completionArg]);
  786. // 3. Don't expect token requested from app check provider.
  787. OCMReject([self.mockAppCheckProvider getTokenWithCompletion:[OCMArg any]]);
  788. // 4. Expect token update notification to be sent.
  789. XCTestExpectation *notificationExpectation = [self tokenUpdateNotificationWithExpectedToken:@""
  790. isInverted:YES];
  791. // 5. Expect token request to be completed.
  792. XCTestExpectation *getTokenExpectation = [self expectationWithDescription:@"getToken"];
  793. return @[ notificationExpectation, getTokenExpectation ];
  794. }
  795. - (NSArray *)expectTokenRequestFromAppCheckProvider {
  796. // 1. Expect token to be requested from storage.
  797. OCMExpect([self.mockStorage getToken]).andReturn([FBLPromise resolvedWith:nil]);
  798. // 2. Expect token requested from app check provider.
  799. GACAppCheckToken *expectedToken = [self validToken];
  800. id completionArg = [OCMArg invokeBlockWithArgs:expectedToken, [NSNull null], nil];
  801. OCMExpect([self.mockAppCheckProvider getTokenWithCompletion:completionArg]);
  802. // 3. Expect new token to be stored.
  803. // 3.1. Create a pending promise to resolve later.
  804. FBLPromise<GACAppCheckToken *> *storeTokenPromise = [FBLPromise pendingPromise];
  805. // 3.2. Stub storage set token method.
  806. OCMExpect([self.mockStorage setToken:expectedToken]).andReturn(storeTokenPromise);
  807. return @[ expectedToken, storeTokenPromise ];
  808. }
  809. - (void)verifyAllMocks {
  810. OCMVerifyAll(self.mockAppCheckProvider);
  811. OCMVerifyAll(self.mockStorage);
  812. OCMVerifyAll(self.mockSettings);
  813. OCMVerifyAll(self.mockTokenRefresher);
  814. }
  815. @end
  816. #endif // GAC_DEVICE_CHECK_SUPPORTED_TARGETS