FIRAppCheckTests.m 45 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070
  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 FirebaseAppCheckInterop;
  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/Public/FirebaseAppCheck/FIRAppCheckProviderFactory.h"
  24. #import "FirebaseAppCheck/Sources/Core/Errors/FIRAppCheckErrorUtil.h"
  25. #import "FirebaseAppCheck/Sources/Core/FIRAppCheckSettings.h"
  26. #import "FirebaseAppCheck/Sources/Core/FIRAppCheckToken+Internal.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. // 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. - (void)testLimitedUseTokenWithSuccess {
  449. // 1. Don't expect token to be requested from storage.
  450. OCMReject([self.mockStorage getToken]);
  451. // 2. Expect token requested from app check provider.
  452. FIRAppCheckToken *expectedToken = [self validToken];
  453. id completionArg = [OCMArg invokeBlockWithArgs:expectedToken, [NSNull null], nil];
  454. OCMExpect([self.mockAppCheckProvider getTokenWithCompletion:completionArg]);
  455. // 3. Don't expect token requested from storage.
  456. OCMReject([self.mockStorage setToken:expectedToken]);
  457. // 4. Don't expect token update notification to be sent.
  458. XCTestExpectation *notificationExpectation = [self tokenUpdateNotificationWithExpectedToken:@""
  459. isInverted:YES];
  460. // 5. Expect token request to be completed.
  461. XCTestExpectation *getTokenExpectation = [self expectationWithDescription:@"getToken"];
  462. [self.appCheck
  463. limitedUseTokenWithCompletion:^(FIRAppCheckToken *_Nullable token, NSError *_Nullable error) {
  464. [getTokenExpectation fulfill];
  465. XCTAssertNotNil(token);
  466. XCTAssertEqualObjects(token.token, expectedToken.token);
  467. XCTAssertNil(error);
  468. }];
  469. [self waitForExpectations:@[ notificationExpectation, getTokenExpectation ] timeout:0.5];
  470. [self verifyAllMocks];
  471. }
  472. - (void)testLimitedUseToken_WhenTokenGenerationErrors {
  473. // 1. Don't expect token to be requested from storage.
  474. OCMReject([self.mockStorage getToken]);
  475. // 2. Expect error when requesting token from app check provider.
  476. NSError *providerError = [FIRAppCheckErrorUtil keychainErrorWithError:[self internalError]];
  477. id completionArg = [OCMArg invokeBlockWithArgs:[NSNull null], providerError, nil];
  478. OCMExpect([self.mockAppCheckProvider getTokenWithCompletion:completionArg]);
  479. // 3. Don't expect token requested from app check provider.
  480. OCMReject([self.mockAppCheckProvider getTokenWithCompletion:[OCMArg any]]);
  481. // 4. Don't expect token update notification to be sent.
  482. XCTestExpectation *notificationExpectation = [self tokenUpdateNotificationWithExpectedToken:@""
  483. isInverted:YES];
  484. // 5. Expect token request to be completed.
  485. XCTestExpectation *getTokenExpectation = [self expectationWithDescription:@"getToken"];
  486. [self.appCheck
  487. limitedUseTokenWithCompletion:^(FIRAppCheckToken *_Nullable token, NSError *_Nullable error) {
  488. [getTokenExpectation fulfill];
  489. XCTAssertNotNil(error);
  490. XCTAssertNil(token.token);
  491. XCTAssertEqualObjects(error, providerError);
  492. XCTAssertEqualObjects(error.domain, FIRAppCheckErrorDomain);
  493. }];
  494. [self waitForExpectations:@[ notificationExpectation, getTokenExpectation ] timeout:0.5];
  495. [self verifyAllMocks];
  496. }
  497. #pragma mark - Token update notifications
  498. - (void)testTokenUpdateNotificationKeys {
  499. XCTAssertEqualObjects([self.appCheck tokenDidChangeNotificationName],
  500. @"FIRAppCheckAppCheckTokenDidChangeNotification");
  501. XCTAssertEqualObjects([self.appCheck notificationAppNameKey],
  502. @"FIRAppCheckAppNameNotificationKey");
  503. XCTAssertEqualObjects([self.appCheck notificationTokenKey], @"FIRAppCheckTokenNotificationKey");
  504. }
  505. #pragma mark - Auto-refresh enabled
  506. - (void)testIsTokenAutoRefreshEnabled {
  507. // Expect value from settings to be used.
  508. [[[self.mockSettings expect] andReturnValue:@(NO)] isTokenAutoRefreshEnabled];
  509. XCTAssertFalse(self.appCheck.isTokenAutoRefreshEnabled);
  510. [[[self.mockSettings expect] andReturnValue:@(YES)] isTokenAutoRefreshEnabled];
  511. XCTAssertTrue(self.appCheck.isTokenAutoRefreshEnabled);
  512. OCMVerifyAll(self.mockSettings);
  513. }
  514. - (void)testSetIsTokenAutoRefreshEnabled {
  515. OCMExpect([self.mockSettings setIsTokenAutoRefreshEnabled:YES]);
  516. self.appCheck.isTokenAutoRefreshEnabled = YES;
  517. OCMExpect([self.mockSettings setIsTokenAutoRefreshEnabled:NO]);
  518. self.appCheck.isTokenAutoRefreshEnabled = NO;
  519. OCMVerifyAll(self.mockSettings);
  520. }
  521. #pragma mark - Merging multiple get token requests
  522. - (void)testGetToken_WhenCalledSeveralTimesSuccess_ThenThereIsOnlyOneOperation {
  523. // 1. Expect a token to be requested and stored.
  524. NSArray * /*[expectedToken, storeTokenPromise]*/ expectedTokenAndPromise =
  525. [self expectTokenRequestFromAppCheckProvider];
  526. FIRAppCheckToken *expectedToken = expectedTokenAndPromise.firstObject;
  527. FBLPromise *storeTokenPromise = expectedTokenAndPromise.lastObject;
  528. // 2. Expect token update notification to be sent.
  529. XCTestExpectation *notificationExpectation =
  530. [self tokenUpdateNotificationWithExpectedToken:expectedToken.token];
  531. // 3. Request token several times.
  532. NSInteger getTokenCallsCount = 10;
  533. NSMutableArray *getTokenCompletionExpectations =
  534. [NSMutableArray arrayWithCapacity:getTokenCallsCount];
  535. for (NSInteger i = 0; i < getTokenCallsCount; i++) {
  536. // 3.1. Expect a completion to be called for each method call.
  537. XCTestExpectation *getTokenExpectation =
  538. [self expectationWithDescription:[NSString stringWithFormat:@"getToken%@", @(i)]];
  539. [getTokenCompletionExpectations addObject:getTokenExpectation];
  540. // 3.2. Request token and verify result.
  541. [self.appCheck
  542. tokenForcingRefresh:NO
  543. completion:^(FIRAppCheckToken *_Nullable token, NSError *_Nullable error) {
  544. [getTokenExpectation fulfill];
  545. XCTAssertNotNil(token);
  546. XCTAssertEqualObjects(token.token, expectedToken.token);
  547. XCTAssertNil(error);
  548. }];
  549. }
  550. // 3.3. Fulfill the pending promise to finish the get token operation.
  551. [storeTokenPromise fulfill:expectedToken];
  552. // 4. Wait for expectations and validate mocks.
  553. NSArray *expectations =
  554. [getTokenCompletionExpectations arrayByAddingObject:notificationExpectation];
  555. [self waitForExpectations:expectations timeout:0.5];
  556. [self verifyAllMocks];
  557. // 5. Check a get token call after.
  558. [self assertGetToken_WhenCachedTokenIsValid_Success];
  559. }
  560. - (void)testGetToken_WhenCalledSeveralTimesError_ThenThereIsOnlyOneOperation {
  561. // 1. Expect a token to be requested and stored.
  562. NSArray * /*[expectedToken, storeTokenPromise]*/ expectedTokenAndPromise =
  563. [self expectTokenRequestFromAppCheckProvider];
  564. FIRAppCheckToken *expectedToken = expectedTokenAndPromise.firstObject;
  565. FBLPromise *storeTokenPromise = expectedTokenAndPromise.lastObject;
  566. // 1.1. Create an expected error to be rejected with later.
  567. NSError *storageError = [NSError errorWithDomain:self.name code:0 userInfo:nil];
  568. // 2. Don't expect token update notification to be sent.
  569. XCTestExpectation *notificationExpectation =
  570. [self tokenUpdateNotificationWithExpectedToken:expectedToken.token isInverted:YES];
  571. // 3. Request token several times.
  572. NSInteger getTokenCallsCount = 10;
  573. NSMutableArray *getTokenCompletionExpectations =
  574. [NSMutableArray arrayWithCapacity:getTokenCallsCount];
  575. for (NSInteger i = 0; i < getTokenCallsCount; i++) {
  576. // 3.1. Expect a completion to be called for each method call.
  577. XCTestExpectation *getTokenExpectation =
  578. [self expectationWithDescription:[NSString stringWithFormat:@"getToken%@", @(i)]];
  579. [getTokenCompletionExpectations addObject:getTokenExpectation];
  580. // 3.2. Request token and verify result.
  581. [self.appCheck
  582. tokenForcingRefresh:NO
  583. completion:^(FIRAppCheckToken *_Nullable token, NSError *_Nullable error) {
  584. [getTokenExpectation fulfill];
  585. XCTAssertNil(token);
  586. XCTAssertNotNil(error);
  587. XCTAssertNotEqualObjects(error, storageError);
  588. XCTAssertEqualObjects(error.domain, FIRAppCheckErrorDomain);
  589. XCTAssertEqualObjects(error.userInfo[NSUnderlyingErrorKey], storageError);
  590. }];
  591. }
  592. // 3.3. Reject the pending promise to finish the get token operation.
  593. [storeTokenPromise reject:storageError];
  594. // 4. Wait for expectations and validate mocks.
  595. NSArray *expectations =
  596. [getTokenCompletionExpectations arrayByAddingObject:notificationExpectation];
  597. [self waitForExpectations:expectations timeout:0.5];
  598. [self verifyAllMocks];
  599. // 5. Check a get token call after.
  600. [self assertGetToken_WhenCachedTokenIsValid_Success];
  601. }
  602. - (void)testInteropGetToken_WhenCalledSeveralTimesSuccess_ThenThereIsOnlyOneOperation {
  603. // 1. Expect a token to be requested and stored.
  604. NSArray * /*[expectedToken, storeTokenPromise]*/ expectedTokenAndPromise =
  605. [self expectTokenRequestFromAppCheckProvider];
  606. FIRAppCheckToken *expectedToken = expectedTokenAndPromise.firstObject;
  607. FBLPromise *storeTokenPromise = expectedTokenAndPromise.lastObject;
  608. // 2. Expect token update notification to be sent.
  609. XCTestExpectation *notificationExpectation =
  610. [self tokenUpdateNotificationWithExpectedToken:expectedToken.token];
  611. // 3. Request token several times.
  612. NSInteger getTokenCallsCount = 10;
  613. NSMutableArray *getTokenCompletionExpectations =
  614. [NSMutableArray arrayWithCapacity:getTokenCallsCount];
  615. for (NSInteger i = 0; i < getTokenCallsCount; i++) {
  616. // 3.1. Expect a completion to be called for each method call.
  617. XCTestExpectation *getTokenExpectation =
  618. [self expectationWithDescription:[NSString stringWithFormat:@"getToken%@", @(i)]];
  619. [getTokenCompletionExpectations addObject:getTokenExpectation];
  620. // 3.2. Request token and verify result.
  621. [self.appCheck getTokenForcingRefresh:NO
  622. completion:^(id<FIRAppCheckTokenResultInterop> tokenResult) {
  623. [getTokenExpectation fulfill];
  624. XCTAssertNotNil(tokenResult);
  625. XCTAssertEqualObjects(tokenResult.token, expectedToken.token);
  626. XCTAssertNil(tokenResult.error);
  627. }];
  628. }
  629. // 3.3. Fulfill the pending promise to finish the get token operation.
  630. [storeTokenPromise fulfill:expectedToken];
  631. // 4. Wait for expectations and validate mocks.
  632. NSArray *expectations =
  633. [getTokenCompletionExpectations arrayByAddingObject:notificationExpectation];
  634. [self waitForExpectations:expectations timeout:0.5];
  635. [self verifyAllMocks];
  636. // 5. Check a get token call after.
  637. [self assertInteropGetToken_WhenCachedTokenIsValid_Success];
  638. }
  639. - (void)testInteropGetToken_WhenCalledSeveralTimesError_ThenThereIsOnlyOneOperation {
  640. // 1. Expect a token to be requested and stored.
  641. NSArray * /*[expectedToken, storeTokenPromise]*/ expectedTokenAndPromise =
  642. [self expectTokenRequestFromAppCheckProvider];
  643. FIRAppCheckToken *expectedToken = expectedTokenAndPromise.firstObject;
  644. FBLPromise *storeTokenPromise = expectedTokenAndPromise.lastObject;
  645. // 1.1. Create an expected error to be reject the store token promise with later.
  646. NSError *storageError = [NSError errorWithDomain:self.name code:0 userInfo:nil];
  647. // 2. Don't expect token update notification to be sent.
  648. XCTestExpectation *notificationExpectation =
  649. [self tokenUpdateNotificationWithExpectedToken:expectedToken.token isInverted:YES];
  650. // 3. Request token several times.
  651. NSInteger getTokenCallsCount = 10;
  652. NSMutableArray *getTokenCompletionExpectations =
  653. [NSMutableArray arrayWithCapacity:getTokenCallsCount];
  654. for (NSInteger i = 0; i < getTokenCallsCount; i++) {
  655. // 3.1. Expect a completion to be called for each method call.
  656. XCTestExpectation *getTokenExpectation =
  657. [self expectationWithDescription:[NSString stringWithFormat:@"getToken%@", @(i)]];
  658. [getTokenCompletionExpectations addObject:getTokenExpectation];
  659. // 3.2. Request token and verify result.
  660. [self.appCheck getTokenForcingRefresh:NO
  661. completion:^(id<FIRAppCheckTokenResultInterop> tokenResult) {
  662. [getTokenExpectation fulfill];
  663. XCTAssertNotNil(tokenResult);
  664. XCTAssertEqualObjects(tokenResult.error, storageError);
  665. XCTAssertEqualObjects(tokenResult.token, kDummyToken);
  666. }];
  667. }
  668. // 3.3. Reject the pending promise to finish the get token operation.
  669. [storeTokenPromise reject:storageError];
  670. // 4. Wait for expectations and validate mocks.
  671. NSArray *expectations =
  672. [getTokenCompletionExpectations arrayByAddingObject:notificationExpectation];
  673. [self waitForExpectations:expectations timeout:0.5];
  674. [self verifyAllMocks];
  675. // 5. Check a get token call after.
  676. [self assertInteropGetToken_WhenCachedTokenIsValid_Success];
  677. }
  678. #pragma mark - Helpers
  679. - (NSError *)internalError {
  680. return [NSError errorWithDomain:@"com.internal.error" code:-1 userInfo:nil];
  681. }
  682. - (FIRAppCheckToken *)validToken {
  683. return [[FIRAppCheckToken alloc] initWithToken:[NSUUID UUID].UUIDString
  684. expirationDate:[NSDate distantFuture]];
  685. }
  686. - (FIRAppCheckToken *)soonExpiringToken {
  687. NSDate *soonExpiringTokenDate = [NSDate dateWithTimeIntervalSinceNow:4.5 * 60];
  688. return [[FIRAppCheckToken alloc] initWithToken:@"valid" expirationDate:soonExpiringTokenDate];
  689. }
  690. - (void)stubSetTokenRefreshHandler {
  691. id arg = [OCMArg checkWithBlock:^BOOL(id handler) {
  692. self.tokenRefreshHandler = handler;
  693. return YES;
  694. }];
  695. OCMExpect([self.mockTokenRefresher setTokenRefreshHandler:arg]);
  696. }
  697. - (XCTestExpectation *)tokenUpdateNotificationWithExpectedToken:(NSString *)expectedToken {
  698. return [self tokenUpdateNotificationWithExpectedToken:expectedToken isInverted:NO];
  699. }
  700. - (XCTestExpectation *)tokenUpdateNotificationWithExpectedToken:(NSString *)expectedToken
  701. isInverted:(BOOL)isInverted {
  702. XCTestExpectation *expectation =
  703. [self expectationForNotification:[self.appCheck tokenDidChangeNotificationName]
  704. object:nil
  705. notificationCenter:self.notificationCenter
  706. handler:^BOOL(NSNotification *_Nonnull notification) {
  707. XCTAssertEqualObjects(
  708. notification.userInfo[[self.appCheck notificationAppNameKey]],
  709. self.appName);
  710. XCTAssertEqualObjects(
  711. notification.userInfo[[self.appCheck notificationTokenKey]],
  712. expectedToken);
  713. XCTAssertEqualObjects(notification.object, self.appCheck);
  714. return YES;
  715. }];
  716. expectation.inverted = isInverted;
  717. return expectation;
  718. }
  719. - (void)assertGetToken_WhenCachedTokenIsValid_Success {
  720. // 1. Create expected token and configure expectations.
  721. FIRAppCheckToken *cachedToken = [self validToken];
  722. NSArray * /*[tokenNotification, getToken]*/ expectations =
  723. [self configuredExpectations_GetTokenWhenCacheTokenIsValid_withExpectedToken:cachedToken];
  724. // 2. Request token and verify result.
  725. [self.appCheck
  726. tokenForcingRefresh:NO
  727. completion:^(FIRAppCheckToken *_Nullable token, NSError *_Nullable error) {
  728. [expectations.lastObject fulfill];
  729. XCTAssertNotNil(token);
  730. XCTAssertEqualObjects(token.token, cachedToken.token);
  731. XCTAssertNil(error);
  732. }];
  733. // 3. Wait for expectations and validate mocks.
  734. [self waitForExpectations:expectations timeout:0.5];
  735. [self verifyAllMocks];
  736. }
  737. - (void)assertInteropGetToken_WhenCachedTokenIsValid_Success {
  738. // 1. Create expected token and configure expectations.
  739. FIRAppCheckToken *cachedToken = [self validToken];
  740. NSArray * /*[tokenNotification, getToken]*/ expectations =
  741. [self configuredExpectations_GetTokenWhenCacheTokenIsValid_withExpectedToken:cachedToken];
  742. // 2. Request token and verify result.
  743. [self.appCheck getTokenForcingRefresh:NO
  744. completion:^(id<FIRAppCheckTokenResultInterop> tokenResult) {
  745. [expectations.lastObject fulfill];
  746. XCTAssertNotNil(tokenResult);
  747. XCTAssertEqualObjects(tokenResult.token, cachedToken.token);
  748. XCTAssertNil(tokenResult.error);
  749. }];
  750. // 3. Wait for expectations and validate mocks.
  751. [self waitForExpectations:expectations timeout:0.5];
  752. [self verifyAllMocks];
  753. }
  754. - (NSArray<XCTestExpectation *> *)configuredExpectations_GetTokenWhenNoCache_withExpectedToken:
  755. (FIRAppCheckToken *)expectedToken {
  756. // 1. Expect token to be requested from storage.
  757. OCMExpect([self.mockStorage getToken]).andReturn([FBLPromise resolvedWith:nil]);
  758. // 2. Expect token requested from app check provider.
  759. id completionArg = [OCMArg invokeBlockWithArgs:expectedToken, [NSNull null], nil];
  760. OCMExpect([self.mockAppCheckProvider getTokenWithCompletion:completionArg]);
  761. // 3. Expect new token to be stored.
  762. OCMExpect([self.mockStorage setToken:expectedToken])
  763. .andReturn([FBLPromise resolvedWith:expectedToken]);
  764. // 4. Expect token update notification to be sent.
  765. XCTestExpectation *tokenNotificationExpectation =
  766. [self tokenUpdateNotificationWithExpectedToken:expectedToken.token];
  767. // 5. Expect token request to be completed.
  768. XCTestExpectation *getTokenExpectation = [self expectationWithDescription:@"getToken"];
  769. return @[ tokenNotificationExpectation, getTokenExpectation ];
  770. }
  771. - (NSArray<XCTestExpectation *> *)
  772. configuredExpectations_GetTokenWhenCacheTokenIsValid_withExpectedToken:
  773. (FIRAppCheckToken *)expectedToken {
  774. // 1. Expect token to be requested from storage.
  775. OCMExpect([self.mockStorage getToken]).andReturn([FBLPromise resolvedWith:expectedToken]);
  776. // 2. Don't expect token requested from app check provider.
  777. OCMReject([self.mockAppCheckProvider getTokenWithCompletion:[OCMArg any]]);
  778. // 3. Don't expect token update notification to be sent.
  779. XCTestExpectation *tokenNotificationExpectation =
  780. [self tokenUpdateNotificationWithExpectedToken:@"" isInverted:YES];
  781. // 4. Expect token request to be completed.
  782. XCTestExpectation *getTokenExpectation = [self expectationWithDescription:@"getToken"];
  783. return @[ tokenNotificationExpectation, getTokenExpectation ];
  784. }
  785. - (NSArray<XCTestExpectation *> *)
  786. configuredExpectations_GetTokenForcingRefreshWhenCacheIsValid_withExpectedToken:
  787. (FIRAppCheckToken *)expectedToken {
  788. // 1. Don't expect token to be requested from storage.
  789. OCMReject([self.mockStorage getToken]);
  790. // 2. Expect token requested from app check provider.
  791. id completionArg = [OCMArg invokeBlockWithArgs:expectedToken, [NSNull null], nil];
  792. OCMExpect([self.mockAppCheckProvider getTokenWithCompletion:completionArg]);
  793. // 3. Expect new token to be stored.
  794. OCMExpect([self.mockStorage setToken:expectedToken])
  795. .andReturn([FBLPromise resolvedWith:expectedToken]);
  796. // 4. Expect token update notification to be sent.
  797. XCTestExpectation *notificationExpectation =
  798. [self tokenUpdateNotificationWithExpectedToken:expectedToken.token];
  799. // 5. Expect token request to be completed.
  800. XCTestExpectation *getTokenExpectation = [self expectationWithDescription:@"getToken"];
  801. return @[ notificationExpectation, getTokenExpectation ];
  802. }
  803. - (NSArray<XCTestExpectation *> *)
  804. configuredExpectations_GetTokenWhenCachedTokenExpired_withExpectedToken:
  805. (FIRAppCheckToken *)expectedToken {
  806. // 1. Expect token to be requested from storage.
  807. FIRAppCheckToken *cachedToken = [[FIRAppCheckToken alloc] initWithToken:@"expired"
  808. expirationDate:[NSDate date]];
  809. OCMExpect([self.mockStorage getToken]).andReturn([FBLPromise resolvedWith:cachedToken]);
  810. // 2. Expect token requested from app check provider.
  811. id completionArg = [OCMArg invokeBlockWithArgs:expectedToken, [NSNull null], nil];
  812. OCMExpect([self.mockAppCheckProvider getTokenWithCompletion:completionArg]);
  813. // 3. Expect new token to be stored.
  814. OCMExpect([self.mockStorage setToken:expectedToken])
  815. .andReturn([FBLPromise resolvedWith:expectedToken]);
  816. // 4. Expect token update notification to be sent.
  817. XCTestExpectation *notificationExpectation =
  818. [self tokenUpdateNotificationWithExpectedToken:expectedToken.token];
  819. // 5. Expect token request to be completed.
  820. XCTestExpectation *getTokenExpectation = [self expectationWithDescription:@"getToken"];
  821. return @[ notificationExpectation, getTokenExpectation ];
  822. }
  823. - (NSArray<XCTestExpectation *> *)
  824. configuredExpectations_GetTokenWhenError_withError:(NSError *_Nonnull)error
  825. andToken:(FIRAppCheckToken *_Nullable)token {
  826. // 1. Expect token to be requested from storage.
  827. OCMExpect([self.mockStorage getToken]).andReturn([FBLPromise resolvedWith:token]);
  828. // 2. Expect token requested from app check provider.
  829. id completionArg = [OCMArg invokeBlockWithArgs:[NSNull null], error, nil];
  830. OCMExpect([self.mockAppCheckProvider getTokenWithCompletion:completionArg]);
  831. // 3. Don't expect token requested from app check provider.
  832. OCMReject([self.mockAppCheckProvider getTokenWithCompletion:[OCMArg any]]);
  833. // 4. Expect token update notification to be sent.
  834. XCTestExpectation *notificationExpectation = [self tokenUpdateNotificationWithExpectedToken:@""
  835. isInverted:YES];
  836. // 5. Expect token request to be completed.
  837. XCTestExpectation *getTokenExpectation = [self expectationWithDescription:@"getToken"];
  838. return @[ notificationExpectation, getTokenExpectation ];
  839. }
  840. - (NSArray *)expectTokenRequestFromAppCheckProvider {
  841. // 1. Expect token to be requested from storage.
  842. OCMExpect([self.mockStorage getToken]).andReturn([FBLPromise resolvedWith:nil]);
  843. // 2. Expect token requested from app check provider.
  844. FIRAppCheckToken *expectedToken = [self validToken];
  845. id completionArg = [OCMArg invokeBlockWithArgs:expectedToken, [NSNull null], nil];
  846. OCMExpect([self.mockAppCheckProvider getTokenWithCompletion:completionArg]);
  847. // 3. Expect new token to be stored.
  848. // 3.1. Create a pending promise to resolve later.
  849. FBLPromise<FIRAppCheckToken *> *storeTokenPromise = [FBLPromise pendingPromise];
  850. // 3.2. Stub storage set token method.
  851. OCMExpect([self.mockStorage setToken:expectedToken]).andReturn(storeTokenPromise);
  852. return @[ expectedToken, storeTokenPromise ];
  853. }
  854. - (void)verifyAllMocks {
  855. OCMVerifyAll(self.mockAppCheckProvider);
  856. OCMVerifyAll(self.mockStorage);
  857. OCMVerifyAll(self.mockSettings);
  858. OCMVerifyAll(self.mockTokenRefresher);
  859. }
  860. @end