local_test.go RawHistoryFolder

1package brokerconfig
2
3import (
4	"context"
5	"errors"
6	"path/filepath"
7	"testing"
8
9	"github.com/stretchr/testify/require"
10
11	"github.com/bnema/vev/internal/ports"
12)
13
14const (
15	localTestIdentity = "offline-local-daemon"
16	localTestSocket   = "/tmp/vev-brokerconfig-test/local-mux.sock"
17)
18
19// localDocument returns the raw local object provisioned by the fixture. The
20// caller may override or delete fields through the returned map.
21func localTestDocument() map[string]any {
22	policy := testPolicy()
23	return map[string]any{
24		"identity": localTestIdentity,
25		"route":    localTestSocket,
26		"policy": map[string]any{
27			"protocolVersion":      policy.ProtocolVersion,
28			"catalogSchemaVersion": policy.CatalogSchemaVersion,
29			"environmentPolicy":    "client-owned",
30			"transport":            policy.Transport,
31			"trust":                policy.Trust,
32			"launch":               policy.Launch,
33			"isolation":            policy.Isolation,
34		},
35	}
36}
37
38// documentWithLocal returns a valid registration document carrying one local
39// binding, with any field overrides applied.
40func documentWithLocal(t *testing.T, local map[string]any) map[string]any {
41	t.Helper()
42	document := registrationDocument(nil)
43	document["local"] = local
44	return document
45}
46
47// loadDocument writes one document into a fresh offline root and loads it.
48func loadDocument(t *testing.T, document any, reserved []string) (*Config, error) {
49	t.Helper()
50	root := t.TempDir()
51	writeConfig(t, root, document, 0o600)
52	layout, err := ResolveLayout(root, reserved)
53	require.NoError(t, err)
54	return Load(layout)
55}
56
57// localRequest builds one local open-stream request under the fixture policy.
58func localRequest() ports.BrokerOpenStreamRequest {
59	return ports.BrokerOpenStreamRequest{
60		Purpose:   ports.BrokerStreamControl,
61		Local:     true,
62		Policy:    testPolicy(),
63		StartMode: ports.BrokerDaemonStartIfNeeded,
64	}
65}
66
67// TestLoadLocalBinding pins the provisioned local binding: the identity,
68// policy, sanitized display origin, and local Unix route are configuration
69// authority, and the local route is resolvable by its opaque pool address.
70func TestLoadLocalBinding(t *testing.T) {
71	config, err := loadDocument(t, documentWithLocal(t, localTestDocument()), nil)
72	require.NoError(t, err)
73
74	binding, ok := config.LocalBinding()
75	require.True(t, ok)
76	require.Equal(t, ports.BrokerDaemonIdentity(localTestIdentity), binding.Identity)
77	require.Equal(t, testPolicy(), binding.Policy)
78	require.Equal(t, LocalDisplayOrigin, binding.DisplayOrigin)
79	require.True(t, binding.Route.IsLocal())
80	require.Equal(t, localTestSocket, binding.Route.Path())
81
82	resolved, err := config.Resolver().ResolveDialTarget(context.Background(), localRequest())
83	require.NoError(t, err)
84	require.Equal(t, binding.Identity, resolved.ExpectedIdentity.Identity)
85	require.Equal(t, binding.Policy, resolved.Policy)
86	require.Equal(t, binding.Route.Address(), resolved.Address)
87
88	route, ok := config.RouteByAddress(resolved.Address)
89	require.True(t, ok)
90	require.Equal(t, localTestSocket, route.Path())
91}
92
93// TestLoadLocalBindingDefaultsDisplayOrigin pins the broker-owned default and
94// the sanitizer: an absent origin becomes "local", and a hostile origin is
95// sanitized rather than published raw.
96func TestLoadLocalBindingDefaultsDisplayOrigin(t *testing.T) {
97	tests := []struct {
98		name   string
99		origin any
100		want   string
101	}{
102		{name: "absent defaults to the broker-owned origin", origin: nil, want: LocalDisplayOrigin},
103		{name: "explicit origin is kept", origin: "this-host", want: "this-host"},
104		{name: "bidi and control runes are stripped", origin: "lo\u202ecal", want: "local"},
105		{name: "wholly undisplayable defaults", origin: "\u202e\u2028", want: LocalDisplayOrigin},
106	}
107	for _, tt := range tests {
108		t.Run(tt.name, func(t *testing.T) {
109			local := localTestDocument()
110			if tt.origin == nil {
111				delete(local, "displayOrigin")
112			} else {
113				local["displayOrigin"] = tt.origin
114			}
115			config, err := loadDocument(t, documentWithLocal(t, local), nil)
116			require.NoError(t, err)
117			binding, ok := config.LocalBinding()
118			require.True(t, ok)
119			require.Equal(t, tt.want, binding.DisplayOrigin)
120		})
121	}
122}
123
124// TestLoadRejectsInvalidLocalBinding pins every local-binding refusal at the
125// load boundary. A binding the broker could not fence is never provisioned.
126func TestLoadRejectsInvalidLocalBinding(t *testing.T) {
127	tests := []struct {
128		name    string
129		mutate  func(t *testing.T, local map[string]any)
130		wantErr string
131	}{
132		{
133			name:    "missing identity",
134			mutate:  func(_ *testing.T, local map[string]any) { delete(local, "identity") },
135			wantErr: "identity",
136		},
137		{
138			name:    "missing policy",
139			mutate:  func(_ *testing.T, local map[string]any) { delete(local, "policy") },
140			wantErr: "policy",
141		},
142		{
143			name:    "missing route",
144			mutate:  func(_ *testing.T, local map[string]any) { delete(local, "route") },
145			wantErr: "route",
146		},
147		{
148			name: "ssh route is refused",
149			mutate: func(_ *testing.T, local map[string]any) {
150				local["route"] = map[string]any{"kind": "ssh-stdio", "target": "user@host", "argv": []string{"vev"}}
151			},
152			wantErr: "Unix daemonmux",
153		},
154		{
155			name:    "relative route is refused",
156			mutate:  func(_ *testing.T, local map[string]any) { local["route"] = "relative/mux.sock" },
157			wantErr: "not absolute",
158		},
159		{
160			name:    "unknown field is refused",
161			mutate:  func(_ *testing.T, local map[string]any) { local["extra"] = 1 },
162			wantErr: "unknown field",
163		},
164	}
165	for _, tt := range tests {
166		t.Run(tt.name, func(t *testing.T) {
167			local := localTestDocument()
168			tt.mutate(t, local)
169			_, err := loadDocument(t, documentWithLocal(t, local), nil)
170			require.Error(t, err)
171			require.Contains(t, err.Error(), tt.wantErr)
172		})
173	}
174}
175
176// TestLoadLocalRouteFencesProduction proves the local route is fenced against
177// the reserved production runtime and state directories exactly like a remote
178// Unix route: an isolated sandbox can never reach a production endpoint
179// through its own local binding.
180func TestLoadLocalRouteFencesProduction(t *testing.T) {
181	reservedDir := t.TempDir()
182	local := localTestDocument()
183	local["route"] = filepath.Join(reservedDir, "mux.sock")
184	_, err := loadDocument(t, documentWithLocal(t, local), []string{reservedDir})
185	require.Error(t, err)
186	require.Contains(t, err.Error(), "overlaps production")
187}
188
189// TestLoadLocalRouteAliasesRegistrationRoute proves the local binding may
190// reuse the exact Unix mux route a registration already provisions: the pool
191// address is identical only when the route is identical.
192func TestLoadLocalRouteAliasesRegistrationRoute(t *testing.T) {
193	local := localTestDocument()
194	local["route"] = "/tmp/vev-brokerconfig-test/mux.sock" // the fixture registration route
195	config, err := loadDocument(t, documentWithLocal(t, local), nil)
196	require.NoError(t, err)
197	binding, ok := config.LocalBinding()
198	require.True(t, ok)
199	require.Equal(t, "/tmp/vev-brokerconfig-test/mux.sock", binding.Route.Path())
200}
201
202// TestLoadRefusesLocalIdentityPoolConflict proves the pool's (identity,
203// policy) keying is honored across the local and remote provisionings: a local
204// binding that shares a registration's exact identity and policy but names a
205// different route is refused rather than silently dialed for one and ignored
206// for the other.
207func TestLoadRefusesLocalIdentityPoolConflict(t *testing.T) {
208	local := localTestDocument()
209	local["identity"] = testRegistrationIdentity()
210	local["route"] = "/tmp/vev-brokerconfig-test/other-mux.sock"
211	_, err := loadDocument(t, documentWithLocal(t, local), nil)
212	require.Error(t, err)
213	require.Contains(t, err.Error(), "already provisioned on a different route")
214}
215
216// testRegistrationIdentity reads the identity the fixture registration
217// provisions, so the collision test is anchored to the real fixture.
218func testRegistrationIdentity() string { return "offline-daemon" }
219
220// TestResolveLocalSuccessAndRefusals pins the local resolution contract:
221// success returns the configured authority and address, a configuration with
222// no local binding refuses as unavailable, a conflicting policy is refused as
223// such, and a cancelled context is reported as cancellation. Every refusal is
224// typed as a ports.BrokerError so the pool classifies it without text matching.
225func TestResolveLocalSuccessAndRefusals(t *testing.T) {
226	config, err := loadDocument(t, documentWithLocal(t, localTestDocument()), nil)
227	require.NoError(t, err)
228	binding, ok := config.LocalBinding()
229	require.True(t, ok)
230	resolver := config.Resolver()
231
232	noLocalConfig, err := loadDocument(t, registrationDocument(nil), nil)
233	require.NoError(t, err)
234
235	tests := []struct {
236		name     string
237		resolver *Resolver
238		request  ports.BrokerOpenStreamRequest
239		wantCode ports.BrokerErrorCode
240		wantErr  error
241	}{
242		{
243			name:     "success returns configured authority",
244			resolver: resolver,
245			request:  localRequest(),
246		},
247		{
248			name:     "no local binding is unavailable",
249			resolver: noLocalConfig.Resolver(),
250			request:  localRequest(),
251			wantCode: ports.BrokerErrorUnavailable,
252			wantErr:  errNoLocalRoute,
253		},
254		{
255			name:     "conflicting policy is refused",
256			resolver: resolver,
257			request: func() ports.BrokerOpenStreamRequest {
258				request := localRequest()
259				request.Policy.Transport = "other-transport"
260				return request
261			}(),
262			wantCode: ports.BrokerErrorConflictingPolicy,
263			wantErr:  errLocalPolicyConflict,
264		},
265		{
266			name:     "cancelled context is cancellation",
267			resolver: resolver,
268			request:  localRequest(),
269			wantErr:  context.Canceled,
270		},
271	}
272	for _, tt := range tests {
273		t.Run(tt.name, func(t *testing.T) {
274			ctx := context.Background()
275			if errors.Is(tt.wantErr, context.Canceled) {
276				cancelled, cancel := context.WithCancel(ctx)
277				cancel()
278				ctx = cancelled
279			}
280			resolved, err := tt.resolver.ResolveDialTarget(ctx, tt.request)
281			if tt.wantErr == nil {
282				require.NoError(t, err)
283				require.Equal(t, binding.Identity, resolved.ExpectedIdentity.Identity)
284				require.Equal(t, binding.Policy, resolved.Policy)
285				require.Equal(t, binding.Route.Address(), resolved.Address)
286				return
287			}
288			require.Error(t, err)
289			if tt.wantCode != 0 {
290				var typed ports.BrokerError
291				require.ErrorAs(t, err, &typed)
292				require.Equal(t, tt.wantCode, typed.Code)
293			}
294			require.ErrorIs(t, err, tt.wantErr)
295		})
296	}
297}
298
299// TestLocalMuxRouteExcludesLocalBinding proves Config.LocalMuxRoute serves the
300// remote-side mux helper's route only. The broker-owned local binding's own
301// carriage is deliberately excluded: no helper ever bridges it, so a
302// configuration provisioning only the local object has no helper route, and one
303// provisioning both returns the registration's route, never the local binding's.
304func TestLocalMuxRouteExcludesLocalBinding(t *testing.T) {
305	t.Run("local-only configuration provisions no helper route", func(t *testing.T) {
306		document := map[string]any{
307			"marker":        Marker,
308			"registrations": []any{},
309			"local":         localTestDocument(),
310		}
311		config, err := loadDocument(t, document, nil)
312		require.NoError(t, err)
313		_, ok := config.LocalBinding()
314		require.True(t, ok, "the fixture provisions a broker-owned local binding")
315		_, err = config.LocalMuxRoute()
316		require.Error(t, err)
317		require.Contains(t, err.Error(), "no Unix daemon-mux route is provisioned")
318	})
319
320	t.Run("registration route wins over the local binding route", func(t *testing.T) {
321		config, err := loadDocument(t, documentWithLocal(t, localTestDocument()), nil)
322		require.NoError(t, err)
323		binding, ok := config.LocalBinding()
324		require.True(t, ok)
325
326		route, err := config.LocalMuxRoute()
327		require.NoError(t, err)
328		require.True(t, route.IsLocal())
329		require.Equal(t, "/tmp/vev-brokerconfig-test/mux.sock", route.Path(), "the helper bridges the registration route")
330		require.NotEqual(t, binding.Route.Path(), route.Path(), "the local binding route is not the helper route")
331		require.NotEqual(t, binding.Route.Address(), route.Address(), "the two routes carry distinct pool addresses")
332	})
333}

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