@@ -20,7 +20,7 @@ import OptimizelyUserContext from '../../optimizely_user_context';
2020import { bucket } from '../bucketer' ;
2121import { getTestProjectConfig , getTestProjectConfigWithFeatures } from '../../tests/test_data' ;
2222import { createProjectConfig , ProjectConfig } from '../../project_config/project_config' ;
23- import { BucketerParams , Experiment , Holdout , OptimizelyDecideOption , UserAttributes , UserProfile } from '../../shared_types' ;
23+ import { BucketerParams , Experiment , ExperimentBucketMap , Holdout , OptimizelyDecideOption , UserAttributes , UserProfile } from '../../shared_types' ;
2424import { CONTROL_ATTRIBUTES , DECISION_SOURCES } from '../../utils/enums' ;
2525import { getDecisionTestDatafile } from '../../tests/decision_test_datafile' ;
2626import { Value } from '../../utils/promise/operation_value' ;
@@ -1607,8 +1607,8 @@ describe('DecisionService', () => {
16071607 return Promise . resolve ( {
16081608 user_id : 'tester-1' ,
16091609 experiment_bucket_map : {
1610- '2003 ' : {
1611- variation_id : '5001 ' ,
1610+ '2001 ' : {
1611+ variation_id : '5002 ' ,
16121612 } ,
16131613 } ,
16141614 } ) ;
@@ -1620,7 +1620,7 @@ describe('DecisionService', () => {
16201620
16211621 mockBucket . mockImplementation ( ( param : BucketerParams ) => {
16221622 const ruleKey = param . experimentKey ;
1623- if ( ruleKey == 'exp_3 ' ) {
1623+ if ( ruleKey == 'exp_1 ' ) {
16241624 return { result : param . trafficAllocationConfig [ 0 ] . entityId , reasons : [ ] }
16251625 }
16261626 return {
@@ -1629,19 +1629,14 @@ describe('DecisionService', () => {
16291629 }
16301630 } ) ;
16311631
1632- cmabService . getDecision . mockResolvedValue ( {
1633- variationId : '5003' ,
1634- cmabUuid : 'uuid-test' ,
1635- } ) ;
1636-
16371632 const config = createProjectConfig ( getDecisionTestDatafile ( ) ) ;
16381633
16391634 const user1 = new OptimizelyUserContext ( {
16401635 optimizely : { } as any ,
16411636 userId : 'tester-1' ,
16421637 attributes : {
16431638 country : 'BD' ,
1644- age : 80 , // should satisfy audience condition for exp_3 which is cmab and not others
1639+ age : 22 , // should satisfy audience condition for exp_1 which is not a cmab
16451640 } ,
16461641 } ) ;
16471642
@@ -1650,7 +1645,7 @@ describe('DecisionService', () => {
16501645 userId : 'tester-2' ,
16511646 attributes : {
16521647 country : 'BD' ,
1653- age : 80 , // should satisfy audience condition for exp_3 which is cmab and not others
1648+ age : 22 , // should satisfy audience condition for exp_1 which is not a cmab
16541649 } ,
16551650 } ) ;
16561651
@@ -1661,12 +1656,11 @@ describe('DecisionService', () => {
16611656 const variation = ( await value ) [ 0 ] ;
16621657
16631658 expect ( variation . result ) . toEqual ( {
1664- experiment : config . experimentKeyMap [ 'exp_3 ' ] ,
1665- variation : config . variationIdMap [ '5001 ' ] ,
1659+ experiment : config . experimentKeyMap [ 'exp_1 ' ] ,
1660+ variation : config . variationIdMap [ '5002 ' ] ,
16661661 decisionSource : DECISION_SOURCES . FEATURE_TEST ,
16671662 } ) ;
16681663
1669- expect ( cmabService . getDecision ) . not . toHaveBeenCalled ( ) ;
16701664 expect ( userProfileServiceAsync ?. lookup ) . toHaveBeenCalledTimes ( 1 ) ;
16711665 expect ( userProfileServiceAsync ?. lookup ) . toHaveBeenCalledWith ( 'tester-1' ) ;
16721666
@@ -1675,9 +1669,8 @@ describe('DecisionService', () => {
16751669
16761670 const variation2 = ( await value2 ) [ 0 ] ;
16771671 expect ( variation2 . result ) . toEqual ( {
1678- cmabUuid : 'uuid-test' ,
1679- experiment : config . experimentKeyMap [ 'exp_3' ] ,
1680- variation : config . variationIdMap [ '5003' ] ,
1672+ experiment : config . experimentKeyMap [ 'exp_1' ] ,
1673+ variation : config . variationIdMap [ '5001' ] ,
16811674 decisionSource : DECISION_SOURCES . FEATURE_TEST ,
16821675 } ) ;
16831676
@@ -1687,8 +1680,8 @@ describe('DecisionService', () => {
16871680 expect ( userProfileServiceAsync ?. save ) . toHaveBeenCalledWith ( {
16881681 user_id : 'tester-2' ,
16891682 experiment_bucket_map : {
1690- '2003 ' : {
1691- variation_id : '5003 ' ,
1683+ '2001 ' : {
1684+ variation_id : '5001 ' ,
16921685 } ,
16931686 } ,
16941687 } ) ;
@@ -1773,7 +1766,7 @@ describe('DecisionService', () => {
17731766
17741767 mockBucket . mockImplementation ( ( param : BucketerParams ) => {
17751768 const ruleKey = param . experimentKey ;
1776- if ( ruleKey == 'exp_3 ' ) {
1769+ if ( ruleKey == 'exp_1 ' ) {
17771770 return { result : param . trafficAllocationConfig [ 0 ] . entityId , reasons : [ ] }
17781771 }
17791772 return {
@@ -1782,19 +1775,14 @@ describe('DecisionService', () => {
17821775 }
17831776 } ) ;
17841777
1785- cmabService . getDecision . mockResolvedValue ( {
1786- variationId : '5003' ,
1787- cmabUuid : 'uuid-test' ,
1788- } ) ;
1789-
17901778 const config = createProjectConfig ( getDecisionTestDatafile ( ) ) ;
17911779
17921780 const user = new OptimizelyUserContext ( {
17931781 optimizely : { } as any ,
17941782 userId : 'tester' ,
17951783 attributes : {
17961784 country : 'BD' ,
1797- age : 80 , // should satisfy audience condition for exp_3 which is cmab and not others
1785+ age : 22 , // should satisfy audience condition for exp_1 which is not cmab
17981786 } ,
17991787 } ) ;
18001788
@@ -1805,27 +1793,19 @@ describe('DecisionService', () => {
18051793 const variation = ( await value ) [ 0 ] ;
18061794
18071795 expect ( variation . result ) . toEqual ( {
1808- cmabUuid : 'uuid-test' ,
1809- experiment : config . experimentKeyMap [ 'exp_3' ] ,
1810- variation : config . variationIdMap [ '5003' ] ,
1796+ experiment : config . experimentKeyMap [ 'exp_1' ] ,
1797+ variation : config . variationIdMap [ '5001' ] ,
18111798 decisionSource : DECISION_SOURCES . FEATURE_TEST ,
18121799 } ) ;
18131800
18141801 expect ( userProfileServiceAsync ?. lookup ) . toHaveBeenCalledWith ( 'tester' ) ;
1815- expect ( cmabService . getDecision ) . toHaveBeenCalledTimes ( 1 ) ;
1816- expect ( cmabService . getDecision ) . toHaveBeenCalledWith (
1817- config ,
1818- user ,
1819- '2003' , // id of exp_3
1820- { } ,
1821- ) ;
18221802
18231803 expect ( userProfileServiceAsync ?. save ) . toHaveBeenCalledTimes ( 1 ) ;
18241804 expect ( userProfileServiceAsync ?. save ) . toHaveBeenCalledWith ( {
18251805 user_id : 'tester' ,
18261806 experiment_bucket_map : {
1827- '2003 ' : {
1828- variation_id : '5003 ' ,
1807+ '2001 ' : {
1808+ variation_id : '5001 ' ,
18291809 } ,
18301810 } ,
18311811 } ) ;
@@ -1847,7 +1827,7 @@ describe('DecisionService', () => {
18471827
18481828 mockBucket . mockImplementation ( ( param : BucketerParams ) => {
18491829 const ruleKey = param . experimentKey ;
1850- if ( ruleKey == 'exp_3 ' ) {
1830+ if ( ruleKey == 'exp_1 ' ) {
18511831 return { result : param . trafficAllocationConfig [ 0 ] . entityId , reasons : [ ] }
18521832 }
18531833 return {
@@ -1856,19 +1836,14 @@ describe('DecisionService', () => {
18561836 }
18571837 } ) ;
18581838
1859- cmabService . getDecision . mockResolvedValue ( {
1860- variationId : '5003' ,
1861- cmabUuid : 'uuid-test' ,
1862- } ) ;
1863-
18641839 const config = createProjectConfig ( getDecisionTestDatafile ( ) ) ;
18651840
18661841 const user = new OptimizelyUserContext ( {
18671842 optimizely : { } as any ,
18681843 userId : 'tester' ,
18691844 attributes : {
18701845 country : 'BD' ,
1871- age : 80 , // should satisfy audience condition for exp_3 which is cmab and not others
1846+ age : 22 , // should satisfy audience condition for exp_1 which is not cmab
18721847 } ,
18731848 } ) ;
18741849
@@ -1879,9 +1854,8 @@ describe('DecisionService', () => {
18791854 const variation = ( await value ) [ 0 ] ;
18801855
18811856 expect ( variation . result ) . toEqual ( {
1882- cmabUuid : 'uuid-test' ,
1883- experiment : config . experimentKeyMap [ 'exp_3' ] ,
1884- variation : config . variationIdMap [ '5003' ] ,
1857+ experiment : config . experimentKeyMap [ 'exp_1' ] ,
1858+ variation : config . variationIdMap [ '5001' ] ,
18851859 decisionSource : DECISION_SOURCES . FEATURE_TEST ,
18861860 } ) ;
18871861
@@ -1892,8 +1866,8 @@ describe('DecisionService', () => {
18921866 expect ( userProfileService ?. save ) . toHaveBeenCalledWith ( {
18931867 user_id : 'tester' ,
18941868 experiment_bucket_map : {
1895- '2003 ' : {
1896- variation_id : '5003 ' ,
1869+ '2001 ' : {
1870+ variation_id : '5001 ' ,
18971871 } ,
18981872 } ,
18991873 } ) ;
@@ -1902,6 +1876,84 @@ describe('DecisionService', () => {
19021876 expect ( userProfileServiceAsync ?. save ) . not . toHaveBeenCalled ( ) ;
19031877 } ) ;
19041878
1879+ it ( 'should not save cmab decisions to user profile service' , async ( ) => {
1880+ const { decisionService, userProfileService, cmabService } = getDecisionService ( {
1881+ userProfileService : true ,
1882+ userProfileServiceAsync : true ,
1883+ } ) ;
1884+
1885+ const upsSyncMap : Record < string , ExperimentBucketMap > = { } ;
1886+ const upsAsyncMap : Record < string , ExperimentBucketMap > = { } ;
1887+
1888+ userProfileService ?. lookup . mockImplementation ( ( userId : string ) => {
1889+ return upsSyncMap [ userId ] || null ;
1890+ } ) ;
1891+
1892+ userProfileService ?. save . mockImplementation ( ( userProfile : UserProfile ) => {
1893+ upsSyncMap [ userProfile . user_id ] = userProfile . experiment_bucket_map ;
1894+ } ) ;
1895+
1896+ mockBucket . mockImplementation ( ( param : BucketerParams ) => {
1897+ const ruleKey = param . experimentKey ;
1898+ if ( ruleKey == 'exp_3' ) {
1899+ return { result : param . trafficAllocationConfig [ 0 ] . entityId , reasons : [ ] }
1900+ }
1901+ return {
1902+ result : null ,
1903+ reasons : [ ] ,
1904+ }
1905+ } ) ;
1906+
1907+ cmabService . getDecision . mockResolvedValueOnce ( {
1908+ variationId : '5003' ,
1909+ cmabUuid : 'uuid-test' ,
1910+ } ) . mockResolvedValueOnce ( {
1911+ variationId : '5001' ,
1912+ cmabUuid : 'uuid-test-2' ,
1913+ } ) ;
1914+
1915+ const config = createProjectConfig ( getDecisionTestDatafile ( ) ) ;
1916+
1917+ const user = new OptimizelyUserContext ( {
1918+ optimizely : { } as any ,
1919+ userId : 'tester' ,
1920+ attributes : {
1921+ country : 'BD' ,
1922+ age : 80 , // should satisfy audience condition for exp_2 which is a cmab and not others
1923+ } ,
1924+ } ) ;
1925+
1926+ const feature = config . featureKeyMap [ 'flag_1' ] ;
1927+ const [ variation ] = await decisionService . resolveVariationsForFeatureList ( 'async' , config , [ feature ] , user , { } ) . get ( ) ;
1928+
1929+ expect ( variation . result ) . toEqual ( {
1930+ cmabUuid : 'uuid-test' ,
1931+ experiment : config . experimentKeyMap [ 'exp_3' ] ,
1932+ variation : config . variationIdMap [ '5003' ] ,
1933+ decisionSource : DECISION_SOURCES . FEATURE_TEST ,
1934+ } ) ;
1935+
1936+ expect ( userProfileService ?. lookup ) . toHaveBeenCalledTimes ( 1 ) ;
1937+ expect ( userProfileService ?. lookup ) . toHaveBeenCalledWith ( 'tester' ) ;
1938+ expect ( userProfileService ?. save ) . not . toHaveBeenCalled ;
1939+ expect ( cmabService . getDecision ) . toHaveBeenCalledTimes ( 1 ) ;
1940+
1941+ // decide again for the same user, now cmab service should return variation 5001
1942+ const [ variation2 ] = await decisionService . resolveVariationsForFeatureList ( 'async' , config , [ feature ] , user , { } ) . get ( ) ;
1943+ expect ( variation2 . result ) . toEqual ( {
1944+ cmabUuid : 'uuid-test-2' ,
1945+ experiment : config . experimentKeyMap [ 'exp_3' ] ,
1946+ variation : config . variationIdMap [ '5001' ] ,
1947+ decisionSource : DECISION_SOURCES . FEATURE_TEST ,
1948+ } ) ;
1949+
1950+ expect ( userProfileService ?. lookup ) . toHaveBeenCalledTimes ( 2 ) ;
1951+ expect ( userProfileService ?. lookup ) . toHaveBeenNthCalledWith ( 2 , 'tester' ) ;
1952+ expect ( userProfileService ?. save ) . not . toHaveBeenCalled ;
1953+ expect ( cmabService . getDecision ) . toHaveBeenCalledTimes ( 2 ) ;
1954+ } ) ;
1955+
1956+
19051957 describe ( 'holdout' , ( ) => {
19061958 beforeEach ( async ( ) => {
19071959 mockHoldoutToggle . mockReturnValue ( true ) ;
0 commit comments