@@ -0,0 +1,97 @@
|
||||
package lintgate
|
||||
|
||||
// Observes the exit status of `task lint` (the lint step of `task check`), not the lint
|
||||
// rules themselves. It runs the repo's real Taskfile in a scratch module so a change to the
|
||||
// lint command that stops it failing on findings (#41) turns this red.
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
const scratchWithFinding = `package main
|
||||
|
||||
import "os"
|
||||
|
||||
func main() { os.Remove("x") }
|
||||
`
|
||||
|
||||
func scratchModule(t *testing.T) string {
|
||||
t.Helper()
|
||||
taskfile, err := os.ReadFile(filepath.Join("..", "..", "Taskfile.yml"))
|
||||
if err != nil {
|
||||
t.Fatalf("read Taskfile.yml: %v", err)
|
||||
}
|
||||
dir := t.TempDir()
|
||||
for name, body := range map[string]string{
|
||||
"Taskfile.yml": string(taskfile),
|
||||
"go.mod": "module scratch\n\ngo 1.21\n",
|
||||
"main.go": scratchWithFinding,
|
||||
} {
|
||||
if err := os.WriteFile(filepath.Join(dir, name), []byte(body), 0o644); err != nil {
|
||||
t.Fatalf("write %s: %v", name, err)
|
||||
}
|
||||
}
|
||||
return dir
|
||||
}
|
||||
|
||||
// runLint runs `task lint` in dir with the given PATH and returns its combined output and exit code.
|
||||
func runLint(t *testing.T, dir, path string) (string, int) {
|
||||
t.Helper()
|
||||
taskBin, err := exec.LookPath("task")
|
||||
if err != nil {
|
||||
t.Skip("task not installed")
|
||||
}
|
||||
cmd := exec.Command(taskBin, "lint")
|
||||
cmd.Dir = dir
|
||||
cmd.Env = append(os.Environ(), "PATH="+path)
|
||||
var out bytes.Buffer
|
||||
cmd.Stdout, cmd.Stderr = &out, &out
|
||||
err = cmd.Run()
|
||||
var exitErr *exec.ExitError
|
||||
switch {
|
||||
case err == nil:
|
||||
return out.String(), 0
|
||||
case errors.As(err, &exitErr):
|
||||
return out.String(), exitErr.ExitCode()
|
||||
default:
|
||||
t.Fatalf("run task lint: %v", err)
|
||||
return "", -1
|
||||
}
|
||||
}
|
||||
|
||||
func TestTaskLint_ExitsNonZeroWhenGolangciLintReportsAFinding(t *testing.T) {
|
||||
if _, err := exec.LookPath("golangci-lint"); err != nil {
|
||||
t.Skip("golangci-lint not installed")
|
||||
}
|
||||
out, code := runLint(t, scratchModule(t), os.Getenv("PATH"))
|
||||
if code == 0 {
|
||||
t.Fatalf("task lint exited 0 on a module with an unchecked error; output:\n%s", out)
|
||||
}
|
||||
if strings.Contains(out, "not installed") {
|
||||
t.Fatalf("task lint blamed a missing binary for a lint failure; output:\n%s", out)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTaskLint_SkipsWithANoticeWhenGolangciLintIsAbsent(t *testing.T) {
|
||||
taskBin, err := exec.LookPath("task")
|
||||
if err != nil {
|
||||
t.Skip("task not installed")
|
||||
}
|
||||
bin := t.TempDir() // a PATH holding task and nothing else
|
||||
if err := os.Symlink(taskBin, filepath.Join(bin, "task")); err != nil {
|
||||
t.Fatalf("symlink task: %v", err)
|
||||
}
|
||||
out, code := runLint(t, scratchModule(t), bin)
|
||||
if code != 0 {
|
||||
t.Fatalf("task lint exited %d without golangci-lint on PATH; output:\n%s", code, out)
|
||||
}
|
||||
if !strings.Contains(out, "not installed") {
|
||||
t.Fatalf("expected the skip notice; output:\n%s", out)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user