FIRAppCheckTests.m 42 KB

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