...

Source file src/github.com/launchdarkly/go-server-sdk-evaluation/v2/ldmodel/preprocess_test.go

Documentation: github.com/launchdarkly/go-server-sdk-evaluation/v2/ldmodel

     1  package ldmodel
     2  
     3  import (
     4  	"regexp"
     5  	"testing"
     6  	"time"
     7  
     8  	"github.com/stretchr/testify/assert"
     9  	"github.com/stretchr/testify/require"
    10  
    11  	"github.com/launchdarkly/go-sdk-common/v3/ldcontext"
    12  	"github.com/launchdarkly/go-sdk-common/v3/ldvalue"
    13  )
    14  
    15  func TestPreprocessFlagBuildsTargetMap(t *testing.T) {
    16  	f := FeatureFlag{
    17  		Targets: []Target{
    18  			{
    19  				Variation: 0,
    20  				Values:    nil,
    21  			},
    22  			{
    23  				Variation: 1,
    24  				Values:    []string{"a", "b"},
    25  			},
    26  		},
    27  	}
    28  
    29  	assert.Nil(t, f.Targets[0].preprocessed.valuesMap)
    30  	assert.Nil(t, f.Targets[1].preprocessed.valuesMap)
    31  
    32  	PreprocessFlag(&f)
    33  
    34  	assert.Nil(t, f.Targets[0].preprocessed.valuesMap)
    35  
    36  	assert.Len(t, f.Targets[1].preprocessed.valuesMap, 2)
    37  	assert.Contains(t, f.Targets[1].preprocessed.valuesMap, "a")
    38  	assert.Contains(t, f.Targets[1].preprocessed.valuesMap, "b")
    39  }
    40  
    41  func TestPreprocessFlagCreatesClauseValuesMapForMultiValueEqualityTest(t *testing.T) {
    42  	f := FeatureFlag{
    43  		Rules: []FlagRule{
    44  			{
    45  				Clauses: []Clause{
    46  					{
    47  						Op:     OperatorIn,
    48  						Values: []ldvalue.Value{ldvalue.Bool(true), ldvalue.String("a"), ldvalue.Int(0)},
    49  					},
    50  				},
    51  			},
    52  		},
    53  	}
    54  
    55  	assert.Nil(t, f.Rules[0].Clauses[0].preprocessed.valuesMap)
    56  
    57  	PreprocessFlag(&f)
    58  
    59  	m := f.Rules[0].Clauses[0].preprocessed.valuesMap
    60  	assert.Equal(t, map[jsonPrimitiveValueKey]struct{}{
    61  		asPrimitiveValueKey(ldvalue.Bool(true)):  {},
    62  		asPrimitiveValueKey(ldvalue.String("a")): {},
    63  		asPrimitiveValueKey(ldvalue.Int(0)):      {},
    64  	}, m)
    65  }
    66  
    67  func TestPreprocessFlagDoesNotCreateClauseValuesMapForSingleValueEqualityTest(t *testing.T) {
    68  	f := FeatureFlag{
    69  		Rules: []FlagRule{
    70  			{
    71  				Clauses: []Clause{
    72  					{
    73  						Op:     OperatorIn,
    74  						Values: []ldvalue.Value{ldvalue.String("a")},
    75  					},
    76  				},
    77  			},
    78  		},
    79  	}
    80  
    81  	assert.Nil(t, f.Rules[0].Clauses[0].preprocessed.valuesMap)
    82  
    83  	PreprocessFlag(&f)
    84  
    85  	assert.Nil(t, f.Rules[0].Clauses[0].preprocessed.valuesMap)
    86  }
    87  
    88  func TestPreprocessFlagDoesNotCreateClauseValuesMapForEmptyEqualityTest(t *testing.T) {
    89  	f := FeatureFlag{
    90  		Rules: []FlagRule{
    91  			{Clauses: []Clause{{Op: OperatorIn, Values: []ldvalue.Value{}}}},
    92  		},
    93  	}
    94  
    95  	assert.Nil(t, f.Rules[0].Clauses[0].preprocessed.valuesMap)
    96  
    97  	PreprocessFlag(&f)
    98  
    99  	assert.Nil(t, f.Rules[0].Clauses[0].preprocessed.valuesMap)
   100  }
   101  
   102  func TestPreprocessFlagDoesNotCreateClauseValuesMapForNonEqualityOperators(t *testing.T) {
   103  	ops := []Operator{
   104  		OperatorEndsWith, OperatorStartsWith, OperatorMatches, OperatorContains, OperatorLessThan,
   105  		OperatorLessThanOrEqual, OperatorGreaterThan, OperatorGreaterThanOrEqual, OperatorBefore,
   106  		OperatorAfter, OperatorSegmentMatch, OperatorSemVerEqual, OperatorSemVerLessThan,
   107  		OperatorSemVerGreaterThan,
   108  	}
   109  
   110  	values := []ldvalue.Value{ldvalue.String("a"), ldvalue.String("b")}
   111  	// The values & types aren't very important here because we won't actually evaluate the clause; all that
   112  	// matters is that they're primitives and there's more than one of them, so that it *would* build a map
   113  	// if the operator were OperatorIn
   114  	for _, op := range ops {
   115  		t.Run(string(op), func(t *testing.T) {
   116  			f := FeatureFlag{
   117  				Rules: []FlagRule{
   118  					{
   119  						Clauses: []Clause{{Op: op, Values: values}},
   120  					},
   121  				},
   122  			}
   123  
   124  			assert.Nil(t, f.Rules[0].Clauses[0].preprocessed.valuesMap)
   125  
   126  			PreprocessFlag(&f)
   127  
   128  			assert.Nil(t, f.Rules[0].Clauses[0].preprocessed.valuesMap)
   129  		})
   130  	}
   131  }
   132  
   133  func TestPreprocessFlagParsesClauseRegex(t *testing.T) {
   134  	f := FeatureFlag{
   135  		Rules: []FlagRule{
   136  			{
   137  				Clauses: []Clause{
   138  					{
   139  						Op:     OperatorMatches,
   140  						Values: []ldvalue.Value{ldvalue.String("x*"), ldvalue.String("\\"), ldvalue.Int(3)},
   141  					},
   142  				},
   143  			},
   144  		},
   145  	}
   146  
   147  	assert.Nil(t, f.Rules[0].Clauses[0].preprocessed.values)
   148  
   149  	PreprocessFlag(&f)
   150  
   151  	p := f.Rules[0].Clauses[0].preprocessed.values
   152  	require.Len(t, p, 3)
   153  
   154  	assert.True(t, p[0].computed)
   155  	assert.True(t, p[0].valid)
   156  	assert.Equal(t, regexp.MustCompile("x*"), p[0].parsedRegexp)
   157  
   158  	assert.True(t, p[1].computed)
   159  	assert.False(t, p[1].valid)
   160  	assert.True(t, p[2].computed)
   161  	assert.False(t, p[2].valid)
   162  }
   163  
   164  func TestPreprocessFlagParsesClauseTime(t *testing.T) {
   165  	time1Str := "2016-04-16T17:09:12-07:00"
   166  	t1, _ := time.Parse(time.RFC3339Nano, time1Str)
   167  	time1 := t1.UTC()
   168  	time2Num := float64(1000000)
   169  	time2 := time.Unix(0, int64(time2Num)*int64(time.Millisecond)).UTC()
   170  
   171  	for _, operator := range []Operator{OperatorAfter, OperatorBefore} {
   172  		t.Run(string(operator), func(t *testing.T) {
   173  			f := FeatureFlag{
   174  				Rules: []FlagRule{
   175  					{
   176  						Clauses: []Clause{
   177  							{
   178  								Op:     operator,
   179  								Values: []ldvalue.Value{ldvalue.String(time1Str), ldvalue.Float64(time2Num), ldvalue.String("x"), ldvalue.Bool(false)},
   180  							},
   181  						},
   182  					},
   183  				},
   184  			}
   185  
   186  			assert.Nil(t, f.Rules[0].Clauses[0].preprocessed.values)
   187  
   188  			PreprocessFlag(&f)
   189  
   190  			p := f.Rules[0].Clauses[0].preprocessed.values
   191  			require.Len(t, p, 4)
   192  
   193  			assert.True(t, p[0].computed)
   194  			assert.True(t, p[0].valid)
   195  			assert.Equal(t, time1, p[0].parsedTime)
   196  
   197  			assert.True(t, p[1].computed)
   198  			assert.True(t, p[1].valid)
   199  			assert.Equal(t, time2, p[1].parsedTime)
   200  
   201  			assert.True(t, p[2].computed)
   202  			assert.False(t, p[2].valid)
   203  			assert.True(t, p[3].computed)
   204  			assert.False(t, p[3].valid)
   205  		})
   206  	}
   207  }
   208  
   209  func TestPreprocessFlagParsesClauseSemver(t *testing.T) {
   210  	expected, ok := parseSemVer(ldvalue.String("1.2.3"))
   211  	require.True(t, ok)
   212  
   213  	for _, operator := range []Operator{OperatorSemVerEqual, OperatorSemVerGreaterThan, OperatorSemVerLessThan} {
   214  		t.Run(string(operator), func(t *testing.T) {
   215  			f := FeatureFlag{
   216  				Rules: []FlagRule{
   217  					{
   218  						Clauses: []Clause{
   219  							{
   220  								Op:     operator,
   221  								Values: []ldvalue.Value{ldvalue.String("1.2.3"), ldvalue.String("x"), ldvalue.Bool(false)},
   222  							},
   223  						},
   224  					},
   225  				},
   226  			}
   227  
   228  			assert.Nil(t, f.Rules[0].Clauses[0].preprocessed.values)
   229  
   230  			PreprocessFlag(&f)
   231  
   232  			p := f.Rules[0].Clauses[0].preprocessed.values
   233  			require.Len(t, p, 3)
   234  
   235  			assert.True(t, p[0].computed)
   236  			assert.True(t, p[0].valid)
   237  			assert.Equal(t, expected, p[0].parsedSemver)
   238  
   239  			assert.True(t, p[1].computed)
   240  			assert.False(t, p[1].valid)
   241  			assert.True(t, p[2].computed)
   242  			assert.False(t, p[2].valid)
   243  		})
   244  	}
   245  }
   246  
   247  func TestPreprocessSegmentBuildsIncludeAndExcludeMaps(t *testing.T) {
   248  	s := Segment{
   249  		Included: []string{"a", "b"},
   250  		Excluded: []string{"c"},
   251  		IncludedContexts: []SegmentTarget{
   252  			{ContextKind: ldcontext.Kind("org"), Values: []string{"x", "y"}},
   253  		},
   254  		ExcludedContexts: []SegmentTarget{
   255  			{ContextKind: ldcontext.Kind("org"), Values: []string{"z"}},
   256  		},
   257  	}
   258  
   259  	assert.Nil(t, s.preprocessed.includeMap)
   260  	assert.Nil(t, s.preprocessed.excludeMap)
   261  
   262  	PreprocessSegment(&s)
   263  
   264  	assert.Equal(t, map[string]struct{}{"a": {}, "b": {}}, s.preprocessed.includeMap)
   265  	assert.Equal(t, map[string]struct{}{"c": {}}, s.preprocessed.excludeMap)
   266  	assert.Equal(t, map[string]struct{}{"x": {}, "y": {}}, s.IncludedContexts[0].preprocessed.valuesMap)
   267  	assert.Equal(t, map[string]struct{}{"z": {}}, s.ExcludedContexts[0].preprocessed.valuesMap)
   268  }
   269  
   270  func TestPreprocessSegmentPreprocessesClausesInRules(t *testing.T) {
   271  	// We'll just check one kind of clause, and assume that the preprocessing works the same as in flag rules
   272  	s := Segment{
   273  		Rules: []SegmentRule{
   274  			{
   275  				Clauses: []Clause{
   276  					{
   277  						Op:     OperatorMatches,
   278  						Values: []ldvalue.Value{ldvalue.String("x*"), ldvalue.String("\\"), ldvalue.Int(3)},
   279  					},
   280  				},
   281  			},
   282  		},
   283  	}
   284  
   285  	assert.Nil(t, s.Rules[0].Clauses[0].preprocessed.values)
   286  
   287  	PreprocessSegment(&s)
   288  
   289  	p := s.Rules[0].Clauses[0].preprocessed.values
   290  	require.Len(t, p, 3)
   291  
   292  	assert.True(t, p[0].computed)
   293  	assert.True(t, p[0].valid)
   294  	assert.Equal(t, regexp.MustCompile("x*"), p[0].parsedRegexp)
   295  
   296  	assert.True(t, p[1].computed)
   297  	assert.False(t, p[1].valid)
   298  	assert.True(t, p[2].computed)
   299  	assert.False(t, p[2].valid)
   300  }
   301  

View as plain text