// Copyright 2013 The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package rules import ( "context" "fmt" "testing" "time" "github.com/prometheus/prometheus/pkg/labels" "github.com/prometheus/prometheus/pkg/timestamp" "github.com/prometheus/prometheus/promql" "github.com/prometheus/prometheus/util/teststorage" "github.com/prometheus/prometheus/util/testutil" ) func TestRuleEval(t *testing.T) { storage := teststorage.New(t) defer storage.Close() opts := promql.EngineOpts{ Logger: nil, Reg: nil, MaxConcurrent: 10, MaxSamples: 10, Timeout: 10 * time.Second, } engine := promql.NewEngine(opts) ctx, cancelCtx := context.WithCancel(context.Background()) defer cancelCtx() now := time.Now() suite := []struct { name string expr promql.Expr labels labels.Labels result promql.Vector }{ { name: "nolabels", expr: &promql.NumberLiteral{Val: 1}, labels: labels.Labels{}, result: promql.Vector{promql.Sample{ Metric: labels.FromStrings("__name__", "nolabels"), Point: promql.Point{V: 1, T: timestamp.FromTime(now)}, }}, }, { name: "labels", expr: &promql.NumberLiteral{Val: 1}, labels: labels.FromStrings("foo", "bar"), result: promql.Vector{promql.Sample{ Metric: labels.FromStrings("__name__", "labels", "foo", "bar"), Point: promql.Point{V: 1, T: timestamp.FromTime(now)}, }}, }, } for _, test := range suite { rule := NewRecordingRule(test.name, test.expr, test.labels) result, err := rule.Eval(ctx, now, EngineQueryFunc(engine, storage), nil) testutil.Ok(t, err) testutil.Equals(t, test.result, result) } } func TestRecordingRuleHTMLSnippet(t *testing.T) { expr, err := promql.ParseExpr(`foo{html="BOLD"}`) testutil.Ok(t, err) rule := NewRecordingRule("testrule", expr, labels.FromStrings("html", "BOLD")) const want = `record: testrule expr: foo{html="<b>BOLD<b>"} labels: html: '<b>BOLD</b>' ` got := rule.HTMLSnippet("/test/prefix") testutil.Assert(t, want == got, "incorrect HTML snippet; want:\n\n%s\n\ngot:\n\n%s", want, got) } // TestRuleEvalDuplicate tests for duplicate labels in recorded metrics, see #5529. func TestRuleEvalDuplicate(t *testing.T) { storage := teststorage.New(t) defer storage.Close() opts := promql.EngineOpts{ Logger: nil, Reg: nil, MaxConcurrent: 10, MaxSamples: 10, Timeout: 10 * time.Second, } engine := promql.NewEngine(opts) ctx, cancelCtx := context.WithCancel(context.Background()) defer cancelCtx() now := time.Now() expr, _ := promql.ParseExpr(`vector(0) or label_replace(vector(0),"test","x","","")`) rule := NewRecordingRule("foo", expr, labels.FromStrings("test", "test")) _, err := rule.Eval(ctx, now, EngineQueryFunc(engine, storage), nil) testutil.NotOk(t, err) e := fmt.Errorf("vector contains metrics with the same labelset after applying rule labels") testutil.ErrorEqual(t, e, err) }