@@ -7,20 +7,29 @@ import {
77 shouldApplyOverride
88} from '../../../flagsmith-engine/index.js' ;
99import { CONSTANTS } from '../../../flagsmith-engine/features/constants.js' ;
10- import { FeatureModel , FeatureStateModel } from '../../../flagsmith-engine/features/models.js' ;
10+ import {
11+ FeatureModel ,
12+ FeatureSegment ,
13+ FeatureStateModel ,
14+ MultivariateFeatureOptionModel ,
15+ MultivariateFeatureStateValueModel
16+ } from '../../../flagsmith-engine/features/models.js' ;
1117import { TraitModel } from '../../../flagsmith-engine/identities/traits/models.js' ;
1218import {
1319 environment ,
1420 environmentWithSegmentOverride ,
1521 feature1 ,
1622 identity ,
1723 identityInSegment ,
24+ segment ,
1825 segmentConditionProperty ,
19- segmentConditionStringValue
26+ segmentConditionStringValue ,
27+ traitMatchingSegment
2028} from './utils.js' ;
2129import { getEvaluationContext } from '../../../flagsmith-engine/evaluation/evaluationContext/mappers.js' ;
2230import { TARGETING_REASONS } from '../../../flagsmith-engine/features/types.js' ;
2331import { EvaluationContext } from '../../../flagsmith-engine/evaluation/evaluationContext/evaluationContext.types.js' ;
32+ import { EvaluationContextWithMetadata } from '../../../flagsmith-engine/evaluation/models.js' ;
2433import { IDENTITY_OVERRIDE_SEGMENT_NAME } from '../../../flagsmith-engine/segments/constants.js' ;
2534
2635test ( 'test_get_evaluation_result_without_any_override' , ( ) => {
@@ -356,3 +365,84 @@ test('evaluateFeatures with multivariate evaluation', () => {
356365 const flags = evaluateFeatures ( context as any , { } ) ;
357366 expect ( flags [ 'Multivariate Feature' ] . value ) . toBe ( 'variant_b' ) ;
358367} ) ;
368+
369+ test ( 'local evaluation returns correct multivariate value for segment override with 100% weight' , ( ) => {
370+ // Given
371+ // a feature with two multivariate variants where the segment override
372+ // assigns 100% weight to the second variant
373+ const env = environment ( ) ;
374+ const seg = segment ( ) ;
375+
376+ const mvFeature = new FeatureModel ( 10 , 'mv_feature' , CONSTANTS . STANDARD ) ;
377+
378+ const controlOption = new MultivariateFeatureOptionModel ( 'control' , 1 ) ;
379+ const variantOption = new MultivariateFeatureOptionModel ( 'variant_b' , 2 ) ;
380+
381+ const envFs = new FeatureStateModel ( mvFeature , true , 10 , 'default' ) ;
382+ envFs . multivariateFeatureStateValues = [
383+ new MultivariateFeatureStateValueModel ( controlOption , 0 , 1 ) ,
384+ new MultivariateFeatureStateValueModel ( variantOption , 100 , 2 )
385+ ] ;
386+ env . featureStates . push ( envFs ) ;
387+
388+ const overrideFs = new FeatureStateModel ( mvFeature , true , 11 , 'default' ) ;
389+ overrideFs . featureSegment = new FeatureSegment ( 0 ) ;
390+ overrideFs . multivariateFeatureStateValues = [
391+ new MultivariateFeatureStateValueModel ( controlOption , 0 , 1 ) ,
392+ new MultivariateFeatureStateValueModel ( variantOption , 100 , 2 )
393+ ] ;
394+ seg . featureStates . push ( overrideFs ) ;
395+ env . project . segments = [ seg ] ;
396+
397+ // When
398+ // evaluating flags for an identity that matches the segment
399+ const context = getEvaluationContext ( env , identityInSegment ( ) , [ traitMatchingSegment ( ) ] ) ;
400+ const result = getEvaluationResult ( context as EvaluationContextWithMetadata ) ;
401+ const flag = result . flags [ 'mv_feature' ] ;
402+
403+ // Then
404+ // the flag value should be the 100%-weighted variant, not the base default
405+ expect ( flag ) . toBeDefined ( ) ;
406+ expect ( flag . value ) . toBe ( 'variant_b' ) ;
407+ } ) ;
408+
409+ test ( 'getEvaluationContext maps multivariate variants onto segment override feature states' , ( ) => {
410+ // Given
411+ // a segment override feature state with multivariate values
412+ const env = environment ( ) ;
413+ const seg = segment ( ) ;
414+
415+ const mvFeature = new FeatureModel ( 10 , 'mv_feature' , CONSTANTS . STANDARD ) ;
416+ env . featureStates . push ( new FeatureStateModel ( mvFeature , true , 10 , 'default' ) ) ;
417+
418+ const overrideFs = new FeatureStateModel ( mvFeature , true , 11 , 'default' ) ;
419+ overrideFs . featureSegment = new FeatureSegment ( 0 ) ;
420+ overrideFs . multivariateFeatureStateValues = [
421+ new MultivariateFeatureStateValueModel (
422+ new MultivariateFeatureOptionModel ( 'variant_value' , 1 ) ,
423+ 100 ,
424+ 1
425+ )
426+ ] ;
427+ seg . featureStates . push ( overrideFs ) ;
428+ env . project . segments = [ seg ] ;
429+
430+ // When
431+ // mapping the environment model to an evaluation context
432+ const context = getEvaluationContext ( env , identityInSegment ( ) , [ traitMatchingSegment ( ) ] ) ;
433+
434+ // Then
435+ // the segment override should include the variants array
436+ const segmentOverrides = Object . values ( context . segments || { } ) ;
437+ const segWithOverrides = segmentOverrides . find (
438+ s => s . overrides && s . overrides . some ( ( o : any ) => o . name === 'mv_feature' )
439+ ) ;
440+ expect ( segWithOverrides ) . toBeDefined ( ) ;
441+
442+ const mvOverride = segWithOverrides ! . overrides ! . find ( ( o : any ) => o . name === 'mv_feature' ) ;
443+ expect ( mvOverride ) . toBeDefined ( ) ;
444+ expect ( ( mvOverride as any ) . variants ) . toBeDefined ( ) ;
445+ expect ( ( mvOverride as any ) . variants ) . toHaveLength ( 1 ) ;
446+ expect ( ( mvOverride as any ) . variants [ 0 ] . value ) . toBe ( 'variant_value' ) ;
447+ expect ( ( mvOverride as any ) . variants [ 0 ] . weight ) . toBe ( 100 ) ;
448+ } ) ;
0 commit comments