detached 8d55edc

fix(remote): address PR review findings

commit
8d55edc4f465eea315dd7befdda45d4bbf2e8438
author
brice · · 2026-07-07 08:54 UTC
parent
2e8f4cf
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
4 files · +141 -10
M internal/adapters/dgram/transport.go +1 -1
@@ -501,7 +501,7 @@ func (t *Transport) resendPending() {
501501 }
502502 t.mu.Lock()
503503 for seq, p := range t.pending {
504- if p.last.IsZero() || now.Sub(p.last) >= defaultResend {
504+ if p.last.IsZero() || now.Sub(p.last) >= t.resendAfter {
505505 p.last = now
506506 resend = append(resend, struct {
507507 seq uint64
@@ -501,7 +501,7 @@ func (t *Transport) resendPending() {
501 }501 }
502 t.mu.Lock()502 t.mu.Lock()
503 for seq, p := range t.pending {503 for seq, p := range t.pending {
504- if p.last.IsZero() || now.Sub(p.last) >= defaultResend {504+ if p.last.IsZero() || now.Sub(p.last) >= t.resendAfter {
505 p.last = now505 p.last = now
506 resend = append(resend, struct {506 resend = append(resend, struct {
507 seq uint64507 seq uint64
M internal/adapters/dgram/transport_test.go +13 -5
@@ -169,7 +169,6 @@ func TestAuthenticatedPeerChangeUpdatesPeerAndEmitsConnected(t *testing.T) {
169169 a.setLinkState(ports.LinkStateOffline, nil)
170170 <-a.LinkEvents()
171171 aPC.in <- packet{[]byte("not authenticated"), testAddr("evil")}
172- time.Sleep(30 * time.Millisecond)
173172 if a.Peer().String() != old {
174173 t.Fatalf("unauthenticated packet changed peer to %v", a.Peer())
175174 }
@@ -178,10 +177,7 @@ func TestAuthenticatedPeerChangeUpdatesPeerAndEmitsConnected(t *testing.T) {
178177 frags, _ := pdgram.FragmentPayload(9, rec, pdgram.DefaultMTU)
179178 raw, _ := pdgram.MarshalFragment(frags[0])
180179 aPC.in <- packet{c.Seal(2, 99, raw, nil), testAddr("evil")}
181- time.Sleep(30 * time.Millisecond)
182- if a.Peer().String() != "evil" {
183- t.Fatalf("authenticated packet did not rehome: %v", a.Peer())
184- }
180+ waitPeer(t, a, "evil")
185181 select {
186182 case ev := <-a.LinkEvents():
187183 if ev.State != ports.LinkStateConnected {
@@ -520,6 +516,18 @@ func recvWithin(t *testing.T, tr *Transport, d time.Duration) ports.Frame {
520516 }
521517 }
522518
519+func waitPeer(t *testing.T, tr *Transport, want string) {
520+ t.Helper()
521+ deadline := time.Now().Add(time.Second)
522+ for time.Now().Before(deadline) {
523+ if tr.Peer().String() == want {
524+ return
525+ }
526+ time.Sleep(time.Millisecond)
527+ }
528+ t.Fatalf("peer=%v, want %s", tr.Peer(), want)
529+}
530+
523531 func recvErrWithin(t *testing.T, tr *Transport, d time.Duration) (ports.Frame, error) {
524532 t.Helper()
525533 type result struct {
@@ -169,7 +169,6 @@ func TestAuthenticatedPeerChangeUpdatesPeerAndEmitsConnected(t *testing.T) {
169 a.setLinkState(ports.LinkStateOffline, nil)169 a.setLinkState(ports.LinkStateOffline, nil)
170 <-a.LinkEvents()170 <-a.LinkEvents()
171 aPC.in <- packet{[]byte("not authenticated"), testAddr("evil")}171 aPC.in <- packet{[]byte("not authenticated"), testAddr("evil")}
172- time.Sleep(30 * time.Millisecond)
173 if a.Peer().String() != old {172 if a.Peer().String() != old {
174 t.Fatalf("unauthenticated packet changed peer to %v", a.Peer())173 t.Fatalf("unauthenticated packet changed peer to %v", a.Peer())
175 }174 }
@@ -178,10 +177,7 @@ func TestAuthenticatedPeerChangeUpdatesPeerAndEmitsConnected(t *testing.T) {
178 frags, _ := pdgram.FragmentPayload(9, rec, pdgram.DefaultMTU)177 frags, _ := pdgram.FragmentPayload(9, rec, pdgram.DefaultMTU)
179 raw, _ := pdgram.MarshalFragment(frags[0])178 raw, _ := pdgram.MarshalFragment(frags[0])
180 aPC.in <- packet{c.Seal(2, 99, raw, nil), testAddr("evil")}179 aPC.in <- packet{c.Seal(2, 99, raw, nil), testAddr("evil")}
181- time.Sleep(30 * time.Millisecond)180+ waitPeer(t, a, "evil")
182- if a.Peer().String() != "evil" {
183- t.Fatalf("authenticated packet did not rehome: %v", a.Peer())
184- }
185 select {181 select {
186 case ev := <-a.LinkEvents():182 case ev := <-a.LinkEvents():
187 if ev.State != ports.LinkStateConnected {183 if ev.State != ports.LinkStateConnected {
@@ -520,6 +516,18 @@ func recvWithin(t *testing.T, tr *Transport, d time.Duration) ports.Frame {
520 }516 }
521 }517 }
522 518
519+func waitPeer(t *testing.T, tr *Transport, want string) {
520+ t.Helper()
521+ deadline := time.Now().Add(time.Second)
522+ for time.Now().Before(deadline) {
523+ if tr.Peer().String() == want {
524+ return
525+ }
526+ time.Sleep(time.Millisecond)
527+ }
528+ t.Fatalf("peer=%v, want %s", tr.Peer(), want)
529+}
530+
523 func recvErrWithin(t *testing.T, tr *Transport, d time.Duration) (ports.Frame, error) {531 func recvErrWithin(t *testing.T, tr *Transport, d time.Duration) (ports.Frame, error) {
524 t.Helper()532 t.Helper()
525 type result struct {533 type result struct {
M internal/ports/mocks/mocks.go +117 -0
@@ -15,6 +15,123 @@ import (
1515 mock "github.com/stretchr/testify/mock"
1616 )
1717
18+// NewMockLinkStateReporter creates a new instance of MockLinkStateReporter. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
19+// The first argument is typically a *testing.T value.
20+func NewMockLinkStateReporter(t interface {
21+ mock.TestingT
22+ Cleanup(func())
23+}) *MockLinkStateReporter {
24+ mock := &MockLinkStateReporter{}
25+ mock.Mock.Test(t)
26+
27+ t.Cleanup(func() { mock.AssertExpectations(t) })
28+
29+ return mock
30+}
31+
32+// MockLinkStateReporter is an autogenerated mock type for the LinkStateReporter type
33+type MockLinkStateReporter struct {
34+ mock.Mock
35+}
36+
37+type MockLinkStateReporter_Expecter struct {
38+ mock *mock.Mock
39+}
40+
41+func (_m *MockLinkStateReporter) EXPECT() *MockLinkStateReporter_Expecter {
42+ return &MockLinkStateReporter_Expecter{mock: &_m.Mock}
43+}
44+
45+// LinkEvents provides a mock function for the type MockLinkStateReporter
46+func (_mock *MockLinkStateReporter) LinkEvents() <-chan ports.LinkEvent {
47+ ret := _mock.Called()
48+
49+ if len(ret) == 0 {
50+ panic("no return value specified for LinkEvents")
51+ }
52+
53+ var r0 <-chan ports.LinkEvent
54+ if returnFunc, ok := ret.Get(0).(func() <-chan ports.LinkEvent); ok {
55+ r0 = returnFunc()
56+ } else {
57+ if ret.Get(0) != nil {
58+ r0 = ret.Get(0).(<-chan ports.LinkEvent)
59+ }
60+ }
61+ return r0
62+}
63+
64+// MockLinkStateReporter_LinkEvents_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'LinkEvents'
65+type MockLinkStateReporter_LinkEvents_Call struct {
66+ *mock.Call
67+}
68+
69+// LinkEvents is a helper method to define mock.On call
70+func (_e *MockLinkStateReporter_Expecter) LinkEvents() *MockLinkStateReporter_LinkEvents_Call {
71+ return &MockLinkStateReporter_LinkEvents_Call{Call: _e.mock.On("LinkEvents")}
72+}
73+
74+func (_c *MockLinkStateReporter_LinkEvents_Call) Run(run func()) *MockLinkStateReporter_LinkEvents_Call {
75+ _c.Call.Run(func(args mock.Arguments) {
76+ run()
77+ })
78+ return _c
79+}
80+
81+func (_c *MockLinkStateReporter_LinkEvents_Call) Return(linkEventCh <-chan ports.LinkEvent) *MockLinkStateReporter_LinkEvents_Call {
82+ _c.Call.Return(linkEventCh)
83+ return _c
84+}
85+
86+func (_c *MockLinkStateReporter_LinkEvents_Call) RunAndReturn(run func() <-chan ports.LinkEvent) *MockLinkStateReporter_LinkEvents_Call {
87+ _c.Call.Return(run)
88+ return _c
89+}
90+
91+// LinkState provides a mock function for the type MockLinkStateReporter
92+func (_mock *MockLinkStateReporter) LinkState() ports.LinkState {
93+ ret := _mock.Called()
94+
95+ if len(ret) == 0 {
96+ panic("no return value specified for LinkState")
97+ }
98+
99+ var r0 ports.LinkState
100+ if returnFunc, ok := ret.Get(0).(func() ports.LinkState); ok {
101+ r0 = returnFunc()
102+ } else {
103+ r0 = ret.Get(0).(ports.LinkState)
104+ }
105+ return r0
106+}
107+
108+// MockLinkStateReporter_LinkState_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'LinkState'
109+type MockLinkStateReporter_LinkState_Call struct {
110+ *mock.Call
111+}
112+
113+// LinkState is a helper method to define mock.On call
114+func (_e *MockLinkStateReporter_Expecter) LinkState() *MockLinkStateReporter_LinkState_Call {
115+ return &MockLinkStateReporter_LinkState_Call{Call: _e.mock.On("LinkState")}
116+}
117+
118+func (_c *MockLinkStateReporter_LinkState_Call) Run(run func()) *MockLinkStateReporter_LinkState_Call {
119+ _c.Call.Run(func(args mock.Arguments) {
120+ run()
121+ })
122+ return _c
123+}
124+
125+func (_c *MockLinkStateReporter_LinkState_Call) Return(linkState ports.LinkState) *MockLinkStateReporter_LinkState_Call {
126+ _c.Call.Return(linkState)
127+ return _c
128+}
129+
130+func (_c *MockLinkStateReporter_LinkState_Call) RunAndReturn(run func() ports.LinkState) *MockLinkStateReporter_LinkState_Call {
131+ _c.Call.Return(run)
132+ return _c
133+}
134+
18135 // NewMockPTY creates a new instance of MockPTY. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
19136 // The first argument is typically a *testing.T value.
20137 func NewMockPTY(t interface {
@@ -15,6 +15,123 @@ import (
15 mock "github.com/stretchr/testify/mock"15 mock "github.com/stretchr/testify/mock"
16 )16 )
17 17
18+// NewMockLinkStateReporter creates a new instance of MockLinkStateReporter. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
19+// The first argument is typically a *testing.T value.
20+func NewMockLinkStateReporter(t interface {
21+ mock.TestingT
22+ Cleanup(func())
23+}) *MockLinkStateReporter {
24+ mock := &MockLinkStateReporter{}
25+ mock.Mock.Test(t)
26+
27+ t.Cleanup(func() { mock.AssertExpectations(t) })
28+
29+ return mock
30+}
31+
32+// MockLinkStateReporter is an autogenerated mock type for the LinkStateReporter type
33+type MockLinkStateReporter struct {
34+ mock.Mock
35+}
36+
37+type MockLinkStateReporter_Expecter struct {
38+ mock *mock.Mock
39+}
40+
41+func (_m *MockLinkStateReporter) EXPECT() *MockLinkStateReporter_Expecter {
42+ return &MockLinkStateReporter_Expecter{mock: &_m.Mock}
43+}
44+
45+// LinkEvents provides a mock function for the type MockLinkStateReporter
46+func (_mock *MockLinkStateReporter) LinkEvents() <-chan ports.LinkEvent {
47+ ret := _mock.Called()
48+
49+ if len(ret) == 0 {
50+ panic("no return value specified for LinkEvents")
51+ }
52+
53+ var r0 <-chan ports.LinkEvent
54+ if returnFunc, ok := ret.Get(0).(func() <-chan ports.LinkEvent); ok {
55+ r0 = returnFunc()
56+ } else {
57+ if ret.Get(0) != nil {
58+ r0 = ret.Get(0).(<-chan ports.LinkEvent)
59+ }
60+ }
61+ return r0
62+}
63+
64+// MockLinkStateReporter_LinkEvents_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'LinkEvents'
65+type MockLinkStateReporter_LinkEvents_Call struct {
66+ *mock.Call
67+}
68+
69+// LinkEvents is a helper method to define mock.On call
70+func (_e *MockLinkStateReporter_Expecter) LinkEvents() *MockLinkStateReporter_LinkEvents_Call {
71+ return &MockLinkStateReporter_LinkEvents_Call{Call: _e.mock.On("LinkEvents")}
72+}
73+
74+func (_c *MockLinkStateReporter_LinkEvents_Call) Run(run func()) *MockLinkStateReporter_LinkEvents_Call {
75+ _c.Call.Run(func(args mock.Arguments) {
76+ run()
77+ })
78+ return _c
79+}
80+
81+func (_c *MockLinkStateReporter_LinkEvents_Call) Return(linkEventCh <-chan ports.LinkEvent) *MockLinkStateReporter_LinkEvents_Call {
82+ _c.Call.Return(linkEventCh)
83+ return _c
84+}
85+
86+func (_c *MockLinkStateReporter_LinkEvents_Call) RunAndReturn(run func() <-chan ports.LinkEvent) *MockLinkStateReporter_LinkEvents_Call {
87+ _c.Call.Return(run)
88+ return _c
89+}
90+
91+// LinkState provides a mock function for the type MockLinkStateReporter
92+func (_mock *MockLinkStateReporter) LinkState() ports.LinkState {
93+ ret := _mock.Called()
94+
95+ if len(ret) == 0 {
96+ panic("no return value specified for LinkState")
97+ }
98+
99+ var r0 ports.LinkState
100+ if returnFunc, ok := ret.Get(0).(func() ports.LinkState); ok {
101+ r0 = returnFunc()
102+ } else {
103+ r0 = ret.Get(0).(ports.LinkState)
104+ }
105+ return r0
106+}
107+
108+// MockLinkStateReporter_LinkState_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'LinkState'
109+type MockLinkStateReporter_LinkState_Call struct {
110+ *mock.Call
111+}
112+
113+// LinkState is a helper method to define mock.On call
114+func (_e *MockLinkStateReporter_Expecter) LinkState() *MockLinkStateReporter_LinkState_Call {
115+ return &MockLinkStateReporter_LinkState_Call{Call: _e.mock.On("LinkState")}
116+}
117+
118+func (_c *MockLinkStateReporter_LinkState_Call) Run(run func()) *MockLinkStateReporter_LinkState_Call {
119+ _c.Call.Run(func(args mock.Arguments) {
120+ run()
121+ })
122+ return _c
123+}
124+
125+func (_c *MockLinkStateReporter_LinkState_Call) Return(linkState ports.LinkState) *MockLinkStateReporter_LinkState_Call {
126+ _c.Call.Return(linkState)
127+ return _c
128+}
129+
130+func (_c *MockLinkStateReporter_LinkState_Call) RunAndReturn(run func() ports.LinkState) *MockLinkStateReporter_LinkState_Call {
131+ _c.Call.Return(run)
132+ return _c
133+}
134+
18 // NewMockPTY creates a new instance of MockPTY. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.135 // NewMockPTY creates a new instance of MockPTY. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
19 // The first argument is typically a *testing.T value.136 // The first argument is typically a *testing.T value.
20 func NewMockPTY(t interface {137 func NewMockPTY(t interface {
M internal/usecase/daemon/resume_test.go +10 -4
@@ -133,18 +133,24 @@ func TestOutputAckGapForcesFullStateRepaint(t *testing.T) {
133133 ac.advanceOutputAck(2)
134134 ac.advanceOutputAck(4)
135135 require.Equal(t, uint64(3), ac.outputAck.Load(), "stale or future ACKs must not move output state incorrectly")
136+ ac.sendMu.Lock()
136137 ac.nextStateNum = 5
137- require.True(t, ac.shouldResetOutputState(false), "client ack gap should force dependency-free full repaint")
138+ reset := ac.shouldResetOutputState(false)
139+ require.True(t, reset, "client ack gap should force dependency-free full repaint")
138140
139- f := ac.nextOutputFrameLocked([]byte("full repaint"), ac.shouldResetOutputState(false))
141+ f := ac.nextOutputFrameLocked([]byte("full repaint"), reset)
142+ ac.sendMu.Unlock()
140143 out, err := ports.UnmarshalOutput(f.Payload)
141144 require.NoError(t, err)
142145 require.Equal(t, uint64(0), out.BaseStateNum, "full repaint must not depend on dropped prior output states")
143146 require.Equal(t, uint64(6), out.NewStateNum)
144147
145148 ac.outputAck.Store(out.NewStateNum)
146- require.False(t, ac.shouldResetOutputState(false), "acked current state can resume incremental output")
147- incremental := ac.nextOutputFrameLocked([]byte("incremental"), ac.shouldResetOutputState(false))
149+ ac.sendMu.Lock()
150+ reset = ac.shouldResetOutputState(false)
151+ require.False(t, reset, "acked current state can resume incremental output")
152+ incremental := ac.nextOutputFrameLocked([]byte("incremental"), reset)
153+ ac.sendMu.Unlock()
148154 incrementalOut, err := ports.UnmarshalOutput(incremental.Payload)
149155 require.NoError(t, err)
150156 require.Equal(t, uint64(6), incrementalOut.BaseStateNum)
@@ -133,18 +133,24 @@ func TestOutputAckGapForcesFullStateRepaint(t *testing.T) {
133 ac.advanceOutputAck(2)133 ac.advanceOutputAck(2)
134 ac.advanceOutputAck(4)134 ac.advanceOutputAck(4)
135 require.Equal(t, uint64(3), ac.outputAck.Load(), "stale or future ACKs must not move output state incorrectly")135 require.Equal(t, uint64(3), ac.outputAck.Load(), "stale or future ACKs must not move output state incorrectly")
136+ ac.sendMu.Lock()
136 ac.nextStateNum = 5137 ac.nextStateNum = 5
137- require.True(t, ac.shouldResetOutputState(false), "client ack gap should force dependency-free full repaint")138+ reset := ac.shouldResetOutputState(false)
139+ require.True(t, reset, "client ack gap should force dependency-free full repaint")
138 140
139- f := ac.nextOutputFrameLocked([]byte("full repaint"), ac.shouldResetOutputState(false))141+ f := ac.nextOutputFrameLocked([]byte("full repaint"), reset)
142+ ac.sendMu.Unlock()
140 out, err := ports.UnmarshalOutput(f.Payload)143 out, err := ports.UnmarshalOutput(f.Payload)
141 require.NoError(t, err)144 require.NoError(t, err)
142 require.Equal(t, uint64(0), out.BaseStateNum, "full repaint must not depend on dropped prior output states")145 require.Equal(t, uint64(0), out.BaseStateNum, "full repaint must not depend on dropped prior output states")
143 require.Equal(t, uint64(6), out.NewStateNum)146 require.Equal(t, uint64(6), out.NewStateNum)
144 147
145 ac.outputAck.Store(out.NewStateNum)148 ac.outputAck.Store(out.NewStateNum)
146- require.False(t, ac.shouldResetOutputState(false), "acked current state can resume incremental output")149+ ac.sendMu.Lock()
147- incremental := ac.nextOutputFrameLocked([]byte("incremental"), ac.shouldResetOutputState(false))150+ reset = ac.shouldResetOutputState(false)
151+ require.False(t, reset, "acked current state can resume incremental output")
152+ incremental := ac.nextOutputFrameLocked([]byte("incremental"), reset)
153+ ac.sendMu.Unlock()
148 incrementalOut, err := ports.UnmarshalOutput(incremental.Payload)154 incrementalOut, err := ports.UnmarshalOutput(incremental.Payload)
149 require.NoError(t, err)155 require.NoError(t, err)
150 require.Equal(t, uint64(6), incrementalOut.BaseStateNum)156 require.Equal(t, uint64(6), incrementalOut.BaseStateNum)

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