diff --git a/internal/cli/cli.go b/internal/cli/cli.go index 4460b60..3043bb1 100644 --- a/internal/cli/cli.go +++ b/internal/cli/cli.go @@ -4,8 +4,8 @@ package cli import ( "fmt" - "io" - "os" + + "github.com/spf13/cobra" ) // appname is the name the program reports in its logs and its version. @@ -13,23 +13,22 @@ const appname = "simplexcalc" // Main runs the command named on the command line and returns the exit // status for main to pass to os.Exit. +// +// It leaves cobra's output streams at their defaults. cobra writes the +// usage and an unknown help topic to stderr, but SetOut moves them onto +// the stream it is given. func Main(version string) int { - return execute(version, os.Args[1:], os.Stdout, os.Stderr) + return execute(rootCmd(version)) } -// execute is Main with the arguments and the output passed in, so that -// a test can supply them. -func execute(version string, args []string, stdout, stderr io.Writer) int { - root := rootCmd(version) - root.SetArgs(args) - root.SetOut(stdout) - root.SetErr(stderr) - +// execute runs root and returns the exit status. It takes the command +// so that a test can set its arguments and output streams first. +func execute(root *cobra.Command) int { err := root.Execute() if err != nil { // cobra has already printed the error; this only sets the exit // status, which is what a supervisor and a shell script read. - _, _ = fmt.Fprintln(stderr, "exiting: "+err.Error()) + _, _ = fmt.Fprintln(root.ErrOrStderr(), "exiting: "+err.Error()) return 1 } diff --git a/internal/cli/cli_test.go b/internal/cli/cli_test.go index ad82d34..e27d5bd 100644 --- a/internal/cli/cli_test.go +++ b/internal/cli/cli_test.go @@ -14,7 +14,12 @@ func TestVersionPrintsVersion(t *testing.T) { var stdout, stderr bytes.Buffer - status := cli.Execute("1.2.3", []string{"version"}, &stdout, &stderr) + root := cli.RootCmd("1.2.3") + root.SetArgs([]string{"version"}) + root.SetOut(&stdout) + root.SetErr(&stderr) + + status := cli.Execute(root) if status != 0 { t.Fatalf("status = %d, want 0; stderr: %q", status, stderr.String()) } @@ -30,7 +35,12 @@ func TestUnknownCommandFails(t *testing.T) { var stdout, stderr bytes.Buffer - status := cli.Execute("1.2.3", []string{"frobnicate"}, &stdout, &stderr) + root := cli.RootCmd("1.2.3") + root.SetArgs([]string{"frobnicate"}) + root.SetOut(&stdout) + root.SetErr(&stderr) + + status := cli.Execute(root) if status == 0 { t.Fatal("status = 0, want non-zero") } diff --git a/internal/cli/export_test.go b/internal/cli/export_test.go index fc75527..5d8b0fe 100644 --- a/internal/cli/export_test.go +++ b/internal/cli/export_test.go @@ -1,7 +1,11 @@ package cli -// Execute is execute, exported for the external test package, which -// needs to supply the arguments and read the output. +// RootCmd and Execute are rootCmd and execute, exported for the external +// test package, which builds the command tree itself so that it can +// supply the arguments and read the output. // -//nolint:gochecknoglobals // a test seam, not mutable state. -var Execute = execute +//nolint:gochecknoglobals // test seams, not mutable state. +var ( + RootCmd = rootCmd + Execute = execute +)