FIRInstanceIDTokenManagerTest.m 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589
  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 <FirebaseInstallations/FirebaseInstallations.h>
  19. #import "FIRInstanceIDFakeKeychain.h"
  20. #import "FIRInstanceIDTokenManager+Test.h"
  21. #import "Firebase/InstanceID/FIRInstanceIDBackupExcludedPlist.h"
  22. #import "Firebase/InstanceID/FIRInstanceIDCheckinPreferences+Internal.h"
  23. #import "Firebase/InstanceID/FIRInstanceIDCheckinStore.h"
  24. #import "Firebase/InstanceID/FIRInstanceIDStore.h"
  25. #import "Firebase/InstanceID/FIRInstanceIDTokenDeleteOperation.h"
  26. #import "Firebase/InstanceID/FIRInstanceIDTokenFetchOperation.h"
  27. #import "Firebase/InstanceID/FIRInstanceIDTokenInfo.h"
  28. #import "Firebase/InstanceID/FIRInstanceIDTokenManager.h"
  29. #import "Firebase/InstanceID/FIRInstanceIDTokenOperation.h"
  30. #import "Firebase/InstanceID/FIRInstanceIDTokenStore.h"
  31. static NSString *const kSubDirectoryName = @"FirebaseInstanceIDTokenManagerTest";
  32. static NSString *const kAuthorizedEntity = @"test-authorized-entity";
  33. static NSString *const kScope = @"test-scope";
  34. static NSString *const kToken =
  35. @"cHu_lDPF4EXfo3cdVQhfGg:APA91bGHesgrEsM5j8afb8kKKVwr2Q82NrX_mhLT0URVLYP_"
  36. @"MVJgvrdNfYfgoiPO4NG8SYA2SsZofP0iRXUv9vKREhLPQh0JDOiQ1MO0ivJyDeRo6_5e8VXLeGTTa0StpzfqETEhMaW7";
  37. // Use a string (which is converted to NSData) as a placeholder for an actual APNs device token.
  38. static NSString *const kNewAPNSTokenString = @"newAPNSData";
  39. @interface FIRInstanceIDTokenOperation ()
  40. - (void)performTokenOperation;
  41. - (void)finishWithResult:(FIRInstanceIDTokenOperationResult)result
  42. token:(nullable NSString *)token
  43. error:(nullable NSError *)error;
  44. @end
  45. @interface FIRInstanceIDTokenManager (ExposedForTests)
  46. - (BOOL)checkTokenRefreshPolicyForIID:(NSString *)IID;
  47. - (void)updateToAPNSDeviceToken:(NSData *)deviceToken isSandbox:(BOOL)isSandbox;
  48. /**
  49. * Create a fetch operation. This method can be stubbed to return a particular operation instance,
  50. * which makes it easier to unit test different behaviors.
  51. */
  52. - (FIRInstanceIDTokenFetchOperation *)
  53. createFetchOperationWithAuthorizedEntity:(NSString *)authorizedEntity
  54. scope:(NSString *)scope
  55. options:(NSDictionary<NSString *, NSString *> *)options
  56. instanceID:(NSString *)instanceID;
  57. /**
  58. * Create a delete operation. This method can be stubbed to return a particular operation instance,
  59. * which makes it easier to unit test different behaviors.
  60. */
  61. - (FIRInstanceIDTokenDeleteOperation *)
  62. createDeleteOperationWithAuthorizedEntity:(NSString *)authorizedEntity
  63. scope:(NSString *)scope
  64. checkinPreferences:(FIRInstanceIDCheckinPreferences *)checkinPreferences
  65. instanceID:(NSString *)instanceID
  66. action:(FIRInstanceIDTokenAction)action;
  67. @end
  68. @interface FIRInstanceIDTokenManagerTest : XCTestCase
  69. @property(nonatomic, readwrite, strong) FIRInstanceIDTokenManager *tokenManager;
  70. @property(nonatomic, readwrite, strong) id mockTokenManager;
  71. @property(nonatomic, readwrite, strong) FIRInstanceIDBackupExcludedPlist *checkinPlist;
  72. @property(nonatomic, readwrite, strong) FIRInstanceIDFakeKeychain *fakeKeyChain;
  73. @property(nonatomic, readwrite, strong) FIRInstanceIDTokenStore *tokenStore;
  74. @property(nonatomic, readwrite, strong) FIRInstanceIDCheckinPreferences *fakeCheckin;
  75. @property(nonatomic, readwrite, strong) id mockInstallations;
  76. @property(nonatomic, readwrite, strong) FIRInstallationsAuthTokenResult *FISAuthTokenResult;
  77. @end
  78. @implementation FIRInstanceIDTokenManagerTest
  79. - (void)setUp {
  80. [super setUp];
  81. [FIRInstanceIDStore createSubDirectory:kSubDirectoryName];
  82. NSString *checkinPlistFilename = @"com.google.test.IIDCheckinTest";
  83. self.checkinPlist =
  84. [[FIRInstanceIDBackupExcludedPlist alloc] initWithFileName:checkinPlistFilename
  85. subDirectory:kSubDirectoryName];
  86. // checkin store
  87. FIRInstanceIDFakeKeychain *fakeCheckinKeychain = [[FIRInstanceIDFakeKeychain alloc] init];
  88. FIRInstanceIDCheckinStore *checkinStore =
  89. [[FIRInstanceIDCheckinStore alloc] initWithCheckinPlist:self.checkinPlist
  90. keychain:fakeCheckinKeychain];
  91. // token store
  92. self.fakeKeyChain = [[FIRInstanceIDFakeKeychain alloc] init];
  93. self.tokenStore = [[FIRInstanceIDTokenStore alloc] initWithKeychain:_fakeKeyChain];
  94. self.tokenManager = [[FIRInstanceIDTokenManager alloc] initWithCheckinStore:checkinStore
  95. tokenStore:self.tokenStore];
  96. self.mockTokenManager = OCMPartialMock(self.tokenManager);
  97. self.fakeCheckin = [[FIRInstanceIDCheckinPreferences alloc] initWithDeviceID:@"fakeDeviceID"
  98. secretToken:@"fakeSecretToken"];
  99. // Installations
  100. self.FISAuthTokenResult = OCMClassMock([FIRInstallationsAuthTokenResult class]);
  101. OCMStub([self.FISAuthTokenResult authToken]).andReturn(@"FISAuthToken");
  102. OCMStub([self.FISAuthTokenResult expirationDate]).andReturn([NSDate distantFuture]);
  103. self.mockInstallations = OCMClassMock([FIRInstallations class]);
  104. OCMStub([self.mockInstallations installations]).andReturn(self.mockInstallations);
  105. id authTokenBlockArg = [OCMArg invokeBlockWithArgs:self.FISAuthTokenResult, [NSNull null], nil];
  106. OCMStub([self.mockInstallations authTokenWithCompletion:authTokenBlockArg]);
  107. }
  108. - (void)tearDown {
  109. self.fakeCheckin = nil;
  110. [self.mockTokenManager stopMocking];
  111. self.mockTokenManager = nil;
  112. self.tokenManager = nil;
  113. self.tokenStore = nil;
  114. self.fakeKeyChain = nil;
  115. NSError *error;
  116. if (![self.checkinPlist deleteFile:&error]) {
  117. XCTFail(@"Failed to delete checkin plist %@", error);
  118. }
  119. self.checkinPlist = nil;
  120. [FIRInstanceIDStore removeSubDirectory:kSubDirectoryName error:nil];
  121. [super tearDown];
  122. }
  123. /**
  124. * Tests that when a new InstanceID token is successfully produced,
  125. * the callback is invoked with a token that is not an empty string and with no error.
  126. */
  127. - (void)testNewTokenSuccess {
  128. XCTestExpectation *tokenExpectation =
  129. [self expectationWithDescription:@"New token handler invoked."];
  130. NSDictionary *tokenOptions = [NSDictionary dictionary];
  131. // Create a fake operation that always returns success
  132. FIRInstanceIDTokenFetchOperation *operation =
  133. [[FIRInstanceIDTokenFetchOperation alloc] initWithAuthorizedEntity:kAuthorizedEntity
  134. scope:kScope
  135. options:tokenOptions
  136. checkinPreferences:self.fakeCheckin
  137. instanceID:[OCMArg any]];
  138. id mockOperation = OCMPartialMock(operation);
  139. [[[mockOperation stub] andDo:^(NSInvocation *invocation) {
  140. [invocation.target finishWithResult:FIRInstanceIDTokenOperationSucceeded
  141. token:kToken
  142. error:nil];
  143. }] performTokenOperation];
  144. XCTestExpectation *operationFinishExpectation =
  145. [self expectationWithDescription:@"operationFinishExpectation"];
  146. operation.completionBlock = ^{
  147. [operationFinishExpectation fulfill];
  148. };
  149. // Return our fake operation when asked for an operation
  150. [[[self.mockTokenManager stub] andReturn:operation]
  151. createFetchOperationWithAuthorizedEntity:[OCMArg any]
  152. scope:[OCMArg any]
  153. options:[OCMArg any]
  154. instanceID:[OCMArg any]];
  155. [self.tokenManager fetchNewTokenWithAuthorizedEntity:kAuthorizedEntity
  156. scope:kScope
  157. instanceID:[OCMArg any]
  158. options:tokenOptions
  159. handler:^(NSString *token, NSError *error) {
  160. XCTAssertNotNil(token);
  161. XCTAssertGreaterThan(token.length, 0);
  162. XCTAssertNil(error);
  163. [tokenExpectation fulfill];
  164. }];
  165. [self waitForExpectations:@[ tokenExpectation, operationFinishExpectation ] timeout:1];
  166. // Make sure the partial mock stops mocking before `operation` is deallocated to avoid crash.
  167. [mockOperation stopMocking];
  168. // Keep 'operation' alive, so it's not prematurely destroyed
  169. XCTAssertNotNil(operation);
  170. }
  171. /**
  172. * Tests that when a new InstanceID token is fetched from the server but unsuccessfully
  173. * saved on the client we should return an error instead of the fetched token.
  174. */
  175. - (void)testNewTokenSaveFailure {
  176. XCTestExpectation *tokenExpectation =
  177. [self expectationWithDescription:@"New token handler invoked."];
  178. NSDictionary *tokenOptions = [NSDictionary dictionary];
  179. // Simulate write to keychain failure.
  180. self.fakeKeyChain.cannotWriteToKeychain = YES;
  181. // Create a fake operation that always returns success
  182. FIRInstanceIDTokenFetchOperation *operation =
  183. [[FIRInstanceIDTokenFetchOperation alloc] initWithAuthorizedEntity:kAuthorizedEntity
  184. scope:kScope
  185. options:tokenOptions
  186. checkinPreferences:self.fakeCheckin
  187. instanceID:[OCMArg any]];
  188. id mockOperation = OCMPartialMock(operation);
  189. [[[mockOperation stub] andDo:^(NSInvocation *invocation) {
  190. [invocation.target finishWithResult:FIRInstanceIDTokenOperationSucceeded
  191. token:kToken
  192. error:nil];
  193. }] performTokenOperation];
  194. XCTestExpectation *operationFinishExpectation =
  195. [self expectationWithDescription:@"operationFinishExpectation"];
  196. operation.completionBlock = ^{
  197. [operationFinishExpectation fulfill];
  198. };
  199. // Return our fake operation when asked for an operation
  200. [[[self.mockTokenManager stub] andReturn:operation]
  201. createFetchOperationWithAuthorizedEntity:[OCMArg any]
  202. scope:[OCMArg any]
  203. options:[OCMArg any]
  204. instanceID:[OCMArg any]];
  205. [self.tokenManager fetchNewTokenWithAuthorizedEntity:kAuthorizedEntity
  206. scope:kScope
  207. instanceID:[OCMArg any]
  208. options:tokenOptions
  209. handler:^(NSString *token, NSError *error) {
  210. XCTAssertNil(token);
  211. XCTAssertNotNil(error);
  212. [tokenExpectation fulfill];
  213. }];
  214. [self waitForExpectations:@[ tokenExpectation, operationFinishExpectation ] timeout:1];
  215. // Make sure the partial mock stops mocking before `operation` is deallocated to avoid crash.
  216. [mockOperation stopMocking];
  217. // Keep 'operation' alive, so it's not prematurely destroyed
  218. XCTAssertNotNil(operation);
  219. }
  220. /**
  221. * Tests that when there is a failure in producing a new InstanceID token,
  222. * the callback is invoked with an error and a nil token.
  223. */
  224. - (void)testNewTokenFailure {
  225. XCTestExpectation *tokenExpectation =
  226. [self expectationWithDescription:@"New token handler invoked."];
  227. NSDictionary *tokenOptions = [NSDictionary dictionary];
  228. // Create a fake operation that always returns failure
  229. FIRInstanceIDTokenFetchOperation *operation =
  230. [[FIRInstanceIDTokenFetchOperation alloc] initWithAuthorizedEntity:kAuthorizedEntity
  231. scope:kScope
  232. options:tokenOptions
  233. checkinPreferences:self.fakeCheckin
  234. instanceID:[OCMArg any]];
  235. id mockOperation = OCMPartialMock(operation);
  236. [[[mockOperation stub] andDo:^(NSInvocation *invocation) {
  237. NSError *someError = [[NSError alloc] initWithDomain:@"InstanceIDUnitTest" code:0 userInfo:nil];
  238. [invocation.target finishWithResult:FIRInstanceIDTokenOperationError token:nil error:someError];
  239. }] performTokenOperation];
  240. XCTestExpectation *operationFinishExpectation =
  241. [self expectationWithDescription:@"operationFinishExpectation"];
  242. operation.completionBlock = ^{
  243. [operationFinishExpectation fulfill];
  244. };
  245. // Return our fake operation when asked for an operation
  246. [[[self.mockTokenManager stub] andReturn:operation]
  247. createFetchOperationWithAuthorizedEntity:[OCMArg any]
  248. scope:[OCMArg any]
  249. options:[OCMArg any]
  250. instanceID:[OCMArg any]];
  251. [self.tokenManager fetchNewTokenWithAuthorizedEntity:kAuthorizedEntity
  252. scope:kScope
  253. instanceID:[OCMArg any]
  254. options:tokenOptions
  255. handler:^(NSString *token, NSError *error) {
  256. XCTAssertNil(token);
  257. XCTAssertNotNil(error);
  258. [tokenExpectation fulfill];
  259. }];
  260. [self waitForExpectations:@[ tokenExpectation, operationFinishExpectation ] timeout:1];
  261. // Make sure the partial mock stops mocking before `operation` is deallocated to avoid crash.
  262. [mockOperation stopMocking];
  263. // Keep 'operation' alive, so it's not prematurely destroyed
  264. XCTAssertNotNil(operation);
  265. }
  266. /**
  267. * Tests that when a token is deleted successfully, the callback is invoked with no error.
  268. */
  269. - (void)testDeleteTokenSuccess {
  270. XCTestExpectation *deleteExpectation =
  271. [self expectationWithDescription:@"Delete handler invoked."];
  272. // Create a fake operation that always succeeds
  273. FIRInstanceIDTokenDeleteOperation *operation = [[FIRInstanceIDTokenDeleteOperation alloc]
  274. initWithAuthorizedEntity:kAuthorizedEntity
  275. scope:kScope
  276. checkinPreferences:self.fakeCheckin
  277. instanceID:[OCMArg any]
  278. action:FIRInstanceIDTokenActionDeleteToken];
  279. id mockOperation = OCMPartialMock(operation);
  280. [[[mockOperation stub] andDo:^(NSInvocation *invocation) {
  281. [invocation.target finishWithResult:FIRInstanceIDTokenOperationSucceeded token:nil error:nil];
  282. }] performTokenOperation];
  283. XCTestExpectation *operationFinishExpectation =
  284. [self expectationWithDescription:@"operationFinishExpectation"];
  285. operation.completionBlock = ^{
  286. [operationFinishExpectation fulfill];
  287. };
  288. // Return our fake operation when asked for an operation
  289. [[[self.mockTokenManager stub] andReturn:operation]
  290. createDeleteOperationWithAuthorizedEntity:[OCMArg any]
  291. scope:[OCMArg any]
  292. checkinPreferences:[OCMArg any]
  293. instanceID:[OCMArg any]
  294. action:FIRInstanceIDTokenActionDeleteToken];
  295. [self.tokenManager deleteTokenWithAuthorizedEntity:kAuthorizedEntity
  296. scope:kScope
  297. instanceID:[OCMArg any]
  298. handler:^(NSError *error) {
  299. XCTAssertNil(error);
  300. [deleteExpectation fulfill];
  301. }];
  302. [self waitForExpectations:@[ deleteExpectation, operationFinishExpectation ] timeout:1];
  303. // Make sure the partial mock stops mocking before `operation` is deallocated to avoid crash.
  304. [mockOperation stopMocking];
  305. // Keep 'operation' alive, so it's not prematurely destroyed
  306. XCTAssertNotNil(operation);
  307. }
  308. /**
  309. * Tests that when a token deletion fails, the callback is invoked with an error.
  310. */
  311. - (void)testDeleteTokenFailure {
  312. XCTestExpectation *deleteExpectation =
  313. [self expectationWithDescription:@"Delete handler invoked."];
  314. // Create a fake operation that always fails
  315. FIRInstanceIDTokenDeleteOperation *operation = [[FIRInstanceIDTokenDeleteOperation alloc]
  316. initWithAuthorizedEntity:kAuthorizedEntity
  317. scope:kScope
  318. checkinPreferences:self.fakeCheckin
  319. instanceID:[OCMArg any]
  320. action:FIRInstanceIDTokenActionDeleteToken];
  321. id mockOperation = OCMPartialMock(operation);
  322. [[[mockOperation stub] andDo:^(NSInvocation *invocation) {
  323. NSError *someError = [[NSError alloc] initWithDomain:@"InstanceIDUnitTest" code:0 userInfo:nil];
  324. [invocation.target finishWithResult:FIRInstanceIDTokenOperationError token:nil error:someError];
  325. }] performTokenOperation];
  326. XCTestExpectation *operationFinishExpectation =
  327. [self expectationWithDescription:@"operationFinishExpectation"];
  328. operation.completionBlock = ^{
  329. [operationFinishExpectation fulfill];
  330. };
  331. // Return our fake operation when asked for an operation
  332. [[[self.mockTokenManager stub] andReturn:operation]
  333. createDeleteOperationWithAuthorizedEntity:[OCMArg any]
  334. scope:[OCMArg any]
  335. checkinPreferences:[OCMArg any]
  336. instanceID:[OCMArg any]
  337. action:FIRInstanceIDTokenActionDeleteToken];
  338. [self.tokenManager deleteTokenWithAuthorizedEntity:kAuthorizedEntity
  339. scope:kScope
  340. instanceID:[OCMArg any]
  341. handler:^(NSError *error) {
  342. XCTAssertNotNil(error);
  343. [deleteExpectation fulfill];
  344. }];
  345. [self waitForExpectations:@[ deleteExpectation, operationFinishExpectation ] timeout:1];
  346. // Make sure the partial mock stops mocking before `operation` is deallocated to avoid crash.
  347. [mockOperation stopMocking];
  348. // Keep 'operation' alive, so it's not prematurely destroyed
  349. XCTAssertNotNil(operation);
  350. }
  351. #pragma mark - Cached Token Invalidation
  352. - (void)testCachedTokensInvalidatedOnAppVersionChange {
  353. // Write some fake tokens to cache with a old app version "0.9"
  354. NSArray<NSString *> *entities = @[ @"entity1", @"entity2" ];
  355. for (NSString *entity in entities) {
  356. FIRInstanceIDTokenInfo *info =
  357. [[FIRInstanceIDTokenInfo alloc] initWithAuthorizedEntity:entity
  358. scope:kScope
  359. token:@"abcdef"
  360. appVersion:@"0.9"
  361. firebaseAppID:nil];
  362. [self.tokenStore saveTokenInfo:info handler:nil];
  363. }
  364. // Ensure they tokens now exist.
  365. for (NSString *entity in entities) {
  366. FIRInstanceIDTokenInfo *cachedTokenInfo =
  367. [self.tokenManager cachedTokenInfoWithAuthorizedEntity:entity scope:kScope];
  368. XCTAssertNotNil(cachedTokenInfo);
  369. }
  370. // Trigger a potential reset, the current app version is 1.0 which is newer than
  371. // the one set in tokenInfo.
  372. [self.tokenManager checkTokenRefreshPolicyWithIID:@"abc"];
  373. // Ensure that token data is now missing
  374. for (NSString *entity in entities) {
  375. FIRInstanceIDTokenInfo *cachedTokenInfo =
  376. [self.tokenManager cachedTokenInfoWithAuthorizedEntity:entity scope:kScope];
  377. XCTAssertNil(cachedTokenInfo);
  378. }
  379. }
  380. - (void)testTokenShouldBeDeletedIfWrongFormat {
  381. // Cache some token
  382. NSArray<NSString *> *entities = @[ @"entity1", @"entity2" ];
  383. for (NSString *entity in entities) {
  384. FIRInstanceIDTokenInfo *info = [[FIRInstanceIDTokenInfo alloc] initWithAuthorizedEntity:entity
  385. scope:kScope
  386. token:kToken
  387. appVersion:nil
  388. firebaseAppID:nil];
  389. [self.tokenStore saveTokenInfo:info handler:nil];
  390. }
  391. // Ensure they tokens now exist.
  392. for (NSString *entity in entities) {
  393. FIRInstanceIDTokenInfo *cachedTokenInfo =
  394. [self.tokenManager cachedTokenInfoWithAuthorizedEntity:entity scope:kScope];
  395. XCTAssertNotNil(cachedTokenInfo);
  396. }
  397. // Trigger a potential reset, the current IID is sth differnt than the token
  398. [self.tokenManager checkTokenRefreshPolicyWithIID:@"d8xQyABOoV8"];
  399. // Ensure that token data is now missing
  400. for (NSString *entity in entities) {
  401. FIRInstanceIDTokenInfo *cachedTokenInfo =
  402. [self.tokenManager cachedTokenInfoWithAuthorizedEntity:entity scope:kScope];
  403. XCTAssertNil(cachedTokenInfo);
  404. }
  405. }
  406. - (void)testCachedTokensInvalidatedOnAPNSAddition {
  407. // Write some fake tokens to cache, which have no APNs info
  408. NSArray<NSString *> *entities = @[ @"entity1", @"entity2" ];
  409. for (NSString *entity in entities) {
  410. FIRInstanceIDTokenInfo *info = [[FIRInstanceIDTokenInfo alloc] initWithAuthorizedEntity:entity
  411. scope:kScope
  412. token:kToken
  413. appVersion:nil
  414. firebaseAppID:nil];
  415. [self.tokenStore saveTokenInfo:info handler:nil];
  416. }
  417. // Ensure the tokens now exist.
  418. for (NSString *entity in entities) {
  419. FIRInstanceIDTokenInfo *cachedTokenInfo =
  420. [self.tokenManager cachedTokenInfoWithAuthorizedEntity:entity scope:kScope];
  421. XCTAssertNotNil(cachedTokenInfo);
  422. }
  423. // Trigger a potential reset.
  424. [self triggerAPNSTokenChange];
  425. // Ensure that token data is now missing
  426. for (NSString *entity in entities) {
  427. FIRInstanceIDTokenInfo *cachedTokenInfo =
  428. [self.tokenManager cachedTokenInfoWithAuthorizedEntity:entity scope:kScope];
  429. XCTAssertNil(cachedTokenInfo);
  430. }
  431. }
  432. - (void)testCachedTokensInvalidatedOnAPNSChange {
  433. // Write some fake tokens to cache
  434. NSArray<NSString *> *entities = @[ @"entity1", @"entity2" ];
  435. NSData *oldAPNSData = [@"oldAPNSToken" dataUsingEncoding:NSUTF8StringEncoding];
  436. for (NSString *entity in entities) {
  437. FIRInstanceIDTokenInfo *info = [[FIRInstanceIDTokenInfo alloc] initWithAuthorizedEntity:entity
  438. scope:kScope
  439. token:kToken
  440. appVersion:nil
  441. firebaseAppID:nil];
  442. info.APNSInfo = [[FIRInstanceIDAPNSInfo alloc] initWithDeviceToken:oldAPNSData isSandbox:NO];
  443. [self.tokenStore saveTokenInfo:info handler:nil];
  444. }
  445. // Ensure the tokens now exist.
  446. for (NSString *entity in entities) {
  447. FIRInstanceIDTokenInfo *cachedTokenInfo =
  448. [self.tokenManager cachedTokenInfoWithAuthorizedEntity:entity scope:kScope];
  449. XCTAssertNotNil(cachedTokenInfo);
  450. }
  451. // Trigger a potential reset.
  452. [self triggerAPNSTokenChange];
  453. // Ensure that token data is now missing
  454. for (NSString *entity in entities) {
  455. FIRInstanceIDTokenInfo *cachedTokenInfo =
  456. [self.tokenManager cachedTokenInfoWithAuthorizedEntity:entity scope:kScope];
  457. XCTAssertNil(cachedTokenInfo);
  458. }
  459. }
  460. - (void)testCachedTokensNotInvalidatedIfAPNSSame {
  461. // Write some fake tokens to cache, with the current APNs token
  462. NSArray<NSString *> *entities = @[ @"entity1", @"entity2" ];
  463. NSString *apnsDataString = kNewAPNSTokenString;
  464. NSData *currentAPNSData = [apnsDataString dataUsingEncoding:NSUTF8StringEncoding];
  465. for (NSString *entity in entities) {
  466. FIRInstanceIDTokenInfo *info = [[FIRInstanceIDTokenInfo alloc] initWithAuthorizedEntity:entity
  467. scope:kScope
  468. token:kToken
  469. appVersion:nil
  470. firebaseAppID:nil];
  471. info.APNSInfo = [[FIRInstanceIDAPNSInfo alloc] initWithDeviceToken:currentAPNSData
  472. isSandbox:NO];
  473. [self.tokenStore saveTokenInfo:info handler:nil];
  474. }
  475. // Ensure the tokens now exist.
  476. for (NSString *entity in entities) {
  477. FIRInstanceIDTokenInfo *cachedTokenInfo =
  478. [self.tokenManager cachedTokenInfoWithAuthorizedEntity:entity scope:kScope];
  479. XCTAssertNotNil(cachedTokenInfo);
  480. }
  481. // Trigger a potential reset.
  482. [self triggerAPNSTokenChange];
  483. // Ensure that token data is still there
  484. for (NSString *entity in entities) {
  485. FIRInstanceIDTokenInfo *cachedTokenInfo =
  486. [self.tokenManager cachedTokenInfoWithAuthorizedEntity:entity scope:kScope];
  487. XCTAssertNotNil(cachedTokenInfo);
  488. }
  489. }
  490. - (void)triggerAPNSTokenChange {
  491. // Trigger a potential reset.
  492. NSData *deviceToken = [kNewAPNSTokenString dataUsingEncoding:NSUTF8StringEncoding];
  493. [self.tokenManager updateTokensToAPNSDeviceToken:deviceToken isSandbox:NO];
  494. }
  495. @end