FIRInstallationsIDControllerTests.m 62 KB

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