package brief import ( "context" "os" "os/exec" "path/filepath" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/yan1h/agent-coding-workflow/internal/infra/git" ) func gitAvailable(t *testing.T) { t.Helper() if _, err := exec.LookPath("git"); err != nil { t.Skip("git not found in PATH") } } func mustGit(t *testing.T, dir string, args ...string) { t.Helper() c := exec.Command("git", args...) c.Dir = dir out, err := c.CombinedOutput() require.NoError(t, err, "git %v: %s", args, string(out)) } // makeWorkRepo 在临时目录里建一个有 N 次提交、可选脏文件的工作仓库,返回路径。 func makeWorkRepo(t *testing.T, commits int, dirty bool) string { t.Helper() gitAvailable(t) dir := t.TempDir() mustGit(t, dir, "init", "-b", "main") mustGit(t, dir, "config", "user.email", "test@example.com") mustGit(t, dir, "config", "user.name", "Test") for i := 0; i < commits; i++ { name := filepath.Join(dir, "f"+string(rune('a'+i))+".txt") require.NoError(t, os.WriteFile(name, []byte("v"), 0o644)) mustGit(t, dir, "add", ".") mustGit(t, dir, "commit", "-m", "commit "+string(rune('a'+i))) } if dirty { require.NoError(t, os.WriteFile(filepath.Join(dir, "dirty.txt"), []byte("x"), 0o644)) } return dir } func TestRepoOrienter_RealRepo(t *testing.T) { t.Parallel() dir := makeWorkRepo(t, 3, true) o := NewRepoOrienter(git.NewDefaultRunner(git.Config{Binary: "git", TmpDir: t.TempDir()})) got := o.Orient(context.Background(), dir, "main", 10, 10) assert.Equal(t, "main", got.Branch) assert.Len(t, got.RecentCommits, 3) // 脏文件含未跟踪的 dirty.txt。 require.NotEmpty(t, got.DirtyFiles) assert.Contains(t, got.DirtyFiles, "dirty.txt") } func TestRepoOrienter_BoundsCommits(t *testing.T) { t.Parallel() dir := makeWorkRepo(t, 5, false) o := NewRepoOrienter(git.NewDefaultRunner(git.Config{Binary: "git", TmpDir: t.TempDir()})) got := o.Orient(context.Background(), dir, "main", 2, 10) assert.LessOrEqual(t, len(got.RecentCommits), 2) } func TestRepoOrienter_NonexistentDirReturnsPartialNoFailure(t *testing.T) { t.Parallel() gitAvailable(t) o := NewRepoOrienter(git.NewDefaultRunner(git.Config{Binary: "git", TmpDir: t.TempDir()})) // 不存在的目录:git 调用失败,但 orienter 不 panic、不返回错误,分支仍带入。 got := o.Orient(context.Background(), filepath.Join(t.TempDir(), "nope"), "wip", 10, 10) assert.Equal(t, "wip", got.Branch) assert.Empty(t, got.RecentCommits) assert.Empty(t, got.DirtyFiles) } func TestRepoOrienter_EmptyDirSkips(t *testing.T) { t.Parallel() o := NewRepoOrienter(git.NewDefaultRunner(git.Config{Binary: "git", TmpDir: t.TempDir()})) got := o.Orient(context.Background(), "", "main", 10, 10) assert.Equal(t, "main", got.Branch) assert.Empty(t, got.RecentCommits) assert.Empty(t, got.DirtyFiles) } func TestRepoOrienter_NilGitClient(t *testing.T) { t.Parallel() o := NewRepoOrienter(nil) got := o.Orient(context.Background(), "/tmp/x", "main", 10, 10) assert.Equal(t, "main", got.Branch) assert.Empty(t, got.RecentCommits) } func TestRepoOrientation_IsEmpty(t *testing.T) { t.Parallel() assert.True(t, RepoOrientation{}.IsEmpty()) assert.False(t, RepoOrientation{Branch: "x"}.IsEmpty()) assert.False(t, RepoOrientation{RecentCommits: []string{"a"}}.IsEmpty()) }