diff --git a/cmd/specter/commands/root.go b/cmd/specter/commands/root.go index 346562a..8274ddc 100644 --- a/cmd/specter/commands/root.go +++ b/cmd/specter/commands/root.go @@ -1,7 +1,9 @@ package commands import ( + "encoding/json" "fmt" + "io" "github.com/ghostwright/specter/internal/tui" "github.com/ghostwright/specter/pkg/version" @@ -42,3 +44,14 @@ func init() { func Execute() error { return rootCmd.Execute() } + +func WriteError(w io.Writer, err error) { + if jsonOutput { + _ = json.NewEncoder(w).Encode(map[string]string{ + "error": err.Error(), + }) + return + } + + fmt.Fprintln(w, err) +} diff --git a/cmd/specter/commands/root_test.go b/cmd/specter/commands/root_test.go new file mode 100644 index 0000000..dacc6f1 --- /dev/null +++ b/cmd/specter/commands/root_test.go @@ -0,0 +1,63 @@ +package commands + +import ( + "bytes" + "encoding/json" + "errors" + "testing" + + "github.com/spf13/cobra" +) + +func TestRootCommandErrorOutputJSON(t *testing.T) { + err := executeFailingRootCommand(t, "--json", "fail") + + var buf bytes.Buffer + WriteError(&buf, err) + + var got map[string]string + if err := json.Unmarshal(buf.Bytes(), &got); err != nil { + t.Fatalf("error output is not valid JSON: %v", err) + } + if got["error"] != "boom" { + t.Fatalf("error = %q, want %q", got["error"], "boom") + } +} + +func TestRootCommandErrorOutputPlainText(t *testing.T) { + err := executeFailingRootCommand(t, "fail") + + var buf bytes.Buffer + WriteError(&buf, err) + + if got, want := buf.String(), "boom\n"; got != want { + t.Fatalf("error output = %q, want %q", got, want) + } +} + +func executeFailingRootCommand(t *testing.T, args ...string) error { + t.Helper() + + oldJSONOutput := jsonOutput + failCmd := &cobra.Command{ + Use: "fail", + RunE: func(cmd *cobra.Command, args []string) error { + return errors.New("boom") + }, + } + + rootCmd.AddCommand(failCmd) + rootCmd.SetArgs(args) + t.Cleanup(func() { + rootCmd.RemoveCommand(failCmd) + rootCmd.SetArgs(nil) + jsonOutput = oldJSONOutput + }) + + err := Execute() + if err == nil { + t.Fatal("Execute() error = nil, want error") + } + + return err +} diff --git a/cmd/specter/main.go b/cmd/specter/main.go index 2766185..24b524b 100644 --- a/cmd/specter/main.go +++ b/cmd/specter/main.go @@ -15,7 +15,7 @@ func main() { runDashboard() } else { if err := commands.Execute(); err != nil { - fmt.Fprintln(os.Stderr, err) + commands.WriteError(os.Stderr, err) os.Exit(1) } }