Skip to content

Commit 5d4b97a

Browse files
wip: add decision listener test cases for holdouts
1 parent b3e855e commit 5d4b97a

File tree

2 files changed

+208
-0
lines changed

2 files changed

+208
-0
lines changed

OptimizelySwiftSDK.xcodeproj/project.pbxproj

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2046,6 +2046,8 @@
20462046
989428BC2DBFA431008BA1C8 /* MockBucketer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 989428B22DBFA431008BA1C8 /* MockBucketer.swift */; };
20472047
989428BD2DBFA431008BA1C8 /* MockBucketer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 989428B22DBFA431008BA1C8 /* MockBucketer.swift */; };
20482048
989428BE2DBFA431008BA1C8 /* MockBucketer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 989428B22DBFA431008BA1C8 /* MockBucketer.swift */; };
2049+
989428C02DBFDCE5008BA1C8 /* DecisionListenerTest_Holdouts.swift in Sources */ = {isa = PBXBuildFile; fileRef = 989428BF2DBFDCE5008BA1C8 /* DecisionListenerTest_Holdouts.swift */; };
2050+
989428C12DBFDCE5008BA1C8 /* DecisionListenerTest_Holdouts.swift in Sources */ = {isa = PBXBuildFile; fileRef = 989428BF2DBFDCE5008BA1C8 /* DecisionListenerTest_Holdouts.swift */; };
20492051
98AC97E22DAE4579001405DD /* HoldoutConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98AC97E12DAE4579001405DD /* HoldoutConfig.swift */; };
20502052
98AC97E32DAE4579001405DD /* HoldoutConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98AC97E12DAE4579001405DD /* HoldoutConfig.swift */; };
20512053
98AC97E42DAE4579001405DD /* HoldoutConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98AC97E12DAE4579001405DD /* HoldoutConfig.swift */; };
@@ -2516,6 +2518,7 @@
25162518
984FE5102CC8AA88004F6F41 /* UserProfileTracker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserProfileTracker.swift; sourceTree = "<group>"; };
25172519
987F11D92AF3F56F0083D3F9 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = "<group>"; };
25182520
989428B22DBFA431008BA1C8 /* MockBucketer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockBucketer.swift; sourceTree = "<group>"; };
2521+
989428BF2DBFDCE5008BA1C8 /* DecisionListenerTest_Holdouts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DecisionListenerTest_Holdouts.swift; sourceTree = "<group>"; };
25192522
98AC97E12DAE4579001405DD /* HoldoutConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HoldoutConfig.swift; sourceTree = "<group>"; };
25202523
98AC97F22DAE9685001405DD /* HoldoutConfigTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HoldoutConfigTests.swift; sourceTree = "<group>"; };
25212524
98AC98452DB7B762001405DD /* BucketTests_HoldoutToVariation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BucketTests_HoldoutToVariation.swift; sourceTree = "<group>"; };
@@ -3033,6 +3036,7 @@
30333036
6E75199622C5211100B2B157 /* DatafileHandlerTests.swift */,
30343037
6E75199822C5211100B2B157 /* DataStoreTests.swift */,
30353038
6E75199022C5211100B2B157 /* DecisionListenerTests.swift */,
3039+
989428BF2DBFDCE5008BA1C8 /* DecisionListenerTest_Holdouts.swift */,
30363040
6E981FC1232C363300FADDD6 /* DecisionListenerTests_Datafile.swift */,
30373041
6E27ECBD266FD78600B4A6D4 /* DecisionReasonsTests.swift */,
30383042
6E75198022C5211100B2B157 /* DecisionServiceTests_Experiments.swift */,
@@ -4910,6 +4914,7 @@
49104914
6EC6DD4C24ABF89B0017D296 /* OptimizelyUserContext.swift in Sources */,
49114915
8464087D28130D3200CCF97D /* Integration.swift in Sources */,
49124916
6E7517E922C520D400B2B157 /* DefaultDecisionService.swift in Sources */,
4917+
989428C02DBFDCE5008BA1C8 /* DecisionListenerTest_Holdouts.swift in Sources */,
49134918
6E5D12282638DDF4000ABFC3 /* MockEventDispatcher.swift in Sources */,
49144919
6E9B116A22C5487100C22D81 /* BucketTests_Base.swift in Sources */,
49154920
6E9B115F22C5487100C22D81 /* MurmurTests.swift in Sources */,
@@ -5192,6 +5197,7 @@
51925197
6E7517CB22C520D400B2B157 /* DefaultBucketer.swift in Sources */,
51935198
845945C1287758A000D13E11 /* OdpConfig.swift in Sources */,
51945199
8464087528130D3200CCF97D /* Integration.swift in Sources */,
5200+
989428C12DBFDCE5008BA1C8 /* DecisionListenerTest_Holdouts.swift in Sources */,
51955201
6E5D12202638DDF4000ABFC3 /* MockEventDispatcher.swift in Sources */,
51965202
6E9B115022C5486E00C22D81 /* BucketTests_Base.swift in Sources */,
51975203
6E9B114522C5486E00C22D81 /* MurmurTests.swift in Sources */,
Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
//
2+
// Copyright 2022, Optimizely, Inc. and contributors
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+
17+
import XCTest
18+
19+
class DecisionListenerTests_Holdouts: XCTestCase {
20+
let kUserId = "11111"
21+
var optimizely: OptimizelyClient!
22+
var notificationCenter: OPTNotificationCenter!
23+
var eventDispatcher = MockEventDispatcher()
24+
25+
var kAttributesCountryMatch: [String: Any] = ["country": "US"]
26+
var kAttributesCountryNotMatch: [String: Any] = ["country": "ca"]
27+
28+
let kFeatureKey = "feature_1"
29+
30+
let kVariableKeyString = "s_foo"
31+
let kVariableKeyInt = "i_42"
32+
let kVariableKeyDouble = "d_4_2"
33+
let kVariableKeyBool = "b_true"
34+
let kVariableKeyJSON = "j_1"
35+
36+
let kVariableValueString = "foo"
37+
let kVariableValueInt = 42
38+
let kVariableValueDouble = 4.2
39+
let kVariableValueBool = true
40+
41+
var sampleHoldout: [String: Any] {
42+
return [
43+
"status": "Running",
44+
"id": "id_holdout",
45+
"key": "key_holdout",
46+
"layerId": "10420273888",
47+
"trafficAllocation": [
48+
["entityId": "id_holdout_variation", "endOfRange": 500]
49+
],
50+
"audienceIds": [],
51+
"variations": [
52+
[
53+
"variables": [],
54+
"id": "id_holdout_variation",
55+
"key": "key_holdout_variation"
56+
]
57+
],
58+
"includedFlags": [],
59+
"excludedFlags": []
60+
]
61+
}
62+
63+
override func setUp() {
64+
super.setUp()
65+
66+
optimizely = OptimizelyClient(sdkKey: OTUtils.randomSdkKey,
67+
eventDispatcher: eventDispatcher,
68+
userProfileService: OTUtils.createClearUserProfileService())
69+
70+
try! optimizely.start(datafile: OTUtils.loadJSONDatafile("decide_datafile")!)
71+
72+
var holdout = try! OTUtils.model(from: sampleHoldout) as Holdout
73+
// Audience "13389130056" requires "country" = "US"
74+
holdout.audienceIds = ["13389130056"]
75+
76+
let mockDecisionService = DefaultDecisionService(userProfileService: OTUtils.createClearUserProfileService(), bucketer: MockBucketer(mockBucketValue: 400))
77+
optimizely.decisionService = mockDecisionService
78+
optimizely.config!.project.holdouts = [holdout]
79+
80+
self.notificationCenter = self.optimizely.notificationCenter!
81+
}
82+
83+
func testDecisionListenerDecideWithUserInHoldout() {
84+
let exp = expectation(description: "x")
85+
86+
let user = optimizely.createUserContext(userId: kUserId, attributes: kAttributesCountryMatch)
87+
88+
notificationCenter.clearAllNotificationListeners()
89+
_ = notificationCenter.addDecisionNotificationListener { (type, userId, attributes, decisionInfo) in
90+
XCTAssertEqual(type, Constants.DecisionType.flag.rawValue)
91+
XCTAssertEqual(userId, user.userId)
92+
XCTAssertEqual(attributes!["country"] as! String, "US")
93+
94+
XCTAssertEqual(decisionInfo[Constants.DecisionInfoKeys.flagKey] as! String, self.kFeatureKey)
95+
XCTAssertEqual(decisionInfo[Constants.DecisionInfoKeys.enabled] as! Bool, false)
96+
97+
XCTAssertNotNil(decisionInfo[Constants.DecisionInfoKeys.variables])
98+
let variableValues = decisionInfo[Constants.DecisionInfoKeys.variables] as! [String: Any]
99+
100+
XCTAssertEqual(variableValues[self.kVariableKeyString] as! String, "foo")
101+
XCTAssertEqual(variableValues[self.kVariableKeyInt] as! Int, self.kVariableValueInt)
102+
XCTAssertEqual(variableValues[self.kVariableKeyDouble] as! Double, self.kVariableValueDouble)
103+
XCTAssertEqual(variableValues[self.kVariableKeyBool] as! Bool, self.kVariableValueBool)
104+
let jsonMap = (variableValues[self.kVariableKeyJSON] as! [String: Any])
105+
XCTAssertEqual(jsonMap["value"] as! Int, 1)
106+
107+
XCTAssertEqual(decisionInfo[Constants.DecisionInfoKeys.variationKey] as! String, "key_holdout_variation")
108+
XCTAssertEqual(decisionInfo[Constants.DecisionInfoKeys.ruleKey] as! String, "key_holdout")
109+
XCTAssertNotNil(decisionInfo[Constants.DecisionInfoKeys.reasons])
110+
XCTAssertEqual(decisionInfo[Constants.DecisionInfoKeys.decisionEventDispatched] as! Bool, true)
111+
exp.fulfill()
112+
}
113+
114+
_ = user.decide(key: self.kFeatureKey)
115+
wait(for: [exp], timeout: 1)
116+
}
117+
118+
func testDecisionListener_DecisionEventDispatched_withSendFlagDecisions() {
119+
let user = optimizely.createUserContext(userId: kUserId, attributes: kAttributesCountryMatch)
120+
121+
// (1) sendFlagDecision = false. feature-test.
122+
123+
optimizely.config?.project.sendFlagDecisions = false
124+
125+
var exp = expectation(description: "x")
126+
notificationCenter.clearAllNotificationListeners()
127+
_ = notificationCenter.addDecisionNotificationListener { (type, userId, attributes, decisionInfo) in
128+
XCTAssertEqual(decisionInfo[Constants.DecisionInfoKeys.decisionEventDispatched] as! Bool, false)
129+
exp.fulfill()
130+
}
131+
_ = user.decide(key: kFeatureKey)
132+
wait(for: [exp], timeout: 1)
133+
134+
// (2) sendFlagDecision = true
135+
136+
optimizely.config?.project.sendFlagDecisions = true
137+
138+
exp = expectation(description: "x")
139+
notificationCenter.clearAllNotificationListeners()
140+
_ = notificationCenter.addDecisionNotificationListener { (type, userId, attributes, decisionInfo) in
141+
XCTAssertEqual(decisionInfo[Constants.DecisionInfoKeys.decisionEventDispatched] as! Bool, true)
142+
exp.fulfill()
143+
}
144+
_ = user.decide(key: kFeatureKey)
145+
wait(for: [exp], timeout: 1)
146+
}
147+
148+
func testDecisionListenerDecide_disableDecisionEvent() {
149+
let user = optimizely.createUserContext(userId: kUserId, attributes:["country": "US"])
150+
151+
// (1) default (send-decision-event)
152+
153+
var exp = expectation(description: "x")
154+
155+
notificationCenter.clearAllNotificationListeners()
156+
_ = notificationCenter.addDecisionNotificationListener { (type, userId, attributes, decisionInfo) in
157+
XCTAssertEqual(decisionInfo[Constants.DecisionInfoKeys.decisionEventDispatched] as! Bool, true)
158+
exp.fulfill()
159+
}
160+
_ = user.decide(key: kFeatureKey)
161+
wait(for: [exp], timeout: 1)
162+
163+
// (2) disable-decision-event)
164+
165+
exp = expectation(description: "x")
166+
167+
notificationCenter.clearAllNotificationListeners()
168+
_ = notificationCenter.addDecisionNotificationListener { (type, userId, attributes, decisionInfo) in
169+
XCTAssertEqual(decisionInfo[Constants.DecisionInfoKeys.decisionEventDispatched] as! Bool, false)
170+
exp.fulfill()
171+
}
172+
_ = user.decide(key: kFeatureKey, options: [.disableDecisionEvent])
173+
wait(for: [exp], timeout: 1)
174+
}
175+
176+
func testDecisionListenerDecideAll() {
177+
let user = optimizely.createUserContext(userId: kUserId, attributes:["country": "US"])
178+
179+
var count = 0
180+
notificationCenter.clearAllNotificationListeners()
181+
_ = notificationCenter.addDecisionNotificationListener { (type, userId, attributes, decisionInfo) in
182+
XCTAssertEqual(type, Constants.DecisionType.flag.rawValue)
183+
XCTAssertEqual(userId, user.userId)
184+
XCTAssertEqual(attributes!["country"] as! String, "US")
185+
186+
XCTAssertNotNil(decisionInfo[Constants.DecisionInfoKeys.flagKey])
187+
XCTAssertNotNil(decisionInfo[Constants.DecisionInfoKeys.enabled])
188+
count += 1
189+
}
190+
191+
_ = user.decideAll()
192+
sleep(1)
193+
194+
XCTAssertEqual(count, 3)
195+
}
196+
197+
// func testFlagWithHoldout() {
198+
// // global
199+
// // include
200+
// // exclude
201+
// }
202+
}

0 commit comments

Comments
 (0)