e2e: build binaries once, run the tests in parallel !454

merged merged by cmc on 2026-09-21 21:48 UTC · krz/gitbay:e2e-speed into main

142 files changed, +311 −79

Layout: unified · split

Makefile +2 −3
@@ -26,9 +26,8 @@ RUNNER_BIN := dist/gitbay-runner-linux-amd64
26help: 26help:
27 @sed -n 's/^# //p' $(MAKEFILE_LIST) 27 @sed -n 's/^# //p' $(MAKEFILE_LIST)
28 28
29# The e2e suite runs 400-700s against go's 600s per-package default, so a 29# -timeout 30m is a ceiling for a real hang, not a working figure: the
30# loaded machine turns a passing tree into a goroutine dump that reads as 30# suite runs in a couple of minutes since the e2e tests went parallel.
31# an unrelated failure. A real hang still fails, just later.
32test: 31test:
33 go test ./... -count=1 -timeout 30m 32 go test ./... -count=1 -timeout 30m
34 33
e2e/aboutbackfill_test.go +1
@@ -11,6 +11,7 @@ import (
11// The about text parked by migration 0058 becomes a file in the owner's 11// The about text parked by migration 0058 becomes a file in the owner's
12// .gitbay repository. Running it twice writes nothing the second time. 12// .gitbay repository. Running it twice writes nothing the second time.
13func TestMigrateProfileAbout(t *testing.T) { 13func TestMigrateProfileAbout(t *testing.T) {
14 t.Parallel()
14 inst := startInstance(t) 15 inst := startInstance(t)
15 aliceKey := inst.newKey(t, "alice") 16 aliceKey := inst.newKey(t, "alice")
16 inst.admin(t, "admin", "user", "create", "alice", 17 inst.admin(t, "admin", "user", "create", "alice",
e2e/accounts_test.go +3
@@ -49,6 +49,7 @@ func browserPost(t *testing.T, c *http.Client, u string, form url.Values) (int,
49} 49}
50 50
51func TestWebAccounts(t *testing.T) { 51func TestWebAccounts(t *testing.T) {
52 t.Parallel()
52 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n") 53 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n")
53 54
54 aliceKey := inst.newKey(t, "alice") 55 aliceKey := inst.newKey(t, "alice")
@@ -246,6 +247,7 @@ func TestWebAccounts(t *testing.T) {
246// TestViewOnlyHasNoLoginOnTheWire is the M8 negative: in view_only mode the 247// TestViewOnlyHasNoLoginOnTheWire is the M8 negative: in view_only mode the
247// login route does not exist and web login over ssh is refused. 248// login route does not exist and web login over ssh is refused.
248func TestViewOnlyHasNoLoginOnTheWire(t *testing.T) { 249func TestViewOnlyHasNoLoginOnTheWire(t *testing.T) {
250 t.Parallel()
249 inst := startInstance(t) // default: view_only 251 inst := startInstance(t) // default: view_only
250 aliceKey := inst.newKey(t, "alice") 252 aliceKey := inst.newKey(t, "alice")
251 inst.admin(t, "admin", "user", "create", "alice", "--key", aliceKey+".pub") 253 inst.admin(t, "admin", "user", "create", "alice", "--key", aliceKey+".pub")
@@ -263,6 +265,7 @@ func TestViewOnlyHasNoLoginOnTheWire(t *testing.T) {
263// and its hostname: the login page prints a command to paste into a terminal, 265// and its hostname: the login page prints a command to paste into a terminal,
264// so it must name the host even when the operator has set a display title. 266// so it must name the host even when the operator has set a display title.
265func TestTitleIsNotAHostname(t *testing.T) { 267func TestTitleIsNotAHostname(t *testing.T) {
268 t.Parallel()
266 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\ntitle = \"GitBay\"\n") 269 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\ntitle = \"GitBay\"\n")
267 270
268 status, body := inst.get(t, "/login") 271 status, body := inst.get(t, "/login")
e2e/accountweb_test.go +1
@@ -13,6 +13,7 @@ import (
13// browser session. Public keys are the only credential-shaped input the web 13// browser session. Public keys are the only credential-shaped input the web
14// accepts; secrets and token minting stay on SSH. 14// accepts; secrets and token minting stay on SSH.
15func TestAccountSettingsWeb(t *testing.T) { 15func TestAccountSettingsWeb(t *testing.T) {
16 t.Parallel()
16 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n") 17 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n")
17 aliceKey := inst.newKey(t, "alice") 18 aliceKey := inst.newKey(t, "alice")
18 inst.admin(t, "admin", "user", "create", "alice", 19 inst.admin(t, "admin", "user", "create", "alice",
e2e/acme_test.go +1
@@ -18,6 +18,7 @@ import (
18// name, which a test cannot have; what matters here is that the plumbing is 18// name, which a test cannot have; what matters here is that the plumbing is
19// correct and failure to issue does not kill the daemon. 19// correct and failure to issue does not kill the daemon.
20func TestACMEServe(t *testing.T) { 20func TestACMEServe(t *testing.T) {
21 t.Parallel()
21 inst := startInstanceWith(t, "") // helper for binary + keys; killed below 22 inst := startInstanceWith(t, "") // helper for binary + keys; killed below
22 inst.proc.Process.Kill() 23 inst.proc.Process.Kill()
23 inst.proc.Wait() 24 inst.proc.Wait()
e2e/activity_test.go +1
@@ -11,6 +11,7 @@ import (
11) 11)
12 12
13func TestActivityGraph(t *testing.T) { 13func TestActivityGraph(t *testing.T) {
14 t.Parallel()
14 inst := startInstance(t) 15 inst := startInstance(t)
15 aliceKey := inst.newKey(t, "alice") 16 aliceKey := inst.newKey(t, "alice")
16 bobKey := inst.newKey(t, "bob") 17 bobKey := inst.newKey(t, "bob")
e2e/adminusers_test.go +7
@@ -17,6 +17,7 @@ type adminUserRow struct {
17} 17}
18 18
19func TestAdminUserListAndShow(t *testing.T) { 19func TestAdminUserListAndShow(t *testing.T) {
20 t.Parallel()
20 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n") 21 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n")
21 adminKey := inst.newKey(t, "root") 22 adminKey := inst.newKey(t, "root")
22 aliceKey := inst.newKey(t, "alice") 23 aliceKey := inst.newKey(t, "alice")
@@ -171,6 +172,7 @@ func TestAdminUserListAndShow(t *testing.T) {
171} 172}
172 173
173func TestAdminPromoteDemote(t *testing.T) { 174func TestAdminPromoteDemote(t *testing.T) {
175 t.Parallel()
174 inst := startInstance(t) 176 inst := startInstance(t)
175 rootKey := inst.newKey(t, "root") 177 rootKey := inst.newKey(t, "root")
176 aliceKey := inst.newKey(t, "alice") 178 aliceKey := inst.newKey(t, "alice")
@@ -226,6 +228,7 @@ func TestAdminPromoteDemote(t *testing.T) {
226} 228}
227 229
228func TestAdminRepoModeration(t *testing.T) { 230func TestAdminRepoModeration(t *testing.T) {
231 t.Parallel()
229 inst := startInstance(t) 232 inst := startInstance(t)
230 rootKey := inst.newKey(t, "root") 233 rootKey := inst.newKey(t, "root")
231 aliceKey := inst.newKey(t, "alice") 234 aliceKey := inst.newKey(t, "alice")
@@ -337,6 +340,7 @@ func TestAdminRepoModeration(t *testing.T) {
337// commands work in an admin's SSH session and audit rows say which path 340// commands work in an admin's SSH session and audit rows say which path
338// ran them. 341// ran them.
339func TestAdminHostAndSSHAreOneSurface(t *testing.T) { 342func TestAdminHostAndSSHAreOneSurface(t *testing.T) {
343 t.Parallel()
340 inst := startInstance(t) 344 inst := startInstance(t)
341 rootKey := inst.newKey(t, "root") 345 rootKey := inst.newKey(t, "root")
342 aliceKey := inst.newKey(t, "alice") 346 aliceKey := inst.newKey(t, "alice")
@@ -402,6 +406,7 @@ func TestAdminHostAndSSHAreOneSurface(t *testing.T) {
402} 406}
403 407
404func TestAuditFilters(t *testing.T) { 408func TestAuditFilters(t *testing.T) {
409 t.Parallel()
405 inst := startInstance(t) 410 inst := startInstance(t)
406 rootKey := inst.newKey(t, "root") 411 rootKey := inst.newKey(t, "root")
407 aliceKey := inst.newKey(t, "alice") 412 aliceKey := inst.newKey(t, "alice")
@@ -454,6 +459,7 @@ func TestAuditFilters(t *testing.T) {
454} 459}
455 460
456func TestAdminQueuesDashboard(t *testing.T) { 461func TestAdminQueuesDashboard(t *testing.T) {
462 t.Parallel()
457 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n[webhooks]\nallow_local = true\n") 463 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n[webhooks]\nallow_local = true\n")
458 rootKey := inst.newKey(t, "root") 464 rootKey := inst.newKey(t, "root")
459 aliceKey := inst.newKey(t, "alice") 465 aliceKey := inst.newKey(t, "alice")
@@ -568,6 +574,7 @@ func TestAdminQueuesDashboard(t *testing.T) {
568} 574}
569 575
570func TestAdminConfigShow(t *testing.T) { 576func TestAdminConfigShow(t *testing.T) {
577 t.Parallel()
571 inst := startInstanceWith(t, "[mail]\nsmtp_host = \"127.0.0.1:1\"\nfrom = \"forge@example.test\"\nsmtp_pass = \"hunter2\"\n") 578 inst := startInstanceWith(t, "[mail]\nsmtp_host = \"127.0.0.1:1\"\nfrom = \"forge@example.test\"\nsmtp_pass = \"hunter2\"\n")
572 out := inst.admin(t, "admin", "config", "show") 579 out := inst.admin(t, "admin", "config", "show")
573 for _, want := range []string{"[server]", "site_url", "ssh_auth_rate = 10", "pull_interval_minutes = 15", "[mail]", `smtp_pass = "<redacted>"`} { 580 for _, want := range []string{"[server]", "site_url", "ssh_auth_rate = 10", "pull_interval_minutes = 15", "[mail]", `smtp_pass = "<redacted>"`} {
e2e/adminusersweb_test.go +1
@@ -10,6 +10,7 @@ import (
10// web can reach now that nothing is held back from it (#234). Demote 10// web can reach now that nothing is held back from it (#234). Demote
11// and disable carry the typed-name check. 11// and disable carry the typed-name check.
12func TestAdminUsersWeb(t *testing.T) { 12func TestAdminUsersWeb(t *testing.T) {
13 t.Parallel()
13 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n") 14 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n")
14 rootKey := inst.newKey(t, "root") 15 rootKey := inst.newKey(t, "root")
15 aliceKey := inst.newKey(t, "alice") 16 aliceKey := inst.newKey(t, "alice")
e2e/api_test.go +3
@@ -40,6 +40,7 @@ func (i *instance) apiCall(t *testing.T, token string, argv []string, stdin stri
40} 40}
41 41
42func TestJSONAPI(t *testing.T) { 42func TestJSONAPI(t *testing.T) {
43 t.Parallel()
43 inst := startInstanceWith(t, "[api]\nenabled = true\n") 44 inst := startInstanceWith(t, "[api]\nenabled = true\n")
44 aliceKey := inst.newKey(t, "alice") 45 aliceKey := inst.newKey(t, "alice")
45 inst.admin(t, "admin", "user", "create", "alice", 46 inst.admin(t, "admin", "user", "create", "alice",
@@ -202,6 +203,7 @@ func TestJSONAPI(t *testing.T) {
202// metered separately from reads, and one caller cannot spend another's 203// metered separately from reads, and one caller cannot spend another's
203// budget. 204// budget.
204func TestAPIRateLimit(t *testing.T) { 205func TestAPIRateLimit(t *testing.T) {
206 t.Parallel()
205 // 6/minute sustained, so the read burst is 6 and the write burst 0.6 — 207 // 6/minute sustained, so the read burst is 6 and the write burst 0.6 —
206 // the first write is allowed and the second is not. 208 // the first write is allowed and the second is not.
207 inst := startInstanceWith(t, "[api]\nenabled = true\n[limits]\napi_rate = 6\n") 209 inst := startInstanceWith(t, "[api]\nenabled = true\n[limits]\napi_rate = 6\n")
@@ -309,6 +311,7 @@ func (i *instance) apiGet(t *testing.T, token string, argv []string, ifNoneMatch
309// with an ETag, 304 on revalidation, writes refused, and one caller's ETag 311// with an ETag, 304 on revalidation, writes refused, and one caller's ETag
310// never matching another's. 312// never matching another's.
311func TestAPIReadGET(t *testing.T) { 313func TestAPIReadGET(t *testing.T) {
314 t.Parallel()
312 inst := startInstanceWith(t, "[api]\nenabled = true\n") 315 inst := startInstanceWith(t, "[api]\nenabled = true\n")
313 aliceKey := inst.newKey(t, "alice") 316 aliceKey := inst.newKey(t, "alice")
314 bobKey := inst.newKey(t, "bob") 317 bobKey := inst.newKey(t, "bob")
e2e/apiread_test.go +1
@@ -14,6 +14,7 @@ import (
14// before: no command returned file contents, and the web's raw route 14// before: no command returned file contents, and the web's raw route
15// authenticates by session cookie, not bearer token. 15// authenticates by session cookie, not bearer token.
16func TestRepoTreeAndCat(t *testing.T) { 16func TestRepoTreeAndCat(t *testing.T) {
17 t.Parallel()
17 inst := startInstance(t) 18 inst := startInstance(t)
18 aliceKey := inst.newKey(t, "alice") 19 aliceKey := inst.newKey(t, "alice")
19 bobKey := inst.newKey(t, "bob") 20 bobKey := inst.newKey(t, "bob")
e2e/approvals_test.go +2
@@ -10,6 +10,7 @@ import (
10) 10)
11 11
12func TestMergeRequirements(t *testing.T) { 12func TestMergeRequirements(t *testing.T) {
13 t.Parallel()
13 inst := startInstance(t) 14 inst := startInstance(t)
14 aliceKey := inst.newKey(t, "alice") 15 aliceKey := inst.newKey(t, "alice")
15 bobKey := inst.newKey(t, "bob") 16 bobKey := inst.newKey(t, "bob")
@@ -147,6 +148,7 @@ func TestMergeRequirements(t *testing.T) {
147// merges. When it is on, it gates independently of require_approvals — 148// merges. When it is on, it gates independently of require_approvals —
148// the coupling that left owners unenforced under default settings (#99). 149// the coupling that left owners unenforced under default settings (#99).
149func TestCodeownersToggle(t *testing.T) { 150func TestCodeownersToggle(t *testing.T) {
151 t.Parallel()
150 inst := startInstance(t) 152 inst := startInstance(t)
151 aliceKey := inst.newKey(t, "alice") 153 aliceKey := inst.newKey(t, "alice")
152 carolKey := inst.newKey(t, "carol") 154 carolKey := inst.newKey(t, "carol")
e2e/archive_test.go +1
@@ -8,6 +8,7 @@ import (
8) 8)
9 9
10func TestArchiveAndTopics(t *testing.T) { 10func TestArchiveAndTopics(t *testing.T) {
11 t.Parallel()
11 inst := startInstance(t) 12 inst := startInstance(t)
12 aliceKey := inst.newKey(t, "alice") 13 aliceKey := inst.newKey(t, "alice")
13 bobKey := inst.newKey(t, "bob") 14 bobKey := inst.newKey(t, "bob")
e2e/audit_test.go +1
@@ -9,6 +9,7 @@ import (
9) 9)
10 10
11func TestAuditAndHardening(t *testing.T) { 11func TestAuditAndHardening(t *testing.T) {
12 t.Parallel()
12 inst := startInstanceWith(t, "[limits]\nssh_auth_rate = 3\nmax_pack_bytes = 2000\n") 13 inst := startInstanceWith(t, "[limits]\nssh_auth_rate = 3\nmax_pack_bytes = 2000\n")
13 adminKey := inst.newKey(t, "root") 14 adminKey := inst.newKey(t, "root")
14 aliceKey := inst.newKey(t, "alice") 15 aliceKey := inst.newKey(t, "alice")
e2e/autolink_test.go +1
@@ -8,6 +8,7 @@ import (
8) 8)
9 9
10func TestWebAutolinks(t *testing.T) { 10func TestWebAutolinks(t *testing.T) {
11 t.Parallel()
11 inst := startInstance(t) 12 inst := startInstance(t)
12 aliceKey := inst.newKey(t, "alice") 13 aliceKey := inst.newKey(t, "alice")
13 bobKey := inst.newKey(t, "bob") 14 bobKey := inst.newKey(t, "bob")
e2e/backup_test.go +1
@@ -12,6 +12,7 @@ import (
12) 12)
13 13
14func TestAdminBackup(t *testing.T) { 14func TestAdminBackup(t *testing.T) {
15 t.Parallel()
15 inst := startInstance(t) 16 inst := startInstance(t)
16 aliceKey := inst.newKey(t, "alice") 17 aliceKey := inst.newKey(t, "alice")
17 inst.admin(t, "admin", "user", "create", "alice", 18 inst.admin(t, "admin", "user", "create", "alice",
e2e/badge_test.go +1
@@ -9,6 +9,7 @@ import (
9// says "unknown" before any build exists, and 404s for a private repo so 9// says "unknown" before any build exists, and 404s for a private repo so
10// it cannot be used to probe for one. 10// it cannot be used to probe for one.
11func TestBuildBadge(t *testing.T) { 11func TestBuildBadge(t *testing.T) {
12 t.Parallel()
12 inst := startInstance(t) 13 inst := startInstance(t)
13 aliceKey := inst.newKey(t, "alice") 14 aliceKey := inst.newKey(t, "alice")
14 inst.admin(t, "admin", "user", "create", "alice", "--key", aliceKey+".pub") 15 inst.admin(t, "admin", "user", "create", "alice", "--key", aliceKey+".pub")
e2e/bin_test.go added +67
@@ -0,0 +1,67 @@
1package e2e
2
3import (
4 "os"
5 "os/exec"
6 "path/filepath"
7 "sync"
8 "testing"
9)
10
11// The suite starts an instance per test, and each one needs gitbayd. Built
12// per test, that is 200-odd links of a byte-identical 35MB binary: go's
13// build cache covers the compile but not the final link, so it cost about
14// 0.8s every time, a quarter of the whole run.
15//
16// Built once per process instead, under a directory TestMain owns. The
17// binaries outlive any single test, so t.TempDir is the wrong home for
18// them — it is removed when the test that asked for it ends.
19var (
20 gitbaydBin = &builtBin{name: "gitbayd", pkg: "gitbay.org/gitbay/cmd/gitbayd"}
21 gitbayBin = &builtBin{name: "gitbay", pkg: "gitbay.org/gitbay/cmd/gitbay"}
22 runnerBin = &builtBin{name: "gitbay-runner", pkg: "gitbay.org/gitbay/cmd/gitbay-runner"}
23)
24
25// binDir is where the built binaries live, set by TestMain.
26var binDir string
27
28type builtBin struct {
29 name string
30 pkg string
31
32 once sync.Once
33 path string
34 out []byte
35 err error
36}
37
38// get builds the binary on first use and returns the same path thereafter.
39// Lazily, so `-run TestOneThing` does not link the two binaries it has no
40// use for.
41func (b *builtBin) get(t *testing.T) string {
42 t.Helper()
43 b.once.Do(func() {
44 b.path = filepath.Join(binDir, b.name)
45 cmd := exec.Command("go", "build", "-o", b.path, b.pkg)
46 cmd.Dir = ".."
47 b.out, b.err = cmd.CombinedOutput()
48 })
49 if b.err != nil {
50 t.Fatalf("build %s: %v\n%s", b.name, b.err, b.out)
51 }
52 return b.path
53}
54
55func buildGitbayd(t *testing.T) string { return gitbaydBin.get(t) }
56func buildGitbayCLI(t *testing.T) string { return gitbayBin.get(t) }
57func buildRunner(t *testing.T) string { return runnerBin.get(t) }
58
59// makeBinDir is called by TestMain. Returned rather than deferred because
60// TestMain ends in os.Exit, which runs no deferred functions.
61func makeBinDir() (dir string, cleanup func(), err error) {
62 dir, err = os.MkdirTemp("", "e2e-bin")
63 if err != nil {
64 return "", func() {}, err
65 }
66 return dir, func() { os.RemoveAll(dir) }, nil
67}
e2e/blame_test.go +1
@@ -9,6 +9,7 @@ import (
9) 9)
10 10
11func TestBlameView(t *testing.T) { 11func TestBlameView(t *testing.T) {
12 t.Parallel()
12 inst := startInstance(t) 13 inst := startInstance(t)
13 aliceKey := inst.newKey(t, "alice") 14 aliceKey := inst.newKey(t, "alice")
14 inst.admin(t, "admin", "user", "create", "alice", 15 inst.admin(t, "admin", "user", "create", "alice",
e2e/blobrender_test.go +1
@@ -10,6 +10,7 @@ import (
10// Markdown and org files render on the blob page, with the source one 10// Markdown and org files render on the blob page, with the source one
11// click away; everything else is unchanged. 11// click away; everything else is unchanged.
12func TestBlobRendersMarkup(t *testing.T) { 12func TestBlobRendersMarkup(t *testing.T) {
13 t.Parallel()
13 inst := startInstance(t) 14 inst := startInstance(t)
14 aliceKey := inst.newKey(t, "alice") 15 aliceKey := inst.newKey(t, "alice")
15 inst.admin(t, "admin", "user", "create", "alice", "--key", aliceKey+".pub") 16 inst.admin(t, "admin", "user", "create", "alice", "--key", aliceKey+".pub")
e2e/bookmarks_test.go +2
@@ -12,6 +12,7 @@ import (
12// are something you do to someone else's repository, and the count is a 12// are something you do to someone else's repository, and the count is a
13// signal of what people found worth returning to (#146). 13// signal of what people found worth returning to (#146).
14func TestBookmarks(t *testing.T) { 14func TestBookmarks(t *testing.T) {
15 t.Parallel()
15 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n") 16 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n")
16 aliceKey := inst.newKey(t, "alice") 17 aliceKey := inst.newKey(t, "alice")
17 bobKey := inst.newKey(t, "bob") 18 bobKey := inst.newKey(t, "bob")
@@ -89,6 +90,7 @@ func TestBookmarks(t *testing.T) {
89// A repository bookmarked while public and since made private drops out 90// A repository bookmarked while public and since made private drops out
90// of the listing rather than leaking its existence. 91// of the listing rather than leaking its existence.
91func TestBookmarkOfRepoGonePrivate(t *testing.T) { 92func TestBookmarkOfRepoGonePrivate(t *testing.T) {
93 t.Parallel()
92 inst := startInstance(t) 94 inst := startInstance(t)
93 aliceKey := inst.newKey(t, "alice") 95 aliceKey := inst.newKey(t, "alice")
94 bobKey := inst.newKey(t, "bob") 96 bobKey := inst.newKey(t, "bob")
e2e/build_cancel_test.go +2
@@ -14,6 +14,7 @@ import (
14// Cancelling a duplicate of a commit that already passed puts that 14// Cancelling a duplicate of a commit that already passed puts that
15// result back on the commit. 15// result back on the commit.
16func TestBuildCancel(t *testing.T) { 16func TestBuildCancel(t *testing.T) {
17 t.Parallel()
17 inst := startInstance(t) 18 inst := startInstance(t)
18 inst.runner = buildRunner(t) 19 inst.runner = buildRunner(t)
19 aliceKey := inst.newKey(t, "alice") 20 aliceKey := inst.newKey(t, "alice")
@@ -91,6 +92,7 @@ func TestBuildCancel(t *testing.T) {
91// server closes the log session, the runner kills the step, and its late 92// server closes the log session, the runner kills the step, and its late
92// report lands on a row that already says cancelled. 93// report lands on a row that already says cancelled.
93func TestBuildCancelRunning(t *testing.T) { 94func TestBuildCancelRunning(t *testing.T) {
95 t.Parallel()
94 inst := startInstance(t) 96 inst := startInstance(t)
95 inst.runner = buildRunner(t) 97 inst.runner = buildRunner(t)
96 aliceKey := inst.newKey(t, "alice") 98 aliceKey := inst.newKey(t, "alice")
e2e/buildcancelweb_test.go +1
@@ -14,6 +14,7 @@ import (
14// real, so a stale or repeated post against a build that is no longer 14// real, so a stale or repeated post against a build that is no longer
15// cancellable shows the refusal rather than a broken page. 15// cancellable shows the refusal rather than a broken page.
16func TestBuildCancelWeb(t *testing.T) { 16func TestBuildCancelWeb(t *testing.T) {
17 t.Parallel()
17 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n") 18 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n")
18 aliceKey := inst.newKey(t, "alice") 19 aliceKey := inst.newKey(t, "alice")
19 inst.admin(t, "admin", "user", "create", "alice", 20 inst.admin(t, "admin", "user", "create", "alice",
e2e/buildorphan_test.go +1
@@ -13,6 +13,7 @@ import (
13// looking like a real failure; it is cancelled when a runner claims it, and 13// looking like a real failure; it is cancelled when a runner claims it, and
14// the runner gets the real build behind it instead, in the same poll. 14// the runner gets the real build behind it instead, in the same poll.
15func TestBuildOrphanedByForcePushCancelledAtClaim(t *testing.T) { 15func TestBuildOrphanedByForcePushCancelledAtClaim(t *testing.T) {
16 t.Parallel()
16 inst := startInstance(t) 17 inst := startInstance(t)
17 inst.runner = buildRunner(t) 18 inst.runner = buildRunner(t)
18 aliceKey := inst.newKey(t, "alice") 19 aliceKey := inst.newKey(t, "alice")
e2e/ci_dedupe_test.go +1
@@ -10,6 +10,7 @@ import (
10// A fast-forward lands the exact commit that was already built on its 10// A fast-forward lands the exact commit that was already built on its
11// branch; that commit is not built again. A commit whose build failed is. 11// branch; that commit is not built again. A commit whose build failed is.
12func TestFastForwardMergeSkipsBuiltCommit(t *testing.T) { 12func TestFastForwardMergeSkipsBuiltCommit(t *testing.T) {
13 t.Parallel()
13 inst := startInstance(t) 14 inst := startInstance(t)
14 inst.runner = buildRunner(t) 15 inst.runner = buildRunner(t)
15 aliceKey := inst.newKey(t, "alice") 16 aliceKey := inst.newKey(t, "alice")
e2e/ci_merge_test.go +1
@@ -12,6 +12,7 @@ import (
12// an MR into a repository with a CI config queued nothing, while pushing 12// an MR into a repository with a CI config queued nothing, while pushing
13// the identical commit ran the whole config. 13// the identical commit ran the whole config.
14func TestMergeQueuesBuildsAndRecordsPush(t *testing.T) { 14func TestMergeQueuesBuildsAndRecordsPush(t *testing.T) {
15 t.Parallel()
15 inst := startInstance(t) 16 inst := startInstance(t)
16 aliceKey := inst.newKey(t, "alice") 17 aliceKey := inst.newKey(t, "alice")
17 inst.admin(t, "admin", "user", "create", "alice", "--key", aliceKey+".pub", 18 inst.admin(t, "admin", "user", "create", "alice", "--key", aliceKey+".pub",
e2e/ci_nojobs_test.go +1
@@ -11,6 +11,7 @@ import (
11// the setting off. Nothing was ever going to report, so the gate has 11// the setting off. Nothing was ever going to report, so the gate has
12// nothing to wait for. 12// nothing to wait for.
13func TestRequireChecksWithoutCIConfigMerges(t *testing.T) { 13func TestRequireChecksWithoutCIConfigMerges(t *testing.T) {
14 t.Parallel()
14 inst := startInstance(t) 15 inst := startInstance(t)
15 aliceKey := inst.newKey(t, "alice") 16 aliceKey := inst.newKey(t, "alice")
16 inst.admin(t, "admin", "user", "create", "alice", "--key", aliceKey+".pub", 17 inst.admin(t, "admin", "user", "create", "alice", "--key", aliceKey+".pub",
e2e/ci_skipped_test.go +1
@@ -13,6 +13,7 @@ import (
13// filtered job now records a "skipped" status instead, which the gate reads 13// filtered job now records a "skipped" status instead, which the gate reads
14// as green (#172). 14// as green (#172).
15func TestSkippedStatusSatisfiesRequireChecks(t *testing.T) { 15func TestSkippedStatusSatisfiesRequireChecks(t *testing.T) {
16 t.Parallel()
16 inst := startInstance(t) 17 inst := startInstance(t)
17 aliceKey := inst.newKey(t, "alice") 18 aliceKey := inst.newKey(t, "alice")
18 inst.admin(t, "admin", "user", "create", "alice", "--key", aliceKey+".pub", 19 inst.admin(t, "admin", "user", "create", "alice", "--key", aliceKey+".pub",
e2e/ci_test.go +3 −11
@@ -11,17 +11,6 @@ import (
11 "time" 11 "time"
12) 12)
13 13
14func buildRunner(t *testing.T) string {
15 t.Helper()
16 bin := filepath.Join(t.TempDir(), "gitbay-runner")
17 cmd := exec.Command("go", "build", "-o", bin, "gitbay.org/gitbay/cmd/gitbay-runner")
18 cmd.Dir = ".."
19 if out, err := cmd.CombinedOutput(); err != nil {
20 t.Fatalf("build gitbay-runner: %v\n%s", err, out)
21 }
22 return bin
23}
24
25// runnerOnce processes at most one pending build with the given key. 14// runnerOnce processes at most one pending build with the given key.
26func (i *instance) runnerOnce(t *testing.T, key string, extra ...string) string { 15func (i *instance) runnerOnce(t *testing.T, key string, extra ...string) string {
27 t.Helper() 16 t.Helper()
@@ -50,6 +39,7 @@ func (i *instance) runnerOnce(t *testing.T, key string, extra ...string) string
50// A failed build mails the repo owner with the log tail; green builds 39// A failed build mails the repo owner with the log tail; green builds
51// stay silent. 40// stay silent.
52func TestBuildFailureMail(t *testing.T) { 41func TestBuildFailureMail(t *testing.T) {
42 t.Parallel()
53 smtp := startFakeSMTP(t) 43 smtp := startFakeSMTP(t)
54 inst := startInstanceWith(t, fmt.Sprintf( 44 inst := startInstanceWith(t, fmt.Sprintf(
55 "[mail]\nsmtp_host = %q\nfrom = \"noreply@gitbay.test\"\n", smtp.addr)) 45 "[mail]\nsmtp_host = %q\nfrom = \"noreply@gitbay.test\"\n", smtp.addr))
@@ -91,6 +81,7 @@ func TestBuildFailureMail(t *testing.T) {
91} 81}
92 82
93func TestCI(t *testing.T) { 83func TestCI(t *testing.T) {
84 t.Parallel()
94 inst := startInstance(t) 85 inst := startInstance(t)
95 inst.runner = buildRunner(t) 86 inst.runner = buildRunner(t)
96 aliceKey := inst.newKey(t, "alice") 87 aliceKey := inst.newKey(t, "alice")
@@ -318,6 +309,7 @@ func (i *instance) runnerJobs(t *testing.T, key, repo string, jobs int) string {
318// transaction that selects and updates, so several workers claiming 309// transaction that selects and updates, so several workers claiming
319// together is safe; the runner simply never used more than one (#115). 310// together is safe; the runner simply never used more than one (#115).
320func TestRunnerConcurrentJobs(t *testing.T) { 311func TestRunnerConcurrentJobs(t *testing.T) {
312 t.Parallel()
321 inst := startInstance(t) 313 inst := startInstance(t)
322 inst.runner = buildRunner(t) 314 inst.runner = buildRunner(t)
323 aliceKey := inst.newKey(t, "alice") 315 aliceKey := inst.newKey(t, "alice")
e2e/cipaths_test.go +2
@@ -11,6 +11,7 @@ import (
11// A doc-only push queues nothing; a push touching the named path queues 11// A doc-only push queues nothing; a push touching the named path queues
12// the job, same as before path filters existed. 12// the job, same as before path filters existed.
13func TestCIPaths(t *testing.T) { 13func TestCIPaths(t *testing.T) {
14 t.Parallel()
14 inst := startInstance(t) 15 inst := startInstance(t)
15 aliceKey := inst.newKey(t, "alice") 16 aliceKey := inst.newKey(t, "alice")
16 inst.admin(t, "admin", "user", "create", "alice", "--key", aliceKey+".pub") 17 inst.admin(t, "admin", "user", "create", "alice", "--key", aliceKey+".pub")
@@ -68,6 +69,7 @@ func TestCIPaths(t *testing.T) {
68// that first push is always a new branch. Without a merge-base fallback, 69// that first push is always a new branch. Without a merge-base fallback,
69// a docs-only branch queues the full suite anyway (#171). 70// a docs-only branch queues the full suite anyway (#171).
70func TestCIPathsNewBranch(t *testing.T) { 71func TestCIPathsNewBranch(t *testing.T) {
72 t.Parallel()
71 inst := startInstance(t) 73 inst := startInstance(t)
72 aliceKey := inst.newKey(t, "alice") 74 aliceKey := inst.newKey(t, "alice")
73 inst.admin(t, "admin", "user", "create", "alice", "--key", aliceKey+".pub") 75 inst.admin(t, "admin", "user", "create", "alice", "--key", aliceKey+".pub")
e2e/cli_test.go +1 −11
@@ -10,17 +10,6 @@ import (
10 "testing" 10 "testing"
11) 11)
12 12
13func buildGitbayCLI(t *testing.T) string {
14 t.Helper()
15 bin := filepath.Join(t.TempDir(), "gitbay")
16 cmd := exec.Command("go", "build", "-o", bin, "gitbay.org/gitbay/cmd/gitbay")
17 cmd.Dir = ".."
18 if out, err := cmd.CombinedOutput(); err != nil {
19 t.Fatalf("build gitbay: %v\n%s", err, out)
20 }
21 return bin
22}
23
24// cli runs the forge binary with an isolated config home. 13// cli runs the forge binary with an isolated config home.
25type cli struct { 14type cli struct {
26 bin string 15 bin string
@@ -66,6 +55,7 @@ func (c *cli) must(t *testing.T, dir, stdin string, args ...string) string {
66} 55}
67 56
68func TestCLI(t *testing.T) { 57func TestCLI(t *testing.T) {
58 t.Parallel()
69 inst := startInstance(t) 59 inst := startInstance(t)
70 aliceKey := inst.newKey(t, "alice") 60 aliceKey := inst.newKey(t, "alice")
71 inst.admin(t, "admin", "user", "create", "alice", 61 inst.admin(t, "admin", "user", "create", "alice",
e2e/commentmigrate_test.go +1
@@ -9,6 +9,7 @@ import (
9) 9)
10 10
11func TestMigrateCommitRefComments(t *testing.T) { 11func TestMigrateCommitRefComments(t *testing.T) {
12 t.Parallel()
12 inst := startInstance(t) 13 inst := startInstance(t)
13 aliceKey := inst.newKey(t, "alice") 14 aliceKey := inst.newKey(t, "alice")
14 inst.admin(t, "admin", "user", "create", "alice", "--key", aliceKey+".pub") 15 inst.admin(t, "admin", "user", "create", "alice", "--key", aliceKey+".pub")
e2e/commitrefs_test.go +1
@@ -8,6 +8,7 @@ import (
8) 8)
9 9
10func TestCommitMessageIssueActions(t *testing.T) { 10func TestCommitMessageIssueActions(t *testing.T) {
11 t.Parallel()
11 inst := startInstance(t) 12 inst := startInstance(t)
12 aliceKey := inst.newKey(t, "alice") 13 aliceKey := inst.newKey(t, "alice")
13 inst.admin(t, "admin", "user", "create", "alice", 14 inst.admin(t, "admin", "user", "create", "alice",
e2e/compare_test.go +1
@@ -10,6 +10,7 @@ import (
10// The compare view shows what a branch adds on top of another from their 10// The compare view shows what a branch adds on top of another from their
11// merge base, and repo diff is the same range over ssh (#118). 11// merge base, and repo diff is the same range over ssh (#118).
12func TestCompareView(t *testing.T) { 12func TestCompareView(t *testing.T) {
13 t.Parallel()
13 inst := startInstance(t) 14 inst := startInstance(t)
14 aliceKey := inst.newKey(t, "alice") 15 aliceKey := inst.newKey(t, "alice")
15 inst.admin(t, "admin", "user", "create", "alice", "--key", aliceKey+".pub") 16 inst.admin(t, "admin", "user", "create", "alice", "--key", aliceKey+".pub")
e2e/dashboard_test.go +4
@@ -9,6 +9,7 @@ import (
9) 9)
10 10
11func TestDashboard(t *testing.T) { 11func TestDashboard(t *testing.T) {
12 t.Parallel()
12 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n") 13 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n")
13 aliceKey := inst.newKey(t, "alice") 14 aliceKey := inst.newKey(t, "alice")
14 bobKey := inst.newKey(t, "bob") 15 bobKey := inst.newKey(t, "bob")
@@ -136,6 +137,7 @@ func TestDashboard(t *testing.T) {
136// dashboard — review queue, assigned/open work, pins, and activity — in 137// dashboard — review queue, assigned/open work, pins, and activity — in
137// one read. Builds remain for clients that already consume them. 138// one read. Builds remain for clients that already consume them.
138func TestDashboardCommand(t *testing.T) { 139func TestDashboardCommand(t *testing.T) {
140 t.Parallel()
139 inst := startInstance(t) 141 inst := startInstance(t)
140 aliceKey := inst.newKey(t, "alice") 142 aliceKey := inst.newKey(t, "alice")
141 bobKey := inst.newKey(t, "bob") 143 bobKey := inst.newKey(t, "bob")
@@ -286,6 +288,7 @@ func TestDashboardCommand(t *testing.T) {
286// TestDashboardQueues covers the parts of the dashboard that answer "what 288// TestDashboardQueues covers the parts of the dashboard that answer "what
287// needs me": the review queue, assigned issues, and the activity feed. 289// needs me": the review queue, assigned issues, and the activity feed.
288func TestDashboardQueues(t *testing.T) { 290func TestDashboardQueues(t *testing.T) {
291 t.Parallel()
289 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n") 292 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n")
290 aliceKey := inst.newKey(t, "alice") 293 aliceKey := inst.newKey(t, "alice")
291 bobKey := inst.newKey(t, "bob") 294 bobKey := inst.newKey(t, "bob")
@@ -361,6 +364,7 @@ func TestDashboardQueues(t *testing.T) {
361// folded line's state is the worse of the two, which the feed no longer 364// folded line's state is the worse of the two, which the feed no longer
362// draws; TestFeedLinesFoldsBuildRun covers that rule. 365// draws; TestFeedLinesFoldsBuildRun covers that rule.
363func TestDashboardFeedFoldsBuildRun(t *testing.T) { 366func TestDashboardFeedFoldsBuildRun(t *testing.T) {
367 t.Parallel()
364 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n") 368 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n")
365 inst.runner = buildRunner(t) 369 inst.runner = buildRunner(t)
366 aliceKey := inst.newKey(t, "alice") 370 aliceKey := inst.newKey(t, "alice")
e2e/dashboardbuild_test.go +1
@@ -9,6 +9,7 @@ import (
9// visible without reading the journal. It is admin-only: the exact build a 9// visible without reading the journal. It is admin-only: the exact build a
10// host runs narrows down which known issues apply to it. 10// host runs narrows down which known issues apply to it.
11func TestDashboardReportsTheServerBuild(t *testing.T) { 11func TestDashboardReportsTheServerBuild(t *testing.T) {
12 t.Parallel()
12 inst := startInstance(t) 13 inst := startInstance(t)
13 14
14 adminKey := inst.newKey(t, "root") 15 adminKey := inst.newKey(t, "root")
e2e/defaultbranch_test.go +1
@@ -11,6 +11,7 @@ import (
11// A first push of a branch other than main moves the unborn HEAD to it, 11// A first push of a branch other than main moves the unborn HEAD to it,
12// and repo settings default-branch moves it later (#189). 12// and repo settings default-branch moves it later (#189).
13func TestDefaultBranch(t *testing.T) { 13func TestDefaultBranch(t *testing.T) {
14 t.Parallel()
14 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n") 15 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n")
15 aliceKey := inst.newKey(t, "alice") 16 aliceKey := inst.newKey(t, "alice")
16 bobKey := inst.newKey(t, "bob") 17 bobKey := inst.newKey(t, "bob")
e2e/deploykey_test.go +1
@@ -9,6 +9,7 @@ import (
9) 9)
10 10
11func TestDeployKeys(t *testing.T) { 11func TestDeployKeys(t *testing.T) {
12 t.Parallel()
12 inst := startInstance(t) 13 inst := startInstance(t)
13 aliceKey := inst.newKey(t, "alice") 14 aliceKey := inst.newKey(t, "alice")
14 bobKey := inst.newKey(t, "bob") 15 bobKey := inst.newKey(t, "bob")
e2e/deps_test.go +1
@@ -20,6 +20,7 @@ type depsStatus struct {
20// can turn it on: the check tells a public registry what the repository 20// can turn it on: the check tells a public registry what the repository
21// depends on. 21// depends on.
22func TestDepsEnableDisable(t *testing.T) { 22func TestDepsEnableDisable(t *testing.T) {
23 t.Parallel()
23 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n") 24 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n")
24 aliceKey := inst.newKey(t, "alice") 25 aliceKey := inst.newKey(t, "alice")
25 inst.admin(t, "admin", "user", "create", "alice", "--key", aliceKey+".pub", 26 inst.admin(t, "admin", "user", "create", "alice", "--key", aliceKey+".pub",
e2e/description_test.go +1
@@ -6,6 +6,7 @@ import (
6) 6)
7 7
8func TestRepoDescriptions(t *testing.T) { 8func TestRepoDescriptions(t *testing.T) {
9 t.Parallel()
9 inst := startInstance(t) 10 inst := startInstance(t)
10 aliceKey := inst.newKey(t, "alice") 11 aliceKey := inst.newKey(t, "alice")
11 inst.admin(t, "admin", "user", "create", "alice", "--key", aliceKey+".pub") 12 inst.admin(t, "admin", "user", "create", "alice", "--key", aliceKey+".pub")
e2e/design_test.go +6
@@ -12,6 +12,7 @@ import (
12) 12)
13 13
14func TestReadmeRelativeLinks(t *testing.T) { 14func TestReadmeRelativeLinks(t *testing.T) {
15 t.Parallel()
15 inst := startInstance(t) 16 inst := startInstance(t)
16 aliceKey := inst.newKey(t, "alice") 17 aliceKey := inst.newKey(t, "alice")
17 inst.admin(t, "admin", "user", "create", "alice", "--key", aliceKey+".pub") 18 inst.admin(t, "admin", "user", "create", "alice", "--key", aliceKey+".pub")
@@ -177,6 +178,7 @@ func TestReadmeRelativeLinks(t *testing.T) {
177 178
178// TestLandingRoutes checks the landing page's copy and the two routes. 179// TestLandingRoutes checks the landing page's copy and the two routes.
179func TestLandingRoutes(t *testing.T) { 180func TestLandingRoutes(t *testing.T) {
181 t.Parallel()
180 smtp := startFakeSMTP(t) 182 smtp := startFakeSMTP(t)
181 inst := startInstanceWith(t, fmt.Sprintf( 183 inst := startInstanceWith(t, fmt.Sprintf(
182 "[web]\nmode = \"accounts\"\n[registration]\nmode = \"open\"\n[mail]\nsmtp_host = %q\nfrom = \"noreply@gitbay.test\"\n", 184 "[web]\nmode = \"accounts\"\n[registration]\nmode = \"open\"\n[mail]\nsmtp_host = %q\nfrom = \"noreply@gitbay.test\"\n",
@@ -204,6 +206,7 @@ func TestLandingRoutes(t *testing.T) {
204} 206}
205 207
206func TestWebInteractions(t *testing.T) { 208func TestWebInteractions(t *testing.T) {
209 t.Parallel()
207 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n") 210 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n")
208 aliceKey := inst.newKey(t, "alice") 211 aliceKey := inst.newKey(t, "alice")
209 inst.admin(t, "admin", "user", "create", "alice", "--key", aliceKey+".pub", "--email", "alice@example.test", "--verified") 212 inst.admin(t, "admin", "user", "create", "alice", "--key", aliceKey+".pub", "--email", "alice@example.test", "--verified")
@@ -271,6 +274,7 @@ func TestWebInteractions(t *testing.T) {
271} 274}
272 275
273func TestCommitParentLinks(t *testing.T) { 276func TestCommitParentLinks(t *testing.T) {
277 t.Parallel()
274 inst := startInstance(t) 278 inst := startInstance(t)
275 aliceKey := inst.newKey(t, "alice") 279 aliceKey := inst.newKey(t, "alice")
276 inst.admin(t, "admin", "user", "create", "alice", "--key", aliceKey+".pub") 280 inst.admin(t, "admin", "user", "create", "alice", "--key", aliceKey+".pub")
@@ -305,6 +309,7 @@ func TestCommitParentLinks(t *testing.T) {
305// verified here displays the account's name rather than whatever git 309// verified here displays the account's name rather than whatever git
306// config carried, and that an unknown address keeps its own name. 310// config carried, and that an unknown address keeps its own name.
307func TestAuthorNamesResolve(t *testing.T) { 311func TestAuthorNamesResolve(t *testing.T) {
312 t.Parallel()
308 inst := startInstance(t) 313 inst := startInstance(t)
309 aliceKey := inst.newKey(t, "alice") 314 aliceKey := inst.newKey(t, "alice")
310 inst.admin(t, "admin", "user", "create", "alice", 315 inst.admin(t, "admin", "user", "create", "alice",
@@ -365,6 +370,7 @@ func TestAuthorNamesResolve(t *testing.T) {
365// TestTreeSearchCodeAndClone: the overview links "Search code", not 370// TestTreeSearchCodeAndClone: the overview links "Search code", not
366// "Find file", and shows two labelled clone blocks after the file table. 371// "Find file", and shows two labelled clone blocks after the file table.
367func TestTreeSearchCodeAndClone(t *testing.T) { 372func TestTreeSearchCodeAndClone(t *testing.T) {
373 t.Parallel()
368 inst := startInstance(t) 374 inst := startInstance(t)
369 key := inst.newKey(t, "alice") 375 key := inst.newKey(t, "alice")
370 inst.admin(t, "admin", "user", "create", "alice", "--key", key+".pub") 376 inst.admin(t, "admin", "user", "create", "alice", "--key", key+".pub")
e2e/diffcomment_test.go +1
@@ -10,6 +10,7 @@ import (
10) 10)
11 11
12func TestDiffComments(t *testing.T) { 12func TestDiffComments(t *testing.T) {
13 t.Parallel()
13 inst := startInstance(t) 14 inst := startInstance(t)
14 aliceKey := inst.newKey(t, "alice") 15 aliceKey := inst.newKey(t, "alice")
15 bobKey := inst.newKey(t, "bob") 16 bobKey := inst.newKey(t, "bob")
e2e/diffweb_test.go +1
@@ -11,6 +11,7 @@ import (
11// line-number gutters, syntax highlighting, and binary files declared 11// line-number gutters, syntax highlighting, and binary files declared
12// rather than dumped. 12// rather than dumped.
13func TestDiffRendering(t *testing.T) { 13func TestDiffRendering(t *testing.T) {
14 t.Parallel()
14 inst := startInstance(t) 15 inst := startInstance(t)
15 aliceKey := inst.newKey(t, "alice") 16 aliceKey := inst.newKey(t, "alice")
16 inst.admin(t, "admin", "user", "create", "alice", "--key", aliceKey+".pub") 17 inst.admin(t, "admin", "user", "create", "alice", "--key", aliceKey+".pub")
e2e/disabled_test.go +1
@@ -10,6 +10,7 @@ import (
10// listener checked the flag and disabling deleted sessions but not tokens 10// listener checked the flag and disabling deleted sessions but not tokens
11// (#95). 11// (#95).
12func TestDisabledAccountAPI(t *testing.T) { 12func TestDisabledAccountAPI(t *testing.T) {
13 t.Parallel()
13 inst := startInstanceWith(t, "[api]\nenabled = true\n") 14 inst := startInstanceWith(t, "[api]\nenabled = true\n")
14 aliceKey := inst.newKey(t, "alice") 15 aliceKey := inst.newKey(t, "alice")
15 inst.admin(t, "admin", "user", "create", "alice", 16 inst.admin(t, "admin", "user", "create", "alice",
e2e/edit_test.go +1
@@ -10,6 +10,7 @@ import (
10) 10)
11 11
12func TestIssueMREditing(t *testing.T) { 12func TestIssueMREditing(t *testing.T) {
13 t.Parallel()
13 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n") 14 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n")
14 aliceKey := inst.newKey(t, "alice") 15 aliceKey := inst.newKey(t, "alice")
15 bobKey := inst.newKey(t, "bob") 16 bobKey := inst.newKey(t, "bob")
e2e/effectiveaccess_test.go +1
@@ -9,6 +9,7 @@ import (
9// outsider gets one answer about an org: it exists and has members; 9// outsider gets one answer about an org: it exists and has members;
10// its teams are not theirs to see (#200). 10// its teams are not theirs to see (#200).
11func TestEffectiveAccessAndOutsiders(t *testing.T) { 11func TestEffectiveAccessAndOutsiders(t *testing.T) {
12 t.Parallel()
12 inst := startInstance(t) 13 inst := startInstance(t)
13 keys := map[string]string{} 14 keys := map[string]string{}
14 for _, u := range []string{"alice", "bob", "carol", "dave", "eve"} { 15 for _, u := range []string{"alice", "bob", "carol", "dave", "eve"} {
e2e/emaillogin_test.go +6
@@ -11,6 +11,7 @@ import (
11// A person with no SSH key can still get into the web UI: they ask for a 11// A person with no SSH key can still get into the web UI: they ask for a
12// link by username or verified address and it arrives by mail (#155). 12// link by username or verified address and it arrives by mail (#155).
13func TestEmailLogin(t *testing.T) { 13func TestEmailLogin(t *testing.T) {
14 t.Parallel()
14 smtp := startFakeSMTP(t) 15 smtp := startFakeSMTP(t)
15 inst := startInstanceWith(t, fmt.Sprintf( 16 inst := startInstanceWith(t, fmt.Sprintf(
16 "[web]\nmode = \"accounts\"\n[mail]\nsmtp_host = %q\nfrom = \"noreply@gitbay.test\"\n", 17 "[web]\nmode = \"accounts\"\n[mail]\nsmtp_host = %q\nfrom = \"noreply@gitbay.test\"\n",
@@ -71,6 +72,7 @@ func TestEmailLogin(t *testing.T) {
71// A verified secondary address stands in for an unverified primary: 72// A verified secondary address stands in for an unverified primary:
72// resolution by username must not stop at the primary (#158). 73// resolution by username must not stop at the primary (#158).
73func TestEmailLoginResolvesVerifiedSecondary(t *testing.T) { 74func TestEmailLoginResolvesVerifiedSecondary(t *testing.T) {
75 t.Parallel()
74 smtp := startFakeSMTP(t) 76 smtp := startFakeSMTP(t)
75 inst := startInstanceWith(t, fmt.Sprintf( 77 inst := startInstanceWith(t, fmt.Sprintf(
76 "[web]\nmode = \"accounts\"\n[mail]\nsmtp_host = %q\nfrom = \"noreply@gitbay.test\"\n", 78 "[web]\nmode = \"accounts\"\n[mail]\nsmtp_host = %q\nfrom = \"noreply@gitbay.test\"\n",
@@ -108,6 +110,7 @@ func TestEmailLoginResolvesVerifiedSecondary(t *testing.T) {
108// The response must not say whether an account exists. A different status, 110// The response must not say whether an account exists. A different status,
109// body, or destination answers "is this person here?" to anyone who asks. 111// body, or destination answers "is this person here?" to anyone who asks.
110func TestEmailLoginDoesNotEnumerate(t *testing.T) { 112func TestEmailLoginDoesNotEnumerate(t *testing.T) {
113 t.Parallel()
111 smtp := startFakeSMTP(t) 114 smtp := startFakeSMTP(t)
112 inst := startInstanceWith(t, fmt.Sprintf( 115 inst := startInstanceWith(t, fmt.Sprintf(
113 "[web]\nmode = \"accounts\"\n[mail]\nsmtp_host = %q\nfrom = \"noreply@gitbay.test\"\n", 116 "[web]\nmode = \"accounts\"\n[mail]\nsmtp_host = %q\nfrom = \"noreply@gitbay.test\"\n",
@@ -175,6 +178,7 @@ func TestEmailLoginDoesNotEnumerate(t *testing.T) {
175// An anonymous endpoint that sends mail needs a durable per-account bound, 178// An anonymous endpoint that sends mail needs a durable per-account bound,
176// the same one email verification has (#136). 179// the same one email verification has (#136).
177func TestEmailLoginThrottled(t *testing.T) { 180func TestEmailLoginThrottled(t *testing.T) {
181 t.Parallel()
178 smtp := startFakeSMTP(t) 182 smtp := startFakeSMTP(t)
179 inst := startInstanceWith(t, fmt.Sprintf( 183 inst := startInstanceWith(t, fmt.Sprintf(
180 "[web]\nmode = \"accounts\"\n[mail]\nsmtp_host = %q\nfrom = \"noreply@gitbay.test\"\n", 184 "[web]\nmode = \"accounts\"\n[mail]\nsmtp_host = %q\nfrom = \"noreply@gitbay.test\"\n",
@@ -220,6 +224,7 @@ func TestEmailLoginThrottled(t *testing.T) {
220// itself a link. It must not get a session out of it: read access to the 224// itself a link. It must not get a session out of it: read access to the
221// private repos it is a member of is what suspension takes away. 225// private repos it is a member of is what suspension takes away.
222func TestEmailLoginRefusesDisabledAccount(t *testing.T) { 226func TestEmailLoginRefusesDisabledAccount(t *testing.T) {
227 t.Parallel()
223 smtp := startFakeSMTP(t) 228 smtp := startFakeSMTP(t)
224 inst := startInstanceWith(t, fmt.Sprintf( 229 inst := startInstanceWith(t, fmt.Sprintf(
225 "[web]\nmode = \"accounts\"\n[mail]\nsmtp_host = %q\nfrom = \"noreply@gitbay.test\"\n", 230 "[web]\nmode = \"accounts\"\n[mail]\nsmtp_host = %q\nfrom = \"noreply@gitbay.test\"\n",
@@ -256,6 +261,7 @@ func TestEmailLoginRefusesDisabledAccount(t *testing.T) {
256// login tokens, and login() refuses a disabled account after consuming one, 261// login tokens, and login() refuses a disabled account after consuming one,
257// so neither the window nor a token that somehow survives it opens a session. 262// so neither the window nor a token that somehow survives it opens a session.
258func TestEmailLoginRefusesLinkMintedBeforeSuspension(t *testing.T) { 263func TestEmailLoginRefusesLinkMintedBeforeSuspension(t *testing.T) {
264 t.Parallel()
259 smtp := startFakeSMTP(t) 265 smtp := startFakeSMTP(t)
260 inst := startInstanceWith(t, fmt.Sprintf( 266 inst := startInstanceWith(t, fmt.Sprintf(
261 "[web]\nmode = \"accounts\"\n[mail]\nsmtp_host = %q\nfrom = \"noreply@gitbay.test\"\n", 267 "[web]\nmode = \"accounts\"\n[mail]\nsmtp_host = %q\nfrom = \"noreply@gitbay.test\"\n",
e2e/emailremove_test.go +1
@@ -11,6 +11,7 @@ import (
11// store holds: the primary stays until another is primary, and the last 11// store holds: the primary stays until another is primary, and the last
12// verified address stays (#181). 12// verified address stays (#181).
13func TestEmailRemoveAndPrimary(t *testing.T) { 13func TestEmailRemoveAndPrimary(t *testing.T) {
14 t.Parallel()
14 smtp := startFakeSMTP(t) 15 smtp := startFakeSMTP(t)
15 inst := startInstanceWith(t, fmt.Sprintf( 16 inst := startInstanceWith(t, fmt.Sprintf(
16 "[mail]\nsmtp_host = %q\nfrom = \"noreply@gitbay.test\"\n", smtp.addr)) 17 "[mail]\nsmtp_host = %q\nfrom = \"noreply@gitbay.test\"\n", smtp.addr))
e2e/emailthrottle_test.go +1
@@ -10,6 +10,7 @@ import (
10// rate-limited; this path was not, so an authenticated account could 10// rate-limited; this path was not, so an authenticated account could
11// enqueue mail without bound (#136). 11// enqueue mail without bound (#136).
12func TestEmailAddThrottled(t *testing.T) { 12func TestEmailAddThrottled(t *testing.T) {
13 t.Parallel()
13 smtp := startFakeSMTP(t) 14 smtp := startFakeSMTP(t)
14 inst := startInstanceWith(t, fmt.Sprintf( 15 inst := startInstanceWith(t, fmt.Sprintf(
15 "[mail]\nsmtp_host = %q\nfrom = \"noreply@gitbay.test\"\n", smtp.addr)) 16 "[mail]\nsmtp_host = %q\nfrom = \"noreply@gitbay.test\"\n", smtp.addr))
e2e/facts_test.go +2
@@ -12,6 +12,7 @@ import (
12// TestRepoFacts covers the repository summary: counts, license, languages, 12// TestRepoFacts covers the repository summary: counts, license, languages,
13// and contributors resolved to accounts where the email is verified. 13// and contributors resolved to accounts where the email is verified.
14func TestRepoFacts(t *testing.T) { 14func TestRepoFacts(t *testing.T) {
15 t.Parallel()
15 inst := startInstance(t) 16 inst := startInstance(t)
16 aliceKey := inst.newKey(t, "alice") 17 aliceKey := inst.newKey(t, "alice")
17 inst.admin(t, "admin", "user", "create", "alice", 18 inst.admin(t, "admin", "user", "create", "alice",
@@ -102,6 +103,7 @@ WITH REGARD TO THIS SOFTWARE.
102// account has verified here. Addresses with neither stay distinct, even 103// account has verified here. Addresses with neither stay distinct, even
103// when they carry the same name. 104// when they carry the same name.
104func TestContributorIdentity(t *testing.T) { 105func TestContributorIdentity(t *testing.T) {
106 t.Parallel()
105 smtp := startFakeSMTP(t) 107 smtp := startFakeSMTP(t)
106 inst := startInstanceWith(t, fmt.Sprintf( 108 inst := startInstanceWith(t, fmt.Sprintf(
107 "[mail]\nsmtp_host = %q\nfrom = \"noreply@gitbay.test\"\n", smtp.addr)) 109 "[mail]\nsmtp_host = %q\nfrom = \"noreply@gitbay.test\"\n", smtp.addr))
e2e/feeds_test.go +1
@@ -11,6 +11,7 @@ import (
11// Atom feeds for releases, commits and an owner's public activity, read 11// Atom feeds for releases, commits and an owner's public activity, read
12// with no session; private repositories stay out of them (#192). 12// with no session; private repositories stay out of them (#192).
13func TestAtomFeeds(t *testing.T) { 13func TestAtomFeeds(t *testing.T) {
14 t.Parallel()
14 inst := startInstance(t) 15 inst := startInstance(t)
15 aliceKey := inst.newKey(t, "alice") 16 aliceKey := inst.newKey(t, "alice")
16 inst.admin(t, "admin", "user", "create", "alice", "--key", aliceKey+".pub") 17 inst.admin(t, "admin", "user", "create", "alice", "--key", aliceKey+".pub")
e2e/filenav_test.go +1
@@ -10,6 +10,7 @@ import (
10// A file page lists its directory beside the file, marks the file, and 10// A file page lists its directory beside the file, marks the file, and
11// links up (desktop layout spec). 11// links up (desktop layout spec).
12func TestFileNavigator(t *testing.T) { 12func TestFileNavigator(t *testing.T) {
13 t.Parallel()
13 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n") 14 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n")
14 key := inst.newKey(t, "alice") 15 key := inst.newKey(t, "alice")
15 inst.admin(t, "admin", "user", "create", "alice", "--key", key+".pub") 16 inst.admin(t, "admin", "user", "create", "alice", "--key", key+".pub")
e2e/forkweb_test.go +1
@@ -12,6 +12,7 @@ import (
12// file in the fork, open the merge request against the parent, without a 12// file in the fork, open the merge request against the parent, without a
13// terminal at any step (#174). 13// terminal at any step (#174).
14func TestForkWeb(t *testing.T) { 14func TestForkWeb(t *testing.T) {
15 t.Parallel()
15 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n") 16 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n")
16 aliceKey := inst.newKey(t, "alice") 17 aliceKey := inst.newKey(t, "alice")
17 bobKey := inst.newKey(t, "bob") 18 bobKey := inst.newKey(t, "bob")
e2e/formatpickerweb_test.go +1
@@ -12,6 +12,7 @@ import (
12// request create forms (#160). Neither form offered --format before; a 12// request create forms (#160). Neither form offered --format before; a
13// browser session could only ever write markdown. 13// browser session could only ever write markdown.
14func TestIssueMRWebFormat(t *testing.T) { 14func TestIssueMRWebFormat(t *testing.T) {
15 t.Parallel()
15 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n") 16 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n")
16 aliceKey := inst.newKey(t, "alice") 17 aliceKey := inst.newKey(t, "alice")
17 inst.admin(t, "admin", "user", "create", "alice", 18 inst.admin(t, "admin", "user", "create", "alice",
e2e/gates_test.go +1
@@ -11,6 +11,7 @@ import (
11// unmet gate at once, and mr review says when a verdict is advisory 11// unmet gate at once, and mr review says when a verdict is advisory
12// (#199). 12// (#199).
13func TestMergeGatesVisible(t *testing.T) { 13func TestMergeGatesVisible(t *testing.T) {
14 t.Parallel()
14 inst := startInstance(t) 15 inst := startInstance(t)
15 aliceKey := inst.newKey(t, "alice") 16 aliceKey := inst.newKey(t, "alice")
16 bobKey := inst.newKey(t, "bob") 17 bobKey := inst.newKey(t, "bob")
e2e/ghimport_test.go +3
@@ -72,6 +72,7 @@ func fakeGitHub(t *testing.T) *httptest.Server {
72} 72}
73 73
74func TestGitHubIssueImport(t *testing.T) { 74func TestGitHubIssueImport(t *testing.T) {
75 t.Parallel()
75 // allow_local lets --api-base reach the loopback fake; a default 76 // allow_local lets --api-base reach the loopback fake; a default
76 // instance refuses it (see the SSRF check at the end). 77 // instance refuses it (see the SSRF check at the end).
77 inst := startInstanceWith(t, "[webhooks]\nallow_local = true\n") 78 inst := startInstanceWith(t, "[webhooks]\nallow_local = true\n")
@@ -143,6 +144,7 @@ func TestGitHubIssueImport(t *testing.T) {
143} 144}
144 145
145func TestGitHubImportSSRFGuard(t *testing.T) { 146func TestGitHubImportSSRFGuard(t *testing.T) {
147 t.Parallel()
146 inst := startInstance(t) // allow_local off: default posture 148 inst := startInstance(t) // allow_local off: default posture
147 aliceKey := inst.newKey(t, "alice") 149 aliceKey := inst.newKey(t, "alice")
148 inst.admin(t, "admin", "user", "create", "alice", "--key", aliceKey+".pub") 150 inst.admin(t, "admin", "user", "create", "alice", "--key", aliceKey+".pub")
@@ -219,6 +221,7 @@ func fakeForgejo(t *testing.T) *httptest.Server {
219} 221}
220 222
221func TestForgejoIssueImport(t *testing.T) { 223func TestForgejoIssueImport(t *testing.T) {
224 t.Parallel()
222 inst := startInstanceWith(t, "[webhooks]\nallow_local = true\n") 225 inst := startInstanceWith(t, "[webhooks]\nallow_local = true\n")
223 aliceKey := inst.newKey(t, "alice") 226 aliceKey := inst.newKey(t, "alice")
224 inst.admin(t, "admin", "user", "create", "alice", "--key", aliceKey+".pub") 227 inst.admin(t, "admin", "user", "create", "alice", "--key", aliceKey+".pub")
e2e/git_test.go +1
@@ -55,6 +55,7 @@ func mustGit(t *testing.T, dir string, env []string, args ...string) string {
55} 55}
56 56
57func TestGitOverSSH(t *testing.T) { 57func TestGitOverSSH(t *testing.T) {
58 t.Parallel()
58 inst := startInstance(t) 59 inst := startInstance(t)
59 60
60 aliceKey := inst.newKey(t, "alice") 61 aliceKey := inst.newKey(t, "alice")
e2e/goimport_test.go +1
@@ -6,6 +6,7 @@ import (
6) 6)
7 7
8func TestGoImportVanity(t *testing.T) { 8func TestGoImportVanity(t *testing.T) {
9 t.Parallel()
9 inst := startInstanceWith(t, "[go_import]\n\"127.0.0.1/tool\" = \"alice/tool\"\n") 10 inst := startInstanceWith(t, "[go_import]\n\"127.0.0.1/tool\" = \"alice/tool\"\n")
10 aliceKey := inst.newKey(t, "alice") 11 aliceKey := inst.newKey(t, "alice")
11 inst.admin(t, "admin", "user", "create", "alice", "--key", aliceKey+".pub") 12 inst.admin(t, "admin", "user", "create", "alice", "--key", aliceKey+".pub")
e2e/http_test.go +2
@@ -64,6 +64,7 @@ func (i *instance) httpURL(repo string) string {
64} 64}
65 65
66func TestHTTPTransport(t *testing.T) { 66func TestHTTPTransport(t *testing.T) {
67 t.Parallel()
67 inst := startInstance(t) 68 inst := startInstance(t)
68 aliceKey := setupPublicRepo(t, inst, "alice/pub") 69 aliceKey := setupPublicRepo(t, inst, "alice/pub")
69 70
@@ -127,6 +128,7 @@ func TestHTTPTransport(t *testing.T) {
127} 128}
128 129
129func TestGitDaemon(t *testing.T) { 130func TestGitDaemon(t *testing.T) {
131 t.Parallel()
130 inst := startInstance(t) 132 inst := startInstance(t)
131 aliceKey := setupPublicRepo(t, inst, "alice/pub") 133 aliceKey := setupPublicRepo(t, inst, "alice/pub")
132 gitURL := func(repo string) string { 134 gitURL := func(repo string) string {
e2e/import_test.go +1
@@ -9,6 +9,7 @@ import (
9) 9)
10 10
11func TestRepoImport(t *testing.T) { 11func TestRepoImport(t *testing.T) {
12 t.Parallel()
12 inst := startInstance(t) 13 inst := startInstance(t)
13 aliceKey := inst.newKey(t, "alice") 14 aliceKey := inst.newKey(t, "alice")
14 inst.admin(t, "admin", "user", "create", "alice", 15 inst.admin(t, "admin", "user", "create", "alice",
e2e/inbox_test.go +1
@@ -41,6 +41,7 @@ func notices(t *testing.T, inst *instance, key string, args ...string) []struct
41} 41}
42 42
43func TestNotificationInbox(t *testing.T) { 43func TestNotificationInbox(t *testing.T) {
44 t.Parallel()
44 inst := startInstance(t) 45 inst := startInstance(t)
45 46
46 aliceKey := inst.newKey(t, "alice") 47 aliceKey := inst.newKey(t, "alice")
e2e/isolation_podman_test.go +5
@@ -46,6 +46,7 @@ func havePodman(t *testing.T) bool {
46// the runner refuses to start rather than running a build on the host. 46// the runner refuses to start rather than running a build on the host.
47// This one needs no podman, so it runs everywhere. 47// This one needs no podman, so it runs everywhere.
48func TestRunnerRefusesToStartWithoutPodman(t *testing.T) { 48func TestRunnerRefusesToStartWithoutPodman(t *testing.T) {
49 t.Parallel()
49 bin := buildRunner(t) 50 bin := buildRunner(t)
50 cmd := exec.Command(bin, "-once", "-remote", "git@127.0.0.1", 51 cmd := exec.Command(bin, "-once", "-remote", "git@127.0.0.1",
51 "-isolation", "podman", "-image", "localhost/whatever:1", "-workdir", t.TempDir()) 52 "-isolation", "podman", "-image", "localhost/whatever:1", "-workdir", t.TempDir())
@@ -66,6 +67,7 @@ func TestRunnerRefusesToStartWithoutPodman(t *testing.T) {
66 67
67// An unknown mode is refused rather than guessed at. 68// An unknown mode is refused rather than guessed at.
68func TestRunnerRefusesUnknownIsolation(t *testing.T) { 69func TestRunnerRefusesUnknownIsolation(t *testing.T) {
70 t.Parallel()
69 bin := buildRunner(t) 71 bin := buildRunner(t)
70 cmd := exec.Command(bin, "-once", "-remote", "git@127.0.0.1", 72 cmd := exec.Command(bin, "-once", "-remote", "git@127.0.0.1",
71 "-isolation", "chroot", "-workdir", t.TempDir()) 73 "-isolation", "chroot", "-workdir", t.TempDir())
@@ -81,6 +83,7 @@ func TestRunnerRefusesUnknownIsolation(t *testing.T) {
81// With podman, a step runs in a container: it cannot read the runner's 83// With podman, a step runs in a container: it cannot read the runner's
82// SSH key, and it does not see the runner's home. 84// SSH key, and it does not see the runner's home.
83func TestPodmanStepCannotReachTheRunnersKey(t *testing.T) { 85func TestPodmanStepCannotReachTheRunnersKey(t *testing.T) {
86 t.Parallel()
84 if !havePodman(t) { 87 if !havePodman(t) {
85 t.Skip("no podman") 88 t.Skip("no podman")
86 } 89 }
@@ -131,6 +134,7 @@ func TestPodmanStepCannotReachTheRunnersKey(t *testing.T) {
131// An image this runner does not have fails the build and says an 134// An image this runner does not have fails the build and says an
132// operator must provision it, rather than pulling it. 135// operator must provision it, rather than pulling it.
133func TestPodmanMissingImageFailsTheBuild(t *testing.T) { 136func TestPodmanMissingImageFailsTheBuild(t *testing.T) {
137 t.Parallel()
134 if !havePodman(t) { 138 if !havePodman(t) {
135 t.Skip("no podman") 139 t.Skip("no podman")
136 } 140 }
@@ -192,6 +196,7 @@ func runnerPodmanOnce(t *testing.T, inst *instance, key string) {
192// guessing one: with --pull=never an image the host does not have fails 196// guessing one: with --pull=never an image the host does not have fails
193// every job that names none. 197// every job that names none.
194func TestRunnerRefusesPodmanWithoutAnImage(t *testing.T) { 198func TestRunnerRefusesPodmanWithoutAnImage(t *testing.T) {
199 t.Parallel()
195 bin := buildRunner(t) 200 bin := buildRunner(t)
196 cmd := exec.Command(bin, "-once", "-remote", "git@127.0.0.1", 201 cmd := exec.Command(bin, "-once", "-remote", "git@127.0.0.1",
197 "-isolation", "podman", "-workdir", t.TempDir()) 202 "-isolation", "podman", "-workdir", t.TempDir())
e2e/isolation_test.go +1
@@ -19,6 +19,7 @@ import (
19// of surfaces is the thing under test, so adding a route without adding 19// of surfaces is the thing under test, so adding a route without adding
20// it here is the omission that shows up. 20// it here is the omission that shows up.
21func TestPrivateRepoIsInvisible(t *testing.T) { 21func TestPrivateRepoIsInvisible(t *testing.T) {
22 t.Parallel()
22 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n") 23 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n")
23 ownerKey := inst.newKey(t, "owner") 24 ownerKey := inst.newKey(t, "owner")
24 strangerKey := inst.newKey(t, "stranger") 25 strangerKey := inst.newKey(t, "stranger")
e2e/issue_test.go +1
@@ -24,6 +24,7 @@ func golden(t *testing.T, raw string) string {
24} 24}
25 25
26func TestIssueLifecycleOverBareSSH(t *testing.T) { 26func TestIssueLifecycleOverBareSSH(t *testing.T) {
27 t.Parallel()
27 inst := startInstance(t) 28 inst := startInstance(t)
28 29
29 aliceKey := inst.newKey(t, "alice") 30 aliceKey := inst.newKey(t, "alice")
e2e/issuesearch_test.go +1
@@ -29,6 +29,7 @@ func issueNumbers(t *testing.T, inst *instance, key string, args ...string) []in
29} 29}
30 30
31func TestIssueSearch(t *testing.T) { 31func TestIssueSearch(t *testing.T) {
32 t.Parallel()
32 inst := startInstance(t) 33 inst := startInstance(t)
33 aliceKey := inst.newKey(t, "alice") 34 aliceKey := inst.newKey(t, "alice")
34 inst.admin(t, "admin", "user", "create", "alice", "--key", aliceKey+".pub") 35 inst.admin(t, "admin", "user", "create", "alice", "--key", aliceKey+".pub")
e2e/issueweb_test.go +1
@@ -10,6 +10,7 @@ import (
10// the browser. Each action runs the issue command the CLI runs, so the 10// the browser. Each action runs the issue command the CLI runs, so the
11// CLI is the check that they took effect. 11// CLI is the check that they took effect.
12func TestIssueWebTriage(t *testing.T) { 12func TestIssueWebTriage(t *testing.T) {
13 t.Parallel()
13 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n") 14 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n")
14 aliceKey := inst.newKey(t, "alice") 15 aliceKey := inst.newKey(t, "alice")
15 bobKey := inst.newKey(t, "bob") 16 bobKey := inst.newKey(t, "bob")
e2e/label_test.go +1
@@ -8,6 +8,7 @@ import (
8// Labels get colours: set from the CLI, listed with their use, painted 8// Labels get colours: set from the CLI, listed with their use, painted
9// on the web, and removed from every issue at once. 9// on the web, and removed from every issue at once.
10func TestLabelColors(t *testing.T) { 10func TestLabelColors(t *testing.T) {
11 t.Parallel()
11 inst := startInstance(t) 12 inst := startInstance(t)
12 aliceKey := inst.newKey(t, "alice") 13 aliceKey := inst.newKey(t, "alice")
13 bobKey := inst.newKey(t, "bob") 14 bobKey := inst.newKey(t, "bob")
e2e/labelweb_test.go +1
@@ -9,6 +9,7 @@ import (
9// The label set itself is managed from the browser: create, recolour and 9// The label set itself is managed from the browser: create, recolour and
10// remove, each through the label command the CLI runs (#163). 10// remove, each through the label command the CLI runs (#163).
11func TestLabelsWeb(t *testing.T) { 11func TestLabelsWeb(t *testing.T) {
12 t.Parallel()
12 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n") 13 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n")
13 aliceKey := inst.newKey(t, "alice") 14 aliceKey := inst.newKey(t, "alice")
14 bobKey := inst.newKey(t, "bob") 15 bobKey := inst.newKey(t, "bob")
e2e/lfs_test.go +1
@@ -34,6 +34,7 @@ func waitForPort(t *testing.T, port int) {
34} 34}
35 35
36func TestLFS(t *testing.T) { 36func TestLFS(t *testing.T) {
37 t.Parallel()
37 if _, err := exec.LookPath("git-lfs"); err != nil { 38 if _, err := exec.LookPath("git-lfs"); err != nil {
38 t.Skip("git-lfs client not installed") 39 t.Skip("git-lfs client not installed")
39 } 40 }
e2e/listfilters_test.go +1
@@ -10,6 +10,7 @@ import (
10// issue list and mr list narrow by label, assignee, author and 10// issue list and mr list narrow by label, assignee, author and
11// milestone, and the web lists take the same names as query parameters. 11// milestone, and the web lists take the same names as query parameters.
12func TestListFilters(t *testing.T) { 12func TestListFilters(t *testing.T) {
13 t.Parallel()
13 inst := startInstance(t) 14 inst := startInstance(t)
14 aliceKey := inst.newKey(t, "alice") 15 aliceKey := inst.newKey(t, "alice")
15 bobKey := inst.newKey(t, "bob") 16 bobKey := inst.newKey(t, "bob")
e2e/mailpref_test.go +1
@@ -12,6 +12,7 @@ import (
12// mail; on brings it back. The account page carries the same switch 12// mail; on brings it back. The account page carries the same switch
13// (#194). 13// (#194).
14func TestMailPreference(t *testing.T) { 14func TestMailPreference(t *testing.T) {
15 t.Parallel()
15 smtp := startFakeSMTP(t) 16 smtp := startFakeSMTP(t)
16 inst := startInstanceWith(t, fmt.Sprintf( 17 inst := startInstanceWith(t, fmt.Sprintf(
17 "[mail]\nsmtp_host = %q\nfrom = \"noreply@gitbay.test\"\n[web]\nmode = \"accounts\"\n", smtp.addr)) 18 "[mail]\nsmtp_host = %q\nfrom = \"noreply@gitbay.test\"\n[web]\nmode = \"accounts\"\n", smtp.addr))
e2e/main_test.go +8 −28
@@ -1,39 +1,19 @@
1package e2e 1package e2e
2 2
3import ( 3import (
4 "flag"
5 "fmt" 4 "fmt"
6 "os" 5 "os"
7 "testing" 6 "testing"
8 "time"
9) 7)
10 8
11// This suite drives real git, ssh, sshd and gpg, and takes six to fifteen
12// minutes depending on the machine — past `go test`'s ten-minute default.
13// Exceeding it panics with the name of whichever test happened to be
14// running, which is never the one at fault and reads like a hang (#143).
15//
16// make test passes -timeout 30m and .gitbay/ci.yml passes -timeout 20m.
17// A bare `go test ./...` gets the default, so say so up front rather than
18// eleven minutes later.
19const minTimeout = 15 * time.Minute
20
21func TestMain(m *testing.M) { 9func TestMain(m *testing.M) {
22 flag.Parse() 10 dir, cleanup, err := makeBinDir()
23 // Only for a whole-package run: `-run TestOneThing -timeout 2m` is a 11 if err != nil {
24 // reasonable thing to type and none of this applies to it. 12 fmt.Fprintf(os.Stderr, "e2e: %v\n", err)
25 if run := flag.Lookup("test.run"); run == nil || run.Value.String() == "" { 13 os.Exit(1)
26 if f := flag.Lookup("test.timeout"); f != nil {
27 d, err := time.ParseDuration(f.Value.String())
28 if err == nil && d > 0 && d < minTimeout {
29 fmt.Fprintf(os.Stderr,
30 "e2e: -timeout is %s; this suite needs about %s.\n"+
31 "Run `make test` (which passes -timeout 30m), or pass -timeout yourself.\n"+
32 "Without this check the run panics part way through and blames whichever\n"+
33 "test was executing at the deadline.\n", d, minTimeout)
34 os.Exit(1)
35 }
36 }
37 } 14 }
38 os.Exit(m.Run()) 15 binDir = dir
16 code := m.Run()
17 cleanup()
18 os.Exit(code)
39} 19}
e2e/maint_test.go +1
@@ -9,6 +9,7 @@ import (
9) 9)
10 10
11func TestAdminGCAndStats(t *testing.T) { 11func TestAdminGCAndStats(t *testing.T) {
12 t.Parallel()
12 inst := startInstance(t) 13 inst := startInstance(t)
13 aliceKey := inst.newKey(t, "alice") 14 aliceKey := inst.newKey(t, "alice")
14 inst.admin(t, "admin", "user", "create", "alice", "--key", aliceKey+".pub") 15 inst.admin(t, "admin", "user", "create", "alice", "--key", aliceKey+".pub")
e2e/mentions_test.go +1
@@ -10,6 +10,7 @@ import (
10// mute holds, and someone who cannot read the repository is not reached 10// mute holds, and someone who cannot read the repository is not reached
11// (#202). 11// (#202).
12func TestMentionsNotify(t *testing.T) { 12func TestMentionsNotify(t *testing.T) {
13 t.Parallel()
13 inst := startInstance(t) 14 inst := startInstance(t)
14 keys := map[string]string{} 15 keys := map[string]string{}
15 for _, u := range []string{"alice", "bob", "carol", "eve"} { 16 for _, u := range []string{"alice", "bob", "carol", "eve"} {
e2e/merge_strategies_test.go +1
@@ -8,6 +8,7 @@ import (
8) 8)
9 9
10func TestSquashAndRebaseMerges(t *testing.T) { 10func TestSquashAndRebaseMerges(t *testing.T) {
11 t.Parallel()
11 inst := startInstance(t) 12 inst := startInstance(t)
12 aliceKey := inst.newKey(t, "alice") 13 aliceKey := inst.newKey(t, "alice")
13 bobKey := inst.newKey(t, "bob") 14 bobKey := inst.newKey(t, "bob")
e2e/mergerecord_test.go +1
@@ -12,6 +12,7 @@ import (
12// or a merge moved the ref and then failed to record itself. Either way 12// or a merge moved the ref and then failed to record itself. Either way
13// refusing left the merge request open for good (#108). 13// refusing left the merge request open for good (#108).
14func TestMergeRecordedWhenTargetContainsHead(t *testing.T) { 14func TestMergeRecordedWhenTargetContainsHead(t *testing.T) {
15 t.Parallel()
15 inst := startInstance(t) 16 inst := startInstance(t)
16 aliceKey := inst.newKey(t, "alice") 17 aliceKey := inst.newKey(t, "alice")
17 inst.admin(t, "admin", "user", "create", "alice", "--key", aliceKey+".pub") 18 inst.admin(t, "admin", "user", "create", "alice", "--key", aliceKey+".pub")
e2e/migrate_test.go +1
@@ -8,6 +8,7 @@ import (
8) 8)
9 9
10func TestAccountMigration(t *testing.T) { 10func TestAccountMigration(t *testing.T) {
11 t.Parallel()
11 src := startInstance(t) 12 src := startInstance(t)
12 dst := startInstance(t) 13 dst := startInstance(t)
13 key := src.newKey(t, "alice") // one identity, both instances 14 key := src.newKey(t, "alice") // one identity, both instances
e2e/migratemr_test.go +1
@@ -17,6 +17,7 @@ import (
17// diff resolves through it and an empty head_sha is invisible — the first 17// diff resolves through it and an empty head_sha is invisible — the first
18// version of this test made that mistake and passed without the fix. 18// version of this test made that mistake and passed without the fix.
19func TestMigratedMRHasADiff(t *testing.T) { 19func TestMigratedMRHasADiff(t *testing.T) {
20 t.Parallel()
20 src := startInstance(t) 21 src := startInstance(t)
21 dst := startInstance(t) 22 dst := startInstance(t)
22 key := src.newKey(t, "alice") 23 key := src.newKey(t, "alice")
e2e/milestone_test.go +1
@@ -9,6 +9,7 @@ import (
9) 9)
10 10
11func TestMilestonesAndTemplates(t *testing.T) { 11func TestMilestonesAndTemplates(t *testing.T) {
12 t.Parallel()
12 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n") 13 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n")
13 aliceKey := inst.newKey(t, "alice") 14 aliceKey := inst.newKey(t, "alice")
14 bobKey := inst.newKey(t, "bob") 15 bobKey := inst.newKey(t, "bob")
e2e/mirror_test.go +1
@@ -186,6 +186,7 @@ func TestMirrors(t *testing.T) {
186} 186}
187 187
188func TestMirrorSSRFGuard(t *testing.T) { 188func TestMirrorSSRFGuard(t *testing.T) {
189 t.Parallel()
189 inst := startInstance(t) // default posture: allow_local off 190 inst := startInstance(t) // default posture: allow_local off
190 aliceKey := inst.newKey(t, "alice") 191 aliceKey := inst.newKey(t, "alice")
191 inst.admin(t, "admin", "user", "create", "alice", "--key", aliceKey+".pub") 192 inst.admin(t, "admin", "user", "create", "alice", "--key", aliceKey+".pub")
e2e/mr_test.go +1
@@ -46,6 +46,7 @@ func (i *instance) mrShow(t *testing.T, key, repo, n string) mrShow {
46} 46}
47 47
48func TestMergeRequests(t *testing.T) { 48func TestMergeRequests(t *testing.T) {
49 t.Parallel()
49 inst := startInstance(t) 50 inst := startInstance(t)
50 51
51 aliceKey := inst.newKey(t, "alice") 52 aliceKey := inst.newKey(t, "alice")
e2e/mrbuilds_test.go +1
@@ -16,6 +16,7 @@ import (
16// merge request unbuildable and, under require-checks, unmergeable (#98). 16// merge request unbuildable and, under require-checks, unmergeable (#98).
17// A head from another repository runs without the target's secrets. 17// A head from another repository runs without the target's secrets.
18func TestForkMRHeadIsBuilt(t *testing.T) { 18func TestForkMRHeadIsBuilt(t *testing.T) {
19 t.Parallel()
19 inst := startInstance(t) 20 inst := startInstance(t)
20 inst.runner = buildRunner(t) 21 inst.runner = buildRunner(t)
21 aliceKey := inst.newKey(t, "alice") 22 aliceKey := inst.newKey(t, "alice")
e2e/mrdraft_test.go +1
@@ -11,6 +11,7 @@ import (
11// TestMRDraft covers the first stage of #111: a merge request opened to 11// TestMRDraft covers the first stage of #111: a merge request opened to
12// show work rather than to ask for a merge. 12// show work rather than to ask for a merge.
13func TestMRDraft(t *testing.T) { 13func TestMRDraft(t *testing.T) {
14 t.Parallel()
14 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n") 15 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n")
15 aliceKey := inst.newKey(t, "alice") 16 aliceKey := inst.newKey(t, "alice")
16 bobKey := inst.newKey(t, "bob") 17 bobKey := inst.newKey(t, "bob")
e2e/mrforkweb_test.go +1
@@ -12,6 +12,7 @@ import (
12// the source picker offers the branches of a fork they can push to, and 12// the source picker offers the branches of a fork they can push to, and
13// the merge request opens against the parent (#168). 13// the merge request opens against the parent (#168).
14func TestMRFromForkWeb(t *testing.T) { 14func TestMRFromForkWeb(t *testing.T) {
15 t.Parallel()
15 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n") 16 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n")
16 aliceKey := inst.newKey(t, "alice") 17 aliceKey := inst.newKey(t, "alice")
17 bobKey := inst.newKey(t, "bob") 18 bobKey := inst.newKey(t, "bob")
e2e/mrrebase_test.go +2
@@ -15,6 +15,7 @@ import (
15// server is never asked to vouch for a commit it did not receive already 15// server is never asked to vouch for a commit it did not receive already
16// signed (#175). 16// signed (#175).
17func TestCLIMRRebase(t *testing.T) { 17func TestCLIMRRebase(t *testing.T) {
18 t.Parallel()
18 inst := startInstance(t) 19 inst := startInstance(t)
19 aliceKey := inst.newKey(t, "alice") 20 aliceKey := inst.newKey(t, "alice")
20 inst.admin(t, "admin", "user", "create", "alice", 21 inst.admin(t, "admin", "user", "create", "alice",
@@ -84,6 +85,7 @@ func TestCLIMRRebase(t *testing.T) {
84 85
85// The guards: a dirty tree, and a source in a fork this clone cannot push. 86// The guards: a dirty tree, and a source in a fork this clone cannot push.
86func TestCLIMRRebaseRefusals(t *testing.T) { 87func TestCLIMRRebaseRefusals(t *testing.T) {
88 t.Parallel()
87 inst := startInstance(t) 89 inst := startInstance(t)
88 aliceKey := inst.newKey(t, "alice") 90 aliceKey := inst.newKey(t, "alice")
89 bobKey := inst.newKey(t, "bob") 91 bobKey := inst.newKey(t, "bob")
e2e/mrretarget_test.go +1
@@ -12,6 +12,7 @@ import (
12// SSH and from the browser. Retargeting changes which diff a review was 12// SSH and from the browser. Retargeting changes which diff a review was
13// of, so the existing approvals have to go stale with it. 13// of, so the existing approvals have to go stale with it.
14func TestMRRetarget(t *testing.T) { 14func TestMRRetarget(t *testing.T) {
15 t.Parallel()
15 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n") 16 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n")
16 aliceKey := inst.newKey(t, "alice") 17 aliceKey := inst.newKey(t, "alice")
17 bobKey := inst.newKey(t, "bob") 18 bobKey := inst.newKey(t, "bob")
e2e/mrweb_test.go +7
@@ -37,6 +37,7 @@ func (i *instance) login(t *testing.T, key string) *http.Client {
37// browser. Every action runs the same control command the CLI runs, so the 37// browser. Every action runs the same control command the CLI runs, so the
38// test also proves the merge gates apply to web merges. 38// test also proves the merge gates apply to web merges.
39func TestMRWebReviewLoop(t *testing.T) { 39func TestMRWebReviewLoop(t *testing.T) {
40 t.Parallel()
40 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n") 41 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n")
41 aliceKey := inst.newKey(t, "alice") 42 aliceKey := inst.newKey(t, "alice")
42 bobKey := inst.newKey(t, "bob") 43 bobKey := inst.newKey(t, "bob")
@@ -182,6 +183,7 @@ func TestMRWebReviewLoop(t *testing.T) {
182// TestMRWebCreate opens a merge request from the browser and checks the 183// TestMRWebCreate opens a merge request from the browser and checks the
183// form survives a refusal with the draft intact. 184// form survives a refusal with the draft intact.
184func TestMRWebCreate(t *testing.T) { 185func TestMRWebCreate(t *testing.T) {
186 t.Parallel()
185 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n") 187 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n")
186 aliceKey := inst.newKey(t, "alice") 188 aliceKey := inst.newKey(t, "alice")
187 inst.admin(t, "admin", "user", "create", "alice", 189 inst.admin(t, "admin", "user", "create", "alice",
@@ -241,6 +243,7 @@ func TestMRWebCreate(t *testing.T) {
241// TestMRListRows checks that the merge request list shows each row's 243// TestMRListRows checks that the merge request list shows each row's
242// combined check state and comment count (#230). 244// combined check state and comment count (#230).
243func TestMRListRows(t *testing.T) { 245func TestMRListRows(t *testing.T) {
246 t.Parallel()
244 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n") 247 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n")
245 aliceKey := inst.newKey(t, "alice") 248 aliceKey := inst.newKey(t, "alice")
246 inst.admin(t, "admin", "user", "create", "alice", 249 inst.admin(t, "admin", "user", "create", "alice",
@@ -292,6 +295,7 @@ func TestMRListRows(t *testing.T) {
292// proves the page dispatched mr diff-comment rather than writing its own 295// proves the page dispatched mr diff-comment rather than writing its own
293// rows. 296// rows.
294func TestMRWebDiffThreads(t *testing.T) { 297func TestMRWebDiffThreads(t *testing.T) {
298 t.Parallel()
295 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n") 299 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n")
296 aliceKey := inst.newKey(t, "alice") 300 aliceKey := inst.newKey(t, "alice")
297 inst.admin(t, "admin", "user", "create", "alice", 301 inst.admin(t, "admin", "user", "create", "alice",
@@ -406,6 +410,7 @@ func (i *instance) mrThreads(t *testing.T, key, repo, n string) []mrThread {
406// TestMRDiffEmptyExplained: a merge request whose head was fast-forwarded 410// TestMRDiffEmptyExplained: a merge request whose head was fast-forwarded
407// into the target outside the request shows why its diff is empty. 411// into the target outside the request shows why its diff is empty.
408func TestMRDiffEmptyExplained(t *testing.T) { 412func TestMRDiffEmptyExplained(t *testing.T) {
413 t.Parallel()
409 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n") 414 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n")
410 key := inst.newKey(t, "alice") 415 key := inst.newKey(t, "alice")
411 inst.admin(t, "admin", "user", "create", "alice", "--key", key+".pub", "--email", "alice@example.test", "--verified") 416 inst.admin(t, "admin", "user", "create", "alice", "--key", key+".pub", "--email", "alice@example.test", "--verified")
@@ -442,6 +447,7 @@ func TestMRDiffEmptyExplained(t *testing.T) {
442// web form, and checks both pages say so; clearing it over ssh removes 447// web form, and checks both pages say so; clearing it over ssh removes
443// both lines again (#223). 448// both lines again (#223).
444func TestMRSupersedes(t *testing.T) { 449func TestMRSupersedes(t *testing.T) {
450 t.Parallel()
445 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n") 451 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n")
446 aliceKey := inst.newKey(t, "alice") 452 aliceKey := inst.newKey(t, "alice")
447 inst.admin(t, "admin", "user", "create", "alice", 453 inst.admin(t, "admin", "user", "create", "alice",
@@ -507,6 +513,7 @@ func TestMRSupersedes(t *testing.T) {
507// list by it (#231). The CLI is the check that the page dispatched 513// list by it (#231). The CLI is the check that the page dispatched
508// mr label rather than writing its own rows. 514// mr label rather than writing its own rows.
509func TestMRWebLabels(t *testing.T) { 515func TestMRWebLabels(t *testing.T) {
516 t.Parallel()
510 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n") 517 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n")
511 aliceKey := inst.newKey(t, "alice") 518 aliceKey := inst.newKey(t, "alice")
512 bobKey := inst.newKey(t, "bob") 519 bobKey := inst.newKey(t, "bob")
e2e/notify_test.go +1
@@ -39,6 +39,7 @@ func (f *fakeSMTP) waitFor(t *testing.T, recipient, substr string) string {
39} 39}
40 40
41func TestActivityNotifications(t *testing.T) { 41func TestActivityNotifications(t *testing.T) {
42 t.Parallel()
42 smtp := startFakeSMTP(t) 43 smtp := startFakeSMTP(t)
43 inst := startInstanceWith(t, fmt.Sprintf( 44 inst := startInstanceWith(t, fmt.Sprintf(
44 "[mail]\nsmtp_host = %q\nfrom = \"noreply@gitbay.test\"\n", smtp.addr)) 45 "[mail]\nsmtp_host = %q\nfrom = \"noreply@gitbay.test\"\n", smtp.addr))
e2e/org_test.go +1
@@ -8,6 +8,7 @@ import (
8) 8)
9 9
10func TestOrganizations(t *testing.T) { 10func TestOrganizations(t *testing.T) {
11 t.Parallel()
11 inst := startInstance(t) 12 inst := startInstance(t)
12 aliceKey := inst.newKey(t, "alice") 13 aliceKey := inst.newKey(t, "alice")
13 bobKey := inst.newKey(t, "bob") 14 bobKey := inst.newKey(t, "bob")
e2e/orgbody_test.go +1
@@ -10,6 +10,7 @@ import (
10// without a format is markdown, which is what everything written before the 10// without a format is markdown, which is what everything written before the
11// format existed carries. 11// format existed carries.
12func TestOrgBodies(t *testing.T) { 12func TestOrgBodies(t *testing.T) {
13 t.Parallel()
13 inst := startInstance(t) 14 inst := startInstance(t)
14 aliceKey := inst.newKey(t, "alice") 15 aliceKey := inst.newKey(t, "alice")
15 inst.admin(t, "admin", "user", "create", "alice", 16 inst.admin(t, "admin", "user", "create", "alice",
e2e/orglabels_test.go +1
@@ -11,6 +11,7 @@ import (
11// commit in one repository closes an issue in another; the org pages 11// commit in one repository closes an issue in another; the org pages
12// answer members and outsiders as their access allows. 12// answer members and outsiders as their access allows.
13func TestOrgLabelsMilestonesAndCrossRepoCloses(t *testing.T) { 13func TestOrgLabelsMilestonesAndCrossRepoCloses(t *testing.T) {
14 t.Parallel()
14 inst := startInstance(t) 15 inst := startInstance(t)
15 aliceKey := inst.newKey(t, "alice") 16 aliceKey := inst.newKey(t, "alice")
16 carolKey := inst.newKey(t, "carol") 17 carolKey := inst.newKey(t, "carol")
e2e/orgremove_test.go +1
@@ -11,6 +11,7 @@ import (
11// teams (#196). Before the fix, team_members rows survived removal, so a 11// teams (#196). Before the fix, team_members rows survived removal, so a
12// former member kept pushing. 12// former member kept pushing.
13func TestOrgMemberRemovalEndsTeamAccess(t *testing.T) { 13func TestOrgMemberRemovalEndsTeamAccess(t *testing.T) {
14 t.Parallel()
14 inst := startInstance(t) 15 inst := startInstance(t)
15 aliceKey := inst.newKey(t, "alice") 16 aliceKey := inst.newKey(t, "alice")
16 bobKey := inst.newKey(t, "bob") 17 bobKey := inst.newKey(t, "bob")
e2e/orgweb_test.go +2
@@ -12,6 +12,7 @@ import (
12// membership and teams, admin-gated, dispatched through the same commands 12// membership and teams, admin-gated, dispatched through the same commands
13// the CLI uses. 13// the CLI uses.
14func TestOrgManagementWeb(t *testing.T) { 14func TestOrgManagementWeb(t *testing.T) {
15 t.Parallel()
15 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n") 16 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n")
16 aliceKey := inst.newKey(t, "alice") 17 aliceKey := inst.newKey(t, "alice")
17 bobKey := inst.newKey(t, "bob") 18 bobKey := inst.newKey(t, "bob")
@@ -157,6 +158,7 @@ func orgMembers(t *testing.T, inst *instance, key string) []string {
157// rename from the org's. Delete stays on the CLI, where a typed 158// rename from the org's. Delete stays on the CLI, where a typed
158// confirmation is the norm (#167). 159// confirmation is the norm (#167).
159func TestOrgLifecycleWeb(t *testing.T) { 160func TestOrgLifecycleWeb(t *testing.T) {
161 t.Parallel()
160 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n") 162 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n")
161 aliceKey := inst.newKey(t, "alice") 163 aliceKey := inst.newKey(t, "alice")
162 bobKey := inst.newKey(t, "bob") 164 bobKey := inst.newKey(t, "bob")
e2e/pagination_test.go +1
@@ -28,6 +28,7 @@ func decodePage[T any](t *testing.T, out string) ([]T, string) {
28// Cursor pagination on the list commands: opaque cursors, stable pages, 28// Cursor pagination on the list commands: opaque cursors, stable pages,
29// and the bare-array shape untouched when the flags are absent. 29// and the bare-array shape untouched when the flags are absent.
30func TestCursorPagination(t *testing.T) { 30func TestCursorPagination(t *testing.T) {
31 t.Parallel()
31 inst := startInstance(t) 32 inst := startInstance(t)
32 aliceKey := inst.newKey(t, "alice") 33 aliceKey := inst.newKey(t, "alice")
33 inst.admin(t, "admin", "user", "create", "alice", "--key", aliceKey+".pub") 34 inst.admin(t, "admin", "user", "create", "alice", "--key", aliceKey+".pub")
e2e/pendingreview_test.go +1
@@ -12,6 +12,7 @@ import (
12// review and submits it as a unit, instead of every comment landing in 12// review and submits it as a unit, instead of every comment landing in
13// the author's inbox the moment it is typed. 13// the author's inbox the moment it is typed.
14func TestPendingReviewBatch(t *testing.T) { 14func TestPendingReviewBatch(t *testing.T) {
15 t.Parallel()
15 inst := startInstance(t) 16 inst := startInstance(t)
16 authorKey := inst.newKey(t, "author") 17 authorKey := inst.newKey(t, "author")
17 reviewerKey := inst.newKey(t, "reviewer") 18 reviewerKey := inst.newKey(t, "reviewer")
e2e/previewweb_test.go +1
@@ -13,6 +13,7 @@ import (
13// create, their edit and comment boxes, release create and edit, the 13// create, their edit and comment boxes, release create and edit, the
14// profile about text, and the file editor on a path the forge renders. 14// profile about text, and the file editor on a path the forge renders.
15func TestMarkupPreviewWeb(t *testing.T) { 15func TestMarkupPreviewWeb(t *testing.T) {
16 t.Parallel()
16 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n") 17 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n")
17 aliceKey := inst.newKey(t, "alice") 18 aliceKey := inst.newKey(t, "alice")
18 inst.admin(t, "admin", "user", "create", "alice", 19 inst.admin(t, "admin", "user", "create", "alice",
e2e/profile_test.go +1
@@ -8,6 +8,7 @@ import (
8) 8)
9 9
10func TestOwnerProfiles(t *testing.T) { 10func TestOwnerProfiles(t *testing.T) {
11 t.Parallel()
11 inst := startInstance(t) 12 inst := startInstance(t)
12 aliceKey := inst.newKey(t, "alice") 13 aliceKey := inst.newKey(t, "alice")
13 bobKey := inst.newKey(t, "bob") 14 bobKey := inst.newKey(t, "bob")
e2e/profileabout_test.go +4
@@ -8,6 +8,7 @@ import (
8// The about text is a file in <owner>/.gitbay, read on every surface with 8// The about text is a file in <owner>/.gitbay, read on every surface with
9// the reader's own access. 9// the reader's own access.
10func TestProfileAboutFromRepo(t *testing.T) { 10func TestProfileAboutFromRepo(t *testing.T) {
11 t.Parallel()
11 inst := startInstance(t) 12 inst := startInstance(t)
12 aliceKey := inst.newKey(t, "alice") 13 aliceKey := inst.newKey(t, "alice")
13 inst.admin(t, "admin", "user", "create", "alice", 14 inst.admin(t, "admin", "user", "create", "alice",
@@ -61,6 +62,7 @@ func TestProfileAboutFromRepo(t *testing.T) {
61// The extension picks the format, .md wins the resolution order, and a 62// The extension picks the format, .md wins the resolution order, and a
62// private .gitbay keeps the about to the people who can read it. 63// private .gitbay keeps the about to the people who can read it.
63func TestProfileAboutFormatAndPrivacy(t *testing.T) { 64func TestProfileAboutFormatAndPrivacy(t *testing.T) {
65 t.Parallel()
64 inst := startInstance(t) 66 inst := startInstance(t)
65 aliceKey := inst.newKey(t, "alice") 67 aliceKey := inst.newKey(t, "alice")
66 inst.admin(t, "admin", "user", "create", "alice", 68 inst.admin(t, "admin", "user", "create", "alice",
@@ -104,6 +106,7 @@ func TestProfileAboutFormatAndPrivacy(t *testing.T) {
104// A dot-repo is infrastructure: it stays out of explore and off the 106// A dot-repo is infrastructure: it stays out of explore and off the
105// profile's repository list, and stays in the owner's own inventory. 107// profile's repository list, and stays in the owner's own inventory.
106func TestDotReposHiddenFromListings(t *testing.T) { 108func TestDotReposHiddenFromListings(t *testing.T) {
109 t.Parallel()
107 inst := startInstance(t) 110 inst := startInstance(t)
108 aliceKey := inst.newKey(t, "alice") 111 aliceKey := inst.newKey(t, "alice")
109 inst.admin(t, "admin", "user", "create", "alice", 112 inst.admin(t, "admin", "user", "create", "alice",
@@ -140,6 +143,7 @@ func TestDotReposHiddenFromListings(t *testing.T) {
140 143
141// The about is not settable through profile set any more: it is a file. 144// The about is not settable through profile set any more: it is a file.
142func TestProfileSetHasNoAbout(t *testing.T) { 145func TestProfileSetHasNoAbout(t *testing.T) {
146 t.Parallel()
143 inst := startInstance(t) 147 inst := startInstance(t)
144 aliceKey := inst.newKey(t, "alice") 148 aliceKey := inst.newKey(t, "alice")
145 inst.admin(t, "admin", "user", "create", "alice", 149 inst.admin(t, "admin", "user", "create", "alice",
e2e/profileweb_test.go +2
@@ -11,6 +11,7 @@ import (
11// emptying a field actually clears it rather than being skipped. The about 11// emptying a field actually clears it rather than being skipped. The about
12// text is not on this form — it is a file, covered below. 12// text is not on this form — it is a file, covered below.
13func TestProfileSettingsWeb(t *testing.T) { 13func TestProfileSettingsWeb(t *testing.T) {
14 t.Parallel()
14 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n") 15 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n")
15 aliceKey := inst.newKey(t, "alice") 16 aliceKey := inst.newKey(t, "alice")
16 inst.admin(t, "admin", "user", "create", "alice", 17 inst.admin(t, "admin", "user", "create", "alice",
@@ -98,6 +99,7 @@ func TestProfileSettingsWeb(t *testing.T) {
98// The settings page does not edit the about text; it creates the 99// The settings page does not edit the about text; it creates the
99// repository that holds it and points at the file editor. 100// repository that holds it and points at the file editor.
100func TestProfileAboutRepoFromWeb(t *testing.T) { 101func TestProfileAboutRepoFromWeb(t *testing.T) {
102 t.Parallel()
101 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n") 103 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n")
102 aliceKey := inst.newKey(t, "alice") 104 aliceKey := inst.newKey(t, "alice")
103 inst.admin(t, "admin", "user", "create", "alice", 105 inst.admin(t, "admin", "user", "create", "alice",
e2e/rangediff_test.go +1
@@ -12,6 +12,7 @@ import (
12// nothing said what had changed between the two heads. A plain diff of 12// nothing said what had changed between the two heads. A plain diff of
13// the heads cannot answer that — it shows the whole branch again. 13// the heads cannot answer that — it shows the whole branch again.
14func TestMRRangeDiff(t *testing.T) { 14func TestMRRangeDiff(t *testing.T) {
15 t.Parallel()
15 inst := startInstance(t) 16 inst := startInstance(t)
16 key := inst.newKey(t, "alice") 17 key := inst.newKey(t, "alice")
17 inst.admin(t, "admin", "user", "create", "alice", "--key", key+".pub") 18 inst.admin(t, "admin", "user", "create", "alice", "--key", key+".pub")
e2e/readonly_test.go +1
@@ -26,6 +26,7 @@ import (
26// Every ReadOnly command needs an entry in readArgs; a new one without 26// Every ReadOnly command needs an entry in readArgs; a new one without
27// arguments here fails the test rather than going untested. 27// arguments here fails the test rather than going untested.
28func TestReadOnlyCommandsWriteNothing(t *testing.T) { 28func TestReadOnlyCommandsWriteNothing(t *testing.T) {
29 t.Parallel()
29 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n[webhooks]\nallow_local = true\n") 30 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n[webhooks]\nallow_local = true\n")
30 aliceKey := inst.newKey(t, "alice") 31 aliceKey := inst.newKey(t, "alice")
31 inst.admin(t, "admin", "user", "create", "alice", "--key", aliceKey+".pub", 32 inst.admin(t, "admin", "user", "create", "alice", "--key", aliceKey+".pub",
e2e/reap_test.go +4
@@ -88,6 +88,7 @@ func TestStaleBuildReapedWithoutRunner(t *testing.T) {
88} 88}
89 89
90func TestAdminRunners(t *testing.T) { 90func TestAdminRunners(t *testing.T) {
91 t.Parallel()
91 inst := startInstance(t) 92 inst := startInstance(t)
92 rootKey := inst.newKey(t, "root") 93 rootKey := inst.newKey(t, "root")
93 aliceKey := inst.newKey(t, "alice") 94 aliceKey := inst.newKey(t, "alice")
@@ -152,6 +153,7 @@ func TestAdminRunners(t *testing.T) {
152 153
153// /healthz is unauthenticated, cache-free, and says which build serves. 154// /healthz is unauthenticated, cache-free, and says which build serves.
154func TestHealthz(t *testing.T) { 155func TestHealthz(t *testing.T) {
156 t.Parallel()
155 inst := startInstance(t) 157 inst := startInstance(t)
156 status, body := inst.get(t, "/healthz") 158 status, body := inst.get(t, "/healthz")
157 if status != 200 || !strings.Contains(body, `"ok":true`) || !strings.Contains(body, `"commit":"`) { 159 if status != 200 || !strings.Contains(body, `"ok":true`) || !strings.Contains(body, `"commit":"`) {
@@ -166,6 +168,7 @@ func TestHealthz(t *testing.T) {
166// gc --lfs removes objects no pointer names, keeps referenced ones, and 168// gc --lfs removes objects no pointer names, keeps referenced ones, and
167// leaves anything young enough to be an upload ahead of its push. 169// leaves anything young enough to be an upload ahead of its push.
168func TestGCLFSOrphans(t *testing.T) { 170func TestGCLFSOrphans(t *testing.T) {
171 t.Parallel()
169 inst := startInstance(t) 172 inst := startInstance(t)
170 aliceKey := inst.newKey(t, "alice") 173 aliceKey := inst.newKey(t, "alice")
171 inst.admin(t, "admin", "user", "create", "alice", "--key", aliceKey+".pub") 174 inst.admin(t, "admin", "user", "create", "alice", "--key", aliceKey+".pub")
@@ -216,6 +219,7 @@ func TestGCLFSOrphans(t *testing.T) {
216// backup --verify reads an archive back and says whether a restore would 219// backup --verify reads an archive back and says whether a restore would
217// have what the database expects. 220// have what the database expects.
218func TestBackupVerify(t *testing.T) { 221func TestBackupVerify(t *testing.T) {
222 t.Parallel()
219 inst := startInstance(t) 223 inst := startInstance(t)
220 aliceKey := inst.newKey(t, "alice") 224 aliceKey := inst.newKey(t, "alice")
221 inst.admin(t, "admin", "user", "create", "alice", "--key", aliceKey+".pub") 225 inst.admin(t, "admin", "user", "create", "alice", "--key", aliceKey+".pub")
e2e/registration_test.go +2
@@ -111,6 +111,7 @@ func extractCode(t *testing.T, mail string) string {
111} 111}
112 112
113func TestOpenRegistration(t *testing.T) { 113func TestOpenRegistration(t *testing.T) {
114 t.Parallel()
114 smtp := startFakeSMTP(t) 115 smtp := startFakeSMTP(t)
115 inst := startInstanceWith(t, fmt.Sprintf( 116 inst := startInstanceWith(t, fmt.Sprintf(
116 "[registration]\nmode = \"open\"\n[mail]\nsmtp_host = %q\nfrom = \"noreply@gitbay.test\"\n", smtp.addr)) 117 "[registration]\nmode = \"open\"\n[mail]\nsmtp_host = %q\nfrom = \"noreply@gitbay.test\"\n", smtp.addr))
@@ -195,6 +196,7 @@ func TestOpenRegistration(t *testing.T) {
195} 196}
196 197
197func TestInviteRegistration(t *testing.T) { 198func TestInviteRegistration(t *testing.T) {
199 t.Parallel()
198 smtp := startFakeSMTP(t) 200 smtp := startFakeSMTP(t)
199 inst := startInstanceWith(t, fmt.Sprintf( 201 inst := startInstanceWith(t, fmt.Sprintf(
200 "[registration]\nmode = \"invite\"\n[mail]\nsmtp_host = %q\nfrom = \"noreply@gitbay.test\"\n", smtp.addr)) 202 "[registration]\nmode = \"invite\"\n[mail]\nsmtp_host = %q\nfrom = \"noreply@gitbay.test\"\n", smtp.addr))
e2e/release_test.go +1
@@ -10,6 +10,7 @@ import (
10) 10)
11 11
12func TestReleases(t *testing.T) { 12func TestReleases(t *testing.T) {
13 t.Parallel()
13 inst := startInstance(t) 14 inst := startInstance(t)
14 aliceKey := inst.newKey(t, "alice") 15 aliceKey := inst.newKey(t, "alice")
15 inst.admin(t, "admin", "user", "create", "alice", "--key", aliceKey+".pub") 16 inst.admin(t, "admin", "user", "create", "alice", "--key", aliceKey+".pub")
e2e/releaseweb_test.go +1
@@ -11,6 +11,7 @@ import (
11// TestReleaseAndBuildWeb creates and edits a release and triggers a build 11// TestReleaseAndBuildWeb creates and edits a release and triggers a build
12// from the browser, each through the command the CLI runs. 12// from the browser, each through the command the CLI runs.
13func TestReleaseAndBuildWeb(t *testing.T) { 13func TestReleaseAndBuildWeb(t *testing.T) {
14 t.Parallel()
14 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n") 15 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n")
15 inst.runner = buildRunner(t) 16 inst.runner = buildRunner(t)
16 aliceKey := inst.newKey(t, "alice") 17 aliceKey := inst.newKey(t, "alice")
e2e/reporename_test.go +1
@@ -10,6 +10,7 @@ import (
10// repo rename moves the directory and the row together; what hangs off 10// repo rename moves the directory and the row together; what hangs off
11// the repository by id (issues here) follows it (#190). 11// the repository by id (issues here) follows it (#190).
12func TestRepoRename(t *testing.T) { 12func TestRepoRename(t *testing.T) {
13 t.Parallel()
13 inst := startInstance(t) 14 inst := startInstance(t)
14 aliceKey := inst.newKey(t, "alice") 15 aliceKey := inst.newKey(t, "alice")
15 bobKey := inst.newKey(t, "bob") 16 bobKey := inst.newKey(t, "bob")
e2e/reposhowstate_test.go +1
@@ -9,6 +9,7 @@ import (
9// the caller can read it, what the repository was forked from — so a 9// the caller can read it, what the repository was forked from — so a
10// client draws the real state without a second read per screen (#178). 10// client draws the real state without a second read per screen (#178).
11func TestRepoShowCarriesViewerState(t *testing.T) { 11func TestRepoShowCarriesViewerState(t *testing.T) {
12 t.Parallel()
12 inst := startInstance(t) 13 inst := startInstance(t)
13 aliceKey := inst.newKey(t, "alice") 14 aliceKey := inst.newKey(t, "alice")
14 bobKey := inst.newKey(t, "bob") 15 bobKey := inst.newKey(t, "bob")
e2e/requiremr_test.go +1
@@ -12,6 +12,7 @@ import (
12// not exist yet can still be created, and unprotected branches are 12// not exist yet can still be created, and unprotected branches are
13// unaffected (#197). 13// unaffected (#197).
14func TestRequireMR(t *testing.T) { 14func TestRequireMR(t *testing.T) {
15 t.Parallel()
15 inst := startInstance(t) 16 inst := startInstance(t)
16 aliceKey := inst.newKey(t, "alice") 17 aliceKey := inst.newKey(t, "alice")
17 inst.admin(t, "admin", "user", "create", "alice", 18 inst.admin(t, "admin", "user", "create", "alice",
e2e/reviewcarry_test.go +1
@@ -10,6 +10,7 @@ import (
10// A rebase that leaves the merge request's diff unchanged keeps its 10// A rebase that leaves the merge request's diff unchanged keeps its
11// fresh approvals; a push that changes the diff stales them (#198). 11// fresh approvals; a push that changes the diff stales them (#198).
12func TestApprovalsSurviveSameDiffRebase(t *testing.T) { 12func TestApprovalsSurviveSameDiffRebase(t *testing.T) {
13 t.Parallel()
13 inst := startInstance(t) 14 inst := startInstance(t)
14 aliceKey := inst.newKey(t, "alice") 15 aliceKey := inst.newKey(t, "alice")
15 bobKey := inst.newKey(t, "bob") 16 bobKey := inst.newKey(t, "bob")
e2e/reviewloop_test.go +1
@@ -20,6 +20,7 @@ import (
20// exercised the loop end to end at all — every feature had its own test 20// exercised the loop end to end at all — every feature had its own test
21// and none of them met. 21// and none of them met.
22func TestTwoAccountReviewLoop(t *testing.T) { 22func TestTwoAccountReviewLoop(t *testing.T) {
23 t.Parallel()
23 inst := startInstance(t) 24 inst := startInstance(t)
24 authorKey := inst.newKey(t, "author") 25 authorKey := inst.newKey(t, "author")
25 reviewerKey := inst.newKey(t, "reviewer") 26 reviewerKey := inst.newKey(t, "reviewer")
e2e/reviewrequest_test.go +1
@@ -13,6 +13,7 @@ import (
13// and --remove takes them out outright. A separate, private repository 13// and --remove takes them out outright. A separate, private repository
14// checks that requesting someone who cannot read it is refused. 14// checks that requesting someone who cannot read it is refused.
15func TestMRReviewRequest(t *testing.T) { 15func TestMRReviewRequest(t *testing.T) {
16 t.Parallel()
16 inst := startInstance(t) 17 inst := startInstance(t)
17 aliceKey := inst.newKey(t, "alice") 18 aliceKey := inst.newKey(t, "alice")
18 bobKey := inst.newKey(t, "bob") 19 bobKey := inst.newKey(t, "bob")
e2e/reviewstanding_test.go +1
@@ -20,6 +20,7 @@ import (
20// Reviewing stays open to everyone: an outside opinion on a public change 20// Reviewing stays open to everyone: an outside opinion on a public change
21// is worth having. It just does not decide the gate. 21// is worth having. It just does not decide the gate.
22func TestReviewsCountOnlyFromWriters(t *testing.T) { 22func TestReviewsCountOnlyFromWriters(t *testing.T) {
23 t.Parallel()
23 inst := startInstance(t) 24 inst := startInstance(t)
24 ownerKey := inst.newKey(t, "owner") 25 ownerKey := inst.newKey(t, "owner")
25 writerKey := inst.newKey(t, "writer") 26 writerKey := inst.newKey(t, "writer")
e2e/robustness_test.go +2
@@ -15,6 +15,7 @@ import (
15// tree, cat, grep, blame and the web blob page. Nothing in the suite 15// tree, cat, grep, blame and the web blob page. Nothing in the suite
16// covered a non-ASCII path before (#129). 16// covered a non-ASCII path before (#129).
17func TestNonASCIIPaths(t *testing.T) { 17func TestNonASCIIPaths(t *testing.T) {
18 t.Parallel()
18 inst := startInstance(t) 19 inst := startInstance(t)
19 aliceKey := inst.newKey(t, "alice") 20 aliceKey := inst.newKey(t, "alice")
20 inst.admin(t, "admin", "user", "create", "alice", "--key", aliceKey+".pub") 21 inst.admin(t, "admin", "user", "create", "alice", "--key", aliceKey+".pub")
@@ -62,6 +63,7 @@ func TestNonASCIIPaths(t *testing.T) {
62// land: the receive hooks, the post-receive work and the store take 63// land: the receive hooks, the post-receive work and the store take
63// them concurrently without losing one (#129). 64// them concurrently without losing one (#129).
64func TestConcurrentPushes(t *testing.T) { 65func TestConcurrentPushes(t *testing.T) {
66 t.Parallel()
65 inst := startInstance(t) 67 inst := startInstance(t)
66 aliceKey := inst.newKey(t, "alice") 68 aliceKey := inst.newKey(t, "alice")
67 inst.admin(t, "admin", "user", "create", "alice", "--key", aliceKey+".pub") 69 inst.admin(t, "admin", "user", "create", "alice", "--key", aliceKey+".pub")
e2e/runner_scope_test.go +2
@@ -13,6 +13,7 @@ import (
13// that should only build one project ends up executing every repository's 13// that should only build one project ends up executing every repository's
14// steps — including those of a repository it has nothing to do with. 14// steps — including those of a repository it has nothing to do with.
15func TestRunnerNextScopedToRepos(t *testing.T) { 15func TestRunnerNextScopedToRepos(t *testing.T) {
16 t.Parallel()
16 inst := startInstance(t) 17 inst := startInstance(t)
17 aliceKey := inst.newKey(t, "alice") 18 aliceKey := inst.newKey(t, "alice")
18 inst.admin(t, "admin", "user", "create", "alice", "--key", aliceKey+".pub") 19 inst.admin(t, "admin", "user", "create", "alice", "--key", aliceKey+".pub")
@@ -65,6 +66,7 @@ func TestRunnerNextScopedToRepos(t *testing.T) {
65// same account's full key nor the runner key reaches the wrong side, so a 66// same account's full key nor the runner key reaches the wrong side, so a
66// key stolen from a build step cannot administer the instance (#92). 67// key stolen from a build step cannot administer the instance (#92).
67func TestRunnerScopedKey(t *testing.T) { 68func TestRunnerScopedKey(t *testing.T) {
69 t.Parallel()
68 inst := startInstance(t) 70 inst := startInstance(t)
69 aliceKey := inst.newKey(t, "alice") 71 aliceKey := inst.newKey(t, "alice")
70 inst.admin(t, "admin", "user", "create", "alice", "--key", aliceKey+".pub") 72 inst.admin(t, "admin", "user", "create", "alice", "--key", aliceKey+".pub")
e2e/runnerattach_test.go +1
@@ -14,6 +14,7 @@ import (
14// wrote. The runner builds their push and leaves a fork's merge request 14// wrote. The runner builds their push and leaves a fork's merge request
15// head alone until started with -untrusted. 15// head alone until started with -untrusted.
16func TestAttachedRunnerBuildsOwnRepo(t *testing.T) { 16func TestAttachedRunnerBuildsOwnRepo(t *testing.T) {
17 t.Parallel()
17 inst := startInstance(t) 18 inst := startInstance(t)
18 inst.runner = buildRunner(t) 19 inst.runner = buildRunner(t)
19 aliceKey := inst.newKey(t, "alice") 20 aliceKey := inst.newKey(t, "alice")
e2e/runnerstop_test.go +3
@@ -79,6 +79,7 @@ func (i *instance) buildStatus(t *testing.T, key string) string {
79} 79}
80 80
81func TestRunnerDrainsOnSIGTERM(t *testing.T) { 81func TestRunnerDrainsOnSIGTERM(t *testing.T) {
82 t.Parallel()
82 inst, key := stopFixture(t) 83 inst, key := stopFixture(t)
83 cmd := exec.Command(inst.runner, inst.runnerArgs(t, key)...) 84 cmd := exec.Command(inst.runner, inst.runnerArgs(t, key)...)
84 cmd.Env = append(os.Environ(), "XDG_CONFIG_HOME="+t.TempDir(), "GIT_CONFIG_NOSYSTEM=1", "GIT_CONFIG_GLOBAL=/dev/null") 85 cmd.Env = append(os.Environ(), "XDG_CONFIG_HOME="+t.TempDir(), "GIT_CONFIG_NOSYSTEM=1", "GIT_CONFIG_GLOBAL=/dev/null")
@@ -137,6 +138,7 @@ func dropInValue(conf, key string) string {
137// The unit must let the drain happen: the stop signal reaches the runner 138// The unit must let the drain happen: the stop signal reaches the runner
138// alone, and the stop timeout outlasts a build plus the report retries. 139// alone, and the stop timeout outlasts a build plus the report retries.
139func TestRunnerDropInLetsTheDrainHappen(t *testing.T) { 140func TestRunnerDropInLetsTheDrainHappen(t *testing.T) {
141 t.Parallel()
140 conf := runnerDropIn(t) 142 conf := runnerDropIn(t)
141 if mode := dropInValue(conf, "KillMode"); mode != "mixed" { 143 if mode := dropInValue(conf, "KillMode"); mode != "mixed" {
142 t.Fatalf("KillMode=%q, want mixed: control-group signals the step and the log session with the runner", mode) 144 t.Fatalf("KillMode=%q, want mixed: control-group signals the step and the log session with the runner", mode)
@@ -173,6 +175,7 @@ func haveUserSystemd(t *testing.T) bool {
173// KillMode the build in flight is reported a success; under systemd's 175// KillMode the build in flight is reported a success; under systemd's
174// default it is not, which is the failure that shipped once. 176// default it is not, which is the failure that shipped once.
175func TestRunnerDrainUnderSystemd(t *testing.T) { 177func TestRunnerDrainUnderSystemd(t *testing.T) {
178 t.Parallel()
176 if !haveUserSystemd(t) { 179 if !haveUserSystemd(t) {
177 t.Skip("no systemd user manager") 180 t.Skip("no systemd user manager")
178 } 181 }
e2e/runnerweb_test.go +1
@@ -10,6 +10,7 @@ import (
10// The settings page attaches and detaches runners through the same 10// The settings page attaches and detaches runners through the same
11// commands the CLI uses, and lists what is attached. 11// commands the CLI uses, and lists what is attached.
12func TestRunnerSettingsWeb(t *testing.T) { 12func TestRunnerSettingsWeb(t *testing.T) {
13 t.Parallel()
13 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n") 14 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n")
14 aliceKey := inst.newKey(t, "alice") 15 aliceKey := inst.newKey(t, "alice")
15 inst.admin(t, "admin", "user", "create", "alice", 16 inst.admin(t, "admin", "user", "create", "alice",
e2e/search_test.go +1
@@ -8,6 +8,7 @@ import (
8) 8)
9 9
10func TestSearch(t *testing.T) { 10func TestSearch(t *testing.T) {
11 t.Parallel()
11 inst := startInstance(t) 12 inst := startInstance(t)
12 aliceKey := inst.newKey(t, "alice") 13 aliceKey := inst.newKey(t, "alice")
13 bobKey := inst.newKey(t, "bob") 14 bobKey := inst.newKey(t, "bob")
e2e/searchweb_test.go +1
@@ -9,6 +9,7 @@ import (
9// TestGlobalSearchAndNotificationsWeb covers the two web surfaces #118 9// TestGlobalSearchAndNotificationsWeb covers the two web surfaces #118
10// still lacked: /search across the instance, and the notification inbox. 10// still lacked: /search across the instance, and the notification inbox.
11func TestGlobalSearchAndNotificationsWeb(t *testing.T) { 11func TestGlobalSearchAndNotificationsWeb(t *testing.T) {
12 t.Parallel()
12 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n") 13 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n")
13 aliceKey := inst.newKey(t, "alice") 14 aliceKey := inst.newKey(t, "alice")
14 bobKey := inst.newKey(t, "bob") 15 bobKey := inst.newKey(t, "bob")
e2e/security_test.go +1
@@ -7,6 +7,7 @@ import (
7) 7)
8 8
9func TestSecurityHeaders(t *testing.T) { 9func TestSecurityHeaders(t *testing.T) {
10 t.Parallel()
10 inst := startInstance(t) 11 inst := startInstance(t)
11 aliceKey := inst.newKey(t, "alice") 12 aliceKey := inst.newKey(t, "alice")
12 inst.admin(t, "admin", "user", "create", "alice", "--key", aliceKey+".pub") 13 inst.admin(t, "admin", "user", "create", "alice", "--key", aliceKey+".pub")
e2e/settingsweb_test.go +1
@@ -9,6 +9,7 @@ import (
9// TestRepoSettingsWeb drives the settings page: each control runs the 9// TestRepoSettingsWeb drives the settings page: each control runs the
10// command the CLI runs, so repo show and settings show are the check. 10// command the CLI runs, so repo show and settings show are the check.
11func TestRepoSettingsWeb(t *testing.T) { 11func TestRepoSettingsWeb(t *testing.T) {
12 t.Parallel()
12 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n") 13 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n")
13 aliceKey := inst.newKey(t, "alice") 14 aliceKey := inst.newKey(t, "alice")
14 bobKey := inst.newKey(t, "bob") 15 bobKey := inst.newKey(t, "bob")
e2e/shutdown_test.go +2
@@ -15,6 +15,7 @@ import (
15// kill: no listener closed, no request or push allowed to finish. The 15// kill: no listener closed, no request or push allowed to finish. The
16// daemon now stops its listeners, drains, and exits 0 (#105). 16// daemon now stops its listeners, drains, and exits 0 (#105).
17func TestServeStopsOnSIGTERM(t *testing.T) { 17func TestServeStopsOnSIGTERM(t *testing.T) {
18 t.Parallel()
18 inst := startInstance(t) 19 inst := startInstance(t)
19 if resp, err := http.Get(inst.base() + "/healthz"); err != nil || resp.StatusCode != 200 { 20 if resp, err := http.Get(inst.base() + "/healthz"); err != nil || resp.StatusCode != 200 {
20 t.Fatalf("healthz before shutdown: %v", err) 21 t.Fatalf("healthz before shutdown: %v", err)
@@ -41,6 +42,7 @@ func TestServeStopsOnSIGTERM(t *testing.T) {
41// idle connection is closed at once rather than holding the drain for its 42// idle connection is closed at once rather than holding the drain for its
42// full 30 s; only a session mid-command is waited for (#141). 43// full 30 s; only a session mid-command is waited for (#141).
43func TestShutdownClosesIdleConnections(t *testing.T) { 44func TestShutdownClosesIdleConnections(t *testing.T) {
45 t.Parallel()
44 inst := startInstance(t) 46 inst := startInstance(t)
45 aliceKey := inst.newKey(t, "alice") 47 aliceKey := inst.newKey(t, "alice")
46 inst.admin(t, "admin", "user", "create", "alice", "--key", aliceKey+".pub") 48 inst.admin(t, "admin", "user", "create", "alice", "--key", aliceKey+".pub")
e2e/sig_test.go +1
@@ -153,6 +153,7 @@ func (i *instance) repoLog(t *testing.T, key, repo string) map[string]logEntry {
153} 153}
154 154
155func TestSignatureVerification(t *testing.T) { 155func TestSignatureVerification(t *testing.T) {
156 t.Parallel()
156 inst := startInstance(t) 157 inst := startInstance(t)
157 158
158 aliceKey := inst.newKey(t, "alice") 159 aliceKey := inst.newKey(t, "alice")
e2e/signupnotice_test.go +2
@@ -11,6 +11,7 @@ import (
11// becomes active. An open-mode signup counts at verification, not when 11// becomes active. An open-mode signup counts at verification, not when
12// the row is created, so an unverified attempt is silent (#234). 12// the row is created, so an unverified attempt is silent (#234).
13func TestSignupNotifiesAdmins(t *testing.T) { 13func TestSignupNotifiesAdmins(t *testing.T) {
14 t.Parallel()
14 smtp := startFakeSMTP(t) 15 smtp := startFakeSMTP(t)
15 inst := startInstanceWith(t, fmt.Sprintf( 16 inst := startInstanceWith(t, fmt.Sprintf(
16 "[registration]\nmode = \"open\"\nnotify_admin = true\n"+ 17 "[registration]\nmode = \"open\"\nnotify_admin = true\n"+
@@ -45,6 +46,7 @@ func TestSignupNotifiesAdmins(t *testing.T) {
45// With notify_admin off, the default, the same signup mails no one but 46// With notify_admin off, the default, the same signup mails no one but
46// the person registering. 47// the person registering.
47func TestSignupNoticeOffByDefault(t *testing.T) { 48func TestSignupNoticeOffByDefault(t *testing.T) {
49 t.Parallel()
48 smtp := startFakeSMTP(t) 50 smtp := startFakeSMTP(t)
49 inst := startInstanceWith(t, fmt.Sprintf( 51 inst := startInstanceWith(t, fmt.Sprintf(
50 "[registration]\nmode = \"open\"\n[mail]\nsmtp_host = %q\nfrom = \"noreply@gitbay.test\"\n", smtp.addr)) 52 "[registration]\nmode = \"open\"\n[mail]\nsmtp_host = %q\nfrom = \"noreply@gitbay.test\"\n", smtp.addr))
e2e/snippet_test.go +1
@@ -10,6 +10,7 @@ import (
10// Snippets over SSH: create from stdin, read back, list by visibility, 10// Snippets over SSH: create from stdin, read back, list by visibility,
11// edit files and metadata, and the not-found rule for private ones. 11// edit files and metadata, and the not-found rule for private ones.
12func TestSnippets(t *testing.T) { 12func TestSnippets(t *testing.T) {
13 t.Parallel()
13 inst := startInstanceWith(t, "[limits]\nmax_snippets_per_user = 3\n") 14 inst := startInstanceWith(t, "[limits]\nmax_snippets_per_user = 3\n")
14 aliceKey := inst.newKey(t, "alice") 15 aliceKey := inst.newKey(t, "alice")
15 bobKey := inst.newKey(t, "bob") 16 bobKey := inst.newKey(t, "bob")
e2e/snippetweb_test.go +1
@@ -24,6 +24,7 @@ func snippetIDFrom(t *testing.T, out string) string {
24// Snippet pages: the owner's list, one snippet with highlighted files, the 24// Snippet pages: the owner's list, one snippet with highlighted files, the
25// raw route, the owner-page link, and 404 for what the viewer may not see. 25// raw route, the owner-page link, and 404 for what the viewer may not see.
26func TestSnippetsWeb(t *testing.T) { 26func TestSnippetsWeb(t *testing.T) {
27 t.Parallel()
27 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n") 28 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n")
28 aliceKey := inst.newKey(t, "alice") 29 aliceKey := inst.newKey(t, "alice")
29 bobKey := inst.newKey(t, "bob") 30 bobKey := inst.newKey(t, "bob")
e2e/ssh_test.go +34 −26
@@ -4,11 +4,13 @@ package e2e
4import ( 4import (
5 "encoding/json" 5 "encoding/json"
6 "fmt" 6 "fmt"
7 "math/rand/v2"
7 "net" 8 "net"
8 "os" 9 "os"
9 "os/exec" 10 "os/exec"
10 "path/filepath" 11 "path/filepath"
11 "strings" 12 "strings"
13 "sync/atomic"
12 "testing" 14 "testing"
13) 15)
14 16
@@ -24,40 +26,44 @@ type instance struct {
24 sshDir string // per-user client keys live here 26 sshDir string // per-user client keys live here
25} 27}
26 28
27func buildGitbayd(t *testing.T) string { 29// nextPort hands out candidate ports. Seeded randomly so two test processes
28 t.Helper() 30// on one machine — `go test ./...` runs packages concurrently — start in
29 bin := filepath.Join(t.TempDir(), "gitbayd") 31// different places.
30 cmd := exec.Command("go", "build", "-o", bin, "gitbay.org/gitbay/cmd/gitbayd") 32var nextPort = func() *atomic.Int32 {
31 cmd.Dir = ".." 33 var n atomic.Int32
32 if out, err := cmd.CombinedOutput(); err != nil { 34 n.Store(int32(20000 + rand.IntN(20000)))
33 t.Fatalf("build gitbayd: %v\n%s", err, out) 35 return &n
34 } 36}()
35 return bin
36}
37 37
38// freePorts reserves n distinct ports. A port is chosen by binding :0 and 38// freePorts reserves n distinct ports, counting up rather than asking the
39// reading back what the kernel assigned, so every listener has to stay open 39// kernel for :0.
40// until all of them are picked — closing one before picking the next lets
41// the kernel hand out the same port again, and the instance that asked for
42// three then fails to bind its second listener.
43// 40//
44// Still a narrowing rather than a guarantee: another process can take a port 41// :0 cannot be made safe once tests run in parallel. A port is picked by
45// between the close here and the bind in gitbayd. Distinctness within one 42// binding, reading the number back and closing, and between that close and
46// instance is the part that is ours. 43// the bind inside gitbayd the kernel is free to hand the same port to
44// another instance picking at that moment. The loser does not fail
45// cleanly: waitForPort only asks whether something is listening, so a test
46// whose port was taken talks to a different test's server and reports
47// whatever that one says.
48//
49// A counter cannot collide within a process, whatever the interleaving.
50// Each candidate is still bind-tested, which skips ports other programs
51// hold. An outside process taking one in the close-to-bind window remains
52// possible, as it was before; that race is not ours to close.
47func freePorts(t *testing.T, n int) []int { 53func freePorts(t *testing.T, n int) []int {
48 t.Helper() 54 t.Helper()
49 lns := make([]net.Listener, 0, n)
50 ports := make([]int, 0, n) 55 ports := make([]int, 0, n)
51 for i := 0; i < n; i++ { 56 for len(ports) < n {
52 ln, err := net.Listen("tcp", "127.0.0.1:0") 57 p := int(nextPort.Add(1))
58 if p > 60000 {
59 t.Fatal("ran out of ports")
60 }
61 ln, err := net.Listen("tcp", fmt.Sprintf("127.0.0.1:%d", p))
53 if err != nil { 62 if err != nil {
54 t.Fatal(err) 63 continue // somebody else has it
55 } 64 }
56 lns = append(lns, ln)
57 ports = append(ports, ln.Addr().(*net.TCPAddr).Port)
58 }
59 for _, ln := range lns {
60 ln.Close() 65 ln.Close()
66 ports = append(ports, p)
61 } 67 }
62 return ports 68 return ports
63} 69}
@@ -184,6 +190,7 @@ func (i *instance) ssh(t *testing.T, key string, stdin string, args ...string) (
184} 190}
185 191
186func TestControlPlaneOverBareSSH(t *testing.T) { 192func TestControlPlaneOverBareSSH(t *testing.T) {
193 t.Parallel()
187 inst := startInstance(t) 194 inst := startInstance(t)
188 195
189 aliceKey := inst.newKey(t, "alice") 196 aliceKey := inst.newKey(t, "alice")
@@ -278,6 +285,7 @@ func TestControlPlaneOverBareSSH(t *testing.T) {
278// a row and then fails to bind its second listener, which surfaces as an 285// a row and then fails to bind its second listener, which surfaces as an
279// unrelated test timing out on "gitbayd did not start listening". 286// unrelated test timing out on "gitbayd did not start listening".
280func TestFreePortsAreDistinct(t *testing.T) { 287func TestFreePortsAreDistinct(t *testing.T) {
288 t.Parallel()
281 for round := 0; round < 50; round++ { 289 for round := 0; round < 50; round++ {
282 seen := map[int]bool{} 290 seen := map[int]bool{}
283 for _, p := range freePorts(t, 8) { 291 for _, p := range freePorts(t, 8) {
e2e/stack_test.go +1
@@ -12,6 +12,7 @@ import (
12// it onto the merged target with its reviews intact; a squash under a 12// it onto the merged target with its reviews intact; a squash under a
13// stack is refused. 13// stack is refused.
14func TestStackedMergeRequests(t *testing.T) { 14func TestStackedMergeRequests(t *testing.T) {
15 t.Parallel()
15 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n") 16 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n")
16 aliceKey := inst.newKey(t, "alice") 17 aliceKey := inst.newKey(t, "alice")
17 bobKey := inst.newKey(t, "bob") 18 bobKey := inst.newKey(t, "bob")
e2e/status_test.go +1
@@ -8,6 +8,7 @@ import (
8) 8)
9 9
10func TestCommitStatuses(t *testing.T) { 10func TestCommitStatuses(t *testing.T) {
11 t.Parallel()
11 inst := startInstance(t) 12 inst := startInstance(t)
12 aliceKey := inst.newKey(t, "alice") 13 aliceKey := inst.newKey(t, "alice")
13 bobKey := inst.newKey(t, "bob") 14 bobKey := inst.newKey(t, "bob")
e2e/system_test.go +1
@@ -15,6 +15,7 @@ import (
15// TestSystemSSHMode runs the M1/M2 scenarios against a real host sshd using 15// TestSystemSSHMode runs the M1/M2 scenarios against a real host sshd using
16// AuthorizedKeysCommand + forced command instead of the embedded listener. 16// AuthorizedKeysCommand + forced command instead of the embedded listener.
17func TestSystemSSHMode(t *testing.T) { 17func TestSystemSSHMode(t *testing.T) {
18 t.Parallel()
18 sshdBin := "/usr/sbin/sshd" 19 sshdBin := "/usr/sbin/sshd"
19 if _, err := os.Stat(sshdBin); err != nil { 20 if _, err := os.Stat(sshdBin); err != nil {
20 t.Skipf("no host sshd at %s", sshdBin) 21 t.Skipf("no host sshd at %s", sshdBin)
e2e/tagprotect_test.go +1
@@ -10,6 +10,7 @@ import (
10// Protected-tag globs refuse moving and deleting matching tags; a tag a 10// Protected-tag globs refuse moving and deleting matching tags; a tag a
11// release is anchored to refuses both on its own (#201). 11// release is anchored to refuses both on its own (#201).
12func TestTagProtection(t *testing.T) { 12func TestTagProtection(t *testing.T) {
13 t.Parallel()
13 inst := startInstance(t) 14 inst := startInstance(t)
14 aliceKey := inst.newKey(t, "alice") 15 aliceKey := inst.newKey(t, "alice")
15 inst.admin(t, "admin", "user", "create", "alice", "--key", aliceKey+".pub") 16 inst.admin(t, "admin", "user", "create", "alice", "--key", aliceKey+".pub")
e2e/teams_test.go +1
@@ -6,6 +6,7 @@ import (
6) 6)
7 7
8func TestOrgTeams(t *testing.T) { 8func TestOrgTeams(t *testing.T) {
9 t.Parallel()
9 inst := startInstance(t) 10 inst := startInstance(t)
10 adminKey := inst.newKey(t, "alice") 11 adminKey := inst.newKey(t, "alice")
11 bobKey := inst.newKey(t, "bob") 12 bobKey := inst.newKey(t, "bob")
e2e/theme_test.go +1
@@ -9,6 +9,7 @@ import (
9// web theme set fixes the colour scheme the layout stamps on <html>; the 9// web theme set fixes the colour scheme the layout stamps on <html>; the
10// account page shows the same setting and changes it (#232). 10// account page shows the same setting and changes it (#232).
11func TestWebTheme(t *testing.T) { 11func TestWebTheme(t *testing.T) {
12 t.Parallel()
12 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n") 13 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n")
13 key := inst.newKey(t, "alice") 14 key := inst.newKey(t, "alice")
14 inst.admin(t, "admin", "user", "create", "alice", "--key", key+".pub") 15 inst.admin(t, "admin", "user", "create", "alice", "--key", key+".pub")
e2e/userdelete_test.go +1
@@ -6,6 +6,7 @@ import (
6) 6)
7 7
8func TestAdminUserDelete(t *testing.T) { 8func TestAdminUserDelete(t *testing.T) {
9 t.Parallel()
9 inst := startInstance(t) 10 inst := startInstance(t)
10 aliceKey := inst.newKey(t, "alice") 11 aliceKey := inst.newKey(t, "alice")
11 inst.admin(t, "admin", "user", "create", "alice", "--key", aliceKey+".pub") 12 inst.admin(t, "admin", "user", "create", "alice", "--key", aliceKey+".pub")
e2e/watchpref_test.go +1
@@ -11,6 +11,7 @@ import (
11// read access is not enough, and off returns it to the default. The 11// read access is not enough, and off returns it to the default. The
12// account page carries the same switch (#194). 12// account page carries the same switch (#194).
13func TestWatchPreference(t *testing.T) { 13func TestWatchPreference(t *testing.T) {
14 t.Parallel()
14 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n") 15 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n")
15 aliceKey := inst.newKey(t, "alice") 16 aliceKey := inst.newKey(t, "alice")
16 bobKey := inst.newKey(t, "bob") 17 bobKey := inst.newKey(t, "bob")
e2e/web_test.go +1
@@ -27,6 +27,7 @@ func (i *instance) get(t *testing.T, path string) (int, string) {
27} 27}
28 28
29func TestWebUI(t *testing.T) { 29func TestWebUI(t *testing.T) {
30 t.Parallel()
30 inst := startInstance(t) 31 inst := startInstance(t)
31 32
32 aliceKey := inst.newKey(t, "alice") 33 aliceKey := inst.newKey(t, "alice")
e2e/webhook_test.go +2
@@ -78,6 +78,7 @@ func (h *hookReceiver) waitN(t *testing.T, n int) []capturedHook {
78} 78}
79 79
80func TestWebhooks(t *testing.T) { 80func TestWebhooks(t *testing.T) {
81 t.Parallel()
81 inst := startInstanceWith(t, "[webhooks]\nallow_local = true\n") 82 inst := startInstanceWith(t, "[webhooks]\nallow_local = true\n")
82 // Restart the daemon with a fast retry base for the failure tests. 83 // Restart the daemon with a fast retry base for the failure tests.
83 inst.proc.Process.Kill() 84 inst.proc.Process.Kill()
@@ -260,6 +261,7 @@ func TestWebhooks(t *testing.T) {
260// asserts each reaches a subscriber. Half the forge's mutations recorded 261// asserts each reaches a subscriber. Half the forge's mutations recorded
261// nothing, so a webhook could be subscribed to them and never fire. 262// nothing, so a webhook could be subscribed to them and never fire.
262func TestWebhookEventCoverage(t *testing.T) { 263func TestWebhookEventCoverage(t *testing.T) {
264 t.Parallel()
263 inst := startInstance(t) 265 inst := startInstance(t)
264 aliceKey := inst.newKey(t, "alice") 266 aliceKey := inst.newKey(t, "alice")
265 bobKey := inst.newKey(t, "bob") 267 bobKey := inst.newKey(t, "bob")
e2e/webonly_test.go +1
@@ -11,6 +11,7 @@ import (
11// ref, an archive, and the public listing. Each existed as a web route 11// ref, an archive, and the public listing. Each existed as a web route
12// whose handler went around the registry, so no other surface had them. 12// whose handler went around the registry, so no other surface had them.
13func TestWebOnlyReadsAreCommands(t *testing.T) { 13func TestWebOnlyReadsAreCommands(t *testing.T) {
14 t.Parallel()
14 inst := startInstance(t) 15 inst := startInstance(t)
15 aliceKey := inst.newKey(t, "alice") 16 aliceKey := inst.newKey(t, "alice")
16 bobKey := inst.newKey(t, "bob") 17 bobKey := inst.newKey(t, "bob")
e2e/websessions_test.go +1
@@ -10,6 +10,7 @@ import (
10// A browser session can be listed and ended from SSH, one at a time or 10// A browser session can be listed and ended from SSH, one at a time or
11// all at once, and only its owner sees it. 11// all at once, and only its owner sees it.
12func TestWebSessionsListRevoke(t *testing.T) { 12func TestWebSessionsListRevoke(t *testing.T) {
13 t.Parallel()
13 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n") 14 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n")
14 aliceKey := inst.newKey(t, "alice") 15 aliceKey := inst.newKey(t, "alice")
15 bobKey := inst.newKey(t, "bob") 16 bobKey := inst.newKey(t, "bob")
e2e/websignup_test.go +2
@@ -9,6 +9,7 @@ import (
9) 9)
10 10
11func TestWebSignup(t *testing.T) { 11func TestWebSignup(t *testing.T) {
12 t.Parallel()
12 smtp := startFakeSMTP(t) 13 smtp := startFakeSMTP(t)
13 inst := startInstanceWith(t, fmt.Sprintf( 14 inst := startInstanceWith(t, fmt.Sprintf(
14 "[web]\nmode = \"accounts\"\n[registration]\nmode = \"invite\"\n[mail]\nsmtp_host = %q\nfrom = \"noreply@gitbay.test\"\n", 15 "[web]\nmode = \"accounts\"\n[registration]\nmode = \"invite\"\n[mail]\nsmtp_host = %q\nfrom = \"noreply@gitbay.test\"\n",
@@ -73,6 +74,7 @@ func TestWebSignup(t *testing.T) {
73} 74}
74 75
75func TestWebSignupClosedInstance(t *testing.T) { 76func TestWebSignupClosedInstance(t *testing.T) {
77 t.Parallel()
76 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n") 78 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n")
77 // Closed registration: no signup route at all, and no landing hint. 79 // Closed registration: no signup route at all, and no landing hint.
78 if status, _ := inst.get(t, "/register"); status != 404 { 80 if status, _ := inst.get(t, "/register"); status != 404 {
e2e/webwrites_test.go +1
@@ -12,6 +12,7 @@ import (
12// store directly and skip the per-account quota; issue comments skipped 12// store directly and skip the per-account quota; issue comments skipped
13// the archived check (#93). 13// the archived check (#93).
14func TestWebWritesGoThroughRegistry(t *testing.T) { 14func TestWebWritesGoThroughRegistry(t *testing.T) {
15 t.Parallel()
15 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n[limits]\nmax_repos_per_user = 1\n") 16 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n[limits]\nmax_repos_per_user = 1\n")
16 aliceKey := inst.newKey(t, "alice") 17 aliceKey := inst.newKey(t, "alice")
17 inst.admin(t, "admin", "user", "create", "alice", 18 inst.admin(t, "admin", "user", "create", "alice",
e2e/wiki_test.go +1
@@ -8,6 +8,7 @@ import (
8) 8)
9 9
10func TestWikis(t *testing.T) { 10func TestWikis(t *testing.T) {
11 t.Parallel()
11 inst := startInstance(t) 12 inst := startInstance(t)
12 aliceKey := inst.newKey(t, "alice") 13 aliceKey := inst.newKey(t, "alice")
13 bobKey := inst.newKey(t, "bob") 14 bobKey := inst.newKey(t, "bob")
e2e/writerate_test.go +2
@@ -11,6 +11,7 @@ import (
11// budget is the same one whichever surface spends it. Reads are not 11// budget is the same one whichever surface spends it. Reads are not
12// charged (#148). 12// charged (#148).
13func TestWriteRateLimit(t *testing.T) { 13func TestWriteRateLimit(t *testing.T) {
14 t.Parallel()
14 inst := startInstanceWith(t, "[limits]\nwrite_rate = 4\n") 15 inst := startInstanceWith(t, "[limits]\nwrite_rate = 4\n")
15 aliceKey := inst.newKey(t, "alice") 16 aliceKey := inst.newKey(t, "alice")
16 bobKey := inst.newKey(t, "bob") 17 bobKey := inst.newKey(t, "bob")
@@ -48,6 +49,7 @@ func TestWriteRateLimit(t *testing.T) {
48// The runner protocol is exempt: a build streams its log in many small 49// The runner protocol is exempt: a build streams its log in many small
49// writes, and throttling those would throttle CI itself. 50// writes, and throttling those would throttle CI itself.
50func TestWriteRateLimitSparesTheRunner(t *testing.T) { 51func TestWriteRateLimitSparesTheRunner(t *testing.T) {
52 t.Parallel()
51 inst := startInstanceWith(t, "[limits]\nwrite_rate = 2\n") 53 inst := startInstanceWith(t, "[limits]\nwrite_rate = 2\n")
52 inst.runner = buildRunner(t) 54 inst.runner = buildRunner(t)
53 aliceKey := inst.newKey(t, "alice") 55 aliceKey := inst.newKey(t, "alice")