flag_groups_test.go 8.7 KB

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