Files
2026-06-22 08:55:57 +08:00

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)
}