cobra/command_test.go

1526 lines
40 KiB
Go

package cobra
import (
"bytes"
"fmt"
"os"
"reflect"
"strings"
"testing"
"github.com/spf13/pflag"
)
func emptyRun(*Command, []string) {}
func executeCommand(root *Command, args ...string) (output string, err error) {
_, output, err = executeCommandC(root, args...)
return output, err
}
func executeCommandC(root *Command, args ...string) (c *Command, output string, err error) {
buf := new(bytes.Buffer)
root.SetOutput(buf)
root.SetArgs(args)
c, err = root.ExecuteC()
return c, buf.String(), err
}
func resetCommandLineFlagSet() {
pflag.CommandLine = pflag.NewFlagSet(os.Args[0], pflag.ExitOnError)
}
func TestSingleCommand(t *testing.T) {
var rootCmdArgs []string
rootCmd := &Command{
Use: "root",
Args: ExactArgs(2),
Run: func(_ *Command, args []string) { rootCmdArgs = args },
}
aCmd := &Command{Use: "a", Args: NoArgs, Run: emptyRun}
bCmd := &Command{Use: "b", Args: NoArgs, Run: emptyRun}
rootCmd.AddCommand(aCmd, bCmd)
output, err := executeCommand(rootCmd, "one", "two")
if output != "" {
t.Errorf("Unexpected output: %v", output)
}
if err != nil {
t.Errorf("Unexpected error: %v", err)
}
got := strings.Join(rootCmdArgs, " ")
expected := "one two"
if got != expected {
t.Errorf("rootCmdArgs expected: %q, got: %q", expected, got)
}
}
func TestChildCommand(t *testing.T) {
var child1CmdArgs []string
rootCmd := &Command{Use: "root", Args: NoArgs, Run: emptyRun}
child1Cmd := &Command{
Use: "child1",
Args: ExactArgs(2),
Run: func(_ *Command, args []string) { child1CmdArgs = args },
}
child2Cmd := &Command{Use: "child2", Args: NoArgs, Run: emptyRun}
rootCmd.AddCommand(child1Cmd, child2Cmd)
output, err := executeCommand(rootCmd, "child1", "one", "two")
if output != "" {
t.Errorf("Unexpected output: %v", output)
}
if err != nil {
t.Errorf("Unexpected error: %v", err)
}
got := strings.Join(child1CmdArgs, " ")
expected := "one two"
if got != expected {
t.Errorf("child1CmdArgs expected: %q, got: %q", expected, got)
}
}
func TestCallCommandWithoutSubcommands(t *testing.T) {
rootCmd := &Command{Use: "root", Args: NoArgs, Run: emptyRun}
_, err := executeCommand(rootCmd)
if err != nil {
t.Errorf("Calling command without subcommands should not have error: %v", err)
}
}
func TestRootExecuteUnknownCommand(t *testing.T) {
rootCmd := &Command{Use: "root", Run: emptyRun}
rootCmd.AddCommand(&Command{Use: "child", Run: emptyRun})
output, _ := executeCommand(rootCmd, "unknown")
expected := "Error: unknown command \"unknown\" for \"root\"\nRun 'root --help' for usage.\n"
if output != expected {
t.Errorf("Expected:\n %q\nGot:\n %q\n", expected, output)
}
}
func TestSubcommandExecuteC(t *testing.T) {
rootCmd := &Command{Use: "root", Run: emptyRun}
childCmd := &Command{Use: "child", Run: emptyRun}
rootCmd.AddCommand(childCmd)
c, output, err := executeCommandC(rootCmd, "child")
if output != "" {
t.Errorf("Unexpected output: %v", output)
}
if err != nil {
t.Errorf("Unexpected error: %v", err)
}
if c.Name() != "child" {
t.Errorf(`invalid command returned from ExecuteC: expected "child"', got %q`, c.Name())
}
}
func TestRootUnknownCommandSilenced(t *testing.T) {
rootCmd := &Command{Use: "root", Run: emptyRun}
rootCmd.SilenceErrors = true
rootCmd.SilenceUsage = true
rootCmd.AddCommand(&Command{Use: "child", Run: emptyRun})
output, _ := executeCommand(rootCmd, "unknown")
if output != "" {
t.Errorf("Expected blank output, because of silenced usage.\nGot:\n %q\n", output)
}
}
func TestCommandAlias(t *testing.T) {
var timesCmdArgs []string
rootCmd := &Command{Use: "root", Args: NoArgs, Run: emptyRun}
echoCmd := &Command{
Use: "echo",
Aliases: []string{"say", "tell"},
Args: NoArgs,
Run: emptyRun,
}
timesCmd := &Command{
Use: "times",
Args: ExactArgs(2),
Run: func(_ *Command, args []string) { timesCmdArgs = args },
}
echoCmd.AddCommand(timesCmd)
rootCmd.AddCommand(echoCmd)
output, err := executeCommand(rootCmd, "tell", "times", "one", "two")
if output != "" {
t.Errorf("Unexpected output: %v", output)
}
if err != nil {
t.Errorf("Unexpected error: %v", err)
}
got := strings.Join(timesCmdArgs, " ")
expected := "one two"
if got != expected {
t.Errorf("timesCmdArgs expected: %v, got: %v", expected, got)
}
}
func TestEnablePrefixMatching(t *testing.T) {
EnablePrefixMatching = true
var aCmdArgs []string
rootCmd := &Command{Use: "root", Args: NoArgs, Run: emptyRun}
aCmd := &Command{
Use: "aCmd",
Args: ExactArgs(2),
Run: func(_ *Command, args []string) { aCmdArgs = args },
}
bCmd := &Command{Use: "bCmd", Args: NoArgs, Run: emptyRun}
rootCmd.AddCommand(aCmd, bCmd)
output, err := executeCommand(rootCmd, "a", "one", "two")
if output != "" {
t.Errorf("Unexpected output: %v", output)
}
if err != nil {
t.Errorf("Unexpected error: %v", err)
}
got := strings.Join(aCmdArgs, " ")
expected := "one two"
if got != expected {
t.Errorf("aCmdArgs expected: %q, got: %q", expected, got)
}
EnablePrefixMatching = false
}
func TestAliasPrefixMatching(t *testing.T) {
EnablePrefixMatching = true
var timesCmdArgs []string
rootCmd := &Command{Use: "root", Args: NoArgs, Run: emptyRun}
echoCmd := &Command{
Use: "echo",
Aliases: []string{"say", "tell"},
Args: NoArgs,
Run: emptyRun,
}
timesCmd := &Command{
Use: "times",
Args: ExactArgs(2),
Run: func(_ *Command, args []string) { timesCmdArgs = args },
}
echoCmd.AddCommand(timesCmd)
rootCmd.AddCommand(echoCmd)
output, err := executeCommand(rootCmd, "sa", "times", "one", "two")
if output != "" {
t.Errorf("Unexpected output: %v", output)
}
if err != nil {
t.Errorf("Unexpected error: %v", err)
}
got := strings.Join(timesCmdArgs, " ")
expected := "one two"
if got != expected {
t.Errorf("timesCmdArgs expected: %v, got: %v", expected, got)
}
EnablePrefixMatching = false
}
// TestChildSameName checks the correct behaviour of cobra in cases,
// when an application with name "foo" and with subcommand "foo"
// is executed with args "foo foo".
func TestChildSameName(t *testing.T) {
var fooCmdArgs []string
rootCmd := &Command{Use: "foo", Args: NoArgs, Run: emptyRun}
fooCmd := &Command{
Use: "foo",
Args: ExactArgs(2),
Run: func(_ *Command, args []string) { fooCmdArgs = args },
}
barCmd := &Command{Use: "bar", Args: NoArgs, Run: emptyRun}
rootCmd.AddCommand(fooCmd, barCmd)
output, err := executeCommand(rootCmd, "foo", "one", "two")
if output != "" {
t.Errorf("Unexpected output: %v", output)
}
if err != nil {
t.Errorf("Unexpected error: %v", err)
}
got := strings.Join(fooCmdArgs, " ")
expected := "one two"
if got != expected {
t.Errorf("fooCmdArgs expected: %v, got: %v", expected, got)
}
}
// TestGrandChildSameName checks the correct behaviour of cobra in cases,
// when user has a root command and a grand child
// with the same name.
func TestGrandChildSameName(t *testing.T) {
var fooCmdArgs []string
rootCmd := &Command{Use: "foo", Args: NoArgs, Run: emptyRun}
barCmd := &Command{Use: "bar", Args: NoArgs, Run: emptyRun}
fooCmd := &Command{
Use: "foo",
Args: ExactArgs(2),
Run: func(_ *Command, args []string) { fooCmdArgs = args },
}
barCmd.AddCommand(fooCmd)
rootCmd.AddCommand(barCmd)
output, err := executeCommand(rootCmd, "bar", "foo", "one", "two")
if output != "" {
t.Errorf("Unexpected output: %v", output)
}
if err != nil {
t.Errorf("Unexpected error: %v", err)
}
got := strings.Join(fooCmdArgs, " ")
expected := "one two"
if got != expected {
t.Errorf("fooCmdArgs expected: %v, got: %v", expected, got)
}
}
func TestFlagLong(t *testing.T) {
var cArgs []string
c := &Command{
Use: "c",
Args: ArbitraryArgs,
Run: func(_ *Command, args []string) { cArgs = args },
}
var intFlagValue int
var stringFlagValue string
c.Flags().IntVar(&intFlagValue, "intf", -1, "")
c.Flags().StringVar(&stringFlagValue, "sf", "", "")
output, err := executeCommand(c, "--intf=7", "--sf=abc", "one", "--", "two")
if output != "" {
t.Errorf("Unexpected output: %v", err)
}
if err != nil {
t.Errorf("Unexpected error: %v", err)
}
if c.ArgsLenAtDash() != 1 {
t.Errorf("Expected ArgsLenAtDash: %v but got %v", 1, c.ArgsLenAtDash())
}
if intFlagValue != 7 {
t.Errorf("Expected intFlagValue: %v, got %v", 7, intFlagValue)
}
if stringFlagValue != "abc" {
t.Errorf("Expected stringFlagValue: %q, got %q", "abc", stringFlagValue)
}
got := strings.Join(cArgs, " ")
expected := "one two"
if got != expected {
t.Errorf("Expected arguments: %q, got %q", expected, got)
}
}
func TestFlagShort(t *testing.T) {
var cArgs []string
c := &Command{
Use: "c",
Args: ArbitraryArgs,
Run: func(_ *Command, args []string) { cArgs = args },
}
var intFlagValue int
var stringFlagValue string
c.Flags().IntVarP(&intFlagValue, "intf", "i", -1, "")
c.Flags().StringVarP(&stringFlagValue, "sf", "s", "", "")
output, err := executeCommand(c, "-i", "7", "-sabc", "one", "two")
if output != "" {
t.Errorf("Unexpected output: %v", err)
}
if err != nil {
t.Errorf("Unexpected error: %v", err)
}
if intFlagValue != 7 {
t.Errorf("Expected flag value: %v, got %v", 7, intFlagValue)
}
if stringFlagValue != "abc" {
t.Errorf("Expected stringFlagValue: %q, got %q", "abc", stringFlagValue)
}
got := strings.Join(cArgs, " ")
expected := "one two"
if got != expected {
t.Errorf("Expected arguments: %q, got %q", expected, got)
}
}
func TestChildFlag(t *testing.T) {
rootCmd := &Command{Use: "root", Run: emptyRun}
childCmd := &Command{Use: "child", Run: emptyRun}
rootCmd.AddCommand(childCmd)
var intFlagValue int
childCmd.Flags().IntVarP(&intFlagValue, "intf", "i", -1, "")
output, err := executeCommand(rootCmd, "child", "-i7")
if output != "" {
t.Errorf("Unexpected output: %v", err)
}
if err != nil {
t.Errorf("Unexpected error: %v", err)
}
if intFlagValue != 7 {
t.Errorf("Expected flag value: %v, got %v", 7, intFlagValue)
}
}
func TestChildFlagWithParentLocalFlag(t *testing.T) {
rootCmd := &Command{Use: "root", Run: emptyRun}
childCmd := &Command{Use: "child", Run: emptyRun}
rootCmd.AddCommand(childCmd)
var intFlagValue int
rootCmd.Flags().StringP("sf", "s", "", "")
childCmd.Flags().IntVarP(&intFlagValue, "intf", "i", -1, "")
_, err := executeCommand(rootCmd, "child", "-i7", "-sabc")
if err == nil {
t.Errorf("Invalid flag should generate error")
}
if !strings.Contains(err.Error(), "unknown shorthand") {
t.Errorf("Wrong error message: %q", err.Error())
}
if intFlagValue != 7 {
t.Errorf("Expected flag value: %v, got %v", 7, intFlagValue)
}
}
func TestFlagInvalidInput(t *testing.T) {
rootCmd := &Command{Use: "root", Run: emptyRun}
rootCmd.Flags().IntP("intf", "i", -1, "")
_, err := executeCommand(rootCmd, "-iabc")
if err == nil {
t.Errorf("Invalid flag value should generate error")
}
if !strings.Contains(err.Error(), "invalid syntax") {
t.Errorf("Wrong error message: %q", err)
}
}
func TestFlagBeforeCommand(t *testing.T) {
rootCmd := &Command{Use: "root", Run: emptyRun}
childCmd := &Command{Use: "child", Run: emptyRun}
rootCmd.AddCommand(childCmd)
var flagValue int
childCmd.Flags().IntVarP(&flagValue, "intf", "i", -1, "")
// With short flag.
_, err := executeCommand(rootCmd, "-i7", "child")
if err != nil {
t.Errorf("Unexpected error: %v", err)
}
if flagValue != 7 {
t.Errorf("Expected flag value: %v, got %v", 7, flagValue)
}
// With long flag.
_, err = executeCommand(rootCmd, "--intf=8", "child")
if err != nil {
t.Errorf("Unexpected error: %v", err)
}
if flagValue != 8 {
t.Errorf("Expected flag value: %v, got %v", 9, flagValue)
}
}
func TestStripFlags(t *testing.T) {
tests := []struct {
input []string
output []string
}{
{
[]string{"foo", "bar"},
[]string{"foo", "bar"},
},
{
[]string{"foo", "--str", "-s"},
[]string{"foo"},
},
{
[]string{"-s", "foo", "--str", "bar"},
[]string{},
},
{
[]string{"-i10", "echo"},
[]string{"echo"},
},
{
[]string{"-i=10", "echo"},
[]string{"echo"},
},
{
[]string{"--int=100", "echo"},
[]string{"echo"},
},
{
[]string{"-ib", "echo", "-sfoo", "baz"},
[]string{"echo", "baz"},
},
{
[]string{"-i=baz", "bar", "-i", "foo", "blah"},
[]string{"bar", "blah"},
},
{
[]string{"--int=baz", "-sbar", "-i", "foo", "blah"},
[]string{"blah"},
},
{
[]string{"--bool", "bar", "-i", "foo", "blah"},
[]string{"bar", "blah"},
},
{
[]string{"-b", "bar", "-i", "foo", "blah"},
[]string{"bar", "blah"},
},
{
[]string{"--persist", "bar"},
[]string{"bar"},
},
{
[]string{"-p", "bar"},
[]string{"bar"},
},
}
c := &Command{Use: "c", Run: emptyRun}
c.PersistentFlags().BoolP("persist", "p", false, "")
c.Flags().IntP("int", "i", -1, "")
c.Flags().StringP("str", "s", "", "")
c.Flags().BoolP("bool", "b", false, "")
for i, test := range tests {
got := stripFlags(test.input, c)
if !reflect.DeepEqual(test.output, got) {
t.Errorf("(%v) Expected: %v, got: %v", i, test.output, got)
}
}
}
func TestDisableFlagParsing(t *testing.T) {
var cArgs []string
c := &Command{
Use: "c",
DisableFlagParsing: true,
Run: func(_ *Command, args []string) {
cArgs = args
},
}
args := []string{"cmd", "-v", "-race", "-file", "foo.go"}
output, err := executeCommand(c, args...)
if output != "" {
t.Errorf("Unexpected output: %v", output)
}
if err != nil {
t.Errorf("Unexpected error: %v", err)
}
if !reflect.DeepEqual(args, cArgs) {
t.Errorf("Expected: %v, got: %v", args, cArgs)
}
}
func TestPersistentFlagsOnSameCommand(t *testing.T) {
var rootCmdArgs []string
rootCmd := &Command{
Use: "root",
Args: ArbitraryArgs,
Run: func(_ *Command, args []string) { rootCmdArgs = args },
}
var flagValue int
rootCmd.PersistentFlags().IntVarP(&flagValue, "intf", "i", -1, "")
output, err := executeCommand(rootCmd, "-i7", "one", "two")
if output != "" {
t.Errorf("Unexpected output: %v", output)
}
if err != nil {
t.Errorf("Unexpected error: %v", err)
}
got := strings.Join(rootCmdArgs, " ")
expected := "one two"
if got != expected {
t.Errorf("rootCmdArgs expected: %q, got %q", expected, got)
}
if flagValue != 7 {
t.Errorf("flagValue expected: %v, got %v", 7, flagValue)
}
}
// TestEmptyInputs checks,
// if flags correctly parsed with blank strings in args.
func TestEmptyInputs(t *testing.T) {
c := &Command{Use: "c", Run: emptyRun}
var flagValue int
c.Flags().IntVarP(&flagValue, "intf", "i", -1, "")
output, err := executeCommand(c, "", "-i7", "")
if output != "" {
t.Errorf("Unexpected output: %v", output)
}
if err != nil {
t.Errorf("Unexpected error: %v", err)
}
if flagValue != 7 {
t.Errorf("flagValue expected: %v, got %v", 7, flagValue)
}
}
func TestOverwrittenFlag(t *testing.T) {
// TODO: This test fails, but should work.
t.Skip()
parent := &Command{Use: "parent", Run: emptyRun}
child := &Command{Use: "child", Run: emptyRun}
parent.PersistentFlags().Bool("boolf", false, "")
parent.PersistentFlags().Int("intf", -1, "")
child.Flags().String("strf", "", "")
child.Flags().Int("intf", -1, "")
parent.AddCommand(child)
childInherited := child.InheritedFlags()
childLocal := child.LocalFlags()
if childLocal.Lookup("strf") == nil {
t.Error(`LocalFlags expected to contain "strf", got "nil"`)
}
if childInherited.Lookup("boolf") == nil {
t.Error(`InheritedFlags expected to contain "boolf", got "nil"`)
}
if childInherited.Lookup("intf") != nil {
t.Errorf(`InheritedFlags should not contain overwritten flag "intf"`)
}
if childLocal.Lookup("intf") == nil {
t.Error(`LocalFlags expected to contain "intf", got "nil"`)
}
}
func TestPersistentFlagsOnChild(t *testing.T) {
var childCmdArgs []string
rootCmd := &Command{Use: "root", Run: emptyRun}
childCmd := &Command{
Use: "child",
Args: ArbitraryArgs,
Run: func(_ *Command, args []string) { childCmdArgs = args },
}
rootCmd.AddCommand(childCmd)
var parentFlagValue int
var childFlagValue int
rootCmd.PersistentFlags().IntVarP(&parentFlagValue, "parentf", "p", -1, "")
childCmd.Flags().IntVarP(&childFlagValue, "childf", "c", -1, "")
output, err := executeCommand(rootCmd, "child", "-c7", "-p8", "one", "two")
if output != "" {
t.Errorf("Unexpected output: %v", output)
}
if err != nil {
t.Errorf("Unexpected error: %v", err)
}
got := strings.Join(childCmdArgs, " ")
expected := "one two"
if got != expected {
t.Errorf("childCmdArgs expected: %q, got %q", expected, got)
}
if parentFlagValue != 8 {
t.Errorf("parentFlagValue expected: %v, got %v", 8, parentFlagValue)
}
if childFlagValue != 7 {
t.Errorf("childFlagValue expected: %v, got %v", 7, childFlagValue)
}
}
func TestRequiredFlags(t *testing.T) {
c := &Command{Use: "c", Run: emptyRun}
c.Flags().String("foo1", "", "")
c.MarkFlagRequired("foo1")
c.Flags().String("foo2", "", "")
c.MarkFlagRequired("foo2")
c.Flags().String("bar", "", "")
expected := fmt.Sprintf("Required flag(s) %q, %q have/has not been set", "foo1", "foo2")
_, err := executeCommand(c)
got := err.Error()
if got != expected {
t.Errorf("Expected error: %q, got: %q", expected, got)
}
}
func TestPersistentRequiredFlags(t *testing.T) {
parent := &Command{Use: "parent", Run: emptyRun}
parent.PersistentFlags().String("foo1", "", "")
parent.MarkPersistentFlagRequired("foo1")
parent.PersistentFlags().String("foo2", "", "")
parent.MarkPersistentFlagRequired("foo2")
parent.Flags().String("foo3", "", "")
child := &Command{Use: "child", Run: emptyRun}
child.Flags().String("bar1", "", "")
child.MarkFlagRequired("bar1")
child.Flags().String("bar2", "", "")
child.MarkFlagRequired("bar2")
child.Flags().String("bar3", "", "")
parent.AddCommand(child)
expected := fmt.Sprintf("Required flag(s) %q, %q, %q, %q have/has not been set", "bar1", "bar2", "foo1", "foo2")
_, err := executeCommand(parent, "child")
if err.Error() != expected {
t.Errorf("Expected %q, got %q", expected, err.Error())
}
}
func TestInitHelpFlagMergesFlags(t *testing.T) {
usage := "custom flag"
rootCmd := &Command{Use: "root"}
rootCmd.PersistentFlags().Bool("help", false, "custom flag")
childCmd := &Command{Use: "child"}
rootCmd.AddCommand(childCmd)
childCmd.InitDefaultHelpFlag()
got := childCmd.Flags().Lookup("help").Usage
if got != usage {
t.Errorf("Expected the help flag from the root command with usage: %v\nGot the default with usage: %v", usage, got)
}
}
func TestHelpCommandExecuted(t *testing.T) {
rootCmd := &Command{Use: "root", Long: "Long description", Run: emptyRun}
rootCmd.AddCommand(&Command{Use: "child", Run: emptyRun})
output, err := executeCommand(rootCmd, "help")
if err != nil {
t.Errorf("Unexpected error: %v", err)
}
if !strings.Contains(output, rootCmd.Long) {
t.Errorf("Expected to contain: %q, got: %q", rootCmd.Long, output)
}
}
func TestHelpCommandExecutedOnChild(t *testing.T) {
rootCmd := &Command{Use: "root", Run: emptyRun}
childCmd := &Command{Use: "child", Long: "Long description", Run: emptyRun}
rootCmd.AddCommand(childCmd)
output, err := executeCommand(rootCmd, "help", "child")
if err != nil {
t.Errorf("Unexpected error: %v", err)
}
if !strings.Contains(output, childCmd.Long) {
t.Errorf("Expected to contain: %q, got: %q", childCmd.Long, output)
}
}
func TestSetHelpCommand(t *testing.T) {
c := &Command{Use: "c", Run: emptyRun}
c.AddCommand(&Command{Use: "empty", Run: emptyRun})
expected := "WORKS"
c.SetHelpCommand(&Command{
Use: "help [command]",
Short: "Help about any command",
Long: `Help provides help for any command in the application.
Simply type ` + c.Name() + ` help [path to command] for full details.`,
Run: func(c *Command, _ []string) { c.Print(expected) },
})
got, err := executeCommand(c, "help")
if err != nil {
t.Errorf("Unexpected error: %v", err)
}
if got != expected {
t.Errorf("Expected to contain %q, got %q", expected, got)
}
}
func TestHelpFlagExecuted(t *testing.T) {
rootCmd := &Command{Use: "root", Long: "Long description", Run: emptyRun}
output, err := executeCommand(rootCmd, "--help")
if err != nil {
t.Errorf("Unexpected error: %v", err)
}
if !strings.Contains(output, rootCmd.Long) {
t.Errorf("Expected to contain: %q, got: %q", rootCmd.Long, output)
}
}
func TestHelpFlagExecutedOnChild(t *testing.T) {
rootCmd := &Command{Use: "root", Run: emptyRun}
childCmd := &Command{Use: "child", Long: "Long description", Run: emptyRun}
rootCmd.AddCommand(childCmd)
output, err := executeCommand(rootCmd, "child", "--help")
if err != nil {
t.Errorf("Unexpected error: %v", err)
}
if !strings.Contains(output, childCmd.Long) {
t.Errorf("Expected to contain: %q, got: %q", childCmd.Long, output)
}
}
// TestHelpFlagInHelp checks,
// if '--help' flag is shown in help for child (executing `parent help child`),
// that has no other flags.
// Related to https://github.com/spf13/cobra/issues/302.
func TestHelpFlagInHelp(t *testing.T) {
output := new(bytes.Buffer)
parent := &Command{Use: "parent", Run: func(*Command, []string) {}}
parent.SetOutput(output)
child := &Command{Use: "child", Run: func(*Command, []string) {}}
parent.AddCommand(child)
parent.SetArgs([]string{"help", "child"})
err := parent.Execute()
if err != nil {
t.Fatal(err)
}
if !strings.Contains(output.String(), "[flags]") {
t.Errorf("\nExpecting to contain: %v\nGot: %v", "[flags]", output.String())
}
}
func TestFlagsInUsage(t *testing.T) {
rootCmd := &Command{Use: "root", Args: NoArgs, Run: func(*Command, []string) {}}
output, err := executeCommand(rootCmd, "--help")
if err != nil {
t.Errorf("Unexpected error: %v", err)
}
expected := "[flags]"
if !strings.Contains(output, expected) {
t.Errorf("Expected to contain %q\ngot: %v", expected, output)
}
}
func TestHelpExecutedOnNonRunnableChild(t *testing.T) {
rootCmd := &Command{Use: "root", Run: emptyRun}
childCmd := &Command{Use: "child", Long: "Long description"}
rootCmd.AddCommand(childCmd)
output, err := executeCommand(rootCmd, "child")
if err != nil {
t.Errorf("Unexpected error: %v", err)
}
if !strings.Contains(output, childCmd.Long) {
t.Errorf("Expected to contain: %q, got: %q", childCmd.Long, output)
}
}
func TestUsageIsNotPrintedTwice(t *testing.T) {
var cmd = &Command{Use: "root"}
var sub = &Command{Use: "sub"}
cmd.AddCommand(sub)
output, _ := executeCommand(cmd, "")
if strings.Count(output, "Usage:") != 1 {
t.Error("Usage output is not printed exactly once")
}
}
func TestVisitParents(t *testing.T) {
c := &Command{Use: "app"}
sub := &Command{Use: "sub"}
dsub := &Command{Use: "dsub"}
sub.AddCommand(dsub)
c.AddCommand(sub)
total := 0
add := func(x *Command) {
total++
}
sub.VisitParents(add)
if total != 1 {
t.Errorf("Should have visited 1 parent but visited %d", total)
}
total = 0
dsub.VisitParents(add)
if total != 2 {
t.Errorf("Should have visited 2 parents but visited %d", total)
}
total = 0
c.VisitParents(add)
if total != 0 {
t.Errorf("Should have visited no parents but visited %d", total)
}
}
func TestSuggestions(t *testing.T) {
rootCmd := &Command{Use: "root", Run: emptyRun}
timesCmd := &Command{
Use: "times",
SuggestFor: []string{"counts"},
Run: emptyRun,
}
rootCmd.AddCommand(timesCmd)
templateWithSuggestions := "Error: unknown command \"%s\" for \"root\"\n\nDid you mean this?\n\t%s\n\nRun 'root --help' for usage.\n"
templateWithoutSuggestions := "Error: unknown command \"%s\" for \"root\"\nRun 'root --help' for usage.\n"
tests := map[string]string{
"time": "times",
"tiems": "times",
"tims": "times",
"timeS": "times",
"rimes": "times",
"ti": "times",
"t": "times",
"timely": "times",
"ri": "",
"timezone": "",
"foo": "",
"counts": "times",
}
for typo, suggestion := range tests {
for _, suggestionsDisabled := range []bool{true, false} {
rootCmd.DisableSuggestions = suggestionsDisabled
var expected string
output, _ := executeCommand(rootCmd, typo)
if suggestion == "" || suggestionsDisabled {
expected = fmt.Sprintf(templateWithoutSuggestions, typo)
} else {
expected = fmt.Sprintf(templateWithSuggestions, typo, suggestion)
}
if output != expected {
t.Errorf("Unexpected response.\nExpected:\n %q\nGot:\n %q\n", expected, output)
}
}
}
}
func TestRemoveCommand(t *testing.T) {
rootCmd := &Command{Use: "root", Args: NoArgs, Run: emptyRun}
childCmd := &Command{Use: "child", Run: emptyRun}
rootCmd.AddCommand(childCmd)
rootCmd.RemoveCommand(childCmd)
_, err := executeCommand(rootCmd, "child")
if err == nil {
t.Error("Expected error on calling removed command. Got nil.")
}
}
func TestReplaceCommandWithRemove(t *testing.T) {
childUsed := 0
rootCmd := &Command{Use: "root", Run: emptyRun}
child1Cmd := &Command{
Use: "child",
Run: func(*Command, []string) { childUsed = 1 },
}
child2Cmd := &Command{
Use: "child",
Run: func(*Command, []string) { childUsed = 2 },
}
rootCmd.AddCommand(child1Cmd)
rootCmd.RemoveCommand(child1Cmd)
rootCmd.AddCommand(child2Cmd)
output, err := executeCommand(rootCmd, "child")
if output != "" {
t.Errorf("Unexpected output: %v", output)
}
if err != nil {
t.Errorf("Unexpected error: %v", err)
}
if childUsed == 1 {
t.Error("Removed command shouldn't be called")
}
if childUsed != 2 {
t.Error("Replacing command should have been called but didn't")
}
}
func TestDeprecatedCommand(t *testing.T) {
rootCmd := &Command{Use: "root", Run: emptyRun}
deprecatedCmd := &Command{
Use: "deprecated",
Deprecated: "This command is deprecated",
Run: emptyRun,
}
rootCmd.AddCommand(deprecatedCmd)
output, err := executeCommand(rootCmd, "deprecated")
if err != nil {
t.Errorf("Unexpected error: %v", err)
}
expected := deprecatedCmd.Deprecated
if !strings.Contains(output, expected) {
t.Errorf("Expected to contain: %q\nGot:%q", expected, output)
}
}
func TestHooks(t *testing.T) {
var (
persPreArgs string
preArgs string
runArgs string
postArgs string
persPostArgs string
)
c := &Command{
Use: "c",
PersistentPreRun: func(_ *Command, args []string) {
persPreArgs = strings.Join(args, " ")
},
PreRun: func(_ *Command, args []string) {
preArgs = strings.Join(args, " ")
},
Run: func(_ *Command, args []string) {
runArgs = strings.Join(args, " ")
},
PostRun: func(_ *Command, args []string) {
postArgs = strings.Join(args, " ")
},
PersistentPostRun: func(_ *Command, args []string) {
persPostArgs = strings.Join(args, " ")
},
}
output, err := executeCommand(c, "one", "two")
if output != "" {
t.Errorf("Unexpected output: %v", output)
}
if err != nil {
t.Errorf("Unexpected error: %v", err)
}
if persPreArgs != "one two" {
t.Errorf("Expected persPreArgs %q, got %q", "one two", persPreArgs)
}
if preArgs != "one two" {
t.Errorf("Expected preArgs %q, got %q", "one two", preArgs)
}
if runArgs != "one two" {
t.Errorf("Expected runArgs %q, got %q", "one two", runArgs)
}
if postArgs != "one two" {
t.Errorf("Expected postArgs %q, got %q", "one two", postArgs)
}
if persPostArgs != "one two" {
t.Errorf("Expected persPostArgs %q, got %q", "one two", persPostArgs)
}
}
func TestPersistentHooks(t *testing.T) {
var (
parentPersPreArgs string
parentPreArgs string
parentRunArgs string
parentPostArgs string
parentPersPostArgs string
)
var (
childPersPreArgs string
childPreArgs string
childRunArgs string
childPostArgs string
childPersPostArgs string
)
parentCmd := &Command{
Use: "parent",
PersistentPreRun: func(_ *Command, args []string) {
parentPersPreArgs = strings.Join(args, " ")
},
PreRun: func(_ *Command, args []string) {
parentPreArgs = strings.Join(args, " ")
},
Run: func(_ *Command, args []string) {
parentRunArgs = strings.Join(args, " ")
},
PostRun: func(_ *Command, args []string) {
parentPostArgs = strings.Join(args, " ")
},
PersistentPostRun: func(_ *Command, args []string) {
parentPersPostArgs = strings.Join(args, " ")
},
}
childCmd := &Command{
Use: "child",
PersistentPreRun: func(_ *Command, args []string) {
childPersPreArgs = strings.Join(args, " ")
},
PreRun: func(_ *Command, args []string) {
childPreArgs = strings.Join(args, " ")
},
Run: func(_ *Command, args []string) {
childRunArgs = strings.Join(args, " ")
},
PostRun: func(_ *Command, args []string) {
childPostArgs = strings.Join(args, " ")
},
PersistentPostRun: func(_ *Command, args []string) {
childPersPostArgs = strings.Join(args, " ")
},
}
parentCmd.AddCommand(childCmd)
output, err := executeCommand(parentCmd, "child", "one", "two")
if output != "" {
t.Errorf("Unexpected output: %v", output)
}
if err != nil {
t.Errorf("Unexpected error: %v", err)
}
// TODO: This test fails, but should not.
// Related to https://github.com/spf13/cobra/issues/252.
//
// if parentPersPreArgs != "one two" {
// t.Errorf("Expected parentPersPreArgs %q, got %q", "one two", parentPersPreArgs)
// }
if parentPreArgs != "" {
t.Errorf("Expected blank parentPreArgs, got %q", parentPreArgs)
}
if parentRunArgs != "" {
t.Errorf("Expected blank parentRunArgs, got %q", parentRunArgs)
}
if parentPostArgs != "" {
t.Errorf("Expected blank parentPostArgs, got %q", parentPostArgs)
}
// TODO: This test fails, but should not.
// Related to https://github.com/spf13/cobra/issues/252.
//
// if parentPersPostArgs != "one two" {
// t.Errorf("Expected parentPersPostArgs %q, got %q", "one two", parentPersPostArgs)
// }
if childPersPreArgs != "one two" {
t.Errorf("Expected childPersPreArgs %q, got %q", "one two", childPersPreArgs)
}
if childPreArgs != "one two" {
t.Errorf("Expected childPreArgs %q, got %q", "one two", childPreArgs)
}
if childRunArgs != "one two" {
t.Errorf("Expected childRunArgs %q, got %q", "one two", childRunArgs)
}
if childPostArgs != "one two" {
t.Errorf("Expected childPostArgs %q, got %q", "one two", childPostArgs)
}
if childPersPostArgs != "one two" {
t.Errorf("Expected childPersPostArgs %q, got %q", "one two", childPersPostArgs)
}
}
// Related to https://github.com/spf13/cobra/issues/521.
func TestGlobalNormFuncPropagation(t *testing.T) {
normFunc := func(f *pflag.FlagSet, name string) pflag.NormalizedName {
return pflag.NormalizedName(name)
}
rootCmd := &Command{Use: "root", Run: emptyRun}
childCmd := &Command{Use: "child", Run: emptyRun}
rootCmd.AddCommand(childCmd)
rootCmd.SetGlobalNormalizationFunc(normFunc)
if reflect.ValueOf(normFunc).Pointer() != reflect.ValueOf(rootCmd.GlobalNormalizationFunc()).Pointer() {
t.Error("rootCmd seems to have a wrong normalization function")
}
if reflect.ValueOf(normFunc).Pointer() != reflect.ValueOf(childCmd.GlobalNormalizationFunc()).Pointer() {
t.Error("childCmd should have had the normalization function of rootCmd")
}
}
// Related to https://github.com/spf13/cobra/issues/521.
func TestNormPassedOnLocal(t *testing.T) {
toUpper := func(f *pflag.FlagSet, name string) pflag.NormalizedName {
return pflag.NormalizedName(strings.ToUpper(name))
}
c := &Command{}
c.Flags().Bool("flagname", true, "this is a dummy flag")
c.SetGlobalNormalizationFunc(toUpper)
if c.LocalFlags().Lookup("flagname") != c.LocalFlags().Lookup("FLAGNAME") {
t.Error("Normalization function should be passed on to Local flag set")
}
}
// Related to https://github.com/spf13/cobra/issues/521.
func TestNormPassedOnInherited(t *testing.T) {
toUpper := func(f *pflag.FlagSet, name string) pflag.NormalizedName {
return pflag.NormalizedName(strings.ToUpper(name))
}
c := &Command{}
c.SetGlobalNormalizationFunc(toUpper)
child1 := &Command{}
c.AddCommand(child1)
c.PersistentFlags().Bool("flagname", true, "")
child2 := &Command{}
c.AddCommand(child2)
inherited := child1.InheritedFlags()
if inherited.Lookup("flagname") == nil || inherited.Lookup("flagname") != inherited.Lookup("FLAGNAME") {
t.Error("Normalization function should be passed on to inherited flag set in command added before flag")
}
inherited = child2.InheritedFlags()
if inherited.Lookup("flagname") == nil || inherited.Lookup("flagname") != inherited.Lookup("FLAGNAME") {
t.Error("Normalization function should be passed on to inherited flag set in command added after flag")
}
}
// Related to https://github.com/spf13/cobra/issues/521.
func TestConsistentNormalizedName(t *testing.T) {
toUpper := func(f *pflag.FlagSet, name string) pflag.NormalizedName {
return pflag.NormalizedName(strings.ToUpper(name))
}
n := func(f *pflag.FlagSet, name string) pflag.NormalizedName {
return pflag.NormalizedName(name)
}
c := &Command{}
c.Flags().Bool("flagname", true, "")
c.SetGlobalNormalizationFunc(toUpper)
c.SetGlobalNormalizationFunc(n)
if c.LocalFlags().Lookup("flagname") == c.LocalFlags().Lookup("FLAGNAME") {
t.Error("Normalizing flag names should not result in duplicate flags")
}
}
func TestFlagOnPflagCommandLine(t *testing.T) {
flagName := "flagOnCommandLine"
pflag.String(flagName, "", "about my flag")
c := &Command{Use: "c", Run: emptyRun}
c.AddCommand(&Command{Use: "child", Run: emptyRun})
output, _ := executeCommand(c, "--help")
if !strings.Contains(output, flagName) {
t.Errorf("Expected to contain: %q\nGot: %v", flagName, output)
}
resetCommandLineFlagSet()
}
// TestHiddenCommandExecutes checks,
// if hidden commands run as intended.
func TestHiddenCommandExecutes(t *testing.T) {
executed := false
c := &Command{
Use: "c",
Hidden: true,
Run: func(*Command, []string) { executed = true },
}
output, err := executeCommand(c)
if output != "" {
t.Errorf("Unexpected output: %v", output)
}
if err != nil {
t.Errorf("Unexpected error: %v", err)
}
if !executed {
t.Error("Hidden command should have been executed")
}
}
// test to ensure hidden commands do not show up in usage/help text
func TestHiddenCommandIsHidden(t *testing.T) {
c := &Command{Use: "c", Hidden: true, Run: emptyRun}
if c.IsAvailableCommand() {
t.Errorf("Hidden command should be unavailable")
}
}
func TestCommandsAreSorted(t *testing.T) {
EnableCommandSorting = true
originalNames := []string{"middle", "zlast", "afirst"}
expectedNames := []string{"afirst", "middle", "zlast"}
var rootCmd = &Command{Use: "root"}
for _, name := range originalNames {
rootCmd.AddCommand(&Command{Use: name})
}
for i, c := range rootCmd.Commands() {
got := c.Name()
if expectedNames[i] != got {
t.Errorf("Expected: %s, got: %s", expectedNames[i], got)
}
}
EnableCommandSorting = true
}
func TestEnableCommandSortingIsDisabled(t *testing.T) {
EnableCommandSorting = false
originalNames := []string{"middle", "zlast", "afirst"}
var rootCmd = &Command{Use: "root"}
for _, name := range originalNames {
rootCmd.AddCommand(&Command{Use: name})
}
for i, c := range rootCmd.Commands() {
got := c.Name()
if originalNames[i] != got {
t.Errorf("expected: %s, got: %s", originalNames[i], got)
}
}
EnableCommandSorting = true
}
func TestSetOutput(t *testing.T) {
c := &Command{}
c.SetOutput(nil)
if out := c.OutOrStdout(); out != os.Stdout {
t.Errorf("Expected setting output to nil to revert back to stdout")
}
}
func TestFlagErrorFunc(t *testing.T) {
c := &Command{Use: "c", Run: emptyRun}
expectedFmt := "This is expected: %v"
c.SetFlagErrorFunc(func(_ *Command, err error) error {
return fmt.Errorf(expectedFmt, err)
})
_, err := executeCommand(c, "--unknown-flag")
got := err.Error()
expected := fmt.Sprintf(expectedFmt, "unknown flag: --unknown-flag")
if got != expected {
t.Errorf("Expected %v, got %v", expected, got)
}
}
// TestSortedFlags checks,
// if cmd.LocalFlags() is unsorted when cmd.Flags().SortFlags set to false.
// Related to https://github.com/spf13/cobra/issues/404.
func TestSortedFlags(t *testing.T) {
c := &Command{}
c.Flags().SortFlags = false
names := []string{"C", "B", "A", "D"}
for _, name := range names {
c.Flags().Bool(name, false, "")
}
i := 0
c.LocalFlags().VisitAll(func(f *pflag.Flag) {
if i == len(names) {
return
}
if stringInSlice(f.Name, names) {
if names[i] != f.Name {
t.Errorf("Incorrect order. Expected %v, got %v", names[i], f.Name)
}
i++
}
})
}
// TestMergeCommandLineToFlags checks,
// if pflag.CommandLine is correctly merged to c.Flags() after first call
// of c.mergePersistentFlags.
// Related to https://github.com/spf13/cobra/issues/443.
func TestMergeCommandLineToFlags(t *testing.T) {
pflag.Bool("boolflag", false, "")
c := &Command{Use: "c", Run: emptyRun}
c.mergePersistentFlags()
if c.Flags().Lookup("boolflag") == nil {
t.Fatal("Expecting to have flag from CommandLine in c.Flags()")
}
resetCommandLineFlagSet()
}
// TestUseDeprecatedFlags checks,
// if cobra.Execute() prints a message, if a deprecated flag is used.
// Related to https://github.com/spf13/cobra/issues/463.
func TestUseDeprecatedFlags(t *testing.T) {
c := &Command{Use: "c", Run: emptyRun}
c.Flags().BoolP("deprecated", "d", false, "deprecated flag")
c.Flags().MarkDeprecated("deprecated", "This flag is deprecated")
output, err := executeCommand(c, "c", "-d")
if err != nil {
t.Error("Unexpected error:", err)
}
if !strings.Contains(output, "This flag is deprecated") {
t.Errorf("Expected to contain deprecated message, but got %q", output)
}
}
func TestTraverseWithParentFlags(t *testing.T) {
rootCmd := &Command{Use: "root", TraverseChildren: true}
rootCmd.Flags().String("str", "", "")
rootCmd.Flags().BoolP("bool", "b", false, "")
childCmd := &Command{Use: "child"}
childCmd.Flags().Int("int", -1, "")
rootCmd.AddCommand(childCmd)
c, args, err := rootCmd.Traverse([]string{"-b", "--str", "ok", "child", "--int"})
if err != nil {
t.Errorf("Unexpected error: %v", err)
}
if len(args) != 1 && args[0] != "--add" {
t.Errorf("Wrong args: %v", args)
}
if c.Name() != childCmd.Name() {
t.Errorf("Expected command: %q, got: %q", childCmd.Name(), c.Name())
}
}
func TestTraverseNoParentFlags(t *testing.T) {
rootCmd := &Command{Use: "root", TraverseChildren: true}
rootCmd.Flags().String("foo", "", "foo things")
childCmd := &Command{Use: "child"}
childCmd.Flags().String("str", "", "")
rootCmd.AddCommand(childCmd)
c, args, err := rootCmd.Traverse([]string{"child"})
if err != nil {
t.Errorf("Unexpected error: %v", err)
}
if len(args) != 0 {
t.Errorf("Wrong args %v", args)
}
if c.Name() != childCmd.Name() {
t.Errorf("Expected command: %q, got: %q", childCmd.Name(), c.Name())
}
}
func TestTraverseWithBadParentFlags(t *testing.T) {
rootCmd := &Command{Use: "root", TraverseChildren: true}
childCmd := &Command{Use: "child"}
childCmd.Flags().String("str", "", "")
rootCmd.AddCommand(childCmd)
expected := "unknown flag: --str"
c, _, err := rootCmd.Traverse([]string{"--str", "ok", "child"})
if err == nil || !strings.Contains(err.Error(), expected) {
t.Errorf("Expected error, %q, got %q", expected, err)
}
if c != nil {
t.Errorf("Expected nil command")
}
}
func TestTraverseWithBadChildFlag(t *testing.T) {
rootCmd := &Command{Use: "root", TraverseChildren: true}
rootCmd.Flags().String("str", "", "")
childCmd := &Command{Use: "child"}
rootCmd.AddCommand(childCmd)
// Expect no error because the last commands args shouldn't be parsed in
// Traverse.
c, args, err := rootCmd.Traverse([]string{"child", "--str"})
if err != nil {
t.Errorf("Unexpected error: %v", err)
}
if len(args) != 1 && args[0] != "--str" {
t.Errorf("Wrong args: %v", args)
}
if c.Name() != childCmd.Name() {
t.Errorf("Expected command %q, got: %q", childCmd.Name(), c.Name())
}
}
func TestTraverseWithTwoSubcommands(t *testing.T) {
rootCmd := &Command{Use: "root", TraverseChildren: true}
subCmd := &Command{Use: "sub", TraverseChildren: true}
rootCmd.AddCommand(subCmd)
subsubCmd := &Command{
Use: "subsub",
}
subCmd.AddCommand(subsubCmd)
c, _, err := rootCmd.Traverse([]string{"sub", "subsub"})
if err != nil {
t.Fatalf("Unexpected error: %v", err)
}
if c.Name() != subsubCmd.Name() {
t.Fatalf("Expected command: %q, got %q", subsubCmd.Name(), c.Name())
}
}
// TestUpdateName checks if c.Name() updates on changed c.Use.
// Related to https://github.com/spf13/cobra/pull/422#discussion_r143918343.
func TestUpdateName(t *testing.T) {
c := &Command{Use: "name xyz"}
originalName := c.Name()
c.Use = "changedName abc"
if originalName == c.Name() || c.Name() != "changedName" {
t.Error("c.Name() should be updated on changed c.Use")
}
}