1
0

command_test.go 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101
  1. package commands
  2. import (
  3. "bytes"
  4. "flag"
  5. "testing"
  6. "github.com/stretchr/testify/assert"
  7. "github.com/stretchr/testify/require"
  8. )
  9. // TestParseArgsAllowsFlagsAfterOperands pins the one way ParseArgs departs from fl.Parse,
  10. // that a flag may follow an operand, and that it departs in no other: a negative number
  11. // is a flag, as Parse reads it, and goes after "--" when it is meant as an operand.
  12. func TestParseArgsAllowsFlagsAfterOperands(t *testing.T) {
  13. cases := map[string]struct {
  14. args []string
  15. wantOps []string
  16. wantN string
  17. wantV bool
  18. wantJSON bool
  19. wantErr string
  20. }{
  21. "flag after operand": {args: []string{"x", "-json"}, wantOps: []string{"x"}, wantJSON: true},
  22. "flags around": {args: []string{"-v", "x", "-n", "1", "y"}, wantOps: []string{"x", "y"}, wantV: true, wantN: "1"},
  23. "value after operand": {args: []string{"x", "-n", "-1"}, wantOps: []string{"x"}, wantN: "-1"},
  24. "equals value": {args: []string{"-n=-1", "x"}, wantOps: []string{"x"}, wantN: "-1"},
  25. "terminator after": {args: []string{"x", "--", "-json", "-1"}, wantOps: []string{"x", "-json", "-1"}},
  26. "terminator": {args: []string{"--", "-json"}, wantOps: []string{"-json"}},
  27. "defined numeric": {args: []string{"-2", "x"}, wantOps: []string{"x"}, wantV: true},
  28. "negative number": {args: []string{"-1"}, wantErr: "flag provided but not defined: -1"},
  29. "bad flag after": {args: []string{"x", "-nope"}, wantErr: "flag provided but not defined: -nope"},
  30. "undefined flag": {args: []string{"-nope", "x"}, wantErr: "flag provided but not defined: -nope"},
  31. "missing value": {args: []string{"-n"}, wantErr: "flag needs an argument: -n"},
  32. }
  33. for name, tc := range cases {
  34. t.Run(name, func(t *testing.T) {
  35. fl := flag.NewFlagSet("t", flag.ContinueOnError)
  36. fl.SetOutput(&bytes.Buffer{})
  37. n := fl.String("n", "", "")
  38. v := fl.Bool("v", false, "")
  39. two := fl.Bool("2", false, "a flag that happens to be a number")
  40. asJSON := fl.Bool(FlagJSON, false, "")
  41. ops, err := ParseArgs(fl, tc.args)
  42. if tc.wantErr != "" {
  43. require.ErrorContains(t, err, tc.wantErr)
  44. return
  45. }
  46. require.NoError(t, err)
  47. assert.Equal(t, tc.wantOps, ops)
  48. assert.Equal(t, tc.wantN, *n)
  49. assert.Equal(t, tc.wantV, *v || *two)
  50. assert.Equal(t, tc.wantJSON, *asJSON)
  51. })
  52. }
  53. }
  54. // TestDispatchFlagAfterOperand: the same through a registered command with no Flags of
  55. // its own, which only has the presentation flags to find.
  56. func TestDispatchFlagAfterOperand(t *testing.T) {
  57. out := &bytes.Buffer{}
  58. require.NoError(t, testRegistry().DispatchArgs([]string{"served", "x", "-json"}, MergedOutput(out)))
  59. assert.Equal(t, "\"read x\"\n", out.String())
  60. }
  61. // TestLookupArg pins the reading of an argument's shape that ParseArgs and nebula ctl
  62. // share.
  63. func TestLookupArg(t *testing.T) {
  64. fl := flag.NewFlagSet("t", flag.ContinueOnError)
  65. fl.String("n", "", "")
  66. fl.Bool("v", false, "")
  67. for _, tc := range []struct {
  68. arg string
  69. name string
  70. hasValue bool
  71. }{
  72. {"-n", "n", false},
  73. {"--n", "n", false},
  74. {"-n=1", "n", true},
  75. {"--n=", "n", true},
  76. {"-v", "v", false},
  77. {"-nope", "", false},
  78. {"-1", "", false},
  79. } {
  80. f, hasValue := LookupArg(fl, tc.arg)
  81. if tc.name == "" {
  82. assert.Nil(t, f, tc.arg)
  83. } else {
  84. require.NotNil(t, f, tc.arg)
  85. assert.Equal(t, tc.name, f.Name, tc.arg)
  86. }
  87. assert.Equal(t, tc.hasValue, hasValue, tc.arg)
  88. }
  89. assert.True(t, IsBoolFlag(fl.Lookup("v")))
  90. assert.False(t, IsBoolFlag(fl.Lookup("n")))
  91. }