orama/pkg/inspector/report.go
2026-02-11 09:53:46 +02:00

137 lines
3.3 KiB
Go

package inspector
import (
"encoding/json"
"fmt"
"io"
"sort"
"strings"
)
// PrintTable writes a human-readable table of check results.
func PrintTable(results *Results, w io.Writer) {
if len(results.Checks) == 0 {
fmt.Fprintf(w, "No checks executed.\n")
return
}
// Sort: failures first, then warnings, then passes, then skips.
// Within each group, sort by severity (critical first).
sorted := make([]CheckResult, len(results.Checks))
copy(sorted, results.Checks)
sort.Slice(sorted, func(i, j int) bool {
oi, oj := statusOrder(sorted[i].Status), statusOrder(sorted[j].Status)
if oi != oj {
return oi < oj
}
// Higher severity first
if sorted[i].Severity != sorted[j].Severity {
return sorted[i].Severity > sorted[j].Severity
}
return sorted[i].ID < sorted[j].ID
})
// Group by subsystem
groups := map[string][]CheckResult{}
var subsystems []string
for _, c := range sorted {
if _, exists := groups[c.Subsystem]; !exists {
subsystems = append(subsystems, c.Subsystem)
}
groups[c.Subsystem] = append(groups[c.Subsystem], c)
}
for _, sub := range subsystems {
checks := groups[sub]
fmt.Fprintf(w, "\n%s %s\n", severityIcon(Critical), strings.ToUpper(sub))
fmt.Fprintf(w, "%s\n", strings.Repeat("-", 70))
for _, c := range checks {
icon := statusIcon(c.Status)
sev := fmt.Sprintf("[%s]", c.Severity)
nodePart := ""
if c.Node != "" {
nodePart = fmt.Sprintf(" (%s)", c.Node)
}
fmt.Fprintf(w, " %s %-8s %s%s\n", icon, sev, c.Name, nodePart)
if c.Message != "" {
fmt.Fprintf(w, " %s\n", c.Message)
}
}
}
passed, failed, warned, skipped := results.Summary()
fmt.Fprintf(w, "\n%s\n", strings.Repeat("=", 70))
fmt.Fprintf(w, "Summary: %d passed, %d failed, %d warnings, %d skipped (%.1fs)\n",
passed, failed, warned, skipped, results.Duration.Seconds())
}
// PrintJSON writes check results as JSON.
func PrintJSON(results *Results, w io.Writer) {
passed, failed, warned, skipped := results.Summary()
output := struct {
Summary struct {
Passed int `json:"passed"`
Failed int `json:"failed"`
Warned int `json:"warned"`
Skipped int `json:"skipped"`
Total int `json:"total"`
Seconds float64 `json:"duration_seconds"`
} `json:"summary"`
Checks []CheckResult `json:"checks"`
}{
Checks: results.Checks,
}
output.Summary.Passed = passed
output.Summary.Failed = failed
output.Summary.Warned = warned
output.Summary.Skipped = skipped
output.Summary.Total = len(results.Checks)
output.Summary.Seconds = results.Duration.Seconds()
enc := json.NewEncoder(w)
enc.SetIndent("", " ")
enc.Encode(output)
}
// SummaryLine returns a one-line summary string.
func SummaryLine(results *Results) string {
passed, failed, warned, skipped := results.Summary()
return fmt.Sprintf("%d passed, %d failed, %d warnings, %d skipped",
passed, failed, warned, skipped)
}
func statusOrder(s Status) int {
switch s {
case StatusFail:
return 0
case StatusWarn:
return 1
case StatusPass:
return 2
case StatusSkip:
return 3
default:
return 4
}
}
func statusIcon(s Status) string {
switch s {
case StatusPass:
return "OK"
case StatusFail:
return "FAIL"
case StatusWarn:
return "WARN"
case StatusSkip:
return "SKIP"
default:
return "??"
}
}
func severityIcon(_ Severity) string {
return "##"
}