...

Source file src/github.com/launchdarkly/go-sdk-common/v3/ldvalue/value_map_test.go

Documentation: github.com/launchdarkly/go-sdk-common/v3/ldvalue

     1  package ldvalue
     2  
     3  import (
     4  	"fmt"
     5  	"sort"
     6  	"testing"
     7  
     8  	"github.com/stretchr/testify/assert"
     9  )
    10  
    11  func TestNilValueMap(t *testing.T) {
    12  	m := ValueMap{}
    13  	assert.False(t, m.IsDefined())
    14  	assert.Equal(t, 0, m.Count())
    15  	assert.Equal(t, Null(), m.Get("item"))
    16  }
    17  
    18  func TestCopyValueMap(t *testing.T) {
    19  	m := map[string]Value{"item0": Int(1), "item1": Int(2)}
    20  	vm1 := CopyValueMap(m)
    21  	assert.Equal(t, m, vm1.data)
    22  	shouldNotBeSameMap(t, m, vm1.data)
    23  
    24  	vm2 := CopyValueMap(nil)
    25  	assert.Nil(t, vm2.data)
    26  	assert.False(t, vm2.IsDefined())
    27  
    28  	vm3 := CopyValueMap(map[string]Value{})
    29  	assert.NotNil(t, vm3.data)
    30  	assert.Equal(t, 0, len(vm3.data))
    31  	assert.True(t, vm3.IsDefined())
    32  }
    33  
    34  func TestCopyArbitraryValueMap(t *testing.T) {
    35  	m := map[string]interface{}{"item0": "a", "item1": "b"}
    36  	vm1 := CopyArbitraryValueMap(m)
    37  	assert.Equal(t, map[string]Value{"item0": String("a"), "item1": String("b")}, vm1.data)
    38  
    39  	vm2 := CopyArbitraryValueMap(nil)
    40  	assert.Nil(t, vm2.data)
    41  }
    42  
    43  func TestValueMapBuild(t *testing.T) {
    44  	item0 := String("a")
    45  	item1 := Int(1)
    46  	b := ValueMapBuild().Set("item0", item0).Set("item1", item1)
    47  	valueMap := b.Build()
    48  
    49  	assert.Equal(t, 2, valueMap.Count())
    50  	keys := valueMap.Keys(nil)
    51  	sort.Strings(keys)
    52  	assert.Equal(t, []string{"item0", "item1"}, keys)
    53  
    54  	item0x := Bool(true)
    55  	b.Set("item0", item0x)
    56  	valueMapAfterModifyingBuilder := b.Build()
    57  	assert.Equal(t, item0x, valueMapAfterModifyingBuilder.Get("item0"))
    58  	assert.Equal(t, item0, valueMap.Get("item0")) // verifies builder's copy-on-write behavior
    59  
    60  	assert.True(t, valueMap.IsDefined())
    61  
    62  	m2 := ValueMapBuildWithCapacity(3).Set("item0", item0).Set("item1", item1).Build()
    63  	assert.Equal(t, valueMap, m2)
    64  }
    65  
    66  func TestValueMapBuildFromMap(t *testing.T) {
    67  	m0 := ValueMapBuild().Set("item0", Int(1)).Set("item1", Int(2)).Build()
    68  
    69  	m1 := ValueMapBuildFromMap(m0).Build()
    70  	assert.Equal(t, m0, m1)
    71  	shouldBeSameMap(t, m0.data, m1.data)
    72  
    73  	m2 := ValueMapBuild().Set("item2", Int(3))
    74  	m2.SetAllFromValueMap(m0)
    75  	assert.Equal(t, ValueMapBuild().Set("item0", Int(1)).Set("item1", Int(2)).Set("item2", Int(3)).Build(), m2.Build())
    76  
    77  	// test copy-on-write behavior
    78  	m3 := ValueMapBuild().Set("item0", Int(1)).Build()
    79  	m4 := ValueMapBuildFromMap(m3).Set("item1", Int(2)).Build()
    80  	assert.Equal(t, ValueMapBuild().Set("item0", Int(1)).Set("item1", Int(2)).Build(), m4)
    81  	shouldNotBeSameMap(t, m3.data, m4.data)
    82  	m5 := ValueMapBuild().SetAllFromValueMap(m3).Set("item1", Int(2)).Build()
    83  	assert.NotEqual(t, m3, m5)
    84  	shouldNotBeSameMap(t, m3.data, m5.data)
    85  	assert.Equal(t, m4, m5)
    86  }
    87  
    88  func TestValueMapBuilderRemove(t *testing.T) {
    89  	m0 := ValueMapBuild().Set("item0", Int(1)).Set("item1", Int(2)).Remove("item0").Build()
    90  	assert.Equal(t, ValueMapBuild().Set("item1", Int(2)).Build(), m0)
    91  
    92  	m1 := ValueMapBuildFromMap(m0).Remove("item1").Build()
    93  	assert.Equal(t, ValueMapBuild().Build(), m1)
    94  	assert.NotEqual(t, m0, m1)
    95  	shouldNotBeSameMap(t, m0.data, m1.data)
    96  }
    97  
    98  func TestValueMapBuilderHasKey(t *testing.T) {
    99  	var b ValueMapBuilder
   100  	assert.False(t, b.HasKey("key1"))
   101  	assert.False(t, b.HasKey("key2"))
   102  
   103  	b.Set("key1", Int(1))
   104  	assert.True(t, b.HasKey("key1"))
   105  	assert.False(t, b.HasKey("key2"))
   106  
   107  	b.Set("key2", Int(2))
   108  	assert.True(t, b.HasKey("key1"))
   109  	assert.True(t, b.HasKey("key2"))
   110  
   111  	b.Remove("key1")
   112  	assert.False(t, b.HasKey("key1"))
   113  	assert.True(t, b.HasKey("key2"))
   114  }
   115  
   116  func TestValueMapBuilderSafety(t *testing.T) {
   117  	// empty instance is safe to use
   118  	var emptyInstance ValueMapBuilder
   119  	emptyInstance.Set("key", Int(1))
   120  	assert.Equal(t, ValueMapBuild().Set("key", Int(1)).Build(), emptyInstance.Build())
   121  
   122  	// nil pointer is safe to use
   123  	var nilPtr *ValueMapBuilder
   124  	assert.Nil(t, nilPtr.Set("key", Int(1)))
   125  	assert.Nil(t, nilPtr.SetAllFromValueMap(ValueMap{}))
   126  	assert.Nil(t, nilPtr.Remove("key1"))
   127  	assert.Equal(t, ValueMap{}, nilPtr.Build())
   128  }
   129  
   130  func TestValueMapGetByKey(t *testing.T) {
   131  	item0 := String("a")
   132  	item1 := Int(1)
   133  	m := ValueMapBuild().Set("item0", item0).Set("item1", item1).Build()
   134  
   135  	assert.Equal(t, item0, m.Get("item0"))
   136  	assert.Equal(t, item1, m.Get("item1"))
   137  	assert.Equal(t, Null(), m.Get("bad-key"))
   138  
   139  	item, ok := m.TryGet("item0")
   140  	assert.True(t, ok)
   141  	assert.Equal(t, item0, item)
   142  	item, ok = m.TryGet("bad-key")
   143  	assert.False(t, ok)
   144  	assert.Equal(t, Null(), item)
   145  }
   146  
   147  func TestConvertValueMapToArbitraryValues(t *testing.T) {
   148  	m := ValueMapBuild().Set("x", ArrayOf(Int(2))).Build()
   149  	expected := map[string]interface{}{"x": []interface{}{float64(2)}}
   150  	assert.Equal(t, expected, m.AsArbitraryValueMap())
   151  }
   152  
   153  func TestConvertValueMapFromArbitraryValuesAndBackAgain(t *testing.T) {
   154  	mapValue0 := map[string]interface{}{"x": []interface{}{"b"}}
   155  	m := CopyArbitraryValueMap(mapValue0)
   156  	mapValue1 := m.AsArbitraryValueMap()
   157  	assert.Equal(t, mapValue0, mapValue1)
   158  	// Verify that the map was deep-copied
   159  	mapValue0["x"].([]interface{})[0] = "c"
   160  	assert.NotEqual(t, mapValue0, mapValue1)
   161  }
   162  
   163  func TestValueMapEqual(t *testing.T) {
   164  	valueFns := []func() ValueMap{
   165  		func() ValueMap { return ValueMap{} },
   166  		func() ValueMap { return ValueMapBuild().Build() },
   167  		func() ValueMap { return ValueMapBuild().Set("a", Int(1)).Build() },
   168  		func() ValueMap { return ValueMapBuild().Set("a", Int(2)).Build() },
   169  		func() ValueMap { return ValueMapBuild().Set("a", Int(1)).Set("b", Int(1)).Build() },
   170  	}
   171  	for i, fn0 := range valueFns {
   172  		v0 := fn0()
   173  		for j, fn1 := range valueFns {
   174  			v1 := fn1()
   175  			if i == j {
   176  				assert.True(t, v0.Equal(v1), "%s should equal %s", v0, v1)
   177  				assert.True(t, v0.Equal(v1), "%s should equal %s conversely", v1, v0)
   178  			} else {
   179  				assert.False(t, v0.Equal(v1), "%s should not equal %s", v0, v1)
   180  				assert.False(t, v1.Equal(v0), "%s should not equal %s", v1, v0)
   181  			}
   182  		}
   183  	}
   184  }
   185  
   186  func TestValueMapKeys(t *testing.T) {
   187  	assert.Nil(t, ValueMap{}.Keys(nil))
   188  	assert.Nil(t, ValueMapBuild().Build().Keys(nil))
   189  
   190  	m := ValueMapBuild().Set("a", Int(1)).Build()
   191  
   192  	assert.Equal(t, []string{"a"}, m.Keys(nil))
   193  	slice1 := []string{"x", "y", "z"}
   194  	assert.Equal(t, []string{"a"}, m.Keys(slice1))
   195  	assert.Equal(t, []string{"a", "y", "z"}, slice1) // proves slice was reused
   196  	slice2 := []string{}
   197  	assert.Equal(t, []string{"a"}, m.Keys(slice2))
   198  
   199  	m1 := ValueMapBuild().Set("a", Int(1)).Set("b", Int(2)).Set("c", Int(3)).Build()
   200  	keys := m1.Keys(nil)
   201  	sort.Strings(keys)
   202  	assert.Equal(t, []string{"a", "b", "c"}, keys)
   203  }
   204  
   205  func TestValueMapAsValue(t *testing.T) {
   206  	assert.Equal(t, Null(), ValueMap{}.AsValue())
   207  
   208  	m := ValueMapBuild().Set("a", Int(1)).Set("b", Int(2)).Build()
   209  	v := m.AsValue()
   210  	assert.Equal(t, ObjectBuild().Set("a", Int(1)).Set("b", Int(2)).Build(), v)
   211  	shouldBeSameMap(t, m.data, v.objectValue.data)
   212  }
   213  
   214  func TestValueMapAsMap(t *testing.T) {
   215  	assert.Nil(t, ValueMap{}.AsMap())
   216  
   217  	m := ValueMapBuild().Set("a", Bool(false)).Set("b", Bool(true)).Build()
   218  	mm := m.AsMap()
   219  	assert.Equal(t, map[string]Value{"a": Bool(false), "b": Bool(true)}, mm)
   220  	shouldNotBeSameMap(t, m.data, mm)
   221  }
   222  
   223  func TestValueMapAsArbitraryValueMap(t *testing.T) {
   224  	assert.Nil(t, ValueMap{}.AsArbitraryValueMap())
   225  
   226  	m := ValueMapBuild().Set("a", Bool(false)).Set("b", Bool(true)).Build()
   227  	mm := m.AsArbitraryValueMap()
   228  	assert.Equal(t, map[string]interface{}{"a": false, "b": true}, mm)
   229  }
   230  
   231  func TestTransformValueMap(t *testing.T) {
   232  	fnNoChanges := func(key string, value Value) (string, Value, bool) {
   233  		return key, value, true
   234  	}
   235  	fnAbsoluteValuesAndNoOddNumbers := func(key string, value Value) (string, Value, bool) {
   236  		if value.IntValue()%2 == 1 {
   237  			return key, value, false // first return value should be ignored since second one is false
   238  		}
   239  		if value.IntValue() < 0 {
   240  			return key, Int(-value.IntValue()), true
   241  		}
   242  		return key, value, true
   243  	}
   244  	fnTransformUsingKey := func(key string, value Value) (string, Value, bool) {
   245  		return key, String(fmt.Sprintf("%s=%s", key, value.JSONString())), true
   246  	}
   247  
   248  	m1 := ValueMapBuild().Set("a", Int(2)).Set("b", Int(4)).Set("c", Int(6)).Build()
   249  	m1a := m1.Transform(fnNoChanges)
   250  	m1b := m1.Transform(fnAbsoluteValuesAndNoOddNumbers)
   251  	// Should have no changes...
   252  	assert.Equal(t, m1, m1a)
   253  	assert.Equal(t, m1, m1b)
   254  	// ...and should be wrapping the *same* map, not a copy
   255  	m1.data["a"] = Int(0)
   256  	assert.Equal(t, m1, m1a)
   257  	assert.Equal(t, m1, m1b)
   258  
   259  	m2 := ValueMapBuild().Set("a", Int(2)).Set("b", Int(4)).Set("c", Int(1)).Set("d", Int(-6)).Build()
   260  	m2a := m2.Transform(fnNoChanges)
   261  	m2b := m2.Transform(fnAbsoluteValuesAndNoOddNumbers)
   262  	// m2a should have no changes, and should be wrapping the same map
   263  	assert.Equal(t, m2, m2a)
   264  	m2.data["a"] = Int(0)
   265  	assert.Equal(t, m2, m2a)
   266  	// m2b should have a transformed map
   267  	assert.Equal(t, ValueMapBuild().Set("a", Int(2)).Set("b", Int(4)).Set("d", Int(6)).Build(), m2b)
   268  
   269  	// Edge case where the only element is dropped
   270  	m3 := ValueMapBuild().Set("a", Int(1)).Build()
   271  	assert.Equal(t, ValueMapBuild().Build(), m3.Transform(fnAbsoluteValuesAndNoOddNumbers))
   272  
   273  	// Transformation function that uses the key parameter
   274  	m4 := ValueMapBuild().Set("a", Int(2)).Set("b", Int(4)).Build()
   275  	assert.Equal(t, ValueMapBuild().Set("a", String("a=2")).Set("b", String("b=4")).Build(),
   276  		m4.Transform(fnTransformUsingKey))
   277  
   278  	// Case where we guarantee that the first element we iterated through is *not* modified - map
   279  	// iteration order is nondeterministic, we just want to verify that we've hit all code paths
   280  	n := 0
   281  	fnTransformUsingKeyButNotFirst := func(key string, value Value) (string, Value, bool) {
   282  		n++
   283  		if n > 1 {
   284  			return fnTransformUsingKey(key, value)
   285  		}
   286  		return key, value, true
   287  	}
   288  	m5 := m4.Transform(fnTransformUsingKeyButNotFirst)
   289  	assert.NotEqual(t, m4, m5)
   290  	assert.Equal(t, m4.Count(), m5.Count())
   291  	if m5.Get("a").IsNumber() {
   292  		assert.Equal(t, Int(2), m5.Get("a"))
   293  		assert.Equal(t, String("b=4"), m5.Get("b"))
   294  	} else {
   295  		assert.Equal(t, String("a=2"), m5.Get("a"))
   296  		assert.Equal(t, Int(4), m5.Get("b"))
   297  	}
   298  
   299  	shouldNotCallThis := func(key string, value Value) (string, Value, bool) {
   300  		assert.Fail(t, "should not have called function")
   301  		return key, value, true
   302  	}
   303  	assert.Equal(t, ValueMap{}, ValueMap{}.Transform(shouldNotCallThis))
   304  	assert.Equal(t, ValueMapBuild().Build(), ValueMapBuild().Build().Transform(shouldNotCallThis))
   305  }
   306  
   307  func shouldBeSameMap(t *testing.T, m0 map[string]Value, m1 map[string]Value) {
   308  	m0["temp-property"] = Bool(true)
   309  	assert.Equal(t, m0, m1, "ValueMaps should be sharing same map but it was copied instead")
   310  	delete(m0, "temp-property")
   311  }
   312  
   313  func shouldNotBeSameMap(t *testing.T, m0 map[string]Value, m1 map[string]Value) {
   314  	m0["temp-property"] = Bool(true)
   315  	assert.NotEqual(t, m0, m1, "ValueMaps should not be sharing same map but they are")
   316  	delete(m0, "temp-property")
   317  }
   318  

View as plain text