detached 59d5a0b

mergeability_reconcile_test.go RawHistoryFolder

1// SPDX-License-Identifier: AGPL-3.0-or-later
2
3package usecase
4
5import (
6	"context"
7	"sync/atomic"
8	"testing"
9	"time"
10
11	"github.com/stretchr/testify/assert"
12	"github.com/stretchr/testify/mock"
13	"github.com/stretchr/testify/require"
14
15	"bnema.dev/ermite/internal/domain"
16	"bnema.dev/ermite/internal/ports/mocks"
17)
18
19func TestMergeabilityBackoffIsBounded(t *testing.T) {
20	assert.Equal(t, time.Second, mergeBackoff(1))
21	assert.Equal(t, 2*time.Second, mergeBackoff(2))
22	assert.Equal(t, 4*time.Second, mergeBackoff(3))
23	assert.Equal(t, time.Minute, mergeBackoff(20))
24}
25
26func TestMergeabilityReadMarksTheRepoDirty(t *testing.T) {
27	repo, err := domain.ParseRepoName("proj")
28	require.NoError(t, err)
29	oid, err := domain.ParseObjectID("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
30	require.NoError(t, err)
31	store := mocks.NewMergeRequestStore(t)
32	store.EXPECT().GetMergeability(mock.Anything, repo, int64(7)).Return(domain.Mergeability{}, domain.ErrNotFound)
33	svc := NewMergeability(context.Background(), MergeabilityConfig{Store: store})
34	defer svc.Close(context.Background())
35	mr := domain.MergeRequest{Repo: repo, Number: 7, HeadID: oid}
36	assert.Equal(t, domain.MergeabilityChecking, svc.Read(context.Background(), mr, oid).State)
37	// Another MR in the same repository is the same unit of work.
38	mr.Number = 8
39	mr.HeadID = domain.ObjectID{}
40	svc.Read(context.Background(), mr, oid)
41	got, found := svc.next()
42	require.True(t, found)
43	assert.Equal(t, repo, got)
44	_, found = svc.next()
45	assert.False(t, found)
46}
47
48func TestMergeabilityOnePassPerRepoAtATimeAndAgain(t *testing.T) {
49	repo, err := domain.ParseRepoName("proj")
50	require.NoError(t, err)
51	other, err := domain.ParseRepoName("other")
52	require.NoError(t, err)
53	meta, store := mocks.NewRepoMetaStore(t), mocks.NewMergeRequestStore(t)
54	meta.EXPECT().List(mock.Anything).Return(nil, nil)
55	meta.EXPECT().Get(mock.Anything, repo).Return(domain.Repo{Name: repo}, nil)
56	meta.EXPECT().Get(mock.Anything, other).Return(domain.Repo{Name: other}, nil)
57	entered, release, otherPassed := make(chan struct{}, 4), make(chan struct{}), make(chan struct{})
58	var passes, inflight, peak atomic.Int32
59	store.EXPECT().ListOpen(mock.Anything, repo).RunAndReturn(func(context.Context, domain.RepoName) ([]domain.MergeRequest, error) {
60		n := inflight.Add(1)
61		if n > peak.Load() {
62			peak.Store(n)
63		}
64		pass := passes.Add(1)
65		entered <- struct{}{}
66		if pass == 1 {
67			<-release
68		}
69		inflight.Add(-1)
70		return nil, nil
71	})
72	store.EXPECT().ListOpen(mock.Anything, other).RunAndReturn(func(context.Context, domain.RepoName) ([]domain.MergeRequest, error) {
73		close(otherPassed)
74		return nil, nil
75	})
76	svc := NewMergeability(context.Background(), MergeabilityConfig{Store: store, Meta: meta, Workers: 2})
77	svc.Start()
78	svc.OnOpened(context.Background(), domain.MergeRequest{Repo: repo})
79	<-entered
80	for range 50 {
81		svc.OnOpened(context.Background(), domain.MergeRequest{Repo: repo})
82	}
83	svc.OnOpened(context.Background(), domain.MergeRequest{Repo: other})
84	<-otherPassed // the other worker has serviced all work eligible meanwhile
85	assert.EqualValues(t, 1, passes.Load())
86	close(release)
87	<-entered
88	svc.Close(context.Background())
89	assert.EqualValues(t, 2, passes.Load(), "all triggers during a pass coalesce into exactly one more pass")
90	assert.EqualValues(t, 1, peak.Load())
91}

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 45

↑↓ moveenter openesc close