diff --git a/cli/command/checkpoint/create_test.go b/cli/command/checkpoint/create_test.go index d0d47f45..32c9d88a 100644 --- a/cli/command/checkpoint/create_test.go +++ b/cli/command/checkpoint/create_test.go @@ -20,11 +20,11 @@ func TestCheckpointCreateErrors(t *testing.T) { }{ { args: []string{"too-few-arguments"}, - expectedError: "requires exactly 2 argument(s)", + expectedError: "requires exactly 2 arguments", }, { args: []string{"too", "many", "arguments"}, - expectedError: "requires exactly 2 argument(s)", + expectedError: "requires exactly 2 arguments", }, { args: []string{"foo", "bar"}, diff --git a/cli/command/checkpoint/remove_test.go b/cli/command/checkpoint/remove_test.go index ecc66d8b..3d85d16c 100644 --- a/cli/command/checkpoint/remove_test.go +++ b/cli/command/checkpoint/remove_test.go @@ -20,11 +20,11 @@ func TestCheckpointRemoveErrors(t *testing.T) { }{ { args: []string{"too-few-arguments"}, - expectedError: "requires exactly 2 argument(s)", + expectedError: "requires exactly 2 arguments", }, { args: []string{"too", "many", "arguments"}, - expectedError: "requires exactly 2 argument(s)", + expectedError: "requires exactly 2 arguments", }, { args: []string{"foo", "bar"}, diff --git a/cli/command/config/create_test.go b/cli/command/config/create_test.go index 5cdafbdb..f2338d00 100644 --- a/cli/command/config/create_test.go +++ b/cli/command/config/create_test.go @@ -26,10 +26,10 @@ func TestConfigCreateErrors(t *testing.T) { }{ { args: []string{"too_few"}, - expectedError: "requires exactly 2 argument(s)", + expectedError: "requires exactly 2 arguments", }, {args: []string{"too", "many", "arguments"}, - expectedError: "requires exactly 2 argument(s)", + expectedError: "requires exactly 2 arguments", }, { args: []string{"name", filepath.Join("testdata", configDataFile)}, diff --git a/cli/command/config/remove_test.go b/cli/command/config/remove_test.go index 85b188c4..2b2b28ae 100644 --- a/cli/command/config/remove_test.go +++ b/cli/command/config/remove_test.go @@ -20,7 +20,7 @@ func TestConfigRemoveErrors(t *testing.T) { }{ { args: []string{}, - expectedError: "requires at least 1 argument(s).", + expectedError: "requires at least 1 argument.", }, { args: []string{"foo"}, diff --git a/cli/command/image/history_test.go b/cli/command/image/history_test.go index b3abd0be..59c79f57 100644 --- a/cli/command/image/history_test.go +++ b/cli/command/image/history_test.go @@ -25,7 +25,7 @@ func TestNewHistoryCommandErrors(t *testing.T) { { name: "wrong-args", args: []string{}, - expectedError: "requires exactly 1 argument(s).", + expectedError: "requires exactly 1 argument.", }, { name: "client-error", diff --git a/cli/command/image/import_test.go b/cli/command/image/import_test.go index 6a8e2e0f..6fda4d6c 100644 --- a/cli/command/image/import_test.go +++ b/cli/command/image/import_test.go @@ -24,7 +24,7 @@ func TestNewImportCommandErrors(t *testing.T) { { name: "wrong-args", args: []string{}, - expectedError: "requires at least 1 argument(s).", + expectedError: "requires at least 1 argument.", }, { name: "import-failed", diff --git a/cli/command/image/inspect_test.go b/cli/command/image/inspect_test.go index 2f336bdb..acf80f37 100644 --- a/cli/command/image/inspect_test.go +++ b/cli/command/image/inspect_test.go @@ -22,7 +22,7 @@ func TestNewInspectCommandErrors(t *testing.T) { { name: "wrong-args", args: []string{}, - expectedError: "requires at least 1 argument(s).", + expectedError: "requires at least 1 argument.", }, } for _, tc := range testCases { diff --git a/cli/command/image/list_test.go b/cli/command/image/list_test.go index b367ef1d..5ae1e47e 100644 --- a/cli/command/image/list_test.go +++ b/cli/command/image/list_test.go @@ -25,7 +25,7 @@ func TestNewImagesCommandErrors(t *testing.T) { { name: "wrong-args", args: []string{"arg1", "arg2"}, - expectedError: "requires at most 1 argument(s).", + expectedError: "requires at most 1 argument.", }, { name: "failed-list", diff --git a/cli/command/image/load_test.go b/cli/command/image/load_test.go index 06446bf6..519604d0 100644 --- a/cli/command/image/load_test.go +++ b/cli/command/image/load_test.go @@ -27,7 +27,7 @@ func TestNewLoadCommandErrors(t *testing.T) { { name: "wrong-args", args: []string{"arg"}, - expectedError: "accepts no argument(s).", + expectedError: "accepts no arguments.", }, { name: "input-to-terminal", diff --git a/cli/command/image/prune_test.go b/cli/command/image/prune_test.go index be25826d..7f320c7a 100644 --- a/cli/command/image/prune_test.go +++ b/cli/command/image/prune_test.go @@ -25,7 +25,7 @@ func TestNewPruneCommandErrors(t *testing.T) { { name: "wrong-args", args: []string{"something"}, - expectedError: "accepts no argument(s).", + expectedError: "accepts no arguments.", }, { name: "prune-error", diff --git a/cli/command/image/pull_test.go b/cli/command/image/pull_test.go index c23484f8..71d25832 100644 --- a/cli/command/image/pull_test.go +++ b/cli/command/image/pull_test.go @@ -19,7 +19,7 @@ func TestNewPullCommandErrors(t *testing.T) { }{ { name: "wrong-args", - expectedError: "requires exactly 1 argument(s).", + expectedError: "requires exactly 1 argument.", args: []string{}, }, { diff --git a/cli/command/image/push_test.go b/cli/command/image/push_test.go index 2c824c46..1ead5b15 100644 --- a/cli/command/image/push_test.go +++ b/cli/command/image/push_test.go @@ -23,7 +23,7 @@ func TestNewPushCommandErrors(t *testing.T) { { name: "wrong-args", args: []string{}, - expectedError: "requires exactly 1 argument(s).", + expectedError: "requires exactly 1 argument.", }, { name: "invalid-name", diff --git a/cli/command/image/remove_test.go b/cli/command/image/remove_test.go index 55942724..f88bc885 100644 --- a/cli/command/image/remove_test.go +++ b/cli/command/image/remove_test.go @@ -29,7 +29,7 @@ func TestNewRemoveCommandErrors(t *testing.T) { }{ { name: "wrong args", - expectedError: "requires at least 1 argument(s).", + expectedError: "requires at least 1 argument.", }, { name: "ImageRemove fail", diff --git a/cli/command/image/save_test.go b/cli/command/image/save_test.go index 1cac4c57..84dee142 100644 --- a/cli/command/image/save_test.go +++ b/cli/command/image/save_test.go @@ -26,7 +26,7 @@ func TestNewSaveCommandErrors(t *testing.T) { { name: "wrong args", args: []string{}, - expectedError: "requires at least 1 argument(s).", + expectedError: "requires at least 1 argument.", }, { name: "output to terminal", diff --git a/cli/command/image/tag_test.go b/cli/command/image/tag_test.go index b537f9d6..c86bd22b 100644 --- a/cli/command/image/tag_test.go +++ b/cli/command/image/tag_test.go @@ -15,7 +15,7 @@ func TestCliNewTagCommandErrors(t *testing.T) { {"image1"}, {"image1", "image2", "image3"}, } - expectedError := "\"tag\" requires exactly 2 argument(s)." + expectedError := "\"tag\" requires exactly 2 arguments." for _, args := range testCases { cmd := NewTagCommand(test.NewFakeCli(&fakeClient{})) cmd.SetArgs(args) diff --git a/cli/command/network/connect_test.go b/cli/command/network/connect_test.go index 790ec25c..577dd8c2 100644 --- a/cli/command/network/connect_test.go +++ b/cli/command/network/connect_test.go @@ -19,7 +19,7 @@ func TestNetworkConnectErrors(t *testing.T) { expectedError string }{ { - expectedError: "requires exactly 2 argument(s)", + expectedError: "requires exactly 2 arguments", }, { args: []string{"toto", "titi"}, diff --git a/cli/command/network/disconnect_test.go b/cli/command/network/disconnect_test.go index 01cba2ff..9b915a19 100644 --- a/cli/command/network/disconnect_test.go +++ b/cli/command/network/disconnect_test.go @@ -17,7 +17,7 @@ func TestNetworkDisconnectErrors(t *testing.T) { expectedError string }{ { - expectedError: "requires exactly 2 argument(s)", + expectedError: "requires exactly 2 arguments", }, { args: []string{"toto", "titi"}, diff --git a/cli/command/secret/create_test.go b/cli/command/secret/create_test.go index 2c4f9f5d..592ede0f 100644 --- a/cli/command/secret/create_test.go +++ b/cli/command/secret/create_test.go @@ -27,10 +27,10 @@ func TestSecretCreateErrors(t *testing.T) { }{ { args: []string{"too_few"}, - expectedError: "requires exactly 2 argument(s)", + expectedError: "requires exactly 2 arguments", }, {args: []string{"too", "many", "arguments"}, - expectedError: "requires exactly 2 argument(s)", + expectedError: "requires exactly 2 arguments", }, { args: []string{"name", filepath.Join("testdata", secretDataFile)}, diff --git a/cli/command/secret/remove_test.go b/cli/command/secret/remove_test.go index 6e5ab5fb..ad479c0a 100644 --- a/cli/command/secret/remove_test.go +++ b/cli/command/secret/remove_test.go @@ -20,7 +20,7 @@ func TestSecretRemoveErrors(t *testing.T) { }{ { args: []string{}, - expectedError: "requires at least 1 argument(s).", + expectedError: "requires at least 1 argument.", }, { args: []string{"foo"}, diff --git a/cli/command/swarm/leave_test.go b/cli/command/swarm/leave_test.go index aecac284..b5b672fe 100644 --- a/cli/command/swarm/leave_test.go +++ b/cli/command/swarm/leave_test.go @@ -21,7 +21,7 @@ func TestSwarmLeaveErrors(t *testing.T) { { name: "too-many-args", args: []string{"foo"}, - expectedError: "accepts no argument(s)", + expectedError: "accepts no arguments", }, { name: "leave-failed", diff --git a/cli/command/swarm/unlock_key_test.go b/cli/command/swarm/unlock_key_test.go index 9289a151..bb3935ce 100644 --- a/cli/command/swarm/unlock_key_test.go +++ b/cli/command/swarm/unlock_key_test.go @@ -29,7 +29,7 @@ func TestSwarmUnlockKeyErrors(t *testing.T) { { name: "too-many-args", args: []string{"foo"}, - expectedError: "accepts no argument(s)", + expectedError: "accepts no arguments", }, { name: "swarm-inspect-rotate-failed", diff --git a/cli/command/swarm/unlock_test.go b/cli/command/swarm/unlock_test.go index bf339faa..6663df1d 100644 --- a/cli/command/swarm/unlock_test.go +++ b/cli/command/swarm/unlock_test.go @@ -25,7 +25,7 @@ func TestSwarmUnlockErrors(t *testing.T) { { name: "too-many-args", args: []string{"foo"}, - expectedError: "accepts no argument(s)", + expectedError: "accepts no arguments", }, { name: "is-not-part-of-a-swarm", diff --git a/cli/command/swarm/update_test.go b/cli/command/swarm/update_test.go index 2b8c4a8a..b3cb02f2 100644 --- a/cli/command/swarm/update_test.go +++ b/cli/command/swarm/update_test.go @@ -30,7 +30,7 @@ func TestSwarmUpdateErrors(t *testing.T) { { name: "too-many-args", args: []string{"foo"}, - expectedError: "accepts no argument(s)", + expectedError: "accepts no arguments", }, { name: "swarm-inspect-error", diff --git a/cli/command/volume/create_test.go b/cli/command/volume/create_test.go index 05b17ecc..e458a26c 100644 --- a/cli/command/volume/create_test.go +++ b/cli/command/volume/create_test.go @@ -31,7 +31,7 @@ func TestVolumeCreateErrors(t *testing.T) { }, { args: []string{"too", "many"}, - expectedError: "requires at most 1 argument(s)", + expectedError: "requires at most 1 argument", }, { volumeCreateFunc: func(createBody volumetypes.VolumesCreateBody) (types.Volume, error) { diff --git a/cli/required.go b/cli/required.go index d28af86b..33a46735 100644 --- a/cli/required.go +++ b/cli/required.go @@ -18,7 +18,7 @@ func NoArgs(cmd *cobra.Command, args []string) error { } return errors.Errorf( - "\"%s\" accepts no argument(s).\nSee '%s --help'.\n\nUsage: %s\n\n%s", + "%q accepts no arguments.\nSee '%s --help'.\n\nUsage: %s\n\n%s", cmd.CommandPath(), cmd.CommandPath(), cmd.UseLine(), @@ -33,9 +33,10 @@ func RequiresMinArgs(min int) cobra.PositionalArgs { return nil } return errors.Errorf( - "\"%s\" requires at least %d argument(s).\nSee '%s --help'.\n\nUsage: %s\n\n%s", + "%q requires at least %d %s.\nSee '%s --help'.\n\nUsage: %s\n\n%s", cmd.CommandPath(), min, + pluralize("argument", min), cmd.CommandPath(), cmd.UseLine(), cmd.Short, @@ -50,9 +51,10 @@ func RequiresMaxArgs(max int) cobra.PositionalArgs { return nil } return errors.Errorf( - "\"%s\" requires at most %d argument(s).\nSee '%s --help'.\n\nUsage: %s\n\n%s", + "%q requires at most %d %s.\nSee '%s --help'.\n\nUsage: %s\n\n%s", cmd.CommandPath(), max, + pluralize("argument", max), cmd.CommandPath(), cmd.UseLine(), cmd.Short, @@ -67,10 +69,11 @@ func RequiresRangeArgs(min int, max int) cobra.PositionalArgs { return nil } return errors.Errorf( - "\"%s\" requires at least %d and at most %d argument(s).\nSee '%s --help'.\n\nUsage: %s\n\n%s", + "%q requires at least %d and at most %d %s.\nSee '%s --help'.\n\nUsage: %s\n\n%s", cmd.CommandPath(), min, max, + pluralize("argument", max), cmd.CommandPath(), cmd.UseLine(), cmd.Short, @@ -85,12 +88,20 @@ func ExactArgs(number int) cobra.PositionalArgs { return nil } return errors.Errorf( - "\"%s\" requires exactly %d argument(s).\nSee '%s --help'.\n\nUsage: %s\n\n%s", + "%q requires exactly %d %s.\nSee '%s --help'.\n\nUsage: %s\n\n%s", cmd.CommandPath(), number, + pluralize("argument", number), cmd.CommandPath(), cmd.UseLine(), cmd.Short, ) } } + +func pluralize(word string, number int) string { + if number == 1 { + return word + } + return word + "s" +} diff --git a/cli/required_test.go b/cli/required_test.go new file mode 100644 index 00000000..b70bafb5 --- /dev/null +++ b/cli/required_test.go @@ -0,0 +1,141 @@ +package cli + +import ( + "errors" + "io/ioutil" + "testing" + + "github.com/spf13/cobra" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestRequiresNoArgs(t *testing.T) { + testCases := []testCase{ + { + validateFunc: NoArgs, + expectedError: "no error", + }, + { + args: []string{"foo"}, + validateFunc: NoArgs, + expectedError: "accepts no arguments.", + }, + } + runTestCases(t, testCases) +} + +func TestRequiresMinArgs(t *testing.T) { + testCases := []testCase{ + { + validateFunc: RequiresMinArgs(0), + expectedError: "no error", + }, + { + validateFunc: RequiresMinArgs(1), + expectedError: "at least 1 argument.", + }, + { + args: []string{"foo"}, + validateFunc: RequiresMinArgs(2), + expectedError: "at least 2 arguments.", + }, + } + runTestCases(t, testCases) +} + +func TestRequiresMaxArgs(t *testing.T) { + testCases := []testCase{ + { + validateFunc: RequiresMaxArgs(0), + expectedError: "no error", + }, + { + args: []string{"foo", "bar"}, + validateFunc: RequiresMaxArgs(1), + expectedError: "at most 1 argument.", + }, + { + args: []string{"foo", "bar", "baz"}, + validateFunc: RequiresMaxArgs(2), + expectedError: "at most 2 arguments.", + }, + } + runTestCases(t, testCases) +} + +func TestRequiresRangeArgs(t *testing.T) { + testCases := []testCase{ + { + validateFunc: RequiresRangeArgs(0, 0), + expectedError: "no error", + }, + { + validateFunc: RequiresRangeArgs(0, 1), + expectedError: "no error", + }, + { + args: []string{"foo", "bar"}, + validateFunc: RequiresRangeArgs(0, 1), + expectedError: "at most 1 argument.", + }, + { + args: []string{"foo", "bar", "baz"}, + validateFunc: RequiresRangeArgs(0, 2), + expectedError: "at most 2 arguments.", + }, + { + validateFunc: RequiresRangeArgs(1, 2), + expectedError: "at least 1 ", + }, + } + runTestCases(t, testCases) +} + +func TestExactArgs(t *testing.T) { + testCases := []testCase{ + { + validateFunc: ExactArgs(0), + expectedError: "no error", + }, + { + validateFunc: ExactArgs(1), + expectedError: "exactly 1 argument.", + }, + { + validateFunc: ExactArgs(2), + expectedError: "exactly 2 arguments.", + }, + } + runTestCases(t, testCases) +} + +type testCase struct { + args []string + validateFunc cobra.PositionalArgs + expectedError string +} + +func runTestCases(t *testing.T, testCases []testCase) { + for _, tc := range testCases { + cmd := newDummyCommand(tc.validateFunc) + cmd.SetArgs(tc.args) + cmd.SetOutput(ioutil.Discard) + + err := cmd.Execute() + + require.Error(t, err, "Expected an error: %s", tc.expectedError) + assert.Contains(t, err.Error(), tc.expectedError) + } +} + +func newDummyCommand(validationFunc cobra.PositionalArgs) *cobra.Command { + cmd := &cobra.Command{ + Use: "dummy", + Args: validationFunc, + RunE: func(cmd *cobra.Command, args []string) error { + return errors.New("no error") + }, + } + return cmd +}