2023-10-13 11:54:33 +00:00
|
|
|
package rule
|
2020-06-01 10:46:37 +00:00
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"reflect"
|
2020-06-09 12:21:20 +00:00
|
|
|
"sort"
|
2020-06-01 10:46:37 +00:00
|
|
|
"testing"
|
|
|
|
|
|
|
|
"github.com/VictoriaMetrics/VictoriaMetrics/app/vmalert/datasource"
|
|
|
|
"github.com/VictoriaMetrics/VictoriaMetrics/app/vmalert/notifier"
|
|
|
|
"github.com/VictoriaMetrics/VictoriaMetrics/lib/prompbmarshal"
|
|
|
|
)
|
|
|
|
|
2023-10-13 11:54:33 +00:00
|
|
|
// CompareRules is a test helper func for other tests
|
|
|
|
func CompareRules(t *testing.T, a, b Rule) error {
|
|
|
|
t.Helper()
|
|
|
|
switch v := a.(type) {
|
|
|
|
case *AlertingRule:
|
|
|
|
br, ok := b.(*AlertingRule)
|
|
|
|
if !ok {
|
|
|
|
return fmt.Errorf("rule %q supposed to be of type AlertingRule", b.ID())
|
|
|
|
}
|
|
|
|
return compareAlertingRules(t, v, br)
|
|
|
|
case *RecordingRule:
|
|
|
|
br, ok := b.(*RecordingRule)
|
|
|
|
if !ok {
|
|
|
|
return fmt.Errorf("rule %q supposed to be of type RecordingRule", b.ID())
|
|
|
|
}
|
|
|
|
return compareRecordingRules(t, v, br)
|
|
|
|
default:
|
|
|
|
return fmt.Errorf("unexpected rule type received %T", a)
|
2023-02-04 03:46:13 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-10-13 11:54:33 +00:00
|
|
|
func compareRecordingRules(t *testing.T, a, b *RecordingRule) error {
|
|
|
|
t.Helper()
|
|
|
|
if a.Expr != b.Expr {
|
|
|
|
return fmt.Errorf("expected to have expression %q; got %q", a.Expr, b.Expr)
|
2023-02-04 03:46:13 +00:00
|
|
|
}
|
2023-10-13 11:54:33 +00:00
|
|
|
if !reflect.DeepEqual(a.Labels, b.Labels) {
|
|
|
|
return fmt.Errorf("expected to have labels %#v; got %#v", a.Labels, b.Labels)
|
2023-03-01 14:48:20 +00:00
|
|
|
}
|
2020-06-01 10:46:37 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2023-10-13 11:54:33 +00:00
|
|
|
func compareAlertingRules(t *testing.T, a, b *AlertingRule) error {
|
|
|
|
t.Helper()
|
|
|
|
if a.Expr != b.Expr {
|
|
|
|
return fmt.Errorf("expected to have expression %q; got %q", a.Expr, b.Expr)
|
|
|
|
}
|
|
|
|
if a.For != b.For {
|
|
|
|
return fmt.Errorf("expected to have for %q; got %q", a.For, b.For)
|
2022-06-18 07:11:37 +00:00
|
|
|
}
|
2023-10-13 11:54:33 +00:00
|
|
|
if a.KeepFiringFor != b.KeepFiringFor {
|
|
|
|
return fmt.Errorf("expected to have KeepFiringFor %q; got %q", a.KeepFiringFor, b.KeepFiringFor)
|
|
|
|
}
|
|
|
|
if !reflect.DeepEqual(a.Annotations, b.Annotations) {
|
|
|
|
return fmt.Errorf("expected to have annotations %#v; got %#v", a.Annotations, b.Annotations)
|
|
|
|
}
|
|
|
|
if !reflect.DeepEqual(a.Labels, b.Labels) {
|
|
|
|
return fmt.Errorf("expected to have labels %#v; got %#v", a.Labels, b.Labels)
|
|
|
|
}
|
|
|
|
if a.Type.String() != b.Type.String() {
|
|
|
|
return fmt.Errorf("expected to have Type %#v; got %#v", a.Type.String(), b.Type.String())
|
|
|
|
}
|
|
|
|
return nil
|
2022-06-18 07:11:37 +00:00
|
|
|
}
|
|
|
|
|
2020-06-01 10:46:37 +00:00
|
|
|
func metricWithValueAndLabels(t *testing.T, value float64, labels ...string) datasource.Metric {
|
2021-06-09 09:20:38 +00:00
|
|
|
return metricWithValuesAndLabels(t, []float64{value}, labels...)
|
|
|
|
}
|
|
|
|
|
|
|
|
func metricWithValuesAndLabels(t *testing.T, values []float64, labels ...string) datasource.Metric {
|
2020-06-01 10:46:37 +00:00
|
|
|
t.Helper()
|
|
|
|
m := metricWithLabels(t, labels...)
|
2021-06-09 09:20:38 +00:00
|
|
|
m.Values = values
|
|
|
|
for i := range values {
|
|
|
|
m.Timestamps = append(m.Timestamps, int64(i))
|
|
|
|
}
|
2020-06-01 10:46:37 +00:00
|
|
|
return m
|
|
|
|
}
|
|
|
|
|
|
|
|
func metricWithLabels(t *testing.T, labels ...string) datasource.Metric {
|
|
|
|
t.Helper()
|
|
|
|
if len(labels) == 0 || len(labels)%2 != 0 {
|
|
|
|
t.Fatalf("expected to get even number of labels")
|
|
|
|
}
|
2021-06-09 09:20:38 +00:00
|
|
|
m := datasource.Metric{Values: []float64{1}, Timestamps: []int64{1}}
|
2020-06-01 10:46:37 +00:00
|
|
|
for i := 0; i < len(labels); i += 2 {
|
|
|
|
m.Labels = append(m.Labels, datasource.Label{
|
|
|
|
Name: labels[i],
|
|
|
|
Value: labels[i+1],
|
|
|
|
})
|
|
|
|
}
|
|
|
|
return m
|
|
|
|
}
|
|
|
|
|
2024-05-14 12:43:39 +00:00
|
|
|
func toPromLabels(t testing.TB, labels ...string) []prompbmarshal.Label {
|
2022-03-29 13:09:07 +00:00
|
|
|
t.Helper()
|
|
|
|
if len(labels) == 0 || len(labels)%2 != 0 {
|
|
|
|
t.Fatalf("expected to get even number of labels")
|
|
|
|
}
|
|
|
|
var ls []prompbmarshal.Label
|
|
|
|
for i := 0; i < len(labels); i += 2 {
|
|
|
|
ls = append(ls, prompbmarshal.Label{
|
|
|
|
Name: labels[i],
|
|
|
|
Value: labels[i+1],
|
|
|
|
})
|
|
|
|
}
|
|
|
|
return ls
|
|
|
|
}
|
|
|
|
|
2020-06-01 10:46:37 +00:00
|
|
|
func compareTimeSeries(t *testing.T, a, b []prompbmarshal.TimeSeries) error {
|
|
|
|
t.Helper()
|
|
|
|
if len(a) != len(b) {
|
|
|
|
return fmt.Errorf("expected number of timeseries %d; got %d", len(a), len(b))
|
|
|
|
}
|
|
|
|
for i := range a {
|
|
|
|
expTS, gotTS := a[i], b[i]
|
|
|
|
if len(expTS.Samples) != len(gotTS.Samples) {
|
|
|
|
return fmt.Errorf("expected number of samples %d; got %d", len(expTS.Samples), len(gotTS.Samples))
|
|
|
|
}
|
|
|
|
for i, exp := range expTS.Samples {
|
|
|
|
got := gotTS.Samples[i]
|
|
|
|
if got.Value != exp.Value {
|
|
|
|
return fmt.Errorf("expected value %.2f; got %.2f", exp.Value, got.Value)
|
|
|
|
}
|
|
|
|
// timestamp validation isn't always correct for now.
|
|
|
|
// this must be improved with time mock.
|
|
|
|
/*if got.Timestamp != exp.Timestamp {
|
|
|
|
return fmt.Errorf("expected timestamp %d; got %d", exp.Timestamp, got.Timestamp)
|
|
|
|
}*/
|
|
|
|
}
|
|
|
|
if len(expTS.Labels) != len(gotTS.Labels) {
|
2021-10-22 09:30:38 +00:00
|
|
|
return fmt.Errorf("expected number of labels %d (%v); got %d (%v)",
|
|
|
|
len(expTS.Labels), expTS.Labels, len(gotTS.Labels), gotTS.Labels)
|
2020-06-01 10:46:37 +00:00
|
|
|
}
|
|
|
|
for i, exp := range expTS.Labels {
|
|
|
|
got := gotTS.Labels[i]
|
|
|
|
if got.Name != exp.Name {
|
|
|
|
return fmt.Errorf("expected label name %q; got %q", exp.Name, got.Name)
|
|
|
|
}
|
|
|
|
if got.Value != exp.Value {
|
|
|
|
return fmt.Errorf("expected label value %q; got %q", exp.Value, got.Value)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
2020-06-09 12:21:20 +00:00
|
|
|
|
|
|
|
func compareAlerts(t *testing.T, as, bs []notifier.Alert) {
|
|
|
|
t.Helper()
|
|
|
|
if len(as) != len(bs) {
|
|
|
|
t.Fatalf("expected to have length %d; got %d", len(as), len(bs))
|
|
|
|
}
|
|
|
|
sort.Slice(as, func(i, j int) bool {
|
|
|
|
return as[i].ID < as[j].ID
|
|
|
|
})
|
|
|
|
sort.Slice(bs, func(i, j int) bool {
|
|
|
|
return bs[i].ID < bs[j].ID
|
|
|
|
})
|
|
|
|
for i := range as {
|
|
|
|
a, b := as[i], bs[i]
|
|
|
|
if a.Name != b.Name {
|
|
|
|
t.Fatalf("expected t have Name %q; got %q", a.Name, b.Name)
|
|
|
|
}
|
|
|
|
if a.State != b.State {
|
|
|
|
t.Fatalf("expected t have State %q; got %q", a.State, b.State)
|
|
|
|
}
|
|
|
|
if a.Value != b.Value {
|
|
|
|
t.Fatalf("expected t have Value %f; got %f", a.Value, b.Value)
|
|
|
|
}
|
|
|
|
if !reflect.DeepEqual(a.Annotations, b.Annotations) {
|
|
|
|
t.Fatalf("expected to have annotations %#v; got %#v", a.Annotations, b.Annotations)
|
|
|
|
}
|
|
|
|
if !reflect.DeepEqual(a.Labels, b.Labels) {
|
|
|
|
t.Fatalf("expected to have labels %#v; got %#v", a.Labels, b.Labels)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|