go-git/service_test.go
Snider bf8e37d522 feat: modernise to Go 1.26 — iterators and slices stdlib
- Add All(), Dirty(), Ahead() iter.Seq[RepoStatus] iterators
- Reimplement DirtyRepos/AheadRepos via slices.Collect
- Use slices.Contains for git status character matching
- Refresh go.sum

Co-Authored-By: Gemini <noreply@google.com>
Co-Authored-By: Virgil <virgil@lethean.io>
2026-02-23 05:06:48 +00:00

186 lines
4.1 KiB
Go

package git
import (
"slices"
"testing"
"github.com/stretchr/testify/assert"
)
// --- Service helper method tests ---
// These test DirtyRepos/AheadRepos filtering without needing the framework.
func TestService_DirtyRepos_Good(t *testing.T) {
s := &Service{
lastStatus: []RepoStatus{
{Name: "clean", Modified: 0, Untracked: 0, Staged: 0},
{Name: "dirty-modified", Modified: 2},
{Name: "dirty-untracked", Untracked: 1},
{Name: "dirty-staged", Staged: 3},
{Name: "errored", Modified: 5, Error: assert.AnError},
},
}
dirty := s.DirtyRepos()
assert.Len(t, dirty, 3)
names := slices.Collect(func(yield func(string) bool) {
for _, d := range dirty {
if !yield(d.Name) {
return
}
}
})
assert.Contains(t, names, "dirty-modified")
assert.Contains(t, names, "dirty-untracked")
assert.Contains(t, names, "dirty-staged")
}
func TestService_DirtyRepos_Good_NoneFound(t *testing.T) {
s := &Service{
lastStatus: []RepoStatus{
{Name: "clean1"},
{Name: "clean2"},
},
}
dirty := s.DirtyRepos()
assert.Empty(t, dirty)
}
func TestService_DirtyRepos_Good_EmptyStatus(t *testing.T) {
s := &Service{}
dirty := s.DirtyRepos()
assert.Empty(t, dirty)
}
func TestService_AheadRepos_Good(t *testing.T) {
s := &Service{
lastStatus: []RepoStatus{
{Name: "up-to-date", Ahead: 0},
{Name: "ahead-by-one", Ahead: 1},
{Name: "ahead-by-five", Ahead: 5},
{Name: "behind-only", Behind: 3},
{Name: "errored-ahead", Ahead: 2, Error: assert.AnError},
},
}
ahead := s.AheadRepos()
assert.Len(t, ahead, 2)
names := slices.Collect(func(yield func(string) bool) {
for _, a := range ahead {
if !yield(a.Name) {
return
}
}
})
assert.Contains(t, names, "ahead-by-one")
assert.Contains(t, names, "ahead-by-five")
}
func TestService_AheadRepos_Good_NoneFound(t *testing.T) {
s := &Service{
lastStatus: []RepoStatus{
{Name: "synced1"},
{Name: "synced2"},
},
}
ahead := s.AheadRepos()
assert.Empty(t, ahead)
}
func TestService_AheadRepos_Good_EmptyStatus(t *testing.T) {
s := &Service{}
ahead := s.AheadRepos()
assert.Empty(t, ahead)
}
func TestService_Iterators_Good(t *testing.T) {
s := &Service{
lastStatus: []RepoStatus{
{Name: "clean"},
{Name: "dirty", Modified: 1},
{Name: "ahead", Ahead: 2},
},
}
// Test All()
all := slices.Collect(s.All())
assert.Len(t, all, 3)
// Test Dirty()
dirty := slices.Collect(s.Dirty())
assert.Len(t, dirty, 1)
assert.Equal(t, "dirty", dirty[0].Name)
// Test Ahead()
ahead := slices.Collect(s.Ahead())
assert.Len(t, ahead, 1)
assert.Equal(t, "ahead", ahead[0].Name)
}
func TestService_Status_Good(t *testing.T) {
expected := []RepoStatus{
{Name: "repo1", Branch: "main"},
{Name: "repo2", Branch: "develop"},
}
s := &Service{lastStatus: expected}
assert.Equal(t, expected, s.Status())
}
func TestService_Status_Good_NilSlice(t *testing.T) {
s := &Service{}
assert.Nil(t, s.Status())
}
// --- Query/Task type tests ---
func TestQueryStatus_MapsToStatusOptions(t *testing.T) {
q := QueryStatus{
Paths: []string{"/path/a", "/path/b"},
Names: map[string]string{"/path/a": "repo-a"},
}
// QueryStatus can be cast directly to StatusOptions.
opts := StatusOptions(q)
assert.Equal(t, q.Paths, opts.Paths)
assert.Equal(t, q.Names, opts.Names)
}
func TestServiceOptions_WorkDir(t *testing.T) {
opts := ServiceOptions{WorkDir: "/home/claude/repos"}
assert.Equal(t, "/home/claude/repos", opts.WorkDir)
}
// --- DirtyRepos excludes errored repos ---
func TestService_DirtyRepos_Good_ExcludesErrors(t *testing.T) {
s := &Service{
lastStatus: []RepoStatus{
{Name: "dirty-ok", Modified: 1},
{Name: "dirty-error", Modified: 1, Error: assert.AnError},
},
}
dirty := s.DirtyRepos()
assert.Len(t, dirty, 1)
assert.Equal(t, "dirty-ok", dirty[0].Name)
}
// --- AheadRepos excludes errored repos ---
func TestService_AheadRepos_Good_ExcludesErrors(t *testing.T) {
s := &Service{
lastStatus: []RepoStatus{
{Name: "ahead-ok", Ahead: 2},
{Name: "ahead-error", Ahead: 3, Error: assert.AnError},
},
}
ahead := s.AheadRepos()
assert.Len(t, ahead, 1)
assert.Equal(t, "ahead-ok", ahead[0].Name)
}