package rags import ( "flag" "fmt" "testing" "github.com/stretchr/testify/assert" ) func TestValue_IsBoolFlag(t *testing.T) { t.Parallel() tcs := []struct { val flag.Getter isBool bool }{ {&Value[bool]{}, true}, {&Value[bool]{Var: new(bool)}, true}, {&Value[bool]{Default: true}, true}, {&Value[int]{}, false}, {&Value[int]{Var: new(int)}, false}, {&Value[int]{Default: 100}, false}, } for i, tc := range tcs { i := i tc := tc t.Run(fmt.Sprintf("%s-%d", t.Name(), i), func(t *testing.T) { t.Parallel() b, ok := tc.val.(interface{ IsBoolFlag() bool }) assert.True(t, ok, "value was expected to implement IsBoolFlag()") if tc.isBool { assert.True(t, b.IsBoolFlag()) } else { assert.False(t, b.IsBoolFlag()) } }) } } func TestValue(t *testing.T) { t.Parallel() tcs := []struct { ptr *int def int set string }{ {}, {ptr: new(int)}, {def: 100}, {ptr: new(int), def: 100}, } for i, tc := range tcs { i := i tc := tc t.Run(fmt.Sprintf("%s-%d", t.Name(), i), func(t *testing.T) { t.Parallel() // Test initialization v := NewValueDefault(tc.ptr, tc.def) assert.True(t, v.initialized) assert.NotNil(t, v.Parse) if tc.ptr != nil { assert.Same(t, tc.ptr, v.Var) assert.Equal(t, *tc.ptr, tc.def) } assert.Equal(t, tc.def, *v.Var) assert.Equal(t, tc.def, v.Default) // Test setting values if tc.set != "" { assert.NoError(t, v.Set(tc.set)) assert.Equal(t, tc.set, v.String()) parsedVal, _ := v.Parse(tc.set) assert.Equal(t, parsedVal, v.Get()) assert.Equal(t, parsedVal, v.Var) // Verify pointer was updated without changing it // Only perform check if we created the value with a test pointer if tc.ptr != nil { assert.Same(t, tc.ptr, v.Var, "pointer used to instantiate flag value is different than values current pointer") assert.Equalf(t, tc.ptr, v.Get(), "pointer used to instantiate flag value references different value than "+ "returned by value: want %v, got %v", *tc.ptr, v.Get()) } } }) } } func TestCollections(t *testing.T) { t.Parallel() tcs := map[string]struct { def []string set []string expList []string expSet []string }{ "one": { set: []string{"prometheus"}, expList: []string{"prometheus"}, expSet: []string{"prometheus"}, }, "two": { set: []string{"prometheus", "linkerd"}, expList: []string{"prometheus", "linkerd"}, expSet: []string{"prometheus", "linkerd"}, }, "trailing multivalue comma": { set: []string{"prometheus,linkerd,"}, expList: []string{"prometheus", "linkerd"}, expSet: []string{"prometheus", "linkerd"}, }, "leading multivalue comma": { set: []string{",prometheus"}, expList: []string{"prometheus"}, expSet: []string{"prometheus"}, }, "dupes": { set: []string{"prometheus", "prometheus"}, expList: []string{"prometheus", "prometheus"}, expSet: []string{"prometheus"}, }, "blank": { set: []string{""}, expList: []string{""}, expSet: []string{""}, }, "blank comma": { set: []string{","}, expList: []string{""}, expSet: []string{""}, }, "defaults": { set: []string{"prometheus"}, expList: []string{"linkerd", "prometheus"}, expSet: []string{"linkerd", "prometheus"}, def: []string{"linkerd"}, }, "defaults duped": { set: []string{"prometheus"}, expList: []string{"prometheus", "prometheus"}, expSet: []string{"prometheus"}, def: []string{"prometheus"}, }, } testCollection := func(t *testing.T, v flag.Getter, set, exp []string) { t.Helper() for _, s := range set { assert.NoErrorf(t, v.Set(s), "setting valid value %s should not error", s) } assert.Equalf(t, collectionStringFn(exp), v.String(), "incorrect value after Set(): want %s, got %s", collectionStringFn(exp), v.String(), ) assert.Equalf(t, exp, v.Get(), "incorrect value after Set(): want %s, got %s", exp, v.Get(), ) } testPtr := func(t *testing.T, v flag.Getter, ptr, valPtr *[]string) { t.Helper() assert.Same(t, ptr, valPtr, "pointer used to instantiate flag value is different than values current pointer") vals := v.Get() for i := range *ptr { assert.Equalf(t, (*ptr)[i], (vals.([]string))[i], "pointer used to instantiate flag value references different value than "+ "returned by value: want %v, got %v", *ptr, v.Get()) } } for name, tc := range tcs { tc := tc name := name t.Run(name, func(t *testing.T) { t.Parallel() // Test List implementation t.Run(fmt.Sprintf("%s-List", name), func(t *testing.T) { t.Parallel() ptr := &([]string{}) v := &StringList{Var: ptr, Default: tc.def} assert.NotPanics(t, func() { v.initialize() }, "initializing List should not panic") assert.True(t, v.initialized) testCollection(t, v, tc.set, tc.expList) testPtr(t, v, ptr, v.Var) }) // Test Set implementation t.Run(fmt.Sprintf("%s-Set", name), func(t *testing.T) { t.Parallel() ptr := &([]string{}) v := &StringSet{Var: ptr, Default: tc.def} assert.NotPanics(t, func() { v.initialize() }, "initializing Set should not panic") assert.True(t, v.initialized) testCollection(t, v, tc.set, tc.expSet) testPtr(t, v, ptr, v.Var) }) }) } } func TestEnum(t *testing.T) { t.Parallel() var s string assert.Panics(t, func() { NewEnum(&s) }, "Enum value without any valid values specified should panic") v := NewEnum(&s, "foo", "bar") assert.Equalf(t, "foo", v.Default, "when default value isnt set, default should be first valid value: "+ "wanted %s, got %s", "foo", v.Default, ) v = &StringEnum{Var: &s, Default: "bar", Valid: []string{"foo", "bar"}} v.initialize() assert.Equalf(t, "bar", v.Default, "when default value is set explicitly, it should be honored if it is valid: "+ "wanted %s, got %s", "bar", v.Default, ) v = &StringEnum{Var: &s, Default: "baz", Valid: []string{"foo", "bar"}} v.initialize() assert.Equalf(t, "foo", v.Default, "when default value is set explicitly to an invalid value, it should be "+ "replaced with first valid value: wanted %s, got %s", "foo", v.Default, ) assert.Error(t, v.Set("baz"), "invalid value should cause an error") assert.NoError(t, v.Set("bar"), "setting a valid value should not cause an error") assert.Equal(t, "bar", v.Get(), "enum value should reflect updates after Set()") }