1package cobra
2
3import (
4	"bytes"
5	"fmt"
6	"os"
7	"reflect"
8	"strings"
9	"testing"
10
11	"github.com/spf13/pflag"
12)
13
14// test to ensure hidden commands run as intended
15func TestHiddenCommandExecutes(t *testing.T) {
16
17	// ensure that outs does not already equal what the command will be setting it
18	// to, if it did this test would not actually be testing anything...
19	if outs == "hidden" {
20		t.Errorf("outs should NOT EQUAL hidden")
21	}
22
23	cmdHidden.Execute()
24
25	// upon running the command, the value of outs should now be 'hidden'
26	if outs != "hidden" {
27		t.Errorf("Hidden command failed to run!")
28	}
29}
30
31// test to ensure hidden commands do not show up in usage/help text
32func TestHiddenCommandIsHidden(t *testing.T) {
33	if cmdHidden.IsAvailableCommand() {
34		t.Errorf("Hidden command found!")
35	}
36}
37
38func TestStripFlags(t *testing.T) {
39	tests := []struct {
40		input  []string
41		output []string
42	}{
43		{
44			[]string{"foo", "bar"},
45			[]string{"foo", "bar"},
46		},
47		{
48			[]string{"foo", "--bar", "-b"},
49			[]string{"foo"},
50		},
51		{
52			[]string{"-b", "foo", "--bar", "bar"},
53			[]string{},
54		},
55		{
56			[]string{"-i10", "echo"},
57			[]string{"echo"},
58		},
59		{
60			[]string{"-i=10", "echo"},
61			[]string{"echo"},
62		},
63		{
64			[]string{"--int=100", "echo"},
65			[]string{"echo"},
66		},
67		{
68			[]string{"-ib", "echo", "-bfoo", "baz"},
69			[]string{"echo", "baz"},
70		},
71		{
72			[]string{"-i=baz", "bar", "-i", "foo", "blah"},
73			[]string{"bar", "blah"},
74		},
75		{
76			[]string{"--int=baz", "-bbar", "-i", "foo", "blah"},
77			[]string{"blah"},
78		},
79		{
80			[]string{"--cat", "bar", "-i", "foo", "blah"},
81			[]string{"bar", "blah"},
82		},
83		{
84			[]string{"-c", "bar", "-i", "foo", "blah"},
85			[]string{"bar", "blah"},
86		},
87		{
88			[]string{"--persist", "bar"},
89			[]string{"bar"},
90		},
91		{
92			[]string{"-p", "bar"},
93			[]string{"bar"},
94		},
95	}
96
97	cmdPrint := &Command{
98		Use:   "print [string to print]",
99		Short: "Print anything to the screen",
100		Long:  `an utterly useless command for testing.`,
101		Run: func(cmd *Command, args []string) {
102			tp = args
103		},
104	}
105
106	var flagi int
107	var flagstr string
108	var flagbool bool
109	cmdPrint.PersistentFlags().BoolVarP(&flagbool, "persist", "p", false, "help for persistent one")
110	cmdPrint.Flags().IntVarP(&flagi, "int", "i", 345, "help message for flag int")
111	cmdPrint.Flags().StringVarP(&flagstr, "bar", "b", "bar", "help message for flag string")
112	cmdPrint.Flags().BoolVarP(&flagbool, "cat", "c", false, "help message for flag bool")
113
114	for _, test := range tests {
115		output := stripFlags(test.input, cmdPrint)
116		if !reflect.DeepEqual(test.output, output) {
117			t.Errorf("expected: %v, got: %v", test.output, output)
118		}
119	}
120}
121
122func TestDisableFlagParsing(t *testing.T) {
123	targs := []string{}
124	cmdPrint := &Command{
125		DisableFlagParsing: true,
126		Run: func(cmd *Command, args []string) {
127			targs = args
128		},
129	}
130	args := []string{"cmd", "-v", "-race", "-file", "foo.go"}
131	cmdPrint.SetArgs(args)
132	err := cmdPrint.Execute()
133	if err != nil {
134		t.Error(err)
135	}
136	if !reflect.DeepEqual(args, targs) {
137		t.Errorf("expected: %v, got: %v", args, targs)
138	}
139}
140
141func TestInitHelpFlagMergesFlags(t *testing.T) {
142	usage := "custom flag"
143	baseCmd := Command{Use: "testcmd"}
144	baseCmd.PersistentFlags().Bool("help", false, usage)
145	cmd := Command{Use: "do"}
146	baseCmd.AddCommand(&cmd)
147
148	cmd.InitDefaultHelpFlag()
149	actual := cmd.Flags().Lookup("help").Usage
150	if actual != usage {
151		t.Fatalf("Expected the help flag from the base command with usage '%s', but got the default with usage '%s'", usage, actual)
152	}
153}
154
155func TestCommandsAreSorted(t *testing.T) {
156	EnableCommandSorting = true
157
158	originalNames := []string{"middle", "zlast", "afirst"}
159	expectedNames := []string{"afirst", "middle", "zlast"}
160
161	var tmpCommand = &Command{Use: "tmp"}
162
163	for _, name := range originalNames {
164		tmpCommand.AddCommand(&Command{Use: name})
165	}
166
167	for i, c := range tmpCommand.Commands() {
168		if expectedNames[i] != c.Name() {
169			t.Errorf("expected: %s, got: %s", expectedNames[i], c.Name())
170		}
171	}
172
173	EnableCommandSorting = true
174}
175
176func TestEnableCommandSortingIsDisabled(t *testing.T) {
177	EnableCommandSorting = false
178
179	originalNames := []string{"middle", "zlast", "afirst"}
180
181	var tmpCommand = &Command{Use: "tmp"}
182
183	for _, name := range originalNames {
184		tmpCommand.AddCommand(&Command{Use: name})
185	}
186
187	for i, c := range tmpCommand.Commands() {
188		if originalNames[i] != c.Name() {
189			t.Errorf("expected: %s, got: %s", originalNames[i], c.Name())
190		}
191	}
192
193	EnableCommandSorting = true
194}
195
196func TestSetOutput(t *testing.T) {
197	cmd := &Command{}
198	cmd.SetOutput(nil)
199	if out := cmd.OutOrStdout(); out != os.Stdout {
200		t.Fatalf("expected setting output to nil to revert back to stdout, got %v", out)
201	}
202}
203
204func TestFlagErrorFunc(t *testing.T) {
205	cmd := &Command{
206		Use: "print",
207		RunE: func(cmd *Command, args []string) error {
208			return nil
209		},
210	}
211	expectedFmt := "This is expected: %s"
212
213	cmd.SetFlagErrorFunc(func(c *Command, err error) error {
214		return fmt.Errorf(expectedFmt, err)
215	})
216	cmd.SetArgs([]string{"--bogus-flag"})
217	cmd.SetOutput(new(bytes.Buffer))
218
219	err := cmd.Execute()
220
221	expected := fmt.Sprintf(expectedFmt, "unknown flag: --bogus-flag")
222	if err.Error() != expected {
223		t.Errorf("expected %v, got %v", expected, err.Error())
224	}
225}
226
227// TestSortedFlags checks,
228// if cmd.LocalFlags() is unsorted when cmd.Flags().SortFlags set to false.
229// Related to https://github.com/spf13/cobra/issues/404.
230func TestSortedFlags(t *testing.T) {
231	cmd := &Command{}
232	cmd.Flags().SortFlags = false
233	names := []string{"C", "B", "A", "D"}
234	for _, name := range names {
235		cmd.Flags().Bool(name, false, "")
236	}
237
238	i := 0
239	cmd.LocalFlags().VisitAll(func(f *pflag.Flag) {
240		if i == len(names) {
241			return
242		}
243		if isStringInStringSlice(f.Name, names) {
244			if names[i] != f.Name {
245				t.Errorf("Incorrect order. Expected %v, got %v", names[i], f.Name)
246			}
247			i++
248		}
249	})
250}
251
252// contains checks, if s is in ss.
253func isStringInStringSlice(s string, ss []string) bool {
254	for _, v := range ss {
255		if v == s {
256			return true
257		}
258	}
259	return false
260}
261
262// TestHelpFlagInHelp checks,
263// if '--help' flag is shown in help for child (executing `parent help child`),
264// that has no other flags.
265// Related to https://github.com/spf13/cobra/issues/302.
266func TestHelpFlagInHelp(t *testing.T) {
267	output := new(bytes.Buffer)
268	parent := &Command{Use: "parent", Run: func(*Command, []string) {}}
269	parent.SetOutput(output)
270
271	child := &Command{Use: "child", Run: func(*Command, []string) {}}
272	parent.AddCommand(child)
273
274	parent.SetArgs([]string{"help", "child"})
275	err := parent.Execute()
276	if err != nil {
277		t.Fatal(err)
278	}
279
280	if !strings.Contains(output.String(), "[flags]") {
281		t.Errorf("\nExpecting to contain: %v\nGot: %v", "[flags]", output.String())
282	}
283}
284
285// TestMergeCommandLineToFlags checks,
286// if pflag.CommandLine is correctly merged to c.Flags() after first call
287// of c.mergePersistentFlags.
288// Related to https://github.com/spf13/cobra/issues/443.
289func TestMergeCommandLineToFlags(t *testing.T) {
290	pflag.Bool("boolflag", false, "")
291	c := &Command{Use: "c", Run: func(*Command, []string) {}}
292	c.mergePersistentFlags()
293	if c.Flags().Lookup("boolflag") == nil {
294		t.Fatal("Expecting to have flag from CommandLine in c.Flags()")
295	}
296
297	// Reset pflag.CommandLine flagset.
298	pflag.CommandLine = pflag.NewFlagSet(os.Args[0], pflag.ExitOnError)
299}
300
301// TestUseDeprecatedFlags checks,
302// if cobra.Execute() prints a message, if a deprecated flag is used.
303// Related to https://github.com/spf13/cobra/issues/463.
304func TestUseDeprecatedFlags(t *testing.T) {
305	c := &Command{Use: "c", Run: func(*Command, []string) {}}
306	output := new(bytes.Buffer)
307	c.SetOutput(output)
308	c.Flags().BoolP("deprecated", "d", false, "deprecated flag")
309	c.Flags().MarkDeprecated("deprecated", "This flag is deprecated")
310
311	c.SetArgs([]string{"c", "-d"})
312	if err := c.Execute(); err != nil {
313		t.Error("Unexpected error:", err)
314	}
315	if !strings.Contains(output.String(), "This flag is deprecated") {
316		t.Errorf("Expected to contain deprecated message, but got %q", output.String())
317	}
318}
319
320// TestSetHelpCommand checks, if SetHelpCommand works correctly.
321func TestSetHelpCommand(t *testing.T) {
322	c := &Command{Use: "c", Run: func(*Command, []string) {}}
323	output := new(bytes.Buffer)
324	c.SetOutput(output)
325	c.SetArgs([]string{"help"})
326
327	// Help will not be shown, if c has no subcommands.
328	c.AddCommand(&Command{
329		Use: "empty",
330		Run: func(cmd *Command, args []string) {},
331	})
332
333	correctMessage := "WORKS"
334	c.SetHelpCommand(&Command{
335		Use:   "help [command]",
336		Short: "Help about any command",
337		Long: `Help provides help for any command in the application.
338	Simply type ` + c.Name() + ` help [path to command] for full details.`,
339		Run: func(c *Command, args []string) { c.Print(correctMessage) },
340	})
341
342	if err := c.Execute(); err != nil {
343		t.Error("Unexpected error:", err)
344	}
345
346	if output.String() != correctMessage {
347		t.Errorf("Expected to contain %q message, but got %q", correctMessage, output.String())
348	}
349}
350