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.

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