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  }