RCNConfigSettingsTest.m 17 KB

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