FIRInstallationsIDControllerTests.m 50 KB

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