all repos

mugit @ a940f43f0e7d974f75672988227623e2e1fa9a08

🐮 git server that your cow will love

mugit/testscript_test.go (view raw)

Oleksandr Smirnov Oleksandr Smirnov
olexsmir@gmail.com
run errcheck, 27 days ago
1
package main_test
2
3
import (
4
	"context"
5
	"fmt"
6
	"net"
7
	"net/http"
8
	"os"
9
	"os/exec"
10
	"path/filepath"
11
	"strconv"
12
	"testing"
13
	"time"
14
15
	"github.com/rogpeppe/go-internal/testscript"
16
	"gopkg.in/yaml.v2"
17
18
	"olexsmir.xyz/mugit/internal/config"
19
	"olexsmir.xyz/mugit/internal/handlers"
20
)
21
22
var (
23
	mugitBin   string
24
	httpPort   int
25
	reposDir   string
26
	configPath string
27
)
28
29
func TestMain(m *testing.M) { os.Exit(testMain(m)) }
30
func testMain(m *testing.M) int {
31
	ctx, cancel := context.WithCancel(context.Background())
32
	defer cancel()
33
34
	tmpDir, err := os.MkdirTemp("", "mugit-test-*")
35
	if err != nil {
36
		fmt.Fprintf(os.Stderr, "failed to create temp dir: %v\n", err)
37
		return 1
38
	}
39
	defer func() { _ = os.RemoveAll(tmpDir) }()
40
41
	reposDir = filepath.Join(tmpDir, "repos")
42
	if jerr := os.MkdirAll(reposDir, 0o755); jerr != nil {
43
		fmt.Fprintf(os.Stderr, "failed to create repo dir: %v\n", jerr)
44
		return 1
45
	}
46
47
	if berr := buildMugitBinary(tmpDir); berr != nil {
48
		fmt.Fprintf(os.Stderr, "failed to build binary: %v\n", berr)
49
		return 1
50
	}
51
52
	port, err := findFreePort()
53
	if err != nil {
54
		fmt.Fprintf(os.Stderr, "failed to find free port: %v\n", err)
55
		return 1
56
	}
57
	httpPort = port
58
59
	cfg := &config.Config{
60
		Server: config.ServerConfig{
61
			Host: "127.0.0.1",
62
			Port: httpPort,
63
		},
64
		Meta: config.MetaConfig{
65
			Title: "test mugit",
66
			Host:  "localhost",
67
			Modt:  "Welcome to test mugit!",
68
		},
69
		Repo: config.RepoConfig{
70
			Dir:     reposDir,
71
			Readmes: []string{"README.md"},
72
		},
73
		SSH:    config.SSHConfig{Enable: true, User: "git"},
74
		Mirror: config.MirrorConfig{Enable: false},
75
		Cache: config.CacheConfig{
76
			HomePage: 0,
77
			Readme:   0,
78
			Diff:     0,
79
		},
80
	}
81
82
	configPath = filepath.Join(tmpDir, "config.yaml")
83
	configBytes, err := yaml.Marshal(cfg)
84
	if err != nil {
85
		fmt.Fprintf(os.Stderr, "failed to marshal config: %v\n", err)
86
		return 1
87
	}
88
	if err := os.WriteFile(configPath, configBytes, 0o600); err != nil {
89
		fmt.Fprintf(os.Stderr, "failed to write config: %v\n", err)
90
		return 1
91
	}
92
93
	httpServer := &http.Server{
94
		Addr:    net.JoinHostPort(cfg.Server.Host, strconv.Itoa(cfg.Server.Port)),
95
		Handler: handlers.InitRoutes(cfg),
96
	}
97
	go func() {
98
		if err := httpServer.ListenAndServe(); err != nil && err != http.ErrServerClosed {
99
			fmt.Fprintf(os.Stderr, "HTTP server error: %v\n", err)
100
		}
101
	}()
102
103
	if err := waitForPort(httpPort, 5*time.Second); err != nil {
104
		fmt.Fprintf(os.Stderr, "server did not become ready: %v\n", err)
105
		return 1
106
	}
107
108
	code := m.Run()
109
	_ = httpServer.Shutdown(ctx)
110
	return code
111
}
112
113
func TestScript(t *testing.T) {
114
	if testing.Short() {
115
		t.Skip("skipping integration tests")
116
	}
117
118
	sshWrapperContent := fmt.Sprintf(`#!/bin/sh
119
export SSH_ORIGINAL_COMMAND="$2"
120
exec %s shell -c %s`, mugitBin, configPath)
121
122
	testscript.Run(t, testscript.Params{
123
		Dir: "testscript",
124
		Cmds: map[string]func(ts *testscript.TestScript, neg bool, args []string){
125
			"mugit": cmdMugit,
126
			"git":   cmdGit,
127
		},
128
		Setup: func(env *testscript.Env) error {
129
			work := env.Getenv("WORK")
130
			sshWrapperPath := filepath.Join(work, "ssh-wrapper.sh")
131
			if err := os.WriteFile(sshWrapperPath, []byte(sshWrapperContent), 0o700); err != nil {
132
				return fmt.Errorf("failed to create ssh wrapper: %w", err)
133
			}
134
135
			env.Setenv("SSH_WRAPPER", sshWrapperPath)
136
			env.Setenv("REPOS", reposDir)
137
			env.Setenv("MPORT", strconv.Itoa(httpPort))
138
			env.Setenv("MURL", fmt.Sprintf("http://127.0.0.1:%d", httpPort))
139
			return nil
140
		},
141
	})
142
}
143
144
func buildMugitBinary(tmpDir string) error {
145
	mugitBin = filepath.Join(tmpDir, "mugit")
146
	cmd := exec.Command("go", "build", "-o", mugitBin, ".")
147
	cmd.Dir = "."
148
	if out, err := cmd.CombinedOutput(); err != nil {
149
		_ = os.RemoveAll(tmpDir)
150
		return fmt.Errorf("go build: %v\n%s", err, out)
151
	}
152
	return nil
153
}
154
155
func findFreePort() (int, error) {
156
	l, err := net.Listen("tcp", "127.0.0.1:0")
157
	if err != nil {
158
		return 0, err
159
	}
160
	port := l.Addr().(*net.TCPAddr).Port
161
	_ = l.Close()
162
	return port, nil
163
}
164
165
func waitForPort(port int, timeout time.Duration) error {
166
	deadline := time.Now().Add(timeout)
167
	for time.Now().Before(deadline) {
168
		if conn, err := net.DialTimeout(
169
			"tcp",
170
			net.JoinHostPort("127.0.0.1", strconv.Itoa(port)),
171
			200*time.Millisecond,
172
		); err == nil {
173
			_ = conn.Close()
174
			return nil
175
		}
176
		time.Sleep(50 * time.Millisecond)
177
	}
178
	return fmt.Errorf("port %d not ready after %s", port, timeout)
179
}
180
181
func cmdMugit(ts *testscript.TestScript, neg bool, args []string) {
182
	if len(args) < 1 {
183
		ts.Fatalf("usage: mugit <subcommand> ...")
184
	}
185
	cmd := exec.Command(mugitBin, append([]string{"-c", configPath}, args...)...)
186
	cmd.Env = os.Environ()
187
	cmd.Stdout = ts.Stdout()
188
	cmd.Stderr = ts.Stderr()
189
	err := cmd.Run()
190
	if neg {
191
		if err == nil {
192
			ts.Fatalf("expected mugit to fail, it succeeded")
193
		}
194
	} else {
195
		if err != nil {
196
			ts.Fatalf("mugit: %v", err)
197
		}
198
	}
199
}
200
201
func cmdGit(ts *testscript.TestScript, neg bool, args []string) {
202
	if len(args) > 0 && args[0] == "init" {
203
		hasBranch := false
204
		for _, arg := range args {
205
			if arg == "-b" || arg == "--initial-branch" {
206
				hasBranch = true
207
				break
208
			}
209
		}
210
		if !hasBranch {
211
			args = append([]string{"init", "-b", "master"}, args[1:]...)
212
		}
213
	}
214
	args = append([]string{
215
		"-c", "user.email=test@test.local",
216
		"-c", "user.name=Test User",
217
	}, args...)
218
	cmd := exec.Command("git", args...)
219
	cmd.Dir = ts.Getenv("WORK")
220
	cmd.Stdout = ts.Stdout()
221
	cmd.Stderr = ts.Stderr()
222
223
	err := cmd.Run()
224
	if err == nil && neg {
225
		ts.Fatalf("expected git to fail, but it succeeded")
226
	}
227
	if err != nil && !neg {
228
		ts.Fatalf("git: %v", err)
229
	}
230
}