FIRAppCheckTests.m 45 KB

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