1package cobra 2 3import ( 4 "bytes" 5 "fmt" 6 "os" 7 "reflect" 8 "strings" 9 "testing" 10 11 "github.com/spf13/pflag" 12) 13 14// test to ensure hidden commands run as intended 15func TestHiddenCommandExecutes(t *testing.T) { 16 17 // ensure that outs does not already equal what the command will be setting it 18 // to, if it did this test would not actually be testing anything... 19 if outs == "hidden" { 20 t.Errorf("outs should NOT EQUAL hidden") 21 } 22 23 cmdHidden.Execute() 24 25 // upon running the command, the value of outs should now be 'hidden' 26 if outs != "hidden" { 27 t.Errorf("Hidden command failed to run!") 28 } 29} 30 31// test to ensure hidden commands do not show up in usage/help text 32func TestHiddenCommandIsHidden(t *testing.T) { 33 if cmdHidden.IsAvailableCommand() { 34 t.Errorf("Hidden command found!") 35 } 36} 37 38func TestStripFlags(t *testing.T) { 39 tests := []struct { 40 input []string 41 output []string 42 }{ 43 { 44 []string{"foo", "bar"}, 45 []string{"foo", "bar"}, 46 }, 47 { 48 []string{"foo", "--bar", "-b"}, 49 []string{"foo"}, 50 }, 51 { 52 []string{"-b", "foo", "--bar", "bar"}, 53 []string{}, 54 }, 55 { 56 []string{"-i10", "echo"}, 57 []string{"echo"}, 58 }, 59 { 60 []string{"-i=10", "echo"}, 61 []string{"echo"}, 62 }, 63 { 64 []string{"--int=100", "echo"}, 65 []string{"echo"}, 66 }, 67 { 68 []string{"-ib", "echo", "-bfoo", "baz"}, 69 []string{"echo", "baz"}, 70 }, 71 { 72 []string{"-i=baz", "bar", "-i", "foo", "blah"}, 73 []string{"bar", "blah"}, 74 }, 75 { 76 []string{"--int=baz", "-bbar", "-i", "foo", "blah"}, 77 []string{"blah"}, 78 }, 79 { 80 []string{"--cat", "bar", "-i", "foo", "blah"}, 81 []string{"bar", "blah"}, 82 }, 83 { 84 []string{"-c", "bar", "-i", "foo", "blah"}, 85 []string{"bar", "blah"}, 86 }, 87 { 88 []string{"--persist", "bar"}, 89 []string{"bar"}, 90 }, 91 { 92 []string{"-p", "bar"}, 93 []string{"bar"}, 94 }, 95 } 96 97 cmdPrint := &Command{ 98 Use: "print [string to print]", 99 Short: "Print anything to the screen", 100 Long: `an utterly useless command for testing.`, 101 Run: func(cmd *Command, args []string) { 102 tp = args 103 }, 104 } 105 106 var flagi int 107 var flagstr string 108 var flagbool bool 109 cmdPrint.PersistentFlags().BoolVarP(&flagbool, "persist", "p", false, "help for persistent one") 110 cmdPrint.Flags().IntVarP(&flagi, "int", "i", 345, "help message for flag int") 111 cmdPrint.Flags().StringVarP(&flagstr, "bar", "b", "bar", "help message for flag string") 112 cmdPrint.Flags().BoolVarP(&flagbool, "cat", "c", false, "help message for flag bool") 113 114 for _, test := range tests { 115 output := stripFlags(test.input, cmdPrint) 116 if !reflect.DeepEqual(test.output, output) { 117 t.Errorf("expected: %v, got: %v", test.output, output) 118 } 119 } 120} 121 122func TestDisableFlagParsing(t *testing.T) { 123 targs := []string{} 124 cmdPrint := &Command{ 125 DisableFlagParsing: true, 126 Run: func(cmd *Command, args []string) { 127 targs = args 128 }, 129 } 130 args := []string{"cmd", "-v", "-race", "-file", "foo.go"} 131 cmdPrint.SetArgs(args) 132 err := cmdPrint.Execute() 133 if err != nil { 134 t.Error(err) 135 } 136 if !reflect.DeepEqual(args, targs) { 137 t.Errorf("expected: %v, got: %v", args, targs) 138 } 139} 140 141func TestInitHelpFlagMergesFlags(t *testing.T) { 142 usage := "custom flag" 143 baseCmd := Command{Use: "testcmd"} 144 baseCmd.PersistentFlags().Bool("help", false, usage) 145 cmd := Command{Use: "do"} 146 baseCmd.AddCommand(&cmd) 147 148 cmd.InitDefaultHelpFlag() 149 actual := cmd.Flags().Lookup("help").Usage 150 if actual != usage { 151 t.Fatalf("Expected the help flag from the base command with usage '%s', but got the default with usage '%s'", usage, actual) 152 } 153} 154 155func TestCommandsAreSorted(t *testing.T) { 156 EnableCommandSorting = true 157 158 originalNames := []string{"middle", "zlast", "afirst"} 159 expectedNames := []string{"afirst", "middle", "zlast"} 160 161 var tmpCommand = &Command{Use: "tmp"} 162 163 for _, name := range originalNames { 164 tmpCommand.AddCommand(&Command{Use: name}) 165 } 166 167 for i, c := range tmpCommand.Commands() { 168 if expectedNames[i] != c.Name() { 169 t.Errorf("expected: %s, got: %s", expectedNames[i], c.Name()) 170 } 171 } 172 173 EnableCommandSorting = true 174} 175 176func TestEnableCommandSortingIsDisabled(t *testing.T) { 177 EnableCommandSorting = false 178 179 originalNames := []string{"middle", "zlast", "afirst"} 180 181 var tmpCommand = &Command{Use: "tmp"} 182 183 for _, name := range originalNames { 184 tmpCommand.AddCommand(&Command{Use: name}) 185 } 186 187 for i, c := range tmpCommand.Commands() { 188 if originalNames[i] != c.Name() { 189 t.Errorf("expected: %s, got: %s", originalNames[i], c.Name()) 190 } 191 } 192 193 EnableCommandSorting = true 194} 195 196func TestSetOutput(t *testing.T) { 197 cmd := &Command{} 198 cmd.SetOutput(nil) 199 if out := cmd.OutOrStdout(); out != os.Stdout { 200 t.Fatalf("expected setting output to nil to revert back to stdout, got %v", out) 201 } 202} 203 204func TestFlagErrorFunc(t *testing.T) { 205 cmd := &Command{ 206 Use: "print", 207 RunE: func(cmd *Command, args []string) error { 208 return nil 209 }, 210 } 211 expectedFmt := "This is expected: %s" 212 213 cmd.SetFlagErrorFunc(func(c *Command, err error) error { 214 return fmt.Errorf(expectedFmt, err) 215 }) 216 cmd.SetArgs([]string{"--bogus-flag"}) 217 cmd.SetOutput(new(bytes.Buffer)) 218 219 err := cmd.Execute() 220 221 expected := fmt.Sprintf(expectedFmt, "unknown flag: --bogus-flag") 222 if err.Error() != expected { 223 t.Errorf("expected %v, got %v", expected, err.Error()) 224 } 225} 226 227// TestSortedFlags checks, 228// if cmd.LocalFlags() is unsorted when cmd.Flags().SortFlags set to false. 229// Related to https://github.com/spf13/cobra/issues/404. 230func TestSortedFlags(t *testing.T) { 231 cmd := &Command{} 232 cmd.Flags().SortFlags = false 233 names := []string{"C", "B", "A", "D"} 234 for _, name := range names { 235 cmd.Flags().Bool(name, false, "") 236 } 237 238 i := 0 239 cmd.LocalFlags().VisitAll(func(f *pflag.Flag) { 240 if i == len(names) { 241 return 242 } 243 if isStringInStringSlice(f.Name, names) { 244 if names[i] != f.Name { 245 t.Errorf("Incorrect order. Expected %v, got %v", names[i], f.Name) 246 } 247 i++ 248 } 249 }) 250} 251 252// contains checks, if s is in ss. 253func isStringInStringSlice(s string, ss []string) bool { 254 for _, v := range ss { 255 if v == s { 256 return true 257 } 258 } 259 return false 260} 261 262// TestHelpFlagInHelp checks, 263// if '--help' flag is shown in help for child (executing `parent help child`), 264// that has no other flags. 265// Related to https://github.com/spf13/cobra/issues/302. 266func TestHelpFlagInHelp(t *testing.T) { 267 output := new(bytes.Buffer) 268 parent := &Command{Use: "parent", Run: func(*Command, []string) {}} 269 parent.SetOutput(output) 270 271 child := &Command{Use: "child", Run: func(*Command, []string) {}} 272 parent.AddCommand(child) 273 274 parent.SetArgs([]string{"help", "child"}) 275 err := parent.Execute() 276 if err != nil { 277 t.Fatal(err) 278 } 279 280 if !strings.Contains(output.String(), "[flags]") { 281 t.Errorf("\nExpecting to contain: %v\nGot: %v", "[flags]", output.String()) 282 } 283} 284 285// TestMergeCommandLineToFlags checks, 286// if pflag.CommandLine is correctly merged to c.Flags() after first call 287// of c.mergePersistentFlags. 288// Related to https://github.com/spf13/cobra/issues/443. 289func TestMergeCommandLineToFlags(t *testing.T) { 290 pflag.Bool("boolflag", false, "") 291 c := &Command{Use: "c", Run: func(*Command, []string) {}} 292 c.mergePersistentFlags() 293 if c.Flags().Lookup("boolflag") == nil { 294 t.Fatal("Expecting to have flag from CommandLine in c.Flags()") 295 } 296 297 // Reset pflag.CommandLine flagset. 298 pflag.CommandLine = pflag.NewFlagSet(os.Args[0], pflag.ExitOnError) 299} 300 301// TestUseDeprecatedFlags checks, 302// if cobra.Execute() prints a message, if a deprecated flag is used. 303// Related to https://github.com/spf13/cobra/issues/463. 304func TestUseDeprecatedFlags(t *testing.T) { 305 c := &Command{Use: "c", Run: func(*Command, []string) {}} 306 output := new(bytes.Buffer) 307 c.SetOutput(output) 308 c.Flags().BoolP("deprecated", "d", false, "deprecated flag") 309 c.Flags().MarkDeprecated("deprecated", "This flag is deprecated") 310 311 c.SetArgs([]string{"c", "-d"}) 312 if err := c.Execute(); err != nil { 313 t.Error("Unexpected error:", err) 314 } 315 if !strings.Contains(output.String(), "This flag is deprecated") { 316 t.Errorf("Expected to contain deprecated message, but got %q", output.String()) 317 } 318} 319 320// TestSetHelpCommand checks, if SetHelpCommand works correctly. 321func TestSetHelpCommand(t *testing.T) { 322 c := &Command{Use: "c", Run: func(*Command, []string) {}} 323 output := new(bytes.Buffer) 324 c.SetOutput(output) 325 c.SetArgs([]string{"help"}) 326 327 // Help will not be shown, if c has no subcommands. 328 c.AddCommand(&Command{ 329 Use: "empty", 330 Run: func(cmd *Command, args []string) {}, 331 }) 332 333 correctMessage := "WORKS" 334 c.SetHelpCommand(&Command{ 335 Use: "help [command]", 336 Short: "Help about any command", 337 Long: `Help provides help for any command in the application. 338 Simply type ` + c.Name() + ` help [path to command] for full details.`, 339 Run: func(c *Command, args []string) { c.Print(correctMessage) }, 340 }) 341 342 if err := c.Execute(); err != nil { 343 t.Error("Unexpected error:", err) 344 } 345 346 if output.String() != correctMessage { 347 t.Errorf("Expected to contain %q message, but got %q", correctMessage, output.String()) 348 } 349} 350 351func TestTraverseWithParentFlags(t *testing.T) { 352 cmd := &Command{ 353 Use: "do", 354 TraverseChildren: true, 355 } 356 cmd.Flags().String("foo", "", "foo things") 357 cmd.Flags().BoolP("goo", "g", false, "foo things") 358 359 sub := &Command{Use: "next"} 360 sub.Flags().String("add", "", "add things") 361 cmd.AddCommand(sub) 362 363 c, args, err := cmd.Traverse([]string{"-g", "--foo", "ok", "next", "--add"}) 364 if err != nil { 365 t.Fatalf("Expected no error: %s", err) 366 } 367 if len(args) != 1 && args[0] != "--add" { 368 t.Fatalf("wrong args %s", args) 369 } 370 if c.Name() != sub.Name() { 371 t.Fatalf("wrong command %q expected %q", c.Name(), sub.Name()) 372 } 373} 374 375func TestTraverseNoParentFlags(t *testing.T) { 376 cmd := &Command{ 377 Use: "do", 378 TraverseChildren: true, 379 } 380 cmd.Flags().String("foo", "", "foo things") 381 382 sub := &Command{Use: "next"} 383 sub.Flags().String("add", "", "add things") 384 cmd.AddCommand(sub) 385 386 c, args, err := cmd.Traverse([]string{"next"}) 387 if err != nil { 388 t.Fatalf("Expected no error: %s", err) 389 } 390 if len(args) != 0 { 391 t.Fatalf("wrong args %s", args) 392 } 393 if c.Name() != sub.Name() { 394 t.Fatalf("wrong command %q expected %q", c.Name(), sub.Name()) 395 } 396} 397 398func TestTraverseWithBadParentFlags(t *testing.T) { 399 cmd := &Command{ 400 Use: "do", 401 TraverseChildren: true, 402 } 403 sub := &Command{Use: "next"} 404 sub.Flags().String("add", "", "add things") 405 cmd.AddCommand(sub) 406 407 expected := "got unknown flag: --add" 408 409 c, _, err := cmd.Traverse([]string{"--add", "ok", "next"}) 410 if err == nil || strings.Contains(err.Error(), expected) { 411 t.Fatalf("Expected error %s got %s", expected, err) 412 } 413 if c != nil { 414 t.Fatalf("Expected nil command") 415 } 416} 417 418func TestTraverseWithBadChildFlag(t *testing.T) { 419 cmd := &Command{ 420 Use: "do", 421 TraverseChildren: true, 422 } 423 cmd.Flags().String("foo", "", "foo things") 424 425 sub := &Command{Use: "next"} 426 cmd.AddCommand(sub) 427 428 // Expect no error because the last commands args shouldn't be parsed in 429 // Traverse 430 c, args, err := cmd.Traverse([]string{"next", "--add"}) 431 if err != nil { 432 t.Fatalf("Expected no error: %s", err) 433 } 434 if len(args) != 1 && args[0] != "--add" { 435 t.Fatalf("wrong args %s", args) 436 } 437 if c.Name() != sub.Name() { 438 t.Fatalf("wrong command %q expected %q", c.Name(), sub.Name()) 439 } 440} 441 442func TestTraverseWithTwoSubcommands(t *testing.T) { 443 cmd := &Command{ 444 Use: "do", 445 TraverseChildren: true, 446 } 447 448 sub := &Command{ 449 Use: "sub", 450 TraverseChildren: true, 451 } 452 cmd.AddCommand(sub) 453 454 subsub := &Command{ 455 Use: "subsub", 456 } 457 sub.AddCommand(subsub) 458 459 c, _, err := cmd.Traverse([]string{"sub", "subsub"}) 460 if err != nil { 461 t.Fatalf("Expected no error: %s", err) 462 } 463 if c.Name() != subsub.Name() { 464 t.Fatalf("wrong command %q expected %q", c.Name(), subsub.Name()) 465 } 466} 467 468func TestRequiredFlags(t *testing.T) { 469 c := &Command{Use: "c", Run: func(*Command, []string) {}} 470 output := new(bytes.Buffer) 471 c.SetOutput(output) 472 c.Flags().String("foo1", "", "required foo1") 473 c.MarkFlagRequired("foo1") 474 c.Flags().String("foo2", "", "required foo2") 475 c.MarkFlagRequired("foo2") 476 c.Flags().String("bar", "", "optional bar") 477 478 expected := fmt.Sprintf("Required flag(s) %q, %q have/has not been set", "foo1", "foo2") 479 480 if err := c.Execute(); err != nil { 481 if err.Error() != expected { 482 t.Errorf("expected %v, got %v", expected, err.Error()) 483 } 484 } 485} 486 487func TestPersistentRequiredFlags(t *testing.T) { 488 parent := &Command{Use: "parent", Run: func(*Command, []string) {}} 489 output := new(bytes.Buffer) 490 parent.SetOutput(output) 491 parent.PersistentFlags().String("foo1", "", "required foo1") 492 parent.MarkPersistentFlagRequired("foo1") 493 parent.PersistentFlags().String("foo2", "", "required foo2") 494 parent.MarkPersistentFlagRequired("foo2") 495 parent.Flags().String("foo3", "", "optional foo3") 496 497 child := &Command{Use: "child", Run: func(*Command, []string) {}} 498 child.Flags().String("bar1", "", "required bar1") 499 child.MarkFlagRequired("bar1") 500 child.Flags().String("bar2", "", "required bar2") 501 child.MarkFlagRequired("bar2") 502 child.Flags().String("bar3", "", "optional bar3") 503 504 parent.AddCommand(child) 505 parent.SetArgs([]string{"child"}) 506 507 expected := fmt.Sprintf("Required flag(s) %q, %q, %q, %q have/has not been set", "bar1", "bar2", "foo1", "foo2") 508 509 if err := parent.Execute(); err != nil { 510 if err.Error() != expected { 511 t.Errorf("expected %v, got %v", expected, err.Error()) 512 } 513 } 514} 515 516// TestUpdateName checks if c.Name() updates on changed c.Use. 517// Related to https://github.com/spf13/cobra/pull/422#discussion_r143918343. 518func TestUpdateName(t *testing.T) { 519 c := &Command{Use: "name xyz"} 520 originalName := c.Name() 521 522 c.Use = "changedName abc" 523 if originalName == c.Name() || c.Name() != "changedName" { 524 t.Error("c.Name() should be updated on changed c.Use") 525 } 526} 527