Skip to content

Commit f2cc7df

Browse files
Add test cases for cmab decision options
1 parent 093e372 commit f2cc7df

File tree

2 files changed

+50
-40
lines changed

2 files changed

+50
-40
lines changed

Sources/Implementation/DefaultDecisionService.swift

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -365,7 +365,7 @@ class DefaultDecisionService: OPTDecisionService {
365365
var decisions = [DecisionResponse<FeatureDecision>]()
366366

367367
for featureFlag in featureFlags {
368-
let flagDecisionResponse = getDecisionForFlag(config: config, featureFlag: featureFlag, user: user, userProfileTracker: profileTracker, opType: opType)
368+
let flagDecisionResponse = getDecisionForFlag(config: config, featureFlag: featureFlag, user: user, userProfileTracker: profileTracker, opType: opType, options: options)
369369
decisions.append(flagDecisionResponse)
370370
}
371371

@@ -398,22 +398,23 @@ class DefaultDecisionService: OPTDecisionService {
398398
let holdoutDecision = getVariationForHoldout(config: config,
399399
flagKey: featureFlag.key,
400400
holdout: holdout,
401-
user: user)
401+
user: user,
402+
options: options)
402403
reasons.merge(holdoutDecision.reasons)
403404
if let variation = holdoutDecision.result {
404405
let featureDicision = FeatureDecision(experiment: holdout, variation: variation, source: Constants.DecisionSource.holdout.rawValue)
405406
return DecisionResponse(result: featureDicision, reasons: reasons)
406407
}
407408
}
408409

409-
let flagExpDecision = getVariationForFeatureExperiments(config: config, featureFlag: featureFlag, user: user, userProfileTracker: userProfileTracker, opType: opType)
410+
let flagExpDecision = getVariationForFeatureExperiments(config: config, featureFlag: featureFlag, user: user, userProfileTracker: userProfileTracker, opType: opType, options: options)
410411
reasons.merge(flagExpDecision.reasons)
411412

412413
if let decision = flagExpDecision.result {
413414
return DecisionResponse(result: decision, reasons: reasons)
414415
}
415416

416-
let rolloutDecision = getVariationForFeatureRollout(config: config, featureFlag: featureFlag, user: user)
417+
let rolloutDecision = getVariationForFeatureRollout(config: config, featureFlag: featureFlag, user: user, options: options)
417418
reasons.merge(rolloutDecision.reasons)
418419

