package diag
import (
"fmt"
"strings"
"testing"
)
func TestFormatNothingForACleanFile(t *testing.T) {
if got := Format(nil, "/r", "/r/a.go"); got != "" {
t.Errorf("nil → %q", got)
}
if got := Format([]Diagnostic{}, "/r", "/r/a.go"); got != "" {
t.Errorf("empty → %q", got)
}
// Hints alone are silence too.
only := []Diagnostic{{File: "/r/a.go", Line: 1, Severity: SeverityHint, Message: "simplify", Source: "gopls"}}
if got := Format(only, "/r", "/r/a.go"); got != "" {
t.Errorf("hints only → %q", got)
}
}
func TestFormatMatchesTheShapeTheModelReads(t *testing.T) {
ds := []Diagnostic{
{File: "/r/main.go", Line: 19, Col: 6, Severity: SeverityError, Message: "declared and not used: n", Source: "gopls"},
{File: "/r/main.go", Line: 14, Col: 2, Severity: SeverityError, Message: "undefined: fmt.Printl", Source: "gopls"},
}
got := Format(ds, "/r", "/r/main.go")
want := "diagnostics (gopls): 2 problems\n" +
" main.go:14:2: undefined: fmt.Printl\n" +
" main.go:19:6: declared and not used: n"
if got != want {
t.Errorf("got:\n%s\nwant:\n%s", got, want)
}
}
func TestFormatSingularWarningsAndMissingColumns(t *testing.T) {
ds := []Diagnostic{{File: "/r/x.py", Line: 3, Severity: SeverityWarning, Message: "E501 Line too long", Source: "ruff"}}
got := Format(ds, "/r", "/r/x.py")
want := "diagnostics (ruff): 1 problem\n x.py:3: warning: E501 Line too long"
if got != want {
t.Errorf("got %q, want %q", got, want)
}
}
func TestFormatChangedFileFirstThenCap(t *testing.T) {
var ds []Diagnostic
for i := 1; i <= 12; i++ {
ds = append(ds, Diagnostic{File: "/r/other.go", Line: i, Col: 1, Severity: SeverityError, Message: fmt.Sprintf("other %d", i), Source: "go build"})
}
ds = append(ds,
Diagnostic{File: "/r/mine.go", Line: 9, Col: 1, Severity: SeverityWarning, Message: "mine warn", Source: "go build"},
Diagnostic{File: "/r/mine.go", Line: 2, Col: 1, Severity: SeverityError, Message: "mine err", Source: "go build"},
)
got := Format(ds, "/r", "/r/mine.go")
lines := strings.Split(got, "\n")
if lines[0] != "diagnostics (go build): 14 problems" {
t.Errorf("header: %q", lines[0])
}
if lines[1] != " mine.go:2:1: mine err" || lines[2] != " mine.go:9:1: warning: mine warn" {
t.Errorf("the changed file should come first, errors before warnings:\n%s", got)
}
if !strings.HasPrefix(lines[3], " other.go:1:1:") {
t.Errorf("neighbours after, labelled: %q", lines[3])
}
// header + 10 shown + the dropped line
if len(lines) != 12 {
t.Errorf("%d lines:\n%s", len(lines), got)
}
if lines[11] != " … and 4 more not shown" {
t.Errorf("dropped line: %q", lines[11])
}
}
func TestFormatMixedSourcesAndPathsOutsideRoot(t *testing.T) {
ds := []Diagnostic{
{File: "/r/sub/a.go", Line: 1, Col: 1, Severity: SeverityError, Message: "a", Source: "go build"},
{File: "/elsewhere/b.go", Line: 1, Col: 1, Severity: SeverityError, Message: "line one\n note: line two", Source: "go vet"},
}
got := Format(ds, "/r", "/r/sub/a.go")
if !strings.HasPrefix(got, "diagnostics (mixed): 2 problems") {
t.Errorf("header: %q", got)
}
if !strings.Contains(got, "\n sub/a.go:1:1: a") {
t.Errorf("relative path: %q", got)
}
if !strings.Contains(got, "\n ../elsewhere/b.go:1:1: line one note: line two") {
t.Errorf("outside-root path and folded message: %q", got)
}
}
func TestSeverityString(t *testing.T) {
for s, want := range map[Severity]string{SeverityError: "error", SeverityWarning: "warning", SeverityInfo: "info", SeverityHint: "hint", 9: "severity(9)"} {
if got := s.String(); got != want {
t.Errorf("%d → %q, want %q", int(s), got, want)
}
}
}