package tests import ( "context" "fmt" "os" "os/exec" "path/filepath" "testing" "github.com/stretchr/testify/assert" "github.com/rockliang/poimen/workflows/action" ) func TestGitCloneAndFetch(t *testing.T) { // Create a temporary directory for the test tmpDir := t.TempDir() sourceDir := filepath.Join(tmpDir, "source") targetDir := filepath.Join(tmpDir, "target") // Initialize source repo if err := os.MkdirAll(sourceDir, 0755); err != nil { t.Fatalf("failed to create source dir: %v", err) } cmd := exec.Command("git", "init", sourceDir) if err := cmd.Run(); err != nil { t.Fatalf("git init failed: %v", err) } // Configure git user exec.Command("git", "-C", sourceDir, "config", "user.email", "test@example.com").Run() exec.Command("git", "-C", sourceDir, "config", "user.name", "Test User").Run() // Create initial commit testFile := filepath.Join(sourceDir, "test.txt") if err := os.WriteFile(testFile, []byte("test content"), 0644); err != nil { t.Fatalf("failed to create test file: %v", err) } cmd = exec.Command("git", "-C", sourceDir, "add", "test.txt") if err := cmd.Run(); err != nil { t.Fatalf("git add failed: %v", err) } cmd = exec.Command("git", "-C", sourceDir, "commit", "-m", "initial commit") if err := cmd.Run(); err != nil { t.Fatalf("git commit failed: %v", err) } // Ensure we're on main branch (git 2.28+ defaults to main, older uses master) cmd = exec.Command("git", "-C", sourceDir, "branch", "-M", "main") if err := cmd.Run(); err != nil { t.Fatalf("git branch -M main failed: %v", err) } // Test clone into empty path ctx := context.Background() err := action.CloneRepoActivity(ctx, action.CloneRepoInput{ RemoteURL: sourceDir, TargetRepoPath: targetDir, }) assert.NoError(t, err, "clone should succeed") // Verify .git exists gitDir := filepath.Join(targetDir, ".git") _, err = os.Stat(gitDir) assert.NoError(t, err, ".git directory should exist") // Verify test.txt was cloned targetTestFile := filepath.Join(targetDir, "test.txt") _, err = os.Stat(targetTestFile) assert.NoError(t, err, "test.txt should exist in target") // Add another commit to source testFile2 := filepath.Join(sourceDir, "test2.txt") if err := os.WriteFile(testFile2, []byte("test content 2"), 0644); err != nil { t.Fatalf("failed to create test2 file: %v", err) } cmd = exec.Command("git", "-C", sourceDir, "add", "test2.txt") if err := cmd.Run(); err != nil { t.Fatalf("git add failed: %v", err) } cmd = exec.Command("git", "-C", sourceDir, "commit", "-m", "second commit") if err := cmd.Run(); err != nil { t.Fatalf("git commit failed: %v", err) } // Test fetch on existing repo err = action.CloneRepoActivity(ctx, action.CloneRepoInput{ RemoteURL: sourceDir, TargetRepoPath: targetDir, }) assert.NoError(t, err, "fetch should succeed") } func TestGitWorktreeAdd(t *testing.T) { tmpDir := t.TempDir() sourceDir := filepath.Join(tmpDir, "source") repoDir := filepath.Join(tmpDir, "repo") // Initialize source repo if err := os.MkdirAll(sourceDir, 0755); err != nil { t.Fatalf("failed to create source dir: %v", err) } cmd := exec.Command("git", "init", sourceDir) if err := cmd.Run(); err != nil { t.Fatalf("git init failed: %v", err) } // Configure git user exec.Command("git", "-C", sourceDir, "config", "user.email", "test@example.com").Run() exec.Command("git", "-C", sourceDir, "config", "user.name", "Test User").Run() // Create initial commit testFile := filepath.Join(sourceDir, "test.txt") if err := os.WriteFile(testFile, []byte("test"), 0644); err != nil { t.Fatalf("failed to create test file: %v", err) } cmd = exec.Command("git", "-C", sourceDir, "add", "test.txt") if err := cmd.Run(); err != nil { t.Fatalf("git add failed: %v", err) } cmd = exec.Command("git", "-C", sourceDir, "commit", "-m", "initial") if err := cmd.Run(); err != nil { t.Fatalf("git commit failed: %v", err) } // Ensure we're on main branch (git 2.28+ defaults to main, older uses master) cmd = exec.Command("git", "-C", sourceDir, "branch", "-M", "main") if err := cmd.Run(); err != nil { t.Fatalf("git branch -M main failed: %v", err) } // Clone the repo ctx := context.Background() err := action.CloneRepoActivity(ctx, action.CloneRepoInput{ RemoteURL: sourceDir, TargetRepoPath: repoDir, }) assert.NoError(t, err, "clone should succeed") // Test worktree add worktreePath, err := action.GitWorktreeAddActivity(ctx, action.GitWorktreeAddInput{ RepoPath: repoDir, TaskID: "T0.1", }) assert.NoError(t, err, "worktree add should succeed") assert.NotEmpty(t, worktreePath, "worktree path should not be empty") // Verify worktree directory exists _, err = os.Stat(worktreePath) assert.NoError(t, err, "worktree directory should exist") // Verify branch exists cmd = exec.Command("git", "-C", repoDir, "branch", "--list", "task/T0.1") output, err := cmd.CombinedOutput() assert.NoError(t, err, "git branch check should succeed") assert.Contains(t, string(output), "task/T0.1", "task/T0.1 branch should exist") } func TestGitCommit(t *testing.T) { tmpDir := t.TempDir() sourceDir := filepath.Join(tmpDir, "source") repoDir := filepath.Join(tmpDir, "repo") // Initialize source repo if err := os.MkdirAll(sourceDir, 0755); err != nil { t.Fatalf("failed to create source dir: %v", err) } cmd := exec.Command("git", "init", sourceDir) if err := cmd.Run(); err != nil { t.Fatalf("git init failed: %v", err) } // Configure git user exec.Command("git", "-C", sourceDir, "config", "user.email", "test@example.com").Run() exec.Command("git", "-C", sourceDir, "config", "user.name", "Test User").Run() // Create initial commit testFile := filepath.Join(sourceDir, "test.txt") if err := os.WriteFile(testFile, []byte("test"), 0644); err != nil { t.Fatalf("failed to create test file: %v", err) } cmd = exec.Command("git", "-C", sourceDir, "add", "test.txt") if err := cmd.Run(); err != nil { t.Fatalf("git add failed: %v", err) } cmd = exec.Command("git", "-C", sourceDir, "commit", "-m", "initial") if err := cmd.Run(); err != nil { t.Fatalf("git commit failed: %v", err) } // Ensure we're on main branch (git 2.28+ defaults to main, older uses master) cmd = exec.Command("git", "-C", sourceDir, "branch", "-M", "main") if err := cmd.Run(); err != nil { t.Fatalf("git branch -M main failed: %v", err) } // Clone the repo ctx := context.Background() err := action.CloneRepoActivity(ctx, action.CloneRepoInput{ RemoteURL: sourceDir, TargetRepoPath: repoDir, }) assert.NoError(t, err, "clone should succeed") // Create a worktree worktreePath, err := action.GitWorktreeAddActivity(ctx, action.GitWorktreeAddInput{ RepoPath: repoDir, TaskID: "T0.1", }) assert.NoError(t, err) // Create a new file in the worktree newFile := filepath.Join(worktreePath, "new.txt") if err := os.WriteFile(newFile, []byte("new content"), 0644); err != nil { t.Fatalf("failed to create new file: %v", err) } // Commit changes err = action.GitCommitActivity(ctx, action.GitCommitInput{ WorktreePath: worktreePath, Message: "Add new file", }) assert.NoError(t, err, "commit should succeed") // Verify commit exists cmd = exec.Command("git", "-C", worktreePath, "log", "--oneline") output, err := cmd.CombinedOutput() assert.NoError(t, err) assert.Contains(t, string(output), "Add new file", "commit message should be in log") } func TestGitDiff(t *testing.T) { tmpDir := t.TempDir() sourceDir := filepath.Join(tmpDir, "source") repoDir := filepath.Join(tmpDir, "repo") // Initialize source repo if err := os.MkdirAll(sourceDir, 0755); err != nil { t.Fatalf("failed to create source dir: %v", err) } cmd := exec.Command("git", "init", sourceDir) if err := cmd.Run(); err != nil { t.Fatalf("git init failed: %v", err) } // Configure git user exec.Command("git", "-C", sourceDir, "config", "user.email", "test@example.com").Run() exec.Command("git", "-C", sourceDir, "config", "user.name", "Test User").Run() // Create initial commit testFile := filepath.Join(sourceDir, "test.txt") if err := os.WriteFile(testFile, []byte("test"), 0644); err != nil { t.Fatalf("failed to create test file: %v", err) } cmd = exec.Command("git", "-C", sourceDir, "add", "test.txt") if err := cmd.Run(); err != nil { t.Fatalf("git add failed: %v", err) } cmd = exec.Command("git", "-C", sourceDir, "commit", "-m", "initial") if err := cmd.Run(); err != nil { t.Fatalf("git commit failed: %v", err) } // Ensure we're on main branch cmd = exec.Command("git", "-C", sourceDir, "branch", "-M", "main") if err := cmd.Run(); err != nil { t.Fatalf("git branch -M main failed: %v", err) } // Clone the repo ctx := context.Background() err := action.CloneRepoActivity(ctx, action.CloneRepoInput{ RemoteURL: sourceDir, TargetRepoPath: repoDir, }) assert.NoError(t, err, "clone should succeed") // Create a worktree worktreePath, err := action.GitWorktreeAddActivity(ctx, action.GitWorktreeAddInput{ RepoPath: repoDir, TaskID: "T0.1", }) assert.NoError(t, err) // Create a new file in the worktree newFile := filepath.Join(worktreePath, "changes.txt") if err := os.WriteFile(newFile, []byte("changed content"), 0644); err != nil { t.Fatalf("failed to create new file: %v", err) } // Stage and commit the change cmd = exec.Command("git", "-C", worktreePath, "add", "changes.txt") if err := cmd.Run(); err != nil { t.Fatalf("git add failed: %v", err) } // Get diff (should show the staged change) diffOutput, err := action.GitDiffActivity(ctx, action.GitDiffInput{ WorktreePath: worktreePath, }) assert.NoError(t, err, "diff should succeed") // Diff against main - since we added a new file on task branch, diff should show it // even if it's empty, just verify the activity works _ = diffOutput // The diff might be empty in test, that's ok } func TestGitSquashMerge(t *testing.T) { if testing.Short() { t.Skip("skipping SquashMerge test: requires multiple branches") } tmpDir := t.TempDir() sourceDir := filepath.Join(tmpDir, "source") repoDir := filepath.Join(tmpDir, "repo") // Initialize source repo with bare=false (allow pushing to this repo) if err := os.MkdirAll(sourceDir, 0755); err != nil { t.Fatalf("failed to create source dir: %v", err) } cmd := exec.Command("git", "init", "--bare", sourceDir) if err := cmd.Run(); err != nil { t.Fatalf("git init --bare failed: %v", err) } // Clone from the bare repo to a working dir to set up initial commit workingDir := filepath.Join(tmpDir, "working") cmd = exec.Command("git", "clone", sourceDir, workingDir) if err := cmd.Run(); err != nil { t.Fatalf("git clone failed: %v", err) } // Configure git user exec.Command("git", "-C", workingDir, "config", "user.email", "test@example.com").Run() exec.Command("git", "-C", workingDir, "config", "user.name", "Test User").Run() // Create initial commit testFile := filepath.Join(workingDir, "test.txt") if err := os.WriteFile(testFile, []byte("initial"), 0644); err != nil { t.Fatalf("failed to create test file: %v", err) } cmd = exec.Command("git", "-C", workingDir, "add", "test.txt") if err := cmd.Run(); err != nil { t.Fatalf("git add failed: %v", err) } cmd = exec.Command("git", "-C", workingDir, "commit", "-m", "initial") if err := cmd.Run(); err != nil { t.Fatalf("git commit failed: %v", err) } // Ensure we're on main branch cmd = exec.Command("git", "-C", workingDir, "branch", "-M", "main") if err := cmd.Run(); err != nil { t.Fatalf("git branch -M main failed: %v", err) } // Push to bare repo cmd = exec.Command("git", "-C", workingDir, "push", "-u", "origin", "main") if err := cmd.Run(); err != nil { t.Fatalf("git push failed: %v", err) } // Clone for the orchestrator to use ctx := context.Background() err := action.CloneRepoActivity(ctx, action.CloneRepoInput{ RemoteURL: sourceDir, TargetRepoPath: repoDir, }) assert.NoError(t, err, "clone should succeed") // Create multiple worktrees with changes for i := 1; i <= 2; i++ { taskID := fmt.Sprintf("T0.%d", i) worktreePath, err := action.GitWorktreeAddActivity(ctx, action.GitWorktreeAddInput{ RepoPath: repoDir, TaskID: taskID, }) assert.NoError(t, err, "worktree add should succeed") // Create a file in the worktree newFile := filepath.Join(worktreePath, fmt.Sprintf("file%d.txt", i)) if err := os.WriteFile(newFile, []byte(fmt.Sprintf("content %d", i)), 0644); err != nil { t.Fatalf("failed to create file: %v", err) } // Commit changes err = action.GitCommitActivity(ctx, action.GitCommitInput{ WorktreePath: worktreePath, Message: fmt.Sprintf("Task %s implementation", taskID), }) assert.NoError(t, err, "commit should succeed") } // Perform squash merge err = action.GitSquashMergeActivity(ctx, action.GitSquashMergeInput{ RepoPath: repoDir, Branches: []string{"task/T0.1", "task/T0.2"}, Message: "Milestone T0: completed all tasks", }) assert.NoError(t, err, "squash merge should succeed") // Verify main branch has the merged content for i := 1; i <= 2; i++ { filePath := filepath.Join(repoDir, fmt.Sprintf("file%d.txt", i)) _, err := os.Stat(filePath) assert.NoError(t, err, "file from task should exist in main branch") } }