FIRAppCheckTokenRefresherTests.m 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502
  1. /*
  2. * Copyright 2021 Google LLC
  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 "FirebaseAppCheck/Sources/Core/FIRAppCheckSettings.h"
  19. #import "FirebaseAppCheck/Sources/Core/TokenRefresh/FIRAppCheckTokenRefreshResult.h"
  20. #import "FirebaseAppCheck/Sources/Core/TokenRefresh/FIRAppCheckTokenRefresher.h"
  21. #import "FirebaseAppCheck/Tests/Unit/Utils/FIRFakeTimer.h"
  22. #import "SharedTestUtilities/Date/FIRDateTestUtils.h"
  23. @interface FIRAppCheckTokenRefresherTests : XCTestCase
  24. @property(nonatomic) FIRFakeTimer *fakeTimer;
  25. @property(nonatomic) OCMockObject<FIRAppCheckSettingsProtocol> *mockSettings;
  26. @property(nonatomic) FIRAppCheckTokenRefreshResult *initialTokenRefreshResult;
  27. @end
  28. @implementation FIRAppCheckTokenRefresherTests
  29. - (void)setUp {
  30. self.mockSettings = OCMProtocolMock(@protocol(FIRAppCheckSettingsProtocol));
  31. self.fakeTimer = [[FIRFakeTimer alloc] init];
  32. NSDate *receivedAtDate = [NSDate date];
  33. self.initialTokenRefreshResult = [[FIRAppCheckTokenRefreshResult alloc]
  34. initWithStatusSuccessAndExpirationDate:[receivedAtDate dateByAddingTimeInterval:1000]
  35. receivedAtDate:receivedAtDate];
  36. }
  37. - (void)tearDown {
  38. self.fakeTimer = nil;
  39. [self.mockSettings stopMocking];
  40. self.mockSettings = nil;
  41. }
  42. #pragma mark - Auto refresh is allowed
  43. - (void)testInitialRefreshWhenAutoRefreshAllowed {
  44. __auto_type weakSelf = self;
  45. self.initialTokenRefreshResult = [[FIRAppCheckTokenRefreshResult alloc] initWithStatusNever];
  46. FIRAppCheckTokenRefresher *refresher = [self createRefresher];
  47. // 1. Expect checking if auto-refresh allowed before scheduling the initial refresh.
  48. [[[self.mockSettings expect] andReturnValue:@(YES)] isTokenAutoRefreshEnabled];
  49. // 2. Don't expect the timer to be scheduled for the first refresh as the refresh should be
  50. // triggered straight away.
  51. XCTestExpectation *initialTimerCreatedExpectation =
  52. [self expectationWithDescription:@"initial refresh timer created"];
  53. initialTimerCreatedExpectation.inverted = YES;
  54. self.fakeTimer.createHandler = ^(NSDate *_Nonnull fireDate) {
  55. weakSelf.fakeTimer.createHandler = nil;
  56. [initialTimerCreatedExpectation fulfill];
  57. };
  58. // 3. Expect checking if auto-refresh allowed before triggering the initial refresh.
  59. [[[self.mockSettings expect] andReturnValue:@(YES)] isTokenAutoRefreshEnabled];
  60. // 4. Expect initial refresh handler to be called.
  61. __block FIRAppCheckTokenRefreshCompletion initialRefreshCompletion;
  62. XCTestExpectation *initialRefreshExpectation =
  63. [self expectationWithDescription:@"initial refresh"];
  64. refresher.tokenRefreshHandler = ^(FIRAppCheckTokenRefreshCompletion _Nonnull completion) {
  65. // Save completion to be called later.
  66. initialRefreshCompletion = completion;
  67. [initialRefreshExpectation fulfill];
  68. };
  69. NSDate *initialTokenExpirationDate = [NSDate dateWithTimeIntervalSinceNow:60 * 60];
  70. NSDate *initialTokenReceivedDate = [NSDate date];
  71. __auto_type initialRefreshResult = [[FIRAppCheckTokenRefreshResult alloc]
  72. initWithStatusSuccessAndExpirationDate:initialTokenExpirationDate
  73. receivedAtDate:initialTokenReceivedDate];
  74. [self waitForExpectations:@[ initialTimerCreatedExpectation, initialRefreshExpectation ]
  75. timeout:1];
  76. // 5. Expect checking if auto-refresh allowed before scheduling next refresh.
  77. [[[self.mockSettings expect] andReturnValue:@(YES)] isTokenAutoRefreshEnabled];
  78. // 6. Expect a next refresh timer to be scheduled on initial refresh completion.
  79. NSDate *expectedRefreshDate =
  80. [self expectedRefreshDateWithReceivedDate:initialTokenReceivedDate
  81. expirationDate:initialTokenExpirationDate];
  82. XCTestExpectation *nextTimerCreateExpectation =
  83. [self expectationWithDescription:@"next refresh create timer"];
  84. self.fakeTimer.createHandler = ^(NSDate *_Nonnull fireDate) {
  85. weakSelf.fakeTimer.createHandler = nil;
  86. XCTAssertEqualObjects(fireDate, expectedRefreshDate);
  87. [nextTimerCreateExpectation fulfill];
  88. };
  89. // 7. Call initial refresh completion and wait for next refresh timer to be scheduled.
  90. initialRefreshCompletion(initialRefreshResult);
  91. [self waitForExpectations:@[ nextTimerCreateExpectation ] timeout:0.5];
  92. // 8. Expect checking if auto-refresh allowed before triggering the next refresh.
  93. [[[self.mockSettings expect] andReturnValue:@(YES)] isTokenAutoRefreshEnabled];
  94. // 9. Expect refresh handler to be called for the next refresh.
  95. __auto_type nextRefreshResult = [[FIRAppCheckTokenRefreshResult alloc]
  96. initWithStatusSuccessAndExpirationDate:[expectedRefreshDate dateByAddingTimeInterval:60 * 60]
  97. receivedAtDate:expectedRefreshDate];
  98. XCTestExpectation *nextRefreshExpectation = [self expectationWithDescription:@"next refresh"];
  99. refresher.tokenRefreshHandler = ^(FIRAppCheckTokenRefreshCompletion _Nonnull completion) {
  100. [nextRefreshExpectation fulfill];
  101. // Call completion.
  102. completion(nextRefreshResult);
  103. };
  104. // 10. Fire the timer.
  105. [self fireTimer];
  106. // 11. Wait for the next refresh handler to be called.
  107. [self waitForExpectations:@[ nextRefreshExpectation ] timeout:1];
  108. OCMVerifyAll(self.mockSettings);
  109. }
  110. - (void)testNoTimeScheduledUntilHandlerSet {
  111. // 1. Don't expect timer to be scheduled.
  112. XCTestExpectation *timerCreateExpectation1 = [self expectationWithDescription:@"create timer 1"];
  113. timerCreateExpectation1.inverted = YES;
  114. self.fakeTimer.createHandler = ^(NSDate *_Nonnull fireDate) {
  115. [timerCreateExpectation1 fulfill];
  116. };
  117. // 2. Create a publisher.
  118. FIRAppCheckTokenRefresher *refresher = [self createRefresher];
  119. XCTAssertNotNil(refresher);
  120. [self waitForExpectations:@[ timerCreateExpectation1 ] timeout:0.5];
  121. // 3. Expect timer to be created after the handler has been set.
  122. // 3.1. Expect checking if auto-refresh allowed one more time when timer fires.
  123. [[[self.mockSettings expect] andReturnValue:@(YES)] isTokenAutoRefreshEnabled];
  124. // 3.2. Expect timer to fire.
  125. XCTestExpectation *timerCreateExpectation2 = [self expectationWithDescription:@"create timer 2"];
  126. self.fakeTimer.createHandler = ^(NSDate *_Nonnull fireDate) {
  127. [timerCreateExpectation2 fulfill];
  128. };
  129. // 3.3. Set handler.
  130. refresher.tokenRefreshHandler = ^(FIRAppCheckTokenRefreshCompletion _Nonnull completion) {
  131. };
  132. [self waitForExpectations:@[ timerCreateExpectation2 ] timeout:0.5];
  133. OCMVerifyAll(self.mockSettings);
  134. }
  135. - (void)testNextRefreshOnRefreshSuccess {
  136. FIRAppCheckTokenRefresher *refresher = [self createRefresher];
  137. NSDate *refreshedTokenExpirationDate =
  138. [self.initialTokenRefreshResult.tokenExpirationDate dateByAddingTimeInterval:60 * 60];
  139. __auto_type refreshResult = [[FIRAppCheckTokenRefreshResult alloc]
  140. initWithStatusSuccessAndExpirationDate:refreshedTokenExpirationDate
  141. receivedAtDate:self.initialTokenRefreshResult.tokenExpirationDate];
  142. // 1. Expect checking if auto-refresh allowed before scheduling initial refresh.
  143. [[[self.mockSettings expect] andReturnValue:@(YES)] isTokenAutoRefreshEnabled];
  144. // 2. Expect checking if auto-refresh allowed before calling the refresh handler.
  145. [[[self.mockSettings expect] andReturnValue:@(YES)] isTokenAutoRefreshEnabled];
  146. // 3. Expect refresh handler.
  147. XCTestExpectation *initialRefreshExpectation =
  148. [self expectationWithDescription:@"initial refresh"];
  149. refresher.tokenRefreshHandler = ^(FIRAppCheckTokenRefreshCompletion _Nonnull completion) {
  150. [initialRefreshExpectation fulfill];
  151. // Call completion in a while.
  152. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)),
  153. dispatch_get_main_queue(), ^{
  154. completion(refreshResult);
  155. });
  156. };
  157. // 4. Expect for new timer to be created.
  158. NSDate *expectedFireDate =
  159. [self expectedRefreshDateWithReceivedDate:refreshResult.tokenReceivedAtDate
  160. expirationDate:refreshResult.tokenExpirationDate];
  161. XCTestExpectation *createTimerExpectation = [self expectationWithDescription:@"create timer"];
  162. self.fakeTimer.createHandler = ^(NSDate *_Nonnull fireDate) {
  163. [createTimerExpectation fulfill];
  164. XCTAssertEqualObjects(fireDate, expectedFireDate);
  165. };
  166. // 5. Expect checking if auto-refresh allowed before refreshing.
  167. [[[self.mockSettings expect] andReturnValue:@(YES)] isTokenAutoRefreshEnabled];
  168. // 6. Fire initial timer and wait for expectations.
  169. [self fireTimer];
  170. [self waitForExpectations:@[ initialRefreshExpectation, createTimerExpectation ]
  171. timeout:1
  172. enforceOrder:YES];
  173. OCMVerifyAll(self.mockSettings);
  174. }
  175. - (void)testBackoff {
  176. FIRAppCheckTokenRefresher *refresher = [self createRefresher];
  177. // Initial backoff interval.
  178. NSTimeInterval expectedBackoffTime = 0;
  179. NSTimeInterval maximumBackoffTime = 16 * 60; // 16 min.
  180. // 1. Expect checking if auto-refresh allowed before scheduling initial refresh.
  181. [[[self.mockSettings expect] andReturnValue:@(YES)] isTokenAutoRefreshEnabled];
  182. for (NSInteger i = 0; i < 10; i++) {
  183. // 2. Expect checking if auto-refresh allowed before calling the refresh handler.
  184. [[[self.mockSettings expect] andReturnValue:@(YES)] isTokenAutoRefreshEnabled];
  185. // 3. Expect refresh handler.
  186. XCTestExpectation *initialRefreshExpectation =
  187. [self expectationWithDescription:@"initial refresh"];
  188. refresher.tokenRefreshHandler = ^(FIRAppCheckTokenRefreshCompletion _Nonnull completion) {
  189. [initialRefreshExpectation fulfill];
  190. // Call completion in a while.
  191. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)),
  192. dispatch_get_main_queue(), ^{
  193. __auto_type refreshFailure =
  194. [[FIRAppCheckTokenRefreshResult alloc] initWithStatusFailure];
  195. completion(refreshFailure);
  196. });
  197. };
  198. // 4. Expect for new timer to be created.
  199. // No backoff initially, 1st backoff 30sec, double backoff on each next attempt until 16min.
  200. expectedBackoffTime = expectedBackoffTime == 0 ? 30 : expectedBackoffTime * 2;
  201. expectedBackoffTime = MIN(expectedBackoffTime, maximumBackoffTime);
  202. NSDate *expectedFireDate = [[NSDate date] dateByAddingTimeInterval:expectedBackoffTime];
  203. XCTestExpectation *createTimerExpectation = [self expectationWithDescription:@"create timer"];
  204. self.fakeTimer.createHandler = ^(NSDate *_Nonnull fireDate) {
  205. [createTimerExpectation fulfill];
  206. // Check expected and actual fire date are not too different (account for the random part
  207. // and request attempt delay).
  208. XCTAssertLessThan(ABS([expectedFireDate timeIntervalSinceDate:fireDate]), 2);
  209. };
  210. // 5. Expect checking if auto-refresh allowed before refreshing.
  211. [[[self.mockSettings expect] andReturnValue:@(YES)] isTokenAutoRefreshEnabled];
  212. // 6. Fire initial timer and wait for expectations.
  213. [self fireTimer];
  214. [self waitForExpectations:@[ initialRefreshExpectation, createTimerExpectation ]
  215. timeout:1
  216. enforceOrder:YES];
  217. }
  218. OCMVerifyAll(self.mockSettings);
  219. }
  220. #pragma mark - Auto refresh is not allowed
  221. - (void)testNoInitialRefreshWhenAutoRefreshIsNotAllowed {
  222. FIRAppCheckTokenRefresher *refresher = [self createRefresher];
  223. // 1. Expect checking if auto-refresh allowed before scheduling initial refresh.
  224. [[[self.mockSettings expect] andReturnValue:@(NO)] isTokenAutoRefreshEnabled];
  225. // 2. Don't expect timer to be scheduled.
  226. XCTestExpectation *timerCreateExpectation = [self expectationWithDescription:@"create timer"];
  227. timerCreateExpectation.inverted = YES;
  228. __auto_type weakSelf = self;
  229. self.fakeTimer.createHandler = ^(NSDate *_Nonnull fireDate) {
  230. weakSelf.fakeTimer.createHandler = nil;
  231. [timerCreateExpectation fulfill];
  232. };
  233. // 3. Don't expect refresh handler to be called.
  234. __auto_type refreshResult = [[FIRAppCheckTokenRefreshResult alloc]
  235. initWithStatusSuccessAndExpirationDate:[NSDate dateWithTimeIntervalSinceNow:60 * 60]
  236. receivedAtDate:[NSDate date]];
  237. XCTestExpectation *refreshExpectation = [self expectationWithDescription:@"refresh"];
  238. refreshExpectation.inverted = YES;
  239. refresher.tokenRefreshHandler = ^(FIRAppCheckTokenRefreshCompletion _Nonnull completion) {
  240. [refreshExpectation fulfill];
  241. // Call completion.
  242. completion(refreshResult);
  243. };
  244. // 4. Check if the handler is not fired before the timer.
  245. [self waitForExpectations:@[ timerCreateExpectation, refreshExpectation ] timeout:1];
  246. OCMVerifyAll(self.mockSettings);
  247. }
  248. - (void)testNoRefreshWhenAutoRefreshWasDisabledAfterInit {
  249. FIRAppCheckTokenRefresher *refresher = [self createRefresher];
  250. // 1. Expect checking if auto-refresh allowed before scheduling initial refresh.
  251. [[[self.mockSettings expect] andReturnValue:@(YES)] isTokenAutoRefreshEnabled];
  252. // 2. Expect timer to be scheduled.
  253. NSDate *expectedTimerFireDate =
  254. [self expectedRefreshDateWithReceivedDate:self.initialTokenRefreshResult.tokenReceivedAtDate
  255. expirationDate:self.initialTokenRefreshResult.tokenExpirationDate];
  256. XCTestExpectation *timerCreateExpectation = [self expectationWithDescription:@"create timer"];
  257. __auto_type weakSelf = self;
  258. self.fakeTimer.createHandler = ^(NSDate *_Nonnull fireDate) {
  259. weakSelf.fakeTimer.createHandler = nil;
  260. XCTAssertEqualObjects(fireDate, expectedTimerFireDate);
  261. [timerCreateExpectation fulfill];
  262. };
  263. // 3. Expect refresh handler to be called.
  264. __auto_type refreshResult = [[FIRAppCheckTokenRefreshResult alloc]
  265. initWithStatusSuccessAndExpirationDate:[expectedTimerFireDate
  266. dateByAddingTimeInterval:60 * 60]
  267. receivedAtDate:expectedTimerFireDate];
  268. XCTestExpectation *noRefreshExpectation = [self expectationWithDescription:@"initial refresh"];
  269. noRefreshExpectation.inverted = YES;
  270. refresher.tokenRefreshHandler = ^(FIRAppCheckTokenRefreshCompletion _Nonnull completion) {
  271. [noRefreshExpectation fulfill];
  272. // Call completion.
  273. completion(refreshResult);
  274. };
  275. // 4. Check if the handler is not fired before the timer.
  276. [self waitForExpectations:@[ timerCreateExpectation ] timeout:1];
  277. // 5. Expect checking if auto-refresh allowed before refreshing.
  278. [[[self.mockSettings expect] andReturnValue:@(NO)] isTokenAutoRefreshEnabled];
  279. // 6. Fire the timer and wait for completion.
  280. [self fireTimer];
  281. [self waitForExpectations:@[ noRefreshExpectation ] timeout:1];
  282. OCMVerifyAll(self.mockSettings);
  283. }
  284. #pragma mark - Update token expiration
  285. - (void)testUpdateWithRefreshResultWhenAutoRefreshIsAllowed {
  286. FIRAppCheckTokenRefresher *refresher = [self createRefresher];
  287. NSDate *newExpirationDate =
  288. [self.initialTokenRefreshResult.tokenExpirationDate dateByAddingTimeInterval:10 * 60];
  289. __auto_type newRefreshResult = [[FIRAppCheckTokenRefreshResult alloc]
  290. initWithStatusSuccessAndExpirationDate:newExpirationDate
  291. receivedAtDate:self.initialTokenRefreshResult.tokenExpirationDate];
  292. // 1. Expect checking if auto-refresh allowed before scheduling refresh.
  293. [[[self.mockSettings expect] andReturnValue:@(YES)] isTokenAutoRefreshEnabled];
  294. // 2. Expect timer to be scheduled.
  295. NSDate *expectedTimerFireDate =
  296. [self expectedRefreshDateWithReceivedDate:newRefreshResult.tokenReceivedAtDate
  297. expirationDate:newRefreshResult.tokenExpirationDate];
  298. XCTestExpectation *timerCreateExpectation = [self expectationWithDescription:@"create timer"];
  299. __auto_type weakSelf = self;
  300. self.fakeTimer.createHandler = ^(NSDate *_Nonnull fireDate) {
  301. weakSelf.fakeTimer.createHandler = nil;
  302. XCTAssertEqualObjects(fireDate, expectedTimerFireDate);
  303. [timerCreateExpectation fulfill];
  304. };
  305. // 3. Update token expiration date.
  306. [refresher updateWithRefreshResult:newRefreshResult];
  307. // 4. Wait for timer to be created.
  308. [self waitForExpectations:@[ timerCreateExpectation ] timeout:1];
  309. OCMVerifyAll(self.mockSettings);
  310. }
  311. - (void)testUpdateWithRefreshResultWhenAutoRefreshIsNotAllowed {
  312. FIRAppCheckTokenRefresher *refresher = [self createRefresher];
  313. __auto_type newRefreshResult = [[FIRAppCheckTokenRefreshResult alloc]
  314. initWithStatusSuccessAndExpirationDate:[NSDate dateWithTimeIntervalSinceNow:60 * 60]
  315. receivedAtDate:self.initialTokenRefreshResult.tokenExpirationDate];
  316. // 1. Expect checking if auto-refresh allowed before scheduling initial refresh.
  317. [[[self.mockSettings expect] andReturnValue:@(NO)] isTokenAutoRefreshEnabled];
  318. // 2. Don't expect timer to be scheduled.
  319. XCTestExpectation *timerCreateExpectation = [self expectationWithDescription:@"create timer"];
  320. timerCreateExpectation.inverted = YES;
  321. __auto_type weakSelf = self;
  322. self.fakeTimer.createHandler = ^(NSDate *_Nonnull fireDate) {
  323. weakSelf.fakeTimer.createHandler = nil;
  324. [timerCreateExpectation fulfill];
  325. };
  326. // 3. Update token expiration date.
  327. [refresher updateWithRefreshResult:newRefreshResult];
  328. // 4. Wait for timer to be created.
  329. [self waitForExpectations:@[ timerCreateExpectation ] timeout:1];
  330. OCMVerifyAll(self.mockSettings);
  331. }
  332. - (void)testUpdateWithRefreshResult_WhenTokenExpiresLessThanIn1Minute {
  333. FIRAppCheckTokenRefresher *refresher = [self createRefresher];
  334. NSDate *newExpirationDate = [NSDate dateWithTimeIntervalSinceNow:0.5 * 60];
  335. __auto_type newRefreshResult = [[FIRAppCheckTokenRefreshResult alloc]
  336. initWithStatusSuccessAndExpirationDate:newExpirationDate
  337. receivedAtDate:[NSDate date]];
  338. // 1. Expect checking if auto-refresh allowed before scheduling refresh.
  339. [[[self.mockSettings expect] andReturnValue:@(YES)] isTokenAutoRefreshEnabled];
  340. // 2. Expect timer to be scheduled in at least 1 minute.
  341. XCTestExpectation *timerCreateExpectation = [self expectationWithDescription:@"create timer"];
  342. __auto_type weakSelf = self;
  343. self.fakeTimer.createHandler = ^(NSDate *_Nonnull fireDate) {
  344. weakSelf.fakeTimer.createHandler = nil;
  345. // 1 minute is the minimal interval between successful refreshes.
  346. XCTAssert([FIRDateTestUtils isDate:fireDate
  347. approximatelyEqualCurrentPlusTimeInterval:60
  348. precision:1]);
  349. [timerCreateExpectation fulfill];
  350. };
  351. // 3. Update token expiration date.
  352. [refresher updateWithRefreshResult:newRefreshResult];
  353. // 4. Wait for timer to be created.
  354. [self waitForExpectations:@[ timerCreateExpectation ] timeout:1];
  355. OCMVerifyAll(self.mockSettings);
  356. }
  357. #pragma mark - Helpers
  358. - (void)fireTimer {
  359. if (self.fakeTimer.handler) {
  360. self.fakeTimer.handler();
  361. } else {
  362. XCTFail(@"handler must not be nil!");
  363. }
  364. }
  365. - (FIRAppCheckTokenRefresher *)createRefresher {
  366. return [[FIRAppCheckTokenRefresher alloc] initWithRefreshResult:self.initialTokenRefreshResult
  367. timerProvider:[self.fakeTimer fakeTimerProvider]
  368. settings:self.mockSettings];
  369. }
  370. - (NSDate *)expectedRefreshDateWithReceivedDate:(NSDate *)receivedDate
  371. expirationDate:(NSDate *)expirationDate {
  372. NSTimeInterval timeToLive = [expirationDate timeIntervalSinceDate:receivedDate];
  373. XCTAssertGreaterThanOrEqual(timeToLive, 0);
  374. NSTimeInterval timeToRefresh = timeToLive / 2 + 5 * 60; // 50% of TTL + 5 min
  375. NSTimeInterval minimalAutoRefreshInterval = 60; // 1 min
  376. timeToRefresh = MAX(timeToRefresh, minimalAutoRefreshInterval);
  377. NSDate *refreshDate = [receivedDate dateByAddingTimeInterval:timeToRefresh];
  378. NSDate *now = [NSDate date];
  379. return [refreshDate laterDate:now];
  380. }
  381. @end