github.com/splunk/qbec@v0.15.2/vm/internal/importers/glob_test.go (about)

     1  /*
     2     Copyright 2021 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 importers
    18  
    19  import (
    20  	"encoding/json"
    21  	"fmt"
    22  	"io/ioutil"
    23  	"os"
    24  	"path/filepath"
    25  	"testing"
    26  
    27  	"github.com/google/go-jsonnet"
    28  	"github.com/stretchr/testify/assert"
    29  	"github.com/stretchr/testify/require"
    30  )
    31  
    32  func makeVM() (*jsonnet.VM, *GlobImporter) {
    33  	vm := jsonnet.MakeVM()
    34  	g1 := NewGlobImporter("import")
    35  	g2 := NewGlobImporter("importstr")
    36  	vm.Importer(
    37  		NewCompositeImporter(
    38  			g1,
    39  			g2,
    40  			NewFileImporter(&jsonnet.FileImporter{}),
    41  		),
    42  	)
    43  	return vm, g1
    44  }
    45  
    46  type outputData map[string]interface{}
    47  
    48  func generateFile(t *testing.T, virtFile string, code string) (outFile string) {
    49  	file := virtFile + ".generated"
    50  	err := ioutil.WriteFile(file, []byte(code), 0644)
    51  	require.NoError(t, err)
    52  	return file
    53  }
    54  
    55  func evaluateVirtual(t *testing.T, vm *jsonnet.VM, virtFile string, code string) outputData {
    56  	file := generateFile(t, virtFile, code)
    57  	defer os.Remove(file)
    58  	jsonStr, err := vm.EvaluateFile(file)
    59  	require.NoError(t, err)
    60  	t.Logf("input from '%s'\n%s\noutput:%s\n", virtFile, code, jsonStr)
    61  	var data outputData
    62  	err = json.Unmarshal([]byte(jsonStr), &data)
    63  	require.NoError(t, err)
    64  	return data
    65  }
    66  
    67  func evaluateVirtualErr(t *testing.T, virtFile string, code string) error {
    68  	file := generateFile(t, virtFile, code)
    69  	defer os.Remove(file)
    70  	vm, _ := makeVM()
    71  	_, err := vm.EvaluateFile(file)
    72  	require.Error(t, err)
    73  	return err
    74  }
    75  
    76  func TestGlobSimple(t *testing.T) {
    77  	vm, _ := makeVM()
    78  	data := evaluateVirtual(t, vm, "testdata/caller.jsonnet", `import 'glob-import:example1/*.json'`)
    79  	for _, k := range []string{"a", "b", "z"} {
    80  		relFile := fmt.Sprintf("example1/%s.json", k)
    81  		val, ok := data[relFile]
    82  		require.True(t, ok)
    83  		mVal, ok := val.(map[string]interface{})
    84  		require.True(t, ok)
    85  		_, ok = mVal[k]
    86  		assert.True(t, ok)
    87  	}
    88  }
    89  
    90  func TestGlobDoublestar(t *testing.T) {
    91  	vm, _ := makeVM()
    92  	data := evaluateVirtual(t, vm, "testdata/caller.jsonnet", `import 'glob-import:example2/**/*.json'`)
    93  	expectedJSON := `
    94  {
    95     "example2/inc1/a.json": {
    96  	  "a": "a"
    97     },
    98     "example2/inc1/subdir/a.json": {
    99  	  "a": "inner a"
   100     },
   101     "example2/inc2/a.json": {
   102  	  "a": "long form a"
   103     }
   104  }
   105  `
   106  	var expected interface{}
   107  	err := json.Unmarshal([]byte(expectedJSON), &expected)
   108  	require.Nil(t, err)
   109  	assert.EqualValues(t, expected, data)
   110  }
   111  
   112  func TestGlobOutsideRoot(t *testing.T) {
   113  	wd, err := os.Getwd()
   114  	require.NoError(t, err)
   115  	dir := filepath.FromSlash("testdata/example1/caller2")
   116  	err = os.Chdir(dir)
   117  	require.NoError(t, err)
   118  	defer func() {
   119  		err := os.Chdir(wd)
   120  		require.NoError(t, err)
   121  	}()
   122  	vm, _ := makeVM()
   123  	data := evaluateVirtual(t, vm, "caller.jsonnet", `import 'glob-import:../*json'`)
   124  	_, ok := data["../a.json"]
   125  	require.True(t, ok)
   126  }
   127  
   128  func TestDuplicateFileName(t *testing.T) {
   129  	vm, _ := makeVM()
   130  	data := evaluateVirtual(t, vm, "testdata/example2/caller.jsonnet", `import 'glob-import:inc?/*.json'`)
   131  	_, firstOk := data["inc1/a.json"]
   132  	require.True(t, firstOk)
   133  	_, secondOk := data["inc2/a.json"]
   134  	require.True(t, secondOk)
   135  }
   136  
   137  func TestGlobNoMatch(t *testing.T) {
   138  	vm, _ := makeVM()
   139  	data := evaluateVirtual(t, vm, "testdata/example1/caller/no-match.jsonnet", `import 'glob-import:*.json'`)
   140  	require.Equal(t, 0, len(data))
   141  }
   142  
   143  func TestGlobImportStr(t *testing.T) {
   144  	vm, _ := makeVM()
   145  	file := generateFile(t, "testdata/example1/caller/synthesized.jsonnet", `importstr 'glob-import:../*.json'`)
   146  	defer os.Remove(file)
   147  	data, err := vm.EvaluateFile(file)
   148  	require.NoError(t, err)
   149  	var str string
   150  	err = json.Unmarshal([]byte(data), &str)
   151  	require.NoError(t, err)
   152  	assert.Equal(t, `{
   153  	'../a.json': import '../a.json',
   154  	'../b.json': import '../b.json',
   155  	'../z.json': import '../z.json',
   156  }`, str)
   157  }
   158  
   159  func TestGlobImportStrVerb(t *testing.T) {
   160  	vm, _ := makeVM()
   161  	data := evaluateVirtual(t, vm, "testdata/example1/caller/synthesized.jsonnet", `import 'glob-importstr:../*.json'`)
   162  	for _, k := range []string{"a", "b", "z"} {
   163  		val, ok := data[fmt.Sprintf("../%s.json", k)]
   164  		require.True(t, ok)
   165  		mVal, ok := val.(string)
   166  		require.True(t, ok)
   167  		assert.Contains(t, mVal, fmt.Sprintf("%q", k))
   168  	}
   169  }
   170  
   171  func TestGlobInternalCaching(t *testing.T) {
   172  	a := assert.New(t)
   173  	vm, gi := makeVM()
   174  	_ = evaluateVirtual(t, vm, "testdata/example1/caller/synthesized.jsonnet", `import 'glob-import:../*.json'`)
   175  	a.Equal(1, len(gi.cache))
   176  	_ = evaluateVirtual(t, vm, "testdata/example1/caller/synthesized2.jsonnet", `import 'glob-import:../*.json'`)
   177  	a.Equal(1, len(gi.cache))
   178  	_ = evaluateVirtual(t, vm, "testdata/example1/caller2/synthesized.jsonnet", `import 'glob-import:../*.json'`)
   179  	a.Equal(1, len(gi.cache))
   180  	_ = evaluateVirtual(t, vm, "testdata/example1/caller/inner/synthesized.jsonnet", `import 'glob-import:../../*.json'`)
   181  	a.Equal(2, len(gi.cache))
   182  	_ = evaluateVirtual(t, vm, "testdata/example1/caller/inner/synthesized2.jsonnet", `import 'glob-import:../../[a,b].json'`)
   183  	a.Equal(3, len(gi.cache))
   184  }
   185  
   186  func TestGlobNegativeCases(t *testing.T) {
   187  	checkMsg := func(m string) func(t *testing.T, err error) {
   188  		return func(t *testing.T, err error) {
   189  			assert.Contains(t, err.Error(), m)
   190  		}
   191  	}
   192  	tests := []struct {
   193  		name     string
   194  		expr     string
   195  		asserter func(t *testing.T, err error)
   196  	}{
   197  		{
   198  			name:     "bad path",
   199  			expr:     `import 'glob-import:/bag-of-files/*.json'`,
   200  			asserter: checkMsg(`RUNTIME ERROR: invalid glob pattern '/bag-of-files/*.json', cannot be absolute`),
   201  		},
   202  		{
   203  			name: "bad pattern",
   204  			expr: `import 'glob-import:../[.json'`,
   205  			asserter: func(t *testing.T, err error) {
   206  				assert.Contains(t, err.Error(), `RUNTIME ERROR: unable to expand glob`)
   207  				assert.Contains(t, err.Error(), `[.json", syntax error in pattern`)
   208  			},
   209  		},
   210  	}
   211  	file := "testdata/example1/caller/synthesized.jsonnet"
   212  	for _, test := range tests {
   213  		t.Run(test.name, func(t *testing.T) {
   214  			test.asserter(t, evaluateVirtualErr(t, file, test.expr))
   215  		})
   216  	}
   217  }
   218  
   219  func TestGlobInit(t *testing.T) {
   220  	require.Panics(t, func() { _ = NewGlobImporter("foobar") })
   221  }