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) } } }