RCNInstanceIDTest.m 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424
  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 <FirebaseRemoteConfig/FIRRemoteConfig.h>
  18. #import "FirebaseRemoteConfig/Sources/Private/FIRRemoteConfig_Private.h"
  19. #import "FirebaseRemoteConfig/Sources/RCNConfigConstants.h"
  20. #import "FirebaseRemoteConfig/Sources/RCNConfigDBManager.h"
  21. #import "FirebaseRemoteConfig/Sources/RCNConfigFetch.h"
  22. #import "FirebaseRemoteConfig/Sources/RCNUserDefaultsManager.h"
  23. #import "FirebaseRemoteConfig/Tests/Unit/RCNTestUtilities.h"
  24. #import <FirebaseCore/FIRAppInternal.h>
  25. #import <FirebaseCore/FIRLogger.h>
  26. #import <FirebaseCore/FIROptions.h>
  27. #import <FirebaseInstanceID/FirebaseInstanceID.h>
  28. #import <GoogleUtilities/GULNSData+zlib.h>
  29. #import <OCMock/OCMock.h>
  30. @interface RCNConfigFetch (ForTest)
  31. - (instancetype)initWithContent:(RCNConfigContent *)content
  32. DBManager:(RCNConfigDBManager *)DBManager
  33. settings:(RCNConfigSettings *)settings
  34. experiment:(RCNConfigExperiment *)experiment
  35. queue:(dispatch_queue_t)queue
  36. namespace:firebaseNamespace
  37. app:firebaseApp;
  38. @end
  39. @interface RCNConfigDBManager (Test)
  40. - (void)removeDatabaseOnDatabaseQueueAtPath:(NSString *)path;
  41. @end
  42. @interface RCNUserDefaultsManager (Test)
  43. + (NSUserDefaults *)sharedUserDefaultsForBundleIdentifier:(NSString *)bundleIdentifier;
  44. @end
  45. typedef NS_ENUM(NSInteger, RCNTestRCInstance) {
  46. RCNTestRCInstanceDefault,
  47. RCNTestRCNumTotalInstances, // TODO(mandard): Remove once OCMock issue is resolved (#4877).
  48. RCNTestRCInstanceSecondNamespace,
  49. RCNTestRCInstanceSecondApp,
  50. };
  51. @interface FIRInstanceID (Tests)
  52. + (FIRInstanceID *)instanceIDForTests;
  53. @end
  54. @interface RCNInstanceIDTest : XCTestCase {
  55. NSTimeInterval _expectationTimeout;
  56. NSTimeInterval _checkCompletionTimeout;
  57. NSMutableArray<FIRRemoteConfig *> *_configInstances;
  58. NSMutableArray<NSDictionary<NSString *, NSString *> *> *_entries;
  59. NSMutableArray<NSDictionary<NSString *, id> *> *_response;
  60. NSMutableArray<NSData *> *_responseData;
  61. NSMutableArray<NSURLResponse *> *_URLResponse;
  62. NSMutableArray<RCNConfigFetch *> *_configFetch;
  63. RCNConfigDBManager *_DBManager;
  64. NSUserDefaults *_userDefaults;
  65. NSString *_userDefaultsSuiteName;
  66. NSString *_DBPath;
  67. }
  68. @end
  69. @implementation RCNInstanceIDTest
  70. - (void)setUp {
  71. [super setUp];
  72. FIRSetLoggerLevel(FIRLoggerLevelMax);
  73. //@@if (![FIRApp isDefaultAppConfigured]) {
  74. [FIRApp configureWithOptions:[self firstAppOptions]];
  75. _expectationTimeout = 5;
  76. _checkCompletionTimeout = 1.0;
  77. // Always remove the database at the start of testing.
  78. _DBPath = [RCNTestUtilities remoteConfigPathForTestDatabase];
  79. id classMock = OCMClassMock([RCNConfigDBManager class]);
  80. OCMStub([classMock remoteConfigPathForDatabase]).andReturn(_DBPath);
  81. _DBManager = [[RCNConfigDBManager alloc] init];
  82. _userDefaultsSuiteName = [RCNTestUtilities userDefaultsSuiteNameForTestSuite];
  83. _userDefaults = [[NSUserDefaults alloc] initWithSuiteName:_userDefaultsSuiteName];
  84. id userDefaultsClassMock = OCMClassMock([RCNUserDefaultsManager class]);
  85. OCMStub([userDefaultsClassMock sharedUserDefaultsForBundleIdentifier:[OCMArg any]])
  86. .andReturn(_userDefaults);
  87. RCNConfigContent *configContent = [[RCNConfigContent alloc] initWithDBManager:_DBManager];
  88. _configInstances = [[NSMutableArray alloc] initWithCapacity:3];
  89. _entries = [[NSMutableArray alloc] initWithCapacity:3];
  90. _response = [[NSMutableArray alloc] initWithCapacity:3];
  91. _responseData = [[NSMutableArray alloc] initWithCapacity:3];
  92. _URLResponse = [[NSMutableArray alloc] initWithCapacity:3];
  93. _configFetch = [[NSMutableArray alloc] initWithCapacity:3];
  94. // Populate the default, second app, second namespace instances.
  95. for (int i = 0; i < RCNTestRCNumTotalInstances; i++) {
  96. // Fake a response for default instance.
  97. NSMutableDictionary<NSString *, NSString *> *valuesDict = [[NSMutableDictionary alloc] init];
  98. for (int count = 1; count <= 100; count++) {
  99. NSString *key = [NSString stringWithFormat:@"key%d-%d", count, i];
  100. NSString *value = [NSString stringWithFormat:@"value%d-%d", count, i];
  101. valuesDict[key] = value;
  102. }
  103. _entries[i] = valuesDict;
  104. NSString *currentAppName = nil;
  105. FIROptions *currentOptions = nil;
  106. NSString *currentNamespace = nil;
  107. switch (i) {
  108. case RCNTestRCInstanceSecondNamespace:
  109. currentAppName = RCNTestsDefaultFIRAppName;
  110. currentOptions = [self firstAppOptions];
  111. currentNamespace = RCNTestsPerfNamespace;
  112. break;
  113. case RCNTestRCInstanceSecondApp:
  114. currentAppName = RCNTestsSecondFIRAppName;
  115. currentOptions = [self secondAppOptions];
  116. currentNamespace = FIRNamespaceGoogleMobilePlatform;
  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] = OCMPartialMock([[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 instanceID retreival method.
  159. id instanceIDMock = OCMPartialMock([FIRInstanceID instanceIDForTests]);
  160. OCMStub([instanceIDMock
  161. tokenWithAuthorizedEntity:[OCMArg any]
  162. scope:[OCMArg any]
  163. options:[OCMArg any]
  164. handler:([OCMArg
  165. invokeBlockWithArgs:(token ? token : [NSNull null]),
  166. (tokenError ? tokenError : [NSNull null]),
  167. nil])]);
  168. OCMStub([instanceIDMock
  169. getIDWithHandler:([OCMArg invokeBlockWithArgs:(identity ? identity : [NSNull null]),
  170. (identityError ? identityError : [NSNull null]),
  171. nil])]);
  172. }
  173. - (void)tearDown {
  174. [_DBManager removeDatabaseOnDatabaseQueueAtPath:_DBPath];
  175. [[NSUserDefaults standardUserDefaults] removePersistentDomainForName:_userDefaultsSuiteName];
  176. [FIRApp resetApps];
  177. [super tearDown];
  178. }
  179. // Instance ID token is nil. Error is not nil. Verify fetch fails.
  180. - (void)testNilInstanceIDTokenAndError {
  181. NSMutableArray<XCTestExpectation *> *expectations =
  182. [[NSMutableArray alloc] initWithCapacity:RCNTestRCNumTotalInstances];
  183. // Set the token as nil.
  184. [self mockInstanceIDMethodForTokenAndIdentity:nil
  185. tokenError:[NSError errorWithDomain:@"com.google.instanceid"
  186. code:FIRInstanceIDErrorUnknown
  187. userInfo:nil]
  188. identity:nil
  189. identityError:nil];
  190. // Test for each RC FIRApp, namespace instance.
  191. for (int i = 0; i < RCNTestRCNumTotalInstances; i++) {
  192. expectations[i] =
  193. [self expectationWithDescription:
  194. [NSString stringWithFormat:@"Test fetch configs successfully - instance %d", i]];
  195. XCTAssertEqual(_configInstances[i].lastFetchStatus, FIRRemoteConfigFetchStatusNoFetchYet);
  196. FIRRemoteConfigFetchCompletion fetchCompletion =
  197. ^void(FIRRemoteConfigFetchStatus status, NSError *error) {
  198. XCTAssertNotNil(error);
  199. [expectations[i] fulfill];
  200. };
  201. [_configInstances[i] fetchWithExpirationDuration:43200 completionHandler:fetchCompletion];
  202. }
  203. [self waitForExpectationsWithTimeout:_expectationTimeout
  204. handler:^(NSError *error) {
  205. XCTAssertNil(error);
  206. }];
  207. }
  208. // Test IID error. Subsequent request also fails with same error (b/148975341).
  209. - (void)testMultipleFetchCallsFailing {
  210. NSMutableArray<XCTestExpectation *> *expectations =
  211. [[NSMutableArray alloc] initWithCapacity:RCNTestRCNumTotalInstances];
  212. // Set the token as nil.
  213. [self mockInstanceIDMethodForTokenAndIdentity:nil
  214. tokenError:[NSError errorWithDomain:@"com.google.instanceid"
  215. code:FIRInstanceIDErrorUnknown
  216. userInfo:nil]
  217. identity:nil
  218. identityError:nil];
  219. // Test for each RC FIRApp, namespace instance.
  220. for (int i = 0; i < RCNTestRCNumTotalInstances; i++) {
  221. expectations[i] =
  222. [self expectationWithDescription:
  223. [NSString stringWithFormat:@"Test fetch configs successfully - instance %d", i]];
  224. XCTAssertEqual(_configInstances[i].lastFetchStatus, FIRRemoteConfigFetchStatusNoFetchYet);
  225. FIRRemoteConfigFetchCompletion fetchCompletion =
  226. ^void(FIRRemoteConfigFetchStatus status, NSError *error) {
  227. XCTAssertNotNil(error);
  228. XCTAssertFalse([[error.userInfo objectForKey:@"NSLocalizedDescription"]
  229. rangeOfString:@"Failed to get InstanceID token"]
  230. .location == NSNotFound);
  231. // Make a second fetch call.
  232. [_configInstances[i]
  233. fetchWithExpirationDuration:43200
  234. completionHandler:^void(FIRRemoteConfigFetchStatus status, NSError *error) {
  235. XCTAssertNotNil(error);
  236. XCTAssertFalse([[error.userInfo objectForKey:@"NSLocalizedDescription"]
  237. rangeOfString:@"Failed to get InstanceID token"]
  238. .location == NSNotFound);
  239. [expectations[i] fulfill];
  240. }];
  241. };
  242. [_configInstances[i] fetchWithExpirationDuration:43200 completionHandler:fetchCompletion];
  243. }
  244. [self waitForExpectationsWithTimeout:_expectationTimeout
  245. handler:^(NSError *error) {
  246. XCTAssertNil(error);
  247. }];
  248. }
  249. // Instance ID token is not nil. Error is not nil. Verify fetch fails.
  250. - (void)testValidInstanceIDTokenAndValidError {
  251. NSMutableArray<XCTestExpectation *> *expectations =
  252. [[NSMutableArray alloc] initWithCapacity:RCNTestRCNumTotalInstances];
  253. // Test for each RC FIRApp, namespace instance.
  254. for (int i = 0; i < RCNTestRCNumTotalInstances; i++) {
  255. // Set the token as nil.
  256. [self mockInstanceIDMethodForTokenAndIdentity:@"abcd"
  257. tokenError:[NSError errorWithDomain:@"com.google.instanceid"
  258. code:FIRInstanceIDErrorUnknown
  259. userInfo:nil]
  260. identity:nil
  261. identityError:nil];
  262. expectations[i] =
  263. [self expectationWithDescription:
  264. [NSString stringWithFormat:@"Test fetch configs successfully - instance %d", i]];
  265. XCTAssertEqual(_configInstances[i].lastFetchStatus, FIRRemoteConfigFetchStatusNoFetchYet);
  266. FIRRemoteConfigFetchCompletion fetchCompletion =
  267. ^void(FIRRemoteConfigFetchStatus status, NSError *error) {
  268. XCTAssertNotNil(error);
  269. [expectations[i] fulfill];
  270. };
  271. [_configInstances[i] fetchWithExpirationDuration:43200 completionHandler:fetchCompletion];
  272. }
  273. [self waitForExpectationsWithTimeout:_expectationTimeout
  274. handler:^(NSError *error) {
  275. XCTAssertNil(error);
  276. }];
  277. }
  278. // Instance ID token is nil. Error is nil. Verify fetch fails.
  279. - (void)testNilInstanceIDTokenAndNilError {
  280. NSMutableArray<XCTestExpectation *> *expectations =
  281. [[NSMutableArray alloc] initWithCapacity:RCNTestRCNumTotalInstances];
  282. // Test for each RC FIRApp, namespace instance.
  283. for (int i = 0; i < RCNTestRCNumTotalInstances; i++) {
  284. // Set the token as nil.
  285. [self mockInstanceIDMethodForTokenAndIdentity:nil
  286. tokenError:nil
  287. identity:nil
  288. identityError:nil];
  289. expectations[i] =
  290. [self expectationWithDescription:
  291. [NSString stringWithFormat:@"Test fetch configs successfully - instance %d", i]];
  292. XCTAssertEqual(_configInstances[i].lastFetchStatus, FIRRemoteConfigFetchStatusNoFetchYet);
  293. FIRRemoteConfigFetchCompletion fetchCompletion =
  294. ^void(FIRRemoteConfigFetchStatus status, NSError *error) {
  295. XCTAssertNotNil(error);
  296. [expectations[i] fulfill];
  297. };
  298. [_configInstances[i] fetchWithExpirationDuration:43200 completionHandler:fetchCompletion];
  299. }
  300. [self waitForExpectationsWithTimeout:_expectationTimeout
  301. handler:^(NSError *error) {
  302. XCTAssertNil(error);
  303. }];
  304. }
  305. // Instance ID token is valid. InstanceID is nil with no error. Verify fetch fails.
  306. - (void)testNilInstanceIDWithValidInstanceIDToken {
  307. NSMutableArray<XCTestExpectation *> *expectations =
  308. [[NSMutableArray alloc] initWithCapacity:RCNTestRCNumTotalInstances];
  309. // Test for each RC FIRApp, namespace instance.
  310. for (int i = 0; i < RCNTestRCNumTotalInstances; i++) {
  311. // Set the token as nil.
  312. [self mockInstanceIDMethodForTokenAndIdentity:@"abcd"
  313. tokenError:nil
  314. identity:nil
  315. identityError:nil];
  316. expectations[i] =
  317. [self expectationWithDescription:
  318. [NSString stringWithFormat:@"Test fetch configs successfully - instance %d", i]];
  319. XCTAssertEqual(_configInstances[i].lastFetchStatus, FIRRemoteConfigFetchStatusNoFetchYet);
  320. FIRRemoteConfigFetchCompletion fetchCompletion =
  321. ^void(FIRRemoteConfigFetchStatus status, NSError *error) {
  322. XCTAssertNotNil(error);
  323. [expectations[i] fulfill];
  324. };
  325. [_configInstances[i] fetchWithExpirationDuration:43200 completionHandler:fetchCompletion];
  326. }
  327. [self waitForExpectationsWithTimeout:_expectationTimeout
  328. handler:^(NSError *error) {
  329. XCTAssertNil(error);
  330. }];
  331. }
  332. // Instance ID is not nil, but IID SDK returns an error. Also token is valid.
  333. - (void)testValidInstanceIDAndError {
  334. NSMutableArray<XCTestExpectation *> *expectations =
  335. [[NSMutableArray alloc] initWithCapacity:RCNTestRCNumTotalInstances];
  336. // Test for each RC FIRApp, namespace instance.
  337. for (int i = 0; i < RCNTestRCNumTotalInstances; i++) {
  338. // Set the token as nil.
  339. [self mockInstanceIDMethodForTokenAndIdentity:@"abcd"
  340. tokenError:nil
  341. identity:@"test-id"
  342. identityError:[NSError errorWithDomain:@"com.google.instanceid"
  343. code:FIRInstanceIDErrorUnknown
  344. userInfo:nil]];
  345. expectations[i] =
  346. [self expectationWithDescription:
  347. [NSString stringWithFormat:@"Test fetch configs successfully - instance %d", i]];
  348. XCTAssertEqual(_configInstances[i].lastFetchStatus, FIRRemoteConfigFetchStatusNoFetchYet);
  349. FIRRemoteConfigFetchCompletion fetchCompletion =
  350. ^void(FIRRemoteConfigFetchStatus status, NSError *error) {
  351. XCTAssertNotNil(error);
  352. [expectations[i] fulfill];
  353. };
  354. [_configInstances[i] fetchWithExpirationDuration:43200 completionHandler:fetchCompletion];
  355. }
  356. [self waitForExpectationsWithTimeout:_expectationTimeout
  357. handler:^(NSError *error) {
  358. XCTAssertNil(error);
  359. }];
  360. }
  361. #pragma mark - Test Helpers
  362. - (FIROptions *)firstAppOptions {
  363. // TODO: Evaluate if we want to hardcode things here instead.
  364. FIROptions *options = [[FIROptions alloc] initWithGoogleAppID:@"1:123:ios:123abc"
  365. GCMSenderID:@"correct_gcm_sender_id"];
  366. options.APIKey = @"correct_api_key";
  367. options.projectID = @"abc-xyz-123";
  368. return options;
  369. }
  370. - (FIROptions *)secondAppOptions {
  371. FIROptions *options =
  372. [[FIROptions alloc] initWithContentsOfFile:[[NSBundle bundleForClass:[self class]]
  373. pathForResource:@"SecondApp-GoogleService-Info"
  374. ofType:@"plist"]];
  375. XCTAssertNotNil(options);
  376. return options;
  377. }
  378. @end