proto_convert_route_hosts_test.go RawHistoryFolder
1package sessionwire
2
3import (
4 "testing"
5
6 "github.com/stretchr/testify/require"
7 "google.golang.org/protobuf/proto"
8
9 "github.com/bnema/vev/internal/domain"
10 "github.com/bnema/vev/internal/protocol"
11 "github.com/bnema/vev/internal/protocol/wire"
12)
13
14func routeHostsTestSnapshot() protocol.RecentRouteSnapshot {
15 target := protocol.ExactSessionTarget{LifecycleID: domain.SessionLifecycleID{7}, SessionName: "work"}
16 return protocol.RecentRouteSnapshot{
17 Generation: 3,
18 Entries: []protocol.RecentRouteEntry{{
19 Key: 1, Generation: 1, Target: target, Name: "work", HostLabel: "box",
20 Kind: protocol.RouteKindRemote, Attention: true, AttentionSeq: 5, Visited: true,
21 }},
22 Hosts: []protocol.RouteHost{
23 {Key: 2, Generation: 1, Label: "a", Kind: protocol.RouteKindRemote},
24 {Key: 3, Generation: 1, Label: "local", Kind: protocol.RouteKindLocal},
25 },
26 }
27}
28
29// TestProtoRouteHostsWire pins the new route snapshot fields byte for byte
30// and checks the envelope rejects a truncated prefix and trailing garbage.
31func TestProtoRouteHostsWire(t *testing.T) {
32 hostBytes, err := proto.Marshal(routeHostToWire(protocol.RouteHost{Key: 2, Generation: 1, Label: "a", Kind: protocol.RouteKindRemote}))
33 require.NoError(t, err)
34 require.Equal(t, []byte{0x08, 0x02, 0x10, 0x01, 0x1A, 0x01, 'a', 0x20, 0x02}, hostBytes)
35 seqBytes, err := proto.Marshal(&wire.RecentRouteEntry{AttentionSeq: 5})
36 require.NoError(t, err)
37 require.Equal(t, []byte{0x50, 0x05}, seqBytes)
38 visitedBytes, err := proto.Marshal(&wire.RecentRouteEntry{Visited: true})
39 require.NoError(t, err)
40 require.Equal(t, []byte{0x58, 0x01}, visitedBytes)
41
42 snapshot := routeHostsTestSnapshot()
43 envelope, err := encodeProtoClient(snapshot)
44 require.NoError(t, err)
45 raw, err := proto.Marshal(envelope)
46 require.NoError(t, err)
47
48 tests := []struct {
49 name string
50 raw []byte
51 wantErr bool
52 }{
53 {name: "exact", raw: raw},
54 {name: "truncated prefix", raw: raw[:len(raw)-1], wantErr: true},
55 {name: "trailing garbage", raw: append(append([]byte(nil), raw...), 0xFF), wantErr: true},
56 }
57 for _, tt := range tests {
58 t.Run(tt.name, func(t *testing.T) {
59 err := wire.ScanEnvelope(&wire.ClientEnvelope{}, tt.raw)
60 if tt.wantErr {
61 require.Error(t, err)
62 return
63 }
64 require.NoError(t, err)
65 decoded := &wire.ClientEnvelope{}
66 require.NoError(t, proto.Unmarshal(tt.raw, decoded))
67 got, err := decodeProtoClient(decoded)
68 require.NoError(t, err)
69 require.Equal(t, snapshot, got)
70 })
71 }
72}
73
74func TestProtoRouteHostsRejectInvalid(t *testing.T) {
75 tests := []struct {
76 name string
77 mutate func(*wire.RecentRouteSnapshot)
78 }{
79 {name: "zero host identity", mutate: func(s *wire.RecentRouteSnapshot) { s.Hosts[0].Key = 0 }},
80 {name: "empty host label", mutate: func(s *wire.RecentRouteSnapshot) { s.Hosts[0].Label = "" }},
81 {name: "unknown host kind", mutate: func(s *wire.RecentRouteSnapshot) { s.Hosts[0].Kind = 9 }},
82 {name: "host collides with entry", mutate: func(s *wire.RecentRouteSnapshot) { s.Hosts[0].Key = 1 }},
83 {name: "duplicate host", mutate: func(s *wire.RecentRouteSnapshot) { s.Hosts[1].Key = s.Hosts[0].Key }},
84 {name: "attention order without attention", mutate: func(s *wire.RecentRouteSnapshot) { s.Entries[0].Attention = false }},
85 {name: "nil host", mutate: func(s *wire.RecentRouteSnapshot) { s.Hosts[0] = nil }},
86 }
87 for _, tt := range tests {
88 t.Run(tt.name, func(t *testing.T) {
89 message, err := recentRouteSnapshotToWire(routeHostsTestSnapshot())
90 require.NoError(t, err)
91 tt.mutate(message)
92 _, err = recentRouteSnapshotFromWire(message)
93 require.Error(t, err)
94 })
95 }
96}