You've already forked agentic-coding-workflow
126 lines
3.5 KiB
Go
126 lines
3.5 KiB
Go
package git
|
|
|
|
import (
|
|
"context"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestBuildDiffArgs(t *testing.T) {
|
|
t.Parallel()
|
|
cases := []struct {
|
|
name string
|
|
opts DiffOptions
|
|
want []string
|
|
}{
|
|
{
|
|
name: "working tree vs HEAD",
|
|
opts: DiffOptions{},
|
|
want: []string{"diff", "--no-color"},
|
|
},
|
|
{
|
|
name: "staged",
|
|
opts: DiffOptions{Staged: true},
|
|
want: []string{"diff", "--no-color", "--cached"},
|
|
},
|
|
{
|
|
name: "single ref",
|
|
opts: DiffOptions{Ref: "HEAD~1"},
|
|
want: []string{"diff", "--no-color", "HEAD~1"},
|
|
},
|
|
{
|
|
name: "ref range",
|
|
opts: DiffOptions{Ref: "main", Against: "feature"},
|
|
want: []string{"diff", "--no-color", "main..feature"},
|
|
},
|
|
{
|
|
name: "context lines + paths",
|
|
opts: DiffOptions{ContextLines: 5, Paths: []string{"a.txt", "dir/b.go"}},
|
|
want: []string{"diff", "--no-color", "-U5", "--", "a.txt", "dir/b.go"},
|
|
},
|
|
{
|
|
name: "staged with ref range and paths",
|
|
opts: DiffOptions{Staged: true, Ref: "main", Against: "dev", Paths: []string{"x"}},
|
|
want: []string{"diff", "--no-color", "--cached", "main..dev", "--", "x"},
|
|
},
|
|
}
|
|
for _, tc := range cases {
|
|
tc := tc
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
require.Equal(t, tc.want, buildDiffArgs(tc.opts))
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestDiff_WorkingTreeAndStaged(t *testing.T) {
|
|
t.Parallel()
|
|
gitAvailable(t)
|
|
bare := makeBareRepo(t)
|
|
r := newTestRunner(t)
|
|
dst := filepath.Join(t.TempDir(), "wc")
|
|
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
|
defer cancel()
|
|
require.NoError(t, r.Clone(ctx, dst, bare, "main", nil))
|
|
|
|
// clean tree => empty diff
|
|
res, err := r.Diff(ctx, dst, DiffOptions{})
|
|
require.NoError(t, err)
|
|
require.Empty(t, res.Diff)
|
|
require.False(t, res.Truncated)
|
|
|
|
// modify a tracked file => unified diff against working tree
|
|
require.NoError(t, os.WriteFile(filepath.Join(dst, "README.md"), []byte("hello\nworld\n"), 0o644))
|
|
res, err = r.Diff(ctx, dst, DiffOptions{})
|
|
require.NoError(t, err)
|
|
require.Contains(t, res.Diff, "diff --git")
|
|
require.Contains(t, res.Diff, "+world")
|
|
|
|
// stage it; working-tree diff now empty, --cached shows the change
|
|
mustGit(t, dst, "add", "README.md")
|
|
res, err = r.Diff(ctx, dst, DiffOptions{})
|
|
require.NoError(t, err)
|
|
require.Empty(t, res.Diff)
|
|
|
|
res, err = r.Diff(ctx, dst, DiffOptions{Staged: true})
|
|
require.NoError(t, err)
|
|
require.Contains(t, res.Diff, "+world")
|
|
}
|
|
|
|
func TestDiff_PathsFilter(t *testing.T) {
|
|
t.Parallel()
|
|
gitAvailable(t)
|
|
bare := makeBareRepo(t)
|
|
r := newTestRunner(t)
|
|
dst := filepath.Join(t.TempDir(), "wc")
|
|
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
|
defer cancel()
|
|
require.NoError(t, r.Clone(ctx, dst, bare, "main", nil))
|
|
|
|
require.NoError(t, os.WriteFile(filepath.Join(dst, "README.md"), []byte("changed\n"), 0o644))
|
|
require.NoError(t, os.WriteFile(filepath.Join(dst, "other.txt"), []byte("new\n"), 0o644))
|
|
mustGit(t, dst, "add", "other.txt")
|
|
|
|
// restrict to README only => other.txt not in output
|
|
res, err := r.Diff(ctx, dst, DiffOptions{Paths: []string{"README.md"}})
|
|
require.NoError(t, err)
|
|
require.Contains(t, res.Diff, "README.md")
|
|
require.False(t, strings.Contains(res.Diff, "other.txt"))
|
|
}
|
|
|
|
func TestDiff_ErrorPropagation(t *testing.T) {
|
|
t.Parallel()
|
|
gitAvailable(t)
|
|
r := newTestRunner(t)
|
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
|
defer cancel()
|
|
// not a git repo => git diff fails, error propagates
|
|
_, err := r.Diff(ctx, t.TempDir(), DiffOptions{})
|
|
require.Error(t, err)
|
|
}
|