...

Source file src/github.com/spf13/cobra/flag_groups_test.go

Documentation: github.com/spf13/cobra

     1  // Copyright 2013-2023 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  	"strings"
    19  	"testing"
    20  )
    21  
    22  func TestValidateFlagGroups(t *testing.T) {
    23  	getCmd := func() *Command {
    24  		c := &Command{
    25  			Use: "testcmd",
    26  			Run: func(cmd *Command, args []string) {
    27  			}}
    28  		// Define lots of flags to utilize for testing.
    29  		for _, v := range []string{"a", "b", "c", "d"} {
    30  			c.Flags().String(v, "", "")
    31  		}
    32  		for _, v := range []string{"e", "f", "g"} {
    33  			c.PersistentFlags().String(v, "", "")
    34  		}
    35  		subC := &Command{
    36  			Use: "subcmd",
    37  			Run: func(cmd *Command, args []string) {
    38  			}}
    39  		subC.Flags().String("subonly", "", "")
    40  		c.AddCommand(subC)
    41  		return c
    42  	}
    43  
    44  	// Each test case uses a unique command from the function above.
    45  	testcases := []struct {
    46  		desc                        string
    47  		flagGroupsRequired          []string
    48  		flagGroupsOneRequired       []string
    49  		flagGroupsExclusive         []string
    50  		subCmdFlagGroupsRequired    []string
    51  		subCmdFlagGroupsOneRequired []string
    52  		subCmdFlagGroupsExclusive   []string
    53  		args                        []string
    54  		expectErr                   string
    55  	}{
    56  		{
    57  			desc: "No flags no problem",
    58  		}, {
    59  			desc:                "No flags no problem even with conflicting groups",
    60  			flagGroupsRequired:  []string{"a b"},
    61  			flagGroupsExclusive: []string{"a b"},
    62  		}, {
    63  			desc:               "Required flag group not satisfied",
    64  			flagGroupsRequired: []string{"a b c"},
    65  			args:               []string{"--a=foo"},
    66  			expectErr:          "if any flags in the group [a b c] are set they must all be set; missing [b c]",
    67  		}, {
    68  			desc:                  "One-required flag group not satisfied",
    69  			flagGroupsOneRequired: []string{"a b"},
    70  			args:                  []string{"--c=foo"},
    71  			expectErr:             "at least one of the flags in the group [a b] is required",
    72  		}, {
    73  			desc:                "Exclusive flag group not satisfied",
    74  			flagGroupsExclusive: []string{"a b c"},
    75  			args:                []string{"--a=foo", "--b=foo"},
    76  			expectErr:           "if any flags in the group [a b c] are set none of the others can be; [a b] were all set",
    77  		}, {
    78  			desc:               "Multiple required flag group not satisfied returns first error",
    79  			flagGroupsRequired: []string{"a b c", "a d"},
    80  			args:               []string{"--c=foo", "--d=foo"},
    81  			expectErr:          `if any flags in the group [a b c] are set they must all be set; missing [a b]`,
    82  		}, {
    83  			desc:                  "Multiple one-required flag group not satisfied returns first error",
    84  			flagGroupsOneRequired: []string{"a b", "d e"},
    85  			args:                  []string{"--c=foo", "--f=foo"},
    86  			expectErr:             `at least one of the flags in the group [a b] is required`,
    87  		}, {
    88  			desc:                "Multiple exclusive flag group not satisfied returns first error",
    89  			flagGroupsExclusive: []string{"a b c", "a d"},
    90  			args:                []string{"--a=foo", "--c=foo", "--d=foo"},
    91  			expectErr:           `if any flags in the group [a b c] are set none of the others can be; [a c] were all set`,
    92  		}, {
    93  			desc:               "Validation of required groups occurs on groups in sorted order",
    94  			flagGroupsRequired: []string{"a d", "a b", "a c"},
    95  			args:               []string{"--a=foo"},
    96  			expectErr:          `if any flags in the group [a b] are set they must all be set; missing [b]`,
    97  		}, {
    98  			desc:                  "Validation of one-required groups occurs on groups in sorted order",
    99  			flagGroupsOneRequired: []string{"d e", "a b", "f g"},
   100  			args:                  []string{"--c=foo"},
   101  			expectErr:             `at least one of the flags in the group [a b] is required`,
   102  		}, {
   103  			desc:                "Validation of exclusive groups occurs on groups in sorted order",
   104  			flagGroupsExclusive: []string{"a d", "a b", "a c"},
   105  			args:                []string{"--a=foo", "--b=foo", "--c=foo"},
   106  			expectErr:           `if any flags in the group [a b] are set none of the others can be; [a b] were all set`,
   107  		}, {
   108  			desc:                "Persistent flags utilize required and exclusive groups and can fail required groups",
   109  			flagGroupsRequired:  []string{"a e", "e f"},
   110  			flagGroupsExclusive: []string{"f g"},
   111  			args:                []string{"--a=foo", "--f=foo", "--g=foo"},
   112  			expectErr:           `if any flags in the group [a e] are set they must all be set; missing [e]`,
   113  		}, {
   114  			desc:                  "Persistent flags utilize one-required and exclusive groups and can fail one-required groups",
   115  			flagGroupsOneRequired: []string{"a b", "e f"},
   116  			flagGroupsExclusive:   []string{"e f"},
   117  			args:                  []string{"--e=foo"},
   118  			expectErr:             `at least one of the flags in the group [a b] is required`,
   119  		}, {
   120  			desc:                "Persistent flags utilize required and exclusive groups and can fail mutually exclusive groups",
   121  			flagGroupsRequired:  []string{"a e", "e f"},
   122  			flagGroupsExclusive: []string{"f g"},
   123  			args:                []string{"--a=foo", "--e=foo", "--f=foo", "--g=foo"},
   124  			expectErr:           `if any flags in the group [f g] are set none of the others can be; [f g] were all set`,
   125  		}, {
   126  			desc:                "Persistent flags utilize required and exclusive groups and can pass",
   127  			flagGroupsRequired:  []string{"a e", "e f"},
   128  			flagGroupsExclusive: []string{"f g"},
   129  			args:                []string{"--a=foo", "--e=foo", "--f=foo"},
   130  		}, {
   131  			desc:                  "Persistent flags utilize one-required and exclusive groups and can pass",
   132  			flagGroupsOneRequired: []string{"a e", "e f"},
   133  			flagGroupsExclusive:   []string{"f g"},
   134  			args:                  []string{"--a=foo", "--e=foo", "--f=foo"},
   135  		}, {
   136  			desc:                     "Subcmds can use required groups using inherited flags",
   137  			subCmdFlagGroupsRequired: []string{"e subonly"},
   138  			args:                     []string{"subcmd", "--e=foo", "--subonly=foo"},
   139  		}, {
   140  			desc:                        "Subcmds can use one-required groups using inherited flags",
   141  			subCmdFlagGroupsOneRequired: []string{"e subonly"},
   142  			args:                        []string{"subcmd", "--e=foo", "--subonly=foo"},
   143  		}, {
   144  			desc:                        "Subcmds can use one-required groups using inherited flags and fail one-required groups",
   145  			subCmdFlagGroupsOneRequired: []string{"e subonly"},
   146  			args:                        []string{"subcmd"},
   147  			expectErr:                   "at least one of the flags in the group [e subonly] is required",
   148  		}, {
   149  			desc:                      "Subcmds can use exclusive groups using inherited flags",
   150  			subCmdFlagGroupsExclusive: []string{"e subonly"},
   151  			args:                      []string{"subcmd", "--e=foo", "--subonly=foo"},
   152  			expectErr:                 "if any flags in the group [e subonly] are set none of the others can be; [e subonly] were all set",
   153  		}, {
   154  			desc:                      "Subcmds can use exclusive groups using inherited flags and pass",
   155  			subCmdFlagGroupsExclusive: []string{"e subonly"},
   156  			args:                      []string{"subcmd", "--e=foo"},
   157  		}, {
   158  			desc:                     "Flag groups not applied if not found on invoked command",
   159  			subCmdFlagGroupsRequired: []string{"e subonly"},
   160  			args:                     []string{"--e=foo"},
   161  		},
   162  	}
   163  	for _, tc := range testcases {
   164  		t.Run(tc.desc, func(t *testing.T) {
   165  			c := getCmd()
   166  			sub := c.Commands()[0]
   167  			for _, flagGroup := range tc.flagGroupsRequired {
   168  				c.MarkFlagsRequiredTogether(strings.Split(flagGroup, " ")...)
   169  			}
   170  			for _, flagGroup := range tc.flagGroupsOneRequired {
   171  				c.MarkFlagsOneRequired(strings.Split(flagGroup, " ")...)
   172  			}
   173  			for _, flagGroup := range tc.flagGroupsExclusive {
   174  				c.MarkFlagsMutuallyExclusive(strings.Split(flagGroup, " ")...)
   175  			}
   176  			for _, flagGroup := range tc.subCmdFlagGroupsRequired {
   177  				sub.MarkFlagsRequiredTogether(strings.Split(flagGroup, " ")...)
   178  			}
   179  			for _, flagGroup := range tc.subCmdFlagGroupsOneRequired {
   180  				sub.MarkFlagsOneRequired(strings.Split(flagGroup, " ")...)
   181  			}
   182  			for _, flagGroup := range tc.subCmdFlagGroupsExclusive {
   183  				sub.MarkFlagsMutuallyExclusive(strings.Split(flagGroup, " ")...)
   184  			}
   185  			c.SetArgs(tc.args)
   186  			err := c.Execute()
   187  			switch {
   188  			case err == nil && len(tc.expectErr) > 0:
   189  				t.Errorf("Expected error %q but got nil", tc.expectErr)
   190  			case err != nil && err.Error() != tc.expectErr:
   191  				t.Errorf("Expected error %q but got %q", tc.expectErr, err)
   192  			}
   193  		})
   194  	}
   195  }
   196  

View as plain text