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.

242 lines
6.8 KiB

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