github.com/splunk/qbec@v0.15.2/vm/internal/natives/nativefuncs_test.go (about) 1 // Copyright 2017 The kubecfg authors 2 // 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 package natives 17 18 import ( 19 "encoding/json" 20 "fmt" 21 "regexp" 22 "testing" 23 24 "github.com/google/go-jsonnet" 25 ) 26 27 // copied from original code at https://github.com/ksonnet/kubecfg/blob/master/utils/nativefuncs_test.go 28 // and modified for use. 29 30 // check there is no err, and a == b. 31 func check(t *testing.T, err error, actual, expected string) { 32 if err != nil { 33 t.Errorf("Expected %q, got error: %q", expected, err.Error()) 34 } else if actual != expected { 35 t.Errorf("Expected %q, got %q", expected, actual) 36 } 37 } 38 39 func TestParseJson(t *testing.T) { 40 vm := jsonnet.MakeVM() 41 Register(vm) 42 43 _, err := vm.EvaluateAnonymousSnippet("failtest", `std.native("parseJson")("barf{")`) 44 if err == nil { 45 t.Errorf("parseJson succeeded on invalid json") 46 } 47 48 x, err := vm.EvaluateAnonymousSnippet("test", `std.native("parseJson")("null")`) 49 check(t, err, x, "null\n") 50 51 x, err = vm.EvaluateAnonymousSnippet("test", ` 52 local a = std.native("parseJson")('{"foo": 3, "bar": 4}'); 53 a.foo + a.bar`) 54 check(t, err, x, "7\n") 55 } 56 57 func TestParseYaml(t *testing.T) { 58 vm := jsonnet.MakeVM() 59 Register(vm) 60 61 _, err := vm.EvaluateAnonymousSnippet("failtest", `std.native("parseYaml")("[barf")`) 62 if err == nil { 63 t.Errorf("parseYaml succeeded on invalid yaml") 64 } 65 66 x, err := vm.EvaluateAnonymousSnippet("test", `std.native("parseYaml")("")`) 67 check(t, err, x, "[ ]\n") 68 69 x, err = vm.EvaluateAnonymousSnippet("test", ` 70 local a = std.native("parseYaml")("foo:\n- 3\n- 4\n")[0]; 71 a.foo[0] + a.foo[1]`) 72 check(t, err, x, "7\n") 73 74 x, err = vm.EvaluateAnonymousSnippet("test", ` 75 local a = std.native("parseYaml")("---\nhello\n---\nworld"); 76 a[0] + a[1]`) 77 check(t, err, x, "\"helloworld\"\n") 78 } 79 80 func TestRenderYaml(t *testing.T) { 81 data := `{ 82 "a": 1, 83 "b": true, 84 "c": "foo" 85 }` 86 vm := jsonnet.MakeVM() 87 Register(vm) 88 vm.ExtCode("data", data) 89 x, err := vm.EvaluateAnonymousSnippet("test", ` 90 local renderYaml = std.native('renderYaml'); 91 local parseYaml = std.native('parseYaml'); 92 parseYaml(renderYaml(std.extVar('data')))[0] 93 `) 94 if err != nil { 95 t.Fatalf("unexpected error: %q", err) 96 } 97 var expected, actual map[string]interface{} 98 err = json.Unmarshal([]byte(data), &expected) 99 if err != nil { 100 t.Fatalf("unexpected error: %q", err) 101 } 102 err = json.Unmarshal([]byte(x), &actual) 103 if err != nil { 104 t.Fatalf("unexpected error: %q", err) 105 } 106 107 for k, v := range expected { 108 if actual[k] != v { 109 t.Errorf("mismatch, want %v got %v", v, actual[k]) 110 } 111 } 112 } 113 114 func TestRenderYamlArray(t *testing.T) { 115 data := `[ 116 { "foo": "bar" }, 117 { "foo": "baz" } 118 ]` 119 vm := jsonnet.MakeVM() 120 Register(vm) 121 vm.ExtCode("data", data) 122 x, err := vm.EvaluateAnonymousSnippet("test", ` 123 local renderYaml = std.native('renderYaml'); 124 local parseYaml = std.native('parseYaml'); 125 parseYaml(renderYaml(std.extVar('data'))) 126 `) 127 if err != nil { 128 t.Fatalf("unexpected error: %q", err) 129 } 130 var expected, actual []interface{} 131 err = json.Unmarshal([]byte(data), &expected) 132 if err != nil { 133 t.Fatalf("unexpected error: %q", err) 134 } 135 err = json.Unmarshal([]byte(x), &actual) 136 if err != nil { 137 t.Fatalf("unexpected error: %q", err) 138 } 139 140 for k, v := range expected { 141 e := v.(map[string]interface{}) 142 a := actual[k].(map[string]interface{}) 143 for k2, v2 := range e { 144 if a[k2] != v2 { 145 t.Errorf("mismatch, want %v got %v", v2, a[k2]) 146 } 147 } 148 } 149 } 150 151 func TestRegexMatch(t *testing.T) { 152 vm := jsonnet.MakeVM() 153 Register(vm) 154 155 _, err := vm.EvaluateAnonymousSnippet("failtest", `std.native("regexMatch")("[f", "foo")`) 156 if err == nil { 157 t.Errorf("regexMatch succeeded with invalid regex") 158 } 159 160 x, err := vm.EvaluateAnonymousSnippet("test", `std.native("regexMatch")("foo.*", "seafood")`) 161 check(t, err, x, "true\n") 162 163 x, err = vm.EvaluateAnonymousSnippet("test", `std.native("regexMatch")("bar.*", "seafood")`) 164 check(t, err, x, "false\n") 165 } 166 167 func TestRegexSubst(t *testing.T) { 168 vm := jsonnet.MakeVM() 169 Register(vm) 170 171 _, err := vm.EvaluateAnonymousSnippet("failtest", `std.native("regexSubst")("[f", "foo", "bar")`) 172 if err == nil { 173 t.Errorf("regexSubst succeeded with invalid regex") 174 } 175 176 x, err := vm.EvaluateAnonymousSnippet("test", `std.native("regexSubst")("a(x*)b", "-ab-axxb-", "T")`) 177 check(t, err, x, "\"-T-T-\"\n") 178 179 x, err = vm.EvaluateAnonymousSnippet("test", `std.native("regexSubst")("a(x*)b", "-ab-axxb-", "${1}W")`) 180 check(t, err, x, "\"-W-xxW-\"\n") 181 } 182 183 func TestRegexQuoteMeta(t *testing.T) { 184 vm := jsonnet.MakeVM() 185 Register(vm) 186 x, err := vm.EvaluateAnonymousSnippet("test", `std.native("escapeStringRegex")("[f]")`) 187 check(t, err, x, `"\\[f\\]"`+"\n") 188 } 189 190 func TestLabelSelectorMatch(t *testing.T) { 191 vm := jsonnet.MakeVM() 192 Register(vm) 193 tests := []struct { 194 name string 195 selector string 196 expected string 197 }{ 198 { 199 name: "presence", 200 selector: "env", 201 expected: "yes", 202 }, 203 { 204 name: "absence", 205 selector: "!env", 206 expected: "no", 207 }, 208 { 209 name: "and-presence", 210 selector: "env,region", 211 expected: "yes", 212 }, 213 { 214 name: "no-presence", 215 selector: "foo", 216 expected: "no", 217 }, 218 { 219 name: "equality", 220 selector: "region=us-west", 221 expected: "yes", 222 }, 223 { 224 name: "equality-no-match", 225 selector: "env=prod", 226 expected: "no", 227 }, 228 { 229 name: "and", 230 selector: "region=us-west,env=dev", 231 expected: "yes", 232 }, 233 { 234 name: "and-no-match", 235 selector: "region=us-west,!env", 236 expected: "no", 237 }, 238 { 239 name: "in", 240 selector: "env in (prod, dev)", 241 expected: "yes", 242 }, 243 { 244 name: "not-in", 245 selector: "env notin (prod, dev)", 246 expected: "no", 247 }, 248 } 249 250 for _, test := range tests { 251 t.Run(test.name, func(t *testing.T) { 252 code := fmt.Sprintf(` 253 local labels = { env: 'dev', region: 'us-west' }; 254 if std.native('labelsMatchSelector')(labels, '%s') then 'yes' else 'no' 255 `, test.selector) 256 ret, err := vm.EvaluateAnonymousSnippet("test.jsonnet", code) 257 check(t, err, ret, fmt.Sprintf(`"%s"`+"\n", test.expected)) 258 }) 259 } 260 } 261 262 func TestLabelSelectorNegative(t *testing.T) { 263 vm := jsonnet.MakeVM() 264 Register(vm) 265 tests := []struct { 266 name string 267 code string 268 errMatch *regexp.Regexp 269 }{ 270 { 271 name: "bad map", 272 code: `std.native('labelsMatchSelector')([],'foo')`, 273 errMatch: regexp.MustCompile(`invalid labels type, \[\]interface {}, want a map`), 274 }, 275 { 276 name: "non-string map", 277 code: `std.native('labelsMatchSelector')({ foo: {} },'foo')`, 278 errMatch: regexp.MustCompile(`invalid label map value, map\[string\]interface {}, want a string`), 279 }, 280 { 281 name: "bad selector type", 282 code: `std.native('labelsMatchSelector')({},{})`, 283 errMatch: regexp.MustCompile(`invalid selector of type map\[string\]interface {}, want a string`), 284 }, 285 { 286 name: "bad selector", 287 code: `std.native('labelsMatchSelector')({},'!!env')`, 288 errMatch: regexp.MustCompile(`invalid label selector: '!!env'`), 289 }, 290 } 291 for _, test := range tests { 292 t.Run(test.name, func(t *testing.T) { 293 _, err := vm.EvaluateAnonymousSnippet("test.jsonnet", test.code) 294 if err == nil { 295 t.Errorf("labelsMatchSelector succeeded on invalid input") 296 } 297 if !test.errMatch.MatchString(err.Error()) { 298 t.Errorf("message %q does not match %v", err.Error(), test.errMatch) 299 } 300 }) 301 } 302 303 }