You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

237 lines
6.7 KiB

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