github.com/vipernet-xyz/tm@v0.34.24/libs/cli/setup_test.go (about) 1 package cli 2 3 import ( 4 "fmt" 5 "os" 6 "strconv" 7 "strings" 8 "testing" 9 10 "github.com/spf13/cobra" 11 "github.com/spf13/viper" 12 "github.com/stretchr/testify/assert" 13 "github.com/stretchr/testify/require" 14 ) 15 16 func TestSetupEnv(t *testing.T) { 17 cases := []struct { 18 args []string 19 env map[string]string 20 expected string 21 }{ 22 {nil, nil, ""}, 23 {[]string{"--foobar", "bang!"}, nil, "bang!"}, 24 // make sure reset is good 25 {nil, nil, ""}, 26 // test both variants of the prefix 27 {nil, map[string]string{"DEMO_FOOBAR": "good"}, "good"}, 28 {nil, map[string]string{"DEMOFOOBAR": "silly"}, "silly"}, 29 // and that cli overrides env... 30 { 31 []string{"--foobar", "important"}, 32 map[string]string{"DEMO_FOOBAR": "ignored"}, 33 "important", 34 }, 35 } 36 37 for idx, tc := range cases { 38 i := strconv.Itoa(idx) 39 // test command that store value of foobar in local variable 40 var foo string 41 demo := &cobra.Command{ 42 Use: "demo", 43 RunE: func(cmd *cobra.Command, args []string) error { 44 foo = viper.GetString("foobar") 45 return nil 46 }, 47 } 48 demo.Flags().String("foobar", "", "Some test value from config") 49 cmd := PrepareBaseCmd(demo, "DEMO", "/qwerty/asdfgh") // some missing dir.. 50 cmd.Exit = func(int) {} 51 52 viper.Reset() 53 args := append([]string{cmd.Use}, tc.args...) 54 err := RunWithArgs(cmd, args, tc.env) 55 require.Nil(t, err, i) 56 assert.Equal(t, tc.expected, foo, i) 57 } 58 } 59 60 func tempDir() string { 61 cdir, err := os.MkdirTemp("", "test-cli") 62 if err != nil { 63 panic(err) 64 } 65 return cdir 66 } 67 68 func TestSetupConfig(t *testing.T) { 69 // we pre-create two config files we can refer to in the rest of 70 // the test cases. 71 cval1 := "fubble" 72 conf1 := tempDir() 73 err := WriteConfigVals(conf1, map[string]string{"boo": cval1}) 74 require.Nil(t, err) 75 76 cases := []struct { 77 args []string 78 env map[string]string 79 expected string 80 expectedTwo string 81 }{ 82 {nil, nil, "", ""}, 83 // setting on the command line 84 {[]string{"--boo", "haha"}, nil, "haha", ""}, 85 {[]string{"--two-words", "rocks"}, nil, "", "rocks"}, 86 {[]string{"--home", conf1}, nil, cval1, ""}, 87 // test both variants of the prefix 88 {nil, map[string]string{"RD_BOO": "bang"}, "bang", ""}, 89 {nil, map[string]string{"RD_TWO_WORDS": "fly"}, "", "fly"}, 90 {nil, map[string]string{"RDTWO_WORDS": "fly"}, "", "fly"}, 91 {nil, map[string]string{"RD_HOME": conf1}, cval1, ""}, 92 {nil, map[string]string{"RDHOME": conf1}, cval1, ""}, 93 } 94 95 for idx, tc := range cases { 96 i := strconv.Itoa(idx) 97 // test command that store value of foobar in local variable 98 var foo, two string 99 boo := &cobra.Command{ 100 Use: "reader", 101 RunE: func(cmd *cobra.Command, args []string) error { 102 foo = viper.GetString("boo") 103 two = viper.GetString("two-words") 104 return nil 105 }, 106 } 107 boo.Flags().String("boo", "", "Some test value from config") 108 boo.Flags().String("two-words", "", "Check out env handling -") 109 cmd := PrepareBaseCmd(boo, "RD", "/qwerty/asdfgh") // some missing dir... 110 cmd.Exit = func(int) {} 111 112 viper.Reset() 113 args := append([]string{cmd.Use}, tc.args...) 114 err := RunWithArgs(cmd, args, tc.env) 115 require.Nil(t, err, i) 116 assert.Equal(t, tc.expected, foo, i) 117 assert.Equal(t, tc.expectedTwo, two, i) 118 } 119 } 120 121 type DemoConfig struct { 122 Name string `mapstructure:"name"` 123 Age int `mapstructure:"age"` 124 Unused int `mapstructure:"unused"` 125 } 126 127 func TestSetupUnmarshal(t *testing.T) { 128 // we pre-create two config files we can refer to in the rest of 129 // the test cases. 130 cval1, cval2 := "someone", "else" 131 conf1 := tempDir() 132 err := WriteConfigVals(conf1, map[string]string{"name": cval1}) 133 require.Nil(t, err) 134 // even with some ignored fields, should be no problem 135 conf2 := tempDir() 136 err = WriteConfigVals(conf2, map[string]string{"name": cval2, "foo": "bar"}) 137 require.Nil(t, err) 138 139 // unused is not declared on a flag and remains from base 140 base := DemoConfig{ 141 Name: "default", 142 Age: 42, 143 Unused: -7, 144 } 145 c := func(name string, age int) DemoConfig { 146 r := base 147 // anything set on the flags as a default is used over 148 // the default config object 149 r.Name = "from-flag" 150 if name != "" { 151 r.Name = name 152 } 153 if age != 0 { 154 r.Age = age 155 } 156 return r 157 } 158 159 cases := []struct { 160 args []string 161 env map[string]string 162 expected DemoConfig 163 }{ 164 {nil, nil, c("", 0)}, 165 // setting on the command line 166 {[]string{"--name", "haha"}, nil, c("haha", 0)}, 167 {[]string{"--home", conf1}, nil, c(cval1, 0)}, 168 // test both variants of the prefix 169 {nil, map[string]string{"MR_AGE": "56"}, c("", 56)}, 170 {nil, map[string]string{"MR_HOME": conf1}, c(cval1, 0)}, 171 {[]string{"--age", "17"}, map[string]string{"MRHOME": conf2}, c(cval2, 17)}, 172 } 173 174 for idx, tc := range cases { 175 i := strconv.Itoa(idx) 176 // test command that store value of foobar in local variable 177 cfg := base 178 marsh := &cobra.Command{ 179 Use: "marsh", 180 RunE: func(cmd *cobra.Command, args []string) error { 181 return viper.Unmarshal(&cfg) 182 }, 183 } 184 marsh.Flags().String("name", "from-flag", "Some test value from config") 185 // if we want a flag to use the proper default, then copy it 186 // from the default config here 187 marsh.Flags().Int("age", base.Age, "Some test value from config") 188 cmd := PrepareBaseCmd(marsh, "MR", "/qwerty/asdfgh") // some missing dir... 189 cmd.Exit = func(int) {} 190 191 viper.Reset() 192 args := append([]string{cmd.Use}, tc.args...) 193 err := RunWithArgs(cmd, args, tc.env) 194 require.Nil(t, err, i) 195 assert.Equal(t, tc.expected, cfg, i) 196 } 197 } 198 199 func TestSetupTrace(t *testing.T) { 200 cases := []struct { 201 args []string 202 env map[string]string 203 long bool 204 expected string 205 }{ 206 {nil, nil, false, "trace flag = false"}, 207 {[]string{"--trace"}, nil, true, "trace flag = true"}, 208 {[]string{"--no-such-flag"}, nil, false, "unknown flag: --no-such-flag"}, 209 {nil, map[string]string{"DBG_TRACE": "true"}, true, "trace flag = true"}, 210 } 211 212 for idx, tc := range cases { 213 i := strconv.Itoa(idx) 214 // test command that store value of foobar in local variable 215 trace := &cobra.Command{ 216 Use: "trace", 217 RunE: func(cmd *cobra.Command, args []string) error { 218 return fmt.Errorf("trace flag = %t", viper.GetBool(TraceFlag)) 219 }, 220 } 221 cmd := PrepareBaseCmd(trace, "DBG", "/qwerty/asdfgh") // some missing dir.. 222 cmd.Exit = func(int) {} 223 224 viper.Reset() 225 args := append([]string{cmd.Use}, tc.args...) 226 stdout, stderr, err := RunCaptureWithArgs(cmd, args, tc.env) 227 require.NotNil(t, err, i) 228 require.Equal(t, "", stdout, i) 229 require.NotEqual(t, "", stderr, i) 230 msg := strings.Split(stderr, "\n") 231 desired := fmt.Sprintf("ERROR: %s", tc.expected) 232 assert.Equal(t, desired, msg[0], i) 233 t.Log(msg) 234 if tc.long && assert.True(t, len(msg) > 2, i) { 235 // the next line starts the stack trace... 236 assert.Contains(t, stderr, "TestSetupTrace", i) 237 assert.Contains(t, stderr, "setup_test.go", i) 238 } 239 } 240 }