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