1// Copyright 2013 The Prometheus Authors 2// Licensed under the Apache License, Version 2.0 (the "License"); 3// you may not use this file except in compliance with the License. 4// You may obtain a copy of the License at 5// 6// http://www.apache.org/licenses/LICENSE-2.0 7// 8// Unless required by applicable law or agreed to in writing, software 9// distributed under the License is distributed on an "AS IS" BASIS, 10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11// See the License for the specific language governing permissions and 12// limitations under the License. 13 14package rules 15 16import ( 17 "context" 18 "fmt" 19 "testing" 20 "time" 21 22 "github.com/prometheus/prometheus/pkg/labels" 23 "github.com/prometheus/prometheus/pkg/timestamp" 24 "github.com/prometheus/prometheus/promql" 25 "github.com/prometheus/prometheus/util/teststorage" 26 "github.com/prometheus/prometheus/util/testutil" 27) 28 29func TestRuleEval(t *testing.T) { 30 storage := teststorage.New(t) 31 defer storage.Close() 32 33 opts := promql.EngineOpts{ 34 Logger: nil, 35 Reg: nil, 36 MaxConcurrent: 10, 37 MaxSamples: 10, 38 Timeout: 10 * time.Second, 39 } 40 41 engine := promql.NewEngine(opts) 42 ctx, cancelCtx := context.WithCancel(context.Background()) 43 defer cancelCtx() 44 45 now := time.Now() 46 47 suite := []struct { 48 name string 49 expr promql.Expr 50 labels labels.Labels 51 result promql.Vector 52 }{ 53 { 54 name: "nolabels", 55 expr: &promql.NumberLiteral{Val: 1}, 56 labels: labels.Labels{}, 57 result: promql.Vector{promql.Sample{ 58 Metric: labels.FromStrings("__name__", "nolabels"), 59 Point: promql.Point{V: 1, T: timestamp.FromTime(now)}, 60 }}, 61 }, 62 { 63 name: "labels", 64 expr: &promql.NumberLiteral{Val: 1}, 65 labels: labels.FromStrings("foo", "bar"), 66 result: promql.Vector{promql.Sample{ 67 Metric: labels.FromStrings("__name__", "labels", "foo", "bar"), 68 Point: promql.Point{V: 1, T: timestamp.FromTime(now)}, 69 }}, 70 }, 71 } 72 73 for _, test := range suite { 74 rule := NewRecordingRule(test.name, test.expr, test.labels) 75 result, err := rule.Eval(ctx, now, EngineQueryFunc(engine, storage), nil) 76 testutil.Ok(t, err) 77 testutil.Equals(t, result, test.result) 78 } 79} 80 81func TestRecordingRuleHTMLSnippet(t *testing.T) { 82 expr, err := promql.ParseExpr(`foo{html="<b>BOLD<b>"}`) 83 testutil.Ok(t, err) 84 rule := NewRecordingRule("testrule", expr, labels.FromStrings("html", "<b>BOLD</b>")) 85 86 const want = `record: <a href="/test/prefix/graph?g0.expr=testrule&g0.tab=1">testrule</a> 87expr: <a href="/test/prefix/graph?g0.expr=foo%7Bhtml%3D%22%3Cb%3EBOLD%3Cb%3E%22%7D&g0.tab=1">foo{html="<b>BOLD<b>"}</a> 88labels: 89 html: '<b>BOLD</b>' 90` 91 92 got := rule.HTMLSnippet("/test/prefix") 93 testutil.Assert(t, want == got, "incorrect HTML snippet; want:\n\n%s\n\ngot:\n\n%s", want, got) 94} 95 96// TestRuleEvalDuplicate tests for duplicate labels in recorded metrics, see #5529. 97func TestRuleEvalDuplicate(t *testing.T) { 98 storage := teststorage.New(t) 99 defer storage.Close() 100 101 opts := promql.EngineOpts{ 102 Logger: nil, 103 Reg: nil, 104 MaxConcurrent: 10, 105 MaxSamples: 10, 106 Timeout: 10 * time.Second, 107 } 108 109 engine := promql.NewEngine(opts) 110 ctx, cancelCtx := context.WithCancel(context.Background()) 111 defer cancelCtx() 112 113 now := time.Now() 114 115 expr, _ := promql.ParseExpr(`vector(0) or label_replace(vector(0),"test","x","","")`) 116 rule := NewRecordingRule("foo", expr, labels.FromStrings("test", "test")) 117 _, err := rule.Eval(ctx, now, EngineQueryFunc(engine, storage), nil) 118 testutil.NotOk(t, err) 119 e := fmt.Errorf("vector contains metrics with the same labelset after applying rule labels") 120 testutil.ErrorEqual(t, e, err) 121} 122