RCNConfigDBManagerTest.m 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512
  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 "sqlite3.h"
  18. #import <FirebaseCore/FIRAppInternal.h>
  19. #import <OCMock/OCMock.h>
  20. #import "FirebaseRemoteConfig/Sources/RCNConfigConstants.h"
  21. #import "FirebaseRemoteConfig/Sources/RCNConfigContent.h"
  22. #import "FirebaseRemoteConfig/Sources/RCNConfigDBManager.h"
  23. #import "FirebaseRemoteConfig/Sources/RCNConfigDefines.h"
  24. #import "FirebaseRemoteConfig/Sources/RCNConfigSettings.h"
  25. #import "FirebaseRemoteConfig/Tests/Unit/RCNTestUtilities.h"
  26. @interface RCNConfigDBManager (Test)
  27. - (void)removeDatabaseOnDatabaseQueueAtPath:(NSString *)path;
  28. - (void)insertExperimentTableWithKey:(NSString *)key
  29. value:(NSData *)serializedValue
  30. completionHandler:(RCNDBCompletion)handler;
  31. - (void)deleteExperimentTableForKey:(NSString *)key;
  32. - (void)createOrOpenDatabase;
  33. @end
  34. @interface RCNConfigDBManagerTest : XCTestCase {
  35. NSString *_DBPath;
  36. }
  37. @property(nonatomic, strong) RCNConfigDBManager *DBManager;
  38. @property(nonatomic, assign) NSTimeInterval expectionTimeout;
  39. @end
  40. @implementation RCNConfigDBManagerTest
  41. - (void)setUp {
  42. [super setUp];
  43. // always remove the database at the start of testing
  44. _DBPath = [RCNTestUtilities remoteConfigPathForTestDatabase];
  45. _expectionTimeout = 10.0;
  46. id classMock = OCMClassMock([RCNConfigDBManager class]);
  47. OCMStub([classMock remoteConfigPathForDatabase]).andReturn(_DBPath);
  48. _DBManager = [[RCNConfigDBManager alloc] init];
  49. }
  50. - (void)tearDown {
  51. // Causes crash if main thread exits before the RCNConfigDB queue cleans up
  52. // [_DBManager removeDatabaseOnDatabaseQueueAtPath:_DBPath];
  53. }
  54. - (void)testV1NamespaceMigrationToV2Namespace {
  55. // Write v1 namespace.
  56. XCTestExpectation *loadConfigContentExpectation =
  57. [self expectationWithDescription:@"test v1 namespace migration to v2 namespace"];
  58. NSString *namespace_p = @"testNamespace";
  59. NSString *bundleIdentifier = [NSBundle mainBundle].bundleIdentifier;
  60. __block int count = 0;
  61. for (int i = 0; i <= 100; ++i) {
  62. // Check namespace is updated after database write is completed.
  63. RCNDBCompletion insertCompletion = ^void(BOOL success,
  64. NSDictionary<NSString *, NSString *> *result) {
  65. count++;
  66. XCTAssertTrue(success);
  67. if (count == 100) {
  68. // Migrate to the new namespace.
  69. [_DBManager createOrOpenDatabase];
  70. [_DBManager
  71. loadMainWithBundleIdentifier:bundleIdentifier
  72. completionHandler:^(
  73. BOOL loadSuccess,
  74. NSDictionary<NSString *, NSDictionary<NSString *, id> *> *fetchedConfig,
  75. NSDictionary<NSString *, NSDictionary<NSString *, id> *> *activeConfig,
  76. NSDictionary<NSString *, NSDictionary<NSString *, id> *>
  77. *defaultConfig) {
  78. XCTAssertTrue(loadSuccess);
  79. NSString *fullyQualifiedNamespace =
  80. [NSString stringWithFormat:@"%@:%@", namespace_p, kFIRDefaultAppName];
  81. XCTAssertNotNil(fetchedConfig[fullyQualifiedNamespace]);
  82. XCTAssertEqual([fetchedConfig[fullyQualifiedNamespace] count], 101U);
  83. XCTAssertEqual([fetchedConfig[namespace_p] count], 0);
  84. if (loadSuccess) {
  85. [loadConfigContentExpectation fulfill];
  86. }
  87. }];
  88. }
  89. };
  90. NSString *value = [NSString stringWithFormat:@"value%d", i];
  91. NSString *key = [NSString stringWithFormat:@"key%d", i];
  92. NSArray<id> *values =
  93. @[ bundleIdentifier, namespace_p, key, [value dataUsingEncoding:NSUTF8StringEncoding] ];
  94. [_DBManager insertMainTableWithValues:values
  95. fromSource:RCNDBSourceFetched
  96. completionHandler:insertCompletion];
  97. }
  98. [self waitForExpectationsWithTimeout:_expectionTimeout
  99. handler:^(NSError *error) {
  100. XCTAssertNil(error);
  101. }];
  102. }
  103. - (void)testWriteAndLoadMainTableResult {
  104. XCTestExpectation *loadConfigContentExpectation =
  105. [self expectationWithDescription:@"Write and read metadata in database serailizedly"];
  106. NSString *namespace_p = @"namespace_1";
  107. NSString *bundleIdentifier = [NSBundle mainBundle].bundleIdentifier;
  108. __block int count = 0;
  109. for (int i = 0; i <= 100; ++i) {
  110. // check DB write correctly
  111. RCNDBCompletion insertCompletion = ^void(BOOL success, NSDictionary *result) {
  112. count++;
  113. XCTAssertTrue(success);
  114. if (count == 100) {
  115. // check DB read correctly
  116. [_DBManager loadMainWithBundleIdentifier:bundleIdentifier
  117. completionHandler:^(BOOL success, NSDictionary *fetchedConfig,
  118. NSDictionary *activeConfig,
  119. NSDictionary *defaultConfig) {
  120. NSMutableDictionary *res = [fetchedConfig mutableCopy];
  121. XCTAssertTrue(success);
  122. FIRRemoteConfigValue *value = res[namespace_p][@"key100"];
  123. XCTAssertEqualObjects(value.stringValue, @"value100");
  124. if (success) {
  125. [loadConfigContentExpectation fulfill];
  126. }
  127. }];
  128. }
  129. };
  130. NSString *value = [NSString stringWithFormat:@"value%d", i];
  131. NSString *key = [NSString stringWithFormat:@"key%d", i];
  132. NSArray *values =
  133. @[ bundleIdentifier, namespace_p, key, [value dataUsingEncoding:NSUTF8StringEncoding] ];
  134. [_DBManager insertMainTableWithValues:values
  135. fromSource:RCNDBSourceFetched
  136. completionHandler:insertCompletion];
  137. }
  138. [self waitForExpectationsWithTimeout:_expectionTimeout
  139. handler:^(NSError *error) {
  140. XCTAssertNil(error);
  141. }];
  142. }
  143. - (void)testWriteAndLoadInternalMetadataResult {
  144. XCTestExpectation *loadConfigContentExpectation = [self
  145. expectationWithDescription:@"Write and read internal metadata in database successfully"];
  146. __block int count = 0;
  147. for (int i = 0; i <= 100; ++i) {
  148. // check DB write correctly
  149. RCNDBCompletion insertCompletion = ^void(BOOL success, NSDictionary *result) {
  150. count++;
  151. XCTAssertTrue(success);
  152. if (count == 100) {
  153. // check DB read correctly
  154. NSDictionary *result = [_DBManager loadInternalMetadataTable];
  155. NSString *stringValue = [[NSString alloc] initWithData:result[@"key100"]
  156. encoding:NSUTF8StringEncoding];
  157. XCTAssertEqualObjects(stringValue, @"value100");
  158. if (success) {
  159. [loadConfigContentExpectation fulfill];
  160. }
  161. }
  162. };
  163. NSString *value = [NSString stringWithFormat:@"value%d", i];
  164. NSString *key = [NSString stringWithFormat:@"key%d", i];
  165. NSArray *values = @[ key, [value dataUsingEncoding:NSUTF8StringEncoding] ];
  166. [_DBManager insertInternalMetadataTableWithValues:values completionHandler:insertCompletion];
  167. }
  168. [self waitForExpectationsWithTimeout:_expectionTimeout
  169. handler:^(NSError *error) {
  170. XCTAssertNil(error);
  171. }];
  172. }
  173. - (void)testWriteAndLoadMetadataResult {
  174. XCTestExpectation *writeAndLoadMetadataExpectation =
  175. [self expectationWithDescription:@"Write and load metadata in database successfully"];
  176. NSString *bundleIdentifier = [NSBundle mainBundle].bundleIdentifier;
  177. NSTimeInterval lastFetchTimestamp = [NSDate date].timeIntervalSince1970;
  178. NSDictionary *deviceContext = @{@"app_version" : @"1.0.1", @"os_version" : @"iOS9.1"};
  179. NSDictionary *syncedDBCustomVariables = @{@"user_level" : @15, @"user_experiences" : @"2468"};
  180. NSArray *successFetchTimes = @[];
  181. NSTimeInterval now = [NSDate date].timeIntervalSince1970;
  182. NSArray *failureFetchTimes =
  183. @[ [NSNumber numberWithDouble:now - 200], [NSNumber numberWithDouble:now] ];
  184. // serialize objects
  185. NSError *error;
  186. NSData *serializedAppContext = [NSJSONSerialization dataWithJSONObject:syncedDBCustomVariables
  187. options:NSJSONWritingPrettyPrinted
  188. error:&error];
  189. NSData *serializedDeviceContext =
  190. [NSJSONSerialization dataWithJSONObject:deviceContext
  191. options:NSJSONWritingPrettyPrinted
  192. error:&error];
  193. NSData *serializedDigestPerNamespace =
  194. [NSJSONSerialization dataWithJSONObject:@{} options:NSJSONWritingPrettyPrinted error:&error];
  195. NSData *serializedSuccessTime = [NSJSONSerialization dataWithJSONObject:successFetchTimes
  196. options:NSJSONWritingPrettyPrinted
  197. error:&error];
  198. NSData *serializedFailureTime = [NSJSONSerialization dataWithJSONObject:failureFetchTimes
  199. options:NSJSONWritingPrettyPrinted
  200. error:&error];
  201. NSDictionary *columnNameToValue = @{
  202. RCNKeyBundleIdentifier : bundleIdentifier,
  203. RCNKeyFetchTime : @(lastFetchTimestamp),
  204. RCNKeyDigestPerNamespace : serializedDigestPerNamespace,
  205. RCNKeyDeviceContext : serializedDeviceContext,
  206. RCNKeyAppContext : serializedAppContext,
  207. RCNKeySuccessFetchTime : serializedSuccessTime,
  208. RCNKeyFailureFetchTime : serializedFailureTime,
  209. RCNKeyLastFetchStatus : @(FIRRemoteConfigFetchStatusSuccess),
  210. RCNKeyLastFetchError : @(FIRRemoteConfigErrorUnknown),
  211. RCNKeyLastApplyTime : @(now - 100),
  212. RCNKeyLastSetDefaultsTime : @(now - 200)
  213. };
  214. RCNDBCompletion completion = ^(BOOL success, NSDictionary *result1) {
  215. NSDictionary *result = [_DBManager loadMetadataWithBundleIdentifier:bundleIdentifier];
  216. XCTAssertNotNil(result);
  217. XCTAssertEqualObjects(result[RCNKeyBundleIdentifier], bundleIdentifier);
  218. XCTAssertEqual([result[RCNKeyFetchTime] doubleValue], lastFetchTimestamp);
  219. XCTAssertEqualObjects([result[RCNKeyDigestPerNamespace] copy], @{});
  220. XCTAssertEqualObjects([result[RCNKeyDeviceContext] copy], deviceContext);
  221. XCTAssertEqualObjects([result[RCNKeyAppContext] copy], syncedDBCustomVariables);
  222. XCTAssertEqualObjects([result[RCNKeySuccessFetchTime] copy], successFetchTimes);
  223. // TODO(chliang): Fix the flakiness caused by the commented out test
  224. // XCTAssertTrue([[result[RCNKeyFailureFetchTime] copy] isEqualToArray:failureFetchTimes]);
  225. XCTAssertEqual([result[RCNKeyLastFetchStatus] intValue],
  226. (int)FIRRemoteConfigFetchStatusSuccess);
  227. XCTAssertEqual([result[RCNKeyLastFetchError] intValue], (int)FIRRemoteConfigErrorUnknown);
  228. XCTAssertEqual([result[RCNKeyLastApplyTime] doubleValue], now - 100);
  229. XCTAssertEqual([result[RCNKeyLastSetDefaultsTime] doubleValue], now - 200);
  230. [writeAndLoadMetadataExpectation fulfill];
  231. };
  232. [_DBManager insertMetadataTableWithValues:columnNameToValue completionHandler:completion];
  233. [self waitForExpectationsWithTimeout:_expectionTimeout
  234. handler:^(NSError *error) {
  235. XCTAssertNil(error);
  236. }];
  237. }
  238. - (void)testWriteAndLoadExperiments {
  239. XCTestExpectation *updateAndLoadExperimentExpectation =
  240. [self expectationWithDescription:@"Update and load experiment in database successfully"];
  241. NSError *error;
  242. NSArray *payload2 = @[ @"ab", @"cd" ];
  243. NSData *payloadData2 = [NSJSONSerialization dataWithJSONObject:payload2
  244. options:NSJSONWritingPrettyPrinted
  245. error:&error];
  246. NSDictionary *payload3 =
  247. @{@"experiment_ID" : @"35667", @"experiment_activate_name" : @"activate_game"};
  248. NSData *payloadData3 = [NSJSONSerialization dataWithJSONObject:payload3
  249. options:NSJSONWritingPrettyPrinted
  250. error:&error];
  251. NSArray *payloads = @[ [[NSData alloc] init], payloadData2, payloadData3 ];
  252. RCNDBCompletion writePayloadCompletion = ^(BOOL success, NSDictionary *result) {
  253. NSDictionary *metadata =
  254. @{@"last_known_start_time" : @(-11), @"experiment_new_metadata" : @"wonderful"};
  255. XCTAssertTrue(success);
  256. RCNDBCompletion writeMetadataCompletion = ^(BOOL success, NSDictionary *result) {
  257. XCTAssertTrue(success);
  258. RCNDBCompletion readCompletion = ^(BOOL success, NSDictionary *experimentResults) {
  259. XCTAssertTrue(success);
  260. XCTAssertNotNil(experimentResults[@RCNExperimentTableKeyPayload]);
  261. XCTAssertEqualObjects(payloads, experimentResults[@RCNExperimentTableKeyPayload]);
  262. XCTAssertNotNil(experimentResults[@RCNExperimentTableKeyMetadata]);
  263. XCTAssertEqualWithAccuracy(
  264. -11,
  265. [experimentResults[@RCNExperimentTableKeyMetadata][@"last_known_start_time"]
  266. doubleValue],
  267. 1.0);
  268. XCTAssertEqualObjects(
  269. @"wonderful",
  270. experimentResults[@RCNExperimentTableKeyMetadata][@"experiment_new_metadata"]);
  271. [updateAndLoadExperimentExpectation fulfill];
  272. };
  273. [_DBManager loadExperimentWithCompletionHandler:readCompletion];
  274. };
  275. NSError *error;
  276. XCTAssertTrue([NSJSONSerialization isValidJSONObject:metadata]);
  277. NSData *serializedMetadata = [NSJSONSerialization dataWithJSONObject:metadata
  278. options:NSJSONWritingPrettyPrinted
  279. error:&error];
  280. [_DBManager insertExperimentTableWithKey:@RCNExperimentTableKeyMetadata
  281. value:serializedMetadata
  282. completionHandler:writeMetadataCompletion];
  283. };
  284. [_DBManager insertExperimentTableWithKey:@RCNExperimentTableKeyPayload
  285. value:[[NSData alloc] init]
  286. completionHandler:nil];
  287. [_DBManager insertExperimentTableWithKey:@RCNExperimentTableKeyPayload
  288. value:payloadData2
  289. completionHandler:nil];
  290. [_DBManager insertExperimentTableWithKey:@RCNExperimentTableKeyPayload
  291. value:payloadData3
  292. completionHandler:writePayloadCompletion];
  293. [self waitForExpectationsWithTimeout:_expectionTimeout handler:nil];
  294. }
  295. - (void)testWriteAndLoadMetadataMultipleTimes {
  296. XCTestExpectation *updateAndLoadMetadataExpectation = [self
  297. expectationWithDescription:@"Update and load experiment metadata in database successfully"];
  298. RCNDBCompletion readCompletion = ^(BOOL success, NSDictionary *experimentResults) {
  299. XCTAssertTrue(success);
  300. XCTAssertNotNil(experimentResults[@RCNExperimentTableKeyPayload]);
  301. XCTAssertNotNil(experimentResults[@RCNExperimentTableKeyMetadata]);
  302. XCTAssertEqualWithAccuracy(
  303. 12345678,
  304. [experimentResults[@RCNExperimentTableKeyMetadata][@"last_known_start_time"] doubleValue],
  305. 1.0);
  306. XCTAssertEqualObjects(
  307. @"wonderful",
  308. experimentResults[@RCNExperimentTableKeyMetadata][@"experiment_new_metadata"]);
  309. [updateAndLoadMetadataExpectation fulfill];
  310. };
  311. NSDictionary *metadata =
  312. @{@"last_known_start_time" : @(-11), @"experiment_new_metadata" : @"wonderful"};
  313. NSError *error;
  314. XCTAssertTrue([NSJSONSerialization isValidJSONObject:metadata]);
  315. NSData *serializedMetadata = [NSJSONSerialization dataWithJSONObject:metadata
  316. options:NSJSONWritingPrettyPrinted
  317. error:&error];
  318. [_DBManager insertExperimentTableWithKey:@RCNExperimentTableKeyMetadata
  319. value:serializedMetadata
  320. completionHandler:nil];
  321. metadata = @{@"last_known_start_time" : @(12345678), @"experiment_new_metadata" : @"wonderful"};
  322. XCTAssertTrue([NSJSONSerialization isValidJSONObject:metadata]);
  323. serializedMetadata = [NSJSONSerialization dataWithJSONObject:metadata
  324. options:NSJSONWritingPrettyPrinted
  325. error:&error];
  326. [_DBManager insertExperimentTableWithKey:@RCNExperimentTableKeyMetadata
  327. value:serializedMetadata
  328. completionHandler:nil];
  329. [_DBManager loadExperimentWithCompletionHandler:readCompletion];
  330. [self waitForExpectationsWithTimeout:_expectionTimeout handler:nil];
  331. }
  332. - (void)testUpdateAndloadLastFetchStatus {
  333. XCTestExpectation *updateAndLoadMetadataExpectation = [self
  334. expectationWithDescription:@"Update and load last fetch status in database successfully."];
  335. NSString *bundleIdentifier = [NSBundle mainBundle].bundleIdentifier;
  336. // Metadata row must exist before update
  337. RCNDBCompletion createMetadataCompletion = ^(BOOL success, NSDictionary *createResult) {
  338. NSDictionary *result = [_DBManager loadMetadataWithBundleIdentifier:bundleIdentifier];
  339. XCTAssertTrue(success);
  340. XCTAssertNotNil(result);
  341. XCTAssertEqual([result[RCNKeyLastFetchStatus] intValue],
  342. (int)FIRRemoteConfigFetchStatusSuccess);
  343. XCTAssertEqual([result[RCNKeyLastFetchError] intValue], (int)FIRRemoteConfigErrorUnknown);
  344. RCNDBCompletion updateMetadataCompletion = ^(BOOL success, NSDictionary *updateResult) {
  345. NSDictionary *result = [_DBManager loadMetadataWithBundleIdentifier:bundleIdentifier];
  346. XCTAssertTrue(success);
  347. XCTAssertNotNil(result);
  348. XCTAssertEqual([result[RCNKeyLastFetchStatus] intValue],
  349. (int)FIRRemoteConfigFetchStatusThrottled);
  350. XCTAssertEqual([result[RCNKeyLastFetchError] intValue], (int)FIRRemoteConfigErrorThrottled);
  351. [updateAndLoadMetadataExpectation fulfill];
  352. };
  353. // Update with throttle status.
  354. [_DBManager
  355. updateMetadataWithOption:RCNUpdateOptionFetchStatus
  356. values:@[
  357. @(FIRRemoteConfigFetchStatusThrottled), @(FIRRemoteConfigErrorThrottled)
  358. ]
  359. completionHandler:updateMetadataCompletion];
  360. };
  361. [_DBManager insertMetadataTableWithValues:[self createSampleMetadata]
  362. completionHandler:createMetadataCompletion];
  363. [self waitForExpectationsWithTimeout:_expectionTimeout handler:nil];
  364. }
  365. /// TODO: Fix test case.
  366. /// Tests that we can insert values in the database and can update them.
  367. - (void)ignore_InsertAndUpdateApplyTime {
  368. XCTestExpectation *updateAndLoadMetadataExpectation =
  369. [self expectationWithDescription:@"Update and load apply time in database successfully."];
  370. NSString *bundleIdentifier = [NSBundle mainBundle].bundleIdentifier;
  371. NSTimeInterval lastApplyTimestamp = [NSDate date].timeIntervalSince1970;
  372. // Metadata row must exist before update
  373. RCNDBCompletion createMetadataCompletion = ^(BOOL success, NSDictionary *createResult) {
  374. XCTAssertTrue(success);
  375. // Read newly created metadata.
  376. NSDictionary *result = [_DBManager loadMetadataWithBundleIdentifier:bundleIdentifier];
  377. XCTAssertNotNil(result);
  378. XCTAssertEqual([result[RCNKeyLastApplyTime] doubleValue], (double)100);
  379. RCNDBCompletion updateMetadataCompletion = ^(BOOL success, NSDictionary *updateResult) {
  380. NSDictionary *result = [_DBManager loadMetadataWithBundleIdentifier:bundleIdentifier];
  381. XCTAssertTrue(success);
  382. XCTAssertNotNil(result);
  383. XCTAssertEqual([result[RCNKeyLastApplyTime] doubleValue], lastApplyTimestamp);
  384. [updateAndLoadMetadataExpectation fulfill];
  385. };
  386. // Update apply config timestamp.
  387. [_DBManager updateMetadataWithOption:RCNUpdateOptionApplyTime
  388. values:@[ @(lastApplyTimestamp) ]
  389. completionHandler:updateMetadataCompletion];
  390. };
  391. [_DBManager insertMetadataTableWithValues:[self createSampleMetadata]
  392. completionHandler:createMetadataCompletion];
  393. [self waitForExpectationsWithTimeout:_expectionTimeout handler:nil];
  394. }
  395. - (void)testUpdateAndLoadSetDefaultsTime {
  396. XCTestExpectation *updateAndLoadMetadataExpectation = [self
  397. expectationWithDescription:@"Update and load set defaults time in database successfully."];
  398. NSString *bundleIdentifier = [NSBundle mainBundle].bundleIdentifier;
  399. NSTimeInterval lastSetDefaultsTimestamp = [NSDate date].timeIntervalSince1970;
  400. // Metadata row must exist before update
  401. RCNDBCompletion createMetadataCompletion = ^(BOOL success, NSDictionary *createResult) {
  402. NSDictionary *result = [_DBManager loadMetadataWithBundleIdentifier:bundleIdentifier];
  403. XCTAssertTrue(success);
  404. XCTAssertNotNil(result);
  405. XCTAssertEqual([result[RCNKeyLastSetDefaultsTime] doubleValue], (double)200);
  406. RCNDBCompletion updateMetadataCompletion = ^(BOOL success, NSDictionary *updateResult) {
  407. NSDictionary *result = [_DBManager loadMetadataWithBundleIdentifier:bundleIdentifier];
  408. XCTAssertTrue(success);
  409. XCTAssertNotNil(result);
  410. XCTAssertEqual([result[RCNKeyLastSetDefaultsTime] doubleValue], lastSetDefaultsTimestamp);
  411. [updateAndLoadMetadataExpectation fulfill];
  412. };
  413. // Update setting default config timestamp.
  414. [_DBManager updateMetadataWithOption:RCNUpdateOptionDefaultTime
  415. values:@[ @(lastSetDefaultsTimestamp) ]
  416. completionHandler:updateMetadataCompletion];
  417. };
  418. [_DBManager insertMetadataTableWithValues:[self createSampleMetadata]
  419. completionHandler:createMetadataCompletion];
  420. [self waitForExpectationsWithTimeout:_expectionTimeout handler:nil];
  421. }
  422. - (NSDictionary *)createSampleMetadata {
  423. NSString *bundleIdentifier = [NSBundle mainBundle].bundleIdentifier;
  424. NSDictionary *deviceContext = @{};
  425. NSDictionary *syncedDBCustomVariables = @{};
  426. NSArray *successFetchTimes = @[];
  427. NSArray *failureFetchTimes = @[];
  428. // serialize objects
  429. NSError *error;
  430. NSData *serializedAppContext = [NSJSONSerialization dataWithJSONObject:syncedDBCustomVariables
  431. options:NSJSONWritingPrettyPrinted
  432. error:&error];
  433. NSData *serializedDeviceContext =
  434. [NSJSONSerialization dataWithJSONObject:deviceContext
  435. options:NSJSONWritingPrettyPrinted
  436. error:&error];
  437. NSData *serializedDigestPerNamespace =
  438. [NSJSONSerialization dataWithJSONObject:@{} options:NSJSONWritingPrettyPrinted error:&error];
  439. NSData *serializedSuccessTime = [NSJSONSerialization dataWithJSONObject:successFetchTimes
  440. options:NSJSONWritingPrettyPrinted
  441. error:&error];
  442. NSData *serializedFailureTime = [NSJSONSerialization dataWithJSONObject:failureFetchTimes
  443. options:NSJSONWritingPrettyPrinted
  444. error:&error];
  445. return @{
  446. RCNKeyBundleIdentifier : bundleIdentifier,
  447. RCNKeyFetchTime : @(0),
  448. RCNKeyDigestPerNamespace : serializedDigestPerNamespace,
  449. RCNKeyDeviceContext : serializedDeviceContext,
  450. RCNKeyAppContext : serializedAppContext,
  451. RCNKeySuccessFetchTime : serializedSuccessTime,
  452. RCNKeyFailureFetchTime : serializedFailureTime,
  453. RCNKeyLastFetchStatus : @(FIRRemoteConfigFetchStatusSuccess),
  454. RCNKeyLastFetchError : @(FIRRemoteConfigErrorUnknown),
  455. RCNKeyLastApplyTime : @(100),
  456. RCNKeyLastSetDefaultsTime : @(200)
  457. };
  458. }
  459. @end