RCNPersonalizationTest.m 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  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.h"
  18. #import "FirebaseCore/Sources/Private/FirebaseCoreInternal.h"
  19. #import "FirebaseRemoteConfig/Sources/Private/FIRRemoteConfig_Private.h"
  20. #import "FirebaseRemoteConfig/Sources/Private/RCNConfigFetch.h"
  21. #import "FirebaseRemoteConfig/Sources/RCNConfigConstants.h"
  22. #import "FirebaseRemoteConfig/Sources/RCNConfigDBManager.h"
  23. #import "FirebaseRemoteConfig/Sources/RCNConfigValue_Internal.h"
  24. #import "FirebaseRemoteConfig/Sources/RCNPersonalization.h"
  25. #import "FirebaseRemoteConfig/Tests/Unit/RCNTestUtilities.h"
  26. #import "Interop/Analytics/Public/FIRAnalyticsInterop.h"
  27. @interface RCNConfigFetch (ForTest)
  28. - (NSURLSessionDataTask *)URLSessionDataTaskWithContent:(NSData *)content
  29. completionHandler:
  30. (RCNConfigFetcherCompletion)fetcherCompletion;
  31. - (void)fetchWithUserProperties:(NSDictionary *)userProperties
  32. completionHandler:(FIRRemoteConfigFetchCompletion)completionHandler;
  33. @end
  34. @interface RCNPersonalizationTest : XCTestCase {
  35. NSDictionary *_configContainer;
  36. NSMutableArray<NSDictionary *> *_fakeLogs;
  37. id _analyticsMock;
  38. RCNPersonalization *_personalization;
  39. FIRRemoteConfig *_configInstance;
  40. }
  41. @end
  42. @implementation RCNPersonalizationTest
  43. - (void)setUp {
  44. [super setUp];
  45. _configContainer = @{
  46. RCNFetchResponseKeyEntries : @{
  47. @"key1" : [[FIRRemoteConfigValue alloc]
  48. initWithData:[@"value1" dataUsingEncoding:NSUTF8StringEncoding]
  49. source:FIRRemoteConfigSourceRemote],
  50. @"key2" : [[FIRRemoteConfigValue alloc]
  51. initWithData:[@"value2" dataUsingEncoding:NSUTF8StringEncoding]
  52. source:FIRRemoteConfigSourceRemote],
  53. @"key3" : [[FIRRemoteConfigValue alloc]
  54. initWithData:[@"value3" dataUsingEncoding:NSUTF8StringEncoding]
  55. source:FIRRemoteConfigSourceRemote]
  56. },
  57. RCNFetchResponseKeyPersonalizationMetadata :
  58. @{@"key1" : @{kPersonalizationId : @"id1"}, @"key2" : @{kPersonalizationId : @"id2"}}
  59. };
  60. _fakeLogs = [[NSMutableArray alloc] init];
  61. _analyticsMock = OCMProtocolMock(@protocol(FIRAnalyticsInterop));
  62. OCMStub([_analyticsMock logEventWithOrigin:kAnalyticsOriginPersonalization
  63. name:kAnalyticsPullEvent
  64. parameters:[OCMArg isKindOfClass:[NSDictionary class]]])
  65. .andDo(^(NSInvocation *invocation) {
  66. __unsafe_unretained NSDictionary *bundle;
  67. [invocation getArgument:&bundle atIndex:4];
  68. [self->_fakeLogs addObject:bundle];
  69. });
  70. _personalization = [[RCNPersonalization alloc] initWithAnalytics:_analyticsMock];
  71. // Always remove the database at the start of testing.
  72. NSString *DBPath = [RCNTestUtilities remoteConfigPathForTestDatabase];
  73. id DBMock = OCMClassMock([RCNConfigDBManager class]);
  74. OCMStub([DBMock remoteConfigPathForDatabase]).andReturn(DBPath);
  75. RCNConfigContent *configContent = [[RCNConfigContent alloc] initWithDBManager:DBMock];
  76. // Create a mock FIRRemoteConfig instance.
  77. _configInstance = OCMPartialMock([[FIRRemoteConfig alloc]
  78. initWithAppName:@"testApp"
  79. FIROptions:[[FIROptions alloc] initWithGoogleAppID:@"1:123:ios:test"
  80. GCMSenderID:@"testSender"]
  81. namespace:@"namespace"
  82. DBManager:DBMock
  83. configContent:configContent
  84. analytics:_analyticsMock]);
  85. [_configInstance setValue:[RCNPersonalizationTest mockFetchRequest] forKey:@"_configFetch"];
  86. }
  87. - (void)tearDown {
  88. [super tearDown];
  89. }
  90. - (void)testNonPersonalizationKey {
  91. [_fakeLogs removeAllObjects];
  92. [_personalization logArmActive:@"key3" config:_configContainer];
  93. OCMVerify(never(),
  94. [_analyticsMock logEventWithOrigin:kAnalyticsOriginPersonalization
  95. name:kAnalyticsPullEvent
  96. parameters:[OCMArg isKindOfClass:[NSDictionary class]]]);
  97. XCTAssertEqual([_fakeLogs count], 0);
  98. }
  99. - (void)testSinglePersonalizationKey {
  100. [_fakeLogs removeAllObjects];
  101. [_personalization logArmActive:@"key1" config:_configContainer];
  102. OCMVerify(times(1),
  103. [_analyticsMock logEventWithOrigin:kAnalyticsOriginPersonalization
  104. name:kAnalyticsPullEvent
  105. parameters:[OCMArg isKindOfClass:[NSDictionary class]]]);
  106. XCTAssertEqual([_fakeLogs count], 1);
  107. NSDictionary *params = @{kArmKey : @"id1", kArmValue : @"value1"};
  108. XCTAssertEqualObjects(_fakeLogs[0], params);
  109. }
  110. - (void)testMultiplePersonalizationKeys {
  111. [_fakeLogs removeAllObjects];
  112. [_personalization logArmActive:@"key1" config:_configContainer];
  113. [_personalization logArmActive:@"key2" config:_configContainer];
  114. OCMVerify(times(2),
  115. [_analyticsMock logEventWithOrigin:kAnalyticsOriginPersonalization
  116. name:kAnalyticsPullEvent
  117. parameters:[OCMArg isKindOfClass:[NSDictionary class]]]);
  118. XCTAssertEqual([_fakeLogs count], 2);
  119. NSDictionary *params1 = @{kArmKey : @"id1", kArmValue : @"value1"};
  120. XCTAssertEqualObjects(_fakeLogs[0], params1);
  121. NSDictionary *params2 = @{kArmKey : @"id2", kArmValue : @"value2"};
  122. XCTAssertEqualObjects(_fakeLogs[1], params2);
  123. }
  124. - (void)testRemoteConfigIntegration {
  125. [_fakeLogs removeAllObjects];
  126. FIRRemoteConfigFetchAndActivateCompletion fetchAndActivateCompletion =
  127. ^void(FIRRemoteConfigFetchAndActivateStatus status, NSError *error) {
  128. OCMVerify(times(2), [self->_analyticsMock
  129. logEventWithOrigin:kAnalyticsOriginPersonalization
  130. name:kAnalyticsPullEvent
  131. parameters:[OCMArg isKindOfClass:[NSDictionary class]]]);
  132. XCTAssertEqual([self->_fakeLogs count], 2);
  133. NSDictionary *params1 = @{kArmKey : @"id1", kArmValue : @"value1"};
  134. XCTAssertEqualObjects(self->_fakeLogs[0], params1);
  135. NSDictionary *params2 = @{kArmKey : @"id2", kArmValue : @"value2"};
  136. XCTAssertEqualObjects(self->_fakeLogs[1], params2);
  137. };
  138. [_configInstance fetchAndActivateWithCompletionHandler:fetchAndActivateCompletion];
  139. [_configInstance configValueForKey:@"key1"];
  140. [_configInstance configValueForKey:@"key2"];
  141. }
  142. + (id)mockFetchRequest {
  143. id configFetch = OCMClassMock([RCNConfigFetch class]);
  144. OCMStub([configFetch fetchConfigWithExpirationDuration:0 completionHandler:OCMOCK_ANY])
  145. .ignoringNonObjectArgs()
  146. .andDo(^(NSInvocation *invocation) {
  147. __unsafe_unretained FIRRemoteConfigFetchCompletion handler;
  148. [invocation getArgument:&handler atIndex:3];
  149. [configFetch fetchWithUserProperties:[[NSDictionary alloc] init] completionHandler:handler];
  150. });
  151. OCMExpect([configFetch
  152. URLSessionDataTaskWithContent:[OCMArg any]
  153. completionHandler:[RCNPersonalizationTest mockResponseHandler]])
  154. .andReturn(nil);
  155. return configFetch;
  156. }
  157. + (id)mockResponseHandler {
  158. NSDictionary *response = @{
  159. RCNFetchResponseKeyState : RCNFetchResponseKeyStateUpdate,
  160. RCNFetchResponseKeyEntries : @{@"key1" : @"value1", @"key2" : @"value2", @"key3" : @"value3"},
  161. RCNFetchResponseKeyPersonalizationMetadata :
  162. @{@"key1" : @{kPersonalizationId : @"id1"}, @"key2" : @{kPersonalizationId : @"id2"}}
  163. };
  164. return [OCMArg invokeBlockWithArgs:[NSJSONSerialization dataWithJSONObject:response
  165. options:0
  166. error:nil],
  167. [[NSHTTPURLResponse alloc]
  168. initWithURL:[NSURL URLWithString:@"https://firebase.com"]
  169. statusCode:200
  170. HTTPVersion:nil
  171. headerFields:@{@"etag" : @"etag1"}],
  172. [NSNull null], nil];
  173. }
  174. @end