FIRInstallationsIDControllerTests.m 53 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254
  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/FIRAppInternal.h>
  19. #import "FBLPromise+Testing.h"
  20. #import "FIRInstallationsErrorUtil+Tests.h"
  21. #import "FIRInstallationsItem+Tests.h"
  22. #import "XCTestCase+DateAsserts.h"
  23. #import "FIRInstallations.h"
  24. #import "FIRInstallationsAPIService.h"
  25. #import "FIRInstallationsErrorUtil.h"
  26. #import "FIRInstallationsHTTPError.h"
  27. #import "FIRInstallationsIDController.h"
  28. #import "FIRInstallationsIIDStore.h"
  29. #import "FIRInstallationsStore.h"
  30. #import "FIRInstallationsStoredAuthToken.h"
  31. #import "FIRInstallationsStoredRegistrationError.h"
  32. #import "FIRInstallationsStoredRegistrationParameters.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. @end
  40. @interface FIRInstallationsIDControllerTests : XCTestCase
  41. @property(nonatomic) FIRInstallationsIDController *controller;
  42. @property(nonatomic) id mockInstallationsStore;
  43. @property(nonatomic) id mockAPIService;
  44. @property(nonatomic) id mockIIDStore;
  45. @property(nonatomic) NSString *appID;
  46. @property(nonatomic) NSString *appName;
  47. @property(nonatomic) NSString *APIKey;
  48. @property(nonatomic) NSString *projectID;
  49. @end
  50. @implementation FIRInstallationsIDControllerTests
  51. - (void)setUp {
  52. [self setUpWithAppName:kFIRDefaultAppName];
  53. }
  54. - (void)setUpWithAppName:(NSString *)appName {
  55. self.appID = @"appID";
  56. self.appName = appName;
  57. self.APIKey = @"APIKey";
  58. self.projectID = @"projectID";
  59. self.mockInstallationsStore = OCMStrictClassMock([FIRInstallationsStore class]);
  60. self.mockAPIService = OCMStrictClassMock([FIRInstallationsAPIService class]);
  61. self.mockIIDStore = OCMStrictClassMock([FIRInstallationsIIDStore class]);
  62. [self stubAPIServiceParameters];
  63. self.controller =
  64. [[FIRInstallationsIDController alloc] initWithGoogleAppID:self.appID
  65. appName:self.appName
  66. installationsStore:self.mockInstallationsStore
  67. APIService:self.mockAPIService
  68. IIDStore:self.mockIIDStore];
  69. }
  70. - (void)tearDown {
  71. self.controller = nil;
  72. self.mockIIDStore = nil;
  73. self.mockAPIService = nil;
  74. self.mockInstallationsStore = nil;
  75. self.appID = nil;
  76. self.appName = nil;
  77. }
  78. - (void)stubAPIServiceParameters {
  79. OCMStub([self.mockAPIService APIKey]).andReturn(self.APIKey);
  80. OCMStub([self.mockAPIService projectID]).andReturn(self.projectID);
  81. }
  82. #pragma mark - Get Installation
  83. - (void)testGetInstallationItem_WhenFIDExists_ThenItIsReturned {
  84. FIRInstallationsItem *storedInstallations =
  85. [FIRInstallationsItem createRegisteredInstallationItem];
  86. OCMExpect([self.mockInstallationsStore installationForAppID:self.appID appName:self.appName])
  87. .andReturn([FBLPromise resolvedWith:storedInstallations]);
  88. // Don't expect FIRInstallationIDDidChangeNotification to be sent.
  89. XCTestExpectation *notificationExpectation =
  90. [self installationIDDidChangeNotificationExpectation];
  91. notificationExpectation.inverted = YES;
  92. FBLPromise<FIRInstallationsItem *> *promise = [self.controller getInstallationItem];
  93. XCTAssert(FBLWaitForPromisesWithTimeout(0.5));
  94. XCTAssertNil(promise.error);
  95. XCTAssertEqual(promise.value, storedInstallations);
  96. OCMVerifyAll(self.mockInstallationsStore);
  97. [self waitForExpectations:@[ notificationExpectation ] timeout:0.5];
  98. }
  99. - (void)testGetInstallationItem_WhenNoFIDAndNoIID_ThenFIDIsCreatedAndRegistered {
  100. // 1. Stub store get installation.
  101. [self expectInstallationsStoreGetInstallationNotFound];
  102. // 2. Stub store save installation.
  103. __block FIRInstallationsItem *createdInstallation;
  104. OCMExpect([self.mockInstallationsStore
  105. saveInstallation:[OCMArg checkWithBlock:^BOOL(FIRInstallationsItem *obj) {
  106. [self assertValidCreatedInstallation:obj];
  107. createdInstallation = obj;
  108. return YES;
  109. }]])
  110. .andReturn([FBLPromise resolvedWith:[NSNull null]]);
  111. // 3. Stub API register installation.
  112. // 3.1. Verify installation to be registered.
  113. id registerInstallationValidation = [OCMArg checkWithBlock:^BOOL(FIRInstallationsItem *obj) {
  114. [self assertValidCreatedInstallation:obj];
  115. XCTAssertEqual(obj.firebaseInstallationID.length, 22);
  116. return YES;
  117. }];
  118. // 3.2. Expect for `registerInstallation` to be called.
  119. FBLPromise<FIRInstallationsItem *> *registerPromise = [FBLPromise pendingPromise];
  120. OCMExpect([self.mockAPIService registerInstallation:registerInstallationValidation])
  121. .andReturn(registerPromise);
  122. // 4. Expect IIDStore to be checked for existing IID.
  123. FBLPromise *rejectedPromise = [FBLPromise pendingPromise];
  124. [rejectedPromise reject:[FIRInstallationsErrorUtil keychainErrorWithFunction:@"" status:-1]];
  125. OCMExpect([self.mockIIDStore existingIID]).andReturn(rejectedPromise);
  126. // 5. Call get installation and check.
  127. FBLPromise<FIRInstallationsItem *> *getInstallationPromise =
  128. [self.controller getInstallationItem];
  129. // 5.1. Wait for the stored item to be read and saved.
  130. OCMVerifyAllWithDelay(self.mockInstallationsStore, 0.5);
  131. // 5.2. Wait for `registerInstallation` to be called.
  132. OCMVerifyAllWithDelay(self.mockAPIService, 0.5);
  133. // 5.3. Expect for the registered installation to be saved.
  134. FIRInstallationsItem *registeredInstallation = [FIRInstallationsItem
  135. createRegisteredInstallationItemWithAppID:createdInstallation.appID
  136. appName:createdInstallation.firebaseAppName];
  137. OCMExpect([self.mockInstallationsStore
  138. saveInstallation:[OCMArg checkWithBlock:^BOOL(FIRInstallationsItem *obj) {
  139. XCTAssertEqual(registeredInstallation, obj);
  140. return YES;
  141. }]])
  142. .andReturn([FBLPromise resolvedWith:[NSNull null]]);
  143. // 5.5. Resolve `registerPromise` to simulate finished registration.
  144. [registerPromise fulfill:registeredInstallation];
  145. // 5.4. Wait for the task to complete.
  146. XCTAssert(FBLWaitForPromisesWithTimeout(0.5));
  147. XCTAssertNil(getInstallationPromise.error);
  148. // We expect the initially created installation to be returned - must not wait for registration to
  149. // complete here.
  150. XCTAssertEqual(getInstallationPromise.value, createdInstallation);
  151. // 5.5. Verify registered installation was saved.
  152. OCMVerifyAll(self.mockInstallationsStore);
  153. OCMVerifyAll(self.mockIIDStore);
  154. }
  155. - (void)testGetInstallationItem_WhenThereIsIIDAndNoFID_ThenFIDIsCreatedAndRegistered {
  156. // 0. Configure controller with not default app.
  157. NSString *appName = @"appName";
  158. [self setUpWithAppName:appName];
  159. // 1. Stub store get installation.
  160. [self expectInstallationsStoreGetInstallationNotFound];
  161. // 2. Don't expect IIDStore to be checked for existing IID (not default app).
  162. OCMReject([self.mockIIDStore existingIID]);
  163. // 3. Stub store save installation.
  164. __block FIRInstallationsItem *createdInstallation;
  165. OCMExpect([self.mockInstallationsStore
  166. saveInstallation:[OCMArg checkWithBlock:^BOOL(FIRInstallationsItem *obj) {
  167. [self assertValidCreatedInstallation:obj];
  168. createdInstallation = obj;
  169. return YES;
  170. }]])
  171. .andReturn([FBLPromise resolvedWith:[NSNull null]]);
  172. // 4. Stub API register installation.
  173. // 4.1. Verify installation to be registered.
  174. id registerInstallationValidation = [OCMArg checkWithBlock:^BOOL(FIRInstallationsItem *obj) {
  175. [self assertValidCreatedInstallation:obj];
  176. return YES;
  177. }];
  178. // 4.2. Expect for `registerInstallation` to be called.
  179. FBLPromise<FIRInstallationsItem *> *registerPromise = [FBLPromise pendingPromise];
  180. OCMExpect([self.mockAPIService registerInstallation:registerInstallationValidation])
  181. .andReturn(registerPromise);
  182. // 5. Call get installation and check.
  183. FBLPromise<FIRInstallationsItem *> *getInstallationPromise =
  184. [self.controller getInstallationItem];
  185. // 5.1. Wait for the stored item to be read and saved.
  186. OCMVerifyAllWithDelay(self.mockInstallationsStore, 0.5);
  187. // 5.2. Wait for `registerInstallation` to be called.
  188. OCMVerifyAllWithDelay(self.mockAPIService, 0.5);
  189. // 5.3. Expect for the registered installation to be saved.
  190. FIRInstallationsItem *registeredInstallation = [FIRInstallationsItem
  191. createRegisteredInstallationItemWithAppID:createdInstallation.appID
  192. appName:createdInstallation.firebaseAppName];
  193. OCMExpect([self.mockInstallationsStore
  194. saveInstallation:[OCMArg checkWithBlock:^BOOL(FIRInstallationsItem *obj) {
  195. XCTAssertEqual(registeredInstallation, obj);
  196. return YES;
  197. }]])
  198. .andReturn([FBLPromise resolvedWith:[NSNull null]]);
  199. // 5.5. Resolve `registerPromise` to simulate finished registration.
  200. [registerPromise fulfill:registeredInstallation];
  201. // 5.4. Wait for the task to complete.
  202. XCTAssert(FBLWaitForPromisesWithTimeout(0.5));
  203. XCTAssertNil(getInstallationPromise.error);
  204. // We expect the initially created installation to be returned - must not wait for registration to
  205. // complete here.
  206. XCTAssertEqual(getInstallationPromise.value, createdInstallation);
  207. // 5.5. Verify registered installation was saved.
  208. OCMVerifyAll(self.mockInstallationsStore);
  209. OCMVerifyAll(self.mockIIDStore);
  210. }
  211. - (void)testGetInstallationItem_WhenThereIsIIDAndNoFIDNotDefaultApp_ThenIIDIsUsedAsFID {
  212. // 1. Stub store get installation.
  213. [self expectInstallationsStoreGetInstallationNotFound];
  214. // 2. Expect IIDStore to be checked for existing IID.
  215. NSString *existingIID = @"existing-iid";
  216. OCMExpect([self.mockIIDStore existingIID]).andReturn([FBLPromise resolvedWith:existingIID]);
  217. // 3. Stub store save installation.
  218. __block FIRInstallationsItem *createdInstallation;
  219. OCMExpect([self.mockInstallationsStore
  220. saveInstallation:[OCMArg checkWithBlock:^BOOL(FIRInstallationsItem *obj) {
  221. [self assertValidCreatedInstallation:obj];
  222. XCTAssertEqualObjects(existingIID, obj.firebaseInstallationID);
  223. createdInstallation = obj;
  224. return YES;
  225. }]])
  226. .andReturn([FBLPromise resolvedWith:[NSNull null]]);
  227. // 4. Stub API register installation.
  228. // 4.1. 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.2. Expect for `registerInstallation` to be called.
  235. FBLPromise<FIRInstallationsItem *> *registerPromise = [FBLPromise pendingPromise];
  236. OCMExpect([self.mockAPIService registerInstallation:registerInstallationValidation])
  237. .andReturn(registerPromise);
  238. // 5. Call get installation and check.
  239. FBLPromise<FIRInstallationsItem *> *getInstallationPromise =
  240. [self.controller getInstallationItem];
  241. // 5.1. Wait for the stored item to be read and saved.
  242. OCMVerifyAllWithDelay(self.mockInstallationsStore, 0.5);
  243. // 5.2. Wait for `registerInstallation` to be called.
  244. OCMVerifyAllWithDelay(self.mockAPIService, 0.5);
  245. // 5.3. Expect for the registered installation to be saved.
  246. FIRInstallationsItem *registeredInstallation = [FIRInstallationsItem
  247. createRegisteredInstallationItemWithAppID:createdInstallation.appID
  248. appName:createdInstallation.firebaseAppName];
  249. OCMExpect([self.mockInstallationsStore
  250. saveInstallation:[OCMArg checkWithBlock:^BOOL(FIRInstallationsItem *obj) {
  251. XCTAssertEqual(registeredInstallation, obj);
  252. return YES;
  253. }]])
  254. .andReturn([FBLPromise resolvedWith:[NSNull null]]);
  255. // 5.5. Resolve `registerPromise` to simulate finished registration.
  256. [registerPromise fulfill:registeredInstallation];
  257. // 5.4. Wait for the task to complete.
  258. XCTAssert(FBLWaitForPromisesWithTimeout(0.5));
  259. XCTAssertNil(getInstallationPromise.error);
  260. // We expect the initially created installation to be returned - must not wait for registration to
  261. // complete here.
  262. XCTAssertEqual(getInstallationPromise.value, createdInstallation);
  263. // 5.5. Verify registered installation was saved.
  264. OCMVerifyAll(self.mockInstallationsStore);
  265. OCMVerifyAll(self.mockIIDStore);
  266. }
  267. - (void)testGetInstallationItem_WhenCalledSeveralTimes_OnlyOneOperationIsPerformed {
  268. // 1. Expect the installation to be requested from the store only once.
  269. FIRInstallationsItem *storedInstallation1 =
  270. [FIRInstallationsItem createRegisteredInstallationItem];
  271. FBLPromise<FIRInstallationsItem *> *pendingStorePromise = [FBLPromise pendingPromise];
  272. OCMExpect([self.mockInstallationsStore installationForAppID:self.appID appName:self.appName])
  273. .andReturn(pendingStorePromise);
  274. // 3. Request installation n times
  275. NSInteger requestCount = 10;
  276. NSMutableArray *installationPromises = [NSMutableArray arrayWithCapacity:requestCount];
  277. for (NSInteger i = 0; i < requestCount; i++) {
  278. [installationPromises addObject:[self.controller getInstallationItem]];
  279. }
  280. // 4. Resolve store promise.
  281. [pendingStorePromise fulfill:storedInstallation1];
  282. // 5. Wait for operation to be completed and check.
  283. XCTAssert(FBLWaitForPromisesWithTimeout(0.5));
  284. for (FBLPromise<FIRInstallationsItem *> *installationPromise in installationPromises) {
  285. XCTAssertNil(installationPromise.error);
  286. XCTAssertEqual(installationPromise.value, storedInstallation1);
  287. }
  288. OCMVerifyAll(self.mockInstallationsStore);
  289. OCMVerifyAll(self.mockAPIService);
  290. // 6. Check that a new request is performed once previous finished.
  291. FIRInstallationsItem *storedInstallation2 =
  292. [FIRInstallationsItem createRegisteredInstallationItem];
  293. OCMExpect([self.mockInstallationsStore installationForAppID:self.appID appName:self.appName])
  294. .andReturn([FBLPromise resolvedWith:storedInstallation2]);
  295. FBLPromise<FIRInstallationsItem *> *installationPromise = [self.controller getInstallationItem];
  296. XCTAssert(FBLWaitForPromisesWithTimeout(0.5));
  297. XCTAssertNil(installationPromise.error);
  298. XCTAssertEqual(installationPromise.value, storedInstallation2);
  299. OCMVerifyAll(self.mockInstallationsStore);
  300. OCMVerifyAll(self.mockAPIService);
  301. }
  302. #pragma mark - Get Auth Token
  303. - (void)testGetAuthToken_WhenValidInstallationExists_ThenItIsReturned {
  304. // 1. Expect installation to be requested from the store.
  305. FIRInstallationsItem *storedInstallation =
  306. [FIRInstallationsItem createRegisteredInstallationItem];
  307. OCMExpect([self.mockInstallationsStore installationForAppID:self.appID appName:self.appName])
  308. .andReturn([FBLPromise resolvedWith:storedInstallation]);
  309. // 2. Request auth token.
  310. FBLPromise<FIRInstallationsItem *> *promise = [self.controller getAuthTokenForcingRefresh:NO];
  311. // 3. Wait for the promise to resolve.
  312. FBLWaitForPromisesWithTimeout(0.5);
  313. // 4. Check.
  314. OCMVerifyAll(self.mockInstallationsStore);
  315. OCMVerifyAll(self.mockAPIService);
  316. XCTAssertNil(promise.error);
  317. XCTAssertNotNil(promise.value);
  318. XCTAssertEqualObjects(promise.value.authToken.token, storedInstallation.authToken.token);
  319. XCTAssertEqualObjects(promise.value.authToken.expirationDate,
  320. storedInstallation.authToken.expirationDate);
  321. }
  322. - (void)testGetAuthToken_WhenValidInstallationWithExpiredTokenExists_ThenTokenRequested {
  323. // 1.1. Expect installation to be requested from the store.
  324. FIRInstallationsItem *storedInstallation =
  325. [FIRInstallationsItem createRegisteredInstallationItem];
  326. storedInstallation.authToken.expirationDate = [NSDate dateWithTimeIntervalSinceNow:60 * 60 - 1];
  327. OCMExpect([self.mockInstallationsStore installationForAppID:self.appID appName:self.appName])
  328. .andReturn([FBLPromise resolvedWith:storedInstallation]);
  329. // 1.2. Expect API request.
  330. FIRInstallationsItem *responseInstallation =
  331. [FIRInstallationsItem createRegisteredInstallationItem];
  332. responseInstallation.authToken.token =
  333. [responseInstallation.authToken.token stringByAppendingString:@"_new"];
  334. OCMExpect([self.mockAPIService refreshAuthTokenForInstallation:storedInstallation])
  335. .andReturn([FBLPromise resolvedWith:responseInstallation]);
  336. // 2. Request auth token.
  337. FBLPromise<FIRInstallationsItem *> *promise = [self.controller getAuthTokenForcingRefresh:NO];
  338. // 3. Wait for the promise to resolve.
  339. FBLWaitForPromisesWithTimeout(0.5);
  340. // 4. Check.
  341. OCMVerifyAll(self.mockInstallationsStore);
  342. OCMVerifyAll(self.mockAPIService);
  343. XCTAssertNil(promise.error);
  344. XCTAssertNotNil(promise.value);
  345. XCTAssertEqualObjects(promise.value.authToken.token, responseInstallation.authToken.token);
  346. XCTAssertEqualObjects(promise.value.authToken.expirationDate,
  347. responseInstallation.authToken.expirationDate);
  348. }
  349. - (void)testGetAuthTokenForcingRefresh_WhenValidInstallationExists_ThenTokenRequested {
  350. // 1.1. Expect installation to be requested from the store.
  351. FIRInstallationsItem *storedInstallation =
  352. [FIRInstallationsItem createRegisteredInstallationItem];
  353. OCMExpect([self.mockInstallationsStore installationForAppID:self.appID appName:self.appName])
  354. .andReturn([FBLPromise resolvedWith:storedInstallation]);
  355. // 1.2. Expect API request.
  356. FIRInstallationsItem *responseInstallation =
  357. [FIRInstallationsItem createRegisteredInstallationItem];
  358. responseInstallation.authToken.token =
  359. [responseInstallation.authToken.token stringByAppendingString:@"_new"];
  360. OCMExpect([self.mockAPIService refreshAuthTokenForInstallation:storedInstallation])
  361. .andReturn([FBLPromise resolvedWith:responseInstallation]);
  362. // 2. Request auth token.
  363. FBLPromise<FIRInstallationsItem *> *promise = [self.controller getAuthTokenForcingRefresh:YES];
  364. // 3. Wait for the promise to resolve.
  365. FBLWaitForPromisesWithTimeout(0.5);
  366. // 4. Check.
  367. OCMVerifyAll(self.mockInstallationsStore);
  368. OCMVerifyAll(self.mockAPIService);
  369. XCTAssertNil(promise.error);
  370. XCTAssertNotNil(promise.value);
  371. XCTAssertEqualObjects(promise.value.authToken.token, responseInstallation.authToken.token);
  372. XCTAssertEqualObjects(promise.value.authToken.expirationDate,
  373. responseInstallation.authToken.expirationDate);
  374. }
  375. - (void)testGetAuthToken_WhenCalledSeveralTimes_OnlyOneOperationIsPerformed {
  376. // 1. Expect installation to be requested from the store.
  377. FIRInstallationsItem *storedInstallation =
  378. [FIRInstallationsItem createRegisteredInstallationItem];
  379. FBLPromise *storagePendingPromise = [FBLPromise pendingPromise];
  380. // Expect the installation to be requested only once.
  381. OCMExpect([self.mockInstallationsStore installationForAppID:self.appID appName:self.appName])
  382. .andReturn(storagePendingPromise);
  383. // 2. Request auth token n times.
  384. NSInteger requestCount = 10;
  385. NSMutableArray *authTokenPromises = [NSMutableArray arrayWithCapacity:requestCount];
  386. for (NSInteger i = 0; i < requestCount; i++) {
  387. [authTokenPromises addObject:[self.controller getAuthTokenForcingRefresh:NO]];
  388. }
  389. // 3. Finish the storage request.
  390. [storagePendingPromise fulfill:storedInstallation];
  391. // 4. Wait for the promise to resolve.
  392. FBLWaitForPromisesWithTimeout(0.5);
  393. // 5. Check.
  394. OCMVerifyAll(self.mockInstallationsStore);
  395. for (FBLPromise<FIRInstallationsItem *> *authPromise in authTokenPromises) {
  396. XCTAssertNil(authPromise.error);
  397. XCTAssertNotNil(authPromise.value);
  398. XCTAssertEqualObjects(authPromise.value.authToken.token, storedInstallation.authToken.token);
  399. XCTAssertEqualObjects(authPromise.value.authToken.expirationDate,
  400. storedInstallation.authToken.expirationDate);
  401. }
  402. }
  403. - (void)testGetAuthTokenForceRefresh_WhenCalledSeveralTimes_OnlyOneOperationIsPerformed {
  404. // 1.1. Expect installation to be requested from the store.
  405. FIRInstallationsItem *storedInstallation =
  406. [FIRInstallationsItem createRegisteredInstallationItem];
  407. OCMExpect([self.mockInstallationsStore installationForAppID:self.appID appName:self.appName])
  408. .andReturn([FBLPromise resolvedWith:storedInstallation]);
  409. // 1.2. Expect API request.
  410. FIRInstallationsItem *responseInstallation =
  411. [FIRInstallationsItem createRegisteredInstallationItem];
  412. responseInstallation.authToken.token =
  413. [responseInstallation.authToken.token stringByAppendingString:@"_new"];
  414. FBLPromise *pendingAPIPromise = [FBLPromise pendingPromise];
  415. OCMExpect([self.mockAPIService refreshAuthTokenForInstallation:storedInstallation])
  416. .andReturn(pendingAPIPromise);
  417. // 2. Request auth token n times.
  418. NSInteger requestCount = 10;
  419. NSMutableArray *authTokenPromises = [NSMutableArray arrayWithCapacity:requestCount];
  420. for (NSInteger i = 0; i < requestCount; i++) {
  421. [authTokenPromises addObject:[self.controller getAuthTokenForcingRefresh:YES]];
  422. }
  423. // 3. Finish the API request.
  424. [pendingAPIPromise fulfill:responseInstallation];
  425. // 4. Wait for the promise to resolve.
  426. FBLWaitForPromisesWithTimeout(0.5);
  427. // 5. Check.
  428. OCMVerifyAll(self.mockInstallationsStore);
  429. for (FBLPromise<FIRInstallationsItem *> *authPromise in authTokenPromises) {
  430. XCTAssertNil(authPromise.error);
  431. XCTAssertNotNil(authPromise.value);
  432. XCTAssertEqualObjects(authPromise.value.authToken.token, responseInstallation.authToken.token);
  433. XCTAssertEqualObjects(authPromise.value.authToken.expirationDate,
  434. responseInstallation.authToken.expirationDate);
  435. }
  436. }
  437. - (void)testGetAuthToken_WhenAPIResponse401_ThenFISResetAndReregistered {
  438. NSTimeInterval timeout = 0.5;
  439. // 1.1. Expect installation to be requested from the store.
  440. FIRInstallationsItem *storedInstallation =
  441. [FIRInstallationsItem createRegisteredInstallationItem];
  442. [self expectInstallationStoreToBeRequestedAndReturnInstallation:storedInstallation];
  443. // 1.2. Expect API request.
  444. FBLPromise *rejectedAPIPromise = [FBLPromise pendingPromise];
  445. OCMExpect([self.mockAPIService refreshAuthTokenForInstallation:storedInstallation])
  446. .andReturn(rejectedAPIPromise);
  447. // 2. Request auth token.
  448. FBLPromise<FIRInstallationsItem *> *promise = [self.controller getAuthTokenForcingRefresh:YES];
  449. // 3. Wait for refresh token request.
  450. OCMVerifyAllWithDelay(self.mockAPIService, timeout);
  451. // 4.1. Expect Installation to be requested before deletion.
  452. [self expectInstallationStoreToBeRequestedAndReturnInstallation:storedInstallation];
  453. // 4. Expect for FIS to be deleted locally.
  454. NSArray<XCTestExpectation *> *deleteExpectations =
  455. [self expectInstallationToBeDeletedLocally:storedInstallation];
  456. // 6. Expect a new installation to be created and registered.
  457. // 6.1. Expect to request FIS from storage.
  458. [self expectInstallationsStoreGetInstallationNotFound];
  459. // 6.2. Expect stored IID not found.
  460. [self expectStoredIIDNotFound];
  461. // 6.3. Expect new Installation to be stored.
  462. __block FIRInstallationsItem *createdInstallation;
  463. OCMExpect([self.mockInstallationsStore
  464. saveInstallation:[OCMArg checkWithBlock:^BOOL(FIRInstallationsItem *obj) {
  465. [self assertValidCreatedInstallation:obj];
  466. createdInstallation = obj;
  467. return YES;
  468. }]])
  469. .andReturn([FBLPromise resolvedWith:[NSNull null]]);
  470. // 6.4. Expect registration API request to be sent.
  471. FBLPromise<FIRInstallationsItem *> *registerPromise = [FBLPromise pendingPromise];
  472. OCMExpect([self.mockAPIService registerInstallation:[OCMArg any]]).andReturn(registerPromise);
  473. // 6.5. Reject API request promise with 401.
  474. NSError *error401 = [FIRInstallationsErrorUtil APIErrorWithHTTPCode:404];
  475. [rejectedAPIPromise reject:error401];
  476. // 6.6. Wait local FIS to be deleted.
  477. [self waitForExpectations:deleteExpectations timeout:timeout];
  478. // 6.7 Wait for the new Installation to be stored.
  479. OCMVerifyAllWithDelay(self.mockInstallationsStore, timeout);
  480. // 6.8. Wait for registration API request to be sent.
  481. OCMVerifyAllWithDelay(self.mockAPIService, timeout);
  482. // 6.9. Expect for the registered installation to be saved.
  483. FIRInstallationsItem *registeredInstallation = [FIRInstallationsItem
  484. createRegisteredInstallationItemWithAppID:createdInstallation.appID
  485. appName:createdInstallation.firebaseAppName];
  486. OCMExpect([self.mockInstallationsStore
  487. saveInstallation:[OCMArg checkWithBlock:^BOOL(FIRInstallationsItem *obj) {
  488. XCTAssertEqual(registeredInstallation, obj);
  489. return YES;
  490. }]])
  491. .andReturn([FBLPromise resolvedWith:[NSNull null]]);
  492. // 6.9. Fulfill the registration API request promise.
  493. [registerPromise fulfill:registeredInstallation];
  494. // 7. Wait for promises.
  495. XCTAssert(FBLWaitForPromisesWithTimeout(timeout));
  496. // 8. Check.
  497. OCMVerifyAll(self.mockInstallationsStore);
  498. OCMVerifyAll(self.mockAPIService);
  499. XCTAssertNil(promise.error);
  500. XCTAssertNotNil(promise.value);
  501. XCTAssertNotEqualObjects(promise.value.firebaseInstallationID,
  502. storedInstallation.firebaseInstallationID);
  503. XCTAssertEqualObjects(promise.value, registeredInstallation);
  504. }
  505. #pragma mark - FID Deletion
  506. - (void)testDeleteRegisteredInstallation {
  507. // 1. Expect installation to be requested from the store.
  508. FIRInstallationsItem *installation = [FIRInstallationsItem createRegisteredInstallationItem];
  509. OCMExpect([self.mockInstallationsStore installationForAppID:installation.appID
  510. appName:installation.firebaseAppName])
  511. .andReturn([FBLPromise resolvedWith:installation]);
  512. // 2. Expect API request to delete installation.
  513. OCMExpect([self.mockAPIService deleteInstallation:installation])
  514. .andReturn([FBLPromise resolvedWith:installation]);
  515. // 3.1. Expect the installation to be removed from the storage.
  516. OCMExpect([self.mockInstallationsStore removeInstallationForAppID:installation.appID
  517. appName:installation.firebaseAppName])
  518. .andReturn([FBLPromise resolvedWith:[NSNull null]]);
  519. // 3.2. Expect IID to be deleted, because it is default app.
  520. OCMExpect([self.mockIIDStore deleteExistingIID])
  521. .andReturn([FBLPromise resolvedWith:[NSNull null]]);
  522. // 4. Expect FIRInstallationIDDidChangeNotification to be sent.
  523. XCTestExpectation *notificationExpectation =
  524. [self installationIDDidChangeNotificationExpectation];
  525. // 5. Call delete installation.
  526. FBLPromise<NSNull *> *promise = [self.controller deleteInstallation];
  527. // 6. Wait for operations to complete and check.
  528. FBLWaitForPromisesWithTimeout(0.5);
  529. XCTAssertNil(promise.error);
  530. XCTAssertTrue(promise.isFulfilled);
  531. [self waitForExpectations:@[ notificationExpectation ] timeout:0.5];
  532. OCMVerifyAll(self.mockInstallationsStore);
  533. OCMVerifyAll(self.mockAPIService);
  534. OCMVerifyAll(self.mockIIDStore);
  535. }
  536. - (void)testDeleteUnregisteredInstallation {
  537. // 1. Expect installation to be requested from the store.
  538. FIRInstallationsItem *installation = [FIRInstallationsItem createUnregisteredInstallationItem];
  539. OCMExpect([self.mockInstallationsStore installationForAppID:installation.appID
  540. appName:installation.firebaseAppName])
  541. .andReturn([FBLPromise resolvedWith:installation]);
  542. // 2. Don't expect API request to delete installation.
  543. OCMReject([self.mockAPIService deleteInstallation:[OCMArg any]]);
  544. // 3.1. Expect the installation to be removed from the storage.
  545. OCMExpect([self.mockInstallationsStore removeInstallationForAppID:installation.appID
  546. appName:installation.firebaseAppName])
  547. .andReturn([FBLPromise resolvedWith:[NSNull null]]);
  548. // 3.2. Expect IID to be deleted, because it is default app.
  549. OCMExpect([self.mockIIDStore deleteExistingIID])
  550. .andReturn([FBLPromise resolvedWith:[NSNull null]]);
  551. // 4. Expect FIRInstallationIDDidChangeNotification to be sent.
  552. XCTestExpectation *notificationExpectation =
  553. [self installationIDDidChangeNotificationExpectation];
  554. // 5. Call delete installation.
  555. FBLPromise<NSNull *> *promise = [self.controller deleteInstallation];
  556. // 6. Wait for operations to complete and check.
  557. FBLWaitForPromisesWithTimeout(0.5);
  558. XCTAssertNil(promise.error);
  559. XCTAssertTrue(promise.isFulfilled);
  560. [self waitForExpectations:@[ notificationExpectation ] timeout:0.5];
  561. OCMVerifyAll(self.mockInstallationsStore);
  562. OCMVerifyAll(self.mockAPIService);
  563. OCMVerifyAll(self.mockIIDStore);
  564. }
  565. - (void)testDeleteRegisteredInstallation_WhenAPIRequestFails_ThenFailsAndInstallationIsNotRemoved {
  566. // 1. Expect installation to be requested from the store.
  567. FIRInstallationsItem *installation = [FIRInstallationsItem createRegisteredInstallationItem];
  568. OCMExpect([self.mockInstallationsStore installationForAppID:installation.appID
  569. appName:installation.firebaseAppName])
  570. .andReturn([FBLPromise resolvedWith:installation]);
  571. // 2. Expect API request to delete installation.
  572. FBLPromise *rejectedAPIPromise = [FBLPromise pendingPromise];
  573. NSError *error500 =
  574. [FIRInstallationsErrorUtil APIErrorWithHTTPCode:FIRInstallationsHTTPCodesServerInternalError];
  575. [rejectedAPIPromise reject:error500];
  576. OCMExpect([self.mockAPIService deleteInstallation:installation]).andReturn(rejectedAPIPromise);
  577. // 3.1. Don't expect the installation to be removed from the storage.
  578. OCMReject([self.mockInstallationsStore removeInstallationForAppID:[OCMArg any]
  579. appName:[OCMArg any]]);
  580. // 3.2. Don't expect IID to be deleted.
  581. OCMReject([self.mockIIDStore deleteExistingIID]);
  582. // 4. Don't expect FIRInstallationIDDidChangeNotification to be sent.
  583. XCTestExpectation *notificationExpectation =
  584. [self installationIDDidChangeNotificationExpectation];
  585. notificationExpectation.inverted = YES;
  586. // 5. Call delete installation.
  587. FBLPromise<NSNull *> *promise = [self.controller deleteInstallation];
  588. // 6. Wait for operations to complete and check.
  589. FBLWaitForPromisesWithTimeout(0.5);
  590. XCTAssertEqualObjects(promise.error, error500);
  591. XCTAssertTrue(promise.isRejected);
  592. [self waitForExpectations:@[ notificationExpectation ] timeout:0.5];
  593. OCMVerifyAll(self.mockInstallationsStore);
  594. OCMVerifyAll(self.mockAPIService);
  595. OCMVerifyAll(self.mockIIDStore);
  596. }
  597. - (void)testDeleteRegisteredInstallation_WhenAPIFailsWithNotFound_ThenInstallationIsRemoved {
  598. // 1. Expect installation to be requested from the store.
  599. FIRInstallationsItem *installation = [FIRInstallationsItem createRegisteredInstallationItem];
  600. OCMExpect([self.mockInstallationsStore installationForAppID:installation.appID
  601. appName:installation.firebaseAppName])
  602. .andReturn([FBLPromise resolvedWith:installation]);
  603. // 2. Expect API request to delete installation.
  604. FBLPromise *rejectedAPIPromise = [FBLPromise pendingPromise];
  605. [rejectedAPIPromise reject:[FIRInstallationsErrorUtil APIErrorWithHTTPCode:404]];
  606. OCMExpect([self.mockAPIService deleteInstallation:installation]).andReturn(rejectedAPIPromise);
  607. // 3. Expect the installation to be removed from the storage.
  608. OCMExpect([self.mockInstallationsStore removeInstallationForAppID:installation.appID
  609. appName:installation.firebaseAppName])
  610. .andReturn([FBLPromise resolvedWith:[NSNull null]]);
  611. // 3.2. Expect IID to be deleted, because it is default app.
  612. OCMExpect([self.mockIIDStore deleteExistingIID])
  613. .andReturn([FBLPromise resolvedWith:[NSNull null]]);
  614. // 4. Expect FIRInstallationIDDidChangeNotification to be sent.
  615. XCTestExpectation *notificationExpectation =
  616. [self installationIDDidChangeNotificationExpectation];
  617. // 5. Call delete installation.
  618. FBLPromise<NSNull *> *promise = [self.controller deleteInstallation];
  619. // 6. Wait for operations to complete and check.
  620. FBLWaitForPromisesWithTimeout(0.5);
  621. XCTAssertNil(promise.error);
  622. XCTAssertTrue(promise.isFulfilled);
  623. [self waitForExpectations:@[ notificationExpectation ] timeout:0.5];
  624. OCMVerifyAll(self.mockInstallationsStore);
  625. OCMVerifyAll(self.mockAPIService);
  626. OCMVerifyAll(self.mockIIDStore);
  627. }
  628. - (void)testDeleteInstallation_WhenThereIsOngoingAuthTokenRequest_ThenUsesItsResult {
  629. // 1. Stub mocks for auth token request.
  630. // 1.1. Expect installation to be requested from the store.
  631. FIRInstallationsItem *storedInstallation =
  632. [FIRInstallationsItem createRegisteredInstallationItem];
  633. OCMExpect([self.mockInstallationsStore installationForAppID:self.appID appName:self.appName])
  634. .andReturn([FBLPromise resolvedWith:storedInstallation]);
  635. // 1.2. Expect API request.
  636. FIRInstallationsItem *responseInstallation =
  637. [FIRInstallationsItem createRegisteredInstallationItem];
  638. responseInstallation.authToken.token =
  639. [responseInstallation.authToken.token stringByAppendingString:@"_new"];
  640. FBLPromise *pendingAuthTokenAPIPromise = [FBLPromise pendingPromise];
  641. OCMExpect([self.mockAPIService refreshAuthTokenForInstallation:storedInstallation])
  642. .andReturn(pendingAuthTokenAPIPromise);
  643. // 2. Send auth token request.
  644. [self.controller getAuthTokenForcingRefresh:YES];
  645. OCMVerifyAllWithDelay(self.mockInstallationsStore, 0.5);
  646. OCMVerifyAllWithDelay(self.mockAPIService, 0.5);
  647. // 3. Delete installation.
  648. // 3.1. Don't expect installation to be requested from the store.
  649. OCMReject([self.mockInstallationsStore installationForAppID:[OCMArg any] appName:[OCMArg any]]);
  650. // 3.2. Expect API request to delete the UPDATED installation.
  651. OCMExpect([self.mockAPIService deleteInstallation:responseInstallation])
  652. .andReturn([FBLPromise resolvedWith:responseInstallation]);
  653. // 3.3. Expect the UPDATED installation to be removed from the storage.
  654. OCMExpect([self.mockInstallationsStore
  655. removeInstallationForAppID:responseInstallation.appID
  656. appName:responseInstallation.firebaseAppName])
  657. .andReturn([FBLPromise resolvedWith:[NSNull null]]);
  658. // 3.4. Expect IID to be deleted, because it is default app.
  659. OCMExpect([self.mockIIDStore deleteExistingIID])
  660. .andReturn([FBLPromise resolvedWith:[NSNull null]]);
  661. // 3.4. Call delete installation.
  662. FBLPromise<NSNull *> *deletePromise = [self.controller deleteInstallation];
  663. // 4. Fulfill auth token promise to proceed.
  664. [pendingAuthTokenAPIPromise fulfill:responseInstallation];
  665. // 5. Wait for operations to complete and check the result.
  666. FBLWaitForPromisesWithTimeout(0.5);
  667. XCTAssertNil(deletePromise.error);
  668. XCTAssertTrue(deletePromise.isFulfilled);
  669. OCMVerifyAll(self.mockInstallationsStore);
  670. OCMVerifyAll(self.mockAPIService);
  671. OCMVerifyAll(self.mockIIDStore);
  672. }
  673. - (void)testDeleteInstallation_WhenNotDefaultApp_ThenIIDIsNotDeleted {
  674. // 0. Configure controller for not default app.
  675. NSString *appName = @"appName";
  676. [self setUpWithAppName:appName];
  677. // 1. Expect installation to be requested from the store.
  678. FIRInstallationsItem *installation =
  679. [FIRInstallationsItem createRegisteredInstallationItemWithAppID:self.appID appName:appName];
  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. // 3.1. Expect the installation to be removed from the storage.
  687. OCMExpect([self.mockInstallationsStore removeInstallationForAppID:installation.appID
  688. appName:installation.firebaseAppName])
  689. .andReturn([FBLPromise resolvedWith:[NSNull null]]);
  690. // 3.2. Don't expect IID to be deleted, because it is not a default app.
  691. OCMReject([self.mockIIDStore deleteExistingIID]);
  692. // 4. Expect FIRInstallationIDDidChangeNotification to be sent.
  693. XCTestExpectation *notificationExpectation =
  694. [self installationIDDidChangeNotificationExpectation];
  695. // 5. Call delete installation.
  696. FBLPromise<NSNull *> *promise = [self.controller deleteInstallation];
  697. // 6. Wait for operations to complete and check.
  698. FBLWaitForPromisesWithTimeout(0.5);
  699. XCTAssertNil(promise.error);
  700. XCTAssertTrue(promise.isFulfilled);
  701. [self waitForExpectations:@[ notificationExpectation ] timeout:0.5];
  702. OCMVerifyAll(self.mockInstallationsStore);
  703. OCMVerifyAll(self.mockAPIService);
  704. OCMVerifyAll(self.mockIIDStore);
  705. }
  706. - (NSArray<XCTestExpectation *> *)expectInstallationToBeDeletedLocally:
  707. (FIRInstallationsItem *)installation {
  708. // 3.1. Expect the installation to be removed from the storage.
  709. OCMExpect([self.mockInstallationsStore removeInstallationForAppID:installation.appID
  710. appName:installation.firebaseAppName])
  711. .andReturn([FBLPromise resolvedWith:[NSNull null]]);
  712. // 3.2. Expect IID to be deleted, because it is default app.
  713. OCMExpect([self.mockIIDStore deleteExistingIID])
  714. .andReturn([FBLPromise resolvedWith:[NSNull null]]);
  715. // 4. Expect FIRInstallationIDDidChangeNotification to be sent.
  716. XCTestExpectation *notificationExpectation =
  717. [self installationIDDidChangeNotificationExpectation];
  718. return @[ notificationExpectation ];
  719. }
  720. // TODO: Test a single delete installation request at a time.
  721. #pragma mark - Notifications
  722. - (void)testFIDDidChangeNotificationIsSentWhenFIDCreated {
  723. // 1. Stub - no installation.
  724. // 1.2. FID store.
  725. [self expectInstallationsStoreGetInstallationNotFound];
  726. OCMStub([self.mockInstallationsStore saveInstallation:[OCMArg any]])
  727. .andReturn([FBLPromise resolvedWith:[NSNull null]]);
  728. // 1.3. IID store.
  729. FBLPromise *rejectedPromise = [FBLPromise pendingPromise];
  730. [rejectedPromise reject:[FIRInstallationsErrorUtil keychainErrorWithFunction:@"" status:-1]];
  731. OCMExpect([self.mockIIDStore existingIID]).andReturn(rejectedPromise);
  732. // 1.4. API Service.
  733. OCMExpect([self.mockAPIService registerInstallation:[OCMArg any]])
  734. .andReturn([FBLPromise resolvedWith:[FIRInstallationsItem createRegisteredInstallationItem]]);
  735. // 2. Expect FIRInstallationIDDidChangeNotification to be sent.
  736. XCTestExpectation *notificationExpectation =
  737. [self installationIDDidChangeNotificationExpectation];
  738. // 3. Request FID.
  739. FBLPromise *promise = [self.controller getInstallationItem];
  740. FBLWaitForPromisesWithTimeout(0.5);
  741. // 4. Check.
  742. XCTAssertNil(promise.error);
  743. XCTAssertNotNil(promise.value);
  744. [self waitForExpectations:@[ notificationExpectation ] timeout:0.5];
  745. OCMVerifyAll(self.mockInstallationsStore);
  746. OCMVerifyAll(self.mockIIDStore);
  747. OCMVerifyAll(self.mockAPIService);
  748. }
  749. - (void)testRegisterInstallation_WhenServerRespondsWithDifferentFID_ThenFIDDidChangeNotification {
  750. // 1.1. Expect installation to be requested from the store.
  751. FIRInstallationsItem *storedInstallation =
  752. [FIRInstallationsItem createUnregisteredInstallationItem];
  753. OCMExpect([self.mockInstallationsStore installationForAppID:self.appID appName:self.appName])
  754. .andReturn([FBLPromise resolvedWith:storedInstallation]);
  755. // 1.2. Expect register FID to be called.
  756. FIRInstallationsItem *receivedInstallation =
  757. [FIRInstallationsItem createRegisteredInstallationItem];
  758. receivedInstallation.firebaseInstallationID =
  759. [storedInstallation.firebaseInstallationID stringByAppendingString:@"_new"];
  760. OCMExpect([self.mockAPIService registerInstallation:storedInstallation])
  761. .andReturn([FBLPromise resolvedWith:receivedInstallation]);
  762. // 1.3. Expect the received installation to be stored.
  763. OCMExpect([self.mockInstallationsStore saveInstallation:receivedInstallation])
  764. .andReturn([FBLPromise resolvedWith:[NSNull null]]);
  765. // 2. Expect FIRInstallationIDDidChangeNotification to be sent.
  766. XCTestExpectation *notificationExpectation =
  767. [self installationIDDidChangeNotificationExpectation];
  768. // 3. Request Auth Token.
  769. FBLPromise<FIRInstallationsItem *> *promise = [self.controller getAuthTokenForcingRefresh:NO];
  770. XCTAssert(FBLWaitForPromisesWithTimeout(0.5));
  771. // 4. Check.
  772. XCTAssertNil(promise.error);
  773. XCTAssertNotNil(promise.value);
  774. XCTAssertEqualObjects(promise.value.firebaseInstallationID,
  775. receivedInstallation.firebaseInstallationID);
  776. [self waitForExpectations:@[ notificationExpectation ] timeout:0.5];
  777. OCMVerifyAll(self.mockInstallationsStore);
  778. OCMVerifyAll(self.mockAPIService);
  779. }
  780. #pragma mark - Registration Failures
  781. - (void)testRegisterInstallation_WhenServerRespondsWith429_ThenErrorIsNotStoredAndReturned {
  782. // 1.1. Expect installation to be requested from the store.
  783. FIRInstallationsItem *storedInstallation =
  784. [FIRInstallationsItem createUnregisteredInstallationItem];
  785. OCMExpect([self.mockInstallationsStore installationForAppID:self.appID appName:self.appName])
  786. .andReturn([FBLPromise resolvedWith:storedInstallation]);
  787. // 1.2. Receive HTTP 429.
  788. NSError *APIError = [FIRInstallationsErrorUtil APIErrorWithHTTPCode:429];
  789. FBLPromise *rejectedAPIPromise = [FBLPromise pendingPromise];
  790. [rejectedAPIPromise reject:APIError];
  791. OCMExpect([self.mockAPIService registerInstallation:storedInstallation])
  792. .andReturn(rejectedAPIPromise);
  793. // 1.3. Don't expect the installation to be stored with the error.
  794. OCMReject([self.mockInstallationsStore saveInstallation:[OCMArg any]]);
  795. // 2. Request Installation.
  796. FBLPromise<FIRInstallationsItem *> *promise = [self.controller getInstallationItem];
  797. XCTAssert(FBLWaitForPromisesWithTimeout(0.5));
  798. // 4. Check.
  799. XCTAssertNil(promise.error);
  800. XCTAssertNotNil(promise.value);
  801. // The unregistered installation should be returned before the registration attempt.
  802. XCTAssertEqual(promise.value.registrationStatus, FIRInstallationStatusUnregistered);
  803. OCMVerifyAll(self.mockInstallationsStore);
  804. OCMVerifyAll(self.mockAPIService);
  805. }
  806. - (void)testRegisterInstallation_WhenServerRespondsWith400_ThenErrorStoredAndReturned {
  807. // 1.1. Expect installation to be requested from the store.
  808. FIRInstallationsItem *storedInstallation =
  809. [FIRInstallationsItem createUnregisteredInstallationItem];
  810. OCMExpect([self.mockInstallationsStore installationForAppID:self.appID appName:self.appName])
  811. .andReturn([FBLPromise resolvedWith:storedInstallation]);
  812. // 1.2. Receive HTTP 400.
  813. NSError *APIError = [FIRInstallationsErrorUtil APIErrorWithHTTPCode:400];
  814. FBLPromise *rejectedAPIPromise = [FBLPromise pendingPromise];
  815. [rejectedAPIPromise reject:APIError];
  816. OCMExpect([self.mockAPIService registerInstallation:storedInstallation])
  817. .andReturn(rejectedAPIPromise);
  818. // 1.3. Expect the installation to be stored with the error.
  819. OCMExpect([self.mockInstallationsStore
  820. saveInstallation:[OCMArg checkWithBlock:^BOOL(FIRInstallationsItem *installation) {
  821. XCTAssertEqual(installation.registrationStatus,
  822. FIRInstallationStatusRegistrationFailed);
  823. XCTAssertEqualObjects(installation.registrationError.APIError, APIError);
  824. XCTAssertEqualObjects(
  825. installation.registrationError.registrationParameters.APIKey, self.APIKey);
  826. XCTAssertEqualObjects(
  827. installation.registrationError.registrationParameters.projectID,
  828. self.projectID);
  829. XCTAssertNotNil(installation.registrationError.date);
  830. [self assertDate:installation.registrationError.date
  831. isApproximatelyEqualCurrentPlusTimeInterval:0];
  832. return YES;
  833. }]])
  834. .andReturn([FBLPromise resolvedWith:[NSNull null]]);
  835. // 2. Request Installation.
  836. FBLPromise<FIRInstallationsItem *> *promise = [self.controller getInstallationItem];
  837. XCTAssert(FBLWaitForPromisesWithTimeout(0.5));
  838. // 4. Check.
  839. XCTAssertNil(promise.error);
  840. XCTAssertNotNil(promise.value);
  841. // The unregistered installation should be returned before the registration attempt.
  842. XCTAssertEqual(promise.value.registrationStatus, FIRInstallationStatusUnregistered);
  843. OCMVerifyAll(self.mockInstallationsStore);
  844. OCMVerifyAll(self.mockAPIService);
  845. }
  846. - (void)testGetInstallation_WhenRegistrationErrorStoredAndConfigurationIsTheSame_ThenFails {
  847. // 1.1. Expect installation to be requested from the store.
  848. FIRInstallationsItem *storedInstallation =
  849. [self createFailedToRegisterInstallationWithParameters:[self currentRegistrationParameters]];
  850. OCMExpect([self.mockInstallationsStore installationForAppID:self.appID appName:self.appName])
  851. .andReturn([FBLPromise resolvedWith:storedInstallation]);
  852. // 1.2. Don't expect registration API request to be sent.
  853. OCMReject([self.mockAPIService registerInstallation:[OCMArg any]]);
  854. // 2. Request Installation.
  855. FBLPromise<FIRInstallationsItem *> *promise = [self.controller getInstallationItem];
  856. XCTAssert(FBLWaitForPromisesWithTimeout(0.5));
  857. // 3. Check.
  858. XCTAssertFalse(promise.isFulfilled);
  859. XCTAssertEqualObjects(promise.error, storedInstallation.registrationError.APIError);
  860. OCMVerifyAll(self.mockInstallationsStore);
  861. OCMVerifyAll(self.mockAPIService);
  862. }
  863. - (void)
  864. testGetInstallation_WhenRegistrationErrorStoredAndConfigurationDifferent_ThenSendsAPIRequest {
  865. __block FBLPromise<FIRInstallationsItem *> *storedInstallationPromise;
  866. OCMExpect([self.mockInstallationsStore installationForAppID:self.appID appName:self.appName])
  867. .andDo(^(NSInvocation *invocation) {
  868. [invocation setReturnValue:&storedInstallationPromise];
  869. });
  870. // 1.1. Expect installation to be requested from the store.
  871. FIRInstallationsItem *storedInstallation =
  872. [self createFailedToRegisterInstallationWithParameters:[self otherRegistrationParameters]];
  873. storedInstallationPromise = [FBLPromise resolvedWith:storedInstallation];
  874. // 1.2. Expect registration API request to be sent.
  875. FIRInstallationsItem *registeredInstallation =
  876. [FIRInstallationsItem createRegisteredInstallationItem];
  877. OCMExpect([self.mockAPIService registerInstallation:storedInstallation])
  878. .andReturn([FBLPromise resolvedWith:registeredInstallation]);
  879. // 1.3. Expect registered Installation to be stored.
  880. OCMExpect([self.mockInstallationsStore saveInstallation:[OCMArg checkWithBlock:^BOOL(id obj) {
  881. XCTAssertEqualObjects(obj, registeredInstallation);
  882. storedInstallationPromise =
  883. [FBLPromise resolvedWith:obj];
  884. return YES;
  885. }]])
  886. .andReturn([FBLPromise resolvedWith:[NSNull null]]);
  887. // 2. Request Installation.
  888. FBLPromise<FIRInstallationsItem *> *promise = [self.controller getInstallationItem];
  889. XCTAssert(FBLWaitForPromisesWithTimeout(0.5));
  890. // 3. Check.
  891. XCTAssertEqualObjects(promise.value.identifier, registeredInstallation.identifier);
  892. XCTAssertNil(promise.error);
  893. OCMVerifyAll(self.mockInstallationsStore);
  894. OCMVerifyAll(self.mockAPIService);
  895. }
  896. - (void)testGetInstallation_WhenStoredRegistrationErrorIsOutdated_ThenSendsAPIRequest {
  897. __block FBLPromise<FIRInstallationsItem *> *storedInstallationPromise;
  898. OCMExpect([self.mockInstallationsStore installationForAppID:self.appID appName:self.appName])
  899. .andDo(^(NSInvocation *invocation) {
  900. [invocation setReturnValue:&storedInstallationPromise];
  901. });
  902. // 1.1. Expect installation to be requested from the store.
  903. NSDate *date25HoursAgo = [NSDate dateWithTimeIntervalSinceNow:-25 * 60 * 60];
  904. FIRInstallationsItem *storedInstallation =
  905. [self createFailedToRegisterInstallationWithParameters:[self currentRegistrationParameters]
  906. date:date25HoursAgo];
  907. storedInstallationPromise = [FBLPromise resolvedWith:storedInstallation];
  908. // 1.2. Expect registration API request to be sent.
  909. FIRInstallationsItem *registeredInstallation =
  910. [FIRInstallationsItem createRegisteredInstallationItem];
  911. OCMExpect([self.mockAPIService registerInstallation:storedInstallation])
  912. .andReturn([FBLPromise resolvedWith:registeredInstallation]);
  913. // 1.3. Expect registered Installation to be stored.
  914. OCMExpect([self.mockInstallationsStore saveInstallation:[OCMArg checkWithBlock:^BOOL(id obj) {
  915. XCTAssertEqualObjects(obj, registeredInstallation);
  916. storedInstallationPromise =
  917. [FBLPromise resolvedWith:obj];
  918. return YES;
  919. }]])
  920. .andReturn([FBLPromise resolvedWith:[NSNull null]]);
  921. // 2. Request Installation.
  922. FBLPromise<FIRInstallationsItem *> *promise = [self.controller getInstallationItem];
  923. XCTAssert(FBLWaitForPromisesWithTimeout(0.5));
  924. // 3. Check.
  925. XCTAssertEqualObjects(promise.value.identifier, registeredInstallation.identifier);
  926. XCTAssertNil(promise.error);
  927. OCMVerifyAll(self.mockInstallationsStore);
  928. OCMVerifyAll(self.mockAPIService);
  929. }
  930. #pragma mark - Helpers
  931. - (void)expectInstallationsStoreGetInstallationNotFound {
  932. NSError *notFoundError =
  933. [FIRInstallationsErrorUtil installationItemNotFoundForAppID:self.appID appName:self.appName];
  934. FBLPromise *installationNotFoundPromise = [FBLPromise pendingPromise];
  935. [installationNotFoundPromise reject:notFoundError];
  936. OCMExpect([self.mockInstallationsStore installationForAppID:self.appID appName:self.appName])
  937. .andReturn(installationNotFoundPromise);
  938. }
  939. - (void)expectStoredIIDNotFound {
  940. FBLPromise *rejectedPromise = [FBLPromise pendingPromise];
  941. [rejectedPromise reject:[FIRInstallationsErrorUtil keychainErrorWithFunction:@"" status:-1]];
  942. OCMExpect([self.mockIIDStore existingIID]).andReturn(rejectedPromise);
  943. }
  944. - (void)assertValidCreatedInstallation:(FIRInstallationsItem *)installation {
  945. XCTAssertEqualObjects([installation class], [FIRInstallationsItem class]);
  946. XCTAssertEqualObjects(installation.appID, self.appID);
  947. XCTAssertEqualObjects(installation.firebaseAppName, self.appName);
  948. XCTAssertEqual(installation.registrationStatus, FIRInstallationStatusUnregistered);
  949. XCTAssertNotNil(installation.firebaseInstallationID);
  950. }
  951. - (XCTestExpectation *)installationIDDidChangeNotificationExpectation {
  952. XCTestExpectation *notificationExpectation = [self
  953. expectationForNotification:FIRInstallationIDDidChangeNotification
  954. object:nil
  955. handler:^BOOL(NSNotification *_Nonnull notification) {
  956. XCTAssertEqualObjects(
  957. notification
  958. .userInfo[kFIRInstallationIDDidChangeNotificationAppNameKey],
  959. self.appName);
  960. return YES;
  961. }];
  962. return notificationExpectation;
  963. }
  964. - (FIRInstallationsItem *)createFailedToRegisterInstallationWithParameters:
  965. (FIRInstallationsStoredRegistrationParameters *)registrationParameters
  966. date:(NSDate *)date {
  967. FIRInstallationsStoredRegistrationError *error = [[FIRInstallationsStoredRegistrationError alloc]
  968. initWithRegistrationParameters:registrationParameters
  969. date:date
  970. APIError:[FIRInstallationsErrorUtil APIErrorWithHTTPCode:400]];
  971. FIRInstallationsItem *installation = [FIRInstallationsItem createWithRegistrationFailure:error];
  972. return installation;
  973. }
  974. - (FIRInstallationsItem *)createFailedToRegisterInstallationWithParameters:
  975. (FIRInstallationsStoredRegistrationParameters *)registrationParameters {
  976. return [self createFailedToRegisterInstallationWithParameters:registrationParameters
  977. date:[NSDate date]];
  978. }
  979. - (FIRInstallationsStoredRegistrationParameters *)currentRegistrationParameters {
  980. return [[FIRInstallationsStoredRegistrationParameters alloc] initWithAPIKey:self.APIKey
  981. projectID:self.projectID];
  982. }
  983. - (FIRInstallationsStoredRegistrationParameters *)otherRegistrationParameters {
  984. NSString *APIKey = [@"another" stringByAppendingString:self.APIKey];
  985. NSString *projectID = [@"another" stringByAppendingString:self.projectID];
  986. return [[FIRInstallationsStoredRegistrationParameters alloc] initWithAPIKey:APIKey
  987. projectID:projectID];
  988. }
  989. - (void)expectInstallationStoreToBeRequestedAndReturnInstallation:
  990. (FIRInstallationsItem *)storedInstallation {
  991. OCMExpect([self.mockInstallationsStore installationForAppID:self.appID appName:self.appName])
  992. .andReturn([FBLPromise resolvedWith:storedInstallation]);
  993. }
  994. @end