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  }