github.com/splunk/dan1-qbec@v0.7.3/internal/vm/vm_test.go (about) 1 /* 2 Copyright 2019 Splunk Inc. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package vm 18 19 import ( 20 "encoding/json" 21 "os" 22 "testing" 23 24 "github.com/spf13/cobra" 25 "github.com/stretchr/testify/assert" 26 "github.com/stretchr/testify/require" 27 ) 28 29 type code struct { 30 Foo string `json:"foo"` 31 Bar string `json:"bar"` 32 } 33 34 type result struct { 35 TLAStr string `json:"tlaStr"` 36 TLACode bool `json:"tlaCode"` 37 ExtStr string `json:"extStr"` 38 ExtCode code `json:"extCode"` 39 LibPath1 code `json:"libpath1"` 40 LibPath2 code `json:"libpath2"` 41 InlineStr string `json:"inlineStr"` 42 InlineCode bool `json:"inlineCode"` 43 ListVar1 string `json:"listVar1"` 44 ListVar2 string `json:"listVar2"` 45 } 46 47 var evalCode = ` 48 function (tlaStr,tlaCode) { 49 tlaStr: tlaStr, 50 tlaCode: tlaCode, 51 extStr: std.extVar('extStr'), 52 extCode: std.extVar('extCode'), 53 libPath1: import 'libcode1.libsonnet', 54 libPath2: import 'libcode2.libsonnet', 55 inlineStr: std.extVar('inlineStr'), 56 inlineCode: std.extVar('inlineCode'), 57 listVar1: std.extVar('listVar1'), 58 listVar2: std.extVar('listVar2'), 59 } 60 ` 61 62 func TestVMScratchConfig(t *testing.T) { 63 a := assert.New(t) 64 c := Config{} 65 a.NotNil(c.Vars()) 66 a.NotNil(c.CodeVars()) 67 a.NotNil(c.TopLevelVars()) 68 a.NotNil(c.TopLevelCodeVars()) 69 70 tla, tlacode, extStr, extCode, paths := map[string]string{"tla-foo": "bar", "tls-bar": "baz"}, 71 map[string]string{"tla-code-foo": "100", "tla-code-bar": "true"}, 72 map[string]string{"ext-foo": "bar"}, 73 map[string]string{"ext-code-foo": "true"}, 74 []string{"lib"} 75 76 c = c.WithVars(extStr). 77 WithCodeVars(extCode). 78 WithTopLevelVars(tla). 79 WithTopLevelCodeVars(tlacode). 80 WithLibPaths(paths). 81 WithImporter(nil) 82 83 a.EqualValues(extStr, c.Vars()) 84 a.EqualValues(extCode, c.CodeVars()) 85 a.EqualValues(tla, c.TopLevelVars()) 86 a.EqualValues(tlacode, c.TopLevelCodeVars()) 87 a.EqualValues(paths, c.LibPaths()) 88 a.True(c.HasTopLevelVar("tla-foo")) 89 a.True(c.HasTopLevelVar("tla-code-foo")) 90 a.False(c.HasTopLevelVar("ext-foo")) 91 a.False(c.HasTopLevelVar("ext-code-foo")) 92 93 a.False(c.HasVar("tla-foo")) 94 a.False(c.HasVar("tla-code-foo")) 95 a.True(c.HasVar("ext-foo")) 96 a.True(c.HasVar("ext-code-foo")) 97 98 c = c.WithoutTopLevel() 99 a.False(c.HasTopLevelVar("tla-foo")) 100 a.False(c.HasTopLevelVar("tla-code-foo")) 101 } 102 103 func TestVMNoopConfig(t *testing.T) { 104 c := Config{} 105 newC := c.WithoutTopLevel().WithLibPaths(nil).WithVars(nil).WithCodeVars(map[string]string{}). 106 WithTopLevelVars(nil).WithTopLevelCodeVars(nil) 107 assert.Equal(t, &newC, &c) 108 } 109 110 func TestVMConfig(t *testing.T) { 111 var fn func() (Config, error) 112 var cfg Config 113 var output string 114 cmd := &cobra.Command{ 115 Use: "show", 116 RunE: func(c *cobra.Command, args []string) error { 117 var err error 118 cfg, err = fn() 119 if err != nil { 120 return err 121 } 122 baseVM := New(cfg) 123 cfg = baseVM.Config().WithLibPaths([]string{"testdata/lib2"}). 124 WithVars(map[string]string{"inlineStr": "ifoo"}). 125 WithCodeVars(map[string]string{"inlineCode": "true"}) 126 jvm := New(cfg) 127 output, err = jvm.EvaluateSnippet("test.jsonnet", evalCode) 128 return err 129 }, 130 } 131 cmd.SilenceUsage = true 132 cmd.SilenceErrors = true 133 fn = ConfigFromCommandParams(cmd, "vm:", false) 134 cmd.SetArgs([]string{ 135 "show", 136 "--vm:ext-str=extStr", 137 "--vm:ext-code-file=extCode=testdata/extCode.libsonnet", 138 "--vm:tla-str=tlaStr=tlafoo", 139 "--vm:tla-code=tlaCode=true", 140 "--vm:jpath=testdata/lib1", 141 "--vm:ext-str-list=testdata/vars.txt", 142 }) 143 os.Setenv("extStr", "envFoo") 144 defer os.Unsetenv("extStr") 145 os.Setenv("listVar2", "l2") 146 defer os.Unsetenv("listVar2") 147 err := cmd.Execute() 148 require.Nil(t, err) 149 var r result 150 err = json.Unmarshal([]byte(output), &r) 151 require.Nil(t, err) 152 assert.EqualValues(t, result{ 153 TLAStr: "tlafoo", 154 TLACode: true, 155 ExtStr: "envFoo", 156 ExtCode: code{Foo: "ec1foo", Bar: "ec1bar"}, 157 LibPath1: code{Foo: "lc1foo", Bar: "lc1bar"}, 158 LibPath2: code{Foo: "lc2foo", Bar: "lc2bar"}, 159 InlineStr: "ifoo", 160 InlineCode: true, 161 ListVar1: "l1", 162 ListVar2: "l2", 163 }, r) 164 } 165 166 func TestVMShorthandConfig(t *testing.T) { 167 var fn func() (Config, error) 168 var cfg Config 169 var output string 170 cmd := &cobra.Command{ 171 Use: "show", 172 RunE: func(c *cobra.Command, args []string) error { 173 var err error 174 cfg, err = fn() 175 if err != nil { 176 return err 177 } 178 baseVM := New(cfg) 179 cfg = baseVM.Config().WithLibPaths([]string{"testdata/lib2"}). 180 WithVars(map[string]string{"inlineStr": "ifoo"}). 181 WithCodeVars(map[string]string{"inlineCode": "true"}) 182 jvm := New(cfg) 183 output, err = jvm.EvaluateSnippet("test.jsonnet", evalCode) 184 return err 185 }, 186 } 187 cmd.SilenceUsage = true 188 cmd.SilenceErrors = true 189 fn = ConfigFromCommandParams(cmd, "vm:", true) 190 cmd.SetArgs([]string{ 191 "show", 192 "-V", 193 "extStr", 194 "--vm:ext-code-file=extCode=testdata/extCode.libsonnet", 195 "-A", 196 "tlaStr=tlafoo", 197 "--vm:tla-code=tlaCode=true", 198 "--vm:jpath=testdata/lib1", 199 "--vm:ext-str-list=testdata/vars.txt", 200 }) 201 os.Setenv("extStr", "envFoo") 202 defer os.Unsetenv("extStr") 203 os.Setenv("listVar2", "l2") 204 defer os.Unsetenv("listVar2") 205 err := cmd.Execute() 206 require.Nil(t, err) 207 var r result 208 err = json.Unmarshal([]byte(output), &r) 209 require.Nil(t, err) 210 assert.EqualValues(t, result{ 211 TLAStr: "tlafoo", 212 TLACode: true, 213 ExtStr: "envFoo", 214 ExtCode: code{Foo: "ec1foo", Bar: "ec1bar"}, 215 LibPath1: code{Foo: "lc1foo", Bar: "lc1bar"}, 216 LibPath2: code{Foo: "lc2foo", Bar: "lc2bar"}, 217 InlineStr: "ifoo", 218 InlineCode: true, 219 ListVar1: "l1", 220 ListVar2: "l2", 221 }, r) 222 } 223 224 func TestVMNegative(t *testing.T) { 225 execInVM := func(code string, args []string) error { 226 var fn func() (Config, error) 227 cmd := &cobra.Command{ 228 Use: "show", 229 RunE: func(c *cobra.Command, args []string) error { 230 var err error 231 cfg, err := fn() 232 if err != nil { 233 return err 234 } 235 jvm := New(cfg) 236 if code == "" { 237 code = "{}" 238 } 239 _, err = jvm.EvaluateSnippet("test.jsonnet", code) 240 return err 241 }, 242 } 243 fn = ConfigFromCommandParams(cmd, "vm:", false) 244 cmd.SetArgs(args) 245 cmd.SilenceUsage = true 246 cmd.SilenceErrors = true 247 return cmd.Execute() 248 } 249 tests := []struct { 250 name string 251 code string 252 args []string 253 asserter func(a *assert.Assertions, err error) 254 }{ 255 { 256 name: "ext-str-undef", 257 args: []string{"show", "--vm:ext-str=undef_foo"}, 258 asserter: func(a *assert.Assertions, err error) { 259 require.NotNil(t, err) 260 a.Contains(err.Error(), "no value found from environment for undef_foo") 261 }, 262 }, 263 { 264 name: "ext-code-undef", 265 args: []string{"show", "--vm:ext-code=undef_foo"}, 266 asserter: func(a *assert.Assertions, err error) { 267 require.NotNil(t, err) 268 a.Contains(err.Error(), "no value found from environment for undef_foo") 269 }, 270 }, 271 { 272 name: "tla-str-undef", 273 args: []string{"show", "--vm:tla-str=undef_foo"}, 274 asserter: func(a *assert.Assertions, err error) { 275 require.NotNil(t, err) 276 a.Contains(err.Error(), "no value found from environment for undef_foo") 277 }, 278 }, 279 { 280 name: "tla-code-undef", 281 args: []string{"show", "--vm:tla-code=undef_foo"}, 282 asserter: func(a *assert.Assertions, err error) { 283 require.NotNil(t, err) 284 a.Contains(err.Error(), "no value found from environment for undef_foo") 285 }, 286 }, 287 { 288 name: "ext-file-undef", 289 args: []string{"show", "--vm:ext-str-file=foo"}, 290 asserter: func(a *assert.Assertions, err error) { 291 require.NotNil(t, err) 292 a.Contains(err.Error(), "ext-str-file no filename specified for foo") 293 }, 294 }, 295 { 296 name: "tla-file-undef", 297 args: []string{"show", "--vm:tla-str-file=foo=bar"}, 298 asserter: func(a *assert.Assertions, err error) { 299 require.NotNil(t, err) 300 a.Contains(err.Error(), "open bar: no such file or directory") 301 }, 302 }, 303 { 304 name: "shorthand-not-enabled", 305 args: []string{"show", "-A extStr"}, 306 asserter: func(a *assert.Assertions, err error) { 307 require.NotNil(t, err) 308 a.Contains(err.Error(), "unknown shorthand flag: 'A'") 309 }, 310 }, 311 { 312 name: "ext-list-bad-file", 313 args: []string{"show", "--vm:ext-str-list=no-such-file"}, 314 asserter: func(a *assert.Assertions, err error) { 315 require.NotNil(t, err) 316 a.Contains(err.Error(), "no such file or directory") 317 }, 318 }, 319 { 320 name: "ext-list-bad-file", 321 args: []string{"show", "--vm:ext-str-list=testdata/vars.txt"}, 322 asserter: func(a *assert.Assertions, err error) { 323 require.NotNil(t, err) 324 a.Contains(err.Error(), "process list testdata/vars.txt, line 3: no value found from environment for listVar2") 325 }, 326 }, 327 } 328 for _, test := range tests { 329 t.Run(test.name, func(t *testing.T) { 330 a := assert.New(t) 331 err := execInVM(test.code, test.args) 332 test.asserter(a, err) 333 }) 334 } 335 } 336 337 func TestVMFromScratch(t *testing.T) { 338 cfg := Config{}. 339 WithVars(map[string]string{"foo": "bar"}). 340 WithCodeVars(map[string]string{"bar": "true"}) 341 jvm := New(cfg) 342 out, err := jvm.EvaluateSnippet("test.jsonnet", `std.extVar('foo') + std.toString(std.extVar('bar'))`) 343 require.Nil(t, err) 344 assert.Equal(t, `"bartrue"`+"\n", out) 345 }