mirror of
https://github.com/spf13/cobra
synced 2024-11-25 07:07:15 +00:00
Unify the behaviour of error reporting when unknown subcommand is passed, add tests.
This commit is contained in:
parent
7c674d9e72
commit
0f10a71a34
3 changed files with 87 additions and 27 deletions
|
@ -53,7 +53,7 @@ COMPREPLY=( "hello" )
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestBashCompletions(t *testing.T) {
|
func TestBashCompletions(t *testing.T) {
|
||||||
c := initializeWithRootCmd()
|
c := initializeWithRootCmdWithRun()
|
||||||
cmdEcho.AddCommand(cmdTimes)
|
cmdEcho.AddCommand(cmdTimes)
|
||||||
c.AddCommand(cmdEcho, cmdPrint, cmdDeprecated, cmdColon)
|
c.AddCommand(cmdEcho, cmdPrint, cmdDeprecated, cmdColon)
|
||||||
|
|
||||||
|
|
100
cobra_test.go
100
cobra_test.go
|
@ -27,6 +27,7 @@ var versionUsed int
|
||||||
|
|
||||||
const strtwoParentHelp = "help message for parent flag strtwo"
|
const strtwoParentHelp = "help message for parent flag strtwo"
|
||||||
const strtwoChildHelp = "help message for child flag strtwo"
|
const strtwoChildHelp = "help message for child flag strtwo"
|
||||||
|
const nonExistentCommand = "non_existent_command"
|
||||||
|
|
||||||
var cmdHidden = &Command{
|
var cmdHidden = &Command{
|
||||||
Use: "hide [secret string to print]",
|
Use: "hide [secret string to print]",
|
||||||
|
@ -212,7 +213,7 @@ func initializeWithSameName() *Command {
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
func initializeWithRootCmd() *Command {
|
func initializeWithRootCmdWithRun() *Command {
|
||||||
cmdRootWithRun.ResetCommands()
|
cmdRootWithRun.ResetCommands()
|
||||||
tt, tp, te, tr, rootcalled = nil, nil, nil, nil, false
|
tt, tp, te, tr, rootcalled = nil, nil, nil, nil, false
|
||||||
flagInit()
|
flagInit()
|
||||||
|
@ -229,16 +230,28 @@ type resulter struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func fullSetupTest(input string) resulter {
|
func fullSetupTest(input string) resulter {
|
||||||
c := initializeWithRootCmd()
|
c := initializeWithRootCmdWithRun()
|
||||||
|
|
||||||
return fullTester(c, input)
|
return fullTester(c, input)
|
||||||
}
|
}
|
||||||
|
|
||||||
func noRRSetupTestSilenced(input string) resulter {
|
func noRRSetupTestSilenced(input string) resulter {
|
||||||
c := initialize()
|
c := initialize()
|
||||||
|
|
||||||
|
silenceErrors := c.SilenceErrors
|
||||||
|
silenceUsage := c.SilenceUsage
|
||||||
|
|
||||||
c.SilenceErrors = true
|
c.SilenceErrors = true
|
||||||
c.SilenceUsage = true
|
c.SilenceUsage = true
|
||||||
return fullTester(c, input)
|
|
||||||
|
res := fullTester(c, input)
|
||||||
|
|
||||||
|
// Restore previous values of silencing errors and usage.
|
||||||
|
// This is a workaround for these values not being reset properly between tests.
|
||||||
|
c.SilenceErrors = silenceErrors
|
||||||
|
c.SilenceUsage = silenceUsage
|
||||||
|
|
||||||
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
func noRRSetupTest(input string) resulter {
|
func noRRSetupTest(input string) resulter {
|
||||||
|
@ -248,7 +261,7 @@ func noRRSetupTest(input string) resulter {
|
||||||
}
|
}
|
||||||
|
|
||||||
func rootOnlySetupTest(input string) resulter {
|
func rootOnlySetupTest(input string) resulter {
|
||||||
c := initializeWithRootCmd()
|
c := initializeWithRootCmdWithRun()
|
||||||
|
|
||||||
return simpleTester(c, input)
|
return simpleTester(c, input)
|
||||||
}
|
}
|
||||||
|
@ -585,7 +598,7 @@ func TestTrailingCommandFlags(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestInvalidSubcommandFlags(t *testing.T) {
|
func TestInvalidSubcommandFlags(t *testing.T) {
|
||||||
cmd := initializeWithRootCmd()
|
cmd := initializeWithRootCmdWithRun()
|
||||||
cmd.AddCommand(cmdTimes)
|
cmd.AddCommand(cmdTimes)
|
||||||
|
|
||||||
result := simpleTester(cmd, "times --inttwo=2 --badflag=bar")
|
result := simpleTester(cmd, "times --inttwo=2 --badflag=bar")
|
||||||
|
@ -599,7 +612,7 @@ func TestInvalidSubcommandFlags(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSubcommandExecuteC(t *testing.T) {
|
func TestSubcommandExecuteC(t *testing.T) {
|
||||||
cmd := initializeWithRootCmd()
|
cmd := initializeWithRootCmdWithRun()
|
||||||
double := &Command{
|
double := &Command{
|
||||||
Use: "double message",
|
Use: "double message",
|
||||||
Run: func(c *Command, args []string) {
|
Run: func(c *Command, args []string) {
|
||||||
|
@ -634,7 +647,7 @@ func TestSubcommandExecuteC(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSubcommandArgEvaluation(t *testing.T) {
|
func TestSubcommandArgEvaluation(t *testing.T) {
|
||||||
cmd := initializeWithRootCmd()
|
cmd := initializeWithRootCmdWithRun()
|
||||||
|
|
||||||
first := &Command{
|
first := &Command{
|
||||||
Use: "first",
|
Use: "first",
|
||||||
|
@ -753,7 +766,7 @@ func TestVisitParents(t *testing.T) {
|
||||||
|
|
||||||
func TestRunnableRootCommandNilInput(t *testing.T) {
|
func TestRunnableRootCommandNilInput(t *testing.T) {
|
||||||
var emptyArg []string
|
var emptyArg []string
|
||||||
c := initializeWithRootCmd()
|
c := initializeWithRootCmdWithRun()
|
||||||
|
|
||||||
buf := new(bytes.Buffer)
|
buf := new(bytes.Buffer)
|
||||||
// Testing flag with invalid input
|
// Testing flag with invalid input
|
||||||
|
@ -777,7 +790,7 @@ func TestRunnableRootCommandEmptyInput(t *testing.T) {
|
||||||
args[0] = ""
|
args[0] = ""
|
||||||
args[1] = "--introot=12"
|
args[1] = "--introot=12"
|
||||||
args[2] = ""
|
args[2] = ""
|
||||||
c := initializeWithRootCmd()
|
c := initializeWithRootCmdWithRun()
|
||||||
|
|
||||||
buf := new(bytes.Buffer)
|
buf := new(bytes.Buffer)
|
||||||
// Testing flag with invalid input
|
// Testing flag with invalid input
|
||||||
|
@ -916,23 +929,70 @@ func TestRootUnknownCommand(t *testing.T) {
|
||||||
|
|
||||||
func TestRootUnknownCommandSilenced(t *testing.T) {
|
func TestRootUnknownCommandSilenced(t *testing.T) {
|
||||||
r := noRRSetupTestSilenced("bogus")
|
r := noRRSetupTestSilenced("bogus")
|
||||||
s := "Run 'cobra-test --help' for usage.\n"
|
|
||||||
|
|
||||||
if r.Output != "" {
|
if r.Output != "" {
|
||||||
t.Errorf("Unexpected response.\nExpecting to be: \n\"\"\n Got:\n %q\n", s, r.Output)
|
t.Errorf("Unexpected response.\nExpecting to be: \n\"\"\n Got:\n %q\n", r.Output)
|
||||||
}
|
}
|
||||||
|
|
||||||
r = noRRSetupTestSilenced("--strtwo=a bogus")
|
r = noRRSetupTestSilenced("--strtwo=a bogus")
|
||||||
if r.Output != "" {
|
if r.Output != "" {
|
||||||
t.Errorf("Unexpected response.\nExpecting to be:\n\"\"\nGot:\n %q\n", s, r.Output)
|
t.Errorf("Unexpected response.\nExpecting to be:\n\"\"\nGot:\n %q\n", r.Output)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRootSuggestions(t *testing.T) {
|
func TestSubcommandChecksErrorsWhenSubcommandNotFound(t *testing.T) {
|
||||||
outputWithSuggestions := "Error: unknown command \"%s\" for \"cobra-test\"\n\nDid you mean this?\n\t%s\n\nRun 'cobra-test --help' for usage.\n"
|
rootCmd := initialize()
|
||||||
outputWithoutSuggestions := "Error: unknown command \"%s\" for \"cobra-test\"\nRun 'cobra-test --help' for usage.\n"
|
rootCmd.AddCommand(cmdSubNoRun)
|
||||||
|
// Current semantics is that commands with no subcommands and no run print out the description.
|
||||||
|
cmdSubNoRun.AddCommand(cmdEcho)
|
||||||
|
|
||||||
cmd := initializeWithRootCmd()
|
result := simpleTester(rootCmd, nonExistentCommand)
|
||||||
|
if result.Error == nil {
|
||||||
|
t.Errorf("Unexpected response.\nExpected error, got success with output:\n %q\n", result.Output)
|
||||||
|
}
|
||||||
|
cmdName := rootCmd.Name()
|
||||||
|
expectedOutput := fmt.Sprintf("Error: unknown command %q for %q\nRun '%s --help' for usage.\n", nonExistentCommand, rootCmd.Name(), rootCmd.Name())
|
||||||
|
if result.Output != expectedOutput {
|
||||||
|
t.Errorf("Unexpected response.\nExpected:\n %q\nGot:\n %q\n", expectedOutput, result.Output)
|
||||||
|
}
|
||||||
|
|
||||||
|
result = simpleTester(rootCmd, cmdSubNoRun.Name() + " " + nonExistentCommand)
|
||||||
|
if result.Error == nil {
|
||||||
|
t.Errorf("Unexpected response.\nExpected error, got success with output:\n %q\n", result.Output)
|
||||||
|
}
|
||||||
|
cmdName = rootCmd.Name() + " " + cmdSubNoRun.Name()
|
||||||
|
expectedOutput = fmt.Sprintf("Error: unknown command %q for %q\nRun '%s --help' for usage.\n", nonExistentCommand, cmdName, cmdName)
|
||||||
|
if result.Output != expectedOutput {
|
||||||
|
t.Errorf("Unexpected response.\nExpected:\n %q\nGot:\n %q\n", expectedOutput, result.Output)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSubcommandChecksNoErrorsWhenRunDefined(t *testing.T) {
|
||||||
|
rootCmd := initializeWithRootCmdWithRun()
|
||||||
|
rootCmd.AddCommand(cmdEcho)
|
||||||
|
|
||||||
|
result := simpleTester(rootCmd, nonExistentCommand)
|
||||||
|
if result.Error != nil {
|
||||||
|
t.Errorf("Unexpected response.\nExpected error, got success with output:\n %q\n", result.Output)
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual([]string{nonExistentCommand}, tr) {
|
||||||
|
t.Errorf("Unexpected response.\nExpected output:\n %q\nGot:\n %q\n", []string{nonExistentCommand}, tr)
|
||||||
|
}
|
||||||
|
|
||||||
|
result = simpleTester(rootCmd, cmdEcho.Name() + " " + nonExistentCommand)
|
||||||
|
if result.Error != nil {
|
||||||
|
t.Errorf("Unexpected response.\nExpected error, got success with output:\n %q\n", result.Output)
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual([]string{nonExistentCommand}, te) {
|
||||||
|
t.Errorf("Unexpected response.\nExpected output:\n %q\nGot:\n %q\n", []string{nonExistentCommand}, te)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSubcommandSuggestions(t *testing.T) {
|
||||||
|
outputWithSuggestions := "Error: unknown command %q for \"cobra-test\"\n\nDid you mean this?\n\t%s\n\nRun 'cobra-test --help' for usage.\n"
|
||||||
|
outputWithoutSuggestions := "Error: unknown command %q for \"cobra-test\"\nRun 'cobra-test --help' for usage.\n"
|
||||||
|
|
||||||
|
cmd := initialize()
|
||||||
cmd.AddCommand(cmdTimes)
|
cmd.AddCommand(cmdTimes)
|
||||||
|
|
||||||
tests := map[string]string{
|
tests := map[string]string{
|
||||||
|
@ -1023,7 +1083,7 @@ func TestFlagsBeforeCommand(t *testing.T) {
|
||||||
|
|
||||||
func TestRemoveCommand(t *testing.T) {
|
func TestRemoveCommand(t *testing.T) {
|
||||||
versionUsed = 0
|
versionUsed = 0
|
||||||
c := initializeWithRootCmd()
|
c := initialize()
|
||||||
c.AddCommand(cmdVersion1)
|
c.AddCommand(cmdVersion1)
|
||||||
c.RemoveCommand(cmdVersion1)
|
c.RemoveCommand(cmdVersion1)
|
||||||
x := fullTester(c, "version")
|
x := fullTester(c, "version")
|
||||||
|
@ -1034,7 +1094,7 @@ func TestRemoveCommand(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCommandWithoutSubcommands(t *testing.T) {
|
func TestCommandWithoutSubcommands(t *testing.T) {
|
||||||
c := initializeWithRootCmd()
|
c := initializeWithRootCmdWithRun()
|
||||||
|
|
||||||
x := simpleTester(c, "")
|
x := simpleTester(c, "")
|
||||||
if x.Error != nil {
|
if x.Error != nil {
|
||||||
|
@ -1044,7 +1104,7 @@ func TestCommandWithoutSubcommands(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCommandWithoutSubcommandsWithArg(t *testing.T) {
|
func TestCommandWithoutSubcommandsWithArg(t *testing.T) {
|
||||||
c := initializeWithRootCmd()
|
c := initializeWithRootCmdWithRun()
|
||||||
expectedArgs := []string{"arg"}
|
expectedArgs := []string{"arg"}
|
||||||
|
|
||||||
x := simpleTester(c, "arg")
|
x := simpleTester(c, "arg")
|
||||||
|
@ -1060,7 +1120,7 @@ func TestCommandWithoutSubcommandsWithArg(t *testing.T) {
|
||||||
|
|
||||||
func TestReplaceCommandWithRemove(t *testing.T) {
|
func TestReplaceCommandWithRemove(t *testing.T) {
|
||||||
versionUsed = 0
|
versionUsed = 0
|
||||||
c := initializeWithRootCmd()
|
c := initializeWithRootCmdWithRun()
|
||||||
c.AddCommand(cmdVersion1)
|
c.AddCommand(cmdVersion1)
|
||||||
c.RemoveCommand(cmdVersion1)
|
c.RemoveCommand(cmdVersion1)
|
||||||
c.AddCommand(cmdVersion2)
|
c.AddCommand(cmdVersion2)
|
||||||
|
|
12
command.go
12
command.go
|
@ -462,14 +462,14 @@ func (c *Command) Find(args []string) (*Command, []string, error) {
|
||||||
return commandFound, a, nil
|
return commandFound, a, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// root command with subcommands, do subcommand checking
|
// command with no run method, do subcommand checking
|
||||||
if commandFound == c && len(argsWOflags) > 0 {
|
if commandFound.Run == nil && commandFound.RunE == nil && len(argsWOflags) > 0 {
|
||||||
suggestionsString := ""
|
suggestionsString := ""
|
||||||
if !c.DisableSuggestions {
|
if !commandFound.DisableSuggestions {
|
||||||
if c.SuggestionsMinimumDistance <= 0 {
|
if commandFound.SuggestionsMinimumDistance <= 0 {
|
||||||
c.SuggestionsMinimumDistance = 2
|
commandFound.SuggestionsMinimumDistance = 2
|
||||||
}
|
}
|
||||||
if suggestions := c.SuggestionsFor(argsWOflags[0]); len(suggestions) > 0 {
|
if suggestions := commandFound.SuggestionsFor(argsWOflags[0]); len(suggestions) > 0 {
|
||||||
suggestionsString += "\n\nDid you mean this?\n"
|
suggestionsString += "\n\nDid you mean this?\n"
|
||||||
for _, s := range suggestions {
|
for _, s := range suggestions {
|
||||||
suggestionsString += fmt.Sprintf("\t%v\n", s)
|
suggestionsString += fmt.Sprintf("\t%v\n", s)
|
||||||
|
|
Loading…
Reference in a new issue