github.com/ActiveState/cli@v0.0.0-20240508170324-6801f60cd051/pkg/platform/runtime/envdef/environment_test.go (about) 1 package envdef_test 2 3 import ( 4 "encoding/json" 5 "os" 6 "path/filepath" 7 "strings" 8 "testing" 9 10 "github.com/ActiveState/cli/internal/testhelpers/suite" 11 "github.com/stretchr/testify/require" 12 13 "github.com/ActiveState/cli/internal/fileutils" 14 "github.com/ActiveState/cli/pkg/platform/runtime/envdef" 15 ) 16 17 type EnvironmentTestSuite struct { 18 suite.Suite 19 } 20 21 func (suite *EnvironmentTestSuite) TestMergeVariables() { 22 23 ev1 := envdef.EnvironmentVariable{} 24 err := json.Unmarshal([]byte(`{ 25 "env_name": "V", 26 "values": ["a", "b"] 27 }`), &ev1) 28 require.NoError(suite.T(), err) 29 ev2 := envdef.EnvironmentVariable{} 30 err = json.Unmarshal([]byte(`{ 31 "env_name": "V", 32 "values": ["b", "c"] 33 }`), &ev2) 34 require.NoError(suite.T(), err) 35 36 expected := &envdef.EnvironmentVariable{} 37 err = json.Unmarshal([]byte(`{ 38 "env_name": "V", 39 "values": ["b", "c", "a"], 40 "join": "prepend" 41 }`), expected) 42 require.NoError(suite.T(), err) 43 44 suite.Assert().True(expected.Inherit, "inherit should be true") 45 suite.Assert().Equal(":", expected.Separator) 46 47 res, err := ev1.Merge(ev2) 48 suite.Assert().NoError(err) 49 suite.Assert().Equal(expected, res) 50 } 51 52 func (suite *EnvironmentTestSuite) TestMerge() { 53 ed1 := &envdef.EnvironmentDefinition{} 54 55 err := json.Unmarshal([]byte(`{ 56 "env": [{"env_name": "V", "values": ["a", "b"]}], 57 "installdir": "abc" 58 }`), ed1) 59 require.NoError(suite.T(), err) 60 61 ed2 := envdef.EnvironmentDefinition{} 62 err = json.Unmarshal([]byte(`{ 63 "env": [{"env_name": "V", "values": ["c", "d"]}], 64 "installdir": "abc" 65 }`), &ed2) 66 require.NoError(suite.T(), err) 67 68 expected := envdef.EnvironmentDefinition{} 69 err = json.Unmarshal([]byte(`{ 70 "env": [{"env_name": "V", "values": ["c", "d", "a", "b"]}], 71 "installdir": "abc" 72 }`), &expected) 73 require.NoError(suite.T(), err) 74 75 ed1, err = ed1.Merge(&ed2) 76 suite.Assert().NoError(err) 77 require.NotNil(suite.T(), ed1) 78 suite.Assert().Equal(expected, *ed1) 79 } 80 81 func (suite *EnvironmentTestSuite) TestInheritPath() { 82 ed1 := &envdef.EnvironmentDefinition{} 83 84 err := json.Unmarshal([]byte(`{ 85 "env": [{"env_name": "PATH", "values": ["NEWVALUE"]}], 86 "join": "prepend", 87 "inherit": true, 88 "separator": ":" 89 }`), ed1) 90 require.NoError(suite.T(), err) 91 92 env, err := ed1.GetEnvBasedOn(func(k string) (string, bool) { 93 return "OLDVALUE", true 94 }) 95 require.NoError(suite.T(), err) 96 suite.True(strings.HasPrefix(env["PATH"], "NEWVALUE"), "%s does not start with NEWVALUE", env["PATH"]) 97 suite.True(strings.HasSuffix(env["PATH"], "OLDVALUE"), "%s does not end with OLDVALUE", env["PATH"]) 98 } 99 100 func (suite *EnvironmentTestSuite) TestSharedTests() { 101 102 type testCase struct { 103 Name string `json:"name"` 104 Definitions []envdef.EnvironmentDefinition `json:"definitions"` 105 BaseEnv map[string]string `json:"base_env"` 106 Expected map[string]string `json:"result"` 107 IsError bool `json:"error"` 108 } 109 110 td, err := os.ReadFile("runtime_test_cases.json") 111 require.NoError(suite.T(), err) 112 113 cases := &[]testCase{} 114 115 err = json.Unmarshal(td, cases) 116 require.NoError(suite.T(), err, "unmarshal the test cases") 117 118 for _, tc := range *cases { 119 suite.Run(tc.Name, func() { 120 ed := &tc.Definitions[0] 121 for i, med := range tc.Definitions[1:] { 122 ed, err = ed.Merge(&med) 123 if tc.IsError { 124 suite.Assert().Error(err) 125 return 126 } 127 suite.Assert().NoError(err, "error merging %d-th definition", i) 128 } 129 130 lookupEnv := func(k string) (string, bool) { 131 res, ok := tc.BaseEnv[k] 132 return res, ok 133 } 134 135 res, err := ed.GetEnvBasedOn(lookupEnv) 136 if tc.IsError { 137 suite.Assert().Error(err) 138 return 139 } 140 suite.Assert().NoError(err) 141 suite.Assert().Equal(tc.Expected, res) 142 }) 143 } 144 145 } 146 147 func (suite *EnvironmentTestSuite) TestValueString() { 148 ev1 := envdef.EnvironmentVariable{} 149 err := json.Unmarshal([]byte(`{ 150 "env_name": "V", 151 "values": ["a", "b"] 152 }`), &ev1) 153 require.NoError(suite.T(), err) 154 155 res := ev1.ValueString() 156 suite.Assert().Equal("a:b", res) 157 } 158 159 func (suite *EnvironmentTestSuite) TestGetEnv() { 160 ed1 := envdef.EnvironmentDefinition{} 161 err := json.Unmarshal([]byte(`{ 162 "env": [{"env_name": "V", "values": ["a", "b"]}], 163 "installdir": "abc" 164 }`), &ed1) 165 require.NoError(suite.T(), err) 166 167 res := ed1.GetEnv(true) 168 suite.Assert().Equal(map[string]string{ 169 "V": "a:b", 170 }, res) 171 } 172 173 func (suite *EnvironmentTestSuite) TestFindBinPathFor() { 174 tmpDir, err := os.MkdirTemp("", "") 175 require.NoError(suite.T(), err, "creating temporary directory") 176 defer os.RemoveAll(tmpDir) 177 178 ed1 := envdef.EnvironmentDefinition{} 179 err = json.Unmarshal([]byte(`{ 180 "env": [{"env_name": "PATH", "values": ["${INSTALLDIR}/bin", "${INSTALLDIR}/bin2"]}], 181 "installdir": "abc" 182 }`), &ed1) 183 require.NoError(suite.T(), err, "un-marshaling test json blob") 184 185 tmpDir, err = fileutils.GetLongPathName(tmpDir) 186 require.NoError(suite.T(), err) 187 188 constants, err := envdef.NewConstants(tmpDir) 189 require.NoError(suite.T(), err) 190 // expand variables 191 ed1.ExpandVariables(constants) 192 193 suite.Assert().Equal("", ed1.FindBinPathFor("executable"), "executable should not exist") 194 195 err = fileutils.Touch(filepath.Join(tmpDir, "bin2", "executable")) 196 require.NoError(suite.T(), err, "creating dummy file") 197 suite.Assert().Equal(filepath.Join(tmpDir, "bin2"), ed1.FindBinPathFor("executable"), "executable should be found") 198 } 199 200 func TestEnvironmentTestSuite(t *testing.T) { 201 suite.Run(t, new(EnvironmentTestSuite)) 202 } 203 204 func TestFilterPATH(t *testing.T) { 205 s := string(os.PathListSeparator) 206 type args struct { 207 env map[string]string 208 excludes []string 209 } 210 tests := []struct { 211 name string 212 args args 213 want string 214 }{ 215 { 216 "Filters out matching path", 217 args{ 218 map[string]string{"PATH": "/path/to/key1" + s + "/path/to/key2" + s + "/path/to/key3"}, 219 []string{"/path/to/key2"}, 220 }, 221 "/path/to/key1" + s + "/path/to/key3", 222 }, 223 { 224 "Filters out matching path despite malformed paths", 225 args{ 226 map[string]string{"PATH": "/path/to/key1" + s + "/path//to/key2" + s + "/path/to/key3"}, 227 []string{"/path/to//key2"}, 228 }, 229 "/path/to/key1" + s + "/path/to/key3", 230 }, 231 { 232 "Preserve original version of PATH, even if it's malformed", 233 args{ 234 map[string]string{"PATH": "/path//to/key1" + s + "/path//to/key2" + s + "/path/to//key3"}, 235 []string{"/path/to//key2"}, 236 }, 237 "/path//to/key1" + s + "/path/to//key3", 238 }, 239 { 240 "Does not filter any paths", 241 args{ 242 map[string]string{"PATH": "/path/to/key1"}, 243 []string{"/path/to/key2", "/path/to/key3"}, 244 }, 245 "/path/to/key1", 246 }, 247 } 248 for _, tt := range tests { 249 t.Run(tt.name, func(t *testing.T) { 250 envdef.FilterPATH(tt.args.env, tt.args.excludes...) 251 require.Equal(t, tt.want, tt.args.env["PATH"]) 252 }) 253 } 254 }