~bigbes/sr-ht-spec

ref: c74776077006e81af82c2fd53cb186271b42bee9 sr-ht-spec/hooks/install_test.go -rw-r--r-- 5.1 KiB
c7477607 — Eugene Blikh authn: accept tokens.sr.ht working tokens beside the agent token 10 days ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
package hooks

import (
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"testing"

	"sourcecraft.dev/bigbes/sr-ht-spec/core"
)

// fakeBinary is something Install will accept as the specsrht binary: it only
// has to exist and be executable.
func fakeBinary(t *testing.T, dir string) string {
	t.Helper()
	path := filepath.Join(dir, "specsrht")
	if err := os.WriteFile(path, []byte("#!/bin/sh\nexit 0\n"), 0o755); err != nil {
		t.Fatalf("write a stand-in binary: %v", err)
	}
	return path
}

func TestInstall(t *testing.T) {
	if _, err := exec.LookPath("git"); err != nil {
		t.Skipf("git is not on PATH: %v", err)
	}
	root := shortTempDir(t)
	repo := bareRepo(t, root, testSpace)
	binary := fakeBinary(t, root)

	if err := InstallSpace(root, testSpace, InstallOptions{Binary: binary}); err != nil {
		t.Fatalf("InstallSpace: %v", err)
	}

	for _, mode := range Modes() {
		path := filepath.Join(repo, "hooks", string(mode))
		target, err := os.Readlink(path)
		if err != nil {
			t.Fatalf("%s is not a symlink: %v", mode, err)
		}
		if target != binary {
			t.Errorf("%s points at %q, want %q", mode, target, binary)
		}
		// git only runs a hook it can execute; the symlink must resolve.
		info, err := os.Stat(path)
		if err != nil {
			t.Fatalf("stat %s: %v", mode, err)
		}
		if info.Mode().Perm()&0o111 == 0 {
			t.Errorf("%s is not executable", mode)
		}
	}

	// Without this, `git push --push-option=...` fails client-side with "the
	// receiving end does not support push options" and the documented escape
	// hatch would not exist at all.
	out := gitMust(t, repo, "config", "--get", "receive.advertisePushOptions")
	if strings.TrimSpace(out) != "true" {
		t.Errorf("receive.advertisePushOptions = %q, want true", strings.TrimSpace(out))
	}

	// The repository must still be usable afterwards: writing the config back
	// through go-git must not lose what git init put there.
	if got := strings.TrimSpace(gitMust(t, repo, "config", "--get", "core.bare")); got != "true" {
		t.Errorf("core.bare = %q after installing hooks; the repository is no longer bare", got)
	}
	if got := strings.TrimSpace(gitMust(t, repo, "symbolic-ref", "HEAD")); got != "refs/heads/main" {
		t.Errorf("HEAD = %q after installing hooks", got)
	}
}

// TestInstallIsIdempotent: refreshing is how an upgrade repairs every
// repository, so it has to be safe to run over and over, and over whatever was
// there before.
func TestInstallIsIdempotent(t *testing.T) {
	if _, err := exec.LookPath("git"); err != nil {
		t.Skipf("git is not on PATH: %v", err)
	}
	root := shortTempDir(t)
	repo := bareRepo(t, root, testSpace)
	binary := fakeBinary(t, root)

	// Something else got there first: git's own sample hook, and a plain file.
	stale := filepath.Join(repo, "hooks", "update")
	if err := os.WriteFile(stale, []byte("#!/bin/sh\nexit 0\n"), 0o755); err != nil {
		t.Fatalf("write a stale hook: %v", err)
	}

	for i := range 3 {
		if err := Install(repo, InstallOptions{Binary: binary}); err != nil {
			t.Fatalf("Install (pass %d): %v", i, err)
		}
	}
	target, err := os.Readlink(stale)
	if err != nil {
		t.Fatalf("the pre-existing hook was not replaced: %v", err)
	}
	if target != binary {
		t.Errorf("update points at %q, want %q", target, binary)
	}
	if _, err := os.Stat(stale + installSuffix); !os.IsNotExist(err) {
		t.Errorf("the temporary link was left behind: %v", err)
	}
}

func TestInstallRefusesBadInput(t *testing.T) {
	if _, err := exec.LookPath("git"); err != nil {
		t.Skipf("git is not on PATH: %v", err)
	}
	root := shortTempDir(t)
	repo := bareRepo(t, root, testSpace)
	binary := fakeBinary(t, root)

	notExecutable := filepath.Join(root, "notexec")
	if err := os.WriteFile(notExecutable, []byte("x"), 0o644); err != nil {
		t.Fatalf("WriteFile: %v", err)
	}

	tests := []struct {
		name string
		repo string
		opts InstallOptions
		want string
	}{
		{"no binary", repo, InstallOptions{}, "no binary"},
		{"relative binary", repo, InstallOptions{Binary: "specsrht"}, "not an absolute path"},
		{"missing binary", repo, InstallOptions{Binary: filepath.Join(root, "gone")}, "no such file"},
		{"non-executable binary", repo, InstallOptions{Binary: notExecutable}, "not an executable file"},
		{"non-executable binary is a directory", repo, InstallOptions{Binary: root}, "not an executable file"},
		{"no repository", "", InstallOptions{Binary: binary}, "no repository directory"},
		{"relative repository", "rfcs", InstallOptions{Binary: binary}, "not absolute"},
		{"not a bare repository", root, InstallOptions{Binary: binary}, "does not look like a bare repository"},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			err := Install(tt.repo, tt.opts)
			if err == nil {
				t.Fatalf("Install accepted %s", tt.name)
			}
			mentions(t, "the error", err.Error(), tt.want)
		})
	}
}

func TestInstallSpaceValidatesTheSpace(t *testing.T) {
	root := shortTempDir(t)
	binary := fakeBinary(t, root)
	for _, ref := range []core.SpaceRef{
		{Owner: "", Name: "rfcs"},
		{Owner: "bigbes", Name: ""},
		{Owner: "../etc", Name: "rfcs"},
		{Owner: "bigbes", Name: "../../etc"},
	} {
		if err := InstallSpace(root, ref, InstallOptions{Binary: binary}); err == nil {
			t.Errorf("InstallSpace accepted %+v", ref)
		}
	}
}