git.sr.ht/~pingoo/stdx@v0.0.0-20240218134121-094174641f6e/cobra/args_test.go (about)

     1  // Copyright 2013-2022 The Cobra Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package cobra
    16  
    17  import (
    18  	"fmt"
    19  	"strings"
    20  	"testing"
    21  )
    22  
    23  func getCommand(args PositionalArgs, withValid bool) *Command {
    24  	c := &Command{
    25  		Use:  "c",
    26  		Args: args,
    27  		Run:  emptyRun,
    28  	}
    29  	if withValid {
    30  		c.ValidArgs = []string{"one", "two", "three"}
    31  	}
    32  	return c
    33  }
    34  
    35  func expectSuccess(output string, err error, t *testing.T) {
    36  	if output != "" {
    37  		t.Errorf("Unexpected output: %v", output)
    38  	}
    39  	if err != nil {
    40  		t.Fatalf("Unexpected error: %v", err)
    41  	}
    42  }
    43  
    44  func validOnlyWithInvalidArgs(err error, t *testing.T) {
    45  	if err == nil {
    46  		t.Fatal("Expected an error")
    47  	}
    48  	got := err.Error()
    49  	expected := `invalid argument "a" for "c"`
    50  	if got != expected {
    51  		t.Errorf("Expected: %q, got: %q", expected, got)
    52  	}
    53  }
    54  
    55  func noArgsWithArgs(err error, t *testing.T, arg string) {
    56  	if err == nil {
    57  		t.Fatal("Expected an error")
    58  	}
    59  	got := err.Error()
    60  	expected := `unknown command "` + arg + `" for "c"`
    61  	if got != expected {
    62  		t.Errorf("Expected: %q, got: %q", expected, got)
    63  	}
    64  }
    65  
    66  func minimumNArgsWithLessArgs(err error, t *testing.T) {
    67  	if err == nil {
    68  		t.Fatal("Expected an error")
    69  	}
    70  	got := err.Error()
    71  	expected := "requires at least 2 arg(s), only received 1"
    72  	if got != expected {
    73  		t.Fatalf("Expected %q, got %q", expected, got)
    74  	}
    75  }
    76  
    77  func maximumNArgsWithMoreArgs(err error, t *testing.T) {
    78  	if err == nil {
    79  		t.Fatal("Expected an error")
    80  	}
    81  	got := err.Error()
    82  	expected := "accepts at most 2 arg(s), received 3"
    83  	if got != expected {
    84  		t.Fatalf("Expected %q, got %q", expected, got)
    85  	}
    86  }
    87  
    88  func exactArgsWithInvalidCount(err error, t *testing.T) {
    89  	if err == nil {
    90  		t.Fatal("Expected an error")
    91  	}
    92  	got := err.Error()
    93  	expected := "accepts 2 arg(s), received 3"
    94  	if got != expected {
    95  		t.Fatalf("Expected %q, got %q", expected, got)
    96  	}
    97  }
    98  
    99  func rangeArgsWithInvalidCount(err error, t *testing.T) {
   100  	if err == nil {
   101  		t.Fatal("Expected an error")
   102  	}
   103  	got := err.Error()
   104  	expected := "accepts between 2 and 4 arg(s), received 1"
   105  	if got != expected {
   106  		t.Fatalf("Expected %q, got %q", expected, got)
   107  	}
   108  }
   109  
   110  // NoArgs
   111  
   112  func TestNoArgs(t *testing.T) {
   113  	c := getCommand(NoArgs, false)
   114  	output, err := executeCommand(c)
   115  	expectSuccess(output, err, t)
   116  }
   117  
   118  func TestNoArgs_WithArgs(t *testing.T) {
   119  	c := getCommand(NoArgs, false)
   120  	_, err := executeCommand(c, "one")
   121  	noArgsWithArgs(err, t, "one")
   122  }
   123  
   124  func TestNoArgs_WithValid_WithArgs(t *testing.T) {
   125  	c := getCommand(NoArgs, true)
   126  	_, err := executeCommand(c, "one")
   127  	noArgsWithArgs(err, t, "one")
   128  }
   129  
   130  func TestNoArgs_WithValid_WithInvalidArgs(t *testing.T) {
   131  	c := getCommand(NoArgs, true)
   132  	_, err := executeCommand(c, "a")
   133  	noArgsWithArgs(err, t, "a")
   134  }
   135  
   136  func TestNoArgs_WithValidOnly_WithInvalidArgs(t *testing.T) {
   137  	c := getCommand(MatchAll(OnlyValidArgs, NoArgs), true)
   138  	_, err := executeCommand(c, "a")
   139  	validOnlyWithInvalidArgs(err, t)
   140  }
   141  
   142  // OnlyValidArgs
   143  
   144  func TestOnlyValidArgs(t *testing.T) {
   145  	c := getCommand(OnlyValidArgs, true)
   146  	output, err := executeCommand(c, "one", "two")
   147  	expectSuccess(output, err, t)
   148  }
   149  
   150  func TestOnlyValidArgs_WithInvalidArgs(t *testing.T) {
   151  	c := getCommand(OnlyValidArgs, true)
   152  	_, err := executeCommand(c, "a")
   153  	validOnlyWithInvalidArgs(err, t)
   154  }
   155  
   156  // ArbitraryArgs
   157  
   158  func TestArbitraryArgs(t *testing.T) {
   159  	c := getCommand(ArbitraryArgs, false)
   160  	output, err := executeCommand(c, "a", "b")
   161  	expectSuccess(output, err, t)
   162  }
   163  
   164  func TestArbitraryArgs_WithValid(t *testing.T) {
   165  	c := getCommand(ArbitraryArgs, true)
   166  	output, err := executeCommand(c, "one", "two")
   167  	expectSuccess(output, err, t)
   168  }
   169  
   170  func TestArbitraryArgs_WithValid_WithInvalidArgs(t *testing.T) {
   171  	c := getCommand(ArbitraryArgs, true)
   172  	output, err := executeCommand(c, "a")
   173  	expectSuccess(output, err, t)
   174  }
   175  
   176  func TestArbitraryArgs_WithValidOnly_WithInvalidArgs(t *testing.T) {
   177  	c := getCommand(MatchAll(OnlyValidArgs, ArbitraryArgs), true)
   178  	_, err := executeCommand(c, "a")
   179  	validOnlyWithInvalidArgs(err, t)
   180  }
   181  
   182  // MinimumNArgs
   183  
   184  func TestMinimumNArgs(t *testing.T) {
   185  	c := getCommand(MinimumNArgs(2), false)
   186  	output, err := executeCommand(c, "a", "b", "c")
   187  	expectSuccess(output, err, t)
   188  }
   189  
   190  func TestMinimumNArgs_WithValid(t *testing.T) {
   191  	c := getCommand(MinimumNArgs(2), true)
   192  	output, err := executeCommand(c, "one", "three")
   193  	expectSuccess(output, err, t)
   194  }
   195  
   196  func TestMinimumNArgs_WithValid__WithInvalidArgs(t *testing.T) {
   197  	c := getCommand(MinimumNArgs(2), true)
   198  	output, err := executeCommand(c, "a", "b")
   199  	expectSuccess(output, err, t)
   200  }
   201  
   202  func TestMinimumNArgs_WithValidOnly_WithInvalidArgs(t *testing.T) {
   203  	c := getCommand(MatchAll(OnlyValidArgs, MinimumNArgs(2)), true)
   204  	_, err := executeCommand(c, "a", "b")
   205  	validOnlyWithInvalidArgs(err, t)
   206  }
   207  
   208  func TestMinimumNArgs_WithLessArgs(t *testing.T) {
   209  	c := getCommand(MinimumNArgs(2), false)
   210  	_, err := executeCommand(c, "a")
   211  	minimumNArgsWithLessArgs(err, t)
   212  }
   213  
   214  func TestMinimumNArgs_WithLessArgs_WithValid(t *testing.T) {
   215  	c := getCommand(MinimumNArgs(2), true)
   216  	_, err := executeCommand(c, "one")
   217  	minimumNArgsWithLessArgs(err, t)
   218  }
   219  
   220  func TestMinimumNArgs_WithLessArgs_WithValid_WithInvalidArgs(t *testing.T) {
   221  	c := getCommand(MinimumNArgs(2), true)
   222  	_, err := executeCommand(c, "a")
   223  	minimumNArgsWithLessArgs(err, t)
   224  }
   225  
   226  func TestMinimumNArgs_WithLessArgs_WithValidOnly_WithInvalidArgs(t *testing.T) {
   227  	c := getCommand(MatchAll(OnlyValidArgs, MinimumNArgs(2)), true)
   228  	_, err := executeCommand(c, "a")
   229  	validOnlyWithInvalidArgs(err, t)
   230  }
   231  
   232  // MaximumNArgs
   233  
   234  func TestMaximumNArgs(t *testing.T) {
   235  	c := getCommand(MaximumNArgs(3), false)
   236  	output, err := executeCommand(c, "a", "b")
   237  	expectSuccess(output, err, t)
   238  }
   239  
   240  func TestMaximumNArgs_WithValid(t *testing.T) {
   241  	c := getCommand(MaximumNArgs(2), true)
   242  	output, err := executeCommand(c, "one", "three")
   243  	expectSuccess(output, err, t)
   244  }
   245  
   246  func TestMaximumNArgs_WithValid_WithInvalidArgs(t *testing.T) {
   247  	c := getCommand(MaximumNArgs(2), true)
   248  	output, err := executeCommand(c, "a", "b")
   249  	expectSuccess(output, err, t)
   250  }
   251  
   252  func TestMaximumNArgs_WithValidOnly_WithInvalidArgs(t *testing.T) {
   253  	c := getCommand(MatchAll(OnlyValidArgs, MaximumNArgs(2)), true)
   254  	_, err := executeCommand(c, "a", "b")
   255  	validOnlyWithInvalidArgs(err, t)
   256  }
   257  
   258  func TestMaximumNArgs_WithMoreArgs(t *testing.T) {
   259  	c := getCommand(MaximumNArgs(2), false)
   260  	_, err := executeCommand(c, "a", "b", "c")
   261  	maximumNArgsWithMoreArgs(err, t)
   262  }
   263  
   264  func TestMaximumNArgs_WithMoreArgs_WithValid(t *testing.T) {
   265  	c := getCommand(MaximumNArgs(2), true)
   266  	_, err := executeCommand(c, "one", "three", "two")
   267  	maximumNArgsWithMoreArgs(err, t)
   268  }
   269  
   270  func TestMaximumNArgs_WithMoreArgs_WithValid_WithInvalidArgs(t *testing.T) {
   271  	c := getCommand(MaximumNArgs(2), true)
   272  	_, err := executeCommand(c, "a", "b", "c")
   273  	maximumNArgsWithMoreArgs(err, t)
   274  }
   275  
   276  func TestMaximumNArgs_WithMoreArgs_WithValidOnly_WithInvalidArgs(t *testing.T) {
   277  	c := getCommand(MatchAll(OnlyValidArgs, MaximumNArgs(2)), true)
   278  	_, err := executeCommand(c, "a", "b", "c")
   279  	validOnlyWithInvalidArgs(err, t)
   280  }
   281  
   282  // ExactArgs
   283  
   284  func TestExactArgs(t *testing.T) {
   285  	c := getCommand(ExactArgs(3), false)
   286  	output, err := executeCommand(c, "a", "b", "c")
   287  	expectSuccess(output, err, t)
   288  }
   289  
   290  func TestExactArgs_WithValid(t *testing.T) {
   291  	c := getCommand(ExactArgs(3), true)
   292  	output, err := executeCommand(c, "three", "one", "two")
   293  	expectSuccess(output, err, t)
   294  }
   295  
   296  func TestExactArgs_WithValid_WithInvalidArgs(t *testing.T) {
   297  	c := getCommand(ExactArgs(3), true)
   298  	output, err := executeCommand(c, "three", "a", "two")
   299  	expectSuccess(output, err, t)
   300  }
   301  
   302  func TestExactArgs_WithValidOnly_WithInvalidArgs(t *testing.T) {
   303  	c := getCommand(MatchAll(OnlyValidArgs, ExactArgs(3)), true)
   304  	_, err := executeCommand(c, "three", "a", "two")
   305  	validOnlyWithInvalidArgs(err, t)
   306  }
   307  
   308  func TestExactArgs_WithInvalidCount(t *testing.T) {
   309  	c := getCommand(ExactArgs(2), false)
   310  	_, err := executeCommand(c, "a", "b", "c")
   311  	exactArgsWithInvalidCount(err, t)
   312  }
   313  
   314  func TestExactArgs_WithInvalidCount_WithValid(t *testing.T) {
   315  	c := getCommand(ExactArgs(2), true)
   316  	_, err := executeCommand(c, "three", "one", "two")
   317  	exactArgsWithInvalidCount(err, t)
   318  }
   319  
   320  func TestExactArgs_WithInvalidCount_WithValid_WithInvalidArgs(t *testing.T) {
   321  	c := getCommand(ExactArgs(2), true)
   322  	_, err := executeCommand(c, "three", "a", "two")
   323  	exactArgsWithInvalidCount(err, t)
   324  }
   325  
   326  func TestExactArgs_WithInvalidCount_WithValidOnly_WithInvalidArgs(t *testing.T) {
   327  	c := getCommand(MatchAll(OnlyValidArgs, ExactArgs(2)), true)
   328  	_, err := executeCommand(c, "three", "a", "two")
   329  	validOnlyWithInvalidArgs(err, t)
   330  }
   331  
   332  // RangeArgs
   333  
   334  func TestRangeArgs(t *testing.T) {
   335  	c := getCommand(RangeArgs(2, 4), false)
   336  	output, err := executeCommand(c, "a", "b", "c")
   337  	expectSuccess(output, err, t)
   338  }
   339  
   340  func TestRangeArgs_WithValid(t *testing.T) {
   341  	c := getCommand(RangeArgs(2, 4), true)
   342  	output, err := executeCommand(c, "three", "one", "two")
   343  	expectSuccess(output, err, t)
   344  }
   345  
   346  func TestRangeArgs_WithValid_WithInvalidArgs(t *testing.T) {
   347  	c := getCommand(RangeArgs(2, 4), true)
   348  	output, err := executeCommand(c, "three", "a", "two")
   349  	expectSuccess(output, err, t)
   350  }
   351  
   352  func TestRangeArgs_WithValidOnly_WithInvalidArgs(t *testing.T) {
   353  	c := getCommand(MatchAll(OnlyValidArgs, RangeArgs(2, 4)), true)
   354  	_, err := executeCommand(c, "three", "a", "two")
   355  	validOnlyWithInvalidArgs(err, t)
   356  }
   357  
   358  func TestRangeArgs_WithInvalidCount(t *testing.T) {
   359  	c := getCommand(RangeArgs(2, 4), false)
   360  	_, err := executeCommand(c, "a")
   361  	rangeArgsWithInvalidCount(err, t)
   362  }
   363  
   364  func TestRangeArgs_WithInvalidCount_WithValid(t *testing.T) {
   365  	c := getCommand(RangeArgs(2, 4), true)
   366  	_, err := executeCommand(c, "two")
   367  	rangeArgsWithInvalidCount(err, t)
   368  }
   369  
   370  func TestRangeArgs_WithInvalidCount_WithValid_WithInvalidArgs(t *testing.T) {
   371  	c := getCommand(RangeArgs(2, 4), true)
   372  	_, err := executeCommand(c, "a")
   373  	rangeArgsWithInvalidCount(err, t)
   374  }
   375  
   376  func TestRangeArgs_WithInvalidCount_WithValidOnly_WithInvalidArgs(t *testing.T) {
   377  	c := getCommand(MatchAll(OnlyValidArgs, RangeArgs(2, 4)), true)
   378  	_, err := executeCommand(c, "a")
   379  	validOnlyWithInvalidArgs(err, t)
   380  }
   381  
   382  // Takes(No)Args
   383  
   384  func TestRootTakesNoArgs(t *testing.T) {
   385  	rootCmd := &Command{Use: "root", Run: emptyRun}
   386  	childCmd := &Command{Use: "child", Run: emptyRun}
   387  	rootCmd.AddCommand(childCmd)
   388  
   389  	_, err := executeCommand(rootCmd, "illegal", "args")
   390  	if err == nil {
   391  		t.Fatal("Expected an error")
   392  	}
   393  
   394  	got := err.Error()
   395  	expected := `unknown command "illegal" for "root"`
   396  	if !strings.Contains(got, expected) {
   397  		t.Errorf("expected %q, got %q", expected, got)
   398  	}
   399  }
   400  
   401  func TestRootTakesArgs(t *testing.T) {
   402  	rootCmd := &Command{Use: "root", Args: ArbitraryArgs, Run: emptyRun}
   403  	childCmd := &Command{Use: "child", Run: emptyRun}
   404  	rootCmd.AddCommand(childCmd)
   405  
   406  	_, err := executeCommand(rootCmd, "legal", "args")
   407  	if err != nil {
   408  		t.Errorf("Unexpected error: %v", err)
   409  	}
   410  }
   411  
   412  func TestChildTakesNoArgs(t *testing.T) {
   413  	rootCmd := &Command{Use: "root", Run: emptyRun}
   414  	childCmd := &Command{Use: "child", Args: NoArgs, Run: emptyRun}
   415  	rootCmd.AddCommand(childCmd)
   416  
   417  	_, err := executeCommand(rootCmd, "child", "illegal", "args")
   418  	if err == nil {
   419  		t.Fatal("Expected an error")
   420  	}
   421  
   422  	got := err.Error()
   423  	expected := `unknown command "illegal" for "root child"`
   424  	if !strings.Contains(got, expected) {
   425  		t.Errorf("expected %q, got %q", expected, got)
   426  	}
   427  }
   428  
   429  func TestChildTakesArgs(t *testing.T) {
   430  	rootCmd := &Command{Use: "root", Run: emptyRun}
   431  	childCmd := &Command{Use: "child", Args: ArbitraryArgs, Run: emptyRun}
   432  	rootCmd.AddCommand(childCmd)
   433  
   434  	_, err := executeCommand(rootCmd, "child", "legal", "args")
   435  	if err != nil {
   436  		t.Fatalf("Unexpected error: %v", err)
   437  	}
   438  }
   439  
   440  func TestMatchAll(t *testing.T) {
   441  	// Somewhat contrived example check that ensures there are exactly 3
   442  	// arguments, and each argument is exactly 2 bytes long.
   443  	pargs := MatchAll(
   444  		ExactArgs(3),
   445  		func(cmd *Command, args []string) error {
   446  			for _, arg := range args {
   447  				if len([]byte(arg)) != 2 {
   448  					return fmt.Errorf("expected to be exactly 2 bytes long")
   449  				}
   450  			}
   451  			return nil
   452  		},
   453  	)
   454  
   455  	testCases := map[string]struct {
   456  		args []string
   457  		fail bool
   458  	}{
   459  		"happy path": {
   460  			[]string{"aa", "bb", "cc"},
   461  			false,
   462  		},
   463  		"incorrect number of args": {
   464  			[]string{"aa", "bb", "cc", "dd"},
   465  			true,
   466  		},
   467  		"incorrect number of bytes in one arg": {
   468  			[]string{"aa", "bb", "abc"},
   469  			true,
   470  		},
   471  	}
   472  
   473  	rootCmd := &Command{Use: "root", Args: pargs, Run: emptyRun}
   474  
   475  	for name, tc := range testCases {
   476  		t.Run(name, func(t *testing.T) {
   477  			_, err := executeCommand(rootCmd, tc.args...)
   478  			if err != nil && !tc.fail {
   479  				t.Errorf("unexpected: %v\n", err)
   480  			}
   481  			if err == nil && tc.fail {
   482  				t.Errorf("expected error")
   483  			}
   484  		})
   485  	}
   486  }
   487  
   488  // DEPRECATED
   489  
   490  func TestExactValidArgs(t *testing.T) {
   491  	c := getCommand(ExactValidArgs(3), true)
   492  	output, err := executeCommand(c, "three", "one", "two")
   493  	expectSuccess(output, err, t)
   494  }
   495  
   496  func TestExactValidArgs_WithInvalidCount(t *testing.T) {
   497  	c := getCommand(ExactValidArgs(2), false)
   498  	_, err := executeCommand(c, "three", "one", "two")
   499  	exactArgsWithInvalidCount(err, t)
   500  }
   501  
   502  func TestExactValidArgs_WithInvalidCount_WithInvalidArgs(t *testing.T) {
   503  	c := getCommand(ExactValidArgs(2), true)
   504  	_, err := executeCommand(c, "three", "a", "two")
   505  	exactArgsWithInvalidCount(err, t)
   506  }
   507  
   508  func TestExactValidArgs_WithInvalidArgs(t *testing.T) {
   509  	c := getCommand(ExactValidArgs(2), true)
   510  	_, err := executeCommand(c, "three", "a")
   511  	validOnlyWithInvalidArgs(err, t)
   512  }
   513  
   514  // This test make sure we keep backwards-compatibility with respect
   515  // to the legacyArgs() function.
   516  // It makes sure the root command accepts arguments if it does not have
   517  // sub-commands.
   518  func TestLegacyArgsRootAcceptsArgs(t *testing.T) {
   519  	rootCmd := &Command{Use: "root", Args: nil, Run: emptyRun}
   520  
   521  	_, err := executeCommand(rootCmd, "somearg")
   522  	if err != nil {
   523  		t.Fatalf("Unexpected error: %v", err)
   524  	}
   525  }
   526  
   527  // This test make sure we keep backwards-compatibility with respect
   528  // to the legacyArgs() function.
   529  // It makes sure a sub-command accepts arguments and further sub-commands
   530  func TestLegacyArgsSubcmdAcceptsArgs(t *testing.T) {
   531  	rootCmd := &Command{Use: "root", Args: nil, Run: emptyRun}
   532  	childCmd := &Command{Use: "child", Args: nil, Run: emptyRun}
   533  	grandchildCmd := &Command{Use: "grandchild", Args: nil, Run: emptyRun}
   534  	rootCmd.AddCommand(childCmd)
   535  	childCmd.AddCommand(grandchildCmd)
   536  
   537  	_, err := executeCommand(rootCmd, "child", "somearg")
   538  	if err != nil {
   539  		t.Fatalf("Unexpected error: %v", err)
   540  	}
   541  }