RCNInstanceIDTest.m 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450
  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 <OCMock/OCMock.h>
  17. #import <XCTest/XCTest.h>
  18. @import FirebaseRemoteConfig;
  19. #import "FirebaseRemoteConfig/Sources/Public/FirebaseRemoteConfig/FIRRemoteConfig.h"
  20. #import "FirebaseRemoteConfig/Tests/Unit/RCNTestUtilities.h"
  21. #import <GoogleUtilities/GULNSData+zlib.h>
  22. #import "FirebaseCore/Extension/FirebaseCoreInternal.h"
  23. #import "FirebaseInstallations/Source/Library/Private/FirebaseInstallationsInternal.h"
  24. #import "FirebaseRemoteConfig/FirebaseRemoteConfig-Swift.h"
  25. typedef void (^FIRRemoteConfigFetchCompletion)(FIRRemoteConfigFetchStatus status,
  26. NSError *_Nullable error);
  27. @import FirebaseRemoteConfigInterop;
  28. @interface RCNConfigFetch (ForTest)
  29. - (instancetype)initWithContent:(RCNConfigContent *)content
  30. DBManager:(RCNConfigDBManager *)DBManager
  31. settings:(RCNConfigSettings *)settings
  32. experiment:(RCNConfigExperiment *)experiment
  33. queue:(dispatch_queue_t)queue
  34. namespace:firebaseNamespace
  35. app:firebaseApp;
  36. @end
  37. @interface RCNUserDefaultsManager (Test)
  38. + (NSUserDefaults *)sharedUserDefaultsForBundleIdentifier:(NSString *)bundleIdentifier;
  39. @end
  40. @interface FIRInstallationsAuthTokenResult (Test)
  41. - (instancetype)initWithToken:(NSString *)token expirationDate:(NSDate *)expirationDate;
  42. @end
  43. typedef NS_ENUM(NSInteger, RCNTestRCInstance) {
  44. RCNTestRCInstanceDefault,
  45. RCNTestRCNumTotalInstances, // TODO(mandard): Remove once OCMock issue is resolved (#4877).
  46. RCNTestRCInstanceSecondNamespace,
  47. RCNTestRCInstanceSecondApp,
  48. };
  49. @class FIRInstallationsIDController;
  50. @interface FIRInstallations (Tests)
  51. - (instancetype)initWithAppOptions:(FIROptions *)appOptions
  52. appName:(NSString *)appName
  53. installationsIDController:(FIRInstallationsIDController *)installationsIDController
  54. prefetchAuthToken:(BOOL)prefetchAuthToken;
  55. @end
  56. @interface RCNInstallationsTests : XCTestCase {
  57. NSTimeInterval _expectationTimeout;
  58. NSTimeInterval _checkCompletionTimeout;
  59. NSMutableArray<FIRRemoteConfig *> *_configInstances;
  60. NSMutableArray<NSDictionary<NSString *, NSString *> *> *_entries;
  61. NSMutableArray<NSDictionary<NSString *, id> *> *_response;
  62. NSMutableArray<NSData *> *_responseData;
  63. NSMutableArray<NSURLResponse *> *_URLResponse;
  64. NSMutableArray<RCNConfigFetch *> *_configFetch;
  65. RCNConfigDBManager *_DBManager;
  66. NSUserDefaults *_userDefaults;
  67. NSString *_userDefaultsSuiteName;
  68. NSString *_DBPath;
  69. id _installationsMock;
  70. }
  71. @end
  72. @implementation RCNInstallationsTests
  73. - (void)setUpConfigMock {
  74. FIRSetLoggerLevel(FIRLoggerLevelMax);
  75. _expectationTimeout = 5;
  76. _checkCompletionTimeout = 1.0;
  77. [FIRApp configureWithOptions:[self firstAppOptions]];
  78. // Always remove the database at the start of testing.
  79. _DBPath = [RCNTestUtilities remoteConfigPathForTestDatabase];
  80. _DBManager = [[RCNConfigDBManager alloc] initWithDbPath:_DBPath];
  81. _userDefaultsSuiteName = [RCNTestUtilities userDefaultsSuiteNameForTestSuite];
  82. _userDefaults = [[NSUserDefaults alloc] initWithSuiteName:_userDefaultsSuiteName];
  83. id userDefaultsClassMock = OCMClassMock([RCNUserDefaultsManager class]);
  84. OCMStub([userDefaultsClassMock sharedUserDefaultsForBundleIdentifier:[OCMArg any]])
  85. .andReturn(_userDefaults);
  86. RCNConfigContent *configContent = [[RCNConfigContent alloc] initWithDBManager:_DBManager];
  87. _configInstances = [[NSMutableArray alloc] initWithCapacity:3];
  88. _entries = [[NSMutableArray alloc] initWithCapacity:3];
  89. _response = [[NSMutableArray alloc] initWithCapacity:3];
  90. _responseData = [[NSMutableArray alloc] initWithCapacity:3];
  91. _URLResponse = [[NSMutableArray alloc] initWithCapacity:3];
  92. _configFetch = [[NSMutableArray alloc] initWithCapacity:3];
  93. // Populate the default, second app, second namespace instances.
  94. for (int i = 0; i < RCNTestRCNumTotalInstances; i++) {
  95. // Fake a response for default instance.
  96. NSMutableDictionary<NSString *, NSString *> *valuesDict = [[NSMutableDictionary alloc] init];
  97. for (int count = 1; count <= 100; count++) {
  98. NSString *key = [NSString stringWithFormat:@"key%d-%d", count, i];
  99. NSString *value = [NSString stringWithFormat:@"value%d-%d", count, i];
  100. valuesDict[key] = value;
  101. }
  102. _entries[i] = valuesDict;
  103. NSString *currentAppName = nil;
  104. FIROptions *currentOptions = nil;
  105. NSString *currentNamespace = nil;
  106. switch (i) {
  107. case RCNTestRCInstanceSecondNamespace:
  108. currentAppName = RCNTestsDefaultFIRAppName;
  109. currentOptions = [self firstAppOptions];
  110. currentNamespace = RCNTestsPerfNamespace;
  111. break;
  112. case RCNTestRCInstanceSecondApp:
  113. currentAppName = RCNTestsSecondFIRAppName;
  114. currentOptions = [self secondAppOptions];
  115. currentNamespace = FIRRemoteConfigConstants.FIRNamespaceGoogleMobilePlatform;
  116. ;
  117. break;
  118. case RCNTestRCInstanceDefault:
  119. default:
  120. currentAppName = RCNTestsDefaultFIRAppName;
  121. currentOptions = [self firstAppOptions];
  122. currentNamespace = RCNTestsFIRNamespace;
  123. break;
  124. }
  125. NSString *fullyQualifiedNamespace =
  126. [NSString stringWithFormat:@"%@:%@", currentNamespace, currentAppName];
  127. FIRRemoteConfig *config =
  128. OCMPartialMock([[FIRRemoteConfig alloc] initWithAppName:currentAppName
  129. FIROptions:currentOptions
  130. namespace:currentNamespace
  131. DBManager:_DBManager
  132. configContent:configContent
  133. analytics:nil]);
  134. _configInstances[i] = config;
  135. RCNConfigSettings *settings =
  136. [[RCNConfigSettings alloc] initWithDatabaseManager:_DBManager
  137. namespace:fullyQualifiedNamespace
  138. firebaseAppName:currentAppName
  139. googleAppID:currentOptions.googleAppID];
  140. dispatch_queue_t queue = dispatch_queue_create(
  141. [[NSString stringWithFormat:@"testqueue: %d", i] cStringUsingEncoding:NSUTF8StringEncoding],
  142. DISPATCH_QUEUE_SERIAL);
  143. _configFetch[i] = [[RCNConfigFetch alloc] initWithContent:configContent
  144. DBManager:_DBManager
  145. settings:settings
  146. analytics:nil
  147. experiment:nil
  148. queue:queue
  149. namespace:fullyQualifiedNamespace
  150. options:currentOptions];
  151. }
  152. }
  153. // Mock instance ID methods.
  154. - (void)mockInstanceIDMethodForTokenAndIdentity:(nullable NSString *)token
  155. tokenError:(nullable NSError *)tokenError
  156. identity:(nullable NSString *)identity
  157. identityError:(nullable NSError *)identityError {
  158. // Mock the installations retrieval method.
  159. _installationsMock = OCMClassMock([FIRInstallations class]);
  160. id installationIDCompletionArg =
  161. [OCMArg checkWithBlock:^BOOL(FIRInstallationsIDHandler completion) {
  162. if (completion) {
  163. completion(identity, identityError);
  164. }
  165. return YES;
  166. }];
  167. OCMStub([_installationsMock installationIDWithCompletion:installationIDCompletionArg]);
  168. FIRInstallationsAuthTokenResult *tokenResult;
  169. if (token) {
  170. tokenResult = [[FIRInstallationsAuthTokenResult alloc] initWithToken:token
  171. expirationDate:[NSDate distantFuture]];
  172. }
  173. id authTokenCompletionArg =
  174. [OCMArg checkWithBlock:^BOOL(FIRInstallationsTokenHandler completion) {
  175. if (completion) {
  176. completion(tokenResult, tokenError);
  177. }
  178. return YES;
  179. }];
  180. OCMStub([_installationsMock authTokenWithCompletion:authTokenCompletionArg]);
  181. OCMStub([_installationsMock installationsWithApp:[OCMArg any]]).andReturn(_installationsMock);
  182. [self setUpConfigMock];
  183. }
  184. - (void)tearDown {
  185. [_DBManager removeDatabaseWithPath:_DBPath];
  186. [[NSUserDefaults standardUserDefaults] removePersistentDomainForName:_userDefaultsSuiteName];
  187. [FIRApp resetApps];
  188. [_installationsMock stopMocking];
  189. _installationsMock = nil;
  190. [super tearDown];
  191. }
  192. // Instance ID token is nil. Error is not nil. Verify fetch fails.
  193. - (void)testNilInstallationsAuthTokenAndError {
  194. NSMutableArray<XCTestExpectation *> *expectations =
  195. [[NSMutableArray alloc] initWithCapacity:RCNTestRCNumTotalInstances];
  196. // Set the token as nil.
  197. [self
  198. mockInstanceIDMethodForTokenAndIdentity:nil
  199. tokenError:[NSError
  200. errorWithDomain:kFirebaseInstallationsErrorDomain
  201. code:FIRInstallationsErrorCodeUnknown
  202. userInfo:nil]
  203. identity:nil
  204. identityError:nil];
  205. // Test for each RC FIRApp, namespace instance.
  206. for (int i = 0; i < RCNTestRCNumTotalInstances; i++) {
  207. expectations[i] =
  208. [self expectationWithDescription:
  209. [NSString stringWithFormat:@"Test fetch configs successfully - instance %d", i]];
  210. XCTAssertEqual(_configInstances[i].lastFetchStatus, FIRRemoteConfigFetchStatusNoFetchYet);
  211. FIRRemoteConfigFetchCompletion fetchCompletion =
  212. ^void(FIRRemoteConfigFetchStatus status, NSError *error) {
  213. XCTAssertNotNil(error);
  214. [expectations[i] fulfill];
  215. };
  216. [_configInstances[i] fetchWithExpirationDuration:43200 completionHandler:fetchCompletion];
  217. }
  218. [self waitForExpectationsWithTimeout:_expectationTimeout
  219. handler:^(NSError *error) {
  220. XCTAssertNil(error);
  221. }];
  222. }
  223. // Test IID error. Subsequent request also fails with same error (b/148975341).
  224. - (void)testMultipleFetchCallsFailing {
  225. NSMutableArray<XCTestExpectation *> *expectations =
  226. [[NSMutableArray alloc] initWithCapacity:RCNTestRCNumTotalInstances];
  227. // Set the token as nil.
  228. NSError *tokenError = [NSError errorWithDomain:kFirebaseInstallationsErrorDomain
  229. code:FIRInstallationsErrorCodeUnknown
  230. userInfo:nil];
  231. [self mockInstanceIDMethodForTokenAndIdentity:nil
  232. tokenError:tokenError
  233. identity:nil
  234. identityError:nil];
  235. // Test for each RC FIRApp, namespace instance.
  236. for (int i = 0; i < RCNTestRCNumTotalInstances; i++) {
  237. expectations[i] =
  238. [self expectationWithDescription:
  239. [NSString stringWithFormat:@"Test fetch configs successfully - instance %d", i]];
  240. XCTAssertEqual(_configInstances[i].lastFetchStatus, FIRRemoteConfigFetchStatusNoFetchYet);
  241. FIRRemoteConfigFetchCompletion fetchCompletion =
  242. ^void(FIRRemoteConfigFetchStatus status, NSError *error) {
  243. XCTAssertNotNil(error);
  244. XCTAssert([[error.userInfo objectForKey:@"NSLocalizedDescription"]
  245. containsString:@"Failed to get installations token"]);
  246. // Make a second fetch call.
  247. [self->_configInstances[i]
  248. fetchWithExpirationDuration:43200
  249. completionHandler:^void(FIRRemoteConfigFetchStatus status, NSError *error) {
  250. XCTAssertNotNil(error);
  251. XCTAssert([[error.userInfo objectForKey:@"NSLocalizedDescription"]
  252. containsString:@"Failed to get installations token"]);
  253. [expectations[i] fulfill];
  254. }];
  255. };
  256. [_configInstances[i] fetchWithExpirationDuration:43200 completionHandler:fetchCompletion];
  257. }
  258. [self waitForExpectationsWithTimeout:_expectationTimeout
  259. handler:^(NSError *error) {
  260. XCTAssertNil(error);
  261. }];
  262. }
  263. // Instance ID token is not nil. Error is not nil. Verify fetch fails.
  264. - (void)testValidInstanceIDTokenAndValidError {
  265. NSMutableArray<XCTestExpectation *> *expectations =
  266. [[NSMutableArray alloc] initWithCapacity:RCNTestRCNumTotalInstances];
  267. // Test for each RC FIRApp, namespace instance.
  268. for (int i = 0; i < RCNTestRCNumTotalInstances; i++) {
  269. // Set the token as nil.
  270. NSError *tokenError = [NSError errorWithDomain:kFirebaseInstallationsErrorDomain
  271. code:FIRInstallationsErrorCodeUnknown
  272. userInfo:nil];
  273. [self mockInstanceIDMethodForTokenAndIdentity:@"abcd"
  274. tokenError:tokenError
  275. identity:nil
  276. identityError:nil];
  277. expectations[i] =
  278. [self expectationWithDescription:
  279. [NSString stringWithFormat:@"Test fetch configs successfully - instance %d", i]];
  280. XCTAssertEqual(_configInstances[i].lastFetchStatus, FIRRemoteConfigFetchStatusNoFetchYet);
  281. FIRRemoteConfigFetchCompletion fetchCompletion =
  282. ^void(FIRRemoteConfigFetchStatus status, NSError *error) {
  283. XCTAssertNotNil(error);
  284. [expectations[i] fulfill];
  285. };
  286. [_configInstances[i] fetchWithExpirationDuration:43200 completionHandler:fetchCompletion];
  287. }
  288. [self waitForExpectationsWithTimeout:_expectationTimeout
  289. handler:^(NSError *error) {
  290. XCTAssertNil(error);
  291. }];
  292. }
  293. // Instance ID token is nil. Error is nil. Verify fetch fails.
  294. - (void)testNilInstanceIDTokenAndNilError {
  295. NSMutableArray<XCTestExpectation *> *expectations =
  296. [[NSMutableArray alloc] initWithCapacity:RCNTestRCNumTotalInstances];
  297. // Test for each RC FIRApp, namespace instance.
  298. for (int i = 0; i < RCNTestRCNumTotalInstances; i++) {
  299. // Set the token as nil.
  300. [self mockInstanceIDMethodForTokenAndIdentity:nil
  301. tokenError:nil
  302. identity:nil
  303. identityError:nil];
  304. expectations[i] =
  305. [self expectationWithDescription:
  306. [NSString stringWithFormat:@"Test fetch configs successfully - instance %d", i]];
  307. XCTAssertEqual(_configInstances[i].lastFetchStatus, FIRRemoteConfigFetchStatusNoFetchYet);
  308. FIRRemoteConfigFetchCompletion fetchCompletion =
  309. ^void(FIRRemoteConfigFetchStatus status, NSError *error) {
  310. XCTAssertNotNil(error);
  311. [expectations[i] fulfill];
  312. };
  313. [_configInstances[i] fetchWithExpirationDuration:43200 completionHandler:fetchCompletion];
  314. }
  315. [self waitForExpectationsWithTimeout:_expectationTimeout
  316. handler:^(NSError *error) {
  317. XCTAssertNil(error);
  318. }];
  319. }
  320. // Instance ID token is valid. InstanceID is nil with no error. Verify fetch fails.
  321. - (void)testNilInstanceIDWithValidInstanceIDToken {
  322. NSMutableArray<XCTestExpectation *> *expectations =
  323. [[NSMutableArray alloc] initWithCapacity:RCNTestRCNumTotalInstances];
  324. // Test for each RC FIRApp, namespace instance.
  325. for (int i = 0; i < RCNTestRCNumTotalInstances; i++) {
  326. // Set the token as nil.
  327. [self mockInstanceIDMethodForTokenAndIdentity:@"abcd"
  328. tokenError:nil
  329. identity:nil
  330. identityError:nil];
  331. expectations[i] =
  332. [self expectationWithDescription:
  333. [NSString stringWithFormat:@"Test fetch configs successfully - instance %d", i]];
  334. XCTAssertEqual(_configInstances[i].lastFetchStatus, FIRRemoteConfigFetchStatusNoFetchYet);
  335. FIRRemoteConfigFetchCompletion fetchCompletion =
  336. ^void(FIRRemoteConfigFetchStatus status, NSError *error) {
  337. XCTAssertNotNil(error);
  338. [expectations[i] fulfill];
  339. };
  340. [_configInstances[i] fetchWithExpirationDuration:43200 completionHandler:fetchCompletion];
  341. }
  342. [self waitForExpectationsWithTimeout:_expectationTimeout
  343. handler:^(NSError *error) {
  344. XCTAssertNil(error);
  345. }];
  346. }
  347. // Instance ID is not nil, but IID SDK returns an error. Also token is valid.
  348. - (void)testValidInstanceIDAndError {
  349. NSMutableArray<XCTestExpectation *> *expectations =
  350. [[NSMutableArray alloc] initWithCapacity:RCNTestRCNumTotalInstances];
  351. // Test for each RC FIRApp, namespace instance.
  352. for (int i = 0; i < RCNTestRCNumTotalInstances; i++) {
  353. // Set the token as nil.
  354. NSError *identityError = [NSError errorWithDomain:kFirebaseInstallationsErrorDomain
  355. code:FIRInstallationsErrorCodeUnknown
  356. userInfo:nil];
  357. [self mockInstanceIDMethodForTokenAndIdentity:@"abcd"
  358. tokenError:nil
  359. identity:@"test-id"
  360. identityError:identityError];
  361. expectations[i] =
  362. [self expectationWithDescription:
  363. [NSString stringWithFormat:@"Test fetch configs successfully - instance %d", i]];
  364. XCTAssertEqual(_configInstances[i].lastFetchStatus, FIRRemoteConfigFetchStatusNoFetchYet);
  365. FIRRemoteConfigFetchCompletion fetchCompletion =
  366. ^void(FIRRemoteConfigFetchStatus status, NSError *error) {
  367. XCTAssertNotNil(error);
  368. [expectations[i] fulfill];
  369. };
  370. [_configInstances[i] fetchWithExpirationDuration:43200 completionHandler:fetchCompletion];
  371. }
  372. [self waitForExpectationsWithTimeout:_expectationTimeout
  373. handler:^(NSError *error) {
  374. XCTAssertNil(error);
  375. }];
  376. }
  377. #pragma mark - Test Helpers
  378. - (FIROptions *)firstAppOptions {
  379. // TODO: Evaluate if we want to hardcode things here instead.
  380. FIROptions *options = [[FIROptions alloc] initWithGoogleAppID:@"1:123:ios:123abc"
  381. GCMSenderID:@"correct_gcm_sender_id"];
  382. options.APIKey = @"AIzaSy-ApiKeyWithValidFormat_0123456789";
  383. options.projectID = @"abc-xyz-123";
  384. return options;
  385. }
  386. - (FIROptions *)secondAppOptions {
  387. FIROptions *options =
  388. [[FIROptions alloc] initWithContentsOfFile:[[NSBundle bundleForClass:[self class]]
  389. pathForResource:@"SecondApp-GoogleService-Info"
  390. ofType:@"plist"]];
  391. XCTAssertNotNil(options);
  392. return options;
  393. }
  394. @end