Vectrum
Sign In
98 lines · 3.6 KB · Go
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)
		}
	}
}