bosun.org@v0.0.0-20210513094433-e25bc3e69a1f/opentsdb/tsdb_test.go (about)

     1  package opentsdb
     2  
     3  import (
     4  	"bytes"
     5  	"io/ioutil"
     6  	"net/http"
     7  	"net/url"
     8  	"testing"
     9  
    10  	"github.com/stretchr/testify/assert"
    11  )
    12  
    13  func TestClean(t *testing.T) {
    14  	clean := "aoeSNVT152-./_"
    15  	if c, err := Clean(clean); c != clean {
    16  		t.Error("was clean", clean)
    17  	} else if err != nil {
    18  		t.Fatal(err)
    19  	}
    20  	notclean := "euon@#$sar:.03   n  ]e/"
    21  	notcleaned := "euonsar.03ne/"
    22  	if c, err := Clean(notclean); c != notcleaned {
    23  		t.Error("wasn't cleaned", notclean, "into:", c)
    24  	} else if err != nil {
    25  		t.Fatal(err)
    26  	}
    27  }
    28  
    29  func TestEmptyPoint(t *testing.T) {
    30  	d := DataPoint{}
    31  	if d.Clean() == nil {
    32  		t.Fatal("empty datapoint should not be cleanable")
    33  	}
    34  }
    35  
    36  func TestParseQueryV2_1(t *testing.T) {
    37  	tests := []struct {
    38  		query string
    39  		error bool
    40  	}{
    41  		{"sum:10m-avg:proc.stat.cpu{t=v,o=k}", false},
    42  		{"sum:10m-avg:rate:proc.stat.cpu", false},
    43  		{"sum:10m-avg:rate{counter,1,2}:proc.stat.cpu{t=v,o=k}", false},
    44  		{"sum:proc.stat.cpu", false},
    45  		{"sum:rate:proc.stat.cpu{t=v,o=k}", false},
    46  
    47  		{"", true},
    48  		{"sum:cpu+", true},
    49  		{"sum:cpu{}", true},
    50  		{"sum:stat{a=b=c}", true},
    51  	}
    52  	for _, q := range tests {
    53  		_, err := ParseQuery(q.query, Version2_1)
    54  		if err != nil && !q.error {
    55  			t.Errorf("got error: %s: %s", q.query, err)
    56  		} else if err == nil && q.error {
    57  			t.Errorf("expected error: %s", q.query)
    58  		}
    59  	}
    60  }
    61  
    62  func TestParseQueryV2_2(t *testing.T) {
    63  	tests := []struct {
    64  		query string
    65  		error bool
    66  	}{
    67  		{"sum:10m-avg:proc.stat.cpu{t=v,o=k}", false},
    68  		{"sum:10m-avg:rate:proc.stat.cpu", false},
    69  		{"sum:10m-avg:rate{counter,1,2}:proc.stat.cpu{t=v,o=k}", false},
    70  		{"sum:10m-avg:rate{counter,1,2}:proc.stat.cpu{t=v,o=k}{t=wildcard(v*)}", false},
    71  		{"sum:10m-avg:rate{counter,1,2}:proc.stat.cpu{}{t=wildcard(v*)}", false},
    72  		{"sum:proc.stat.cpu", false},
    73  		{"sum:rate:proc.stat.cpu{t=v,o=k}", false},
    74  
    75  		{"", true},
    76  		{"sum:cpu+", true},
    77  		// This is supported in 2.2
    78  		{"sum:cpu{}", false},
    79  		// This wouldn't be valid, but we are more permissive and will let opentsdb
    80  		// return errors. This is because there can be a regex filter now. Might
    81  		// be issues with escaping there however
    82  		{"sum:stat{a=b=c}", false},
    83  
    84  		//test fill policies
    85  		{"sum:10m-avg-zero:proc.stat.cpu{t=v,o=k}", false},
    86  		{"sum:10m-avg-:proc.stat.cpu{t=v,o=k}", true},
    87  		{"sum:10m-avg-none:rate:proc.stat.cpu", false},
    88  		{"sum:10m-avg-:rate{counter,1,2}:proc.stat.cpu{t=v,o=k}", true},
    89  	}
    90  	for _, q := range tests {
    91  		_, err := ParseQuery(q.query, Version2_2)
    92  		if err != nil && !q.error {
    93  			t.Errorf("got error: %s: %s", q.query, err)
    94  		} else if err == nil && q.error {
    95  			t.Errorf("expected error: %s", q.query)
    96  		}
    97  	}
    98  }
    99  
   100  func TestParseRequestV2_1(t *testing.T) {
   101  	tests := []struct {
   102  		query string
   103  		error bool
   104  	}{
   105  		{"start=1&m=sum:c", false},
   106  		{"start=1&m=sum:c&end=2", false},
   107  		{"start=1&m=sum:10m-avg:rate:proc.stat.cpu{t=v,o=k}", false},
   108  
   109  		{"start=&m=", true},
   110  		{"m=sum:c", true},
   111  		{"start=1", true},
   112  	}
   113  	for _, q := range tests {
   114  		_, err := ParseRequest(q.query, Version2_1)
   115  		if err != nil && !q.error {
   116  			t.Errorf("got error: %s: %s", q.query, err)
   117  		} else if err == nil && q.error {
   118  			t.Errorf("expected error: %s", q.query)
   119  		}
   120  	}
   121  }
   122  
   123  func TestParseRequestV2_2(t *testing.T) {
   124  	tests := []struct {
   125  		query string
   126  		error bool
   127  	}{
   128  		{"start=1&m=sum:c", false},
   129  		{"start=1&m=sum:c&end=2", false},
   130  		{"start=1&m=sum:10m-avg:rate:proc.stat.cpu{t=v,o=k}", false},
   131  		{"start=1&m=sum:10m-avg:rate:proc.stat.cpu{}{t=v,o=k}", false},
   132  		{"start=1&m=sum:10m-avg:rate:proc.stat.cpu{z=iwildcard(foo*)}{t=v,o=k}", false},
   133  
   134  		{"start=&m=", true},
   135  		{"m=sum:c", true},
   136  		{"start=1", true},
   137  	}
   138  	for _, q := range tests {
   139  		_, err := ParseRequest(q.query, Version2_2)
   140  		if err != nil && !q.error {
   141  			t.Errorf("got error: %s: %s", q.query, err)
   142  		} else if err == nil && q.error {
   143  			t.Errorf("expected error: %s", q.query)
   144  		}
   145  	}
   146  }
   147  func TestTagGroupParsing(t *testing.T) {
   148  	tests := []struct {
   149  		query  string
   150  		groups string
   151  	}{
   152  		{"sum:10m-avg:proc.stat.cpu{}{t=v,o=k}", "{}"},
   153  		{"sum:10m-avg:proc.stat.cpu{dc=uk}{t=v,o=k}", "{dc=}"},
   154  	}
   155  	for _, q := range tests {
   156  		r, err := ParseQuery(q.query, Version2_2)
   157  		if err == nil {
   158  			if r.GroupByTags.String() != q.groups {
   159  				t.Errorf("expected group tags %s got %s", q.groups, r.GroupByTags)
   160  			}
   161  
   162  		}
   163  	}
   164  }
   165  
   166  func TestParseRateOptions(t *testing.T) {
   167  	tests := []struct {
   168  		query string
   169  		rate  RateOptions
   170  	}{
   171  		{"sum:10m-avg:rate{counter,1,2}:test.metric",
   172  			RateOptions{
   173  				Counter:    true,
   174  				CounterMax: 1,
   175  				ResetValue: 2,
   176  			},
   177  		},
   178  		{"sum:10m-avg:rate{dropcounter}:test.metric",
   179  			RateOptions{
   180  				Counter:    true,
   181  				DropResets: true,
   182  			},
   183  		},
   184  		{"sum:10m-avg:rate{counter,,2}:test.metric",
   185  			RateOptions{
   186  				Counter:    true,
   187  				ResetValue: 2,
   188  			},
   189  		},
   190  		{"sum:10m-avg:rate{counter,1}:test.metric",
   191  			RateOptions{
   192  				Counter:    true,
   193  				CounterMax: 1,
   194  			},
   195  		},
   196  	}
   197  	for _, q := range tests {
   198  		parsedQuery, err := ParseQuery(q.query, Version2_2)
   199  		if err != nil {
   200  			t.Errorf("error parsing query %s: %s", q.query, err)
   201  			continue
   202  		}
   203  		if q.rate != parsedQuery.RateOptions {
   204  			t.Errorf("for query %s expected parsed rate options %+v, got: %+v", q.query, q.rate, parsedQuery.RateOptions)
   205  		}
   206  	}
   207  }
   208  
   209  func TestParseFilters(t *testing.T) {
   210  	tests := []struct {
   211  		query   string
   212  		filters Filters
   213  	}{
   214  		{"sum:10m-avg:rate{counter,1,2}:proc.stat.cpu{t=v,o=k}",
   215  			Filters{
   216  				Filter{
   217  					Type:    "literal_or",
   218  					TagK:    "t",
   219  					Filter:  "v",
   220  					GroupBy: true,
   221  				},
   222  				Filter{
   223  					Type:    "literal_or",
   224  					TagK:    "o",
   225  					Filter:  "k",
   226  					GroupBy: true,
   227  				},
   228  			},
   229  		},
   230  		{"sum:10m-avg:rate:proc.stat.cpu{}{t=v,o=k}",
   231  			Filters{
   232  				Filter{
   233  					Type:    "literal_or",
   234  					TagK:    "t",
   235  					Filter:  "v",
   236  					GroupBy: false,
   237  				},
   238  				Filter{
   239  					Type:    "literal_or",
   240  					TagK:    "o",
   241  					Filter:  "k",
   242  					GroupBy: false,
   243  				},
   244  			},
   245  		},
   246  		{"sum:10m-avg:rate:proc.stat.cpu{t=v}{o=k}",
   247  			Filters{
   248  				Filter{
   249  					Type:    "literal_or",
   250  					TagK:    "t",
   251  					Filter:  "v",
   252  					GroupBy: true,
   253  				},
   254  				Filter{
   255  					Type:    "literal_or",
   256  					TagK:    "o",
   257  					Filter:  "k",
   258  					GroupBy: false,
   259  				},
   260  			},
   261  		},
   262  		{"sum:10m-avg:rate:proc.stat.cpu{t=v}{o=iwildcard(foo*)}",
   263  			Filters{
   264  				Filter{
   265  					Type:    "literal_or",
   266  					TagK:    "t",
   267  					Filter:  "v",
   268  					GroupBy: true,
   269  				},
   270  				Filter{
   271  					Type:    "iwildcard",
   272  					TagK:    "o",
   273  					Filter:  "foo*",
   274  					GroupBy: false,
   275  				},
   276  			},
   277  		},
   278  	}
   279  	for _, q := range tests {
   280  		parsedQuery, err := ParseQuery(q.query, Version2_2)
   281  		if err != nil {
   282  			t.Errorf("error parsing query %s: %s", q.query, err)
   283  			continue
   284  		}
   285  		if len(q.filters) != len(parsedQuery.Filters) {
   286  			t.Errorf("expected %d filters, got: %d", len(q.filters), len(parsedQuery.Filters))
   287  		} else {
   288  			for i, f := range parsedQuery.Filters {
   289  				if q.filters[i] != f {
   290  					t.Errorf("expected parsed filter %+v, got: %+v", q.filters[i], f)
   291  				}
   292  			}
   293  		}
   294  	}
   295  }
   296  
   297  func TestQueryString(t *testing.T) {
   298  	tests := []struct {
   299  		in  Query
   300  		out string
   301  	}{
   302  		{
   303  			Query{
   304  				Aggregator: "avg",
   305  				Metric:     "test.metric",
   306  				Rate:       true,
   307  				RateOptions: RateOptions{
   308  					Counter:    true,
   309  					CounterMax: 1,
   310  					ResetValue: 2,
   311  				},
   312  			},
   313  			"avg:rate{counter,1,2}:test.metric",
   314  		},
   315  		{
   316  			Query{
   317  				Aggregator: "avg",
   318  				Metric:     "test.metric",
   319  				Rate:       true,
   320  				RateOptions: RateOptions{
   321  					Counter:    true,
   322  					CounterMax: 1,
   323  				},
   324  			},
   325  			"avg:rate{counter,1}:test.metric",
   326  		},
   327  		{
   328  			Query{
   329  				Aggregator: "avg",
   330  				Metric:     "test.metric",
   331  				Rate:       true,
   332  				RateOptions: RateOptions{
   333  					Counter: true,
   334  				},
   335  			},
   336  			"avg:rate{counter}:test.metric",
   337  		},
   338  		{
   339  			Query{
   340  				Aggregator: "avg",
   341  				Metric:     "test.metric",
   342  				Rate:       true,
   343  				RateOptions: RateOptions{
   344  					CounterMax: 1,
   345  					ResetValue: 2,
   346  				},
   347  			},
   348  			"avg:rate:test.metric",
   349  		},
   350  		{
   351  			Query{
   352  				Aggregator: "avg",
   353  				Metric:     "test.metric",
   354  				RateOptions: RateOptions{
   355  					Counter:    true,
   356  					CounterMax: 1,
   357  					ResetValue: 2,
   358  				},
   359  			},
   360  			"avg:test.metric",
   361  		},
   362  		{
   363  			Query{
   364  				Aggregator: "avg",
   365  				Metric:     "test.metric",
   366  				Rate:       true,
   367  				RateOptions: RateOptions{
   368  					Counter:    true,
   369  					DropResets: true,
   370  				},
   371  			},
   372  			"avg:rate{dropcounter}:test.metric",
   373  		},
   374  	}
   375  	for _, q := range tests {
   376  		s := q.in.String()
   377  		if s != q.out {
   378  			t.Errorf(`got "%s", expected "%s"`, s, q.out)
   379  		}
   380  	}
   381  }
   382  
   383  func TestValidTSDBString(t *testing.T) {
   384  	tests := map[string]bool{
   385  		"abcXYZ012_./-": true,
   386  
   387  		"":    false,
   388  		"a|c": false,
   389  		"a=b": false,
   390  	}
   391  	for s, v := range tests {
   392  		r := ValidTSDBString(s)
   393  		if v != r {
   394  			t.Errorf("%v: got %v, expected %v", s, r, v)
   395  		}
   396  	}
   397  }
   398  
   399  func TestValidTags(t *testing.T) {
   400  	tests := map[string]bool{
   401  		"a=b|c,d=*": true,
   402  
   403  		"":        false,
   404  		"a=b,a=c": false,
   405  		"a=b=c":   false,
   406  	}
   407  	for s, v := range tests {
   408  		_, err := ParseTags(s)
   409  		r := err == nil
   410  		if v != r {
   411  			t.Errorf("%v: got %v, expected %v", s, r, v)
   412  		}
   413  	}
   414  }
   415  
   416  func TestAllSubsets(t *testing.T) {
   417  	ts, _ := ParseTags("a=1,b=2,c=3,d=4")
   418  	subsets := ts.AllSubsets()
   419  	if len(subsets) != 15 {
   420  		t.Fatal("Expect 15 subsets")
   421  	}
   422  }
   423  
   424  func TestReplace(t *testing.T) {
   425  	tests := []struct{ in, out string }{
   426  		{"abc", "abc"},
   427  		{"ny-web01", "ny-web01"},
   428  		{"_./", "_./"},
   429  		{"%%%a", ".a"},
   430  	}
   431  	for i, test := range tests {
   432  		out, err := Replace(test.in, ".")
   433  		if err != nil {
   434  			t.Errorf("Test %d: %s", i, err)
   435  		}
   436  		if out != test.out {
   437  			t.Errorf("Test %d: %s != %s", i, out, test.out)
   438  		}
   439  	}
   440  }
   441  
   442  // RoundTripFunc .
   443  type RoundTripFunc func(req *http.Request) *http.Response
   444  
   445  // RoundTrip .
   446  func (f RoundTripFunc) RoundTrip(req *http.Request) (*http.Response, error) {
   447  	return f(req), nil
   448  }
   449  
   450  //NewTestClient returns *http.Client with Transport replaced to avoid making real calls
   451  func NewTestClient(fn RoundTripFunc) *http.Client {
   452  	return &http.Client{
   453  		Transport: RoundTripFunc(fn),
   454  	}
   455  }
   456  
   457  func TestQueryResponseUrlParsing(t *testing.T) {
   458  
   459  	tests := []struct {
   460  		raw string
   461  		URL url.URL
   462  	}{
   463  		{"localhost:4242", url.URL{Scheme: "http", Host: "localhost:4242", Path: "/api/query"}},
   464  		{"127.0.0.1:4444", url.URL{Scheme: "http", Host: "127.0.0.1:4444", Path: "/api/query"}},
   465  		{"https://tsdb.skyscanner.net:4242", url.URL{Scheme: "https", Host: "tsdb.skyscanner.net:4242", Path: "/api/query"}},
   466  	}
   467  
   468  	r := Request{}
   469  	stockResponse := http.Response{
   470  		StatusCode: 200,
   471  		Body:       ioutil.NopCloser(bytes.NewBufferString(`OK`)),
   472  		Header:     make(http.Header),
   473  	}
   474  
   475  	for _, test := range tests {
   476  		client := NewTestClient(func(req *http.Request) *http.Response {
   477  			assert.Equal(t, test.URL, *req.URL)
   478  			return &stockResponse
   479  		})
   480  		_, _ = r.QueryResponse(test.raw, client)
   481  
   482  	}
   483  
   484  }
   485  
   486  func BenchmarkReplace_Noop(b *testing.B) {
   487  	for i := 0; i < b.N; i++ {
   488  		Replace("abcdefghijklmnopqrstuvwxyz", "")
   489  	}
   490  }
   491  
   492  func BenchmarkReplace_Something(b *testing.B) {
   493  	for i := 0; i < b.N; i++ {
   494  		Replace("abcdef&hij@@$$opq#stuvw*yz", "")
   495  	}
   496  }