session_environment_test.go RawHistoryFolder

1package client
2
3import (
4	"strings"
5	"testing"
6
7	"github.com/stretchr/testify/require"
8
9	"github.com/bnema/vev/internal/ports"
10)
11
12func TestSessionEnvironmentValidate(t *testing.T) {
13	oversizeEntry := "K=" + strings.Repeat("x", ports.BrokerMaxEnvEntryBytes-2+1)
14	maxEntry := "K=" + strings.Repeat("x", ports.BrokerMaxEnvEntryBytes-2)
15	require.Len(t, maxEntry, ports.BrokerMaxEnvEntryBytes)
16	longCwd := "/" + strings.Repeat("a", 65535)
17	require.Len(t, longCwd, 65536)
18
19	validLocal := SessionEnvironment{
20		Provenance: SessionEnvironmentLocalCLI,
21		Env:        []string{"FOO=bar", "EMPTY="},
22		Cwd:        "/tmp",
23	}
24
25	tests := []struct {
26		name    string
27		env     SessionEnvironment
28		wantErr bool
29	}{
30		{name: "unspecified empty", env: SessionEnvironment{}, wantErr: true},
31		{name: "unspecified with env", env: SessionEnvironment{Env: []string{"A=b"}}, wantErr: true},
32		{name: "unknown provenance", env: SessionEnvironment{Provenance: SessionEnvironmentProvenance(99)}, wantErr: true},
33		{name: "local cli valid", env: validLocal, wantErr: false},
34		{name: "local picker valid empty", env: SessionEnvironment{Provenance: SessionEnvironmentLocalPicker}, wantErr: false},
35		{name: "local picker valid cwd", env: SessionEnvironment{Provenance: SessionEnvironmentLocalPicker, Env: []string{"A=b"}, Cwd: "/x"}, wantErr: false},
36		{name: "local max entry valid", env: SessionEnvironment{Provenance: SessionEnvironmentLocalCLI, Env: []string{maxEntry}}, wantErr: false},
37		{name: "too many entries", env: SessionEnvironment{Provenance: SessionEnvironmentLocalCLI, Env: make([]string, ports.BrokerMaxEnvEntries+1)}, wantErr: true},
38		{name: "entry invalid utf8", env: SessionEnvironment{Provenance: SessionEnvironmentLocalCLI, Env: []string{"A=" + string([]byte{0xff})}}, wantErr: true},
39		{name: "entry NUL", env: SessionEnvironment{Provenance: SessionEnvironmentLocalCLI, Env: []string{"A=b\x00c"}}, wantErr: true},
40		{name: "entry too long", env: SessionEnvironment{Provenance: SessionEnvironmentLocalCLI, Env: []string{oversizeEntry}}, wantErr: true},
41		{name: "entry missing equals", env: SessionEnvironment{Provenance: SessionEnvironmentLocalCLI, Env: []string{"NOEQUALS"}}, wantErr: true},
42		{name: "entry empty name", env: SessionEnvironment{Provenance: SessionEnvironmentLocalCLI, Env: []string{"=value"}}, wantErr: true},
43		{name: "cwd relative", env: SessionEnvironment{Provenance: SessionEnvironmentLocalCLI, Cwd: "relative/path"}, wantErr: true},
44		{name: "cwd invalid utf8", env: SessionEnvironment{Provenance: SessionEnvironmentLocalCLI, Cwd: "/tmp/" + string([]byte{0xff})}, wantErr: true},
45		{name: "cwd NUL", env: SessionEnvironment{Provenance: SessionEnvironmentLocalCLI, Cwd: "/tmp/\x00x"}, wantErr: true},
46		{name: "cwd too long", env: SessionEnvironment{Provenance: SessionEnvironmentLocalCLI, Cwd: longCwd}, wantErr: true},
47		{name: "remote empty valid", env: SessionEnvironment{Provenance: SessionEnvironmentRemote}, wantErr: false},
48		{name: "remote with env", env: SessionEnvironment{Provenance: SessionEnvironmentRemote, Env: []string{"A=b"}}, wantErr: true},
49		{name: "remote with cwd", env: SessionEnvironment{Provenance: SessionEnvironmentRemote, Cwd: "/tmp"}, wantErr: true},
50	}
51
52	for _, tc := range tests {
53		t.Run(tc.name, func(t *testing.T) {
54			err := tc.env.Validate()
55			if tc.wantErr {
56				require.Error(t, err)
57			} else {
58				require.NoError(t, err)
59			}
60		})
61	}
62}
63
64func TestSessionEnvironmentClone(t *testing.T) {
65	t.Run("nil stays nil", func(t *testing.T) {
66		orig := SessionEnvironment{Provenance: SessionEnvironmentLocalCLI}
67		require.Nil(t, orig.Env)
68		cloned := orig.Clone()
69		require.Nil(t, cloned.Env)
70		require.Equal(t, orig, cloned)
71	})
72
73	t.Run("empty stays non-nil empty", func(t *testing.T) {
74		orig := SessionEnvironment{Provenance: SessionEnvironmentLocalCLI, Env: []string{}}
75		require.NotNil(t, orig.Env)
76		cloned := orig.Clone()
77		require.NotNil(t, cloned.Env)
78		require.Empty(t, cloned.Env)
79	})
80
81	t.Run("deep copy isolates", func(t *testing.T) {
82		orig := SessionEnvironment{
83			Provenance: SessionEnvironmentLocalPicker,
84			Env:        []string{"A=1", "B=2"},
85			Cwd:        "/tmp",
86		}
87		cloned := orig.Clone()
88		require.Equal(t, orig, cloned)
89		cloned.Env[0] = "MUTATED=x"
90		require.Equal(t, "A=1", orig.Env[0])
91		orig.Env[1] = "MUTATED=y"
92		require.Equal(t, "B=2", cloned.Env[1])
93	})
94}

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