@@ -1890,3 +1890,252 @@ def test_get_variation_for_feature_returns_rollout_in_experiment_bucket_range_25
18901890 mock_config_logging .debug .assert_called_with (
18911891 'Assigned bucket 4000 to user with bucketing ID "test_user".' )
18921892 mock_generate_bucket_value .assert_called_with ("test_user211147" )
1893+
1894+ def test_get_variation_cmab_experiment_excludes_ups_lookup (self ):
1895+ """Test that CMAB experiments skip UPS lookup for stored variations."""
1896+
1897+ # Create a user context
1898+ user = optimizely_user_context .OptimizelyUserContext (
1899+ optimizely_client = None ,
1900+ logger = None ,
1901+ user_id = "test_user" ,
1902+ user_attributes = {}
1903+ )
1904+
1905+ # Create a CMAB experiment
1906+ cmab_experiment = entities .Experiment (
1907+ '111150' ,
1908+ 'cmab_experiment' ,
1909+ 'Running' ,
1910+ '111150' ,
1911+ [], # No audience IDs
1912+ {},
1913+ [
1914+ entities .Variation ('111151' , 'variation_1' ),
1915+ entities .Variation ('111152' , 'variation_2' )
1916+ ],
1917+ [
1918+ {'entityId' : '111151' , 'endOfRange' : 5000 },
1919+ {'entityId' : '111152' , 'endOfRange' : 10000 }
1920+ ],
1921+ cmab = {'trafficAllocation' : 5000 , 'attributeIds' : []}
1922+ )
1923+
1924+ # Create a mock user profile tracker with a stored variation
1925+ mock_user_profile_tracker = mock .Mock ()
1926+ mock_user_profile = user_profile .UserProfile (
1927+ user_id = "test_user" ,
1928+ experiment_bucket_map = {'111150' : {'variation_id' : '111152' }} # Stored as variation_2
1929+ )
1930+ mock_user_profile_tracker .get_user_profile .return_value = mock_user_profile
1931+
1932+ with mock .patch ('optimizely.helpers.experiment.is_experiment_running' , return_value = True ), \
1933+ mock .patch ('optimizely.helpers.audience.does_user_meet_audience_conditions' , return_value = [True , []]), \
1934+ mock .patch .object (self .decision_service .bucketer , 'bucket_to_entity_id' ,
1935+ return_value = ['$' , []]), \
1936+ mock .patch .object (self .decision_service , 'cmab_service' ) as mock_cmab_service , \
1937+ mock .patch .object (self .project_config , 'get_variation_from_id' ,
1938+ return_value = entities .Variation ('111151' , 'variation_1' )), \
1939+ mock .patch .object (self .decision_service , 'get_stored_variation' ) as mock_get_stored :
1940+
1941+ # Configure CMAB service to return variation_1
1942+ mock_cmab_service .get_decision .return_value = (
1943+ {
1944+ 'variation_id' : '111151' ,
1945+ 'cmab_uuid' : 'test-cmab-uuid-123'
1946+ },
1947+ []
1948+ )
1949+
1950+ # Call get_variation with CMAB experiment and user profile tracker
1951+ variation_result = self .decision_service .get_variation (
1952+ self .project_config ,
1953+ cmab_experiment ,
1954+ user ,
1955+ mock_user_profile_tracker
1956+ )
1957+
1958+ # Verify that get_stored_variation was NOT called (UPS lookup skipped)
1959+ mock_get_stored .assert_not_called ()
1960+
1961+ # Verify that CMAB decision was used (variation_1, not stored variation_2)
1962+ self .assertEqual (entities .Variation ('111151' , 'variation_1' ), variation_result ['variation' ])
1963+ self .assertEqual ('test-cmab-uuid-123' , variation_result ['cmab_uuid' ])
1964+
1965+ def test_get_variation_cmab_experiment_excludes_ups_save (self ):
1966+ """Test that CMAB experiments skip UPS save for new decisions."""
1967+
1968+ # Create a user context
1969+ user = optimizely_user_context .OptimizelyUserContext (
1970+ optimizely_client = None ,
1971+ logger = None ,
1972+ user_id = "test_user" ,
1973+ user_attributes = {}
1974+ )
1975+
1976+ # Create a CMAB experiment
1977+ cmab_experiment = entities .Experiment (
1978+ '111150' ,
1979+ 'cmab_experiment' ,
1980+ 'Running' ,
1981+ '111150' ,
1982+ [],
1983+ {},
1984+ [
1985+ entities .Variation ('111151' , 'variation_1' ),
1986+ entities .Variation ('111152' , 'variation_2' )
1987+ ],
1988+ [
1989+ {'entityId' : '111151' , 'endOfRange' : 5000 },
1990+ {'entityId' : '111152' , 'endOfRange' : 10000 }
1991+ ],
1992+ cmab = {'trafficAllocation' : 5000 , 'attributeIds' : []}
1993+ )
1994+
1995+ # Create a mock user profile tracker
1996+ mock_user_profile_tracker = mock .Mock ()
1997+
1998+ with mock .patch ('optimizely.helpers.experiment.is_experiment_running' , return_value = True ), \
1999+ mock .patch ('optimizely.helpers.audience.does_user_meet_audience_conditions' , return_value = [True , []]), \
2000+ mock .patch .object (self .decision_service .bucketer , 'bucket_to_entity_id' ,
2001+ return_value = ['$' , []]), \
2002+ mock .patch .object (self .decision_service , 'cmab_service' ) as mock_cmab_service , \
2003+ mock .patch .object (self .project_config , 'get_variation_from_id' ,
2004+ return_value = entities .Variation ('111151' , 'variation_1' )):
2005+
2006+ # Configure CMAB service to return a decision
2007+ mock_cmab_service .get_decision .return_value = (
2008+ {
2009+ 'variation_id' : '111151' ,
2010+ 'cmab_uuid' : 'test-cmab-uuid-123'
2011+ },
2012+ []
2013+ )
2014+
2015+ # Call get_variation with CMAB experiment and user profile tracker
2016+ variation_result = self .decision_service .get_variation (
2017+ self .project_config ,
2018+ cmab_experiment ,
2019+ user ,
2020+ mock_user_profile_tracker
2021+ )
2022+
2023+ # Verify that update_user_profile was NOT called (UPS save skipped)
2024+ mock_user_profile_tracker .update_user_profile .assert_not_called ()
2025+
2026+ # Verify variation was still returned correctly
2027+ self .assertEqual (entities .Variation ('111151' , 'variation_1' ), variation_result ['variation' ])
2028+ self .assertEqual ('test-cmab-uuid-123' , variation_result ['cmab_uuid' ])
2029+
2030+ def test_get_variation_non_cmab_experiment_uses_ups_lookup (self ):
2031+ """Test that non-CMAB experiments still use UPS lookup for stored variations."""
2032+
2033+ # Create a user context
2034+ user = optimizely_user_context .OptimizelyUserContext (
2035+ optimizely_client = None ,
2036+ logger = None ,
2037+ user_id = "test_user" ,
2038+ user_attributes = {}
2039+ )
2040+
2041+ # Create a NON-CMAB experiment (cmab=None)
2042+ regular_experiment = entities .Experiment (
2043+ '111150' ,
2044+ 'regular_experiment' ,
2045+ 'Running' ,
2046+ '111150' ,
2047+ [],
2048+ {},
2049+ [
2050+ entities .Variation ('111151' , 'variation_1' ),
2051+ entities .Variation ('111152' , 'variation_2' )
2052+ ],
2053+ [
2054+ {'entityId' : '111151' , 'endOfRange' : 5000 },
2055+ {'entityId' : '111152' , 'endOfRange' : 10000 }
2056+ ],
2057+ cmab = None # Not a CMAB experiment
2058+ )
2059+
2060+ # Create a mock user profile tracker with a stored variation
2061+ mock_user_profile_tracker = mock .Mock ()
2062+ mock_user_profile = user_profile .UserProfile (
2063+ user_id = "test_user" ,
2064+ experiment_bucket_map = {'111150' : {'variation_id' : '111152' }}
2065+ )
2066+ mock_user_profile_tracker .get_user_profile .return_value = mock_user_profile
2067+
2068+ with mock .patch ('optimizely.helpers.experiment.is_experiment_running' , return_value = True ), \
2069+ mock .patch .object (self .decision_service , 'get_stored_variation' ,
2070+ return_value = entities .Variation ('111152' , 'variation_2' )) as mock_get_stored :
2071+
2072+ # Call get_variation with non-CMAB experiment and user profile tracker
2073+ variation_result = self .decision_service .get_variation (
2074+ self .project_config ,
2075+ regular_experiment ,
2076+ user ,
2077+ mock_user_profile_tracker
2078+ )
2079+
2080+ # Verify that get_stored_variation WAS called (UPS lookup used)
2081+ mock_get_stored .assert_called_once ()
2082+
2083+ # Verify that stored variation was returned
2084+ self .assertEqual (entities .Variation ('111152' , 'variation_2' ), variation_result ['variation' ])
2085+ self .assertIsNone (variation_result ['cmab_uuid' ])
2086+
2087+ def test_get_variation_non_cmab_experiment_uses_ups_save (self ):
2088+ """Test that non-CMAB experiments still use UPS save for new decisions."""
2089+
2090+ # Create a user context
2091+ user = optimizely_user_context .OptimizelyUserContext (
2092+ optimizely_client = None ,
2093+ logger = None ,
2094+ user_id = "test_user" ,
2095+ user_attributes = {}
2096+ )
2097+
2098+ # Create a NON-CMAB experiment
2099+ regular_experiment = entities .Experiment (
2100+ '111150' ,
2101+ 'regular_experiment' ,
2102+ 'Running' ,
2103+ '111150' ,
2104+ [],
2105+ {},
2106+ [
2107+ entities .Variation ('111151' , 'variation_1' ),
2108+ entities .Variation ('111152' , 'variation_2' )
2109+ ],
2110+ [
2111+ {'entityId' : '111151' , 'endOfRange' : 5000 },
2112+ {'entityId' : '111152' , 'endOfRange' : 10000 }
2113+ ],
2114+ cmab = None # Not a CMAB experiment
2115+ )
2116+
2117+ # Create a mock user profile tracker
2118+ mock_user_profile_tracker = mock .Mock ()
2119+
2120+ with mock .patch ('optimizely.helpers.experiment.is_experiment_running' , return_value = True ), \
2121+ mock .patch ('optimizely.helpers.audience.does_user_meet_audience_conditions' , return_value = [True , []]), \
2122+ mock .patch .object (self .decision_service .bucketer , 'bucket' ,
2123+ return_value = [entities .Variation ('111151' , 'variation_1' ), []]):
2124+
2125+ # Call get_variation with non-CMAB experiment and user profile tracker
2126+ variation_result = self .decision_service .get_variation (
2127+ self .project_config ,
2128+ regular_experiment ,
2129+ user ,
2130+ mock_user_profile_tracker
2131+ )
2132+
2133+ # Verify that update_user_profile WAS called (UPS save used)
2134+ mock_user_profile_tracker .update_user_profile .assert_called_once_with (
2135+ regular_experiment ,
2136+ entities .Variation ('111151' , 'variation_1' )
2137+ )
2138+
2139+ # Verify variation was returned correctly
2140+ self .assertEqual (entities .Variation ('111151' , 'variation_1' ), variation_result ['variation' ])
2141+ self .assertIsNone (variation_result ['cmab_uuid' ])
0 commit comments