Files
ReleaseKit/internal/validation/format_test.go

163 lines
6.5 KiB
Go

package validation
import (
"context"
"errors"
"os"
"path/filepath"
"strings"
"testing"
"gitea.maximumdirect.net/eric/ReleaseKit/internal/git"
)
func TestCheckGoFormatUsesCapturedRegularGoBlobs(t *testing.T) {
repository := newTreeRepository(t)
writeTreeFile(t, repository, "main.go", []byte("package main\n\nfunc main() {}\n"))
writeTreeFile(t, repository, "vendor/example/format.go", []byte("package example\nfunc Value( ){return 1}\n"))
writeTreeFile(t, repository, "nested/module/odd name.go", []byte("package nested\nfunc Value( ){return 2}\n"))
writeTreeFile(t, repository, "notes.txt", []byte("not Go source\n"))
if err := os.Symlink("main.go", filepath.Join(repository, "link.go")); err != nil {
t.Fatal(err)
}
gitTree(t, repository, "add", ".")
gitTree(t, repository, "commit", "-m", "capture Go files")
commit := strings.TrimSpace(gitTree(t, repository, "rev-parse", "HEAD"))
// Worktree edits must not affect the formatting result for the captured commit.
writeTreeFile(t, repository, "main.go", []byte("package main\nfunc main( ){}\n"))
writeTreeFile(t, repository, "vendor/example/format.go", []byte("package example\n\nfunc Value() { return 1 }\n"))
worktreeMain, err := os.ReadFile(filepath.Join(repository, "main.go"))
if err != nil {
t.Fatal(err)
}
tree, err := ReadCandidateTree(context.Background(), repository, commit)
if err != nil {
t.Fatal(err)
}
cleanupCandidateTree(t, tree)
err = checkGoFormat(context.Background(), tree)
var failure *Failure
if !errors.As(err, &failure) || failure.Check != GoFormat {
t.Fatalf("checkGoFormat error = %v, want go-format failure", err)
}
wantPaths := []string{"nested/module/odd name.go", "vendor/example/format.go"}
if len(failure.Findings) != len(wantPaths) {
t.Fatalf("format findings = %#v, want paths %q", failure.Findings, wantPaths)
}
for index, path := range wantPaths {
if failure.Findings[index].Path != path {
t.Errorf("format finding %d path = %q, want %q", index, failure.Findings[index].Path, path)
}
}
worktreeMainAfter, err := os.ReadFile(filepath.Join(repository, "main.go"))
if err != nil {
t.Fatal(err)
}
if string(worktreeMainAfter) != string(worktreeMain) {
t.Fatalf("format check changed worktree source: got %q, want %q", worktreeMainAfter, worktreeMain)
}
}
func TestCheckGoFormatPreservesParserAndInspectionCauses(t *testing.T) {
t.Run("parser", func(t *testing.T) {
repository := newTreeRepository(t)
writeTreeFile(t, repository, "broken.go", []byte("package broken\nfunc ( {\n"))
gitTree(t, repository, "add", ".")
gitTree(t, repository, "commit", "-m", "capture invalid Go source")
commit := strings.TrimSpace(gitTree(t, repository, "rev-parse", "HEAD"))
tree, err := ReadCandidateTree(context.Background(), repository, commit)
if err != nil {
t.Fatal(err)
}
cleanupCandidateTree(t, tree)
err = checkGoFormat(context.Background(), tree)
var failure *Failure
if !errors.As(err, &failure) || failure.Check != GoFormat || failure.Cause == nil {
t.Fatalf("parser failure = %#v, want go-format failure with cause", err)
}
if len(failure.Findings) != 1 || failure.Findings[0].Path != "broken.go" {
t.Fatalf("parser findings = %#v, want broken.go", failure.Findings)
}
if !strings.Contains(failure.Cause.Error(), "broken.go") {
t.Fatalf("parser cause = %v, want committed filename context", failure.Cause)
}
})
t.Run("inspection", func(t *testing.T) {
tree := &CandidateTree{
repositoryDir: t.TempDir(),
entries: []git.TreeEntry{{Path: "missing.go", ObjectID: strings.Repeat("0", 40), Mode: "100644", Type: "blob"}},
byPath: map[string]int{"missing.go": 0},
}
cleanupCandidateTree(t, tree)
err := checkGoFormat(context.Background(), tree)
var failure *Failure
if !errors.As(err, &failure) || failure.Check != GoFormat || failure.Cause == nil {
t.Fatalf("inspection failure = %#v, want go-format failure with cause", err)
}
if len(failure.Findings) != 1 || failure.Findings[0].Path != "missing.go" {
t.Fatalf("inspection findings = %#v, want missing.go", failure.Findings)
}
})
}
func TestCheckGoFormatReportsEveryPathForRepeatedUnformattedContent(t *testing.T) {
repository := newTreeRepository(t)
contents := []byte("package app\nfunc value(){return}\n")
writeTreeFile(t, repository, "first.go", contents)
writeTreeFile(t, repository, "nested/second.go", contents)
commit := commitCandidateTree(t, repository)
tree, err := ReadCandidateTree(context.Background(), repository, commit)
if err != nil {
t.Fatal(err)
}
cleanupCandidateTree(t, tree)
err = checkGoFormat(context.Background(), tree)
var failure *Failure
if !errors.As(err, &failure) || failure.Check != GoFormat {
t.Fatalf("checkGoFormat() error = %v, want go-format findings", err)
}
if len(failure.Findings) != 2 || failure.Findings[0].Path != "first.go" || failure.Findings[1].Path != "nested/second.go" {
t.Fatalf("findings = %#v, want both paths in tree order", failure.Findings)
}
first, _ := tree.Entry("first.go")
second, _ := tree.Entry("nested/second.go")
if first.ObjectID != second.ObjectID {
t.Fatalf("fixture object IDs = %q and %q, want repeated content", first.ObjectID, second.ObjectID)
}
}
func TestRunGoFormatIsOptionalAndStopsAfterFinding(t *testing.T) {
repository := newTreeRepository(t)
writeTreeFile(t, repository, "unformatted.go", []byte("package fixture\nfunc Value( ){return 1}\n"))
gitTree(t, repository, "add", ".")
gitTree(t, repository, "commit", "-m", "capture unformatted Go source")
commit := strings.TrimSpace(gitTree(t, repository, "rev-parse", "HEAD"))
logPath := installGoShim(t, "", false)
if err := Run(context.Background(), Request{RepositoryDir: repository, Commit: commit}); err != nil {
t.Fatalf("default validation = %v, want success with formatting disabled", err)
}
if got := strings.Join(readGoShimLog(t, logPath), "\n"); !strings.Contains(got, "test -mod=readonly -count=1 ./...") {
t.Fatalf("default Go commands = %q, want standard suite", got)
}
logPath = installGoShim(t, "", false)
err := Run(context.Background(), Request{
RepositoryDir: repository,
Commit: commit,
Settings: Settings{GoFormat: true},
})
var failure *Failure
if !errors.As(err, &failure) || failure.Check != GoFormat {
t.Fatalf("selected formatting error = %v, want go-format failure", err)
}
commands := strings.Join(readGoShimLog(t, logPath), "\n")
if strings.Contains(commands, "build ") || strings.Contains(commands, "vet ") {
t.Fatalf("later Go commands ran after formatting failure: %q", commands)
}
}