RCNConfigSettingsTest.m 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306
  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/Sources/Private/RCNConfigSettings.h"
  19. #import "FirebaseRemoteConfig/Sources/RCNConfigConstants.h"
  20. #import "FirebaseRemoteConfig/Sources/RCNConfigDBManager.h"
  21. #import "FirebaseRemoteConfig/Tests/Unit/RCNTestUtilities.h"
  22. @interface RCNConfigSettings (ExposedTestCase)
  23. - (RCNConfigFetchRequest *)nextRequestWithUserProperties:(NSDictionary *)userProperties
  24. fetchedConfig:(NSDictionary *)fetchedConfig;
  25. - (void)updateInternalContentWithResponse:(RCNConfigFetchResponse *)response;
  26. - (void)updateConfigContentWithResponse:(RCNConfigFetchResponse *)response;
  27. - (void)updateFetchTimeWithSuccessFetch:(BOOL)isSuccessfulFetch;
  28. - (BOOL)hasCachedData;
  29. - (BOOL)isCachedDataFresh;
  30. @end
  31. @interface RCNConfigSettingsTest : XCTestCase {
  32. RCNConfigSettings *_mockSettings;
  33. }
  34. @end
  35. @implementation RCNConfigSettingsTest
  36. - (void)setUp {
  37. [super setUp];
  38. // Mock the read/write DB operations, which are not needed in these tests.
  39. _mockSettings = [[RCNConfigSettings alloc] initWithDatabaseManager:nil];
  40. }
  41. - (void)testCrashShouldNotHappenWithoutMainBundleID {
  42. id mockBundle = OCMPartialMock([NSBundle mainBundle]);
  43. OCMStub([NSBundle mainBundle]).andReturn(mockBundle);
  44. OCMStub([mockBundle bundleIdentifier]).andReturn(nil);
  45. _mockSettings =
  46. [[RCNConfigSettings alloc] initWithDatabaseManager:[[RCNConfigDBManager alloc] init]];
  47. [mockBundle stopMocking];
  48. }
  49. #ifdef FIX_OR_DELETE
  50. - (void)testUpdateInternalMetadata {
  51. RCNConfigFetchResponse *response = [[RCNConfigFetchResponse alloc] init];
  52. // Mock internal metadata array with all_packages prefix key
  53. response.internalMetadataArray = [RCNTestUtilities entryArrayWithKeyValuePair:@{
  54. [NSString stringWithFormat:@"%@:%@", RCNInternalMetadataAllPackagesPrefix,
  55. RCNHTTPConnectionTimeoutInMillisecondsKey] : @"50",
  56. [NSString stringWithFormat:@"%@:%@", RCNInternalMetadataAllPackagesPrefix,
  57. RCNHTTPReadTimeoutInMillisecondsKey] : @"2000000",
  58. [NSString stringWithFormat:@"%@:%@", RCNInternalMetadataAllPackagesPrefix,
  59. RCNThrottledSuccessFetchTimeIntervalInSecondsKey] : @"300",
  60. [NSString stringWithFormat:@"%@:%@", RCNInternalMetadataAllPackagesPrefix,
  61. RCNThrottledSuccessFetchCountKey] : @"-6",
  62. [NSString stringWithFormat:@"%@:%@", RCNInternalMetadataAllPackagesPrefix,
  63. RCNThrottledFailureFetchTimeIntervalInSecondsKey] : @"10000000",
  64. [NSString stringWithFormat:@"%@:%@", RCNInternalMetadataAllPackagesPrefix,
  65. RCNThrottledFailureFetchCountKey] : @"21",
  66. }];
  67. [_mockSettings updateInternalContentWithResponse:response];
  68. XCTAssertEqual(
  69. [_mockSettings internalMetadataValueForKey:RCNHTTPConnectionTimeoutInMillisecondsKey
  70. minValue:RCNHTTPConnectionTimeoutInMillisecondsMin
  71. maxValue:RCNHTTPConnectionTimeoutInMillisecondsMax
  72. defaultValue:RCNHTTPConnectionTimeoutInMillisecondsDefault],
  73. RCNHTTPConnectionTimeoutInMillisecondsMin,
  74. @"HTTP Connection Timeout must be within the range.");
  75. XCTAssertEqual(
  76. [_mockSettings internalMetadataValueForKey:RCNHTTPReadTimeoutInMillisecondsKey
  77. minValue:RCNHTTPReadTimeoutInMillisecondsMin
  78. maxValue:RCNHTTPReadTimeoutInMillisecondsMax
  79. defaultValue:RCNHTTPReadTimeoutInMillisecondsDefault],
  80. RCNHTTPReadTimeoutInMillisecondsMax, @"HTTP Read Timeout must be within the range");
  81. XCTAssertEqual(
  82. [_mockSettings
  83. internalMetadataValueForKey:RCNThrottledSuccessFetchTimeIntervalInSecondsKey
  84. minValue:RCNThrottledSuccessFetchTimeIntervalInSecondsMin
  85. maxValue:RCNThrottledSuccessFetchTimeIntervalInSecondsMax
  86. defaultValue:RCNThrottledSuccessFetchTimeIntervalInSecondsDefault],
  87. RCNThrottledSuccessFetchTimeIntervalInSecondsMin,
  88. @"Throttling success internal must be within the range");
  89. XCTAssertEqual([_mockSettings internalMetadataValueForKey:RCNThrottledSuccessFetchCountKey
  90. minValue:RCNThrottledSuccessFetchCountMin
  91. maxValue:RCNThrottledSuccessFetchCountMax
  92. defaultValue:RCNThrottledSuccessFetchCountDefault],
  93. RCNThrottledSuccessFetchCountMin);
  94. XCTAssertEqual(
  95. [_mockSettings
  96. internalMetadataValueForKey:RCNThrottledFailureFetchTimeIntervalInSecondsKey
  97. minValue:RCNThrottledFailureFetchTimeIntervalInSecondsMin
  98. maxValue:RCNThrottledFailureFetchTimeIntervalInSecondsMax
  99. defaultValue:RCNThrottledFailureFetchTimeIntervalInSecondsDefault],
  100. RCNThrottledFailureFetchTimeIntervalInSecondsMax);
  101. XCTAssertEqual([_mockSettings internalMetadataValueForKey:RCNThrottledFailureFetchCountKey
  102. minValue:RCNThrottledFailureFetchCountMin
  103. maxValue:RCNThrottledFailureFetchCountMax
  104. defaultValue:RCNThrottledFailureFetchCountDefault],
  105. RCNThrottledFailureFetchCountMax);
  106. // Mock internal metadata array with bundle_identifier prefix key.
  107. // bundle_identifier prefixed key should override all_packages prefix key
  108. NSString *bundleIdentifier = [[NSBundle mainBundle] bundleIdentifier];
  109. response.internalMetadataArray = [RCNTestUtilities entryArrayWithKeyValuePair:@{
  110. [NSString stringWithFormat:@"%@:%@", bundleIdentifier,
  111. RCNHTTPConnectionTimeoutInMillisecondsKey] : @"70000",
  112. [NSString stringWithFormat:@"%@:%@", bundleIdentifier, RCNHTTPReadTimeoutInMillisecondsKey] :
  113. @"70000",
  114. [NSString stringWithFormat:@"%@:%@", bundleIdentifier,
  115. RCNThrottledSuccessFetchTimeIntervalInSecondsKey] : @"1800",
  116. [NSString stringWithFormat:@"%@:%@", bundleIdentifier, RCNThrottledSuccessFetchCountKey] :
  117. @"100",
  118. [NSString stringWithFormat:@"%@:%@", bundleIdentifier,
  119. RCNThrottledFailureFetchTimeIntervalInSecondsKey] : @"1800",
  120. [NSString stringWithFormat:@"%@:%@", bundleIdentifier, RCNThrottledFailureFetchCountKey] : @"0",
  121. }];
  122. [_mockSettings updateInternalContentWithResponse:response];
  123. XCTAssertEqual(
  124. [_mockSettings internalMetadataValueForKey:RCNHTTPConnectionTimeoutInMillisecondsKey
  125. minValue:RCNHTTPConnectionTimeoutInMillisecondsMin
  126. maxValue:RCNHTTPConnectionTimeoutInMillisecondsMax
  127. defaultValue:RCNHTTPConnectionTimeoutInMillisecondsDefault],
  128. 70000);
  129. XCTAssertEqual(
  130. [_mockSettings internalMetadataValueForKey:RCNHTTPReadTimeoutInMillisecondsKey
  131. minValue:RCNHTTPReadTimeoutInMillisecondsMin
  132. maxValue:RCNHTTPReadTimeoutInMillisecondsMax
  133. defaultValue:RCNHTTPReadTimeoutInMillisecondsDefault],
  134. 70000);
  135. XCTAssertEqual(
  136. [_mockSettings
  137. internalMetadataValueForKey:RCNThrottledSuccessFetchTimeIntervalInSecondsKey
  138. minValue:RCNThrottledSuccessFetchTimeIntervalInSecondsMin
  139. maxValue:RCNThrottledSuccessFetchTimeIntervalInSecondsMax
  140. defaultValue:RCNThrottledSuccessFetchTimeIntervalInSecondsDefault],
  141. 1800);
  142. XCTAssertEqual([_mockSettings internalMetadataValueForKey:RCNThrottledSuccessFetchCountKey
  143. minValue:RCNThrottledSuccessFetchCountMin
  144. maxValue:RCNThrottledSuccessFetchCountMax
  145. defaultValue:RCNThrottledSuccessFetchCountDefault],
  146. 20);
  147. XCTAssertEqual(
  148. [_mockSettings
  149. internalMetadataValueForKey:RCNThrottledFailureFetchTimeIntervalInSecondsKey
  150. minValue:RCNThrottledFailureFetchTimeIntervalInSecondsMin
  151. maxValue:RCNThrottledFailureFetchTimeIntervalInSecondsMax
  152. defaultValue:RCNThrottledFailureFetchTimeIntervalInSecondsDefault],
  153. 1800);
  154. XCTAssertEqual([_mockSettings internalMetadataValueForKey:RCNThrottledFailureFetchCountKey
  155. minValue:RCNThrottledFailureFetchCountMin
  156. maxValue:RCNThrottledFailureFetchCountMax
  157. defaultValue:RCNThrottledFailureFetchCountDefault],
  158. 1);
  159. }
  160. - (void)testInternalMetadataOverride {
  161. // Mock response after fetching.
  162. RCNConfigFetchResponse *response = [[RCNConfigFetchResponse alloc] init];
  163. NSString *onePackageKey =
  164. [NSString stringWithFormat:@"%@:%@", [[NSBundle mainBundle] bundleIdentifier],
  165. RCNThrottledSuccessFetchCountKey];
  166. NSString *allPackageKey =
  167. [NSString stringWithFormat:@"%@:%@", RCNInternalMetadataAllPackagesPrefix,
  168. RCNThrottledSuccessFetchCountKey];
  169. [_mockSettings updateInternalContentWithResponse:response];
  170. XCTAssertEqual([_mockSettings internalMetadataValueForKey:RCNThrottledSuccessFetchCountKey
  171. minValue:RCNThrottledSuccessFetchCountMin
  172. maxValue:RCNThrottledSuccessFetchCountMax
  173. defaultValue:RCNThrottledSuccessFetchCountDefault],
  174. RCNThrottledSuccessFetchCountDefault,
  175. @"Fetch with no internal metadata, must return default value.");
  176. response.internalMetadataArray =
  177. [RCNTestUtilities entryArrayWithKeyValuePair:@{onePackageKey : @"8", allPackageKey : @"9"}];
  178. [_mockSettings updateInternalContentWithResponse:response];
  179. XCTAssertEqual([_mockSettings internalMetadataValueForKey:RCNThrottledSuccessFetchCountKey
  180. minValue:RCNThrottledSuccessFetchCountMin
  181. maxValue:RCNThrottledSuccessFetchCountMax
  182. defaultValue:RCNThrottledSuccessFetchCountDefault],
  183. 8, @"Fetch with both keys, must return the one with package key.");
  184. [response.internalMetadataArray removeAllObjects];
  185. [_mockSettings updateInternalContentWithResponse:response];
  186. XCTAssertEqual(
  187. [_mockSettings internalMetadataValueForKey:RCNThrottledSuccessFetchCountKey
  188. minValue:RCNThrottledSuccessFetchCountMin
  189. maxValue:RCNThrottledSuccessFetchCountMax
  190. defaultValue:RCNThrottledSuccessFetchCountDefault],
  191. 9, @"Fetch with no internal metadata, must return the one with previous all_packages key.");
  192. [response.internalMetadataArray removeAllObjects];
  193. response.internalMetadataArray = [RCNTestUtilities entryArrayWithKeyValuePair:@{
  194. onePackageKey : @"6",
  195. }];
  196. [_mockSettings updateInternalContentWithResponse:response];
  197. XCTAssertEqual([_mockSettings internalMetadataValueForKey:RCNThrottledSuccessFetchCountKey
  198. minValue:RCNThrottledSuccessFetchCountMin
  199. maxValue:RCNThrottledSuccessFetchCountMax
  200. defaultValue:RCNThrottledSuccessFetchCountDefault],
  201. 6, @"Fetch with one package key, must return the one with package key.");
  202. }
  203. - (void)testThrottlingFresh {
  204. NSTimeInterval endTimestamp = [_mockSettings cachedDataThrottledEndTimestamp];
  205. NSTimeInterval now = [[NSDate date] timeIntervalSince1970];
  206. XCTAssertTrue(endTimestamp <= now);
  207. // Fetch failed once.
  208. [_mockSettings updateFetchTimeWithSuccessFetch:NO];
  209. endTimestamp = [_mockSettings cachedDataThrottledEndTimestamp];
  210. now = [[NSDate date] timeIntervalSince1970];
  211. XCTAssertTrue(endTimestamp <= now);
  212. // Fetch succeeded once.
  213. [_mockSettings updateFetchTimeWithSuccessFetch:YES];
  214. endTimestamp = [_mockSettings cachedDataThrottledEndTimestamp];
  215. now = [[NSDate date] timeIntervalSince1970];
  216. XCTAssertTrue(endTimestamp <= now);
  217. // The failure fetch rate is 5. Try another 3 times, and do not go over the limit.
  218. for (int i = 0; i < 3; i++) {
  219. [_mockSettings updateFetchTimeWithSuccessFetch:NO];
  220. }
  221. endTimestamp = [_mockSettings cachedDataThrottledEndTimestamp];
  222. now = [[NSDate date] timeIntervalSince1970];
  223. XCTAssertTrue(endTimestamp <= now);
  224. // The success fetch rate is 5. Try another 4 times, which should go over the limit afterwards.
  225. for (int i = 0; i < 4; i++) {
  226. [_mockSettings updateFetchTimeWithSuccessFetch:YES];
  227. }
  228. endTimestamp = [_mockSettings cachedDataThrottledEndTimestamp];
  229. now = [[NSDate date] timeIntervalSince1970];
  230. // Now it should go over the limit.
  231. XCTAssertFalse(endTimestamp <= now);
  232. XCTAssertTrue([_mockSettings hasCachedData]);
  233. }
  234. #endif
  235. - (void)testResetDigestInNextRequest {
  236. NSDictionary *digestPerNamespace = @{@"firebase" : @"1234", @"p4" : @"5678"};
  237. [_mockSettings setNamespaceToDigest:digestPerNamespace];
  238. RCNNamedValue *firebaseDigest = [[RCNNamedValue alloc] init];
  239. firebaseDigest.name = @"firebase";
  240. firebaseDigest.value = @"1234";
  241. RCNNamedValue *p4Digest = [[RCNNamedValue alloc] init];
  242. p4Digest.name = @"p4";
  243. p4Digest.value = @"5678";
  244. // Test where each namespace's fetched config is a non-empty dictionary, request should include
  245. // the namespace's digest.
  246. NSDictionary *fetchedConfig =
  247. @{@"firebase" : @{@"a" : @"b", @"c" : @"d"}, @"p4" : @{@"p4key" : @"p4value"}};
  248. RCNConfigFetchRequest *request = [_mockSettings nextRequestWithUserProperties:nil
  249. fetchedConfig:fetchedConfig];
  250. XCTAssertEqual(request.packageDataArray.count, 1);
  251. NSArray *expectedArray = @[ firebaseDigest, p4Digest ];
  252. XCTAssertEqualObjects(request.packageDataArray[0].namespaceDigestArray, expectedArray);
  253. // Test when the namespace's fetched config doesn't exist, reset the digest by not included
  254. // in the request.
  255. fetchedConfig = @{@"firebase" : @{@"a" : @"b", @"c" : @"d"}};
  256. request = [_mockSettings nextRequestWithUserProperties:nil fetchedConfig:fetchedConfig];
  257. XCTAssertEqual(request.packageDataArray.count, 1);
  258. XCTAssertEqualObjects(request.packageDataArray[0].namespaceDigestArray, @[ firebaseDigest ]);
  259. // Test when a namespace's fetched config is empty, reset the digest.
  260. fetchedConfig = @{@"firebase" : @{@"a" : @"b", @"c" : @"d"}, @"p4" : @{}};
  261. request = [_mockSettings nextRequestWithUserProperties:nil fetchedConfig:fetchedConfig];
  262. XCTAssertEqual(request.packageDataArray.count, 1);
  263. XCTAssertEqualObjects(request.packageDataArray[0].namespaceDigestArray, @[ firebaseDigest ]);
  264. // Test when a namespace's fetched config is a invalid format (non-dictionary), reset the digest.
  265. fetchedConfig = @{@"firebase" : @{@"a" : @"b", @"c" : @"d"}, @"p4" : @[]};
  266. request = [_mockSettings nextRequestWithUserProperties:nil fetchedConfig:fetchedConfig];
  267. XCTAssertEqual(request.packageDataArray.count, 1);
  268. XCTAssertEqualObjects(request.packageDataArray[0].namespaceDigestArray, @[ firebaseDigest ]);
  269. // Test when a namespace's fetched config is a invalid format (non-dictionary), reset the digest.
  270. fetchedConfig = @{@"firebase" : @{@"a" : @"b", @"c" : @"d"}, @"p4" : @"wrong format of config"};
  271. request = [_mockSettings nextRequestWithUserProperties:nil fetchedConfig:fetchedConfig];
  272. XCTAssertEqual(request.packageDataArray.count, 1);
  273. XCTAssertEqualObjects(request.packageDataArray[0].namespaceDigestArray, @[ firebaseDigest ]);
  274. }
  275. @end