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.

231 lines
6.9 KiB

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