mirror of
https://github.com/taigrr/gico.git
synced 2026-04-14 09:10:46 -07:00
test(commits): add unit tests for commits and cache packages
Add tests for CommitSet filtering (by year, author regex), ToYearFreq, channel-based frequency functions, cache round-trip operations, and hash determinism. Also includes goimports formatting fix in common_test.go.
This commit is contained in:
99
commits/cache_test.go
Normal file
99
commits/cache_test.go
Normal file
@@ -0,0 +1,99 @@
|
||||
package commits
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/taigrr/gico/types"
|
||||
)
|
||||
|
||||
func TestHashSliceDeterministic(t *testing.T) {
|
||||
a := hashSlice([]string{"foo", "bar", "baz"})
|
||||
b := hashSlice([]string{"baz", "foo", "bar"})
|
||||
if a != b {
|
||||
t.Error("hashSlice should be order-independent")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHashSliceDifferentInputs(t *testing.T) {
|
||||
a := hashSlice([]string{"foo", "bar"})
|
||||
b := hashSlice([]string{"foo", "baz"})
|
||||
if a == b {
|
||||
t.Error("different inputs should produce different hashes")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCacheRepoRoundTrip(t *testing.T) {
|
||||
path := "/test/cache-repo"
|
||||
head := "deadbeef123"
|
||||
commits := []types.Commit{
|
||||
{Hash: head, Author: types.Author{Name: "test"}, TimeStamp: time.Now()},
|
||||
}
|
||||
|
||||
// Should miss before caching
|
||||
_, ok := GetCachedRepo(path, head)
|
||||
if ok {
|
||||
t.Error("expected cache miss before storing")
|
||||
}
|
||||
|
||||
CacheRepo(path, commits)
|
||||
|
||||
// Should hit after caching
|
||||
cached, ok := GetCachedRepo(path, head)
|
||||
if !ok {
|
||||
t.Error("expected cache hit after storing")
|
||||
}
|
||||
if len(cached) != 1 || cached[0].Hash != head {
|
||||
t.Error("cached data doesn't match stored data")
|
||||
}
|
||||
|
||||
// Different head should miss
|
||||
_, ok = GetCachedRepo(path, "differenthead")
|
||||
if ok {
|
||||
t.Error("expected cache miss for different head")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCacheGraphRoundTrip(t *testing.T) {
|
||||
year := 2025
|
||||
authors := []string{"alice@example.com"}
|
||||
paths := []string{"/repo/one", "/repo/two"}
|
||||
freq := types.Freq{1, 2, 3, 0, 0}
|
||||
|
||||
// Should miss before caching
|
||||
_, ok := GetCachedGraph(year, authors, paths)
|
||||
if ok {
|
||||
t.Error("expected cache miss before storing")
|
||||
}
|
||||
|
||||
CacheGraph(year, authors, paths, freq)
|
||||
|
||||
// Should hit
|
||||
cached, ok := GetCachedGraph(year, authors, paths)
|
||||
if !ok {
|
||||
t.Error("expected cache hit")
|
||||
}
|
||||
if len(cached) != len(freq) {
|
||||
t.Errorf("expected freq length %d, got %d", len(freq), len(cached))
|
||||
}
|
||||
}
|
||||
|
||||
func TestCacheReposAuthorsRoundTrip(t *testing.T) {
|
||||
paths := []string{"/repo/author-test"}
|
||||
authors := []string{"alice", "bob"}
|
||||
|
||||
_, ok := GetCachedReposAuthors(paths)
|
||||
if ok {
|
||||
t.Error("expected cache miss")
|
||||
}
|
||||
|
||||
CacheReposAuthors(paths, authors)
|
||||
|
||||
cached, ok := GetCachedReposAuthors(paths)
|
||||
if !ok {
|
||||
t.Error("expected cache hit")
|
||||
}
|
||||
if len(cached) != 2 {
|
||||
t.Errorf("expected 2 authors, got %d", len(cached))
|
||||
}
|
||||
}
|
||||
212
commits/commits_test.go
Normal file
212
commits/commits_test.go
Normal file
@@ -0,0 +1,212 @@
|
||||
package commits
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/taigrr/gico/types"
|
||||
)
|
||||
|
||||
func makeCommit(name, email string, ts time.Time) types.Commit {
|
||||
return types.Commit{
|
||||
Author: types.Author{Name: name, Email: email},
|
||||
TimeStamp: ts,
|
||||
Hash: "abc123",
|
||||
Repo: "/test/repo",
|
||||
}
|
||||
}
|
||||
|
||||
func TestCommitSetFilterByYear(t *testing.T) {
|
||||
cs := CommitSet{
|
||||
Commits: []types.Commit{
|
||||
makeCommit("a", "a@x.com", time.Date(2024, 3, 15, 10, 0, 0, 0, time.UTC)),
|
||||
makeCommit("b", "b@x.com", time.Date(2025, 6, 1, 12, 0, 0, 0, time.UTC)),
|
||||
makeCommit("c", "c@x.com", time.Date(2024, 12, 31, 23, 59, 0, 0, time.UTC)),
|
||||
makeCommit("d", "d@x.com", time.Date(2023, 1, 1, 0, 0, 0, 0, time.UTC)),
|
||||
},
|
||||
}
|
||||
|
||||
filtered := cs.FilterByYear(2024)
|
||||
if filtered.Year != 2024 {
|
||||
t.Errorf("expected Year=2024, got %d", filtered.Year)
|
||||
}
|
||||
if len(filtered.Commits) != 2 {
|
||||
t.Errorf("expected 2 commits for 2024, got %d", len(filtered.Commits))
|
||||
}
|
||||
|
||||
filtered = cs.FilterByYear(2025)
|
||||
if len(filtered.Commits) != 1 {
|
||||
t.Errorf("expected 1 commit for 2025, got %d", len(filtered.Commits))
|
||||
}
|
||||
|
||||
filtered = cs.FilterByYear(2000)
|
||||
if len(filtered.Commits) != 0 {
|
||||
t.Errorf("expected 0 commits for 2000, got %d", len(filtered.Commits))
|
||||
}
|
||||
}
|
||||
|
||||
func TestCommitSetFilterByAuthorRegex(t *testing.T) {
|
||||
cs := CommitSet{
|
||||
Commits: []types.Commit{
|
||||
makeCommit("Alice", "alice@example.com", time.Now()),
|
||||
makeCommit("Bob", "bob@example.com", time.Now()),
|
||||
makeCommit("Charlie", "charlie@example.com", time.Now()),
|
||||
},
|
||||
}
|
||||
|
||||
// Filter by name regex
|
||||
filtered, err := cs.FilterByAuthorRegex([]string{"^Ali"})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if len(filtered.Commits) != 1 {
|
||||
t.Errorf("expected 1 commit matching ^Ali, got %d", len(filtered.Commits))
|
||||
}
|
||||
|
||||
// Filter by email regex
|
||||
filtered, err = cs.FilterByAuthorRegex([]string{"bob@"})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if len(filtered.Commits) != 1 {
|
||||
t.Errorf("expected 1 commit matching bob@, got %d", len(filtered.Commits))
|
||||
}
|
||||
|
||||
// Multiple patterns
|
||||
filtered, err = cs.FilterByAuthorRegex([]string{"Alice", "Charlie"})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if len(filtered.Commits) != 2 {
|
||||
t.Errorf("expected 2 commits matching Alice|Charlie, got %d", len(filtered.Commits))
|
||||
}
|
||||
|
||||
// Invalid regex
|
||||
_, err = cs.FilterByAuthorRegex([]string{"[invalid"})
|
||||
if err == nil {
|
||||
t.Error("expected error for invalid regex, got nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCommitSetToYearFreq(t *testing.T) {
|
||||
// Regular year (2025)
|
||||
cs := CommitSet{
|
||||
Year: 2025,
|
||||
Commits: []types.Commit{
|
||||
makeCommit("a", "a@x.com", time.Date(2025, 1, 1, 10, 0, 0, 0, time.UTC)),
|
||||
makeCommit("a", "a@x.com", time.Date(2025, 1, 1, 14, 0, 0, 0, time.UTC)),
|
||||
makeCommit("b", "b@x.com", time.Date(2025, 3, 15, 12, 0, 0, 0, time.UTC)),
|
||||
},
|
||||
}
|
||||
|
||||
freq := cs.ToYearFreq()
|
||||
if len(freq) != 365 {
|
||||
t.Errorf("expected 365 days for 2025, got %d", len(freq))
|
||||
}
|
||||
if freq[0] != 2 {
|
||||
t.Errorf("expected 2 commits on Jan 1, got %d", freq[0])
|
||||
}
|
||||
// March 15 = day 74 (31+28+15=74, index 73)
|
||||
if freq[73] != 1 {
|
||||
t.Errorf("expected 1 commit on Mar 15, got %d", freq[73])
|
||||
}
|
||||
|
||||
// Leap year (2024)
|
||||
csLeap := CommitSet{
|
||||
Year: 2024,
|
||||
Commits: []types.Commit{
|
||||
makeCommit("a", "a@x.com", time.Date(2024, 12, 31, 10, 0, 0, 0, time.UTC)),
|
||||
},
|
||||
}
|
||||
freqLeap := csLeap.ToYearFreq()
|
||||
if len(freqLeap) != 366 {
|
||||
t.Errorf("expected 366 days for 2024, got %d", len(freqLeap))
|
||||
}
|
||||
}
|
||||
|
||||
func TestYearFreqFromChan(t *testing.T) {
|
||||
cc := make(chan types.Commit, 5)
|
||||
cc <- makeCommit("a", "a@x.com", time.Date(2025, 1, 1, 10, 0, 0, 0, time.UTC))
|
||||
cc <- makeCommit("a", "a@x.com", time.Date(2025, 1, 1, 14, 0, 0, 0, time.UTC))
|
||||
cc <- makeCommit("b", "b@x.com", time.Date(2025, 6, 15, 12, 0, 0, 0, time.UTC))
|
||||
close(cc)
|
||||
|
||||
freq := YearFreqFromChan(cc, 2025)
|
||||
if len(freq) != 365 {
|
||||
t.Errorf("expected 365, got %d", len(freq))
|
||||
}
|
||||
if freq[0] != 2 {
|
||||
t.Errorf("expected 2 on Jan 1, got %d", freq[0])
|
||||
}
|
||||
}
|
||||
|
||||
func TestFreqFromChan(t *testing.T) {
|
||||
cc := make(chan types.Commit, 5)
|
||||
cc <- makeCommit("a", "a@x.com", time.Date(2025, 1, 1, 10, 0, 0, 0, time.UTC))
|
||||
cc <- makeCommit("b", "b@x.com", time.Date(2024, 6, 15, 12, 0, 0, 0, time.UTC)) // wrong year
|
||||
cc <- makeCommit("c", "c@x.com", time.Date(2025, 12, 31, 23, 0, 0, 0, time.UTC))
|
||||
close(cc)
|
||||
|
||||
freq := FreqFromChan(cc, 2025)
|
||||
if len(freq) != 365 {
|
||||
t.Errorf("expected 365, got %d", len(freq))
|
||||
}
|
||||
if freq[0] != 1 {
|
||||
t.Errorf("expected 1 on Jan 1 (skip wrong year), got %d", freq[0])
|
||||
}
|
||||
if freq[364] != 1 {
|
||||
t.Errorf("expected 1 on Dec 31, got %d", freq[364])
|
||||
}
|
||||
}
|
||||
|
||||
func TestFilterCChanByYear(t *testing.T) {
|
||||
in := make(chan types.Commit, 5)
|
||||
in <- makeCommit("a", "a@x.com", time.Date(2025, 3, 1, 10, 0, 0, 0, time.UTC))
|
||||
in <- makeCommit("b", "b@x.com", time.Date(2024, 3, 1, 10, 0, 0, 0, time.UTC))
|
||||
in <- makeCommit("c", "c@x.com", time.Date(2025, 7, 4, 10, 0, 0, 0, time.UTC))
|
||||
close(in)
|
||||
|
||||
out := FilterCChanByYear(in, 2025)
|
||||
count := 0
|
||||
for range out {
|
||||
count++
|
||||
}
|
||||
if count != 2 {
|
||||
t.Errorf("expected 2 commits for 2025, got %d", count)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFilterCChanByAuthor(t *testing.T) {
|
||||
in := make(chan types.Commit, 5)
|
||||
in <- makeCommit("Alice", "alice@x.com", time.Now())
|
||||
in <- makeCommit("Bob", "bob@x.com", time.Now())
|
||||
in <- makeCommit("Charlie", "charlie@x.com", time.Now())
|
||||
close(in)
|
||||
|
||||
out, err := FilterCChanByAuthor(in, []string{"Alice", "Charlie"})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
count := 0
|
||||
for range out {
|
||||
count++
|
||||
}
|
||||
if count != 2 {
|
||||
t.Errorf("expected 2 commits, got %d", count)
|
||||
}
|
||||
}
|
||||
|
||||
func TestOpenRepoNonExistent(t *testing.T) {
|
||||
_, err := OpenRepo("/nonexistent/path")
|
||||
if err == nil {
|
||||
t.Error("expected error for nonexistent path")
|
||||
}
|
||||
}
|
||||
|
||||
func TestOpenRepoNonDirectory(t *testing.T) {
|
||||
// Use a known file (not a directory)
|
||||
_, err := OpenRepo("/dev/null")
|
||||
if err == nil {
|
||||
t.Error("expected error for non-directory path")
|
||||
}
|
||||
}
|
||||
@@ -8,10 +8,10 @@ import (
|
||||
|
||||
func TestMinMax(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input []int
|
||||
wantMin int
|
||||
wantMax int
|
||||
name string
|
||||
input []int
|
||||
wantMin int
|
||||
wantMax int
|
||||
}{
|
||||
{
|
||||
name: "normal values",
|
||||
|
||||
Reference in New Issue
Block a user