1package query 2 3import ( 4 "fmt" 5 "io/ioutil" 6 "sort" 7 "strings" 8 "testing" 9 "time" 10 11 "github.com/pelletier/go-toml" 12) 13 14type queryTestNode struct { 15 value interface{} 16 position toml.Position 17} 18 19func valueString(root interface{}) string { 20 result := "" //fmt.Sprintf("%T:", root) 21 switch node := root.(type) { 22 case *Result: 23 items := []string{} 24 for i, v := range node.Values() { 25 items = append(items, fmt.Sprintf("%s:%s", 26 node.Positions()[i].String(), valueString(v))) 27 } 28 sort.Strings(items) 29 result = "[" + strings.Join(items, ", ") + "]" 30 case queryTestNode: 31 result = fmt.Sprintf("%s:%s", 32 node.position.String(), valueString(node.value)) 33 case []interface{}: 34 items := []string{} 35 for _, v := range node { 36 items = append(items, valueString(v)) 37 } 38 sort.Strings(items) 39 result = "[" + strings.Join(items, ", ") + "]" 40 case *toml.Tree: 41 // workaround for unreliable map key ordering 42 items := []string{} 43 for _, k := range node.Keys() { 44 v := node.GetPath([]string{k}) 45 items = append(items, k+":"+valueString(v)) 46 } 47 sort.Strings(items) 48 result = "{" + strings.Join(items, ", ") + "}" 49 case map[string]interface{}: 50 // workaround for unreliable map key ordering 51 items := []string{} 52 for k, v := range node { 53 items = append(items, k+":"+valueString(v)) 54 } 55 sort.Strings(items) 56 result = "{" + strings.Join(items, ", ") + "}" 57 case int64: 58 result += fmt.Sprintf("%d", node) 59 case string: 60 result += "'" + node + "'" 61 case float64: 62 result += fmt.Sprintf("%f", node) 63 case bool: 64 result += fmt.Sprintf("%t", node) 65 case time.Time: 66 result += fmt.Sprintf("'%v'", node) 67 } 68 return result 69} 70 71func assertValue(t *testing.T, result, ref interface{}) { 72 pathStr := valueString(result) 73 refStr := valueString(ref) 74 if pathStr != refStr { 75 t.Errorf("values do not match") 76 t.Log("test:", pathStr) 77 t.Log("ref: ", refStr) 78 } 79} 80 81func assertQueryPositions(t *testing.T, tomlDoc string, query string, ref []interface{}) { 82 tree, err := toml.Load(tomlDoc) 83 if err != nil { 84 t.Errorf("Non-nil toml parse error: %v", err) 85 return 86 } 87 q, err := Compile(query) 88 if err != nil { 89 t.Error(err) 90 return 91 } 92 results := q.Execute(tree) 93 assertValue(t, results, ref) 94} 95 96func TestQueryRoot(t *testing.T) { 97 assertQueryPositions(t, 98 "a = 42", 99 "$", 100 []interface{}{ 101 queryTestNode{ 102 map[string]interface{}{ 103 "a": int64(42), 104 }, toml.Position{1, 1}, 105 }, 106 }) 107} 108 109func TestQueryKey(t *testing.T) { 110 assertQueryPositions(t, 111 "[foo]\na = 42", 112 "$.foo.a", 113 []interface{}{ 114 queryTestNode{ 115 int64(42), toml.Position{2, 1}, 116 }, 117 }) 118} 119 120func TestQueryKeyString(t *testing.T) { 121 assertQueryPositions(t, 122 "[foo]\na = 42", 123 "$.foo['a']", 124 []interface{}{ 125 queryTestNode{ 126 int64(42), toml.Position{2, 1}, 127 }, 128 }) 129} 130 131func TestQueryIndex(t *testing.T) { 132 assertQueryPositions(t, 133 "[foo]\na = [1,2,3,4,5,6,7,8,9,0]", 134 "$.foo.a[5]", 135 []interface{}{ 136 queryTestNode{ 137 int64(6), toml.Position{2, 1}, 138 }, 139 }) 140} 141 142func TestQuerySliceRange(t *testing.T) { 143 assertQueryPositions(t, 144 "[foo]\na = [1,2,3,4,5,6,7,8,9,0]", 145 "$.foo.a[0:5]", 146 []interface{}{ 147 queryTestNode{ 148 int64(1), toml.Position{2, 1}, 149 }, 150 queryTestNode{ 151 int64(2), toml.Position{2, 1}, 152 }, 153 queryTestNode{ 154 int64(3), toml.Position{2, 1}, 155 }, 156 queryTestNode{ 157 int64(4), toml.Position{2, 1}, 158 }, 159 queryTestNode{ 160 int64(5), toml.Position{2, 1}, 161 }, 162 }) 163} 164 165func TestQuerySliceStep(t *testing.T) { 166 assertQueryPositions(t, 167 "[foo]\na = [1,2,3,4,5,6,7,8,9,0]", 168 "$.foo.a[0:5:2]", 169 []interface{}{ 170 queryTestNode{ 171 int64(1), toml.Position{2, 1}, 172 }, 173 queryTestNode{ 174 int64(3), toml.Position{2, 1}, 175 }, 176 queryTestNode{ 177 int64(5), toml.Position{2, 1}, 178 }, 179 }) 180} 181 182func TestQueryAny(t *testing.T) { 183 assertQueryPositions(t, 184 "[foo.bar]\na=1\nb=2\n[foo.baz]\na=3\nb=4", 185 "$.foo.*", 186 []interface{}{ 187 queryTestNode{ 188 map[string]interface{}{ 189 "a": int64(1), 190 "b": int64(2), 191 }, toml.Position{1, 1}, 192 }, 193 queryTestNode{ 194 map[string]interface{}{ 195 "a": int64(3), 196 "b": int64(4), 197 }, toml.Position{4, 1}, 198 }, 199 }) 200} 201func TestQueryUnionSimple(t *testing.T) { 202 assertQueryPositions(t, 203 "[foo.bar]\na=1\nb=2\n[baz.foo]\na=3\nb=4\n[gorf.foo]\na=5\nb=6", 204 "$.*[bar,foo]", 205 []interface{}{ 206 queryTestNode{ 207 map[string]interface{}{ 208 "a": int64(1), 209 "b": int64(2), 210 }, toml.Position{1, 1}, 211 }, 212 queryTestNode{ 213 map[string]interface{}{ 214 "a": int64(3), 215 "b": int64(4), 216 }, toml.Position{4, 1}, 217 }, 218 queryTestNode{ 219 map[string]interface{}{ 220 "a": int64(5), 221 "b": int64(6), 222 }, toml.Position{7, 1}, 223 }, 224 }) 225} 226 227func TestQueryRecursionAll(t *testing.T) { 228 assertQueryPositions(t, 229 "[foo.bar]\na=1\nb=2\n[baz.foo]\na=3\nb=4\n[gorf.foo]\na=5\nb=6", 230 "$..*", 231 []interface{}{ 232 queryTestNode{ 233 map[string]interface{}{ 234 "foo": map[string]interface{}{ 235 "bar": map[string]interface{}{ 236 "a": int64(1), 237 "b": int64(2), 238 }, 239 }, 240 "baz": map[string]interface{}{ 241 "foo": map[string]interface{}{ 242 "a": int64(3), 243 "b": int64(4), 244 }, 245 }, 246 "gorf": map[string]interface{}{ 247 "foo": map[string]interface{}{ 248 "a": int64(5), 249 "b": int64(6), 250 }, 251 }, 252 }, toml.Position{1, 1}, 253 }, 254 queryTestNode{ 255 map[string]interface{}{ 256 "bar": map[string]interface{}{ 257 "a": int64(1), 258 "b": int64(2), 259 }, 260 }, toml.Position{1, 1}, 261 }, 262 queryTestNode{ 263 map[string]interface{}{ 264 "a": int64(1), 265 "b": int64(2), 266 }, toml.Position{1, 1}, 267 }, 268 queryTestNode{ 269 int64(1), toml.Position{2, 1}, 270 }, 271 queryTestNode{ 272 int64(2), toml.Position{3, 1}, 273 }, 274 queryTestNode{ 275 map[string]interface{}{ 276 "foo": map[string]interface{}{ 277 "a": int64(3), 278 "b": int64(4), 279 }, 280 }, toml.Position{4, 1}, 281 }, 282 queryTestNode{ 283 map[string]interface{}{ 284 "a": int64(3), 285 "b": int64(4), 286 }, toml.Position{4, 1}, 287 }, 288 queryTestNode{ 289 int64(3), toml.Position{5, 1}, 290 }, 291 queryTestNode{ 292 int64(4), toml.Position{6, 1}, 293 }, 294 queryTestNode{ 295 map[string]interface{}{ 296 "foo": map[string]interface{}{ 297 "a": int64(5), 298 "b": int64(6), 299 }, 300 }, toml.Position{7, 1}, 301 }, 302 queryTestNode{ 303 map[string]interface{}{ 304 "a": int64(5), 305 "b": int64(6), 306 }, toml.Position{7, 1}, 307 }, 308 queryTestNode{ 309 int64(5), toml.Position{8, 1}, 310 }, 311 queryTestNode{ 312 int64(6), toml.Position{9, 1}, 313 }, 314 }) 315} 316 317func TestQueryRecursionUnionSimple(t *testing.T) { 318 assertQueryPositions(t, 319 "[foo.bar]\na=1\nb=2\n[baz.foo]\na=3\nb=4\n[gorf.foo]\na=5\nb=6", 320 "$..['foo','bar']", 321 []interface{}{ 322 queryTestNode{ 323 map[string]interface{}{ 324 "bar": map[string]interface{}{ 325 "a": int64(1), 326 "b": int64(2), 327 }, 328 }, toml.Position{1, 1}, 329 }, 330 queryTestNode{ 331 map[string]interface{}{ 332 "a": int64(3), 333 "b": int64(4), 334 }, toml.Position{4, 1}, 335 }, 336 queryTestNode{ 337 map[string]interface{}{ 338 "a": int64(1), 339 "b": int64(2), 340 }, toml.Position{1, 1}, 341 }, 342 queryTestNode{ 343 map[string]interface{}{ 344 "a": int64(5), 345 "b": int64(6), 346 }, toml.Position{7, 1}, 347 }, 348 }) 349} 350 351func TestQueryFilterFn(t *testing.T) { 352 buff, err := ioutil.ReadFile("../example.toml") 353 if err != nil { 354 t.Error(err) 355 return 356 } 357 358 assertQueryPositions(t, string(buff), 359 "$..[?(int)]", 360 []interface{}{ 361 queryTestNode{ 362 int64(8001), toml.Position{13, 1}, 363 }, 364 queryTestNode{ 365 int64(8001), toml.Position{13, 1}, 366 }, 367 queryTestNode{ 368 int64(8002), toml.Position{13, 1}, 369 }, 370 queryTestNode{ 371 int64(5000), toml.Position{14, 1}, 372 }, 373 }) 374 375 assertQueryPositions(t, string(buff), 376 "$..[?(string)]", 377 []interface{}{ 378 queryTestNode{ 379 "TOML Example", toml.Position{3, 1}, 380 }, 381 queryTestNode{ 382 "Tom Preston-Werner", toml.Position{6, 1}, 383 }, 384 queryTestNode{ 385 "GitHub", toml.Position{7, 1}, 386 }, 387 queryTestNode{ 388 "GitHub Cofounder & CEO\nLikes tater tots and beer.", 389 toml.Position{8, 1}, 390 }, 391 queryTestNode{ 392 "192.168.1.1", toml.Position{12, 1}, 393 }, 394 queryTestNode{ 395 "10.0.0.1", toml.Position{21, 3}, 396 }, 397 queryTestNode{ 398 "eqdc10", toml.Position{22, 3}, 399 }, 400 queryTestNode{ 401 "10.0.0.2", toml.Position{25, 3}, 402 }, 403 queryTestNode{ 404 "eqdc10", toml.Position{26, 3}, 405 }, 406 }) 407 408 assertQueryPositions(t, string(buff), 409 "$..[?(float)]", 410 []interface{}{ // no float values in document 411 }) 412 413 tv, _ := time.Parse(time.RFC3339, "1979-05-27T07:32:00Z") 414 assertQueryPositions(t, string(buff), 415 "$..[?(tree)]", 416 []interface{}{ 417 queryTestNode{ 418 map[string]interface{}{ 419 "name": "Tom Preston-Werner", 420 "organization": "GitHub", 421 "bio": "GitHub Cofounder & CEO\nLikes tater tots and beer.", 422 "dob": tv, 423 }, toml.Position{5, 1}, 424 }, 425 queryTestNode{ 426 map[string]interface{}{ 427 "server": "192.168.1.1", 428 "ports": []interface{}{int64(8001), int64(8001), int64(8002)}, 429 "connection_max": int64(5000), 430 "enabled": true, 431 }, toml.Position{11, 1}, 432 }, 433 queryTestNode{ 434 map[string]interface{}{ 435 "alpha": map[string]interface{}{ 436 "ip": "10.0.0.1", 437 "dc": "eqdc10", 438 }, 439 "beta": map[string]interface{}{ 440 "ip": "10.0.0.2", 441 "dc": "eqdc10", 442 }, 443 }, toml.Position{17, 1}, 444 }, 445 queryTestNode{ 446 map[string]interface{}{ 447 "ip": "10.0.0.1", 448 "dc": "eqdc10", 449 }, toml.Position{20, 3}, 450 }, 451 queryTestNode{ 452 map[string]interface{}{ 453 "ip": "10.0.0.2", 454 "dc": "eqdc10", 455 }, toml.Position{24, 3}, 456 }, 457 queryTestNode{ 458 map[string]interface{}{ 459 "data": []interface{}{ 460 []interface{}{"gamma", "delta"}, 461 []interface{}{int64(1), int64(2)}, 462 }, 463 }, toml.Position{28, 1}, 464 }, 465 }) 466 467 assertQueryPositions(t, string(buff), 468 "$..[?(time)]", 469 []interface{}{ 470 queryTestNode{ 471 tv, toml.Position{9, 1}, 472 }, 473 }) 474 475 assertQueryPositions(t, string(buff), 476 "$..[?(bool)]", 477 []interface{}{ 478 queryTestNode{ 479 true, toml.Position{15, 1}, 480 }, 481 }) 482} 483