detached 4fabf6f

test(protocol): strengthen PR 167 review coverage

commit
4fabf6fef53c78a9d941c9f439688507208ddbe0
author
brice · · 2026-08-02 06:12 UTC
parent
e147aea
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
3 files · +37 -3
M internal/adapters/sshstdio/transport_test.go +18 -0
@@ -166,6 +166,24 @@ func TestTransportRejectsZeroLengthFrame(t *testing.T) {
166166 }
167167
168168 func TestTransportRejectsFramesOverSharedMaximum(t *testing.T) {
169+ boundaryPayload := make([]byte, ports.MaxFrameLen-1)
170+ boundaryWire := &bytes.Buffer{}
171+ boundarySend := NewTransport(nil, boundaryWire, nil)
172+ if err := boundarySend.Send(ports.Frame{Type: ports.MsgOutput, Payload: boundaryPayload}); err != nil {
173+ t.Fatalf("boundary Send error = %v", err)
174+ }
175+ if got := binary.BigEndian.Uint32(boundaryWire.Bytes()[:frameHeaderLen]); got != ports.MaxFrameLen {
176+ t.Fatalf("boundary frame length = %d, want %d", got, ports.MaxFrameLen)
177+ }
178+ boundaryRecv := NewTransport(bytes.NewReader(boundaryWire.Bytes()), io.Discard, nil)
179+ boundaryFrame, boundaryErr := boundaryRecv.Recv()
180+ if boundaryErr != nil {
181+ t.Fatalf("boundary Recv error = %v", boundaryErr)
182+ }
183+ if boundaryFrame.Type != ports.MsgOutput || len(boundaryFrame.Payload) != len(boundaryPayload) {
184+ t.Fatalf("boundary frame = type %d, payload %d bytes; want type %d, payload %d bytes", boundaryFrame.Type, len(boundaryFrame.Payload), ports.MsgOutput, len(boundaryPayload))
185+ }
186+
169187 send := NewTransport(nil, io.Discard, nil)
170188 err := send.Send(ports.Frame{Type: ports.MsgOutput, Payload: make([]byte, ports.MaxFrameLen)})
171189 if !errors.Is(err, ErrFrameTooLarge) {
@@ -166,6 +166,24 @@ func TestTransportRejectsZeroLengthFrame(t *testing.T) {
166 }166 }
167 167
168 func TestTransportRejectsFramesOverSharedMaximum(t *testing.T) {168 func TestTransportRejectsFramesOverSharedMaximum(t *testing.T) {
169+ boundaryPayload := make([]byte, ports.MaxFrameLen-1)
170+ boundaryWire := &bytes.Buffer{}
171+ boundarySend := NewTransport(nil, boundaryWire, nil)
172+ if err := boundarySend.Send(ports.Frame{Type: ports.MsgOutput, Payload: boundaryPayload}); err != nil {
173+ t.Fatalf("boundary Send error = %v", err)
174+ }
175+ if got := binary.BigEndian.Uint32(boundaryWire.Bytes()[:frameHeaderLen]); got != ports.MaxFrameLen {
176+ t.Fatalf("boundary frame length = %d, want %d", got, ports.MaxFrameLen)
177+ }
178+ boundaryRecv := NewTransport(bytes.NewReader(boundaryWire.Bytes()), io.Discard, nil)
179+ boundaryFrame, boundaryErr := boundaryRecv.Recv()
180+ if boundaryErr != nil {
181+ t.Fatalf("boundary Recv error = %v", boundaryErr)
182+ }
183+ if boundaryFrame.Type != ports.MsgOutput || len(boundaryFrame.Payload) != len(boundaryPayload) {
184+ t.Fatalf("boundary frame = type %d, payload %d bytes; want type %d, payload %d bytes", boundaryFrame.Type, len(boundaryFrame.Payload), ports.MsgOutput, len(boundaryPayload))
185+ }
186+
169 send := NewTransport(nil, io.Discard, nil)187 send := NewTransport(nil, io.Discard, nil)
170 err := send.Send(ports.Frame{Type: ports.MsgOutput, Payload: make([]byte, ports.MaxFrameLen)})188 err := send.Send(ports.Frame{Type: ports.MsgOutput, Payload: make([]byte, ports.MaxFrameLen)})
171 if !errors.Is(err, ErrFrameTooLarge) {189 if !errors.Is(err, ErrFrameTooLarge) {
M internal/usecase/daemon/proxy_screen_state.go +5 -3
@@ -183,9 +183,11 @@ func (s *proxyScreenState) AcknowledgeDamage(generation uint64) bool {
183183 return true
184184 }
185185
186-// CaptureInto updates dst from the live frame. Safe scroll/span damage is
187-// replayed incrementally; snapshots, uncertain damage, and dimensions use a
188-// complete clone.
186+// CaptureInto updates dst from the live frame. dst must already contain the
187+// previous synchronized generation before incremental damage can be replayed;
188+// callers should initialize it with a full clone or equivalent first capture.
189+// Safe scroll/span damage is replayed incrementally; snapshots, uncertain
190+// damage, and dimensions use a complete clone.
189191 func (s *proxyScreenState) CaptureInto(dst *renderer.Frame) {
190192 if s == nil || dst == nil || !validProxyFrame(*s) {
191193 return
@@ -183,9 +183,11 @@ func (s *proxyScreenState) AcknowledgeDamage(generation uint64) bool {
183 return true183 return true
184 }184 }
185 185
186-// CaptureInto updates dst from the live frame. Safe scroll/span damage is186+// CaptureInto updates dst from the live frame. dst must already contain the
187-// replayed incrementally; snapshots, uncertain damage, and dimensions use a187+// previous synchronized generation before incremental damage can be replayed;
188-// complete clone.188+// callers should initialize it with a full clone or equivalent first capture.
189+// Safe scroll/span damage is replayed incrementally; snapshots, uncertain
190+// damage, and dimensions use a complete clone.
189 func (s *proxyScreenState) CaptureInto(dst *renderer.Frame) {191 func (s *proxyScreenState) CaptureInto(dst *renderer.Frame) {
190 if s == nil || dst == nil || !validProxyFrame(*s) {192 if s == nil || dst == nil || !validProxyFrame(*s) {
191 return193 return
M internal/usecase/daemon/proxy_screen_state_test.go +14 -0
@@ -63,6 +63,20 @@ func TestProxyScreenStateInvalidUpdateIsAtomic(t *testing.T) {
6363 t.Fatal("invalid update mutated metadata")
6464 }
6565
66+ invalid = ports.ScreenUpdate{
67+ Kind: ports.ScreenUpdateDelta,
68+ BaseStateNum: 1,
69+ NewStateNum: 2,
70+ Size: domain.Size{Cols: 3, Rows: 2},
71+ Cursor: ports.ScreenCursor{Row: 1, Col: 1, Visible: true},
72+ }
73+ if err := s.Apply(invalid); err == nil {
74+ t.Fatal("delta with mismatched dimensions was accepted")
75+ }
76+ if !reflect.DeepEqual(s.frame, beforeFrame) || !reflect.DeepEqual(s.scratch, beforeScratch) || s.cursorOut != beforeCursor || s.generation != beforeGeneration || s.stateNum != beforeState {
77+ t.Fatal("mismatched-dimension update mutated state")
78+ }
79+
6680 invalid = ports.ScreenUpdate{
6781 Kind: ports.ScreenUpdateDelta,
6882 BaseStateNum: 1,
@@ -63,6 +63,20 @@ func TestProxyScreenStateInvalidUpdateIsAtomic(t *testing.T) {
63 t.Fatal("invalid update mutated metadata")63 t.Fatal("invalid update mutated metadata")
64 }64 }
65 65
66+ invalid = ports.ScreenUpdate{
67+ Kind: ports.ScreenUpdateDelta,
68+ BaseStateNum: 1,
69+ NewStateNum: 2,
70+ Size: domain.Size{Cols: 3, Rows: 2},
71+ Cursor: ports.ScreenCursor{Row: 1, Col: 1, Visible: true},
72+ }
73+ if err := s.Apply(invalid); err == nil {
74+ t.Fatal("delta with mismatched dimensions was accepted")
75+ }
76+ if !reflect.DeepEqual(s.frame, beforeFrame) || !reflect.DeepEqual(s.scratch, beforeScratch) || s.cursorOut != beforeCursor || s.generation != beforeGeneration || s.stateNum != beforeState {
77+ t.Fatal("mismatched-dimension update mutated state")
78+ }
79+
66 invalid = ports.ScreenUpdate{80 invalid = ports.ScreenUpdate{
67 Kind: ports.ScreenUpdateDelta,81 Kind: ports.ScreenUpdateDelta,
68 BaseStateNum: 1,82 BaseStateNum: 1,

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