diff --git a/Sources/Implementation/DefaultDecisionService.swift b/Sources/Implementation/DefaultDecisionService.swift index f2210635..7036d5cd 100644 --- a/Sources/Implementation/DefaultDecisionService.swift +++ b/Sources/Implementation/DefaultDecisionService.swift @@ -658,18 +658,20 @@ class DefaultDecisionService: OPTDecisionService { } // check local holdouts targeting this rule - let localHoldouts = config.getHoldoutsForRule(ruleId: rule.id) - for holdout in localHoldouts { - let holdoutDecision = getVariationForHoldout(config: config, - flagKey: flagKey, - holdout: holdout, - user: user, - options: options) - reasons.merge(holdoutDecision.reasons) - if let variation = holdoutDecision.result { - // User is in holdout — return holdout variation immediately, skip this rule - let variationDecision = VariationDecision(variation: variation, holdout: holdout) - return DecisionResponse(result: variationDecision, reasons: reasons) + if FeatureGates.localHoldouts { + let localHoldouts = config.getHoldoutsForRule(ruleId: rule.id) + for holdout in localHoldouts { + let holdoutDecision = getVariationForHoldout(config: config, + flagKey: flagKey, + holdout: holdout, + user: user, + options: options) + reasons.merge(holdoutDecision.reasons) + if let variation = holdoutDecision.result { + // User is in holdout — return holdout variation immediately, skip this rule + let variationDecision = VariationDecision(variation: variation, holdout: holdout) + return DecisionResponse(result: variationDecision, reasons: reasons) + } } } @@ -716,18 +718,20 @@ class DefaultDecisionService: OPTDecisionService { } // check local holdouts targeting this delivery rule - let localHoldouts = config.getHoldoutsForRule(ruleId: rule.id) - for holdout in localHoldouts { - let holdoutDecision = getVariationForHoldout(config: config, - flagKey: flagKey, - holdout: holdout, - user: user, - options: options) - reasons.merge(holdoutDecision.reasons) - if let variation = holdoutDecision.result { - // User is in holdout — return holdout variation with holdout info - let decision = DeliveryRuleDecision(variation: variation, skipToEveryoneElse: skipToEveryoneElse, holdout: holdout) - return DecisionResponse(result: decision, reasons: reasons) + if FeatureGates.localHoldouts { + let localHoldouts = config.getHoldoutsForRule(ruleId: rule.id) + for holdout in localHoldouts { + let holdoutDecision = getVariationForHoldout(config: config, + flagKey: flagKey, + holdout: holdout, + user: user, + options: options) + reasons.merge(holdoutDecision.reasons) + if let variation = holdoutDecision.result { + // User is in holdout — return holdout variation with holdout info + let decision = DeliveryRuleDecision(variation: variation, skipToEveryoneElse: skipToEveryoneElse, holdout: holdout) + return DecisionResponse(result: decision, reasons: reasons) + } } } diff --git a/Sources/Utils/Constants.swift b/Sources/Utils/Constants.swift index 0c106b2a..db04f22c 100644 --- a/Sources/Utils/Constants.swift +++ b/Sources/Utils/Constants.swift @@ -16,6 +16,10 @@ import Foundation +struct FeatureGates { + static var localHoldouts = false +} + struct Constants { struct Attributes { static let reservedBucketIdAttribute = "$opt_bucketing_id" diff --git a/Tests/OptimizelyTests-Common/DecisionListenerTest_Holdouts.swift b/Tests/OptimizelyTests-Common/DecisionListenerTest_Holdouts.swift index 0470af28..c5bf8e56 100644 --- a/Tests/OptimizelyTests-Common/DecisionListenerTest_Holdouts.swift +++ b/Tests/OptimizelyTests-Common/DecisionListenerTest_Holdouts.swift @@ -61,13 +61,14 @@ class DecisionListenerTests_Holdouts: XCTestCase { override func setUp() { super.setUp() - + FeatureGates.localHoldouts = true + optimizely = OptimizelyClient(sdkKey: OTUtils.randomSdkKey, eventDispatcher: eventDispatcher, userProfileService: OTUtils.createClearUserProfileService()) - + try! optimizely.start(datafile: OTUtils.loadJSONDatafile("decide_datafile")!) - + var holdout = try! OTUtils.model(from: sampleHoldout) as Holdout // Audience "13389130056" requires "country" = "US" holdout.audienceIds = ["13389130056"] @@ -79,6 +80,11 @@ class DecisionListenerTests_Holdouts: XCTestCase { self.notificationCenter = self.optimizely.notificationCenter! } + + override func tearDown() { + FeatureGates.localHoldouts = false + super.tearDown() + } func testDecisionListenerDecideWithUserInHoldout() { let exp = expectation(description: "x") diff --git a/Tests/OptimizelyTests-Common/DecisionServiceTests_Holdouts.swift b/Tests/OptimizelyTests-Common/DecisionServiceTests_Holdouts.swift index 31f2e857..c6028120 100644 --- a/Tests/OptimizelyTests-Common/DecisionServiceTests_Holdouts.swift +++ b/Tests/OptimizelyTests-Common/DecisionServiceTests_Holdouts.swift @@ -189,7 +189,7 @@ class DecisionServiceTests_Holdouts: XCTestCase { override func setUp() { super.setUp() - + FeatureGates.localHoldouts = true self.optimizely = OTUtils.createOptimizely(datafileName: "empty_datafile", clearUserProfileService: true) self.config = self.optimizely.config! @@ -212,6 +212,11 @@ class DecisionServiceTests_Holdouts: XCTestCase { self.config.holdoutConfig.allHoldouts = [holdout] } + override func tearDown() { + FeatureGates.localHoldouts = false + super.tearDown() + } + } // MARK: - Test doesMeetAudienceConditions() diff --git a/Tests/OptimizelyTests-Common/DecisionServiceTests_LocalHoldouts.swift b/Tests/OptimizelyTests-Common/DecisionServiceTests_LocalHoldouts.swift index 0d39787a..e5d64fd1 100644 --- a/Tests/OptimizelyTests-Common/DecisionServiceTests_LocalHoldouts.swift +++ b/Tests/OptimizelyTests-Common/DecisionServiceTests_LocalHoldouts.swift @@ -52,6 +52,7 @@ class DecisionServiceTests_LocalHoldouts: XCTestCase { override func setUp() { super.setUp() + FeatureGates.localHoldouts = true // Load a real datafile for testing optimizely = OTUtils.createOptimizely(datafileName: "decide_datafile", @@ -60,6 +61,11 @@ class DecisionServiceTests_LocalHoldouts: XCTestCase { decisionService = optimizely.decisionService as? DefaultDecisionService } + override func tearDown() { + FeatureGates.localHoldouts = false + super.tearDown() + } + // MARK: - Global Holdouts Tests func testGlobalHoldout_EvaluatedBeforeAllRules() { diff --git a/Tests/OptimizelyTests-Common/OptimizelyUserContextTests_Decide_Holdouts.swift b/Tests/OptimizelyTests-Common/OptimizelyUserContextTests_Decide_Holdouts.swift index a21ca3c0..2a33db0b 100644 --- a/Tests/OptimizelyTests-Common/OptimizelyUserContextTests_Decide_Holdouts.swift +++ b/Tests/OptimizelyTests-Common/OptimizelyUserContextTests_Decide_Holdouts.swift @@ -45,13 +45,19 @@ class OptimizelyUserContextTests_Decide_Holdouts: XCTestCase { override func setUp() { super.setUp() - + FeatureGates.localHoldouts = true + optimizely = OptimizelyClient(sdkKey: OTUtils.randomSdkKey, eventDispatcher: eventDispatcher, userProfileService: OTUtils.createClearUserProfileService()) - + try! optimizely.start(datafile: OTUtils.loadJSONDatafile("decide_datafile")!) } + + override func tearDown() { + FeatureGates.localHoldouts = false + super.tearDown() + } func test_decide_with_global_holdout_audience_matched() { let featureKey = "feature_1" diff --git a/Tests/OptimizelyTests-Common/OptimizelyUserContextTests_Decide_With_Holdouts_Reasons.swift b/Tests/OptimizelyTests-Common/OptimizelyUserContextTests_Decide_With_Holdouts_Reasons.swift index f05d7ba0..e459b4a1 100644 --- a/Tests/OptimizelyTests-Common/OptimizelyUserContextTests_Decide_With_Holdouts_Reasons.swift +++ b/Tests/OptimizelyTests-Common/OptimizelyUserContextTests_Decide_With_Holdouts_Reasons.swift @@ -44,12 +44,18 @@ class OptimizelyUserContextTests_Decide_With_Holdouts_Reasons: XCTestCase { override func setUp() { super.setUp() - + FeatureGates.localHoldouts = true + optimizely = OptimizelyClient(sdkKey: OTUtils.randomSdkKey, userProfileService: OTUtils.createClearUserProfileService()) - + try! optimizely.start(datafile: OTUtils.loadJSONDatafile("decide_datafile")!) } + + override func tearDown() { + FeatureGates.localHoldouts = false + super.tearDown() + } /// Test when user is bucketed into the global holdout func testDecideReasons_userBucketedIntoGlobalHoldout() { diff --git a/Tests/OptimizelyTests-DataModel/HoldoutConfigTests.swift b/Tests/OptimizelyTests-DataModel/HoldoutConfigTests.swift index acc9f804..b9d02535 100644 --- a/Tests/OptimizelyTests-DataModel/HoldoutConfigTests.swift +++ b/Tests/OptimizelyTests-DataModel/HoldoutConfigTests.swift @@ -17,6 +17,7 @@ import XCTest class HoldoutConfigTests: XCTestCase { + func testEmptyHoldouts_shouldHaveEmptyMaps() { let config = HoldoutConfig(allholdouts: [])