FIRMessagingTokenOperationsTest.m 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508
  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 "FirebaseCore/Extension/FirebaseCoreInternal.h"
  19. #import "FirebaseInstallations/Source/Library/Private/FirebaseInstallationsInternal.h"
  20. #import "FirebaseMessaging/Sources/FIRMessagingConstants.h"
  21. #import "FirebaseMessaging/Sources/NSError+FIRMessaging.h"
  22. #import "FirebaseMessaging/Sources/Token/FIRMessagingAuthService.h"
  23. #import "FirebaseMessaging/Sources/Token/FIRMessagingCheckinPreferences.h"
  24. #import "FirebaseMessaging/Sources/Token/FIRMessagingCheckinService.h"
  25. #import "FirebaseMessaging/Sources/Token/FIRMessagingCheckinStore.h"
  26. #import "FirebaseMessaging/Sources/Token/FIRMessagingKeychain.h"
  27. #import "FirebaseMessaging/Sources/Token/FIRMessagingTokenDeleteOperation.h"
  28. #import "FirebaseMessaging/Sources/Token/FIRMessagingTokenFetchOperation.h"
  29. #import "FirebaseMessaging/Sources/Token/FIRMessagingTokenOperation.h"
  30. #import "FirebaseMessaging/Sources/Token/FIRMessagingTokenStore.h"
  31. #import "SharedTestUtilities/URLSession/FIRURLSessionOCMockStub.h"
  32. static NSString *kDeviceID = @"fakeDeviceID";
  33. static NSString *kSecretToken = @"fakeSecretToken";
  34. static NSString *kDigestString = @"test-digest";
  35. static NSString *kVersionInfoString = @"version_info-1.0.0";
  36. static NSString *kAuthorizedEntity = @"sender-1234567";
  37. static NSString *kScope = @"fcm";
  38. static NSString *kRegistrationToken = @"token-12345";
  39. @interface FIRMessagingTokenOperation (ExposedForTest)
  40. - (void)performTokenOperation;
  41. + (NSString *)HTTPAuthHeaderFromCheckin:(FIRMessagingCheckinPreferences *)checkin;
  42. @end
  43. @interface FIRInstallationsAuthTokenResult (Tests)
  44. - (instancetype)initWithToken:(NSString *)token expirationDate:(NSDate *)expirationDate;
  45. @end
  46. #pragma mark - Fakes
  47. // A Fake operation that allows us to check that perform was called.
  48. // We are not using mocks here because we have no way of forcing NSOperationQueues to release
  49. // their operations, and this means that there is always going to be a race condition between
  50. // when we "stop" our partial mock vs when NSOperationQueue attempts to access the mock object on a
  51. // separate thread. We had mocks previously.
  52. @interface FIRMessagingTokenOperationFake : FIRMessagingTokenOperation
  53. @property(nonatomic, assign) BOOL performWasCalled;
  54. @end
  55. @implementation FIRMessagingTokenOperationFake
  56. - (void)performTokenOperation {
  57. self.performWasCalled = YES;
  58. }
  59. @end
  60. /// A fake heartbeat logger used for dependency injection during testing.
  61. @interface FIRHeartbeatLoggerFake : NSObject <FIRHeartbeatLoggerProtocol>
  62. @property(nonatomic, copy, nullable) FIRDailyHeartbeatCode (^onHeartbeatCodeForTodayHandler)(void);
  63. @end
  64. @implementation FIRHeartbeatLoggerFake
  65. - (nonnull FIRHeartbeatsPayload *)flushHeartbeatsIntoPayload {
  66. // This API should not be used by the below tests because the Messaging
  67. // SDK uses only the V1 heartbeat API (`heartbeatCodeForToday`) for
  68. // getting a single heartbeat.
  69. [self doesNotRecognizeSelector:_cmd];
  70. return nil;
  71. }
  72. - (FIRDailyHeartbeatCode)heartbeatCodeForToday {
  73. if (self.onHeartbeatCodeForTodayHandler) {
  74. return self.onHeartbeatCodeForTodayHandler();
  75. } else {
  76. return FIRDailyHeartbeatCodeNone;
  77. }
  78. }
  79. - (void)log {
  80. // This API should not be used by the below tests because the Messaging
  81. // SDK does not log heartbeats in its networking context.
  82. [self doesNotRecognizeSelector:_cmd];
  83. }
  84. - (NSString *_Nullable)headerValue {
  85. return @"unimplemented";
  86. }
  87. - (void)asyncHeaderValueWithCompletionHandler:
  88. (nonnull void (^)(NSString *_Nullable))completionHandler {
  89. [self doesNotRecognizeSelector:_cmd];
  90. }
  91. @end
  92. #pragma mark - FIRMessagingTokenOperationsTest
  93. @interface FIRMessagingTokenOperationsTest : XCTestCase
  94. @property(nonatomic) id URLSessionMock;
  95. @property(strong, readonly, nonatomic) FIRMessagingAuthService *authService;
  96. @property(strong, readonly, nonatomic) id mockAuthService;
  97. @property(strong, readonly, nonatomic) id mockTokenStore;
  98. @property(strong, readonly, nonatomic) FIRMessagingCheckinService *checkinService;
  99. @property(strong, readonly, nonatomic) id mockCheckinService;
  100. @property(strong, readonly, nonatomic) id mockInstallations;
  101. @property(strong, readonly, nonatomic) id mockHeartbeatInfo;
  102. @property(strong, readonly, nonatomic) NSString *instanceID;
  103. @property(nonatomic, readwrite, strong) FIRMessagingCheckinPreferences *checkinPreferences;
  104. @end
  105. @implementation FIRMessagingTokenOperationsTest
  106. - (void)setUp {
  107. [super setUp];
  108. // Stub NSURLSession constructor before instantiating FIRMessagingCheckinService to inject
  109. // URLSessionMock.
  110. self.URLSessionMock = OCMClassMock([NSURLSession class]);
  111. OCMStub(ClassMethod([self.URLSessionMock sessionWithConfiguration:[OCMArg any]]))
  112. .andReturn(self.URLSessionMock);
  113. _mockTokenStore = OCMClassMock([FIRMessagingTokenStore class]);
  114. _checkinService = [[FIRMessagingCheckinService alloc] init];
  115. _mockCheckinService = OCMPartialMock(_checkinService);
  116. _authService = [[FIRMessagingAuthService alloc] init];
  117. _instanceID = @"instanceID";
  118. // `FIRMessagingTokenOperation` uses `FIRInstallations` under the hood to get FIS auth token.
  119. // Stub `FIRInstallations` to avoid using a real object.
  120. [self stubInstallations];
  121. }
  122. - (void)tearDown {
  123. _authService = nil;
  124. [_mockCheckinService stopMocking];
  125. _mockCheckinService = nil;
  126. _checkinService = nil;
  127. _mockTokenStore = nil;
  128. [_mockInstallations stopMocking];
  129. [_mockHeartbeatInfo stopMocking];
  130. }
  131. - (void)testThatTokenOperationsAuthHeaderStringMatchesCheckin {
  132. int64_t tenHoursAgo = FIRMessagingCurrentTimestampInMilliseconds() - 10 * 60 * 60 * 1000;
  133. FIRMessagingCheckinPreferences *checkin =
  134. [self setCheckinPreferencesWithLastCheckinTime:tenHoursAgo];
  135. NSString *expectedAuthHeader = [FIRMessagingTokenOperation HTTPAuthHeaderFromCheckin:checkin];
  136. XCTestExpectation *authHeaderMatchesCheckinExpectation =
  137. [self expectationWithDescription:@"Auth header string in request matches checkin info"];
  138. FIRMessagingTokenFetchOperation *operation = [[FIRMessagingTokenFetchOperation alloc]
  139. initWithAuthorizedEntity:kAuthorizedEntity
  140. scope:kScope
  141. options:nil
  142. checkinPreferences:checkin
  143. instanceID:self.instanceID
  144. heartbeatLogger:[[FIRHeartbeatLoggerFake alloc] init]];
  145. NSURL *expectedRequestURL = [NSURL URLWithString:FIRMessagingTokenRegisterServer()];
  146. NSHTTPURLResponse *expectedResponse = [[NSHTTPURLResponse alloc] initWithURL:expectedRequestURL
  147. statusCode:200
  148. HTTPVersion:@"HTTP/1.1"
  149. headerFields:nil];
  150. [FIRURLSessionOCMockStub
  151. stubURLSessionDataTaskWithResponse:expectedResponse
  152. body:[self dataForResponseWithValidToken:YES]
  153. error:nil
  154. URLSessionMock:self.URLSessionMock
  155. requestValidationBlock:^BOOL(NSURLRequest *_Nonnull sentRequest) {
  156. NSDictionary<NSString *, NSString *> *headers = sentRequest.allHTTPHeaderFields;
  157. NSString *authHeader = headers[@"Authorization"];
  158. if ([authHeader isEqualToString:expectedAuthHeader]) {
  159. [authHeaderMatchesCheckinExpectation fulfill];
  160. }
  161. return YES;
  162. }];
  163. [operation start];
  164. [self waitForExpectationsWithTimeout:0.25
  165. handler:^(NSError *_Nullable error) {
  166. XCTAssertNil(error.localizedDescription);
  167. }];
  168. }
  169. - (void)testThatTokenOperationWithoutCheckInFails {
  170. // If asserts are enabled, test for the assert to be thrown, otherwise check for the resulting
  171. // error in the completion handler.
  172. XCTestExpectation *failedExpectation =
  173. [self expectationWithDescription:@"Operation failed without checkin info"];
  174. // This will return hasCheckinInfo == NO
  175. FIRMessagingCheckinPreferences *emptyCheckinPreferences =
  176. [[FIRMessagingCheckinPreferences alloc] initWithDeviceID:@"" secretToken:@""];
  177. FIRMessagingTokenOperation *operation =
  178. [[FIRMessagingTokenOperation alloc] initWithAction:FIRMessagingTokenActionFetch
  179. forAuthorizedEntity:kAuthorizedEntity
  180. scope:kScope
  181. options:nil
  182. checkinPreferences:emptyCheckinPreferences
  183. instanceID:self.instanceID
  184. heartbeatLogger:[[FIRHeartbeatLoggerFake alloc] init]];
  185. [operation addCompletionHandler:^(FIRMessagingTokenOperationResult result,
  186. NSString *_Nullable token, NSError *_Nullable error) {
  187. [failedExpectation fulfill];
  188. }];
  189. @try {
  190. [operation start];
  191. } @catch (NSException *exception) {
  192. if (exception.name == NSInternalInconsistencyException) {
  193. [failedExpectation fulfill];
  194. }
  195. } @finally {
  196. }
  197. [self waitForExpectationsWithTimeout:0.25
  198. handler:^(NSError *_Nullable error) {
  199. XCTAssertNil(error.localizedDescription);
  200. }];
  201. }
  202. - (void)testThatAnAlreadyCancelledOperationFinishesWithoutStarting {
  203. XCTestExpectation *cancelledExpectation =
  204. [self expectationWithDescription:@"Operation finished as cancelled"];
  205. XCTestExpectation *didNotCallPerform =
  206. [self expectationWithDescription:@"Did not call performTokenOperation"];
  207. int64_t tenHoursAgo = FIRMessagingCurrentTimestampInMilliseconds() - 10 * 60 * 60 * 1000;
  208. FIRMessagingCheckinPreferences *checkinPreferences =
  209. [self setCheckinPreferencesWithLastCheckinTime:tenHoursAgo];
  210. FIRMessagingTokenOperationFake *operation =
  211. [[FIRMessagingTokenOperationFake alloc] initWithAction:FIRMessagingTokenActionFetch
  212. forAuthorizedEntity:kAuthorizedEntity
  213. scope:kScope
  214. options:nil
  215. checkinPreferences:checkinPreferences
  216. instanceID:self.instanceID
  217. heartbeatLogger:[[FIRHeartbeatLoggerFake alloc] init]];
  218. operation.performWasCalled = NO;
  219. __weak FIRMessagingTokenOperationFake *weakOperation = operation;
  220. [operation addCompletionHandler:^(FIRMessagingTokenOperationResult result,
  221. NSString *_Nullable token, NSError *_Nullable error) {
  222. if (result == FIRMessagingTokenOperationCancelled) {
  223. [cancelledExpectation fulfill];
  224. }
  225. if (!weakOperation.performWasCalled) {
  226. [didNotCallPerform fulfill];
  227. }
  228. }];
  229. [operation cancel];
  230. [operation start];
  231. [self waitForExpectationsWithTimeout:0.25
  232. handler:^(NSError *_Nullable error) {
  233. XCTAssertNil(error.localizedDescription);
  234. }];
  235. }
  236. - (void)testThatOptionsDictionaryIsIncludedWithFetchRequest {
  237. XCTestExpectation *optionsIncludedExpectation =
  238. [self expectationWithDescription:@"Options keys were included in token URL request"];
  239. int64_t tenHoursAgo = FIRMessagingCurrentTimestampInMilliseconds() - 10 * 60 * 60 * 1000;
  240. FIRMessagingCheckinPreferences *checkinPreferences =
  241. [self setCheckinPreferencesWithLastCheckinTime:tenHoursAgo];
  242. NSData *fakeDeviceToken = [@"fakeAPNSToken" dataUsingEncoding:NSUTF8StringEncoding];
  243. BOOL isSandbox = NO;
  244. NSString *apnsTupleString =
  245. FIRMessagingAPNSTupleStringForTokenAndServerType(fakeDeviceToken, isSandbox);
  246. NSDictionary *options = @{
  247. kFIRMessagingTokenOptionsFirebaseAppIDKey : @"fakeGMPAppID",
  248. kFIRMessagingTokenOptionsAPNSKey : fakeDeviceToken,
  249. kFIRMessagingTokenOptionsAPNSIsSandboxKey : @(isSandbox),
  250. };
  251. FIRMessagingTokenFetchOperation *operation = [[FIRMessagingTokenFetchOperation alloc]
  252. initWithAuthorizedEntity:kAuthorizedEntity
  253. scope:kScope
  254. options:options
  255. checkinPreferences:checkinPreferences
  256. instanceID:self.instanceID
  257. heartbeatLogger:[[FIRHeartbeatLoggerFake alloc] init]];
  258. NSURL *expectedRequestURL = [NSURL URLWithString:FIRMessagingTokenRegisterServer()];
  259. NSHTTPURLResponse *expectedResponse = [[NSHTTPURLResponse alloc] initWithURL:expectedRequestURL
  260. statusCode:200
  261. HTTPVersion:@"HTTP/1.1"
  262. headerFields:nil];
  263. [FIRURLSessionOCMockStub
  264. stubURLSessionDataTaskWithResponse:expectedResponse
  265. body:[self dataForResponseWithValidToken:YES]
  266. error:nil
  267. URLSessionMock:self.URLSessionMock
  268. requestValidationBlock:^BOOL(NSURLRequest *_Nonnull sentRequest) {
  269. NSString *query = [[NSString alloc] initWithData:sentRequest.HTTPBody
  270. encoding:NSUTF8StringEncoding];
  271. NSString *gmpAppIDQueryTuple = [NSString
  272. stringWithFormat:@"%@=%@", kFIRMessagingTokenOptionsFirebaseAppIDKey,
  273. options[kFIRMessagingTokenOptionsFirebaseAppIDKey]];
  274. NSRange gmpAppIDRange = [query rangeOfString:gmpAppIDQueryTuple];
  275. NSString *apnsQueryTuple =
  276. [NSString stringWithFormat:@"%@=%@", kFIRMessagingTokenOptionsAPNSKey,
  277. apnsTupleString];
  278. NSRange apnsRange = [query rangeOfString:apnsQueryTuple];
  279. if (gmpAppIDRange.location != NSNotFound && apnsRange.location != NSNotFound) {
  280. [optionsIncludedExpectation fulfill];
  281. }
  282. return YES;
  283. }];
  284. [operation start];
  285. [self waitForExpectationsWithTimeout:0.25
  286. handler:^(NSError *_Nullable error) {
  287. XCTAssertNil(error.localizedDescription);
  288. }];
  289. }
  290. - (void)testServerResetCommand {
  291. XCTestExpectation *shouldResetIdentityExpectation =
  292. [self expectationWithDescription:
  293. @"When server sends down RST error, clients should return reset identity error."];
  294. int64_t tenHoursAgo = FIRMessagingCurrentTimestampInMilliseconds() - 10 * 60 * 60 * 1000;
  295. FIRMessagingCheckinPreferences *checkinPreferences =
  296. [self setCheckinPreferencesWithLastCheckinTime:tenHoursAgo];
  297. FIRMessagingTokenFetchOperation *operation = [[FIRMessagingTokenFetchOperation alloc]
  298. initWithAuthorizedEntity:kAuthorizedEntity
  299. scope:kScope
  300. options:nil
  301. checkinPreferences:checkinPreferences
  302. instanceID:self.instanceID
  303. heartbeatLogger:[[FIRHeartbeatLoggerFake alloc] init]];
  304. NSURL *expectedRequestURL = [NSURL URLWithString:FIRMessagingTokenRegisterServer()];
  305. NSHTTPURLResponse *expectedResponse = [[NSHTTPURLResponse alloc] initWithURL:expectedRequestURL
  306. statusCode:200
  307. HTTPVersion:@"HTTP/1.1"
  308. headerFields:nil];
  309. [FIRURLSessionOCMockStub
  310. stubURLSessionDataTaskWithResponse:expectedResponse
  311. body:[self dataForResponseWithValidToken:NO]
  312. error:nil
  313. URLSessionMock:self.URLSessionMock
  314. requestValidationBlock:^BOOL(NSURLRequest *_Nonnull sentRequest) {
  315. return YES;
  316. }];
  317. [operation addCompletionHandler:^(FIRMessagingTokenOperationResult result,
  318. NSString *_Nullable token, NSError *_Nullable error) {
  319. XCTAssertEqual(result, FIRMessagingTokenOperationError);
  320. XCTAssertNotNil(error);
  321. XCTAssertEqual(error.code, kFIRMessagingErrorCodeInvalidIdentity);
  322. [shouldResetIdentityExpectation fulfill];
  323. }];
  324. [operation start];
  325. [self waitForExpectationsWithTimeout:0.25
  326. handler:^(NSError *_Nullable error) {
  327. XCTAssertNil(error.localizedDescription);
  328. }];
  329. }
  330. - (void)testHTTPAuthHeaderGenerationFromCheckin {
  331. FIRMessagingCheckinPreferences *checkinPreferences =
  332. [[FIRMessagingCheckinPreferences alloc] initWithDeviceID:kDeviceID secretToken:kSecretToken];
  333. NSString *expectedHeader =
  334. [NSString stringWithFormat:@"AidLogin %@:%@", checkinPreferences.deviceID,
  335. checkinPreferences.secretToken];
  336. NSString *generatedHeader =
  337. [FIRMessagingTokenOperation HTTPAuthHeaderFromCheckin:checkinPreferences];
  338. XCTAssertEqualObjects(generatedHeader, expectedHeader);
  339. }
  340. - (void)testTokenFetchOperationFirebaseUserAgentAndHeartbeatHeader_WhenHeartbeatNeedsSending {
  341. [self assertTokenFetchOperationRequestContainsFirebaseUserAgentAndHeartbeatInfoCode:
  342. FIRDailyHeartbeatCodeSome];
  343. }
  344. - (void)testTokenFetchOperationFirebaseUserAgentAndHeartbeatHeader_WhenNoHeartbeatNeedsSending {
  345. [self assertTokenFetchOperationRequestContainsFirebaseUserAgentAndHeartbeatInfoCode:
  346. FIRDailyHeartbeatCodeNone];
  347. }
  348. #pragma mark - Internal Helpers
  349. - (void)assertTokenFetchOperationRequestContainsFirebaseUserAgentAndHeartbeatInfoCode:
  350. (FIRDailyHeartbeatCode)heartbeatInfoCode {
  351. XCTestExpectation *completionExpectation =
  352. [self expectationWithDescription:@"completionExpectation"];
  353. FIRHeartbeatLoggerFake *heartbeatLoggerFake = [[FIRHeartbeatLoggerFake alloc] init];
  354. XCTestExpectation *heartbeatExpectation =
  355. [self expectationWithDescription:@"heartbeatExpectation"];
  356. heartbeatLoggerFake.onHeartbeatCodeForTodayHandler = ^FIRDailyHeartbeatCode {
  357. [heartbeatExpectation fulfill];
  358. return heartbeatInfoCode;
  359. };
  360. FIRMessagingCheckinPreferences *checkinPreferences =
  361. [self setCheckinPreferencesWithLastCheckinTime:0];
  362. FIRMessagingTokenFetchOperation *operation =
  363. [[FIRMessagingTokenFetchOperation alloc] initWithAuthorizedEntity:kAuthorizedEntity
  364. scope:kScope
  365. options:nil
  366. checkinPreferences:checkinPreferences
  367. instanceID:self.instanceID
  368. heartbeatLogger:heartbeatLoggerFake];
  369. NSURL *expectedRequestURL = [NSURL URLWithString:FIRMessagingTokenRegisterServer()];
  370. NSHTTPURLResponse *expectedResponse = [[NSHTTPURLResponse alloc] initWithURL:expectedRequestURL
  371. statusCode:200
  372. HTTPVersion:@"HTTP/1.1"
  373. headerFields:nil];
  374. [FIRURLSessionOCMockStub
  375. stubURLSessionDataTaskWithResponse:expectedResponse
  376. body:[self dataForResponseWithValidToken:NO]
  377. error:nil
  378. URLSessionMock:self.URLSessionMock
  379. requestValidationBlock:^BOOL(NSURLRequest *_Nonnull sentRequest) {
  380. NSString *userAgentValue =
  381. sentRequest.allHTTPHeaderFields[kFIRMessagingFirebaseUserAgentKey];
  382. XCTAssertEqualObjects(userAgentValue, [FIRApp firebaseUserAgent]);
  383. NSString *heartBeatCode =
  384. sentRequest.allHTTPHeaderFields[kFIRMessagingFirebaseHeartbeatKey];
  385. // It is expected that the global heartbeat matches passed in
  386. // `heartbeatInfoCode`.
  387. XCTAssertEqual(heartBeatCode.integerValue, heartbeatInfoCode);
  388. [completionExpectation fulfill];
  389. return YES;
  390. }];
  391. [operation start];
  392. [self waitForExpectationsWithTimeout:0.25
  393. handler:^(NSError *_Nullable error) {
  394. XCTAssertNil(error.localizedDescription);
  395. }];
  396. }
  397. - (NSData *)dataForResponseWithValidToken:(BOOL)validToken {
  398. NSString *response;
  399. if (validToken) {
  400. response = [NSString stringWithFormat:@"token=%@", kRegistrationToken];
  401. } else {
  402. response = @"Error=RST";
  403. }
  404. return [response dataUsingEncoding:NSUTF8StringEncoding];
  405. }
  406. - (FIRMessagingCheckinPreferences *)setCheckinPreferencesWithLastCheckinTime:(int64_t)time {
  407. FIRMessagingCheckinPreferences *checkinPreferences =
  408. [[FIRMessagingCheckinPreferences alloc] initWithDeviceID:kDeviceID secretToken:kSecretToken];
  409. NSDictionary *checkinPlistContents = @{
  410. kFIRMessagingDigestStringKey : kDigestString,
  411. kFIRMessagingVersionInfoStringKey : kVersionInfoString,
  412. kFIRMessagingLastCheckinTimeKey : @(time)
  413. };
  414. [checkinPreferences updateWithCheckinPlistContents:checkinPlistContents];
  415. // manually initialize the checkin preferences
  416. self.checkinPreferences = checkinPreferences;
  417. return checkinPreferences;
  418. }
  419. - (void)stubInstallations {
  420. _mockInstallations = OCMClassMock([FIRInstallations class]);
  421. OCMStub([_mockInstallations installations]).andReturn(_mockInstallations);
  422. FIRInstallationsAuthTokenResult *authToken =
  423. [[FIRInstallationsAuthTokenResult alloc] initWithToken:@"fis-auth-token"
  424. expirationDate:[NSDate distantFuture]];
  425. id authTokenWithCompletionArg = [OCMArg invokeBlockWithArgs:authToken, [NSNull null], nil];
  426. OCMStub([_mockInstallations authTokenWithCompletion:authTokenWithCompletionArg]);
  427. }
  428. @end