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}