Skip to content

Commit 4a0c23d

Browse files
Patel230claude
andcommitted
test(context): cover git exec functions (DiffBase/ChangedFiles/Blame/Enrich)
internal/context was ~30%: the git-shelling functions were untested. Added tests that build a throwaway git repo in t.TempDir (deterministic env, t.Skip if git absent) and exercise ChangedFiles, DiffBase, Blame (+ invalid-path guard), and Enrich's recent-commits path. internal/context 30%->85%; sight overall 75%->77%. Gate 70->74. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 47c5c3f commit 4a0c23d

2 files changed

Lines changed: 162 additions & 1 deletion

File tree

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ jobs:
115115
- name: Coverage threshold
116116
run: |
117117
COVERAGE=$(go tool cover -func=coverage.out | tail -1 | grep -oE '[0-9]+\.[0-9]+' || echo "0")
118-
THRESHOLD=70
118+
THRESHOLD=74
119119
if [ "$(echo "$COVERAGE < $THRESHOLD" | bc -l)" -eq 1 ]; then
120120
echo "::error::Coverage ${COVERAGE}% is below threshold ${THRESHOLD}%"
121121
exit 1

internal/context/git_exec_test.go

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
package context
2+
3+
import (
4+
"os"
5+
"os/exec"
6+
"path/filepath"
7+
"strings"
8+
"testing"
9+
)
10+
11+
// gitRepo creates a throwaway git repo in a temp dir, chdirs into it for the
12+
// duration of the test, and returns a commit helper. It skips the test if git
13+
// is unavailable. chdir is process-global, so these tests must not run in
14+
// parallel (they don't call t.Parallel).
15+
func gitRepo(t *testing.T) func(msg string) {
16+
t.Helper()
17+
if _, err := exec.LookPath("git"); err != nil {
18+
t.Skip("git not available")
19+
}
20+
dir := t.TempDir()
21+
22+
orig, err := os.Getwd()
23+
if err != nil {
24+
t.Fatalf("getwd: %v", err)
25+
}
26+
if err := os.Chdir(dir); err != nil {
27+
t.Fatalf("chdir: %v", err)
28+
}
29+
t.Cleanup(func() { _ = os.Chdir(orig) })
30+
31+
run := func(args ...string) {
32+
t.Helper()
33+
cmd := exec.Command("git", args...)
34+
// Keep config local + deterministic regardless of the host's git config.
35+
cmd.Env = append(os.Environ(),
36+
"GIT_AUTHOR_NAME=Tester", "GIT_AUTHOR_EMAIL=tester@example.com",
37+
"GIT_COMMITTER_NAME=Tester", "GIT_COMMITTER_EMAIL=tester@example.com",
38+
"GIT_CONFIG_GLOBAL=/dev/null", "GIT_CONFIG_SYSTEM=/dev/null",
39+
)
40+
if out, err := cmd.CombinedOutput(); err != nil {
41+
t.Fatalf("git %s: %v\n%s", strings.Join(args, " "), err, out)
42+
}
43+
}
44+
45+
run("init", "-q")
46+
run("config", "user.name", "Tester")
47+
run("config", "user.email", "tester@example.com")
48+
run("checkout", "-q", "-b", "main")
49+
50+
commit := func(msg string) {
51+
t.Helper()
52+
run("add", "-A")
53+
run("commit", "-q", "--allow-empty", "-m", msg)
54+
}
55+
return commit
56+
}
57+
58+
func writeFile(t *testing.T, name, content string) {
59+
t.Helper()
60+
if err := os.WriteFile(filepath.Join(".", name), []byte(content), 0o644); err != nil {
61+
t.Fatalf("write %s: %v", name, err)
62+
}
63+
}
64+
65+
func TestChangedFiles(t *testing.T) {
66+
commit := gitRepo(t)
67+
writeFile(t, "a.txt", "one\n")
68+
commit("base commit")
69+
// Tag the base, then add a new file on top.
70+
writeFile(t, "b.txt", "two\n")
71+
commit("add b.txt")
72+
73+
files, err := ChangedFiles("HEAD~1")
74+
if err != nil {
75+
t.Fatalf("ChangedFiles: %v", err)
76+
}
77+
found := false
78+
for _, f := range files {
79+
if f == "b.txt" {
80+
found = true
81+
}
82+
}
83+
if !found {
84+
t.Errorf("expected b.txt in changed files, got %v", files)
85+
}
86+
}
87+
88+
func TestChangedFiles_NoChanges(t *testing.T) {
89+
commit := gitRepo(t)
90+
writeFile(t, "a.txt", "one\n")
91+
commit("only commit")
92+
93+
// HEAD...HEAD has no changes -> nil slice, no error.
94+
files, err := ChangedFiles("HEAD")
95+
if err != nil {
96+
t.Fatalf("ChangedFiles: %v", err)
97+
}
98+
if len(files) != 0 {
99+
t.Errorf("expected no changed files, got %v", files)
100+
}
101+
}
102+
103+
func TestDiffBase(t *testing.T) {
104+
commit := gitRepo(t)
105+
writeFile(t, "a.txt", "one\n")
106+
commit("base")
107+
writeFile(t, "a.txt", "one\ntwo\n")
108+
commit("change a.txt")
109+
110+
diff, err := DiffBase("HEAD~1")
111+
if err != nil {
112+
t.Fatalf("DiffBase: %v", err)
113+
}
114+
if !strings.Contains(diff, "a.txt") {
115+
t.Errorf("expected diff to mention a.txt, got:\n%s", diff)
116+
}
117+
if !strings.Contains(diff, "+two") {
118+
t.Errorf("expected diff to contain the added line '+two', got:\n%s", diff)
119+
}
120+
}
121+
122+
func TestBlame(t *testing.T) {
123+
commit := gitRepo(t)
124+
writeFile(t, "a.txt", "line one\nline two\nline three\n")
125+
commit("add a.txt")
126+
127+
out, err := Blame("a.txt", 1, 3)
128+
if err != nil {
129+
t.Fatalf("Blame: %v", err)
130+
}
131+
// parseBlameAuthors yields "Author(count)" — our committer is "Tester".
132+
if !strings.Contains(out, "Tester") {
133+
t.Errorf("expected blame summary to credit Tester, got %q", out)
134+
}
135+
}
136+
137+
func TestBlame_InvalidPath(t *testing.T) {
138+
gitRepo(t)
139+
if _, err := Blame("../escape.txt", 1, 1); err == nil {
140+
t.Error("expected error for path traversing outside working dir")
141+
}
142+
}
143+
144+
func TestEnrich_RecentCommits(t *testing.T) {
145+
commit := gitRepo(t)
146+
writeFile(t, "a.txt", "v1\n")
147+
commit("first change to a.txt")
148+
writeFile(t, "a.txt", "v2\n")
149+
commit("second change to a.txt")
150+
151+
ctxs := Enrich([]string{"a.txt"})
152+
if len(ctxs) != 1 {
153+
t.Fatalf("expected 1 context, got %d", len(ctxs))
154+
}
155+
if ctxs[0].Path != "a.txt" {
156+
t.Errorf("path = %q, want a.txt", ctxs[0].Path)
157+
}
158+
if len(ctxs[0].RecentCommits) == 0 {
159+
t.Errorf("expected recent commits for a.txt, got none")
160+
}
161+
}

0 commit comments

Comments
 (0)