github.com/nfisher/gitit@v0.0.7-0.20240131193748-bc8dd26542cc/cmd/helper_test.go (about) 1 package cmd_test 2 3 import ( 4 "context" 5 "fmt" 6 "github.com/go-git/go-git/v5" 7 "github.com/go-git/go-git/v5/config" 8 "github.com/go-git/go-git/v5/plumbing" 9 "github.com/go-git/go-git/v5/plumbing/object" 10 "net" 11 "net/http" 12 "net/http/cgi" 13 "os" 14 "os/exec" 15 "path/filepath" 16 "strings" 17 "testing" 18 ) 19 20 func WorkTree(t *testing.T, repo *git.Repository) *git.Worktree { 21 t.Helper() 22 wt, err := repo.Worktree() 23 if err != nil { 24 t.Fatalf("call=WorkTree err=`%v`", err) 25 } 26 return wt 27 } 28 29 func CreateBareDir(t *testing.T) func() { 30 t.Helper() 31 32 pwd, err := os.Getwd() 33 if err != nil { 34 t.Fatalf("call=Getwd err=`%v`\n", err) 35 } 36 37 dir := t.TempDir() 38 err = os.Chdir(dir) 39 if err != nil { 40 t.Fatalf("call=Chdir err=`%v`\n", err) 41 } 42 43 return func() { 44 os.Chdir(pwd) 45 os.RemoveAll(dir) 46 } 47 } 48 49 func CreateRepo(t *testing.T) (*git.Repository, func()) { 50 t.Helper() 51 52 pwd, err := os.Getwd() 53 if err != nil { 54 t.Fatalf("call=Getwd err=`%v`\n", err) 55 } 56 57 dir := t.TempDir() 58 err = os.Chdir(dir) 59 if err != nil { 60 t.Fatalf("call=Chdir err=`%v`\n", err) 61 } 62 63 repo, err := git.PlainInit(dir, false) 64 if err != nil { 65 t.Fatalf("call=PlainInit err=`%v`\n", err) 66 } 67 68 return repo, func() { 69 os.Chdir(pwd) 70 os.RemoveAll(dir) 71 } 72 } 73 74 func Commit(t *testing.T, wt *git.Worktree, files map[string]string, msg string) { 75 t.Helper() 76 for n, c := range files { 77 AddFile(t, wt, n, c) 78 } 79 80 _, err := wt.Commit(msg, &git.CommitOptions{ 81 Author: &object.Signature{Email: "nate@fisher.com", Name: "Nate Fisher"}, 82 }) 83 if err != nil { 84 t.Fatalf("call=Commit err=`%v`\n", err) 85 } 86 } 87 88 func CreateFile(t *testing.T, filename, contents string) { 89 t.Helper() 90 w, err := os.Create(filename) 91 if err != nil { 92 t.Fatalf("call=os.Create err=`%v`\n", err) 93 } 94 defer w.Close() 95 96 _, err = w.WriteString(contents) 97 if err != nil { 98 t.Fatalf("call=w.WriteString err=`%v`\n", err) 99 } 100 } 101 102 func AddFile(t *testing.T, wt *git.Worktree, filename, contents string) { 103 t.Helper() 104 w, err := os.Create(filename) 105 if err != nil { 106 t.Fatalf("call=os.Create err=`%v`\n", err) 107 } 108 defer w.Close() 109 110 _, err = w.WriteString(contents) 111 if err != nil { 112 t.Fatalf("call=w.WriteString err=`%v`\n", err) 113 } 114 115 _, err = wt.Add(filename) 116 if err != nil { 117 t.Fatalf("call=Add err=`%v`\n", err) 118 } 119 } 120 121 func InitialCommit(t *testing.T, repo *git.Repository) { 122 t.Helper() 123 wt, err := repo.Worktree() 124 if err != nil { 125 t.Fatalf("call=CreateBranch err=`%v`\n", err) 126 } 127 128 Commit(t, wt, map[string]string{ 129 ".gitignore": "*.sw?", 130 }, "Add .gitignore") 131 } 132 133 func CreateBranch(t *testing.T, repo *git.Repository, stack, branch string) { 134 t.Helper() 135 name := fmt.Sprintf("%s/%s", stack, branch) 136 wt, err := repo.Worktree() 137 if err != nil { 138 t.Fatalf("call=Worktree err=`%v`\n", err) 139 } 140 141 err = wt.Checkout(&git.CheckoutOptions{ 142 Branch: plumbing.NewBranchReferenceName(name), 143 Create: true, 144 }) 145 if err != nil { 146 t.Fatalf("call=Checkout err=`%v`\n", err) 147 } 148 } 149 150 func InitStack(t *testing.T, repo *git.Repository, stack, branch string) { 151 t.Helper() 152 CreateBranch(t, repo, stack, branch) 153 } 154 155 func SkipWIP(t *testing.T, runWip bool) { 156 t.Helper() 157 if !runWip { 158 t.Skip("WIP") 159 } 160 } 161 162 type server struct { 163 Port int 164 Root string 165 Repo *git.Repository 166 } 167 168 func (s *server) Address() string { 169 return fmt.Sprintf("http://localhost:%d", s.Port) 170 } 171 172 func LaunchServer(t *testing.T) (*server, func()) { 173 // inspired by go-git tests. 174 l, err := net.Listen("tcp", "localhost:0") 175 if err != nil { 176 t.Fatalf("call=Listen err=`%v`\n", err) 177 } 178 d := t.TempDir() 179 180 cmd := exec.Command("git", "--exec-path") 181 b, err := cmd.CombinedOutput() 182 if err != nil { 183 t.Fatalf("call=CombinedOutput err=`%v`\n", err) 184 } 185 186 backend := filepath.Join(strings.TrimSpace(string(b)), "git-http-backend") 187 188 srv := http.Server{ 189 Handler: &cgi.Handler{ 190 Path: backend, 191 Env: []string{"GIT_HTTP_EXPORT_ALL=true", fmt.Sprintf("GIT_PROJECT_ROOT=%s", d)}, 192 }, 193 } 194 195 repo, err := git.PlainInit(d, true) 196 if err != nil { 197 t.Fatalf("call=PlainInit err=`%v`\n", err) 198 } 199 200 err = os.WriteFile(filepath.Join(d, "config"), []byte(configContents), os.ModePerm) 201 if err != nil { 202 t.Fatalf("call=WriteFile err=`%v`\n", err) 203 } 204 205 s := &server{ 206 Root: d, 207 Port: l.Addr().(*net.TCPAddr).Port, 208 Repo: repo, 209 } 210 211 go func() { 212 srv.Serve(l) 213 }() 214 215 return s, func() { 216 srv.Shutdown(context.Background()) 217 } 218 } 219 220 var configContents = `[core] 221 bare = true 222 [http] 223 receivepack = true 224 ` 225 226 func CreateRemote(t *testing.T, repo *git.Repository, s *server) { 227 _, err := repo.CreateRemote(&config.RemoteConfig{ 228 Name: "origin", 229 URLs: []string{fmt.Sprintf("http://localhost:%d", s.Port)}, 230 }) 231 if err != nil { 232 t.Fatalf("call=CreateRemote err=`%v`\n", err) 233 } 234 } 235 236 func CreateThreeLayerStack(t *testing.T, repo *git.Repository) { 237 wt := WorkTree(t, repo) 238 InitialCommit(t, repo) 239 InitStack(t, repo, "kb3456", "001_migration") 240 Commit(t, wt, map[string]string{"001_create.sql": "SELECT 1;"}, "Add 001_create.sql") 241 InitStack(t, repo, "kb1234", "001_docs") 242 Commit(t, wt, map[string]string{"README.md": "Hello world"}, "Add README.md") 243 CreateBranch(t, repo, "kb1234", "002_api") 244 Commit(t, wt, map[string]string{"api.js": "function api() {}"}, "Add api.js") 245 CreateBranch(t, repo, "kb1234", "003_ui") 246 Commit(t, wt, map[string]string{"ui.js": "function ui() {}"}, "Add ui.js") 247 } 248 249 func PushBranch(t *testing.T, repo *git.Repository, branchName string) { 250 t.Helper() 251 spec := config.RefSpec(fmt.Sprintf("refs/heads/%[1]s:refs/heads/%[1]s", branchName)) 252 err := repo.Push(&git.PushOptions{ 253 RemoteName: "origin", 254 RefSpecs: []config.RefSpec{spec}, 255 }) 256 if err != nil { 257 t.Fatalf("call=Push err=`%v`\n", err) 258 } 259 } 260 261 func CheckoutBranch(t *testing.T, wt *git.Worktree, name string) { 262 t.Helper() 263 err := wt.Checkout(&git.CheckoutOptions{ 264 Branch: plumbing.NewBranchReferenceName(name), 265 }) 266 if err != nil { 267 t.Fatalf("call=Checkout err=`%v`\n", err) 268 } 269 } 270 271 func Chdir(t *testing.T, wt *git.Worktree) { 272 t.Helper() 273 274 err := os.Chdir(wt.Filesystem.Root()) 275 if err != nil { 276 t.Fatalf("call=Chdir err=`%v`\n", err) 277 } 278 } 279 280 func CloneRepo(t *testing.T, s *server, name string) (*git.Repository, func()) { 281 t.Helper() 282 283 pwd, err := os.Getwd() 284 if err != nil { 285 t.Fatalf("call=Getwd err=`%v`\n", err) 286 } 287 288 dir := t.TempDir() 289 err = os.Chdir(dir) 290 if err != nil { 291 t.Fatalf("call=Chdir err=`%v`\n", err) 292 } 293 294 repo, err := git.PlainClone(dir, false, &git.CloneOptions{ 295 RemoteName: "origin", 296 URL: s.Address(), 297 ReferenceName: plumbing.ReferenceName("refs/heads/" + name), 298 }) 299 if err != nil { 300 t.Fatalf("call=PlainClone err=`%v`\n", err) 301 } 302 303 return repo, func() { 304 os.Chdir(pwd) 305 os.RemoveAll(dir) 306 } 307 } 308 309 func FetchRefs(t *testing.T, repo *git.Repository, refs string) { 310 t.Helper() 311 spec := config.RefSpec(fmt.Sprintf("refs/heads/%[1]s:refs/heads/%[1]s", refs)) 312 err := repo.Fetch(&git.FetchOptions{ 313 RemoteName: "origin", 314 RefSpecs: []config.RefSpec{spec}, 315 }) 316 if err != nil { 317 t.Fatalf("call=Fetch err=`%v`\n", err) 318 } 319 }