request_test.go 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. package commands
  2. import (
  3. "flag"
  4. "strings"
  5. "testing"
  6. "time"
  7. "github.com/stretchr/testify/assert"
  8. "github.com/stretchr/testify/require"
  9. )
  10. func lineFlags() *flag.FlagSet {
  11. fl := flag.NewFlagSet("set", flag.ContinueOnError)
  12. fl.Bool("verbose", false, "")
  13. fl.Duration("wait", 0, "")
  14. fl.String("label", "none", "")
  15. fl.Bool(FlagJSON, false, "")
  16. fl.Bool(FlagPretty, false, "")
  17. return fl
  18. }
  19. // TestParseLineReadsThePresentationFlagsOnce: presentationFormat is the one reading of
  20. // -json and -pretty, for every surface, and ParseLine carries its answer. A flag set to
  21. // false expresses no preference, and neither flag ever travels as a parameter.
  22. func TestParseLineReadsThePresentationFlagsOnce(t *testing.T) {
  23. for _, tc := range []struct {
  24. args []string
  25. want Format
  26. }{
  27. {nil, FormatUnset},
  28. {[]string{"-json=false"}, FormatUnset},
  29. {[]string{"-pretty=false"}, FormatUnset},
  30. {[]string{"-json"}, FormatJSON},
  31. {[]string{"-pretty"}, FormatPrettyJSON},
  32. {[]string{"-pretty", "-json=false"}, FormatPrettyJSON},
  33. } {
  34. fl := lineFlags()
  35. operands, err := ParseArgs(fl, tc.args)
  36. require.NoError(t, err)
  37. req := ParseLine(fl, operands)
  38. assert.Equal(t, tc.want, req.Format, "%v", tc.args)
  39. assert.Nil(t, req.Params, "%v: presentation flags are not parameters", tc.args)
  40. }
  41. }
  42. // TestParseLineCarriesWhatWasSetInParseableForm: only the flags the line set travel,
  43. // each as the string its flag parses from, so the daemon's Invoke can apply them to a
  44. // fresh FlagSet and get the same values back. Operands come through untouched, those
  45. // after "--" included.
  46. func TestParseLineCarriesWhatWasSetInParseableForm(t *testing.T) {
  47. fl := lineFlags()
  48. operands, err := ParseArgs(fl, []string{"-verbose", "-wait", "2s", "x", "--", "-1"})
  49. require.NoError(t, err)
  50. req := ParseLine(fl, operands)
  51. assert.Equal(t, map[string]string{"verbose": "true", "wait": "2s"}, req.Params, "label was left alone and must be absent")
  52. assert.Equal(t, []string{"x", "-1"}, req.Args)
  53. assert.Equal(t, FormatUnset, req.Format)
  54. // The round trip a client makes: what ParseLine produced is what Invoke applies.
  55. r, seen := paramRegistry(t)
  56. req.Command = r.Get("set")
  57. _, err = Invoke(req)
  58. require.NoError(t, err)
  59. require.Len(t, *seen, 1)
  60. assert.Equal(t, paramsSeen{Verbose: true, Wait: 2 * time.Second}, (*seen)[0])
  61. }
  62. // TestResolveTellsUnknownFromWithheld: the two cases read differently to an operator,
  63. // and Resolve is the edge that decides which one a name from the wire is.
  64. func TestResolveTellsUnknownFromWithheld(t *testing.T) {
  65. r := testRegistry().Filter(notWithheld, testReason)
  66. c, err := r.Resolve("served")
  67. require.NoError(t, err)
  68. assert.Equal(t, Name("served"), c.Name)
  69. _, err = r.Resolve("withheld")
  70. require.ErrorContains(t, err, "withheld is not available on this surface")
  71. _, err = r.Resolve("no-such-command")
  72. require.ErrorContains(t, err, `unknown command "no-such-command"`)
  73. }
  74. // TestLineRunsTheFlagSetItParsed: the ssh path hands Run the FlagSet the line was parsed
  75. // into, with no trip through a Request. A flag.Func has no String worth re-parsing, so a
  76. // round trip would hand Run an empty value and no error; the line's value must arrive.
  77. func TestLineRunsTheFlagSetItParsed(t *testing.T) {
  78. var seen []string
  79. r := New()
  80. r.Register(&Command{
  81. Name: "mode",
  82. Flags: func() (*flag.FlagSet, any) {
  83. fl := flag.NewFlagSet("", flag.ContinueOnError)
  84. s := new(string)
  85. fl.Func("mode", "the mode", func(v string) error {
  86. *s = "set:" + v
  87. return nil
  88. })
  89. return fl, s
  90. },
  91. Run: func(fs any, a []string) (Result, error) {
  92. seen = append(seen, *fs.(*string))
  93. return Value("ok"), nil
  94. },
  95. })
  96. var out, errw strings.Builder
  97. r.Dispatch("mode -json -mode fast", Output{Out: &out, Err: &errw})
  98. require.Equal(t, []string{"set:fast"}, seen)
  99. assert.Empty(t, errw.String())
  100. assert.Equal(t, "\"ok\"\n", out.String(), "-json was read from the same FlagSet")
  101. }