v0.9.0 e5e4ded

pool_idle_test.go RawHistoryFolder

1package broker
2
3import (
4	"context"
5	"sync/atomic"
6	"testing"
7	"time"
8
9	"github.com/bnema/vev/internal/ports"
10	"github.com/stretchr/testify/require"
11)
12
13func waitPoolEmpty(t *testing.T, p *Pool) {
14	t.Helper()
15	require.Eventually(t, func() bool { p.mu.Lock(); defer p.mu.Unlock(); return len(p.entries) == 0 }, time.Second, time.Millisecond)
16}
17
18func TestPoolIdleEvictionAndReconnect(t *testing.T) {
19	var calls atomic.Int32
20	p, clock := setupPool(t, func(_ context.Context, e ports.BrokerDialTarget) (ports.BrokerPhysicalConnection, error) {
21		calls.Add(1)
22		return &fakePhysical{endpoint: e, done: make(chan struct{})}, nil
23	})
24	id, _ := p.RegisterClient()
25	s, err := p.OpenStream(context.Background(), poolRequest(id, 1))
26	require.NoError(t, err)
27	require.NoError(t, s.Close())
28	require.Eventually(t, func() bool { clock.mu.Lock(); defer clock.mu.Unlock(); return len(clock.timers) == 1 }, time.Second, time.Millisecond)
29	clock.Advance(time.Minute)
30	waitPoolEmpty(t, p)
31	s, err = p.OpenStream(context.Background(), poolRequest(id, 2))
32	require.NoError(t, err)
33	require.EqualValues(t, 2, calls.Load())
34	require.NoError(t, s.Close())
35}
36
37type retiringPhysical struct {
38	*fakePhysical
39	closing, allowClose chan struct{}
40}
41
42func (f *retiringPhysical) Close() error {
43	close(f.closing)
44	<-f.allowClose
45	return f.fakePhysical.Close()
46}
47
48func TestPoolRetiringKeyUntilCloseCompletes(t *testing.T) {
49	var calls atomic.Int32
50	var first *retiringPhysical
51	allow := make(chan struct{})
52	p, _ := setupPool(t, func(_ context.Context, e ports.BrokerDialTarget) (ports.BrokerPhysicalConnection, error) {
53		f := &fakePhysical{endpoint: e, done: make(chan struct{})}
54		if calls.Add(1) == 1 {
55			first = &retiringPhysical{fakePhysical: f, closing: make(chan struct{}), allowClose: allow}
56			return first, nil
57		}
58		return f, nil
59	})
60	defer func() {
61		select {
62		case <-allow:
63		default:
64			close(allow)
65		}
66	}()
67	id, _ := p.RegisterClient()
68	s, err := p.OpenStream(context.Background(), poolRequest(id, 1))
69	require.NoError(t, err)
70	require.NoError(t, first.fakePhysical.Close())
71	await(t, first.closing)
72	await(t, s.Done())
73	_, err = p.OpenStream(context.Background(), poolRequest(id, 2))
74	var typed ports.BrokerError
75	require.ErrorAs(t, err, &typed)
76	require.Equal(t, ports.BrokerErrorUnavailable, typed.Code)
77	require.EqualValues(t, 1, calls.Load())
78	close(allow)
79	waitPoolEmpty(t, p)
80	s, err = p.OpenStream(context.Background(), poolRequest(id, 3))
81	require.NoError(t, err)
82	require.EqualValues(t, 2, calls.Load())
83	require.NoError(t, s.Close())
84}
85
86func TestPoolFailedConnectRetry(t *testing.T) {
87	var calls atomic.Int32
88	p, _ := setupPool(t, func(_ context.Context, e ports.BrokerDialTarget) (ports.BrokerPhysicalConnection, error) {
89		if calls.Add(1) == 1 {
90			return nil, context.DeadlineExceeded
91		}
92		return &fakePhysical{endpoint: e, done: make(chan struct{})}, nil
93	})
94	id, _ := p.RegisterClient()
95	_, err := p.OpenStream(context.Background(), poolRequest(id, 1))
96	require.ErrorIs(t, err, context.DeadlineExceeded)
97	waitPoolEmpty(t, p)
98	_, err = p.OpenStream(context.Background(), poolRequest(id, 1))
99	require.ErrorIs(t, err, ports.BrokerAdmissionStale)
100	s, err := p.OpenStream(context.Background(), poolRequest(id, 2))
101	require.NoError(t, err)
102	require.NoError(t, s.Close())
103	require.EqualValues(t, 2, calls.Load())
104}

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 12

Tags 11

↑↓ moveenter openesc close