diff --git a/DemoSwiftApp/Samples/SamplesForAPI.swift b/DemoSwiftApp/Samples/SamplesForAPI.swift index 2ecb50c1..3d318a92 100644 --- a/DemoSwiftApp/Samples/SamplesForAPI.swift +++ b/DemoSwiftApp/Samples/SamplesForAPI.swift @@ -444,7 +444,7 @@ class SamplesForAPI { userId: "USER_123", attributes: ["country": "us"] ) - let options: [OptimizelyDecideOption] = [.ignoreCmabCache, .ignoreUserProfileService] + let options: [OptimizelyDecideOption] = [.ignoreCmabCache] let decision = await user.decideAsync(key: FLAG_KEY, options: options) print("CMAB decision: \(decision)") } @@ -462,7 +462,7 @@ class SamplesForAPI { userId: "USER_123", attributes: ["country": "us"] ) - let options: [OptimizelyDecideOption] = [.ignoreCmabCache, .ignoreUserProfileService] + let options: [OptimizelyDecideOption] = [.ignoreCmabCache] user.decideAsync(key: FLAG_KEY, options: options, completion: { decision in print("CMAB decision: \(decision)") }) @@ -494,7 +494,7 @@ class SamplesForAPI { userId: "USER_123", attributes: ["country": "us"] ) - let options: [OptimizelyDecideOption] = [.ignoreCmabCache, .ignoreUserProfileService] + let options: [OptimizelyDecideOption] = [.ignoreCmabCache] let decision = await user.decideAsync(key: FLAG_KEY, options: options) print("CMAB decision: \(decision)") } @@ -512,7 +512,7 @@ class SamplesForAPI { userId: "USER_123", attributes: ["country": "us"] ) - let options: [OptimizelyDecideOption] = [.ignoreCmabCache, .ignoreUserProfileService] + let options: [OptimizelyDecideOption] = [.ignoreCmabCache] user.decideAsync(key: FLAG_KEY, options: options, completion: { decision in print("CMAB decision: \(decision)") }) diff --git a/Sources/Implementation/DefaultDecisionService.swift b/Sources/Implementation/DefaultDecisionService.swift index b4fa3ff2..002e66b4 100644 --- a/Sources/Implementation/DefaultDecisionService.swift +++ b/Sources/Implementation/DefaultDecisionService.swift @@ -249,6 +249,8 @@ class DefaultDecisionService: OPTDecisionService { user: user) reasons.merge(audienceResponse.reasons) + var ignoreUPS = false + if audienceResponse.result ?? false { // Acquire bucketingId let bucketingId = getBucketingId(userId: userId, attributes: attributes) @@ -262,6 +264,9 @@ class DefaultDecisionService: OPTDecisionService { options: options) reasons.merge(cmabDecisionResponse.reasons) variationDecision = cmabDecisionResponse.result + + // CMAB decision shouldn't be in the UPS + ignoreUPS = cmabDecisionResponse.result?.variation != nil } else { // bucket user into a variation let decisionResponse = bucketer.bucketExperiment(config: config, @@ -277,7 +282,10 @@ class DefaultDecisionService: OPTDecisionService { let info = LogMessage.userBucketedIntoVariationInExperiment(userId, experiment.key, variation.key) logger.i(info) reasons.addInfo(info) - userProfileTracker?.updateProfile(experiment: experiment, variation: variation) + if !ignoreUPS { + userProfileTracker?.updateProfile(experiment: experiment, variation: variation) + } + } else { let info = LogMessage.userNotBucketedIntoVariation(userId) logger.i(info) diff --git a/Tests/OptimizelyTests-Common/OptimizelyUserContextTests_Decide_CMAB.swift b/Tests/OptimizelyTests-Common/OptimizelyUserContextTests_Decide_CMAB.swift index 84fa2401..eafb6fe9 100644 --- a/Tests/OptimizelyTests-Common/OptimizelyUserContextTests_Decide_CMAB.swift +++ b/Tests/OptimizelyTests-Common/OptimizelyUserContextTests_Decide_CMAB.swift @@ -132,7 +132,7 @@ class OptimizelyUserContextTests_Decide_CMAB: XCTestCase { let user = optimizely.createUserContext(userId: kUserId, attributes: ["gender": "f"]) // Test multiple decisions with decideAsync - user.decideAsync(keys: featureKeys, options: [.ignoreUserProfileService]) { decisions in + user.decideAsync(keys: featureKeys) { decisions in // Verify correct number of decisions were returned XCTAssertEqual(decisions.count, 2) @@ -175,7 +175,7 @@ class OptimizelyUserContextTests_Decide_CMAB: XCTestCase { wait(for: [expectation], timeout: 5) // Increased timeout for reliability } - func testDecideAsync_cmabWithUserProfileCahing() { + func testDecideAsync_cmabIgnoreUPSCacheing() { let expectation1 = XCTestExpectation(description: "First CMAB decision") let expectation2 = XCTestExpectation(description: "Second CMAB decision") @@ -192,17 +192,17 @@ class OptimizelyUserContextTests_Decide_CMAB: XCTestCase { attributes: ["gender": "f", "age": 25] ) - // First decision cache into user profile + user.decideAsync(key: "feature_1") { decision in XCTAssertEqual(decision.variationKey, "a") XCTAssertEqual(self.mockCmabService.decisionCallCount, 1) expectation1.fulfill() - // Second decision (should use cache) + // Second decision, ignore UPS, fetch decision again user.decideAsync(key: "feature_1") { decision in XCTAssertEqual(decision.variationKey, "a") - // Call count should still be 1 (cached) - XCTAssertEqual(self.mockCmabService.decisionCallCount, 1) + // Call count should be increased by 1 + XCTAssertEqual(self.mockCmabService.decisionCallCount, 2) expectation2.fulfill() } } @@ -228,17 +228,17 @@ class OptimizelyUserContextTests_Decide_CMAB: XCTestCase { userId: kUserId, attributes: ["gender": "f", "age": 25] ) - user.decideAsync(key: "feature_1", options: [.ignoreUserProfileService, .ignoreCmabCache]) { decision in + user.decideAsync(key: "feature_1", options: [.ignoreCmabCache]) { decision in XCTAssertEqual(decision.variationKey, "a") XCTAssertTrue(self.mockCmabService.ignoreCacheUsed) exp1.fulfill() } - user.decideAsync(key: "feature_1", options: [.ignoreUserProfileService, .resetCmabCache]) { decision in + user.decideAsync(key: "feature_1", options: [.resetCmabCache]) { decision in XCTAssertEqual(decision.variationKey, "a") XCTAssertTrue(self.mockCmabService.resetCacheCache) exp2.fulfill() } - user.decideAsync(key: "feature_1", options: [.ignoreUserProfileService, .invalidateUserCmabCache]) { decision in + user.decideAsync(key: "feature_1", options: [.invalidateUserCmabCache]) { decision in XCTAssertEqual(decision.variationKey, "a") XCTAssertTrue(self.mockCmabService.invalidateUserCmabCache) exp3.fulfill() @@ -263,7 +263,7 @@ class OptimizelyUserContextTests_Decide_CMAB: XCTestCase { attributes: ["gender": "f", "age": 25] ) - user.decideAsync(key: "feature_1", options: [.ignoreUserProfileService, .includeReasons]) { decision in + user.decideAsync(key: "feature_1", options: [.includeReasons]) { decision in XCTAssertTrue(decision.reasons.contains(LogMessage.cmabFetchFailed("exp_with_audience").reason)) expectation.fulfill() }