RCNConfigContentTest.m 13 KB

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