165 lines
4.0 KiB
Go
165 lines
4.0 KiB
Go
package hooks
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"io/fs"
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
|
|
|
|
|
|
const expectedHookBody = `#!/bin/sh
|
|
# simplegit-managed git hook. Forwards this invocation to the simplegit daemon
|
|
# hook bus via ` + "`simplegit hook`" + `. The daemon exports SIMPLEGIT_BIN (absolute
|
|
# path to the simplegit binary) and SIMPLEGIT_ROOT (data root, where hook.sock
|
|
# lives). If SIMPLEGIT_BIN is unset -- the hook running outside the daemon --
|
|
# fail open (exit 0) so the git operation is never blocked by the event bus.
|
|
test -n "$SIMPLEGIT_BIN" || exit 0
|
|
exec "$SIMPLEGIT_BIN" hook --root="${SIMPLEGIT_ROOT:-}" %s "$@"
|
|
`
|
|
|
|
|
|
|
|
|
|
func TestTemplateInSync(t *testing.T) {
|
|
entries, err := fs.ReadDir(templateFS, "template/hooks")
|
|
if err != nil {
|
|
t.Fatalf("read embedded template/hooks: %v", err)
|
|
}
|
|
got := map[string]bool{}
|
|
for _, e := range entries {
|
|
got[e.Name()] = true
|
|
}
|
|
for _, name := range hookNames {
|
|
if !got[name] {
|
|
t.Errorf("hook %q: no checked-in template file", name)
|
|
}
|
|
}
|
|
for name := range got {
|
|
found := false
|
|
for _, n := range hookNames {
|
|
if n == name {
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
if !found {
|
|
t.Errorf("template/hooks/%s: not in hookNames (stale file)", name)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestEnsureTemplate(t *testing.T) {
|
|
root := t.TempDir()
|
|
dir, err := EnsureTemplate(root)
|
|
if err != nil {
|
|
t.Fatalf("EnsureTemplate: %v", err)
|
|
}
|
|
if dir != TemplateDir(root) {
|
|
t.Fatalf("template dir = %s, want %s", dir, TemplateDir(root))
|
|
}
|
|
for _, name := range hookNames {
|
|
p := filepath.Join(dir, "hooks", name)
|
|
fi, err := os.Stat(p)
|
|
if err != nil {
|
|
t.Errorf("hook %s: not installed: %v", name, err)
|
|
continue
|
|
}
|
|
if fi.Mode()&0o111 == 0 {
|
|
t.Errorf("hook %s: not executable", name)
|
|
}
|
|
body, err := os.ReadFile(p)
|
|
if err != nil {
|
|
t.Errorf("hook %s: read: %v", name, err)
|
|
continue
|
|
}
|
|
if want := fmt.Sprintf(expectedHookBody, name); string(body) != want {
|
|
t.Errorf("hook %s: body mismatch\n got: %q\nwant: %q", name, string(body), want)
|
|
}
|
|
}
|
|
|
|
if _, err := EnsureTemplate(root); err != nil {
|
|
t.Fatalf("EnsureTemplate second run: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestStartRoundTrip(t *testing.T) {
|
|
root := t.TempDir()
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
defer cancel()
|
|
resolver, err := Start(ctx, root)
|
|
if err != nil {
|
|
t.Fatalf("Start: %v", err)
|
|
}
|
|
|
|
|
|
|
|
deadline := time.Now().Add(2 * time.Second)
|
|
for {
|
|
if _, err := os.Stat(SockPath(root)); err == nil {
|
|
break
|
|
}
|
|
if time.Now().After(deadline) {
|
|
t.Fatalf("socket never appeared")
|
|
}
|
|
time.Sleep(5 * time.Millisecond)
|
|
}
|
|
|
|
want := HookEvent{
|
|
Type: "post-receive",
|
|
Repo: "acme/widget",
|
|
RepoPath: filepath.Join(root, "repos", "acme", "widget.git"),
|
|
Args: []string{},
|
|
Stdin: "0000000000000000000000000000000000000000 1234567890abcdef refs/heads/main\n",
|
|
Env: map[string]string{"GIT_DIR": "."},
|
|
}
|
|
if exit := forward(want, SockPath(root)); exit != 0 {
|
|
t.Fatalf("forward exit = %d, want 0", exit)
|
|
}
|
|
|
|
select {
|
|
case got := <-resolver:
|
|
if got.Type != want.Type || got.Repo != want.Repo || got.Stdin != want.Stdin {
|
|
t.Fatalf("events got %+v, want %+v", got, want)
|
|
}
|
|
case <-time.After(2 * time.Second):
|
|
t.Fatalf("events channel did not receive the event")
|
|
}
|
|
}
|
|
|
|
func TestForwardFailOpen(t *testing.T) {
|
|
|
|
|
|
root := t.TempDir()
|
|
exit := forward(HookEvent{Type: "pre-receive"}, SockPath(root))
|
|
if exit != 0 {
|
|
t.Fatalf("forward exit = %d, want 0 (fail open)", exit)
|
|
}
|
|
}
|
|
|
|
func TestResolveRepo(t *testing.T) {
|
|
root := t.TempDir()
|
|
|
|
|
|
|
|
cases := []struct{ gitDir, cwd, want string }{
|
|
{filepath.Join(root, "repos", "acme", "widget.git"), "/somewhere", "acme/widget"},
|
|
{filepath.Join(root, "repos", "org", "team", "svc.git"), "/somewhere", "org/team/svc"},
|
|
|
|
{"", filepath.Join(root, "repos", "acme", "widget.git"), "acme/widget"},
|
|
|
|
{"", "/tmp/elsewhere.git", ""},
|
|
{filepath.Join(root, "repos"), "/somewhere", ""},
|
|
}
|
|
for _, c := range cases {
|
|
if got := resolveRepo(root, c.gitDir, c.cwd); got != c.want {
|
|
t.Errorf("resolveRepo(gitDir=%q, cwd=%q) = %q, want %q", c.gitDir, c.cwd, got, c.want)
|
|
}
|
|
}
|
|
}
|