mirror of
https://github.com/spf13/cobra
synced 2025-09-22 17:51:54 +00:00
test: Add tests for error structs
This commit is contained in:
parent
0fd2fcdab5
commit
e37797b5ed
4 changed files with 331 additions and 1 deletions
77
args_test.go
77
args_test.go
|
@ -15,7 +15,9 @@
|
|||
package cobra
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
@ -32,6 +34,14 @@ func getCommand(args PositionalArgs, withValid bool) *Command {
|
|||
return c
|
||||
}
|
||||
|
||||
func getCommandName(c *Command) string {
|
||||
if c == nil {
|
||||
return "<nil>"
|
||||
} else {
|
||||
return c.Name()
|
||||
}
|
||||
}
|
||||
|
||||
func expectSuccess(output string, err error, t *testing.T) {
|
||||
if output != "" {
|
||||
t.Errorf("Unexpected output: %v", output)
|
||||
|
@ -41,6 +51,31 @@ func expectSuccess(output string, err error, t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func expectErrorAs(err error, target error, t *testing.T) {
|
||||
if err == nil {
|
||||
t.Fatalf("Expected error, got nil")
|
||||
}
|
||||
|
||||
targetType := reflect.TypeOf(target)
|
||||
targetPtr := reflect.New(targetType).Interface() // *SomeError
|
||||
if !errors.As(err, targetPtr) {
|
||||
t.Fatalf("Expected error to be %T, got %T", target, err)
|
||||
}
|
||||
}
|
||||
|
||||
func expectErrorHasCommand(err error, cmd *Command, t *testing.T) {
|
||||
getCommand, ok := err.(interface{ GetCommand() *Command })
|
||||
if !ok {
|
||||
t.Fatalf("Expected error to have GetCommand method, but did not")
|
||||
}
|
||||
|
||||
got := getCommand.GetCommand()
|
||||
if cmd != got {
|
||||
t.Errorf("Expected err.GetCommand to return %v, got %v",
|
||||
getCommandName(cmd), getCommandName(got))
|
||||
}
|
||||
}
|
||||
|
||||
func validOnlyWithInvalidArgs(err error, t *testing.T) {
|
||||
if err == nil {
|
||||
t.Fatal("Expected an error")
|
||||
|
@ -139,6 +174,13 @@ func TestNoArgs_WithValidOnly_WithInvalidArgs(t *testing.T) {
|
|||
validOnlyWithInvalidArgs(err, t)
|
||||
}
|
||||
|
||||
func TestNoArgs_ReturnsUnknownSubcommandError(t *testing.T) {
|
||||
c := getCommand(NoArgs, false)
|
||||
_, err := executeCommand(c, "a")
|
||||
expectErrorAs(err, &UnknownSubcommandError{}, t)
|
||||
expectErrorHasCommand(err, c, t)
|
||||
}
|
||||
|
||||
// OnlyValidArgs
|
||||
|
||||
func TestOnlyValidArgs(t *testing.T) {
|
||||
|
@ -153,6 +195,13 @@ func TestOnlyValidArgs_WithInvalidArgs(t *testing.T) {
|
|||
validOnlyWithInvalidArgs(err, t)
|
||||
}
|
||||
|
||||
func TestOnlyValidArgs_ReturnsInvalidArgValueError(t *testing.T) {
|
||||
c := getCommand(OnlyValidArgs, true)
|
||||
_, err := executeCommand(c, "a")
|
||||
expectErrorAs(err, &InvalidArgValueError{}, t)
|
||||
expectErrorHasCommand(err, c, t)
|
||||
}
|
||||
|
||||
// ArbitraryArgs
|
||||
|
||||
func TestArbitraryArgs(t *testing.T) {
|
||||
|
@ -229,6 +278,13 @@ func TestMinimumNArgs_WithLessArgs_WithValidOnly_WithInvalidArgs(t *testing.T) {
|
|||
validOnlyWithInvalidArgs(err, t)
|
||||
}
|
||||
|
||||
func TestMinimumNArgs_ReturnsInvalidArgCountError(t *testing.T) {
|
||||
c := getCommand(MinimumNArgs(2), true)
|
||||
_, err := executeCommand(c, "a")
|
||||
expectErrorAs(err, &InvalidArgCountError{}, t)
|
||||
expectErrorHasCommand(err, c, t)
|
||||
}
|
||||
|
||||
// MaximumNArgs
|
||||
|
||||
func TestMaximumNArgs(t *testing.T) {
|
||||
|
@ -279,6 +335,13 @@ func TestMaximumNArgs_WithMoreArgs_WithValidOnly_WithInvalidArgs(t *testing.T) {
|
|||
validOnlyWithInvalidArgs(err, t)
|
||||
}
|
||||
|
||||
func TestMaximumNArgs_ReturnsInvalidArgCountError(t *testing.T) {
|
||||
c := getCommand(MaximumNArgs(2), true)
|
||||
_, err := executeCommand(c, "a", "b", "c")
|
||||
expectErrorAs(err, &InvalidArgCountError{}, t)
|
||||
expectErrorHasCommand(err, c, t)
|
||||
}
|
||||
|
||||
// ExactArgs
|
||||
|
||||
func TestExactArgs(t *testing.T) {
|
||||
|
@ -329,6 +392,13 @@ func TestExactArgs_WithInvalidCount_WithValidOnly_WithInvalidArgs(t *testing.T)
|
|||
validOnlyWithInvalidArgs(err, t)
|
||||
}
|
||||
|
||||
func TestExactArgs_ReturnsInvalidArgCountError(t *testing.T) {
|
||||
c := getCommand(ExactArgs(2), true)
|
||||
_, err := executeCommand(c, "a")
|
||||
expectErrorAs(err, &InvalidArgCountError{}, t)
|
||||
expectErrorHasCommand(err, c, t)
|
||||
}
|
||||
|
||||
// RangeArgs
|
||||
|
||||
func TestRangeArgs(t *testing.T) {
|
||||
|
@ -379,6 +449,13 @@ func TestRangeArgs_WithInvalidCount_WithValidOnly_WithInvalidArgs(t *testing.T)
|
|||
validOnlyWithInvalidArgs(err, t)
|
||||
}
|
||||
|
||||
func TestRangeArgs_ReturnsInvalidArgCountError(t *testing.T) {
|
||||
c := getCommand(RangeArgs(2, 4), true)
|
||||
_, err := executeCommand(c, "a")
|
||||
expectErrorAs(err, &InvalidArgCountError{}, t)
|
||||
expectErrorHasCommand(err, c, t)
|
||||
}
|
||||
|
||||
// Takes(No)Args
|
||||
|
||||
func TestRootTakesNoArgs(t *testing.T) {
|
||||
|
|
|
@ -17,6 +17,7 @@ package cobra
|
|||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
|
@ -866,6 +867,21 @@ func TestRequiredFlags(t *testing.T) {
|
|||
if got != expected {
|
||||
t.Errorf("Expected error: %q, got: %q", expected, got)
|
||||
}
|
||||
|
||||
// Test it returns valid RequiredFlagError.
|
||||
var requiredFlagErr *RequiredFlagError
|
||||
if !errors.As(err, &requiredFlagErr) {
|
||||
t.Fatalf("Expected error to be RequiredFlagError, got %T", err)
|
||||
}
|
||||
|
||||
expectedMissingFlagNames := "foo1 foo2"
|
||||
gotMissingFlagNames := strings.Join(requiredFlagErr.missingFlagNames, " ")
|
||||
if expectedMissingFlagNames != gotMissingFlagNames {
|
||||
t.Errorf("Expected error missingFlagNames to be %q, got %q",
|
||||
expectedMissingFlagNames, gotMissingFlagNames)
|
||||
}
|
||||
|
||||
expectErrorHasCommand(err, c, t)
|
||||
}
|
||||
|
||||
func TestPersistentRequiredFlags(t *testing.T) {
|
||||
|
|
195
errors_test.go
Normal file
195
errors_test.go
Normal file
|
@ -0,0 +1,195 @@
|
|||
// Copyright 2013-2023 The Cobra Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package cobra
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// InvalidArgCountError
|
||||
|
||||
func TestInvalidArgCountError_GetCommand(t *testing.T) {
|
||||
expected := &Command{}
|
||||
err := &InvalidArgCountError{cmd: expected}
|
||||
|
||||
got := err.GetCommand()
|
||||
if got != expected {
|
||||
t.Errorf("expected %v, got %v",
|
||||
getCommandName(expected), getCommandName(got))
|
||||
}
|
||||
}
|
||||
|
||||
func TestInvalidArgCountError_GetArgs(t *testing.T) {
|
||||
expected := []string{"a", "b", "c"}
|
||||
err := &InvalidArgCountError{args: expected}
|
||||
|
||||
got := err.GetArguments()
|
||||
if strings.Join(expected, " ") != strings.Join(got, " ") {
|
||||
t.Fatalf("expected %v, got %v", expected, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestInvalidArgCountError_GetMinArgumentCount(t *testing.T) {
|
||||
expected := 1
|
||||
err := &InvalidArgCountError{atLeast: expected}
|
||||
|
||||
got := err.GetMinArgumentCount()
|
||||
if got != expected {
|
||||
t.Fatalf("expected %v, got %v", expected, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestInvalidArgCountError_GetMaxArgumentCount(t *testing.T) {
|
||||
expected := 1
|
||||
err := &InvalidArgCountError{atMost: expected}
|
||||
|
||||
got := err.GetMaxArgumentCount()
|
||||
if got != expected {
|
||||
t.Fatalf("expected %v, got %v", expected, got)
|
||||
}
|
||||
}
|
||||
|
||||
// InvalidArgValueError
|
||||
|
||||
func TestInvalidArgValueError_GetCommand(t *testing.T) {
|
||||
expected := &Command{}
|
||||
err := &InvalidArgValueError{cmd: expected}
|
||||
|
||||
got := err.GetCommand()
|
||||
if got != expected {
|
||||
t.Errorf("expected %v, got %v",
|
||||
getCommandName(expected), getCommandName(got))
|
||||
}
|
||||
}
|
||||
|
||||
func TestInvalidArgValueError_GetArgument(t *testing.T) {
|
||||
expected := "a"
|
||||
err := &InvalidArgValueError{arg: expected}
|
||||
|
||||
got := err.GetArgument()
|
||||
if got != expected {
|
||||
t.Fatalf("expected %v, got %v", expected, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestInvalidArgValueError_GetSuggestions(t *testing.T) {
|
||||
expected := "a"
|
||||
err := &InvalidArgValueError{suggestions: expected}
|
||||
|
||||
got := err.GetSuggestions()
|
||||
if got != expected {
|
||||
t.Fatalf("expected %v, got %v", expected, got)
|
||||
}
|
||||
}
|
||||
|
||||
// UnknownSubcommandError
|
||||
|
||||
func TestUnknownSubcommandError_GetCommand(t *testing.T) {
|
||||
expected := &Command{}
|
||||
err := &UnknownSubcommandError{cmd: expected}
|
||||
|
||||
got := err.GetCommand()
|
||||
if got != expected {
|
||||
t.Errorf("expected %v, got %v",
|
||||
getCommandName(expected), getCommandName(got))
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnknownSubcommandError_GetSubcommand(t *testing.T) {
|
||||
expected := "a"
|
||||
err := &UnknownSubcommandError{subcmd: expected}
|
||||
|
||||
got := err.GetSubcommand()
|
||||
if got != expected {
|
||||
t.Fatalf("expected %v, got %v", expected, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnknownSubcommandError_GetSuggestions(t *testing.T) {
|
||||
expected := "a"
|
||||
err := &UnknownSubcommandError{suggestions: expected}
|
||||
|
||||
got := err.GetSuggestions()
|
||||
if got != expected {
|
||||
t.Fatalf("expected %v, got %v", expected, got)
|
||||
}
|
||||
}
|
||||
|
||||
// RequiredFlagError
|
||||
|
||||
func TestRequiredFlagError_GetCommand(t *testing.T) {
|
||||
expected := &Command{}
|
||||
err := &UnknownSubcommandError{cmd: expected}
|
||||
|
||||
got := err.GetCommand()
|
||||
if got != expected {
|
||||
t.Errorf("expected %v, got %v",
|
||||
getCommandName(expected), getCommandName(got))
|
||||
}
|
||||
}
|
||||
|
||||
func TestRequiredFlagError_GetFlags(t *testing.T) {
|
||||
expected := []string{"a", "b", "c"}
|
||||
err := &RequiredFlagError{missingFlagNames: expected}
|
||||
|
||||
got := err.GetFlags()
|
||||
if strings.Join(expected, " ") != strings.Join(got, " ") {
|
||||
t.Fatalf("expected %v, got %v", expected, got)
|
||||
}
|
||||
}
|
||||
|
||||
// FlagGroupError
|
||||
|
||||
func TestFlagGroupError_GetCommand(t *testing.T) {
|
||||
expected := &Command{}
|
||||
err := &FlagGroupError{cmd: expected}
|
||||
|
||||
got := err.GetCommand()
|
||||
if got != expected {
|
||||
t.Errorf("expected %v, got %v",
|
||||
getCommandName(expected), getCommandName(got))
|
||||
}
|
||||
}
|
||||
|
||||
func TestFlagGroupError_GetFlags(t *testing.T) {
|
||||
expected := []string{"a", "b", "c"}
|
||||
err := &FlagGroupError{flagList: "a b c"}
|
||||
|
||||
got := err.GetFlags()
|
||||
if strings.Join(expected, " ") != strings.Join(got, " ") {
|
||||
t.Fatalf("expected %v, got %v", expected, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFlagGroupError_GetProblemFlags(t *testing.T) {
|
||||
expected := []string{"a", "b", "c"}
|
||||
err := &FlagGroupError{problemFlags: expected}
|
||||
|
||||
got := err.GetProblemFlags()
|
||||
if strings.Join(expected, " ") != strings.Join(got, " ") {
|
||||
t.Fatalf("expected %v, got %v", expected, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFlagGroupError_GetFlagGroupType(t *testing.T) {
|
||||
expected := FlagsAreMutuallyExclusive
|
||||
err := &FlagGroupError{flagGroupType: expected}
|
||||
|
||||
got := err.GetFlagGroupType()
|
||||
if got != expected {
|
||||
t.Fatalf("expected %v, got %v", expected, got)
|
||||
}
|
||||
}
|
|
@ -15,6 +15,7 @@
|
|||
package cobra
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
@ -52,6 +53,7 @@ func TestValidateFlagGroups(t *testing.T) {
|
|||
subCmdFlagGroupsExclusive []string
|
||||
args []string
|
||||
expectErr string
|
||||
expectErrGroupType FlagGroupType
|
||||
}{
|
||||
{
|
||||
desc: "No flags no problem",
|
||||
|
@ -64,64 +66,76 @@ func TestValidateFlagGroups(t *testing.T) {
|
|||
flagGroupsRequired: []string{"a b c"},
|
||||
args: []string{"--a=foo"},
|
||||
expectErr: "if any flags in the group [a b c] are set they must all be set; missing [b c]",
|
||||
expectErrGroupType: FlagsAreRequiredTogether,
|
||||
}, {
|
||||
desc: "One-required flag group not satisfied",
|
||||
flagGroupsOneRequired: []string{"a b"},
|
||||
args: []string{"--c=foo"},
|
||||
expectErr: "at least one of the flags in the group [a b] is required",
|
||||
expectErrGroupType: FlagsAreOneRequired,
|
||||
}, {
|
||||
desc: "Exclusive flag group not satisfied",
|
||||
flagGroupsExclusive: []string{"a b c"},
|
||||
args: []string{"--a=foo", "--b=foo"},
|
||||
expectErr: "if any flags in the group [a b c] are set none of the others can be; [a b] were all set",
|
||||
expectErrGroupType: FlagsAreMutuallyExclusive,
|
||||
}, {
|
||||
desc: "Multiple required flag group not satisfied returns first error",
|
||||
flagGroupsRequired: []string{"a b c", "a d"},
|
||||
args: []string{"--c=foo", "--d=foo"},
|
||||
expectErr: `if any flags in the group [a b c] are set they must all be set; missing [a b]`,
|
||||
expectErrGroupType: FlagsAreRequiredTogether,
|
||||
}, {
|
||||
desc: "Multiple one-required flag group not satisfied returns first error",
|
||||
flagGroupsOneRequired: []string{"a b", "d e"},
|
||||
args: []string{"--c=foo", "--f=foo"},
|
||||
expectErr: `at least one of the flags in the group [a b] is required`,
|
||||
expectErrGroupType: FlagsAreOneRequired,
|
||||
}, {
|
||||
desc: "Multiple exclusive flag group not satisfied returns first error",
|
||||
flagGroupsExclusive: []string{"a b c", "a d"},
|
||||
args: []string{"--a=foo", "--c=foo", "--d=foo"},
|
||||
expectErr: `if any flags in the group [a b c] are set none of the others can be; [a c] were all set`,
|
||||
expectErrGroupType: FlagsAreMutuallyExclusive,
|
||||
}, {
|
||||
desc: "Validation of required groups occurs on groups in sorted order",
|
||||
flagGroupsRequired: []string{"a d", "a b", "a c"},
|
||||
args: []string{"--a=foo"},
|
||||
expectErr: `if any flags in the group [a b] are set they must all be set; missing [b]`,
|
||||
expectErrGroupType: FlagsAreRequiredTogether,
|
||||
}, {
|
||||
desc: "Validation of one-required groups occurs on groups in sorted order",
|
||||
flagGroupsOneRequired: []string{"d e", "a b", "f g"},
|
||||
args: []string{"--c=foo"},
|
||||
expectErr: `at least one of the flags in the group [a b] is required`,
|
||||
expectErrGroupType: FlagsAreOneRequired,
|
||||
}, {
|
||||
desc: "Validation of exclusive groups occurs on groups in sorted order",
|
||||
flagGroupsExclusive: []string{"a d", "a b", "a c"},
|
||||
args: []string{"--a=foo", "--b=foo", "--c=foo"},
|
||||
expectErr: `if any flags in the group [a b] are set none of the others can be; [a b] were all set`,
|
||||
expectErrGroupType: FlagsAreMutuallyExclusive,
|
||||
}, {
|
||||
desc: "Persistent flags utilize required and exclusive groups and can fail required groups",
|
||||
flagGroupsRequired: []string{"a e", "e f"},
|
||||
flagGroupsExclusive: []string{"f g"},
|
||||
args: []string{"--a=foo", "--f=foo", "--g=foo"},
|
||||
expectErr: `if any flags in the group [a e] are set they must all be set; missing [e]`,
|
||||
expectErrGroupType: FlagsAreRequiredTogether,
|
||||
}, {
|
||||
desc: "Persistent flags utilize one-required and exclusive groups and can fail one-required groups",
|
||||
flagGroupsOneRequired: []string{"a b", "e f"},
|
||||
flagGroupsExclusive: []string{"e f"},
|
||||
args: []string{"--e=foo"},
|
||||
expectErr: `at least one of the flags in the group [a b] is required`,
|
||||
expectErrGroupType: FlagsAreOneRequired,
|
||||
}, {
|
||||
desc: "Persistent flags utilize required and exclusive groups and can fail mutually exclusive groups",
|
||||
flagGroupsRequired: []string{"a e", "e f"},
|
||||
flagGroupsExclusive: []string{"f g"},
|
||||
args: []string{"--a=foo", "--e=foo", "--f=foo", "--g=foo"},
|
||||
expectErr: `if any flags in the group [f g] are set none of the others can be; [f g] were all set`,
|
||||
expectErrGroupType: FlagsAreMutuallyExclusive,
|
||||
}, {
|
||||
desc: "Persistent flags utilize required and exclusive groups and can pass",
|
||||
flagGroupsRequired: []string{"a e", "e f"},
|
||||
|
@ -145,11 +159,13 @@ func TestValidateFlagGroups(t *testing.T) {
|
|||
subCmdFlagGroupsOneRequired: []string{"e subonly"},
|
||||
args: []string{"subcmd"},
|
||||
expectErr: "at least one of the flags in the group [e subonly] is required",
|
||||
expectErrGroupType: FlagsAreOneRequired,
|
||||
}, {
|
||||
desc: "Subcmds can use exclusive groups using inherited flags",
|
||||
subCmdFlagGroupsExclusive: []string{"e subonly"},
|
||||
args: []string{"subcmd", "--e=foo", "--subonly=foo"},
|
||||
expectErr: "if any flags in the group [e subonly] are set none of the others can be; [e subonly] were all set",
|
||||
expectErrGroupType: FlagsAreMutuallyExclusive,
|
||||
}, {
|
||||
desc: "Subcmds can use exclusive groups using inherited flags and pass",
|
||||
subCmdFlagGroupsExclusive: []string{"e subonly"},
|
||||
|
@ -183,13 +199,39 @@ func TestValidateFlagGroups(t *testing.T) {
|
|||
sub.MarkFlagsMutuallyExclusive(strings.Split(flagGroup, " ")...)
|
||||
}
|
||||
c.SetArgs(tc.args)
|
||||
err := c.Execute()
|
||||
executedCmd, err := c.ExecuteC()
|
||||
switch {
|
||||
case err == nil && len(tc.expectErr) > 0:
|
||||
t.Errorf("Expected error %q but got nil", tc.expectErr)
|
||||
case err != nil && err.Error() != tc.expectErr:
|
||||
t.Errorf("Expected error %q but got %q", tc.expectErr, err)
|
||||
}
|
||||
|
||||
if len(tc.expectErr) > 0 {
|
||||
var flagGroupErr *FlagGroupError
|
||||
if !errors.As(err, &flagGroupErr) {
|
||||
t.Fatalf("Expected error to be FlagGroupError, got %T", err)
|
||||
}
|
||||
|
||||
gotGroupType := flagGroupErr.flagGroupType
|
||||
if gotGroupType != tc.expectErrGroupType {
|
||||
t.Errorf("Expected FlagGroupError flag group type to be %q, got %q",
|
||||
tc.expectErrGroupType, gotGroupType)
|
||||
}
|
||||
|
||||
if flagGroupErr.cmd != executedCmd {
|
||||
t.Errorf("Expected FlagGroupError to have command %v, got %v",
|
||||
getCommandName(executedCmd), getCommandName(flagGroupErr.cmd))
|
||||
}
|
||||
|
||||
if flagGroupErr.flagList == "" {
|
||||
t.Errorf("Expected FlagGroupError to have flagList, but was empty")
|
||||
}
|
||||
|
||||
if gotGroupType != FlagsAreOneRequired && flagGroupErr.problemFlags == nil {
|
||||
t.Errorf("Expected FlagGroupError to have problemFlags, but was nil")
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue