From dc6fa6fb53abb3bb424d5b356aad0e45ecbf2682 Mon Sep 17 00:00:00 2001 From: Victor Vrantchan Date: Fri, 10 Jul 2020 08:31:29 -0400 Subject: [PATCH] Added command test. - cmd/micromdm now has a simple test. - changed flagset defaults to support testing and remove default side effects. Go flags write to os.Stderr and call os.Exit(2) when parsing usage. --- cmd/micromdm/micromdm.go | 22 +++++++++-- cmd/micromdm/micromdm_test.go | 72 +++++++++++++++++++++++++++++++++++ 2 files changed, 91 insertions(+), 3 deletions(-) create mode 100644 cmd/micromdm/micromdm_test.go diff --git a/cmd/micromdm/micromdm.go b/cmd/micromdm/micromdm.go index e9d51eb3..351160ca 100644 --- a/cmd/micromdm/micromdm.go +++ b/cmd/micromdm/micromdm.go @@ -33,11 +33,15 @@ func micromdm(args []string, stdin io.Reader, stdout, stderr io.Writer) int { ctx = context.Background() logger = log.New() ffOptions = []ff.Option{ff.WithConfigFileParser(ff.PlainParser), ff.WithConfigFileFlag("config")} - rootfs = flag.NewFlagSet("micromdm", flag.ExitOnError) + rootfs = flag.NewFlagSet("micromdm", flag.ContinueOnError) pidfile = rootfs.String("pidfile", "/tmp/micromdm.pid", "Path to server pidfile") _ = rootfs.String("config", "", "Path to config file (optional)") ) + // default output is os.Stderr. + // setting the output and flag.ContinueOnError overrides allows testing usage. + rootfs.SetOutput(stderr) + version := &ffcli.Command{ Name: "version", ShortUsage: "version [ ...]", @@ -48,11 +52,21 @@ func micromdm(args []string, stdin io.Reader, stdout, stderr io.Writer) int { }, } + // add a help subcommand to make usage more discoverable. + helpCmd := &ffcli.Command{ + Name: "help", + UsageFunc: func(c *ffcli.Command) string { return "" }, + Exec: func(_ context.Context, args []string) error { + rootfs.Usage() + return flag.ErrHelp + }, + } + root := &ffcli.Command{ ShortUsage: "micromdm [flags] ", FlagSet: rootfs, Options: ffOptions, - Subcommands: []*ffcli.Command{version}, + Subcommands: []*ffcli.Command{helpCmd, version}, Exec: func(context.Context, []string) error { if err := writePID(*pidfile); err != nil { return err @@ -107,8 +121,10 @@ func micromdm(args []string, stdin io.Reader, stdout, stderr io.Writer) int { } switch err := root.ParseAndRun(ctx, args[1:]); { - case err == nil, errors.Is(err, flag.ErrHelp): + case err == nil: return 0 + case errors.Is(err, flag.ErrHelp): + return 2 default: fmt.Fprintln(stderr) // when Ctrl+C is used, avoid messing up the logger line log.Info(logger).Log("exit", err) diff --git a/cmd/micromdm/micromdm_test.go b/cmd/micromdm/micromdm_test.go new file mode 100644 index 00000000..bd24bcb6 --- /dev/null +++ b/cmd/micromdm/micromdm_test.go @@ -0,0 +1,72 @@ +package main + +import ( + "bytes" + "strings" + "testing" +) + +type mainIOFunc func(t *testing.T, stdin, stdout, stderr *bytes.Buffer) + +func mainUsage(t *testing.T, stdin, stdout, stderr *bytes.Buffer) { + output := stderr.String() + words := []string{"USAGE", "pidfile"} + for _, word := range words { + if !strings.Contains(output, word) { + t.Errorf("expected %q in output, got:\n%s", word, output) + } + } +} + +func TestMain(t *testing.T) { + tests := []struct { + name string + stdin *bytes.Buffer + stdout *bytes.Buffer + stderr *bytes.Buffer + args []string + exit int + check mainIOFunc + }{ + { + name: "short usage", + stdin: new(bytes.Buffer), + stdout: new(bytes.Buffer), + stderr: new(bytes.Buffer), + args: []string{"micromdm", "-h"}, + exit: 2, + check: mainUsage, + }, + { + name: "long usage", + stdin: new(bytes.Buffer), + stdout: new(bytes.Buffer), + stderr: new(bytes.Buffer), + args: []string{"micromdm", "-help"}, + exit: 2, + check: mainUsage, + }, + { + name: "subcommand help usage", + stdin: new(bytes.Buffer), + stdout: new(bytes.Buffer), + stderr: new(bytes.Buffer), + args: []string{"micromdm", "help"}, + exit: 2, + check: mainUsage, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + if got, want := micromdm(tt.args, tt.stdin, tt.stdout, tt.stderr), tt.exit; got != want { + t.Fatalf("exit code: got %d, want %d", got, want) + } + + tt.check(t, tt.stdin, tt.stdout, tt.stderr) + }) + } +}