419420
if let decision = rolloutDecision.result {
@@ -631,6 +632,7 @@ class DefaultDecisionService: OPTDecisionService {
631632
let decisionResponse = getVariation(config: config,
632633
experiment: rule,
633634
user: user,
635+
options: options,
634636
opType: opType,
635637
userProfileTracker: userProfileTracker)
636638
let variationResult = decisionResponse.result

Tests/OptimizelyTests-Common/OptimizelyUserContextTests_Decide_CMAB.swift

Lines changed: 44 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ class OptimizelyUserContextTests_Decide_CMAB: XCTestCase {
152152
wait(for: [expectation], timeout: 5) // Increased timeout for reliability
153153
}
154154

155-
func testDecideAsync_cmabWithCaching() {
155+
func testDecideAsync_cmabWithUserProfileCahing() {
156156
let expectation1 = XCTestExpectation(description: "First CMAB decision")
157157
let expectation2 = XCTestExpectation(description: "Second CMAB decision")
158158

@@ -169,7 +169,7 @@ class OptimizelyUserContextTests_Decide_CMAB: XCTestCase {
169169
attributes: ["gender": "f", "age": 25]
170170
)
171171

172-
// First decision
172+
// First decision cache into user profile
173173
user.decideAsync(key: "feature_1") { decision in
174174
XCTAssertEqual(decision.variationKey, "a")
175175
XCTAssertEqual(self.mockCmabService.decisionCallCount, 1)
@@ -186,38 +186,44 @@ class OptimizelyUserContextTests_Decide_CMAB: XCTestCase {
186186

187187
wait(for: [expectation1, expectation2], timeout: 1)
188188
}
189-
// FixMe: Need to fix the test case
190-
// func testDecideAsync_cmabWithIgnoreCache() {
191-
// let expectation = XCTestExpectation(description: "CMAB ignore cache")
192-
// // Set up the CMAB experiment
193-
// let cmab: Cmab = try! OTUtils.model(from: ["trafficAllocation": 10000, "attributeIds": ["10389729780"]])
194-
// var experiments = optimizely.config!.project.experiments
195-
// experiments[0].cmab = cmab
196-
// optimizely.config?.project.experiments = experiments
197-
// mockCmabService.variationId = "10389729780" // corresponds to variation "a"
198-
//
199-
// // Create user with attributes that match CMAB experiment
200-
// let user = optimizely.createUserContext(
201-
// userId: kUserId,
202-
// attributes: ["gender": "f", "age": 25]
203-
// )
204-
//
205-
// // Make decision with ignoreCmabCache option
206-
// user.decideAsync(
207-
// key: "feature_1",
208-
// options: [.ignoreCmabCache, .ignoreUserProfileService]
209-
// ) { decision in
210-
// XCTAssertEqual(decision.variationKey, "a")
211-
// XCTAssertTrue(self.mockCmabService.decisionCalled)
212-
// XCTAssertTrue(self.mockCmabService.ignoreCacheUsed)
213-
// expectation.fulfill()
214-
// }
215-
//
216-
// wait(for: [expectation], timeout: 1)
217-
// }
218-
219-
220189

190+
func testDecideAsync_cmabCacheOptions() {
191+
let exp1 = XCTestExpectation(description: "First call")
192+
let exp2 = XCTestExpectation(description: "Second call")
193+
let exp3 = XCTestExpectation(description: "Third call")
194+
195+
196+
// Set up the CMAB experiment
197+
let cmab: Cmab = try! OTUtils.model(from: ["trafficAllocation": 10000, "attributeIds": ["10389729780"]])
198+
var experiments = optimizely.config!.project.experiments
199+
experiments[0].cmab = cmab
200+
optimizely.config?.project.experiments = experiments
201+
mockCmabService.variationId = "10389729780" // corresponds to variation "a"
202+
203+
// Create user with attributes that match CMAB experiment
204+
let user = optimizely.createUserContext(
205+
userId: kUserId,
206+
attributes: ["gender": "f", "age": 25]
207+
)
208+
user.decideAsync(key: "feature_1", options: [.ignoreUserProfileService, .ignoreCmabCache]) { decision in
209+
XCTAssertEqual(decision.variationKey, "a")
210+
XCTAssertTrue(self.mockCmabService.ignoreCacheUsed)
211+
exp1.fulfill()
212+
}
213+
user.decideAsync(key: "feature_1", options: [.ignoreUserProfileService, .resetCmabCache]) { decision in
214+
XCTAssertEqual(decision.variationKey, "a")
215+
XCTAssertTrue(self.mockCmabService.resetCacheCache)
216+
exp2.fulfill()
217+
}
218+
user.decideAsync(key: "feature_1", options: [.ignoreUserProfileService, .invalidateUserCmabCache]) { decision in
219+
XCTAssertEqual(decision.variationKey, "a")
220+
XCTAssertTrue(self.mockCmabService.invalidateUserCmabCache)
221+
exp3.fulfill()
222+
}
223+
wait(for: [exp1, exp2, exp3], timeout: 1)
224+
225+
}
226+
221227
func testDecideAsync_cmabError() {
222228
let expectation = XCTestExpectation(description: "CMAB error handling")
223229
// Set up the CMAB experiment
@@ -244,25 +250,27 @@ class OptimizelyUserContextTests_Decide_CMAB: XCTestCase {
244250

245251
}
246252

247-
248253
fileprivate class MockCmabService: DefaultCmabService {
249254
var variationId: String?
250255
var error: Error?
251256
var decisionCalled = false
252257
var decisionCallCount = 0
253258
var lastRuleId: String?
254259
var ignoreCacheUsed = false
260+
var resetCacheCache = false
261+
var invalidateUserCmabCache = false
255262

256263
init() {
257264
super.init(cmabClient: DefaultCmabClient(), cmabCache: LruCache(size: 10, timeoutInSecs: 10))
258265
}
259266

260267
override func getDecision(config: ProjectConfig, userContext: OptimizelyUserContext, ruleId: String, options: [OptimizelyDecideOption]) -> Result<CmabDecision, any Error> {
261268
decisionCalled = true
262-
decisionCallCount += 1
263269
lastRuleId = ruleId
264270
ignoreCacheUsed = options.contains(.ignoreCmabCache)
265-
271+
resetCacheCache = options.contains(.resetCmabCache)
272+
invalidateUserCmabCache = options.contains(.invalidateUserCmabCache)
273+
decisionCallCount += 1
266274
if let error = error {
267275
return .failure(error)
268276
}

0 commit comments

Comments
 (0)