Skip to content

Commit 4b3dc82

Browse files
committed
Added Unit Tests
1 parent 2b056dd commit 4b3dc82

3 files changed

Lines changed: 297 additions & 0 deletions

File tree

internal/git/operations_test.go

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
package git
2+
3+
import (
4+
"os"
5+
"os/exec"
6+
"path/filepath"
7+
"strings"
8+
"testing"
9+
10+
"github.com/dfanso/commit-msg/pkg/types"
11+
)
12+
13+
func TestIsRepository(t *testing.T) {
14+
t.Parallel()
15+
16+
if _, err := exec.LookPath("git"); err != nil {
17+
t.Skip("git executable not available")
18+
}
19+
20+
t.Run("returns true for initialized repo", func(t *testing.T) {
21+
t.Parallel()
22+
23+
dir := t.TempDir()
24+
25+
cmd := exec.Command("git", "init", dir)
26+
if output, err := cmd.CombinedOutput(); err != nil {
27+
t.Fatalf("failed to init git repo: %v: %s", err, string(output))
28+
}
29+
30+
if got := IsRepository(dir); !got {
31+
t.Fatalf("IsRepository(%q) = false, want true", dir)
32+
}
33+
})
34+
35+
t.Run("returns false for non repo", func(t *testing.T) {
36+
t.Parallel()
37+
38+
dir := t.TempDir()
39+
if got := IsRepository(dir); got {
40+
t.Fatalf("IsRepository(%q) = true, want false", dir)
41+
}
42+
})
43+
44+
t.Run("returns false for missing path", func(t *testing.T) {
45+
t.Parallel()
46+
47+
dir := t.TempDir()
48+
missing := filepath.Join(dir, "does-not-exist")
49+
if got := IsRepository(missing); got {
50+
t.Fatalf("IsRepository(%q) = true, want false", missing)
51+
}
52+
})
53+
}
54+
55+
func TestGetChangesIncludesSections(t *testing.T) {
56+
if testing.Short() {
57+
t.Skip("skipping integration-style test in short mode")
58+
}
59+
60+
if _, err := exec.LookPath("git"); err != nil {
61+
t.Skip("git executable not available")
62+
}
63+
64+
dir := t.TempDir()
65+
66+
runGit(t, dir, "init")
67+
runGit(t, dir, "config", "user.name", "Test User")
68+
runGit(t, dir, "config", "user.email", "test@example.com")
69+
70+
tracked := filepath.Join(dir, "tracked.txt")
71+
if err := os.WriteFile(tracked, []byte("first version\n"), 0o644); err != nil {
72+
t.Fatalf("failed to write tracked file: %v", err)
73+
}
74+
runGit(t, dir, "add", "tracked.txt")
75+
runGit(t, dir, "commit", "-m", "initial commit")
76+
77+
if err := os.WriteFile(tracked, []byte("updated version\n"), 0o644); err != nil {
78+
t.Fatalf("failed to modify tracked file: %v", err)
79+
}
80+
81+
staged := filepath.Join(dir, "staged.txt")
82+
if err := os.WriteFile(staged, []byte("staged content\n"), 0o644); err != nil {
83+
t.Fatalf("failed to write staged file: %v", err)
84+
}
85+
runGit(t, dir, "add", "staged.txt")
86+
87+
untracked := filepath.Join(dir, "new.txt")
88+
if err := os.WriteFile(untracked, []byte("brand new file\n"), 0o644); err != nil {
89+
t.Fatalf("failed to write untracked file: %v", err)
90+
}
91+
92+
output, err := GetChanges(&types.RepoConfig{Path: dir})
93+
if err != nil {
94+
t.Fatalf("GetChanges returned error: %v", err)
95+
}
96+
97+
fragments := []string{
98+
"Unstaged changes:",
99+
"Staged changes:",
100+
"Untracked files:",
101+
"Content of new file new.txt:",
102+
"Recent commits for context:",
103+
"brand new file",
104+
}
105+
106+
for _, fragment := range fragments {
107+
if !strings.Contains(output, fragment) {
108+
t.Fatalf("output missing fragment %q\noutput: %s", fragment, output)
109+
}
110+
}
111+
}
112+
113+
func TestGetChangesErrorsOutsideRepo(t *testing.T) {
114+
t.Parallel()
115+
116+
dir := t.TempDir()
117+
118+
if _, err := GetChanges(&types.RepoConfig{Path: dir}); err == nil {
119+
t.Fatal("expected error for directory without git repository")
120+
}
121+
}
122+
123+
func runGit(t *testing.T, dir string, args ...string) {
124+
t.Helper()
125+
126+
cmdArgs := append([]string{"-C", dir}, args...)
127+
cmd := exec.Command("git", cmdArgs...)
128+
cmd.Env = append(os.Environ(), "GIT_TERMINAL_PROMPT=0")
129+
output, err := cmd.CombinedOutput()
130+
if err != nil {
131+
t.Fatalf("git %v failed: %v\n%s", args, err, output)
132+
}
133+
}

