@@ -44,3 +44,147 @@ describe("TargetingKeyValues", () => {
4444 } ) ;
4545 } ) ;
4646} ) ;
47+
48+ describe ( "determineABTest" , ( ) => {
49+ // Mock Math.random to control the random bucket selection
50+ let originalMathRandom ;
51+
52+ beforeEach ( ( ) => {
53+ originalMathRandom = Math . random ;
54+ } ) ;
55+
56+ afterEach ( ( ) => {
57+ Math . random = originalMathRandom ;
58+ } ) ;
59+
60+ test ( "returns null when no abTests provided" , ( ) => {
61+ const { determineABTest } = require ( "./targeting" ) ;
62+ expect ( determineABTest ( ) ) . toBeNull ( ) ;
63+ expect ( determineABTest ( null ) ) . toBeNull ( ) ;
64+ expect ( determineABTest ( [ ] ) ) . toBeNull ( ) ;
65+ } ) ;
66+
67+ test ( "returns null when traffic percentage sum exceeds 100%" , ( ) => {
68+ const { determineABTest } = require ( "./targeting" ) ;
69+ const abTests = [
70+ { id : "test1" , trafficPercentage : 60 } ,
71+ { id : "test2" , trafficPercentage : 50 } ,
72+ ] ;
73+
74+ // Mock console.error to capture the error message
75+ const consoleSpy = jest . spyOn ( console , "error" ) . mockImplementation ( ( ) => { } ) ;
76+
77+ expect ( determineABTest ( abTests ) ) . toBeNull ( ) ;
78+ expect ( consoleSpy ) . toHaveBeenCalledWith ( "AB Test Config Error: Traffic Percentage Sum Exceeds 100%" ) ;
79+
80+ consoleSpy . mockRestore ( ) ;
81+ } ) ;
82+
83+ test ( "returns correct test based on random bucket" , ( ) => {
84+ const { determineABTest } = require ( "./targeting" ) ;
85+ const abTests = [
86+ { id : "test1" , trafficPercentage : 30 } ,
87+ { id : "test2" , trafficPercentage : 40 } ,
88+ { id : "test3" , trafficPercentage : 20 } ,
89+ ] ;
90+
91+ // Test bucket 0-29 (first test)
92+ Math . random = jest . fn ( ) . mockReturnValue ( 0.15 ) ; // bucket = 15
93+ expect ( determineABTest ( abTests ) ) . toEqual ( { id : "test1" , trafficPercentage : 30 } ) ;
94+
95+ // Test bucket 30-69 (second test)
96+ Math . random = jest . fn ( ) . mockReturnValue ( 0.5 ) ; // bucket = 50
97+ expect ( determineABTest ( abTests ) ) . toEqual ( { id : "test2" , trafficPercentage : 40 } ) ;
98+
99+ // Test bucket 70-89 (third test)
100+ Math . random = jest . fn ( ) . mockReturnValue ( 0.8 ) ; // bucket = 80
101+ expect ( determineABTest ( abTests ) ) . toEqual ( { id : "test3" , trafficPercentage : 20 } ) ;
102+
103+ // Test bucket 90-99 (no test selected)
104+ Math . random = jest . fn ( ) . mockReturnValue ( 0.95 ) ; // bucket = 95
105+ expect ( determineABTest ( abTests ) ) . toBeNull ( ) ;
106+ } ) ;
107+
108+ test ( "handles single test configuration" , ( ) => {
109+ const { determineABTest } = require ( "./targeting" ) ;
110+ const abTests = [ { id : "single-test" , trafficPercentage : 50 } ] ;
111+
112+ // Test bucket 0-49 (test selected)
113+ Math . random = jest . fn ( ) . mockReturnValue ( 0.25 ) ; // bucket = 25
114+ expect ( determineABTest ( abTests ) ) . toEqual ( { id : "single-test" , trafficPercentage : 50 } ) ;
115+
116+ // Test bucket 50-99 (no test selected)
117+ Math . random = jest . fn ( ) . mockReturnValue ( 0.75 ) ; // bucket = 75
118+ expect ( determineABTest ( abTests ) ) . toBeNull ( ) ;
119+ } ) ;
120+
121+ test ( "handles edge cases with 0% traffic" , ( ) => {
122+ const { determineABTest } = require ( "./targeting" ) ;
123+ const abTests = [
124+ { id : "test1" , trafficPercentage : 0 } ,
125+ { id : "test2" , trafficPercentage : 100 } ,
126+ ] ;
127+
128+ // Any bucket should return test2 since test1 has 0% traffic
129+ Math . random = jest . fn ( ) . mockReturnValue ( 0.5 ) ; // bucket = 50
130+ expect ( determineABTest ( abTests ) ) . toEqual ( { id : "test2" , trafficPercentage : 100 } ) ;
131+ } ) ;
132+
133+ test ( "handles tests with matcher_override" , ( ) => {
134+ const { determineABTest } = require ( "./targeting" ) ;
135+ const abTests = [
136+ {
137+ id : "test1" ,
138+ trafficPercentage : 50 ,
139+ matcher_override : [ { id : "override1" , rank : 1 } ] ,
140+ } ,
141+ {
142+ id : "test2" ,
143+ trafficPercentage : 50 ,
144+ matcher_override : [ { id : "override2" , rank : 2 } ] ,
145+ } ,
146+ ] ;
147+
148+ Math . random = jest . fn ( ) . mockReturnValue ( 0.25 ) ; // bucket = 25
149+ expect ( determineABTest ( abTests ) ) . toEqual ( {
150+ id : "test1" ,
151+ trafficPercentage : 50 ,
152+ matcher_override : [ { id : "override1" , rank : 1 } ] ,
153+ } ) ;
154+ } ) ;
155+
156+ test ( "handles boundary conditions" , ( ) => {
157+ const { determineABTest } = require ( "./targeting" ) ;
158+ const abTests = [
159+ { id : "test1" , trafficPercentage : 50 } ,
160+ { id : "test2" , trafficPercentage : 50 } ,
161+ ] ;
162+
163+ // Test exact boundary at 50
164+ Math . random = jest . fn ( ) . mockReturnValue ( 0.5 ) ; // bucket = 50
165+ expect ( determineABTest ( abTests ) ) . toEqual ( { id : "test2" , trafficPercentage : 50 } ) ;
166+
167+ // Test just below boundary
168+ Math . random = jest . fn ( ) . mockReturnValue ( 0.499 ) ; // bucket = 49
169+ expect ( determineABTest ( abTests ) ) . toEqual ( { id : "test1" , trafficPercentage : 50 } ) ;
170+ } ) ;
171+
172+ test ( "handles floating point precision issues" , ( ) => {
173+ const { determineABTest } = require ( "./targeting" ) ;
174+ const abTests = [
175+ { id : "test1" , trafficPercentage : 33.33 } ,
176+ { id : "test2" , trafficPercentage : 33.33 } ,
177+ { id : "test3" , trafficPercentage : 33.34 } ,
178+ ] ;
179+
180+ // Test cumulative calculation with floating point
181+ Math . random = jest . fn ( ) . mockReturnValue ( 0.333 ) ; // bucket = 33
182+ expect ( determineABTest ( abTests ) ) . toEqual ( { id : "test1" , trafficPercentage : 33.33 } ) ;
183+
184+ Math . random = jest . fn ( ) . mockReturnValue ( 0.666 ) ; // bucket = 66
185+ expect ( determineABTest ( abTests ) ) . toEqual ( { id : "test2" , trafficPercentage : 33.33 } ) ;
186+
187+ Math . random = jest . fn ( ) . mockReturnValue ( 0.999 ) ; // bucket = 99
188+ expect ( determineABTest ( abTests ) ) . toEqual ( { id : "test3" , trafficPercentage : 33.34 } ) ;
189+ } ) ;
190+ } ) ;
0 commit comments