main ece7682

proto_handshake_test.go RawHistoryFolder

1package sessionwire
2
3import (
4	"context"
5	"sync"
6	"testing"
7	"time"
8
9	"google.golang.org/protobuf/proto"
10
11	"github.com/bnema/vev/internal/protocol"
12	"github.com/bnema/vev/internal/protocol/wire"
13	"github.com/stretchr/testify/require"
14)
15
16// protoPipe is a scripted in-memory wire.Transport for preamble tests.
17type protoPipe struct {
18	mu     sync.Mutex
19	in     chan wire.Envelope
20	peer   *protoPipe
21	closed bool
22}
23
24func newProtoPipePair() (*protoPipe, *protoPipe) {
25	a := &protoPipe{in: make(chan wire.Envelope, 16)}
26	b := &protoPipe{in: make(chan wire.Envelope, 16)}
27	a.peer, b.peer = b, a
28	return a, b
29}
30
31func (p *protoPipe) Send(envelope wire.Envelope) error {
32	p.mu.Lock()
33	peer, closed := p.peer, p.closed
34	p.mu.Unlock()
35	if closed {
36		return context.Canceled
37	}
38	select {
39	case peer.in <- envelope:
40		return nil
41	default:
42		return ErrUnsupportedSend
43	}
44}
45
46func (p *protoPipe) Recv() (wire.Envelope, error) {
47	envelope, ok := <-p.in
48	if !ok {
49		return wire.Envelope{}, context.DeadlineExceeded
50	}
51	return envelope, nil
52}
53
54func (p *protoPipe) Close() error {
55	p.mu.Lock()
56	defer p.mu.Unlock()
57	p.closed = true
58	return nil
59}
60
61// TestProtoPreambleHandshake proves the preamble exchange over raw
62// envelope transports: acceptance negotiates minima, mismatches refuse with
63// a typed response, and the deadline bounds a silent peer.
64func TestProtoPreambleHandshake(t *testing.T) {
65	t.Run("accept negotiates minima", func(t *testing.T) {
66		client, server := newProtoPipePair()
67		serverCeilings := protoCeilings{maxReceiveEnvelopeBytes: wire.AbsoluteEnvelopeLimit / 2, outputDataLimit: uint64(protocol.MaxOutputDataLen) / 2}
68		serverDone := make(chan protoCeilings, 1)
69		go func() {
70			ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
71			defer cancel()
72			ceilings, err := runProtoServerPreamble(ctx, server, serverCeilings)
73			require.NoError(t, err)
74			serverDone <- ceilings
75		}()
76		ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
77		defer cancel()
78		clientCeilings, err := runProtoClientPreamble(ctx, client, defaultProtoCeilings())
79		require.NoError(t, err)
80		require.Equal(t, serverCeilings, clientCeilings)
81		require.Equal(t, serverCeilings, <-serverDone)
82	})
83
84	t.Run("version mismatch refuses", func(t *testing.T) {
85		client, server := newProtoPipePair()
86		go func() {
87			ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
88			defer cancel()
89			_, _ = runProtoServerPreamble(ctx, server, defaultProtoCeilings())
90		}()
91		bad := preambleRequestToWire(defaultProtoCeilings())
92		bad.Version = uint32(protocol.Version) + 1
93		raw, err := (proto.Marshal(bad))
94		require.NoError(t, err)
95		require.NoError(t, client.Send(wire.Envelope{Payload: raw}))
96		envelope, err := client.Recv()
97		require.NoError(t, err)
98		var response wire.PreambleResponse
99		require.NoError(t, (proto.UnmarshalOptions{DiscardUnknown: false}.Unmarshal(envelope.Payload, &response)))
100		require.False(t, response.GetAccepted())
101		require.Equal(t, uint32(3), response.GetRejection().GetCode())
102	})
103
104	t.Run("wrong magic refuses", func(t *testing.T) {
105		bad := preambleRequestToWire(defaultProtoCeilings())
106		bad.Magic = 0xDEADBEEF
107		_, err := checkPreambleRequest(bad)
108		require.ErrorIs(t, err, ErrPreambleRejected)
109	})
110
111	t.Run("wrong role refuses", func(t *testing.T) {
112		bad := preambleRequestToWire(defaultProtoCeilings())
113		bad.Role.Role = 2
114		_, err := checkPreambleRequest(bad)
115		require.ErrorIs(t, err, ErrPreambleRejected)
116
117		resp := preambleResponseToWire(true, defaultProtoCeilings(), 0)
118		resp.Role.Role = 1
119		_, err = checkPreambleResponse(resp)
120		require.ErrorIs(t, err, ErrPreambleRejected)
121	})
122
123	t.Run("oversized advertisement refuses", func(t *testing.T) {
124		bad := preambleRequestToWire(defaultProtoCeilings())
125		bad.MaxReceiveEnvelopeBytes = wire.AbsoluteEnvelopeLimit + 1
126		_, err := checkPreambleRequest(bad)
127		require.ErrorIs(t, err, ErrPreambleRejected)
128
129		bad = preambleRequestToWire(defaultProtoCeilings())
130		bad.OutputDataLimit = uint64(protocol.MaxOutputDataLen) + 1
131		_, err = checkPreambleRequest(bad)
132		require.ErrorIs(t, err, ErrPreambleRejected)
133	})
134
135	t.Run("deadline bounds silent peer", func(t *testing.T) {
136		client, _ := newProtoPipePair()
137		ctx, cancel := context.WithTimeout(context.Background(), 50*time.Millisecond)
138		defer cancel()
139		_, err := runProtoClientPreamble(ctx, client, defaultProtoCeilings())
140		require.ErrorIs(t, err, ErrPreambleTimeout)
141	})
142
143	t.Run("oversize preamble rejected", func(t *testing.T) {
144		client, server := newProtoPipePair()
145		go func() {
146			_ = client.Send(wire.Envelope{Payload: make([]byte, wire.PreambleLimit+1)})
147		}()
148		ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
149		defer cancel()
150		_, err := runProtoServerPreamble(ctx, server, defaultProtoCeilings())
151		require.ErrorIs(t, err, wire.ErrScanLength)
152	})
153}

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