...

Source file src/github.com/launchdarkly/go-server-sdk/v6/interfaces/flagstate/flags_state_test.go

Documentation: github.com/launchdarkly/go-server-sdk/v6/interfaces/flagstate

     1  package flagstate
     2  
     3  import (
     4  	"testing"
     5  
     6  	"github.com/launchdarkly/go-sdk-common/v3/ldreason"
     7  	"github.com/launchdarkly/go-sdk-common/v3/ldtime"
     8  	"github.com/launchdarkly/go-sdk-common/v3/ldvalue"
     9  
    10  	"github.com/stretchr/testify/assert"
    11  )
    12  
    13  func TestAllFlags(t *testing.T) {
    14  	t.Run("IsValid", func(t *testing.T) {
    15  		assert.False(t, AllFlags{}.IsValid())
    16  		assert.True(t, AllFlags{valid: true}.IsValid())
    17  	})
    18  
    19  	t.Run("GetFlag", func(t *testing.T) {
    20  		f := FlagState{}
    21  		a := AllFlags{
    22  			flags: map[string]FlagState{"known-flag": f},
    23  		}
    24  
    25  		f1, ok := a.GetFlag("known-flag")
    26  		assert.True(t, ok)
    27  		assert.Equal(t, f, f1)
    28  
    29  		f2, ok := a.GetFlag("unknown-flag")
    30  		assert.False(t, ok)
    31  		assert.Equal(t, FlagState{}, f2)
    32  	})
    33  
    34  	t.Run("GetValue", func(t *testing.T) {
    35  		f := FlagState{Value: ldvalue.String("hi")}
    36  		a := AllFlags{
    37  			flags: map[string]FlagState{"known-flag": f},
    38  		}
    39  
    40  		assert.Equal(t, f.Value, a.GetValue("known-flag"))
    41  		assert.Equal(t, ldvalue.Null(), a.GetValue("unknown-flag"))
    42  	})
    43  
    44  	t.Run("ToValuesMap", func(t *testing.T) {
    45  		a0 := AllFlags{}
    46  		assert.Len(t, a0.ToValuesMap(), 0)
    47  		assert.NotNil(t, a0.ToValuesMap())
    48  
    49  		a1 := AllFlags{
    50  			flags: map[string]FlagState{
    51  				"flag1": {Value: ldvalue.String("value1")},
    52  				"flag2": {Value: ldvalue.String("value2")},
    53  			},
    54  		}
    55  		assert.Equal(t, map[string]ldvalue.Value{
    56  			"flag1": ldvalue.String("value1"),
    57  			"flag2": ldvalue.String("value2"),
    58  		}, a1.ToValuesMap())
    59  	})
    60  }
    61  
    62  func TestAllFlagsJSON(t *testing.T) {
    63  	t.Run("invalid state", func(t *testing.T) {
    64  		bytes, err := AllFlags{}.MarshalJSON()
    65  		assert.NoError(t, err)
    66  		assert.JSONEq(t, `{"$valid":false,"$flagsState":{}}`, string(bytes))
    67  	})
    68  
    69  	t.Run("minimal flag", func(t *testing.T) {
    70  		a := AllFlags{
    71  			valid: true,
    72  			flags: map[string]FlagState{
    73  				"flag1": {
    74  					Value:   ldvalue.String("value1"),
    75  					Version: 1000,
    76  				},
    77  			},
    78  		}
    79  		bytes, err := a.MarshalJSON()
    80  		assert.NoError(t, err)
    81  		assert.JSONEq(t,
    82  			`{
    83    "$valid":true,
    84    "flag1": "value1",
    85    "$flagsState":{
    86      "flag1": {"version":1000}
    87    }
    88  }`, string(bytes))
    89  	})
    90  
    91  	t.Run("flag with all properties except trackReason", func(t *testing.T) {
    92  		a := AllFlags{
    93  			valid: true,
    94  			flags: map[string]FlagState{
    95  				"flag1": {
    96  					Value:                ldvalue.String("value1"),
    97  					Variation:            ldvalue.NewOptionalInt(1),
    98  					Version:              1000,
    99  					Reason:               ldreason.NewEvalReasonFallthrough(),
   100  					TrackEvents:          true,
   101  					DebugEventsUntilDate: ldtime.UnixMillisecondTime(100000),
   102  				},
   103  			},
   104  		}
   105  		bytes, err := a.MarshalJSON()
   106  		assert.NoError(t, err)
   107  		assert.JSONEq(t,
   108  			`{
   109    "$valid":true,
   110    "flag1": "value1",
   111    "$flagsState":{
   112      "flag1": {"variation":1,"version":1000,"reason":{"kind":"FALLTHROUGH"},"trackEvents":true,"debugEventsUntilDate":100000}
   113    }
   114  }`, string(bytes))
   115  	})
   116  
   117  	t.Run("flag with trackReason", func(t *testing.T) {
   118  		a := AllFlags{
   119  			valid: true,
   120  			flags: map[string]FlagState{
   121  				"flag1": {
   122  					Value:       ldvalue.String("value1"),
   123  					Variation:   ldvalue.NewOptionalInt(1),
   124  					Version:     1000,
   125  					Reason:      ldreason.NewEvalReasonFallthrough(),
   126  					TrackEvents: true,
   127  					TrackReason: true,
   128  				},
   129  			},
   130  		}
   131  		bytes, err := a.MarshalJSON()
   132  		assert.NoError(t, err)
   133  		assert.JSONEq(t,
   134  			`{
   135    "$valid":true,
   136    "flag1": "value1",
   137    "$flagsState":{
   138      "flag1": {"variation":1,"version":1000,"reason":{"kind":"FALLTHROUGH"},"trackEvents":true,"trackReason":true}
   139    }
   140  }`, string(bytes))
   141  	})
   142  
   143  	t.Run("omitting details", func(t *testing.T) {
   144  		a := AllFlags{
   145  			valid: true,
   146  			flags: map[string]FlagState{
   147  				"flag1": {
   148  					Value:       ldvalue.String("value1"),
   149  					Variation:   ldvalue.NewOptionalInt(1),
   150  					Version:     1000,
   151  					Reason:      ldreason.NewEvalReasonFallthrough(),
   152  					OmitDetails: true,
   153  				},
   154  			},
   155  		}
   156  		bytes, err := a.MarshalJSON()
   157  		assert.NoError(t, err)
   158  		assert.JSONEq(t,
   159  			`{
   160    "$valid":true,
   161    "flag1": "value1",
   162    "$flagsState":{
   163      "flag1": {"variation":1}
   164    }
   165  }`, string(bytes))
   166  	})
   167  }
   168  
   169  func TestAllFlagsBuilder(t *testing.T) {
   170  	t.Run("result is always valid", func(t *testing.T) {
   171  		assert.True(t, NewAllFlagsBuilder().Build().IsValid())
   172  	})
   173  
   174  	t.Run("add flags without reasons", func(t *testing.T) {
   175  		b := NewAllFlagsBuilder()
   176  
   177  		flag1 := FlagState{
   178  			Value:     ldvalue.String("value1"),
   179  			Variation: ldvalue.NewOptionalInt(1),
   180  			Version:   1000,
   181  			Reason:    ldreason.NewEvalReasonFallthrough(),
   182  		}
   183  		flag2 := FlagState{
   184  			Value:                ldvalue.String("value2"),
   185  			Version:              2000,
   186  			Reason:               ldreason.NewEvalReasonError(ldreason.EvalErrorException),
   187  			TrackEvents:          true,
   188  			DebugEventsUntilDate: ldtime.UnixMillisecondTime(100000),
   189  		}
   190  		b.AddFlag("flag1", flag1)
   191  		b.AddFlag("flag2", flag2)
   192  
   193  		flag1WithoutReason, flag2WithoutReason := flag1, flag2
   194  		flag1WithoutReason.Reason = ldreason.EvaluationReason{}
   195  		flag2WithoutReason.Reason = ldreason.EvaluationReason{}
   196  
   197  		a := b.Build()
   198  		assert.Equal(t, map[string]FlagState{
   199  			"flag1": flag1WithoutReason,
   200  			"flag2": flag2WithoutReason,
   201  		}, a.flags)
   202  	})
   203  
   204  	t.Run("add flags with reasons", func(t *testing.T) {
   205  		b := NewAllFlagsBuilder(OptionWithReasons())
   206  
   207  		flag1 := FlagState{
   208  			Value:     ldvalue.String("value1"),
   209  			Variation: ldvalue.NewOptionalInt(1),
   210  			Version:   1000,
   211  			Reason:    ldreason.NewEvalReasonFallthrough(),
   212  		}
   213  		flag2 := FlagState{
   214  			Value:                ldvalue.String("value2"),
   215  			Version:              2000,
   216  			Reason:               ldreason.NewEvalReasonError(ldreason.EvalErrorException),
   217  			TrackEvents:          true,
   218  			TrackReason:          true,
   219  			DebugEventsUntilDate: ldtime.UnixMillisecondTime(100000),
   220  		}
   221  		b.AddFlag("flag1", flag1)
   222  		b.AddFlag("flag2", flag2)
   223  
   224  		a := b.Build()
   225  		assert.Equal(t, map[string]FlagState{
   226  			"flag1": flag1,
   227  			"flag2": flag2,
   228  		}, a.flags)
   229  	})
   230  
   231  	t.Run("add flags with details only if tracked", func(t *testing.T) {
   232  		b := NewAllFlagsBuilder(OptionWithReasons(), OptionDetailsOnlyForTrackedFlags())
   233  
   234  		// flag1 should not get full details
   235  		flag1 := FlagState{
   236  			Value:     ldvalue.String("value1"),
   237  			Variation: ldvalue.NewOptionalInt(1),
   238  			Version:   1000,
   239  			Reason:    ldreason.NewEvalReasonFallthrough(),
   240  		}
   241  
   242  		// flag2 does not get a reason because, even though DebugEventsUntilDate is set, debugging
   243  		// has already expired (the timestamp is in the past)
   244  		flag2 := FlagState{
   245  			Value:                ldvalue.String("value2"),
   246  			Variation:            ldvalue.NewOptionalInt(2),
   247  			Version:              2000,
   248  			Reason:               ldreason.NewEvalReasonFallthrough(),
   249  			DebugEventsUntilDate: ldtime.UnixMillisecondTime(1),
   250  		}
   251  
   252  		// flag3 gets a reason because TrackEvents is true
   253  		flag3 := FlagState{
   254  			Value:       ldvalue.String("value3"),
   255  			Variation:   ldvalue.NewOptionalInt(3),
   256  			Version:     3000,
   257  			Reason:      ldreason.NewEvalReasonRuleMatch(3, "rule3"),
   258  			TrackEvents: true,
   259  		}
   260  
   261  		// flag4 gets a reason because DebugEventsUntilDate is set and is in the future
   262  		flag4 := FlagState{
   263  			Value:                ldvalue.String("value4"),
   264  			Variation:            ldvalue.NewOptionalInt(4),
   265  			Version:              4000,
   266  			Reason:               ldreason.NewEvalReasonRuleMatch(4, "rule4"),
   267  			DebugEventsUntilDate: ldtime.UnixMillisNow() + 10000,
   268  		}
   269  
   270  		// flag5 gets a reason because TrackReason is true
   271  		flag5 := FlagState{
   272  			Value:       ldvalue.String("value5"),
   273  			Variation:   ldvalue.NewOptionalInt(5),
   274  			Version:     5000,
   275  			Reason:      ldreason.NewEvalReasonRuleMatch(5, "rule5"),
   276  			TrackReason: true,
   277  		}
   278  
   279  		b.AddFlag("flag1", flag1)
   280  		b.AddFlag("flag2", flag2)
   281  		b.AddFlag("flag3", flag3)
   282  		b.AddFlag("flag4", flag4)
   283  		b.AddFlag("flag5", flag5)
   284  
   285  		flag1WithoutDetails, flag2WithoutDetails := flag1, flag2
   286  		flag1WithoutDetails.OmitDetails = true
   287  		flag2WithoutDetails.OmitDetails = true
   288  
   289  		a := b.Build()
   290  		assert.Equal(t, map[string]FlagState{
   291  			"flag1": flag1WithoutDetails,
   292  			"flag2": flag2WithoutDetails,
   293  			"flag3": flag3,
   294  			"flag4": flag4,
   295  			"flag5": flag5,
   296  		}, a.flags)
   297  	})
   298  }
   299  
   300  func TestAllFlagsOptions(t *testing.T) {
   301  	assert.Equal(t, "ClientSideOnly", OptionClientSideOnly().String())
   302  	assert.Equal(t, "WithReasons", OptionWithReasons().String())
   303  	assert.Equal(t, "DetailsOnlyForTrackedFlags", OptionDetailsOnlyForTrackedFlags().String())
   304  }
   305  

View as plain text