detached 8954329

test: run unit tests in parallel and enable the tparallel linter

t.Parallel in domain, app, clientlimit, web, githttp, rpc and eventbus unit
tests; tests that use t.Setenv, shared rigs or package-level state stay serial.
commit
895432914bb6b3475e68b8ac11d804456b314227
author
brice · · 2026-10-11 20:08 UTC
parent
0e9351b
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
46 files · +434 -1
M .golangci.yml +1 -0
@@ -7,6 +7,7 @@ linters:
77 - ineffassign
88 - misspell
99 - staticcheck
10+ - tparallel
1011 - unconvert
1112 - unused
1213 exclusions:
@@ -7,6 +7,7 @@ linters:
7 - ineffassign7 - ineffassign
8 - misspell8 - misspell
9 - staticcheck9 - staticcheck
10+ - tparallel
10 - unconvert11 - unconvert
11 - unused12 - unused
12 exclusions:13 exclusions:
M internal/adapters/eventbus/bus_test.go +5 -0
@@ -36,6 +36,7 @@ func recv(t *testing.T, ch <-chan domain.Event) domain.Event {
3636 }
3737
3838 func TestFanOut(t *testing.T) {
39+ t.Parallel()
3940 synctest.Test(t, func(t *testing.T) {
4041 ctx, cancel := context.WithCancel(context.Background())
4142 defer cancel()
@@ -52,6 +53,7 @@ func TestFanOut(t *testing.T) {
5253 }
5354
5455 func TestOnlyEventsFromSubscription(t *testing.T) {
56+ t.Parallel()
5557 synctest.Test(t, func(t *testing.T) {
5658 ctx, cancel := context.WithCancel(context.Background())
5759 defer cancel()
@@ -66,6 +68,7 @@ func TestOnlyEventsFromSubscription(t *testing.T) {
6668 }
6769
6870 func TestSlowSubscriberGetsGapAndDoesNotBlock(t *testing.T) {
71+ t.Parallel()
6972 synctest.Test(t, func(t *testing.T) {
7073 ctx, cancel := context.WithCancel(context.Background())
7174 defer cancel()
@@ -99,6 +102,7 @@ func TestSlowSubscriberGetsGapAndDoesNotBlock(t *testing.T) {
99102 }
100103
101104 func TestUnsubscribeOnContext(t *testing.T) {
105+ t.Parallel()
102106 synctest.Test(t, func(t *testing.T) {
103107 b := eventbus.New()
104108 ctx, cancel := context.WithCancel(context.Background())
@@ -120,6 +124,7 @@ func TestUnsubscribeOnContext(t *testing.T) {
120124 }
121125
122126 func TestConcurrentPublishSubscribe(t *testing.T) {
127+ t.Parallel()
123128 synctest.Test(t, func(t *testing.T) {
124129 b := eventbus.NewWithBuffer(8)
125130 ctx, cancel := context.WithCancel(context.Background())
@@ -36,6 +36,7 @@ func recv(t *testing.T, ch <-chan domain.Event) domain.Event {
36 }36 }
37 37
38 func TestFanOut(t *testing.T) {38 func TestFanOut(t *testing.T) {
39+ t.Parallel()
39 synctest.Test(t, func(t *testing.T) {40 synctest.Test(t, func(t *testing.T) {
40 ctx, cancel := context.WithCancel(context.Background())41 ctx, cancel := context.WithCancel(context.Background())
41 defer cancel()42 defer cancel()
@@ -52,6 +53,7 @@ func TestFanOut(t *testing.T) {
52 }53 }
53 54
54 func TestOnlyEventsFromSubscription(t *testing.T) {55 func TestOnlyEventsFromSubscription(t *testing.T) {
56+ t.Parallel()
55 synctest.Test(t, func(t *testing.T) {57 synctest.Test(t, func(t *testing.T) {
56 ctx, cancel := context.WithCancel(context.Background())58 ctx, cancel := context.WithCancel(context.Background())
57 defer cancel()59 defer cancel()
@@ -66,6 +68,7 @@ func TestOnlyEventsFromSubscription(t *testing.T) {
66 }68 }
67 69
68 func TestSlowSubscriberGetsGapAndDoesNotBlock(t *testing.T) {70 func TestSlowSubscriberGetsGapAndDoesNotBlock(t *testing.T) {
71+ t.Parallel()
69 synctest.Test(t, func(t *testing.T) {72 synctest.Test(t, func(t *testing.T) {
70 ctx, cancel := context.WithCancel(context.Background())73 ctx, cancel := context.WithCancel(context.Background())
71 defer cancel()74 defer cancel()
@@ -99,6 +102,7 @@ func TestSlowSubscriberGetsGapAndDoesNotBlock(t *testing.T) {
99 }102 }
100 103
101 func TestUnsubscribeOnContext(t *testing.T) {104 func TestUnsubscribeOnContext(t *testing.T) {
105+ t.Parallel()
102 synctest.Test(t, func(t *testing.T) {106 synctest.Test(t, func(t *testing.T) {
103 b := eventbus.New()107 b := eventbus.New()
104 ctx, cancel := context.WithCancel(context.Background())108 ctx, cancel := context.WithCancel(context.Background())
@@ -120,6 +124,7 @@ func TestUnsubscribeOnContext(t *testing.T) {
120 }124 }
121 125
122 func TestConcurrentPublishSubscribe(t *testing.T) {126 func TestConcurrentPublishSubscribe(t *testing.T) {
127+ t.Parallel()
123 synctest.Test(t, func(t *testing.T) {128 synctest.Test(t, func(t *testing.T) {
124 b := eventbus.NewWithBuffer(8)129 b := eventbus.NewWithBuffer(8)
125 ctx, cancel := context.WithCancel(context.Background())130 ctx, cancel := context.WithCancel(context.Background())
M internal/adapters/githttp/handler_test.go +19 -1
@@ -206,7 +206,7 @@ func envOf(body string) map[string]string {
206206 }
207207
208208 func TestHandlerRoutesAndStatuses(t *testing.T) {
209- r := newRig(t, nil)
209+ t.Parallel()
210210 tests := []struct {
211211 name string
212212 method string
@@ -235,6 +235,8 @@ func TestHandlerRoutesAndStatuses(t *testing.T) {
235235 }
236236 for _, tt := range tests {
237237 t.Run(tt.name, func(t *testing.T) {
238+ t.Parallel()
239+ r := newRig(t, nil) // a rig per subtest: its mocks record without locking
238240 user := ""
239241 if tt.pass != "" {
240242 user = "whatever" // the username is ignored
@@ -251,6 +253,7 @@ func TestHandlerRoutesAndStatuses(t *testing.T) {
251253 }
252254
253255 func TestHandlerPushAuthorizationAnswers(t *testing.T) {
256+ t.Parallel()
254257 r := newRig(t, nil)
255258 get := func() int {
256259 code, _, _ := r.do(t, "GET", "/p.git/info/refs?service=git-receive-pack", "u", "root-secret", nil, nil)
@@ -282,6 +285,7 @@ type errBoom struct{}
282285 func (errBoom) Error() string { return "boom with /internal/detail" }
283286
284287 func TestHandlerBackendEnvironment(t *testing.T) {
288+ t.Parallel()
285289 r := newRig(t, nil)
286290 hdr := map[string]string{
287291 "Cookie": "session=secret",
@@ -338,12 +342,14 @@ func TestHandlerBackendEnvironment(t *testing.T) {
338342 }
339343
340344 func TestHandlerNormalizesRepositoryPath(t *testing.T) {
345+ t.Parallel()
341346 r := newRig(t, nil)
342347 _, body, _ := r.do(t, "GET", "/pub/info/refs?service=git-upload-pack", "", "", nil, nil)
343348 assert.Equal(t, "/pub.git/info/refs", envOf(body)["PATH_INFO"], "the backend always sees <name>.git")
344349 }
345350
346351 func TestHandlerChunkedBodyIsStreamed(t *testing.T) {
352+ t.Parallel()
347353 r := newRig(t, nil)
348354 payload := strings.Repeat("x", 300_000)
349355 // An io.Reader of unknown length makes the client send chunked.
@@ -359,6 +365,7 @@ func TestHandlerChunkedBodyIsStreamed(t *testing.T) {
359365 }
360366
361367 func TestHandlerBodyLimit(t *testing.T) {
368+ t.Parallel()
362369 r := newRig(t, func(c *githttp.Config) { c.MaxBodyBytes = 1000 })
363370 big := strings.Repeat("x", 5000)
364371 code, _, _ := r.do(t, "POST", "/pub.git/git-receive-pack", "u", "root-secret", strings.NewReader(big), nil)
@@ -376,6 +383,7 @@ func TestHandlerBodyLimit(t *testing.T) {
376383 }
377384
378385 func TestHandlerUploadPackBodyIsBoundedBelowThePushLimit(t *testing.T) {
386+ t.Parallel()
379387 r := newRig(t, nil) // the push limit is 4 GiB
380388 big := strings.NewReader(strings.Repeat("x", 64<<20+1))
381389 code, _, _ := r.do(t, "POST", "/pub.git/git-upload-pack", "", "", big, nil)
@@ -383,6 +391,7 @@ func TestHandlerUploadPackBodyIsBoundedBelowThePushLimit(t *testing.T) {
383391 }
384392
385393 func TestHandlerServiceQuery(t *testing.T) {
394+ t.Parallel()
386395 r := newRig(t, nil)
387396 for _, q := range []string{
388397 "service=git-upload-pack&service=git-receive-pack",
@@ -400,6 +409,7 @@ func TestHandlerServiceQuery(t *testing.T) {
400409 }
401410
402411 func TestHandlerGitProtocolHeader(t *testing.T) {
412+ t.Parallel()
403413 r := newRig(t, nil)
404414 tests := []struct {
405415 value string
@@ -437,6 +447,7 @@ func TestHandlerDoesNotInheritTheProcessEnvironment(t *testing.T) {
437447 }
438448
439449 func TestHandlerBackendFailure(t *testing.T) {
450+ t.Parallel()
440451 r := newRig(t, nil)
441452 r.backendErr = io.ErrUnexpectedEOF
442453 code, body, _ := r.do(t, "GET", "/pub.git/info/refs?service=git-upload-pack", "", "", nil, nil)
@@ -445,6 +456,7 @@ func TestHandlerBackendFailure(t *testing.T) {
445456 }
446457
447458 func TestHandlerLimiter(t *testing.T) {
459+ t.Parallel()
448460 r := newRig(t, func(c *githttp.Config) { c.Limiter = clientlimit.NewLimiter(nil) })
449461 for i := range clientlimit.MaxFailures {
450462 code, _, _ := r.do(t, "GET", "/pub.git/info/refs?service=git-upload-pack", "u", "wrong", nil, nil)
@@ -460,6 +472,7 @@ func TestHandlerLimiter(t *testing.T) {
460472 }
461473
462474 func TestHandlerLimiterTrustedProxyViaConfig(t *testing.T) {
475+ t.Parallel()
463476 r := newRig(t, func(c *githttp.Config) {
464477 c.TrustedProxies = []netip.Prefix{netip.MustParsePrefix("127.0.0.0/8")}
465478 })
@@ -473,6 +486,7 @@ func TestHandlerLimiterTrustedProxyViaConfig(t *testing.T) {
473486 }
474487
475488 func TestHandlerBearerAndMalformedAuthorization(t *testing.T) {
489+ t.Parallel()
476490 r := newRig(t, nil)
477491 path := "/priv.git/info/refs?service=git-upload-pack"
478492 code, _, _ := r.do(t, "GET", path, "", "", nil, map[string]string{"Authorization": "Bearer reader-secret"})
@@ -493,8 +507,10 @@ func TestHandlerBearerAndMalformedAuthorization(t *testing.T) {
493507 }
494508
495509 func TestHandlerUsesTheFailureLimiter(t *testing.T) {
510+ t.Parallel()
496511 path := "/pub.git/info/refs?service=git-upload-pack"
497512 t.Run("a blocked client is refused before authentication", func(t *testing.T) {
513+ t.Parallel()
498514 limiter := ghmocks.NewFailureLimiter(t)
499515 limiter.EXPECT().Blocked(mock.Anything, "127.0.0.1").Return(true, 3*time.Second).Once()
500516 r := newRig(t, func(c *githttp.Config) { c.Limiter = limiter })
@@ -504,6 +520,7 @@ func TestHandlerUsesTheFailureLimiter(t *testing.T) {
504520 assert.Zero(t, r.authenticateCalls())
505521 })
506522 t.Run("an invalid secret is recorded as a failure", func(t *testing.T) {
523+ t.Parallel()
507524 limiter := ghmocks.NewFailureLimiter(t)
508525 limiter.EXPECT().Blocked(mock.Anything, "127.0.0.1").Return(false, time.Duration(0)).Once()
509526 limiter.EXPECT().Fail(mock.Anything, "127.0.0.1").Once()
@@ -512,6 +529,7 @@ func TestHandlerUsesTheFailureLimiter(t *testing.T) {
512529 assert.Equal(t, 401, code)
513530 })
514531 t.Run("a valid secret is not a failure", func(t *testing.T) {
532+ t.Parallel()
515533 limiter := ghmocks.NewFailureLimiter(t)
516534 limiter.EXPECT().Blocked(mock.Anything, "127.0.0.1").Return(false, time.Duration(0)).Once()
517535 r := newRig(t, func(c *githttp.Config) { c.Limiter = limiter })
@@ -206,7 +206,7 @@ func envOf(body string) map[string]string {
206 }206 }
207 207
208 func TestHandlerRoutesAndStatuses(t *testing.T) {208 func TestHandlerRoutesAndStatuses(t *testing.T) {
209- r := newRig(t, nil)209+ t.Parallel()
210 tests := []struct {210 tests := []struct {
211 name string211 name string
212 method string212 method string
@@ -235,6 +235,8 @@ func TestHandlerRoutesAndStatuses(t *testing.T) {
235 }235 }
236 for _, tt := range tests {236 for _, tt := range tests {
237 t.Run(tt.name, func(t *testing.T) {237 t.Run(tt.name, func(t *testing.T) {
238+ t.Parallel()
239+ r := newRig(t, nil) // a rig per subtest: its mocks record without locking
238 user := ""240 user := ""
239 if tt.pass != "" {241 if tt.pass != "" {
240 user = "whatever" // the username is ignored242 user = "whatever" // the username is ignored
@@ -251,6 +253,7 @@ func TestHandlerRoutesAndStatuses(t *testing.T) {
251 }253 }
252 254
253 func TestHandlerPushAuthorizationAnswers(t *testing.T) {255 func TestHandlerPushAuthorizationAnswers(t *testing.T) {
256+ t.Parallel()
254 r := newRig(t, nil)257 r := newRig(t, nil)
255 get := func() int {258 get := func() int {
256 code, _, _ := r.do(t, "GET", "/p.git/info/refs?service=git-receive-pack", "u", "root-secret", nil, nil)259 code, _, _ := r.do(t, "GET", "/p.git/info/refs?service=git-receive-pack", "u", "root-secret", nil, nil)
@@ -282,6 +285,7 @@ type errBoom struct{}
282 func (errBoom) Error() string { return "boom with /internal/detail" }285 func (errBoom) Error() string { return "boom with /internal/detail" }
283 286
284 func TestHandlerBackendEnvironment(t *testing.T) {287 func TestHandlerBackendEnvironment(t *testing.T) {
288+ t.Parallel()
285 r := newRig(t, nil)289 r := newRig(t, nil)
286 hdr := map[string]string{290 hdr := map[string]string{
287 "Cookie": "session=secret",291 "Cookie": "session=secret",
@@ -338,12 +342,14 @@ func TestHandlerBackendEnvironment(t *testing.T) {
338 }342 }
339 343
340 func TestHandlerNormalizesRepositoryPath(t *testing.T) {344 func TestHandlerNormalizesRepositoryPath(t *testing.T) {
345+ t.Parallel()
341 r := newRig(t, nil)346 r := newRig(t, nil)
342 _, body, _ := r.do(t, "GET", "/pub/info/refs?service=git-upload-pack", "", "", nil, nil)347 _, body, _ := r.do(t, "GET", "/pub/info/refs?service=git-upload-pack", "", "", nil, nil)
343 assert.Equal(t, "/pub.git/info/refs", envOf(body)["PATH_INFO"], "the backend always sees <name>.git")348 assert.Equal(t, "/pub.git/info/refs", envOf(body)["PATH_INFO"], "the backend always sees <name>.git")
344 }349 }
345 350
346 func TestHandlerChunkedBodyIsStreamed(t *testing.T) {351 func TestHandlerChunkedBodyIsStreamed(t *testing.T) {
352+ t.Parallel()
347 r := newRig(t, nil)353 r := newRig(t, nil)
348 payload := strings.Repeat("x", 300_000)354 payload := strings.Repeat("x", 300_000)
349 // An io.Reader of unknown length makes the client send chunked.355 // An io.Reader of unknown length makes the client send chunked.
@@ -359,6 +365,7 @@ func TestHandlerChunkedBodyIsStreamed(t *testing.T) {
359 }365 }
360 366
361 func TestHandlerBodyLimit(t *testing.T) {367 func TestHandlerBodyLimit(t *testing.T) {
368+ t.Parallel()
362 r := newRig(t, func(c *githttp.Config) { c.MaxBodyBytes = 1000 })369 r := newRig(t, func(c *githttp.Config) { c.MaxBodyBytes = 1000 })
363 big := strings.Repeat("x", 5000)370 big := strings.Repeat("x", 5000)
364 code, _, _ := r.do(t, "POST", "/pub.git/git-receive-pack", "u", "root-secret", strings.NewReader(big), nil)371 code, _, _ := r.do(t, "POST", "/pub.git/git-receive-pack", "u", "root-secret", strings.NewReader(big), nil)
@@ -376,6 +383,7 @@ func TestHandlerBodyLimit(t *testing.T) {
376 }383 }
377 384
378 func TestHandlerUploadPackBodyIsBoundedBelowThePushLimit(t *testing.T) {385 func TestHandlerUploadPackBodyIsBoundedBelowThePushLimit(t *testing.T) {
386+ t.Parallel()
379 r := newRig(t, nil) // the push limit is 4 GiB387 r := newRig(t, nil) // the push limit is 4 GiB
380 big := strings.NewReader(strings.Repeat("x", 64<<20+1))388 big := strings.NewReader(strings.Repeat("x", 64<<20+1))
381 code, _, _ := r.do(t, "POST", "/pub.git/git-upload-pack", "", "", big, nil)389 code, _, _ := r.do(t, "POST", "/pub.git/git-upload-pack", "", "", big, nil)
@@ -383,6 +391,7 @@ func TestHandlerUploadPackBodyIsBoundedBelowThePushLimit(t *testing.T) {
383 }391 }
384 392
385 func TestHandlerServiceQuery(t *testing.T) {393 func TestHandlerServiceQuery(t *testing.T) {
394+ t.Parallel()
386 r := newRig(t, nil)395 r := newRig(t, nil)
387 for _, q := range []string{396 for _, q := range []string{
388 "service=git-upload-pack&service=git-receive-pack",397 "service=git-upload-pack&service=git-receive-pack",
@@ -400,6 +409,7 @@ func TestHandlerServiceQuery(t *testing.T) {
400 }409 }
401 410
402 func TestHandlerGitProtocolHeader(t *testing.T) {411 func TestHandlerGitProtocolHeader(t *testing.T) {
412+ t.Parallel()
403 r := newRig(t, nil)413 r := newRig(t, nil)
404 tests := []struct {414 tests := []struct {
405 value string415 value string
@@ -437,6 +447,7 @@ func TestHandlerDoesNotInheritTheProcessEnvironment(t *testing.T) {
437 }447 }
438 448
439 func TestHandlerBackendFailure(t *testing.T) {449 func TestHandlerBackendFailure(t *testing.T) {
450+ t.Parallel()
440 r := newRig(t, nil)451 r := newRig(t, nil)
441 r.backendErr = io.ErrUnexpectedEOF452 r.backendErr = io.ErrUnexpectedEOF
442 code, body, _ := r.do(t, "GET", "/pub.git/info/refs?service=git-upload-pack", "", "", nil, nil)453 code, body, _ := r.do(t, "GET", "/pub.git/info/refs?service=git-upload-pack", "", "", nil, nil)
@@ -445,6 +456,7 @@ func TestHandlerBackendFailure(t *testing.T) {
445 }456 }
446 457
447 func TestHandlerLimiter(t *testing.T) {458 func TestHandlerLimiter(t *testing.T) {
459+ t.Parallel()
448 r := newRig(t, func(c *githttp.Config) { c.Limiter = clientlimit.NewLimiter(nil) })460 r := newRig(t, func(c *githttp.Config) { c.Limiter = clientlimit.NewLimiter(nil) })
449 for i := range clientlimit.MaxFailures {461 for i := range clientlimit.MaxFailures {
450 code, _, _ := r.do(t, "GET", "/pub.git/info/refs?service=git-upload-pack", "u", "wrong", nil, nil)462 code, _, _ := r.do(t, "GET", "/pub.git/info/refs?service=git-upload-pack", "u", "wrong", nil, nil)
@@ -460,6 +472,7 @@ func TestHandlerLimiter(t *testing.T) {
460 }472 }
461 473
462 func TestHandlerLimiterTrustedProxyViaConfig(t *testing.T) {474 func TestHandlerLimiterTrustedProxyViaConfig(t *testing.T) {
475+ t.Parallel()
463 r := newRig(t, func(c *githttp.Config) {476 r := newRig(t, func(c *githttp.Config) {
464 c.TrustedProxies = []netip.Prefix{netip.MustParsePrefix("127.0.0.0/8")}477 c.TrustedProxies = []netip.Prefix{netip.MustParsePrefix("127.0.0.0/8")}
465 })478 })
@@ -473,6 +486,7 @@ func TestHandlerLimiterTrustedProxyViaConfig(t *testing.T) {
473 }486 }
474 487
475 func TestHandlerBearerAndMalformedAuthorization(t *testing.T) {488 func TestHandlerBearerAndMalformedAuthorization(t *testing.T) {
489+ t.Parallel()
476 r := newRig(t, nil)490 r := newRig(t, nil)
477 path := "/priv.git/info/refs?service=git-upload-pack"491 path := "/priv.git/info/refs?service=git-upload-pack"
478 code, _, _ := r.do(t, "GET", path, "", "", nil, map[string]string{"Authorization": "Bearer reader-secret"})492 code, _, _ := r.do(t, "GET", path, "", "", nil, map[string]string{"Authorization": "Bearer reader-secret"})
@@ -493,8 +507,10 @@ func TestHandlerBearerAndMalformedAuthorization(t *testing.T) {
493 }507 }
494 508
495 func TestHandlerUsesTheFailureLimiter(t *testing.T) {509 func TestHandlerUsesTheFailureLimiter(t *testing.T) {
510+ t.Parallel()
496 path := "/pub.git/info/refs?service=git-upload-pack"511 path := "/pub.git/info/refs?service=git-upload-pack"
497 t.Run("a blocked client is refused before authentication", func(t *testing.T) {512 t.Run("a blocked client is refused before authentication", func(t *testing.T) {
513+ t.Parallel()
498 limiter := ghmocks.NewFailureLimiter(t)514 limiter := ghmocks.NewFailureLimiter(t)
499 limiter.EXPECT().Blocked(mock.Anything, "127.0.0.1").Return(true, 3*time.Second).Once()515 limiter.EXPECT().Blocked(mock.Anything, "127.0.0.1").Return(true, 3*time.Second).Once()
500 r := newRig(t, func(c *githttp.Config) { c.Limiter = limiter })516 r := newRig(t, func(c *githttp.Config) { c.Limiter = limiter })
@@ -504,6 +520,7 @@ func TestHandlerUsesTheFailureLimiter(t *testing.T) {
504 assert.Zero(t, r.authenticateCalls())520 assert.Zero(t, r.authenticateCalls())
505 })521 })
506 t.Run("an invalid secret is recorded as a failure", func(t *testing.T) {522 t.Run("an invalid secret is recorded as a failure", func(t *testing.T) {
523+ t.Parallel()
507 limiter := ghmocks.NewFailureLimiter(t)524 limiter := ghmocks.NewFailureLimiter(t)
508 limiter.EXPECT().Blocked(mock.Anything, "127.0.0.1").Return(false, time.Duration(0)).Once()525 limiter.EXPECT().Blocked(mock.Anything, "127.0.0.1").Return(false, time.Duration(0)).Once()
509 limiter.EXPECT().Fail(mock.Anything, "127.0.0.1").Once()526 limiter.EXPECT().Fail(mock.Anything, "127.0.0.1").Once()
@@ -512,6 +529,7 @@ func TestHandlerUsesTheFailureLimiter(t *testing.T) {
512 assert.Equal(t, 401, code)529 assert.Equal(t, 401, code)
513 })530 })
514 t.Run("a valid secret is not a failure", func(t *testing.T) {531 t.Run("a valid secret is not a failure", func(t *testing.T) {
532+ t.Parallel()
515 limiter := ghmocks.NewFailureLimiter(t)533 limiter := ghmocks.NewFailureLimiter(t)
516 limiter.EXPECT().Blocked(mock.Anything, "127.0.0.1").Return(false, time.Duration(0)).Once()534 limiter.EXPECT().Blocked(mock.Anything, "127.0.0.1").Return(false, time.Duration(0)).Once()
517 r := newRig(t, func(c *githttp.Config) { c.Limiter = limiter })535 r := newRig(t, func(c *githttp.Config) { c.Limiter = limiter })
M internal/adapters/githttp/hooks_test.go +7 -0
@@ -79,6 +79,7 @@ func raw(t *testing.T, sock, line string) githttp.HookReply {
7979 }
8080
8181 func TestHookSocketMode(t *testing.T) {
82+ t.Parallel()
8283 _, sock := startHooks(t, ghmocks.NewPushHooks(t))
8384 fi, err := os.Stat(sock)
8485 require.NoError(t, err)
@@ -87,6 +88,7 @@ func TestHookSocketMode(t *testing.T) {
8788 }
8889
8990 func TestHookServerReplacesStaleSocketFile(t *testing.T) {
91+ t.Parallel()
9092 dir, err := os.MkdirTemp("", "hk")
9193 require.NoError(t, err)
9294 defer func() { _ = os.RemoveAll(dir) }()
@@ -98,6 +100,7 @@ func TestHookServerReplacesStaleSocketFile(t *testing.T) {
98100 }
99101
100102 func TestHookServerRejectsTooLongSocketPath(t *testing.T) {
103+ t.Parallel()
101104 s := githttp.NewHookServer("/"+strings.Repeat("a", 120)+"/hook.sock", ghmocks.NewPushHooks(t))
102105 err := s.Listen(context.Background())
103106 require.Error(t, err)
@@ -198,6 +201,7 @@ func TestHookProtocol(t *testing.T) {
198201 // A shutdown while a push is being recorded must not drop the recording: the
199202 // push is already in the repository.
200203 func TestHookRecordSurvivesShutdown(t *testing.T) {
204+ t.Parallel()
201205 hooks := ghmocks.NewPushHooks(t)
202206 started, proceed := make(chan struct{}), make(chan struct{})
203207 var cancelled, hasDeadline bool
@@ -252,6 +256,7 @@ func TestHookRecordSurvivesShutdown(t *testing.T) {
252256 }
253257
254258 func TestHookConcurrentTokens(t *testing.T) {
259+ t.Parallel()
255260 hooks := ghmocks.NewPushHooks(t)
256261 srv, sock := startHooks(t, hooks)
257262 var wg sync.WaitGroup
@@ -358,6 +363,7 @@ func TestRunHook(t *testing.T) {
358363 }
359364
360365 func TestWriteHookScripts(t *testing.T) {
366+ t.Parallel()
361367 dir := filepath.Join(t.TempDir(), "run", "hooks")
362368 exe := "/opt/it's here/\"ermite\" $HOME `x`/ermite"
363369 require.NoError(t, githttp.WriteHookScripts(dir, exe))
@@ -384,6 +390,7 @@ func TestWriteHookScripts(t *testing.T) {
384390
385391 // The shell really passes the awkward path and the arguments through.
386392 func TestHookScriptExecsTheBinaryWithQuotedPath(t *testing.T) {
393+ t.Parallel()
387394 base := filepath.Join(t.TempDir(), "it's a \"dir\" $X `y` ; z")
388395 require.NoError(t, os.MkdirAll(base, 0o755))
389396 exe := filepath.Join(base, "fake ermite")
@@ -79,6 +79,7 @@ func raw(t *testing.T, sock, line string) githttp.HookReply {
79 }79 }
80 80
81 func TestHookSocketMode(t *testing.T) {81 func TestHookSocketMode(t *testing.T) {
82+ t.Parallel()
82 _, sock := startHooks(t, ghmocks.NewPushHooks(t))83 _, sock := startHooks(t, ghmocks.NewPushHooks(t))
83 fi, err := os.Stat(sock)84 fi, err := os.Stat(sock)
84 require.NoError(t, err)85 require.NoError(t, err)
@@ -87,6 +88,7 @@ func TestHookSocketMode(t *testing.T) {
87 }88 }
88 89
89 func TestHookServerReplacesStaleSocketFile(t *testing.T) {90 func TestHookServerReplacesStaleSocketFile(t *testing.T) {
91+ t.Parallel()
90 dir, err := os.MkdirTemp("", "hk")92 dir, err := os.MkdirTemp("", "hk")
91 require.NoError(t, err)93 require.NoError(t, err)
92 defer func() { _ = os.RemoveAll(dir) }()94 defer func() { _ = os.RemoveAll(dir) }()
@@ -98,6 +100,7 @@ func TestHookServerReplacesStaleSocketFile(t *testing.T) {
98 }100 }
99 101
100 func TestHookServerRejectsTooLongSocketPath(t *testing.T) {102 func TestHookServerRejectsTooLongSocketPath(t *testing.T) {
103+ t.Parallel()
101 s := githttp.NewHookServer("/"+strings.Repeat("a", 120)+"/hook.sock", ghmocks.NewPushHooks(t))104 s := githttp.NewHookServer("/"+strings.Repeat("a", 120)+"/hook.sock", ghmocks.NewPushHooks(t))
102 err := s.Listen(context.Background())105 err := s.Listen(context.Background())
103 require.Error(t, err)106 require.Error(t, err)
@@ -198,6 +201,7 @@ func TestHookProtocol(t *testing.T) {
198 // A shutdown while a push is being recorded must not drop the recording: the201 // A shutdown while a push is being recorded must not drop the recording: the
199 // push is already in the repository.202 // push is already in the repository.
200 func TestHookRecordSurvivesShutdown(t *testing.T) {203 func TestHookRecordSurvivesShutdown(t *testing.T) {
204+ t.Parallel()
201 hooks := ghmocks.NewPushHooks(t)205 hooks := ghmocks.NewPushHooks(t)
202 started, proceed := make(chan struct{}), make(chan struct{})206 started, proceed := make(chan struct{}), make(chan struct{})
203 var cancelled, hasDeadline bool207 var cancelled, hasDeadline bool
@@ -252,6 +256,7 @@ func TestHookRecordSurvivesShutdown(t *testing.T) {
252 }256 }
253 257
254 func TestHookConcurrentTokens(t *testing.T) {258 func TestHookConcurrentTokens(t *testing.T) {
259+ t.Parallel()
255 hooks := ghmocks.NewPushHooks(t)260 hooks := ghmocks.NewPushHooks(t)
256 srv, sock := startHooks(t, hooks)261 srv, sock := startHooks(t, hooks)
257 var wg sync.WaitGroup262 var wg sync.WaitGroup
@@ -358,6 +363,7 @@ func TestRunHook(t *testing.T) {
358 }363 }
359 364
360 func TestWriteHookScripts(t *testing.T) {365 func TestWriteHookScripts(t *testing.T) {
366+ t.Parallel()
361 dir := filepath.Join(t.TempDir(), "run", "hooks")367 dir := filepath.Join(t.TempDir(), "run", "hooks")
362 exe := "/opt/it's here/\"ermite\" $HOME `x`/ermite"368 exe := "/opt/it's here/\"ermite\" $HOME `x`/ermite"
363 require.NoError(t, githttp.WriteHookScripts(dir, exe))369 require.NoError(t, githttp.WriteHookScripts(dir, exe))
@@ -384,6 +390,7 @@ func TestWriteHookScripts(t *testing.T) {
384 390
385 // The shell really passes the awkward path and the arguments through.391 // The shell really passes the awkward path and the arguments through.
386 func TestHookScriptExecsTheBinaryWithQuotedPath(t *testing.T) {392 func TestHookScriptExecsTheBinaryWithQuotedPath(t *testing.T) {
393+ t.Parallel()
387 base := filepath.Join(t.TempDir(), "it's a \"dir\" $X `y` ; z")394 base := filepath.Join(t.TempDir(), "it's a \"dir\" $X `y` ; z")
388 require.NoError(t, os.MkdirAll(base, 0o755))395 require.NoError(t, os.MkdirAll(base, 0o755))
389 exe := filepath.Join(base, "fake ermite")396 exe := filepath.Join(base, "fake ermite")
M internal/adapters/githttp/limits_test.go +6 -0
@@ -114,6 +114,7 @@ func (g *gatedRig) open() {
114114 }
115115
116116 func TestHandlerBackendLimits(t *testing.T) {
117+ t.Parallel()
117118 tests := []struct {
118119 name string
119120 cfg func(*githttp.Config)
@@ -187,6 +188,7 @@ func TestHandlerBackendLimits(t *testing.T) {
187188 }
188189 for _, tt := range tests {
189190 t.Run(tt.name, func(t *testing.T) {
191+ t.Parallel()
190192 g := newGatedRig(t, func(c *githttp.Config) {
191193 c.MaxUploadPacks, c.MaxReceivePacks, c.MaxPerClient = 8, 8, 8
192194 tt.cfg(c)
@@ -210,6 +212,7 @@ func TestHandlerBackendLimits(t *testing.T) {
210212 }
211213
212214 func TestHandlerBackendSlotsAreReleased(t *testing.T) {
215+ t.Parallel()
213216 g := newGatedRig(t, func(c *githttp.Config) {
214217 c.MaxUploadPacks, c.MaxReceivePacks, c.MaxPerClient = 1, 1, 1
215218 })
@@ -230,6 +233,7 @@ func TestHandlerBackendSlotsAreReleased(t *testing.T) {
230233 }
231234
232235 func TestHandlerBackendFailureReleasesSlots(t *testing.T) {
236+ t.Parallel()
233237 r := newRig(t, func(c *githttp.Config) { c.MaxUploadPacks, c.MaxPerClient = 1, 1 })
234238 r.backendErr = context.DeadlineExceeded
235239 for range 3 {
@@ -251,6 +255,7 @@ func (d *deadlineRecorder) SetReadDeadline(t time.Time) error {
251255 }
252256
253257 func TestHandlerBodyReadDeadline(t *testing.T) {
258+ t.Parallel()
254259 tests := []struct {
255260 name string
256261 path string
@@ -262,6 +267,7 @@ func TestHandlerBodyReadDeadline(t *testing.T) {
262267 }
263268 for _, tt := range tests {
264269 t.Run(tt.name, func(t *testing.T) {
270+ t.Parallel()
265271 r := newRig(t, nil)
266272 req := httptest.NewRequest(http.MethodPost, tt.path, stringsReader("payload"))
267273 req.SetBasicAuth("u", "root-secret")
@@ -114,6 +114,7 @@ func (g *gatedRig) open() {
114 }114 }
115 115
116 func TestHandlerBackendLimits(t *testing.T) {116 func TestHandlerBackendLimits(t *testing.T) {
117+ t.Parallel()
117 tests := []struct {118 tests := []struct {
118 name string119 name string
119 cfg func(*githttp.Config)120 cfg func(*githttp.Config)
@@ -187,6 +188,7 @@ func TestHandlerBackendLimits(t *testing.T) {
187 }188 }
188 for _, tt := range tests {189 for _, tt := range tests {
189 t.Run(tt.name, func(t *testing.T) {190 t.Run(tt.name, func(t *testing.T) {
191+ t.Parallel()
190 g := newGatedRig(t, func(c *githttp.Config) {192 g := newGatedRig(t, func(c *githttp.Config) {
191 c.MaxUploadPacks, c.MaxReceivePacks, c.MaxPerClient = 8, 8, 8193 c.MaxUploadPacks, c.MaxReceivePacks, c.MaxPerClient = 8, 8, 8
192 tt.cfg(c)194 tt.cfg(c)
@@ -210,6 +212,7 @@ func TestHandlerBackendLimits(t *testing.T) {
210 }212 }
211 213
212 func TestHandlerBackendSlotsAreReleased(t *testing.T) {214 func TestHandlerBackendSlotsAreReleased(t *testing.T) {
215+ t.Parallel()
213 g := newGatedRig(t, func(c *githttp.Config) {216 g := newGatedRig(t, func(c *githttp.Config) {
214 c.MaxUploadPacks, c.MaxReceivePacks, c.MaxPerClient = 1, 1, 1217 c.MaxUploadPacks, c.MaxReceivePacks, c.MaxPerClient = 1, 1, 1
215 })218 })
@@ -230,6 +233,7 @@ func TestHandlerBackendSlotsAreReleased(t *testing.T) {
230 }233 }
231 234
232 func TestHandlerBackendFailureReleasesSlots(t *testing.T) {235 func TestHandlerBackendFailureReleasesSlots(t *testing.T) {
236+ t.Parallel()
233 r := newRig(t, func(c *githttp.Config) { c.MaxUploadPacks, c.MaxPerClient = 1, 1 })237 r := newRig(t, func(c *githttp.Config) { c.MaxUploadPacks, c.MaxPerClient = 1, 1 })
234 r.backendErr = context.DeadlineExceeded238 r.backendErr = context.DeadlineExceeded
235 for range 3 {239 for range 3 {
@@ -251,6 +255,7 @@ func (d *deadlineRecorder) SetReadDeadline(t time.Time) error {
251 }255 }
252 256
253 func TestHandlerBodyReadDeadline(t *testing.T) {257 func TestHandlerBodyReadDeadline(t *testing.T) {
258+ t.Parallel()
254 tests := []struct {259 tests := []struct {
255 name string260 name string
256 path string261 path string
@@ -262,6 +267,7 @@ func TestHandlerBodyReadDeadline(t *testing.T) {
262 }267 }
263 for _, tt := range tests {268 for _, tt := range tests {
264 t.Run(tt.name, func(t *testing.T) {269 t.Run(tt.name, func(t *testing.T) {
270+ t.Parallel()
265 r := newRig(t, nil)271 r := newRig(t, nil)
266 req := httptest.NewRequest(http.MethodPost, tt.path, stringsReader("payload"))272 req := httptest.NewRequest(http.MethodPost, tt.path, stringsReader("payload"))
267 req.SetBasicAuth("u", "root-secret")273 req.SetBasicAuth("u", "root-secret")
M internal/adapters/rpc/convert_test.go +16 -0
@@ -23,7 +23,9 @@ import (
2323 // UNSPECIFIED, and the mappers that have an inverse round-trip. A value added
2424 // to the domain and forgotten here would reach clients as "not set".
2525 func TestEnumMappersCoverEveryDomainValue(t *testing.T) {
26+ t.Parallel()
2627 t.Run("visibility", func(t *testing.T) {
28+ t.Parallel()
2729 for _, v := range []domain.Visibility{domain.VisibilityPublic, domain.VisibilityPrivate} {
2830 got := visibilityToPB(v)
2931 assert.NotEqual(t, pb.Visibility_VISIBILITY_UNSPECIFIED, got, v)
@@ -37,6 +39,7 @@ func TestEnumMappersCoverEveryDomainValue(t *testing.T) {
3739 })
3840
3941 t.Run("scope", func(t *testing.T) {
42+ t.Parallel()
4043 for _, s := range domain.AllScopes() {
4144 got, ok := scopeToPB[s]
4245 require.True(t, ok, "scope %s has no counterpart in the contract", s)
@@ -58,6 +61,7 @@ func TestEnumMappersCoverEveryDomainValue(t *testing.T) {
5861 })
5962
6063 t.Run("ref kind", func(t *testing.T) {
64+ t.Parallel()
6165 for _, k := range []domain.RefKind{domain.RefKindHead, domain.RefKindBranch, domain.RefKindTag} {
6266 got := refKindToPB(k)
6367 assert.NotEqual(t, pb.RefKind_REF_KIND_UNSPECIFIED, got, k)
@@ -70,6 +74,7 @@ func TestEnumMappersCoverEveryDomainValue(t *testing.T) {
7074 })
7175
7276 t.Run("entry kind", func(t *testing.T) {
77+ t.Parallel()
7378 seen := map[pb.EntryKind]domain.EntryKind{}
7479 for _, k := range []domain.EntryKind{
7580 domain.EntryFile, domain.EntryExecutable, domain.EntryDirectory, domain.EntrySymlink, domain.EntrySubmodule,
@@ -82,6 +87,7 @@ func TestEnumMappersCoverEveryDomainValue(t *testing.T) {
8287 })
8388
8489 t.Run("file status", func(t *testing.T) {
90+ t.Parallel()
8591 seen := map[pb.FileStatus]domain.FileStatus{}
8692 for _, s := range []domain.FileStatus{
8793 domain.FileAdded, domain.FileDeleted, domain.FileModified, domain.FileRenamed, domain.FileCopied, domain.FileTypeChanged,
@@ -94,6 +100,7 @@ func TestEnumMappersCoverEveryDomainValue(t *testing.T) {
94100 })
95101
96102 t.Run("line kind", func(t *testing.T) {
103+ t.Parallel()
97104 seen := map[pb.LineKind]domain.LineKind{}
98105 for _, k := range []domain.LineKind{domain.LineContext, domain.LineAddition, domain.LineDeletion} {
99106 got := lineKindToPB(k)
@@ -106,6 +113,7 @@ func TestEnumMappersCoverEveryDomainValue(t *testing.T) {
106113 // Every value of the contract except UNSPECIFIED is produced by some value
107114 // of the domain: nothing in the contract is unreachable.
108115 t.Run("the contract has no value the domain cannot produce", func(t *testing.T) {
116+ t.Parallel()
109117 produced := func(names map[int32]string, from func(v int32) bool) {
110118 for v, n := range names {
111119 if v == 0 {
@@ -142,6 +150,7 @@ func TestEnumMappersCoverEveryDomainValue(t *testing.T) {
142150 }
143151
144152 func TestMergeModeMappers(t *testing.T) {
153+ t.Parallel()
145154 for _, m := range domain.MergeModes() {
146155 got := mergeModeToPB(m)
147156 assert.NotEqual(t, pb.MergeMode_MERGE_MODE_UNSPECIFIED, got, m)
@@ -168,6 +177,7 @@ func TestMergeModeMappers(t *testing.T) {
168177 }
169178
170179 func TestMergeRequestEnumMappers(t *testing.T) {
180+ t.Parallel()
171181 for _, s := range []domain.MRState{domain.MRStateOpen, domain.MRStateMerged, domain.MRStateClosed} {
172182 got := mergeRequestStateToPB(s)
173183 assert.NotEqual(t, pb.MergeRequestState_MERGE_REQUEST_STATE_UNSPECIFIED, got, s)
@@ -192,6 +202,7 @@ func TestMergeRequestEnumMappers(t *testing.T) {
192202 }
193203
194204 func TestCommentID(t *testing.T) {
205+ t.Parallel()
195206 assert.EqualValues(t, 7, commentID("7"))
196207 for _, bad := range []string{"", "0", "-1", "07", "+7", "x", "7 ", "9223372036854775808"} {
197208 assert.Zero(t, commentID(bad), "%q names no comment", bad)
@@ -199,6 +210,7 @@ func TestCommentID(t *testing.T) {
199210 }
200211
201212 func TestRepoToPBAlwaysHasSettings(t *testing.T) {
213+ t.Parallel()
202214 // A repository value without settings (never stored with them) reads as the defaults.
203215 r := repoToPB(app.RepoView{Repo: domain.Repo{Name: mustName(t, "x")}})
204216 assert.Equal(t, []pb.MergeMode{pb.MergeMode_MERGE_MODE_FAST_FORWARD, pb.MergeMode_MERGE_MODE_SQUASH}, r.GetSettings().GetAllowedMergeModes())
@@ -218,6 +230,7 @@ func mustName(t *testing.T, s string) domain.RepoName {
218230 }
219231
220232 func TestUpdateParamsSettingsPaths(t *testing.T) {
233+ t.Parallel()
221234 msg := func(paths ...string) *pb.UpdateRepoRequest {
222235 return &pb.UpdateRepoRequest{
223236 UpdateMask: &fieldmaskpb.FieldMask{Paths: paths},
@@ -255,6 +268,7 @@ func TestUpdateParamsSettingsPaths(t *testing.T) {
255268 }
256269
257270 func TestConnectErrorCarriesTheFieldsOfARejection(t *testing.T) {
271+ t.Parallel()
258272 var v domain.Violations
259273 v.Add("title", "must be one line")
260274 v.Add("source_branch", "does not exist")
@@ -282,6 +296,7 @@ func TestConnectErrorCarriesTheFieldsOfARejection(t *testing.T) {
282296 }
283297
284298 func TestWatchEventToPBMergeRequestEvents(t *testing.T) {
299+ t.Parallel()
285300 repo := mustName(t, "proj")
286301 title, err := domain.ParseMRTitle("Add x")
287302 require.NoError(t, err)
@@ -297,6 +312,7 @@ func TestWatchEventToPBMergeRequestEvents(t *testing.T) {
297312 "closed": {domain.MergeRequestClosed{MergeRequest: mr, At: at}, func(e *pb.Event) *pb.MergeRequest { return e.GetMergeRequestClosed().GetMergeRequest() }},
298313 } {
299314 t.Run(name, func(t *testing.T) {
315+ t.Parallel()
300316 got := watchEventToPB(app.WatchEvent{ID: "e1", Event: tc.ev})
301317 require.NotNil(t, got)
302318 assert.Equal(t, "e1", got.GetId())
@@ -23,7 +23,9 @@ import (
23 // UNSPECIFIED, and the mappers that have an inverse round-trip. A value added23 // UNSPECIFIED, and the mappers that have an inverse round-trip. A value added
24 // to the domain and forgotten here would reach clients as "not set".24 // to the domain and forgotten here would reach clients as "not set".
25 func TestEnumMappersCoverEveryDomainValue(t *testing.T) {25 func TestEnumMappersCoverEveryDomainValue(t *testing.T) {
26+ t.Parallel()
26 t.Run("visibility", func(t *testing.T) {27 t.Run("visibility", func(t *testing.T) {
28+ t.Parallel()
27 for _, v := range []domain.Visibility{domain.VisibilityPublic, domain.VisibilityPrivate} {29 for _, v := range []domain.Visibility{domain.VisibilityPublic, domain.VisibilityPrivate} {
28 got := visibilityToPB(v)30 got := visibilityToPB(v)
29 assert.NotEqual(t, pb.Visibility_VISIBILITY_UNSPECIFIED, got, v)31 assert.NotEqual(t, pb.Visibility_VISIBILITY_UNSPECIFIED, got, v)
@@ -37,6 +39,7 @@ func TestEnumMappersCoverEveryDomainValue(t *testing.T) {
37 })39 })
38 40
39 t.Run("scope", func(t *testing.T) {41 t.Run("scope", func(t *testing.T) {
42+ t.Parallel()
40 for _, s := range domain.AllScopes() {43 for _, s := range domain.AllScopes() {
41 got, ok := scopeToPB[s]44 got, ok := scopeToPB[s]
42 require.True(t, ok, "scope %s has no counterpart in the contract", s)45 require.True(t, ok, "scope %s has no counterpart in the contract", s)
@@ -58,6 +61,7 @@ func TestEnumMappersCoverEveryDomainValue(t *testing.T) {
58 })61 })
59 62
60 t.Run("ref kind", func(t *testing.T) {63 t.Run("ref kind", func(t *testing.T) {
64+ t.Parallel()
61 for _, k := range []domain.RefKind{domain.RefKindHead, domain.RefKindBranch, domain.RefKindTag} {65 for _, k := range []domain.RefKind{domain.RefKindHead, domain.RefKindBranch, domain.RefKindTag} {
62 got := refKindToPB(k)66 got := refKindToPB(k)
63 assert.NotEqual(t, pb.RefKind_REF_KIND_UNSPECIFIED, got, k)67 assert.NotEqual(t, pb.RefKind_REF_KIND_UNSPECIFIED, got, k)
@@ -70,6 +74,7 @@ func TestEnumMappersCoverEveryDomainValue(t *testing.T) {
70 })74 })
71 75
72 t.Run("entry kind", func(t *testing.T) {76 t.Run("entry kind", func(t *testing.T) {
77+ t.Parallel()
73 seen := map[pb.EntryKind]domain.EntryKind{}78 seen := map[pb.EntryKind]domain.EntryKind{}
74 for _, k := range []domain.EntryKind{79 for _, k := range []domain.EntryKind{
75 domain.EntryFile, domain.EntryExecutable, domain.EntryDirectory, domain.EntrySymlink, domain.EntrySubmodule,80 domain.EntryFile, domain.EntryExecutable, domain.EntryDirectory, domain.EntrySymlink, domain.EntrySubmodule,
@@ -82,6 +87,7 @@ func TestEnumMappersCoverEveryDomainValue(t *testing.T) {
82 })87 })
83 88
84 t.Run("file status", func(t *testing.T) {89 t.Run("file status", func(t *testing.T) {
90+ t.Parallel()
85 seen := map[pb.FileStatus]domain.FileStatus{}91 seen := map[pb.FileStatus]domain.FileStatus{}
86 for _, s := range []domain.FileStatus{92 for _, s := range []domain.FileStatus{
87 domain.FileAdded, domain.FileDeleted, domain.FileModified, domain.FileRenamed, domain.FileCopied, domain.FileTypeChanged,93 domain.FileAdded, domain.FileDeleted, domain.FileModified, domain.FileRenamed, domain.FileCopied, domain.FileTypeChanged,
@@ -94,6 +100,7 @@ func TestEnumMappersCoverEveryDomainValue(t *testing.T) {
94 })100 })
95 101
96 t.Run("line kind", func(t *testing.T) {102 t.Run("line kind", func(t *testing.T) {
103+ t.Parallel()
97 seen := map[pb.LineKind]domain.LineKind{}104 seen := map[pb.LineKind]domain.LineKind{}
98 for _, k := range []domain.LineKind{domain.LineContext, domain.LineAddition, domain.LineDeletion} {105 for _, k := range []domain.LineKind{domain.LineContext, domain.LineAddition, domain.LineDeletion} {
99 got := lineKindToPB(k)106 got := lineKindToPB(k)
@@ -106,6 +113,7 @@ func TestEnumMappersCoverEveryDomainValue(t *testing.T) {
106 // Every value of the contract except UNSPECIFIED is produced by some value113 // Every value of the contract except UNSPECIFIED is produced by some value
107 // of the domain: nothing in the contract is unreachable.114 // of the domain: nothing in the contract is unreachable.
108 t.Run("the contract has no value the domain cannot produce", func(t *testing.T) {115 t.Run("the contract has no value the domain cannot produce", func(t *testing.T) {
116+ t.Parallel()
109 produced := func(names map[int32]string, from func(v int32) bool) {117 produced := func(names map[int32]string, from func(v int32) bool) {
110 for v, n := range names {118 for v, n := range names {
111 if v == 0 {119 if v == 0 {
@@ -142,6 +150,7 @@ func TestEnumMappersCoverEveryDomainValue(t *testing.T) {
142 }150 }
143 151
144 func TestMergeModeMappers(t *testing.T) {152 func TestMergeModeMappers(t *testing.T) {
153+ t.Parallel()
145 for _, m := range domain.MergeModes() {154 for _, m := range domain.MergeModes() {
146 got := mergeModeToPB(m)155 got := mergeModeToPB(m)
147 assert.NotEqual(t, pb.MergeMode_MERGE_MODE_UNSPECIFIED, got, m)156 assert.NotEqual(t, pb.MergeMode_MERGE_MODE_UNSPECIFIED, got, m)
@@ -168,6 +177,7 @@ func TestMergeModeMappers(t *testing.T) {
168 }177 }
169 178
170 func TestMergeRequestEnumMappers(t *testing.T) {179 func TestMergeRequestEnumMappers(t *testing.T) {
180+ t.Parallel()
171 for _, s := range []domain.MRState{domain.MRStateOpen, domain.MRStateMerged, domain.MRStateClosed} {181 for _, s := range []domain.MRState{domain.MRStateOpen, domain.MRStateMerged, domain.MRStateClosed} {
172 got := mergeRequestStateToPB(s)182 got := mergeRequestStateToPB(s)
173 assert.NotEqual(t, pb.MergeRequestState_MERGE_REQUEST_STATE_UNSPECIFIED, got, s)183 assert.NotEqual(t, pb.MergeRequestState_MERGE_REQUEST_STATE_UNSPECIFIED, got, s)
@@ -192,6 +202,7 @@ func TestMergeRequestEnumMappers(t *testing.T) {
192 }202 }
193 203
194 func TestCommentID(t *testing.T) {204 func TestCommentID(t *testing.T) {
205+ t.Parallel()
195 assert.EqualValues(t, 7, commentID("7"))206 assert.EqualValues(t, 7, commentID("7"))
196 for _, bad := range []string{"", "0", "-1", "07", "+7", "x", "7 ", "9223372036854775808"} {207 for _, bad := range []string{"", "0", "-1", "07", "+7", "x", "7 ", "9223372036854775808"} {
197 assert.Zero(t, commentID(bad), "%q names no comment", bad)208 assert.Zero(t, commentID(bad), "%q names no comment", bad)
@@ -199,6 +210,7 @@ func TestCommentID(t *testing.T) {
199 }210 }
200 211
201 func TestRepoToPBAlwaysHasSettings(t *testing.T) {212 func TestRepoToPBAlwaysHasSettings(t *testing.T) {
213+ t.Parallel()
202 // A repository value without settings (never stored with them) reads as the defaults.214 // A repository value without settings (never stored with them) reads as the defaults.
203 r := repoToPB(app.RepoView{Repo: domain.Repo{Name: mustName(t, "x")}})215 r := repoToPB(app.RepoView{Repo: domain.Repo{Name: mustName(t, "x")}})
204 assert.Equal(t, []pb.MergeMode{pb.MergeMode_MERGE_MODE_FAST_FORWARD, pb.MergeMode_MERGE_MODE_SQUASH}, r.GetSettings().GetAllowedMergeModes())216 assert.Equal(t, []pb.MergeMode{pb.MergeMode_MERGE_MODE_FAST_FORWARD, pb.MergeMode_MERGE_MODE_SQUASH}, r.GetSettings().GetAllowedMergeModes())
@@ -218,6 +230,7 @@ func mustName(t *testing.T, s string) domain.RepoName {
218 }230 }
219 231
220 func TestUpdateParamsSettingsPaths(t *testing.T) {232 func TestUpdateParamsSettingsPaths(t *testing.T) {
233+ t.Parallel()
221 msg := func(paths ...string) *pb.UpdateRepoRequest {234 msg := func(paths ...string) *pb.UpdateRepoRequest {
222 return &pb.UpdateRepoRequest{235 return &pb.UpdateRepoRequest{
223 UpdateMask: &fieldmaskpb.FieldMask{Paths: paths},236 UpdateMask: &fieldmaskpb.FieldMask{Paths: paths},
@@ -255,6 +268,7 @@ func TestUpdateParamsSettingsPaths(t *testing.T) {
255 }268 }
256 269
257 func TestConnectErrorCarriesTheFieldsOfARejection(t *testing.T) {270 func TestConnectErrorCarriesTheFieldsOfARejection(t *testing.T) {
271+ t.Parallel()
258 var v domain.Violations272 var v domain.Violations
259 v.Add("title", "must be one line")273 v.Add("title", "must be one line")
260 v.Add("source_branch", "does not exist")274 v.Add("source_branch", "does not exist")
@@ -282,6 +296,7 @@ func TestConnectErrorCarriesTheFieldsOfARejection(t *testing.T) {
282 }296 }
283 297
284 func TestWatchEventToPBMergeRequestEvents(t *testing.T) {298 func TestWatchEventToPBMergeRequestEvents(t *testing.T) {
299+ t.Parallel()
285 repo := mustName(t, "proj")300 repo := mustName(t, "proj")
286 title, err := domain.ParseMRTitle("Add x")301 title, err := domain.ParseMRTitle("Add x")
287 require.NoError(t, err)302 require.NoError(t, err)
@@ -297,6 +312,7 @@ func TestWatchEventToPBMergeRequestEvents(t *testing.T) {
297 "closed": {domain.MergeRequestClosed{MergeRequest: mr, At: at}, func(e *pb.Event) *pb.MergeRequest { return e.GetMergeRequestClosed().GetMergeRequest() }},312 "closed": {domain.MergeRequestClosed{MergeRequest: mr, At: at}, func(e *pb.Event) *pb.MergeRequest { return e.GetMergeRequestClosed().GetMergeRequest() }},
298 } {313 } {
299 t.Run(name, func(t *testing.T) {314 t.Run(name, func(t *testing.T) {
315+ t.Parallel()
300 got := watchEventToPB(app.WatchEvent{ID: "e1", Event: tc.ev})316 got := watchEventToPB(app.WatchEvent{ID: "e1", Event: tc.ev})
301 require.NotNil(t, got)317 require.NotNil(t, got)
302 assert.Equal(t, "e1", got.GetId())318 assert.Equal(t, "e1", got.GetId())
M internal/adapters/rpc/git_test.go +13 -0
@@ -17,6 +17,7 @@ import (
1717 )
1818
1919 func TestGitServiceGetTree(t *testing.T) {
20+ t.Parallel()
2021 tests := []struct {
2122 name string
2223 req *pb.GetTreeRequest
@@ -32,6 +33,7 @@ func TestGitServiceGetTree(t *testing.T) {
3233 }
3334 for _, tt := range tests {
3435 t.Run(tt.name, func(t *testing.T) {
36+ t.Parallel()
3537 browse := mocks.NewBrowse(t)
3638 if tt.wantCall {
3739 browse.EXPECT().GetTree(mock.Anything, domain.Owner(), mustRepoName(t, "demo"), mock.Anything, mock.Anything, false).
@@ -49,6 +51,7 @@ func TestGitServiceGetTree(t *testing.T) {
4951 }
5052
5153 func TestGitServiceListRefs(t *testing.T) {
54+ t.Parallel()
5255 tests := []struct {
5356 name string
5457 req *pb.ListRefsRequest
@@ -64,6 +67,7 @@ func TestGitServiceListRefs(t *testing.T) {
6467 }
6568 for _, tt := range tests {
6669 t.Run(tt.name, func(t *testing.T) {
70+ t.Parallel()
6771 browse := mocks.NewBrowse(t)
6872 if tt.wantCall {
6973 browse.EXPECT().ListRefs(mock.Anything, domain.Owner(), mustRepoName(t, "demo"), mock.Anything).
@@ -81,28 +85,33 @@ func TestGitServiceListRefs(t *testing.T) {
8185 }
8286
8387 func TestGitServiceGetCommitAndResolveRevision(t *testing.T) {
88+ t.Parallel()
8489 invalid := []struct{ name, repo, rev string }{
8590 {"invalid repo", "../x", "main"},
8691 {"invalid revision", "demo", "a b"},
8792 }
8893 for _, tt := range invalid {
8994 t.Run("GetCommit "+tt.name, func(t *testing.T) {
95+ t.Parallel()
9096 _, err := (&gitService{mocks.NewBrowse(t)}).GetCommit(ownerCtx(), connect.NewRequest(&pb.GetCommitRequest{Repo: tt.repo, Revision: tt.rev}))
9197 requireCode(t, connect.CodeInvalidArgument, err)
9298 })
9399 t.Run("ResolveRevision "+tt.name, func(t *testing.T) {
100+ t.Parallel()
94101 _, err := (&gitService{mocks.NewBrowse(t)}).ResolveRevision(ownerCtx(), connect.NewRequest(&pb.ResolveRevisionRequest{Repo: tt.repo, Revision: tt.rev}))
95102 requireCode(t, connect.CodeInvalidArgument, err)
96103 })
97104 }
98105
99106 t.Run("GetCommit not found", func(t *testing.T) {
107+ t.Parallel()
100108 browse := mocks.NewBrowse(t)
101109 browse.EXPECT().GetCommit(mock.Anything, domain.Owner(), mustRepoName(t, "demo"), mock.Anything).Return(domain.CommitDetail{}, domain.ErrNotFound).Once()
102110 _, err := (&gitService{browse}).GetCommit(ownerCtx(), connect.NewRequest(&pb.GetCommitRequest{Repo: "demo", Revision: "main"}))
103111 requireCode(t, connect.CodeNotFound, err)
104112 })
105113 t.Run("ResolveRevision ok", func(t *testing.T) {
114+ t.Parallel()
106115 browse := mocks.NewBrowse(t)
107116 browse.EXPECT().ResolveRevision(mock.Anything, domain.Owner(), mustRepoName(t, "demo"), mock.Anything).
108117 Return(domain.ResolvedRevision{Ref: "refs/heads/main"}, nil).Once()
@@ -113,6 +122,7 @@ func TestGitServiceGetCommitAndResolveRevision(t *testing.T) {
113122 }
114123
115124 func TestGitServiceListCommits(t *testing.T) {
125+ t.Parallel()
116126 tests := []struct {
117127 name string
118128 req *pb.ListCommitsRequest
@@ -127,6 +137,7 @@ func TestGitServiceListCommits(t *testing.T) {
127137 }
128138 for _, tt := range tests {
129139 t.Run(tt.name, func(t *testing.T) {
140+ t.Parallel()
130141 browse := mocks.NewBrowse(t)
131142 if tt.wantCall {
132143 browse.EXPECT().ListCommits(mock.Anything, domain.Owner(), mustRepoName(t, "demo"), mock.Anything).
@@ -144,6 +155,7 @@ func TestGitServiceListCommits(t *testing.T) {
144155
145156 // GetBlob validates before it opens anything or touches the stream.
146157 func TestGitServiceGetBlobRejectsBadInput(t *testing.T) {
158+ t.Parallel()
147159 tests := []struct {
148160 name string
149161 req *pb.GetBlobRequest
@@ -154,6 +166,7 @@ func TestGitServiceGetBlobRejectsBadInput(t *testing.T) {
154166 }
155167 for _, tt := range tests {
156168 t.Run(tt.name, func(t *testing.T) {
169+ t.Parallel()
157170 err := (&gitService{mocks.NewBrowse(t)}).GetBlob(ownerCtx(), connect.NewRequest(tt.req), nil)
158171 requireCode(t, connect.CodeInvalidArgument, err)
159172 })
@@ -17,6 +17,7 @@ import (
17 )17 )
18 18
19 func TestGitServiceGetTree(t *testing.T) {19 func TestGitServiceGetTree(t *testing.T) {
20+ t.Parallel()
20 tests := []struct {21 tests := []struct {
21 name string22 name string
22 req *pb.GetTreeRequest23 req *pb.GetTreeRequest
@@ -32,6 +33,7 @@ func TestGitServiceGetTree(t *testing.T) {
32 }33 }
33 for _, tt := range tests {34 for _, tt := range tests {
34 t.Run(tt.name, func(t *testing.T) {35 t.Run(tt.name, func(t *testing.T) {
36+ t.Parallel()
35 browse := mocks.NewBrowse(t)37 browse := mocks.NewBrowse(t)
36 if tt.wantCall {38 if tt.wantCall {
37 browse.EXPECT().GetTree(mock.Anything, domain.Owner(), mustRepoName(t, "demo"), mock.Anything, mock.Anything, false).39 browse.EXPECT().GetTree(mock.Anything, domain.Owner(), mustRepoName(t, "demo"), mock.Anything, mock.Anything, false).
@@ -49,6 +51,7 @@ func TestGitServiceGetTree(t *testing.T) {
49 }51 }
50 52
51 func TestGitServiceListRefs(t *testing.T) {53 func TestGitServiceListRefs(t *testing.T) {
54+ t.Parallel()
52 tests := []struct {55 tests := []struct {
53 name string56 name string
54 req *pb.ListRefsRequest57 req *pb.ListRefsRequest
@@ -64,6 +67,7 @@ func TestGitServiceListRefs(t *testing.T) {
64 }67 }
65 for _, tt := range tests {68 for _, tt := range tests {
66 t.Run(tt.name, func(t *testing.T) {69 t.Run(tt.name, func(t *testing.T) {
70+ t.Parallel()
67 browse := mocks.NewBrowse(t)71 browse := mocks.NewBrowse(t)
68 if tt.wantCall {72 if tt.wantCall {
69 browse.EXPECT().ListRefs(mock.Anything, domain.Owner(), mustRepoName(t, "demo"), mock.Anything).73 browse.EXPECT().ListRefs(mock.Anything, domain.Owner(), mustRepoName(t, "demo"), mock.Anything).
@@ -81,28 +85,33 @@ func TestGitServiceListRefs(t *testing.T) {
81 }85 }
82 86
83 func TestGitServiceGetCommitAndResolveRevision(t *testing.T) {87 func TestGitServiceGetCommitAndResolveRevision(t *testing.T) {
88+ t.Parallel()
84 invalid := []struct{ name, repo, rev string }{89 invalid := []struct{ name, repo, rev string }{
85 {"invalid repo", "../x", "main"},90 {"invalid repo", "../x", "main"},
86 {"invalid revision", "demo", "a b"},91 {"invalid revision", "demo", "a b"},
87 }92 }
88 for _, tt := range invalid {93 for _, tt := range invalid {
89 t.Run("GetCommit "+tt.name, func(t *testing.T) {94 t.Run("GetCommit "+tt.name, func(t *testing.T) {
95+ t.Parallel()
90 _, err := (&gitService{mocks.NewBrowse(t)}).GetCommit(ownerCtx(), connect.NewRequest(&pb.GetCommitRequest{Repo: tt.repo, Revision: tt.rev}))96 _, err := (&gitService{mocks.NewBrowse(t)}).GetCommit(ownerCtx(), connect.NewRequest(&pb.GetCommitRequest{Repo: tt.repo, Revision: tt.rev}))
91 requireCode(t, connect.CodeInvalidArgument, err)97 requireCode(t, connect.CodeInvalidArgument, err)
92 })98 })
93 t.Run("ResolveRevision "+tt.name, func(t *testing.T) {99 t.Run("ResolveRevision "+tt.name, func(t *testing.T) {
100+ t.Parallel()
94 _, err := (&gitService{mocks.NewBrowse(t)}).ResolveRevision(ownerCtx(), connect.NewRequest(&pb.ResolveRevisionRequest{Repo: tt.repo, Revision: tt.rev}))101 _, err := (&gitService{mocks.NewBrowse(t)}).ResolveRevision(ownerCtx(), connect.NewRequest(&pb.ResolveRevisionRequest{Repo: tt.repo, Revision: tt.rev}))
95 requireCode(t, connect.CodeInvalidArgument, err)102 requireCode(t, connect.CodeInvalidArgument, err)
96 })103 })
97 }104 }
98 105
99 t.Run("GetCommit not found", func(t *testing.T) {106 t.Run("GetCommit not found", func(t *testing.T) {
107+ t.Parallel()
100 browse := mocks.NewBrowse(t)108 browse := mocks.NewBrowse(t)
101 browse.EXPECT().GetCommit(mock.Anything, domain.Owner(), mustRepoName(t, "demo"), mock.Anything).Return(domain.CommitDetail{}, domain.ErrNotFound).Once()109 browse.EXPECT().GetCommit(mock.Anything, domain.Owner(), mustRepoName(t, "demo"), mock.Anything).Return(domain.CommitDetail{}, domain.ErrNotFound).Once()
102 _, err := (&gitService{browse}).GetCommit(ownerCtx(), connect.NewRequest(&pb.GetCommitRequest{Repo: "demo", Revision: "main"}))110 _, err := (&gitService{browse}).GetCommit(ownerCtx(), connect.NewRequest(&pb.GetCommitRequest{Repo: "demo", Revision: "main"}))
103 requireCode(t, connect.CodeNotFound, err)111 requireCode(t, connect.CodeNotFound, err)
104 })112 })
105 t.Run("ResolveRevision ok", func(t *testing.T) {113 t.Run("ResolveRevision ok", func(t *testing.T) {
114+ t.Parallel()
106 browse := mocks.NewBrowse(t)115 browse := mocks.NewBrowse(t)
107 browse.EXPECT().ResolveRevision(mock.Anything, domain.Owner(), mustRepoName(t, "demo"), mock.Anything).116 browse.EXPECT().ResolveRevision(mock.Anything, domain.Owner(), mustRepoName(t, "demo"), mock.Anything).
108 Return(domain.ResolvedRevision{Ref: "refs/heads/main"}, nil).Once()117 Return(domain.ResolvedRevision{Ref: "refs/heads/main"}, nil).Once()
@@ -113,6 +122,7 @@ func TestGitServiceGetCommitAndResolveRevision(t *testing.T) {
113 }122 }
114 123
115 func TestGitServiceListCommits(t *testing.T) {124 func TestGitServiceListCommits(t *testing.T) {
125+ t.Parallel()
116 tests := []struct {126 tests := []struct {
117 name string127 name string
118 req *pb.ListCommitsRequest128 req *pb.ListCommitsRequest
@@ -127,6 +137,7 @@ func TestGitServiceListCommits(t *testing.T) {
127 }137 }
128 for _, tt := range tests {138 for _, tt := range tests {
129 t.Run(tt.name, func(t *testing.T) {139 t.Run(tt.name, func(t *testing.T) {
140+ t.Parallel()
130 browse := mocks.NewBrowse(t)141 browse := mocks.NewBrowse(t)
131 if tt.wantCall {142 if tt.wantCall {
132 browse.EXPECT().ListCommits(mock.Anything, domain.Owner(), mustRepoName(t, "demo"), mock.Anything).143 browse.EXPECT().ListCommits(mock.Anything, domain.Owner(), mustRepoName(t, "demo"), mock.Anything).
@@ -144,6 +155,7 @@ func TestGitServiceListCommits(t *testing.T) {
144 155
145 // GetBlob validates before it opens anything or touches the stream.156 // GetBlob validates before it opens anything or touches the stream.
146 func TestGitServiceGetBlobRejectsBadInput(t *testing.T) {157 func TestGitServiceGetBlobRejectsBadInput(t *testing.T) {
158+ t.Parallel()
147 tests := []struct {159 tests := []struct {
148 name string160 name string
149 req *pb.GetBlobRequest161 req *pb.GetBlobRequest
@@ -154,6 +166,7 @@ func TestGitServiceGetBlobRejectsBadInput(t *testing.T) {
154 }166 }
155 for _, tt := range tests {167 for _, tt := range tests {
156 t.Run(tt.name, func(t *testing.T) {168 t.Run(tt.name, func(t *testing.T) {
169+ t.Parallel()
157 err := (&gitService{mocks.NewBrowse(t)}).GetBlob(ownerCtx(), connect.NewRequest(tt.req), nil)170 err := (&gitService{mocks.NewBrowse(t)}).GetBlob(ownerCtx(), connect.NewRequest(tt.req), nil)
158 requireCode(t, connect.CodeInvalidArgument, err)171 requireCode(t, connect.CodeInvalidArgument, err)
159 })172 })
M internal/adapters/rpc/interceptor_test.go +3 -0
@@ -30,6 +30,7 @@ func bearerHeader(v string) http.Header {
3030 }
3131
3232 func TestInterceptorAuthenticate(t *testing.T) {
33+ t.Parallel()
3334 const procedure = ermiteconnect.RepoServiceListReposProcedure
3435 tests := []struct {
3536 name string
@@ -67,6 +68,7 @@ func TestInterceptorAuthenticate(t *testing.T) {
6768 }
6869 for _, tt := range tests {
6970 t.Run(tt.name, func(t *testing.T) {
71+ t.Parallel()
7072 auth := mocks.NewAuthenticator(t)
7173 limiter := mocks.NewFailureLimiter(t)
7274 if tt.wantLimit {
@@ -110,6 +112,7 @@ func TestInterceptorAuthenticate(t *testing.T) {
110112 }
111113
112114 func TestInterceptorIgnoresForwardedForFromUntrustedPeer(t *testing.T) {
115+ t.Parallel()
113116 limiter := mocks.NewFailureLimiter(t)
114117 limiter.EXPECT().Blocked(mock.Anything, "203.0.113.7").Return(false, time.Duration(0)).Once()
115118 h := bearerHeader("")
@@ -30,6 +30,7 @@ func bearerHeader(v string) http.Header {
30 }30 }
31 31
32 func TestInterceptorAuthenticate(t *testing.T) {32 func TestInterceptorAuthenticate(t *testing.T) {
33+ t.Parallel()
33 const procedure = ermiteconnect.RepoServiceListReposProcedure34 const procedure = ermiteconnect.RepoServiceListReposProcedure
34 tests := []struct {35 tests := []struct {
35 name string36 name string
@@ -67,6 +68,7 @@ func TestInterceptorAuthenticate(t *testing.T) {
67 }68 }
68 for _, tt := range tests {69 for _, tt := range tests {
69 t.Run(tt.name, func(t *testing.T) {70 t.Run(tt.name, func(t *testing.T) {
71+ t.Parallel()
70 auth := mocks.NewAuthenticator(t)72 auth := mocks.NewAuthenticator(t)
71 limiter := mocks.NewFailureLimiter(t)73 limiter := mocks.NewFailureLimiter(t)
72 if tt.wantLimit {74 if tt.wantLimit {
@@ -110,6 +112,7 @@ func TestInterceptorAuthenticate(t *testing.T) {
110 }112 }
111 113
112 func TestInterceptorIgnoresForwardedForFromUntrustedPeer(t *testing.T) {114 func TestInterceptorIgnoresForwardedForFromUntrustedPeer(t *testing.T) {
115+ t.Parallel()
113 limiter := mocks.NewFailureLimiter(t)116 limiter := mocks.NewFailureLimiter(t)
114 limiter.EXPECT().Blocked(mock.Anything, "203.0.113.7").Return(false, time.Duration(0)).Once()117 limiter.EXPECT().Blocked(mock.Anything, "203.0.113.7").Return(false, time.Duration(0)).Once()
115 h := bearerHeader("")118 h := bearerHeader("")
M internal/adapters/rpc/mergerequest_test.go +17 -0
@@ -17,6 +17,7 @@ import (
1717 )
1818
1919 func TestMergeRequestServiceCreate(t *testing.T) {
20+ t.Parallel()
2021 tests := []struct {
2122 name string
2223 repo string
@@ -33,6 +34,7 @@ func TestMergeRequestServiceCreate(t *testing.T) {
3334 }
3435 for _, tt := range tests {
3536 t.Run(tt.name, func(t *testing.T) {
37+ t.Parallel()
3638 mrs := mocks.NewMergeRequests(t)
3739 if tt.wantCall {
3840 mrs.EXPECT().Create(mock.Anything, domain.Owner(), mustRepoName(t, "demo"), app.CreateMergeRequestParams{
@@ -54,12 +56,14 @@ func TestMergeRequestServiceCreate(t *testing.T) {
5456 }
5557
5658 func TestMergeRequestServiceListRejectsUnknownState(t *testing.T) {
59+ t.Parallel()
5760 _, err := (&mergeRequestService{mocks.NewMergeRequests(t)}).ListMergeRequests(ownerCtx(),
5861 connect.NewRequest(&pb.ListMergeRequestsRequest{Repo: "demo", State: pb.MergeRequestState(99)}))
5962 requireCode(t, connect.CodeInvalidArgument, err)
6063 }
6164
6265 func TestMergeRequestServiceListFiltersByState(t *testing.T) {
66+ t.Parallel()
6367 mrs := mocks.NewMergeRequests(t)
6468 mrs.EXPECT().List(mock.Anything, domain.Owner(), mustRepoName(t, "demo"), app.ListMergeRequestsParams{
6569 State: domain.MRStateMerged, PageSize: 3, PageToken: "tok",
@@ -73,6 +77,7 @@ func TestMergeRequestServiceListFiltersByState(t *testing.T) {
7377 }
7478
7579 func TestMergeRequestServiceAddComment(t *testing.T) {
80+ t.Parallel()
7681 tests := []struct {
7782 name string
7883 side pb.DiffSide
@@ -88,6 +93,7 @@ func TestMergeRequestServiceAddComment(t *testing.T) {
8893 }
8994 for _, tt := range tests {
9095 t.Run(tt.name, func(t *testing.T) {
96+ t.Parallel()
9197 mrs := mocks.NewMergeRequests(t)
9298 if tt.wantCall {
9399 mrs.EXPECT().AddComment(mock.Anything, domain.Owner(), mustRepoName(t, "demo"), int64(2), mock.Anything).
@@ -107,8 +113,10 @@ func TestMergeRequestServiceAddComment(t *testing.T) {
107113 }
108114
109115 func TestMergeRequestServiceResolveCommentMapsUnissuedIDsToZero(t *testing.T) {
116+ t.Parallel()
110117 for _, id := range []string{"", "abc", "-1", "0", "007", "99999999999999999999"} {
111118 t.Run(id, func(t *testing.T) {
119+ t.Parallel()
112120 mrs := mocks.NewMergeRequests(t)
113121 mrs.EXPECT().ResolveComment(mock.Anything, domain.Owner(), mustRepoName(t, "demo"), int64(1), int64(0), true).
114122 Return(domain.Comment{}, domain.ErrNotFound).Once()
@@ -121,6 +129,7 @@ func TestMergeRequestServiceResolveCommentMapsUnissuedIDsToZero(t *testing.T) {
121129 }
122130
123131 func TestMergeRequestServiceMerge(t *testing.T) {
132+ t.Parallel()
124133 const head = "0123456789abcdef0123456789abcdef01234567"
125134 tests := []struct {
126135 name string
@@ -138,6 +147,7 @@ func TestMergeRequestServiceMerge(t *testing.T) {
138147 }
139148 for _, tt := range tests {
140149 t.Run(tt.name, func(t *testing.T) {
150+ t.Parallel()
141151 mrs := mocks.NewMergeRequests(t)
142152 if tt.wantCall {
143153 mrs.EXPECT().Merge(mock.Anything, domain.Owner(), mustRepoName(t, "demo"), int64(1), mock.MatchedBy(func(p app.MergeParams) bool {
@@ -156,13 +166,16 @@ func TestMergeRequestServiceMerge(t *testing.T) {
156166 }
157167
158168 func TestMergeRequestServiceCloseAndMarkRead(t *testing.T) {
169+ t.Parallel()
159170 t.Run("close not found", func(t *testing.T) {
171+ t.Parallel()
160172 mrs := mocks.NewMergeRequests(t)
161173 mrs.EXPECT().Close(mock.Anything, domain.Owner(), mustRepoName(t, "demo"), int64(9)).Return(domain.MergeRequest{}, domain.ErrNotFound).Once()
162174 _, err := (&mergeRequestService{mrs}).CloseMergeRequest(ownerCtx(), connect.NewRequest(&pb.CloseMergeRequestRequest{Repo: "demo", Number: 9}))
163175 requireCode(t, connect.CodeNotFound, err)
164176 })
165177 t.Run("close ok", func(t *testing.T) {
178+ t.Parallel()
166179 mrs := mocks.NewMergeRequests(t)
167180 mrs.EXPECT().Close(mock.Anything, domain.Owner(), mustRepoName(t, "demo"), int64(9)).Return(domain.MergeRequest{Number: 9, State: domain.MRStateClosed}, nil).Once()
168181 resp, err := (&mergeRequestService{mrs}).CloseMergeRequest(ownerCtx(), connect.NewRequest(&pb.CloseMergeRequestRequest{Repo: "demo", Number: 9}))
@@ -170,11 +183,13 @@ func TestMergeRequestServiceCloseAndMarkRead(t *testing.T) {
170183 assert.Equal(t, pb.MergeRequestState_MERGE_REQUEST_STATE_CLOSED, resp.Msg.GetMergeRequest().GetState())
171184 })
172185 t.Run("mark read rejects a short head", func(t *testing.T) {
186+ t.Parallel()
173187 _, err := (&mergeRequestService{mocks.NewMergeRequests(t)}).MarkRead(ownerCtx(),
174188 connect.NewRequest(&pb.MarkReadRequest{Repo: "demo", Number: 1, HeadId: "abc"}))
175189 requireCode(t, connect.CodeInvalidArgument, err)
176190 })
177191 t.Run("mark read unauthenticated", func(t *testing.T) {
192+ t.Parallel()
178193 mrs := mocks.NewMergeRequests(t)
179194 mrs.EXPECT().MarkRead(mock.Anything, domain.Owner(), mustRepoName(t, "demo"), int64(1), domain.ObjectID{}).Return(domain.ObjectID{}, domain.ErrUnauthenticated).Once()
180195 _, err := (&mergeRequestService{mrs}).MarkRead(ownerCtx(), connect.NewRequest(&pb.MarkReadRequest{Repo: "demo", Number: 1}))
@@ -183,6 +198,7 @@ func TestMergeRequestServiceCloseAndMarkRead(t *testing.T) {
183198 }
184199
185200 func TestMergeRequestServiceGet(t *testing.T) {
201+ t.Parallel()
186202 tests := []struct {
187203 name string
188204 usecase error
@@ -194,6 +210,7 @@ func TestMergeRequestServiceGet(t *testing.T) {
194210 }
195211 for _, tt := range tests {
196212 t.Run(tt.name, func(t *testing.T) {
213+ t.Parallel()
197214 mrs := mocks.NewMergeRequests(t)
198215 mrs.EXPECT().Get(mock.Anything, domain.Owner(), mustRepoName(t, "demo"), int64(4), true).
199216 Return(app.MergeRequestDetail{MergeRequest: domain.MergeRequest{Number: 4}, CommitsTruncated: true}, tt.usecase).Once()
@@ -17,6 +17,7 @@ import (
17 )17 )
18 18
19 func TestMergeRequestServiceCreate(t *testing.T) {19 func TestMergeRequestServiceCreate(t *testing.T) {
20+ t.Parallel()
20 tests := []struct {21 tests := []struct {
21 name string22 name string
22 repo string23 repo string
@@ -33,6 +34,7 @@ func TestMergeRequestServiceCreate(t *testing.T) {
33 }34 }
34 for _, tt := range tests {35 for _, tt := range tests {
35 t.Run(tt.name, func(t *testing.T) {36 t.Run(tt.name, func(t *testing.T) {
37+ t.Parallel()
36 mrs := mocks.NewMergeRequests(t)38 mrs := mocks.NewMergeRequests(t)
37 if tt.wantCall {39 if tt.wantCall {
38 mrs.EXPECT().Create(mock.Anything, domain.Owner(), mustRepoName(t, "demo"), app.CreateMergeRequestParams{40 mrs.EXPECT().Create(mock.Anything, domain.Owner(), mustRepoName(t, "demo"), app.CreateMergeRequestParams{
@@ -54,12 +56,14 @@ func TestMergeRequestServiceCreate(t *testing.T) {
54 }56 }
55 57
56 func TestMergeRequestServiceListRejectsUnknownState(t *testing.T) {58 func TestMergeRequestServiceListRejectsUnknownState(t *testing.T) {
59+ t.Parallel()
57 _, err := (&mergeRequestService{mocks.NewMergeRequests(t)}).ListMergeRequests(ownerCtx(),60 _, err := (&mergeRequestService{mocks.NewMergeRequests(t)}).ListMergeRequests(ownerCtx(),
58 connect.NewRequest(&pb.ListMergeRequestsRequest{Repo: "demo", State: pb.MergeRequestState(99)}))61 connect.NewRequest(&pb.ListMergeRequestsRequest{Repo: "demo", State: pb.MergeRequestState(99)}))
59 requireCode(t, connect.CodeInvalidArgument, err)62 requireCode(t, connect.CodeInvalidArgument, err)
60 }63 }
61 64
62 func TestMergeRequestServiceListFiltersByState(t *testing.T) {65 func TestMergeRequestServiceListFiltersByState(t *testing.T) {
66+ t.Parallel()
63 mrs := mocks.NewMergeRequests(t)67 mrs := mocks.NewMergeRequests(t)
64 mrs.EXPECT().List(mock.Anything, domain.Owner(), mustRepoName(t, "demo"), app.ListMergeRequestsParams{68 mrs.EXPECT().List(mock.Anything, domain.Owner(), mustRepoName(t, "demo"), app.ListMergeRequestsParams{
65 State: domain.MRStateMerged, PageSize: 3, PageToken: "tok",69 State: domain.MRStateMerged, PageSize: 3, PageToken: "tok",
@@ -73,6 +77,7 @@ func TestMergeRequestServiceListFiltersByState(t *testing.T) {
73 }77 }
74 78
75 func TestMergeRequestServiceAddComment(t *testing.T) {79 func TestMergeRequestServiceAddComment(t *testing.T) {
80+ t.Parallel()
76 tests := []struct {81 tests := []struct {
77 name string82 name string
78 side pb.DiffSide83 side pb.DiffSide
@@ -88,6 +93,7 @@ func TestMergeRequestServiceAddComment(t *testing.T) {
88 }93 }
89 for _, tt := range tests {94 for _, tt := range tests {
90 t.Run(tt.name, func(t *testing.T) {95 t.Run(tt.name, func(t *testing.T) {
96+ t.Parallel()
91 mrs := mocks.NewMergeRequests(t)97 mrs := mocks.NewMergeRequests(t)
92 if tt.wantCall {98 if tt.wantCall {
93 mrs.EXPECT().AddComment(mock.Anything, domain.Owner(), mustRepoName(t, "demo"), int64(2), mock.Anything).99 mrs.EXPECT().AddComment(mock.Anything, domain.Owner(), mustRepoName(t, "demo"), int64(2), mock.Anything).
@@ -107,8 +113,10 @@ func TestMergeRequestServiceAddComment(t *testing.T) {
107 }113 }
108 114
109 func TestMergeRequestServiceResolveCommentMapsUnissuedIDsToZero(t *testing.T) {115 func TestMergeRequestServiceResolveCommentMapsUnissuedIDsToZero(t *testing.T) {
116+ t.Parallel()
110 for _, id := range []string{"", "abc", "-1", "0", "007", "99999999999999999999"} {117 for _, id := range []string{"", "abc", "-1", "0", "007", "99999999999999999999"} {
111 t.Run(id, func(t *testing.T) {118 t.Run(id, func(t *testing.T) {
119+ t.Parallel()
112 mrs := mocks.NewMergeRequests(t)120 mrs := mocks.NewMergeRequests(t)
113 mrs.EXPECT().ResolveComment(mock.Anything, domain.Owner(), mustRepoName(t, "demo"), int64(1), int64(0), true).121 mrs.EXPECT().ResolveComment(mock.Anything, domain.Owner(), mustRepoName(t, "demo"), int64(1), int64(0), true).
114 Return(domain.Comment{}, domain.ErrNotFound).Once()122 Return(domain.Comment{}, domain.ErrNotFound).Once()
@@ -121,6 +129,7 @@ func TestMergeRequestServiceResolveCommentMapsUnissuedIDsToZero(t *testing.T) {
121 }129 }
122 130
123 func TestMergeRequestServiceMerge(t *testing.T) {131 func TestMergeRequestServiceMerge(t *testing.T) {
132+ t.Parallel()
124 const head = "0123456789abcdef0123456789abcdef01234567"133 const head = "0123456789abcdef0123456789abcdef01234567"
125 tests := []struct {134 tests := []struct {
126 name string135 name string
@@ -138,6 +147,7 @@ func TestMergeRequestServiceMerge(t *testing.T) {
138 }147 }
139 for _, tt := range tests {148 for _, tt := range tests {
140 t.Run(tt.name, func(t *testing.T) {149 t.Run(tt.name, func(t *testing.T) {
150+ t.Parallel()
141 mrs := mocks.NewMergeRequests(t)151 mrs := mocks.NewMergeRequests(t)
142 if tt.wantCall {152 if tt.wantCall {
143 mrs.EXPECT().Merge(mock.Anything, domain.Owner(), mustRepoName(t, "demo"), int64(1), mock.MatchedBy(func(p app.MergeParams) bool {153 mrs.EXPECT().Merge(mock.Anything, domain.Owner(), mustRepoName(t, "demo"), int64(1), mock.MatchedBy(func(p app.MergeParams) bool {
@@ -156,13 +166,16 @@ func TestMergeRequestServiceMerge(t *testing.T) {
156 }166 }
157 167
158 func TestMergeRequestServiceCloseAndMarkRead(t *testing.T) {168 func TestMergeRequestServiceCloseAndMarkRead(t *testing.T) {
169+ t.Parallel()
159 t.Run("close not found", func(t *testing.T) {170 t.Run("close not found", func(t *testing.T) {
171+ t.Parallel()
160 mrs := mocks.NewMergeRequests(t)172 mrs := mocks.NewMergeRequests(t)
161 mrs.EXPECT().Close(mock.Anything, domain.Owner(), mustRepoName(t, "demo"), int64(9)).Return(domain.MergeRequest{}, domain.ErrNotFound).Once()173 mrs.EXPECT().Close(mock.Anything, domain.Owner(), mustRepoName(t, "demo"), int64(9)).Return(domain.MergeRequest{}, domain.ErrNotFound).Once()
162 _, err := (&mergeRequestService{mrs}).CloseMergeRequest(ownerCtx(), connect.NewRequest(&pb.CloseMergeRequestRequest{Repo: "demo", Number: 9}))174 _, err := (&mergeRequestService{mrs}).CloseMergeRequest(ownerCtx(), connect.NewRequest(&pb.CloseMergeRequestRequest{Repo: "demo", Number: 9}))
163 requireCode(t, connect.CodeNotFound, err)175 requireCode(t, connect.CodeNotFound, err)
164 })176 })
165 t.Run("close ok", func(t *testing.T) {177 t.Run("close ok", func(t *testing.T) {
178+ t.Parallel()
166 mrs := mocks.NewMergeRequests(t)179 mrs := mocks.NewMergeRequests(t)
167 mrs.EXPECT().Close(mock.Anything, domain.Owner(), mustRepoName(t, "demo"), int64(9)).Return(domain.MergeRequest{Number: 9, State: domain.MRStateClosed}, nil).Once()180 mrs.EXPECT().Close(mock.Anything, domain.Owner(), mustRepoName(t, "demo"), int64(9)).Return(domain.MergeRequest{Number: 9, State: domain.MRStateClosed}, nil).Once()
168 resp, err := (&mergeRequestService{mrs}).CloseMergeRequest(ownerCtx(), connect.NewRequest(&pb.CloseMergeRequestRequest{Repo: "demo", Number: 9}))181 resp, err := (&mergeRequestService{mrs}).CloseMergeRequest(ownerCtx(), connect.NewRequest(&pb.CloseMergeRequestRequest{Repo: "demo", Number: 9}))
@@ -170,11 +183,13 @@ func TestMergeRequestServiceCloseAndMarkRead(t *testing.T) {
170 assert.Equal(t, pb.MergeRequestState_MERGE_REQUEST_STATE_CLOSED, resp.Msg.GetMergeRequest().GetState())183 assert.Equal(t, pb.MergeRequestState_MERGE_REQUEST_STATE_CLOSED, resp.Msg.GetMergeRequest().GetState())
171 })184 })
172 t.Run("mark read rejects a short head", func(t *testing.T) {185 t.Run("mark read rejects a short head", func(t *testing.T) {
186+ t.Parallel()
173 _, err := (&mergeRequestService{mocks.NewMergeRequests(t)}).MarkRead(ownerCtx(),187 _, err := (&mergeRequestService{mocks.NewMergeRequests(t)}).MarkRead(ownerCtx(),
174 connect.NewRequest(&pb.MarkReadRequest{Repo: "demo", Number: 1, HeadId: "abc"}))188 connect.NewRequest(&pb.MarkReadRequest{Repo: "demo", Number: 1, HeadId: "abc"}))
175 requireCode(t, connect.CodeInvalidArgument, err)189 requireCode(t, connect.CodeInvalidArgument, err)
176 })190 })
177 t.Run("mark read unauthenticated", func(t *testing.T) {191 t.Run("mark read unauthenticated", func(t *testing.T) {
192+ t.Parallel()
178 mrs := mocks.NewMergeRequests(t)193 mrs := mocks.NewMergeRequests(t)
179 mrs.EXPECT().MarkRead(mock.Anything, domain.Owner(), mustRepoName(t, "demo"), int64(1), domain.ObjectID{}).Return(domain.ObjectID{}, domain.ErrUnauthenticated).Once()194 mrs.EXPECT().MarkRead(mock.Anything, domain.Owner(), mustRepoName(t, "demo"), int64(1), domain.ObjectID{}).Return(domain.ObjectID{}, domain.ErrUnauthenticated).Once()
180 _, err := (&mergeRequestService{mrs}).MarkRead(ownerCtx(), connect.NewRequest(&pb.MarkReadRequest{Repo: "demo", Number: 1}))195 _, err := (&mergeRequestService{mrs}).MarkRead(ownerCtx(), connect.NewRequest(&pb.MarkReadRequest{Repo: "demo", Number: 1}))
@@ -183,6 +198,7 @@ func TestMergeRequestServiceCloseAndMarkRead(t *testing.T) {
183 }198 }
184 199
185 func TestMergeRequestServiceGet(t *testing.T) {200 func TestMergeRequestServiceGet(t *testing.T) {
201+ t.Parallel()
186 tests := []struct {202 tests := []struct {
187 name string203 name string
188 usecase error204 usecase error
@@ -194,6 +210,7 @@ func TestMergeRequestServiceGet(t *testing.T) {
194 }210 }
195 for _, tt := range tests {211 for _, tt := range tests {
196 t.Run(tt.name, func(t *testing.T) {212 t.Run(tt.name, func(t *testing.T) {
213+ t.Parallel()
197 mrs := mocks.NewMergeRequests(t)214 mrs := mocks.NewMergeRequests(t)
198 mrs.EXPECT().Get(mock.Anything, domain.Owner(), mustRepoName(t, "demo"), int64(4), true).215 mrs.EXPECT().Get(mock.Anything, domain.Owner(), mustRepoName(t, "demo"), int64(4), true).
199 Return(app.MergeRequestDetail{MergeRequest: domain.MergeRequest{Number: 4}, CommitsTruncated: true}, tt.usecase).Once()216 Return(app.MergeRequestDetail{MergeRequest: domain.MergeRequest{Number: 4}, CommitsTruncated: true}, tt.usecase).Once()
M internal/adapters/rpc/repo_test.go +11 -0
@@ -39,6 +39,7 @@ func requireCode(t *testing.T, want connect.Code, err error) {
3939 }
4040
4141 func TestRepoServiceGetRepo(t *testing.T) {
42+ t.Parallel()
4243 boom := errors.New("disk on fire")
4344 tests := []struct {
4445 name string
@@ -58,6 +59,7 @@ func TestRepoServiceGetRepo(t *testing.T) {
5859 }
5960 for _, tt := range tests {
6061 t.Run(tt.name, func(t *testing.T) {
62+ t.Parallel()
6163 repos := mocks.NewRepos(t)
6264 if tt.wantCall {
6365 repo := domain.Repo{Name: mustRepoName(t, "demo"), Description: "d", Visibility: domain.VisibilityPublic}
@@ -83,6 +85,7 @@ func TestRepoServiceGetRepo(t *testing.T) {
8385 }
8486
8587 func TestRepoServiceCreateRepo(t *testing.T) {
88+ t.Parallel()
8689 tests := []struct {
8790 name string
8891 req *pb.CreateRepoRequest
@@ -98,6 +101,7 @@ func TestRepoServiceCreateRepo(t *testing.T) {
98101 }
99102 for _, tt := range tests {
100103 t.Run(tt.name, func(t *testing.T) {
104+ t.Parallel()
101105 repos := mocks.NewRepos(t)
102106 if tt.wantCall {
103107 repo := domain.Repo{Name: mustRepoName(t, "demo"), Visibility: domain.VisibilityPrivate}
@@ -121,6 +125,7 @@ func TestRepoServiceCreateRepo(t *testing.T) {
121125 }
122126
123127 func TestRepoServiceUpdateRepoValidatesMask(t *testing.T) {
128+ t.Parallel()
124129 tests := []struct {
125130 name string
126131 req *pb.UpdateRepoRequest
@@ -164,6 +169,7 @@ func TestRepoServiceUpdateRepoValidatesMask(t *testing.T) {
164169 }
165170 for _, tt := range tests {
166171 t.Run(tt.name, func(t *testing.T) {
172+ t.Parallel()
167173 repos := mocks.NewRepos(t)
168174 if tt.wantCall {
169175 repo := domain.Repo{Name: mustRepoName(t, "demo"), Description: "new"}
@@ -184,6 +190,7 @@ func TestRepoServiceUpdateRepoValidatesMask(t *testing.T) {
184190 }
185191
186192 func TestRepoServiceDeleteRepo(t *testing.T) {
193+ t.Parallel()
187194 tests := []struct {
188195 name string
189196 repo string
@@ -198,6 +205,7 @@ func TestRepoServiceDeleteRepo(t *testing.T) {
198205 }
199206 for _, tt := range tests {
200207 t.Run(tt.name, func(t *testing.T) {
208+ t.Parallel()
201209 repos := mocks.NewRepos(t)
202210 if tt.wantCall {
203211 repos.EXPECT().Delete(mock.Anything, domain.Owner(), mustRepoName(t, tt.repo)).Return(tt.usecase).Once()
@@ -213,7 +221,9 @@ func TestRepoServiceDeleteRepo(t *testing.T) {
213221 }
214222
215223 func TestRepoServiceListRepos(t *testing.T) {
224+ t.Parallel()
216225 t.Run("pages and views every repo", func(t *testing.T) {
226+ t.Parallel()
217227 repos := mocks.NewRepos(t)
218228 a := domain.Repo{Name: mustRepoName(t, "a")}
219229 b := domain.Repo{Name: mustRepoName(t, "b")}
@@ -234,6 +244,7 @@ func TestRepoServiceListRepos(t *testing.T) {
234244 "unauthenticated": {domain.ErrUnauthenticated, connect.CodeUnauthenticated},
235245 } {
236246 t.Run(name, func(t *testing.T) {
247+ t.Parallel()
237248 repos := mocks.NewRepos(t)
238249 repos.EXPECT().ListPage(mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(app.RepoPage{}, tc.err).Once()
239250 _, err := (&repoService{repos}).ListRepos(ownerCtx(), connect.NewRequest(&pb.ListReposRequest{}))
@@ -39,6 +39,7 @@ func requireCode(t *testing.T, want connect.Code, err error) {
39 }39 }
40 40
41 func TestRepoServiceGetRepo(t *testing.T) {41 func TestRepoServiceGetRepo(t *testing.T) {
42+ t.Parallel()
42 boom := errors.New("disk on fire")43 boom := errors.New("disk on fire")
43 tests := []struct {44 tests := []struct {
44 name string45 name string
@@ -58,6 +59,7 @@ func TestRepoServiceGetRepo(t *testing.T) {
58 }59 }
59 for _, tt := range tests {60 for _, tt := range tests {
60 t.Run(tt.name, func(t *testing.T) {61 t.Run(tt.name, func(t *testing.T) {
62+ t.Parallel()
61 repos := mocks.NewRepos(t)63 repos := mocks.NewRepos(t)
62 if tt.wantCall {64 if tt.wantCall {
63 repo := domain.Repo{Name: mustRepoName(t, "demo"), Description: "d", Visibility: domain.VisibilityPublic}65 repo := domain.Repo{Name: mustRepoName(t, "demo"), Description: "d", Visibility: domain.VisibilityPublic}
@@ -83,6 +85,7 @@ func TestRepoServiceGetRepo(t *testing.T) {
83 }85 }
84 86
85 func TestRepoServiceCreateRepo(t *testing.T) {87 func TestRepoServiceCreateRepo(t *testing.T) {
88+ t.Parallel()
86 tests := []struct {89 tests := []struct {
87 name string90 name string
88 req *pb.CreateRepoRequest91 req *pb.CreateRepoRequest
@@ -98,6 +101,7 @@ func TestRepoServiceCreateRepo(t *testing.T) {
98 }101 }
99 for _, tt := range tests {102 for _, tt := range tests {
100 t.Run(tt.name, func(t *testing.T) {103 t.Run(tt.name, func(t *testing.T) {
104+ t.Parallel()
101 repos := mocks.NewRepos(t)105 repos := mocks.NewRepos(t)
102 if tt.wantCall {106 if tt.wantCall {
103 repo := domain.Repo{Name: mustRepoName(t, "demo"), Visibility: domain.VisibilityPrivate}107 repo := domain.Repo{Name: mustRepoName(t, "demo"), Visibility: domain.VisibilityPrivate}
@@ -121,6 +125,7 @@ func TestRepoServiceCreateRepo(t *testing.T) {
121 }125 }
122 126
123 func TestRepoServiceUpdateRepoValidatesMask(t *testing.T) {127 func TestRepoServiceUpdateRepoValidatesMask(t *testing.T) {
128+ t.Parallel()
124 tests := []struct {129 tests := []struct {
125 name string130 name string
126 req *pb.UpdateRepoRequest131 req *pb.UpdateRepoRequest
@@ -164,6 +169,7 @@ func TestRepoServiceUpdateRepoValidatesMask(t *testing.T) {
164 }169 }
165 for _, tt := range tests {170 for _, tt := range tests {
166 t.Run(tt.name, func(t *testing.T) {171 t.Run(tt.name, func(t *testing.T) {
172+ t.Parallel()
167 repos := mocks.NewRepos(t)173 repos := mocks.NewRepos(t)
168 if tt.wantCall {174 if tt.wantCall {
169 repo := domain.Repo{Name: mustRepoName(t, "demo"), Description: "new"}175 repo := domain.Repo{Name: mustRepoName(t, "demo"), Description: "new"}
@@ -184,6 +190,7 @@ func TestRepoServiceUpdateRepoValidatesMask(t *testing.T) {
184 }190 }
185 191
186 func TestRepoServiceDeleteRepo(t *testing.T) {192 func TestRepoServiceDeleteRepo(t *testing.T) {
193+ t.Parallel()
187 tests := []struct {194 tests := []struct {
188 name string195 name string
189 repo string196 repo string
@@ -198,6 +205,7 @@ func TestRepoServiceDeleteRepo(t *testing.T) {
198 }205 }
199 for _, tt := range tests {206 for _, tt := range tests {
200 t.Run(tt.name, func(t *testing.T) {207 t.Run(tt.name, func(t *testing.T) {
208+ t.Parallel()
201 repos := mocks.NewRepos(t)209 repos := mocks.NewRepos(t)
202 if tt.wantCall {210 if tt.wantCall {
203 repos.EXPECT().Delete(mock.Anything, domain.Owner(), mustRepoName(t, tt.repo)).Return(tt.usecase).Once()211 repos.EXPECT().Delete(mock.Anything, domain.Owner(), mustRepoName(t, tt.repo)).Return(tt.usecase).Once()
@@ -213,7 +221,9 @@ func TestRepoServiceDeleteRepo(t *testing.T) {
213 }221 }
214 222
215 func TestRepoServiceListRepos(t *testing.T) {223 func TestRepoServiceListRepos(t *testing.T) {
224+ t.Parallel()
216 t.Run("pages and views every repo", func(t *testing.T) {225 t.Run("pages and views every repo", func(t *testing.T) {
226+ t.Parallel()
217 repos := mocks.NewRepos(t)227 repos := mocks.NewRepos(t)
218 a := domain.Repo{Name: mustRepoName(t, "a")}228 a := domain.Repo{Name: mustRepoName(t, "a")}
219 b := domain.Repo{Name: mustRepoName(t, "b")}229 b := domain.Repo{Name: mustRepoName(t, "b")}
@@ -234,6 +244,7 @@ func TestRepoServiceListRepos(t *testing.T) {
234 "unauthenticated": {domain.ErrUnauthenticated, connect.CodeUnauthenticated},244 "unauthenticated": {domain.ErrUnauthenticated, connect.CodeUnauthenticated},
235 } {245 } {
236 t.Run(name, func(t *testing.T) {246 t.Run(name, func(t *testing.T) {
247+ t.Parallel()
237 repos := mocks.NewRepos(t)248 repos := mocks.NewRepos(t)
238 repos.EXPECT().ListPage(mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(app.RepoPage{}, tc.err).Once()249 repos.EXPECT().ListPage(mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(app.RepoPage{}, tc.err).Once()
239 _, err := (&repoService{repos}).ListRepos(ownerCtx(), connect.NewRequest(&pb.ListReposRequest{}))250 _, err := (&repoService{repos}).ListRepos(ownerCtx(), connect.NewRequest(&pb.ListReposRequest{}))
M internal/adapters/rpc/server_test.go +2 -0
@@ -16,6 +16,7 @@ import (
1616 )
1717
1818 func TestServerServiceGetVersion(t *testing.T) {
19+ t.Parallel()
1920 svc := &serverService{version: "v1.2.3", key: mocks.NewSigningKey(t)}
2021 resp, err := svc.GetVersion(context.Background(), connect.NewRequest(&pb.GetVersionRequest{}))
2122 require.NoError(t, err)
@@ -28,6 +29,7 @@ func TestServerServiceGetVersion(t *testing.T) {
2829 }
2930
3031 func TestServerServiceGetSigningKey(t *testing.T) {
32+ t.Parallel()
3133 key := mocks.NewSigningKey(t)
3234 key.EXPECT().Info().Return(domain.SigningKeyInfo{
3335 Key: domain.SigningPublicKey{AuthorizedKey: "ssh-ed25519 AAAA", Fingerprint: "SHA256:xyz"},
@@ -16,6 +16,7 @@ import (
16 )16 )
17 17
18 func TestServerServiceGetVersion(t *testing.T) {18 func TestServerServiceGetVersion(t *testing.T) {
19+ t.Parallel()
19 svc := &serverService{version: "v1.2.3", key: mocks.NewSigningKey(t)}20 svc := &serverService{version: "v1.2.3", key: mocks.NewSigningKey(t)}
20 resp, err := svc.GetVersion(context.Background(), connect.NewRequest(&pb.GetVersionRequest{}))21 resp, err := svc.GetVersion(context.Background(), connect.NewRequest(&pb.GetVersionRequest{}))
21 require.NoError(t, err)22 require.NoError(t, err)
@@ -28,6 +29,7 @@ func TestServerServiceGetVersion(t *testing.T) {
28 }29 }
29 30
30 func TestServerServiceGetSigningKey(t *testing.T) {31 func TestServerServiceGetSigningKey(t *testing.T) {
32+ t.Parallel()
31 key := mocks.NewSigningKey(t)33 key := mocks.NewSigningKey(t)
32 key.EXPECT().Info().Return(domain.SigningKeyInfo{34 key.EXPECT().Info().Return(domain.SigningKeyInfo{
33 Key: domain.SigningPublicKey{AuthorizedKey: "ssh-ed25519 AAAA", Fingerprint: "SHA256:xyz"},35 Key: domain.SigningPublicKey{AuthorizedKey: "ssh-ed25519 AAAA", Fingerprint: "SHA256:xyz"},
M internal/adapters/rpc/session_test.go +4 -0
@@ -18,6 +18,7 @@ import (
1818 )
1919
2020 func TestSessionServiceCreateLoginLink(t *testing.T) {
21+ t.Parallel()
2122 exp := time.Date(2030, 1, 2, 3, 4, 5, 0, time.UTC)
2223 tests := []struct {
2324 name string
@@ -30,6 +31,7 @@ func TestSessionServiceCreateLoginLink(t *testing.T) {
3031 }
3132 for _, tt := range tests {
3233 t.Run(tt.name, func(t *testing.T) {
34+ t.Parallel()
3335 sessions := mocks.NewSessions(t)
3436 sessions.EXPECT().CreateLoginLink(mock.Anything, domain.Owner()).
3537 Return(app.LoginLink{URL: "https://x/-/login/n", ExpiresAt: exp}, tt.usecase).Once()
@@ -46,6 +48,7 @@ func TestSessionServiceCreateLoginLink(t *testing.T) {
4648 }
4749
4850 func TestSessionServiceSignOutEverywhere(t *testing.T) {
51+ t.Parallel()
4952 tests := []struct {
5053 name string
5154 usecase error
@@ -56,6 +59,7 @@ func TestSessionServiceSignOutEverywhere(t *testing.T) {
5659 }
5760 for _, tt := range tests {
5861 t.Run(tt.name, func(t *testing.T) {
62+ t.Parallel()
5963 sessions := mocks.NewSessions(t)
6064 sessions.EXPECT().SignOutEverywhere(mock.Anything, domain.Owner()).Return(tt.usecase).Once()
6165 _, err := (&sessionService{sessions}).SignOutEverywhere(ownerCtx(), connect.NewRequest(&pb.SignOutEverywhereRequest{}))
@@ -18,6 +18,7 @@ import (
18 )18 )
19 19
20 func TestSessionServiceCreateLoginLink(t *testing.T) {20 func TestSessionServiceCreateLoginLink(t *testing.T) {
21+ t.Parallel()
21 exp := time.Date(2030, 1, 2, 3, 4, 5, 0, time.UTC)22 exp := time.Date(2030, 1, 2, 3, 4, 5, 0, time.UTC)
22 tests := []struct {23 tests := []struct {
23 name string24 name string
@@ -30,6 +31,7 @@ func TestSessionServiceCreateLoginLink(t *testing.T) {
30 }31 }
31 for _, tt := range tests {32 for _, tt := range tests {
32 t.Run(tt.name, func(t *testing.T) {33 t.Run(tt.name, func(t *testing.T) {
34+ t.Parallel()
33 sessions := mocks.NewSessions(t)35 sessions := mocks.NewSessions(t)
34 sessions.EXPECT().CreateLoginLink(mock.Anything, domain.Owner()).36 sessions.EXPECT().CreateLoginLink(mock.Anything, domain.Owner()).
35 Return(app.LoginLink{URL: "https://x/-/login/n", ExpiresAt: exp}, tt.usecase).Once()37 Return(app.LoginLink{URL: "https://x/-/login/n", ExpiresAt: exp}, tt.usecase).Once()
@@ -46,6 +48,7 @@ func TestSessionServiceCreateLoginLink(t *testing.T) {
46 }48 }
47 49
48 func TestSessionServiceSignOutEverywhere(t *testing.T) {50 func TestSessionServiceSignOutEverywhere(t *testing.T) {
51+ t.Parallel()
49 tests := []struct {52 tests := []struct {
50 name string53 name string
51 usecase error54 usecase error
@@ -56,6 +59,7 @@ func TestSessionServiceSignOutEverywhere(t *testing.T) {
56 }59 }
57 for _, tt := range tests {60 for _, tt := range tests {
58 t.Run(tt.name, func(t *testing.T) {61 t.Run(tt.name, func(t *testing.T) {
62+ t.Parallel()
59 sessions := mocks.NewSessions(t)63 sessions := mocks.NewSessions(t)
60 sessions.EXPECT().SignOutEverywhere(mock.Anything, domain.Owner()).Return(tt.usecase).Once()64 sessions.EXPECT().SignOutEverywhere(mock.Anything, domain.Owner()).Return(tt.usecase).Once()
61 _, err := (&sessionService{sessions}).SignOutEverywhere(ownerCtx(), connect.NewRequest(&pb.SignOutEverywhereRequest{}))65 _, err := (&sessionService{sessions}).SignOutEverywhere(ownerCtx(), connect.NewRequest(&pb.SignOutEverywhereRequest{}))
M internal/adapters/rpc/token_test.go +6 -0
@@ -17,6 +17,7 @@ import (
1717 )
1818
1919 func TestTokenServiceCreateToken(t *testing.T) {
20+ t.Parallel()
2021 tests := []struct {
2122 name string
2223 req *pb.CreateTokenRequest
@@ -33,6 +34,7 @@ func TestTokenServiceCreateToken(t *testing.T) {
3334 }
3435 for _, tt := range tests {
3536 t.Run(tt.name, func(t *testing.T) {
37+ t.Parallel()
3638 tokens := mocks.NewTokens(t)
3739 if tt.wantCall {
3840 tokens.EXPECT().Create(mock.Anything, domain.Owner(), mock.Anything).
@@ -51,6 +53,7 @@ func TestTokenServiceCreateToken(t *testing.T) {
5153 }
5254
5355 func TestTokenServiceCreateTokenPassesParams(t *testing.T) {
56+ t.Parallel()
5457 tokens := mocks.NewTokens(t)
5558 tokens.EXPECT().Create(mock.Anything, domain.Owner(), mock.MatchedBy(func(p app.CreateTokenParams) bool {
5659 return p.Label == "ci" && len(p.Scopes) == 1 && len(p.Repos) == 1 && p.Repos[0].String() == "demo"
@@ -62,6 +65,7 @@ func TestTokenServiceCreateTokenPassesParams(t *testing.T) {
6265 }
6366
6467 func TestTokenServiceRevokeToken(t *testing.T) {
68+ t.Parallel()
6569 tests := []struct {
6670 name string
6771 usecase error
@@ -73,6 +77,7 @@ func TestTokenServiceRevokeToken(t *testing.T) {
7377 }
7478 for _, tt := range tests {
7579 t.Run(tt.name, func(t *testing.T) {
80+ t.Parallel()
7681 tokens := mocks.NewTokens(t)
7782 tokens.EXPECT().Revoke(mock.Anything, domain.Owner(), "t1").Return(tt.usecase).Once()
7883 _, err := (&tokenService{tokens}).RevokeToken(ownerCtx(), connect.NewRequest(&pb.RevokeTokenRequest{Id: "t1"}))
@@ -86,6 +91,7 @@ func TestTokenServiceRevokeToken(t *testing.T) {
8691 }
8792
8893 func TestTokenServiceListTokens(t *testing.T) {
94+ t.Parallel()
8995 tokens := mocks.NewTokens(t)
9096 tokens.EXPECT().ListPage(mock.Anything, domain.Owner(), 5, "tok").
9197 Return(app.TokenPage{Tokens: []domain.Token{{ID: "a"}, {ID: "b"}}, NextPageToken: "n"}, nil).Once()
@@ -17,6 +17,7 @@ import (
17 )17 )
18 18
19 func TestTokenServiceCreateToken(t *testing.T) {19 func TestTokenServiceCreateToken(t *testing.T) {
20+ t.Parallel()
20 tests := []struct {21 tests := []struct {
21 name string22 name string
22 req *pb.CreateTokenRequest23 req *pb.CreateTokenRequest
@@ -33,6 +34,7 @@ func TestTokenServiceCreateToken(t *testing.T) {
33 }34 }
34 for _, tt := range tests {35 for _, tt := range tests {
35 t.Run(tt.name, func(t *testing.T) {36 t.Run(tt.name, func(t *testing.T) {
37+ t.Parallel()
36 tokens := mocks.NewTokens(t)38 tokens := mocks.NewTokens(t)
37 if tt.wantCall {39 if tt.wantCall {
38 tokens.EXPECT().Create(mock.Anything, domain.Owner(), mock.Anything).40 tokens.EXPECT().Create(mock.Anything, domain.Owner(), mock.Anything).
@@ -51,6 +53,7 @@ func TestTokenServiceCreateToken(t *testing.T) {
51 }53 }
52 54
53 func TestTokenServiceCreateTokenPassesParams(t *testing.T) {55 func TestTokenServiceCreateTokenPassesParams(t *testing.T) {
56+ t.Parallel()
54 tokens := mocks.NewTokens(t)57 tokens := mocks.NewTokens(t)
55 tokens.EXPECT().Create(mock.Anything, domain.Owner(), mock.MatchedBy(func(p app.CreateTokenParams) bool {58 tokens.EXPECT().Create(mock.Anything, domain.Owner(), mock.MatchedBy(func(p app.CreateTokenParams) bool {
56 return p.Label == "ci" && len(p.Scopes) == 1 && len(p.Repos) == 1 && p.Repos[0].String() == "demo"59 return p.Label == "ci" && len(p.Scopes) == 1 && len(p.Repos) == 1 && p.Repos[0].String() == "demo"
@@ -62,6 +65,7 @@ func TestTokenServiceCreateTokenPassesParams(t *testing.T) {
62 }65 }
63 66
64 func TestTokenServiceRevokeToken(t *testing.T) {67 func TestTokenServiceRevokeToken(t *testing.T) {
68+ t.Parallel()
65 tests := []struct {69 tests := []struct {
66 name string70 name string
67 usecase error71 usecase error
@@ -73,6 +77,7 @@ func TestTokenServiceRevokeToken(t *testing.T) {
73 }77 }
74 for _, tt := range tests {78 for _, tt := range tests {
75 t.Run(tt.name, func(t *testing.T) {79 t.Run(tt.name, func(t *testing.T) {
80+ t.Parallel()
76 tokens := mocks.NewTokens(t)81 tokens := mocks.NewTokens(t)
77 tokens.EXPECT().Revoke(mock.Anything, domain.Owner(), "t1").Return(tt.usecase).Once()82 tokens.EXPECT().Revoke(mock.Anything, domain.Owner(), "t1").Return(tt.usecase).Once()
78 _, err := (&tokenService{tokens}).RevokeToken(ownerCtx(), connect.NewRequest(&pb.RevokeTokenRequest{Id: "t1"}))83 _, err := (&tokenService{tokens}).RevokeToken(ownerCtx(), connect.NewRequest(&pb.RevokeTokenRequest{Id: "t1"}))
@@ -86,6 +91,7 @@ func TestTokenServiceRevokeToken(t *testing.T) {
86 }91 }
87 92
88 func TestTokenServiceListTokens(t *testing.T) {93 func TestTokenServiceListTokens(t *testing.T) {
94+ t.Parallel()
89 tokens := mocks.NewTokens(t)95 tokens := mocks.NewTokens(t)
90 tokens.EXPECT().ListPage(mock.Anything, domain.Owner(), 5, "tok").96 tokens.EXPECT().ListPage(mock.Anything, domain.Owner(), 5, "tok").
91 Return(app.TokenPage{Tokens: []domain.Token{{ID: "a"}, {ID: "b"}}, NextPageToken: "n"}, nil).Once()97 Return(app.TokenPage{Tokens: []domain.Token{{ID: "a"}, {ID: "b"}}, NextPageToken: "n"}, nil).Once()
M internal/adapters/web/commits_test.go +2 -0
@@ -15,6 +15,7 @@ import (
1515 )
1616
1717 func TestIsCommitID(t *testing.T) {
18+ t.Parallel()
1819 for _, s := range []string{"abcdef0", "0123456789abcdef0123456789abcdef01234567"} {
1920 assert.True(t, isCommitID(s), s)
2021 }
@@ -26,6 +27,7 @@ func TestIsCommitID(t *testing.T) {
2627 // The ref switcher marks the ref the page shows, whatever the page: a tag, a
2728 // branch other than the default one, or none for a commit.
2829 func TestPinRefMarksCurrent(t *testing.T) {
30+ t.Parallel()
2931 b, tag := pb.RefKind_REF_KIND_BRANCH, pb.RefKind_REF_KIND_TAG
3032 d := &repoData{
3133 repo: &pb.Repo{Name: "demo", DefaultBranch: "main"},
@@ -15,6 +15,7 @@ import (
15 )15 )
16 16
17 func TestIsCommitID(t *testing.T) {17 func TestIsCommitID(t *testing.T) {
18+ t.Parallel()
18 for _, s := range []string{"abcdef0", "0123456789abcdef0123456789abcdef01234567"} {19 for _, s := range []string{"abcdef0", "0123456789abcdef0123456789abcdef01234567"} {
19 assert.True(t, isCommitID(s), s)20 assert.True(t, isCommitID(s), s)
20 }21 }
@@ -26,6 +27,7 @@ func TestIsCommitID(t *testing.T) {
26 // The ref switcher marks the ref the page shows, whatever the page: a tag, a27 // The ref switcher marks the ref the page shows, whatever the page: a tag, a
27 // branch other than the default one, or none for a commit.28 // branch other than the default one, or none for a commit.
28 func TestPinRefMarksCurrent(t *testing.T) {29 func TestPinRefMarksCurrent(t *testing.T) {
30+ t.Parallel()
29 b, tag := pb.RefKind_REF_KIND_BRANCH, pb.RefKind_REF_KIND_TAG31 b, tag := pb.RefKind_REF_KIND_BRANCH, pb.RefKind_REF_KIND_TAG
30 d := &repoData{32 d := &repoData{
31 repo: &pb.Repo{Name: "demo", DefaultBranch: "main"},33 repo: &pb.Repo{Name: "demo", DefaultBranch: "main"},
M internal/adapters/web/compress_test.go +13 -0
@@ -22,6 +22,7 @@ import (
2222 )
2323
2424 func TestAcceptsGzip(t *testing.T) {
25+ t.Parallel()
2526 for _, tc := range []struct {
2627 name string
2728 header []string
@@ -47,6 +48,7 @@ func TestAcceptsGzip(t *testing.T) {
4748 {"substring is not a coding", []string{"gzipped"}, false},
4849 } {
4950 t.Run(tc.name, func(t *testing.T) {
51+ t.Parallel()
5052 h := http.Header{}
5153 for _, v := range tc.header {
5254 h.Add("Accept-Encoding", v)
@@ -57,6 +59,7 @@ func TestAcceptsGzip(t *testing.T) {
5759 }
5860
5961 func TestEtagMatches(t *testing.T) {
62+ t.Parallel()
6063 const tag = `"abc"`
6164 for _, tc := range []struct {
6265 name, header, etag string
@@ -77,12 +80,14 @@ func TestEtagMatches(t *testing.T) {
7780 {"comma in a tag", `"a,b", "abc"`, tag, true},
7881 } {
7982 t.Run(tc.name, func(t *testing.T) {
83+ t.Parallel()
8084 assert.Equal(t, tc.want, etagMatches(tc.header, tc.etag))
8185 })
8286 }
8387 }
8488
8589 func TestPageETag(t *testing.T) {
90+ t.Parallel()
8691 body := []byte("<html>x</html>")
8792 a := pageETag(body, false, false)
8893 assert.Equal(t, a, pageETag(body, false, false), "stable")
@@ -113,6 +118,7 @@ func gunzip(t *testing.T, b []byte) string {
113118
114119 // write: gzip by size and Accept-Encoding, validators, 304.
115120 func TestWritePage(t *testing.T) {
121+ t.Parallel()
116122 big := "<p>" + strings.Repeat("ermite page ", 400) + "</p>"
117123 small := "<p>tiny</p>"
118124
@@ -137,6 +143,7 @@ func TestWritePage(t *testing.T) {
137143 {"no-transform: untouched", "GET", "gzip", big, 200, "no-transform", false, true},
138144 } {
139145 t.Run(tc.name, func(t *testing.T) {
146+ t.Parallel()
140147 srv := newBareServer(t)
141148 rec := httptest.NewRecorder()
142149 r := httptest.NewRequestWithContext(context.Background(), tc.method, "/", nil)
@@ -172,6 +179,7 @@ func TestWritePage(t *testing.T) {
172179 }
173180
174181 func TestWritePageConditional(t *testing.T) {
182+ t.Parallel()
175183 big := "<p>" + strings.Repeat("ermite page ", 400) + "</p>"
176184 srv := newBareServer(t)
177185 do := func(ae, inm string) *http.Response {
@@ -207,6 +215,7 @@ func TestWritePageConditional(t *testing.T) {
207215 {"stale", "", `"stale"`, 200},
208216 } {
209217 t.Run(tc.name, func(t *testing.T) {
218+ t.Parallel()
210219 res := do(tc.ae, tc.inm)
211220 assert.Equal(t, tc.want, res.StatusCode)
212221 b, _ := io.ReadAll(res.Body)
@@ -225,6 +234,7 @@ func TestWritePageConditional(t *testing.T) {
225234
226235 // The owner's page is private and never revalidated, whatever the client sends.
227236 func TestWritePageOwnerHasNoValidator(t *testing.T) {
237+ t.Parallel()
228238 srv := newBareServer(t)
229239 ctx := context.WithValue(context.Background(), signedInKey{}, struct{}{})
230240 rec := httptest.NewRecorder()
@@ -240,6 +250,7 @@ func TestWritePageOwnerHasNoValidator(t *testing.T) {
240250
241251 // The assets: gzip once at load, chosen by Accept-Encoding, not for fonts.
242252 func TestAssetsGzip(t *testing.T) {
253+ t.Parallel()
243254 srv := newBareServer(t)
244255 get := func(url, ae string) *httptest.ResponseRecorder {
245256 rec := httptest.NewRecorder()
@@ -265,6 +276,7 @@ func TestAssetsGzip(t *testing.T) {
265276 {"font never", u.Font, "gzip", false, false},
266277 } {
267278 t.Run(tc.name, func(t *testing.T) {
279+ t.Parallel()
268280 rec := get(tc.url, tc.ae)
269281 require.Equal(t, 200, rec.Code)
270282 assert.Equal(t, "public, max-age=31536000, immutable", rec.Header().Get("Cache-Control"))
@@ -305,6 +317,7 @@ func TestAssetsGzip(t *testing.T) {
305317
306318 // The event stream is never compressed, even for a client that accepts gzip.
307319 func TestEventsNotCompressed(t *testing.T) {
320+ t.Parallel()
308321 f := &fakeEvents{release: make(chan struct{})}
309322 ts := eventsServer(t, f, 20*time.Millisecond, 0)
310323 resp, _ := open(t, ts, "topic=home", map[string]string{"Accept-Encoding": "gzip"})
@@ -22,6 +22,7 @@ import (
22 )22 )
23 23
24 func TestAcceptsGzip(t *testing.T) {24 func TestAcceptsGzip(t *testing.T) {
25+ t.Parallel()
25 for _, tc := range []struct {26 for _, tc := range []struct {
26 name string27 name string
27 header []string28 header []string
@@ -47,6 +48,7 @@ func TestAcceptsGzip(t *testing.T) {
47 {"substring is not a coding", []string{"gzipped"}, false},48 {"substring is not a coding", []string{"gzipped"}, false},
48 } {49 } {
49 t.Run(tc.name, func(t *testing.T) {50 t.Run(tc.name, func(t *testing.T) {
51+ t.Parallel()
50 h := http.Header{}52 h := http.Header{}
51 for _, v := range tc.header {53 for _, v := range tc.header {
52 h.Add("Accept-Encoding", v)54 h.Add("Accept-Encoding", v)
@@ -57,6 +59,7 @@ func TestAcceptsGzip(t *testing.T) {
57 }59 }
58 60
59 func TestEtagMatches(t *testing.T) {61 func TestEtagMatches(t *testing.T) {
62+ t.Parallel()
60 const tag = `"abc"`63 const tag = `"abc"`
61 for _, tc := range []struct {64 for _, tc := range []struct {
62 name, header, etag string65 name, header, etag string
@@ -77,12 +80,14 @@ func TestEtagMatches(t *testing.T) {
77 {"comma in a tag", `"a,b", "abc"`, tag, true},80 {"comma in a tag", `"a,b", "abc"`, tag, true},
78 } {81 } {
79 t.Run(tc.name, func(t *testing.T) {82 t.Run(tc.name, func(t *testing.T) {
83+ t.Parallel()
80 assert.Equal(t, tc.want, etagMatches(tc.header, tc.etag))84 assert.Equal(t, tc.want, etagMatches(tc.header, tc.etag))
81 })85 })
82 }86 }
83 }87 }
84 88
85 func TestPageETag(t *testing.T) {89 func TestPageETag(t *testing.T) {
90+ t.Parallel()
86 body := []byte("<html>x</html>")91 body := []byte("<html>x</html>")
87 a := pageETag(body, false, false)92 a := pageETag(body, false, false)
88 assert.Equal(t, a, pageETag(body, false, false), "stable")93 assert.Equal(t, a, pageETag(body, false, false), "stable")
@@ -113,6 +118,7 @@ func gunzip(t *testing.T, b []byte) string {
113 118
114 // write: gzip by size and Accept-Encoding, validators, 304.119 // write: gzip by size and Accept-Encoding, validators, 304.
115 func TestWritePage(t *testing.T) {120 func TestWritePage(t *testing.T) {
121+ t.Parallel()
116 big := "<p>" + strings.Repeat("ermite page ", 400) + "</p>"122 big := "<p>" + strings.Repeat("ermite page ", 400) + "</p>"
117 small := "<p>tiny</p>"123 small := "<p>tiny</p>"
118 124
@@ -137,6 +143,7 @@ func TestWritePage(t *testing.T) {
137 {"no-transform: untouched", "GET", "gzip", big, 200, "no-transform", false, true},143 {"no-transform: untouched", "GET", "gzip", big, 200, "no-transform", false, true},
138 } {144 } {
139 t.Run(tc.name, func(t *testing.T) {145 t.Run(tc.name, func(t *testing.T) {
146+ t.Parallel()
140 srv := newBareServer(t)147 srv := newBareServer(t)
141 rec := httptest.NewRecorder()148 rec := httptest.NewRecorder()
142 r := httptest.NewRequestWithContext(context.Background(), tc.method, "/", nil)149 r := httptest.NewRequestWithContext(context.Background(), tc.method, "/", nil)
@@ -172,6 +179,7 @@ func TestWritePage(t *testing.T) {
172 }179 }
173 180
174 func TestWritePageConditional(t *testing.T) {181 func TestWritePageConditional(t *testing.T) {
182+ t.Parallel()
175 big := "<p>" + strings.Repeat("ermite page ", 400) + "</p>"183 big := "<p>" + strings.Repeat("ermite page ", 400) + "</p>"
176 srv := newBareServer(t)184 srv := newBareServer(t)
177 do := func(ae, inm string) *http.Response {185 do := func(ae, inm string) *http.Response {
@@ -207,6 +215,7 @@ func TestWritePageConditional(t *testing.T) {
207 {"stale", "", `"stale"`, 200},215 {"stale", "", `"stale"`, 200},
208 } {216 } {
209 t.Run(tc.name, func(t *testing.T) {217 t.Run(tc.name, func(t *testing.T) {
218+ t.Parallel()
210 res := do(tc.ae, tc.inm)219 res := do(tc.ae, tc.inm)
211 assert.Equal(t, tc.want, res.StatusCode)220 assert.Equal(t, tc.want, res.StatusCode)
212 b, _ := io.ReadAll(res.Body)221 b, _ := io.ReadAll(res.Body)
@@ -225,6 +234,7 @@ func TestWritePageConditional(t *testing.T) {
225 234
226 // The owner's page is private and never revalidated, whatever the client sends.235 // The owner's page is private and never revalidated, whatever the client sends.
227 func TestWritePageOwnerHasNoValidator(t *testing.T) {236 func TestWritePageOwnerHasNoValidator(t *testing.T) {
237+ t.Parallel()
228 srv := newBareServer(t)238 srv := newBareServer(t)
229 ctx := context.WithValue(context.Background(), signedInKey{}, struct{}{})239 ctx := context.WithValue(context.Background(), signedInKey{}, struct{}{})
230 rec := httptest.NewRecorder()240 rec := httptest.NewRecorder()
@@ -240,6 +250,7 @@ func TestWritePageOwnerHasNoValidator(t *testing.T) {
240 250
241 // The assets: gzip once at load, chosen by Accept-Encoding, not for fonts.251 // The assets: gzip once at load, chosen by Accept-Encoding, not for fonts.
242 func TestAssetsGzip(t *testing.T) {252 func TestAssetsGzip(t *testing.T) {
253+ t.Parallel()
243 srv := newBareServer(t)254 srv := newBareServer(t)
244 get := func(url, ae string) *httptest.ResponseRecorder {255 get := func(url, ae string) *httptest.ResponseRecorder {
245 rec := httptest.NewRecorder()256 rec := httptest.NewRecorder()
@@ -265,6 +276,7 @@ func TestAssetsGzip(t *testing.T) {
265 {"font never", u.Font, "gzip", false, false},276 {"font never", u.Font, "gzip", false, false},
266 } {277 } {
267 t.Run(tc.name, func(t *testing.T) {278 t.Run(tc.name, func(t *testing.T) {
279+ t.Parallel()
268 rec := get(tc.url, tc.ae)280 rec := get(tc.url, tc.ae)
269 require.Equal(t, 200, rec.Code)281 require.Equal(t, 200, rec.Code)
270 assert.Equal(t, "public, max-age=31536000, immutable", rec.Header().Get("Cache-Control"))282 assert.Equal(t, "public, max-age=31536000, immutable", rec.Header().Get("Cache-Control"))
@@ -305,6 +317,7 @@ func TestAssetsGzip(t *testing.T) {
305 317
306 // The event stream is never compressed, even for a client that accepts gzip.318 // The event stream is never compressed, even for a client that accepts gzip.
307 func TestEventsNotCompressed(t *testing.T) {319 func TestEventsNotCompressed(t *testing.T) {
320+ t.Parallel()
308 f := &fakeEvents{release: make(chan struct{})}321 f := &fakeEvents{release: make(chan struct{})}
309 ts := eventsServer(t, f, 20*time.Millisecond, 0)322 ts := eventsServer(t, f, 20*time.Millisecond, 0)
310 resp, _ := open(t, ts, "topic=home", map[string]string{"Accept-Encoding": "gzip"})323 resp, _ := open(t, ts, "topic=home", map[string]string{"Accept-Encoding": "gzip"})
M internal/adapters/web/diff_test.go +7 -0
@@ -17,6 +17,7 @@ import (
1717 // characters are shown as markers; ordinary text, tabs and non-Latin letters
1818 // are not touched.
1919 func TestSegmentsShowInvisibleCharacters(t *testing.T) {
20+ t.Parallel()
2021 tests := []struct {
2122 name, in string
2223 want []Seg
@@ -35,6 +36,7 @@ func TestSegmentsShowInvisibleCharacters(t *testing.T) {
3536 }
3637 for _, tt := range tests {
3738 t.Run(tt.name, func(t *testing.T) {
39+ t.Parallel()
3840 assert.Equal(t, tt.want, segments(tt.in))
3941 })
4042 }
@@ -51,6 +53,7 @@ func line(kind pb.LineKind, oldN, newN int32, text string) *pb.Line {
5153 // context fills both sides; the extra lines of the longer run have an empty
5254 // opposite side.
5355 func TestPairLines(t *testing.T) {
56+ t.Parallel()
5457 ctx, add, del := pb.LineKind_LINE_KIND_CONTEXT, pb.LineKind_LINE_KIND_ADDITION, pb.LineKind_LINE_KIND_DELETION
5558 var ls []DiffLine
5659 for _, l := range []*pb.Line{
@@ -83,6 +86,7 @@ func TestPairLines(t *testing.T) {
8386 }
8487
8588 func TestChangeBar(t *testing.T) {
89+ t.Parallel()
8690 assert.Equal(t, Bar{Rest: "··········"}, changeBar(0, 0, 0))
8791 assert.Equal(t, Bar{Add: "++++++++++"}, changeBar(100, 0, 100))
8892 assert.Equal(t, Bar{Add: "+++++", Del: "-----", Rest: ""}, changeBar(50, 50, 100))
@@ -91,6 +95,7 @@ func TestChangeBar(t *testing.T) {
9195 }
9296
9397 func TestFileNotes(t *testing.T) {
98+ t.Parallel()
9499 assert.Equal(t, "binary file: no text diff", fileNote(&pb.FileDiff{Binary: true, Status: pb.FileStatus_FILE_STATUS_MODIFIED}))
95100 assert.Equal(t, "mode 100644 → 100755", fileNote(&pb.FileDiff{Status: pb.FileStatus_FILE_STATUS_MODIFIED, OldMode: 0o100644, NewMode: 0o100755}))
96101 assert.Empty(t, fileNote(&pb.FileDiff{Status: pb.FileStatus_FILE_STATUS_ADDED, NewMode: 0o100644}), "an added file has no mode change")
@@ -99,6 +104,7 @@ func TestFileNotes(t *testing.T) {
99104 }
100105
101106 func TestDiffFilesPaths(t *testing.T) {
107+ t.Parallel()
102108 fs := diffFiles([]*pb.FileDiff{
103109 {Status: pb.FileStatus_FILE_STATUS_ADDED, NewPath: "a.go", Additions: 1, Hunks: []*pb.Hunk{{Lines: []*pb.Line{line(pb.LineKind_LINE_KIND_ADDITION, 0, 1, "x")}}}},
104110 {Status: pb.FileStatus_FILE_STATUS_DELETED, OldPath: "b.go", Deletions: 1},
@@ -117,6 +123,7 @@ func TestDiffFilesPaths(t *testing.T) {
117123 // A code cell holds exactly the text: escaped, with markers, and nothing
118124 // added between the pieces (the cell keeps its white space).
119125 func TestCodeLine(t *testing.T) {
126+ t.Parallel()
120127 var b strings.Builder
121128 ln := diffLine(&pb.Line{Kind: pb.LineKind_LINE_KIND_ADDITION, NewNumber: 1, Text: "a <b> \u202e c d", NoNewlineAtEof: true})
122129 require.NoError(t, codeLine(ln).Render(context.Background(), &b))
@@ -17,6 +17,7 @@ import (
17 // characters are shown as markers; ordinary text, tabs and non-Latin letters17 // characters are shown as markers; ordinary text, tabs and non-Latin letters
18 // are not touched.18 // are not touched.
19 func TestSegmentsShowInvisibleCharacters(t *testing.T) {19 func TestSegmentsShowInvisibleCharacters(t *testing.T) {
20+ t.Parallel()
20 tests := []struct {21 tests := []struct {
21 name, in string22 name, in string
22 want []Seg23 want []Seg
@@ -35,6 +36,7 @@ func TestSegmentsShowInvisibleCharacters(t *testing.T) {
35 }36 }
36 for _, tt := range tests {37 for _, tt := range tests {
37 t.Run(tt.name, func(t *testing.T) {38 t.Run(tt.name, func(t *testing.T) {
39+ t.Parallel()
38 assert.Equal(t, tt.want, segments(tt.in))40 assert.Equal(t, tt.want, segments(tt.in))
39 })41 })
40 }42 }
@@ -51,6 +53,7 @@ func line(kind pb.LineKind, oldN, newN int32, text string) *pb.Line {
51 // context fills both sides; the extra lines of the longer run have an empty53 // context fills both sides; the extra lines of the longer run have an empty
52 // opposite side.54 // opposite side.
53 func TestPairLines(t *testing.T) {55 func TestPairLines(t *testing.T) {
56+ t.Parallel()
54 ctx, add, del := pb.LineKind_LINE_KIND_CONTEXT, pb.LineKind_LINE_KIND_ADDITION, pb.LineKind_LINE_KIND_DELETION57 ctx, add, del := pb.LineKind_LINE_KIND_CONTEXT, pb.LineKind_LINE_KIND_ADDITION, pb.LineKind_LINE_KIND_DELETION
55 var ls []DiffLine58 var ls []DiffLine
56 for _, l := range []*pb.Line{59 for _, l := range []*pb.Line{
@@ -83,6 +86,7 @@ func TestPairLines(t *testing.T) {
83 }86 }
84 87
85 func TestChangeBar(t *testing.T) {88 func TestChangeBar(t *testing.T) {
89+ t.Parallel()
86 assert.Equal(t, Bar{Rest: "··········"}, changeBar(0, 0, 0))90 assert.Equal(t, Bar{Rest: "··········"}, changeBar(0, 0, 0))
87 assert.Equal(t, Bar{Add: "++++++++++"}, changeBar(100, 0, 100))91 assert.Equal(t, Bar{Add: "++++++++++"}, changeBar(100, 0, 100))
88 assert.Equal(t, Bar{Add: "+++++", Del: "-----", Rest: ""}, changeBar(50, 50, 100))92 assert.Equal(t, Bar{Add: "+++++", Del: "-----", Rest: ""}, changeBar(50, 50, 100))
@@ -91,6 +95,7 @@ func TestChangeBar(t *testing.T) {
91 }95 }
92 96
93 func TestFileNotes(t *testing.T) {97 func TestFileNotes(t *testing.T) {
98+ t.Parallel()
94 assert.Equal(t, "binary file: no text diff", fileNote(&pb.FileDiff{Binary: true, Status: pb.FileStatus_FILE_STATUS_MODIFIED}))99 assert.Equal(t, "binary file: no text diff", fileNote(&pb.FileDiff{Binary: true, Status: pb.FileStatus_FILE_STATUS_MODIFIED}))
95 assert.Equal(t, "mode 100644 → 100755", fileNote(&pb.FileDiff{Status: pb.FileStatus_FILE_STATUS_MODIFIED, OldMode: 0o100644, NewMode: 0o100755}))100 assert.Equal(t, "mode 100644 → 100755", fileNote(&pb.FileDiff{Status: pb.FileStatus_FILE_STATUS_MODIFIED, OldMode: 0o100644, NewMode: 0o100755}))
96 assert.Empty(t, fileNote(&pb.FileDiff{Status: pb.FileStatus_FILE_STATUS_ADDED, NewMode: 0o100644}), "an added file has no mode change")101 assert.Empty(t, fileNote(&pb.FileDiff{Status: pb.FileStatus_FILE_STATUS_ADDED, NewMode: 0o100644}), "an added file has no mode change")
@@ -99,6 +104,7 @@ func TestFileNotes(t *testing.T) {
99 }104 }
100 105
101 func TestDiffFilesPaths(t *testing.T) {106 func TestDiffFilesPaths(t *testing.T) {
107+ t.Parallel()
102 fs := diffFiles([]*pb.FileDiff{108 fs := diffFiles([]*pb.FileDiff{
103 {Status: pb.FileStatus_FILE_STATUS_ADDED, NewPath: "a.go", Additions: 1, Hunks: []*pb.Hunk{{Lines: []*pb.Line{line(pb.LineKind_LINE_KIND_ADDITION, 0, 1, "x")}}}},109 {Status: pb.FileStatus_FILE_STATUS_ADDED, NewPath: "a.go", Additions: 1, Hunks: []*pb.Hunk{{Lines: []*pb.Line{line(pb.LineKind_LINE_KIND_ADDITION, 0, 1, "x")}}}},
104 {Status: pb.FileStatus_FILE_STATUS_DELETED, OldPath: "b.go", Deletions: 1},110 {Status: pb.FileStatus_FILE_STATUS_DELETED, OldPath: "b.go", Deletions: 1},
@@ -117,6 +123,7 @@ func TestDiffFilesPaths(t *testing.T) {
117 // A code cell holds exactly the text: escaped, with markers, and nothing123 // A code cell holds exactly the text: escaped, with markers, and nothing
118 // added between the pieces (the cell keeps its white space).124 // added between the pieces (the cell keeps its white space).
119 func TestCodeLine(t *testing.T) {125 func TestCodeLine(t *testing.T) {
126+ t.Parallel()
120 var b strings.Builder127 var b strings.Builder
121 ln := diffLine(&pb.Line{Kind: pb.LineKind_LINE_KIND_ADDITION, NewNumber: 1, Text: "a <b> \u202e c d", NoNewlineAtEof: true})128 ln := diffLine(&pb.Line{Kind: pb.LineKind_LINE_KIND_ADDITION, NewNumber: 1, Text: "a <b> \u202e c d", NoNewlineAtEof: true})
122 require.NoError(t, codeLine(ln).Render(context.Background(), &b))129 require.NoError(t, codeLine(ln).Render(context.Background(), &b))
M internal/adapters/web/errors_test.go +2 -0
@@ -51,6 +51,7 @@ func serve(t *testing.T, err error, path string) (*httptest.ResponseRecorder, st
5151 // Errors of the API: not found (and an invalid name) are the 404 page, any
5252 // other is the 500 page with an id that the log carries.
5353 func TestErrorPages(t *testing.T) {
54+ t.Parallel()
5455 rec, logs := serve(t, connect.NewError(connect.CodeNotFound, errors.New("repository not found")), "/gone")
5556 assert.Equal(t, http.StatusNotFound, rec.Code)
5657 assert.Contains(t, rec.Body.String(), "not found")
@@ -76,6 +77,7 @@ func TestErrorPages(t *testing.T) {
7677 }
7778
7879 func TestNewValidates(t *testing.T) {
80+ t.Parallel()
7981 _, err := New(Config{PublicURL: "https://forge.example"})
8082 assert.Error(t, err, "clients are required")
8183 git := ermiteconnect.NewGitServiceClient(http.DefaultClient, "http://x.invalid")
@@ -51,6 +51,7 @@ func serve(t *testing.T, err error, path string) (*httptest.ResponseRecorder, st
51 // Errors of the API: not found (and an invalid name) are the 404 page, any51 // Errors of the API: not found (and an invalid name) are the 404 page, any
52 // other is the 500 page with an id that the log carries.52 // other is the 500 page with an id that the log carries.
53 func TestErrorPages(t *testing.T) {53 func TestErrorPages(t *testing.T) {
54+ t.Parallel()
54 rec, logs := serve(t, connect.NewError(connect.CodeNotFound, errors.New("repository not found")), "/gone")55 rec, logs := serve(t, connect.NewError(connect.CodeNotFound, errors.New("repository not found")), "/gone")
55 assert.Equal(t, http.StatusNotFound, rec.Code)56 assert.Equal(t, http.StatusNotFound, rec.Code)
56 assert.Contains(t, rec.Body.String(), "not found")57 assert.Contains(t, rec.Body.String(), "not found")
@@ -76,6 +77,7 @@ func TestErrorPages(t *testing.T) {
76 }77 }
77 78
78 func TestNewValidates(t *testing.T) {79 func TestNewValidates(t *testing.T) {
80+ t.Parallel()
79 _, err := New(Config{PublicURL: "https://forge.example"})81 _, err := New(Config{PublicURL: "https://forge.example"})
80 assert.Error(t, err, "clients are required")82 assert.Error(t, err, "clients are required")
81 git := ermiteconnect.NewGitServiceClient(http.DefaultClient, "http://x.invalid")83 git := ermiteconnect.NewGitServiceClient(http.DefaultClient, "http://x.invalid")
M internal/adapters/web/events_test.go +13 -0
@@ -23,6 +23,7 @@ import (
2323 )
2424
2525 func TestFormatEvent(t *testing.T) {
26+ t.Parallel()
2627 assert.Equal(t, "event: home-list\nid: b-1\ndata: <tr id=\"x\">\ndata: <td>1</td>\ndata: </tr>\n\n",
2728 formatEvent("b-1", "home-list", "<tr id=\"x\">\n <td>1</td>\n</tr>", true), "one data line per line")
2829 assert.Equal(t, "event: gap\ndata: gap\n\n", formatEvent("", "gap", "gap", true))
@@ -32,6 +33,7 @@ func TestFormatEvent(t *testing.T) {
3233 }
3334
3435 func TestParseTopic(t *testing.T) {
36+ t.Parallel()
3537 for in, want := range map[string]string{"home": "", "repo:x": "x", "repo:a.b-c": "a.b-c"} {
3638 repo, ok := parseTopic(in)
3739 assert.True(t, ok, in)
@@ -44,11 +46,13 @@ func TestParseTopic(t *testing.T) {
4446 }
4547
4648 func TestLiveURL(t *testing.T) {
49+ t.Parallel()
4750 assert.Equal(t, "/-/events?topic=home", liveURL("home", ""))
4851 assert.Equal(t, "/-/events?topic=repo%3Ax&view=refs", liveURL("repo:x", "view=refs"))
4952 }
5053
5154 func TestNewCommitsText(t *testing.T) {
55+ t.Parallel()
5256 assert.Equal(t, "1 new commit — reload", newCommits("1", 1))
5357 assert.Equal(t, "3 new commits — reload", newCommits("3", 3))
5458 assert.Equal(t, "100+ new commits — reload", newCommits("100+", 2))
@@ -150,6 +154,7 @@ func open(t *testing.T, ts *httptest.Server, query string, hdr map[string]string
150154 // An idle stream gets a comment every heartbeat, and the write timeout of the
151155 // server does not end it.
152156 func TestEventsHeartbeatSurvivesWriteTimeout(t *testing.T) {
157+ t.Parallel()
153158 f := &fakeEvents{release: make(chan struct{})}
154159 ts := eventsServer(t, f, 40*time.Millisecond, 100*time.Millisecond)
155160 resp, cancel := open(t, ts, "topic=home", nil)
@@ -173,6 +178,7 @@ func TestEventsHeartbeatSurvivesWriteTimeout(t *testing.T) {
173178 // The id of the event, its name and the lines of its data come through; the
174179 // Last-Event-ID header is the resume point; a Gap becomes a gap event.
175180 func TestEventsStreamsEventsAndResumes(t *testing.T) {
181+ t.Parallel()
176182 f := &fakeEvents{release: make(chan struct{}), events: []*pb.Event{
177183 {Id: "b-7", Kind: &pb.Event_Pushed{Pushed: &pb.Pushed{Repo: "x"}}},
178184 {Kind: &pb.Event_Gap{Gap: &pb.Gap{}}},
@@ -195,6 +201,7 @@ func TestEventsStreamsEventsAndResumes(t *testing.T) {
195201
196202 // Too many watchers is a 503 the browser can retry, not a stream.
197203 func TestEventsResourceExhausted(t *testing.T) {
204+ t.Parallel()
198205 f := &fakeEvents{err: connect.NewError(connect.CodeResourceExhausted, errors.New("too many watchers"))}
199206 ts := eventsServer(t, f, time.Hour, 0)
200207 resp, _ := open(t, ts, "topic=home", nil)
@@ -213,6 +220,7 @@ func TestEventsResourceExhausted(t *testing.T) {
213220 // The handler returns, and the watch it opened ends, when the visitor goes
214221 // away or the API ends the stream.
215222 func TestEventsNoLeak(t *testing.T) {
223+ t.Parallel()
216224 f := &fakeEvents{release: make(chan struct{})}
217225 ts := eventsServer(t, f, time.Hour, 0)
218226
@@ -237,6 +245,7 @@ func TestEventsNoLeak(t *testing.T) {
237245 }
238246
239247 func TestEventsOffWithoutClient(t *testing.T) {
248+ t.Parallel()
240249 srv, err := New(Config{
241250 Repos: ermiteconnect.NewRepoServiceClient(http.DefaultClient, "http://x.invalid"),
242251 Git: ermiteconnect.NewGitServiceClient(http.DefaultClient, "http://x.invalid"),
@@ -252,6 +261,7 @@ func TestEventsOffWithoutClient(t *testing.T) {
252261 // show it load themselves again: the summary and the refs page, whose rows
253262 // and marks depend on it. The commits page shows a branch, not the default.
254263 func TestRepoUpdatedReloadsSummaryAndRefs(t *testing.T) {
264+ t.Parallel()
255265 api := http.NewServeMux()
256266 api.Handle(ermiteconnect.NewRepoServiceHandler(repoOK{}))
257267 backend := httptest.NewServer(api)
@@ -298,6 +308,7 @@ func (r *revocable) Verify(ctx context.Context, tok string) (context.Context, bo
298308 // only on the heartbeat: after the session ended, the next event is not sent
299309 // and the stream ends.
300310 func TestEventsOwnerStreamEndsBeforeNextEvent(t *testing.T) {
311+ t.Parallel()
301312 f := &fakeEvents{release: make(chan struct{}), push: make(chan *pb.Event)}
302313 sess := &revocable{fakeSessions: fakeSessions{valid: "tok"}}
303314 ts := eventsServerWith(t, f, time.Hour, 0, sess) // no heartbeat in the test
@@ -340,6 +351,7 @@ func TestEventsOwnerStreamEndsBeforeNextEvent(t *testing.T) {
340351
341352 // A visitor's stream does not depend on sessions: events keep flowing.
342353 func TestEventsVisitorStreamIgnoresSessions(t *testing.T) {
354+ t.Parallel()
343355 f := &fakeEvents{release: make(chan struct{}), push: make(chan *pb.Event)}
344356 sess := &revocable{fakeSessions: fakeSessions{valid: "tok"}}
345357 sess.ended.Store(true)
@@ -361,6 +373,7 @@ func TestEventsVisitorStreamIgnoresSessions(t *testing.T) {
361373 // GO-006: a stream opened with an invalid cookie is anonymous, and its headers
362374 // say the answer must not be stored and varies on the cookie.
363375 func TestEventsInvalidCookieStream(t *testing.T) {
376+ t.Parallel()
364377 f := &fakeEvents{release: make(chan struct{})}
365378 ts := eventsServerWith(t, f, time.Hour, 0, &fakeSessions{valid: "tok"})
366379 resp, _ := open(t, ts, "topic=home", map[string]string{"Cookie": CookieName + "=bad"})
@@ -23,6 +23,7 @@ import (
23 )23 )
24 24
25 func TestFormatEvent(t *testing.T) {25 func TestFormatEvent(t *testing.T) {
26+ t.Parallel()
26 assert.Equal(t, "event: home-list\nid: b-1\ndata: <tr id=\"x\">\ndata: <td>1</td>\ndata: </tr>\n\n",27 assert.Equal(t, "event: home-list\nid: b-1\ndata: <tr id=\"x\">\ndata: <td>1</td>\ndata: </tr>\n\n",
27 formatEvent("b-1", "home-list", "<tr id=\"x\">\n <td>1</td>\n</tr>", true), "one data line per line")28 formatEvent("b-1", "home-list", "<tr id=\"x\">\n <td>1</td>\n</tr>", true), "one data line per line")
28 assert.Equal(t, "event: gap\ndata: gap\n\n", formatEvent("", "gap", "gap", true))29 assert.Equal(t, "event: gap\ndata: gap\n\n", formatEvent("", "gap", "gap", true))
@@ -32,6 +33,7 @@ func TestFormatEvent(t *testing.T) {
32 }33 }
33 34
34 func TestParseTopic(t *testing.T) {35 func TestParseTopic(t *testing.T) {
36+ t.Parallel()
35 for in, want := range map[string]string{"home": "", "repo:x": "x", "repo:a.b-c": "a.b-c"} {37 for in, want := range map[string]string{"home": "", "repo:x": "x", "repo:a.b-c": "a.b-c"} {
36 repo, ok := parseTopic(in)38 repo, ok := parseTopic(in)
37 assert.True(t, ok, in)39 assert.True(t, ok, in)
@@ -44,11 +46,13 @@ func TestParseTopic(t *testing.T) {
44 }46 }
45 47
46 func TestLiveURL(t *testing.T) {48 func TestLiveURL(t *testing.T) {
49+ t.Parallel()
47 assert.Equal(t, "/-/events?topic=home", liveURL("home", ""))50 assert.Equal(t, "/-/events?topic=home", liveURL("home", ""))
48 assert.Equal(t, "/-/events?topic=repo%3Ax&view=refs", liveURL("repo:x", "view=refs"))51 assert.Equal(t, "/-/events?topic=repo%3Ax&view=refs", liveURL("repo:x", "view=refs"))
49 }52 }
50 53
51 func TestNewCommitsText(t *testing.T) {54 func TestNewCommitsText(t *testing.T) {
55+ t.Parallel()
52 assert.Equal(t, "1 new commit — reload", newCommits("1", 1))56 assert.Equal(t, "1 new commit — reload", newCommits("1", 1))
53 assert.Equal(t, "3 new commits — reload", newCommits("3", 3))57 assert.Equal(t, "3 new commits — reload", newCommits("3", 3))
54 assert.Equal(t, "100+ new commits — reload", newCommits("100+", 2))58 assert.Equal(t, "100+ new commits — reload", newCommits("100+", 2))
@@ -150,6 +154,7 @@ func open(t *testing.T, ts *httptest.Server, query string, hdr map[string]string
150 // An idle stream gets a comment every heartbeat, and the write timeout of the154 // An idle stream gets a comment every heartbeat, and the write timeout of the
151 // server does not end it.155 // server does not end it.
152 func TestEventsHeartbeatSurvivesWriteTimeout(t *testing.T) {156 func TestEventsHeartbeatSurvivesWriteTimeout(t *testing.T) {
157+ t.Parallel()
153 f := &fakeEvents{release: make(chan struct{})}158 f := &fakeEvents{release: make(chan struct{})}
154 ts := eventsServer(t, f, 40*time.Millisecond, 100*time.Millisecond)159 ts := eventsServer(t, f, 40*time.Millisecond, 100*time.Millisecond)
155 resp, cancel := open(t, ts, "topic=home", nil)160 resp, cancel := open(t, ts, "topic=home", nil)
@@ -173,6 +178,7 @@ func TestEventsHeartbeatSurvivesWriteTimeout(t *testing.T) {
173 // The id of the event, its name and the lines of its data come through; the178 // The id of the event, its name and the lines of its data come through; the
174 // Last-Event-ID header is the resume point; a Gap becomes a gap event.179 // Last-Event-ID header is the resume point; a Gap becomes a gap event.
175 func TestEventsStreamsEventsAndResumes(t *testing.T) {180 func TestEventsStreamsEventsAndResumes(t *testing.T) {
181+ t.Parallel()
176 f := &fakeEvents{release: make(chan struct{}), events: []*pb.Event{182 f := &fakeEvents{release: make(chan struct{}), events: []*pb.Event{
177 {Id: "b-7", Kind: &pb.Event_Pushed{Pushed: &pb.Pushed{Repo: "x"}}},183 {Id: "b-7", Kind: &pb.Event_Pushed{Pushed: &pb.Pushed{Repo: "x"}}},
178 {Kind: &pb.Event_Gap{Gap: &pb.Gap{}}},184 {Kind: &pb.Event_Gap{Gap: &pb.Gap{}}},
@@ -195,6 +201,7 @@ func TestEventsStreamsEventsAndResumes(t *testing.T) {
195 201
196 // Too many watchers is a 503 the browser can retry, not a stream.202 // Too many watchers is a 503 the browser can retry, not a stream.
197 func TestEventsResourceExhausted(t *testing.T) {203 func TestEventsResourceExhausted(t *testing.T) {
204+ t.Parallel()
198 f := &fakeEvents{err: connect.NewError(connect.CodeResourceExhausted, errors.New("too many watchers"))}205 f := &fakeEvents{err: connect.NewError(connect.CodeResourceExhausted, errors.New("too many watchers"))}
199 ts := eventsServer(t, f, time.Hour, 0)206 ts := eventsServer(t, f, time.Hour, 0)
200 resp, _ := open(t, ts, "topic=home", nil)207 resp, _ := open(t, ts, "topic=home", nil)
@@ -213,6 +220,7 @@ func TestEventsResourceExhausted(t *testing.T) {
213 // The handler returns, and the watch it opened ends, when the visitor goes220 // The handler returns, and the watch it opened ends, when the visitor goes
214 // away or the API ends the stream.221 // away or the API ends the stream.
215 func TestEventsNoLeak(t *testing.T) {222 func TestEventsNoLeak(t *testing.T) {
223+ t.Parallel()
216 f := &fakeEvents{release: make(chan struct{})}224 f := &fakeEvents{release: make(chan struct{})}
217 ts := eventsServer(t, f, time.Hour, 0)225 ts := eventsServer(t, f, time.Hour, 0)
218 226
@@ -237,6 +245,7 @@ func TestEventsNoLeak(t *testing.T) {
237 }245 }
238 246
239 func TestEventsOffWithoutClient(t *testing.T) {247 func TestEventsOffWithoutClient(t *testing.T) {
248+ t.Parallel()
240 srv, err := New(Config{249 srv, err := New(Config{
241 Repos: ermiteconnect.NewRepoServiceClient(http.DefaultClient, "http://x.invalid"),250 Repos: ermiteconnect.NewRepoServiceClient(http.DefaultClient, "http://x.invalid"),
242 Git: ermiteconnect.NewGitServiceClient(http.DefaultClient, "http://x.invalid"),251 Git: ermiteconnect.NewGitServiceClient(http.DefaultClient, "http://x.invalid"),
@@ -252,6 +261,7 @@ func TestEventsOffWithoutClient(t *testing.T) {
252 // show it load themselves again: the summary and the refs page, whose rows261 // show it load themselves again: the summary and the refs page, whose rows
253 // and marks depend on it. The commits page shows a branch, not the default.262 // and marks depend on it. The commits page shows a branch, not the default.
254 func TestRepoUpdatedReloadsSummaryAndRefs(t *testing.T) {263 func TestRepoUpdatedReloadsSummaryAndRefs(t *testing.T) {
264+ t.Parallel()
255 api := http.NewServeMux()265 api := http.NewServeMux()
256 api.Handle(ermiteconnect.NewRepoServiceHandler(repoOK{}))266 api.Handle(ermiteconnect.NewRepoServiceHandler(repoOK{}))
257 backend := httptest.NewServer(api)267 backend := httptest.NewServer(api)
@@ -298,6 +308,7 @@ func (r *revocable) Verify(ctx context.Context, tok string) (context.Context, bo
298 // only on the heartbeat: after the session ended, the next event is not sent308 // only on the heartbeat: after the session ended, the next event is not sent
299 // and the stream ends.309 // and the stream ends.
300 func TestEventsOwnerStreamEndsBeforeNextEvent(t *testing.T) {310 func TestEventsOwnerStreamEndsBeforeNextEvent(t *testing.T) {
311+ t.Parallel()
301 f := &fakeEvents{release: make(chan struct{}), push: make(chan *pb.Event)}312 f := &fakeEvents{release: make(chan struct{}), push: make(chan *pb.Event)}
302 sess := &revocable{fakeSessions: fakeSessions{valid: "tok"}}313 sess := &revocable{fakeSessions: fakeSessions{valid: "tok"}}
303 ts := eventsServerWith(t, f, time.Hour, 0, sess) // no heartbeat in the test314 ts := eventsServerWith(t, f, time.Hour, 0, sess) // no heartbeat in the test
@@ -340,6 +351,7 @@ func TestEventsOwnerStreamEndsBeforeNextEvent(t *testing.T) {
340 351
341 // A visitor's stream does not depend on sessions: events keep flowing.352 // A visitor's stream does not depend on sessions: events keep flowing.
342 func TestEventsVisitorStreamIgnoresSessions(t *testing.T) {353 func TestEventsVisitorStreamIgnoresSessions(t *testing.T) {
354+ t.Parallel()
343 f := &fakeEvents{release: make(chan struct{}), push: make(chan *pb.Event)}355 f := &fakeEvents{release: make(chan struct{}), push: make(chan *pb.Event)}
344 sess := &revocable{fakeSessions: fakeSessions{valid: "tok"}}356 sess := &revocable{fakeSessions: fakeSessions{valid: "tok"}}
345 sess.ended.Store(true)357 sess.ended.Store(true)
@@ -361,6 +373,7 @@ func TestEventsVisitorStreamIgnoresSessions(t *testing.T) {
361 // GO-006: a stream opened with an invalid cookie is anonymous, and its headers373 // GO-006: a stream opened with an invalid cookie is anonymous, and its headers
362 // say the answer must not be stored and varies on the cookie.374 // say the answer must not be stored and varies on the cookie.
363 func TestEventsInvalidCookieStream(t *testing.T) {375 func TestEventsInvalidCookieStream(t *testing.T) {
376+ t.Parallel()
364 f := &fakeEvents{release: make(chan struct{})}377 f := &fakeEvents{release: make(chan struct{})}
365 ts := eventsServerWith(t, f, time.Hour, 0, &fakeSessions{valid: "tok"})378 ts := eventsServerWith(t, f, time.Hour, 0, &fakeSessions{valid: "tok"})
366 resp, _ := open(t, ts, "topic=home", map[string]string{"Cookie": CookieName + "=bad"})379 resp, _ := open(t, ts, "topic=home", map[string]string{"Cookie": CookieName + "=bad"})
M internal/adapters/web/fanout_test.go +7 -0
@@ -22,6 +22,7 @@ import (
2222 func frag(s string) []fragment { return []fragment{{event: "refs-list", html: s}} }
2323
2424 func TestFanoutBuildsOncePerKey(t *testing.T) {
25+ t.Parallel()
2526 srvA, srvB := &Server{}, &Server{}
2627 base := fanKey{srv: srvA, repo: "x", event: "b-1"}
2728 for _, tt := range []struct {
@@ -37,6 +38,7 @@ func TestFanoutBuildsOncePerKey(t *testing.T) {
3738 {"no event id: nothing to share by", []fanKey{{srv: srvA, repo: "x"}, {srv: srvA, repo: "x"}}, 2},
3839 } {
3940 t.Run(tt.name, func(t *testing.T) {
41+ t.Parallel()
4042 var f fanout
4143 var built atomic.Int32
4244 for _, k := range tt.keys {
@@ -54,6 +56,7 @@ func TestFanoutBuildsOncePerKey(t *testing.T) {
5456 }
5557
5658 func TestFanoutConcurrentWatchersShareOneBuild(t *testing.T) {
59+ t.Parallel()
5760 var f fanout
5861 var built atomic.Int32
5962 release := make(chan struct{})
@@ -80,6 +83,7 @@ func TestFanoutConcurrentWatchersShareOneBuild(t *testing.T) {
8083 }
8184
8285 func TestFanoutFailureIsNotKept(t *testing.T) {
86+ t.Parallel()
8387 var f fanout
8488 key := fanKey{srv: &Server{}, repo: "x", event: "b-1"}
8589 _, err := f.do(context.Background(), key, func() ([]fragment, error) { return nil, errors.New("boom") })
@@ -90,6 +94,7 @@ func TestFanoutFailureIsNotKept(t *testing.T) {
9094 }
9195
9296 func TestFanoutWaiterStopsWithItsContext(t *testing.T) {
97+ t.Parallel()
9398 var f fanout
9499 key := fanKey{srv: &Server{}, repo: "x", event: "b-1"}
95100 started, release := make(chan struct{}), make(chan struct{})
@@ -109,6 +114,7 @@ func TestFanoutWaiterStopsWithItsContext(t *testing.T) {
109114 }
110115
111116 func TestFanoutIsBounded(t *testing.T) {
117+ t.Parallel()
112118 var f fanout
113119 srv := &Server{}
114120 for i := range fanMax + 10 {
@@ -202,6 +208,7 @@ func TestLiveRefsSharesAPushBetweenWatchers(t *testing.T) {
202208
203209 // The refs are asked in one page of up to maxRefs.
204210 func TestLoadRepoAsksTheRefsInOnePage(t *testing.T) {
211+ t.Parallel()
205212 api := &refsAPI{}
206213 s := refsServer(t, api)
207214 d, err := s.loadRepo(context.Background(), "x")
@@ -22,6 +22,7 @@ import (
22 func frag(s string) []fragment { return []fragment{{event: "refs-list", html: s}} }22 func frag(s string) []fragment { return []fragment{{event: "refs-list", html: s}} }
23 23
24 func TestFanoutBuildsOncePerKey(t *testing.T) {24 func TestFanoutBuildsOncePerKey(t *testing.T) {
25+ t.Parallel()
25 srvA, srvB := &Server{}, &Server{}26 srvA, srvB := &Server{}, &Server{}
26 base := fanKey{srv: srvA, repo: "x", event: "b-1"}27 base := fanKey{srv: srvA, repo: "x", event: "b-1"}
27 for _, tt := range []struct {28 for _, tt := range []struct {
@@ -37,6 +38,7 @@ func TestFanoutBuildsOncePerKey(t *testing.T) {
37 {"no event id: nothing to share by", []fanKey{{srv: srvA, repo: "x"}, {srv: srvA, repo: "x"}}, 2},38 {"no event id: nothing to share by", []fanKey{{srv: srvA, repo: "x"}, {srv: srvA, repo: "x"}}, 2},
38 } {39 } {
39 t.Run(tt.name, func(t *testing.T) {40 t.Run(tt.name, func(t *testing.T) {
41+ t.Parallel()
40 var f fanout42 var f fanout
41 var built atomic.Int3243 var built atomic.Int32
42 for _, k := range tt.keys {44 for _, k := range tt.keys {
@@ -54,6 +56,7 @@ func TestFanoutBuildsOncePerKey(t *testing.T) {
54 }56 }
55 57
56 func TestFanoutConcurrentWatchersShareOneBuild(t *testing.T) {58 func TestFanoutConcurrentWatchersShareOneBuild(t *testing.T) {
59+ t.Parallel()
57 var f fanout60 var f fanout
58 var built atomic.Int3261 var built atomic.Int32
59 release := make(chan struct{})62 release := make(chan struct{})
@@ -80,6 +83,7 @@ func TestFanoutConcurrentWatchersShareOneBuild(t *testing.T) {
80 }83 }
81 84
82 func TestFanoutFailureIsNotKept(t *testing.T) {85 func TestFanoutFailureIsNotKept(t *testing.T) {
86+ t.Parallel()
83 var f fanout87 var f fanout
84 key := fanKey{srv: &Server{}, repo: "x", event: "b-1"}88 key := fanKey{srv: &Server{}, repo: "x", event: "b-1"}
85 _, err := f.do(context.Background(), key, func() ([]fragment, error) { return nil, errors.New("boom") })89 _, err := f.do(context.Background(), key, func() ([]fragment, error) { return nil, errors.New("boom") })
@@ -90,6 +94,7 @@ func TestFanoutFailureIsNotKept(t *testing.T) {
90 }94 }
91 95
92 func TestFanoutWaiterStopsWithItsContext(t *testing.T) {96 func TestFanoutWaiterStopsWithItsContext(t *testing.T) {
97+ t.Parallel()
93 var f fanout98 var f fanout
94 key := fanKey{srv: &Server{}, repo: "x", event: "b-1"}99 key := fanKey{srv: &Server{}, repo: "x", event: "b-1"}
95 started, release := make(chan struct{}), make(chan struct{})100 started, release := make(chan struct{}), make(chan struct{})
@@ -109,6 +114,7 @@ func TestFanoutWaiterStopsWithItsContext(t *testing.T) {
109 }114 }
110 115
111 func TestFanoutIsBounded(t *testing.T) {116 func TestFanoutIsBounded(t *testing.T) {
117+ t.Parallel()
112 var f fanout118 var f fanout
113 srv := &Server{}119 srv := &Server{}
114 for i := range fanMax + 10 {120 for i := range fanMax + 10 {
@@ -202,6 +208,7 @@ func TestLiveRefsSharesAPushBetweenWatchers(t *testing.T) {
202 208
203 // The refs are asked in one page of up to maxRefs.209 // The refs are asked in one page of up to maxRefs.
204 func TestLoadRepoAsksTheRefsInOnePage(t *testing.T) {210 func TestLoadRepoAsksTheRefsInOnePage(t *testing.T) {
211+ t.Parallel()
205 api := &refsAPI{}212 api := &refsAPI{}
206 s := refsServer(t, api)213 s := refsServer(t, api)
207 d, err := s.loadRepo(context.Background(), "x")214 d, err := s.loadRepo(context.Background(), "x")
M internal/adapters/web/fragments_test.go +4 -0
@@ -24,6 +24,7 @@ func renderToString(t *testing.T, c templ.Component) string {
2424 // (same id, role and marker), so that ermiteLive can replace the children and
2525 // leave the region where a screen reader watches it.
2626 func TestLiveSlotsAreStableStatusRegions(t *testing.T) {
27+ t.Parallel()
2728 for _, tt := range []struct {
2829 name string
2930 c templ.Component
@@ -36,6 +37,7 @@ func TestLiveSlotsAreStableStatusRegions(t *testing.T) {
3637 {"mr notice", MRNotice("a comment was added — reload", "/x/merge-requests/1"), "mr-notice", "a comment was added — reload"},
3738 } {
3839 t.Run(tt.name, func(t *testing.T) {
40+ t.Parallel()
3941 got := renderToString(t, tt.c)
4042 assert.True(t, strings.HasPrefix(got, `<div id="`+tt.id+`" class="live-slot" role="status" data-slot>`), got)
4143 if tt.text == "" {
@@ -51,6 +53,7 @@ func TestLiveSlotsAreStableStatusRegions(t *testing.T) {
5153 // The header's refs button is a link to the refs page, which the script turns
5254 // into the dialog; the filters and the buttons that need scripts are marked.
5355 func TestShellDegradesWithoutScripts(t *testing.T) {
56+ t.Parallel()
5457 l := Layout{Repo: "demo", Ref: "main", Short: "abc1234", Assets: Assets{}}
5558 got := renderToString(t, Header(l))
5659 assert.Contains(t, got, `<a class="btn ref" href="/demo/refs" data-fire="refs"`)
@@ -63,6 +66,7 @@ func TestShellDegradesWithoutScripts(t *testing.T) {
6366 // Data reaches the scripts through data-* attributes, never spliced into an
6467 // attribute that is evaluated.
6568 func TestDiffControlsPassDataThroughAttributes(t *testing.T) {
69+ t.Parallel()
6670 files := renderToString(t, FileList([]DiffFile{{ID: "f1'x", Path: "a.go", Status: "M"}}))
6771 assert.NotContains(t, files, ":aria-current", "the file in view is marked by ermite.js")
6872 assert.NotContains(t, files, "f1'x'", "no id is spliced into a script")
@@ -24,6 +24,7 @@ func renderToString(t *testing.T, c templ.Component) string {
24 // (same id, role and marker), so that ermiteLive can replace the children and24 // (same id, role and marker), so that ermiteLive can replace the children and
25 // leave the region where a screen reader watches it.25 // leave the region where a screen reader watches it.
26 func TestLiveSlotsAreStableStatusRegions(t *testing.T) {26 func TestLiveSlotsAreStableStatusRegions(t *testing.T) {
27+ t.Parallel()
27 for _, tt := range []struct {28 for _, tt := range []struct {
28 name string29 name string
29 c templ.Component30 c templ.Component
@@ -36,6 +37,7 @@ func TestLiveSlotsAreStableStatusRegions(t *testing.T) {
36 {"mr notice", MRNotice("a comment was added — reload", "/x/merge-requests/1"), "mr-notice", "a comment was added — reload"},37 {"mr notice", MRNotice("a comment was added — reload", "/x/merge-requests/1"), "mr-notice", "a comment was added — reload"},
37 } {38 } {
38 t.Run(tt.name, func(t *testing.T) {39 t.Run(tt.name, func(t *testing.T) {
40+ t.Parallel()
39 got := renderToString(t, tt.c)41 got := renderToString(t, tt.c)
40 assert.True(t, strings.HasPrefix(got, `<div id="`+tt.id+`" class="live-slot" role="status" data-slot>`), got)42 assert.True(t, strings.HasPrefix(got, `<div id="`+tt.id+`" class="live-slot" role="status" data-slot>`), got)
41 if tt.text == "" {43 if tt.text == "" {
@@ -51,6 +53,7 @@ func TestLiveSlotsAreStableStatusRegions(t *testing.T) {
51 // The header's refs button is a link to the refs page, which the script turns53 // The header's refs button is a link to the refs page, which the script turns
52 // into the dialog; the filters and the buttons that need scripts are marked.54 // into the dialog; the filters and the buttons that need scripts are marked.
53 func TestShellDegradesWithoutScripts(t *testing.T) {55 func TestShellDegradesWithoutScripts(t *testing.T) {
56+ t.Parallel()
54 l := Layout{Repo: "demo", Ref: "main", Short: "abc1234", Assets: Assets{}}57 l := Layout{Repo: "demo", Ref: "main", Short: "abc1234", Assets: Assets{}}
55 got := renderToString(t, Header(l))58 got := renderToString(t, Header(l))
56 assert.Contains(t, got, `<a class="btn ref" href="/demo/refs" data-fire="refs"`)59 assert.Contains(t, got, `<a class="btn ref" href="/demo/refs" data-fire="refs"`)
@@ -63,6 +66,7 @@ func TestShellDegradesWithoutScripts(t *testing.T) {
63 // Data reaches the scripts through data-* attributes, never spliced into an66 // Data reaches the scripts through data-* attributes, never spliced into an
64 // attribute that is evaluated.67 // attribute that is evaluated.
65 func TestDiffControlsPassDataThroughAttributes(t *testing.T) {68 func TestDiffControlsPassDataThroughAttributes(t *testing.T) {
69+ t.Parallel()
66 files := renderToString(t, FileList([]DiffFile{{ID: "f1'x", Path: "a.go", Status: "M"}}))70 files := renderToString(t, FileList([]DiffFile{{ID: "f1'x", Path: "a.go", Status: "M"}}))
67 assert.NotContains(t, files, ":aria-current", "the file in view is marked by ermite.js")71 assert.NotContains(t, files, ":aria-current", "the file in view is marked by ermite.js")
68 assert.NotContains(t, files, "f1'x'", "no id is spliced into a script")72 assert.NotContains(t, files, "f1'x'", "no id is spliced into a script")
M internal/adapters/web/highlight_test.go +12 -0
@@ -15,6 +15,7 @@ import (
1515 )
1616
1717 func TestHighlightLines(t *testing.T) {
18+ t.Parallel()
1819 v := highlight(context.Background(), "a.go", "package main\n\nfunc main() {}\n")
1920 assert.Equal(t, 3, v.lines)
2021 assert.Equal(t, "Go", v.lang)
@@ -30,6 +31,7 @@ func TestHighlightLines(t *testing.T) {
3031 }
3132
3233 func TestHighlightFallbackIsPlainAndEscaped(t *testing.T) {
34+ t.Parallel()
3335 v := highlight(context.Background(), "unknown.zzz", "<script>alert(1)</script> & \"q\"\n")
3436 assert.Equal(t, "", v.lang)
3537 h := string(v.html)
@@ -41,6 +43,7 @@ func TestHighlightFallbackIsPlainAndEscaped(t *testing.T) {
4143 }
4244
4345 func TestHighlightMarksInvisibleCharacters(t *testing.T) {
46+ t.Parallel()
4447 for r, cp := range map[rune]string{
4548 0x202E: "U+202E", 0x2066: "U+2066", 0x2069: "U+2069", 0x200B: "U+200B", 0xFEFF: "U+FEFF",
4649 0x0000: "U+0000", 0x001B: "U+001B", 0x2028: "U+2028", 0x3164: "U+3164", 0x1F: "U+001F",
@@ -63,6 +66,7 @@ func TestHighlightMarksInvisibleCharacters(t *testing.T) {
6366 // The lexers do not lose or add characters: the text of the lines is the
6467 // source, whatever the language.
6568 func TestHighlightKeepsTheSource(t *testing.T) {
69+ t.Parallel()
6670 srcs := map[string]string{
6771 "a.go": "package a\n\nvar s = `multi\nline`\n// end",
6872 "b.py": "def f():\n '''doc\n string'''\n return 1\n",
@@ -93,6 +97,7 @@ func TestHighlightKeepsTheSource(t *testing.T) {
9397 }
9498
9599 func TestFirstLinesAndCount(t *testing.T) {
100+ t.Parallel()
96101 assert.Equal(t, "a\nb\n", firstLines("a\nb\nc\n", 2))
97102 assert.Equal(t, "a\nb", firstLines("a\nb", 5))
98103 assert.Equal(t, 3, countLines([]byte("a\nb\nc")))
@@ -101,6 +106,7 @@ func TestFirstLinesAndCount(t *testing.T) {
101106 }
102107
103108 func TestHighlightCtxCancelled(t *testing.T) {
109+ t.Parallel()
104110 ctx, cancel := context.WithCancel(context.Background())
105111 cancel()
106112 v := highlightCtx(ctx, "a.go", "package a\n")
@@ -116,12 +122,14 @@ func (panicLexer) Tokenise(*chroma.TokeniseOptions, string) (chroma.Iterator, er
116122 }
117123
118124 func TestTokeniseRecoversFromALexerPanic(t *testing.T) {
125+ t.Parallel()
119126 toks, ok := tokenise(context.Background(), panicLexer{}, "x\n")
120127 assert.False(t, ok)
121128 assert.Nil(t, toks)
122129 }
123130
124131 func TestSniffImage(t *testing.T) {
132+ t.Parallel()
125133 assert.Equal(t, "image/png", sniffImage([]byte("\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR")))
126134 assert.Equal(t, "image/gif", sniffImage([]byte("GIF89a....")))
127135 assert.Equal(t, "image/jpeg", sniffImage([]byte("\xff\xd8\xff\xe0\x00\x10JFIF")))
@@ -132,6 +140,7 @@ func TestSniffImage(t *testing.T) {
132140 }
133141
134142 func TestLocate(t *testing.T) {
143+ t.Parallel()
135144 b, tag := pb.RefKind_REF_KIND_BRANCH, pb.RefKind_REF_KIND_TAG
136145 d := &repoData{
137146 branches: []*pb.Ref{ref(b, "main"), ref(b, "feature/x")},
@@ -169,6 +178,7 @@ func TestLocate(t *testing.T) {
169178 }
170179
171180 func TestFileCrumbs(t *testing.T) {
181+ t.Parallel()
172182 cs := pathCrumbs("demo", "blob", target{name: "feature/x", path: "src/a.go"})
173183 require.Len(t, cs, 5)
174184 assert.Equal(t, "/demo/tree/feature/x/src", cs[3].Href)
@@ -220,6 +230,7 @@ func normSpace(s string) string {
220230 }
221231
222232 func TestTokensMatch(t *testing.T) {
233+ t.Parallel()
223234 toks := func(vs ...string) []chroma.Token {
224235 var out []chroma.Token
225236 for _, v := range vs {
@@ -247,6 +258,7 @@ func TestTokensMatch(t *testing.T) {
247258 {"unicode", toks("é", "日本"), "é日本", true},
248259 } {
249260 t.Run(tt.name, func(t *testing.T) {
261+ t.Parallel()
250262 assert.Equal(t, tt.want, tokensMatch(tt.toks, tt.src))
251263 })
252264 }
@@ -15,6 +15,7 @@ import (
15 )15 )
16 16
17 func TestHighlightLines(t *testing.T) {17 func TestHighlightLines(t *testing.T) {
18+ t.Parallel()
18 v := highlight(context.Background(), "a.go", "package main\n\nfunc main() {}\n")19 v := highlight(context.Background(), "a.go", "package main\n\nfunc main() {}\n")
19 assert.Equal(t, 3, v.lines)20 assert.Equal(t, 3, v.lines)
20 assert.Equal(t, "Go", v.lang)21 assert.Equal(t, "Go", v.lang)
@@ -30,6 +31,7 @@ func TestHighlightLines(t *testing.T) {
30 }31 }
31 32
32 func TestHighlightFallbackIsPlainAndEscaped(t *testing.T) {33 func TestHighlightFallbackIsPlainAndEscaped(t *testing.T) {
34+ t.Parallel()
33 v := highlight(context.Background(), "unknown.zzz", "<script>alert(1)</script> & \"q\"\n")35 v := highlight(context.Background(), "unknown.zzz", "<script>alert(1)</script> & \"q\"\n")
34 assert.Equal(t, "", v.lang)36 assert.Equal(t, "", v.lang)
35 h := string(v.html)37 h := string(v.html)
@@ -41,6 +43,7 @@ func TestHighlightFallbackIsPlainAndEscaped(t *testing.T) {
41 }43 }
42 44
43 func TestHighlightMarksInvisibleCharacters(t *testing.T) {45 func TestHighlightMarksInvisibleCharacters(t *testing.T) {
46+ t.Parallel()
44 for r, cp := range map[rune]string{47 for r, cp := range map[rune]string{
45 0x202E: "U+202E", 0x2066: "U+2066", 0x2069: "U+2069", 0x200B: "U+200B", 0xFEFF: "U+FEFF",48 0x202E: "U+202E", 0x2066: "U+2066", 0x2069: "U+2069", 0x200B: "U+200B", 0xFEFF: "U+FEFF",
46 0x0000: "U+0000", 0x001B: "U+001B", 0x2028: "U+2028", 0x3164: "U+3164", 0x1F: "U+001F",49 0x0000: "U+0000", 0x001B: "U+001B", 0x2028: "U+2028", 0x3164: "U+3164", 0x1F: "U+001F",
@@ -63,6 +66,7 @@ func TestHighlightMarksInvisibleCharacters(t *testing.T) {
63 // The lexers do not lose or add characters: the text of the lines is the66 // The lexers do not lose or add characters: the text of the lines is the
64 // source, whatever the language.67 // source, whatever the language.
65 func TestHighlightKeepsTheSource(t *testing.T) {68 func TestHighlightKeepsTheSource(t *testing.T) {
69+ t.Parallel()
66 srcs := map[string]string{70 srcs := map[string]string{
67 "a.go": "package a\n\nvar s = `multi\nline`\n// end",71 "a.go": "package a\n\nvar s = `multi\nline`\n// end",
68 "b.py": "def f():\n '''doc\n string'''\n return 1\n",72 "b.py": "def f():\n '''doc\n string'''\n return 1\n",
@@ -93,6 +97,7 @@ func TestHighlightKeepsTheSource(t *testing.T) {
93 }97 }
94 98
95 func TestFirstLinesAndCount(t *testing.T) {99 func TestFirstLinesAndCount(t *testing.T) {
100+ t.Parallel()
96 assert.Equal(t, "a\nb\n", firstLines("a\nb\nc\n", 2))101 assert.Equal(t, "a\nb\n", firstLines("a\nb\nc\n", 2))
97 assert.Equal(t, "a\nb", firstLines("a\nb", 5))102 assert.Equal(t, "a\nb", firstLines("a\nb", 5))
98 assert.Equal(t, 3, countLines([]byte("a\nb\nc")))103 assert.Equal(t, 3, countLines([]byte("a\nb\nc")))
@@ -101,6 +106,7 @@ func TestFirstLinesAndCount(t *testing.T) {
101 }106 }
102 107
103 func TestHighlightCtxCancelled(t *testing.T) {108 func TestHighlightCtxCancelled(t *testing.T) {
109+ t.Parallel()
104 ctx, cancel := context.WithCancel(context.Background())110 ctx, cancel := context.WithCancel(context.Background())
105 cancel()111 cancel()
106 v := highlightCtx(ctx, "a.go", "package a\n")112 v := highlightCtx(ctx, "a.go", "package a\n")
@@ -116,12 +122,14 @@ func (panicLexer) Tokenise(*chroma.TokeniseOptions, string) (chroma.Iterator, er
116 }122 }
117 123
118 func TestTokeniseRecoversFromALexerPanic(t *testing.T) {124 func TestTokeniseRecoversFromALexerPanic(t *testing.T) {
125+ t.Parallel()
119 toks, ok := tokenise(context.Background(), panicLexer{}, "x\n")126 toks, ok := tokenise(context.Background(), panicLexer{}, "x\n")
120 assert.False(t, ok)127 assert.False(t, ok)
121 assert.Nil(t, toks)128 assert.Nil(t, toks)
122 }129 }
123 130
124 func TestSniffImage(t *testing.T) {131 func TestSniffImage(t *testing.T) {
132+ t.Parallel()
125 assert.Equal(t, "image/png", sniffImage([]byte("\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR")))133 assert.Equal(t, "image/png", sniffImage([]byte("\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR")))
126 assert.Equal(t, "image/gif", sniffImage([]byte("GIF89a....")))134 assert.Equal(t, "image/gif", sniffImage([]byte("GIF89a....")))
127 assert.Equal(t, "image/jpeg", sniffImage([]byte("\xff\xd8\xff\xe0\x00\x10JFIF")))135 assert.Equal(t, "image/jpeg", sniffImage([]byte("\xff\xd8\xff\xe0\x00\x10JFIF")))
@@ -132,6 +140,7 @@ func TestSniffImage(t *testing.T) {
132 }140 }
133 141
134 func TestLocate(t *testing.T) {142 func TestLocate(t *testing.T) {
143+ t.Parallel()
135 b, tag := pb.RefKind_REF_KIND_BRANCH, pb.RefKind_REF_KIND_TAG144 b, tag := pb.RefKind_REF_KIND_BRANCH, pb.RefKind_REF_KIND_TAG
136 d := &repoData{145 d := &repoData{
137 branches: []*pb.Ref{ref(b, "main"), ref(b, "feature/x")},146 branches: []*pb.Ref{ref(b, "main"), ref(b, "feature/x")},
@@ -169,6 +178,7 @@ func TestLocate(t *testing.T) {
169 }178 }
170 179
171 func TestFileCrumbs(t *testing.T) {180 func TestFileCrumbs(t *testing.T) {
181+ t.Parallel()
172 cs := pathCrumbs("demo", "blob", target{name: "feature/x", path: "src/a.go"})182 cs := pathCrumbs("demo", "blob", target{name: "feature/x", path: "src/a.go"})
173 require.Len(t, cs, 5)183 require.Len(t, cs, 5)
174 assert.Equal(t, "/demo/tree/feature/x/src", cs[3].Href)184 assert.Equal(t, "/demo/tree/feature/x/src", cs[3].Href)
@@ -220,6 +230,7 @@ func normSpace(s string) string {
220 }230 }
221 231
222 func TestTokensMatch(t *testing.T) {232 func TestTokensMatch(t *testing.T) {
233+ t.Parallel()
223 toks := func(vs ...string) []chroma.Token {234 toks := func(vs ...string) []chroma.Token {
224 var out []chroma.Token235 var out []chroma.Token
225 for _, v := range vs {236 for _, v := range vs {
@@ -247,6 +258,7 @@ func TestTokensMatch(t *testing.T) {
247 {"unicode", toks("é", "日本"), "é日本", true},258 {"unicode", toks("é", "日本"), "é日本", true},
248 } {259 } {
249 t.Run(tt.name, func(t *testing.T) {260 t.Run(tt.name, func(t *testing.T) {
261+ t.Parallel()
250 assert.Equal(t, tt.want, tokensMatch(tt.toks, tt.src))262 assert.Equal(t, tt.want, tokensMatch(tt.toks, tt.src))
251 })263 })
252 }264 }
M internal/adapters/web/markdown_test.go +6 -0
@@ -19,6 +19,7 @@ func renderMD(t *testing.T, src string) (string, []Heading) {
1919
2020 // AC4: raw HTML, javascript: links and SVG are neutralised.
2121 func TestMarkdownNeutralisation(t *testing.T) {
22+ t.Parallel()
2223 tests := []struct {
2324 name, src string
2425 absent []string
@@ -59,6 +60,7 @@ func TestMarkdownNeutralisation(t *testing.T) {
5960 }
6061 for _, tt := range tests {
6162 t.Run(tt.name, func(t *testing.T) {
63+ t.Parallel()
6264 out, _ := renderMD(t, tt.src)
6365 for _, s := range tt.absent {
6466 assert.NotContains(t, out, s)
@@ -80,6 +82,7 @@ func TestMarkdownNeutralisation(t *testing.T) {
8082 }
8183
8284 func TestMarkdownLinks(t *testing.T) {
85+ t.Parallel()
8386 tests := []struct{ name, src, href string }{
8487 {"relative from the file's directory", "[a](other.md)", `href="/demo/blob/main/docs/other.md"`},
8588 {"parent", "[a](../LICENSE)", `href="/demo/blob/main/LICENSE"`},
@@ -95,6 +98,7 @@ func TestMarkdownLinks(t *testing.T) {
9598 }
9699 for _, tt := range tests {
97100 t.Run(tt.name, func(t *testing.T) {
101+ t.Parallel()
98102 out, _ := renderMD(t, tt.src)
99103 if tt.href == "" {
100104 assert.NotContains(t, out, "<a ")
@@ -106,6 +110,7 @@ func TestMarkdownLinks(t *testing.T) {
106110 }
107111
108112 func TestMarkdownOutline(t *testing.T) {
113+ t.Parallel()
109114 out, outline := renderMD(t, "# Title\n\n## Install `now`\n\n### Install `now`\n\ntext\n\n#### raw *em*\n")
110115 assert.Equal(t, []Heading{
111116 {Level: 1, ID: "md-title", Text: "Title"},
@@ -124,6 +129,7 @@ func TestMarkdownOutline(t *testing.T) {
124129 }
125130
126131 func TestMarkdownGFM(t *testing.T) {
132+ t.Parallel()
127133 out, _ := renderMD(t, "| a | b |\n|:-|-:|\n| 1 | 2 |\n\n~~gone~~\n\n- [x] task\n")
128134 assert.Contains(t, out, `<th align="right">b</th>`, "alignment is an attribute, never a style")
129135 assert.NotContains(t, out, "style=")
@@ -19,6 +19,7 @@ func renderMD(t *testing.T, src string) (string, []Heading) {
19 19
20 // AC4: raw HTML, javascript: links and SVG are neutralised.20 // AC4: raw HTML, javascript: links and SVG are neutralised.
21 func TestMarkdownNeutralisation(t *testing.T) {21 func TestMarkdownNeutralisation(t *testing.T) {
22+ t.Parallel()
22 tests := []struct {23 tests := []struct {
23 name, src string24 name, src string
24 absent []string25 absent []string
@@ -59,6 +60,7 @@ func TestMarkdownNeutralisation(t *testing.T) {
59 }60 }
60 for _, tt := range tests {61 for _, tt := range tests {
61 t.Run(tt.name, func(t *testing.T) {62 t.Run(tt.name, func(t *testing.T) {
63+ t.Parallel()
62 out, _ := renderMD(t, tt.src)64 out, _ := renderMD(t, tt.src)
63 for _, s := range tt.absent {65 for _, s := range tt.absent {
64 assert.NotContains(t, out, s)66 assert.NotContains(t, out, s)
@@ -80,6 +82,7 @@ func TestMarkdownNeutralisation(t *testing.T) {
80 }82 }
81 83
82 func TestMarkdownLinks(t *testing.T) {84 func TestMarkdownLinks(t *testing.T) {
85+ t.Parallel()
83 tests := []struct{ name, src, href string }{86 tests := []struct{ name, src, href string }{
84 {"relative from the file's directory", "[a](other.md)", `href="/demo/blob/main/docs/other.md"`},87 {"relative from the file's directory", "[a](other.md)", `href="/demo/blob/main/docs/other.md"`},
85 {"parent", "[a](../LICENSE)", `href="/demo/blob/main/LICENSE"`},88 {"parent", "[a](../LICENSE)", `href="/demo/blob/main/LICENSE"`},
@@ -95,6 +98,7 @@ func TestMarkdownLinks(t *testing.T) {
95 }98 }
96 for _, tt := range tests {99 for _, tt := range tests {
97 t.Run(tt.name, func(t *testing.T) {100 t.Run(tt.name, func(t *testing.T) {
101+ t.Parallel()
98 out, _ := renderMD(t, tt.src)102 out, _ := renderMD(t, tt.src)
99 if tt.href == "" {103 if tt.href == "" {
100 assert.NotContains(t, out, "<a ")104 assert.NotContains(t, out, "<a ")
@@ -106,6 +110,7 @@ func TestMarkdownLinks(t *testing.T) {
106 }110 }
107 111
108 func TestMarkdownOutline(t *testing.T) {112 func TestMarkdownOutline(t *testing.T) {
113+ t.Parallel()
109 out, outline := renderMD(t, "# Title\n\n## Install `now`\n\n### Install `now`\n\ntext\n\n#### raw *em*\n")114 out, outline := renderMD(t, "# Title\n\n## Install `now`\n\n### Install `now`\n\ntext\n\n#### raw *em*\n")
110 assert.Equal(t, []Heading{115 assert.Equal(t, []Heading{
111 {Level: 1, ID: "md-title", Text: "Title"},116 {Level: 1, ID: "md-title", Text: "Title"},
@@ -124,6 +129,7 @@ func TestMarkdownOutline(t *testing.T) {
124 }129 }
125 130
126 func TestMarkdownGFM(t *testing.T) {131 func TestMarkdownGFM(t *testing.T) {
132+ t.Parallel()
127 out, _ := renderMD(t, "| a | b |\n|:-|-:|\n| 1 | 2 |\n\n~~gone~~\n\n- [x] task\n")133 out, _ := renderMD(t, "| a | b |\n|:-|-:|\n| 1 | 2 |\n\n~~gone~~\n\n- [x] task\n")
128 assert.Contains(t, out, `<th align="right">b</th>`, "alignment is an attribute, never a style")134 assert.Contains(t, out, `<th align="right">b</th>`, "alignment is an attribute, never a style")
129 assert.NotContains(t, out, "style=")135 assert.NotContains(t, out, "style=")
M internal/adapters/web/plaintext_test.go +6 -0
@@ -14,6 +14,7 @@ import (
1414 // Plain text stays text; only http(s) addresses become links, without the
1515 // punctuation that ends a sentence; invisible characters are marked.
1616 func TestPlainParts(t *testing.T) {
17+ t.Parallel()
1718 tests := []struct {
1819 name, in string
1920 want []TextPart
@@ -32,6 +33,7 @@ func TestPlainParts(t *testing.T) {
3233 }
3334 for _, tt := range tests {
3435 t.Run(tt.name, func(t *testing.T) {
36+ t.Parallel()
3537 assert.Equal(t, tt.want, plainParts(tt.in))
3638 })
3739 }
@@ -39,12 +41,14 @@ func TestPlainParts(t *testing.T) {
3941
4042 // userText escapes the text and the address, and marks the link as untrusted.
4143 func TestUserTextEscapes(t *testing.T) {
44+ t.Parallel()
4245 var b bytes.Buffer
4346 require.NoError(t, userText(plainParts(`<b>x</b> & http://x.test/?a=1&b=2`)).Render(context.Background(), &b))
4447 assert.Equal(t, `&lt;b&gt;x&lt;/b&gt; &amp; <a href="http://x.test/?a=1&amp;b=2" rel="noopener nofollow ugc">http://x.test/?a=1&amp;b=2</a>`, b.String())
4548 }
4649
4750 func TestParseOn(t *testing.T) {
51+ t.Parallel()
4852 assert.Equal(t, &lineRef{side: "new", line: 12, path: "a/b:c.go"}, parseOn("new:12:a/b:c.go"))
4953 assert.Equal(t, &lineRef{side: "old", line: 1, path: "x"}, parseOn("old:1:x"))
5054 for _, s := range []string{"", "new", "new:12", "new:12:", "new:0:x", "new:-1:x", "new:x:y", "up:1:x", "new::x"} {
@@ -53,6 +57,7 @@ func TestParseOn(t *testing.T) {
5357 }
5458
5559 func TestShellWord(t *testing.T) {
60+ t.Parallel()
5661 assert.Equal(t, "feat/search", shellWord("feat/search"))
5762 assert.Equal(t, `'a b'`, shellWord("a b"))
5863 assert.Equal(t, `'it'\''s'`, shellWord("it's"))
@@ -61,6 +66,7 @@ func TestShellWord(t *testing.T) {
6166
6267 // The notes of a split row: a context line counts once.
6368 func TestSplitRowNotes(t *testing.T) {
69+ t.Parallel()
6470 a, b := CommentView{ID: "1"}, CommentView{ID: "2"}
6571 ctx := &DiffLine{Notes: []CommentView{a}}
6672 assert.Equal(t, []CommentView{a}, SplitRow{ctx, ctx}.Notes())
@@ -14,6 +14,7 @@ import (
14 // Plain text stays text; only http(s) addresses become links, without the14 // Plain text stays text; only http(s) addresses become links, without the
15 // punctuation that ends a sentence; invisible characters are marked.15 // punctuation that ends a sentence; invisible characters are marked.
16 func TestPlainParts(t *testing.T) {16 func TestPlainParts(t *testing.T) {
17+ t.Parallel()
17 tests := []struct {18 tests := []struct {
18 name, in string19 name, in string
19 want []TextPart20 want []TextPart
@@ -32,6 +33,7 @@ func TestPlainParts(t *testing.T) {
32 }33 }
33 for _, tt := range tests {34 for _, tt := range tests {
34 t.Run(tt.name, func(t *testing.T) {35 t.Run(tt.name, func(t *testing.T) {
36+ t.Parallel()
35 assert.Equal(t, tt.want, plainParts(tt.in))37 assert.Equal(t, tt.want, plainParts(tt.in))
36 })38 })
37 }39 }
@@ -39,12 +41,14 @@ func TestPlainParts(t *testing.T) {
39 41
40 // userText escapes the text and the address, and marks the link as untrusted.42 // userText escapes the text and the address, and marks the link as untrusted.
41 func TestUserTextEscapes(t *testing.T) {43 func TestUserTextEscapes(t *testing.T) {
44+ t.Parallel()
42 var b bytes.Buffer45 var b bytes.Buffer
43 require.NoError(t, userText(plainParts(`<b>x</b> & http://x.test/?a=1&b=2`)).Render(context.Background(), &b))46 require.NoError(t, userText(plainParts(`<b>x</b> & http://x.test/?a=1&b=2`)).Render(context.Background(), &b))
44 assert.Equal(t, `&lt;b&gt;x&lt;/b&gt; &amp; <a href="http://x.test/?a=1&amp;b=2" rel="noopener nofollow ugc">http://x.test/?a=1&amp;b=2</a>`, b.String())47 assert.Equal(t, `&lt;b&gt;x&lt;/b&gt; &amp; <a href="http://x.test/?a=1&amp;b=2" rel="noopener nofollow ugc">http://x.test/?a=1&amp;b=2</a>`, b.String())
45 }48 }
46 49
47 func TestParseOn(t *testing.T) {50 func TestParseOn(t *testing.T) {
51+ t.Parallel()
48 assert.Equal(t, &lineRef{side: "new", line: 12, path: "a/b:c.go"}, parseOn("new:12:a/b:c.go"))52 assert.Equal(t, &lineRef{side: "new", line: 12, path: "a/b:c.go"}, parseOn("new:12:a/b:c.go"))
49 assert.Equal(t, &lineRef{side: "old", line: 1, path: "x"}, parseOn("old:1:x"))53 assert.Equal(t, &lineRef{side: "old", line: 1, path: "x"}, parseOn("old:1:x"))
50 for _, s := range []string{"", "new", "new:12", "new:12:", "new:0:x", "new:-1:x", "new:x:y", "up:1:x", "new::x"} {54 for _, s := range []string{"", "new", "new:12", "new:12:", "new:0:x", "new:-1:x", "new:x:y", "up:1:x", "new::x"} {
@@ -53,6 +57,7 @@ func TestParseOn(t *testing.T) {
53 }57 }
54 58
55 func TestShellWord(t *testing.T) {59 func TestShellWord(t *testing.T) {
60+ t.Parallel()
56 assert.Equal(t, "feat/search", shellWord("feat/search"))61 assert.Equal(t, "feat/search", shellWord("feat/search"))
57 assert.Equal(t, `'a b'`, shellWord("a b"))62 assert.Equal(t, `'a b'`, shellWord("a b"))
58 assert.Equal(t, `'it'\''s'`, shellWord("it's"))63 assert.Equal(t, `'it'\''s'`, shellWord("it's"))
@@ -61,6 +66,7 @@ func TestShellWord(t *testing.T) {
61 66
62 // The notes of a split row: a context line counts once.67 // The notes of a split row: a context line counts once.
63 func TestSplitRowNotes(t *testing.T) {68 func TestSplitRowNotes(t *testing.T) {
69+ t.Parallel()
64 a, b := CommentView{ID: "1"}, CommentView{ID: "2"}70 a, b := CommentView{ID: "1"}, CommentView{ID: "2"}
65 ctx := &DiffLine{Notes: []CommentView{a}}71 ctx := &DiffLine{Notes: []CommentView{a}}
66 assert.Equal(t, []CommentView{a}, SplitRow{ctx, ctx}.Notes())72 assert.Equal(t, []CommentView{a}, SplitRow{ctx, ctx}.Notes())
M internal/adapters/web/raw_test.go +2 -0
@@ -22,6 +22,7 @@ const blobID = "0123456789abcdef0123456789abcdef01234567"
2222 // A revalidation of an unchanged file is answered from the tree entry, before
2323 // the content is opened; anything else opens the file.
2424 func TestRawChecksETagBeforeOpeningTheBlob(t *testing.T) {
25+ t.Parallel()
2526 entry := func(kind pb.EntryKind, id string) *pb.TreeEntry {
2627 return &pb.TreeEntry{Name: "big.bin", Path: "docs/big.bin", Kind: kind, Id: id}
2728 }
@@ -82,6 +83,7 @@ func TestRawChecksETagBeforeOpeningTheBlob(t *testing.T) {
8283 }
8384 for _, tt := range tests {
8485 t.Run(tt.name, func(t *testing.T) {
86+ t.Parallel()
8587 repos := mocks.NewRepoServiceClient(t)
8688 git := mocks.NewGitServiceClient(t)
8789 repos.EXPECT().GetRepo(mock.Anything, mock.Anything).
@@ -22,6 +22,7 @@ const blobID = "0123456789abcdef0123456789abcdef01234567"
22 // A revalidation of an unchanged file is answered from the tree entry, before22 // A revalidation of an unchanged file is answered from the tree entry, before
23 // the content is opened; anything else opens the file.23 // the content is opened; anything else opens the file.
24 func TestRawChecksETagBeforeOpeningTheBlob(t *testing.T) {24 func TestRawChecksETagBeforeOpeningTheBlob(t *testing.T) {
25+ t.Parallel()
25 entry := func(kind pb.EntryKind, id string) *pb.TreeEntry {26 entry := func(kind pb.EntryKind, id string) *pb.TreeEntry {
26 return &pb.TreeEntry{Name: "big.bin", Path: "docs/big.bin", Kind: kind, Id: id}27 return &pb.TreeEntry{Name: "big.bin", Path: "docs/big.bin", Kind: kind, Id: id}
27 }28 }
@@ -82,6 +83,7 @@ func TestRawChecksETagBeforeOpeningTheBlob(t *testing.T) {
82 }83 }
83 for _, tt := range tests {84 for _, tt := range tests {
84 t.Run(tt.name, func(t *testing.T) {85 t.Run(tt.name, func(t *testing.T) {
86+ t.Parallel()
85 repos := mocks.NewRepoServiceClient(t)87 repos := mocks.NewRepoServiceClient(t)
86 git := mocks.NewGitServiceClient(t)88 git := mocks.NewGitServiceClient(t)
87 repos.EXPECT().GetRepo(mock.Anything, mock.Anything).89 repos.EXPECT().GetRepo(mock.Anything, mock.Anything).
M internal/adapters/web/refs_test.go +3 -0
@@ -16,6 +16,7 @@ func ref(kind pb.RefKind, short string) *pb.Ref {
1616
1717 // AC3: a ref with slashes is found by the longest matching prefix.
1818 func TestResolveRef(t *testing.T) {
19+ t.Parallel()
1920 b, tag := pb.RefKind_REF_KIND_BRANCH, pb.RefKind_REF_KIND_TAG
2021 refs := []*pb.Ref{
2122 ref(b, "main"), ref(b, "feature"), ref(b, "feature/x"), ref(b, "feature/x/y"), ref(tag, "v1/rc"),
@@ -39,6 +40,7 @@ func TestResolveRef(t *testing.T) {
3940 }
4041 for _, tt := range tests {
4142 t.Run(tt.path, func(t *testing.T) {
43+ t.Parallel()
4244 r, rest, ok := resolveRef(refs, tt.path)
4345 assert.Equal(t, tt.ok, ok)
4446 if !ok {
@@ -52,6 +54,7 @@ func TestResolveRef(t *testing.T) {
5254 }
5355
5456 func TestRepoHref(t *testing.T) {
57+ t.Parallel()
5558 assert.Equal(t, "/demo", repoHref("demo"))
5659 assert.Equal(t, "/demo/tree/feature/x/a%20b/c%23.go", repoHref("demo", "tree", "feature/x", "a b/c#.go"))
5760 assert.Equal(t, "/demo/blob/main/%3Fq", repoHref("demo", "blob", "main", "", "?q"))
@@ -16,6 +16,7 @@ func ref(kind pb.RefKind, short string) *pb.Ref {
16 16
17 // AC3: a ref with slashes is found by the longest matching prefix.17 // AC3: a ref with slashes is found by the longest matching prefix.
18 func TestResolveRef(t *testing.T) {18 func TestResolveRef(t *testing.T) {
19+ t.Parallel()
19 b, tag := pb.RefKind_REF_KIND_BRANCH, pb.RefKind_REF_KIND_TAG20 b, tag := pb.RefKind_REF_KIND_BRANCH, pb.RefKind_REF_KIND_TAG
20 refs := []*pb.Ref{21 refs := []*pb.Ref{
21 ref(b, "main"), ref(b, "feature"), ref(b, "feature/x"), ref(b, "feature/x/y"), ref(tag, "v1/rc"),22 ref(b, "main"), ref(b, "feature"), ref(b, "feature/x"), ref(b, "feature/x/y"), ref(tag, "v1/rc"),
@@ -39,6 +40,7 @@ func TestResolveRef(t *testing.T) {
39 }40 }
40 for _, tt := range tests {41 for _, tt := range tests {
41 t.Run(tt.path, func(t *testing.T) {42 t.Run(tt.path, func(t *testing.T) {
43+ t.Parallel()
42 r, rest, ok := resolveRef(refs, tt.path)44 r, rest, ok := resolveRef(refs, tt.path)
43 assert.Equal(t, tt.ok, ok)45 assert.Equal(t, tt.ok, ok)
44 if !ok {46 if !ok {
@@ -52,6 +54,7 @@ func TestResolveRef(t *testing.T) {
52 }54 }
53 55
54 func TestRepoHref(t *testing.T) {56 func TestRepoHref(t *testing.T) {
57+ t.Parallel()
55 assert.Equal(t, "/demo", repoHref("demo"))58 assert.Equal(t, "/demo", repoHref("demo"))
56 assert.Equal(t, "/demo/tree/feature/x/a%20b/c%23.go", repoHref("demo", "tree", "feature/x", "a b/c#.go"))59 assert.Equal(t, "/demo/tree/feature/x/a%20b/c%23.go", repoHref("demo", "tree", "feature/x", "a b/c#.go"))
57 assert.Equal(t, "/demo/blob/main/%3Fq", repoHref("demo", "blob", "main", "", "?q"))60 assert.Equal(t, "/demo/blob/main/%3Fq", repoHref("demo", "blob", "main", "", "?q"))
M internal/adapters/web/rules_test.go +1 -0
@@ -13,6 +13,7 @@ import (
1313 // The speculation rules prefetch the light pages and leave the heavy ones
1414 // (several Git reads each) to a click.
1515 func TestSpeculationRulesSkipHeavyPages(t *testing.T) {
16+ t.Parallel()
1617 raw, err := staticFS.ReadFile("static/rules.json")
1718 require.NoError(t, err)
1819 var rules struct {
@@ -13,6 +13,7 @@ import (
13 // The speculation rules prefetch the light pages and leave the heavy ones13 // The speculation rules prefetch the light pages and leave the heavy ones
14 // (several Git reads each) to a click.14 // (several Git reads each) to a click.
15 func TestSpeculationRulesSkipHeavyPages(t *testing.T) {15 func TestSpeculationRulesSkipHeavyPages(t *testing.T) {
16+ t.Parallel()
16 raw, err := staticFS.ReadFile("static/rules.json")17 raw, err := staticFS.ReadFile("static/rules.json")
17 require.NoError(t, err)18 require.NoError(t, err)
18 var rules struct {19 var rules struct {
M internal/adapters/web/session_test.go +8 -0
@@ -57,6 +57,7 @@ func newSessionServer(t *testing.T, sess SessionAuth, publicURL string) (*Server
5757 }
5858
5959 func TestOriginOf(t *testing.T) {
60+ t.Parallel()
6061 for in, want := range map[string]string{
6162 "https://forge.example": "https://forge.example",
6263 "https://Forge.Example/": "https://forge.example",
@@ -75,6 +76,7 @@ func TestOriginOf(t *testing.T) {
7576 }
7677
7778 func TestValidNonce(t *testing.T) {
79+ t.Parallel()
7880 assert.True(t, validNonce(strings.Repeat("aZ09-_", 7)+"a"))
7981 for _, n := range []string{"", "short", strings.Repeat("a", 44), strings.Repeat("a", 42) + "=", strings.Repeat("a", 42) + "/", strings.Repeat("é", 22)} {
8082 assert.False(t, validNonce(n), n)
@@ -84,6 +86,7 @@ func TestValidNonce(t *testing.T) {
8486 // The gate: a POST needs the Origin of the public URL, whatever Host says, and
8587 // a session unless it is the login.
8688 func TestGate(t *testing.T) {
89+ t.Parallel()
8790 srv, lim := newSessionServer(t, &fakeSessions{valid: "tok"}, "https://forge.example")
8891 do := func(path, origin, cookie string) int {
8992 req := httptest.NewRequest(http.MethodPost, path, nil)
@@ -122,6 +125,7 @@ func TestGate(t *testing.T) {
122125 // one the router matches, and on exactly loginPrefix + nonce. An encoded slash
123126 // must not turn another route into a "login".
124127 func TestGateLoginExemptionIsExact(t *testing.T) {
128+ t.Parallel()
125129 sess := &fakeSessions{valid: "tok"}
126130 lim := &countingLimiter{}
127131 srv, err := New(Config{
@@ -168,6 +172,7 @@ func TestGateLoginExemptionIsExact(t *testing.T) {
168172 // A session check that cannot be made fails closed (anonymous) and keeps the
169173 // cookie; a check that says no clears it.
170174 func TestWithSessionFailsClosed(t *testing.T) {
175+ t.Parallel()
171176 broken := &fakeSessions{valid: "tok", err: errors.New("kv down")}
172177 srv, _ := newSessionServer(t, broken, "https://forge.example")
173178 req := httptest.NewRequest(http.MethodGet, "/", nil)
@@ -194,6 +199,7 @@ func TestWithSessionFailsClosed(t *testing.T) {
194199 // varies on Cookie, and nothing later in the request undoes it: the page
195200 // writer, the raw file handler and the event stream keep it.
196201 func TestInvalidCookieAnswerIsNotCached(t *testing.T) {
202+ t.Parallel()
197203 sess := &fakeSessions{valid: "tok"}
198204 srv, _ := newSessionServer(t, sess, "https://forge.example")
199205
@@ -251,6 +257,7 @@ func TestInvalidCookieAnswerIsNotCached(t *testing.T) {
251257
252258 // Without sessions configured there is no login route and no cookie is read.
253259 func TestNoSessionsNoRoutes(t *testing.T) {
260+ t.Parallel()
254261 srv, err := New(Config{
255262 Repos: failing{err: errors.New("x")}, Git: ermiteconnect.NewGitServiceClient(http.DefaultClient, "http://unused.invalid"),
256263 PublicURL: "https://forge.example", SourceURL: "https://forge.example/ermite", Version: "v1",
@@ -268,6 +275,7 @@ func TestNoSessionsNoRoutes(t *testing.T) {
268275 }
269276
270277 func TestLogPathHidesLinks(t *testing.T) {
278+ t.Parallel()
271279 assert.Equal(t, "/-/login/…", logPath("/-/login/"+strings.Repeat("a", 43)))
272280 assert.Equal(t, "/demo/tree", logPath("/demo/tree"))
273281 for _, p := range []string{"//-/login/secret", "/x/../-/login/secret", "/-/login/secret/more", "/-/./login/secret"} {
@@ -57,6 +57,7 @@ func newSessionServer(t *testing.T, sess SessionAuth, publicURL string) (*Server
57 }57 }
58 58
59 func TestOriginOf(t *testing.T) {59 func TestOriginOf(t *testing.T) {
60+ t.Parallel()
60 for in, want := range map[string]string{61 for in, want := range map[string]string{
61 "https://forge.example": "https://forge.example",62 "https://forge.example": "https://forge.example",
62 "https://Forge.Example/": "https://forge.example",63 "https://Forge.Example/": "https://forge.example",
@@ -75,6 +76,7 @@ func TestOriginOf(t *testing.T) {
75 }76 }
76 77
77 func TestValidNonce(t *testing.T) {78 func TestValidNonce(t *testing.T) {
79+ t.Parallel()
78 assert.True(t, validNonce(strings.Repeat("aZ09-_", 7)+"a"))80 assert.True(t, validNonce(strings.Repeat("aZ09-_", 7)+"a"))
79 for _, n := range []string{"", "short", strings.Repeat("a", 44), strings.Repeat("a", 42) + "=", strings.Repeat("a", 42) + "/", strings.Repeat("é", 22)} {81 for _, n := range []string{"", "short", strings.Repeat("a", 44), strings.Repeat("a", 42) + "=", strings.Repeat("a", 42) + "/", strings.Repeat("é", 22)} {
80 assert.False(t, validNonce(n), n)82 assert.False(t, validNonce(n), n)
@@ -84,6 +86,7 @@ func TestValidNonce(t *testing.T) {
84 // The gate: a POST needs the Origin of the public URL, whatever Host says, and86 // The gate: a POST needs the Origin of the public URL, whatever Host says, and
85 // a session unless it is the login.87 // a session unless it is the login.
86 func TestGate(t *testing.T) {88 func TestGate(t *testing.T) {
89+ t.Parallel()
87 srv, lim := newSessionServer(t, &fakeSessions{valid: "tok"}, "https://forge.example")90 srv, lim := newSessionServer(t, &fakeSessions{valid: "tok"}, "https://forge.example")
88 do := func(path, origin, cookie string) int {91 do := func(path, origin, cookie string) int {
89 req := httptest.NewRequest(http.MethodPost, path, nil)92 req := httptest.NewRequest(http.MethodPost, path, nil)
@@ -122,6 +125,7 @@ func TestGate(t *testing.T) {
122 // one the router matches, and on exactly loginPrefix + nonce. An encoded slash125 // one the router matches, and on exactly loginPrefix + nonce. An encoded slash
123 // must not turn another route into a "login".126 // must not turn another route into a "login".
124 func TestGateLoginExemptionIsExact(t *testing.T) {127 func TestGateLoginExemptionIsExact(t *testing.T) {
128+ t.Parallel()
125 sess := &fakeSessions{valid: "tok"}129 sess := &fakeSessions{valid: "tok"}
126 lim := &countingLimiter{}130 lim := &countingLimiter{}
127 srv, err := New(Config{131 srv, err := New(Config{
@@ -168,6 +172,7 @@ func TestGateLoginExemptionIsExact(t *testing.T) {
168 // A session check that cannot be made fails closed (anonymous) and keeps the172 // A session check that cannot be made fails closed (anonymous) and keeps the
169 // cookie; a check that says no clears it.173 // cookie; a check that says no clears it.
170 func TestWithSessionFailsClosed(t *testing.T) {174 func TestWithSessionFailsClosed(t *testing.T) {
175+ t.Parallel()
171 broken := &fakeSessions{valid: "tok", err: errors.New("kv down")}176 broken := &fakeSessions{valid: "tok", err: errors.New("kv down")}
172 srv, _ := newSessionServer(t, broken, "https://forge.example")177 srv, _ := newSessionServer(t, broken, "https://forge.example")
173 req := httptest.NewRequest(http.MethodGet, "/", nil)178 req := httptest.NewRequest(http.MethodGet, "/", nil)
@@ -194,6 +199,7 @@ func TestWithSessionFailsClosed(t *testing.T) {
194 // varies on Cookie, and nothing later in the request undoes it: the page199 // varies on Cookie, and nothing later in the request undoes it: the page
195 // writer, the raw file handler and the event stream keep it.200 // writer, the raw file handler and the event stream keep it.
196 func TestInvalidCookieAnswerIsNotCached(t *testing.T) {201 func TestInvalidCookieAnswerIsNotCached(t *testing.T) {
202+ t.Parallel()
197 sess := &fakeSessions{valid: "tok"}203 sess := &fakeSessions{valid: "tok"}
198 srv, _ := newSessionServer(t, sess, "https://forge.example")204 srv, _ := newSessionServer(t, sess, "https://forge.example")
199 205
@@ -251,6 +257,7 @@ func TestInvalidCookieAnswerIsNotCached(t *testing.T) {
251 257
252 // Without sessions configured there is no login route and no cookie is read.258 // Without sessions configured there is no login route and no cookie is read.
253 func TestNoSessionsNoRoutes(t *testing.T) {259 func TestNoSessionsNoRoutes(t *testing.T) {
260+ t.Parallel()
254 srv, err := New(Config{261 srv, err := New(Config{
255 Repos: failing{err: errors.New("x")}, Git: ermiteconnect.NewGitServiceClient(http.DefaultClient, "http://unused.invalid"),262 Repos: failing{err: errors.New("x")}, Git: ermiteconnect.NewGitServiceClient(http.DefaultClient, "http://unused.invalid"),
256 PublicURL: "https://forge.example", SourceURL: "https://forge.example/ermite", Version: "v1",263 PublicURL: "https://forge.example", SourceURL: "https://forge.example/ermite", Version: "v1",
@@ -268,6 +275,7 @@ func TestNoSessionsNoRoutes(t *testing.T) {
268 }275 }
269 276
270 func TestLogPathHidesLinks(t *testing.T) {277 func TestLogPathHidesLinks(t *testing.T) {
278+ t.Parallel()
271 assert.Equal(t, "/-/login/…", logPath("/-/login/"+strings.Repeat("a", 43)))279 assert.Equal(t, "/-/login/…", logPath("/-/login/"+strings.Repeat("a", 43)))
272 assert.Equal(t, "/demo/tree", logPath("/demo/tree"))280 assert.Equal(t, "/demo/tree", logPath("/demo/tree"))
273 for _, p := range []string{"//-/login/secret", "/x/../-/login/secret", "/-/login/secret/more", "/-/./login/secret"} {281 for _, p := range []string{"//-/login/secret", "/x/../-/login/secret", "/-/login/secret/more", "/-/./login/secret"} {
M internal/adapters/web/text_test.go +1 -0
@@ -10,6 +10,7 @@ import (
1010
1111 // Invisible characters in names and subjects show as markers.
1212 func TestVisibleText(t *testing.T) {
13+ t.Parallel()
1314 assert.Equal(t, "a<U+202E>b.go", visibleText("a\u202eb.go"))
1415 assert.Equal(t, "plain π\t“é”", visibleText("plain π\t“é”"))
1516 assert.Equal(t, "a<U+0000>b<U+200B>", visibleText("a\x00b\u200b"))
@@ -10,6 +10,7 @@ import (
10 10
11 // Invisible characters in names and subjects show as markers.11 // Invisible characters in names and subjects show as markers.
12 func TestVisibleText(t *testing.T) {12 func TestVisibleText(t *testing.T) {
13+ t.Parallel()
13 assert.Equal(t, "a<U+202E>b.go", visibleText("a\u202eb.go"))14 assert.Equal(t, "a<U+202E>b.go", visibleText("a\u202eb.go"))
14 assert.Equal(t, "plain π\t“é”", visibleText("plain π\t“é”"))15 assert.Equal(t, "plain π\t“é”", visibleText("plain π\t“é”"))
15 assert.Equal(t, "a<U+0000>b<U+200B>", visibleText("a\x00b\u200b"))16 assert.Equal(t, "a<U+0000>b<U+200B>", visibleText("a\x00b\u200b"))
M internal/app/browse_test.go +12 -0
@@ -106,6 +106,7 @@ func browseOps() []browseOp {
106106 // not found; stored tokens need repo:read on the repository; and the Git
107107 // reader is never reached when access is refused.
108108 func TestBrowseAccess(t *testing.T) {
109+ t.Parallel()
109110 ctx := context.Background()
110111 pub := domain.Repo{Name: name(t, "pub"), Visibility: domain.VisibilityPublic}
111112 priv := domain.Repo{Name: name(t, "priv"), Visibility: domain.VisibilityPrivate}
@@ -129,6 +130,7 @@ func TestBrowseAccess(t *testing.T) {
129130 for _, pr := range principals {
130131 for _, repo := range []domain.Repo{pub, priv} {
131132 t.Run(op.name+"/"+pr.name+"/"+repo.Name.String(), func(t *testing.T) {
133+ t.Parallel()
132134 f := newBrowseFixture(t)
133135 f.meta.EXPECT().Get(ctx, repo.Name).Return(repo, nil)
134136 allowed := pr.pub
@@ -149,12 +151,14 @@ func TestBrowseAccess(t *testing.T) {
149151 }
150152 }
151153 t.Run(op.name+"/missing repository", func(t *testing.T) {
154+ t.Parallel()
152155 f := newBrowseFixture(t)
153156 f.meta.EXPECT().Get(ctx, name(t, "nope")).Return(domain.Repo{}, domain.ErrNotFound)
154157 err := op.call(f.browse, ctx, domain.Root(), name(t, "nope"))
155158 assert.ErrorIs(t, err, domain.ErrNotFound)
156159 })
157160 t.Run(op.name+"/zero name", func(t *testing.T) {
161+ t.Parallel()
158162 f := newBrowseFixture(t)
159163 err := op.call(f.browse, ctx, domain.Root(), domain.RepoName{})
160164 assert.ErrorIs(t, err, domain.ErrInvalidName)
@@ -163,6 +167,7 @@ func TestBrowseAccess(t *testing.T) {
163167 }
164168
165169 func TestBrowseHidesPrivateOnReaderErrors(t *testing.T) {
170+ t.Parallel()
166171 // A reader error is passed through for a caller who may read.
167172 ctx := context.Background()
168173 f := newBrowseFixture(t)
@@ -178,6 +183,7 @@ func ref(kind domain.RefKind, name string) domain.Ref {
178183 }
179184
180185 func TestListRefsPagingAndKinds(t *testing.T) {
186+ t.Parallel()
181187 ctx := context.Background()
182188 pub := domain.Repo{Name: name(t, "pub"), Visibility: domain.VisibilityPublic}
183189 all := []domain.Ref{
@@ -258,6 +264,7 @@ func TestListRefsPagingAndKinds(t *testing.T) {
258264 })
259265
260266 t.Run("bad input never reaches the reader", func(t *testing.T) {
267+ t.Parallel()
261268 f := newBrowseFixture(t)
262269 for _, params := range []app.ListRefsParams{
263270 {PageSize: -1},
@@ -278,11 +285,13 @@ func TestListRefsPagingAndKinds(t *testing.T) {
278285 }
279286
280287 func TestListCommitsPaging(t *testing.T) {
288+ t.Parallel()
281289 ctx := context.Background()
282290 pub := domain.Repo{Name: name(t, "pub"), Visibility: domain.VisibilityPublic}
283291 p := mustPath(t, "d/f")
284292
285293 t.Run("first page asks for one more than the page and returns a token", func(t *testing.T) {
294+ t.Parallel()
286295 f := newBrowseFixture(t)
287296 f.meta.EXPECT().Get(ctx, pub.Name).Return(pub, nil)
288297 f.reader.EXPECT().ListCommits(ctx, pub.Name, ports.CommitQuery{Path: p, Limit: 10}).
@@ -308,6 +317,7 @@ func TestListCommitsPaging(t *testing.T) {
308317 })
309318
310319 t.Run("a token is bound to its path and must be well formed", func(t *testing.T) {
320+ t.Parallel()
311321 f := newBrowseFixture(t)
312322 f.meta.EXPECT().Get(ctx, pub.Name).Return(pub, nil)
313323 f.reader.EXPECT().ListCommits(ctx, pub.Name, mock.Anything).
@@ -328,6 +338,7 @@ func TestListCommitsPaging(t *testing.T) {
328338 })
329339
330340 t.Run("a token cannot skip deeper than the cap", func(t *testing.T) {
341+ t.Parallel()
331342 f := newBrowseFixture(t)
332343 f.meta.EXPECT().Get(ctx, pub.Name).Return(pub, nil).Maybe()
333344 f.reader.EXPECT().ListCommits(ctx, pub.Name, mock.Anything).
@@ -352,6 +363,7 @@ func TestListCommitsPaging(t *testing.T) {
352363 })
353364
354365 t.Run("page size", func(t *testing.T) {
366+ t.Parallel()
355367 f := newBrowseFixture(t)
356368 f.meta.EXPECT().Get(ctx, pub.Name).Return(pub, nil).Times(2)
357369 f.reader.EXPECT().ListCommits(ctx, pub.Name, ports.CommitQuery{Limit: app.DefaultPageSize}).Return(ports.CommitPage{}, nil)
@@ -106,6 +106,7 @@ func browseOps() []browseOp {
106 // not found; stored tokens need repo:read on the repository; and the Git106 // not found; stored tokens need repo:read on the repository; and the Git
107 // reader is never reached when access is refused.107 // reader is never reached when access is refused.
108 func TestBrowseAccess(t *testing.T) {108 func TestBrowseAccess(t *testing.T) {
109+ t.Parallel()
109 ctx := context.Background()110 ctx := context.Background()
110 pub := domain.Repo{Name: name(t, "pub"), Visibility: domain.VisibilityPublic}111 pub := domain.Repo{Name: name(t, "pub"), Visibility: domain.VisibilityPublic}
111 priv := domain.Repo{Name: name(t, "priv"), Visibility: domain.VisibilityPrivate}112 priv := domain.Repo{Name: name(t, "priv"), Visibility: domain.VisibilityPrivate}
@@ -129,6 +130,7 @@ func TestBrowseAccess(t *testing.T) {
129 for _, pr := range principals {130 for _, pr := range principals {
130 for _, repo := range []domain.Repo{pub, priv} {131 for _, repo := range []domain.Repo{pub, priv} {
131 t.Run(op.name+"/"+pr.name+"/"+repo.Name.String(), func(t *testing.T) {132 t.Run(op.name+"/"+pr.name+"/"+repo.Name.String(), func(t *testing.T) {
133+ t.Parallel()
132 f := newBrowseFixture(t)134 f := newBrowseFixture(t)
133 f.meta.EXPECT().Get(ctx, repo.Name).Return(repo, nil)135 f.meta.EXPECT().Get(ctx, repo.Name).Return(repo, nil)
134 allowed := pr.pub136 allowed := pr.pub
@@ -149,12 +151,14 @@ func TestBrowseAccess(t *testing.T) {
149 }151 }
150 }152 }
151 t.Run(op.name+"/missing repository", func(t *testing.T) {153 t.Run(op.name+"/missing repository", func(t *testing.T) {
154+ t.Parallel()
152 f := newBrowseFixture(t)155 f := newBrowseFixture(t)
153 f.meta.EXPECT().Get(ctx, name(t, "nope")).Return(domain.Repo{}, domain.ErrNotFound)156 f.meta.EXPECT().Get(ctx, name(t, "nope")).Return(domain.Repo{}, domain.ErrNotFound)
154 err := op.call(f.browse, ctx, domain.Root(), name(t, "nope"))157 err := op.call(f.browse, ctx, domain.Root(), name(t, "nope"))
155 assert.ErrorIs(t, err, domain.ErrNotFound)158 assert.ErrorIs(t, err, domain.ErrNotFound)
156 })159 })
157 t.Run(op.name+"/zero name", func(t *testing.T) {160 t.Run(op.name+"/zero name", func(t *testing.T) {
161+ t.Parallel()
158 f := newBrowseFixture(t)162 f := newBrowseFixture(t)
159 err := op.call(f.browse, ctx, domain.Root(), domain.RepoName{})163 err := op.call(f.browse, ctx, domain.Root(), domain.RepoName{})
160 assert.ErrorIs(t, err, domain.ErrInvalidName)164 assert.ErrorIs(t, err, domain.ErrInvalidName)
@@ -163,6 +167,7 @@ func TestBrowseAccess(t *testing.T) {
163 }167 }
164 168
165 func TestBrowseHidesPrivateOnReaderErrors(t *testing.T) {169 func TestBrowseHidesPrivateOnReaderErrors(t *testing.T) {
170+ t.Parallel()
166 // A reader error is passed through for a caller who may read.171 // A reader error is passed through for a caller who may read.
167 ctx := context.Background()172 ctx := context.Background()
168 f := newBrowseFixture(t)173 f := newBrowseFixture(t)
@@ -178,6 +183,7 @@ func ref(kind domain.RefKind, name string) domain.Ref {
178 }183 }
179 184
180 func TestListRefsPagingAndKinds(t *testing.T) {185 func TestListRefsPagingAndKinds(t *testing.T) {
186+ t.Parallel()
181 ctx := context.Background()187 ctx := context.Background()
182 pub := domain.Repo{Name: name(t, "pub"), Visibility: domain.VisibilityPublic}188 pub := domain.Repo{Name: name(t, "pub"), Visibility: domain.VisibilityPublic}
183 all := []domain.Ref{189 all := []domain.Ref{
@@ -258,6 +264,7 @@ func TestListRefsPagingAndKinds(t *testing.T) {
258 })264 })
259 265
260 t.Run("bad input never reaches the reader", func(t *testing.T) {266 t.Run("bad input never reaches the reader", func(t *testing.T) {
267+ t.Parallel()
261 f := newBrowseFixture(t)268 f := newBrowseFixture(t)
262 for _, params := range []app.ListRefsParams{269 for _, params := range []app.ListRefsParams{
263 {PageSize: -1},270 {PageSize: -1},
@@ -278,11 +285,13 @@ func TestListRefsPagingAndKinds(t *testing.T) {
278 }285 }
279 286
280 func TestListCommitsPaging(t *testing.T) {287 func TestListCommitsPaging(t *testing.T) {
288+ t.Parallel()
281 ctx := context.Background()289 ctx := context.Background()
282 pub := domain.Repo{Name: name(t, "pub"), Visibility: domain.VisibilityPublic}290 pub := domain.Repo{Name: name(t, "pub"), Visibility: domain.VisibilityPublic}
283 p := mustPath(t, "d/f")291 p := mustPath(t, "d/f")
284 292
285 t.Run("first page asks for one more than the page and returns a token", func(t *testing.T) {293 t.Run("first page asks for one more than the page and returns a token", func(t *testing.T) {
294+ t.Parallel()
286 f := newBrowseFixture(t)295 f := newBrowseFixture(t)
287 f.meta.EXPECT().Get(ctx, pub.Name).Return(pub, nil)296 f.meta.EXPECT().Get(ctx, pub.Name).Return(pub, nil)
288 f.reader.EXPECT().ListCommits(ctx, pub.Name, ports.CommitQuery{Path: p, Limit: 10}).297 f.reader.EXPECT().ListCommits(ctx, pub.Name, ports.CommitQuery{Path: p, Limit: 10}).
@@ -308,6 +317,7 @@ func TestListCommitsPaging(t *testing.T) {
308 })317 })
309 318
310 t.Run("a token is bound to its path and must be well formed", func(t *testing.T) {319 t.Run("a token is bound to its path and must be well formed", func(t *testing.T) {
320+ t.Parallel()
311 f := newBrowseFixture(t)321 f := newBrowseFixture(t)
312 f.meta.EXPECT().Get(ctx, pub.Name).Return(pub, nil)322 f.meta.EXPECT().Get(ctx, pub.Name).Return(pub, nil)
313 f.reader.EXPECT().ListCommits(ctx, pub.Name, mock.Anything).323 f.reader.EXPECT().ListCommits(ctx, pub.Name, mock.Anything).
@@ -328,6 +338,7 @@ func TestListCommitsPaging(t *testing.T) {
328 })338 })
329 339
330 t.Run("a token cannot skip deeper than the cap", func(t *testing.T) {340 t.Run("a token cannot skip deeper than the cap", func(t *testing.T) {
341+ t.Parallel()
331 f := newBrowseFixture(t)342 f := newBrowseFixture(t)
332 f.meta.EXPECT().Get(ctx, pub.Name).Return(pub, nil).Maybe()343 f.meta.EXPECT().Get(ctx, pub.Name).Return(pub, nil).Maybe()
333 f.reader.EXPECT().ListCommits(ctx, pub.Name, mock.Anything).344 f.reader.EXPECT().ListCommits(ctx, pub.Name, mock.Anything).
@@ -352,6 +363,7 @@ func TestListCommitsPaging(t *testing.T) {
352 })363 })
353 364
354 t.Run("page size", func(t *testing.T) {365 t.Run("page size", func(t *testing.T) {
366+ t.Parallel()
355 f := newBrowseFixture(t)367 f := newBrowseFixture(t)
356 f.meta.EXPECT().Get(ctx, pub.Name).Return(pub, nil).Times(2)368 f.meta.EXPECT().Get(ctx, pub.Name).Return(pub, nil).Times(2)
357 f.reader.EXPECT().ListCommits(ctx, pub.Name, ports.CommitQuery{Limit: app.DefaultPageSize}).Return(ports.CommitPage{}, nil)369 f.reader.EXPECT().ListCommits(ctx, pub.Name, ports.CommitQuery{Limit: app.DefaultPageSize}).Return(ports.CommitPage{}, nil)
M internal/app/browsecache_test.go +11 -0
@@ -35,6 +35,7 @@ func treeWithLast(c string) domain.Tree {
3535 // TestGetTreeLastCommitCache covers which GetTree calls are served from the
3636 // cache: only the ones with last commits at a full commit id.
3737 func TestGetTreeLastCommitCache(t *testing.T) {
38+ t.Parallel()
3839 ctx := context.Background()
3940 pub := domain.Repo{Name: name(t, "pub"), Visibility: domain.VisibilityPublic}
4041 full := domain.RevisionOf(oid("a"))
@@ -55,6 +56,7 @@ func TestGetTreeLastCommitCache(t *testing.T) {
5556 }
5657 for _, tc := range tests {
5758 t.Run(tc.name, func(t *testing.T) {
59+ t.Parallel()
5860 f := newBrowseFixture(t)
5961 f.meta.EXPECT().Get(mock.Anything, pub.Name).Return(pub, nil)
6062 f.reader.EXPECT().GetTree(mock.Anything, pub.Name, tc.rev, dir, tc.includeLast).
@@ -69,6 +71,7 @@ func TestGetTreeLastCommitCache(t *testing.T) {
6971 }
7072
7173 func TestGetTreeLastCommitCacheKey(t *testing.T) {
74+ t.Parallel()
7275 ctx := context.Background()
7376 pubA := domain.Repo{Name: name(t, "a"), Visibility: domain.VisibilityPublic}
7477 pubB := domain.Repo{Name: name(t, "b"), Visibility: domain.VisibilityPublic}
@@ -102,6 +105,7 @@ func TestGetTreeLastCommitCacheKey(t *testing.T) {
102105 // TestGetTreeCacheKeepsAccessRule: a hit never skips the access check, and an
103106 // error is not cached.
104107 func TestGetTreeCacheKeepsAccessRule(t *testing.T) {
108+ t.Parallel()
105109 ctx := context.Background()
106110 priv := domain.Repo{Name: name(t, "priv"), Visibility: domain.VisibilityPrivate}
107111 rev, dir := domain.RevisionOf(oid("a")), treePath(t, "d")
@@ -122,6 +126,7 @@ func TestGetTreeCacheKeepsAccessRule(t *testing.T) {
122126 // TestGetTreeCacheReturnsCopies: a caller that edits its tree does not alter
123127 // what the next one gets.
124128 func TestGetTreeCacheReturnsCopies(t *testing.T) {
129+ t.Parallel()
125130 ctx := context.Background()
126131 pub := domain.Repo{Name: name(t, "pub"), Visibility: domain.VisibilityPublic}
127132 rev, dir := domain.RevisionOf(oid("a")), treePath(t, "d")
@@ -143,6 +148,7 @@ func TestGetTreeCacheReturnsCopies(t *testing.T) {
143148 // TestHistoryCacheBounds: the entry bound and the byte bound evict the least
144149 // recently used answer.
145150 func TestHistoryCacheBounds(t *testing.T) {
151+ t.Parallel()
146152 ctx := context.Background()
147153 pub := domain.Repo{Name: name(t, "pub"), Visibility: domain.VisibilityPublic}
148154 paths := []string{"p0", "p1", "p2"}
@@ -167,6 +173,7 @@ func TestHistoryCacheBounds(t *testing.T) {
167173 }
168174 for _, tc := range tests {
169175 t.Run(tc.name, func(t *testing.T) {
176+ t.Parallel()
170177 f := newBrowseFixture(t)
171178 f.browse.WithHistoryCache(tc.entries, tc.bytes)
172179 f.meta.EXPECT().Get(mock.Anything, pub.Name).Return(pub, nil)
@@ -212,6 +219,7 @@ func TestHistoryCacheBounds(t *testing.T) {
212219 // TestListCommitsLastChangeCache: the latest commit of a path at a full
213220 // commit id is cached; every other page shape is not.
214221 func TestListCommitsLastChangeCache(t *testing.T) {
222+ t.Parallel()
215223 ctx := context.Background()
216224 pub := domain.Repo{Name: name(t, "pub"), Visibility: domain.VisibilityPublic}
217225 full := domain.RevisionOf(oid("a"))
@@ -234,6 +242,7 @@ func TestListCommitsLastChangeCache(t *testing.T) {
234242 }
235243 for _, tc := range tests {
236244 t.Run(tc.name, func(t *testing.T) {
245+ t.Parallel()
237246 f := newBrowseFixture(t)
238247 f.meta.EXPECT().Get(mock.Anything, pub.Name).Return(pub, nil)
239248 q := ports.CommitQuery{Revision: tc.rev, Path: tc.path, Limit: tc.size}
@@ -252,6 +261,7 @@ func TestListCommitsLastChangeCache(t *testing.T) {
252261 }
253262
254263 t.Run("a following page is not cached", func(t *testing.T) {
264+ t.Parallel()
255265 f := newBrowseFixture(t)
256266 f.meta.EXPECT().Get(mock.Anything, pub.Name).Return(pub, nil)
257267 f.reader.EXPECT().ListCommits(mock.Anything, pub.Name, ports.CommitQuery{Revision: full, Path: file, Limit: 1}).
@@ -269,6 +279,7 @@ func TestListCommitsLastChangeCache(t *testing.T) {
269279 }
270280
271281 func TestHistoryCacheConcurrent(t *testing.T) {
282+ t.Parallel()
272283 ctx := context.Background()
273284 pub := domain.Repo{Name: name(t, "pub"), Visibility: domain.VisibilityPublic}
274285 f := newBrowseFixture(t)
@@ -35,6 +35,7 @@ func treeWithLast(c string) domain.Tree {
35 // TestGetTreeLastCommitCache covers which GetTree calls are served from the35 // TestGetTreeLastCommitCache covers which GetTree calls are served from the
36 // cache: only the ones with last commits at a full commit id.36 // cache: only the ones with last commits at a full commit id.
37 func TestGetTreeLastCommitCache(t *testing.T) {37 func TestGetTreeLastCommitCache(t *testing.T) {
38+ t.Parallel()
38 ctx := context.Background()39 ctx := context.Background()
39 pub := domain.Repo{Name: name(t, "pub"), Visibility: domain.VisibilityPublic}40 pub := domain.Repo{Name: name(t, "pub"), Visibility: domain.VisibilityPublic}
40 full := domain.RevisionOf(oid("a"))41 full := domain.RevisionOf(oid("a"))
@@ -55,6 +56,7 @@ func TestGetTreeLastCommitCache(t *testing.T) {
55 }56 }
56 for _, tc := range tests {57 for _, tc := range tests {
57 t.Run(tc.name, func(t *testing.T) {58 t.Run(tc.name, func(t *testing.T) {
59+ t.Parallel()
58 f := newBrowseFixture(t)60 f := newBrowseFixture(t)
59 f.meta.EXPECT().Get(mock.Anything, pub.Name).Return(pub, nil)61 f.meta.EXPECT().Get(mock.Anything, pub.Name).Return(pub, nil)
60 f.reader.EXPECT().GetTree(mock.Anything, pub.Name, tc.rev, dir, tc.includeLast).62 f.reader.EXPECT().GetTree(mock.Anything, pub.Name, tc.rev, dir, tc.includeLast).
@@ -69,6 +71,7 @@ func TestGetTreeLastCommitCache(t *testing.T) {
69 }71 }
70 72
71 func TestGetTreeLastCommitCacheKey(t *testing.T) {73 func TestGetTreeLastCommitCacheKey(t *testing.T) {
74+ t.Parallel()
72 ctx := context.Background()75 ctx := context.Background()
73 pubA := domain.Repo{Name: name(t, "a"), Visibility: domain.VisibilityPublic}76 pubA := domain.Repo{Name: name(t, "a"), Visibility: domain.VisibilityPublic}
74 pubB := domain.Repo{Name: name(t, "b"), Visibility: domain.VisibilityPublic}77 pubB := domain.Repo{Name: name(t, "b"), Visibility: domain.VisibilityPublic}
@@ -102,6 +105,7 @@ func TestGetTreeLastCommitCacheKey(t *testing.T) {
102 // TestGetTreeCacheKeepsAccessRule: a hit never skips the access check, and an105 // TestGetTreeCacheKeepsAccessRule: a hit never skips the access check, and an
103 // error is not cached.106 // error is not cached.
104 func TestGetTreeCacheKeepsAccessRule(t *testing.T) {107 func TestGetTreeCacheKeepsAccessRule(t *testing.T) {
108+ t.Parallel()
105 ctx := context.Background()109 ctx := context.Background()
106 priv := domain.Repo{Name: name(t, "priv"), Visibility: domain.VisibilityPrivate}110 priv := domain.Repo{Name: name(t, "priv"), Visibility: domain.VisibilityPrivate}
107 rev, dir := domain.RevisionOf(oid("a")), treePath(t, "d")111 rev, dir := domain.RevisionOf(oid("a")), treePath(t, "d")
@@ -122,6 +126,7 @@ func TestGetTreeCacheKeepsAccessRule(t *testing.T) {
122 // TestGetTreeCacheReturnsCopies: a caller that edits its tree does not alter126 // TestGetTreeCacheReturnsCopies: a caller that edits its tree does not alter
123 // what the next one gets.127 // what the next one gets.
124 func TestGetTreeCacheReturnsCopies(t *testing.T) {128 func TestGetTreeCacheReturnsCopies(t *testing.T) {
129+ t.Parallel()
125 ctx := context.Background()130 ctx := context.Background()
126 pub := domain.Repo{Name: name(t, "pub"), Visibility: domain.VisibilityPublic}131 pub := domain.Repo{Name: name(t, "pub"), Visibility: domain.VisibilityPublic}
127 rev, dir := domain.RevisionOf(oid("a")), treePath(t, "d")132 rev, dir := domain.RevisionOf(oid("a")), treePath(t, "d")
@@ -143,6 +148,7 @@ func TestGetTreeCacheReturnsCopies(t *testing.T) {
143 // TestHistoryCacheBounds: the entry bound and the byte bound evict the least148 // TestHistoryCacheBounds: the entry bound and the byte bound evict the least
144 // recently used answer.149 // recently used answer.
145 func TestHistoryCacheBounds(t *testing.T) {150 func TestHistoryCacheBounds(t *testing.T) {
151+ t.Parallel()
146 ctx := context.Background()152 ctx := context.Background()
147 pub := domain.Repo{Name: name(t, "pub"), Visibility: domain.VisibilityPublic}153 pub := domain.Repo{Name: name(t, "pub"), Visibility: domain.VisibilityPublic}
148 paths := []string{"p0", "p1", "p2"}154 paths := []string{"p0", "p1", "p2"}
@@ -167,6 +173,7 @@ func TestHistoryCacheBounds(t *testing.T) {
167 }173 }
168 for _, tc := range tests {174 for _, tc := range tests {
169 t.Run(tc.name, func(t *testing.T) {175 t.Run(tc.name, func(t *testing.T) {
176+ t.Parallel()
170 f := newBrowseFixture(t)177 f := newBrowseFixture(t)
171 f.browse.WithHistoryCache(tc.entries, tc.bytes)178 f.browse.WithHistoryCache(tc.entries, tc.bytes)
172 f.meta.EXPECT().Get(mock.Anything, pub.Name).Return(pub, nil)179 f.meta.EXPECT().Get(mock.Anything, pub.Name).Return(pub, nil)
@@ -212,6 +219,7 @@ func TestHistoryCacheBounds(t *testing.T) {
212 // TestListCommitsLastChangeCache: the latest commit of a path at a full219 // TestListCommitsLastChangeCache: the latest commit of a path at a full
213 // commit id is cached; every other page shape is not.220 // commit id is cached; every other page shape is not.
214 func TestListCommitsLastChangeCache(t *testing.T) {221 func TestListCommitsLastChangeCache(t *testing.T) {
222+ t.Parallel()
215 ctx := context.Background()223 ctx := context.Background()
216 pub := domain.Repo{Name: name(t, "pub"), Visibility: domain.VisibilityPublic}224 pub := domain.Repo{Name: name(t, "pub"), Visibility: domain.VisibilityPublic}
217 full := domain.RevisionOf(oid("a"))225 full := domain.RevisionOf(oid("a"))
@@ -234,6 +242,7 @@ func TestListCommitsLastChangeCache(t *testing.T) {
234 }242 }
235 for _, tc := range tests {243 for _, tc := range tests {
236 t.Run(tc.name, func(t *testing.T) {244 t.Run(tc.name, func(t *testing.T) {
245+ t.Parallel()
237 f := newBrowseFixture(t)246 f := newBrowseFixture(t)
238 f.meta.EXPECT().Get(mock.Anything, pub.Name).Return(pub, nil)247 f.meta.EXPECT().Get(mock.Anything, pub.Name).Return(pub, nil)
239 q := ports.CommitQuery{Revision: tc.rev, Path: tc.path, Limit: tc.size}248 q := ports.CommitQuery{Revision: tc.rev, Path: tc.path, Limit: tc.size}
@@ -252,6 +261,7 @@ func TestListCommitsLastChangeCache(t *testing.T) {
252 }261 }
253 262
254 t.Run("a following page is not cached", func(t *testing.T) {263 t.Run("a following page is not cached", func(t *testing.T) {
264+ t.Parallel()
255 f := newBrowseFixture(t)265 f := newBrowseFixture(t)
256 f.meta.EXPECT().Get(mock.Anything, pub.Name).Return(pub, nil)266 f.meta.EXPECT().Get(mock.Anything, pub.Name).Return(pub, nil)
257 f.reader.EXPECT().ListCommits(mock.Anything, pub.Name, ports.CommitQuery{Revision: full, Path: file, Limit: 1}).267 f.reader.EXPECT().ListCommits(mock.Anything, pub.Name, ports.CommitQuery{Revision: full, Path: file, Limit: 1}).
@@ -269,6 +279,7 @@ func TestListCommitsLastChangeCache(t *testing.T) {
269 }279 }
270 280
271 func TestHistoryCacheConcurrent(t *testing.T) {281 func TestHistoryCacheConcurrent(t *testing.T) {
282+ t.Parallel()
272 ctx := context.Background()283 ctx := context.Background()
273 pub := domain.Repo{Name: name(t, "pub"), Visibility: domain.VisibilityPublic}284 pub := domain.Repo{Name: name(t, "pub"), Visibility: domain.VisibilityPublic}
274 f := newBrowseFixture(t)285 f := newBrowseFixture(t)
M internal/app/events_test.go +33 -0
@@ -142,6 +142,7 @@ func (w *watcher) quiet(t *testing.T, r *eventRig, n domain.RepoName) {
142142 }
143143
144144 func TestWatchLiveAndFiltered(t *testing.T) {
145+ t.Parallel()
145146 synctest.Test(t, func(t *testing.T) {
146147 r := newEventRig(t, 0)
147148 pub := r.repo("pub", domain.VisibilityPublic)
@@ -177,6 +178,7 @@ func TestWatchLiveAndFiltered(t *testing.T) {
177178 }
178179
179180 func TestWatchRechecksAccessPerEvent(t *testing.T) {
181+ t.Parallel()
180182 synctest.Test(t, func(t *testing.T) {
181183 r := newEventRig(t, 0)
182184 n := name(t, "flip")
@@ -205,6 +207,7 @@ func TestWatchRechecksAccessPerEvent(t *testing.T) {
205207 }
206208
207209 func TestWatchResumeAndGap(t *testing.T) {
210+ t.Parallel()
208211 const size = 8
209212 // Each subtest gets its own bubble and a fresh run of 20 events recorded
210213 // through a first watcher; the ring then holds the last 8.
@@ -227,6 +230,7 @@ func TestWatchResumeAndGap(t *testing.T) {
227230 }
228231
229232 t.Run("an id that fell out of the buffer gives a gap, then the oldest buffered event", func(t *testing.T) {
233+ t.Parallel()
230234 synctest.Test(t, func(t *testing.T) {
231235 r, _, ctx, ids := setup(t)
232236 // 20 events were taken, the buffer holds the last 8: ids[12] is the
@@ -242,6 +246,7 @@ func TestWatchResumeAndGap(t *testing.T) {
242246 })
243247
244248 t.Run("an unknown id gives a gap", func(t *testing.T) {
249+ t.Parallel()
245250 synctest.Test(t, func(t *testing.T) {
246251 r, _, ctx, _ := setup(t)
247252 for _, id := range []string{"nonsense", "zz-1", "-", "0-0"} {
@@ -252,6 +257,7 @@ func TestWatchResumeAndGap(t *testing.T) {
252257 })
253258
254259 t.Run("an id of a later run is a gap, not a newer event", func(t *testing.T) {
260+ t.Parallel()
255261 synctest.Test(t, func(t *testing.T) {
256262 r, _, ctx, _ := setup(t)
257263 w := r.watch(ctx, domain.Root(), app.WatchParams{ResumeAfter: "otherboot-999"})
@@ -260,6 +266,7 @@ func TestWatchResumeAndGap(t *testing.T) {
260266 })
261267
262268 t.Run("resume replays the buffered events after the id, then goes live", func(t *testing.T) {
269+ t.Parallel()
263270 synctest.Test(t, func(t *testing.T) {
264271 r, n, ctx, ids := setup(t)
265272 w := r.watch(ctx, domain.Root(), app.WatchParams{ResumeAfter: ids[16]})
@@ -277,6 +284,7 @@ func TestWatchResumeAndGap(t *testing.T) {
277284 })
278285
279286 t.Run("resume at the last event replays nothing, then goes live", func(t *testing.T) {
287+ t.Parallel()
280288 synctest.Test(t, func(t *testing.T) {
281289 r, n, ctx, ids := setup(t)
282290 w := r.watch(ctx, domain.Root(), app.WatchParams{ResumeAfter: ids[len(ids)-1]})
@@ -289,6 +297,7 @@ func TestWatchResumeAndGap(t *testing.T) {
289297 }
290298
291299 func TestWatchTopicErrors(t *testing.T) {
300+ t.Parallel()
292301 r := newEventRig(t, 0)
293302 r.repo("pub", domain.VisibilityPublic)
294303 r.repo("priv", domain.VisibilityPrivate)
@@ -310,7 +319,9 @@ func TestWatchTopicErrors(t *testing.T) {
310319 }
311320
312321 func TestWatchEndsCleanly(t *testing.T) {
322+ t.Parallel()
313323 t.Run("on context cancellation", func(t *testing.T) {
324+ t.Parallel()
314325 synctest.Test(t, func(t *testing.T) {
315326 r := newEventRig(t, 0)
316327 ctx, cancel := context.WithCancel(context.Background())
@@ -330,6 +341,7 @@ func TestWatchEndsCleanly(t *testing.T) {
330341 })
331342
332343 t.Run("on Close", func(t *testing.T) {
344+ t.Parallel()
333345 synctest.Test(t, func(t *testing.T) {
334346 r := newEventRig(t, 0)
335347 w := r.watch(context.Background(), domain.Root(), app.WatchParams{})
@@ -348,6 +360,7 @@ func TestWatchEndsCleanly(t *testing.T) {
348360 })
349361
350362 t.Run("a failing send ends the watch with its error", func(t *testing.T) {
363+ t.Parallel()
351364 synctest.Test(t, func(t *testing.T) {
352365 r := newEventRig(t, 0)
353366 n := r.repo("pub", domain.VisibilityPublic)
@@ -365,6 +378,7 @@ func TestWatchEndsCleanly(t *testing.T) {
365378 }
366379
367380 func TestWatchSlowWatcherGetsAGap(t *testing.T) {
381+ t.Parallel()
368382 synctest.Test(t, func(t *testing.T) {
369383 const size = 4
370384 r := newEventRig(t, size)
@@ -423,6 +437,7 @@ func TestWatchSlowWatcherGetsAGap(t *testing.T) {
423437 }
424438
425439 func TestEventsRepoPayloads(t *testing.T) {
440+ t.Parallel()
426441 synctest.Test(t, func(t *testing.T) {
427442 r := newEventRig(t, 0)
428443 pub := r.repo("pub", domain.VisibilityPublic)
@@ -468,6 +483,7 @@ func tokenWith(id string, expires time.Time, repos ...string) domain.Principal {
468483 }
469484
470485 func TestWatchEndsWhenTheTokenIsRevoked(t *testing.T) {
486+ t.Parallel()
471487 synctest.Test(t, func(t *testing.T) {
472488 // Asking at every turn keeps the test independent of the clock.
473489 r := newEventRigConfig(t, app.EventsConfig{RevalidateEvery: -1})
@@ -501,6 +517,7 @@ func TestWatchEndsWhenTheTokenIsRevoked(t *testing.T) {
501517 }
502518
503519 func TestWatchEndsWhenTheTokenExpires(t *testing.T) {
520+ t.Parallel()
504521 synctest.Test(t, func(t *testing.T) {
505522 r := newEventRig(t, 0)
506523 pub := r.repo("pub", domain.VisibilityPublic)
@@ -520,7 +537,9 @@ func TestWatchEndsWhenTheTokenExpires(t *testing.T) {
520537 }
521538
522539 func TestWatchRevalidatesIdleWatchersAndCachesTheAnswer(t *testing.T) {
540+ t.Parallel()
523541 t.Run("a quiet forge still notices a revocation", func(t *testing.T) {
542+ t.Parallel()
524543 synctest.Test(t, func(t *testing.T) {
525544 r := newEventRigConfig(t, app.EventsConfig{}) // the production default interval
526545 w := r.watch(context.Background(), tokenWith("tok", time.Time{}), app.WatchParams{})
@@ -546,6 +565,7 @@ func TestWatchRevalidatesIdleWatchersAndCachesTheAnswer(t *testing.T) {
546565 })
547566
548567 t.Run("the answer is kept between checks", func(t *testing.T) {
568+ t.Parallel()
549569 synctest.Test(t, func(t *testing.T) {
550570 r := newEventRigConfig(t, app.EventsConfig{RevalidateEvery: time.Hour})
551571 pub := r.repo("pub", domain.VisibilityPublic)
@@ -568,6 +588,7 @@ func TestWatchRevalidatesIdleWatchersAndCachesTheAnswer(t *testing.T) {
568588 })
569589
570590 t.Run("root and anonymous never ask", func(t *testing.T) {
591+ t.Parallel()
571592 synctest.Test(t, func(t *testing.T) {
572593 r := newEventRigConfig(t, app.EventsConfig{RevalidateEvery: -1})
573594 pub := r.repo("pub", domain.VisibilityPublic)
@@ -584,9 +605,11 @@ func TestWatchRevalidatesIdleWatchersAndCachesTheAnswer(t *testing.T) {
584605 }
585606
586607 func TestWatchLimits(t *testing.T) {
608+ t.Parallel()
587609 noop := func(app.WatchEvent) error { return nil }
588610
589611 t.Run("too many topics", func(t *testing.T) {
612+ t.Parallel()
590613 synctest.Test(t, func(t *testing.T) {
591614 r := newEventRig(t, 0)
592615 r.repo("pub", domain.VisibilityPublic)
@@ -603,6 +626,7 @@ func TestWatchLimits(t *testing.T) {
603626 })
604627
605628 t.Run("total watchers", func(t *testing.T) {
629+ t.Parallel()
606630 synctest.Test(t, func(t *testing.T) {
607631 r := newEventRig(t, 0)
608632 ctx, cancel := context.WithCancel(context.Background())
@@ -626,6 +650,7 @@ func TestWatchLimits(t *testing.T) {
626650 })
627651
628652 t.Run("watchers per client", func(t *testing.T) {
653+ t.Parallel()
629654 synctest.Test(t, func(t *testing.T) {
630655 r := newEventRig(t, 0)
631656 ctx, cancel := context.WithCancel(context.Background())
@@ -679,7 +704,9 @@ func (r *eventRig) liveRepo(s string, vis domain.Visibility, created time.Time)
679704 }
680705
681706 func TestEventsKeepTheVisibilityOfTheirTime(t *testing.T) {
707+ t.Parallel()
682708 t.Run("a repository made public does not reveal what happened while it was private", func(t *testing.T) {
709+ t.Parallel()
683710 synctest.Test(t, func(t *testing.T) {
684711 ctx, cancel := context.WithCancel(context.Background())
685712 defer cancel()
@@ -713,6 +740,7 @@ func TestEventsKeepTheVisibilityOfTheirTime(t *testing.T) {
713740 })
714741
715742 t.Run("a repository deleted and created again does not inherit old events", func(t *testing.T) {
743+ t.Parallel()
716744 synctest.Test(t, func(t *testing.T) {
717745 ctx, cancel := context.WithCancel(context.Background())
718746 defer cancel()
@@ -739,6 +767,7 @@ func TestEventsKeepTheVisibilityOfTheirTime(t *testing.T) {
739767 })
740768
741769 t.Run("a repository made private tells those who could read it, once", func(t *testing.T) {
770+ t.Parallel()
742771 synctest.Test(t, func(t *testing.T) {
743772 ctx, cancel := context.WithCancel(context.Background())
744773 defer cancel()
@@ -774,6 +803,7 @@ func TestEventsKeepTheVisibilityOfTheirTime(t *testing.T) {
774803 })
775804
776805 t.Run("a private repository that never was public stays hidden", func(t *testing.T) {
806+ t.Parallel()
777807 synctest.Test(t, func(t *testing.T) {
778808 ctx, cancel := context.WithCancel(context.Background())
779809 defer cancel()
@@ -792,6 +822,7 @@ func TestEventsKeepTheVisibilityOfTheirTime(t *testing.T) {
792822 // TestPushEventsKeepTheirSnapshot: a push is told by the repository as it was
793823 // when published, not as the store says when the feeder gets to it.
794824 func TestPushEventsKeepTheirSnapshot(t *testing.T) {
825+ t.Parallel()
795826 tests := []struct {
796827 name string
797828 swap func(repo *domain.Repo)
@@ -805,6 +836,7 @@ func TestPushEventsKeepTheirSnapshot(t *testing.T) {
805836 }
806837 for _, tt := range tests {
807838 t.Run(tt.name, func(t *testing.T) {
839+ t.Parallel()
808840 synctest.Test(t, func(t *testing.T) {
809841 ctx, cancel := context.WithCancel(context.Background())
810842 defer cancel()
@@ -835,6 +867,7 @@ func TestPushEventsKeepTheirSnapshot(t *testing.T) {
835867 }
836868
837869 t.Run("a push of an unknown repository is told to nobody", func(t *testing.T) {
870+ t.Parallel()
838871 synctest.Test(t, func(t *testing.T) {
839872 ctx, cancel := context.WithCancel(context.Background())
840873 defer cancel()
@@ -142,6 +142,7 @@ func (w *watcher) quiet(t *testing.T, r *eventRig, n domain.RepoName) {
142 }142 }
143 143
144 func TestWatchLiveAndFiltered(t *testing.T) {144 func TestWatchLiveAndFiltered(t *testing.T) {
145+ t.Parallel()
145 synctest.Test(t, func(t *testing.T) {146 synctest.Test(t, func(t *testing.T) {
146 r := newEventRig(t, 0)147 r := newEventRig(t, 0)
147 pub := r.repo("pub", domain.VisibilityPublic)148 pub := r.repo("pub", domain.VisibilityPublic)
@@ -177,6 +178,7 @@ func TestWatchLiveAndFiltered(t *testing.T) {
177 }178 }
178 179
179 func TestWatchRechecksAccessPerEvent(t *testing.T) {180 func TestWatchRechecksAccessPerEvent(t *testing.T) {
181+ t.Parallel()
180 synctest.Test(t, func(t *testing.T) {182 synctest.Test(t, func(t *testing.T) {
181 r := newEventRig(t, 0)183 r := newEventRig(t, 0)
182 n := name(t, "flip")184 n := name(t, "flip")
@@ -205,6 +207,7 @@ func TestWatchRechecksAccessPerEvent(t *testing.T) {
205 }207 }
206 208
207 func TestWatchResumeAndGap(t *testing.T) {209 func TestWatchResumeAndGap(t *testing.T) {
210+ t.Parallel()
208 const size = 8211 const size = 8
209 // Each subtest gets its own bubble and a fresh run of 20 events recorded212 // Each subtest gets its own bubble and a fresh run of 20 events recorded
210 // through a first watcher; the ring then holds the last 8.213 // through a first watcher; the ring then holds the last 8.
@@ -227,6 +230,7 @@ func TestWatchResumeAndGap(t *testing.T) {
227 }230 }
228 231
229 t.Run("an id that fell out of the buffer gives a gap, then the oldest buffered event", func(t *testing.T) {232 t.Run("an id that fell out of the buffer gives a gap, then the oldest buffered event", func(t *testing.T) {
233+ t.Parallel()
230 synctest.Test(t, func(t *testing.T) {234 synctest.Test(t, func(t *testing.T) {
231 r, _, ctx, ids := setup(t)235 r, _, ctx, ids := setup(t)
232 // 20 events were taken, the buffer holds the last 8: ids[12] is the236 // 20 events were taken, the buffer holds the last 8: ids[12] is the
@@ -242,6 +246,7 @@ func TestWatchResumeAndGap(t *testing.T) {
242 })246 })
243 247
244 t.Run("an unknown id gives a gap", func(t *testing.T) {248 t.Run("an unknown id gives a gap", func(t *testing.T) {
249+ t.Parallel()
245 synctest.Test(t, func(t *testing.T) {250 synctest.Test(t, func(t *testing.T) {
246 r, _, ctx, _ := setup(t)251 r, _, ctx, _ := setup(t)
247 for _, id := range []string{"nonsense", "zz-1", "-", "0-0"} {252 for _, id := range []string{"nonsense", "zz-1", "-", "0-0"} {
@@ -252,6 +257,7 @@ func TestWatchResumeAndGap(t *testing.T) {
252 })257 })
253 258
254 t.Run("an id of a later run is a gap, not a newer event", func(t *testing.T) {259 t.Run("an id of a later run is a gap, not a newer event", func(t *testing.T) {
260+ t.Parallel()
255 synctest.Test(t, func(t *testing.T) {261 synctest.Test(t, func(t *testing.T) {
256 r, _, ctx, _ := setup(t)262 r, _, ctx, _ := setup(t)
257 w := r.watch(ctx, domain.Root(), app.WatchParams{ResumeAfter: "otherboot-999"})263 w := r.watch(ctx, domain.Root(), app.WatchParams{ResumeAfter: "otherboot-999"})
@@ -260,6 +266,7 @@ func TestWatchResumeAndGap(t *testing.T) {
260 })266 })
261 267
262 t.Run("resume replays the buffered events after the id, then goes live", func(t *testing.T) {268 t.Run("resume replays the buffered events after the id, then goes live", func(t *testing.T) {
269+ t.Parallel()
263 synctest.Test(t, func(t *testing.T) {270 synctest.Test(t, func(t *testing.T) {
264 r, n, ctx, ids := setup(t)271 r, n, ctx, ids := setup(t)
265 w := r.watch(ctx, domain.Root(), app.WatchParams{ResumeAfter: ids[16]})272 w := r.watch(ctx, domain.Root(), app.WatchParams{ResumeAfter: ids[16]})
@@ -277,6 +284,7 @@ func TestWatchResumeAndGap(t *testing.T) {
277 })284 })
278 285
279 t.Run("resume at the last event replays nothing, then goes live", func(t *testing.T) {286 t.Run("resume at the last event replays nothing, then goes live", func(t *testing.T) {
287+ t.Parallel()
280 synctest.Test(t, func(t *testing.T) {288 synctest.Test(t, func(t *testing.T) {
281 r, n, ctx, ids := setup(t)289 r, n, ctx, ids := setup(t)
282 w := r.watch(ctx, domain.Root(), app.WatchParams{ResumeAfter: ids[len(ids)-1]})290 w := r.watch(ctx, domain.Root(), app.WatchParams{ResumeAfter: ids[len(ids)-1]})
@@ -289,6 +297,7 @@ func TestWatchResumeAndGap(t *testing.T) {
289 }297 }
290 298
291 func TestWatchTopicErrors(t *testing.T) {299 func TestWatchTopicErrors(t *testing.T) {
300+ t.Parallel()
292 r := newEventRig(t, 0)301 r := newEventRig(t, 0)
293 r.repo("pub", domain.VisibilityPublic)302 r.repo("pub", domain.VisibilityPublic)
294 r.repo("priv", domain.VisibilityPrivate)303 r.repo("priv", domain.VisibilityPrivate)
@@ -310,7 +319,9 @@ func TestWatchTopicErrors(t *testing.T) {
310 }319 }
311 320
312 func TestWatchEndsCleanly(t *testing.T) {321 func TestWatchEndsCleanly(t *testing.T) {
322+ t.Parallel()
313 t.Run("on context cancellation", func(t *testing.T) {323 t.Run("on context cancellation", func(t *testing.T) {
324+ t.Parallel()
314 synctest.Test(t, func(t *testing.T) {325 synctest.Test(t, func(t *testing.T) {
315 r := newEventRig(t, 0)326 r := newEventRig(t, 0)
316 ctx, cancel := context.WithCancel(context.Background())327 ctx, cancel := context.WithCancel(context.Background())
@@ -330,6 +341,7 @@ func TestWatchEndsCleanly(t *testing.T) {
330 })341 })
331 342
332 t.Run("on Close", func(t *testing.T) {343 t.Run("on Close", func(t *testing.T) {
344+ t.Parallel()
333 synctest.Test(t, func(t *testing.T) {345 synctest.Test(t, func(t *testing.T) {
334 r := newEventRig(t, 0)346 r := newEventRig(t, 0)
335 w := r.watch(context.Background(), domain.Root(), app.WatchParams{})347 w := r.watch(context.Background(), domain.Root(), app.WatchParams{})
@@ -348,6 +360,7 @@ func TestWatchEndsCleanly(t *testing.T) {
348 })360 })
349 361
350 t.Run("a failing send ends the watch with its error", func(t *testing.T) {362 t.Run("a failing send ends the watch with its error", func(t *testing.T) {
363+ t.Parallel()
351 synctest.Test(t, func(t *testing.T) {364 synctest.Test(t, func(t *testing.T) {
352 r := newEventRig(t, 0)365 r := newEventRig(t, 0)
353 n := r.repo("pub", domain.VisibilityPublic)366 n := r.repo("pub", domain.VisibilityPublic)
@@ -365,6 +378,7 @@ func TestWatchEndsCleanly(t *testing.T) {
365 }378 }
366 379
367 func TestWatchSlowWatcherGetsAGap(t *testing.T) {380 func TestWatchSlowWatcherGetsAGap(t *testing.T) {
381+ t.Parallel()
368 synctest.Test(t, func(t *testing.T) {382 synctest.Test(t, func(t *testing.T) {
369 const size = 4383 const size = 4
370 r := newEventRig(t, size)384 r := newEventRig(t, size)
@@ -423,6 +437,7 @@ func TestWatchSlowWatcherGetsAGap(t *testing.T) {
423 }437 }
424 438
425 func TestEventsRepoPayloads(t *testing.T) {439 func TestEventsRepoPayloads(t *testing.T) {
440+ t.Parallel()
426 synctest.Test(t, func(t *testing.T) {441 synctest.Test(t, func(t *testing.T) {
427 r := newEventRig(t, 0)442 r := newEventRig(t, 0)
428 pub := r.repo("pub", domain.VisibilityPublic)443 pub := r.repo("pub", domain.VisibilityPublic)
@@ -468,6 +483,7 @@ func tokenWith(id string, expires time.Time, repos ...string) domain.Principal {
468 }483 }
469 484
470 func TestWatchEndsWhenTheTokenIsRevoked(t *testing.T) {485 func TestWatchEndsWhenTheTokenIsRevoked(t *testing.T) {
486+ t.Parallel()
471 synctest.Test(t, func(t *testing.T) {487 synctest.Test(t, func(t *testing.T) {
472 // Asking at every turn keeps the test independent of the clock.488 // Asking at every turn keeps the test independent of the clock.
473 r := newEventRigConfig(t, app.EventsConfig{RevalidateEvery: -1})489 r := newEventRigConfig(t, app.EventsConfig{RevalidateEvery: -1})
@@ -501,6 +517,7 @@ func TestWatchEndsWhenTheTokenIsRevoked(t *testing.T) {
501 }517 }
502 518
503 func TestWatchEndsWhenTheTokenExpires(t *testing.T) {519 func TestWatchEndsWhenTheTokenExpires(t *testing.T) {
520+ t.Parallel()
504 synctest.Test(t, func(t *testing.T) {521 synctest.Test(t, func(t *testing.T) {
505 r := newEventRig(t, 0)522 r := newEventRig(t, 0)
506 pub := r.repo("pub", domain.VisibilityPublic)523 pub := r.repo("pub", domain.VisibilityPublic)
@@ -520,7 +537,9 @@ func TestWatchEndsWhenTheTokenExpires(t *testing.T) {
520 }537 }
521 538
522 func TestWatchRevalidatesIdleWatchersAndCachesTheAnswer(t *testing.T) {539 func TestWatchRevalidatesIdleWatchersAndCachesTheAnswer(t *testing.T) {
540+ t.Parallel()
523 t.Run("a quiet forge still notices a revocation", func(t *testing.T) {541 t.Run("a quiet forge still notices a revocation", func(t *testing.T) {
542+ t.Parallel()
524 synctest.Test(t, func(t *testing.T) {543 synctest.Test(t, func(t *testing.T) {
525 r := newEventRigConfig(t, app.EventsConfig{}) // the production default interval544 r := newEventRigConfig(t, app.EventsConfig{}) // the production default interval
526 w := r.watch(context.Background(), tokenWith("tok", time.Time{}), app.WatchParams{})545 w := r.watch(context.Background(), tokenWith("tok", time.Time{}), app.WatchParams{})
@@ -546,6 +565,7 @@ func TestWatchRevalidatesIdleWatchersAndCachesTheAnswer(t *testing.T) {
546 })565 })
547 566
548 t.Run("the answer is kept between checks", func(t *testing.T) {567 t.Run("the answer is kept between checks", func(t *testing.T) {
568+ t.Parallel()
549 synctest.Test(t, func(t *testing.T) {569 synctest.Test(t, func(t *testing.T) {
550 r := newEventRigConfig(t, app.EventsConfig{RevalidateEvery: time.Hour})570 r := newEventRigConfig(t, app.EventsConfig{RevalidateEvery: time.Hour})
551 pub := r.repo("pub", domain.VisibilityPublic)571 pub := r.repo("pub", domain.VisibilityPublic)
@@ -568,6 +588,7 @@ func TestWatchRevalidatesIdleWatchersAndCachesTheAnswer(t *testing.T) {
568 })588 })
569 589
570 t.Run("root and anonymous never ask", func(t *testing.T) {590 t.Run("root and anonymous never ask", func(t *testing.T) {
591+ t.Parallel()
571 synctest.Test(t, func(t *testing.T) {592 synctest.Test(t, func(t *testing.T) {
572 r := newEventRigConfig(t, app.EventsConfig{RevalidateEvery: -1})593 r := newEventRigConfig(t, app.EventsConfig{RevalidateEvery: -1})
573 pub := r.repo("pub", domain.VisibilityPublic)594 pub := r.repo("pub", domain.VisibilityPublic)
@@ -584,9 +605,11 @@ func TestWatchRevalidatesIdleWatchersAndCachesTheAnswer(t *testing.T) {
584 }605 }
585 606
586 func TestWatchLimits(t *testing.T) {607 func TestWatchLimits(t *testing.T) {
608+ t.Parallel()
587 noop := func(app.WatchEvent) error { return nil }609 noop := func(app.WatchEvent) error { return nil }
588 610
589 t.Run("too many topics", func(t *testing.T) {611 t.Run("too many topics", func(t *testing.T) {
612+ t.Parallel()
590 synctest.Test(t, func(t *testing.T) {613 synctest.Test(t, func(t *testing.T) {
591 r := newEventRig(t, 0)614 r := newEventRig(t, 0)
592 r.repo("pub", domain.VisibilityPublic)615 r.repo("pub", domain.VisibilityPublic)
@@ -603,6 +626,7 @@ func TestWatchLimits(t *testing.T) {
603 })626 })
604 627
605 t.Run("total watchers", func(t *testing.T) {628 t.Run("total watchers", func(t *testing.T) {
629+ t.Parallel()
606 synctest.Test(t, func(t *testing.T) {630 synctest.Test(t, func(t *testing.T) {
607 r := newEventRig(t, 0)631 r := newEventRig(t, 0)
608 ctx, cancel := context.WithCancel(context.Background())632 ctx, cancel := context.WithCancel(context.Background())
@@ -626,6 +650,7 @@ func TestWatchLimits(t *testing.T) {
626 })650 })
627 651
628 t.Run("watchers per client", func(t *testing.T) {652 t.Run("watchers per client", func(t *testing.T) {
653+ t.Parallel()
629 synctest.Test(t, func(t *testing.T) {654 synctest.Test(t, func(t *testing.T) {
630 r := newEventRig(t, 0)655 r := newEventRig(t, 0)
631 ctx, cancel := context.WithCancel(context.Background())656 ctx, cancel := context.WithCancel(context.Background())
@@ -679,7 +704,9 @@ func (r *eventRig) liveRepo(s string, vis domain.Visibility, created time.Time)
679 }704 }
680 705
681 func TestEventsKeepTheVisibilityOfTheirTime(t *testing.T) {706 func TestEventsKeepTheVisibilityOfTheirTime(t *testing.T) {
707+ t.Parallel()
682 t.Run("a repository made public does not reveal what happened while it was private", func(t *testing.T) {708 t.Run("a repository made public does not reveal what happened while it was private", func(t *testing.T) {
709+ t.Parallel()
683 synctest.Test(t, func(t *testing.T) {710 synctest.Test(t, func(t *testing.T) {
684 ctx, cancel := context.WithCancel(context.Background())711 ctx, cancel := context.WithCancel(context.Background())
685 defer cancel()712 defer cancel()
@@ -713,6 +740,7 @@ func TestEventsKeepTheVisibilityOfTheirTime(t *testing.T) {
713 })740 })
714 741
715 t.Run("a repository deleted and created again does not inherit old events", func(t *testing.T) {742 t.Run("a repository deleted and created again does not inherit old events", func(t *testing.T) {
743+ t.Parallel()
716 synctest.Test(t, func(t *testing.T) {744 synctest.Test(t, func(t *testing.T) {
717 ctx, cancel := context.WithCancel(context.Background())745 ctx, cancel := context.WithCancel(context.Background())
718 defer cancel()746 defer cancel()
@@ -739,6 +767,7 @@ func TestEventsKeepTheVisibilityOfTheirTime(t *testing.T) {
739 })767 })
740 768
741 t.Run("a repository made private tells those who could read it, once", func(t *testing.T) {769 t.Run("a repository made private tells those who could read it, once", func(t *testing.T) {
770+ t.Parallel()
742 synctest.Test(t, func(t *testing.T) {771 synctest.Test(t, func(t *testing.T) {
743 ctx, cancel := context.WithCancel(context.Background())772 ctx, cancel := context.WithCancel(context.Background())
744 defer cancel()773 defer cancel()
@@ -774,6 +803,7 @@ func TestEventsKeepTheVisibilityOfTheirTime(t *testing.T) {
774 })803 })
775 804
776 t.Run("a private repository that never was public stays hidden", func(t *testing.T) {805 t.Run("a private repository that never was public stays hidden", func(t *testing.T) {
806+ t.Parallel()
777 synctest.Test(t, func(t *testing.T) {807 synctest.Test(t, func(t *testing.T) {
778 ctx, cancel := context.WithCancel(context.Background())808 ctx, cancel := context.WithCancel(context.Background())
779 defer cancel()809 defer cancel()
@@ -792,6 +822,7 @@ func TestEventsKeepTheVisibilityOfTheirTime(t *testing.T) {
792 // TestPushEventsKeepTheirSnapshot: a push is told by the repository as it was822 // TestPushEventsKeepTheirSnapshot: a push is told by the repository as it was
793 // when published, not as the store says when the feeder gets to it.823 // when published, not as the store says when the feeder gets to it.
794 func TestPushEventsKeepTheirSnapshot(t *testing.T) {824 func TestPushEventsKeepTheirSnapshot(t *testing.T) {
825+ t.Parallel()
795 tests := []struct {826 tests := []struct {
796 name string827 name string
797 swap func(repo *domain.Repo)828 swap func(repo *domain.Repo)
@@ -805,6 +836,7 @@ func TestPushEventsKeepTheirSnapshot(t *testing.T) {
805 }836 }
806 for _, tt := range tests {837 for _, tt := range tests {
807 t.Run(tt.name, func(t *testing.T) {838 t.Run(tt.name, func(t *testing.T) {
839+ t.Parallel()
808 synctest.Test(t, func(t *testing.T) {840 synctest.Test(t, func(t *testing.T) {
809 ctx, cancel := context.WithCancel(context.Background())841 ctx, cancel := context.WithCancel(context.Background())
810 defer cancel()842 defer cancel()
@@ -835,6 +867,7 @@ func TestPushEventsKeepTheirSnapshot(t *testing.T) {
835 }867 }
836 868
837 t.Run("a push of an unknown repository is told to nobody", func(t *testing.T) {869 t.Run("a push of an unknown repository is told to nobody", func(t *testing.T) {
870+ t.Parallel()
838 synctest.Test(t, func(t *testing.T) {871 synctest.Test(t, func(t *testing.T) {
839 ctx, cancel := context.WithCancel(context.Background())872 ctx, cancel := context.WithCancel(context.Background())
840 defer cancel()873 defer cancel()
M internal/app/maintenance_test.go +9 -0
@@ -25,6 +25,7 @@ import (
2525 // hangs is reported as a bubble deadlock instead of a timeout.
2626
2727 func TestMaintainerDebounces(t *testing.T) {
28+ t.Parallel()
2829 synctest.Test(t, func(t *testing.T) {
2930 ctx := context.Background()
3031 n := name(t, "tools")
@@ -56,6 +57,7 @@ func TestMaintainerDebounces(t *testing.T) {
5657 }
5758
5859 func TestMaintainerOneRunAtATimePerRepo(t *testing.T) {
60+ t.Parallel()
5961 synctest.Test(t, func(t *testing.T) {
6062 ctx := context.Background()
6163 n, other := name(t, "tools"), name(t, "other")
@@ -107,8 +109,10 @@ func TestMaintainerOneRunAtATimePerRepo(t *testing.T) {
107109 }
108110
109111 func TestMaintainerTellsTheReaderAfterARun(t *testing.T) {
112+ t.Parallel()
110113 for label, failure := range map[string]error{"success": nil, "failure": errors.New("boom")} {
111114 t.Run(label, func(t *testing.T) {
115+ t.Parallel()
112116 synctest.Test(t, func(t *testing.T) {
113117 ctx := context.Background()
114118 n := name(t, "tools")
@@ -132,6 +136,7 @@ func TestMaintainerTellsTheReaderAfterARun(t *testing.T) {
132136 }
133137
134138 func TestMaintainerFailureIsLoggedNotFatal(t *testing.T) {
139+ t.Parallel()
135140 synctest.Test(t, func(t *testing.T) {
136141 ctx := context.Background()
137142 n := name(t, "tools")
@@ -153,10 +158,12 @@ func TestMaintainerFailureIsLoggedNotFatal(t *testing.T) {
153158 }
154159
155160 func TestMaintainerClose(t *testing.T) {
161+ t.Parallel()
156162 ctx := context.Background()
157163 n := name(t, "tools")
158164
159165 t.Run("pending runs are dropped", func(t *testing.T) {
166+ t.Parallel()
160167 synctest.Test(t, func(t *testing.T) {
161168 git := mocks.NewGitRepos(t) // no Maintain expected
162169 m := app.NewMaintainer(ctx, git, app.MaintenanceDelay)
@@ -169,6 +176,7 @@ func TestMaintainerClose(t *testing.T) {
169176 })
170177
171178 t.Run("a run in progress is cancelled and awaited", func(t *testing.T) {
179+ t.Parallel()
172180 synctest.Test(t, func(t *testing.T) {
173181 git := mocks.NewGitRepos(t)
174182 var cancelled atomic.Bool
@@ -188,6 +196,7 @@ func TestMaintainerClose(t *testing.T) {
188196 }
189197
190198 func TestMaintainerConcurrentSchedule(t *testing.T) {
199+ t.Parallel()
191200 synctest.Test(t, func(t *testing.T) {
192201 ctx := context.Background()
193202 git := mocks.NewGitRepos(t)
@@ -25,6 +25,7 @@ import (
25 // hangs is reported as a bubble deadlock instead of a timeout.25 // hangs is reported as a bubble deadlock instead of a timeout.
26 26
27 func TestMaintainerDebounces(t *testing.T) {27 func TestMaintainerDebounces(t *testing.T) {
28+ t.Parallel()
28 synctest.Test(t, func(t *testing.T) {29 synctest.Test(t, func(t *testing.T) {
29 ctx := context.Background()30 ctx := context.Background()
30 n := name(t, "tools")31 n := name(t, "tools")
@@ -56,6 +57,7 @@ func TestMaintainerDebounces(t *testing.T) {
56 }57 }
57 58
58 func TestMaintainerOneRunAtATimePerRepo(t *testing.T) {59 func TestMaintainerOneRunAtATimePerRepo(t *testing.T) {
60+ t.Parallel()
59 synctest.Test(t, func(t *testing.T) {61 synctest.Test(t, func(t *testing.T) {
60 ctx := context.Background()62 ctx := context.Background()
61 n, other := name(t, "tools"), name(t, "other")63 n, other := name(t, "tools"), name(t, "other")
@@ -107,8 +109,10 @@ func TestMaintainerOneRunAtATimePerRepo(t *testing.T) {
107 }109 }
108 110
109 func TestMaintainerTellsTheReaderAfterARun(t *testing.T) {111 func TestMaintainerTellsTheReaderAfterARun(t *testing.T) {
112+ t.Parallel()
110 for label, failure := range map[string]error{"success": nil, "failure": errors.New("boom")} {113 for label, failure := range map[string]error{"success": nil, "failure": errors.New("boom")} {
111 t.Run(label, func(t *testing.T) {114 t.Run(label, func(t *testing.T) {
115+ t.Parallel()
112 synctest.Test(t, func(t *testing.T) {116 synctest.Test(t, func(t *testing.T) {
113 ctx := context.Background()117 ctx := context.Background()
114 n := name(t, "tools")118 n := name(t, "tools")
@@ -132,6 +136,7 @@ func TestMaintainerTellsTheReaderAfterARun(t *testing.T) {
132 }136 }
133 137
134 func TestMaintainerFailureIsLoggedNotFatal(t *testing.T) {138 func TestMaintainerFailureIsLoggedNotFatal(t *testing.T) {
139+ t.Parallel()
135 synctest.Test(t, func(t *testing.T) {140 synctest.Test(t, func(t *testing.T) {
136 ctx := context.Background()141 ctx := context.Background()
137 n := name(t, "tools")142 n := name(t, "tools")
@@ -153,10 +158,12 @@ func TestMaintainerFailureIsLoggedNotFatal(t *testing.T) {
153 }158 }
154 159
155 func TestMaintainerClose(t *testing.T) {160 func TestMaintainerClose(t *testing.T) {
161+ t.Parallel()
156 ctx := context.Background()162 ctx := context.Background()
157 n := name(t, "tools")163 n := name(t, "tools")
158 164
159 t.Run("pending runs are dropped", func(t *testing.T) {165 t.Run("pending runs are dropped", func(t *testing.T) {
166+ t.Parallel()
160 synctest.Test(t, func(t *testing.T) {167 synctest.Test(t, func(t *testing.T) {
161 git := mocks.NewGitRepos(t) // no Maintain expected168 git := mocks.NewGitRepos(t) // no Maintain expected
162 m := app.NewMaintainer(ctx, git, app.MaintenanceDelay)169 m := app.NewMaintainer(ctx, git, app.MaintenanceDelay)
@@ -169,6 +176,7 @@ func TestMaintainerClose(t *testing.T) {
169 })176 })
170 177
171 t.Run("a run in progress is cancelled and awaited", func(t *testing.T) {178 t.Run("a run in progress is cancelled and awaited", func(t *testing.T) {
179+ t.Parallel()
172 synctest.Test(t, func(t *testing.T) {180 synctest.Test(t, func(t *testing.T) {
173 git := mocks.NewGitRepos(t)181 git := mocks.NewGitRepos(t)
174 var cancelled atomic.Bool182 var cancelled atomic.Bool
@@ -188,6 +196,7 @@ func TestMaintainerClose(t *testing.T) {
188 }196 }
189 197
190 func TestMaintainerConcurrentSchedule(t *testing.T) {198 func TestMaintainerConcurrentSchedule(t *testing.T) {
199+ t.Parallel()
191 synctest.Test(t, func(t *testing.T) {200 synctest.Test(t, func(t *testing.T) {
192 ctx := context.Background()201 ctx := context.Background()
193 git := mocks.NewGitRepos(t)202 git := mocks.NewGitRepos(t)
M internal/app/mergerequests_delete_test.go +10 -0
@@ -16,8 +16,10 @@ import (
1616
1717 // A merge can delete its source branch; the push event carries both refs.
1818 func TestMergeDeletesTheSourceBranch(t *testing.T) {
19+ t.Parallel()
1920 for _, mode := range []domain.MergeMode{domain.MergeModeFastForward, domain.MergeModeSquash} {
2021 t.Run(string(mode), func(t *testing.T) {
22+ t.Parallel()
2123 r := newMRRig(t)
2224 main := r.seed()
2325 head := r.feature("feat")
@@ -47,6 +49,7 @@ func TestMergeDeletesTheSourceBranch(t *testing.T) {
4749 }
4850
4951 t.Run("without the option the branch stays", func(t *testing.T) {
52+ t.Parallel()
5053 r := newMRRig(t)
5154 r.seed()
5255 head := r.feature("feat")
@@ -61,7 +64,9 @@ func TestMergeDeletesTheSourceBranch(t *testing.T) {
6164 // Deleting the source is refused before the merge when the branch is still
6265 // needed: nothing changes.
6366 func TestMergeRefusesToDeleteANeededBranch(t *testing.T) {
67+ t.Parallel()
6468 t.Run("another open request uses it as its source", func(t *testing.T) {
69+ t.Parallel()
6570 r := newMRRig(t)
6671 main := r.seed()
6772 head := r.feature("feat")
@@ -82,6 +87,7 @@ func TestMergeRefusesToDeleteANeededBranch(t *testing.T) {
8287 })
8388
8489 t.Run("another open request targets it", func(t *testing.T) {
90+ t.Parallel()
8591 r := newMRRig(t)
8692 r.seed()
8793 head := r.feature("feat")
@@ -97,6 +103,7 @@ func TestMergeRefusesToDeleteANeededBranch(t *testing.T) {
97103 })
98104
99105 t.Run("the default branch", func(t *testing.T) {
106+ t.Parallel()
100107 r := newMRRig(t)
101108 main := r.seed()
102109 r.sh("checkout", "-q", "-B", "old", "main~1")
@@ -115,6 +122,7 @@ func TestMergeRefusesToDeleteANeededBranch(t *testing.T) {
115122 // After a squash with deletion, gc may prune the reviewed commits: the merged
116123 // request still reads, without commits or diff.
117124 func TestSquashMergedRequestReadsAfterItsCommitsArePruned(t *testing.T) {
125+ t.Parallel()
118126 r := newMRRig(t)
119127 r.seed()
120128 head := r.feature("feat")
@@ -141,6 +149,7 @@ func TestSquashMergedRequestReadsAfterItsCommitsArePruned(t *testing.T) {
141149 // A request opened on a branch a merge is deleting waits for the merge lock
142150 // and then sees the branch is gone.
143151 func TestCreateAfterAMergeDeletedTheBranch(t *testing.T) {
152+ t.Parallel()
144153 r := newMRRig(t)
145154 r.seed()
146155 head := r.feature("feat")
@@ -165,6 +174,7 @@ func (g movingGit) DeleteBranch(ctx context.Context, repo domain.RepoName, branc
165174
166175 // A branch that moves after the merge is kept: the merge stands.
167176 func TestMergeKeepsASourceThatMovedAfterTheMerge(t *testing.T) {
177+ t.Parallel()
168178 r := newMRRig(t)
169179 r.seed()
170180 head := r.feature("feat")
@@ -16,8 +16,10 @@ import (
16 16
17 // A merge can delete its source branch; the push event carries both refs.17 // A merge can delete its source branch; the push event carries both refs.
18 func TestMergeDeletesTheSourceBranch(t *testing.T) {18 func TestMergeDeletesTheSourceBranch(t *testing.T) {
19+ t.Parallel()
19 for _, mode := range []domain.MergeMode{domain.MergeModeFastForward, domain.MergeModeSquash} {20 for _, mode := range []domain.MergeMode{domain.MergeModeFastForward, domain.MergeModeSquash} {
20 t.Run(string(mode), func(t *testing.T) {21 t.Run(string(mode), func(t *testing.T) {
22+ t.Parallel()
21 r := newMRRig(t)23 r := newMRRig(t)
22 main := r.seed()24 main := r.seed()
23 head := r.feature("feat")25 head := r.feature("feat")
@@ -47,6 +49,7 @@ func TestMergeDeletesTheSourceBranch(t *testing.T) {
47 }49 }
48 50
49 t.Run("without the option the branch stays", func(t *testing.T) {51 t.Run("without the option the branch stays", func(t *testing.T) {
52+ t.Parallel()
50 r := newMRRig(t)53 r := newMRRig(t)
51 r.seed()54 r.seed()
52 head := r.feature("feat")55 head := r.feature("feat")
@@ -61,7 +64,9 @@ func TestMergeDeletesTheSourceBranch(t *testing.T) {
61 // Deleting the source is refused before the merge when the branch is still64 // Deleting the source is refused before the merge when the branch is still
62 // needed: nothing changes.65 // needed: nothing changes.
63 func TestMergeRefusesToDeleteANeededBranch(t *testing.T) {66 func TestMergeRefusesToDeleteANeededBranch(t *testing.T) {
67+ t.Parallel()
64 t.Run("another open request uses it as its source", func(t *testing.T) {68 t.Run("another open request uses it as its source", func(t *testing.T) {
69+ t.Parallel()
65 r := newMRRig(t)70 r := newMRRig(t)
66 main := r.seed()71 main := r.seed()
67 head := r.feature("feat")72 head := r.feature("feat")
@@ -82,6 +87,7 @@ func TestMergeRefusesToDeleteANeededBranch(t *testing.T) {
82 })87 })
83 88
84 t.Run("another open request targets it", func(t *testing.T) {89 t.Run("another open request targets it", func(t *testing.T) {
90+ t.Parallel()
85 r := newMRRig(t)91 r := newMRRig(t)
86 r.seed()92 r.seed()
87 head := r.feature("feat")93 head := r.feature("feat")
@@ -97,6 +103,7 @@ func TestMergeRefusesToDeleteANeededBranch(t *testing.T) {
97 })103 })
98 104
99 t.Run("the default branch", func(t *testing.T) {105 t.Run("the default branch", func(t *testing.T) {
106+ t.Parallel()
100 r := newMRRig(t)107 r := newMRRig(t)
101 main := r.seed()108 main := r.seed()
102 r.sh("checkout", "-q", "-B", "old", "main~1")109 r.sh("checkout", "-q", "-B", "old", "main~1")
@@ -115,6 +122,7 @@ func TestMergeRefusesToDeleteANeededBranch(t *testing.T) {
115 // After a squash with deletion, gc may prune the reviewed commits: the merged122 // After a squash with deletion, gc may prune the reviewed commits: the merged
116 // request still reads, without commits or diff.123 // request still reads, without commits or diff.
117 func TestSquashMergedRequestReadsAfterItsCommitsArePruned(t *testing.T) {124 func TestSquashMergedRequestReadsAfterItsCommitsArePruned(t *testing.T) {
125+ t.Parallel()
118 r := newMRRig(t)126 r := newMRRig(t)
119 r.seed()127 r.seed()
120 head := r.feature("feat")128 head := r.feature("feat")
@@ -141,6 +149,7 @@ func TestSquashMergedRequestReadsAfterItsCommitsArePruned(t *testing.T) {
141 // A request opened on a branch a merge is deleting waits for the merge lock149 // A request opened on a branch a merge is deleting waits for the merge lock
142 // and then sees the branch is gone.150 // and then sees the branch is gone.
143 func TestCreateAfterAMergeDeletedTheBranch(t *testing.T) {151 func TestCreateAfterAMergeDeletedTheBranch(t *testing.T) {
152+ t.Parallel()
144 r := newMRRig(t)153 r := newMRRig(t)
145 r.seed()154 r.seed()
146 head := r.feature("feat")155 head := r.feature("feat")
@@ -165,6 +174,7 @@ func (g movingGit) DeleteBranch(ctx context.Context, repo domain.RepoName, branc
165 174
166 // A branch that moves after the merge is kept: the merge stands.175 // A branch that moves after the merge is kept: the merge stands.
167 func TestMergeKeepsASourceThatMovedAfterTheMerge(t *testing.T) {176 func TestMergeKeepsASourceThatMovedAfterTheMerge(t *testing.T) {
177+ t.Parallel()
168 r := newMRRig(t)178 r := newMRRig(t)
169 r.seed()179 r.seed()
170 head := r.feature("feat")180 head := r.feature("feat")
M internal/app/mergerequests_test.go +6 -0
@@ -49,10 +49,12 @@ func newMRUnit(t *testing.T) *mrUnit {
4949 // GO-004: a failure to read Git is an error for the log, not a field violation
5050 // that would show its text to the caller.
5151 func TestCreateReturnsGitFailuresAsErrors(t *testing.T) {
52+ t.Parallel()
5253 boom := errors.New("fatal: /srv/ermite/repos/proj.git: permission denied")
5354 params := app.CreateMergeRequestParams{Title: "t", Source: "feat", Target: "main"}
5455
5556 t.Run("reading a branch", func(t *testing.T) {
57+ t.Parallel()
5658 u := newMRUnit(t)
5759 u.meta.EXPECT().Get(mock.Anything, u.repo.Name).Return(u.repo, nil)
5860 u.git.EXPECT().BranchHead(mock.Anything, u.repo.Name, "feat").Return(domain.ObjectID{}, boom)
@@ -64,6 +66,7 @@ func TestCreateReturnsGitFailuresAsErrors(t *testing.T) {
6466 })
6567
6668 t.Run("reading the default branch", func(t *testing.T) {
69+ t.Parallel()
6770 u := newMRUnit(t)
6871 u.meta.EXPECT().Get(mock.Anything, u.repo.Name).Return(u.repo, nil)
6972 u.repos.EXPECT().DefaultBranch(mock.Anything, u.repo.Name).Return("", false, boom)
@@ -74,6 +77,7 @@ func TestCreateReturnsGitFailuresAsErrors(t *testing.T) {
7477 })
7578
7679 t.Run("a branch that does not exist stays a violation", func(t *testing.T) {
80+ t.Parallel()
7781 u := newMRUnit(t)
7882 u.meta.EXPECT().Get(mock.Anything, u.repo.Name).Return(u.repo, nil)
7983 u.git.EXPECT().BranchHead(mock.Anything, u.repo.Name, "feat").Return(domain.ObjectID{}, domain.ErrNotFound)
@@ -91,12 +95,14 @@ func someID(t *testing.T) domain.ObjectID {
9195 // GO-003: the settings are read again under the merge lock; a failure of that
9296 // read is returned, and a repository deleted meanwhile is not found.
9397 func TestMergeReturnsTheFailureToReadTheSettings(t *testing.T) {
98+ t.Parallel()
9499 boom := errors.New("kv: closed")
95100 for label, tc := range map[string]struct{ err, want error }{
96101 "read failure": {boom, boom},
97102 "deleted meanwhile": {domain.ErrNotFound, domain.ErrNotFound},
98103 } {
99104 t.Run(label, func(t *testing.T) {
105+ t.Parallel()
100106 u := newMRUnit(t)
101107 u.meta.EXPECT().Get(mock.Anything, u.repo.Name).Return(u.repo, nil).Once()
102108 u.meta.EXPECT().Get(mock.Anything, u.repo.Name).Return(domain.Repo{}, tc.err).Once()
@@ -49,10 +49,12 @@ func newMRUnit(t *testing.T) *mrUnit {
49 // GO-004: a failure to read Git is an error for the log, not a field violation49 // GO-004: a failure to read Git is an error for the log, not a field violation
50 // that would show its text to the caller.50 // that would show its text to the caller.
51 func TestCreateReturnsGitFailuresAsErrors(t *testing.T) {51 func TestCreateReturnsGitFailuresAsErrors(t *testing.T) {
52+ t.Parallel()
52 boom := errors.New("fatal: /srv/ermite/repos/proj.git: permission denied")53 boom := errors.New("fatal: /srv/ermite/repos/proj.git: permission denied")
53 params := app.CreateMergeRequestParams{Title: "t", Source: "feat", Target: "main"}54 params := app.CreateMergeRequestParams{Title: "t", Source: "feat", Target: "main"}
54 55
55 t.Run("reading a branch", func(t *testing.T) {56 t.Run("reading a branch", func(t *testing.T) {
57+ t.Parallel()
56 u := newMRUnit(t)58 u := newMRUnit(t)
57 u.meta.EXPECT().Get(mock.Anything, u.repo.Name).Return(u.repo, nil)59 u.meta.EXPECT().Get(mock.Anything, u.repo.Name).Return(u.repo, nil)
58 u.git.EXPECT().BranchHead(mock.Anything, u.repo.Name, "feat").Return(domain.ObjectID{}, boom)60 u.git.EXPECT().BranchHead(mock.Anything, u.repo.Name, "feat").Return(domain.ObjectID{}, boom)
@@ -64,6 +66,7 @@ func TestCreateReturnsGitFailuresAsErrors(t *testing.T) {
64 })66 })
65 67
66 t.Run("reading the default branch", func(t *testing.T) {68 t.Run("reading the default branch", func(t *testing.T) {
69+ t.Parallel()
67 u := newMRUnit(t)70 u := newMRUnit(t)
68 u.meta.EXPECT().Get(mock.Anything, u.repo.Name).Return(u.repo, nil)71 u.meta.EXPECT().Get(mock.Anything, u.repo.Name).Return(u.repo, nil)
69 u.repos.EXPECT().DefaultBranch(mock.Anything, u.repo.Name).Return("", false, boom)72 u.repos.EXPECT().DefaultBranch(mock.Anything, u.repo.Name).Return("", false, boom)
@@ -74,6 +77,7 @@ func TestCreateReturnsGitFailuresAsErrors(t *testing.T) {
74 })77 })
75 78
76 t.Run("a branch that does not exist stays a violation", func(t *testing.T) {79 t.Run("a branch that does not exist stays a violation", func(t *testing.T) {
80+ t.Parallel()
77 u := newMRUnit(t)81 u := newMRUnit(t)
78 u.meta.EXPECT().Get(mock.Anything, u.repo.Name).Return(u.repo, nil)82 u.meta.EXPECT().Get(mock.Anything, u.repo.Name).Return(u.repo, nil)
79 u.git.EXPECT().BranchHead(mock.Anything, u.repo.Name, "feat").Return(domain.ObjectID{}, domain.ErrNotFound)83 u.git.EXPECT().BranchHead(mock.Anything, u.repo.Name, "feat").Return(domain.ObjectID{}, domain.ErrNotFound)
@@ -91,12 +95,14 @@ func someID(t *testing.T) domain.ObjectID {
91 // GO-003: the settings are read again under the merge lock; a failure of that95 // GO-003: the settings are read again under the merge lock; a failure of that
92 // read is returned, and a repository deleted meanwhile is not found.96 // read is returned, and a repository deleted meanwhile is not found.
93 func TestMergeReturnsTheFailureToReadTheSettings(t *testing.T) {97 func TestMergeReturnsTheFailureToReadTheSettings(t *testing.T) {
98+ t.Parallel()
94 boom := errors.New("kv: closed")99 boom := errors.New("kv: closed")
95 for label, tc := range map[string]struct{ err, want error }{100 for label, tc := range map[string]struct{ err, want error }{
96 "read failure": {boom, boom},101 "read failure": {boom, boom},
97 "deleted meanwhile": {domain.ErrNotFound, domain.ErrNotFound},102 "deleted meanwhile": {domain.ErrNotFound, domain.ErrNotFound},
98 } {103 } {
99 t.Run(label, func(t *testing.T) {104 t.Run(label, func(t *testing.T) {
105+ t.Parallel()
100 u := newMRUnit(t)106 u := newMRUnit(t)
101 u.meta.EXPECT().Get(mock.Anything, u.repo.Name).Return(u.repo, nil).Once()107 u.meta.EXPECT().Get(mock.Anything, u.repo.Name).Return(u.repo, nil).Once()
102 u.meta.EXPECT().Get(mock.Anything, u.repo.Name).Return(domain.Repo{}, tc.err).Once()108 u.meta.EXPECT().Get(mock.Anything, u.repo.Name).Return(domain.Repo{}, tc.err).Once()
M internal/app/pushes_test.go +25 -0
@@ -52,6 +52,7 @@ const (
5252 )
5353
5454 func TestPushesOpen(t *testing.T) {
55+ t.Parallel()
5556 ctx := context.Background()
5657 n := name(t, "tools")
5758 priv := domain.Repo{Name: n, Visibility: domain.VisibilityPrivate}
@@ -79,6 +80,7 @@ func TestPushesOpen(t *testing.T) {
7980 }
8081 for _, tt := range tests {
8182 t.Run(tt.name, func(t *testing.T) {
83+ t.Parallel()
8284 f := newPushFixture(t)
8385 if !tt.p.IsAnonymous() {
8486 if tt.repo != nil {
@@ -98,6 +100,7 @@ func TestPushesOpen(t *testing.T) {
98100 }
99101
100102 t.Run("creates a private repository and publishes RepoCreated", func(t *testing.T) {
103+ t.Parallel()
101104 f := newPushFixture(t)
102105 f.meta.EXPECT().Get(ctx, n).Return(domain.Repo{}, domain.ErrNotFound)
103106 f.git.EXPECT().Stage(ctx).Return(ports.StagedID("s1"), nil)
@@ -110,6 +113,7 @@ func TestPushesOpen(t *testing.T) {
110113 })
111114
112115 t.Run("a concurrent first push wins: this one pushes to its repository", func(t *testing.T) {
116+ t.Parallel()
113117 f := newPushFixture(t)
114118 f.meta.EXPECT().Get(ctx, n).Return(domain.Repo{}, domain.ErrNotFound).Once()
115119 f.git.EXPECT().Stage(ctx).Return(ports.StagedID("s1"), nil)
@@ -122,6 +126,7 @@ func TestPushesOpen(t *testing.T) {
122126 })
123127
124128 t.Run("store failure", func(t *testing.T) {
129+ t.Parallel()
125130 f := newPushFixture(t)
126131 boom := errors.New("boom")
127132 f.meta.EXPECT().Get(ctx, n).Return(domain.Repo{}, boom)
@@ -130,6 +135,7 @@ func TestPushesOpen(t *testing.T) {
130135 })
131136
132137 t.Run("create failure is returned and nothing is published", func(t *testing.T) {
138+ t.Parallel()
133139 f := newPushFixture(t)
134140 boom := errors.New("boom")
135141 f.meta.EXPECT().Get(ctx, n).Return(domain.Repo{}, domain.ErrNotFound)
@@ -140,6 +146,7 @@ func TestPushesOpen(t *testing.T) {
140146 }
141147
142148 func TestPushesCheck(t *testing.T) {
149+ t.Parallel()
143150 ctx := context.Background()
144151 n := name(t, "tools")
145152 ups := []domain.RefUpdate{{Old: domain.ZeroOID, New: oidA, Ref: "refs/heads/main"}}
@@ -147,29 +154,34 @@ func TestPushesCheck(t *testing.T) {
147154 reader := domain.TokenPrincipal(domain.Token{ID: "r1", Scopes: scopes(domain.ScopeRepoRead)})
148155
149156 t.Run("root is always valid and needs no lookup", func(t *testing.T) {
157+ t.Parallel()
150158 f := newPushFixture(t) // the token store mock expects no call
151159 require.NoError(t, f.pushes.Check(ctx, domain.Root(), n, ups))
152160 })
153161
154162 t.Run("a writer whose token still exists", func(t *testing.T) {
163+ t.Parallel()
155164 f := newPushFixture(t)
156165 f.tokens.EXPECT().Get(ctx, "w1").Return(domain.Token{ID: "w1", Scopes: scopes(domain.ScopeRepoWrite)}, nil)
157166 require.NoError(t, f.pushes.Check(ctx, writer, n, ups))
158167 })
159168
160169 t.Run("a token revoked during the push is rejected", func(t *testing.T) {
170+ t.Parallel()
161171 f := newPushFixture(t)
162172 f.tokens.EXPECT().Get(ctx, "w1").Return(domain.Token{}, domain.ErrNotFound)
163173 assert.ErrorIs(t, f.pushes.Check(ctx, writer, n, ups), domain.ErrRejected)
164174 })
165175
166176 t.Run("a token that expired during the push is rejected", func(t *testing.T) {
177+ t.Parallel()
167178 f := newPushFixture(t)
168179 f.tokens.EXPECT().Get(ctx, "w1").Return(domain.Token{ID: "w1", Scopes: scopes(domain.ScopeRepoWrite), ExpiresAt: now.Add(-time.Second)}, nil)
169180 assert.ErrorIs(t, f.pushes.Check(ctx, writer, n, ups), domain.ErrRejected)
170181 })
171182
172183 t.Run("a store failure fails closed", func(t *testing.T) {
184+ t.Parallel()
173185 f := newPushFixture(t)
174186 boom := errors.New("boom")
175187 f.tokens.EXPECT().Get(ctx, "w1").Return(domain.Token{}, boom)
@@ -179,18 +191,21 @@ func TestPushesCheck(t *testing.T) {
179191 })
180192
181193 t.Run("a token without write access is rejected", func(t *testing.T) {
194+ t.Parallel()
182195 f := newPushFixture(t)
183196 f.tokens.EXPECT().Get(ctx, "r1").Return(domain.Token{ID: "r1", Scopes: scopes(domain.ScopeRepoRead)}, nil)
184197 assert.ErrorIs(t, f.pushes.Check(ctx, reader, n, ups), domain.ErrRejected)
185198 })
186199
187200 t.Run("anonymous is rejected", func(t *testing.T) {
201+ t.Parallel()
188202 f := newPushFixture(t)
189203 assert.ErrorIs(t, f.pushes.Check(ctx, domain.Anonymous(), n, ups), domain.ErrRejected)
190204 })
191205 }
192206
193207 func TestPushesRecord(t *testing.T) {
208+ t.Parallel()
194209 ctx := context.Background()
195210 n := name(t, "tools")
196211 p := tokenPrincipal(domain.ScopeRepoWrite)
@@ -201,6 +216,7 @@ func TestPushesRecord(t *testing.T) {
201216 }
202217
203218 t.Run("sets pushed_at, publishes the exact updates, schedules maintenance", func(t *testing.T) {
219+ t.Parallel()
204220 f := newPushFixture(t)
205221 f.sched.EXPECT().Schedule(ctx, n).Return().Once()
206222 repo := domain.Repo{Name: n, Visibility: domain.VisibilityPrivate, CreatedAt: now.Add(-time.Hour)}
@@ -213,6 +229,7 @@ func TestPushesRecord(t *testing.T) {
213229 })
214230
215231 t.Run("tells the reader that the repository changed", func(t *testing.T) {
232+ t.Parallel()
216233 f := newPushFixture(t)
217234 f.sched.EXPECT().Schedule(ctx, n).Return().Once()
218235 fresh := mocks.NewRepoFreshener(t)
@@ -226,6 +243,7 @@ func TestPushesRecord(t *testing.T) {
226243 })
227244
228245 t.Run("pushed_at retries after a conflict and never goes backwards", func(t *testing.T) {
246+ t.Parallel()
229247 f := newPushFixture(t)
230248 f.sched.EXPECT().Schedule(ctx, n).Return().Once()
231249 repo := domain.Repo{Name: n}
@@ -239,6 +257,7 @@ func TestPushesRecord(t *testing.T) {
239257 })
240258
241259 t.Run("a deleted repository is not an error", func(t *testing.T) {
260+ t.Parallel()
242261 f := newPushFixture(t)
243262 f.sched.EXPECT().Schedule(ctx, n).Return().Once()
244263 f.meta.EXPECT().Get(ctx, n).Return(domain.Repo{}, domain.ErrNotFound)
@@ -248,6 +267,7 @@ func TestPushesRecord(t *testing.T) {
248267 })
249268
250269 t.Run("a repository deleted before the default branch step is not an error", func(t *testing.T) {
270+ t.Parallel()
251271 f := newPushFixture(t)
252272 f.sched.EXPECT().Schedule(ctx, n).Return().Once()
253273 f.meta.EXPECT().Get(ctx, n).Return(domain.Repo{}, domain.ErrNotFound)
@@ -257,6 +277,7 @@ func TestPushesRecord(t *testing.T) {
257277 })
258278
259279 t.Run("a repository deleted before HEAD is set is not an error", func(t *testing.T) {
280+ t.Parallel()
260281 f := newPushFixture(t)
261282 f.sched.EXPECT().Schedule(ctx, n).Return().Once()
262283 f.meta.EXPECT().Get(ctx, n).Return(domain.Repo{}, domain.ErrNotFound)
@@ -267,6 +288,7 @@ func TestPushesRecord(t *testing.T) {
267288 })
268289
269290 t.Run("every step runs when one fails", func(t *testing.T) {
291+ t.Parallel()
270292 f := newPushFixture(t)
271293 f.sched.EXPECT().Schedule(ctx, n).Return().Once()
272294 boom := errors.New("boom")
@@ -279,6 +301,7 @@ func TestPushesRecord(t *testing.T) {
279301 }
280302
281303 func TestPushesDefaultBranchRule(t *testing.T) {
304+ t.Parallel()
282305 ctx := context.Background()
283306 n := name(t, "tools")
284307 p := domain.Root()
@@ -303,6 +326,7 @@ func TestPushesDefaultBranchRule(t *testing.T) {
303326 }
304327 for _, tt := range tests {
305328 t.Run(tt.name, func(t *testing.T) {
329+ t.Parallel()
306330 f := newPushFixture(t)
307331 f.sched.EXPECT().Schedule(ctx, n).Return().Once()
308332 f.meta.EXPECT().Get(ctx, n).Return(domain.Repo{Name: n, PushedAt: now}, nil)
@@ -316,6 +340,7 @@ func TestPushesDefaultBranchRule(t *testing.T) {
316340 }
317341
318342 t.Run("set failure is reported", func(t *testing.T) {
343+ t.Parallel()
319344 f := newPushFixture(t)
320345 f.sched.EXPECT().Schedule(ctx, n).Return().Once()
321346 boom := errors.New("boom")
@@ -52,6 +52,7 @@ const (
52 )52 )
53 53
54 func TestPushesOpen(t *testing.T) {54 func TestPushesOpen(t *testing.T) {
55+ t.Parallel()
55 ctx := context.Background()56 ctx := context.Background()
56 n := name(t, "tools")57 n := name(t, "tools")
57 priv := domain.Repo{Name: n, Visibility: domain.VisibilityPrivate}58 priv := domain.Repo{Name: n, Visibility: domain.VisibilityPrivate}
@@ -79,6 +80,7 @@ func TestPushesOpen(t *testing.T) {
79 }80 }
80 for _, tt := range tests {81 for _, tt := range tests {
81 t.Run(tt.name, func(t *testing.T) {82 t.Run(tt.name, func(t *testing.T) {
83+ t.Parallel()
82 f := newPushFixture(t)84 f := newPushFixture(t)
83 if !tt.p.IsAnonymous() {85 if !tt.p.IsAnonymous() {
84 if tt.repo != nil {86 if tt.repo != nil {
@@ -98,6 +100,7 @@ func TestPushesOpen(t *testing.T) {
98 }100 }
99 101
100 t.Run("creates a private repository and publishes RepoCreated", func(t *testing.T) {102 t.Run("creates a private repository and publishes RepoCreated", func(t *testing.T) {
103+ t.Parallel()
101 f := newPushFixture(t)104 f := newPushFixture(t)
102 f.meta.EXPECT().Get(ctx, n).Return(domain.Repo{}, domain.ErrNotFound)105 f.meta.EXPECT().Get(ctx, n).Return(domain.Repo{}, domain.ErrNotFound)
103 f.git.EXPECT().Stage(ctx).Return(ports.StagedID("s1"), nil)106 f.git.EXPECT().Stage(ctx).Return(ports.StagedID("s1"), nil)
@@ -110,6 +113,7 @@ func TestPushesOpen(t *testing.T) {
110 })113 })
111 114
112 t.Run("a concurrent first push wins: this one pushes to its repository", func(t *testing.T) {115 t.Run("a concurrent first push wins: this one pushes to its repository", func(t *testing.T) {
116+ t.Parallel()
113 f := newPushFixture(t)117 f := newPushFixture(t)
114 f.meta.EXPECT().Get(ctx, n).Return(domain.Repo{}, domain.ErrNotFound).Once()118 f.meta.EXPECT().Get(ctx, n).Return(domain.Repo{}, domain.ErrNotFound).Once()
115 f.git.EXPECT().Stage(ctx).Return(ports.StagedID("s1"), nil)119 f.git.EXPECT().Stage(ctx).Return(ports.StagedID("s1"), nil)
@@ -122,6 +126,7 @@ func TestPushesOpen(t *testing.T) {
122 })126 })
123 127
124 t.Run("store failure", func(t *testing.T) {128 t.Run("store failure", func(t *testing.T) {
129+ t.Parallel()
125 f := newPushFixture(t)130 f := newPushFixture(t)
126 boom := errors.New("boom")131 boom := errors.New("boom")
127 f.meta.EXPECT().Get(ctx, n).Return(domain.Repo{}, boom)132 f.meta.EXPECT().Get(ctx, n).Return(domain.Repo{}, boom)
@@ -130,6 +135,7 @@ func TestPushesOpen(t *testing.T) {
130 })135 })
131 136
132 t.Run("create failure is returned and nothing is published", func(t *testing.T) {137 t.Run("create failure is returned and nothing is published", func(t *testing.T) {
138+ t.Parallel()
133 f := newPushFixture(t)139 f := newPushFixture(t)
134 boom := errors.New("boom")140 boom := errors.New("boom")
135 f.meta.EXPECT().Get(ctx, n).Return(domain.Repo{}, domain.ErrNotFound)141 f.meta.EXPECT().Get(ctx, n).Return(domain.Repo{}, domain.ErrNotFound)
@@ -140,6 +146,7 @@ func TestPushesOpen(t *testing.T) {
140 }146 }
141 147
142 func TestPushesCheck(t *testing.T) {148 func TestPushesCheck(t *testing.T) {
149+ t.Parallel()
143 ctx := context.Background()150 ctx := context.Background()
144 n := name(t, "tools")151 n := name(t, "tools")
145 ups := []domain.RefUpdate{{Old: domain.ZeroOID, New: oidA, Ref: "refs/heads/main"}}152 ups := []domain.RefUpdate{{Old: domain.ZeroOID, New: oidA, Ref: "refs/heads/main"}}
@@ -147,29 +154,34 @@ func TestPushesCheck(t *testing.T) {
147 reader := domain.TokenPrincipal(domain.Token{ID: "r1", Scopes: scopes(domain.ScopeRepoRead)})154 reader := domain.TokenPrincipal(domain.Token{ID: "r1", Scopes: scopes(domain.ScopeRepoRead)})
148 155
149 t.Run("root is always valid and needs no lookup", func(t *testing.T) {156 t.Run("root is always valid and needs no lookup", func(t *testing.T) {
157+ t.Parallel()
150 f := newPushFixture(t) // the token store mock expects no call158 f := newPushFixture(t) // the token store mock expects no call
151 require.NoError(t, f.pushes.Check(ctx, domain.Root(), n, ups))159 require.NoError(t, f.pushes.Check(ctx, domain.Root(), n, ups))
152 })160 })
153 161
154 t.Run("a writer whose token still exists", func(t *testing.T) {162 t.Run("a writer whose token still exists", func(t *testing.T) {
163+ t.Parallel()
155 f := newPushFixture(t)164 f := newPushFixture(t)
156 f.tokens.EXPECT().Get(ctx, "w1").Return(domain.Token{ID: "w1", Scopes: scopes(domain.ScopeRepoWrite)}, nil)165 f.tokens.EXPECT().Get(ctx, "w1").Return(domain.Token{ID: "w1", Scopes: scopes(domain.ScopeRepoWrite)}, nil)
157 require.NoError(t, f.pushes.Check(ctx, writer, n, ups))166 require.NoError(t, f.pushes.Check(ctx, writer, n, ups))
158 })167 })
159 168
160 t.Run("a token revoked during the push is rejected", func(t *testing.T) {169 t.Run("a token revoked during the push is rejected", func(t *testing.T) {
170+ t.Parallel()
161 f := newPushFixture(t)171 f := newPushFixture(t)
162 f.tokens.EXPECT().Get(ctx, "w1").Return(domain.Token{}, domain.ErrNotFound)172 f.tokens.EXPECT().Get(ctx, "w1").Return(domain.Token{}, domain.ErrNotFound)
163 assert.ErrorIs(t, f.pushes.Check(ctx, writer, n, ups), domain.ErrRejected)173 assert.ErrorIs(t, f.pushes.Check(ctx, writer, n, ups), domain.ErrRejected)
164 })174 })
165 175
166 t.Run("a token that expired during the push is rejected", func(t *testing.T) {176 t.Run("a token that expired during the push is rejected", func(t *testing.T) {
177+ t.Parallel()
167 f := newPushFixture(t)178 f := newPushFixture(t)
168 f.tokens.EXPECT().Get(ctx, "w1").Return(domain.Token{ID: "w1", Scopes: scopes(domain.ScopeRepoWrite), ExpiresAt: now.Add(-time.Second)}, nil)179 f.tokens.EXPECT().Get(ctx, "w1").Return(domain.Token{ID: "w1", Scopes: scopes(domain.ScopeRepoWrite), ExpiresAt: now.Add(-time.Second)}, nil)
169 assert.ErrorIs(t, f.pushes.Check(ctx, writer, n, ups), domain.ErrRejected)180 assert.ErrorIs(t, f.pushes.Check(ctx, writer, n, ups), domain.ErrRejected)
170 })181 })
171 182
172 t.Run("a store failure fails closed", func(t *testing.T) {183 t.Run("a store failure fails closed", func(t *testing.T) {
184+ t.Parallel()
173 f := newPushFixture(t)185 f := newPushFixture(t)
174 boom := errors.New("boom")186 boom := errors.New("boom")
175 f.tokens.EXPECT().Get(ctx, "w1").Return(domain.Token{}, boom)187 f.tokens.EXPECT().Get(ctx, "w1").Return(domain.Token{}, boom)
@@ -179,18 +191,21 @@ func TestPushesCheck(t *testing.T) {
179 })191 })
180 192
181 t.Run("a token without write access is rejected", func(t *testing.T) {193 t.Run("a token without write access is rejected", func(t *testing.T) {
194+ t.Parallel()
182 f := newPushFixture(t)195 f := newPushFixture(t)
183 f.tokens.EXPECT().Get(ctx, "r1").Return(domain.Token{ID: "r1", Scopes: scopes(domain.ScopeRepoRead)}, nil)196 f.tokens.EXPECT().Get(ctx, "r1").Return(domain.Token{ID: "r1", Scopes: scopes(domain.ScopeRepoRead)}, nil)
184 assert.ErrorIs(t, f.pushes.Check(ctx, reader, n, ups), domain.ErrRejected)197 assert.ErrorIs(t, f.pushes.Check(ctx, reader, n, ups), domain.ErrRejected)
185 })198 })
186 199
187 t.Run("anonymous is rejected", func(t *testing.T) {200 t.Run("anonymous is rejected", func(t *testing.T) {
201+ t.Parallel()
188 f := newPushFixture(t)202 f := newPushFixture(t)
189 assert.ErrorIs(t, f.pushes.Check(ctx, domain.Anonymous(), n, ups), domain.ErrRejected)203 assert.ErrorIs(t, f.pushes.Check(ctx, domain.Anonymous(), n, ups), domain.ErrRejected)
190 })204 })
191 }205 }
192 206
193 func TestPushesRecord(t *testing.T) {207 func TestPushesRecord(t *testing.T) {
208+ t.Parallel()
194 ctx := context.Background()209 ctx := context.Background()
195 n := name(t, "tools")210 n := name(t, "tools")
196 p := tokenPrincipal(domain.ScopeRepoWrite)211 p := tokenPrincipal(domain.ScopeRepoWrite)
@@ -201,6 +216,7 @@ func TestPushesRecord(t *testing.T) {
201 }216 }
202 217
203 t.Run("sets pushed_at, publishes the exact updates, schedules maintenance", func(t *testing.T) {218 t.Run("sets pushed_at, publishes the exact updates, schedules maintenance", func(t *testing.T) {
219+ t.Parallel()
204 f := newPushFixture(t)220 f := newPushFixture(t)
205 f.sched.EXPECT().Schedule(ctx, n).Return().Once()221 f.sched.EXPECT().Schedule(ctx, n).Return().Once()
206 repo := domain.Repo{Name: n, Visibility: domain.VisibilityPrivate, CreatedAt: now.Add(-time.Hour)}222 repo := domain.Repo{Name: n, Visibility: domain.VisibilityPrivate, CreatedAt: now.Add(-time.Hour)}
@@ -213,6 +229,7 @@ func TestPushesRecord(t *testing.T) {
213 })229 })
214 230
215 t.Run("tells the reader that the repository changed", func(t *testing.T) {231 t.Run("tells the reader that the repository changed", func(t *testing.T) {
232+ t.Parallel()
216 f := newPushFixture(t)233 f := newPushFixture(t)
217 f.sched.EXPECT().Schedule(ctx, n).Return().Once()234 f.sched.EXPECT().Schedule(ctx, n).Return().Once()
218 fresh := mocks.NewRepoFreshener(t)235 fresh := mocks.NewRepoFreshener(t)
@@ -226,6 +243,7 @@ func TestPushesRecord(t *testing.T) {
226 })243 })
227 244
228 t.Run("pushed_at retries after a conflict and never goes backwards", func(t *testing.T) {245 t.Run("pushed_at retries after a conflict and never goes backwards", func(t *testing.T) {
246+ t.Parallel()
229 f := newPushFixture(t)247 f := newPushFixture(t)
230 f.sched.EXPECT().Schedule(ctx, n).Return().Once()248 f.sched.EXPECT().Schedule(ctx, n).Return().Once()
231 repo := domain.Repo{Name: n}249 repo := domain.Repo{Name: n}
@@ -239,6 +257,7 @@ func TestPushesRecord(t *testing.T) {
239 })257 })
240 258
241 t.Run("a deleted repository is not an error", func(t *testing.T) {259 t.Run("a deleted repository is not an error", func(t *testing.T) {
260+ t.Parallel()
242 f := newPushFixture(t)261 f := newPushFixture(t)
243 f.sched.EXPECT().Schedule(ctx, n).Return().Once()262 f.sched.EXPECT().Schedule(ctx, n).Return().Once()
244 f.meta.EXPECT().Get(ctx, n).Return(domain.Repo{}, domain.ErrNotFound)263 f.meta.EXPECT().Get(ctx, n).Return(domain.Repo{}, domain.ErrNotFound)
@@ -248,6 +267,7 @@ func TestPushesRecord(t *testing.T) {
248 })267 })
249 268
250 t.Run("a repository deleted before the default branch step is not an error", func(t *testing.T) {269 t.Run("a repository deleted before the default branch step is not an error", func(t *testing.T) {
270+ t.Parallel()
251 f := newPushFixture(t)271 f := newPushFixture(t)
252 f.sched.EXPECT().Schedule(ctx, n).Return().Once()272 f.sched.EXPECT().Schedule(ctx, n).Return().Once()
253 f.meta.EXPECT().Get(ctx, n).Return(domain.Repo{}, domain.ErrNotFound)273 f.meta.EXPECT().Get(ctx, n).Return(domain.Repo{}, domain.ErrNotFound)
@@ -257,6 +277,7 @@ func TestPushesRecord(t *testing.T) {
257 })277 })
258 278
259 t.Run("a repository deleted before HEAD is set is not an error", func(t *testing.T) {279 t.Run("a repository deleted before HEAD is set is not an error", func(t *testing.T) {
280+ t.Parallel()
260 f := newPushFixture(t)281 f := newPushFixture(t)
261 f.sched.EXPECT().Schedule(ctx, n).Return().Once()282 f.sched.EXPECT().Schedule(ctx, n).Return().Once()
262 f.meta.EXPECT().Get(ctx, n).Return(domain.Repo{}, domain.ErrNotFound)283 f.meta.EXPECT().Get(ctx, n).Return(domain.Repo{}, domain.ErrNotFound)
@@ -267,6 +288,7 @@ func TestPushesRecord(t *testing.T) {
267 })288 })
268 289
269 t.Run("every step runs when one fails", func(t *testing.T) {290 t.Run("every step runs when one fails", func(t *testing.T) {
291+ t.Parallel()
270 f := newPushFixture(t)292 f := newPushFixture(t)
271 f.sched.EXPECT().Schedule(ctx, n).Return().Once()293 f.sched.EXPECT().Schedule(ctx, n).Return().Once()
272 boom := errors.New("boom")294 boom := errors.New("boom")
@@ -279,6 +301,7 @@ func TestPushesRecord(t *testing.T) {
279 }301 }
280 302
281 func TestPushesDefaultBranchRule(t *testing.T) {303 func TestPushesDefaultBranchRule(t *testing.T) {
304+ t.Parallel()
282 ctx := context.Background()305 ctx := context.Background()
283 n := name(t, "tools")306 n := name(t, "tools")
284 p := domain.Root()307 p := domain.Root()
@@ -303,6 +326,7 @@ func TestPushesDefaultBranchRule(t *testing.T) {
303 }326 }
304 for _, tt := range tests {327 for _, tt := range tests {
305 t.Run(tt.name, func(t *testing.T) {328 t.Run(tt.name, func(t *testing.T) {
329+ t.Parallel()
306 f := newPushFixture(t)330 f := newPushFixture(t)
307 f.sched.EXPECT().Schedule(ctx, n).Return().Once()331 f.sched.EXPECT().Schedule(ctx, n).Return().Once()
308 f.meta.EXPECT().Get(ctx, n).Return(domain.Repo{Name: n, PushedAt: now}, nil)332 f.meta.EXPECT().Get(ctx, n).Return(domain.Repo{Name: n, PushedAt: now}, nil)
@@ -316,6 +340,7 @@ func TestPushesDefaultBranchRule(t *testing.T) {
316 }340 }
317 341
318 t.Run("set failure is reported", func(t *testing.T) {342 t.Run("set failure is reported", func(t *testing.T) {
343+ t.Parallel()
319 f := newPushFixture(t)344 f := newPushFixture(t)
320 f.sched.EXPECT().Schedule(ctx, n).Return().Once()345 f.sched.EXPECT().Schedule(ctx, n).Return().Once()
321 boom := errors.New("boom")346 boom := errors.New("boom")
M internal/app/repos_test.go +21 -0
@@ -49,10 +49,12 @@ func name(t *testing.T, s string) domain.RepoName {
4949 }
5050
5151 func TestCreate(t *testing.T) {
52+ t.Parallel()
5253 ctx := context.Background()
5354 errBoom := errors.New("boom")
5455
5556 t.Run("success is private by default", func(t *testing.T) {
57+ t.Parallel()
5658 f := newFixture(t)
5759 n := name(t, "tools")
5860 want := domain.Repo{Name: n, Description: "d", Visibility: domain.VisibilityPrivate, CreatedAt: now, Settings: domain.DefaultRepoSettings()}
@@ -67,6 +69,7 @@ func TestCreate(t *testing.T) {
6769 })
6870
6971 t.Run("anonymous is refused before anything runs", func(t *testing.T) {
72+ t.Parallel()
7073 f := newFixture(t)
7174 _, err := f.repos.Create(ctx, domain.Anonymous(), app.CreateParams{Name: name(t, "tools")})
7275 assert.ErrorIs(t, err, domain.ErrUnauthenticated)
@@ -84,6 +87,7 @@ func TestCreate(t *testing.T) {
8487 })
8588
8689 t.Run("token with repo:write creates", func(t *testing.T) {
90+ t.Parallel()
8791 f := newFixture(t)
8892 n := name(t, "tools")
8993 f.git.EXPECT().Stage(ctx).Return(ports.StagedID("s1"), nil)
@@ -95,18 +99,21 @@ func TestCreate(t *testing.T) {
9599 })
96100
97101 t.Run("zero name is invalid", func(t *testing.T) {
102+ t.Parallel()
98103 f := newFixture(t)
99104 _, err := f.repos.Create(ctx, domain.Root(), app.CreateParams{})
100105 assert.ErrorIs(t, err, domain.ErrInvalidName)
101106 })
102107
103108 t.Run("unknown visibility is invalid", func(t *testing.T) {
109+ t.Parallel()
104110 f := newFixture(t)
105111 _, err := f.repos.Create(ctx, domain.Root(), app.CreateParams{Name: name(t, "tools"), Visibility: "secret"})
106112 assert.ErrorIs(t, err, domain.ErrInvalidVisibility)
107113 })
108114
109115 t.Run("stage failure stores nothing", func(t *testing.T) {
116+ t.Parallel()
110117 f := newFixture(t)
111118 f.git.EXPECT().Stage(ctx).Return("", errBoom)
112119 _, err := f.repos.Create(ctx, domain.Root(), app.CreateParams{Name: name(t, "tools")})
@@ -114,6 +121,7 @@ func TestCreate(t *testing.T) {
114121 })
115122
116123 t.Run("existing name discards the staged repository", func(t *testing.T) {
124+ t.Parallel()
117125 f := newFixture(t)
118126 f.git.EXPECT().Stage(ctx).Return(ports.StagedID("s1"), nil)
119127 f.meta.EXPECT().Create(ctx, mock.Anything).Return(domain.ErrAlreadyExists)
@@ -123,6 +131,7 @@ func TestCreate(t *testing.T) {
123131 })
124132
125133 t.Run("publish conflict removes metadata and staging", func(t *testing.T) {
134+ t.Parallel()
126135 f := newFixture(t)
127136 n := name(t, "tools")
128137 f.git.EXPECT().Stage(ctx).Return(ports.StagedID("s1"), nil)
@@ -135,6 +144,7 @@ func TestCreate(t *testing.T) {
135144 })
136145
137146 t.Run("failed publish removes metadata and staging", func(t *testing.T) {
147+ t.Parallel()
138148 f := newFixture(t)
139149 n := name(t, "tools")
140150 f.git.EXPECT().Stage(ctx).Return(ports.StagedID("s1"), nil)
@@ -147,6 +157,7 @@ func TestCreate(t *testing.T) {
147157 })
148158
149159 t.Run("cleanup survives a canceled context", func(t *testing.T) {
160+ t.Parallel()
150161 f := newFixture(t)
151162 n := name(t, "tools")
152163 cctx, cancel := context.WithCancel(ctx)
@@ -181,6 +192,7 @@ func tokenPrincipal(s domain.Scope, repos ...string) domain.Principal {
181192 }
182193
183194 func TestGetAndListAccess(t *testing.T) {
195+ t.Parallel()
184196 ctx := context.Background()
185197 pub := domain.Repo{Name: name(t, "pub"), Visibility: domain.VisibilityPublic}
186198 priv := domain.Repo{Name: name(t, "priv"), Visibility: domain.VisibilityPrivate}
@@ -204,6 +216,7 @@ func TestGetAndListAccess(t *testing.T) {
204216 }
205217 for _, tt := range tests {
206218 t.Run("get/"+tt.name, func(t *testing.T) {
219+ t.Parallel()
207220 f := newFixture(t)
208221 f.meta.EXPECT().Get(ctx, tt.get.Name).Return(tt.get, nil)
209222 got, err := f.repos.Get(ctx, tt.principal, tt.get.Name)
@@ -218,6 +231,7 @@ func TestGetAndListAccess(t *testing.T) {
218231 }
219232
220233 t.Run("get missing is not found", func(t *testing.T) {
234+ t.Parallel()
221235 f := newFixture(t)
222236 f.meta.EXPECT().Get(ctx, pub.Name).Return(domain.Repo{}, domain.ErrNotFound)
223237 _, err := f.repos.Get(ctx, domain.Root(), pub.Name)
@@ -225,6 +239,7 @@ func TestGetAndListAccess(t *testing.T) {
225239 })
226240
227241 t.Run("private missing and hidden look the same", func(t *testing.T) {
242+ t.Parallel()
228243 f := newFixture(t)
229244 f.meta.EXPECT().Get(ctx, priv.Name).Return(priv, nil)
230245 _, errHidden := f.repos.Get(ctx, domain.Anonymous(), priv.Name)
@@ -243,6 +258,7 @@ func TestGetAndListAccess(t *testing.T) {
243258 }
244259 for _, tt := range listTests {
245260 t.Run("list/"+tt.name, func(t *testing.T) {
261+ t.Parallel()
246262 f := newFixture(t)
247263 f.meta.EXPECT().List(ctx).Return([]domain.Repo{priv, pub}, nil)
248264 got, err := f.repos.List(ctx, tt.principal)
@@ -252,6 +268,7 @@ func TestGetAndListAccess(t *testing.T) {
252268 }
253269
254270 t.Run("list error", func(t *testing.T) {
271+ t.Parallel()
255272 f := newFixture(t)
256273 boom := errors.New("boom")
257274 f.meta.EXPECT().List(ctx).Return(nil, boom)
@@ -261,9 +278,11 @@ func TestGetAndListAccess(t *testing.T) {
261278 }
262279
263280 func TestReconcile(t *testing.T) {
281+ t.Parallel()
264282 ctx := context.Background()
265283
266284 t.Run("registers orphan dir as private, reports missing dir, skips invalid", func(t *testing.T) {
285+ t.Parallel()
267286 f := newFixture(t)
268287 orphan, kept, ghost := name(t, "orphan"), name(t, "kept"), name(t, "ghost")
269288 f.git.EXPECT().PurgeStaging(ctx).Return(nil)
@@ -285,6 +304,7 @@ func TestReconcile(t *testing.T) {
285304 })
286305
287306 t.Run("purge failure stops", func(t *testing.T) {
307+ t.Parallel()
288308 f := newFixture(t)
289309 boom := errors.New("boom")
290310 f.git.EXPECT().PurgeStaging(ctx).Return(boom)
@@ -293,6 +313,7 @@ func TestReconcile(t *testing.T) {
293313 })
294314
295315 t.Run("register failure stops", func(t *testing.T) {
316+ t.Parallel()
296317 f := newFixture(t)
297318 boom := errors.New("boom")
298319 orphan := name(t, "orphan")
@@ -49,10 +49,12 @@ func name(t *testing.T, s string) domain.RepoName {
49 }49 }
50 50
51 func TestCreate(t *testing.T) {51 func TestCreate(t *testing.T) {
52+ t.Parallel()
52 ctx := context.Background()53 ctx := context.Background()
53 errBoom := errors.New("boom")54 errBoom := errors.New("boom")
54 55
55 t.Run("success is private by default", func(t *testing.T) {56 t.Run("success is private by default", func(t *testing.T) {
57+ t.Parallel()
56 f := newFixture(t)58 f := newFixture(t)
57 n := name(t, "tools")59 n := name(t, "tools")
58 want := domain.Repo{Name: n, Description: "d", Visibility: domain.VisibilityPrivate, CreatedAt: now, Settings: domain.DefaultRepoSettings()}60 want := domain.Repo{Name: n, Description: "d", Visibility: domain.VisibilityPrivate, CreatedAt: now, Settings: domain.DefaultRepoSettings()}
@@ -67,6 +69,7 @@ func TestCreate(t *testing.T) {
67 })69 })
68 70
69 t.Run("anonymous is refused before anything runs", func(t *testing.T) {71 t.Run("anonymous is refused before anything runs", func(t *testing.T) {
72+ t.Parallel()
70 f := newFixture(t)73 f := newFixture(t)
71 _, err := f.repos.Create(ctx, domain.Anonymous(), app.CreateParams{Name: name(t, "tools")})74 _, err := f.repos.Create(ctx, domain.Anonymous(), app.CreateParams{Name: name(t, "tools")})
72 assert.ErrorIs(t, err, domain.ErrUnauthenticated)75 assert.ErrorIs(t, err, domain.ErrUnauthenticated)
@@ -84,6 +87,7 @@ func TestCreate(t *testing.T) {
84 })87 })
85 88
86 t.Run("token with repo:write creates", func(t *testing.T) {89 t.Run("token with repo:write creates", func(t *testing.T) {
90+ t.Parallel()
87 f := newFixture(t)91 f := newFixture(t)
88 n := name(t, "tools")92 n := name(t, "tools")
89 f.git.EXPECT().Stage(ctx).Return(ports.StagedID("s1"), nil)93 f.git.EXPECT().Stage(ctx).Return(ports.StagedID("s1"), nil)
@@ -95,18 +99,21 @@ func TestCreate(t *testing.T) {
95 })99 })
96 100
97 t.Run("zero name is invalid", func(t *testing.T) {101 t.Run("zero name is invalid", func(t *testing.T) {
102+ t.Parallel()
98 f := newFixture(t)103 f := newFixture(t)
99 _, err := f.repos.Create(ctx, domain.Root(), app.CreateParams{})104 _, err := f.repos.Create(ctx, domain.Root(), app.CreateParams{})
100 assert.ErrorIs(t, err, domain.ErrInvalidName)105 assert.ErrorIs(t, err, domain.ErrInvalidName)
101 })106 })
102 107
103 t.Run("unknown visibility is invalid", func(t *testing.T) {108 t.Run("unknown visibility is invalid", func(t *testing.T) {
109+ t.Parallel()
104 f := newFixture(t)110 f := newFixture(t)
105 _, err := f.repos.Create(ctx, domain.Root(), app.CreateParams{Name: name(t, "tools"), Visibility: "secret"})111 _, err := f.repos.Create(ctx, domain.Root(), app.CreateParams{Name: name(t, "tools"), Visibility: "secret"})
106 assert.ErrorIs(t, err, domain.ErrInvalidVisibility)112 assert.ErrorIs(t, err, domain.ErrInvalidVisibility)
107 })113 })
108 114
109 t.Run("stage failure stores nothing", func(t *testing.T) {115 t.Run("stage failure stores nothing", func(t *testing.T) {
116+ t.Parallel()
110 f := newFixture(t)117 f := newFixture(t)
111 f.git.EXPECT().Stage(ctx).Return("", errBoom)118 f.git.EXPECT().Stage(ctx).Return("", errBoom)
112 _, err := f.repos.Create(ctx, domain.Root(), app.CreateParams{Name: name(t, "tools")})119 _, err := f.repos.Create(ctx, domain.Root(), app.CreateParams{Name: name(t, "tools")})
@@ -114,6 +121,7 @@ func TestCreate(t *testing.T) {
114 })121 })
115 122
116 t.Run("existing name discards the staged repository", func(t *testing.T) {123 t.Run("existing name discards the staged repository", func(t *testing.T) {
124+ t.Parallel()
117 f := newFixture(t)125 f := newFixture(t)
118 f.git.EXPECT().Stage(ctx).Return(ports.StagedID("s1"), nil)126 f.git.EXPECT().Stage(ctx).Return(ports.StagedID("s1"), nil)
119 f.meta.EXPECT().Create(ctx, mock.Anything).Return(domain.ErrAlreadyExists)127 f.meta.EXPECT().Create(ctx, mock.Anything).Return(domain.ErrAlreadyExists)
@@ -123,6 +131,7 @@ func TestCreate(t *testing.T) {
123 })131 })
124 132
125 t.Run("publish conflict removes metadata and staging", func(t *testing.T) {133 t.Run("publish conflict removes metadata and staging", func(t *testing.T) {
134+ t.Parallel()
126 f := newFixture(t)135 f := newFixture(t)
127 n := name(t, "tools")136 n := name(t, "tools")
128 f.git.EXPECT().Stage(ctx).Return(ports.StagedID("s1"), nil)137 f.git.EXPECT().Stage(ctx).Return(ports.StagedID("s1"), nil)
@@ -135,6 +144,7 @@ func TestCreate(t *testing.T) {
135 })144 })
136 145
137 t.Run("failed publish removes metadata and staging", func(t *testing.T) {146 t.Run("failed publish removes metadata and staging", func(t *testing.T) {
147+ t.Parallel()
138 f := newFixture(t)148 f := newFixture(t)
139 n := name(t, "tools")149 n := name(t, "tools")
140 f.git.EXPECT().Stage(ctx).Return(ports.StagedID("s1"), nil)150 f.git.EXPECT().Stage(ctx).Return(ports.StagedID("s1"), nil)
@@ -147,6 +157,7 @@ func TestCreate(t *testing.T) {
147 })157 })
148 158
149 t.Run("cleanup survives a canceled context", func(t *testing.T) {159 t.Run("cleanup survives a canceled context", func(t *testing.T) {
160+ t.Parallel()
150 f := newFixture(t)161 f := newFixture(t)
151 n := name(t, "tools")162 n := name(t, "tools")
152 cctx, cancel := context.WithCancel(ctx)163 cctx, cancel := context.WithCancel(ctx)
@@ -181,6 +192,7 @@ func tokenPrincipal(s domain.Scope, repos ...string) domain.Principal {
181 }192 }
182 193
183 func TestGetAndListAccess(t *testing.T) {194 func TestGetAndListAccess(t *testing.T) {
195+ t.Parallel()
184 ctx := context.Background()196 ctx := context.Background()
185 pub := domain.Repo{Name: name(t, "pub"), Visibility: domain.VisibilityPublic}197 pub := domain.Repo{Name: name(t, "pub"), Visibility: domain.VisibilityPublic}
186 priv := domain.Repo{Name: name(t, "priv"), Visibility: domain.VisibilityPrivate}198 priv := domain.Repo{Name: name(t, "priv"), Visibility: domain.VisibilityPrivate}
@@ -204,6 +216,7 @@ func TestGetAndListAccess(t *testing.T) {
204 }216 }
205 for _, tt := range tests {217 for _, tt := range tests {
206 t.Run("get/"+tt.name, func(t *testing.T) {218 t.Run("get/"+tt.name, func(t *testing.T) {
219+ t.Parallel()
207 f := newFixture(t)220 f := newFixture(t)
208 f.meta.EXPECT().Get(ctx, tt.get.Name).Return(tt.get, nil)221 f.meta.EXPECT().Get(ctx, tt.get.Name).Return(tt.get, nil)
209 got, err := f.repos.Get(ctx, tt.principal, tt.get.Name)222 got, err := f.repos.Get(ctx, tt.principal, tt.get.Name)
@@ -218,6 +231,7 @@ func TestGetAndListAccess(t *testing.T) {
218 }231 }
219 232
220 t.Run("get missing is not found", func(t *testing.T) {233 t.Run("get missing is not found", func(t *testing.T) {
234+ t.Parallel()
221 f := newFixture(t)235 f := newFixture(t)
222 f.meta.EXPECT().Get(ctx, pub.Name).Return(domain.Repo{}, domain.ErrNotFound)236 f.meta.EXPECT().Get(ctx, pub.Name).Return(domain.Repo{}, domain.ErrNotFound)
223 _, err := f.repos.Get(ctx, domain.Root(), pub.Name)237 _, err := f.repos.Get(ctx, domain.Root(), pub.Name)
@@ -225,6 +239,7 @@ func TestGetAndListAccess(t *testing.T) {
225 })239 })
226 240
227 t.Run("private missing and hidden look the same", func(t *testing.T) {241 t.Run("private missing and hidden look the same", func(t *testing.T) {
242+ t.Parallel()
228 f := newFixture(t)243 f := newFixture(t)
229 f.meta.EXPECT().Get(ctx, priv.Name).Return(priv, nil)244 f.meta.EXPECT().Get(ctx, priv.Name).Return(priv, nil)
230 _, errHidden := f.repos.Get(ctx, domain.Anonymous(), priv.Name)245 _, errHidden := f.repos.Get(ctx, domain.Anonymous(), priv.Name)
@@ -243,6 +258,7 @@ func TestGetAndListAccess(t *testing.T) {
243 }258 }
244 for _, tt := range listTests {259 for _, tt := range listTests {
245 t.Run("list/"+tt.name, func(t *testing.T) {260 t.Run("list/"+tt.name, func(t *testing.T) {
261+ t.Parallel()
246 f := newFixture(t)262 f := newFixture(t)
247 f.meta.EXPECT().List(ctx).Return([]domain.Repo{priv, pub}, nil)263 f.meta.EXPECT().List(ctx).Return([]domain.Repo{priv, pub}, nil)
248 got, err := f.repos.List(ctx, tt.principal)264 got, err := f.repos.List(ctx, tt.principal)
@@ -252,6 +268,7 @@ func TestGetAndListAccess(t *testing.T) {
252 }268 }
253 269
254 t.Run("list error", func(t *testing.T) {270 t.Run("list error", func(t *testing.T) {
271+ t.Parallel()
255 f := newFixture(t)272 f := newFixture(t)
256 boom := errors.New("boom")273 boom := errors.New("boom")
257 f.meta.EXPECT().List(ctx).Return(nil, boom)274 f.meta.EXPECT().List(ctx).Return(nil, boom)
@@ -261,9 +278,11 @@ func TestGetAndListAccess(t *testing.T) {
261 }278 }
262 279
263 func TestReconcile(t *testing.T) {280 func TestReconcile(t *testing.T) {
281+ t.Parallel()
264 ctx := context.Background()282 ctx := context.Background()
265 283
266 t.Run("registers orphan dir as private, reports missing dir, skips invalid", func(t *testing.T) {284 t.Run("registers orphan dir as private, reports missing dir, skips invalid", func(t *testing.T) {
285+ t.Parallel()
267 f := newFixture(t)286 f := newFixture(t)
268 orphan, kept, ghost := name(t, "orphan"), name(t, "kept"), name(t, "ghost")287 orphan, kept, ghost := name(t, "orphan"), name(t, "kept"), name(t, "ghost")
269 f.git.EXPECT().PurgeStaging(ctx).Return(nil)288 f.git.EXPECT().PurgeStaging(ctx).Return(nil)
@@ -285,6 +304,7 @@ func TestReconcile(t *testing.T) {
285 })304 })
286 305
287 t.Run("purge failure stops", func(t *testing.T) {306 t.Run("purge failure stops", func(t *testing.T) {
307+ t.Parallel()
288 f := newFixture(t)308 f := newFixture(t)
289 boom := errors.New("boom")309 boom := errors.New("boom")
290 f.git.EXPECT().PurgeStaging(ctx).Return(boom)310 f.git.EXPECT().PurgeStaging(ctx).Return(boom)
@@ -293,6 +313,7 @@ func TestReconcile(t *testing.T) {
293 })313 })
294 314
295 t.Run("register failure stops", func(t *testing.T) {315 t.Run("register failure stops", func(t *testing.T) {
316+ t.Parallel()
296 f := newFixture(t)317 f := newFixture(t)
297 boom := errors.New("boom")318 boom := errors.New("boom")
298 orphan := name(t, "orphan")319 orphan := name(t, "orphan")
M internal/app/repos_write_test.go +18 -0
@@ -20,12 +20,14 @@ import (
2020 func ptr[T any](v T) *T { return &v }
2121
2222 func TestUpdate(t *testing.T) {
23+ t.Parallel()
2324 ctx := context.Background()
2425 n := name(t, "tools")
2526 priv := domain.Repo{Name: n, Visibility: domain.VisibilityPrivate, Description: "old"}
2627 admin := tokenPrincipal(domain.ScopeRepoAdmin, "tools")
2728
2829 t.Run("description and visibility are stored and RepoUpdated is published", func(t *testing.T) {
30+ t.Parallel()
2931 f := newFixture(t)
3032 f.meta.EXPECT().Get(ctx, n).Return(priv, nil).Twice()
3133 want := domain.Repo{Name: n, Visibility: domain.VisibilityPublic, Description: "new", Settings: domain.DefaultRepoSettings()}
@@ -42,6 +44,7 @@ func TestUpdate(t *testing.T) {
4244 })
4345
4446 t.Run("a lost race on the metadata is retried", func(t *testing.T) {
47+ t.Parallel()
4548 f := newFixture(t)
4649 f.meta.EXPECT().Get(ctx, n).Return(priv, nil)
4750 f.meta.EXPECT().Update(ctx, mock.Anything).Return(domain.ErrConflict).Once()
@@ -52,6 +55,7 @@ func TestUpdate(t *testing.T) {
5255 })
5356
5457 t.Run("the default branch is checked, then HEAD moves", func(t *testing.T) {
58+ t.Parallel()
5559 f := newFixture(t)
5660 f.meta.EXPECT().Get(ctx, n).Return(priv, nil)
5761 f.git.EXPECT().HasBranch(ctx, n, "dev").Return(true, nil)
@@ -63,6 +67,7 @@ func TestUpdate(t *testing.T) {
6367 })
6468
6569 t.Run("metadata is written before HEAD moves, and the event follows both", func(t *testing.T) {
70+ t.Parallel()
6671 f := newFixture(t)
6772 var order []string
6873 f.meta.EXPECT().Get(ctx, n).Return(priv, nil)
@@ -82,6 +87,7 @@ func TestUpdate(t *testing.T) {
8287 })
8388
8489 t.Run("a HEAD that cannot move after the metadata was stored still publishes, then fails", func(t *testing.T) {
90+ t.Parallel()
8591 f := newFixture(t)
8692 boom := errors.New("boom")
8793 pub := domain.Repo{Name: n, Visibility: domain.VisibilityPublic, Description: "old"}
@@ -97,6 +103,7 @@ func TestUpdate(t *testing.T) {
97103 })
98104
99105 t.Run("a HEAD that cannot move with no metadata change publishes nothing", func(t *testing.T) {
106+ t.Parallel()
100107 f := newFixture(t)
101108 boom := errors.New("boom")
102109 f.meta.EXPECT().Get(ctx, n).Return(priv, nil)
@@ -107,6 +114,7 @@ func TestUpdate(t *testing.T) {
107114 })
108115
109116 t.Run("a missing branch is a failed precondition and HEAD stays", func(t *testing.T) {
117+ t.Parallel()
110118 f := newFixture(t)
111119 f.meta.EXPECT().Get(ctx, n).Return(priv, nil)
112120 f.git.EXPECT().HasBranch(ctx, n, "nope").Return(false, nil)
@@ -146,6 +154,7 @@ func TestUpdate(t *testing.T) {
146154 }
147155 for _, tt := range access {
148156 t.Run(tt.name, func(t *testing.T) {
157+ t.Parallel()
149158 f := newFixture(t)
150159 f.meta.EXPECT().Get(ctx, n).Return(tt.repo, nil)
151160 _, err := f.repos.Update(ctx, tt.p, n, app.UpdateParams{Description: ptr("x")})
@@ -156,6 +165,7 @@ func TestUpdate(t *testing.T) {
156165 }
157166
158167 t.Run("a missing repository is not found", func(t *testing.T) {
168+ t.Parallel()
159169 f := newFixture(t)
160170 f.meta.EXPECT().Get(ctx, n).Return(domain.Repo{}, domain.ErrNotFound)
161171 _, err := f.repos.Update(ctx, domain.Root(), n, app.UpdateParams{Description: ptr("x")})
@@ -164,11 +174,13 @@ func TestUpdate(t *testing.T) {
164174 }
165175
166176 func TestDelete(t *testing.T) {
177+ t.Parallel()
167178 ctx := context.Background()
168179 n := name(t, "tools")
169180 priv := domain.Repo{Name: n, Visibility: domain.VisibilityPrivate}
170181
171182 t.Run("trash, forget the metadata, publish RepoDeleted with the visibility", func(t *testing.T) {
183+ t.Parallel()
172184 f := newFixture(t)
173185 f.meta.EXPECT().Get(ctx, n).Return(priv, nil)
174186 f.git.EXPECT().Trash(ctx, n).Return(nil)
@@ -178,6 +190,7 @@ func TestDelete(t *testing.T) {
178190 })
179191
180192 t.Run("a directory already gone still loses its metadata", func(t *testing.T) {
193+ t.Parallel()
181194 f := newFixture(t)
182195 f.meta.EXPECT().Get(ctx, n).Return(priv, nil)
183196 f.git.EXPECT().Trash(ctx, n).Return(domain.ErrNotFound)
@@ -187,6 +200,7 @@ func TestDelete(t *testing.T) {
187200 })
188201
189202 t.Run("a failed move keeps the metadata", func(t *testing.T) {
203+ t.Parallel()
190204 f := newFixture(t)
191205 boom := errors.New("boom")
192206 f.meta.EXPECT().Get(ctx, n).Return(priv, nil)
@@ -196,6 +210,7 @@ func TestDelete(t *testing.T) {
196210 }
197211
198212 func TestView(t *testing.T) {
213+ t.Parallel()
199214 ctx := context.Background()
200215 n := name(t, "tools")
201216 repo := domain.Repo{Name: n}
@@ -213,6 +228,7 @@ func TestView(t *testing.T) {
213228 }
214229
215230 func TestListPageSizes(t *testing.T) {
231+ t.Parallel()
216232 ctx := context.Background()
217233 var all []domain.Repo
218234 for i := range app.MaxRepoPageSize + 10 {
@@ -230,6 +246,7 @@ func TestListPageSizes(t *testing.T) {
230246 {"lowered to the maximum", app.MaxRepoPageSize * 10, app.MaxRepoPageSize, true},
231247 } {
232248 t.Run(tt.name, func(t *testing.T) {
249+ t.Parallel()
233250 f := newFixture(t)
234251 f.meta.EXPECT().List(ctx).Return(all, nil)
235252 page, err := f.repos.ListPage(ctx, domain.Anonymous(), tt.size, "")
@@ -241,6 +258,7 @@ func TestListPageSizes(t *testing.T) {
241258 }
242259
243260 func TestListPage(t *testing.T) {
261+ t.Parallel()
244262 ctx := context.Background()
245263 var all []domain.Repo
246264 for _, s := range []string{"a", "b", "c", "d", "e"} {
@@ -20,12 +20,14 @@ import (
20 func ptr[T any](v T) *T { return &v }20 func ptr[T any](v T) *T { return &v }
21 21
22 func TestUpdate(t *testing.T) {22 func TestUpdate(t *testing.T) {
23+ t.Parallel()
23 ctx := context.Background()24 ctx := context.Background()
24 n := name(t, "tools")25 n := name(t, "tools")
25 priv := domain.Repo{Name: n, Visibility: domain.VisibilityPrivate, Description: "old"}26 priv := domain.Repo{Name: n, Visibility: domain.VisibilityPrivate, Description: "old"}
26 admin := tokenPrincipal(domain.ScopeRepoAdmin, "tools")27 admin := tokenPrincipal(domain.ScopeRepoAdmin, "tools")
27 28
28 t.Run("description and visibility are stored and RepoUpdated is published", func(t *testing.T) {29 t.Run("description and visibility are stored and RepoUpdated is published", func(t *testing.T) {
30+ t.Parallel()
29 f := newFixture(t)31 f := newFixture(t)
30 f.meta.EXPECT().Get(ctx, n).Return(priv, nil).Twice()32 f.meta.EXPECT().Get(ctx, n).Return(priv, nil).Twice()
31 want := domain.Repo{Name: n, Visibility: domain.VisibilityPublic, Description: "new", Settings: domain.DefaultRepoSettings()}33 want := domain.Repo{Name: n, Visibility: domain.VisibilityPublic, Description: "new", Settings: domain.DefaultRepoSettings()}
@@ -42,6 +44,7 @@ func TestUpdate(t *testing.T) {
42 })44 })
43 45
44 t.Run("a lost race on the metadata is retried", func(t *testing.T) {46 t.Run("a lost race on the metadata is retried", func(t *testing.T) {
47+ t.Parallel()
45 f := newFixture(t)48 f := newFixture(t)
46 f.meta.EXPECT().Get(ctx, n).Return(priv, nil)49 f.meta.EXPECT().Get(ctx, n).Return(priv, nil)
47 f.meta.EXPECT().Update(ctx, mock.Anything).Return(domain.ErrConflict).Once()50 f.meta.EXPECT().Update(ctx, mock.Anything).Return(domain.ErrConflict).Once()
@@ -52,6 +55,7 @@ func TestUpdate(t *testing.T) {
52 })55 })
53 56
54 t.Run("the default branch is checked, then HEAD moves", func(t *testing.T) {57 t.Run("the default branch is checked, then HEAD moves", func(t *testing.T) {
58+ t.Parallel()
55 f := newFixture(t)59 f := newFixture(t)
56 f.meta.EXPECT().Get(ctx, n).Return(priv, nil)60 f.meta.EXPECT().Get(ctx, n).Return(priv, nil)
57 f.git.EXPECT().HasBranch(ctx, n, "dev").Return(true, nil)61 f.git.EXPECT().HasBranch(ctx, n, "dev").Return(true, nil)
@@ -63,6 +67,7 @@ func TestUpdate(t *testing.T) {
63 })67 })
64 68
65 t.Run("metadata is written before HEAD moves, and the event follows both", func(t *testing.T) {69 t.Run("metadata is written before HEAD moves, and the event follows both", func(t *testing.T) {
70+ t.Parallel()
66 f := newFixture(t)71 f := newFixture(t)
67 var order []string72 var order []string
68 f.meta.EXPECT().Get(ctx, n).Return(priv, nil)73 f.meta.EXPECT().Get(ctx, n).Return(priv, nil)
@@ -82,6 +87,7 @@ func TestUpdate(t *testing.T) {
82 })87 })
83 88
84 t.Run("a HEAD that cannot move after the metadata was stored still publishes, then fails", func(t *testing.T) {89 t.Run("a HEAD that cannot move after the metadata was stored still publishes, then fails", func(t *testing.T) {
90+ t.Parallel()
85 f := newFixture(t)91 f := newFixture(t)
86 boom := errors.New("boom")92 boom := errors.New("boom")
87 pub := domain.Repo{Name: n, Visibility: domain.VisibilityPublic, Description: "old"}93 pub := domain.Repo{Name: n, Visibility: domain.VisibilityPublic, Description: "old"}
@@ -97,6 +103,7 @@ func TestUpdate(t *testing.T) {
97 })103 })
98 104
99 t.Run("a HEAD that cannot move with no metadata change publishes nothing", func(t *testing.T) {105 t.Run("a HEAD that cannot move with no metadata change publishes nothing", func(t *testing.T) {
106+ t.Parallel()
100 f := newFixture(t)107 f := newFixture(t)
101 boom := errors.New("boom")108 boom := errors.New("boom")
102 f.meta.EXPECT().Get(ctx, n).Return(priv, nil)109 f.meta.EXPECT().Get(ctx, n).Return(priv, nil)
@@ -107,6 +114,7 @@ func TestUpdate(t *testing.T) {
107 })114 })
108 115
109 t.Run("a missing branch is a failed precondition and HEAD stays", func(t *testing.T) {116 t.Run("a missing branch is a failed precondition and HEAD stays", func(t *testing.T) {
117+ t.Parallel()
110 f := newFixture(t)118 f := newFixture(t)
111 f.meta.EXPECT().Get(ctx, n).Return(priv, nil)119 f.meta.EXPECT().Get(ctx, n).Return(priv, nil)
112 f.git.EXPECT().HasBranch(ctx, n, "nope").Return(false, nil)120 f.git.EXPECT().HasBranch(ctx, n, "nope").Return(false, nil)
@@ -146,6 +154,7 @@ func TestUpdate(t *testing.T) {
146 }154 }
147 for _, tt := range access {155 for _, tt := range access {
148 t.Run(tt.name, func(t *testing.T) {156 t.Run(tt.name, func(t *testing.T) {
157+ t.Parallel()
149 f := newFixture(t)158 f := newFixture(t)
150 f.meta.EXPECT().Get(ctx, n).Return(tt.repo, nil)159 f.meta.EXPECT().Get(ctx, n).Return(tt.repo, nil)
151 _, err := f.repos.Update(ctx, tt.p, n, app.UpdateParams{Description: ptr("x")})160 _, err := f.repos.Update(ctx, tt.p, n, app.UpdateParams{Description: ptr("x")})
@@ -156,6 +165,7 @@ func TestUpdate(t *testing.T) {
156 }165 }
157 166
158 t.Run("a missing repository is not found", func(t *testing.T) {167 t.Run("a missing repository is not found", func(t *testing.T) {
168+ t.Parallel()
159 f := newFixture(t)169 f := newFixture(t)
160 f.meta.EXPECT().Get(ctx, n).Return(domain.Repo{}, domain.ErrNotFound)170 f.meta.EXPECT().Get(ctx, n).Return(domain.Repo{}, domain.ErrNotFound)
161 _, err := f.repos.Update(ctx, domain.Root(), n, app.UpdateParams{Description: ptr("x")})171 _, err := f.repos.Update(ctx, domain.Root(), n, app.UpdateParams{Description: ptr("x")})
@@ -164,11 +174,13 @@ func TestUpdate(t *testing.T) {
164 }174 }
165 175
166 func TestDelete(t *testing.T) {176 func TestDelete(t *testing.T) {
177+ t.Parallel()
167 ctx := context.Background()178 ctx := context.Background()
168 n := name(t, "tools")179 n := name(t, "tools")
169 priv := domain.Repo{Name: n, Visibility: domain.VisibilityPrivate}180 priv := domain.Repo{Name: n, Visibility: domain.VisibilityPrivate}
170 181
171 t.Run("trash, forget the metadata, publish RepoDeleted with the visibility", func(t *testing.T) {182 t.Run("trash, forget the metadata, publish RepoDeleted with the visibility", func(t *testing.T) {
183+ t.Parallel()
172 f := newFixture(t)184 f := newFixture(t)
173 f.meta.EXPECT().Get(ctx, n).Return(priv, nil)185 f.meta.EXPECT().Get(ctx, n).Return(priv, nil)
174 f.git.EXPECT().Trash(ctx, n).Return(nil)186 f.git.EXPECT().Trash(ctx, n).Return(nil)
@@ -178,6 +190,7 @@ func TestDelete(t *testing.T) {
178 })190 })
179 191
180 t.Run("a directory already gone still loses its metadata", func(t *testing.T) {192 t.Run("a directory already gone still loses its metadata", func(t *testing.T) {
193+ t.Parallel()
181 f := newFixture(t)194 f := newFixture(t)
182 f.meta.EXPECT().Get(ctx, n).Return(priv, nil)195 f.meta.EXPECT().Get(ctx, n).Return(priv, nil)
183 f.git.EXPECT().Trash(ctx, n).Return(domain.ErrNotFound)196 f.git.EXPECT().Trash(ctx, n).Return(domain.ErrNotFound)
@@ -187,6 +200,7 @@ func TestDelete(t *testing.T) {
187 })200 })
188 201
189 t.Run("a failed move keeps the metadata", func(t *testing.T) {202 t.Run("a failed move keeps the metadata", func(t *testing.T) {
203+ t.Parallel()
190 f := newFixture(t)204 f := newFixture(t)
191 boom := errors.New("boom")205 boom := errors.New("boom")
192 f.meta.EXPECT().Get(ctx, n).Return(priv, nil)206 f.meta.EXPECT().Get(ctx, n).Return(priv, nil)
@@ -196,6 +210,7 @@ func TestDelete(t *testing.T) {
196 }210 }
197 211
198 func TestView(t *testing.T) {212 func TestView(t *testing.T) {
213+ t.Parallel()
199 ctx := context.Background()214 ctx := context.Background()
200 n := name(t, "tools")215 n := name(t, "tools")
201 repo := domain.Repo{Name: n}216 repo := domain.Repo{Name: n}
@@ -213,6 +228,7 @@ func TestView(t *testing.T) {
213 }228 }
214 229
215 func TestListPageSizes(t *testing.T) {230 func TestListPageSizes(t *testing.T) {
231+ t.Parallel()
216 ctx := context.Background()232 ctx := context.Background()
217 var all []domain.Repo233 var all []domain.Repo
218 for i := range app.MaxRepoPageSize + 10 {234 for i := range app.MaxRepoPageSize + 10 {
@@ -230,6 +246,7 @@ func TestListPageSizes(t *testing.T) {
230 {"lowered to the maximum", app.MaxRepoPageSize * 10, app.MaxRepoPageSize, true},246 {"lowered to the maximum", app.MaxRepoPageSize * 10, app.MaxRepoPageSize, true},
231 } {247 } {
232 t.Run(tt.name, func(t *testing.T) {248 t.Run(tt.name, func(t *testing.T) {
249+ t.Parallel()
233 f := newFixture(t)250 f := newFixture(t)
234 f.meta.EXPECT().List(ctx).Return(all, nil)251 f.meta.EXPECT().List(ctx).Return(all, nil)
235 page, err := f.repos.ListPage(ctx, domain.Anonymous(), tt.size, "")252 page, err := f.repos.ListPage(ctx, domain.Anonymous(), tt.size, "")
@@ -241,6 +258,7 @@ func TestListPageSizes(t *testing.T) {
241 }258 }
242 259
243 func TestListPage(t *testing.T) {260 func TestListPage(t *testing.T) {
261+ t.Parallel()
244 ctx := context.Background()262 ctx := context.Background()
245 var all []domain.Repo263 var all []domain.Repo
246 for _, s := range []string{"a", "b", "c", "d", "e"} {264 for _, s := range []string{"a", "b", "c", "d", "e"} {
M internal/app/sessions_test.go +8 -0
@@ -72,6 +72,7 @@ func allScopeToken(repos ...domain.RepoName) domain.Principal {
7272 // Only the owner creates a link: root, or a token with every scope and no
7373 // repository list.
7474 func TestLoginLinkIsOwnerOnly(t *testing.T) {
75+ t.Parallel()
7576 ctx := context.Background()
7677 f := newSessionFixture(t)
7778 partial := domain.TokenPrincipal(domain.Token{ID: "p", Scopes: domain.AllScopes()[:8]})
@@ -113,6 +114,7 @@ func mustRepo(t *testing.T, s string) domain.RepoName {
113114 // AC1: checking a link does not use it; logging in uses it once; an expired or
114115 // replayed link fails.
115116 func TestLoginLinkLifecycle(t *testing.T) {
117+ t.Parallel()
116118 ctx := context.Background()
117119 f := newSessionFixture(t)
118120
@@ -155,6 +157,7 @@ func TestLoginLinkLifecycle(t *testing.T) {
155157 // GO-003a: the session of a link made with a token never outlives that token;
156158 // root and a token that never expires leave it at SessionTTL.
157159 func TestSessionIsCappedByTheTokenThatMadeTheLink(t *testing.T) {
160+ t.Parallel()
158161 ctx := context.Background()
159162 f := newSessionFixture(t)
160163 tokenExpiring := func(in time.Duration) domain.Principal {
@@ -210,6 +213,7 @@ func TestSessionIsCappedByTheTokenThatMadeTheLink(t *testing.T) {
210213
211214 // A single use also holds under concurrent confirmations.
212215 func TestLoginLinkConsumedOnceConcurrently(t *testing.T) {
216+ t.Parallel()
213217 f := newSessionFixture(t)
214218 nonce := f.nonce(t)
215219 var wg sync.WaitGroup
@@ -231,6 +235,7 @@ func TestLoginLinkConsumedOnceConcurrently(t *testing.T) {
231235 }
232236
233237 func TestLoginLinksAreBounded(t *testing.T) {
238+ t.Parallel()
234239 ctx := context.Background()
235240 f := newSessionFixture(t)
236241 for range app.MaxPendingLoginLinks {
@@ -259,6 +264,7 @@ type claims struct {
259264 // AC1: a valid session is the owner; none, alg none, another algorithm, another
260265 // key, an old epoch, expiry, a missing claim and garbage are rejected.
261266 func TestSessionTokenVerification(t *testing.T) {
267+ t.Parallel()
262268 ctx := context.Background()
263269 f := newSessionFixture(t)
264270 now := f.clock.Now()
@@ -308,6 +314,7 @@ func TestSessionTokenVerification(t *testing.T) {
308314 }
309315
310316 func TestSignOutEverywhereEndsSessions(t *testing.T) {
317+ t.Parallel()
311318 ctx := context.Background()
312319 f := newSessionFixture(t)
313320 a, _, err := f.sessions.Login(ctx, f.nonce(t))
@@ -331,6 +338,7 @@ func TestSignOutEverywhereEndsSessions(t *testing.T) {
331338 // The owner signed in on the web signs what it writes as "owner", where root
332339 // signs as "root" although both hold every right.
333340 func TestAuthorOfOwnerSession(t *testing.T) {
341+ t.Parallel()
334342 assert.Equal(t, "owner", app.AuthorOf(domain.Owner()))
335343 assert.Equal(t, "root", app.AuthorOf(domain.Root()))
336344 assert.Equal(t, "ci", app.AuthorOf(domain.TokenPrincipal(domain.Token{Label: "ci", Scopes: domain.AllScopes()})))
@@ -72,6 +72,7 @@ func allScopeToken(repos ...domain.RepoName) domain.Principal {
72 // Only the owner creates a link: root, or a token with every scope and no72 // Only the owner creates a link: root, or a token with every scope and no
73 // repository list.73 // repository list.
74 func TestLoginLinkIsOwnerOnly(t *testing.T) {74 func TestLoginLinkIsOwnerOnly(t *testing.T) {
75+ t.Parallel()
75 ctx := context.Background()76 ctx := context.Background()
76 f := newSessionFixture(t)77 f := newSessionFixture(t)
77 partial := domain.TokenPrincipal(domain.Token{ID: "p", Scopes: domain.AllScopes()[:8]})78 partial := domain.TokenPrincipal(domain.Token{ID: "p", Scopes: domain.AllScopes()[:8]})
@@ -113,6 +114,7 @@ func mustRepo(t *testing.T, s string) domain.RepoName {
113 // AC1: checking a link does not use it; logging in uses it once; an expired or114 // AC1: checking a link does not use it; logging in uses it once; an expired or
114 // replayed link fails.115 // replayed link fails.
115 func TestLoginLinkLifecycle(t *testing.T) {116 func TestLoginLinkLifecycle(t *testing.T) {
117+ t.Parallel()
116 ctx := context.Background()118 ctx := context.Background()
117 f := newSessionFixture(t)119 f := newSessionFixture(t)
118 120
@@ -155,6 +157,7 @@ func TestLoginLinkLifecycle(t *testing.T) {
155 // GO-003a: the session of a link made with a token never outlives that token;157 // GO-003a: the session of a link made with a token never outlives that token;
156 // root and a token that never expires leave it at SessionTTL.158 // root and a token that never expires leave it at SessionTTL.
157 func TestSessionIsCappedByTheTokenThatMadeTheLink(t *testing.T) {159 func TestSessionIsCappedByTheTokenThatMadeTheLink(t *testing.T) {
160+ t.Parallel()
158 ctx := context.Background()161 ctx := context.Background()
159 f := newSessionFixture(t)162 f := newSessionFixture(t)
160 tokenExpiring := func(in time.Duration) domain.Principal {163 tokenExpiring := func(in time.Duration) domain.Principal {
@@ -210,6 +213,7 @@ func TestSessionIsCappedByTheTokenThatMadeTheLink(t *testing.T) {
210 213
211 // A single use also holds under concurrent confirmations.214 // A single use also holds under concurrent confirmations.
212 func TestLoginLinkConsumedOnceConcurrently(t *testing.T) {215 func TestLoginLinkConsumedOnceConcurrently(t *testing.T) {
216+ t.Parallel()
213 f := newSessionFixture(t)217 f := newSessionFixture(t)
214 nonce := f.nonce(t)218 nonce := f.nonce(t)
215 var wg sync.WaitGroup219 var wg sync.WaitGroup
@@ -231,6 +235,7 @@ func TestLoginLinkConsumedOnceConcurrently(t *testing.T) {
231 }235 }
232 236
233 func TestLoginLinksAreBounded(t *testing.T) {237 func TestLoginLinksAreBounded(t *testing.T) {
238+ t.Parallel()
234 ctx := context.Background()239 ctx := context.Background()
235 f := newSessionFixture(t)240 f := newSessionFixture(t)
236 for range app.MaxPendingLoginLinks {241 for range app.MaxPendingLoginLinks {
@@ -259,6 +264,7 @@ type claims struct {
259 // AC1: a valid session is the owner; none, alg none, another algorithm, another264 // AC1: a valid session is the owner; none, alg none, another algorithm, another
260 // key, an old epoch, expiry, a missing claim and garbage are rejected.265 // key, an old epoch, expiry, a missing claim and garbage are rejected.
261 func TestSessionTokenVerification(t *testing.T) {266 func TestSessionTokenVerification(t *testing.T) {
267+ t.Parallel()
262 ctx := context.Background()268 ctx := context.Background()
263 f := newSessionFixture(t)269 f := newSessionFixture(t)
264 now := f.clock.Now()270 now := f.clock.Now()
@@ -308,6 +314,7 @@ func TestSessionTokenVerification(t *testing.T) {
308 }314 }
309 315
310 func TestSignOutEverywhereEndsSessions(t *testing.T) {316 func TestSignOutEverywhereEndsSessions(t *testing.T) {
317+ t.Parallel()
311 ctx := context.Background()318 ctx := context.Background()
312 f := newSessionFixture(t)319 f := newSessionFixture(t)
313 a, _, err := f.sessions.Login(ctx, f.nonce(t))320 a, _, err := f.sessions.Login(ctx, f.nonce(t))
@@ -331,6 +338,7 @@ func TestSignOutEverywhereEndsSessions(t *testing.T) {
331 // The owner signed in on the web signs what it writes as "owner", where root338 // The owner signed in on the web signs what it writes as "owner", where root
332 // signs as "root" although both hold every right.339 // signs as "root" although both hold every right.
333 func TestAuthorOfOwnerSession(t *testing.T) {340 func TestAuthorOfOwnerSession(t *testing.T) {
341+ t.Parallel()
334 assert.Equal(t, "owner", app.AuthorOf(domain.Owner()))342 assert.Equal(t, "owner", app.AuthorOf(domain.Owner()))
335 assert.Equal(t, "root", app.AuthorOf(domain.Root()))343 assert.Equal(t, "root", app.AuthorOf(domain.Root()))
336 assert.Equal(t, "ci", app.AuthorOf(domain.TokenPrincipal(domain.Token{Label: "ci", Scopes: domain.AllScopes()})))344 assert.Equal(t, "ci", app.AuthorOf(domain.TokenPrincipal(domain.Token{Label: "ci", Scopes: domain.AllScopes()})))
M internal/app/tokens_test.go +39 -0
@@ -51,6 +51,7 @@ func names(t *testing.T, ss ...string) []domain.RepoName {
5151
5252 // Labels that only contain a reserved word are fine.
5353 func TestTokensCreateAllowsLabelsNearReserved(t *testing.T) {
54+ t.Parallel()
5455 ctx := context.Background()
5556 tests := []struct{ name, label string }{
5657 {"digit suffix", "owner2"},
@@ -61,6 +62,7 @@ func TestTokensCreateAllowsLabelsNearReserved(t *testing.T) {
6162 }
6263 for _, tt := range tests {
6364 t.Run(tt.name, func(t *testing.T) {
65+ t.Parallel()
6466 f := newTokenFixture(t)
6567 f.store.EXPECT().Create(ctx, mock.Anything).Return(nil)
6668 _, err := f.tokens.Create(ctx, domain.Root(), app.CreateTokenParams{Label: tt.label, Scopes: scopes(domain.ScopeRepoRead)})
@@ -70,9 +72,11 @@ func TestTokensCreateAllowsLabelsNearReserved(t *testing.T) {
7072 }
7173
7274 func TestTokensCreate(t *testing.T) {
75+ t.Parallel()
7376 ctx := context.Background()
7477
7578 t.Run("root creates a token with a well-formed secret", func(t *testing.T) {
79+ t.Parallel()
7680 f := newTokenFixture(t)
7781 var stored domain.Token
7882 f.store.EXPECT().Create(ctx, mock.Anything).Run(func(_ context.Context, tok domain.Token) { stored = tok }).Return(nil)
@@ -102,6 +106,7 @@ func TestTokensCreate(t *testing.T) {
102106 })
103107
104108 t.Run("secrets are unique", func(t *testing.T) {
109+ t.Parallel()
105110 f := newTokenFixture(t)
106111 f.store.EXPECT().Create(ctx, mock.Anything).Return(nil)
107112 a, err := f.tokens.Create(ctx, domain.Root(), app.CreateTokenParams{Label: "a", Scopes: scopes(domain.ScopeRepoRead)})
@@ -113,6 +118,7 @@ func TestTokensCreate(t *testing.T) {
113118 })
114119
115120 t.Run("retries on a collision", func(t *testing.T) {
121+ t.Parallel()
116122 f := newTokenFixture(t)
117123 f.store.EXPECT().Create(ctx, mock.Anything).Return(domain.ErrAlreadyExists).Once()
118124 f.store.EXPECT().Create(ctx, mock.Anything).Return(nil).Once()
@@ -121,6 +127,7 @@ func TestTokensCreate(t *testing.T) {
121127 })
122128
123129 t.Run("store failure", func(t *testing.T) {
130+ t.Parallel()
124131 f := newTokenFixture(t)
125132 boom := errors.New("boom")
126133 f.store.EXPECT().Create(ctx, mock.Anything).Return(boom)
@@ -148,6 +155,7 @@ func TestTokensCreate(t *testing.T) {
148155 }
149156 for _, tt := range invalid {
150157 t.Run("invalid/"+tt.name, func(t *testing.T) {
158+ t.Parallel()
151159 f := newTokenFixture(t)
152160 _, err := f.tokens.Create(ctx, domain.Root(), tt.params)
153161 assert.ErrorIs(t, err, tt.want)
@@ -156,6 +164,7 @@ func TestTokensCreate(t *testing.T) {
156164 }
157165
158166 func TestTokensCreateNoEscalation(t *testing.T) {
167+ t.Parallel()
159168 ctx := context.Background()
160169 exp := now.Add(time.Hour)
161170 admin := func(repos []domain.RepoName, expires time.Time, extra ...domain.Scope) domain.Principal {
@@ -204,6 +213,7 @@ func TestTokensCreateNoEscalation(t *testing.T) {
204213 }
205214 for _, tt := range tests {
206215 t.Run(tt.name, func(t *testing.T) {
216+ t.Parallel()
207217 f := newTokenFixture(t)
208218 if tt.want == nil {
209219 f.store.EXPECT().Create(ctx, mock.Anything).Return(nil)
@@ -221,11 +231,13 @@ func TestTokensCreateNoEscalation(t *testing.T) {
221231 }
222232
223233 func TestTokensListRevoke(t *testing.T) {
234+ t.Parallel()
224235 ctx := context.Background()
225236 tokenAdmin := domain.TokenPrincipal(domain.Token{ID: "adm", Scopes: scopes(domain.ScopeTokenAdmin)})
226237 writer := domain.TokenPrincipal(domain.Token{Scopes: scopes(domain.ScopeRepoWrite)})
227238
228239 t.Run("list for the admin token and root", func(t *testing.T) {
240+ t.Parallel()
229241 f := newTokenFixture(t)
230242 want := []domain.Token{{ID: "a", Scopes: scopes(domain.ScopeTokenAdmin)}}
231243 f.store.EXPECT().List(ctx).Return(want, nil).Twice()
@@ -241,6 +253,7 @@ func TestTokensListRevoke(t *testing.T) {
241253 })
242254
243255 t.Run("revoke", func(t *testing.T) {
256+ t.Parallel()
244257 f := newTokenFixture(t)
245258 f.store.EXPECT().Get(ctx, "a").Return(domain.Token{ID: "a", Scopes: scopes(domain.ScopeTokenAdmin)}, nil).Twice()
246259 f.store.EXPECT().Get(ctx, "zz").Return(domain.Token{}, domain.ErrNotFound).Once()
@@ -256,6 +269,7 @@ func TestTokensListRevoke(t *testing.T) {
256269 // GO-003b: revoking a token with owner rights signs every browser out (the
257270 // session epoch is bumped); revoking any other token does not.
258271 func TestTokensRevokeOwnerRightsEndsWebSessions(t *testing.T) {
272+ t.Parallel()
259273 ctx := context.Background()
260274 ownerTok := domain.Token{ID: "own", Scopes: domain.AllScopes()}
261275
@@ -270,6 +284,7 @@ func TestTokensRevokeOwnerRightsEndsWebSessions(t *testing.T) {
270284 "repo list": {domain.Token{ID: "l", Scopes: domain.AllScopes(), Repos: names(t, "a")}, domain.Root(), false},
271285 } {
272286 t.Run(name, func(t *testing.T) {
287+ t.Parallel()
273288 f := newTokenFixture(t)
274289 f.store.EXPECT().Get(ctx, tc.tok.ID).Return(tc.tok, nil)
275290 f.store.EXPECT().Revoke(ctx, tc.tok.ID).Return(nil)
@@ -281,6 +296,7 @@ func TestTokensRevokeOwnerRightsEndsWebSessions(t *testing.T) {
281296 }
282297
283298 t.Run("a revoke that fails bumps nothing", func(t *testing.T) {
299+ t.Parallel()
284300 f := newTokenFixture(t)
285301 f.store.EXPECT().Get(ctx, "own").Return(ownerTok, nil)
286302 f.store.EXPECT().Revoke(ctx, "own").Return(errors.New("kv down"))
@@ -288,6 +304,7 @@ func TestTokensRevokeOwnerRightsEndsWebSessions(t *testing.T) {
288304 })
289305
290306 t.Run("a token out of reach is not revoked and bumps nothing", func(t *testing.T) {
307+ t.Parallel()
291308 f := newTokenFixture(t)
292309 limited := domain.TokenPrincipal(domain.Token{ID: "adm", Scopes: scopes(domain.ScopeTokenAdmin)})
293310 f.store.EXPECT().Get(ctx, "own").Return(ownerTok, nil)
@@ -295,6 +312,7 @@ func TestTokensRevokeOwnerRightsEndsWebSessions(t *testing.T) {
295312 })
296313
297314 t.Run("a failure to sign out is reported once the token is revoked", func(t *testing.T) {
315+ t.Parallel()
298316 f := newTokenFixture(t)
299317 f.store.EXPECT().Get(ctx, "own").Return(ownerTok, nil)
300318 f.store.EXPECT().Revoke(ctx, "own").Return(nil)
@@ -306,6 +324,7 @@ func TestTokensRevokeOwnerRightsEndsWebSessions(t *testing.T) {
306324 // A token admin sees and revokes only the tokens it could have created: the
307325 // no-escalation rule of Create, applied to scopes, repositories and expiry.
308326 func TestTokensAdminReach(t *testing.T) {
327+ t.Parallel()
309328 ctx := context.Background()
310329 exp := now.Add(48 * time.Hour)
311330 admin := func(repos []domain.RepoName, expires time.Time, extra ...domain.Scope) domain.Principal {
@@ -343,6 +362,7 @@ func TestTokensAdminReach(t *testing.T) {
343362 }
344363 for _, tt := range tests {
345364 t.Run("list: "+tt.name, func(t *testing.T) {
365+ t.Parallel()
346366 f := newTokenFixture(t)
347367 other := tok("other", scopes(domain.ScopeRepoRead), nil, time.Time{})
348368 f.store.EXPECT().List(ctx).Return([]domain.Token{tt.target, other}, nil)
@@ -356,6 +376,7 @@ func TestTokensAdminReach(t *testing.T) {
356376 assert.Contains(t, got, tt.target)
357377 })
358378 t.Run("revoke: "+tt.name, func(t *testing.T) {
379+ t.Parallel()
359380 f := newTokenFixture(t)
360381 f.store.EXPECT().Get(ctx, "t").Return(tt.target, nil)
361382 if tt.within {
@@ -369,6 +390,7 @@ func TestTokensAdminReach(t *testing.T) {
369390 }
370391
371392 t.Run("root lists and revokes everything", func(t *testing.T) {
393+ t.Parallel()
372394 f := newTokenFixture(t)
373395 all := []domain.Token{tok("x", domain.AllScopes(), nil, time.Time{}), tok("y", scopes(domain.ScopeMRMerge), a, exp)}
374396 f.store.EXPECT().List(ctx).Return(all, nil)
@@ -382,12 +404,14 @@ func TestTokensAdminReach(t *testing.T) {
382404 })
383405
384406 t.Run("an unknown id is not found for everyone", func(t *testing.T) {
407+ t.Parallel()
385408 f := newTokenFixture(t)
386409 f.store.EXPECT().Get(ctx, "nope").Return(domain.Token{}, domain.ErrNotFound)
387410 assert.ErrorIs(t, f.tokens.Revoke(ctx, admin(nil, time.Time{}), "nope"), domain.ErrNotFound)
388411 })
389412
390413 t.Run("a store failure on the lookup is returned", func(t *testing.T) {
414+ t.Parallel()
391415 f := newTokenFixture(t)
392416 boom := errors.New("boom")
393417 f.store.EXPECT().Get(ctx, "t").Return(domain.Token{}, boom)
@@ -396,33 +420,40 @@ func TestTokensAdminReach(t *testing.T) {
396420 }
397421
398422 func TestAuthRevalidate(t *testing.T) {
423+ t.Parallel()
399424 ctx := context.Background()
400425 p := domain.TokenPrincipal(domain.Token{ID: "t1", Scopes: scopes(domain.ScopeRepoWrite)})
401426
402427 t.Run("root is always valid", func(t *testing.T) {
428+ t.Parallel()
403429 f := newTokenFixture(t)
404430 require.NoError(t, f.auth.Revalidate(ctx, domain.Root()))
405431 })
406432 t.Run("anonymous is not", func(t *testing.T) {
433+ t.Parallel()
407434 f := newTokenFixture(t)
408435 assert.ErrorIs(t, f.auth.Revalidate(ctx, domain.Anonymous()), domain.ErrUnauthenticated)
409436 })
410437 t.Run("a stored token that exists", func(t *testing.T) {
438+ t.Parallel()
411439 f := newTokenFixture(t)
412440 f.store.EXPECT().Get(ctx, "t1").Return(domain.Token{ID: "t1", ExpiresAt: now.Add(time.Hour)}, nil)
413441 require.NoError(t, f.auth.Revalidate(ctx, p))
414442 })
415443 t.Run("revoked", func(t *testing.T) {
444+ t.Parallel()
416445 f := newTokenFixture(t)
417446 f.store.EXPECT().Get(ctx, "t1").Return(domain.Token{}, domain.ErrNotFound)
418447 assert.ErrorIs(t, f.auth.Revalidate(ctx, p), domain.ErrUnauthenticated)
419448 })
420449 t.Run("expired", func(t *testing.T) {
450+ t.Parallel()
421451 f := newTokenFixture(t)
422452 f.store.EXPECT().Get(ctx, "t1").Return(domain.Token{ID: "t1", ExpiresAt: now}, nil)
423453 assert.ErrorIs(t, f.auth.Revalidate(ctx, p), domain.ErrUnauthenticated)
424454 })
425455 t.Run("a store failure is an error, not a pass", func(t *testing.T) {
456+ t.Parallel()
426457 f := newTokenFixture(t)
427458 boom := errors.New("boom")
428459 f.store.EXPECT().Get(ctx, "t1").Return(domain.Token{}, boom)
@@ -433,6 +464,7 @@ func TestAuthRevalidate(t *testing.T) {
433464 }
434465
435466 func TestAuthenticate(t *testing.T) {
467+ t.Parallel()
436468 ctx := context.Background()
437469 secret := "ermite_stored-secret"
438470 hash := domain.HashTokenSecret(secret)
@@ -442,6 +474,7 @@ func TestAuthenticate(t *testing.T) {
442474 }
443475
444476 t.Run("root", func(t *testing.T) {
477+ t.Parallel()
445478 f := newTokenFixture(t)
446479 p, err := f.auth.Authenticate(ctx, rootSecret)
447480 require.NoError(t, err)
@@ -449,6 +482,7 @@ func TestAuthenticate(t *testing.T) {
449482 })
450483
451484 t.Run("empty and unknown secrets", func(t *testing.T) {
485+ t.Parallel()
452486 f := newTokenFixture(t)
453487 _, err := f.auth.Authenticate(ctx, "")
454488 assert.ErrorIs(t, err, domain.ErrUnauthenticated)
@@ -458,6 +492,7 @@ func TestAuthenticate(t *testing.T) {
458492 })
459493
460494 t.Run("stored token first use touches", func(t *testing.T) {
495+ t.Parallel()
461496 f := newTokenFixture(t)
462497 f.store.EXPECT().FindByHash(ctx, hash).Return(tok, nil)
463498 f.store.EXPECT().Touch(ctx, "t1", now).Return(nil)
@@ -469,6 +504,7 @@ func TestAuthenticate(t *testing.T) {
469504 })
470505
471506 t.Run("touch at most once a minute", func(t *testing.T) {
507+ t.Parallel()
472508 f := newTokenFixture(t)
473509 recent := tok
474510 recent.LastUsedAt = now.Add(-30 * time.Second)
@@ -486,6 +522,7 @@ func TestAuthenticate(t *testing.T) {
486522 })
487523
488524 t.Run("touch failure does not fail authentication", func(t *testing.T) {
525+ t.Parallel()
489526 f := newTokenFixture(t)
490527 f.store.EXPECT().FindByHash(ctx, hash).Return(tok, nil)
491528 f.store.EXPECT().Touch(ctx, "t1", now).Return(errors.New("disk"))
@@ -494,6 +531,7 @@ func TestAuthenticate(t *testing.T) {
494531 })
495532
496533 t.Run("expired", func(t *testing.T) {
534+ t.Parallel()
497535 f := newTokenFixture(t)
498536 exp := tok
499537 exp.ExpiresAt = now
@@ -503,6 +541,7 @@ func TestAuthenticate(t *testing.T) {
503541 })
504542
505543 t.Run("store error is not an authentication failure", func(t *testing.T) {
544+ t.Parallel()
506545 f := newTokenFixture(t)
507546 boom := errors.New("boom")
508547 f.store.EXPECT().FindByHash(ctx, hash).Return(domain.Token{}, boom)
@@ -51,6 +51,7 @@ func names(t *testing.T, ss ...string) []domain.RepoName {
51 51
52 // Labels that only contain a reserved word are fine.52 // Labels that only contain a reserved word are fine.
53 func TestTokensCreateAllowsLabelsNearReserved(t *testing.T) {53 func TestTokensCreateAllowsLabelsNearReserved(t *testing.T) {
54+ t.Parallel()
54 ctx := context.Background()55 ctx := context.Background()
55 tests := []struct{ name, label string }{56 tests := []struct{ name, label string }{
56 {"digit suffix", "owner2"},57 {"digit suffix", "owner2"},
@@ -61,6 +62,7 @@ func TestTokensCreateAllowsLabelsNearReserved(t *testing.T) {
61 }62 }
62 for _, tt := range tests {63 for _, tt := range tests {
63 t.Run(tt.name, func(t *testing.T) {64 t.Run(tt.name, func(t *testing.T) {
65+ t.Parallel()
64 f := newTokenFixture(t)66 f := newTokenFixture(t)
65 f.store.EXPECT().Create(ctx, mock.Anything).Return(nil)67 f.store.EXPECT().Create(ctx, mock.Anything).Return(nil)
66 _, err := f.tokens.Create(ctx, domain.Root(), app.CreateTokenParams{Label: tt.label, Scopes: scopes(domain.ScopeRepoRead)})68 _, err := f.tokens.Create(ctx, domain.Root(), app.CreateTokenParams{Label: tt.label, Scopes: scopes(domain.ScopeRepoRead)})
@@ -70,9 +72,11 @@ func TestTokensCreateAllowsLabelsNearReserved(t *testing.T) {
70 }72 }
71 73
72 func TestTokensCreate(t *testing.T) {74 func TestTokensCreate(t *testing.T) {
75+ t.Parallel()
73 ctx := context.Background()76 ctx := context.Background()
74 77
75 t.Run("root creates a token with a well-formed secret", func(t *testing.T) {78 t.Run("root creates a token with a well-formed secret", func(t *testing.T) {
79+ t.Parallel()
76 f := newTokenFixture(t)80 f := newTokenFixture(t)
77 var stored domain.Token81 var stored domain.Token
78 f.store.EXPECT().Create(ctx, mock.Anything).Run(func(_ context.Context, tok domain.Token) { stored = tok }).Return(nil)82 f.store.EXPECT().Create(ctx, mock.Anything).Run(func(_ context.Context, tok domain.Token) { stored = tok }).Return(nil)
@@ -102,6 +106,7 @@ func TestTokensCreate(t *testing.T) {
102 })106 })
103 107
104 t.Run("secrets are unique", func(t *testing.T) {108 t.Run("secrets are unique", func(t *testing.T) {
109+ t.Parallel()
105 f := newTokenFixture(t)110 f := newTokenFixture(t)
106 f.store.EXPECT().Create(ctx, mock.Anything).Return(nil)111 f.store.EXPECT().Create(ctx, mock.Anything).Return(nil)
107 a, err := f.tokens.Create(ctx, domain.Root(), app.CreateTokenParams{Label: "a", Scopes: scopes(domain.ScopeRepoRead)})112 a, err := f.tokens.Create(ctx, domain.Root(), app.CreateTokenParams{Label: "a", Scopes: scopes(domain.ScopeRepoRead)})
@@ -113,6 +118,7 @@ func TestTokensCreate(t *testing.T) {
113 })118 })
114 119
115 t.Run("retries on a collision", func(t *testing.T) {120 t.Run("retries on a collision", func(t *testing.T) {
121+ t.Parallel()
116 f := newTokenFixture(t)122 f := newTokenFixture(t)
117 f.store.EXPECT().Create(ctx, mock.Anything).Return(domain.ErrAlreadyExists).Once()123 f.store.EXPECT().Create(ctx, mock.Anything).Return(domain.ErrAlreadyExists).Once()
118 f.store.EXPECT().Create(ctx, mock.Anything).Return(nil).Once()124 f.store.EXPECT().Create(ctx, mock.Anything).Return(nil).Once()
@@ -121,6 +127,7 @@ func TestTokensCreate(t *testing.T) {
121 })127 })
122 128
123 t.Run("store failure", func(t *testing.T) {129 t.Run("store failure", func(t *testing.T) {
130+ t.Parallel()
124 f := newTokenFixture(t)131 f := newTokenFixture(t)
125 boom := errors.New("boom")132 boom := errors.New("boom")
126 f.store.EXPECT().Create(ctx, mock.Anything).Return(boom)133 f.store.EXPECT().Create(ctx, mock.Anything).Return(boom)
@@ -148,6 +155,7 @@ func TestTokensCreate(t *testing.T) {
148 }155 }
149 for _, tt := range invalid {156 for _, tt := range invalid {
150 t.Run("invalid/"+tt.name, func(t *testing.T) {157 t.Run("invalid/"+tt.name, func(t *testing.T) {
158+ t.Parallel()
151 f := newTokenFixture(t)159 f := newTokenFixture(t)
152 _, err := f.tokens.Create(ctx, domain.Root(), tt.params)160 _, err := f.tokens.Create(ctx, domain.Root(), tt.params)
153 assert.ErrorIs(t, err, tt.want)161 assert.ErrorIs(t, err, tt.want)
@@ -156,6 +164,7 @@ func TestTokensCreate(t *testing.T) {
156 }164 }
157 165
158 func TestTokensCreateNoEscalation(t *testing.T) {166 func TestTokensCreateNoEscalation(t *testing.T) {
167+ t.Parallel()
159 ctx := context.Background()168 ctx := context.Background()
160 exp := now.Add(time.Hour)169 exp := now.Add(time.Hour)
161 admin := func(repos []domain.RepoName, expires time.Time, extra ...domain.Scope) domain.Principal {170 admin := func(repos []domain.RepoName, expires time.Time, extra ...domain.Scope) domain.Principal {
@@ -204,6 +213,7 @@ func TestTokensCreateNoEscalation(t *testing.T) {
204 }213 }
205 for _, tt := range tests {214 for _, tt := range tests {
206 t.Run(tt.name, func(t *testing.T) {215 t.Run(tt.name, func(t *testing.T) {
216+ t.Parallel()
207 f := newTokenFixture(t)217 f := newTokenFixture(t)
208 if tt.want == nil {218 if tt.want == nil {
209 f.store.EXPECT().Create(ctx, mock.Anything).Return(nil)219 f.store.EXPECT().Create(ctx, mock.Anything).Return(nil)
@@ -221,11 +231,13 @@ func TestTokensCreateNoEscalation(t *testing.T) {
221 }231 }
222 232
223 func TestTokensListRevoke(t *testing.T) {233 func TestTokensListRevoke(t *testing.T) {
234+ t.Parallel()
224 ctx := context.Background()235 ctx := context.Background()
225 tokenAdmin := domain.TokenPrincipal(domain.Token{ID: "adm", Scopes: scopes(domain.ScopeTokenAdmin)})236 tokenAdmin := domain.TokenPrincipal(domain.Token{ID: "adm", Scopes: scopes(domain.ScopeTokenAdmin)})
226 writer := domain.TokenPrincipal(domain.Token{Scopes: scopes(domain.ScopeRepoWrite)})237 writer := domain.TokenPrincipal(domain.Token{Scopes: scopes(domain.ScopeRepoWrite)})
227 238
228 t.Run("list for the admin token and root", func(t *testing.T) {239 t.Run("list for the admin token and root", func(t *testing.T) {
240+ t.Parallel()
229 f := newTokenFixture(t)241 f := newTokenFixture(t)
230 want := []domain.Token{{ID: "a", Scopes: scopes(domain.ScopeTokenAdmin)}}242 want := []domain.Token{{ID: "a", Scopes: scopes(domain.ScopeTokenAdmin)}}
231 f.store.EXPECT().List(ctx).Return(want, nil).Twice()243 f.store.EXPECT().List(ctx).Return(want, nil).Twice()
@@ -241,6 +253,7 @@ func TestTokensListRevoke(t *testing.T) {
241 })253 })
242 254
243 t.Run("revoke", func(t *testing.T) {255 t.Run("revoke", func(t *testing.T) {
256+ t.Parallel()
244 f := newTokenFixture(t)257 f := newTokenFixture(t)
245 f.store.EXPECT().Get(ctx, "a").Return(domain.Token{ID: "a", Scopes: scopes(domain.ScopeTokenAdmin)}, nil).Twice()258 f.store.EXPECT().Get(ctx, "a").Return(domain.Token{ID: "a", Scopes: scopes(domain.ScopeTokenAdmin)}, nil).Twice()
246 f.store.EXPECT().Get(ctx, "zz").Return(domain.Token{}, domain.ErrNotFound).Once()259 f.store.EXPECT().Get(ctx, "zz").Return(domain.Token{}, domain.ErrNotFound).Once()
@@ -256,6 +269,7 @@ func TestTokensListRevoke(t *testing.T) {
256 // GO-003b: revoking a token with owner rights signs every browser out (the269 // GO-003b: revoking a token with owner rights signs every browser out (the
257 // session epoch is bumped); revoking any other token does not.270 // session epoch is bumped); revoking any other token does not.
258 func TestTokensRevokeOwnerRightsEndsWebSessions(t *testing.T) {271 func TestTokensRevokeOwnerRightsEndsWebSessions(t *testing.T) {
272+ t.Parallel()
259 ctx := context.Background()273 ctx := context.Background()
260 ownerTok := domain.Token{ID: "own", Scopes: domain.AllScopes()}274 ownerTok := domain.Token{ID: "own", Scopes: domain.AllScopes()}
261 275
@@ -270,6 +284,7 @@ func TestTokensRevokeOwnerRightsEndsWebSessions(t *testing.T) {
270 "repo list": {domain.Token{ID: "l", Scopes: domain.AllScopes(), Repos: names(t, "a")}, domain.Root(), false},284 "repo list": {domain.Token{ID: "l", Scopes: domain.AllScopes(), Repos: names(t, "a")}, domain.Root(), false},
271 } {285 } {
272 t.Run(name, func(t *testing.T) {286 t.Run(name, func(t *testing.T) {
287+ t.Parallel()
273 f := newTokenFixture(t)288 f := newTokenFixture(t)
274 f.store.EXPECT().Get(ctx, tc.tok.ID).Return(tc.tok, nil)289 f.store.EXPECT().Get(ctx, tc.tok.ID).Return(tc.tok, nil)
275 f.store.EXPECT().Revoke(ctx, tc.tok.ID).Return(nil)290 f.store.EXPECT().Revoke(ctx, tc.tok.ID).Return(nil)
@@ -281,6 +296,7 @@ func TestTokensRevokeOwnerRightsEndsWebSessions(t *testing.T) {
281 }296 }
282 297
283 t.Run("a revoke that fails bumps nothing", func(t *testing.T) {298 t.Run("a revoke that fails bumps nothing", func(t *testing.T) {
299+ t.Parallel()
284 f := newTokenFixture(t)300 f := newTokenFixture(t)
285 f.store.EXPECT().Get(ctx, "own").Return(ownerTok, nil)301 f.store.EXPECT().Get(ctx, "own").Return(ownerTok, nil)
286 f.store.EXPECT().Revoke(ctx, "own").Return(errors.New("kv down"))302 f.store.EXPECT().Revoke(ctx, "own").Return(errors.New("kv down"))
@@ -288,6 +304,7 @@ func TestTokensRevokeOwnerRightsEndsWebSessions(t *testing.T) {
288 })304 })
289 305
290 t.Run("a token out of reach is not revoked and bumps nothing", func(t *testing.T) {306 t.Run("a token out of reach is not revoked and bumps nothing", func(t *testing.T) {
307+ t.Parallel()
291 f := newTokenFixture(t)308 f := newTokenFixture(t)
292 limited := domain.TokenPrincipal(domain.Token{ID: "adm", Scopes: scopes(domain.ScopeTokenAdmin)})309 limited := domain.TokenPrincipal(domain.Token{ID: "adm", Scopes: scopes(domain.ScopeTokenAdmin)})
293 f.store.EXPECT().Get(ctx, "own").Return(ownerTok, nil)310 f.store.EXPECT().Get(ctx, "own").Return(ownerTok, nil)
@@ -295,6 +312,7 @@ func TestTokensRevokeOwnerRightsEndsWebSessions(t *testing.T) {
295 })312 })
296 313
297 t.Run("a failure to sign out is reported once the token is revoked", func(t *testing.T) {314 t.Run("a failure to sign out is reported once the token is revoked", func(t *testing.T) {
315+ t.Parallel()
298 f := newTokenFixture(t)316 f := newTokenFixture(t)
299 f.store.EXPECT().Get(ctx, "own").Return(ownerTok, nil)317 f.store.EXPECT().Get(ctx, "own").Return(ownerTok, nil)
300 f.store.EXPECT().Revoke(ctx, "own").Return(nil)318 f.store.EXPECT().Revoke(ctx, "own").Return(nil)
@@ -306,6 +324,7 @@ func TestTokensRevokeOwnerRightsEndsWebSessions(t *testing.T) {
306 // A token admin sees and revokes only the tokens it could have created: the324 // A token admin sees and revokes only the tokens it could have created: the
307 // no-escalation rule of Create, applied to scopes, repositories and expiry.325 // no-escalation rule of Create, applied to scopes, repositories and expiry.
308 func TestTokensAdminReach(t *testing.T) {326 func TestTokensAdminReach(t *testing.T) {
327+ t.Parallel()
309 ctx := context.Background()328 ctx := context.Background()
310 exp := now.Add(48 * time.Hour)329 exp := now.Add(48 * time.Hour)
311 admin := func(repos []domain.RepoName, expires time.Time, extra ...domain.Scope) domain.Principal {330 admin := func(repos []domain.RepoName, expires time.Time, extra ...domain.Scope) domain.Principal {
@@ -343,6 +362,7 @@ func TestTokensAdminReach(t *testing.T) {
343 }362 }
344 for _, tt := range tests {363 for _, tt := range tests {
345 t.Run("list: "+tt.name, func(t *testing.T) {364 t.Run("list: "+tt.name, func(t *testing.T) {
365+ t.Parallel()
346 f := newTokenFixture(t)366 f := newTokenFixture(t)
347 other := tok("other", scopes(domain.ScopeRepoRead), nil, time.Time{})367 other := tok("other", scopes(domain.ScopeRepoRead), nil, time.Time{})
348 f.store.EXPECT().List(ctx).Return([]domain.Token{tt.target, other}, nil)368 f.store.EXPECT().List(ctx).Return([]domain.Token{tt.target, other}, nil)
@@ -356,6 +376,7 @@ func TestTokensAdminReach(t *testing.T) {
356 assert.Contains(t, got, tt.target)376 assert.Contains(t, got, tt.target)
357 })377 })
358 t.Run("revoke: "+tt.name, func(t *testing.T) {378 t.Run("revoke: "+tt.name, func(t *testing.T) {
379+ t.Parallel()
359 f := newTokenFixture(t)380 f := newTokenFixture(t)
360 f.store.EXPECT().Get(ctx, "t").Return(tt.target, nil)381 f.store.EXPECT().Get(ctx, "t").Return(tt.target, nil)
361 if tt.within {382 if tt.within {
@@ -369,6 +390,7 @@ func TestTokensAdminReach(t *testing.T) {
369 }390 }
370 391
371 t.Run("root lists and revokes everything", func(t *testing.T) {392 t.Run("root lists and revokes everything", func(t *testing.T) {
393+ t.Parallel()
372 f := newTokenFixture(t)394 f := newTokenFixture(t)
373 all := []domain.Token{tok("x", domain.AllScopes(), nil, time.Time{}), tok("y", scopes(domain.ScopeMRMerge), a, exp)}395 all := []domain.Token{tok("x", domain.AllScopes(), nil, time.Time{}), tok("y", scopes(domain.ScopeMRMerge), a, exp)}
374 f.store.EXPECT().List(ctx).Return(all, nil)396 f.store.EXPECT().List(ctx).Return(all, nil)
@@ -382,12 +404,14 @@ func TestTokensAdminReach(t *testing.T) {
382 })404 })
383 405
384 t.Run("an unknown id is not found for everyone", func(t *testing.T) {406 t.Run("an unknown id is not found for everyone", func(t *testing.T) {
407+ t.Parallel()
385 f := newTokenFixture(t)408 f := newTokenFixture(t)
386 f.store.EXPECT().Get(ctx, "nope").Return(domain.Token{}, domain.ErrNotFound)409 f.store.EXPECT().Get(ctx, "nope").Return(domain.Token{}, domain.ErrNotFound)
387 assert.ErrorIs(t, f.tokens.Revoke(ctx, admin(nil, time.Time{}), "nope"), domain.ErrNotFound)410 assert.ErrorIs(t, f.tokens.Revoke(ctx, admin(nil, time.Time{}), "nope"), domain.ErrNotFound)
388 })411 })
389 412
390 t.Run("a store failure on the lookup is returned", func(t *testing.T) {413 t.Run("a store failure on the lookup is returned", func(t *testing.T) {
414+ t.Parallel()
391 f := newTokenFixture(t)415 f := newTokenFixture(t)
392 boom := errors.New("boom")416 boom := errors.New("boom")
393 f.store.EXPECT().Get(ctx, "t").Return(domain.Token{}, boom)417 f.store.EXPECT().Get(ctx, "t").Return(domain.Token{}, boom)
@@ -396,33 +420,40 @@ func TestTokensAdminReach(t *testing.T) {
396 }420 }
397 421
398 func TestAuthRevalidate(t *testing.T) {422 func TestAuthRevalidate(t *testing.T) {
423+ t.Parallel()
399 ctx := context.Background()424 ctx := context.Background()
400 p := domain.TokenPrincipal(domain.Token{ID: "t1", Scopes: scopes(domain.ScopeRepoWrite)})425 p := domain.TokenPrincipal(domain.Token{ID: "t1", Scopes: scopes(domain.ScopeRepoWrite)})
401 426
402 t.Run("root is always valid", func(t *testing.T) {427 t.Run("root is always valid", func(t *testing.T) {
428+ t.Parallel()
403 f := newTokenFixture(t)429 f := newTokenFixture(t)
404 require.NoError(t, f.auth.Revalidate(ctx, domain.Root()))430 require.NoError(t, f.auth.Revalidate(ctx, domain.Root()))
405 })431 })
406 t.Run("anonymous is not", func(t *testing.T) {432 t.Run("anonymous is not", func(t *testing.T) {
433+ t.Parallel()
407 f := newTokenFixture(t)434 f := newTokenFixture(t)
408 assert.ErrorIs(t, f.auth.Revalidate(ctx, domain.Anonymous()), domain.ErrUnauthenticated)435 assert.ErrorIs(t, f.auth.Revalidate(ctx, domain.Anonymous()), domain.ErrUnauthenticated)
409 })436 })
410 t.Run("a stored token that exists", func(t *testing.T) {437 t.Run("a stored token that exists", func(t *testing.T) {
438+ t.Parallel()
411 f := newTokenFixture(t)439 f := newTokenFixture(t)
412 f.store.EXPECT().Get(ctx, "t1").Return(domain.Token{ID: "t1", ExpiresAt: now.Add(time.Hour)}, nil)440 f.store.EXPECT().Get(ctx, "t1").Return(domain.Token{ID: "t1", ExpiresAt: now.Add(time.Hour)}, nil)
413 require.NoError(t, f.auth.Revalidate(ctx, p))441 require.NoError(t, f.auth.Revalidate(ctx, p))
414 })442 })
415 t.Run("revoked", func(t *testing.T) {443 t.Run("revoked", func(t *testing.T) {
444+ t.Parallel()
416 f := newTokenFixture(t)445 f := newTokenFixture(t)
417 f.store.EXPECT().Get(ctx, "t1").Return(domain.Token{}, domain.ErrNotFound)446 f.store.EXPECT().Get(ctx, "t1").Return(domain.Token{}, domain.ErrNotFound)
418 assert.ErrorIs(t, f.auth.Revalidate(ctx, p), domain.ErrUnauthenticated)447 assert.ErrorIs(t, f.auth.Revalidate(ctx, p), domain.ErrUnauthenticated)
419 })448 })
420 t.Run("expired", func(t *testing.T) {449 t.Run("expired", func(t *testing.T) {
450+ t.Parallel()
421 f := newTokenFixture(t)451 f := newTokenFixture(t)
422 f.store.EXPECT().Get(ctx, "t1").Return(domain.Token{ID: "t1", ExpiresAt: now}, nil)452 f.store.EXPECT().Get(ctx, "t1").Return(domain.Token{ID: "t1", ExpiresAt: now}, nil)
423 assert.ErrorIs(t, f.auth.Revalidate(ctx, p), domain.ErrUnauthenticated)453 assert.ErrorIs(t, f.auth.Revalidate(ctx, p), domain.ErrUnauthenticated)
424 })454 })
425 t.Run("a store failure is an error, not a pass", func(t *testing.T) {455 t.Run("a store failure is an error, not a pass", func(t *testing.T) {
456+ t.Parallel()
426 f := newTokenFixture(t)457 f := newTokenFixture(t)
427 boom := errors.New("boom")458 boom := errors.New("boom")
428 f.store.EXPECT().Get(ctx, "t1").Return(domain.Token{}, boom)459 f.store.EXPECT().Get(ctx, "t1").Return(domain.Token{}, boom)
@@ -433,6 +464,7 @@ func TestAuthRevalidate(t *testing.T) {
433 }464 }
434 465
435 func TestAuthenticate(t *testing.T) {466 func TestAuthenticate(t *testing.T) {
467+ t.Parallel()
436 ctx := context.Background()468 ctx := context.Background()
437 secret := "ermite_stored-secret"469 secret := "ermite_stored-secret"
438 hash := domain.HashTokenSecret(secret)470 hash := domain.HashTokenSecret(secret)
@@ -442,6 +474,7 @@ func TestAuthenticate(t *testing.T) {
442 }474 }
443 475
444 t.Run("root", func(t *testing.T) {476 t.Run("root", func(t *testing.T) {
477+ t.Parallel()
445 f := newTokenFixture(t)478 f := newTokenFixture(t)
446 p, err := f.auth.Authenticate(ctx, rootSecret)479 p, err := f.auth.Authenticate(ctx, rootSecret)
447 require.NoError(t, err)480 require.NoError(t, err)
@@ -449,6 +482,7 @@ func TestAuthenticate(t *testing.T) {
449 })482 })
450 483
451 t.Run("empty and unknown secrets", func(t *testing.T) {484 t.Run("empty and unknown secrets", func(t *testing.T) {
485+ t.Parallel()
452 f := newTokenFixture(t)486 f := newTokenFixture(t)
453 _, err := f.auth.Authenticate(ctx, "")487 _, err := f.auth.Authenticate(ctx, "")
454 assert.ErrorIs(t, err, domain.ErrUnauthenticated)488 assert.ErrorIs(t, err, domain.ErrUnauthenticated)
@@ -458,6 +492,7 @@ func TestAuthenticate(t *testing.T) {
458 })492 })
459 493
460 t.Run("stored token first use touches", func(t *testing.T) {494 t.Run("stored token first use touches", func(t *testing.T) {
495+ t.Parallel()
461 f := newTokenFixture(t)496 f := newTokenFixture(t)
462 f.store.EXPECT().FindByHash(ctx, hash).Return(tok, nil)497 f.store.EXPECT().FindByHash(ctx, hash).Return(tok, nil)
463 f.store.EXPECT().Touch(ctx, "t1", now).Return(nil)498 f.store.EXPECT().Touch(ctx, "t1", now).Return(nil)
@@ -469,6 +504,7 @@ func TestAuthenticate(t *testing.T) {
469 })504 })
470 505
471 t.Run("touch at most once a minute", func(t *testing.T) {506 t.Run("touch at most once a minute", func(t *testing.T) {
507+ t.Parallel()
472 f := newTokenFixture(t)508 f := newTokenFixture(t)
473 recent := tok509 recent := tok
474 recent.LastUsedAt = now.Add(-30 * time.Second)510 recent.LastUsedAt = now.Add(-30 * time.Second)
@@ -486,6 +522,7 @@ func TestAuthenticate(t *testing.T) {
486 })522 })
487 523
488 t.Run("touch failure does not fail authentication", func(t *testing.T) {524 t.Run("touch failure does not fail authentication", func(t *testing.T) {
525+ t.Parallel()
489 f := newTokenFixture(t)526 f := newTokenFixture(t)
490 f.store.EXPECT().FindByHash(ctx, hash).Return(tok, nil)527 f.store.EXPECT().FindByHash(ctx, hash).Return(tok, nil)
491 f.store.EXPECT().Touch(ctx, "t1", now).Return(errors.New("disk"))528 f.store.EXPECT().Touch(ctx, "t1", now).Return(errors.New("disk"))
@@ -494,6 +531,7 @@ func TestAuthenticate(t *testing.T) {
494 })531 })
495 532
496 t.Run("expired", func(t *testing.T) {533 t.Run("expired", func(t *testing.T) {
534+ t.Parallel()
497 f := newTokenFixture(t)535 f := newTokenFixture(t)
498 exp := tok536 exp := tok
499 exp.ExpiresAt = now537 exp.ExpiresAt = now
@@ -503,6 +541,7 @@ func TestAuthenticate(t *testing.T) {
503 })541 })
504 542
505 t.Run("store error is not an authentication failure", func(t *testing.T) {543 t.Run("store error is not an authentication failure", func(t *testing.T) {
544+ t.Parallel()
506 f := newTokenFixture(t)545 f := newTokenFixture(t)
507 boom := errors.New("boom")546 boom := errors.New("boom")
508 f.store.EXPECT().FindByHash(ctx, hash).Return(domain.Token{}, boom)547 f.store.EXPECT().FindByHash(ctx, hash).Return(domain.Token{}, boom)
M internal/clientlimit/limiter_test.go +9 -0
@@ -23,6 +23,7 @@ type fakeClock struct{ t time.Time }
2323 func (c *fakeClock) now() time.Time { return c.t }
2424
2525 func TestLimiter(t *testing.T) {
26+ t.Parallel()
2627 clk := &fakeClock{t: time.Date(2026, 10, 10, 8, 0, 0, 0, time.UTC)}
2728 l := clientlimit.NewLimiter(clk.now)
2829
@@ -52,6 +53,7 @@ func TestLimiter(t *testing.T) {
5253 }
5354
5455 func TestLimiterSlidingWindow(t *testing.T) {
56+ t.Parallel()
5557 clk := &fakeClock{t: time.Date(2026, 10, 10, 8, 0, 0, 0, time.UTC)}
5658 l := clientlimit.NewLimiter(clk.now)
5759 for range 5 {
@@ -71,6 +73,7 @@ func TestLimiterSlidingWindow(t *testing.T) {
7173 }
7274
7375 func TestLimiterOneAddressStaysSmall(t *testing.T) {
76+ t.Parallel()
7477 clk := &fakeClock{t: time.Date(2026, 10, 10, 8, 0, 0, 0, time.UTC)}
7578 l := clientlimit.NewLimiter(clk.now)
7679 for range 5000 {
@@ -82,6 +85,7 @@ func TestLimiterOneAddressStaysSmall(t *testing.T) {
8285 }
8386
8487 func TestLimiterBoundsMemory(t *testing.T) {
88+ t.Parallel()
8589 clk := &fakeClock{t: time.Date(2026, 10, 10, 8, 0, 0, 0, time.UTC)}
8690 l := clientlimit.NewLimiter(clk.now)
8791 for i := range clientlimit.MaxTracked + 5000 {
@@ -103,6 +107,7 @@ func TestLimiterBoundsMemory(t *testing.T) {
103107 }
104108
105109 func TestLimiterSweepsExpiredEntriesOncePerInterval(t *testing.T) {
110+ t.Parallel()
106111 clk := &fakeClock{t: time.Date(2026, 10, 10, 8, 0, 0, 0, time.UTC)}
107112 l := clientlimit.NewLimiter(clk.now)
108113 for i := range 100 {
@@ -135,6 +140,7 @@ func BenchmarkLimiterFailAtMaxTracked(b *testing.B) {
135140 }
136141
137142 func TestLimiterGroupsIPv6By64(t *testing.T) {
143+ t.Parallel()
138144 clk := &fakeClock{t: time.Date(2026, 10, 10, 8, 0, 0, 0, time.UTC)}
139145 l := clientlimit.NewLimiter(clk.now)
140146
@@ -155,6 +161,7 @@ func TestLimiterGroupsIPv6By64(t *testing.T) {
155161 }
156162
157163 func TestLimiterIPv4StaysPerAddress(t *testing.T) {
164+ t.Parallel()
158165 clk := &fakeClock{t: time.Date(2026, 10, 10, 8, 0, 0, 0, time.UTC)}
159166 l := clientlimit.NewLimiter(clk.now)
160167 for range clientlimit.MaxFailures {
@@ -170,6 +177,7 @@ func TestLimiterIPv4StaysPerAddress(t *testing.T) {
170177 }
171178
172179 func TestClientAddr(t *testing.T) {
180+ t.Parallel()
173181 trusted := []netip.Prefix{netip.MustParsePrefix("10.0.0.0/8"), netip.MustParsePrefix("fd00::/8")}
174182 tests := []struct {
175183 name string
@@ -194,6 +202,7 @@ func TestClientAddr(t *testing.T) {
194202 }
195203 for _, tt := range tests {
196204 t.Run(tt.name, func(t *testing.T) {
205+ t.Parallel()
197206 r := &http.Request{RemoteAddr: tt.remote, Header: http.Header{}}
198207 for _, v := range tt.xff {
199208 r.Header.Add("X-Forwarded-For", v)
@@ -23,6 +23,7 @@ type fakeClock struct{ t time.Time }
23 func (c *fakeClock) now() time.Time { return c.t }23 func (c *fakeClock) now() time.Time { return c.t }
24 24
25 func TestLimiter(t *testing.T) {25 func TestLimiter(t *testing.T) {
26+ t.Parallel()
26 clk := &fakeClock{t: time.Date(2026, 10, 10, 8, 0, 0, 0, time.UTC)}27 clk := &fakeClock{t: time.Date(2026, 10, 10, 8, 0, 0, 0, time.UTC)}
27 l := clientlimit.NewLimiter(clk.now)28 l := clientlimit.NewLimiter(clk.now)
28 29
@@ -52,6 +53,7 @@ func TestLimiter(t *testing.T) {
52 }53 }
53 54
54 func TestLimiterSlidingWindow(t *testing.T) {55 func TestLimiterSlidingWindow(t *testing.T) {
56+ t.Parallel()
55 clk := &fakeClock{t: time.Date(2026, 10, 10, 8, 0, 0, 0, time.UTC)}57 clk := &fakeClock{t: time.Date(2026, 10, 10, 8, 0, 0, 0, time.UTC)}
56 l := clientlimit.NewLimiter(clk.now)58 l := clientlimit.NewLimiter(clk.now)
57 for range 5 {59 for range 5 {
@@ -71,6 +73,7 @@ func TestLimiterSlidingWindow(t *testing.T) {
71 }73 }
72 74
73 func TestLimiterOneAddressStaysSmall(t *testing.T) {75 func TestLimiterOneAddressStaysSmall(t *testing.T) {
76+ t.Parallel()
74 clk := &fakeClock{t: time.Date(2026, 10, 10, 8, 0, 0, 0, time.UTC)}77 clk := &fakeClock{t: time.Date(2026, 10, 10, 8, 0, 0, 0, time.UTC)}
75 l := clientlimit.NewLimiter(clk.now)78 l := clientlimit.NewLimiter(clk.now)
76 for range 5000 {79 for range 5000 {
@@ -82,6 +85,7 @@ func TestLimiterOneAddressStaysSmall(t *testing.T) {
82 }85 }
83 86
84 func TestLimiterBoundsMemory(t *testing.T) {87 func TestLimiterBoundsMemory(t *testing.T) {
88+ t.Parallel()
85 clk := &fakeClock{t: time.Date(2026, 10, 10, 8, 0, 0, 0, time.UTC)}89 clk := &fakeClock{t: time.Date(2026, 10, 10, 8, 0, 0, 0, time.UTC)}
86 l := clientlimit.NewLimiter(clk.now)90 l := clientlimit.NewLimiter(clk.now)
87 for i := range clientlimit.MaxTracked + 5000 {91 for i := range clientlimit.MaxTracked + 5000 {
@@ -103,6 +107,7 @@ func TestLimiterBoundsMemory(t *testing.T) {
103 }107 }
104 108
105 func TestLimiterSweepsExpiredEntriesOncePerInterval(t *testing.T) {109 func TestLimiterSweepsExpiredEntriesOncePerInterval(t *testing.T) {
110+ t.Parallel()
106 clk := &fakeClock{t: time.Date(2026, 10, 10, 8, 0, 0, 0, time.UTC)}111 clk := &fakeClock{t: time.Date(2026, 10, 10, 8, 0, 0, 0, time.UTC)}
107 l := clientlimit.NewLimiter(clk.now)112 l := clientlimit.NewLimiter(clk.now)
108 for i := range 100 {113 for i := range 100 {
@@ -135,6 +140,7 @@ func BenchmarkLimiterFailAtMaxTracked(b *testing.B) {
135 }140 }
136 141
137 func TestLimiterGroupsIPv6By64(t *testing.T) {142 func TestLimiterGroupsIPv6By64(t *testing.T) {
143+ t.Parallel()
138 clk := &fakeClock{t: time.Date(2026, 10, 10, 8, 0, 0, 0, time.UTC)}144 clk := &fakeClock{t: time.Date(2026, 10, 10, 8, 0, 0, 0, time.UTC)}
139 l := clientlimit.NewLimiter(clk.now)145 l := clientlimit.NewLimiter(clk.now)
140 146
@@ -155,6 +161,7 @@ func TestLimiterGroupsIPv6By64(t *testing.T) {
155 }161 }
156 162
157 func TestLimiterIPv4StaysPerAddress(t *testing.T) {163 func TestLimiterIPv4StaysPerAddress(t *testing.T) {
164+ t.Parallel()
158 clk := &fakeClock{t: time.Date(2026, 10, 10, 8, 0, 0, 0, time.UTC)}165 clk := &fakeClock{t: time.Date(2026, 10, 10, 8, 0, 0, 0, time.UTC)}
159 l := clientlimit.NewLimiter(clk.now)166 l := clientlimit.NewLimiter(clk.now)
160 for range clientlimit.MaxFailures {167 for range clientlimit.MaxFailures {
@@ -170,6 +177,7 @@ func TestLimiterIPv4StaysPerAddress(t *testing.T) {
170 }177 }
171 178
172 func TestClientAddr(t *testing.T) {179 func TestClientAddr(t *testing.T) {
180+ t.Parallel()
173 trusted := []netip.Prefix{netip.MustParsePrefix("10.0.0.0/8"), netip.MustParsePrefix("fd00::/8")}181 trusted := []netip.Prefix{netip.MustParsePrefix("10.0.0.0/8"), netip.MustParsePrefix("fd00::/8")}
174 tests := []struct {182 tests := []struct {
175 name string183 name string
@@ -194,6 +202,7 @@ func TestClientAddr(t *testing.T) {
194 }202 }
195 for _, tt := range tests {203 for _, tt := range tests {
196 t.Run(tt.name, func(t *testing.T) {204 t.Run(tt.name, func(t *testing.T) {
205+ t.Parallel()
197 r := &http.Request{RemoteAddr: tt.remote, Header: http.Header{}}206 r := &http.Request{RemoteAddr: tt.remote, Header: http.Header{}}
198 for _, v := range tt.xff {207 for _, v := range tt.xff {
199 r.Header.Add("X-Forwarded-For", v)208 r.Header.Add("X-Forwarded-For", v)
M internal/domain/event_test.go +2 -0
@@ -13,6 +13,7 @@ import (
1313 )
1414
1515 func TestNewRefUpdate(t *testing.T) {
16+ t.Parallel()
1617 a, b := strings.Repeat("a", 40), strings.Repeat("b", 40)
1718
1819 u, err := domain.NewRefUpdate(domain.ZeroOID, a, "refs/heads/main")
@@ -46,6 +47,7 @@ func TestNewRefUpdate(t *testing.T) {
4647 }
4748
4849 func TestEventKinds(t *testing.T) {
50+ t.Parallel()
4951 assert.Equal(t, "repo.created", domain.RepoCreated{}.EventKind())
5052 assert.Equal(t, "repo.pushed", domain.PushEvent{}.EventKind())
5153 assert.Equal(t, "gap", domain.EventGap{}.EventKind())
@@ -13,6 +13,7 @@ import (
13 )13 )
14 14
15 func TestNewRefUpdate(t *testing.T) {15 func TestNewRefUpdate(t *testing.T) {
16+ t.Parallel()
16 a, b := strings.Repeat("a", 40), strings.Repeat("b", 40)17 a, b := strings.Repeat("a", 40), strings.Repeat("b", 40)
17 18
18 u, err := domain.NewRefUpdate(domain.ZeroOID, a, "refs/heads/main")19 u, err := domain.NewRefUpdate(domain.ZeroOID, a, "refs/heads/main")
@@ -46,6 +47,7 @@ func TestNewRefUpdate(t *testing.T) {
46 }47 }
47 48
48 func TestEventKinds(t *testing.T) {49 func TestEventKinds(t *testing.T) {
50+ t.Parallel()
49 assert.Equal(t, "repo.created", domain.RepoCreated{}.EventKind())51 assert.Equal(t, "repo.created", domain.RepoCreated{}.EventKind())
50 assert.Equal(t, "repo.pushed", domain.PushEvent{}.EventKind())52 assert.Equal(t, "repo.pushed", domain.PushEvent{}.EventKind())
51 assert.Equal(t, "gap", domain.EventGap{}.EventKind())53 assert.Equal(t, "gap", domain.EventGap{}.EventKind())
M internal/domain/git_test.go +6 -0
@@ -13,6 +13,7 @@ import (
1313 )
1414
1515 func TestParseObjectID(t *testing.T) {
16+ t.Parallel()
1617 good := strings.Repeat("ab12", 10)
1718 id, err := domain.ParseObjectID(good)
1819 require.NoError(t, err)
@@ -27,6 +28,7 @@ func TestParseObjectID(t *testing.T) {
2728 }
2829
2930 func TestParseRevision(t *testing.T) {
31+ t.Parallel()
3032 valid := []string{
3133 "", "main", "feature/x", "refs/heads/feature/x", "v1.0.0", "HEAD",
3234 "abc1234", strings.Repeat("a", 40), "release-2026.10", "café",
@@ -47,6 +49,7 @@ func TestParseRevision(t *testing.T) {
4749 }
4850
4951 func TestRevisionKinds(t *testing.T) {
52+ t.Parallel()
5053 head, _ := domain.ParseRevision("")
5154 assert.True(t, head.IsHead())
5255 full, _ := domain.ParseRevision(strings.Repeat("a", 40))
@@ -64,6 +67,7 @@ func TestRevisionKinds(t *testing.T) {
6467 }
6568
6669 func TestParseTreePath(t *testing.T) {
70+ t.Parallel()
6771 root, err := domain.ParseTreePath("")
6872 require.NoError(t, err)
6973 assert.True(t, root.IsRoot())
@@ -81,6 +85,7 @@ func TestParseTreePath(t *testing.T) {
8185 }
8286
8387 func TestEntryKindOf(t *testing.T) {
88+ t.Parallel()
8489 tests := map[uint32]domain.EntryKind{
8590 0o100644: domain.EntryFile,
8691 0o100664: domain.EntryFile,
@@ -97,5 +102,6 @@ func TestEntryKindOf(t *testing.T) {
97102 }
98103
99104 func TestDefaultDiffLimits(t *testing.T) {
105+ t.Parallel()
100106 assert.Equal(t, domain.DiffLimits{MaxFiles: 300, MaxFileLines: 2000, MaxTotalLines: 20000, MaxTotalBytes: 8 << 20}, domain.DefaultDiffLimits())
101107 }
@@ -13,6 +13,7 @@ import (
13 )13 )
14 14
15 func TestParseObjectID(t *testing.T) {15 func TestParseObjectID(t *testing.T) {
16+ t.Parallel()
16 good := strings.Repeat("ab12", 10)17 good := strings.Repeat("ab12", 10)
17 id, err := domain.ParseObjectID(good)18 id, err := domain.ParseObjectID(good)
18 require.NoError(t, err)19 require.NoError(t, err)
@@ -27,6 +28,7 @@ func TestParseObjectID(t *testing.T) {
27 }28 }
28 29
29 func TestParseRevision(t *testing.T) {30 func TestParseRevision(t *testing.T) {
31+ t.Parallel()
30 valid := []string{32 valid := []string{
31 "", "main", "feature/x", "refs/heads/feature/x", "v1.0.0", "HEAD",33 "", "main", "feature/x", "refs/heads/feature/x", "v1.0.0", "HEAD",
32 "abc1234", strings.Repeat("a", 40), "release-2026.10", "café",34 "abc1234", strings.Repeat("a", 40), "release-2026.10", "café",
@@ -47,6 +49,7 @@ func TestParseRevision(t *testing.T) {
47 }49 }
48 50
49 func TestRevisionKinds(t *testing.T) {51 func TestRevisionKinds(t *testing.T) {
52+ t.Parallel()
50 head, _ := domain.ParseRevision("")53 head, _ := domain.ParseRevision("")
51 assert.True(t, head.IsHead())54 assert.True(t, head.IsHead())
52 full, _ := domain.ParseRevision(strings.Repeat("a", 40))55 full, _ := domain.ParseRevision(strings.Repeat("a", 40))
@@ -64,6 +67,7 @@ func TestRevisionKinds(t *testing.T) {
64 }67 }
65 68
66 func TestParseTreePath(t *testing.T) {69 func TestParseTreePath(t *testing.T) {
70+ t.Parallel()
67 root, err := domain.ParseTreePath("")71 root, err := domain.ParseTreePath("")
68 require.NoError(t, err)72 require.NoError(t, err)
69 assert.True(t, root.IsRoot())73 assert.True(t, root.IsRoot())
@@ -81,6 +85,7 @@ func TestParseTreePath(t *testing.T) {
81 }85 }
82 86
83 func TestEntryKindOf(t *testing.T) {87 func TestEntryKindOf(t *testing.T) {
88+ t.Parallel()
84 tests := map[uint32]domain.EntryKind{89 tests := map[uint32]domain.EntryKind{
85 0o100644: domain.EntryFile,90 0o100644: domain.EntryFile,
86 0o100664: domain.EntryFile,91 0o100664: domain.EntryFile,
@@ -97,5 +102,6 @@ func TestEntryKindOf(t *testing.T) {
97 }102 }
98 103
99 func TestDefaultDiffLimits(t *testing.T) {104 func TestDefaultDiffLimits(t *testing.T) {
105+ t.Parallel()
100 assert.Equal(t, domain.DiffLimits{MaxFiles: 300, MaxFileLines: 2000, MaxTotalLines: 20000, MaxTotalBytes: 8 << 20}, domain.DefaultDiffLimits())106 assert.Equal(t, domain.DiffLimits{MaxFiles: 300, MaxFileLines: 2000, MaxTotalLines: 20000, MaxTotalBytes: 8 << 20}, domain.DefaultDiffLimits())
101 }107 }
M internal/domain/mergerequest_test.go +10 -0
@@ -21,6 +21,7 @@ func oid(t *testing.T, c byte) domain.ObjectID {
2121 }
2222
2323 func TestDefaultRepoSettings(t *testing.T) {
24+ t.Parallel()
2425 s := domain.DefaultRepoSettings()
2526 assert.Equal(t, []domain.MergeMode{domain.MergeModeFastForward, domain.MergeModeSquash}, s.AllowedMergeModes)
2627 assert.True(t, s.FailingChecksBlock)
@@ -35,6 +36,7 @@ func TestDefaultRepoSettings(t *testing.T) {
3536 }
3637
3738 func TestNormalizeMergeModes(t *testing.T) {
39+ t.Parallel()
3840 got, err := domain.NormalizeMergeModes([]domain.MergeMode{domain.MergeModeSquash, domain.MergeModeFastForward, domain.MergeModeSquash})
3941 require.NoError(t, err)
4042 assert.Equal(t, domain.MergeModes(), got)
@@ -50,6 +52,7 @@ func TestNormalizeMergeModes(t *testing.T) {
5052 }
5153
5254 func TestParseMergeMode(t *testing.T) {
55+ t.Parallel()
5356 m, err := domain.ParseMergeMode("fast-forward")
5457 require.NoError(t, err)
5558 assert.Equal(t, domain.MergeModeFastForward, m)
@@ -78,6 +81,7 @@ func hunkDiff() domain.Diff {
7881 }
7982
8083 func TestDiffHasLine(t *testing.T) {
84+ t.Parallel()
8185 d := hunkDiff()
8286 tests := []struct {
8387 name string
@@ -106,6 +110,7 @@ func TestDiffHasLine(t *testing.T) {
106110 }
107111 for _, tc := range tests {
108112 t.Run(tc.name, func(t *testing.T) {
113+ t.Parallel()
109114 assert.Equal(t, tc.want, d.HasLine(tc.path, tc.side, tc.line))
110115 })
111116 }
@@ -116,6 +121,7 @@ func sig(name, email string, sec int64) domain.Signature {
116121 }
117122
118123 func TestNewSquashCommit(t *testing.T) {
124+ t.Parallel()
119125 title, err := domain.ParseMRTitle("Add search")
120126 require.NoError(t, err)
121127 mr := domain.MergeRequest{Number: 7, Title: title}
@@ -151,6 +157,7 @@ func TestNewSquashCommit(t *testing.T) {
151157 }
152158
153159 func TestSquashCommitSigned(t *testing.T) {
160+ t.Parallel()
154161 title, err := domain.ParseMRTitle("T")
155162 require.NoError(t, err)
156163 c := domain.NewSquashCommit(domain.MergeRequest{Number: 1, Title: title}, oid(t, 'a'), oid(t, 'b'),
@@ -169,6 +176,7 @@ func TestSquashCommitSigned(t *testing.T) {
169176 }
170177
171178 func TestSquashCommitCleansIdentitiesAndSummaries(t *testing.T) {
179+ t.Parallel()
172180 title, err := domain.ParseMRTitle("T")
173181 require.NoError(t, err)
174182 commits := []domain.Commit{
@@ -183,6 +191,7 @@ func TestSquashCommitCleansIdentitiesAndSummaries(t *testing.T) {
183191 }
184192
185193 func TestSigningKeyInfoAllowedSigners(t *testing.T) {
194+ t.Parallel()
186195 i := domain.SigningKeyInfo{
187196 Key: domain.SigningPublicKey{AuthorizedKey: "ssh-ed25519 AAAA"},
188197 Committer: domain.Identity{Name: "ermite", Email: "ermite@git.example.org"},
@@ -191,6 +200,7 @@ func TestSigningKeyInfoAllowedSigners(t *testing.T) {
191200 }
192201
193202 func TestParseBranchName(t *testing.T) {
203+ t.Parallel()
194204 for _, ok := range []string{"main", "feat/x", "v1.2", "a-b_c"} {
195205 got, err := domain.ParseBranchName(ok)
196206 require.NoError(t, err, ok)
@@ -21,6 +21,7 @@ func oid(t *testing.T, c byte) domain.ObjectID {
21 }21 }
22 22
23 func TestDefaultRepoSettings(t *testing.T) {23 func TestDefaultRepoSettings(t *testing.T) {
24+ t.Parallel()
24 s := domain.DefaultRepoSettings()25 s := domain.DefaultRepoSettings()
25 assert.Equal(t, []domain.MergeMode{domain.MergeModeFastForward, domain.MergeModeSquash}, s.AllowedMergeModes)26 assert.Equal(t, []domain.MergeMode{domain.MergeModeFastForward, domain.MergeModeSquash}, s.AllowedMergeModes)
26 assert.True(t, s.FailingChecksBlock)27 assert.True(t, s.FailingChecksBlock)
@@ -35,6 +36,7 @@ func TestDefaultRepoSettings(t *testing.T) {
35 }36 }
36 37
37 func TestNormalizeMergeModes(t *testing.T) {38 func TestNormalizeMergeModes(t *testing.T) {
39+ t.Parallel()
38 got, err := domain.NormalizeMergeModes([]domain.MergeMode{domain.MergeModeSquash, domain.MergeModeFastForward, domain.MergeModeSquash})40 got, err := domain.NormalizeMergeModes([]domain.MergeMode{domain.MergeModeSquash, domain.MergeModeFastForward, domain.MergeModeSquash})
39 require.NoError(t, err)41 require.NoError(t, err)
40 assert.Equal(t, domain.MergeModes(), got)42 assert.Equal(t, domain.MergeModes(), got)
@@ -50,6 +52,7 @@ func TestNormalizeMergeModes(t *testing.T) {
50 }52 }
51 53
52 func TestParseMergeMode(t *testing.T) {54 func TestParseMergeMode(t *testing.T) {
55+ t.Parallel()
53 m, err := domain.ParseMergeMode("fast-forward")56 m, err := domain.ParseMergeMode("fast-forward")
54 require.NoError(t, err)57 require.NoError(t, err)
55 assert.Equal(t, domain.MergeModeFastForward, m)58 assert.Equal(t, domain.MergeModeFastForward, m)
@@ -78,6 +81,7 @@ func hunkDiff() domain.Diff {
78 }81 }
79 82
80 func TestDiffHasLine(t *testing.T) {83 func TestDiffHasLine(t *testing.T) {
84+ t.Parallel()
81 d := hunkDiff()85 d := hunkDiff()
82 tests := []struct {86 tests := []struct {
83 name string87 name string
@@ -106,6 +110,7 @@ func TestDiffHasLine(t *testing.T) {
106 }110 }
107 for _, tc := range tests {111 for _, tc := range tests {
108 t.Run(tc.name, func(t *testing.T) {112 t.Run(tc.name, func(t *testing.T) {
113+ t.Parallel()
109 assert.Equal(t, tc.want, d.HasLine(tc.path, tc.side, tc.line))114 assert.Equal(t, tc.want, d.HasLine(tc.path, tc.side, tc.line))
110 })115 })
111 }116 }
@@ -116,6 +121,7 @@ func sig(name, email string, sec int64) domain.Signature {
116 }121 }
117 122
118 func TestNewSquashCommit(t *testing.T) {123 func TestNewSquashCommit(t *testing.T) {
124+ t.Parallel()
119 title, err := domain.ParseMRTitle("Add search")125 title, err := domain.ParseMRTitle("Add search")
120 require.NoError(t, err)126 require.NoError(t, err)
121 mr := domain.MergeRequest{Number: 7, Title: title}127 mr := domain.MergeRequest{Number: 7, Title: title}
@@ -151,6 +157,7 @@ func TestNewSquashCommit(t *testing.T) {
151 }157 }
152 158
153 func TestSquashCommitSigned(t *testing.T) {159 func TestSquashCommitSigned(t *testing.T) {
160+ t.Parallel()
154 title, err := domain.ParseMRTitle("T")161 title, err := domain.ParseMRTitle("T")
155 require.NoError(t, err)162 require.NoError(t, err)
156 c := domain.NewSquashCommit(domain.MergeRequest{Number: 1, Title: title}, oid(t, 'a'), oid(t, 'b'),163 c := domain.NewSquashCommit(domain.MergeRequest{Number: 1, Title: title}, oid(t, 'a'), oid(t, 'b'),
@@ -169,6 +176,7 @@ func TestSquashCommitSigned(t *testing.T) {
169 }176 }
170 177
171 func TestSquashCommitCleansIdentitiesAndSummaries(t *testing.T) {178 func TestSquashCommitCleansIdentitiesAndSummaries(t *testing.T) {
179+ t.Parallel()
172 title, err := domain.ParseMRTitle("T")180 title, err := domain.ParseMRTitle("T")
173 require.NoError(t, err)181 require.NoError(t, err)
174 commits := []domain.Commit{182 commits := []domain.Commit{
@@ -183,6 +191,7 @@ func TestSquashCommitCleansIdentitiesAndSummaries(t *testing.T) {
183 }191 }
184 192
185 func TestSigningKeyInfoAllowedSigners(t *testing.T) {193 func TestSigningKeyInfoAllowedSigners(t *testing.T) {
194+ t.Parallel()
186 i := domain.SigningKeyInfo{195 i := domain.SigningKeyInfo{
187 Key: domain.SigningPublicKey{AuthorizedKey: "ssh-ed25519 AAAA"},196 Key: domain.SigningPublicKey{AuthorizedKey: "ssh-ed25519 AAAA"},
188 Committer: domain.Identity{Name: "ermite", Email: "ermite@git.example.org"},197 Committer: domain.Identity{Name: "ermite", Email: "ermite@git.example.org"},
@@ -191,6 +200,7 @@ func TestSigningKeyInfoAllowedSigners(t *testing.T) {
191 }200 }
192 201
193 func TestParseBranchName(t *testing.T) {202 func TestParseBranchName(t *testing.T) {
203+ t.Parallel()
194 for _, ok := range []string{"main", "feat/x", "v1.2", "a-b_c"} {204 for _, ok := range []string{"main", "feat/x", "v1.2", "a-b_c"} {
195 got, err := domain.ParseBranchName(ok)205 got, err := domain.ParseBranchName(ok)
196 require.NoError(t, err, ok)206 require.NoError(t, err, ok)
M internal/domain/name_test.go +6 -0
@@ -14,6 +14,7 @@ import (
1414 )
1515
1616 func TestParseRepoName(t *testing.T) {
17+ t.Parallel()
1718 tests := []struct {
1819 in string
1920 valid bool
@@ -45,6 +46,7 @@ func TestParseRepoName(t *testing.T) {
4546 }
4647 for _, tt := range tests {
4748 t.Run(tt.in, func(t *testing.T) {
49+ t.Parallel()
4850 n, err := domain.ParseRepoName(tt.in)
4951 if tt.valid {
5052 require.NoError(t, err)
@@ -66,6 +68,7 @@ func TestParseRepoName(t *testing.T) {
6668 }
6769
6870 func TestParseRepoPath(t *testing.T) {
71+ t.Parallel()
6972 tests := []struct {
7073 in string
7174 want string
@@ -80,6 +83,7 @@ func TestParseRepoPath(t *testing.T) {
8083 }
8184 for _, tt := range tests {
8285 t.Run(tt.in, func(t *testing.T) {
86+ t.Parallel()
8387 n, err := domain.ParseRepoPath(tt.in)
8488 if !tt.valid {
8589 assert.ErrorIs(t, err, domain.ErrInvalidName)
@@ -92,6 +96,7 @@ func TestParseRepoPath(t *testing.T) {
9296 }
9397
9498 func TestParseVisibility(t *testing.T) {
99+ t.Parallel()
95100 for in, want := range map[string]domain.Visibility{"public": domain.VisibilityPublic, "private": domain.VisibilityPrivate} {
96101 got, err := domain.ParseVisibility(in)
97102 require.NoError(t, err)
@@ -104,6 +109,7 @@ func TestParseVisibility(t *testing.T) {
104109 }
105110
106111 func TestPrincipal(t *testing.T) {
112+ t.Parallel()
107113 var zero domain.Principal
108114 assert.True(t, zero.IsAnonymous())
109115 assert.False(t, zero.IsRoot())
@@ -14,6 +14,7 @@ import (
14 )14 )
15 15
16 func TestParseRepoName(t *testing.T) {16 func TestParseRepoName(t *testing.T) {
17+ t.Parallel()
17 tests := []struct {18 tests := []struct {
18 in string19 in string
19 valid bool20 valid bool
@@ -45,6 +46,7 @@ func TestParseRepoName(t *testing.T) {
45 }46 }
46 for _, tt := range tests {47 for _, tt := range tests {
47 t.Run(tt.in, func(t *testing.T) {48 t.Run(tt.in, func(t *testing.T) {
49+ t.Parallel()
48 n, err := domain.ParseRepoName(tt.in)50 n, err := domain.ParseRepoName(tt.in)
49 if tt.valid {51 if tt.valid {
50 require.NoError(t, err)52 require.NoError(t, err)
@@ -66,6 +68,7 @@ func TestParseRepoName(t *testing.T) {
66 }68 }
67 69
68 func TestParseRepoPath(t *testing.T) {70 func TestParseRepoPath(t *testing.T) {
71+ t.Parallel()
69 tests := []struct {72 tests := []struct {
70 in string73 in string
71 want string74 want string
@@ -80,6 +83,7 @@ func TestParseRepoPath(t *testing.T) {
80 }83 }
81 for _, tt := range tests {84 for _, tt := range tests {
82 t.Run(tt.in, func(t *testing.T) {85 t.Run(tt.in, func(t *testing.T) {
86+ t.Parallel()
83 n, err := domain.ParseRepoPath(tt.in)87 n, err := domain.ParseRepoPath(tt.in)
84 if !tt.valid {88 if !tt.valid {
85 assert.ErrorIs(t, err, domain.ErrInvalidName)89 assert.ErrorIs(t, err, domain.ErrInvalidName)
@@ -92,6 +96,7 @@ func TestParseRepoPath(t *testing.T) {
92 }96 }
93 97
94 func TestParseVisibility(t *testing.T) {98 func TestParseVisibility(t *testing.T) {
99+ t.Parallel()
95 for in, want := range map[string]domain.Visibility{"public": domain.VisibilityPublic, "private": domain.VisibilityPrivate} {100 for in, want := range map[string]domain.Visibility{"public": domain.VisibilityPublic, "private": domain.VisibilityPrivate} {
96 got, err := domain.ParseVisibility(in)101 got, err := domain.ParseVisibility(in)
97 require.NoError(t, err)102 require.NoError(t, err)
@@ -104,6 +109,7 @@ func TestParseVisibility(t *testing.T) {
104 }109 }
105 110
106 func TestPrincipal(t *testing.T) {111 func TestPrincipal(t *testing.T) {
112+ t.Parallel()
107 var zero domain.Principal113 var zero domain.Principal
108 assert.True(t, zero.IsAnonymous())114 assert.True(t, zero.IsAnonymous())
109 assert.False(t, zero.IsRoot())115 assert.False(t, zero.IsRoot())
M internal/domain/text_test.go +6 -0
@@ -16,6 +16,7 @@ import (
1616 // textCases are the rules shared by every text; multi says whether line breaks
1717 // are allowed.
1818 func TestParseMRTitle(t *testing.T) {
19+ t.Parallel()
1920 tests := []struct {
2021 name string
2122 in string
@@ -48,6 +49,7 @@ func TestParseMRTitle(t *testing.T) {
4849 }
4950 for _, tc := range tests {
5051 t.Run(tc.name, func(t *testing.T) {
52+ t.Parallel()
5153 got, err := domain.ParseMRTitle(tc.in)
5254 if tc.reason == "" {
5355 require.NoError(t, err)
@@ -64,6 +66,7 @@ func TestParseMRTitle(t *testing.T) {
6466 }
6567
6668 func TestParsePlainText(t *testing.T) {
69+ t.Parallel()
6770 tests := []struct {
6871 name string
6972 in string
@@ -92,6 +95,7 @@ func TestParsePlainText(t *testing.T) {
9295 }
9396 for _, tc := range tests {
9497 t.Run(tc.name, func(t *testing.T) {
98+ t.Parallel()
9599 got, err := domain.ParsePlainText(tc.in)
96100 if tc.reason == "" {
97101 require.NoError(t, err)
@@ -107,6 +111,7 @@ func TestParsePlainText(t *testing.T) {
107111 }
108112
109113 func TestPlainTextIsBlank(t *testing.T) {
114+ t.Parallel()
110115 for in, blank := range map[string]bool{"": true, " \n\n ": true, "x": false, " x ": false} {
111116 got, err := domain.ParsePlainText(in)
112117 require.NoError(t, err)
@@ -115,6 +120,7 @@ func TestPlainTextIsBlank(t *testing.T) {
115120 }
116121
117122 func TestViolations(t *testing.T) {
123+ t.Parallel()
118124 var v domain.Violations
119125 require.NoError(t, v.Err())
120126
@@ -16,6 +16,7 @@ import (
16 // textCases are the rules shared by every text; multi says whether line breaks16 // textCases are the rules shared by every text; multi says whether line breaks
17 // are allowed.17 // are allowed.
18 func TestParseMRTitle(t *testing.T) {18 func TestParseMRTitle(t *testing.T) {
19+ t.Parallel()
19 tests := []struct {20 tests := []struct {
20 name string21 name string
21 in string22 in string
@@ -48,6 +49,7 @@ func TestParseMRTitle(t *testing.T) {
48 }49 }
49 for _, tc := range tests {50 for _, tc := range tests {
50 t.Run(tc.name, func(t *testing.T) {51 t.Run(tc.name, func(t *testing.T) {
52+ t.Parallel()
51 got, err := domain.ParseMRTitle(tc.in)53 got, err := domain.ParseMRTitle(tc.in)
52 if tc.reason == "" {54 if tc.reason == "" {
53 require.NoError(t, err)55 require.NoError(t, err)
@@ -64,6 +66,7 @@ func TestParseMRTitle(t *testing.T) {
64 }66 }
65 67
66 func TestParsePlainText(t *testing.T) {68 func TestParsePlainText(t *testing.T) {
69+ t.Parallel()
67 tests := []struct {70 tests := []struct {
68 name string71 name string
69 in string72 in string
@@ -92,6 +95,7 @@ func TestParsePlainText(t *testing.T) {
92 }95 }
93 for _, tc := range tests {96 for _, tc := range tests {
94 t.Run(tc.name, func(t *testing.T) {97 t.Run(tc.name, func(t *testing.T) {
98+ t.Parallel()
95 got, err := domain.ParsePlainText(tc.in)99 got, err := domain.ParsePlainText(tc.in)
96 if tc.reason == "" {100 if tc.reason == "" {
97 require.NoError(t, err)101 require.NoError(t, err)
@@ -107,6 +111,7 @@ func TestParsePlainText(t *testing.T) {
107 }111 }
108 112
109 func TestPlainTextIsBlank(t *testing.T) {113 func TestPlainTextIsBlank(t *testing.T) {
114+ t.Parallel()
110 for in, blank := range map[string]bool{"": true, " \n\n ": true, "x": false, " x ": false} {115 for in, blank := range map[string]bool{"": true, " \n\n ": true, "x": false, " x ": false} {
111 got, err := domain.ParsePlainText(in)116 got, err := domain.ParsePlainText(in)
112 require.NoError(t, err)117 require.NoError(t, err)
@@ -115,6 +120,7 @@ func TestPlainTextIsBlank(t *testing.T) {
115 }120 }
116 121
117 func TestViolations(t *testing.T) {122 func TestViolations(t *testing.T) {
123+ t.Parallel()
118 var v domain.Violations124 var v domain.Violations
119 require.NoError(t, v.Err())125 require.NoError(t, v.Err())
120 126
M internal/domain/token_test.go +6 -0
@@ -13,6 +13,7 @@ import (
1313 )
1414
1515 func TestScopes(t *testing.T) {
16+ t.Parallel()
1617 all := domain.AllScopes()
1718 assert.Len(t, all, 9)
1819 for _, s := range all {
@@ -27,6 +28,7 @@ func TestScopes(t *testing.T) {
2728 }
2829
2930 func TestHashTokenSecret(t *testing.T) {
31+ t.Parallel()
3032 h := domain.HashTokenSecret("ermite_abc")
3133 assert.Equal(t, h, domain.HashTokenSecret("ermite_abc"))
3234 assert.NotEqual(t, h, domain.HashTokenSecret("ermite_abd"))
@@ -36,6 +38,7 @@ func TestHashTokenSecret(t *testing.T) {
3638 }
3739
3840 func TestTokenExpired(t *testing.T) {
41+ t.Parallel()
3942 now := time.Date(2026, 10, 10, 8, 0, 0, 0, time.UTC)
4043 assert.False(t, domain.Token{}.Expired(now), "no expiry never expires")
4144 assert.False(t, domain.Token{ExpiresAt: now.Add(time.Second)}.Expired(now))
@@ -44,6 +47,7 @@ func TestTokenExpired(t *testing.T) {
4447 }
4548
4649 func TestPrincipalScopes(t *testing.T) {
50+ t.Parallel()
4751 a, b := mustName(t, "a"), mustName(t, "b")
4852 tok := domain.Token{ID: "t1", Scopes: []domain.Scope{domain.ScopeRepoRead, domain.ScopeRepoWrite}, Repos: []domain.RepoName{a}}
4953 p := domain.TokenPrincipal(tok)
@@ -84,6 +88,7 @@ func mustName(t *testing.T, s string) domain.RepoName {
8488 }
8589
8690 func TestOwnerPrincipal(t *testing.T) {
91+ t.Parallel()
8792 o := domain.Owner()
8893 assert.Equal(t, "owner", o.Label())
8994 assert.True(t, o.IsOwner())
@@ -96,6 +101,7 @@ func TestOwnerPrincipal(t *testing.T) {
96101 }
97102
98103 func TestHasOwnerRights(t *testing.T) {
104+ t.Parallel()
99105 a := mustName(t, "a")
100106 all := domain.AllScopes()
101107 assert.True(t, domain.Root().HasOwnerRights())
@@ -13,6 +13,7 @@ import (
13 )13 )
14 14
15 func TestScopes(t *testing.T) {15 func TestScopes(t *testing.T) {
16+ t.Parallel()
16 all := domain.AllScopes()17 all := domain.AllScopes()
17 assert.Len(t, all, 9)18 assert.Len(t, all, 9)
18 for _, s := range all {19 for _, s := range all {
@@ -27,6 +28,7 @@ func TestScopes(t *testing.T) {
27 }28 }
28 29
29 func TestHashTokenSecret(t *testing.T) {30 func TestHashTokenSecret(t *testing.T) {
31+ t.Parallel()
30 h := domain.HashTokenSecret("ermite_abc")32 h := domain.HashTokenSecret("ermite_abc")
31 assert.Equal(t, h, domain.HashTokenSecret("ermite_abc"))33 assert.Equal(t, h, domain.HashTokenSecret("ermite_abc"))
32 assert.NotEqual(t, h, domain.HashTokenSecret("ermite_abd"))34 assert.NotEqual(t, h, domain.HashTokenSecret("ermite_abd"))
@@ -36,6 +38,7 @@ func TestHashTokenSecret(t *testing.T) {
36 }38 }
37 39
38 func TestTokenExpired(t *testing.T) {40 func TestTokenExpired(t *testing.T) {
41+ t.Parallel()
39 now := time.Date(2026, 10, 10, 8, 0, 0, 0, time.UTC)42 now := time.Date(2026, 10, 10, 8, 0, 0, 0, time.UTC)
40 assert.False(t, domain.Token{}.Expired(now), "no expiry never expires")43 assert.False(t, domain.Token{}.Expired(now), "no expiry never expires")
41 assert.False(t, domain.Token{ExpiresAt: now.Add(time.Second)}.Expired(now))44 assert.False(t, domain.Token{ExpiresAt: now.Add(time.Second)}.Expired(now))
@@ -44,6 +47,7 @@ func TestTokenExpired(t *testing.T) {
44 }47 }
45 48
46 func TestPrincipalScopes(t *testing.T) {49 func TestPrincipalScopes(t *testing.T) {
50+ t.Parallel()
47 a, b := mustName(t, "a"), mustName(t, "b")51 a, b := mustName(t, "a"), mustName(t, "b")
48 tok := domain.Token{ID: "t1", Scopes: []domain.Scope{domain.ScopeRepoRead, domain.ScopeRepoWrite}, Repos: []domain.RepoName{a}}52 tok := domain.Token{ID: "t1", Scopes: []domain.Scope{domain.ScopeRepoRead, domain.ScopeRepoWrite}, Repos: []domain.RepoName{a}}
49 p := domain.TokenPrincipal(tok)53 p := domain.TokenPrincipal(tok)
@@ -84,6 +88,7 @@ func mustName(t *testing.T, s string) domain.RepoName {
84 }88 }
85 89
86 func TestOwnerPrincipal(t *testing.T) {90 func TestOwnerPrincipal(t *testing.T) {
91+ t.Parallel()
87 o := domain.Owner()92 o := domain.Owner()
88 assert.Equal(t, "owner", o.Label())93 assert.Equal(t, "owner", o.Label())
89 assert.True(t, o.IsOwner())94 assert.True(t, o.IsOwner())
@@ -96,6 +101,7 @@ func TestOwnerPrincipal(t *testing.T) {
96 }101 }
97 102
98 func TestHasOwnerRights(t *testing.T) {103 func TestHasOwnerRights(t *testing.T) {
104+ t.Parallel()
99 a := mustName(t, "a")105 a := mustName(t, "a")
100 all := domain.AllScopes()106 all := domain.AllScopes()
101 assert.True(t, domain.Root().HasOwnerRights())107 assert.True(t, domain.Root().HasOwnerRights())

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 2

Tags 42

↑↓ moveenter openesc close