internal/utils/utils_test.go

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
package utils
2+
3+
import (
4+
"bytes"
5+
"os"
6+
"path/filepath"
7+
"testing"
8+
)
9+
10+
func TestNormalizePath(t *testing.T) {
11+
t.Parallel()
12+
13+
tests := []struct {
14+
name string
15+
input string
16+
expected string
17+
}{
18+
{name: "windows style", input: "foo\\bar\\", expected: "foo/bar"},
19+
{name: "already normalized", input: "foo/bar", expected: "foo/bar"},
20+
{name: "no trailing slash", input: "foo", expected: "foo"},
21+
}
22+
23+
for _, tt := range tests {
24+
tc := tt
25+
t.Run(tc.name, func(t *testing.T) {
26+
t.Parallel()
27+
28+
if got := NormalizePath(tc.input); got != tc.expected {
29+
t.Fatalf("NormalizePath(%q) = %q, want %q", tc.input, got, tc.expected)
30+
}
31+
})
32+
}
33+
}
34+
35+
func TestIsTextFile(t *testing.T) {
36+
t.Parallel()
37+
38+
tests := []struct {
39+
name string
40+
filename string
41+
want bool
42+
}{
43+
{name: "go source", filename: "main.go", want: true},
44+
{name: "markdown upper", filename: "README.MD", want: true},
45+
{name: "binary extension", filename: "image.png", want: false},
46+
{name: "no extension", filename: "LICENSE", want: false},
47+
}
48+
49+
for _, tt := range tests {
50+
t.Run(tt.name, func(t *testing.T) {
51+
t.Parallel()
52+
53+
if got := IsTextFile(tt.filename); got != tt.want {
54+
t.Fatalf("IsTextFile(%q) = %v, want %v", tt.filename, got, tt.want)
55+
}
56+
})
57+
}
58+
}
59+
60+
func TestIsSmallFile(t *testing.T) {
61+
t.Parallel()
62+
63+
dir := t.TempDir()
64+
65+
smallPath := filepath.Join(dir, "small.txt")
66+
if err := os.WriteFile(smallPath, bytes.Repeat([]byte("x"), 1024), 0o644); err != nil {
67+
t.Fatalf("failed to write small file: %v", err)
68+
}
69+
70+
largePath := filepath.Join(dir, "large.txt")
71+
if err := os.WriteFile(largePath, bytes.Repeat([]byte("y"), 11*1024), 0o644); err != nil {
72+
t.Fatalf("failed to write large file: %v", err)
73+
}
74+
75+
tests := []struct {
76+
name string
77+
path string
78+
want bool
79+
}{
80+
{name: "small file", path: smallPath, want: true},
81+
{name: "large file", path: largePath, want: false},
82+
{name: "missing file", path: filepath.Join(dir, "missing.txt"), want: false},
83+
}
84+
85+
for _, tt := range tests {
86+
tc := tt
87+
t.Run(tc.name, func(t *testing.T) {
88+
t.Parallel()
89+
90+
if got := IsSmallFile(tc.path); got != tc.want {
91+
t.Fatalf("IsSmallFile(%q) = %v, want %v", tc.path, got, tc.want)
92+
}
93+
})
94+
}
95+
}
96+
97+
func TestFilterEmpty(t *testing.T) {
98+
t.Parallel()
99+
100+
input := []string{"feat", "", "test", " "}
101+
want := []string{"feat", "test", " "}
102+
103+
got := FilterEmpty(input)
104+
105+
if len(got) != len(want) {
106+
t.Fatalf("FilterEmpty returned %d items, want %d", len(got), len(want))
107+
}
108+
109+
for i := range want {
110+
if got[i] != want[i] {
111+
t.Fatalf("FilterEmpty mismatch at index %d: got %q want %q", i, got[i], want[i])
112+
}
113+
}
114+
}

pkg/types/types_test.go

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package types
2+
3+
import (
4+
"encoding/json"
5+
"strings"
6+
"testing"
7+
)
8+
9+
func TestConfigJSONMarshalling(t *testing.T) {
10+
t.Parallel()
11+
12+
cfg := Config{
13+
GrokAPI: "https://api.x.ai/v1/chat/completions",
14+
Repos: map[string]RepoConfig{
15+
"repo-a": {
16+
Path: "/tmp/project",
17+
LastRun: "2024-06-01T12:00:00Z",
18+
},
19+
},
20+
}
21+
22+
data, err := json.Marshal(cfg)
23+
if err != nil {
24+
t.Fatalf("json.Marshal returned error: %v", err)
25+
}
26+
27+
jsonStr := string(data)
28+
if !strings.Contains(jsonStr, "\"grok_api\"") {
29+
t.Fatalf("expected grok_api key in JSON: %s", jsonStr)
30+
}
31+
if !strings.Contains(jsonStr, "\"repos\"") {
32+
t.Fatalf("expected repos key in JSON: %s", jsonStr)
33+
}
34+
}
35+
36+
func TestCommitPromptContent(t *testing.T) {
37+
t.Parallel()
38+
39+
requiredFragments := []string{
40+
"Starts with a verb",
41+
"Is clear and descriptive",
42+
"Here are the changes",
43+
}
44+
45+
for _, fragment := range requiredFragments {
46+
if !strings.Contains(CommitPrompt, fragment) {
47+
t.Fatalf("CommitPrompt missing fragment %q", fragment)
48+
}
49+
}
50+
}

0 commit comments

Comments
 (0)