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