func TestCommand_Describe(t *testing.T) { fakeApi := mocks.NewFakeApi() fakeApi.AddPair(api.TaggedMetric{"series_0", api.ParseTagSet("dc=west,env=production,host=a")}, emptyGraphiteName) fakeApi.AddPair(api.TaggedMetric{"series_0", api.ParseTagSet("dc=west,env=staging,host=b")}, emptyGraphiteName) fakeApi.AddPair(api.TaggedMetric{"series_0", api.ParseTagSet("dc=east,env=production,host=c")}, emptyGraphiteName) fakeApi.AddPair(api.TaggedMetric{"series_0", api.ParseTagSet("dc=east,env=staging,host=d")}, emptyGraphiteName) for _, test := range []struct { query string backend api.API expected map[string][]string }{ {"describe series_0", fakeApi, map[string][]string{"dc": {"east", "west"}, "env": {"production", "staging"}, "host": {"a", "b", "c", "d"}}}, {"describe`series_0`", fakeApi, map[string][]string{"dc": {"east", "west"}, "env": {"production", "staging"}, "host": {"a", "b", "c", "d"}}}, {"describe series_0 where dc='west'", fakeApi, map[string][]string{"dc": {"west"}, "env": {"production", "staging"}, "host": {"a", "b"}}}, {"describe`series_0`where(dc='west')", fakeApi, map[string][]string{"dc": {"west"}, "env": {"production", "staging"}, "host": {"a", "b"}}}, {"describe series_0 where dc='west' or env = 'production'", fakeApi, map[string][]string{"dc": {"east", "west"}, "env": {"production", "staging"}, "host": {"a", "b", "c"}}}, {"describe series_0 where`dc`='west'or`env`='production'", fakeApi, map[string][]string{"dc": {"east", "west"}, "env": {"production", "staging"}, "host": {"a", "b", "c"}}}, {"describe series_0 where dc='west' or env = 'production' and doesnotexist = ''", fakeApi, map[string][]string{"dc": {"west"}, "env": {"production", "staging"}, "host": {"a", "b"}}}, {"describe series_0 where env = 'production' and doesnotexist = '' or dc = 'west'", fakeApi, map[string][]string{"dc": {"west"}, "env": {"production", "staging"}, "host": {"a", "b"}}}, {"describe series_0 where (dc='west' or env = 'production') and doesnotexist = ''", fakeApi, map[string][]string{}}, {"describe series_0 where(dc='west' or env = 'production')and`doesnotexist` = ''", fakeApi, map[string][]string{}}, } { a := assert.New(t).Contextf("query=%s", test.query) command, err := Parse(test.query) if err != nil { a.Errorf("Unexpected error while parsing") continue } a.EqString(command.Name(), "describe") rawResult, _ := command.Execute(ExecutionContext{Backend: nil, API: test.backend, FetchLimit: 1000, Timeout: 0}) a.Eq(rawResult, test.expected) } }
func TestCommand_Describe(t *testing.T) { fakeApi := mocks.NewFakeApi() fakeApi.AddPair(api.TaggedMetric{"series_0", api.ParseTagSet("dc=west,env=production,host=a")}, emptyGraphiteName) fakeApi.AddPair(api.TaggedMetric{"series_0", api.ParseTagSet("dc=west,env=staging,host=b")}, emptyGraphiteName) fakeApi.AddPair(api.TaggedMetric{"series_0", api.ParseTagSet("dc=east,env=production,host=c")}, emptyGraphiteName) fakeApi.AddPair(api.TaggedMetric{"series_0", api.ParseTagSet("dc=east,env=staging,host=d")}, emptyGraphiteName) for _, test := range []struct { query string backend api.API length int // expected length of the result. }{ {"describe series_0", fakeApi, 4}, {"describe`series_0`", fakeApi, 4}, {"describe series_0 where dc='west'", fakeApi, 2}, {"describe`series_0`where(dc='west')", fakeApi, 2}, {"describe series_0 where dc='west' or env = 'production'", fakeApi, 3}, {"describe series_0 where`dc`='west'or`env`='production'", fakeApi, 3}, {"describe series_0 where dc='west' or env = 'production' and doesnotexist = ''", fakeApi, 2}, {"describe series_0 where env = 'production' and doesnotexist = '' or dc = 'west'", fakeApi, 2}, {"describe series_0 where (dc='west' or env = 'production') and doesnotexist = ''", fakeApi, 0}, {"describe series_0 where(dc='west' or env = 'production')and`doesnotexist` = ''", fakeApi, 0}, } { a := assert.New(t).Contextf("query=%s", test.query) command, err := Parse(test.query) if err != nil { a.Errorf("Unexpected error while parsing") continue } a.EqString(command.Name(), "describe") rawResult, _ := command.Execute(ExecutionContext{Backend: nil, API: test.backend, FetchLimit: 1000, Timeout: 0}) parsedResult := rawResult.([]string) a.EqInt(len(parsedResult), test.length) } }
func TestCommand_DescribeAll(t *testing.T) { fakeApi := mocks.NewFakeApi() fakeApi.AddPair(api.TaggedMetric{"series_0", api.ParseTagSet("")}, emptyGraphiteName) fakeApi.AddPair(api.TaggedMetric{"series_1", api.ParseTagSet("")}, emptyGraphiteName) fakeApi.AddPair(api.TaggedMetric{"series_2", api.ParseTagSet("")}, emptyGraphiteName) fakeApi.AddPair(api.TaggedMetric{"series_3", api.ParseTagSet("")}, emptyGraphiteName) for _, test := range []struct { query string backend api.API expected []api.MetricKey }{ {"describe all", fakeApi, []api.MetricKey{"series_0", "series_1", "series_2", "series_3"}}, {"describe all match '_0'", fakeApi, []api.MetricKey{"series_0"}}, {"describe all match '_5'", fakeApi, []api.MetricKey{}}, } { a := assert.New(t).Contextf("query=%s", test.query) command, err := Parse(test.query) if err != nil { a.Errorf("Unexpected error while parsing") continue } a.EqString(command.Name(), "describe all") rawResult, err := command.Execute(ExecutionContext{Backend: nil, API: test.backend, FetchLimit: 1000, Timeout: 0}) a.CheckError(err) a.Eq(rawResult, test.expected) } }
func Test_TagIndex(t *testing.T) { a := assert.New(t) db := newDatabase(t) if db == nil { return } defer cleanDatabase(t, db) if db == nil { return } if rows, err := db.GetMetricKeys("environment", "production"); err != nil { a.CheckError(err) } else { a.EqInt(len(rows), 0) } a.CheckError(db.AddToTagIndex("environment", "production", "a.b.c")) a.CheckError(db.AddToTagIndex("environment", "production", "d.e.f")) if rows, err := db.GetMetricKeys("environment", "production"); err != nil { a.CheckError(err) } else { a.EqInt(len(rows), 2) } a.CheckError(db.RemoveFromTagIndex("environment", "production", "a.b.c")) if rows, err := db.GetMetricKeys("environment", "production"); err != nil { a.CheckError(err) } else { a.EqInt(len(rows), 1) a.EqString(string(rows[0]), "d.e.f") } }
func TestFunctionName(t *testing.T) { a := assert.New(t) a.EqString(functionName(0), "TestFunctionName") first, second := testFunction1() a.EqString(first, "testFunction1") a.EqString(second, "TestFunctionName") }
func TestCompile_Good(t *testing.T) { a := assert.New(t) _, err := Compile(RawRule{ Pattern: "prefix.%foo%", MetricKeyPattern: "test-metric", }) a.CheckError(err) }
func checkConversionErrorCode(t *testing.T, err error, expected ConversionErrorCode) { casted, ok := err.(ConversionError) if !ok { t.Errorf("Invalid Error type") return } a := assert.New(t) a.EqInt(int(casted.Code()), int(expected)) }
func TestCompile(t *testing.T) { for _, row := range inputs { a := assert.New(t).Contextf(row) p := Parser{Buffer: row} p.Init() a.CheckError(p.Parse()) p.Execute() testParserResult(a, p) } }
func TestUnescapeLiteral(t *testing.T) { a := assert.New(t) a.EqString(unescapeLiteral("'foo'"), "foo") a.EqString(unescapeLiteral("foo"), "foo") a.EqString(unescapeLiteral("nodes.cpu.io"), "nodes.cpu.io") a.EqString(unescapeLiteral(`"hello"`), `hello`) a.EqString(unescapeLiteral(`"\"hello\""`), `"hello"`) a.EqString(unescapeLiteral(`'\"hello\"'`), `"hello"`) a.EqString(unescapeLiteral("\"\\`\""), "`") }
func Test_Registry_Default(t *testing.T) { a := assert.New(t) sr := StandardRegistry{mapping: make(map[string]function.MetricFunction)} a.Eq(sr.All(), []string{}) if err := sr.Register(function.MetricFunction{Name: "foo", Compute: dummyCompute}); err != nil { a.CheckError(err) } if err := sr.Register(function.MetricFunction{Name: "bar", Compute: dummyCompute}); err != nil { a.CheckError(err) } a.Eq(sr.All(), []string{"bar", "foo"}) }
func TestRandom(t *testing.T) { a := assert.New(t) expected := []string{"Apple", "apple", "file2", "file22", "file90", "file99", "file100", "Zoo", "zoo"} test := []string{"Apple", "apple", "file2", "file22", "file90", "file99", "file100", "Zoo", "zoo"} Sort(test) a.Eq(test, expected) for i := 0; i < 1000; i++ { testShuffle(test) a := a.Contextf("input: %+v", test) Sort(test) a.Eq(test, expected) } }
func TestLoadYAML_Invalid(t *testing.T) { a := assert.New(t) rawYAML := ` rules - pattern: foo.bar.baz.%tag% metric_key: abc regex: {} ` ruleSet, err := LoadYAML([]byte(rawYAML)) checkRuleErrorCode(a, err, InvalidYaml) a.EqInt(len(ruleSet.rules), 0) }
func TestNaturalSort(t *testing.T) { a := assert.New(t) expected := []string{"Apple", "apple", "file2", "file22", "file90", "file99", "file100", "Zoo", "zoo"} tests := [][]string{ {"Apple", "apple", "file2", "file90", "file99", "file100", "Zoo", "zoo", "file22"}, {"Zoo", "Apple", "apple", "file100", "file2", "file90", "file99", "zoo", "file22"}, {"file2", "file90", "apple", "Zoo", "file100", "file22", "file99", "zoo", "Apple"}, } Sort([]string{}) // check that no panic occurs for _, test := range tests { Sort(test) a.Eq(test, expected) } }
func Test_FetchCounter(t *testing.T) { c := NewFetchCounter(10) a := assert.New(t) a.EqInt(c.Current(), 0) a.EqInt(c.Limit(), 10) a.EqBool(c.Consume(5), true) a.EqInt(c.Current(), 5) a.EqBool(c.Consume(4), true) a.EqInt(c.Current(), 9) a.EqBool(c.Consume(1), true) a.EqInt(c.Current(), 10) a.EqBool(c.Consume(1), false) a.EqInt(c.Current(), 11) }
func TestLoadYAML(t *testing.T) { a := assert.New(t) rawYAML := ` rules: - pattern: foo.bar.baz.%tag% metric_key: abc regex: {} ` ruleSet, err := LoadYAML([]byte(rawYAML)) a.CheckError(err) a.EqInt(len(ruleSet.rules), 1) a.EqString(string(ruleSet.rules[0].raw.MetricKeyPattern), "abc") a.Eq(ruleSet.rules[0].graphitePatternTags, []string{"tag"}) }
func Test_ParallelMultiBackend_Success(t *testing.T) { a := assert.New(t) suite := newSuite() defer suite.cleanup() go func() { _, err := suite.multiBackend.FetchMultipleSeries(api.FetchMultipleRequest{ Metrics: []api.TaggedMetric{api.TaggedMetric{"a", api.NewTagSet()}}, Cancellable: suite.cancellable, }) a.CheckError(err) suite.waitGroup.Done() }() suite.backend.tickets <- struct{}{} suite.waitGroup.Wait() }
func TestToGraphiteName(t *testing.T) { a := assert.New(t) rule, err := Compile(RawRule{ Pattern: "prefix.%foo%", MetricKeyPattern: "test-metric", }) a.CheckError(err) tm := api.TaggedMetric{ MetricKey: "test-metric", TagSet: api.ParseTagSet("foo=fooValue"), } reversed, err := rule.ToGraphiteName(tm) a.CheckError(err) a.EqString(string(reversed), "prefix.fooValue") }
func TestCompile_Error(t *testing.T) { for _, test := range []struct { rawRule RawRule expectedCode RuleErrorCode }{ {RawRule{Pattern: "prefix.%foo%", MetricKeyPattern: ""}, InvalidMetricKey}, {RawRule{Pattern: "prefix.%foo%abc%", MetricKeyPattern: "test-metric"}, InvalidPattern}, {RawRule{Pattern: "", MetricKeyPattern: "test-metric"}, InvalidPattern}, {RawRule{Pattern: "prefix.%foo%.%foo%", MetricKeyPattern: "test-metric"}, InvalidPattern}, {RawRule{Pattern: "prefix.%foo%.abc.%%", MetricKeyPattern: "test-metric"}, InvalidPattern}, {RawRule{Pattern: "prefix.%foo%", MetricKeyPattern: "test-metric", Regex: map[string]string{"foo": "(bar)"}}, InvalidCustomRegex}, } { _, err := Compile(test.rawRule) a := assert.New(t).Contextf("%s", test.rawRule.Pattern) checkRuleErrorCode(a, err, test.expectedCode) } }
func TestTimeseries_Downsample(t *testing.T) { a := assert.New(t) for _, suite := range []struct { input []float64 inputRange Timerange newRange Timerange sampler func([]float64) float64 expected []float64 }{ {[]float64{1, 2, 3, 4, 5}, Timerange{0, 4, 1}, Timerange{0, 4, 2}, max, []float64{2, 4, 5}}, } { tagset := ParseTagSet("key=value") ts := Timeseries{suite.input, tagset} sampled, err := ts.downsample(suite.inputRange, suite.newRange, suite.sampler) a.CheckError(err) a.Eq(sampled.Values, suite.expected) } }
func Test_ScalarExpression(t *testing.T) { timerangeA, err := api.NewTimerange(0, 10, 2) if err != nil { t.Fatalf("invalid timerange used for testcase") return } for _, test := range []struct { expr scalarExpression timerange api.Timerange expectedSeries []api.Timeseries }{ { scalarExpression{5}, timerangeA, []api.Timeseries{ api.Timeseries{ []float64{5.0, 5.0, 5.0, 5.0, 5.0, 5.0}, api.NewTagSet(), }, }, }, } { a := assert.New(t).Contextf("%+v", test) result, err := evaluateToSeriesList(test.expr, function.EvaluationContext{ MultiBackend: backend.NewSequentialMultiBackend(FakeBackend{}), Timerange: test.timerange, SampleMethod: api.SampleMean, FetchLimit: function.NewFetchCounter(1000), Registry: registry.Default(), }) if err != nil { t.Fatalf("failed to convert number into serieslist") } a.EqInt(len(result.Series), len(test.expectedSeries)) for i := 0; i < len(result.Series); i += 1 { a.Eq(result.Series[i].Values, test.expectedSeries[i].Values) } } }
func Test_GetAllMetrics(t *testing.T) { a := assert.New(t) db := newDatabase(t) if db == nil { return } defer cleanDatabase(t, db) a.CheckError(db.AddMetricName("metric.a", api.ParseTagSet("foo=a"))) a.CheckError(db.AddMetricName("metric.a", api.ParseTagSet("foo=b"))) keys, err := db.GetAllMetrics() a.CheckError(err) sort.Sort(api.MetricKeys(keys)) a.Eq(keys, []api.MetricKey{"metric.a"}) a.CheckError(db.AddMetricName("metric.b", api.ParseTagSet("foo=c"))) a.CheckError(db.AddMetricName("metric.b", api.ParseTagSet("foo=c"))) keys, err = db.GetAllMetrics() a.CheckError(err) sort.Sort(api.MetricKeys(keys)) a.Eq(keys, []api.MetricKey{"metric.a", "metric.b"}) }
func TestMatchRule_FilterTag(t *testing.T) { a := assert.New(t) rule, err := Compile(RawRule{ Pattern: "prefix.%foo%.%bar%", MetricKeyPattern: "test-metric.%bar%", }) a.CheckError(err) originalName := "prefix.fooValue.barValue" matcher, matched := rule.MatchRule(originalName) if !matched { t.Errorf("Expected matching but didn't occur") return } a.EqString(string(matcher.MetricKey), "test-metric.barValue") a.Eq(matcher.TagSet, api.TagSet(map[string]string{"foo": "fooValue"})) // perform the reverse. reversed, err := rule.ToGraphiteName(matcher) a.CheckError(err) a.EqString(string(reversed), originalName) }
func TestToGraphiteName_Error(t *testing.T) { a := assert.New(t) rule, err := Compile(RawRule{ Pattern: "prefix.%foo%", MetricKeyPattern: "test-metric", }) a.CheckError(err) reversed, err := rule.ToGraphiteName(api.TaggedMetric{ MetricKey: "test-metric", TagSet: api.ParseTagSet(""), }) checkConversionErrorCode(t, err, MissingTag) a.EqString(string(reversed), "") reversed, err = rule.ToGraphiteName(api.TaggedMetric{ MetricKey: "test-metric-foo", TagSet: api.ParseTagSet("foo=fooValue"), }) checkConversionErrorCode(t, err, CannotInterpolate) a.EqString(string(reversed), "") }
func Test_Registry_Error(t *testing.T) { for _, suite := range []struct { Name string Function function.MetricFunction }{ {"empty name", function.MetricFunction{Name: "", Compute: dummyCompute}}, {"duplicate name", function.MetricFunction{Name: "existing", Compute: dummyCompute}}, {"no compute", function.MetricFunction{Name: "notexisting", Compute: nil}}, } { a := assert.New(t).Contextf("%s", suite.Name) // set up the standard registry sr := StandardRegistry{mapping: make(map[string]function.MetricFunction)} if err := sr.Register(function.MetricFunction{Name: "existing", Compute: dummyCompute}); err != nil { a.CheckError(err) return } if err := sr.Register(suite.Function); err == nil { a.Errorf("Expected error, but got none.") } } }
func TestMatchRule_Simple(t *testing.T) { a := assert.New(t) rule, err := Compile(RawRule{ Pattern: "prefix.%foo%", MetricKeyPattern: "test-metric", }) a.CheckError(err) _, matches := rule.MatchRule("") if matches { t.Errorf("Unexpected matching") } matcher, matches := rule.MatchRule("prefix.abc") if !matches { t.Errorf("Expected matching but didn't occur") } a.EqString(string(matcher.MetricKey), "test-metric") a.EqString(matcher.TagSet["foo"], "abc") _, matches = rule.MatchRule("prefix.abc.def") if matches { t.Errorf("Unexpected matching") } }
func TestMatchRule_CustomRegex(t *testing.T) { a := assert.New(t) regex := make(map[string]string) regex["name"] = "[a-z]+" regex["shard"] = "[0-9]+" rule, err := Compile(RawRule{ Pattern: "feed.%name%-shard-%shard%", MetricKeyPattern: "test-feed-metric", Regex: regex, }) a.CheckError(err) _, matches := rule.MatchRule("") if matches { t.Errorf("Unexpected matching") } matcher, matches := rule.MatchRule("feed.feedname-shard-12") if !matches { t.Errorf("Expected matching but didn't occur") } a.EqString(string(matcher.MetricKey), "test-feed-metric") a.EqString(matcher.TagSet["name"], "feedname") a.EqString(matcher.TagSet["shard"], "12") }
func Test_ParallelMultiBackend_Timeout(t *testing.T) { a := assert.New(t) suite := newSuite() defer suite.cleanup() go func() { _, err := suite.multiBackend.FetchMultipleSeries(api.FetchMultipleRequest{ Metrics: []api.TaggedMetric{api.TaggedMetric{"a", api.NewTagSet()}}, Cancellable: suite.cancellable, }) if err == nil { t.Errorf("Error expected, but got nil") } else { casted, ok := err.(api.BackendError) if !ok { t.Errorf("Invalid error type") } else { a.Eq(casted.Code, api.FetchTimeoutError) } } suite.waitGroup.Done() }() close(suite.cancellable.Done()) suite.waitGroup.Wait() }
func Test_MetricName_GetTagSet(t *testing.T) { a := assert.New(t) db := newDatabase(t) if db == nil { return } defer cleanDatabase(t, db) if db == nil { return } if tags, err := db.GetTagSet("sample"); err != nil { t.Errorf("Error fetching tags from Cassandra") } else { a.EqInt(len(tags), 0) } metricNamesTests := []struct { addTest bool metricName string tagString string expectedTags map[string][]string // { metricName: [ tags ] } }{ {true, "sample", "foo=bar1", map[string][]string{ "sample": []string{"foo=bar1"}, }}, {true, "sample", "foo=bar2", map[string][]string{ "sample": []string{"foo=bar1", "foo=bar2"}, }}, {true, "sample2", "foo=bar2", map[string][]string{ "sample": []string{"foo=bar1", "foo=bar2"}, "sample2": []string{"foo=bar2"}, }}, {false, "sample2", "foo=bar2", map[string][]string{ "sample": []string{"foo=bar1", "foo=bar2"}, }}, {false, "sample", "foo=bar1", map[string][]string{ "sample": []string{"foo=bar2"}, }}, } for _, c := range metricNamesTests { if c.addTest { a.CheckError(db.AddMetricName(api.MetricKey(c.metricName), api.ParseTagSet(c.tagString))) } else { a.CheckError(db.RemoveMetricName(api.MetricKey(c.metricName), api.ParseTagSet(c.tagString))) } for k, v := range c.expectedTags { if tags, err := db.GetTagSet(api.MetricKey(k)); err != nil { t.Errorf("Error fetching tags") } else { stringTags := make([]string, len(tags)) for i, tag := range tags { stringTags[i] = tag.Serialize() } a.EqInt(len(stringTags), len(v)) sort.Sort(sort.StringSlice(stringTags)) sort.Sort(sort.StringSlice(v)) a.Eq(stringTags, v) } } } }
func TestTag(t *testing.T) { fakeApi := mocks.NewFakeApi() fakeBackend := backend.NewSequentialMultiBackend(fakeApiBackend{}) tests := []struct { query string expected api.SeriesList }{ { query: "select series_2 | tag.drop('dc') from 0 to 120", expected: api.SeriesList{ Series: []api.Timeseries{ { Values: []float64{1, 2, 3, 4, 5}, TagSet: api.TagSet{}, }, { Values: []float64{3, 0, 3, 6, 2}, TagSet: api.TagSet{}, }, }, }, }, { query: "select series_2 | tag.drop('none') from 0 to 120", expected: api.SeriesList{ Series: []api.Timeseries{ { Values: []float64{1, 2, 3, 4, 5}, TagSet: api.TagSet{"dc": "west"}, }, { Values: []float64{3, 0, 3, 6, 2}, TagSet: api.TagSet{"dc": "east"}, }, }, }, }, { query: "select series_2 | tag.set('dc', 'north') from 0 to 120", expected: api.SeriesList{ Series: []api.Timeseries{ { Values: []float64{1, 2, 3, 4, 5}, TagSet: api.TagSet{"dc": "north"}, }, { Values: []float64{3, 0, 3, 6, 2}, TagSet: api.TagSet{"dc": "north"}, }, }, }, }, { query: "select series_2 | tag.set('none', 'north') from 0 to 120", expected: api.SeriesList{ Series: []api.Timeseries{ { Values: []float64{1, 2, 3, 4, 5}, TagSet: api.TagSet{"dc": "west", "none": "north"}, }, { Values: []float64{3, 0, 3, 6, 2}, TagSet: api.TagSet{"dc": "east", "none": "north"}, }, }, }, }, } for _, test := range tests { command, err := Parse(test.query) if err != nil { t.Fatalf("Unexpected error while parsing") return } if command.Name() != "select" { t.Errorf("Expected select command but got %s", command.Name()) continue } rawResult, err := command.Execute(ExecutionContext{Backend: fakeBackend, API: fakeApi, FetchLimit: 1000, Timeout: 0}) if err != nil { t.Errorf("Unexpected error while execution: %s", err.Error()) continue } seriesListList, ok := rawResult.([]api.SeriesList) if !ok || len(seriesListList) != 1 { t.Errorf("expected query `%s` to produce []value; got %+v :: %T", test.query, rawResult, rawResult) continue } list := seriesListList[0] if err != nil { t.Fatal(err) } a := assert.New(t) expectedSeries := test.expected.Series for i, series := range list.Series { a.EqFloatArray(series.Values, expectedSeries[i].Values, 1e-100) if !series.TagSet.Equals(expectedSeries[i].TagSet) { t.Errorf("expected tagset %+v but got %+v for series %d of query %s", expectedSeries[i].TagSet, series.TagSet, i, test.query) } } } }
func TestCommand_Select(t *testing.T) { epsilon := 1e-10 fakeApi := mocks.NewFakeApi() fakeApi.AddPair(api.TaggedMetric{"series_1", api.ParseTagSet("dc=west")}, emptyGraphiteName) fakeApi.AddPair(api.TaggedMetric{"series_2", api.ParseTagSet("dc=east")}, emptyGraphiteName) fakeApi.AddPair(api.TaggedMetric{"series_2", api.ParseTagSet("dc=west")}, emptyGraphiteName) fakeApi.AddPair(api.TaggedMetric{"series_3", api.ParseTagSet("dc=west")}, emptyGraphiteName) fakeApi.AddPair(api.TaggedMetric{"series_3", api.ParseTagSet("dc=east")}, emptyGraphiteName) fakeApi.AddPair(api.TaggedMetric{"series_3", api.ParseTagSet("dc=north")}, emptyGraphiteName) fakeApi.AddPair(api.TaggedMetric{"series_timeout", api.ParseTagSet("dc=west")}, emptyGraphiteName) var fakeBackend fakeApiBackend testTimerange, err := api.NewTimerange(0, 120, 30) if err != nil { t.Errorf("Invalid test timerange") return } earlyTimerange, err := api.NewTimerange(0, 60, 30) if err != nil { t.Errorf("Invalid test timerange") } lateTimerange, err := api.NewTimerange(60, 120, 30) if err != nil { t.Errorf("Invalid test timerange") } for _, test := range []struct { query string expectError bool expected api.SeriesList }{ {"select does_not_exist from 0 to 120 resolution 30ms", true, api.SeriesList{}}, {"select series_1 from 0 to 120 resolution 30ms", false, api.SeriesList{ Series: []api.Timeseries{{ []float64{1, 2, 3, 4, 5}, api.ParseTagSet("dc=west"), }}, Timerange: testTimerange, Name: "series_1", }}, {"select series_timeout from 0 to 120 resolution 30ms", true, api.SeriesList{}}, {"select series_1 + 1 from 0 to 120 resolution 30ms", false, api.SeriesList{ Series: []api.Timeseries{{ []float64{2, 3, 4, 5, 6}, api.ParseTagSet("dc=west"), }}, Timerange: testTimerange, Name: "", }}, {"select series_1 * 2 from 0 to 120 resolution 30ms", false, api.SeriesList{ Series: []api.Timeseries{{ []float64{2, 4, 6, 8, 10}, api.ParseTagSet("dc=west"), }}, Timerange: testTimerange, Name: "", }}, {"select aggregate.max(series_2) from 0 to 120 resolution 30ms", false, api.SeriesList{ Series: []api.Timeseries{{ []float64{3, 2, 3, 6, 5}, api.NewTagSet(), }}, Timerange: testTimerange, Name: "series_2", }}, {"select (1 + series_2) | aggregate.max from 0 to 120 resolution 30ms", false, api.SeriesList{ Series: []api.Timeseries{{ []float64{4, 3, 4, 7, 6}, api.NewTagSet(), }}, Timerange: testTimerange, Name: "series_2", }}, {"select series_1 from 0 to 60 resolution 30ms", false, api.SeriesList{ Series: []api.Timeseries{{ []float64{1, 2, 3}, api.ParseTagSet("dc=west"), }}, Timerange: earlyTimerange, Name: "series_1", }}, {"select transform.timeshift(series_1,31ms) from 0 to 60 resolution 30ms", false, api.SeriesList{ Series: []api.Timeseries{{ []float64{2, 3, 4}, api.ParseTagSet("dc=west"), }}, Timerange: earlyTimerange, Name: "series_1", }}, {"select transform.timeshift(series_1,62ms) from 0 to 60 resolution 30ms", false, api.SeriesList{ Series: []api.Timeseries{{ []float64{3, 4, 5}, api.ParseTagSet("dc=west"), }}, Timerange: earlyTimerange, Name: "series_1", }}, {"select transform.timeshift(series_1,29ms) from 0 to 60 resolution 30ms", false, api.SeriesList{ Series: []api.Timeseries{{ []float64{2, 3, 4}, api.ParseTagSet("dc=west"), }}, Timerange: earlyTimerange, Name: "series_1", }}, {"select transform.timeshift(series_1,-31ms) from 60 to 120 resolution 30ms", false, api.SeriesList{ Series: []api.Timeseries{{ []float64{2, 3, 4}, api.ParseTagSet("dc=west"), }}, Timerange: lateTimerange, Name: "series_1", }}, {"select transform.timeshift(series_1,-29ms) from 60 to 120 resolution 30ms", false, api.SeriesList{ Series: []api.Timeseries{{ []float64{2, 3, 4}, api.ParseTagSet("dc=west"), }}, Timerange: lateTimerange, Name: "series_1", }}, {"select series_3 from 0 to 120 resolution 30ms", false, api.SeriesList{ Series: []api.Timeseries{ { []float64{1, 1, 1, 4, 4}, api.ParseTagSet("dc=west"), }, { []float64{5, 5, 5, 2, 2}, api.ParseTagSet("dc=east"), }, { []float64{3, 3, 3, 3, 3}, api.ParseTagSet("dc=north"), }, }, }}, {"select series_3 | filter.recent_highest_max(3, 30ms) from 0 to 120 resolution 30ms", false, api.SeriesList{ Series: []api.Timeseries{ { []float64{1, 1, 1, 4, 4}, api.ParseTagSet("dc=west"), }, { []float64{3, 3, 3, 3, 3}, api.ParseTagSet("dc=north"), }, { []float64{5, 5, 5, 2, 2}, api.ParseTagSet("dc=east"), }, }, }}, {"select series_3 | filter.recent_highest_max(2, 30ms) from 0 to 120 resolution 30ms", false, api.SeriesList{ Series: []api.Timeseries{ { []float64{1, 1, 1, 4, 4}, api.ParseTagSet("dc=west"), }, { []float64{3, 3, 3, 3, 3}, api.ParseTagSet("dc=north"), }, }, }}, {"select series_3 | filter.recent_highest_max(1, 30ms) from 0 to 120 resolution 30ms", false, api.SeriesList{ Series: []api.Timeseries{ { []float64{1, 1, 1, 4, 4}, api.ParseTagSet("dc=west"), }, }, }}, {"select series_3 | filter.recent_lowest_max(3, 30ms) from 0 to 120 resolution 30ms", false, api.SeriesList{ Series: []api.Timeseries{ { []float64{5, 5, 5, 2, 2}, api.ParseTagSet("dc=east"), }, { []float64{3, 3, 3, 3, 3}, api.ParseTagSet("dc=north"), }, { []float64{1, 1, 1, 4, 4}, api.ParseTagSet("dc=west"), }, }, }}, {"select series_3 | filter.recent_lowest_max(4, 30ms) from 0 to 120 resolution 30ms", false, api.SeriesList{ Series: []api.Timeseries{ { []float64{5, 5, 5, 2, 2}, api.ParseTagSet("dc=east"), }, { []float64{3, 3, 3, 3, 3}, api.ParseTagSet("dc=north"), }, { []float64{1, 1, 1, 4, 4}, api.ParseTagSet("dc=west"), }, }, }}, {"select series_3 | filter.recent_highest_max(70, 30ms) from 0 to 120 resolution 30ms", false, api.SeriesList{ Series: []api.Timeseries{ { []float64{1, 1, 1, 4, 4}, api.ParseTagSet("dc=west"), }, { []float64{3, 3, 3, 3, 3}, api.ParseTagSet("dc=north"), }, { []float64{5, 5, 5, 2, 2}, api.ParseTagSet("dc=east"), }, }, }}, {"select series_3 | filter.recent_lowest_max(2, 30ms) from 0 to 120 resolution 30ms", false, api.SeriesList{ Series: []api.Timeseries{ { []float64{5, 5, 5, 2, 2}, api.ParseTagSet("dc=east"), }, { []float64{3, 3, 3, 3, 3}, api.ParseTagSet("dc=north"), }, }, }}, {"select series_3 | filter.recent_lowest_max(1, 30ms) from 0 to 120 resolution 30ms", false, api.SeriesList{ Series: []api.Timeseries{ { []float64{5, 5, 5, 2, 2}, api.ParseTagSet("dc=east"), }, }, }}, {"select series_3 | filter.recent_highest_max(3, 3000ms) from 0 to 120 resolution 30ms", false, api.SeriesList{ Series: []api.Timeseries{ { []float64{5, 5, 5, 2, 2}, api.ParseTagSet("dc=east"), }, { []float64{1, 1, 1, 4, 4}, api.ParseTagSet("dc=west"), }, { []float64{3, 3, 3, 3, 3}, api.ParseTagSet("dc=north"), }, }, }}, {"select series_3 | filter.recent_highest_max(2, 3000ms) from 0 to 120 resolution 30ms", false, api.SeriesList{ Series: []api.Timeseries{ { []float64{5, 5, 5, 2, 2}, api.ParseTagSet("dc=east"), }, { []float64{1, 1, 1, 4, 4}, api.ParseTagSet("dc=west"), }, }, }}, {"select series_3 | filter.recent_highest_max(1, 3000ms) from 0 to 120 resolution 30ms", false, api.SeriesList{ Series: []api.Timeseries{ { []float64{5, 5, 5, 2, 2}, api.ParseTagSet("dc=east"), }, }, }}, {"select series_3 | filter.recent_lowest_max(3, 3000ms) from 0 to 120 resolution 30ms", false, api.SeriesList{ Series: []api.Timeseries{ { []float64{3, 3, 3, 3, 3}, api.ParseTagSet("dc=north"), }, { []float64{1, 1, 1, 4, 4}, api.ParseTagSet("dc=west"), }, { []float64{5, 5, 5, 2, 2}, api.ParseTagSet("dc=east"), }, }, }}, {"select series_3 | filter.recent_lowest_max(2, 3000ms) from 0 to 120 resolution 30ms", false, api.SeriesList{ Series: []api.Timeseries{ { []float64{3, 3, 3, 3, 3}, api.ParseTagSet("dc=north"), }, { []float64{1, 1, 1, 4, 4}, api.ParseTagSet("dc=west"), }, }, }}, {"select series_3 | filter.recent_lowest_max(1, 3000ms) from 0 to 120 resolution 30ms", false, api.SeriesList{ Series: []api.Timeseries{ { []float64{3, 3, 3, 3, 3}, api.ParseTagSet("dc=north"), }, }, }}, {"select series_1 from -1000d to now resolution 30s", true, api.SeriesList{}}, } { a := assert.New(t).Contextf("query=%s", test.query) expected := test.expected command, err := Parse(test.query) if err != nil { a.Errorf("Unexpected error while parsing") continue } a.EqString(command.Name(), "select") rawResult, err := command.Execute(ExecutionContext{ Backend: backend.NewSequentialMultiBackend(fakeBackend), API: fakeApi, FetchLimit: 1000, Timeout: 10 * time.Millisecond, }) if err != nil { if !test.expectError { a.Errorf("Unexpected error while executing: %s", err.Error()) } } else { casted := rawResult.([]function.Value) actual, _ := casted[0].ToSeriesList(api.Timerange{}) a.EqInt(len(actual.Series), len(expected.Series)) if len(actual.Series) == len(expected.Series) { for i := 0; i < len(expected.Series); i++ { a.Eq(actual.Series[i].TagSet, expected.Series[i].TagSet) actualLength := len(actual.Series[i].Values) expectedLength := len(actual.Series[i].Values) a.Eq(actualLength, expectedLength) if actualLength == expectedLength { for j := 0; j < actualLength; j++ { a.EqFloat(actual.Series[i].Values[j], expected.Series[i].Values[j], epsilon) } } } } } } // Test that the limit is correct command, err := Parse("select series_1, series_2 from 0 to 120 resolution 30ms") if err != nil { t.Fatalf("Unexpected error while parsing") return } context := ExecutionContext{Backend: backend.NewSequentialMultiBackend(fakeBackend), API: fakeApi, FetchLimit: 3, Timeout: 0} _, err = command.Execute(context) if err != nil { t.Fatalf("expected success with limit 3 but got err = %s", err.Error()) return } context.FetchLimit = 2 _, err = command.Execute(context) if err == nil { t.Fatalf("expected failure with limit = 2") return } command, err = Parse("select series2 from 0 to 120 resolution 30ms") if err != nil { t.Fatalf("Unexpected error while parsing") return } _, err = command.Execute(context) if err != nil { t.Fatalf("expected success with limit = 2 but got %s", err.Error()) } }