session_admission_test.go RawHistoryFolder

1package ports
2
3import (
4	"strings"
5	"testing"
6
7	"github.com/stretchr/testify/require"
8
9	"github.com/bnema/vev/internal/domain"
10	"github.com/bnema/vev/internal/protocol"
11)
12
13func admissionTestPolicy() BrokerPolicy {
14	return BrokerPolicy{
15		ProtocolVersion:      protocol.Version,
16		CatalogSchemaVersion: 3,
17		EnvironmentPolicy:    protocol.EnvironmentPolicyClientOwned,
18		Transport:            "unix-mux",
19		Trust:                "same-user",
20		Launch:               "explicit",
21		Isolation:            "per-user",
22	}
23}
24
25func admissionTestTarget() protocol.ExactSessionTarget {
26	return protocol.ExactSessionTarget{LifecycleID: domain.SessionLifecycleID{1, 2, 3}, SessionName: "work"}
27}
28
29// TestSessionConnectionOriginValidateMatrix pins the closed origin taxonomy: the
30// two provisioned localities validate and Unknown plus every out-of-range value
31// is refused, so an admission can never inherit an implicit locality.
32func TestSessionConnectionOriginValidateMatrix(t *testing.T) {
33	tests := []struct {
34		name   string
35		origin SessionConnectionOrigin
36		valid  bool
37	}{
38		{name: "unknown", origin: SessionOriginUnknown, valid: false},
39		{name: "local", origin: SessionOriginLocal, valid: true},
40		{name: "remote", origin: SessionOriginRemote, valid: true},
41		{name: "out of range", origin: SessionConnectionOrigin(9), valid: false},
42	}
43	for _, tt := range tests {
44		t.Run(tt.name, func(t *testing.T) {
45			err := tt.origin.Validate()
46			if tt.valid {
47				require.NoError(t, err)
48				return
49			}
50			require.Error(t, err)
51		})
52	}
53}
54
55// TestSessionAdmissionValidateMatrix pins the closed admission contract: origin,
56// policy, purpose, admission variant, name, target, and environment bounds are
57// each enforced, and a shape that contradicts its purpose is refused rather than
58// narrowed.
59func TestSessionAdmissionValidateMatrix(t *testing.T) {
60	env := []string{"TERM=xterm"}
61	atEnvLimit := make([]string, BrokerMaxEnvEntries)
62	for i := range atEnvLimit {
63		atEnvLimit[i] = "K=v"
64	}
65	oversizeEnv := []string{"K=" + strings.Repeat("x", BrokerMaxEnvEntryBytes)}
66
67	base := SessionAdmission{Origin: SessionOriginLocal, Policy: admissionTestPolicy(), Purpose: BrokerStreamControl}
68
69	tests := []struct {
70		name      string
71		admission SessionAdmission
72		valid     bool
73	}{
74		{name: "control", admission: base, valid: true},
75		{name: "observation", admission: func() SessionAdmission {
76			a := base
77			a.Purpose = BrokerStreamObservation
78			return a
79		}(), valid: true},
80		{name: "exact attach", admission: func() SessionAdmission {
81			a := base
82			a.Purpose = BrokerStreamAttachment
83			a.Admission = BrokerAdmissionExact
84			a.Target = admissionTestTarget()
85			a.Env = append([]string(nil), env...)
86			return a
87		}(), valid: true},
88		{name: "named creation", admission: func() SessionAdmission {
89			a := base
90			a.Purpose = BrokerStreamAttachment
91			a.Admission = BrokerAdmissionCreateNamed
92			a.Name = "work"
93			return a
94		}(), valid: true},
95		{name: "ephemeral creation", admission: func() SessionAdmission {
96			a := base
97			a.Purpose = BrokerStreamAttachment
98			a.Admission = BrokerAdmissionCreateEphemeral
99			return a
100		}(), valid: true},
101		{name: "remote exact attach", admission: func() SessionAdmission {
102			a := base
103			a.Origin = SessionOriginRemote
104			a.Purpose = BrokerStreamAttachment
105			a.Admission = BrokerAdmissionExact
106			a.Target = admissionTestTarget()
107			return a
108		}(), valid: true},
109		{name: "environment at bound", admission: func() SessionAdmission {
110			a := base
111			a.Purpose = BrokerStreamAttachment
112			a.Admission = BrokerAdmissionExact
113			a.Target = admissionTestTarget()
114			a.Env = atEnvLimit
115			return a
116		}(), valid: true},
117
118		{name: "unknown origin", admission: func() SessionAdmission {
119			a := base
120			a.Origin = SessionOriginUnknown
121			return a
122		}(), valid: false},
123		{name: "invalid origin", admission: func() SessionAdmission {
124			a := base
125			a.Origin = SessionConnectionOrigin(7)
126			return a
127		}(), valid: false},
128		{name: "invalid policy", admission: func() SessionAdmission {
129			a := base
130			a.Policy.ProtocolVersion = 0
131			return a
132		}(), valid: false},
133		{name: "zero purpose", admission: func() SessionAdmission {
134			a := base
135			a.Purpose = 0
136			return a
137		}(), valid: false},
138		{name: "invalid purpose", admission: func() SessionAdmission {
139			a := base
140			a.Purpose = BrokerStreamPurpose(9)
141			return a
142		}(), valid: false},
143		{name: "attachment without admission", admission: func() SessionAdmission {
144			a := base
145			a.Purpose = BrokerStreamAttachment
146			return a
147		}(), valid: false},
148		{name: "exact without target", admission: func() SessionAdmission {
149			a := base
150			a.Purpose = BrokerStreamAttachment
151			a.Admission = BrokerAdmissionExact
152			return a
153		}(), valid: false},
154		{name: "exact with name", admission: func() SessionAdmission {
155			a := base
156			a.Purpose = BrokerStreamAttachment
157			a.Admission = BrokerAdmissionExact
158			a.Target = admissionTestTarget()
159			a.Name = "work"
160			return a
161		}(), valid: false},
162		{name: "named creation without name", admission: func() SessionAdmission {
163			a := base
164			a.Purpose = BrokerStreamAttachment
165			a.Admission = BrokerAdmissionCreateNamed
166			return a
167		}(), valid: false},
168		{name: "named creation with target", admission: func() SessionAdmission {
169			a := base
170			a.Purpose = BrokerStreamAttachment
171			a.Admission = BrokerAdmissionCreateNamed
172			a.Name = "work"
173			a.Target = admissionTestTarget()
174			return a
175		}(), valid: false},
176		{name: "ephemeral with name", admission: func() SessionAdmission {
177			a := base
178			a.Purpose = BrokerStreamAttachment
179			a.Admission = BrokerAdmissionCreateEphemeral
180			a.Name = "work"
181			return a
182		}(), valid: false},
183		{name: "ephemeral with target", admission: func() SessionAdmission {
184			a := base
185			a.Purpose = BrokerStreamAttachment
186			a.Admission = BrokerAdmissionCreateEphemeral
187			a.Target = admissionTestTarget()
188			return a
189		}(), valid: false},
190		{name: "control carries admission", admission: func() SessionAdmission {
191			a := base
192			a.Admission = BrokerAdmissionExact
193			return a
194		}(), valid: false},
195		{name: "control carries name", admission: func() SessionAdmission {
196			a := base
197			a.Name = "work"
198			return a
199		}(), valid: false},
200		{name: "control carries target", admission: func() SessionAdmission {
201			a := base
202			a.Target = admissionTestTarget()
203			return a
204		}(), valid: false},
205		{name: "control carries env", admission: func() SessionAdmission {
206			a := base
207			a.Env = append([]string(nil), env...)
208			return a
209		}(), valid: false},
210		{name: "observation carries env", admission: func() SessionAdmission {
211			a := base
212			a.Purpose = BrokerStreamObservation
213			a.Env = append([]string(nil), env...)
214			return a
215		}(), valid: false},
216		{name: "too many environment entries", admission: func() SessionAdmission {
217			a := base
218			a.Purpose = BrokerStreamAttachment
219			a.Admission = BrokerAdmissionExact
220			a.Target = admissionTestTarget()
221			a.Env = append(append([]string(nil), atEnvLimit...), "K=v")
222			return a
223		}(), valid: false},
224		{name: "oversize environment entry", admission: func() SessionAdmission {
225			a := base
226			a.Purpose = BrokerStreamAttachment
227			a.Admission = BrokerAdmissionExact
228			a.Target = admissionTestTarget()
229			a.Env = oversizeEnv
230			return a
231		}(), valid: false},
232		{name: "invalid utf8 environment entry", admission: func() SessionAdmission {
233			a := base
234			a.Purpose = BrokerStreamAttachment
235			a.Admission = BrokerAdmissionExact
236			a.Target = admissionTestTarget()
237			a.Env = []string{"K=\xff"}
238			return a
239		}(), valid: false},
240	}
241	for _, tt := range tests {
242		t.Run(tt.name, func(t *testing.T) {
243			err := tt.admission.Validate()
244			if tt.valid {
245				require.NoError(t, err)
246				return
247			}
248			require.Error(t, err)
249		})
250	}
251}
252
253// TestSessionAdmissionCloneIsDeep pins the defensive copy: mutating the clone's
254// environment or its nested values never changes the original, and a nil
255// environment stays nil.
256func TestSessionAdmissionCloneIsDeep(t *testing.T) {
257	original := SessionAdmission{
258		Origin: SessionOriginRemote, Policy: admissionTestPolicy(), Purpose: BrokerStreamAttachment,
259		Admission: BrokerAdmissionExact, Target: admissionTestTarget(), Env: []string{"TERM=xterm"},
260	}
261	clone := original.Clone()
262	require.Equal(t, original, clone)
263
264	clone.Env[0] = "TERM=changed"
265	clone.Policy.Trust = "changed"
266	clone.Target.SessionName = "changed"
267	require.Equal(t, "TERM=xterm", original.Env[0], "clone environment is independent")
268	require.Equal(t, "same-user", original.Policy.Trust)
269	require.Equal(t, "work", original.Target.SessionName)
270
271	empty := SessionAdmission{Origin: SessionOriginLocal, Policy: admissionTestPolicy(), Purpose: BrokerStreamControl}
272	require.Nil(t, empty.Clone().Env, "an absent environment stays nil")
273	require.Equal(t, empty, empty.Clone())
274}

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