v0.7.0 4c16dd8

envelope_inventory_test.go RawHistoryFolder

1package wire
2
3import (
4	"strings"
5	"testing"
6
7	"github.com/bnema/vev/internal/protocol"
8	"google.golang.org/protobuf/proto"
9	"google.golang.org/protobuf/reflect/protoreflect"
10)
11
12// TestEnvelopeInventoryProvesDirectionAndSemanticPath is the P2.1 schema
13// inventory: every ClientEnvelope variant names a protocol.ClientMessage,
14// every ServerEnvelope variant names a protocol.ServerMessage, neither side
15// shares a payload type except route navigation failure, and the preamble
16// constants agree with the schema authority.
17func TestEnvelopeInventoryProvesDirectionAndSemanticPath(t *testing.T) {
18	if protocol.Version != 65 {
19		t.Fatalf("protocol.Version = %d, want 65", protocol.Version)
20	}
21	if ProtocolEpoch != 1 {
22		t.Fatalf("ProtocolEpoch = %d, want 1", ProtocolEpoch)
23	}
24	if PreambleMagic != 0x56455631 {
25		t.Fatalf("PreambleMagic = %#x, want VEV1", PreambleMagic)
26	}
27
28	clientVariants := envelopeVariants(t, (&ClientEnvelope{}).ProtoReflect())
29	serverVariants := envelopeVariants(t, (&ServerEnvelope{}).ProtoReflect())
30
31	// Closed semantic unions, in wire-variant spelling.
32	clientSemantic := map[string]bool{
33		"suspend_attachment": true, "activate_attachment": true,
34		"hello": true, "input": true, "resize": true, "detach": true, "ping": true,
35		"list": true, "kill": true, "theme": true, "ack": true, "image_push": true,
36		"client_notice": true, "command_request": true, "output_reset_request": true,
37		"remote_preview_watch": true, "route_attention_subscription": true,
38		"same_peer_switch_request": true, "recent_route_snapshot": true,
39		"route_navigation_failure": true, "session_creation_failure": true,
40		"ui_fence": true, "navigation_inventory_request": true,
41		"navigation_inventory_publication": true, "navigation_inventory_failure": true,
42		"picker_close": true, "picker_selection": true,
43		"select_tab": true, "terminal_focus": true,
44	}
45	serverSemantic := map[string]bool{
46		"attachment_suspended": true, "attachment_activated": true,
47		"welcome": true, "error": true, "output": true, "detached": true, "pong": true,
48		"sessions": true, "command_result": true, "attach_target": true,
49		"remote_preview": true, "committed_route_identity": true,
50		"route_navigation_action": true, "route_create_session_action": true,
51		"route_navigation_failure": true, "route_position": true, "route_retired": true,
52		"same_peer_switch_failure": true, "ui_receipt": true, "ui_view_update": true,
53		"navigation_inventory_response": true, "navigation_inventory_demand": true,
54		"navigation_inventory_selection": true,
55		"picker_offer":                   true, "picker_snapshot": true, "picker_closed": true,
56		"picker_result": true, "picker_failure": true,
57		"kill_result": true,
58	}
59
60	if len(clientVariants) != len(clientSemantic) {
61		t.Fatalf("client envelope has %d variants, want %d (%v)", len(clientVariants), len(clientSemantic), clientVariants)
62	}
63	for _, name := range clientVariants {
64		if !clientSemantic[name] {
65			t.Fatalf("client variant %q has no semantic validation path", name)
66		}
67	}
68	if len(serverVariants) != len(serverSemantic) {
69		t.Fatalf("server envelope has %d variants, want %d (%v)", len(serverVariants), len(serverSemantic), serverVariants)
70	}
71	for _, name := range serverVariants {
72		if !serverSemantic[name] {
73			t.Fatalf("server variant %q has no semantic validation path", name)
74		}
75	}
76
77	// Direction closure: exactly one shared payload may appear on both sides.
78	clientSet := map[string]bool{}
79	for _, name := range clientVariants {
80		clientSet[name] = true
81	}
82	shared := []string{}
83	for _, name := range serverVariants {
84		if clientSet[name] {
85			shared = append(shared, name)
86		}
87	}
88	if len(shared) != 1 || shared[0] != "route_navigation_failure" {
89		t.Fatalf("shared client/server payloads = %v, want [route_navigation_failure]", shared)
90	}
91
92	// Every variant must be a distinct generated message type (no generic
93	// escape hatch: no Any, no shared catch-all payload).
94	// Every variant within one envelope must be a distinct generated message
95	// type (no generic escape hatch: no Any, no catch-all). The one shared
96	// payload (route navigation failure) is allowed across envelopes only;
97	// direction is enforced by which envelope wraps it.
98	for _, envelope := range []proto.Message{&ClientEnvelope{}, &ServerEnvelope{}} {
99		seen := map[string]string{}
100		descriptor := envelope.ProtoReflect().Descriptor()
101		if descriptor.Oneofs().Len() != 1 {
102			t.Fatalf("envelope has %d oneofs, want exactly the payload oneof", descriptor.Oneofs().Len())
103		}
104		oneof := descriptor.Oneofs().Get(0)
105		if string(oneof.Name()) != "payload" {
106			t.Fatalf("envelope oneof %q is not the payload oneof", oneof.Name())
107		}
108		for j := range oneof.Fields().Len() {
109			variant := oneof.Fields().Get(j)
110			message := variant.Message()
111			if message == nil {
112				t.Fatalf("variant %q is not a message", variant.Name())
113			}
114			key := string(message.FullName())
115			if prev, dup := seen[key]; dup {
116				t.Fatalf("payload type %v shared by %q and %q", key, prev, variant.Name())
117			}
118			seen[key] = string(variant.Name())
119			if variant.Number() == 0 {
120				t.Fatalf("variant %q has no field number", variant.Name())
121			}
122		}
123	}
124}
125
126func envelopeVariants(t *testing.T, message protoreflect.Message) []string {
127	t.Helper()
128	descriptor := message.Descriptor()
129	if descriptor.Oneofs().Len() != 1 {
130		t.Fatalf("envelope has %d oneofs, want exactly the payload oneof", descriptor.Oneofs().Len())
131	}
132	oneof := descriptor.Oneofs().Get(0)
133	if string(oneof.Name()) != "payload" {
134		t.Fatalf("envelope oneof %q is not the payload oneof", oneof.Name())
135	}
136	var names []string
137	for i := range oneof.Fields().Len() {
138		names = append(names, strings.ToLower(string(oneof.Fields().Get(i).Name())))
139	}
140	return names
141}

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