246 lines
7.3 KiB
Go
246 lines
7.3 KiB
Go
package git_test
|
|
|
|
import (
|
|
"context"
|
|
"os"
|
|
"path/filepath"
|
|
"strconv"
|
|
"strings"
|
|
"testing"
|
|
|
|
"superwork-tui/internal/git"
|
|
)
|
|
|
|
func makeHookCtx(workspaceRoot, worktreePath, branch string, issue int, main string) git.HookContext {
|
|
return git.HookContext{
|
|
WorkspaceRoot: workspaceRoot,
|
|
WorktreePath: worktreePath,
|
|
Branch: branch,
|
|
IssueNumber: issue,
|
|
MainBranch: main,
|
|
}
|
|
}
|
|
|
|
func writeScript(t *testing.T, dir, name, content string) string {
|
|
t.Helper()
|
|
p := filepath.Join(dir, name)
|
|
if err := os.WriteFile(p, []byte(content), 0o755); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return p
|
|
}
|
|
|
|
// setupWorkspace returns workspaceRoot with .spx/ created.
|
|
func setupWorkspace(t *testing.T) (workspaceRoot, spxDir string) {
|
|
t.Helper()
|
|
workspaceRoot = t.TempDir()
|
|
spxDir = filepath.Join(workspaceRoot, ".spx")
|
|
if err := os.MkdirAll(spxDir, 0o755); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return
|
|
}
|
|
|
|
// --- RunPostCreateHook ---
|
|
|
|
func TestRunPostCreateHook_Missing(t *testing.T) {
|
|
root, _ := setupWorkspace(t)
|
|
ctx := makeHookCtx(root, root, "feat/x", 42, "main")
|
|
// No script on disk → skipped.
|
|
result := git.RunPostCreateHook(context.Background(), ctx)
|
|
if result.Status != git.HookStatusSkipped {
|
|
t.Errorf("want skipped, got %v", result.Status)
|
|
}
|
|
}
|
|
|
|
func TestRunPostCreateHook_Ok(t *testing.T) {
|
|
root, spxDir := setupWorkspace(t)
|
|
worktree := t.TempDir()
|
|
|
|
// Script writes env vars to a file so we can assert them.
|
|
envFile := filepath.Join(root, "env-dump.txt")
|
|
script := `#!/bin/bash
|
|
echo "WORKTREE_PATH=$WORKTREE_PATH" >> "` + envFile + `"
|
|
echo "WORKSPACE_ROOT=$WORKSPACE_ROOT" >> "` + envFile + `"
|
|
echo "BRANCH=$BRANCH" >> "` + envFile + `"
|
|
echo "ISSUE_NUMBER=$ISSUE_NUMBER" >> "` + envFile + `"
|
|
echo "MAIN_BRANCH=$MAIN_BRANCH" >> "` + envFile + `"
|
|
exit 0
|
|
`
|
|
writeScript(t, spxDir, "worktree-post-create.sh", script)
|
|
|
|
ctx := makeHookCtx(root, worktree, "feat/my-branch", 99, "develop")
|
|
result := git.RunPostCreateHook(context.Background(), ctx)
|
|
|
|
if result.Status != git.HookStatusOk {
|
|
t.Fatalf("want ok, got %v (err=%s stderr=%s)", result.Status, result.ErrorMessage, result.Stderr)
|
|
}
|
|
if result.ExitCode != 0 {
|
|
t.Errorf("want exit code 0, got %d", result.ExitCode)
|
|
}
|
|
if result.ScriptPath == "" {
|
|
t.Error("ScriptPath must not be empty")
|
|
}
|
|
|
|
envData, err := os.ReadFile(envFile)
|
|
if err != nil {
|
|
t.Fatalf("env dump not written: %v", err)
|
|
}
|
|
env := string(envData)
|
|
checks := map[string]string{
|
|
"WORKTREE_PATH": worktree,
|
|
"WORKSPACE_ROOT": root,
|
|
"BRANCH": "feat/my-branch",
|
|
"ISSUE_NUMBER": "99",
|
|
"MAIN_BRANCH": "develop",
|
|
}
|
|
for k, v := range checks {
|
|
if !strings.Contains(env, k+"="+v) {
|
|
t.Errorf("env missing %s=%s; got:\n%s", k, v, env)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestRunPostCreateHook_Failed(t *testing.T) {
|
|
root, spxDir := setupWorkspace(t)
|
|
writeScript(t, spxDir, "worktree-post-create.sh", "#!/bin/bash\nexit 3\n")
|
|
|
|
ctx := makeHookCtx(root, root, "feat/x", 1, "main")
|
|
result := git.RunPostCreateHook(context.Background(), ctx)
|
|
|
|
if result.Status != git.HookStatusFailed {
|
|
t.Errorf("want failed, got %v", result.Status)
|
|
}
|
|
if result.ExitCode != 3 {
|
|
t.Errorf("want ExitCode=3, got %d", result.ExitCode)
|
|
}
|
|
}
|
|
|
|
// --- RunPreRemoveHook ---
|
|
|
|
func TestRunPreRemoveHook_Missing(t *testing.T) {
|
|
root, _ := setupWorkspace(t)
|
|
ctx := makeHookCtx(root, root, "feat/x", 1, "main")
|
|
result := git.RunPreRemoveHook(context.Background(), ctx)
|
|
if result.Status != git.HookStatusSkipped {
|
|
t.Errorf("want skipped, got %v", result.Status)
|
|
}
|
|
}
|
|
|
|
func TestRunPreRemoveHook_Ok(t *testing.T) {
|
|
root, spxDir := setupWorkspace(t)
|
|
writeScript(t, spxDir, "worktree-pre-remove.sh", "#!/bin/bash\nexit 0\n")
|
|
ctx := makeHookCtx(root, root, "feat/x", 1, "main")
|
|
result := git.RunPreRemoveHook(context.Background(), ctx)
|
|
if result.Status != git.HookStatusOk {
|
|
t.Errorf("want ok, got %v", result.Status)
|
|
}
|
|
}
|
|
|
|
// --- RunImplTabPreCreateHook ---
|
|
|
|
func TestRunImplTabPreCreateHook_Missing(t *testing.T) {
|
|
root, _ := setupWorkspace(t)
|
|
ctx := makeHookCtx(root, root, "feat/x", 1, "main")
|
|
result := git.RunImplTabPreCreateHook(context.Background(), ctx)
|
|
if result.Status != git.HookStatusSkipped {
|
|
t.Errorf("want skipped, got %v", result.Status)
|
|
}
|
|
}
|
|
|
|
func TestRunImplTabPreCreateHook_Ok(t *testing.T) {
|
|
root, spxDir := setupWorkspace(t)
|
|
writeScript(t, spxDir, "impl-tab-pre-create.sh", "#!/bin/bash\nexit 0\n")
|
|
ctx := makeHookCtx(root, root, "feat/x", 7, "main")
|
|
result := git.RunImplTabPreCreateHook(context.Background(), ctx)
|
|
if result.Status != git.HookStatusOk {
|
|
t.Errorf("want ok, got %v", result.Status)
|
|
}
|
|
if result.ExitCode != 0 {
|
|
t.Errorf("want exit 0, got %d", result.ExitCode)
|
|
}
|
|
}
|
|
|
|
func TestRunImplTabPreCreateHook_Failed(t *testing.T) {
|
|
root, spxDir := setupWorkspace(t)
|
|
writeScript(t, spxDir, "impl-tab-pre-create.sh", "#!/bin/bash\nexit 7\n")
|
|
ctx := makeHookCtx(root, root, "feat/x", 1, "main")
|
|
result := git.RunImplTabPreCreateHook(context.Background(), ctx)
|
|
if result.Status != git.HookStatusFailed {
|
|
t.Errorf("want failed, got %v", result.Status)
|
|
}
|
|
if result.ExitCode != 7 {
|
|
t.Errorf("want ExitCode=7, got %d", result.ExitCode)
|
|
}
|
|
}
|
|
|
|
// --- RunImplTabPostCloseHook ---
|
|
|
|
func TestRunImplTabPostCloseHook_Missing(t *testing.T) {
|
|
root, _ := setupWorkspace(t)
|
|
ctx := makeHookCtx(root, root, "feat/x", 1, "main")
|
|
result := git.RunImplTabPostCloseHook(context.Background(), ctx)
|
|
if result.Status != git.HookStatusSkipped {
|
|
t.Errorf("want skipped, got %v", result.Status)
|
|
}
|
|
}
|
|
|
|
func TestRunImplTabPostCloseHook_Ok(t *testing.T) {
|
|
root, spxDir := setupWorkspace(t)
|
|
writeScript(t, spxDir, "impl-tab-post-close.sh", "#!/bin/bash\nexit 0\n")
|
|
ctx := makeHookCtx(root, root, "feat/x", 3, "main")
|
|
result := git.RunImplTabPostCloseHook(context.Background(), ctx)
|
|
if result.Status != git.HookStatusOk {
|
|
t.Errorf("want ok, got %v", result.Status)
|
|
}
|
|
}
|
|
|
|
// --- CustomScriptPath override ---
|
|
|
|
func TestRunPostCreateHook_CustomScriptPath(t *testing.T) {
|
|
root, _ := setupWorkspace(t)
|
|
customDir := t.TempDir()
|
|
customScript := filepath.Join(customDir, "my-hook.sh")
|
|
if err := os.WriteFile(customScript, []byte("#!/bin/bash\nexit 0\n"), 0o755); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
ctx := git.HookContext{
|
|
WorkspaceRoot: root,
|
|
WorktreePath: root,
|
|
Branch: "feat/x",
|
|
IssueNumber: 1,
|
|
MainBranch: "main",
|
|
CustomScriptPath: customScript, // absolute override
|
|
}
|
|
result := git.RunPostCreateHook(context.Background(), ctx)
|
|
if result.Status != git.HookStatusOk {
|
|
t.Errorf("want ok with custom path, got %v", result.Status)
|
|
}
|
|
if result.ScriptPath != customScript {
|
|
t.Errorf("ScriptPath: want %q, got %q", customScript, result.ScriptPath)
|
|
}
|
|
}
|
|
|
|
// IssueNumber env value must be the decimal string, not anything else.
|
|
func TestRunPostCreateHook_IssueNumberEnv(t *testing.T) {
|
|
root, spxDir := setupWorkspace(t)
|
|
envFile := filepath.Join(root, "issue-num.txt")
|
|
script := "#!/bin/bash\necho -n \"$ISSUE_NUMBER\" > \"" + envFile + "\"\nexit 0\n"
|
|
writeScript(t, spxDir, "worktree-post-create.sh", script)
|
|
|
|
ctx := makeHookCtx(root, root, "feat/x", 12345, "main")
|
|
result := git.RunPostCreateHook(context.Background(), ctx)
|
|
if result.Status != git.HookStatusOk {
|
|
t.Fatalf("want ok, got %v", result.Status)
|
|
}
|
|
data, err := os.ReadFile(envFile)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
got := strings.TrimSpace(string(data))
|
|
if got != strconv.Itoa(12345) {
|
|
t.Errorf("ISSUE_NUMBER env: want %q, got %q", "12345", got)
|
|
}
|
|
}
|