Go 145 lines
package workflow
import (
"os"
"path/filepath"
"testing"
)
// Trigger literals are assembled from fragments so this test source stays
// self-clean for eeco's own attribution scan (Constraint 3), matching the
// discipline in attribution.go.
func coTrailer() string {
return "Co-" + "Authored-" + "By: " + "A Real Person <[email protected]>"
}
func genLine() string {
return "Gen" + "erated " + "with our " + "Assistant"
}
func newGuardDetector(t *testing.T) *Detector {
t.Helper()
det, err := NewDetector(nil)
if err != nil {
t.Fatal(err)
}
return det
}
func TestScanCommitGuard_InlineMessageTrailer(t *testing.T) {
det := newGuardDetector(t)
cmd := `git commit -m "fix: thing" -m "` + coTrailer() + `"`
res := ScanCommitGuard(det, cmd, t.TempDir())
if !res.IsCommit {
t.Fatal("expected IsCommit true")
}
if len(res.Findings) == 0 {
t.Errorf("expected a finding for the inline trailer, got none")
}
}
func TestScanCommitGuard_CleanCommitNoFinding(t *testing.T) {
det := newGuardDetector(t)
res := ScanCommitGuard(det, `git commit -m "fix: a real change"`, t.TempDir())
if !res.IsCommit {
t.Fatal("expected IsCommit true")
}
if len(res.Findings) != 0 {
t.Errorf("clean commit produced findings: %+v", res.Findings)
}
}
func TestScanCommitGuard_FileMessageTrailer(t *testing.T) {
det := newGuardDetector(t)
dir := t.TempDir()
msgPath := filepath.Join(dir, "MSG.txt")
body := "feat: x\n\n" + coTrailer() + "\n"
if err := os.WriteFile(msgPath, []byte(body), 0o644); err != nil {
t.Fatal(err)
}
res := ScanCommitGuard(det, `git commit -F MSG.txt`, dir)
if !res.IsCommit || len(res.Findings) == 0 {
t.Errorf("expected commit + finding from -F file, got IsCommit=%v findings=%d", res.IsCommit, len(res.Findings))
}
}
func TestScanCommitGuard_NonCommitSegments(t *testing.T) {
det := newGuardDetector(t)
for _, cmd := range []string{
`echo "git commit -m bad"`,
`git status`,
`git log --oneline`,
`ls -la && pwd`,
} {
res := ScanCommitGuard(det, cmd, t.TempDir())
if res.IsCommit {
t.Errorf("%q wrongly qualified as a commit", cmd)
}
if len(res.Findings) != 0 {
t.Errorf("%q produced findings: %+v", cmd, res.Findings)
}
}
}
func TestScanCommitGuard_ChainedCommit(t *testing.T) {
det := newGuardDetector(t)
cmd := `git add . && git commit -m "subject" -m "` + coTrailer() + `"`
res := ScanCommitGuard(det, cmd, t.TempDir())
if !res.IsCommit || len(res.Findings) == 0 {
t.Errorf("chained commit: IsCommit=%v findings=%d, want true + >0", res.IsCommit, len(res.Findings))
}
}
func TestScanCommitGuard_GlobalOptionAndEnvPrefix(t *testing.T) {
det := newGuardDetector(t)
for _, cmd := range []string{
`git -C /tmp/repo commit -m "x" -m "` + coTrailer() + `"`,
`GIT_AUTHOR_NAME=bot git commit -m "x" -m "` + coTrailer() + `"`,
`git -c user.name=x commit -am "` + coTrailer() + `"`,
} {
res := ScanCommitGuard(det, cmd, t.TempDir())
if !res.IsCommit || len(res.Findings) == 0 {
t.Errorf("%q: IsCommit=%v findings=%d, want true + >0", cmd, res.IsCommit, len(res.Findings))
}
}
}
func TestScanCommitGuard_StagedDiffFinding(t *testing.T) {
det := newGuardDetector(t)
orig := stagedDiff
defer func() { stagedDiff = orig }()
diff := "diff --git a/x b/x\n+// " + genLine() + "\n"
stagedDiff = func(string) string { return diff }
// A clean message, but the staged diff carries a generated-by line.
res := ScanCommitGuard(det, `git commit -m "fix: clean"`, t.TempDir())
if !res.IsCommit || len(res.Findings) == 0 {
t.Errorf("expected a finding from the staged diff, got IsCommit=%v findings=%d", res.IsCommit, len(res.Findings))
}
}
func TestScanCommitGuard_DegradeOpenOnCommandSubstitution(t *testing.T) {
det := newGuardDetector(t)
orig := stagedDiff
defer func() { stagedDiff = orig }()
stagedDiff = func(string) string { return "" }
// A $()-resolved message cannot be statically read: no finding (allow),
// no panic. IsCommit is still true (the segment is a commit).
res := ScanCommitGuard(det, `git commit -m "$(cat msg.txt)"`, t.TempDir())
if !res.IsCommit {
t.Fatal("expected IsCommit true")
}
if len(res.Findings) != 0 {
t.Errorf("command-substitution message must degrade open, got findings: %+v", res.Findings)
}
}
func TestScanCommitGuard_QuotedSeparatorDoesNotSplit(t *testing.T) {
det := newGuardDetector(t)
// The ';' and '&&' live inside the quoted message and must not split
// the segment, so the commit is still detected as one.
res := ScanCommitGuard(det, `git commit -m "fix; really && done"`, t.TempDir())
if !res.IsCommit {
t.Error("quoted separators wrongly split the commit segment")
}
}