FIRInstallationsIDControllerTests.m 63 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521
  1. /*
  2. * Copyright 2019 Google
  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 "FirebaseCore/Sources/Private/FirebaseCoreInternal.h"
  19. #import "FBLPromise+Testing.h"
  20. #import "FBLPromise+Then.h"
  21. #import "FirebaseInstallations/Source/Tests/Utils/FIRInstallationsErrorUtil+Tests.h"
  22. #import "FirebaseInstallations/Source/Tests/Utils/FIRInstallationsItem+Tests.h"
  23. #import "FirebaseInstallations/Source/Library/Errors/FIRInstallationsErrorUtil.h"
  24. #import "FirebaseInstallations/Source/Library/Errors/FIRInstallationsHTTPError.h"
  25. #import "FirebaseInstallations/Source/Library/IIDMigration/FIRInstallationsIIDStore.h"
  26. #import "FirebaseInstallations/Source/Library/IIDMigration/FIRInstallationsIIDTokenStore.h"
  27. #import "FirebaseInstallations/Source/Library/InstallationsAPI/FIRInstallationsAPIService.h"
  28. #import "FirebaseInstallations/Source/Library/InstallationsIDController/FIRInstallationsBackoffController.h"
  29. #import "FirebaseInstallations/Source/Library/InstallationsIDController/FIRInstallationsIDController.h"
  30. #import "FirebaseInstallations/Source/Library/InstallationsStore/FIRInstallationsStore.h"
  31. #import "FirebaseInstallations/Source/Library/Public/FirebaseInstallations/FIRInstallations.h"
  32. #import "FirebaseInstallations/Source/Library/InstallationsStore/FIRInstallationsStoredAuthToken.h"
  33. @interface FIRInstallationsIDController (Tests)
  34. - (instancetype)initWithGoogleAppID:(NSString *)appID
  35. appName:(NSString *)appName
  36. installationsStore:(FIRInstallationsStore *)installationsStore
  37. APIService:(FIRInstallationsAPIService *)APIService
  38. IIDStore:(FIRInstallationsIIDStore *)IIDStore
  39. IIDTokenStore:(FIRInstallationsIIDTokenStore *)IIDTokenStore
  40. backoffController:
  41. (id<FIRInstallationsBackoffControllerProtocol>)backoffController;
  42. @end
  43. @interface FIRInstallationsIDControllerTests : XCTestCase
  44. @property(nonatomic) FIRInstallationsIDController *controller;
  45. @property(nonatomic) id mockInstallationsStore;
  46. @property(nonatomic) id mockAPIService;
  47. @property(nonatomic) id mockIIDStore;
  48. @property(nonatomic) id mockIIDTokenStore;
  49. @property(nonatomic) id mockBackoffController;
  50. @property(nonatomic) NSString *appID;
  51. @property(nonatomic) NSString *appName;
  52. @end
  53. @implementation FIRInstallationsIDControllerTests
  54. - (void)setUp {
  55. [self setUpWithAppName:kFIRDefaultAppName];
  56. }
  57. - (void)setUpWithAppName:(NSString *)appName {
  58. self.appID = @"appID";
  59. self.appName = appName;
  60. self.mockInstallationsStore = OCMStrictClassMock([FIRInstallationsStore class]);
  61. self.mockAPIService = OCMStrictClassMock([FIRInstallationsAPIService class]);
  62. self.mockIIDStore = OCMStrictClassMock([FIRInstallationsIIDStore class]);
  63. self.mockIIDTokenStore = OCMStrictClassMock([FIRInstallationsIIDTokenStore class]);
  64. self.mockBackoffController =
  65. OCMProtocolMock(@protocol(FIRInstallationsBackoffControllerProtocol));
  66. self.controller =
  67. [[FIRInstallationsIDController alloc] initWithGoogleAppID:self.appID
  68. appName:self.appName
  69. installationsStore:self.mockInstallationsStore
  70. APIService:self.mockAPIService
  71. IIDStore:self.mockIIDStore
  72. IIDTokenStore:self.mockIIDTokenStore
  73. backoffController:self.mockBackoffController];
  74. }
  75. - (void)tearDown {
  76. self.controller = nil;
  77. self.mockIIDStore = nil;
  78. self.mockAPIService = nil;
  79. self.mockInstallationsStore = nil;
  80. self.appID = nil;
  81. self.appName = nil;
  82. }
  83. #pragma mark - Initialization
  84. - (void)testInitWhenProjectIDSetThenItIsPassedToAPIService {
  85. NSString *APIKey = @"api-key";
  86. NSString *projectID = @"project-id";
  87. OCMExpect([self.mockAPIService alloc]).andReturn(self.mockAPIService);
  88. OCMExpect([self.mockAPIService initWithAPIKey:APIKey projectID:projectID])
  89. .andReturn(self.mockAPIService);
  90. FIRInstallationsIDController *controller =
  91. [[FIRInstallationsIDController alloc] initWithGoogleAppID:@"app-id"
  92. appName:@"app-name"
  93. APIKey:APIKey
  94. projectID:projectID
  95. GCMSenderID:@"sender-id"
  96. accessGroup:nil];
  97. XCTAssertNotNil(controller);
  98. OCMVerifyAll(self.mockAPIService);
  99. }
  100. - (void)testInitWhenProjectIDIsNilThenGCMSenderIDIsPassedToAPIServiceAsProjectID {
  101. NSString *APIKey = @"api-key";
  102. NSString *GCMSenderID = @"sender-id";
  103. OCMExpect([self.mockAPIService alloc]).andReturn(self.mockAPIService);
  104. OCMExpect([self.mockAPIService initWithAPIKey:APIKey projectID:GCMSenderID])
  105. .andReturn(self.mockAPIService);
  106. FIRInstallationsIDController *controller =
  107. [[FIRInstallationsIDController alloc] initWithGoogleAppID:@"app-id"
  108. appName:@"app-name"
  109. APIKey:APIKey
  110. projectID:@""
  111. GCMSenderID:GCMSenderID
  112. accessGroup:nil];
  113. XCTAssertNotNil(controller);
  114. OCMVerifyAll(self.mockAPIService);
  115. }
  116. #pragma mark - Get Installation
  117. - (void)testGetInstallationItem_WhenFIDExists_ThenItIsReturned {
  118. FIRInstallationsItem *storedInstallations =
  119. [FIRInstallationsItem createRegisteredInstallationItem];
  120. OCMExpect([self.mockInstallationsStore installationForAppID:self.appID appName:self.appName])
  121. .andReturn([FBLPromise resolvedWith:storedInstallations]);
  122. // Don't expect FIRInstallationIDDidChangeNotification to be sent.
  123. XCTestExpectation *notificationExpectation =
  124. [self installationIDDidChangeNotificationExpectation];
  125. notificationExpectation.inverted = YES;
  126. FBLPromise<FIRInstallationsItem *> *promise = [self.controller getInstallationItem];
  127. XCTAssert(FBLWaitForPromisesWithTimeout(0.5));
  128. XCTAssertNil(promise.error);
  129. XCTAssertEqual(promise.value, storedInstallations);
  130. OCMVerifyAll(self.mockInstallationsStore);
  131. [self waitForExpectations:@[ notificationExpectation ] timeout:0.5];
  132. }
  133. - (void)testGetInstallationItem_WhenNoFIDAndNoIID_ThenFIDIsCreatedAndRegistered {
  134. [self expectInstallationsStoreGetInstallationNotFound];
  135. [self expectStoredIIDNotFound];
  136. [self assertGetInstallation_NewFIDCreatedAndRegistered];
  137. }
  138. - (void)testGetInstallationItem_WhenFIDCorruptedAndNoIID_ThenFIDIsCreatedAndRegistered {
  139. [self expectInstallationsStoreGetInstallationCorruptedFID];
  140. [self expectStoredIIDNotFound];
  141. [self assertGetInstallation_NewFIDCreatedAndRegistered];
  142. }
  143. - (void)testGetInstallationItem_WhenThereIsIIDAndNoFIDNotDefaultApp_ThenIIDIsNotUsedAsFID {
  144. // 0. Configure controller with not default app.
  145. NSString *appName = @"appName";
  146. [self setUpWithAppName:appName];
  147. // 1. Stub store get installation.
  148. [self expectInstallationsStoreGetInstallationNotFound];
  149. // 2. Don't expect IIDStore to be checked for existing IID (not default app).
  150. OCMReject([self.mockIIDStore existingIID]);
  151. // 3. Stub store save installation.
  152. __block FIRInstallationsItem *createdInstallation;
  153. OCMExpect([self.mockInstallationsStore
  154. saveInstallation:[OCMArg checkWithBlock:^BOOL(FIRInstallationsItem *obj) {
  155. [self assertValidCreatedInstallation:obj];
  156. createdInstallation = obj;
  157. return YES;
  158. }]])
  159. .andReturn([FBLPromise resolvedWith:[NSNull null]]);
  160. // 4. Stub API register installation.
  161. // 4.1. Expect backoff controller to be requested.
  162. [self expectIsNextRequestAllowedWithResult:YES];
  163. // 4.2. Verify installation to be registered.
  164. id registerInstallationValidation = [OCMArg checkWithBlock:^BOOL(FIRInstallationsItem *obj) {
  165. [self assertValidCreatedInstallation:obj];
  166. return YES;
  167. }];
  168. // 4.3. Expect for `registerInstallation` to be called.
  169. FBLPromise<FIRInstallationsItem *> *registerPromise = [FBLPromise pendingPromise];
  170. OCMExpect([self.mockAPIService registerInstallation:registerInstallationValidation])
  171. .andReturn(registerPromise);
  172. // 4.4. Expect backoff success.
  173. [self expectBackoffEvent:FIRInstallationsBackoffEventSuccess];
  174. // 5. Call get installation and check.
  175. FBLPromise<FIRInstallationsItem *> *getInstallationPromise =
  176. [self.controller getInstallationItem];
  177. // 5.1. Wait for the stored item to be read and saved.
  178. OCMVerifyAllWithDelay(self.mockInstallationsStore, 0.5);
  179. // 5.2. Wait for `registerInstallation` to be called.
  180. OCMVerifyAllWithDelay(self.mockAPIService, 0.5);
  181. // 5.3. Expect for the registered installation to be saved.
  182. FIRInstallationsItem *registeredInstallation = [FIRInstallationsItem
  183. createRegisteredInstallationItemWithAppID:createdInstallation.appID
  184. appName:createdInstallation.firebaseAppName];
  185. OCMExpect([self.mockInstallationsStore
  186. saveInstallation:[OCMArg checkWithBlock:^BOOL(FIRInstallationsItem *obj) {
  187. XCTAssertEqual(registeredInstallation, obj);
  188. return YES;
  189. }]])
  190. .andReturn([FBLPromise resolvedWith:[NSNull null]]);
  191. // 5.5. Resolve `registerPromise` to simulate finished registration.
  192. [registerPromise fulfill:registeredInstallation];
  193. // 5.4. Wait for the task to complete.
  194. XCTAssert(FBLWaitForPromisesWithTimeout(0.5));
  195. XCTAssertNil(getInstallationPromise.error);
  196. // We expect the initially created installation to be returned - must not wait for registration to
  197. // complete here.
  198. XCTAssertEqual(getInstallationPromise.value, createdInstallation);
  199. // 5.5. Verify registered installation was saved.
  200. OCMVerifyAll(self.mockInstallationsStore);
  201. OCMVerifyAll(self.mockIIDStore);
  202. OCMVerifyAll(self.mockBackoffController);
  203. }
  204. - (void)testGetInstallationItem_WhenThereIsIIDAndNoFID_ThenIIDIsRegisteredAsFID {
  205. // 1. Stub store get installation.
  206. [self expectInstallationsStoreGetInstallationNotFound];
  207. // 2. Expect IIDStore to be checked for existing IID.
  208. NSString *existingIID = @"existing-iid";
  209. OCMExpect([self.mockIIDStore existingIID]).andReturn([FBLPromise resolvedWith:existingIID]);
  210. // 3. Expect IID checkin store to be requested for checkin data.
  211. NSString *existingIIDDefaultToken = @"existing-iid-token";
  212. OCMExpect([self.mockIIDTokenStore existingIIDDefaultToken])
  213. .andReturn([FBLPromise resolvedWith:existingIIDDefaultToken]);
  214. // 3. Stub store save installation.
  215. __block FIRInstallationsItem *createdInstallation;
  216. OCMExpect([self.mockInstallationsStore
  217. saveInstallation:[OCMArg checkWithBlock:^BOOL(FIRInstallationsItem *obj) {
  218. [self assertValidCreatedInstallation:obj];
  219. XCTAssertEqualObjects(existingIID, obj.firebaseInstallationID);
  220. XCTAssertEqualObjects(obj.IIDDefaultToken, existingIIDDefaultToken);
  221. createdInstallation = obj;
  222. return YES;
  223. }]])
  224. .andReturn([FBLPromise resolvedWith:[NSNull null]]);
  225. // 4. Stub API register installation.
  226. // 4.1. Expect backoff controller to be requested.
  227. [self expectIsNextRequestAllowedWithResult:YES];
  228. // 4.2. Verify installation to be registered.
  229. id registerInstallationValidation = [OCMArg checkWithBlock:^BOOL(FIRInstallationsItem *obj) {
  230. [self assertValidCreatedInstallation:obj];
  231. XCTAssertEqualObjects(existingIID, obj.firebaseInstallationID);
  232. return YES;
  233. }];
  234. // 4.3. Expect for `registerInstallation` to be called.
  235. FBLPromise<FIRInstallationsItem *> *registerPromise = [FBLPromise pendingPromise];
  236. OCMExpect([self.mockAPIService registerInstallation:registerInstallationValidation])
  237. .andReturn(registerPromise);
  238. // 4.4. Expect backoff success.
  239. [self expectBackoffEvent:FIRInstallationsBackoffEventSuccess];
  240. // 5. Call get installation and check.
  241. FBLPromise<FIRInstallationsItem *> *getInstallationPromise =
  242. [self.controller getInstallationItem];
  243. // 5.1. Wait for the stored item to be read and saved.
  244. OCMVerifyAllWithDelay(self.mockInstallationsStore, 0.5);
  245. // 5.2. Wait for `registerInstallation` to be called.
  246. OCMVerifyAllWithDelay(self.mockAPIService, 0.5);
  247. // 5.3. Expect for the registered installation to be saved.
  248. FIRInstallationsItem *registeredInstallation = [FIRInstallationsItem
  249. createRegisteredInstallationItemWithAppID:createdInstallation.appID
  250. appName:createdInstallation.firebaseAppName];
  251. OCMExpect([self.mockInstallationsStore
  252. saveInstallation:[OCMArg checkWithBlock:^BOOL(FIRInstallationsItem *obj) {
  253. XCTAssertEqual(registeredInstallation, obj);
  254. return YES;
  255. }]])
  256. .andReturn([FBLPromise resolvedWith:[NSNull null]]);
  257. // 5.5. Resolve `registerPromise` to simulate finished registration.
  258. [registerPromise fulfill:registeredInstallation];
  259. // 5.4. Wait for the task to complete.
  260. XCTAssert(FBLWaitForPromisesWithTimeout(0.5));
  261. XCTAssertNil(getInstallationPromise.error);
  262. // We expect the initially created installation to be returned - must not wait for registration to
  263. // complete here.
  264. XCTAssertEqual(getInstallationPromise.value, createdInstallation);
  265. // 5.5. Verify registered installation was saved.
  266. OCMVerifyAll(self.mockInstallationsStore);
  267. OCMVerifyAll(self.mockIIDStore);
  268. OCMVerifyAll(self.mockIIDTokenStore);
  269. OCMVerifyAll(self.mockBackoffController);
  270. }
  271. - (void)testGetInstallationItem_WhenCalledWhileRegistering_DoesNotWaitForAPIResponse {
  272. // 1. Expect the installation to be requested from the store only once.
  273. FIRInstallationsItem *storedInstallation1 =
  274. [FIRInstallationsItem createUnregisteredInstallationItem];
  275. OCMExpect([self.mockInstallationsStore installationForAppID:self.appID appName:self.appName])
  276. .andReturn([FBLPromise resolvedWith:storedInstallation1]);
  277. // 2. Expect backoff controller to be requested.
  278. [self expectIsNextRequestAllowedWithResult:YES];
  279. // 3. Expect registration API request to be sent.
  280. FBLPromise<FIRInstallationsItem *> *pendingAPIPromise = [FBLPromise pendingPromise];
  281. OCMExpect([self.mockAPIService registerInstallation:storedInstallation1])
  282. .andReturn(pendingAPIPromise);
  283. // 4. Expect backoff recoverable error for HTTP 429.
  284. [self expectBackoffEvent:FIRInstallationsBackoffEventRecoverableFailure];
  285. // 5. Request and wait for 1st FID.
  286. FBLPromise<FIRInstallationsItem *> *getInstallationPromise1 =
  287. [self.controller getInstallationItem];
  288. XCTestExpectation *getInstallationsExpectation1 =
  289. [self expectationWithDescription:@"getInstallationsExpectation1"];
  290. getInstallationPromise1.then(^id(FIRInstallationsItem *installation) {
  291. [getInstallationsExpectation1 fulfill];
  292. return nil;
  293. });
  294. [self waitForExpectations:@[ getInstallationsExpectation1 ] timeout:0.5];
  295. // 6. Request FID 2nd time.
  296. OCMExpect([self.mockInstallationsStore installationForAppID:self.appID appName:self.appName])
  297. .andReturn([FBLPromise resolvedWith:storedInstallation1]);
  298. FBLPromise<FIRInstallationsItem *> *getInstallationPromise2 =
  299. [self.controller getInstallationItem];
  300. XCTestExpectation *getInstallationsExpectation2 =
  301. [self expectationWithDescription:@"getInstallationsExpectation2"];
  302. getInstallationPromise2.then(^id(FIRInstallationsItem *installation) {
  303. [getInstallationsExpectation2 fulfill];
  304. return nil;
  305. });
  306. [self waitForExpectations:@[ getInstallationsExpectation2 ] timeout:0.5];
  307. // 7. Resolve API promise.
  308. [pendingAPIPromise reject:[FIRInstallationsErrorUtil APIErrorWithHTTPCode:429]];
  309. // 8. Check
  310. XCTAssert(FBLWaitForPromisesWithTimeout(0.5));
  311. OCMVerifyAll(self.mockInstallationsStore);
  312. OCMVerifyAll(self.mockAPIService);
  313. OCMVerifyAll(self.mockBackoffController);
  314. }
  315. - (void)testGetInstallationItem_WhenCalledSeveralTimesWaitingForStore_OnlyOneOperationIsPerformed {
  316. // 1. Expect the installation to be requested from the store only once.
  317. FIRInstallationsItem *storedInstallation1 =
  318. [FIRInstallationsItem createRegisteredInstallationItem];
  319. FBLPromise<FIRInstallationsItem *> *pendingStorePromise = [FBLPromise pendingPromise];
  320. OCMExpect([self.mockInstallationsStore installationForAppID:self.appID appName:self.appName])
  321. .andReturn(pendingStorePromise);
  322. // 2. Request installation n times
  323. NSInteger requestCount = 10;
  324. NSMutableArray *installationPromises = [NSMutableArray arrayWithCapacity:requestCount];
  325. for (NSInteger i = 0; i < requestCount; i++) {
  326. [installationPromises addObject:[self.controller getInstallationItem]];
  327. }
  328. // 3. Resolve store promise.
  329. [pendingStorePromise fulfill:storedInstallation1];
  330. // 4. Wait for operation to be completed and check.
  331. XCTAssert(FBLWaitForPromisesWithTimeout(0.5));
  332. for (FBLPromise<FIRInstallationsItem *> *installationPromise in installationPromises) {
  333. XCTAssertNil(installationPromise.error);
  334. XCTAssertEqual(installationPromise.value, storedInstallation1);
  335. }
  336. OCMVerifyAll(self.mockInstallationsStore);
  337. OCMVerifyAll(self.mockAPIService);
  338. // 5. Check that a new request is performed once previous finished.
  339. FIRInstallationsItem *storedInstallation2 =
  340. [FIRInstallationsItem createRegisteredInstallationItem];
  341. OCMExpect([self.mockInstallationsStore installationForAppID:self.appID appName:self.appName])
  342. .andReturn([FBLPromise resolvedWith:storedInstallation2]);
  343. FBLPromise<FIRInstallationsItem *> *installationPromise = [self.controller getInstallationItem];
  344. XCTAssert(FBLWaitForPromisesWithTimeout(0.5));
  345. XCTAssertNil(installationPromise.error);
  346. XCTAssertEqual(installationPromise.value, storedInstallation2);
  347. OCMVerifyAll(self.mockInstallationsStore);
  348. OCMVerifyAll(self.mockAPIService);
  349. }
  350. - (void)testGetInstallationItem_WhenCalledSeveralTimesWaitingForAPI_OnlyOneAPIRequestIsSent {
  351. // 1. Expect a single API request.
  352. // 1.1. Expect backoff controller to be requested.
  353. [self expectIsNextRequestAllowedWithResult:YES];
  354. // 1.2. Expect the request itself.
  355. FBLPromise<FIRInstallationsItem *> *registerAPIPromise = [FBLPromise pendingPromise];
  356. OCMExpect([self.mockAPIService registerInstallation:[OCMArg any]]).andReturn(registerAPIPromise);
  357. // 2. Request FID multiple times.
  358. NSInteger requestCount = 10;
  359. for (NSInteger i = 0; i < requestCount; i++) {
  360. XCTestExpectation *getFIDExpectation = [self
  361. expectationWithDescription:[NSString stringWithFormat:@"getFIDExpectation%ld", (long)i]];
  362. // 2.1. Expect stored FID to be requested.
  363. FIRInstallationsItem *storedInstallation =
  364. [FIRInstallationsItem createUnregisteredInstallationItem];
  365. OCMExpect([self.mockInstallationsStore installationForAppID:self.appID appName:self.appName])
  366. .andReturn([FBLPromise resolvedWith:storedInstallation]);
  367. // 2.2. Expect the FID to be returned.
  368. FBLPromise<FIRInstallationsItem *> *getFIDPromise = [self.controller getInstallationItem];
  369. [getFIDPromise then:^id _Nullable(FIRInstallationsItem *_Nullable value) {
  370. XCTAssertNotNil(value);
  371. XCTAssertEqualObjects(value.firebaseInstallationID,
  372. storedInstallation.firebaseInstallationID);
  373. [getFIDExpectation fulfill];
  374. return nil;
  375. }];
  376. [self waitForExpectations:@[ getFIDExpectation ] timeout:0.5];
  377. }
  378. // 3. Expect backoff unrecoverable failure.
  379. [self expectBackoffEvent:FIRInstallationsBackoffEventUnrecoverableFailure];
  380. // 4. Finish API request.
  381. [registerAPIPromise reject:[FIRInstallationsErrorUtil APIErrorWithHTTPCode:400]];
  382. // 5. Verify mocks
  383. XCTAssert(FBLWaitForPromisesWithTimeout(0.5));
  384. OCMVerifyAll(self.mockInstallationsStore);
  385. OCMVerifyAll(self.mockAPIService);
  386. OCMVerifyAll(self.mockBackoffController);
  387. }
  388. - (void)testGetInstallationItem_WhenRegistrationError_ThenBackoffEventIsLogged {
  389. [self expectBackoffEvent:FIRInstallationsBackoffEventUnrecoverableFailure
  390. forRegisterFIDAPIError:[FIRInstallationsErrorUtil APIErrorWithHTTPCode:400]];
  391. [self expectBackoffEvent:FIRInstallationsBackoffEventUnrecoverableFailure
  392. forRegisterFIDAPIError:[FIRInstallationsErrorUtil APIErrorWithHTTPCode:403]];
  393. [self expectBackoffEvent:FIRInstallationsBackoffEventRecoverableFailure
  394. forRegisterFIDAPIError:[FIRInstallationsErrorUtil APIErrorWithHTTPCode:429]];
  395. [self expectBackoffEvent:FIRInstallationsBackoffEventRecoverableFailure
  396. forRegisterFIDAPIError:[FIRInstallationsErrorUtil APIErrorWithHTTPCode:500]];
  397. [self expectBackoffEvent:FIRInstallationsBackoffEventRecoverableFailure
  398. forRegisterFIDAPIError:[FIRInstallationsErrorUtil APIErrorWithHTTPCode:503]];
  399. // An arbitrary unknown server response.
  400. [self expectBackoffEvent:FIRInstallationsBackoffEventRecoverableFailure
  401. forRegisterFIDAPIError:[FIRInstallationsErrorUtil APIErrorWithHTTPCode:444]];
  402. // A connection error.
  403. [self expectBackoffEvent:kNoBackoffEvents
  404. forRegisterFIDAPIError:[FIRInstallationsErrorUtil
  405. networkErrorWithError:[NSError errorWithDomain:@"tests"
  406. code:-1
  407. userInfo:nil]]];
  408. // An unknown error.
  409. [self expectBackoffEvent:kNoBackoffEvents
  410. forRegisterFIDAPIError:[NSError errorWithDomain:@"tests" code:-1 userInfo:nil]];
  411. }
  412. - (void)testGetInstallationItem_WhenNextRequestIsNotAllowed {
  413. // 1. Stub store get installation.
  414. [self expectInstallationsStoreGetInstallationNotFound];
  415. // 2. Stub store save installation.
  416. __block FIRInstallationsItem *createdInstallation;
  417. OCMExpect([self.mockInstallationsStore
  418. saveInstallation:[OCMArg checkWithBlock:^BOOL(FIRInstallationsItem *obj) {
  419. [self assertValidCreatedInstallation:obj];
  420. createdInstallation = obj;
  421. return YES;
  422. }]])
  423. .andReturn([FBLPromise resolvedWith:[NSNull null]]);
  424. // 3. Expect IIDStore to be checked for existing IID.
  425. [self expectStoredIIDNotFound];
  426. // 4. Stub API register installation.
  427. // 4.1. Expect backoff controller to be requested.
  428. [self expectIsNextRequestAllowedWithResult:NO];
  429. // 4.2. Don't expect for `registerInstallation` to be called.
  430. OCMReject([self.mockAPIService registerInstallation:[OCMArg any]]);
  431. // 4.3. Don't expect backoff updates.
  432. [self rejectBackoffEvent];
  433. // 5. Call get installation and check.
  434. FBLPromise<FIRInstallationsItem *> *getInstallationPromise =
  435. [self.controller getInstallationItem];
  436. // 5.1. Wait for the stored item to be read and saved.
  437. OCMVerifyAllWithDelay(self.mockInstallationsStore, 0.5);
  438. // 5.2. Wait for `registerInstallation` to be called.
  439. OCMVerifyAllWithDelay(self.mockAPIService, 0.5);
  440. // 5.3. Don't Expect for the registered installation to be saved.
  441. OCMReject([self.mockInstallationsStore saveInstallation:[OCMArg any]]);
  442. // 5.4. Wait for the task to complete.
  443. XCTAssert(FBLWaitForPromisesWithTimeout(0.5));
  444. XCTAssertNil(getInstallationPromise.error);
  445. // We expect the initially created installation to be returned - must not wait for registration to
  446. // complete here.
  447. XCTAssertEqual(getInstallationPromise.value, createdInstallation);
  448. // 5.5. Verify registered installation was saved.
  449. OCMVerifyAll(self.mockInstallationsStore);
  450. OCMVerifyAll(self.mockIIDStore);
  451. OCMVerifyAll(self.mockBackoffController);
  452. }
  453. #pragma mark - Get Auth Token
  454. - (void)testGetAuthToken_WhenValidInstallationExists_ThenItIsReturned {
  455. // 1. Expect installation to be requested from the store.
  456. FIRInstallationsItem *storedInstallation =
  457. [FIRInstallationsItem createRegisteredInstallationItem];
  458. OCMExpect([self.mockInstallationsStore installationForAppID:self.appID appName:self.appName])
  459. .andReturn([FBLPromise resolvedWith:storedInstallation]);
  460. // 2. Request auth token.
  461. FBLPromise<FIRInstallationsItem *> *promise = [self.controller getAuthTokenForcingRefresh:NO];
  462. // 3. Wait for the promise to resolve.
  463. XCTAssert(FBLWaitForPromisesWithTimeout(0.5));
  464. // 4. Check.
  465. OCMVerifyAll(self.mockInstallationsStore);
  466. OCMVerifyAll(self.mockAPIService);
  467. XCTAssertNil(promise.error);
  468. XCTAssertNotNil(promise.value);
  469. XCTAssertEqualObjects(promise.value.authToken.token, storedInstallation.authToken.token);
  470. XCTAssertEqualObjects(promise.value.authToken.expirationDate,
  471. storedInstallation.authToken.expirationDate);
  472. }
  473. - (void)testGetAuthToken_WhenValidInstallationWithExpiredTokenExists_ThenTokenRequested {
  474. // 1.1. Expect installation to be requested from the store.
  475. FIRInstallationsItem *storedInstallation =
  476. [FIRInstallationsItem createRegisteredInstallationItem];
  477. storedInstallation.authToken.expirationDate = [NSDate dateWithTimeIntervalSinceNow:60 * 60 - 1];
  478. OCMExpect([self.mockInstallationsStore installationForAppID:self.appID appName:self.appName])
  479. .andReturn([FBLPromise resolvedWith:storedInstallation]);
  480. // 1.2. Auth Token refresh.
  481. FIRInstallationsItem *responseInstallation =
  482. [self expectAuthTokenRefreshForInstallation:storedInstallation];
  483. // 2. Request auth token.
  484. FBLPromise<FIRInstallationsItem *> *promise = [self.controller getAuthTokenForcingRefresh:NO];
  485. // 3. Wait for the promise to resolve.
  486. XCTAssert(FBLWaitForPromisesWithTimeout(0.5));
  487. // 4. Check.
  488. OCMVerifyAll(self.mockInstallationsStore);
  489. OCMVerifyAll(self.mockAPIService);
  490. OCMVerifyAll(self.mockBackoffController);
  491. XCTAssertNil(promise.error);
  492. XCTAssertNotNil(promise.value);
  493. XCTAssertEqualObjects(promise.value.authToken.token, responseInstallation.authToken.token);
  494. XCTAssertEqualObjects(promise.value.authToken.expirationDate,
  495. responseInstallation.authToken.expirationDate);
  496. }
  497. - (void)testGetAuthTokenForcingRefresh_WhenValidInstallationExists_ThenTokenRequested {
  498. // 1.1. Expect installation to be requested from the store.
  499. FIRInstallationsItem *storedInstallation =
  500. [FIRInstallationsItem createRegisteredInstallationItem];
  501. OCMExpect([self.mockInstallationsStore installationForAppID:self.appID appName:self.appName])
  502. .andReturn([FBLPromise resolvedWith:storedInstallation]);
  503. // 1.2. Auth Token refresh.
  504. FIRInstallationsItem *responseInstallation =
  505. [self expectAuthTokenRefreshForInstallation:storedInstallation];
  506. // 2. Request auth token.
  507. FBLPromise<FIRInstallationsItem *> *promise = [self.controller getAuthTokenForcingRefresh:YES];
  508. // 3. Wait for the promise to resolve.
  509. XCTAssert(FBLWaitForPromisesWithTimeout(0.5));
  510. // 4. Check.
  511. OCMVerifyAll(self.mockInstallationsStore);
  512. OCMVerifyAll(self.mockAPIService);
  513. OCMVerifyAll(self.mockBackoffController);
  514. XCTAssertNil(promise.error);
  515. XCTAssertNotNil(promise.value);
  516. XCTAssertEqualObjects(promise.value.authToken.token, responseInstallation.authToken.token);
  517. XCTAssertEqualObjects(promise.value.authToken.expirationDate,
  518. responseInstallation.authToken.expirationDate);
  519. }
  520. - (void)testGetAuthToken_WhenCalledSeveralTimes_OnlyOneOperationIsPerformed {
  521. // 1. Expect installation to be requested from the store.
  522. FIRInstallationsItem *storedInstallation =
  523. [FIRInstallationsItem createRegisteredInstallationItem];
  524. FBLPromise *storagePendingPromise = [FBLPromise pendingPromise];
  525. // Expect the installation to be requested only once.
  526. OCMExpect([self.mockInstallationsStore installationForAppID:self.appID appName:self.appName])
  527. .andReturn(storagePendingPromise);
  528. // 2. Request auth token n times.
  529. NSInteger requestCount = 10;
  530. NSMutableArray *authTokenPromises = [NSMutableArray arrayWithCapacity:requestCount];
  531. for (NSInteger i = 0; i < requestCount; i++) {
  532. [authTokenPromises addObject:[self.controller getAuthTokenForcingRefresh:NO]];
  533. }
  534. // 3. Finish the storage request.
  535. [storagePendingPromise fulfill:storedInstallation];
  536. // 4. Wait for the promise to resolve.
  537. XCTAssert(FBLWaitForPromisesWithTimeout(0.5));
  538. // 5. Check.
  539. OCMVerifyAll(self.mockInstallationsStore);
  540. for (FBLPromise<FIRInstallationsItem *> *authPromise in authTokenPromises) {
  541. XCTAssertNil(authPromise.error);
  542. XCTAssertNotNil(authPromise.value);
  543. XCTAssertEqualObjects(authPromise.value.authToken.token, storedInstallation.authToken.token);
  544. XCTAssertEqualObjects(authPromise.value.authToken.expirationDate,
  545. storedInstallation.authToken.expirationDate);
  546. }
  547. }
  548. - (void)testGetAuthTokenForceRefresh_WhenCalledSeveralTimes_OnlyOneOperationIsPerformed {
  549. // 1.1. Expect installation to be requested from the store.
  550. FIRInstallationsItem *storedInstallation =
  551. [FIRInstallationsItem createRegisteredInstallationItem];
  552. OCMExpect([self.mockInstallationsStore installationForAppID:self.appID appName:self.appName])
  553. .andReturn([FBLPromise resolvedWith:storedInstallation]);
  554. // 1.2. Expect backoff controller to be requested.
  555. [self expectIsNextRequestAllowedWithResult:YES];
  556. // 1.3. Expect API request.
  557. FIRInstallationsItem *responseInstallation =
  558. [FIRInstallationsItem createRegisteredInstallationItem];
  559. responseInstallation.authToken.token =
  560. [responseInstallation.authToken.token stringByAppendingString:@"_new"];
  561. FBLPromise *pendingAPIPromise = [FBLPromise pendingPromise];
  562. OCMExpect([self.mockAPIService refreshAuthTokenForInstallation:storedInstallation])
  563. .andReturn(pendingAPIPromise);
  564. // 1.4. Expect backoff success.
  565. [self expectBackoffEvent:FIRInstallationsBackoffEventSuccess];
  566. // 1.5. Expect new token to be stored.
  567. OCMExpect([self.mockInstallationsStore saveInstallation:responseInstallation])
  568. .andReturn([FBLPromise resolvedWith:[NSNull null]]);
  569. // 2. Request auth token n times.
  570. NSInteger requestCount = 10;
  571. NSMutableArray *authTokenPromises = [NSMutableArray arrayWithCapacity:requestCount];
  572. for (NSInteger i = 0; i < requestCount; i++) {
  573. [authTokenPromises addObject:[self.controller getAuthTokenForcingRefresh:YES]];
  574. }
  575. // 3. Finish the API request.
  576. [pendingAPIPromise fulfill:responseInstallation];
  577. // 4. Wait for the promise to resolve.
  578. XCTAssert(FBLWaitForPromisesWithTimeout(0.5));
  579. // 5. Check.
  580. OCMVerifyAll(self.mockInstallationsStore);
  581. OCMVerifyAll(self.mockBackoffController);
  582. for (FBLPromise<FIRInstallationsItem *> *authPromise in authTokenPromises) {
  583. XCTAssertNil(authPromise.error);
  584. XCTAssertNotNil(authPromise.value);
  585. XCTAssertEqualObjects(authPromise.value.authToken.token, responseInstallation.authToken.token);
  586. XCTAssertEqualObjects(authPromise.value.authToken.expirationDate,
  587. responseInstallation.authToken.expirationDate);
  588. }
  589. }
  590. - (void)testGetAuthToken_WhenAPIResponse404_ThenFISResetAndReregistered {
  591. NSTimeInterval timeout = 0.5;
  592. // 1.1. Expect installation to be requested from the store.
  593. FIRInstallationsItem *storedInstallation =
  594. [FIRInstallationsItem createRegisteredInstallationItem];
  595. [self expectInstallationStoreToBeRequestedAndReturnInstallation:storedInstallation];
  596. // 1.2. Expect backoff controller to be requested.
  597. [self expectIsNextRequestAllowedWithResult:YES];
  598. // 1.3. Expect API request.
  599. FBLPromise *rejectedAPIPromise = [FBLPromise pendingPromise];
  600. OCMExpect([self.mockAPIService refreshAuthTokenForInstallation:storedInstallation])
  601. .andReturn(rejectedAPIPromise);
  602. // 1.4. We don't expect a backoff event corresponding to the 404 sever response as the error is
  603. // auto-recovered by the controller.
  604. // 2. Request auth token.
  605. FBLPromise<FIRInstallationsItem *> *promise = [self.controller getAuthTokenForcingRefresh:YES];
  606. // 3. Wait for refresh token request.
  607. OCMVerifyAllWithDelay(self.mockAPIService, timeout);
  608. // 4.1. Expect Installation to be requested before deletion.
  609. [self expectInstallationStoreToBeRequestedAndReturnInstallation:storedInstallation];
  610. // 4. Expect for FIS to be deleted locally.
  611. NSArray<XCTestExpectation *> *deleteExpectations =
  612. [self expectInstallationToBeDeletedLocally:storedInstallation];
  613. // 6. Expect a new installation to be created and registered.
  614. // 6.1. Expect to request FIS from storage.
  615. [self expectInstallationsStoreGetInstallationNotFound];
  616. // 6.2. Expect stored IID not found.
  617. [self expectStoredIIDNotFound];
  618. // 6.3. Expect new Installation to be stored.
  619. __block FIRInstallationsItem *createdInstallation;
  620. OCMExpect([self.mockInstallationsStore
  621. saveInstallation:[OCMArg checkWithBlock:^BOOL(FIRInstallationsItem *obj) {
  622. [self assertValidCreatedInstallation:obj];
  623. createdInstallation = obj;
  624. return YES;
  625. }]])
  626. .andReturn([FBLPromise resolvedWith:[NSNull null]]);
  627. // 6.4 Expect backoff controller to be requested.
  628. [self expectIsNextRequestAllowedWithResult:YES];
  629. // 6.5. Expect registration API request to be sent.
  630. FBLPromise<FIRInstallationsItem *> *registerPromise = [FBLPromise pendingPromise];
  631. OCMExpect([self.mockAPIService registerInstallation:[OCMArg any]]).andReturn(registerPromise);
  632. // 6.6. Expect backoff success.
  633. [self expectBackoffEvent:FIRInstallationsBackoffEventSuccess];
  634. // 6.7. Reject API request promise with 404.
  635. NSError *error404 = [FIRInstallationsErrorUtil APIErrorWithHTTPCode:404];
  636. [rejectedAPIPromise reject:error404];
  637. // 6.8. Wait local FIS to be deleted.
  638. [self waitForExpectations:deleteExpectations timeout:timeout];
  639. // 6.9 Wait for the new Installation to be stored.
  640. OCMVerifyAllWithDelay(self.mockInstallationsStore, timeout);
  641. // 6.10. Wait for registration API request to be sent.
  642. OCMVerifyAllWithDelay(self.mockAPIService, timeout);
  643. // 6.11. Expect for the registered installation to be saved.
  644. FIRInstallationsItem *registeredInstallation = [FIRInstallationsItem
  645. createRegisteredInstallationItemWithAppID:createdInstallation.appID
  646. appName:createdInstallation.firebaseAppName];
  647. OCMExpect([self.mockInstallationsStore
  648. saveInstallation:[OCMArg checkWithBlock:^BOOL(FIRInstallationsItem *obj) {
  649. XCTAssertEqual(registeredInstallation, obj);
  650. return YES;
  651. }]])
  652. .andReturn([FBLPromise resolvedWith:[NSNull null]]);
  653. // 6.12. Fulfill the registration API request promise.
  654. [registerPromise fulfill:registeredInstallation];
  655. // 7. Wait for promises.
  656. XCTAssert(FBLWaitForPromisesWithTimeout(timeout));
  657. // 8. Check.
  658. OCMVerifyAll(self.mockInstallationsStore);
  659. OCMVerifyAll(self.mockAPIService);
  660. OCMVerifyAll(self.mockBackoffController);
  661. XCTAssertNil(promise.error);
  662. XCTAssertNotNil(promise.value);
  663. XCTAssertNotEqualObjects(promise.value.firebaseInstallationID,
  664. storedInstallation.firebaseInstallationID);
  665. XCTAssertEqualObjects(promise.value, registeredInstallation);
  666. }
  667. - (void)testGetAuthToken_WhenNextRequestIsNotAllowed {
  668. // 1.1. Expect installation to be requested from the store.
  669. FIRInstallationsItem *storedInstallation =
  670. [FIRInstallationsItem createRegisteredInstallationItem];
  671. storedInstallation.authToken.expirationDate = [NSDate dateWithTimeIntervalSinceNow:60 * 60 - 1];
  672. OCMExpect([self.mockInstallationsStore installationForAppID:self.appID appName:self.appName])
  673. .andReturn([FBLPromise resolvedWith:storedInstallation]);
  674. // 1.2 Expect backoff controller to be requested.
  675. [self expectIsNextRequestAllowedWithResult:NO];
  676. // 1.3. Don't expect API request.
  677. OCMReject([self.mockAPIService refreshAuthTokenForInstallation:[OCMArg any]]);
  678. // 1.4. Don't expect new token to be stored.
  679. OCMReject([self.mockInstallationsStore saveInstallation:[OCMArg any]]);
  680. // 1.5. Don't expect backoff events.
  681. [self rejectBackoffEvent];
  682. // 2. Request auth token.
  683. FBLPromise<FIRInstallationsItem *> *promise = [self.controller getAuthTokenForcingRefresh:NO];
  684. // 3. Wait for the promise to resolve.
  685. XCTAssert(FBLWaitForPromisesWithTimeout(0.5));
  686. // 4. Check.
  687. OCMVerifyAll(self.mockInstallationsStore);
  688. OCMVerifyAll(self.mockAPIService);
  689. OCMVerifyAll(self.mockBackoffController);
  690. XCTAssertNil(promise.value);
  691. XCTAssertNotNil(promise.error);
  692. XCTAssertEqualObjects(promise.error, [FIRInstallationsErrorUtil backoffIntervalWaitError]);
  693. }
  694. #pragma mark - FID Deletion
  695. - (void)testDeleteRegisteredInstallation {
  696. // 1. Expect installation to be requested from the store.
  697. FIRInstallationsItem *installation = [FIRInstallationsItem createRegisteredInstallationItem];
  698. OCMExpect([self.mockInstallationsStore installationForAppID:installation.appID
  699. appName:installation.firebaseAppName])
  700. .andReturn([FBLPromise resolvedWith:installation]);
  701. // 2. Expect API request to delete installation.
  702. OCMExpect([self.mockAPIService deleteInstallation:installation])
  703. .andReturn([FBLPromise resolvedWith:installation]);
  704. // 2.1. Don't expect backoff calls for the delete API method.
  705. OCMReject([self.mockBackoffController isNextRequestAllowed]);
  706. [self rejectBackoffEvent];
  707. // 3.1. Expect the installation to be removed from the storage.
  708. OCMExpect([self.mockInstallationsStore removeInstallationForAppID:installation.appID
  709. appName:installation.firebaseAppName])
  710. .andReturn([FBLPromise resolvedWith:[NSNull null]]);
  711. // 3.2. Expect IID to be deleted, because it is default app.
  712. OCMExpect([self.mockIIDStore deleteExistingIID])
  713. .andReturn([FBLPromise resolvedWith:[NSNull null]]);
  714. // 4. Expect FIRInstallationIDDidChangeNotification to be sent.
  715. XCTestExpectation *notificationExpectation =
  716. [self installationIDDidChangeNotificationExpectation];
  717. // 5. Call delete installation.
  718. FBLPromise<NSNull *> *promise = [self.controller deleteInstallation];
  719. // 6. Wait for operations to complete and check.
  720. XCTAssert(FBLWaitForPromisesWithTimeout(0.5));
  721. XCTAssertNil(promise.error);
  722. XCTAssertTrue(promise.isFulfilled);
  723. [self waitForExpectations:@[ notificationExpectation ] timeout:0.5];
  724. OCMVerifyAll(self.mockInstallationsStore);
  725. OCMVerifyAll(self.mockAPIService);
  726. OCMVerifyAll(self.mockIIDStore);
  727. OCMVerifyAll(self.mockBackoffController);
  728. }
  729. - (void)testDeleteUnregisteredInstallation {
  730. // 1. Expect installation to be requested from the store.
  731. FIRInstallationsItem *installation = [FIRInstallationsItem createUnregisteredInstallationItem];
  732. OCMExpect([self.mockInstallationsStore installationForAppID:installation.appID
  733. appName:installation.firebaseAppName])
  734. .andReturn([FBLPromise resolvedWith:installation]);
  735. // 2. Don't expect backoff calls for the delete API method.
  736. OCMReject([self.mockBackoffController isNextRequestAllowed]);
  737. [self rejectBackoffEvent];
  738. // 3. Don't expect API request to delete installation.
  739. OCMReject([self.mockAPIService deleteInstallation:[OCMArg any]]);
  740. // 4.1. Expect the installation to be removed from the storage.
  741. OCMExpect([self.mockInstallationsStore removeInstallationForAppID:installation.appID
  742. appName:installation.firebaseAppName])
  743. .andReturn([FBLPromise resolvedWith:[NSNull null]]);
  744. // 4.2. Expect IID to be deleted, because it is default app.
  745. OCMExpect([self.mockIIDStore deleteExistingIID])
  746. .andReturn([FBLPromise resolvedWith:[NSNull null]]);
  747. // 5. Expect FIRInstallationIDDidChangeNotification to be sent.
  748. XCTestExpectation *notificationExpectation =
  749. [self installationIDDidChangeNotificationExpectation];
  750. // 6. Call delete installation.
  751. FBLPromise<NSNull *> *promise = [self.controller deleteInstallation];
  752. // 7. Wait for operations to complete and check.
  753. XCTAssert(FBLWaitForPromisesWithTimeout(0.5));
  754. XCTAssertNil(promise.error);
  755. XCTAssertTrue(promise.isFulfilled);
  756. [self waitForExpectations:@[ notificationExpectation ] timeout:0.5];
  757. OCMVerifyAll(self.mockInstallationsStore);
  758. OCMVerifyAll(self.mockAPIService);
  759. OCMVerifyAll(self.mockIIDStore);
  760. OCMVerifyAll(self.mockBackoffController);
  761. }
  762. - (void)testDeleteRegisteredInstallation_WhenAPIRequestFails_ThenFailsAndInstallationIsNotRemoved {
  763. // 1. Expect installation to be requested from the store.
  764. FIRInstallationsItem *installation = [FIRInstallationsItem createRegisteredInstallationItem];
  765. OCMExpect([self.mockInstallationsStore installationForAppID:installation.appID
  766. appName:installation.firebaseAppName])
  767. .andReturn([FBLPromise resolvedWith:installation]);
  768. // 2. Expect API request to delete installation.
  769. FBLPromise *rejectedAPIPromise = [FBLPromise pendingPromise];
  770. NSError *error500 =
  771. [FIRInstallationsErrorUtil APIErrorWithHTTPCode:FIRInstallationsHTTPCodesServerInternalError];
  772. [rejectedAPIPromise reject:error500];
  773. OCMExpect([self.mockAPIService deleteInstallation:installation]).andReturn(rejectedAPIPromise);
  774. // 2.1. Don't expect backoff calls for the delete API method.
  775. OCMReject([self.mockBackoffController isNextRequestAllowed]);
  776. [self rejectBackoffEvent];
  777. // 3.1. Don't expect the installation to be removed from the storage.
  778. OCMReject([self.mockInstallationsStore removeInstallationForAppID:[OCMArg any]
  779. appName:[OCMArg any]]);
  780. // 3.2. Don't expect IID to be deleted.
  781. OCMReject([self.mockIIDStore deleteExistingIID]);
  782. // 4. Don't expect FIRInstallationIDDidChangeNotification to be sent.
  783. XCTestExpectation *notificationExpectation =
  784. [self installationIDDidChangeNotificationExpectation];
  785. notificationExpectation.inverted = YES;
  786. // 5. Call delete installation.
  787. FBLPromise<NSNull *> *promise = [self.controller deleteInstallation];
  788. // 6. Wait for operations to complete and check.
  789. XCTAssert(FBLWaitForPromisesWithTimeout(0.5));
  790. XCTAssertEqualObjects(promise.error, error500);
  791. XCTAssertTrue(promise.isRejected);
  792. [self waitForExpectations:@[ notificationExpectation ] timeout:0.5];
  793. OCMVerifyAll(self.mockInstallationsStore);
  794. OCMVerifyAll(self.mockAPIService);
  795. OCMVerifyAll(self.mockIIDStore);
  796. OCMVerifyAll(self.mockBackoffController);
  797. }
  798. - (void)testDeleteRegisteredInstallation_WhenAPIFailsWithNotFound_ThenInstallationIsRemoved {
  799. // 1. Expect installation to be requested from the store.
  800. FIRInstallationsItem *installation = [FIRInstallationsItem createRegisteredInstallationItem];
  801. OCMExpect([self.mockInstallationsStore installationForAppID:installation.appID
  802. appName:installation.firebaseAppName])
  803. .andReturn([FBLPromise resolvedWith:installation]);
  804. // 2. Expect API request to delete installation.
  805. FBLPromise *rejectedAPIPromise = [FBLPromise pendingPromise];
  806. [rejectedAPIPromise reject:[FIRInstallationsErrorUtil APIErrorWithHTTPCode:404]];
  807. OCMExpect([self.mockAPIService deleteInstallation:installation]).andReturn(rejectedAPIPromise);
  808. // 2.1. Don't expect backoff calls for the delete API method.
  809. OCMReject([self.mockBackoffController isNextRequestAllowed]);
  810. [self rejectBackoffEvent];
  811. // 3. Expect the installation to be removed from the storage.
  812. OCMExpect([self.mockInstallationsStore removeInstallationForAppID:installation.appID
  813. appName:installation.firebaseAppName])
  814. .andReturn([FBLPromise resolvedWith:[NSNull null]]);
  815. // 3.2. Expect IID to be deleted, because it is default app.
  816. OCMExpect([self.mockIIDStore deleteExistingIID])
  817. .andReturn([FBLPromise resolvedWith:[NSNull null]]);
  818. // 4. Expect FIRInstallationIDDidChangeNotification to be sent.
  819. XCTestExpectation *notificationExpectation =
  820. [self installationIDDidChangeNotificationExpectation];
  821. // 5. Call delete installation.
  822. FBLPromise<NSNull *> *promise = [self.controller deleteInstallation];
  823. // 6. Wait for operations to complete and check.
  824. XCTAssert(FBLWaitForPromisesWithTimeout(0.5));
  825. XCTAssertNil(promise.error);
  826. XCTAssertTrue(promise.isFulfilled);
  827. [self waitForExpectations:@[ notificationExpectation ] timeout:0.5];
  828. OCMVerifyAll(self.mockInstallationsStore);
  829. OCMVerifyAll(self.mockAPIService);
  830. OCMVerifyAll(self.mockIIDStore);
  831. OCMVerifyAll(self.mockBackoffController);
  832. }
  833. - (void)testDeleteInstallation_WhenThereIsOngoingAuthTokenRequest_ThenUsesItsResult {
  834. // 1. Stub mocks for auth token request.
  835. // 1.1. Expect installation to be requested from the store.
  836. FIRInstallationsItem *storedInstallation =
  837. [FIRInstallationsItem createRegisteredInstallationItem];
  838. OCMExpect([self.mockInstallationsStore installationForAppID:self.appID appName:self.appName])
  839. .andReturn([FBLPromise resolvedWith:storedInstallation]);
  840. // 1.2 Expect backoff controller to be requested.
  841. [self expectIsNextRequestAllowedWithResult:YES];
  842. // 1.3. Expect API request.
  843. FIRInstallationsItem *responseInstallation =
  844. [FIRInstallationsItem createRegisteredInstallationItem];
  845. responseInstallation.authToken.token =
  846. [responseInstallation.authToken.token stringByAppendingString:@"_new"];
  847. FBLPromise *pendingAuthTokenAPIPromise = [FBLPromise pendingPromise];
  848. OCMExpect([self.mockAPIService refreshAuthTokenForInstallation:storedInstallation])
  849. .andReturn(pendingAuthTokenAPIPromise);
  850. // 2. Send auth token request.
  851. [self.controller getAuthTokenForcingRefresh:YES];
  852. OCMVerifyAllWithDelay(self.mockInstallationsStore, 0.5);
  853. OCMVerifyAllWithDelay(self.mockAPIService, 0.5);
  854. // 3. Delete installation.
  855. // 3.1. Don't expect installation to be requested from the store.
  856. OCMReject([self.mockInstallationsStore installationForAppID:[OCMArg any] appName:[OCMArg any]]);
  857. // 3.2. Expect API request to delete the UPDATED installation.
  858. OCMExpect([self.mockAPIService deleteInstallation:responseInstallation])
  859. .andReturn([FBLPromise resolvedWith:responseInstallation]);
  860. // 3.3. Expect the UPDATED installation to be removed from the storage.
  861. OCMExpect([self.mockInstallationsStore
  862. removeInstallationForAppID:responseInstallation.appID
  863. appName:responseInstallation.firebaseAppName])
  864. .andReturn([FBLPromise resolvedWith:[NSNull null]]);
  865. // 3.4. Expect IID to be deleted, because it is default app.
  866. OCMExpect([self.mockIIDStore deleteExistingIID])
  867. .andReturn([FBLPromise resolvedWith:[NSNull null]]);
  868. // 3.4. Call delete installation.
  869. FBLPromise<NSNull *> *deletePromise = [self.controller deleteInstallation];
  870. // 4. Fulfill auth token promise to proceed.
  871. // 4.1. Expect new token to be stored on API response.
  872. OCMExpect([self.mockInstallationsStore saveInstallation:responseInstallation])
  873. .andReturn([FBLPromise resolvedWith:[NSNull null]]);
  874. [pendingAuthTokenAPIPromise fulfill:responseInstallation];
  875. // 5. Wait for operations to complete and check the result.
  876. XCTAssert(FBLWaitForPromisesWithTimeout(0.5));
  877. XCTAssertNil(deletePromise.error);
  878. XCTAssertTrue(deletePromise.isFulfilled);
  879. OCMVerifyAll(self.mockInstallationsStore);
  880. OCMVerifyAll(self.mockAPIService);
  881. OCMVerifyAll(self.mockIIDStore);
  882. OCMVerifyAll(self.mockBackoffController);
  883. }
  884. - (void)testDeleteInstallation_WhenNotDefaultApp_ThenIIDIsNotDeleted {
  885. // 0. Configure controller for not default app.
  886. NSString *appName = @"appName";
  887. [self setUpWithAppName:appName];
  888. // 1. Expect installation to be requested from the store.
  889. FIRInstallationsItem *installation =
  890. [FIRInstallationsItem createRegisteredInstallationItemWithAppID:self.appID appName:appName];
  891. OCMExpect([self.mockInstallationsStore installationForAppID:installation.appID
  892. appName:installation.firebaseAppName])
  893. .andReturn([FBLPromise resolvedWith:installation]);
  894. // 2. Expect API request to delete installation.
  895. OCMExpect([self.mockAPIService deleteInstallation:installation])
  896. .andReturn([FBLPromise resolvedWith:installation]);
  897. // 2.1. Don't expect backoff calls for the delete API method.
  898. OCMReject([self.mockBackoffController isNextRequestAllowed]);
  899. [self rejectBackoffEvent];
  900. // 3.1. Expect the installation to be removed from the storage.
  901. OCMExpect([self.mockInstallationsStore removeInstallationForAppID:installation.appID
  902. appName:installation.firebaseAppName])
  903. .andReturn([FBLPromise resolvedWith:[NSNull null]]);
  904. // 3.2. Don't expect IID to be deleted, because it is not a default app.
  905. OCMReject([self.mockIIDStore deleteExistingIID]);
  906. // 4. Expect FIRInstallationIDDidChangeNotification to be sent.
  907. XCTestExpectation *notificationExpectation =
  908. [self installationIDDidChangeNotificationExpectation];
  909. // 5. Call delete installation.
  910. FBLPromise<NSNull *> *promise = [self.controller deleteInstallation];
  911. // 6. Wait for operations to complete and check.
  912. XCTAssert(FBLWaitForPromisesWithTimeout(0.5));
  913. XCTAssertNil(promise.error);
  914. XCTAssertTrue(promise.isFulfilled);
  915. [self waitForExpectations:@[ notificationExpectation ] timeout:0.5];
  916. OCMVerifyAll(self.mockInstallationsStore);
  917. OCMVerifyAll(self.mockAPIService);
  918. OCMVerifyAll(self.mockIIDStore);
  919. OCMVerifyAll(self.mockBackoffController);
  920. }
  921. - (NSArray<XCTestExpectation *> *)expectInstallationToBeDeletedLocally:
  922. (FIRInstallationsItem *)installation {
  923. // 3.1. Expect the installation to be removed from the storage.
  924. OCMExpect([self.mockInstallationsStore removeInstallationForAppID:installation.appID
  925. appName:installation.firebaseAppName])
  926. .andReturn([FBLPromise resolvedWith:[NSNull null]]);
  927. // 3.2. Expect IID to be deleted, because it is default app.
  928. OCMExpect([self.mockIIDStore deleteExistingIID])
  929. .andReturn([FBLPromise resolvedWith:[NSNull null]]);
  930. // 4. Expect FIRInstallationIDDidChangeNotification to be sent.
  931. XCTestExpectation *notificationExpectation =
  932. [self installationIDDidChangeNotificationExpectation];
  933. return @[ notificationExpectation ];
  934. }
  935. // TODO: Test a single delete installation request at a time.
  936. #pragma mark - Notifications
  937. - (void)testFIDDidChangeNotificationIsSentWhenFIDCreated {
  938. // 1. Stub - no installation.
  939. // 1.2. FID store.
  940. [self expectInstallationsStoreGetInstallationNotFound];
  941. OCMStub([self.mockInstallationsStore saveInstallation:[OCMArg any]])
  942. .andReturn([FBLPromise resolvedWith:[NSNull null]]);
  943. // 1.3. IID store.
  944. [self expectStoredIIDNotFound];
  945. // 1.4. Expect backoff controller to be requested.
  946. [self expectIsNextRequestAllowedWithResult:YES];
  947. // 1.5. API Service.
  948. OCMExpect([self.mockAPIService registerInstallation:[OCMArg any]])
  949. .andReturn([FBLPromise resolvedWith:[FIRInstallationsItem createRegisteredInstallationItem]]);
  950. // 1.6. Expect backoff success.
  951. [self expectBackoffEvent:FIRInstallationsBackoffEventSuccess];
  952. // 2. Expect FIRInstallationIDDidChangeNotification to be sent.
  953. XCTestExpectation *notificationExpectation =
  954. [self installationIDDidChangeNotificationExpectation];
  955. // 3. Request FID.
  956. FBLPromise *promise = [self.controller getInstallationItem];
  957. XCTAssert(FBLWaitForPromisesWithTimeout(0.5));
  958. // 4. Check.
  959. XCTAssertNil(promise.error);
  960. XCTAssertNotNil(promise.value);
  961. [self waitForExpectations:@[ notificationExpectation ] timeout:0.5];
  962. OCMVerifyAll(self.mockInstallationsStore);
  963. OCMVerifyAll(self.mockIIDStore);
  964. OCMVerifyAll(self.mockAPIService);
  965. OCMVerifyAll(self.mockBackoffController);
  966. }
  967. - (void)testRegisterInstallation_WhenServerRespondsWithDifferentFID_ThenFIDDidChangeNotification {
  968. // 1.1. Expect installation to be requested from the store.
  969. FIRInstallationsItem *storedInstallation =
  970. [FIRInstallationsItem createUnregisteredInstallationItem];
  971. OCMExpect([self.mockInstallationsStore installationForAppID:self.appID appName:self.appName])
  972. .andReturn([FBLPromise resolvedWith:storedInstallation]);
  973. // 1.2. Expect backoff controller to be requested.
  974. [self expectIsNextRequestAllowedWithResult:YES];
  975. // 1.3. Expect register FID to be called.
  976. FIRInstallationsItem *receivedInstallation =
  977. [FIRInstallationsItem createRegisteredInstallationItem];
  978. receivedInstallation.firebaseInstallationID =
  979. [storedInstallation.firebaseInstallationID stringByAppendingString:@"_new"];
  980. OCMExpect([self.mockAPIService registerInstallation:storedInstallation])
  981. .andReturn([FBLPromise resolvedWith:receivedInstallation]);
  982. // 1.4. Expect backoff success.
  983. [self expectBackoffEvent:FIRInstallationsBackoffEventSuccess];
  984. // 1.5. Expect the received installation to be stored.
  985. OCMExpect([self.mockInstallationsStore saveInstallation:receivedInstallation])
  986. .andReturn([FBLPromise resolvedWith:[NSNull null]]);
  987. // 2. Expect FIRInstallationIDDidChangeNotification to be sent.
  988. XCTestExpectation *notificationExpectation =
  989. [self installationIDDidChangeNotificationExpectation];
  990. // 3. Request Auth Token.
  991. FBLPromise<FIRInstallationsItem *> *promise = [self.controller getAuthTokenForcingRefresh:NO];
  992. XCTAssert(FBLWaitForPromisesWithTimeout(0.5));
  993. // 4. Check.
  994. XCTAssertNil(promise.error);
  995. XCTAssertNotNil(promise.value);
  996. XCTAssertEqualObjects(promise.value.firebaseInstallationID,
  997. receivedInstallation.firebaseInstallationID);
  998. [self waitForExpectations:@[ notificationExpectation ] timeout:0.5];
  999. OCMVerifyAll(self.mockInstallationsStore);
  1000. OCMVerifyAll(self.mockAPIService);
  1001. OCMVerifyAll(self.mockBackoffController);
  1002. }
  1003. #pragma mark - Helpers
  1004. - (void)expectInstallationsStoreGetInstallationNotFound {
  1005. NSError *notFoundError =
  1006. [FIRInstallationsErrorUtil installationItemNotFoundForAppID:self.appID appName:self.appName];
  1007. FBLPromise *installationNotFoundPromise = [FBLPromise pendingPromise];
  1008. [installationNotFoundPromise reject:notFoundError];
  1009. OCMExpect([self.mockInstallationsStore installationForAppID:self.appID appName:self.appName])
  1010. .andReturn(installationNotFoundPromise);
  1011. }
  1012. - (void)expectInstallationsStoreGetInstallationCorruptedFID {
  1013. FIRInstallationsItem *storedInstallations = [FIRInstallationsItem createCorruptedItem];
  1014. OCMExpect([self.mockInstallationsStore installationForAppID:self.appID appName:self.appName])
  1015. .andReturn([FBLPromise resolvedWith:storedInstallations]);
  1016. }
  1017. - (void)expectStoredIIDNotFound {
  1018. FBLPromise *rejectedPromise = [FBLPromise pendingPromise];
  1019. [rejectedPromise reject:[FIRInstallationsErrorUtil keychainErrorWithFunction:@"" status:-1]];
  1020. OCMExpect([self.mockIIDStore existingIID]).andReturn(rejectedPromise);
  1021. OCMExpect([self.mockIIDTokenStore existingIIDDefaultToken]).andReturn(rejectedPromise);
  1022. }
  1023. - (void)assertValidCreatedInstallation:(FIRInstallationsItem *)installation {
  1024. XCTAssertEqualObjects([installation class], [FIRInstallationsItem class]);
  1025. XCTAssertEqualObjects(installation.appID, self.appID);
  1026. XCTAssertEqualObjects(installation.firebaseAppName, self.appName);
  1027. XCTAssertEqual(installation.registrationStatus, FIRInstallationStatusUnregistered);
  1028. XCTAssertNotNil(installation.firebaseInstallationID);
  1029. }
  1030. - (XCTestExpectation *)installationIDDidChangeNotificationExpectation {
  1031. XCTestExpectation *notificationExpectation = [self
  1032. expectationForNotification:FIRInstallationIDDidChangeNotification
  1033. object:nil
  1034. handler:^BOOL(NSNotification *_Nonnull notification) {
  1035. XCTAssertEqualObjects(
  1036. notification
  1037. .userInfo[kFIRInstallationIDDidChangeNotificationAppNameKey],
  1038. self.appName);
  1039. return YES;
  1040. }];
  1041. return notificationExpectation;
  1042. }
  1043. - (void)expectInstallationStoreToBeRequestedAndReturnInstallation:
  1044. (FIRInstallationsItem *)storedInstallation {
  1045. OCMExpect([self.mockInstallationsStore installationForAppID:self.appID appName:self.appName])
  1046. .andReturn([FBLPromise resolvedWith:storedInstallation]);
  1047. }
  1048. - (FIRInstallationsItem *)expectAuthTokenRefreshForInstallation:
  1049. (FIRInstallationsItem *)installation {
  1050. FIRInstallationsItem *responseInstallation =
  1051. [FIRInstallationsItem createRegisteredInstallationItem];
  1052. responseInstallation.authToken.token =
  1053. [responseInstallation.authToken.token stringByAppendingString:@"_new"];
  1054. // 1. Expect backoff controller to be requested.
  1055. [self expectIsNextRequestAllowedWithResult:YES];
  1056. // 2. Expect API request.
  1057. OCMExpect([self.mockAPIService refreshAuthTokenForInstallation:installation])
  1058. .andReturn([FBLPromise resolvedWith:responseInstallation]);
  1059. // 3. Expect new token to be stored.
  1060. OCMExpect([self.mockInstallationsStore saveInstallation:responseInstallation])
  1061. .andReturn([FBLPromise resolvedWith:[NSNull null]]);
  1062. // 4. Expect backoff success.
  1063. [self expectBackoffEvent:FIRInstallationsBackoffEventSuccess];
  1064. return responseInstallation;
  1065. }
  1066. - (void)expectIsNextRequestAllowedWithResult:(BOOL)isNextRequestAllowed {
  1067. OCMExpect([self.mockBackoffController isNextRequestAllowed]).andReturn(isNextRequestAllowed);
  1068. }
  1069. - (void)expectBackoffEvent:(FIRInstallationsBackoffEvent)event {
  1070. OCMExpect([self.mockBackoffController registerEvent:event]);
  1071. }
  1072. - (void)rejectBackoffEvent {
  1073. OCMReject([self.mockBackoffController registerEvent:FIRInstallationsBackoffEventSuccess]);
  1074. OCMReject(
  1075. [self.mockBackoffController registerEvent:FIRInstallationsBackoffEventRecoverableFailure]);
  1076. OCMReject(
  1077. [self.mockBackoffController registerEvent:FIRInstallationsBackoffEventUnrecoverableFailure]);
  1078. }
  1079. static const NSInteger kNoBackoffEvents = -1;
  1080. - (void)expectBackoffEvent:(FIRInstallationsBackoffEvent)event
  1081. forRegisterFIDAPIError:(NSError *)error {
  1082. // 1. Stub store get installation.
  1083. [self expectInstallationsStoreGetInstallationNotFound];
  1084. // 2. Expect IIDStore to be checked for existing IID.
  1085. [self expectStoredIIDNotFound];
  1086. // 3. Stub store save installation.
  1087. __block FIRInstallationsItem *createdInstallation;
  1088. OCMExpect([self.mockInstallationsStore
  1089. saveInstallation:[OCMArg checkWithBlock:^BOOL(FIRInstallationsItem *obj) {
  1090. [self assertValidCreatedInstallation:obj];
  1091. createdInstallation = obj;
  1092. return YES;
  1093. }]])
  1094. .andReturn([FBLPromise resolvedWith:[NSNull null]]);
  1095. // 4. Stub API register installation.
  1096. // 4.1. Expect backoff controller to be requested.
  1097. [self expectIsNextRequestAllowedWithResult:YES];
  1098. // 4.2. Verify installation to be registered.
  1099. id registerInstallationValidation = [OCMArg checkWithBlock:^BOOL(FIRInstallationsItem *obj) {
  1100. [self assertValidCreatedInstallation:obj];
  1101. XCTAssertEqual(obj.firebaseInstallationID.length, 22);
  1102. return YES;
  1103. }];
  1104. // 4.3. Expect for `registerInstallation` to be called.
  1105. FBLPromise<FIRInstallationsItem *> *registerPromise = [FBLPromise pendingPromise];
  1106. OCMExpect([self.mockAPIService registerInstallation:registerInstallationValidation])
  1107. .andReturn(registerPromise);
  1108. // 4.4. Expect backoff event.
  1109. if (event == kNoBackoffEvents) {
  1110. [self rejectBackoffEvent];
  1111. } else {
  1112. [self expectBackoffEvent:event];
  1113. }
  1114. // 5. Call get installation and check.
  1115. FBLPromise<FIRInstallationsItem *> *getInstallationPromise =
  1116. [self.controller getInstallationItem];
  1117. // 5.1. Wait for the stored item to be read and saved.
  1118. OCMVerifyAllWithDelay(self.mockInstallationsStore, 0.5);
  1119. // 5.2. Wait for `registerInstallation` to be called.
  1120. OCMVerifyAllWithDelay(self.mockAPIService, 0.5);
  1121. // 5.3. Resolve `registerPromise` to simulate finished registration.
  1122. [registerPromise reject:error];
  1123. // 5.4. Wait for the task to complete.
  1124. XCTAssert(FBLWaitForPromisesWithTimeout(0.5));
  1125. // Get installation returns a value no matter what.
  1126. XCTAssertNil(getInstallationPromise.error);
  1127. // We expect the initially created installation to be returned - must not wait for registration to
  1128. // complete here.
  1129. XCTAssertEqual(getInstallationPromise.value, createdInstallation);
  1130. // 5.5. Verify registered installation was saved.
  1131. OCMVerifyAll(self.mockInstallationsStore);
  1132. OCMVerifyAll(self.mockIIDStore);
  1133. OCMVerifyAll(self.mockBackoffController);
  1134. }
  1135. - (void)assertGetInstallation_NewFIDCreatedAndRegistered {
  1136. // 2. Stub store save installation.
  1137. __block FIRInstallationsItem *createdInstallation;
  1138. OCMExpect([self.mockInstallationsStore
  1139. saveInstallation:[OCMArg checkWithBlock:^BOOL(FIRInstallationsItem *obj) {
  1140. [self assertValidCreatedInstallation:obj];
  1141. createdInstallation = obj;
  1142. return YES;
  1143. }]])
  1144. .andReturn([FBLPromise resolvedWith:[NSNull null]]);
  1145. // 3. Stub API register installation.
  1146. // 3.1. Expect backoff controller to be requested.
  1147. [self expectIsNextRequestAllowedWithResult:YES];
  1148. // 3.2. Verify installation to be registered.
  1149. id registerInstallationValidation = [OCMArg checkWithBlock:^BOOL(FIRInstallationsItem *obj) {
  1150. [self assertValidCreatedInstallation:obj];
  1151. XCTAssertEqual(obj.firebaseInstallationID.length, 22);
  1152. return YES;
  1153. }];
  1154. // 3.3. Expect for `registerInstallation` to be called.
  1155. FBLPromise<FIRInstallationsItem *> *registerPromise = [FBLPromise pendingPromise];
  1156. OCMExpect([self.mockAPIService registerInstallation:registerInstallationValidation])
  1157. .andReturn(registerPromise);
  1158. // 3.4. Expect backoff success.
  1159. [self expectBackoffEvent:FIRInstallationsBackoffEventSuccess];
  1160. // 5. Call get installation and check.
  1161. FBLPromise<FIRInstallationsItem *> *getInstallationPromise =
  1162. [self.controller getInstallationItem];
  1163. // 5.1. Wait for the stored item to be read and saved.
  1164. OCMVerifyAllWithDelay(self.mockInstallationsStore, 0.5);
  1165. // 5.2. Wait for `registerInstallation` to be called.
  1166. OCMVerifyAllWithDelay(self.mockAPIService, 0.5);
  1167. // 5.3. Expect for the registered installation to be saved.
  1168. FIRInstallationsItem *registeredInstallation = [FIRInstallationsItem
  1169. createRegisteredInstallationItemWithAppID:createdInstallation.appID
  1170. appName:createdInstallation.firebaseAppName];
  1171. OCMExpect([self.mockInstallationsStore
  1172. saveInstallation:[OCMArg checkWithBlock:^BOOL(FIRInstallationsItem *obj) {
  1173. XCTAssertEqual(registeredInstallation, obj);
  1174. return YES;
  1175. }]])
  1176. .andReturn([FBLPromise resolvedWith:[NSNull null]]);
  1177. // 5.5. Resolve `registerPromise` to simulate finished registration.
  1178. [registerPromise fulfill:registeredInstallation];
  1179. // 5.4. Wait for the task to complete.
  1180. XCTAssert(FBLWaitForPromisesWithTimeout(0.5));
  1181. XCTAssertNil(getInstallationPromise.error);
  1182. // We expect the initially created installation to be returned - must not wait for registration to
  1183. // complete here.
  1184. XCTAssertEqual(getInstallationPromise.value, createdInstallation);
  1185. // 5.5. Verify registered installation was saved.
  1186. OCMVerifyAll(self.mockInstallationsStore);
  1187. OCMVerifyAll(self.mockIIDStore);
  1188. OCMVerifyAll(self.mockBackoffController);
  1189. }
  1190. @end