FIRMessagingCheckinStoreTest.m 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  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 <TargetConditionals.h>
  17. #if !TARGET_OS_MACCATALYST
  18. #import <XCTest/XCTest.h>
  19. #import <OCMock/OCMock.h>
  20. #import "FirebaseMessaging/Sources/FIRMessagingUtilities.h"
  21. #import "FirebaseMessaging/Sources/Public/FirebaseMessaging/FIRMessaging.h"
  22. #import "FirebaseMessaging/Sources/Token/FIRMessagingAuthKeychain.h"
  23. #import "FirebaseMessaging/Sources/Token/FIRMessagingBackupExcludedPlist.h"
  24. #import "FirebaseMessaging/Sources/Token/FIRMessagingCheckinPreferences.h"
  25. #import "FirebaseMessaging/Sources/Token/FIRMessagingCheckinService.h"
  26. #import "FirebaseMessaging/Sources/Token/FIRMessagingCheckinStore.h"
  27. #import "FirebaseMessaging/Tests/UnitTests/FIRMessagingFakeKeychain.h"
  28. static const NSTimeInterval kExpectationTimeout = 12;
  29. @interface FIRMessagingCheckinStore ()
  30. @property(nonatomic, readwrite, strong) FIRMessagingAuthKeychain *keychain;
  31. @property(nonatomic, readwrite, strong) FIRMessagingBackupExcludedPlist *plist;
  32. - (NSString *)bundleIdentifierForKeychainAccount;
  33. @end
  34. @interface FIRMessaging (ExposedForTest)
  35. + (BOOL)createSubDirectory:(NSString *)subDirectoryName;
  36. @end
  37. @interface FIRMessagingBackupExcludedPlist (ExposedForTest)
  38. - (BOOL)deleteFile:(NSError **)error;
  39. @end
  40. // Testing constants
  41. static NSString *const kFakeCheckinPlistName = @"com.google.test.TestCheckin";
  42. static NSString *const kSubDirectoryName = @"FirebaseInstanceIDCheckinTest";
  43. static NSString *const kAuthID = @"test-auth-id";
  44. static NSString *const kDigest = @"test-digest";
  45. static NSString *const kSecret = @"test-secret";
  46. static NSString *const kFakeErrorDomain = @"fakeDomain";
  47. static const NSUInteger kFakeErrorCode = -1;
  48. static int64_t const kLastCheckinTimestamp = 123456;
  49. @interface FIRMessagingCheckinStoreTest : XCTestCase
  50. @property(nonatomic, strong) FIRMessagingCheckinStore *checkinStore;
  51. @property(nonatomic, strong) FIRMessagingBackupExcludedPlist *plist;
  52. @end
  53. @implementation FIRMessagingCheckinStoreTest
  54. - (void)setUp {
  55. [super setUp];
  56. [FIRMessaging createSubDirectory:kSubDirectoryName];
  57. self.checkinStore = [[FIRMessagingCheckinStore alloc] init];
  58. self.plist = [[FIRMessagingBackupExcludedPlist alloc] initWithFileName:kFakeCheckinPlistName
  59. subDirectory:kSubDirectoryName];
  60. self.checkinStore.plist = self.plist;
  61. }
  62. - (void)tearDown {
  63. [self.plist deleteFile:nil];
  64. [super tearDown];
  65. }
  66. /**
  67. * Keychain read failure should lead to checkin preferences with invalid credentials.
  68. */
  69. - (void)testInvalidCheckinPreferencesOnKeychainFail {
  70. XCTestExpectation *checkinInvalidExpectation = [self
  71. expectationWithDescription:@"Checkin preference should be invalid after keychain failure"];
  72. FIRMessagingFakeKeychain *fakeKeychain = [[FIRMessagingFakeKeychain alloc] init];
  73. self.checkinStore.keychain = fakeKeychain;
  74. __block FIRMessagingCheckinPreferences *preferences =
  75. [[FIRMessagingCheckinPreferences alloc] initWithDeviceID:kAuthID secretToken:kSecret];
  76. [preferences updateWithCheckinPlistContents:[[self class] newCheckinPlistPreferences]];
  77. [self.checkinStore saveCheckinPreferences:preferences
  78. handler:^(NSError *error) {
  79. XCTAssertNil(error);
  80. fakeKeychain.cannotReadFromKeychain = YES;
  81. preferences = [self->_checkinStore cachedCheckinPreferences];
  82. XCTAssertNil(preferences.deviceID);
  83. XCTAssertNil(preferences.secretToken);
  84. XCTAssertFalse([preferences hasValidCheckinInfo]);
  85. [checkinInvalidExpectation fulfill];
  86. }];
  87. [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
  88. }
  89. /**
  90. * CheckinStore should not be able to save the checkin preferences if the write to the
  91. * Keychain fails.
  92. */
  93. - (void)testCheckinSaveFailsOnKeychainWriteFailure {
  94. XCTestExpectation *checkinSaveFailsExpectation =
  95. [self expectationWithDescription:@"Checkin save should fail after keychain write failure"];
  96. FIRMessagingFakeKeychain *fakeKeychain = [[FIRMessagingFakeKeychain alloc] init];
  97. fakeKeychain.cannotWriteToKeychain = YES;
  98. self.checkinStore.keychain = fakeKeychain;
  99. __block FIRMessagingCheckinPreferences *preferences =
  100. [[FIRMessagingCheckinPreferences alloc] initWithDeviceID:kAuthID secretToken:kSecret];
  101. [preferences updateWithCheckinPlistContents:[[self class] newCheckinPlistPreferences]];
  102. [self.checkinStore saveCheckinPreferences:preferences
  103. handler:^(NSError *error) {
  104. XCTAssertNotNil(error);
  105. preferences = [self->_checkinStore cachedCheckinPreferences];
  106. XCTAssertNil(preferences.deviceID);
  107. XCTAssertNil(preferences.secretToken);
  108. XCTAssertFalse([preferences hasValidCheckinInfo]);
  109. [checkinSaveFailsExpectation fulfill];
  110. }];
  111. [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
  112. }
  113. - (void)testCheckinSaveFailsOnPlistWriteFailure {
  114. XCTestExpectation *checkinSaveFailsExpectation =
  115. [self expectationWithDescription:@"Checkin save should fail after plist write failure"];
  116. id plistMock = OCMPartialMock(self.plist);
  117. NSError *error = [NSError errorWithDomain:kFakeErrorDomain code:kFakeErrorCode userInfo:nil];
  118. OCMStub([plistMock writeDictionary:[OCMArg any] error:[OCMArg setTo:error]]).andReturn(NO);
  119. FIRMessagingFakeKeychain *fakeKeychain = [[FIRMessagingFakeKeychain alloc] init];
  120. self.checkinStore.keychain = fakeKeychain;
  121. __block FIRMessagingCheckinPreferences *preferences =
  122. [[FIRMessagingCheckinPreferences alloc] initWithDeviceID:kAuthID secretToken:kSecret];
  123. [preferences updateWithCheckinPlistContents:[[self class] newCheckinPlistPreferences]];
  124. [self.checkinStore saveCheckinPreferences:preferences
  125. handler:^(NSError *error) {
  126. XCTAssertNotNil(error);
  127. XCTAssertEqual(error.code, kFakeErrorCode);
  128. preferences = [self->_checkinStore cachedCheckinPreferences];
  129. XCTAssertNil(preferences.deviceID);
  130. XCTAssertNil(preferences.secretToken);
  131. XCTAssertFalse([preferences hasValidCheckinInfo]);
  132. [checkinSaveFailsExpectation fulfill];
  133. }];
  134. [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
  135. }
  136. - (void)testCheckinSaveSuccess {
  137. XCTestExpectation *checkinSaveSuccessExpectation =
  138. [self expectationWithDescription:@"Checkin save should succeed"];
  139. FIRMessagingFakeKeychain *fakeKeychain = [[FIRMessagingFakeKeychain alloc] init];
  140. self.checkinStore.keychain = fakeKeychain;
  141. __block FIRMessagingCheckinPreferences *preferences =
  142. [[FIRMessagingCheckinPreferences alloc] initWithDeviceID:kAuthID secretToken:kSecret];
  143. [preferences updateWithCheckinPlistContents:[[self class] newCheckinPlistPreferences]];
  144. [self.checkinStore saveCheckinPreferences:preferences
  145. handler:^(NSError *error) {
  146. XCTAssertNil(error);
  147. preferences = [self->_checkinStore cachedCheckinPreferences];
  148. XCTAssertEqualObjects(preferences.deviceID, kAuthID);
  149. XCTAssertEqualObjects(preferences.secretToken, kSecret);
  150. [checkinSaveSuccessExpectation fulfill];
  151. }];
  152. [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
  153. }
  154. #pragma mark - Private Helpers
  155. + (NSDictionary *)checkinPreferences {
  156. return @{
  157. kFIRMessagingDeviceAuthIdKey : kAuthID,
  158. kFIRMessagingSecretTokenKey : kSecret,
  159. kFIRMessagingDigestStringKey : kDigest,
  160. kFIRMessagingGServicesDictionaryKey : @{},
  161. kFIRMessagingLastCheckinTimeKey : @(kLastCheckinTimestamp),
  162. };
  163. }
  164. + (NSDictionary *)newCheckinPlistPreferences {
  165. NSMutableDictionary *oldPreferences = [[self checkinPreferences] mutableCopy];
  166. [oldPreferences removeObjectForKey:kFIRMessagingDeviceAuthIdKey];
  167. [oldPreferences removeObjectForKey:kFIRMessagingSecretTokenKey];
  168. oldPreferences[kFIRMessagingLastCheckinTimeKey] =
  169. @(FIRMessagingCurrentTimestampInMilliseconds() - 1000);
  170. return [oldPreferences copy];
  171. }
  172. @end
  173. #endif