FIRInstallationsStoreTests.m 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247
  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 <GoogleUtilities/GULKeychainStorage.h>
  19. #import <GoogleUtilities/GULUserDefaults.h>
  20. #import "FBLPromise+Testing.h"
  21. #import "FirebaseInstallations/Source/Library/Errors/FIRInstallationsErrorUtil.h"
  22. #import "FirebaseInstallations/Source/Library/FIRInstallationsItem.h"
  23. #import "FirebaseInstallations/Source/Library/InstallationsStore/FIRInstallationsStore.h"
  24. #import "FirebaseInstallations/Source/Library/InstallationsStore/FIRInstallationsStoredItem.h"
  25. #import "FirebaseInstallations/Source/Tests/Utils/FIRInstallationsItem+Tests.h"
  26. @interface FIRInstallationsStoreTests : XCTestCase
  27. @property(nonatomic) NSString *accessGroup;
  28. @property(nonatomic) FIRInstallationsStore *store;
  29. @property(nonatomic) id mockSecureStorage;
  30. @property(nonatomic) GULUserDefaults *userDefaults;
  31. @end
  32. @implementation FIRInstallationsStoreTests
  33. - (void)setUp {
  34. self.accessGroup = @"accessGroup";
  35. self.mockSecureStorage = OCMClassMock([GULKeychainStorage class]);
  36. self.store = [[FIRInstallationsStore alloc] initWithSecureStorage:self.mockSecureStorage
  37. accessGroup:self.accessGroup];
  38. // TODO: Replace real user defaults by an injected mock or a test specific user defaults instance
  39. // with a specific suite name.
  40. self.userDefaults = [[GULUserDefaults alloc] initWithSuiteName:self.accessGroup];
  41. }
  42. - (void)tearDown {
  43. self.userDefaults = nil;
  44. self.store = nil;
  45. self.mockSecureStorage = nil;
  46. [self.mockSecureStorage stopMocking];
  47. }
  48. - (void)testInstallationID_WhenNoUserDefaultsItem_ThenNotFound {
  49. NSString *appID = @"123";
  50. NSString *appName = @"name";
  51. NSString *itemID = [self itemIDWithAppID:appID appName:appName];
  52. [self.userDefaults removeObjectForKey:itemID];
  53. // Check with empty keychain.
  54. OCMReject([self.mockSecureStorage getObjectForKey:[OCMArg any]
  55. objectClass:[OCMArg any]
  56. accessGroup:[OCMArg any]]);
  57. [self assertInstallationIDNotFoundForAppID:appID appName:appName];
  58. OCMVerifyAll(self.mockSecureStorage);
  59. }
  60. - (void)testInstallationID_WhenThereIsUserDefaultsAndKeychain_ThenReturnsItem {
  61. NSString *appID = @"123";
  62. NSString *appName = @"name";
  63. NSString *itemID = [self itemIDWithAppID:appID appName:appName];
  64. [self.userDefaults setObject:@(YES) forKey:itemID];
  65. FIRInstallationsStoredItem *storedItem = [self createValidStoredItem];
  66. OCMExpect([self.mockSecureStorage getObjectForKey:itemID
  67. objectClass:[FIRInstallationsStoredItem class]
  68. accessGroup:self.accessGroup])
  69. .andReturn([FBLPromise resolvedWith:storedItem]);
  70. FBLPromise<FIRInstallationsItem *> *itemPromise = [self.store installationForAppID:appID
  71. appName:appName];
  72. XCTAssert(FBLWaitForPromisesWithTimeout(0.5));
  73. XCTAssertTrue(itemPromise.isFulfilled);
  74. XCTAssertNil(itemPromise.error);
  75. XCTAssertNotNil(itemPromise.value);
  76. FIRInstallationsItem *item = itemPromise.value;
  77. XCTAssertEqualObjects(item.appID, appID);
  78. XCTAssertEqualObjects(item.firebaseAppName, appName);
  79. [self assertStoredItem:storedItem correspondsToItem:item];
  80. OCMVerifyAll(self.mockSecureStorage);
  81. }
  82. - (void)testInstallationID_WhenThereIsUserDefaultsAndNoKeychain_ThenNotFound {
  83. NSString *appID = @"123";
  84. NSString *appName = @"name";
  85. NSString *itemID = [self itemIDWithAppID:appID appName:appName];
  86. [self.userDefaults setObject:@(YES) forKey:itemID];
  87. OCMExpect([self.mockSecureStorage getObjectForKey:itemID
  88. objectClass:[FIRInstallationsStoredItem class]
  89. accessGroup:self.accessGroup])
  90. .andReturn([FBLPromise resolvedWith:nil]);
  91. FBLPromise<FIRInstallationsItem *> *itemPromise = [self.store installationForAppID:appID
  92. appName:appName];
  93. XCTAssert(FBLWaitForPromisesWithTimeout(0.5));
  94. XCTAssertNotNil(itemPromise.error);
  95. XCTAssertEqualObjects(itemPromise.error,
  96. [FIRInstallationsErrorUtil installationItemNotFoundForAppID:appID
  97. appName:appName]);
  98. XCTAssertNil(itemPromise.value);
  99. OCMVerifyAll(self.mockSecureStorage);
  100. }
  101. - (void)testSaveInstallationWhenKeychainSucceds {
  102. FIRInstallationsItem *item = [FIRInstallationsItem createUnregisteredInstallationItem];
  103. NSString *itemID = [item identifier];
  104. // Reset user defaults key.
  105. [self.userDefaults removeObjectForKey:itemID];
  106. id storedItemArg = [OCMArg checkWithBlock:^BOOL(FIRInstallationsStoredItem *obj) {
  107. XCTAssertEqualObjects([obj class], [FIRInstallationsStoredItem class]);
  108. [self assertStoredItem:obj correspondsToItem:item];
  109. return YES;
  110. }];
  111. OCMExpect([self.mockSecureStorage setObject:storedItemArg
  112. forKey:itemID
  113. accessGroup:self.accessGroup])
  114. .andReturn([FBLPromise resolvedWith:[NSNull null]]);
  115. FBLPromise<NSNull *> *promise = [self.store saveInstallation:item];
  116. XCTAssert(FBLWaitForPromisesWithTimeout(0.5));
  117. XCTAssertNil(promise.error);
  118. XCTAssertTrue(promise.isFulfilled);
  119. OCMVerifyAll(self.mockSecureStorage);
  120. // Check the user defaults key updated.
  121. XCTAssertNotNil([self.userDefaults objectForKey:itemID]);
  122. }
  123. - (void)testSaveInstallationWhenKeychainFails {
  124. FIRInstallationsItem *item = [FIRInstallationsItem createUnregisteredInstallationItem];
  125. NSString *itemID = [item identifier];
  126. // Reset user defaults key.
  127. [self.userDefaults removeObjectForKey:itemID];
  128. NSError *keychainError = [FIRInstallationsErrorUtil keychainErrorWithFunction:@"Get" status:-1];
  129. FBLPromise *rejectedPromise = [FBLPromise pendingPromise];
  130. [rejectedPromise reject:keychainError];
  131. id storedItemArg = [OCMArg checkWithBlock:^BOOL(FIRInstallationsStoredItem *obj) {
  132. XCTAssertEqualObjects([obj class], [FIRInstallationsStoredItem class]);
  133. [self assertStoredItem:obj correspondsToItem:item];
  134. return YES;
  135. }];
  136. OCMExpect([self.mockSecureStorage setObject:storedItemArg
  137. forKey:itemID
  138. accessGroup:self.accessGroup])
  139. .andReturn(rejectedPromise);
  140. FBLPromise<NSNull *> *promise = [self.store saveInstallation:item];
  141. XCTAssert(FBLWaitForPromisesWithTimeout(0.5));
  142. XCTAssertTrue(promise.isRejected);
  143. XCTAssertEqualObjects(promise.error, keychainError);
  144. OCMVerifyAll(self.mockSecureStorage);
  145. // Check the user defaults key wasn't updated.
  146. XCTAssertNil([self.userDefaults objectForKey:itemID]);
  147. }
  148. - (void)testRemoveInstallation {
  149. NSString *appID = @"123";
  150. NSString *appName = @"name";
  151. NSString *itemID = [self itemIDWithAppID:appID appName:appName];
  152. [self.userDefaults setObject:@(YES) forKey:itemID];
  153. OCMExpect([self.mockSecureStorage removeObjectForKey:itemID accessGroup:self.accessGroup])
  154. .andReturn([FBLPromise resolvedWith:[NSNull null]]);
  155. FBLPromise<NSNull *> *promise = [self.store removeInstallationForAppID:appID appName:appName];
  156. XCTAssert(FBLWaitForPromisesWithTimeout(0.5));
  157. XCTAssertTrue(promise.isFulfilled);
  158. XCTAssertNil(promise.error);
  159. OCMVerifyAll(self.mockSecureStorage);
  160. XCTAssertNil([self.userDefaults objectForKey:itemID]);
  161. }
  162. #pragma mark - Common
  163. - (void)assertInstallationIDNotFoundForAppID:(NSString *)appID appName:(NSString *)appName {
  164. FBLPromise<FIRInstallationsItem *> *itemPromise = [self.store installationForAppID:appID
  165. appName:appName];
  166. XCTAssert(FBLWaitForPromisesWithTimeout(0.5));
  167. XCTAssertTrue(itemPromise.isRejected, @"%@", self.name);
  168. XCTAssertEqualObjects(itemPromise.error,
  169. [FIRInstallationsErrorUtil installationItemNotFoundForAppID:appID
  170. appName:appName],
  171. @"%@", self.name);
  172. }
  173. #pragma mark - Helpers
  174. - (NSString *)itemIDWithAppID:(NSString *)appID appName:(NSString *)appName {
  175. return [FIRInstallationsItem identifierWithAppID:appID appName:appName];
  176. }
  177. - (FIRInstallationsStoredItem *)createValidStoredItem {
  178. FIRInstallationsStoredItem *storedItem = [[FIRInstallationsStoredItem alloc] init];
  179. storedItem.firebaseInstallationID = @"firebaseInstallationID";
  180. storedItem.refreshToken = @"refreshToken";
  181. return storedItem;
  182. }
  183. - (void)assertStoredItem:(FIRInstallationsStoredItem *)storedItem
  184. correspondsToItem:(FIRInstallationsItem *)item {
  185. XCTAssertEqualObjects(item.refreshToken, storedItem.refreshToken);
  186. XCTAssertEqualObjects(item.firebaseInstallationID, storedItem.firebaseInstallationID);
  187. XCTAssertEqual(item.registrationStatus, storedItem.registrationStatus);
  188. }
  189. @end