RCNConfigContentTest.m 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265
  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 <OCMock/OCMock.h>
  18. #import "FirebaseRemoteConfig/Sources/Private/RCNConfigSettings.h"
  19. #import "FirebaseRemoteConfig/Sources/Public/FIRRemoteConfig.h"
  20. #import "FirebaseRemoteConfig/Sources/RCNConfigContent.h"
  21. #import "FirebaseRemoteConfig/Sources/RCNConfigDBManager.h"
  22. #import "FirebaseRemoteConfig/Sources/RCNConfigValue_Internal.h"
  23. #import "FirebaseRemoteConfig/Tests/Unit/RCNTestUtilities.h"
  24. @interface RCNConfigContent (Testing)
  25. - (void)checkAndWaitForInitialDatabaseLoad;
  26. @end
  27. @interface RCNConfigContentTest : XCTestCase {
  28. NSTimeInterval _expectationTimeout;
  29. RCNConfigContent *_configContent;
  30. NSString *namespaceApp1, *namespaceApp2;
  31. }
  32. @end
  33. /// Unit Tests for RCNConfigContent methods.
  34. @implementation RCNConfigContentTest
  35. - (void)setUp {
  36. [super setUp];
  37. _expectationTimeout = 1.0;
  38. namespaceApp1 = [NSString
  39. stringWithFormat:@"%@:%@", FIRNamespaceGoogleMobilePlatform, RCNTestsDefaultFIRAppName];
  40. namespaceApp2 = [NSString
  41. stringWithFormat:@"%@:%@", FIRNamespaceGoogleMobilePlatform, RCNTestsSecondFIRAppName];
  42. _configContent = [[RCNConfigContent alloc] initWithDBManager:nil];
  43. id partialMock = OCMPartialMock(_configContent);
  44. OCMStub([partialMock checkAndWaitForInitialDatabaseLoad]).andDo(nil);
  45. }
  46. /// Passing in a nil bundleID should not crash the app
  47. - (void)testCrashShouldNotHappenWithoutMainBundleID {
  48. id mockBundle = OCMPartialMock([NSBundle mainBundle]);
  49. OCMStub([mockBundle bundleIdentifier]).andReturn(nil);
  50. _configContent = [[RCNConfigContent alloc] initWithDBManager:nil];
  51. [mockBundle stopMocking];
  52. }
  53. /// Standard test case of receiving updated config from fetch.
  54. - (void)testUpdateConfigContentForMultipleApps {
  55. NSMutableDictionary<NSString *, id> *config1ToSet =
  56. [[NSMutableDictionary alloc] initWithObjectsAndKeys:@"UPDATE", @"state", nil];
  57. NSDictionary<NSString *, NSString *> *entries = @{@"key1" : @"value1", @"key2" : @"value2"};
  58. [config1ToSet setValue:entries forKey:@"entries"];
  59. [_configContent updateConfigContentWithResponse:config1ToSet forNamespace:namespaceApp1];
  60. // Update for second app.
  61. NSMutableDictionary<NSString *, id> *config2ToSet =
  62. [[NSMutableDictionary alloc] initWithObjectsAndKeys:@"UPDATE", @"state", nil];
  63. NSDictionary<NSString *, NSString *> *entries2 = @{@"key11" : @"value11", @"key21" : @"value21"};
  64. [config2ToSet setValue:entries2 forKey:@"entries"];
  65. [_configContent updateConfigContentWithResponse:config2ToSet forNamespace:namespaceApp2];
  66. // Check config for first app.
  67. NSDictionary *fetchedConfig = _configContent.fetchedConfig;
  68. XCTAssertNotNil(fetchedConfig[namespaceApp1][@"key1"]);
  69. XCTAssertEqualObjects([fetchedConfig[namespaceApp1][@"key1"] stringValue], @"value1");
  70. XCTAssertNotNil(fetchedConfig[namespaceApp1][@"key2"]);
  71. XCTAssertEqualObjects([fetchedConfig[namespaceApp1][@"key2"] stringValue], @"value2");
  72. // Check config for second app.
  73. fetchedConfig = _configContent.fetchedConfig;
  74. XCTAssertNotNil(fetchedConfig[namespaceApp2][@"key11"]);
  75. XCTAssertEqualObjects([fetchedConfig[namespaceApp2][@"key11"] stringValue], @"value11");
  76. XCTAssertNotNil(fetchedConfig[namespaceApp2][@"key21"]);
  77. XCTAssertEqualObjects([fetchedConfig[namespaceApp2][@"key21"] stringValue], @"value21");
  78. }
  79. /// Standard test case of receiving updated config from fetch.
  80. - (void)testUpdateConfigContentWithResponse {
  81. NSMutableDictionary *configToSet =
  82. [[NSMutableDictionary alloc] initWithObjectsAndKeys:@"UPDATE", @"state", nil];
  83. NSDictionary *entries = @{@"key1" : @"value1", @"key2" : @"value2"};
  84. [configToSet setValue:entries forKey:@"entries"];
  85. [_configContent updateConfigContentWithResponse:configToSet
  86. forNamespace:FIRNamespaceGoogleMobilePlatform];
  87. NSDictionary *fetchedConfig = _configContent.fetchedConfig;
  88. XCTAssertNotNil(fetchedConfig[FIRNamespaceGoogleMobilePlatform][@"key1"]);
  89. XCTAssertEqualObjects([fetchedConfig[FIRNamespaceGoogleMobilePlatform][@"key1"] stringValue],
  90. @"value1");
  91. XCTAssertNotNil(fetchedConfig[FIRNamespaceGoogleMobilePlatform][@"key2"]);
  92. XCTAssertEqualObjects([fetchedConfig[FIRNamespaceGoogleMobilePlatform][@"key2"] stringValue],
  93. @"value2");
  94. }
  95. /// Verify that fetchedConfig is overwritten for a new fetch call.
  96. - (void)testUpdateConfigContentWithStatusUpdateWithDifferentKeys {
  97. NSMutableDictionary *configToSet =
  98. [[NSMutableDictionary alloc] initWithObjectsAndKeys:@"UPDATE", @"state", nil];
  99. NSDictionary *entries = @{@"key1" : @"value1"};
  100. [configToSet setValue:entries forKey:@"entries"];
  101. [_configContent updateConfigContentWithResponse:configToSet
  102. forNamespace:FIRNamespaceGoogleMobilePlatform];
  103. configToSet = [[NSMutableDictionary alloc] initWithObjectsAndKeys:@"UPDATE", @"state", nil];
  104. entries = @{@"key2" : @"value2", @"key3" : @"value3"};
  105. [configToSet setValue:entries forKey:@"entries"];
  106. [_configContent updateConfigContentWithResponse:configToSet
  107. forNamespace:FIRNamespaceGoogleMobilePlatform];
  108. NSDictionary *fetchedConfig = _configContent.fetchedConfig;
  109. XCTAssertNil(fetchedConfig[FIRNamespaceGoogleMobilePlatform][@"key1"]);
  110. XCTAssertNotNil(fetchedConfig[FIRNamespaceGoogleMobilePlatform][@"key2"]);
  111. XCTAssertEqualObjects([fetchedConfig[FIRNamespaceGoogleMobilePlatform][@"key2"] stringValue],
  112. @"value2");
  113. XCTAssertNotNil(fetchedConfig[FIRNamespaceGoogleMobilePlatform][@"key3"]);
  114. XCTAssertEqualObjects([fetchedConfig[FIRNamespaceGoogleMobilePlatform][@"key3"] stringValue],
  115. @"value3");
  116. }
  117. /// Verify fetchedConfig is available across different namespaces.
  118. - (void)testUpdateConfigContentWithStatusUpdateWithDifferentNamespaces {
  119. NSMutableDictionary *configToSet =
  120. [[NSMutableDictionary alloc] initWithObjectsAndKeys:@"UPDATE", @"state", nil];
  121. NSMutableDictionary *configToSet2 =
  122. [[NSMutableDictionary alloc] initWithObjectsAndKeys:@"UPDATE", @"state", nil];
  123. NSDictionary *entries = @{@"key1" : @"value1"};
  124. NSDictionary *entries2 = @{@"key2" : @"value2"};
  125. [configToSet setValue:entries forKey:@"entries"];
  126. [configToSet2 setValue:entries2 forKey:@"entries"];
  127. [_configContent updateConfigContentWithResponse:configToSet forNamespace:@"namespace_1"];
  128. [_configContent updateConfigContentWithResponse:configToSet2 forNamespace:@"namespace_2"];
  129. [_configContent updateConfigContentWithResponse:configToSet forNamespace:@"namespace_3"];
  130. [_configContent updateConfigContentWithResponse:configToSet2 forNamespace:@"namespace_4"];
  131. NSDictionary *fetchedConfig = _configContent.fetchedConfig;
  132. XCTAssertNotNil(fetchedConfig[@"namespace_1"][@"key1"]);
  133. XCTAssertEqualObjects([fetchedConfig[@"namespace_1"][@"key1"] stringValue], @"value1");
  134. XCTAssertNotNil(fetchedConfig[@"namespace_2"][@"key2"]);
  135. XCTAssertEqualObjects([fetchedConfig[@"namespace_2"][@"key2"] stringValue], @"value2");
  136. XCTAssertNotNil(fetchedConfig[@"namespace_3"][@"key1"]);
  137. XCTAssertEqualObjects([fetchedConfig[@"namespace_3"][@"key1"] stringValue], @"value1");
  138. XCTAssertNotNil(fetchedConfig[@"namespace_4"][@"key2"]);
  139. XCTAssertEqualObjects([fetchedConfig[@"namespace_4"][@"key2"] stringValue], @"value2");
  140. }
  141. - (void)skip_testUpdateConfigContentWithStatusNoChange {
  142. // TODO: Add test case once new eTag based logic is implemented.
  143. }
  144. - (void)skip_testUpdateConfigContentWithRemoveNamespaceStatus {
  145. // TODO: Add test case once new eTag based logic is implemented.
  146. }
  147. - (void)skip_testUpdateConfigContentWithEmptyConfig {
  148. // TODO: Add test case once new eTag based logic is implemented.
  149. }
  150. - (void)testCopyFromDictionaryDoesNotUpdateFetchedConfig {
  151. NSMutableDictionary *configToSet =
  152. [[NSMutableDictionary alloc] initWithObjectsAndKeys:@"UPDATE", @"state", nil];
  153. NSDictionary *entries = @{@"key1" : @"value1", @"key2" : @"value2"};
  154. [configToSet setValue:entries forKey:@"entries"];
  155. [_configContent updateConfigContentWithResponse:configToSet forNamespace:@"dummy_namespace"];
  156. NSDictionary *namespaceToConfig = @{
  157. @"dummy_namespace" : @{
  158. @"new_key" : @"new_value",
  159. }
  160. };
  161. [_configContent copyFromDictionary:namespaceToConfig
  162. toSource:RCNDBSourceFetched
  163. forNamespace:@"dummy_namespace"];
  164. XCTAssertEqual(((NSDictionary *)_configContent.fetchedConfig[@"dummy_namespace"]).count, 2);
  165. XCTAssertEqual(_configContent.activeConfig.count, 0);
  166. XCTAssertEqual(_configContent.defaultConfig.count, 0);
  167. }
  168. - (void)testCopyFromDictionaryUpdatesDefaultConfig {
  169. NSDictionary *embeddedDictionary = @{@"default_embedded_key" : @"default_embedded_Value"};
  170. NSData *dataValue = [NSJSONSerialization dataWithJSONObject:embeddedDictionary
  171. options:NSJSONWritingPrettyPrinted
  172. error:nil];
  173. NSDate *now = [NSDate date];
  174. NSError *error;
  175. NSData *JSONData = [NSJSONSerialization dataWithJSONObject:@{@"key1" : @"value1"}
  176. options:0
  177. error:&error];
  178. NSString *JSONString = [[NSString alloc] initWithData:JSONData encoding:NSUTF8StringEncoding];
  179. NSDictionary *namespaceToConfig = @{
  180. @"default_namespace" : @{
  181. @"new_string_key" : @"new_string_value",
  182. @"new_number_key" : @1234,
  183. @"new_data_key" : dataValue,
  184. @"new_date_key" : now,
  185. @"new_json_key" : JSONString
  186. }
  187. };
  188. [_configContent copyFromDictionary:namespaceToConfig
  189. toSource:RCNDBSourceDefault
  190. forNamespace:@"default_namespace"];
  191. NSDictionary *defaultConfig = _configContent.defaultConfig;
  192. XCTAssertEqual(_configContent.fetchedConfig.count, 0);
  193. XCTAssertEqual(_configContent.activeConfig.count, 0);
  194. XCTAssertNotNil(defaultConfig[@"default_namespace"]);
  195. XCTAssertEqual(((NSDictionary *)defaultConfig[@"default_namespace"]).count, 5);
  196. XCTAssertEqualObjects(@"new_string_value",
  197. [defaultConfig[@"default_namespace"][@"new_string_key"] stringValue]);
  198. XCTAssertEqualObjects(
  199. @1234, [((FIRRemoteConfigValue *)defaultConfig[@"default_namespace"][@"new_number_key"])
  200. numberValue]);
  201. NSDictionary<NSString *, NSString *> *sampleJSON = @{@"key1" : @"value1"};
  202. id configJSON = [(defaultConfig[@"default_namespace"][@"new_json_key"]) JSONValue];
  203. XCTAssertTrue([configJSON isKindOfClass:[NSDictionary class]]);
  204. XCTAssertTrue([sampleJSON isKindOfClass:[NSDictionary class]]);
  205. XCTAssertEqualObjects(sampleJSON, (NSDictionary *)configJSON);
  206. XCTAssertEqualObjects(dataValue,
  207. [defaultConfig[@"default_namespace"][@"new_data_key"] dataValue]);
  208. NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
  209. [dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
  210. NSString *strValueForDate = [dateFormatter stringFromDate:now];
  211. XCTAssertEqualObjects(strValueForDate,
  212. [defaultConfig[@"default_namespace"][@"new_date_key"] stringValue]);
  213. }
  214. - (void)testCopyFromDictionaryUpdatesActiveConfig {
  215. // Active config values must be RCNConfigValue format
  216. NSDictionary *embeddedDictionary = @{@"active_embedded_key" : @"active_embedded_Value"};
  217. NSData *dataValue = [NSJSONSerialization dataWithJSONObject:embeddedDictionary
  218. options:NSJSONWritingPrettyPrinted
  219. error:nil];
  220. NSDictionary *namespaceToConfig = @{
  221. @"dummy_namespace" : @{
  222. @"new_key" : [[FIRRemoteConfigValue alloc] initWithData:dataValue source:-1],
  223. }
  224. };
  225. [_configContent copyFromDictionary:namespaceToConfig
  226. toSource:RCNDBSourceActive
  227. forNamespace:@"dummy_namespace"];
  228. XCTAssertEqual(((NSDictionary *)_configContent.activeConfig[@"dummy_namespace"]).count, 1);
  229. XCTAssertEqual(_configContent.fetchedConfig.count, 0);
  230. XCTAssertEqual(_configContent.defaultConfig.count, 0);
  231. XCTAssertEqualObjects(dataValue,
  232. [_configContent.activeConfig[@"dummy_namespace"][@"new_key"] dataValue]);
  233. }
  234. @end