Commit 76df81288e

76df81288ee5e15a8fa73e0b5bd2633fb83b4cd0

parent: d2fa3c3cbb

Verified · cmc

cmc <hello@cleberg.net> · 2026-09-21 21:36 UTC

e2e: run the tests in parallel

t.Parallel on 202 of the 207 tests. The five that call t.Setenv stay
serial, since it panics in a parallel test.

freePorts had to change first. It picked a port by binding :0, reading
the number back and closing it, and between that close and the bind
inside gitbayd the kernel is free to hand the same port to another
instance picking at that moment. Run one at a time that window is never
contended. Run in parallel it is, and the loser does not fail cleanly:
waitForPort only asks whether something is listening, so a test whose
port was taken talks to another test's server and reports what it says.
An atomic counter cannot collide within a process whatever the
interleaving; each candidate is still bind-tested, so ports held by
other programs are skipped.

Locally, 12 cores: the whole suite goes from 637s to two or three
minutes. Six runs after the change measured 120s, 136s, 147s, 149s,
166s and 176s — the spread is the laptop, not the suite; all six were
green with the same 658 pass, 3 skip, 0 fail as before.

Ref #246
Makefile +2 −3
@@ -26,9 +26,8 @@ RUNNER_BIN := dist/gitbay-runner-linux-amd64
2626help:
2727 @sed -n 's/^# //p' $(MAKEFILE_LIST)
2828
29# The e2e suite runs 400-700s against go's 600s per-package default, so a
30# loaded machine turns a passing tree into a goroutine dump that reads as
31# an unrelated failure. A real hang still fails, just later.
29# -timeout 30m is a ceiling for a real hang, not a working figure: the
30# suite runs in a couple of minutes since the e2e tests went parallel.
3231test:
3332 go test ./... -count=1 -timeout 30m
3433
e2e/aboutbackfill_test.go +1
@@ -11,6 +11,7 @@ import (
1111// The about text parked by migration 0058 becomes a file in the owner's
1212// .gitbay repository. Running it twice writes nothing the second time.
1313func TestMigrateProfileAbout(t *testing.T) {
14 t.Parallel()
1415 inst := startInstance(t)
1516 aliceKey := inst.newKey(t, "alice")
1617 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,
4949}
5050
5151func TestWebAccounts(t *testing.T) {
52 t.Parallel()
5253 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n")
5354
5455 aliceKey := inst.newKey(t, "alice")
@@ -246,6 +247,7 @@ func TestWebAccounts(t *testing.T) {
246247// TestViewOnlyHasNoLoginOnTheWire is the M8 negative: in view_only mode the
247248// login route does not exist and web login over ssh is refused.
248249func TestViewOnlyHasNoLoginOnTheWire(t *testing.T) {
250 t.Parallel()
249251 inst := startInstance(t) // default: view_only
250252 aliceKey := inst.newKey(t, "alice")
251253 inst.admin(t, "admin", "user", "create", "alice", "--key", aliceKey+".pub")
@@ -263,6 +265,7 @@ func TestViewOnlyHasNoLoginOnTheWire(t *testing.T) {
263265// and its hostname: the login page prints a command to paste into a terminal,
264266// so it must name the host even when the operator has set a display title.
265267func TestTitleIsNotAHostname(t *testing.T) {
268 t.Parallel()
266269 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\ntitle = \"GitBay\"\n")
267270
268271 status, body := inst.get(t, "/login")
e2e/accountweb_test.go +1
@@ -13,6 +13,7 @@ import (
1313// browser session. Public keys are the only credential-shaped input the web
1414// accepts; secrets and token minting stay on SSH.
1515func TestAccountSettingsWeb(t *testing.T) {
16 t.Parallel()
1617 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n")
1718 aliceKey := inst.newKey(t, "alice")
1819 inst.admin(t, "admin", "user", "create", "alice",
e2e/acme_test.go +1
@@ -18,6 +18,7 @@ import (
1818// name, which a test cannot have; what matters here is that the plumbing is
1919// correct and failure to issue does not kill the daemon.
2020func TestACMEServe(t *testing.T) {
21 t.Parallel()
2122 inst := startInstanceWith(t, "") // helper for binary + keys; killed below
2223 inst.proc.Process.Kill()
2324 inst.proc.Wait()
e2e/activity_test.go +1
@@ -11,6 +11,7 @@ import (
1111)
1212
1313func TestActivityGraph(t *testing.T) {
14 t.Parallel()
1415 inst := startInstance(t)
1516 aliceKey := inst.newKey(t, "alice")
1617 bobKey := inst.newKey(t, "bob")
e2e/adminusers_test.go +7
@@ -17,6 +17,7 @@ type adminUserRow struct {
1717}
1818
1919func TestAdminUserListAndShow(t *testing.T) {
20 t.Parallel()
2021 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n")
2122 adminKey := inst.newKey(t, "root")
2223 aliceKey := inst.newKey(t, "alice")
@@ -171,6 +172,7 @@ func TestAdminUserListAndShow(t *testing.T) {
171172}
172173
173174func TestAdminPromoteDemote(t *testing.T) {
175 t.Parallel()
174176 inst := startInstance(t)
175177 rootKey := inst.newKey(t, "root")
176178 aliceKey := inst.newKey(t, "alice")
@@ -226,6 +228,7 @@ func TestAdminPromoteDemote(t *testing.T) {
226228}
227229
228230func TestAdminRepoModeration(t *testing.T) {
231 t.Parallel()
229232 inst := startInstance(t)
230233 rootKey := inst.newKey(t, "root")
231234 aliceKey := inst.newKey(t, "alice")
@@ -337,6 +340,7 @@ func TestAdminRepoModeration(t *testing.T) {
337340// commands work in an admin's SSH session and audit rows say which path
338341// ran them.
339342func TestAdminHostAndSSHAreOneSurface(t *testing.T) {
343 t.Parallel()
340344 inst := startInstance(t)
341345 rootKey := inst.newKey(t, "root")
342346 aliceKey := inst.newKey(t, "alice")
@@ -402,6 +406,7 @@ func TestAdminHostAndSSHAreOneSurface(t *testing.T) {
402406}
403407
404408func TestAuditFilters(t *testing.T) {
409 t.Parallel()
405410 inst := startInstance(t)
406411 rootKey := inst.newKey(t, "root")
407412 aliceKey := inst.newKey(t, "alice")
@@ -454,6 +459,7 @@ func TestAuditFilters(t *testing.T) {
454459}
455460
456461func TestAdminQueuesDashboard(t *testing.T) {
462 t.Parallel()
457463 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n[webhooks]\nallow_local = true\n")
458464 rootKey := inst.newKey(t, "root")
459465 aliceKey := inst.newKey(t, "alice")
@@ -568,6 +574,7 @@ func TestAdminQueuesDashboard(t *testing.T) {
568574}
569575
570576func TestAdminConfigShow(t *testing.T) {
577 t.Parallel()
571578 inst := startInstanceWith(t, "[mail]\nsmtp_host = \"127.0.0.1:1\"\nfrom = \"forge@example.test\"\nsmtp_pass = \"hunter2\"\n")
572579 out := inst.admin(t, "admin", "config", "show")
573580 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 (
1010// web can reach now that nothing is held back from it (#234). Demote
1111// and disable carry the typed-name check.
1212func TestAdminUsersWeb(t *testing.T) {
13 t.Parallel()
1314 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n")
1415 rootKey := inst.newKey(t, "root")
1516 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
4040}
4141
4242func TestJSONAPI(t *testing.T) {
43 t.Parallel()
4344 inst := startInstanceWith(t, "[api]\nenabled = true\n")
4445 aliceKey := inst.newKey(t, "alice")
4546 inst.admin(t, "admin", "user", "create", "alice",
@@ -202,6 +203,7 @@ func TestJSONAPI(t *testing.T) {
202203// metered separately from reads, and one caller cannot spend another's
203204// budget.
204205func TestAPIRateLimit(t *testing.T) {
206 t.Parallel()
205207 // 6/minute sustained, so the read burst is 6 and the write burst 0.6 —
206208 // the first write is allowed and the second is not.
207209 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
309311// with an ETag, 304 on revalidation, writes refused, and one caller's ETag
310312// never matching another's.
311313func TestAPIReadGET(t *testing.T) {
314 t.Parallel()
312315 inst := startInstanceWith(t, "[api]\nenabled = true\n")
313316 aliceKey := inst.newKey(t, "alice")
314317 bobKey := inst.newKey(t, "bob")
e2e/apiread_test.go +1
@@ -14,6 +14,7 @@ import (
1414// before: no command returned file contents, and the web's raw route
1515// authenticates by session cookie, not bearer token.
1616func TestRepoTreeAndCat(t *testing.T) {
17 t.Parallel()
1718 inst := startInstance(t)
1819 aliceKey := inst.newKey(t, "alice")
1920 bobKey := inst.newKey(t, "bob")
e2e/approvals_test.go +2
@@ -10,6 +10,7 @@ import (
1010)
1111
1212func TestMergeRequirements(t *testing.T) {
13 t.Parallel()
1314 inst := startInstance(t)
1415 aliceKey := inst.newKey(t, "alice")
1516 bobKey := inst.newKey(t, "bob")
@@ -147,6 +148,7 @@ func TestMergeRequirements(t *testing.T) {
147148// merges. When it is on, it gates independently of require_approvals —
148149// the coupling that left owners unenforced under default settings (#99).
149150func TestCodeownersToggle(t *testing.T) {
151 t.Parallel()
150152 inst := startInstance(t)
151153 aliceKey := inst.newKey(t, "alice")
152154 carolKey := inst.newKey(t, "carol")
e2e/archive_test.go +1
@@ -8,6 +8,7 @@ import (
88)
99
1010func TestArchiveAndTopics(t *testing.T) {
11 t.Parallel()
1112 inst := startInstance(t)
1213 aliceKey := inst.newKey(t, "alice")
1314 bobKey := inst.newKey(t, "bob")
e2e/audit_test.go +1
@@ -9,6 +9,7 @@ import (
99)
1010
1111func TestAuditAndHardening(t *testing.T) {
12 t.Parallel()
1213 inst := startInstanceWith(t, "[limits]\nssh_auth_rate = 3\nmax_pack_bytes = 2000\n")
1314 adminKey := inst.newKey(t, "root")
1415 aliceKey := inst.newKey(t, "alice")
e2e/autolink_test.go +1
@@ -8,6 +8,7 @@ import (
88)
99
1010func TestWebAutolinks(t *testing.T) {
11 t.Parallel()
1112 inst := startInstance(t)
1213 aliceKey := inst.newKey(t, "alice")
1314 bobKey := inst.newKey(t, "bob")
e2e/backup_test.go +1
@@ -12,6 +12,7 @@ import (
1212)
1313
1414func TestAdminBackup(t *testing.T) {
15 t.Parallel()
1516 inst := startInstance(t)
1617 aliceKey := inst.newKey(t, "alice")
1718 inst.admin(t, "admin", "user", "create", "alice",
e2e/badge_test.go +1
@@ -9,6 +9,7 @@ import (
99// says "unknown" before any build exists, and 404s for a private repo so
1010// it cannot be used to probe for one.
1111func TestBuildBadge(t *testing.T) {
12 t.Parallel()
1213 inst := startInstance(t)
1314 aliceKey := inst.newKey(t, "alice")
1415 inst.admin(t, "admin", "user", "create", "alice", "--key", aliceKey+".pub")
e2e/blame_test.go +1
@@ -9,6 +9,7 @@ import (
99)
1010
1111func TestBlameView(t *testing.T) {
12 t.Parallel()
1213 inst := startInstance(t)
1314 aliceKey := inst.newKey(t, "alice")
1415 inst.admin(t, "admin", "user", "create", "alice",
e2e/blobrender_test.go +1
@@ -10,6 +10,7 @@ import (
1010// Markdown and org files render on the blob page, with the source one
1111// click away; everything else is unchanged.
1212func TestBlobRendersMarkup(t *testing.T) {
13 t.Parallel()
1314 inst := startInstance(t)
1415 aliceKey := inst.newKey(t, "alice")
1516 inst.admin(t, "admin", "user", "create", "alice", "--key", aliceKey+".pub")
e2e/bookmarks_test.go +2
@@ -12,6 +12,7 @@ import (
1212// are something you do to someone else's repository, and the count is a
1313// signal of what people found worth returning to (#146).
1414func TestBookmarks(t *testing.T) {
15 t.Parallel()
1516 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n")
1617 aliceKey := inst.newKey(t, "alice")
1718 bobKey := inst.newKey(t, "bob")
@@ -89,6 +90,7 @@ func TestBookmarks(t *testing.T) {
8990// A repository bookmarked while public and since made private drops out
9091// of the listing rather than leaking its existence.
9192func TestBookmarkOfRepoGonePrivate(t *testing.T) {
93 t.Parallel()
9294 inst := startInstance(t)
9395 aliceKey := inst.newKey(t, "alice")
9496 bobKey := inst.newKey(t, "bob")
e2e/build_cancel_test.go +2
@@ -14,6 +14,7 @@ import (
1414// Cancelling a duplicate of a commit that already passed puts that
1515// result back on the commit.
1616func TestBuildCancel(t *testing.T) {
17 t.Parallel()
1718 inst := startInstance(t)
1819 inst.runner = buildRunner(t)
1920 aliceKey := inst.newKey(t, "alice")
@@ -91,6 +92,7 @@ func TestBuildCancel(t *testing.T) {
9192// server closes the log session, the runner kills the step, and its late
9293// report lands on a row that already says cancelled.
9394func TestBuildCancelRunning(t *testing.T) {
95 t.Parallel()
9496 inst := startInstance(t)
9597 inst.runner = buildRunner(t)
9698 aliceKey := inst.newKey(t, "alice")
e2e/buildcancelweb_test.go +1
@@ -14,6 +14,7 @@ import (
1414// real, so a stale or repeated post against a build that is no longer
1515// cancellable shows the refusal rather than a broken page.
1616func TestBuildCancelWeb(t *testing.T) {
17 t.Parallel()
1718 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n")
1819 aliceKey := inst.newKey(t, "alice")
1920 inst.admin(t, "admin", "user", "create", "alice",
e2e/buildorphan_test.go +1
@@ -13,6 +13,7 @@ import (
1313// looking like a real failure; it is cancelled when a runner claims it, and
1414// the runner gets the real build behind it instead, in the same poll.
1515func TestBuildOrphanedByForcePushCancelledAtClaim(t *testing.T) {
16 t.Parallel()
1617 inst := startInstance(t)
1718 inst.runner = buildRunner(t)
1819 aliceKey := inst.newKey(t, "alice")
e2e/ci_dedupe_test.go +1
@@ -10,6 +10,7 @@ import (
1010// A fast-forward lands the exact commit that was already built on its
1111// branch; that commit is not built again. A commit whose build failed is.
1212func TestFastForwardMergeSkipsBuiltCommit(t *testing.T) {
13 t.Parallel()
1314 inst := startInstance(t)
1415 inst.runner = buildRunner(t)
1516 aliceKey := inst.newKey(t, "alice")
e2e/ci_merge_test.go +1
@@ -12,6 +12,7 @@ import (
1212// an MR into a repository with a CI config queued nothing, while pushing
1313// the identical commit ran the whole config.
1414func TestMergeQueuesBuildsAndRecordsPush(t *testing.T) {
15 t.Parallel()
1516 inst := startInstance(t)
1617 aliceKey := inst.newKey(t, "alice")
1718 inst.admin(t, "admin", "user", "create", "alice", "--key", aliceKey+".pub",
e2e/ci_nojobs_test.go +1
@@ -11,6 +11,7 @@ import (
1111// the setting off. Nothing was ever going to report, so the gate has
1212// nothing to wait for.
1313func TestRequireChecksWithoutCIConfigMerges(t *testing.T) {
14 t.Parallel()
1415 inst := startInstance(t)
1516 aliceKey := inst.newKey(t, "alice")
1617 inst.admin(t, "admin", "user", "create", "alice", "--key", aliceKey+".pub",
e2e/ci_skipped_test.go +1
@@ -13,6 +13,7 @@ import (
1313// filtered job now records a "skipped" status instead, which the gate reads
1414// as green (#172).
1515func TestSkippedStatusSatisfiesRequireChecks(t *testing.T) {
16 t.Parallel()
1617 inst := startInstance(t)
1718 aliceKey := inst.newKey(t, "alice")
1819 inst.admin(t, "admin", "user", "create", "alice", "--key", aliceKey+".pub",
e2e/ci_test.go +3
@@ -39,6 +39,7 @@ func (i *instance) runnerOnce(t *testing.T, key string, extra ...string) string
3939// A failed build mails the repo owner with the log tail; green builds
4040// stay silent.
4141func TestBuildFailureMail(t *testing.T) {
42 t.Parallel()
4243 smtp := startFakeSMTP(t)
4344 inst := startInstanceWith(t, fmt.Sprintf(
4445 "[mail]\nsmtp_host = %q\nfrom = \"noreply@gitbay.test\"\n", smtp.addr))
@@ -80,6 +81,7 @@ func TestBuildFailureMail(t *testing.T) {
8081}
8182
8283func TestCI(t *testing.T) {
84 t.Parallel()
8385 inst := startInstance(t)
8486 inst.runner = buildRunner(t)
8587 aliceKey := inst.newKey(t, "alice")
@@ -307,6 +309,7 @@ func (i *instance) runnerJobs(t *testing.T, key, repo string, jobs int) string {
307309// transaction that selects and updates, so several workers claiming
308310// together is safe; the runner simply never used more than one (#115).
309311func TestRunnerConcurrentJobs(t *testing.T) {
312 t.Parallel()
310313 inst := startInstance(t)
311314 inst.runner = buildRunner(t)
312315 aliceKey := inst.newKey(t, "alice")
e2e/cipaths_test.go +2
@@ -11,6 +11,7 @@ import (
1111// A doc-only push queues nothing; a push touching the named path queues
1212// the job, same as before path filters existed.
1313func TestCIPaths(t *testing.T) {
14 t.Parallel()
1415 inst := startInstance(t)
1516 aliceKey := inst.newKey(t, "alice")
1617 inst.admin(t, "admin", "user", "create", "alice", "--key", aliceKey+".pub")
@@ -68,6 +69,7 @@ func TestCIPaths(t *testing.T) {
6869// that first push is always a new branch. Without a merge-base fallback,
6970// a docs-only branch queues the full suite anyway (#171).
7071func TestCIPathsNewBranch(t *testing.T) {
72 t.Parallel()
7173 inst := startInstance(t)
7274 aliceKey := inst.newKey(t, "alice")
7375 inst.admin(t, "admin", "user", "create", "alice", "--key", aliceKey+".pub")
e2e/cli_test.go +1
@@ -55,6 +55,7 @@ func (c *cli) must(t *testing.T, dir, stdin string, args ...string) string {
5555}
5656
5757func TestCLI(t *testing.T) {
58 t.Parallel()
5859 inst := startInstance(t)
5960 aliceKey := inst.newKey(t, "alice")
6061 inst.admin(t, "admin", "user", "create", "alice",
e2e/commentmigrate_test.go +1
@@ -9,6 +9,7 @@ import (
99)
1010
1111func TestMigrateCommitRefComments(t *testing.T) {
12 t.Parallel()
1213 inst := startInstance(t)
1314 aliceKey := inst.newKey(t, "alice")
1415 inst.admin(t, "admin", "user", "create", "alice", "--key", aliceKey+".pub")
e2e/commitrefs_test.go +1
@@ -8,6 +8,7 @@ import (
88)
99
1010func TestCommitMessageIssueActions(t *testing.T) {
11 t.Parallel()
1112 inst := startInstance(t)
1213 aliceKey := inst.newKey(t, "alice")
1314 inst.admin(t, "admin", "user", "create", "alice",
e2e/compare_test.go +1
@@ -10,6 +10,7 @@ import (
1010// The compare view shows what a branch adds on top of another from their
1111// merge base, and repo diff is the same range over ssh (#118).
1212func TestCompareView(t *testing.T) {
13 t.Parallel()
1314 inst := startInstance(t)
1415 aliceKey := inst.newKey(t, "alice")
1516 inst.admin(t, "admin", "user", "create", "alice", "--key", aliceKey+".pub")
e2e/dashboard_test.go +4
@@ -9,6 +9,7 @@ import (
99)
1010
1111func TestDashboard(t *testing.T) {
12 t.Parallel()
1213 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n")
1314 aliceKey := inst.newKey(t, "alice")
1415 bobKey := inst.newKey(t, "bob")
@@ -136,6 +137,7 @@ func TestDashboard(t *testing.T) {
136137// dashboard — review queue, assigned/open work, pins, and activity — in
137138// one read. Builds remain for clients that already consume them.
138139func TestDashboardCommand(t *testing.T) {
140 t.Parallel()
139141 inst := startInstance(t)
140142 aliceKey := inst.newKey(t, "alice")
141143 bobKey := inst.newKey(t, "bob")
@@ -286,6 +288,7 @@ func TestDashboardCommand(t *testing.T) {
286288// TestDashboardQueues covers the parts of the dashboard that answer "what
287289// needs me": the review queue, assigned issues, and the activity feed.
288290func TestDashboardQueues(t *testing.T) {
291 t.Parallel()
289292 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n")
290293 aliceKey := inst.newKey(t, "alice")
291294 bobKey := inst.newKey(t, "bob")
@@ -361,6 +364,7 @@ func TestDashboardQueues(t *testing.T) {
361364// folded line's state is the worse of the two, which the feed no longer
362365// draws; TestFeedLinesFoldsBuildRun covers that rule.
363366func TestDashboardFeedFoldsBuildRun(t *testing.T) {
367 t.Parallel()
364368 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n")
365369 inst.runner = buildRunner(t)
366370 aliceKey := inst.newKey(t, "alice")
e2e/dashboardbuild_test.go +1
@@ -9,6 +9,7 @@ import (
99// visible without reading the journal. It is admin-only: the exact build a
1010// host runs narrows down which known issues apply to it.
1111func TestDashboardReportsTheServerBuild(t *testing.T) {
12 t.Parallel()
1213 inst := startInstance(t)
1314
1415 adminKey := inst.newKey(t, "root")
e2e/defaultbranch_test.go +1
@@ -11,6 +11,7 @@ import (
1111// A first push of a branch other than main moves the unborn HEAD to it,
1212// and repo settings default-branch moves it later (#189).
1313func TestDefaultBranch(t *testing.T) {
14 t.Parallel()
1415 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n")
1516 aliceKey := inst.newKey(t, "alice")
1617 bobKey := inst.newKey(t, "bob")
e2e/deploykey_test.go +1
@@ -9,6 +9,7 @@ import (
99)
1010
1111func TestDeployKeys(t *testing.T) {
12 t.Parallel()
1213 inst := startInstance(t)
1314 aliceKey := inst.newKey(t, "alice")
1415 bobKey := inst.newKey(t, "bob")
e2e/deps_test.go +1
@@ -20,6 +20,7 @@ type depsStatus struct {
2020// can turn it on: the check tells a public registry what the repository
2121// depends on.
2222func TestDepsEnableDisable(t *testing.T) {
23 t.Parallel()
2324 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n")
2425 aliceKey := inst.newKey(t, "alice")
2526 inst.admin(t, "admin", "user", "create", "alice", "--key", aliceKey+".pub",
e2e/description_test.go +1
@@ -6,6 +6,7 @@ import (
66)
77
88func TestRepoDescriptions(t *testing.T) {
9 t.Parallel()
910 inst := startInstance(t)
1011 aliceKey := inst.newKey(t, "alice")
1112 inst.admin(t, "admin", "user", "create", "alice", "--key", aliceKey+".pub")
e2e/design_test.go +6
@@ -12,6 +12,7 @@ import (
1212)
1313
1414func TestReadmeRelativeLinks(t *testing.T) {
15 t.Parallel()
1516 inst := startInstance(t)
1617 aliceKey := inst.newKey(t, "alice")
1718 inst.admin(t, "admin", "user", "create", "alice", "--key", aliceKey+".pub")
@@ -177,6 +178,7 @@ func TestReadmeRelativeLinks(t *testing.T) {
177178
178179// TestLandingRoutes checks the landing page's copy and the two routes.
179180func TestLandingRoutes(t *testing.T) {
181 t.Parallel()
180182 smtp := startFakeSMTP(t)
181183 inst := startInstanceWith(t, fmt.Sprintf(
182184 "[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) {
204206}
205207
206208func TestWebInteractions(t *testing.T) {
209 t.Parallel()
207210 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n")
208211 aliceKey := inst.newKey(t, "alice")
209212 inst.admin(t, "admin", "user", "create", "alice", "--key", aliceKey+".pub", "--email", "alice@example.test", "--verified")
@@ -271,6 +274,7 @@ func TestWebInteractions(t *testing.T) {
271274}
272275
273276func TestCommitParentLinks(t *testing.T) {
277 t.Parallel()
274278 inst := startInstance(t)
275279 aliceKey := inst.newKey(t, "alice")
276280 inst.admin(t, "admin", "user", "create", "alice", "--key", aliceKey+".pub")
@@ -305,6 +309,7 @@ func TestCommitParentLinks(t *testing.T) {
305309// verified here displays the account's name rather than whatever git
306310// config carried, and that an unknown address keeps its own name.
307311func TestAuthorNamesResolve(t *testing.T) {
312 t.Parallel()
308313 inst := startInstance(t)
309314 aliceKey := inst.newKey(t, "alice")
310315 inst.admin(t, "admin", "user", "create", "alice",
@@ -365,6 +370,7 @@ func TestAuthorNamesResolve(t *testing.T) {
365370// TestTreeSearchCodeAndClone: the overview links "Search code", not
366371// "Find file", and shows two labelled clone blocks after the file table.
367372func TestTreeSearchCodeAndClone(t *testing.T) {
373 t.Parallel()
368374 inst := startInstance(t)
369375 key := inst.newKey(t, "alice")
370376 inst.admin(t, "admin", "user", "create", "alice", "--key", key+".pub")
e2e/diffcomment_test.go +1
@@ -10,6 +10,7 @@ import (
1010)
1111
1212func TestDiffComments(t *testing.T) {
13 t.Parallel()
1314 inst := startInstance(t)
1415 aliceKey := inst.newKey(t, "alice")
1516 bobKey := inst.newKey(t, "bob")
e2e/diffweb_test.go +1
@@ -11,6 +11,7 @@ import (
1111// line-number gutters, syntax highlighting, and binary files declared
1212// rather than dumped.
1313func TestDiffRendering(t *testing.T) {
14 t.Parallel()
1415 inst := startInstance(t)
1516 aliceKey := inst.newKey(t, "alice")
1617 inst.admin(t, "admin", "user", "create", "alice", "--key", aliceKey+".pub")
e2e/disabled_test.go +1
@@ -10,6 +10,7 @@ import (
1010// listener checked the flag and disabling deleted sessions but not tokens
1111// (#95).
1212func TestDisabledAccountAPI(t *testing.T) {
13 t.Parallel()
1314 inst := startInstanceWith(t, "[api]\nenabled = true\n")
1415 aliceKey := inst.newKey(t, "alice")
1516 inst.admin(t, "admin", "user", "create", "alice",
e2e/edit_test.go +1
@@ -10,6 +10,7 @@ import (
1010)
1111
1212func TestIssueMREditing(t *testing.T) {
13 t.Parallel()
1314 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n")
1415 aliceKey := inst.newKey(t, "alice")
1516 bobKey := inst.newKey(t, "bob")
e2e/effectiveaccess_test.go +1
@@ -9,6 +9,7 @@ import (
99// outsider gets one answer about an org: it exists and has members;
1010// its teams are not theirs to see (#200).
1111func TestEffectiveAccessAndOutsiders(t *testing.T) {
12 t.Parallel()
1213 inst := startInstance(t)
1314 keys := map[string]string{}
1415 for _, u := range []string{"alice", "bob", "carol", "dave", "eve"} {
e2e/emaillogin_test.go +6
@@ -11,6 +11,7 @@ import (
1111// A person with no SSH key can still get into the web UI: they ask for a
1212// link by username or verified address and it arrives by mail (#155).
1313func TestEmailLogin(t *testing.T) {
14 t.Parallel()
1415 smtp := startFakeSMTP(t)
1516 inst := startInstanceWith(t, fmt.Sprintf(
1617 "[web]\nmode = \"accounts\"\n[mail]\nsmtp_host = %q\nfrom = \"noreply@gitbay.test\"\n",
@@ -71,6 +72,7 @@ func TestEmailLogin(t *testing.T) {
7172// A verified secondary address stands in for an unverified primary:
7273// resolution by username must not stop at the primary (#158).
7374func TestEmailLoginResolvesVerifiedSecondary(t *testing.T) {
75 t.Parallel()
7476 smtp := startFakeSMTP(t)
7577 inst := startInstanceWith(t, fmt.Sprintf(
7678 "[web]\nmode = \"accounts\"\n[mail]\nsmtp_host = %q\nfrom = \"noreply@gitbay.test\"\n",
@@ -108,6 +110,7 @@ func TestEmailLoginResolvesVerifiedSecondary(t *testing.T) {
108110// The response must not say whether an account exists. A different status,
109111// body, or destination answers "is this person here?" to anyone who asks.
110112func TestEmailLoginDoesNotEnumerate(t *testing.T) {
113 t.Parallel()
111114 smtp := startFakeSMTP(t)
112115 inst := startInstanceWith(t, fmt.Sprintf(
113116 "[web]\nmode = \"accounts\"\n[mail]\nsmtp_host = %q\nfrom = \"noreply@gitbay.test\"\n",
@@ -175,6 +178,7 @@ func TestEmailLoginDoesNotEnumerate(t *testing.T) {
175178// An anonymous endpoint that sends mail needs a durable per-account bound,
176179// the same one email verification has (#136).
177180func TestEmailLoginThrottled(t *testing.T) {
181 t.Parallel()
178182 smtp := startFakeSMTP(t)
179183 inst := startInstanceWith(t, fmt.Sprintf(
180184 "[web]\nmode = \"accounts\"\n[mail]\nsmtp_host = %q\nfrom = \"noreply@gitbay.test\"\n",
@@ -220,6 +224,7 @@ func TestEmailLoginThrottled(t *testing.T) {
220224// itself a link. It must not get a session out of it: read access to the
221225// private repos it is a member of is what suspension takes away.
222226func TestEmailLoginRefusesDisabledAccount(t *testing.T) {
227 t.Parallel()
223228 smtp := startFakeSMTP(t)
224229 inst := startInstanceWith(t, fmt.Sprintf(
225230 "[web]\nmode = \"accounts\"\n[mail]\nsmtp_host = %q\nfrom = \"noreply@gitbay.test\"\n",
@@ -256,6 +261,7 @@ func TestEmailLoginRefusesDisabledAccount(t *testing.T) {
256261// login tokens, and login() refuses a disabled account after consuming one,
257262// so neither the window nor a token that somehow survives it opens a session.
258263func TestEmailLoginRefusesLinkMintedBeforeSuspension(t *testing.T) {
264 t.Parallel()
259265 smtp := startFakeSMTP(t)
260266 inst := startInstanceWith(t, fmt.Sprintf(
261267 "[web]\nmode = \"accounts\"\n[mail]\nsmtp_host = %q\nfrom = \"noreply@gitbay.test\"\n",
e2e/emailremove_test.go +1
@@ -11,6 +11,7 @@ import (
1111// store holds: the primary stays until another is primary, and the last
1212// verified address stays (#181).
1313func TestEmailRemoveAndPrimary(t *testing.T) {
14 t.Parallel()
1415 smtp := startFakeSMTP(t)
1516 inst := startInstanceWith(t, fmt.Sprintf(
1617 "[mail]\nsmtp_host = %q\nfrom = \"noreply@gitbay.test\"\n", smtp.addr))
e2e/emailthrottle_test.go +1
@@ -10,6 +10,7 @@ import (
1010// rate-limited; this path was not, so an authenticated account could
1111// enqueue mail without bound (#136).
1212func TestEmailAddThrottled(t *testing.T) {
13 t.Parallel()
1314 smtp := startFakeSMTP(t)
1415 inst := startInstanceWith(t, fmt.Sprintf(
1516 "[mail]\nsmtp_host = %q\nfrom = \"noreply@gitbay.test\"\n", smtp.addr))
e2e/facts_test.go +2
@@ -12,6 +12,7 @@ import (
1212// TestRepoFacts covers the repository summary: counts, license, languages,
1313// and contributors resolved to accounts where the email is verified.
1414func TestRepoFacts(t *testing.T) {
15 t.Parallel()
1516 inst := startInstance(t)
1617 aliceKey := inst.newKey(t, "alice")
1718 inst.admin(t, "admin", "user", "create", "alice",
@@ -102,6 +103,7 @@ WITH REGARD TO THIS SOFTWARE.
102103// account has verified here. Addresses with neither stay distinct, even
103104// when they carry the same name.
104105func TestContributorIdentity(t *testing.T) {
106 t.Parallel()
105107 smtp := startFakeSMTP(t)
106108 inst := startInstanceWith(t, fmt.Sprintf(
107109 "[mail]\nsmtp_host = %q\nfrom = \"noreply@gitbay.test\"\n", smtp.addr))
e2e/feeds_test.go +1
@@ -11,6 +11,7 @@ import (
1111// Atom feeds for releases, commits and an owner's public activity, read
1212// with no session; private repositories stay out of them (#192).
1313func TestAtomFeeds(t *testing.T) {
14 t.Parallel()
1415 inst := startInstance(t)
1516 aliceKey := inst.newKey(t, "alice")
1617 inst.admin(t, "admin", "user", "create", "alice", "--key", aliceKey+".pub")
e2e/filenav_test.go +1
@@ -10,6 +10,7 @@ import (
1010// A file page lists its directory beside the file, marks the file, and
1111// links up (desktop layout spec).
1212func TestFileNavigator(t *testing.T) {
13 t.Parallel()
1314 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n")
1415 key := inst.newKey(t, "alice")
1516 inst.admin(t, "admin", "user", "create", "alice", "--key", key+".pub")
e2e/forkweb_test.go +1
@@ -12,6 +12,7 @@ import (
1212// file in the fork, open the merge request against the parent, without a
1313// terminal at any step (#174).
1414func TestForkWeb(t *testing.T) {
15 t.Parallel()
1516 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n")
1617 aliceKey := inst.newKey(t, "alice")
1718 bobKey := inst.newKey(t, "bob")
e2e/formatpickerweb_test.go +1
@@ -12,6 +12,7 @@ import (
1212// request create forms (#160). Neither form offered --format before; a
1313// browser session could only ever write markdown.
1414func TestIssueMRWebFormat(t *testing.T) {
15 t.Parallel()
1516 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n")
1617 aliceKey := inst.newKey(t, "alice")
1718 inst.admin(t, "admin", "user", "create", "alice",
e2e/gates_test.go +1
@@ -11,6 +11,7 @@ import (
1111// unmet gate at once, and mr review says when a verdict is advisory
1212// (#199).
1313func TestMergeGatesVisible(t *testing.T) {
14 t.Parallel()
1415 inst := startInstance(t)
1516 aliceKey := inst.newKey(t, "alice")
1617 bobKey := inst.newKey(t, "bob")
e2e/ghimport_test.go +3
@@ -72,6 +72,7 @@ func fakeGitHub(t *testing.T) *httptest.Server {
7272}
7373
7474func TestGitHubIssueImport(t *testing.T) {
75 t.Parallel()
7576 // allow_local lets --api-base reach the loopback fake; a default
7677 // instance refuses it (see the SSRF check at the end).
7778 inst := startInstanceWith(t, "[webhooks]\nallow_local = true\n")
@@ -143,6 +144,7 @@ func TestGitHubIssueImport(t *testing.T) {
143144}
144145
145146func TestGitHubImportSSRFGuard(t *testing.T) {
147 t.Parallel()
146148 inst := startInstance(t) // allow_local off: default posture
147149 aliceKey := inst.newKey(t, "alice")
148150 inst.admin(t, "admin", "user", "create", "alice", "--key", aliceKey+".pub")
@@ -219,6 +221,7 @@ func fakeForgejo(t *testing.T) *httptest.Server {
219221}
220222
221223func TestForgejoIssueImport(t *testing.T) {
224 t.Parallel()
222225 inst := startInstanceWith(t, "[webhooks]\nallow_local = true\n")
223226 aliceKey := inst.newKey(t, "alice")
224227 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 {
5555}
5656
5757func TestGitOverSSH(t *testing.T) {
58 t.Parallel()
5859 inst := startInstance(t)
5960
6061 aliceKey := inst.newKey(t, "alice")
e2e/goimport_test.go +1
@@ -6,6 +6,7 @@ import (
66)
77
88func TestGoImportVanity(t *testing.T) {
9 t.Parallel()
910 inst := startInstanceWith(t, "[go_import]\n\"127.0.0.1/tool\" = \"alice/tool\"\n")
1011 aliceKey := inst.newKey(t, "alice")
1112 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 {
6464}
6565
6666func TestHTTPTransport(t *testing.T) {
67 t.Parallel()
6768 inst := startInstance(t)
6869 aliceKey := setupPublicRepo(t, inst, "alice/pub")
6970
@@ -127,6 +128,7 @@ func TestHTTPTransport(t *testing.T) {
127128}
128129
129130func TestGitDaemon(t *testing.T) {
131 t.Parallel()
130132 inst := startInstance(t)
131133 aliceKey := setupPublicRepo(t, inst, "alice/pub")
132134 gitURL := func(repo string) string {
e2e/import_test.go +1
@@ -9,6 +9,7 @@ import (
99)
1010
1111func TestRepoImport(t *testing.T) {
12 t.Parallel()
1213 inst := startInstance(t)
1314 aliceKey := inst.newKey(t, "alice")
1415 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
4141}
4242
4343func TestNotificationInbox(t *testing.T) {
44 t.Parallel()
4445 inst := startInstance(t)
4546
4647 aliceKey := inst.newKey(t, "alice")
e2e/isolation_podman_test.go +5
@@ -46,6 +46,7 @@ func havePodman(t *testing.T) bool {
4646// the runner refuses to start rather than running a build on the host.
4747// This one needs no podman, so it runs everywhere.
4848func TestRunnerRefusesToStartWithoutPodman(t *testing.T) {
49 t.Parallel()
4950 bin := buildRunner(t)
5051 cmd := exec.Command(bin, "-once", "-remote", "git@127.0.0.1",
5152 "-isolation", "podman", "-image", "localhost/whatever:1", "-workdir", t.TempDir())
@@ -66,6 +67,7 @@ func TestRunnerRefusesToStartWithoutPodman(t *testing.T) {
6667
6768// An unknown mode is refused rather than guessed at.
6869func TestRunnerRefusesUnknownIsolation(t *testing.T) {
70 t.Parallel()
6971 bin := buildRunner(t)
7072 cmd := exec.Command(bin, "-once", "-remote", "git@127.0.0.1",
7173 "-isolation", "chroot", "-workdir", t.TempDir())
@@ -81,6 +83,7 @@ func TestRunnerRefusesUnknownIsolation(t *testing.T) {
8183// With podman, a step runs in a container: it cannot read the runner's
8284// SSH key, and it does not see the runner's home.
8385func TestPodmanStepCannotReachTheRunnersKey(t *testing.T) {
86 t.Parallel()
8487 if !havePodman(t) {
8588 t.Skip("no podman")
8689 }
@@ -131,6 +134,7 @@ func TestPodmanStepCannotReachTheRunnersKey(t *testing.T) {
131134// An image this runner does not have fails the build and says an
132135// operator must provision it, rather than pulling it.
133136func TestPodmanMissingImageFailsTheBuild(t *testing.T) {
137 t.Parallel()
134138 if !havePodman(t) {
135139 t.Skip("no podman")
136140 }
@@ -192,6 +196,7 @@ func runnerPodmanOnce(t *testing.T, inst *instance, key string) {
192196// guessing one: with --pull=never an image the host does not have fails
193197// every job that names none.
194198func TestRunnerRefusesPodmanWithoutAnImage(t *testing.T) {
199 t.Parallel()
195200 bin := buildRunner(t)
196201 cmd := exec.Command(bin, "-once", "-remote", "git@127.0.0.1",
197202 "-isolation", "podman", "-workdir", t.TempDir())
e2e/isolation_test.go +1
@@ -19,6 +19,7 @@ import (
1919// of surfaces is the thing under test, so adding a route without adding
2020// it here is the omission that shows up.
2121func TestPrivateRepoIsInvisible(t *testing.T) {
22 t.Parallel()
2223 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n")
2324 ownerKey := inst.newKey(t, "owner")
2425 strangerKey := inst.newKey(t, "stranger")
e2e/issue_test.go +1
@@ -24,6 +24,7 @@ func golden(t *testing.T, raw string) string {
2424}
2525
2626func TestIssueLifecycleOverBareSSH(t *testing.T) {
27 t.Parallel()
2728 inst := startInstance(t)
2829
2930 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
2929}
3030
3131func TestIssueSearch(t *testing.T) {
32 t.Parallel()
3233 inst := startInstance(t)
3334 aliceKey := inst.newKey(t, "alice")
3435 inst.admin(t, "admin", "user", "create", "alice", "--key", aliceKey+".pub")
e2e/issueweb_test.go +1
@@ -10,6 +10,7 @@ import (
1010// the browser. Each action runs the issue command the CLI runs, so the
1111// CLI is the check that they took effect.
1212func TestIssueWebTriage(t *testing.T) {
13 t.Parallel()
1314 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n")
1415 aliceKey := inst.newKey(t, "alice")
1516 bobKey := inst.newKey(t, "bob")
e2e/label_test.go +1
@@ -8,6 +8,7 @@ import (
88// Labels get colours: set from the CLI, listed with their use, painted
99// on the web, and removed from every issue at once.
1010func TestLabelColors(t *testing.T) {
11 t.Parallel()
1112 inst := startInstance(t)
1213 aliceKey := inst.newKey(t, "alice")
1314 bobKey := inst.newKey(t, "bob")
e2e/labelweb_test.go +1
@@ -9,6 +9,7 @@ import (
99// The label set itself is managed from the browser: create, recolour and
1010// remove, each through the label command the CLI runs (#163).
1111func TestLabelsWeb(t *testing.T) {
12 t.Parallel()
1213 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n")
1314 aliceKey := inst.newKey(t, "alice")
1415 bobKey := inst.newKey(t, "bob")
e2e/lfs_test.go +1
@@ -34,6 +34,7 @@ func waitForPort(t *testing.T, port int) {
3434}
3535
3636func TestLFS(t *testing.T) {
37 t.Parallel()
3738 if _, err := exec.LookPath("git-lfs"); err != nil {
3839 t.Skip("git-lfs client not installed")
3940 }
e2e/listfilters_test.go +1
@@ -10,6 +10,7 @@ import (
1010// issue list and mr list narrow by label, assignee, author and
1111// milestone, and the web lists take the same names as query parameters.
1212func TestListFilters(t *testing.T) {
13 t.Parallel()
1314 inst := startInstance(t)
1415 aliceKey := inst.newKey(t, "alice")
1516 bobKey := inst.newKey(t, "bob")
e2e/mailpref_test.go +1
@@ -12,6 +12,7 @@ import (
1212// mail; on brings it back. The account page carries the same switch
1313// (#194).
1414func TestMailPreference(t *testing.T) {
15 t.Parallel()
1516 smtp := startFakeSMTP(t)
1617 inst := startInstanceWith(t, fmt.Sprintf(
1718 "[mail]\nsmtp_host = %q\nfrom = \"noreply@gitbay.test\"\n[web]\nmode = \"accounts\"\n", smtp.addr))
e2e/maint_test.go +1
@@ -9,6 +9,7 @@ import (
99)
1010
1111func TestAdminGCAndStats(t *testing.T) {
12 t.Parallel()
1213 inst := startInstance(t)
1314 aliceKey := inst.newKey(t, "alice")
1415 inst.admin(t, "admin", "user", "create", "alice", "--key", aliceKey+".pub")
e2e/mentions_test.go +1
@@ -10,6 +10,7 @@ import (
1010// mute holds, and someone who cannot read the repository is not reached
1111// (#202).
1212func TestMentionsNotify(t *testing.T) {
13 t.Parallel()
1314 inst := startInstance(t)
1415 keys := map[string]string{}
1516 for _, u := range []string{"alice", "bob", "carol", "eve"} {
e2e/merge_strategies_test.go +1
@@ -8,6 +8,7 @@ import (
88)
99
1010func TestSquashAndRebaseMerges(t *testing.T) {
11 t.Parallel()
1112 inst := startInstance(t)
1213 aliceKey := inst.newKey(t, "alice")
1314 bobKey := inst.newKey(t, "bob")
e2e/mergerecord_test.go +1
@@ -12,6 +12,7 @@ import (
1212// or a merge moved the ref and then failed to record itself. Either way
1313// refusing left the merge request open for good (#108).
1414func TestMergeRecordedWhenTargetContainsHead(t *testing.T) {
15 t.Parallel()
1516 inst := startInstance(t)
1617 aliceKey := inst.newKey(t, "alice")
1718 inst.admin(t, "admin", "user", "create", "alice", "--key", aliceKey+".pub")
e2e/migrate_test.go +1
@@ -8,6 +8,7 @@ import (
88)
99
1010func TestAccountMigration(t *testing.T) {
11 t.Parallel()
1112 src := startInstance(t)
1213 dst := startInstance(t)
1314 key := src.newKey(t, "alice") // one identity, both instances
e2e/migratemr_test.go +1
@@ -17,6 +17,7 @@ import (
1717// diff resolves through it and an empty head_sha is invisible — the first
1818// version of this test made that mistake and passed without the fix.
1919func TestMigratedMRHasADiff(t *testing.T) {
20 t.Parallel()
2021 src := startInstance(t)
2122 dst := startInstance(t)
2223 key := src.newKey(t, "alice")
e2e/milestone_test.go +1
@@ -9,6 +9,7 @@ import (
99)
1010
1111func TestMilestonesAndTemplates(t *testing.T) {
12 t.Parallel()
1213 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n")
1314 aliceKey := inst.newKey(t, "alice")
1415 bobKey := inst.newKey(t, "bob")
e2e/mirror_test.go +1
@@ -186,6 +186,7 @@ func TestMirrors(t *testing.T) {
186186}
187187
188188func TestMirrorSSRFGuard(t *testing.T) {
189 t.Parallel()
189190 inst := startInstance(t) // default posture: allow_local off
190191 aliceKey := inst.newKey(t, "alice")
191192 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 {
4646}
4747
4848func TestMergeRequests(t *testing.T) {
49 t.Parallel()
4950 inst := startInstance(t)
5051
5152 aliceKey := inst.newKey(t, "alice")
e2e/mrbuilds_test.go +1
@@ -16,6 +16,7 @@ import (
1616// merge request unbuildable and, under require-checks, unmergeable (#98).
1717// A head from another repository runs without the target's secrets.
1818func TestForkMRHeadIsBuilt(t *testing.T) {
19 t.Parallel()
1920 inst := startInstance(t)
2021 inst.runner = buildRunner(t)
2122 aliceKey := inst.newKey(t, "alice")
e2e/mrdraft_test.go +1
@@ -11,6 +11,7 @@ import (
1111// TestMRDraft covers the first stage of #111: a merge request opened to
1212// show work rather than to ask for a merge.
1313func TestMRDraft(t *testing.T) {
14 t.Parallel()
1415 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n")
1516 aliceKey := inst.newKey(t, "alice")
1617 bobKey := inst.newKey(t, "bob")
e2e/mrforkweb_test.go +1
@@ -12,6 +12,7 @@ import (
1212// the source picker offers the branches of a fork they can push to, and
1313// the merge request opens against the parent (#168).
1414func TestMRFromForkWeb(t *testing.T) {
15 t.Parallel()
1516 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n")
1617 aliceKey := inst.newKey(t, "alice")
1718 bobKey := inst.newKey(t, "bob")
e2e/mrrebase_test.go +2
@@ -15,6 +15,7 @@ import (
1515// server is never asked to vouch for a commit it did not receive already
1616// signed (#175).
1717func TestCLIMRRebase(t *testing.T) {
18 t.Parallel()
1819 inst := startInstance(t)
1920 aliceKey := inst.newKey(t, "alice")
2021 inst.admin(t, "admin", "user", "create", "alice",
@@ -84,6 +85,7 @@ func TestCLIMRRebase(t *testing.T) {
8485
8586// The guards: a dirty tree, and a source in a fork this clone cannot push.
8687func TestCLIMRRebaseRefusals(t *testing.T) {
88 t.Parallel()
8789 inst := startInstance(t)
8890 aliceKey := inst.newKey(t, "alice")
8991 bobKey := inst.newKey(t, "bob")
e2e/mrretarget_test.go +1
@@ -12,6 +12,7 @@ import (
1212// SSH and from the browser. Retargeting changes which diff a review was
1313// of, so the existing approvals have to go stale with it.
1414func TestMRRetarget(t *testing.T) {
15 t.Parallel()
1516 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n")
1617 aliceKey := inst.newKey(t, "alice")
1718 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 {
3737// browser. Every action runs the same control command the CLI runs, so the
3838// test also proves the merge gates apply to web merges.
3939func TestMRWebReviewLoop(t *testing.T) {
40 t.Parallel()
4041 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n")
4142 aliceKey := inst.newKey(t, "alice")
4243 bobKey := inst.newKey(t, "bob")
@@ -182,6 +183,7 @@ func TestMRWebReviewLoop(t *testing.T) {
182183// TestMRWebCreate opens a merge request from the browser and checks the
183184// form survives a refusal with the draft intact.
184185func TestMRWebCreate(t *testing.T) {
186 t.Parallel()
185187 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n")
186188 aliceKey := inst.newKey(t, "alice")
187189 inst.admin(t, "admin", "user", "create", "alice",
@@ -241,6 +243,7 @@ func TestMRWebCreate(t *testing.T) {
241243// TestMRListRows checks that the merge request list shows each row's
242244// combined check state and comment count (#230).
243245func TestMRListRows(t *testing.T) {
246 t.Parallel()
244247 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n")
245248 aliceKey := inst.newKey(t, "alice")
246249 inst.admin(t, "admin", "user", "create", "alice",
@@ -292,6 +295,7 @@ func TestMRListRows(t *testing.T) {
292295// proves the page dispatched mr diff-comment rather than writing its own
293296// rows.
294297func TestMRWebDiffThreads(t *testing.T) {
298 t.Parallel()
295299 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n")
296300 aliceKey := inst.newKey(t, "alice")
297301 inst.admin(t, "admin", "user", "create", "alice",
@@ -406,6 +410,7 @@ func (i *instance) mrThreads(t *testing.T, key, repo, n string) []mrThread {
406410// TestMRDiffEmptyExplained: a merge request whose head was fast-forwarded
407411// into the target outside the request shows why its diff is empty.
408412func TestMRDiffEmptyExplained(t *testing.T) {
413 t.Parallel()
409414 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n")
410415 key := inst.newKey(t, "alice")
411416 inst.admin(t, "admin", "user", "create", "alice", "--key", key+".pub", "--email", "alice@example.test", "--verified")
@@ -442,6 +447,7 @@ func TestMRDiffEmptyExplained(t *testing.T) {
442447// web form, and checks both pages say so; clearing it over ssh removes
443448// both lines again (#223).
444449func TestMRSupersedes(t *testing.T) {
450 t.Parallel()
445451 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n")
446452 aliceKey := inst.newKey(t, "alice")
447453 inst.admin(t, "admin", "user", "create", "alice",
@@ -507,6 +513,7 @@ func TestMRSupersedes(t *testing.T) {
507513// list by it (#231). The CLI is the check that the page dispatched
508514// mr label rather than writing its own rows.
509515func TestMRWebLabels(t *testing.T) {
516 t.Parallel()
510517 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n")
511518 aliceKey := inst.newKey(t, "alice")
512519 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 {
3939}
4040
4141func TestActivityNotifications(t *testing.T) {
42 t.Parallel()
4243 smtp := startFakeSMTP(t)
4344 inst := startInstanceWith(t, fmt.Sprintf(
4445 "[mail]\nsmtp_host = %q\nfrom = \"noreply@gitbay.test\"\n", smtp.addr))
e2e/org_test.go +1
@@ -8,6 +8,7 @@ import (
88)
99
1010func TestOrganizations(t *testing.T) {
11 t.Parallel()
1112 inst := startInstance(t)
1213 aliceKey := inst.newKey(t, "alice")
1314 bobKey := inst.newKey(t, "bob")
e2e/orgbody_test.go +1
@@ -10,6 +10,7 @@ import (
1010// without a format is markdown, which is what everything written before the
1111// format existed carries.
1212func TestOrgBodies(t *testing.T) {
13 t.Parallel()
1314 inst := startInstance(t)
1415 aliceKey := inst.newKey(t, "alice")
1516 inst.admin(t, "admin", "user", "create", "alice",
e2e/orglabels_test.go +1
@@ -11,6 +11,7 @@ import (
1111// commit in one repository closes an issue in another; the org pages
1212// answer members and outsiders as their access allows.
1313func TestOrgLabelsMilestonesAndCrossRepoCloses(t *testing.T) {
14 t.Parallel()
1415 inst := startInstance(t)
1516 aliceKey := inst.newKey(t, "alice")
1617 carolKey := inst.newKey(t, "carol")
e2e/orgremove_test.go +1
@@ -11,6 +11,7 @@ import (
1111// teams (#196). Before the fix, team_members rows survived removal, so a
1212// former member kept pushing.
1313func TestOrgMemberRemovalEndsTeamAccess(t *testing.T) {
14 t.Parallel()
1415 inst := startInstance(t)
1516 aliceKey := inst.newKey(t, "alice")
1617 bobKey := inst.newKey(t, "bob")
e2e/orgweb_test.go +2
@@ -12,6 +12,7 @@ import (
1212// membership and teams, admin-gated, dispatched through the same commands
1313// the CLI uses.
1414func TestOrgManagementWeb(t *testing.T) {
15 t.Parallel()
1516 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n")
1617 aliceKey := inst.newKey(t, "alice")
1718 bobKey := inst.newKey(t, "bob")
@@ -157,6 +158,7 @@ func orgMembers(t *testing.T, inst *instance, key string) []string {
157158// rename from the org's. Delete stays on the CLI, where a typed
158159// confirmation is the norm (#167).
159160func TestOrgLifecycleWeb(t *testing.T) {
161 t.Parallel()
160162 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n")
161163 aliceKey := inst.newKey(t, "alice")
162164 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) {
2828// Cursor pagination on the list commands: opaque cursors, stable pages,
2929// and the bare-array shape untouched when the flags are absent.
3030func TestCursorPagination(t *testing.T) {
31 t.Parallel()
3132 inst := startInstance(t)
3233 aliceKey := inst.newKey(t, "alice")
3334 inst.admin(t, "admin", "user", "create", "alice", "--key", aliceKey+".pub")
e2e/pendingreview_test.go +1
@@ -12,6 +12,7 @@ import (
1212// review and submits it as a unit, instead of every comment landing in
1313// the author's inbox the moment it is typed.
1414func TestPendingReviewBatch(t *testing.T) {
15 t.Parallel()
1516 inst := startInstance(t)
1617 authorKey := inst.newKey(t, "author")
1718 reviewerKey := inst.newKey(t, "reviewer")
e2e/previewweb_test.go +1
@@ -13,6 +13,7 @@ import (
1313// create, their edit and comment boxes, release create and edit, the
1414// profile about text, and the file editor on a path the forge renders.
1515func TestMarkupPreviewWeb(t *testing.T) {
16 t.Parallel()
1617 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n")
1718 aliceKey := inst.newKey(t, "alice")
1819 inst.admin(t, "admin", "user", "create", "alice",
e2e/profile_test.go +1
@@ -8,6 +8,7 @@ import (
88)
99
1010func TestOwnerProfiles(t *testing.T) {
11 t.Parallel()
1112 inst := startInstance(t)
1213 aliceKey := inst.newKey(t, "alice")
1314 bobKey := inst.newKey(t, "bob")
e2e/profileabout_test.go +4
@@ -8,6 +8,7 @@ import (
88// The about text is a file in <owner>/.gitbay, read on every surface with
99// the reader's own access.
1010func TestProfileAboutFromRepo(t *testing.T) {
11 t.Parallel()
1112 inst := startInstance(t)
1213 aliceKey := inst.newKey(t, "alice")
1314 inst.admin(t, "admin", "user", "create", "alice",
@@ -61,6 +62,7 @@ func TestProfileAboutFromRepo(t *testing.T) {
6162// The extension picks the format, .md wins the resolution order, and a
6263// private .gitbay keeps the about to the people who can read it.
6364func TestProfileAboutFormatAndPrivacy(t *testing.T) {
65 t.Parallel()
6466 inst := startInstance(t)
6567 aliceKey := inst.newKey(t, "alice")
6668 inst.admin(t, "admin", "user", "create", "alice",
@@ -104,6 +106,7 @@ func TestProfileAboutFormatAndPrivacy(t *testing.T) {
104106// A dot-repo is infrastructure: it stays out of explore and off the
105107// profile's repository list, and stays in the owner's own inventory.
106108func TestDotReposHiddenFromListings(t *testing.T) {
109 t.Parallel()
107110 inst := startInstance(t)
108111 aliceKey := inst.newKey(t, "alice")
109112 inst.admin(t, "admin", "user", "create", "alice",
@@ -140,6 +143,7 @@ func TestDotReposHiddenFromListings(t *testing.T) {
140143
141144// The about is not settable through profile set any more: it is a file.
142145func TestProfileSetHasNoAbout(t *testing.T) {
146 t.Parallel()
143147 inst := startInstance(t)
144148 aliceKey := inst.newKey(t, "alice")
145149 inst.admin(t, "admin", "user", "create", "alice",
e2e/profileweb_test.go +2
@@ -11,6 +11,7 @@ import (
1111// emptying a field actually clears it rather than being skipped. The about
1212// text is not on this form — it is a file, covered below.
1313func TestProfileSettingsWeb(t *testing.T) {
14 t.Parallel()
1415 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n")
1516 aliceKey := inst.newKey(t, "alice")
1617 inst.admin(t, "admin", "user", "create", "alice",
@@ -98,6 +99,7 @@ func TestProfileSettingsWeb(t *testing.T) {
9899// The settings page does not edit the about text; it creates the
99100// repository that holds it and points at the file editor.
100101func TestProfileAboutRepoFromWeb(t *testing.T) {
102 t.Parallel()
101103 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n")
102104 aliceKey := inst.newKey(t, "alice")
103105 inst.admin(t, "admin", "user", "create", "alice",
e2e/rangediff_test.go +1
@@ -12,6 +12,7 @@ import (
1212// nothing said what had changed between the two heads. A plain diff of
1313// the heads cannot answer that — it shows the whole branch again.
1414func TestMRRangeDiff(t *testing.T) {
15 t.Parallel()
1516 inst := startInstance(t)
1617 key := inst.newKey(t, "alice")
1718 inst.admin(t, "admin", "user", "create", "alice", "--key", key+".pub")
e2e/readonly_test.go +1
@@ -26,6 +26,7 @@ import (
2626// Every ReadOnly command needs an entry in readArgs; a new one without
2727// arguments here fails the test rather than going untested.
2828func TestReadOnlyCommandsWriteNothing(t *testing.T) {
29 t.Parallel()
2930 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n[webhooks]\nallow_local = true\n")
3031 aliceKey := inst.newKey(t, "alice")
3132 inst.admin(t, "admin", "user", "create", "alice", "--key", aliceKey+".pub",
e2e/reap_test.go +4
@@ -88,6 +88,7 @@ func TestStaleBuildReapedWithoutRunner(t *testing.T) {
8888}
8989
9090func TestAdminRunners(t *testing.T) {
91 t.Parallel()
9192 inst := startInstance(t)
9293 rootKey := inst.newKey(t, "root")
9394 aliceKey := inst.newKey(t, "alice")
@@ -152,6 +153,7 @@ func TestAdminRunners(t *testing.T) {
152153
153154// /healthz is unauthenticated, cache-free, and says which build serves.
154155func TestHealthz(t *testing.T) {
156 t.Parallel()
155157 inst := startInstance(t)
156158 status, body := inst.get(t, "/healthz")
157159 if status != 200 || !strings.Contains(body, `"ok":true`) || !strings.Contains(body, `"commit":"`) {
@@ -166,6 +168,7 @@ func TestHealthz(t *testing.T) {
166168// gc --lfs removes objects no pointer names, keeps referenced ones, and
167169// leaves anything young enough to be an upload ahead of its push.
168170func TestGCLFSOrphans(t *testing.T) {
171 t.Parallel()
169172 inst := startInstance(t)
170173 aliceKey := inst.newKey(t, "alice")
171174 inst.admin(t, "admin", "user", "create", "alice", "--key", aliceKey+".pub")
@@ -216,6 +219,7 @@ func TestGCLFSOrphans(t *testing.T) {
216219// backup --verify reads an archive back and says whether a restore would
217220// have what the database expects.
218221func TestBackupVerify(t *testing.T) {
222 t.Parallel()
219223 inst := startInstance(t)
220224 aliceKey := inst.newKey(t, "alice")
221225 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 {
111111}
112112
113113func TestOpenRegistration(t *testing.T) {
114 t.Parallel()
114115 smtp := startFakeSMTP(t)
115116 inst := startInstanceWith(t, fmt.Sprintf(
116117 "[registration]\nmode = \"open\"\n[mail]\nsmtp_host = %q\nfrom = \"noreply@gitbay.test\"\n", smtp.addr))
@@ -195,6 +196,7 @@ func TestOpenRegistration(t *testing.T) {
195196}
196197
197198func TestInviteRegistration(t *testing.T) {
199 t.Parallel()
198200 smtp := startFakeSMTP(t)
199201 inst := startInstanceWith(t, fmt.Sprintf(
200202 "[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 (
1010)
1111
1212func TestReleases(t *testing.T) {
13 t.Parallel()
1314 inst := startInstance(t)
1415 aliceKey := inst.newKey(t, "alice")
1516 inst.admin(t, "admin", "user", "create", "alice", "--key", aliceKey+".pub")
e2e/releaseweb_test.go +1
@@ -11,6 +11,7 @@ import (
1111// TestReleaseAndBuildWeb creates and edits a release and triggers a build
1212// from the browser, each through the command the CLI runs.
1313func TestReleaseAndBuildWeb(t *testing.T) {
14 t.Parallel()
1415 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n")
1516 inst.runner = buildRunner(t)
1617 aliceKey := inst.newKey(t, "alice")
e2e/reporename_test.go +1
@@ -10,6 +10,7 @@ import (
1010// repo rename moves the directory and the row together; what hangs off
1111// the repository by id (issues here) follows it (#190).
1212func TestRepoRename(t *testing.T) {
13 t.Parallel()
1314 inst := startInstance(t)
1415 aliceKey := inst.newKey(t, "alice")
1516 bobKey := inst.newKey(t, "bob")
e2e/reposhowstate_test.go +1
@@ -9,6 +9,7 @@ import (
99// the caller can read it, what the repository was forked from — so a
1010// client draws the real state without a second read per screen (#178).
1111func TestRepoShowCarriesViewerState(t *testing.T) {
12 t.Parallel()
1213 inst := startInstance(t)
1314 aliceKey := inst.newKey(t, "alice")
1415 bobKey := inst.newKey(t, "bob")
e2e/requiremr_test.go +1
@@ -12,6 +12,7 @@ import (
1212// not exist yet can still be created, and unprotected branches are
1313// unaffected (#197).
1414func TestRequireMR(t *testing.T) {
15 t.Parallel()
1516 inst := startInstance(t)
1617 aliceKey := inst.newKey(t, "alice")
1718 inst.admin(t, "admin", "user", "create", "alice",
e2e/reviewcarry_test.go +1
@@ -10,6 +10,7 @@ import (
1010// A rebase that leaves the merge request's diff unchanged keeps its
1111// fresh approvals; a push that changes the diff stales them (#198).
1212func TestApprovalsSurviveSameDiffRebase(t *testing.T) {
13 t.Parallel()
1314 inst := startInstance(t)
1415 aliceKey := inst.newKey(t, "alice")
1516 bobKey := inst.newKey(t, "bob")
e2e/reviewloop_test.go +1
@@ -20,6 +20,7 @@ import (
2020// exercised the loop end to end at all — every feature had its own test
2121// and none of them met.
2222func TestTwoAccountReviewLoop(t *testing.T) {
23 t.Parallel()
2324 inst := startInstance(t)
2425 authorKey := inst.newKey(t, "author")
2526 reviewerKey := inst.newKey(t, "reviewer")
e2e/reviewrequest_test.go +1
@@ -13,6 +13,7 @@ import (
1313// and --remove takes them out outright. A separate, private repository
1414// checks that requesting someone who cannot read it is refused.
1515func TestMRReviewRequest(t *testing.T) {
16 t.Parallel()
1617 inst := startInstance(t)
1718 aliceKey := inst.newKey(t, "alice")
1819 bobKey := inst.newKey(t, "bob")
e2e/reviewstanding_test.go +1
@@ -20,6 +20,7 @@ import (
2020// Reviewing stays open to everyone: an outside opinion on a public change
2121// is worth having. It just does not decide the gate.
2222func TestReviewsCountOnlyFromWriters(t *testing.T) {
23 t.Parallel()
2324 inst := startInstance(t)
2425 ownerKey := inst.newKey(t, "owner")
2526 writerKey := inst.newKey(t, "writer")
e2e/robustness_test.go +2
@@ -15,6 +15,7 @@ import (
1515// tree, cat, grep, blame and the web blob page. Nothing in the suite
1616// covered a non-ASCII path before (#129).
1717func TestNonASCIIPaths(t *testing.T) {
18 t.Parallel()
1819 inst := startInstance(t)
1920 aliceKey := inst.newKey(t, "alice")
2021 inst.admin(t, "admin", "user", "create", "alice", "--key", aliceKey+".pub")
@@ -62,6 +63,7 @@ func TestNonASCIIPaths(t *testing.T) {
6263// land: the receive hooks, the post-receive work and the store take
6364// them concurrently without losing one (#129).
6465func TestConcurrentPushes(t *testing.T) {
66 t.Parallel()
6567 inst := startInstance(t)
6668 aliceKey := inst.newKey(t, "alice")
6769 inst.admin(t, "admin", "user", "create", "alice", "--key", aliceKey+".pub")
e2e/runner_scope_test.go +2
@@ -13,6 +13,7 @@ import (
1313// that should only build one project ends up executing every repository's
1414// steps — including those of a repository it has nothing to do with.
1515func TestRunnerNextScopedToRepos(t *testing.T) {
16 t.Parallel()
1617 inst := startInstance(t)
1718 aliceKey := inst.newKey(t, "alice")
1819 inst.admin(t, "admin", "user", "create", "alice", "--key", aliceKey+".pub")
@@ -65,6 +66,7 @@ func TestRunnerNextScopedToRepos(t *testing.T) {
6566// same account's full key nor the runner key reaches the wrong side, so a
6667// key stolen from a build step cannot administer the instance (#92).
6768func TestRunnerScopedKey(t *testing.T) {
69 t.Parallel()
6870 inst := startInstance(t)
6971 aliceKey := inst.newKey(t, "alice")
7072 inst.admin(t, "admin", "user", "create", "alice", "--key", aliceKey+".pub")
e2e/runnerattach_test.go +1
@@ -14,6 +14,7 @@ import (
1414// wrote. The runner builds their push and leaves a fork's merge request
1515// head alone until started with -untrusted.
1616func TestAttachedRunnerBuildsOwnRepo(t *testing.T) {
17 t.Parallel()
1718 inst := startInstance(t)
1819 inst.runner = buildRunner(t)
1920 aliceKey := inst.newKey(t, "alice")
e2e/runnerstop_test.go +3
@@ -79,6 +79,7 @@ func (i *instance) buildStatus(t *testing.T, key string) string {
7979}
8080
8181func TestRunnerDrainsOnSIGTERM(t *testing.T) {
82 t.Parallel()
8283 inst, key := stopFixture(t)
8384 cmd := exec.Command(inst.runner, inst.runnerArgs(t, key)...)
8485 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 {
137138// The unit must let the drain happen: the stop signal reaches the runner
138139// alone, and the stop timeout outlasts a build plus the report retries.
139140func TestRunnerDropInLetsTheDrainHappen(t *testing.T) {
141 t.Parallel()
140142 conf := runnerDropIn(t)
141143 if mode := dropInValue(conf, "KillMode"); mode != "mixed" {
142144 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 {
173175// KillMode the build in flight is reported a success; under systemd's
174176// default it is not, which is the failure that shipped once.
175177func TestRunnerDrainUnderSystemd(t *testing.T) {
178 t.Parallel()
176179 if !haveUserSystemd(t) {
177180 t.Skip("no systemd user manager")
178181 }
e2e/runnerweb_test.go +1
@@ -10,6 +10,7 @@ import (
1010// The settings page attaches and detaches runners through the same
1111// commands the CLI uses, and lists what is attached.
1212func TestRunnerSettingsWeb(t *testing.T) {
13 t.Parallel()
1314 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n")
1415 aliceKey := inst.newKey(t, "alice")
1516 inst.admin(t, "admin", "user", "create", "alice",
e2e/search_test.go +1
@@ -8,6 +8,7 @@ import (
88)
99
1010func TestSearch(t *testing.T) {
11 t.Parallel()
1112 inst := startInstance(t)
1213 aliceKey := inst.newKey(t, "alice")
1314 bobKey := inst.newKey(t, "bob")
e2e/searchweb_test.go +1
@@ -9,6 +9,7 @@ import (
99// TestGlobalSearchAndNotificationsWeb covers the two web surfaces #118
1010// still lacked: /search across the instance, and the notification inbox.
1111func TestGlobalSearchAndNotificationsWeb(t *testing.T) {
12 t.Parallel()
1213 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n")
1314 aliceKey := inst.newKey(t, "alice")
1415 bobKey := inst.newKey(t, "bob")
e2e/security_test.go +1
@@ -7,6 +7,7 @@ import (
77)
88
99func TestSecurityHeaders(t *testing.T) {
10 t.Parallel()
1011 inst := startInstance(t)
1112 aliceKey := inst.newKey(t, "alice")
1213 inst.admin(t, "admin", "user", "create", "alice", "--key", aliceKey+".pub")
e2e/settingsweb_test.go +1
@@ -9,6 +9,7 @@ import (
99// TestRepoSettingsWeb drives the settings page: each control runs the
1010// command the CLI runs, so repo show and settings show are the check.
1111func TestRepoSettingsWeb(t *testing.T) {
12 t.Parallel()
1213 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n")
1314 aliceKey := inst.newKey(t, "alice")
1415 bobKey := inst.newKey(t, "bob")
e2e/shutdown_test.go +2
@@ -15,6 +15,7 @@ import (
1515// kill: no listener closed, no request or push allowed to finish. The
1616// daemon now stops its listeners, drains, and exits 0 (#105).
1717func TestServeStopsOnSIGTERM(t *testing.T) {
18 t.Parallel()
1819 inst := startInstance(t)
1920 if resp, err := http.Get(inst.base() + "/healthz"); err != nil || resp.StatusCode != 200 {
2021 t.Fatalf("healthz before shutdown: %v", err)
@@ -41,6 +42,7 @@ func TestServeStopsOnSIGTERM(t *testing.T) {
4142// idle connection is closed at once rather than holding the drain for its
4243// full 30 s; only a session mid-command is waited for (#141).
4344func TestShutdownClosesIdleConnections(t *testing.T) {
45 t.Parallel()
4446 inst := startInstance(t)
4547 aliceKey := inst.newKey(t, "alice")
4648 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 {
153153}
154154
155155func TestSignatureVerification(t *testing.T) {
156 t.Parallel()
156157 inst := startInstance(t)
157158
158159 aliceKey := inst.newKey(t, "alice")
e2e/signupnotice_test.go +2
@@ -11,6 +11,7 @@ import (
1111// becomes active. An open-mode signup counts at verification, not when
1212// the row is created, so an unverified attempt is silent (#234).
1313func TestSignupNotifiesAdmins(t *testing.T) {
14 t.Parallel()
1415 smtp := startFakeSMTP(t)
1516 inst := startInstanceWith(t, fmt.Sprintf(
1617 "[registration]\nmode = \"open\"\nnotify_admin = true\n"+
@@ -45,6 +46,7 @@ func TestSignupNotifiesAdmins(t *testing.T) {
4546// With notify_admin off, the default, the same signup mails no one but
4647// the person registering.
4748func TestSignupNoticeOffByDefault(t *testing.T) {
49 t.Parallel()
4850 smtp := startFakeSMTP(t)
4951 inst := startInstanceWith(t, fmt.Sprintf(
5052 "[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 (
1010// Snippets over SSH: create from stdin, read back, list by visibility,
1111// edit files and metadata, and the not-found rule for private ones.
1212func TestSnippets(t *testing.T) {
13 t.Parallel()
1314 inst := startInstanceWith(t, "[limits]\nmax_snippets_per_user = 3\n")
1415 aliceKey := inst.newKey(t, "alice")
1516 bobKey := inst.newKey(t, "bob")
e2e/snippetweb_test.go +1
@@ -24,6 +24,7 @@ func snippetIDFrom(t *testing.T, out string) string {
2424// Snippet pages: the owner's list, one snippet with highlighted files, the
2525// raw route, the owner-page link, and 404 for what the viewer may not see.
2626func TestSnippetsWeb(t *testing.T) {
27 t.Parallel()
2728 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n")
2829 aliceKey := inst.newKey(t, "alice")
2930 bobKey := inst.newKey(t, "bob")
e2e/ssh_test.go +35 −16
@@ -4,11 +4,13 @@ package e2e
44import (
55 "encoding/json"
66 "fmt"
7 "math/rand/v2"
78 "net"
89 "os"
910 "os/exec"
1011 "path/filepath"
1112 "strings"
13 "sync/atomic"
1214 "testing"
1315)
1416
@@ -24,29 +26,44 @@ type instance struct {
2426 sshDir string // per-user client keys live here
2527}
2628
27// freePorts reserves n distinct ports. A port is chosen by binding :0 and
28// reading back what the kernel assigned, so every listener has to stay open
29// until all of them are picked — closing one before picking the next lets
30// the kernel hand out the same port again, and the instance that asked for
31// three then fails to bind its second listener.
29// nextPort hands out candidate ports. Seeded randomly so two test processes
30// on one machine — `go test ./...` runs packages concurrently — start in
31// different places.
32var nextPort = func() *atomic.Int32 {
33 var n atomic.Int32
34 n.Store(int32(20000 + rand.IntN(20000)))
35 return &n
36}()
37
38// freePorts reserves n distinct ports, counting up rather than asking the
39// kernel for :0.
40//
41// :0 cannot be made safe once tests run in parallel. A port is picked by
42// binding, reading the number back and closing, and between that close and
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.
3248//
33// Still a narrowing rather than a guarantee: another process can take a port
34// between the close here and the bind in gitbayd. Distinctness within one
35// instance is the part that is ours.
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.
3653func freePorts(t *testing.T, n int) []int {
3754 t.Helper()
38 lns := make([]net.Listener, 0, n)
3955 ports := make([]int, 0, n)
40 for i := 0; i < n; i++ {
41 ln, err := net.Listen("tcp", "127.0.0.1:0")
56 for len(ports) < n {
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))
4262 if err != nil {
43 t.Fatal(err)
63 continue // somebody else has it
4464 }
45 lns = append(lns, ln)
46 ports = append(ports, ln.Addr().(*net.TCPAddr).Port)
47 }
48 for _, ln := range lns {
4965 ln.Close()
66 ports = append(ports, p)
5067 }
5168 return ports
5269}
@@ -173,6 +190,7 @@ func (i *instance) ssh(t *testing.T, key string, stdin string, args ...string) (
173190}
174191
175192func TestControlPlaneOverBareSSH(t *testing.T) {
193 t.Parallel()
176194 inst := startInstance(t)
177195
178196 aliceKey := inst.newKey(t, "alice")
@@ -267,6 +285,7 @@ func TestControlPlaneOverBareSSH(t *testing.T) {
267285// a row and then fails to bind its second listener, which surfaces as an
268286// unrelated test timing out on "gitbayd did not start listening".
269287func TestFreePortsAreDistinct(t *testing.T) {
288 t.Parallel()
270289 for round := 0; round < 50; round++ {
271290 seen := map[int]bool{}
272291 for _, p := range freePorts(t, 8) {
e2e/stack_test.go +1
@@ -12,6 +12,7 @@ import (
1212// it onto the merged target with its reviews intact; a squash under a
1313// stack is refused.
1414func TestStackedMergeRequests(t *testing.T) {
15 t.Parallel()
1516 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n")
1617 aliceKey := inst.newKey(t, "alice")
1718 bobKey := inst.newKey(t, "bob")
e2e/status_test.go +1
@@ -8,6 +8,7 @@ import (
88)
99
1010func TestCommitStatuses(t *testing.T) {
11 t.Parallel()
1112 inst := startInstance(t)
1213 aliceKey := inst.newKey(t, "alice")
1314 bobKey := inst.newKey(t, "bob")
e2e/system_test.go +1
@@ -15,6 +15,7 @@ import (
1515// TestSystemSSHMode runs the M1/M2 scenarios against a real host sshd using
1616// AuthorizedKeysCommand + forced command instead of the embedded listener.
1717func TestSystemSSHMode(t *testing.T) {
18 t.Parallel()
1819 sshdBin := "/usr/sbin/sshd"
1920 if _, err := os.Stat(sshdBin); err != nil {
2021 t.Skipf("no host sshd at %s", sshdBin)
e2e/tagprotect_test.go +1
@@ -10,6 +10,7 @@ import (
1010// Protected-tag globs refuse moving and deleting matching tags; a tag a
1111// release is anchored to refuses both on its own (#201).
1212func TestTagProtection(t *testing.T) {
13 t.Parallel()
1314 inst := startInstance(t)
1415 aliceKey := inst.newKey(t, "alice")
1516 inst.admin(t, "admin", "user", "create", "alice", "--key", aliceKey+".pub")
e2e/teams_test.go +1
@@ -6,6 +6,7 @@ import (
66)
77
88func TestOrgTeams(t *testing.T) {
9 t.Parallel()
910 inst := startInstance(t)
1011 adminKey := inst.newKey(t, "alice")
1112 bobKey := inst.newKey(t, "bob")
e2e/theme_test.go +1
@@ -9,6 +9,7 @@ import (
99// web theme set fixes the colour scheme the layout stamps on <html>; the
1010// account page shows the same setting and changes it (#232).
1111func TestWebTheme(t *testing.T) {
12 t.Parallel()
1213 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n")
1314 key := inst.newKey(t, "alice")
1415 inst.admin(t, "admin", "user", "create", "alice", "--key", key+".pub")
e2e/userdelete_test.go +1
@@ -6,6 +6,7 @@ import (
66)
77
88func TestAdminUserDelete(t *testing.T) {
9 t.Parallel()
910 inst := startInstance(t)
1011 aliceKey := inst.newKey(t, "alice")
1112 inst.admin(t, "admin", "user", "create", "alice", "--key", aliceKey+".pub")
e2e/watchpref_test.go +1
@@ -11,6 +11,7 @@ import (
1111// read access is not enough, and off returns it to the default. The
1212// account page carries the same switch (#194).
1313func TestWatchPreference(t *testing.T) {
14 t.Parallel()
1415 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n")
1516 aliceKey := inst.newKey(t, "alice")
1617 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) {
2727}
2828
2929func TestWebUI(t *testing.T) {
30 t.Parallel()
3031 inst := startInstance(t)
3132
3233 aliceKey := inst.newKey(t, "alice")
e2e/webhook_test.go +2
@@ -78,6 +78,7 @@ func (h *hookReceiver) waitN(t *testing.T, n int) []capturedHook {
7878}
7979
8080func TestWebhooks(t *testing.T) {
81 t.Parallel()
8182 inst := startInstanceWith(t, "[webhooks]\nallow_local = true\n")
8283 // Restart the daemon with a fast retry base for the failure tests.
8384 inst.proc.Process.Kill()
@@ -260,6 +261,7 @@ func TestWebhooks(t *testing.T) {
260261// asserts each reaches a subscriber. Half the forge's mutations recorded
261262// nothing, so a webhook could be subscribed to them and never fire.
262263func TestWebhookEventCoverage(t *testing.T) {
264 t.Parallel()
263265 inst := startInstance(t)
264266 aliceKey := inst.newKey(t, "alice")
265267 bobKey := inst.newKey(t, "bob")
e2e/webonly_test.go +1
@@ -11,6 +11,7 @@ import (
1111// ref, an archive, and the public listing. Each existed as a web route
1212// whose handler went around the registry, so no other surface had them.
1313func TestWebOnlyReadsAreCommands(t *testing.T) {
14 t.Parallel()
1415 inst := startInstance(t)
1516 aliceKey := inst.newKey(t, "alice")
1617 bobKey := inst.newKey(t, "bob")
e2e/websessions_test.go +1
@@ -10,6 +10,7 @@ import (
1010// A browser session can be listed and ended from SSH, one at a time or
1111// all at once, and only its owner sees it.
1212func TestWebSessionsListRevoke(t *testing.T) {
13 t.Parallel()
1314 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n")
1415 aliceKey := inst.newKey(t, "alice")
1516 bobKey := inst.newKey(t, "bob")
e2e/websignup_test.go +2
@@ -9,6 +9,7 @@ import (
99)
1010
1111func TestWebSignup(t *testing.T) {
12 t.Parallel()
1213 smtp := startFakeSMTP(t)
1314 inst := startInstanceWith(t, fmt.Sprintf(
1415 "[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) {
7374}
7475
7576func TestWebSignupClosedInstance(t *testing.T) {
77 t.Parallel()
7678 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n")
7779 // Closed registration: no signup route at all, and no landing hint.
7880 if status, _ := inst.get(t, "/register"); status != 404 {
e2e/webwrites_test.go +1
@@ -12,6 +12,7 @@ import (
1212// store directly and skip the per-account quota; issue comments skipped
1313// the archived check (#93).
1414func TestWebWritesGoThroughRegistry(t *testing.T) {
15 t.Parallel()
1516 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n[limits]\nmax_repos_per_user = 1\n")
1617 aliceKey := inst.newKey(t, "alice")
1718 inst.admin(t, "admin", "user", "create", "alice",
e2e/wiki_test.go +1
@@ -8,6 +8,7 @@ import (
88)
99
1010func TestWikis(t *testing.T) {
11 t.Parallel()
1112 inst := startInstance(t)
1213 aliceKey := inst.newKey(t, "alice")
1314 bobKey := inst.newKey(t, "bob")
e2e/writerate_test.go +2
@@ -11,6 +11,7 @@ import (
1111// budget is the same one whichever surface spends it. Reads are not
1212// charged (#148).
1313func TestWriteRateLimit(t *testing.T) {
14 t.Parallel()
1415 inst := startInstanceWith(t, "[limits]\nwrite_rate = 4\n")
1516 aliceKey := inst.newKey(t, "alice")
1617 bobKey := inst.newKey(t, "bob")
@@ -48,6 +49,7 @@ func TestWriteRateLimit(t *testing.T) {
4849// The runner protocol is exempt: a build streams its log in many small
4950// writes, and throttling those would throttle CI itself.
5051func TestWriteRateLimitSparesTheRunner(t *testing.T) {
52 t.Parallel()
5153 inst := startInstanceWith(t, "[limits]\nwrite_rate = 2\n")
5254 inst.runner = buildRunner(t)
5355 aliceKey := inst.newKey(t, "alice")