bosun.org@v0.0.0-20210513094433-e25bc3e69a1f/cmd/bosun/expr/expr_test.go (about) 1 package expr 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "log" 7 "math" 8 "net/http" 9 "net/http/httptest" 10 "net/url" 11 "testing" 12 "time" 13 14 "bosun.org/opentsdb" 15 "github.com/influxdata/influxdb/client/v2" 16 ) 17 18 func TestExprSimple(t *testing.T) { 19 var exprTests = []struct { 20 input string 21 output Scalar 22 }{ 23 {"!1", 0}, 24 {"-2", -2}, 25 {"1.444-010+2*3e2-4/5+0xff", 847.644}, 26 {"1>2", 0}, 27 {"3>2", 1}, 28 {"1==1", 1}, 29 {"1==2", 0}, 30 {"1!=01", 0}, 31 {"1!=2", 1}, 32 {"1<2", 1}, 33 {"2<1", 0}, 34 {"1||0", 1}, 35 {"0||0", 0}, 36 {"1&&0", 0}, 37 {"1&&2", 1}, 38 {"1<=0", 0}, 39 {"1<=1", 1}, 40 {"1<=2", 1}, 41 {"1>=0", 1}, 42 {"1>=1", 1}, 43 {"1>=2", 0}, 44 {"-1 > 0", 0}, 45 {"-1 < 0", 1}, 46 {"30 % 3", 0}, 47 {"5 % 7", 5}, 48 {"25.5 % 5", .5}, 49 50 // NaN 51 {"0 / 0", Scalar(math.NaN())}, 52 {"1 / 0", Scalar(math.Inf(1))}, 53 54 // short circuit 55 {"0 && 0 / 0", 0}, 56 {"1 || 0 / 0", 1}, 57 {"1 && 0 / 0", Scalar(math.NaN())}, 58 {"0 || 0 / 0", Scalar(math.NaN())}, 59 } 60 61 for _, et := range exprTests { 62 e, err := New(et.input) 63 if err != nil { 64 t.Error(err) 65 break 66 } 67 backends := &Backends{ 68 InfluxConfig: client.HTTPConfig{}, 69 } 70 providers := &BosunProviders{} 71 r, _, err := e.Execute(backends, providers, nil, time.Now(), 0, false, t.Name()) 72 if err != nil { 73 t.Error(err) 74 break 75 } else if len(r.Results) != 1 { 76 t.Error("bad r len", len(r.Results)) 77 break 78 } else if len(r.Results[0].Group) != 0 { 79 t.Error("bad group len", r.Results[0].Group) 80 break 81 } else if math.IsNaN(float64(et.output)) && math.IsNaN(float64(r.Results[0].Value.(Scalar))) { 82 // ok 83 } else if r.Results[0].Value != et.output { 84 t.Errorf("expected %v, got %v: %v\nast: %v", et.output, r.Results[0].Value, et.input, e) 85 } 86 } 87 } 88 89 func TestExprParse(t *testing.T) { 90 var exprTests = []struct { 91 input string 92 valid bool 93 tags string 94 }{ 95 {`avg(q("test", "1m", 1))`, false, ""}, 96 {`avg(q("avg:m", "1m", ""))`, true, ""}, 97 {`avg(q("avg:m{a=*}", "1m", ""))`, true, "a"}, 98 {`avg(q("avg:m{a=*,b=1}", "1m", ""))`, true, "a,b"}, 99 {`avg(q("avg:m{a=*,b=1}", "1m", "")) + 1`, true, "a,b"}, 100 } 101 102 for _, et := range exprTests { 103 e, err := New(et.input, TSDB) 104 if et.valid && err != nil { 105 t.Error(err) 106 } else if !et.valid && err == nil { 107 t.Errorf("expected invalid, but no error: %v", et.input) 108 } else if et.valid { 109 tags, err := e.Root.Tags() 110 if err != nil { 111 t.Error(err) 112 continue 113 } 114 if et.tags != tags.String() { 115 t.Errorf("%v: unexpected tags: got %v, expected %v", et.input, tags, et.tags) 116 } 117 } 118 } 119 } 120 121 var queryTime = time.Date(2000, 1, 1, 12, 0, 0, 0, time.UTC) 122 123 func TestQueryExpr(t *testing.T) { 124 queries := map[string]opentsdb.ResponseSet{ 125 `q("avg:m{a=*}", "9.467277e+08", "9.46728e+08")`: { 126 { 127 Metric: "m", 128 Tags: opentsdb.TagSet{"a": "b"}, 129 DPS: map[string]opentsdb.Point{"0": 0, "1": 3}, 130 }, 131 { 132 Metric: "m", 133 Tags: opentsdb.TagSet{"a": "c"}, 134 DPS: map[string]opentsdb.Point{"5": 1, "7": 4}, 135 }, 136 }, 137 138 `q("avg:m{a=*}", "9.467241e+08", "9.467244e+08")`: { 139 { 140 Metric: "m", 141 Tags: opentsdb.TagSet{"a": "b"}, 142 DPS: map[string]opentsdb.Point{"0": 1, "1": 2}, 143 }, 144 { 145 Metric: "m", 146 Tags: opentsdb.TagSet{"a": "c"}, 147 DPS: map[string]opentsdb.Point{"3": 7, "1": 8}, 148 }, 149 }, 150 `q("avg:m{a=*}", "9.467205e+08", "9.467208e+08")`: { 151 { 152 Metric: "m", 153 Tags: opentsdb.TagSet{"a": "b"}, 154 DPS: map[string]opentsdb.Point{"2": 6, "3": 4}, 155 }, 156 { 157 Metric: "m", 158 Tags: opentsdb.TagSet{"a": "d"}, 159 DPS: map[string]opentsdb.Point{"8": 8, "9": 9}, 160 }, 161 }, 162 } 163 d := time.Date(1970, time.January, 1, 0, 0, 0, 0, time.UTC) 164 tests := map[string]map[string]Value{ 165 `window("avg:m{a=*}", "5m", "1h", 2, "max")`: { 166 "a=b": Series{ 167 d: 2, 168 d.Add(time.Second * 2): 6, 169 }, 170 "a=c": Series{ 171 d.Add(time.Second * 1): 8, 172 }, 173 "a=d": Series{ 174 d.Add(time.Second * 8): 9, 175 }, 176 }, 177 `window("avg:m{a=*}", "5m", "1h", 2, "avg")`: { 178 "a=b": Series{ 179 d: 1.5, 180 d.Add(time.Second * 2): 5, 181 }, 182 "a=c": Series{ 183 d.Add(time.Second * 1): 7.5, 184 }, 185 "a=d": Series{ 186 d.Add(time.Second * 8): 8.5, 187 }, 188 }, 189 `over("avg:m{a=*}", "5m", "1h", 3)`: { 190 "a=b,shift=0s": Series{ 191 d: 0, 192 d.Add(time.Second * 1): 3, 193 }, 194 "a=b,shift=1h0m0s": Series{ 195 d.Add(time.Hour): 1, 196 d.Add(time.Hour + time.Second*1): 2, 197 }, 198 "a=b,shift=2h0m0s": Series{ 199 d.Add(time.Hour*2 + time.Second*2): 6, 200 d.Add(time.Hour*2 + time.Second*3): 4, 201 }, 202 "a=c,shift=0s": Series{ 203 d.Add(time.Second * 5): 1, 204 d.Add(time.Second * 7): 4, 205 }, 206 "a=c,shift=1h0m0s": Series{ 207 d.Add(time.Hour + time.Second*3): 7, 208 d.Add(time.Hour + time.Second*1): 8, 209 }, 210 "a=d,shift=2h0m0s": Series{ 211 d.Add(time.Hour*2 + time.Second*8): 8, 212 d.Add(time.Hour*2 + time.Second*9): 9, 213 }, 214 }, 215 `band("avg:m{a=*}", "5m", "1h", 2)`: { 216 "a=b": Series{ 217 d: 1, 218 d.Add(time.Second * 1): 2, 219 d.Add(time.Second * 2): 6, 220 d.Add(time.Second * 3): 4, 221 }, 222 "a=c": Series{ 223 d.Add(time.Second * 3): 7, 224 d.Add(time.Second * 1): 8, 225 }, 226 "a=d": Series{ 227 d.Add(time.Second * 8): 8, 228 d.Add(time.Second * 9): 9, 229 }, 230 }, 231 `shiftBand("avg:m{a=*}", "5m", "1h", 2)`: { 232 "a=b,shift=1h0m0s": Series{ 233 d.Add(time.Hour): 1, 234 d.Add(time.Hour + time.Second*1): 2, 235 }, 236 "a=b,shift=2h0m0s": Series{ 237 d.Add(time.Hour*2 + time.Second*2): 6, 238 d.Add(time.Hour*2 + time.Second*3): 4, 239 }, 240 "a=c,shift=1h0m0s": Series{ 241 d.Add(time.Hour + time.Second*3): 7, 242 d.Add(time.Hour + time.Second*1): 8, 243 }, 244 "a=d,shift=2h0m0s": Series{ 245 d.Add(time.Hour*2 + time.Second*8): 8, 246 d.Add(time.Hour*2 + time.Second*9): 9, 247 }, 248 }, 249 `abs(-1)`: {"": Number(1)}, 250 } 251 252 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 253 var req opentsdb.Request 254 if err := json.NewDecoder(r.Body).Decode(&req); err != nil { 255 log.Fatal(err) 256 } 257 var resp opentsdb.ResponseSet 258 for _, rq := range req.Queries { 259 qs := fmt.Sprintf(`q("%s", "%v", "%v")`, rq, req.Start, req.End) 260 q, ok := queries[qs] 261 if !ok { 262 t.Errorf("unknown query: %s", qs) 263 return 264 } 265 if q == nil { 266 return // Put nil entry in map to simulate opentsdb error. 267 } 268 resp = append(resp, q...) 269 } 270 if err := json.NewEncoder(w).Encode(&resp); err != nil { 271 log.Fatal(err) 272 } 273 })) 274 defer ts.Close() 275 u, err := url.Parse(ts.URL) 276 if err != nil { 277 t.Fatal(err) 278 } 279 280 for exprText, expected := range tests { 281 e, err := New(exprText, TSDB) 282 if err != nil { 283 t.Fatal(err) 284 } 285 backends := &Backends{ 286 TSDBContext: &opentsdb.LimitContext{Host: u.Host, Limit: 1e10, TSDBVersion: opentsdb.Version2_1}, 287 InfluxConfig: client.HTTPConfig{}, 288 } 289 providers := &BosunProviders{} 290 results, _, err := e.Execute(backends, providers, nil, queryTime, 0, false, t.Name()) 291 if err != nil { 292 t.Fatal(err) 293 } 294 for _, r := range results.Results { 295 tag := r.Group.Tags() 296 ex := expected[tag] 297 if ex == nil { 298 t.Errorf("missing tag %v", tag) 299 continue 300 } 301 switch val := r.Value.(type) { 302 case Series: 303 ex, ok := ex.(Series) 304 if !ok { 305 t.Errorf("%v: bad type %T", exprText, ex) 306 continue 307 } 308 if len(val) != len(ex) { 309 t.Errorf("unmatched values in %v", tag) 310 continue 311 } 312 for k, v := range ex { 313 got := val[k] 314 if got != v { 315 t.Errorf("%v, %v: got %v, expected %v", tag, k, got, v) 316 } 317 } 318 case Number: 319 ex, ok := ex.(Number) 320 if !ok { 321 t.Errorf("%v: bad type %T", exprText, ex) 322 continue 323 } 324 if ex != val { 325 t.Errorf("%v: got %v, expected %v", exprText, r.Value, ex) 326 } 327 default: 328 t.Errorf("%v: unknown type %T", exprText, r.Value) 329 } 330 } 331 } 332 } 333 334 func TestSetVariant(t *testing.T) { 335 series := `series("key1=a,key2=b", 0, 1, 1, 3)` 336 seriesAbs := `series("", 0, 1, 1, -3)` 337 tests := []exprInOut{ 338 { 339 fmt.Sprintf(`addtags(addtags(%v, "key3=a"), "key4=b") + 1`, series), 340 Results{ 341 Results: ResultSlice{ 342 &Result{ 343 Value: Series{ 344 time.Unix(0, 0): 2, 345 time.Unix(1, 0): 4, 346 }, 347 Group: opentsdb.TagSet{"key1": "a", "key2": "b", "key3": "a", "key4": "b"}, 348 }, 349 }, 350 }, 351 false, 352 }, 353 { 354 fmt.Sprintf(`addtags(addtags(avg(%v + 1), "key3=a"), "key4=b") + 1`, series), 355 Results{ 356 Results: ResultSlice{ 357 &Result{ 358 Value: Number(4), 359 Group: opentsdb.TagSet{"key1": "a", "key2": "b", "key3": "a", "key4": "b"}, 360 }, 361 }, 362 }, 363 false, 364 }, 365 { 366 fmt.Sprintf(`avg(addtags(addtags(avg(%v + 1), "key3=a"), "key4=b")) + 1`, series), 367 Results{}, 368 true, 369 }, 370 371 { 372 fmt.Sprintf(`1 + abs(%v)`, seriesAbs), 373 Results{ 374 Results: ResultSlice{ 375 &Result{ 376 Value: Series{ 377 time.Unix(0, 0): 2, 378 time.Unix(1, 0): 4, 379 }, 380 Group: opentsdb.TagSet{}, 381 }, 382 }, 383 }, 384 false, 385 }, 386 { 387 fmt.Sprintf(`1 + abs(avg(%v))`, seriesAbs), 388 Results{ 389 Results: ResultSlice{ 390 &Result{ 391 Value: Number(2), 392 Group: opentsdb.TagSet{}, 393 }, 394 }, 395 }, 396 false, 397 }, 398 } 399 for _, test := range tests { 400 err := testExpression(test, t) 401 if err != nil { 402 t.Error(err) 403 } 404 } 405 } 406 407 func TestSeriesOperations(t *testing.T) { 408 seriesA := `series("key=a", 0, 1, 1, 2, 2, 1, 3, 4)` 409 seriesB := `series("key=a", 0, 1, 2, 0, 3, 4)` 410 seriesC := `series("key=a", 4, 1, 6, 0, 7, 4)` 411 template := "%v %v %v" 412 tests := []exprInOut{ 413 { 414 fmt.Sprintf(template, seriesA, "+", seriesB), 415 Results{ 416 Results: ResultSlice{ 417 &Result{ 418 Value: Series{ 419 time.Unix(0, 0): 2, 420 time.Unix(2, 0): 1, 421 time.Unix(3, 0): 8, 422 }, 423 Group: opentsdb.TagSet{"key": "a"}, 424 }, 425 }, 426 }, 427 false, 428 }, 429 { 430 fmt.Sprintf(template, seriesA, "+", seriesC), 431 Results{ 432 Results: ResultSlice{ 433 &Result{ 434 Value: Series{ 435 // Should be empty 436 }, 437 Group: opentsdb.TagSet{"key": "a"}, 438 }, 439 }, 440 }, 441 false, 442 }, 443 { 444 fmt.Sprintf(template, seriesA, "/", seriesB), 445 Results{ 446 Results: ResultSlice{ 447 &Result{ 448 Value: Series{ 449 time.Unix(0, 0): 1, 450 time.Unix(2, 0): math.Inf(1), 451 time.Unix(3, 0): 1, 452 }, 453 Group: opentsdb.TagSet{"key": "a"}, 454 }, 455 }, 456 }, 457 false, 458 }, 459 } 460 for _, test := range tests { 461 err := testExpression(test, t) 462 if err != nil { 463 t.Error(err) 464 } 465 } 466 }