github.com/blend/go-sdk@v1.20220411.3/selector/parse_test.go (about) 1 /* 2 3 Copyright (c) 2022 - Present. Blend Labs, Inc. All rights reserved 4 Use of this source code is governed by a MIT license that can be found in the LICENSE file. 5 6 */ 7 8 package selector 9 10 import ( 11 "testing" 12 13 "github.com/blend/go-sdk/assert" 14 "github.com/blend/go-sdk/ex" 15 ) 16 17 func TestMustParse(t *testing.T) { 18 assert := assert.New(t) 19 20 assert.Equal("x == a", MustParse("x==a").String()) 21 22 var err error 23 func() { 24 defer func() { 25 if r := recover(); r != nil { 26 err = ex.New(r) 27 } 28 }() 29 MustParse("x!!") 30 }() 31 assert.NotNil(err) 32 } 33 34 func TestParseInvalid(t *testing.T) { 35 assert := assert.New(t) 36 37 testBadStrings := []string{ 38 "x=a||y=b", 39 "x==a==b", 40 "!x=a", 41 "x<a", 42 "x>1", 43 "x>1,z<5", 44 "x=", 45 "x= ", 46 "x=,z= ", 47 "x= ,z= ", 48 "foo == bar foo", 49 } 50 var err error 51 for _, str := range testBadStrings { 52 _, err = Parse(str) 53 assert.NotNil(err, str) 54 } 55 } 56 57 func TestParseSemiValid(t *testing.T) { 58 assert := assert.New(t) 59 60 testGoodStrings := []string{ 61 "", 62 "x=a,y=b,z=c", 63 "x!=a,y=b", 64 "x", 65 "!x", 66 "!x,y", 67 "!x,y,z", 68 "!x,y,!z", 69 "!x,y,z,a", 70 "!x,y,z,!a", 71 } 72 73 var err error 74 for _, str := range testGoodStrings { 75 _, err = Parse(str) 76 assert.Nil(err, str) 77 } 78 } 79 80 func TestParseTrailingComma(t *testing.T) { 81 assert := assert.New(t) 82 83 testBadStrings := []string{ 84 ",", 85 "x,", 86 "!x,", 87 "foo==bar,", 88 "foo!=bar,", 89 "foo in (bar,baz),", 90 "foo not in (bar,baz),", 91 } 92 93 var err error 94 for _, str := range testBadStrings { 95 _, err = Parse(str) 96 assert.NotNil(err, str) 97 } 98 } 99 100 func TestParseEquals(t *testing.T) { 101 assert := assert.New(t) 102 103 valid := Labels{ 104 "foo": "bar", 105 "moo": "lar", 106 } 107 invalid := Labels{ 108 "zoo": "mar", 109 "moo": "lar", 110 } 111 112 selector, err := Parse("foo == bar") 113 assert.Nil(err) 114 assert.True(selector.Matches(valid)) 115 assert.False(selector.Matches(invalid)) 116 } 117 118 func TestParseNotEquals(t *testing.T) { 119 assert := assert.New(t) 120 121 valid := Labels{ 122 "foo": "far", 123 "moo": "lar", 124 } 125 invalidPresent := Labels{ 126 "foo": "bar", 127 "moo": "lar", 128 } 129 invalidMissing := Labels{ 130 "zoo": "mar", 131 "moo": "lar", 132 } 133 134 selector, err := Parse("foo != bar") 135 assert.Nil(err) 136 assert.True(selector.Matches(valid)) 137 assert.True(selector.Matches(invalidMissing)) 138 assert.False(selector.Matches(invalidPresent)) 139 } 140 141 func TestParseIn(t *testing.T) { 142 assert := assert.New(t) 143 144 valid := Labels{ 145 "foo": "far", 146 "moo": "lar", 147 } 148 valid2 := Labels{ 149 "foo": "bar", 150 "moo": "lar", 151 } 152 invalid := Labels{ 153 "foo": "mar", 154 "moo": "lar", 155 } 156 invalidMissing := Labels{ 157 "zoo": "mar", 158 "moo": "lar", 159 } 160 161 selector, err := Parse("foo in (bar,far)") 162 assert.Nil(err) 163 assert.True(selector.Matches(valid), selector.String()) 164 assert.True(selector.Matches(valid2)) 165 assert.False(selector.Matches(invalidMissing)) 166 assert.False(selector.Matches(invalid), selector.String()) 167 } 168 169 func TestParseGroup(t *testing.T) { 170 assert := assert.New(t) 171 172 valid := Labels{ 173 "zoo": "mar", 174 "moo": "lar", 175 "thing": "map", 176 } 177 invalid := Labels{ 178 "zoo": "mar", 179 "moo": "something", 180 "thing": "map", 181 } 182 invalid2 := Labels{ 183 "zoo": "mar", 184 "moo": "lar", 185 "!thing": "map", 186 } 187 selector, err := Parse("zoo=mar, moo=lar, thing") 188 assert.Nil(err) 189 assert.True(selector.Matches(valid)) 190 assert.False(selector.Matches(invalid)) 191 assert.False(selector.Matches(invalid2)) 192 193 complicated, err := Parse("zoo in (mar,lar,dar),moo,!thingy") 194 assert.Nil(err) 195 assert.NotNil(complicated) 196 assert.True(complicated.Matches(valid)) 197 } 198 199 func TestParseGroupComplicated(t *testing.T) { 200 assert := assert.New(t) 201 valid := Labels{ 202 "zoo": "mar", 203 "moo": "lar", 204 "thing": "map", 205 } 206 complicated, err := Parse("zoo in (mar,lar,dar),moo,thing == map,!thingy") 207 assert.Nil(err) 208 assert.NotNil(complicated) 209 assert.True(complicated.Matches(valid)) 210 } 211 212 func TestParseDocsExample(t *testing.T) { 213 assert := assert.New(t) 214 sel, err := Parse("x in (foo,,baz),y,z notin ()") 215 assert.Nil(err) 216 assert.NotNil(sel) 217 } 218 219 func TestParseSubdomainKey(t *testing.T) { 220 221 assert := assert.New(t) 222 sel, err := Parse("example.com/failure-domain == primary") 223 assert.Nil(err) 224 assert.NotNil(sel) 225 assert.Equal("example.com/failure-domain == primary", sel.String()) 226 assert.True(sel.Matches(map[string]string{ 227 "bar": "foo", 228 "example.com/failure-domain": "primary", 229 "foo": "bar", 230 })) 231 } 232 233 func TestParseEqualsOperators(t *testing.T) { 234 assert := assert.New(t) 235 236 selector, err := Parse("notin=in") 237 assert.Nil(err) 238 239 typed, isTyped := selector.(Equals) 240 assert.True(isTyped) 241 assert.Equal("notin", typed.Key) 242 assert.Equal("in", typed.Value) 243 } 244 245 func TestParseValidate(t *testing.T) { 246 assert := assert.New(t) 247 248 _, err := Parse("zoo=bar") 249 assert.Nil(err) 250 251 _, err = Parse("_zoo=bar") 252 assert.NotNil(err) 253 254 _, err = Parse("_zoo=_bar") 255 assert.NotNil(err) 256 257 _, err = Parse("zoo=bar,foo=_mar") 258 assert.NotNil(err) 259 } 260 261 func TestParseRegressionCSVSymbols(t *testing.T) { 262 assert := assert.New(t) 263 264 sel, err := Parse("foo in (bar-bar, baz.baz, buzz_buzz), moo=boo") 265 assert.Nil(err, "regression is csv values can have '-', '.' and '_' in them") 266 assert.NotEmpty(sel.String()) 267 } 268 269 func TestParseRegressionCSVEarlyTermination(t *testing.T) { 270 assert := assert.New(t) 271 272 sel, err := Parse("foo in (bar,), moo=boo") 273 assert.Nil(err) 274 assert.NotEmpty(sel.String()) 275 } 276 277 func TestParseRegressionCSVMultipleSpaces(t *testing.T) { 278 assert := assert.New(t) 279 280 sel, err := Parse("foo in (bar, foo), moo=boo") 281 assert.Nil(err) 282 assert.NotEmpty(sel.String()) 283 } 284 285 func TestParseRegressionIn(t *testing.T) { 286 assert := assert.New(t) 287 288 _, err := Parse("foo in bar, buzz)") 289 assert.NotNil(err) 290 } 291 292 func TestParseMultiByte(t *testing.T) { 293 assert := assert.New(t) 294 295 selector, err := Parse("함=수,목=록") // number=number, number=rock 296 assert.Nil(err) 297 assert.NotNil(selector) 298 299 typed, isTyped := selector.(And) 300 assert.True(isTyped) 301 assert.Len(typed, 2) 302 } 303 304 func TestParseOptions(t *testing.T) { 305 assert := assert.New(t) 306 307 selQuery := "bar=foo@bar" 308 labels := Labels{ 309 "foo": "bar", 310 "bar": "foo@bar", 311 } 312 313 sel, err := Parse(selQuery) 314 assert.NotNil(err) 315 assert.Nil(sel) 316 317 sel, err = Parse(selQuery, SkipValidation) 318 assert.Nil(err) 319 assert.NotNil(sel) 320 321 assert.True(sel.Matches(labels)) 322 } 323 324 func BenchmarkParse(b *testing.B) { 325 valid := Labels{ 326 "zoo": "mar", 327 "moo": "lar", 328 "thing": "map", 329 } 330 331 for i := 0; i < b.N; i++ { 332 selector, err := Parse("zoo in (mar,lar,dar),moo,!thingy") 333 if err != nil { 334 b.Fail() 335 } 336 if !selector.Matches(valid) { 337 b.Fail() 338 } 339 } 340 } 341 342 func TestParse_FuzzRegressions(t *testing.T) { 343 assert := assert.New(t) 344 345 var sel Selector 346 var err error 347 testBadStrings := []string{ 348 "!0!0", 349 "0!=0,!", 350 } 351 for _, str := range testBadStrings { 352 _, err = Parse(str) 353 assert.NotNil(err, str, err) 354 } 355 356 testGoodStrings := []string{ 357 "0,!0", 358 "0 in (0), !0", 359 } 360 for _, str := range testGoodStrings { 361 sel, err = Parse(str) 362 assert.Nil(err) 363 _, err = Parse(sel.String()) 364 assert.Nil(err) 365 } 366 } 367 368 func TestParse_InRegression(t *testing.T) { 369 its := assert.New(t) 370 371 good0 := Labels{ 372 "role": "job", 373 "team": "internal-engineering", 374 } 375 good1 := Labels{ 376 "role": "job-worker", 377 "team": "internal-engineering", 378 } 379 bad0 := Labels{ 380 "role": "not-job", 381 } 382 383 sel, err := Parse("role in (job, job-worker),team=internal-engineering") 384 its.Nil(err) 385 386 its.True(sel.Matches(good0)) 387 its.True(sel.Matches(good1)) 388 its.False(sel.Matches(bad0)) 389 }