diff --git a/cmd/log.go b/cmd/log.go index 52f67b4..aa23c20 100644 --- a/cmd/log.go +++ b/cmd/log.go @@ -100,10 +100,15 @@ func writeLog(sc int, target, method, errorMsg, response string) { switch sc { case 8899: + // -q suppresses the spec metadata, leaving only the results array. + title, description := specTitle, specDescription + if quiet { + title, description = "", "" + } if verbose { - logVerboseJSON(specTitle, specDescription, out) + logVerboseJSON(title, description, out) } else { - logJSON(specTitle, specDescription, out) + logJSON(title, description, out) } default: logResult(sc, target, method, errorMsg, preview, out) @@ -155,8 +160,8 @@ func logResult(sc int, target, method, errorMsg, preview string, out io.Writer) func logJSON(title, description string, out io.Writer) { output := struct { - APITitle string `json:"apiTitle"` - Description string `json:"description"` + APITitle string `json:"apiTitle,omitempty"` + Description string `json:"description,omitempty"` Results []Result `json:"results"` }{ APITitle: title, @@ -169,8 +174,8 @@ func logJSON(title, description string, out io.Writer) { func logVerboseJSON(title, description string, out io.Writer) { output := struct { - APITitle string `json:"apiTitle"` - Description string `json:"description"` + APITitle string `json:"apiTitle,omitempty"` + Description string `json:"description,omitempty"` Results []VerboseResult `json:"results"` }{ APITitle: title, diff --git a/cmd/log_test.go b/cmd/log_test.go new file mode 100644 index 0000000..9c10ee6 --- /dev/null +++ b/cmd/log_test.go @@ -0,0 +1,131 @@ +package cmd + +import ( + "encoding/json" + "os" + "testing" +) + +// captureJSONOutput runs writeLog's JSON branch and returns the decoded stdout. +func captureJSONOutput(t *testing.T, isQuiet, isVerbose bool) map[string]interface{} { + t.Helper() + + stdout, err := os.CreateTemp(t.TempDir(), "stdout") + if err != nil { + t.Fatal(err) + } + defer stdout.Close() + + previousStdout := os.Stdout + previousFormat, previousQuiet, previousVerbose := outputFormat, quiet, verbose + previousTitle, previousDescription := specTitle, specDescription + previousResults, previousVerboseResults := jsonResultArray, jsonVerboseResultArray + defer func() { + os.Stdout = previousStdout + outputFormat, quiet, verbose = previousFormat, previousQuiet, previousVerbose + specTitle, specDescription = previousTitle, previousDescription + jsonResultArray, jsonVerboseResultArray = previousResults, previousVerboseResults + }() + + os.Stdout = stdout + outputFormat, quiet, verbose = "json", isQuiet, isVerbose + specTitle, specDescription = "Example API", "A long spec description." + jsonResultArray = []Result{{Method: "GET", Status: 200, Target: "/pet/1"}} + jsonVerboseResultArray = []VerboseResult{{Method: "GET", Status: 200, Target: "/pet/1"}} + + writeLog(8899, "", "", "", "") + + data, err := os.ReadFile(stdout.Name()) + if err != nil { + t.Fatal(err) + } + + var decoded map[string]interface{} + if err := json.Unmarshal(data, &decoded); err != nil { + t.Fatalf("output is not valid JSON: %v (%s)", err, data) + } + return decoded +} + +func TestQuietOmitsSpecMetadataJSON(t *testing.T) { + for _, tc := range []struct { + name string + verbose bool + }{ + {"concise", false}, + {"verbose", true}, + } { + t.Run(tc.name, func(t *testing.T) { + decoded := captureJSONOutput(t, true, tc.verbose) + + if _, ok := decoded["apiTitle"]; ok { + t.Error("apiTitle should be omitted when the quiet flag is set") + } + if _, ok := decoded["description"]; ok { + t.Error("description should be omitted when the quiet flag is set") + } + + results, ok := decoded["results"].([]interface{}) + if !ok { + t.Fatalf("results missing or wrong type: %#v", decoded["results"]) + } + if len(results) != 1 { + t.Errorf("expected 1 result, got %d", len(results)) + } + }) + } +} + +func TestSpecMetadataPresentWithoutQuiet(t *testing.T) { + for _, tc := range []struct { + name string + verbose bool + }{ + {"concise", false}, + {"verbose", true}, + } { + t.Run(tc.name, func(t *testing.T) { + decoded := captureJSONOutput(t, false, tc.verbose) + + if decoded["apiTitle"] != "Example API" { + t.Errorf("expected apiTitle to be present, got %#v", decoded["apiTitle"]) + } + if decoded["description"] != "A long spec description." { + t.Errorf("expected description to be present, got %#v", decoded["description"]) + } + }) + } +} + +func TestPrintSpecInfoQuietSuppressesConsoleOutput(t *testing.T) { + stderr, err := os.CreateTemp(t.TempDir(), "stderr") + if err != nil { + t.Fatal(err) + } + defer stderr.Close() + + previousStderr := os.Stderr + previousFormat, previousQuiet := outputFormat, quiet + defer func() { + os.Stderr = previousStderr + outputFormat, quiet = previousFormat, previousQuiet + }() + + os.Stderr = stderr + outputFormat, quiet = "console", true + + PrintSpecInfo(map[string]interface{}{ + "info": map[string]interface{}{ + "title": "Example API", + "description": "A long spec description.", + }, + }) + + data, err := os.ReadFile(stderr.Name()) + if err != nil { + t.Fatal(err) + } + if len(data) != 0 { + t.Errorf("expected no console output when quiet is set, got %q", data) + } +} diff --git a/cmd/utils.go b/cmd/utils.go index f1a7cd7..d6eea73 100644 --- a/cmd/utils.go +++ b/cmd/utils.go @@ -824,13 +824,15 @@ func ResolveExternalRef(ref string, baseDir string) map[string]interface{} { func PrintSpecInfo(spec map[string]interface{}) { info, ok := spec["info"].(map[string]interface{}) if !ok || info == nil { - printInfo("No information defined in the documentation.\n") + if !quiet { + printInfo("No information defined in the documentation.\n") + } } else { title, ok := info["title"].(string) if ok && title != "" { if outputFormat == "json" { specTitle = title - } else { + } else if !quiet { printInfo("Title: %s\n", title) } } @@ -839,7 +841,7 @@ func PrintSpecInfo(spec map[string]interface{}) { if ok && description != "" { if outputFormat == "json" { specDescription = description - } else { + } else if !quiet { printInfo("Description: %s\n", description) } }