github.com/thanos-io/thanos@v0.32.5/pkg/rules/rules_test.go (about)

     1  // Copyright (c) The Thanos Authors.
     2  // Licensed under the Apache License 2.0.
     3  
     4  package rules
     5  
     6  import (
     7  	"context"
     8  	"path"
     9  	"path/filepath"
    10  	"testing"
    11  	"time"
    12  
    13  	"github.com/gogo/protobuf/proto"
    14  	"github.com/prometheus/prometheus/model/labels"
    15  	"github.com/prometheus/prometheus/storage"
    16  
    17  	"github.com/efficientgo/core/testutil"
    18  	"github.com/thanos-io/thanos/pkg/rules/rulespb"
    19  	"github.com/thanos-io/thanos/pkg/store/labelpb"
    20  	"github.com/thanos-io/thanos/pkg/store/storepb"
    21  	"github.com/thanos-io/thanos/pkg/testutil/custom"
    22  )
    23  
    24  func TestMain(m *testing.M) {
    25  	custom.TolerantVerifyLeakMain(m)
    26  }
    27  
    28  // testRulesAgainstExamples tests against alerts.yaml and rules.yaml examples.
    29  func testRulesAgainstExamples(t *testing.T, dir string, server rulespb.RulesServer) {
    30  	t.Helper()
    31  
    32  	// We don't test internals, just if groups are expected.
    33  	// TODO(bwplotka): Test internals as well, especially labels!
    34  	someAlert := &rulespb.Rule{Result: &rulespb.Rule_Alert{Alert: &rulespb.Alert{Name: "some"}}}
    35  	someRecording := &rulespb.Rule{Result: &rulespb.Rule_Recording{Recording: &rulespb.RecordingRule{Name: "some"}}}
    36  
    37  	expected := []*rulespb.RuleGroup{
    38  		{
    39  			Name:                    "thanos-bucket-replicate",
    40  			File:                    filepath.Join(dir, "alerts.yaml"),
    41  			Rules:                   []*rulespb.Rule{someAlert, someAlert},
    42  			Interval:                60,
    43  			PartialResponseStrategy: storepb.PartialResponseStrategy_ABORT,
    44  		},
    45  		{
    46  			Name:                    "thanos-compact",
    47  			File:                    filepath.Join(dir, "alerts.yaml"),
    48  			Rules:                   []*rulespb.Rule{someAlert, someAlert, someAlert, someAlert, someAlert},
    49  			Interval:                60,
    50  			PartialResponseStrategy: storepb.PartialResponseStrategy_ABORT,
    51  		},
    52  		{
    53  			Name:                    "thanos-component-absent",
    54  			File:                    filepath.Join(dir, "alerts.yaml"),
    55  			Rules:                   []*rulespb.Rule{someAlert, someAlert, someAlert, someAlert, someAlert, someAlert},
    56  			Interval:                60,
    57  			PartialResponseStrategy: storepb.PartialResponseStrategy_ABORT,
    58  		},
    59  		{
    60  			Name: "thanos-query",
    61  			File: filepath.Join(dir, "alerts.yaml"),
    62  			Rules: []*rulespb.Rule{
    63  				someAlert, someAlert, someAlert, someAlert, someAlert, someAlert, someAlert, someAlert,
    64  			},
    65  			Interval:                60,
    66  			PartialResponseStrategy: storepb.PartialResponseStrategy_ABORT,
    67  		},
    68  		{
    69  			Name: "thanos-receive",
    70  			File: filepath.Join(dir, "alerts.yaml"),
    71  			Rules: []*rulespb.Rule{
    72  				someAlert, someAlert, someAlert, someAlert, someAlert, someAlert, someAlert, someAlert, someAlert, someAlert,
    73  			},
    74  			Interval:                60,
    75  			PartialResponseStrategy: storepb.PartialResponseStrategy_ABORT,
    76  		},
    77  		{
    78  			Name:                    "thanos-rule",
    79  			File:                    filepath.Join(dir, "alerts.yaml"),
    80  			Rules:                   []*rulespb.Rule{someAlert, someAlert, someAlert, someAlert, someAlert, someAlert, someAlert, someAlert, someAlert, someAlert, someAlert},
    81  			Interval:                60,
    82  			PartialResponseStrategy: storepb.PartialResponseStrategy_ABORT,
    83  		},
    84  		{
    85  			Name:                    "thanos-sidecar",
    86  			File:                    filepath.Join(dir, "alerts.yaml"),
    87  			Rules:                   []*rulespb.Rule{someAlert, someAlert},
    88  			Interval:                60,
    89  			PartialResponseStrategy: storepb.PartialResponseStrategy_ABORT,
    90  		},
    91  		{
    92  			Name: "thanos-store",
    93  			File: filepath.Join(dir, "alerts.yaml"),
    94  			Rules: []*rulespb.Rule{
    95  				someAlert, someAlert, someAlert, someAlert,
    96  			},
    97  			Interval:                60,
    98  			PartialResponseStrategy: storepb.PartialResponseStrategy_ABORT,
    99  		},
   100  		{
   101  			Name:                    "thanos-bucket-replicate.rules",
   102  			File:                    filepath.Join(dir, "rules.yaml"),
   103  			Rules:                   nil,
   104  			Interval:                60,
   105  			PartialResponseStrategy: storepb.PartialResponseStrategy_ABORT,
   106  		},
   107  		{
   108  			Name: "thanos-query.rules",
   109  			File: filepath.Join(dir, "rules.yaml"),
   110  			Rules: []*rulespb.Rule{
   111  				someRecording, someRecording, someRecording, someRecording, someRecording,
   112  			},
   113  			Interval:                60,
   114  			PartialResponseStrategy: storepb.PartialResponseStrategy_ABORT,
   115  		},
   116  		{
   117  			Name: "thanos-receive.rules",
   118  			File: filepath.Join(dir, "rules.yaml"),
   119  			Rules: []*rulespb.Rule{
   120  				someRecording, someRecording, someRecording, someRecording, someRecording, someRecording, someRecording,
   121  			},
   122  			Interval:                60,
   123  			PartialResponseStrategy: storepb.PartialResponseStrategy_ABORT,
   124  		},
   125  		{
   126  			Name: "thanos-store.rules",
   127  			File: filepath.Join(dir, "rules.yaml"),
   128  			Rules: []*rulespb.Rule{
   129  				someRecording, someRecording, someRecording, someRecording,
   130  			},
   131  			Interval:                60,
   132  			PartialResponseStrategy: storepb.PartialResponseStrategy_ABORT,
   133  		},
   134  	}
   135  
   136  	for _, tcase := range []struct {
   137  		requestedType rulespb.RulesRequest_Type
   138  		expectedErr   error
   139  	}{
   140  		{
   141  			requestedType: rulespb.RulesRequest_ALL,
   142  		},
   143  		{
   144  			requestedType: rulespb.RulesRequest_ALERT,
   145  		},
   146  		{
   147  			requestedType: rulespb.RulesRequest_RECORD,
   148  		},
   149  	} {
   150  		t.Run(tcase.requestedType.String(), func(t *testing.T) {
   151  			groups, w, err := NewGRPCClientWithDedup(server, nil).Rules(context.Background(), &rulespb.RulesRequest{
   152  				Type: tcase.requestedType,
   153  			})
   154  			testutil.Equals(t, storage.Warnings(nil), w)
   155  			if tcase.expectedErr != nil {
   156  				testutil.NotOk(t, err)
   157  				testutil.Equals(t, tcase.expectedErr.Error(), err.Error())
   158  				return
   159  			}
   160  			testutil.Ok(t, err)
   161  
   162  			expectedForType := expected
   163  			if tcase.requestedType != rulespb.RulesRequest_ALL {
   164  				expectedForType = make([]*rulespb.RuleGroup, len(expected))
   165  				for i, g := range expected {
   166  					expectedForType[i] = proto.Clone(g).(*rulespb.RuleGroup)
   167  					expectedForType[i].Rules = nil
   168  
   169  					for _, r := range g.Rules {
   170  						switch tcase.requestedType {
   171  						case rulespb.RulesRequest_ALERT:
   172  							if r.GetAlert() != nil {
   173  								expectedForType[i].Rules = append(expectedForType[i].Rules, someAlert)
   174  							}
   175  						case rulespb.RulesRequest_RECORD:
   176  							if r.GetRecording() != nil {
   177  								expectedForType[i].Rules = append(expectedForType[i].Rules, someRecording)
   178  							}
   179  						}
   180  					}
   181  				}
   182  			}
   183  
   184  			got := groups.Groups
   185  
   186  			// We don't want to be picky, just check what number and types of rules within group are.
   187  			for i := range got {
   188  				for j, r := range got[i].Rules {
   189  					if r.GetAlert() != nil {
   190  						got[i].Rules[j] = someAlert
   191  						continue
   192  					}
   193  					if r.GetRecording() != nil {
   194  						got[i].Rules[j] = someRecording
   195  						continue
   196  					}
   197  					t.Fatalf("Found rule in group %s that is neither recording not alert.", got[i].Name)
   198  				}
   199  				if len(got[i].Rules) == 0 {
   200  					// Fix, for test purposes.
   201  					got[i].Rules = nil
   202  				}
   203  				// Mask nondeterministic fields.
   204  				got[i].EvaluationDurationSeconds = 0
   205  				got[i].LastEvaluation = time.Time{}
   206  
   207  				t.Run(got[i].Name+" "+path.Base(got[i].File), func(t *testing.T) {
   208  					testutil.Equals(t, expectedForType[i], got[i])
   209  				})
   210  			}
   211  			testutil.Equals(t, expectedForType, got)
   212  		})
   213  	}
   214  }
   215  
   216  func TestDedupRules(t *testing.T) {
   217  	for _, tc := range []struct {
   218  		name          string
   219  		rules, want   []*rulespb.Rule
   220  		replicaLabels []string
   221  	}{
   222  		{
   223  			name:  "nil slice",
   224  			rules: nil,
   225  			want:  nil,
   226  		},
   227  		{
   228  			name:  "empty rule slice",
   229  			rules: []*rulespb.Rule{},
   230  			want:  []*rulespb.Rule{},
   231  		},
   232  		{
   233  			name: "single recording rule",
   234  			rules: []*rulespb.Rule{
   235  				rulespb.NewRecordingRule(&rulespb.RecordingRule{Name: "a1"}),
   236  			},
   237  			want: []*rulespb.Rule{
   238  				rulespb.NewRecordingRule(&rulespb.RecordingRule{Name: "a1"}),
   239  			},
   240  		},
   241  		{
   242  			name: "single alert",
   243  			rules: []*rulespb.Rule{
   244  				rulespb.NewAlertingRule(&rulespb.Alert{Name: "a1"}),
   245  			},
   246  			want: []*rulespb.Rule{
   247  				rulespb.NewAlertingRule(&rulespb.Alert{Name: "a1"}),
   248  			},
   249  		},
   250  		{
   251  			name: "rule type",
   252  			rules: []*rulespb.Rule{
   253  				rulespb.NewRecordingRule(&rulespb.RecordingRule{Name: "a1"}),
   254  				rulespb.NewAlertingRule(&rulespb.Alert{Name: "a1"}),
   255  			},
   256  			want: []*rulespb.Rule{
   257  				rulespb.NewAlertingRule(&rulespb.Alert{Name: "a1"}),
   258  				rulespb.NewRecordingRule(&rulespb.RecordingRule{Name: "a1"}),
   259  			},
   260  		},
   261  		{
   262  			name: "rule name",
   263  			rules: []*rulespb.Rule{
   264  				rulespb.NewRecordingRule(&rulespb.RecordingRule{Name: "a1"}),
   265  				rulespb.NewAlertingRule(&rulespb.Alert{Name: "a1"}),
   266  				rulespb.NewRecordingRule(&rulespb.RecordingRule{Name: "a1"}),
   267  				rulespb.NewAlertingRule(&rulespb.Alert{Name: "a1"}),
   268  			},
   269  			want: []*rulespb.Rule{
   270  				rulespb.NewAlertingRule(&rulespb.Alert{Name: "a1"}),
   271  				rulespb.NewRecordingRule(&rulespb.RecordingRule{Name: "a1"}),
   272  			},
   273  		},
   274  		{
   275  			name: "rule labels",
   276  			rules: []*rulespb.Rule{
   277  				rulespb.NewAlertingRule(&rulespb.Alert{
   278  					Name: "a1",
   279  					Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{
   280  						{Name: "a", Value: "1"},
   281  					}}}),
   282  				rulespb.NewRecordingRule(&rulespb.RecordingRule{
   283  					Name: "a1",
   284  					Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{
   285  						{Name: "a", Value: "1"},
   286  					}}}),
   287  				rulespb.NewRecordingRule(&rulespb.RecordingRule{
   288  					Name: "a1",
   289  					Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{
   290  						{Name: "a", Value: "1"},
   291  					}}}),
   292  				rulespb.NewAlertingRule(&rulespb.Alert{
   293  					Name: "a1",
   294  					Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{
   295  						{Name: "a", Value: "1"},
   296  					}}}),
   297  			},
   298  			want: []*rulespb.Rule{
   299  				rulespb.NewAlertingRule(&rulespb.Alert{
   300  					Name: "a1",
   301  					Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{
   302  						{Name: "a", Value: "1"},
   303  					}}}),
   304  				rulespb.NewRecordingRule(&rulespb.RecordingRule{
   305  					Name: "a1",
   306  					Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{
   307  						{Name: "a", Value: "1"},
   308  					}}}),
   309  			},
   310  		},
   311  		{
   312  			name: "rule expression",
   313  			rules: []*rulespb.Rule{
   314  				rulespb.NewRecordingRule(&rulespb.RecordingRule{
   315  					Name:  "a1",
   316  					Query: "up",
   317  					Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{
   318  						{Name: "a", Value: "1"},
   319  					}}}),
   320  				rulespb.NewRecordingRule(&rulespb.RecordingRule{
   321  					Name:  "a1",
   322  					Query: "up",
   323  					Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{
   324  						{Name: "a", Value: "1"},
   325  					}}}),
   326  				rulespb.NewAlertingRule(&rulespb.Alert{
   327  					Name:  "a1",
   328  					Query: "up",
   329  					Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{
   330  						{Name: "a", Value: "1"},
   331  					}}}),
   332  				rulespb.NewRecordingRule(&rulespb.RecordingRule{
   333  					Name: "a1",
   334  					Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{
   335  						{Name: "a", Value: "1"},
   336  					}}}),
   337  				rulespb.NewAlertingRule(&rulespb.Alert{
   338  					Name:  "a1",
   339  					Query: "up",
   340  					Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{
   341  						{Name: "a", Value: "1"},
   342  					}}}),
   343  			},
   344  			want: []*rulespb.Rule{
   345  				rulespb.NewAlertingRule(&rulespb.Alert{
   346  					Name:  "a1",
   347  					Query: "up",
   348  					Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{
   349  						{Name: "a", Value: "1"},
   350  					}}}),
   351  				rulespb.NewRecordingRule(&rulespb.RecordingRule{
   352  					Name: "a1", Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{
   353  						{Name: "a", Value: "1"},
   354  					}}}),
   355  				rulespb.NewRecordingRule(&rulespb.RecordingRule{
   356  					Name:  "a1",
   357  					Query: "up",
   358  					Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{
   359  						{Name: "a", Value: "1"},
   360  					}}}),
   361  			},
   362  		},
   363  		{
   364  			name: "alert duration",
   365  			rules: []*rulespb.Rule{
   366  				rulespb.NewRecordingRule(&rulespb.RecordingRule{
   367  					Name:  "a1",
   368  					Query: "up",
   369  					Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{
   370  						{Name: "a", Value: "1"},
   371  					}}}),
   372  				rulespb.NewRecordingRule(&rulespb.RecordingRule{
   373  					Name:  "a1",
   374  					Query: "up",
   375  					Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{
   376  						{Name: "a", Value: "1"},
   377  					}}}),
   378  				rulespb.NewAlertingRule(&rulespb.Alert{
   379  					Name:            "a1",
   380  					Query:           "up",
   381  					DurationSeconds: 1.0,
   382  					Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{
   383  						{Name: "a", Value: "1"},
   384  					}}}),
   385  				rulespb.NewRecordingRule(&rulespb.RecordingRule{
   386  					Name: "a1",
   387  					Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{
   388  						{Name: "a", Value: "1"},
   389  					}}}),
   390  				rulespb.NewAlertingRule(&rulespb.Alert{
   391  					Name:            "a1",
   392  					Query:           "up",
   393  					DurationSeconds: 1.0,
   394  					Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{
   395  						{Name: "a", Value: "1"},
   396  					}}}),
   397  				rulespb.NewAlertingRule(&rulespb.Alert{
   398  					Name:            "a1",
   399  					Query:           "up",
   400  					DurationSeconds: 2.0,
   401  					Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{
   402  						{Name: "a", Value: "1"},
   403  					}}}),
   404  			},
   405  			want: []*rulespb.Rule{
   406  				rulespb.NewAlertingRule(&rulespb.Alert{
   407  					Name:            "a1",
   408  					Query:           "up",
   409  					DurationSeconds: 1.0,
   410  					Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{
   411  						{Name: "a", Value: "1"},
   412  					}}}),
   413  				rulespb.NewAlertingRule(&rulespb.Alert{
   414  					Name:            "a1",
   415  					Query:           "up",
   416  					DurationSeconds: 2.0,
   417  					Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{
   418  						{Name: "a", Value: "1"},
   419  					}}}),
   420  				rulespb.NewRecordingRule(&rulespb.RecordingRule{
   421  					Name: "a1", Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{
   422  						{Name: "a", Value: "1"},
   423  					}}}),
   424  				rulespb.NewRecordingRule(&rulespb.RecordingRule{
   425  					Name: "a1", Query: "up", Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{
   426  						{Name: "a", Value: "1"},
   427  					}}}),
   428  			},
   429  		},
   430  		{
   431  			name: "alert duration with replicas",
   432  			rules: []*rulespb.Rule{
   433  				rulespb.NewAlertingRule(&rulespb.Alert{
   434  					Name:            "a1",
   435  					Query:           "up",
   436  					DurationSeconds: 1.0,
   437  					Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{
   438  						{Name: "a", Value: "1"},
   439  						{Name: "replica", Value: "1"},
   440  					}}}),
   441  				rulespb.NewAlertingRule(&rulespb.Alert{
   442  					Name:            "a1",
   443  					Query:           "up",
   444  					DurationSeconds: 2.0,
   445  					Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{
   446  						{Name: "a", Value: "1"},
   447  					}}}),
   448  			},
   449  			want: []*rulespb.Rule{
   450  				rulespb.NewAlertingRule(&rulespb.Alert{
   451  					Name:            "a1",
   452  					Query:           "up",
   453  					DurationSeconds: 1.0,
   454  					Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{
   455  						{Name: "a", Value: "1"},
   456  					}}}),
   457  				rulespb.NewAlertingRule(&rulespb.Alert{
   458  					Name:            "a1",
   459  					Query:           "up",
   460  					DurationSeconds: 2.0,
   461  					Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{
   462  						{Name: "a", Value: "1"},
   463  					}}}),
   464  			},
   465  			replicaLabels: []string{"replica"},
   466  		},
   467  		{
   468  			name: "replica labels",
   469  			rules: []*rulespb.Rule{
   470  				rulespb.NewRecordingRule(&rulespb.RecordingRule{Name: "a1", Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{
   471  					{Name: "a", Value: "1"},
   472  					{Name: "replica", Value: "3"},
   473  				}}}),
   474  				rulespb.NewRecordingRule(&rulespb.RecordingRule{Name: "a1", Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{
   475  					{Name: "a", Value: "1"},
   476  					{Name: "replica", Value: "1"},
   477  				}}}),
   478  				rulespb.NewRecordingRule(&rulespb.RecordingRule{Name: "a1", Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{
   479  					{Name: "a", Value: "1"},
   480  					{Name: "replica", Value: "2"},
   481  				}}}),
   482  			},
   483  			want: []*rulespb.Rule{
   484  				rulespb.NewRecordingRule(&rulespb.RecordingRule{Name: "a1", Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{
   485  					{Name: "a", Value: "1"},
   486  				}}}),
   487  			},
   488  			replicaLabels: []string{"replica"},
   489  		},
   490  		{
   491  			name: "ambiguous replica labels",
   492  			rules: []*rulespb.Rule{
   493  				rulespb.NewRecordingRule(&rulespb.RecordingRule{Name: "a1", Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{
   494  					{Name: "replica", Value: "1"},
   495  					{Name: "a", Value: "1"},
   496  				}}}),
   497  				rulespb.NewRecordingRule(&rulespb.RecordingRule{Name: "a1", Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{
   498  					{Name: "replica", Value: "1"},
   499  				}}}),
   500  				rulespb.NewRecordingRule(&rulespb.RecordingRule{Name: "a1", Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{
   501  					{Name: "replica", Value: "1"},
   502  					{Name: "a", Value: "2"},
   503  				}}}),
   504  				rulespb.NewRecordingRule(&rulespb.RecordingRule{Name: "a1", Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{
   505  					{Name: "replica", Value: "1"},
   506  					{Name: "a", Value: "1"},
   507  				}}}),
   508  			},
   509  			want: []*rulespb.Rule{
   510  				rulespb.NewRecordingRule(&rulespb.RecordingRule{Name: "a1"}),
   511  				rulespb.NewRecordingRule(&rulespb.RecordingRule{Name: "a1", Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{
   512  					{Name: "a", Value: "1"},
   513  				}}}),
   514  				rulespb.NewRecordingRule(&rulespb.RecordingRule{Name: "a1", Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{
   515  					{Name: "a", Value: "2"},
   516  				}}}),
   517  			},
   518  			replicaLabels: []string{"replica"},
   519  		},
   520  		{
   521  			name: "youngest recording rule",
   522  			rules: []*rulespb.Rule{
   523  				rulespb.NewRecordingRule(&rulespb.RecordingRule{
   524  					Name:  "a1",
   525  					Query: "up",
   526  					Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{
   527  						{Name: "replica", Value: "2"},
   528  					}},
   529  					LastEvaluation: time.Unix(0, 0),
   530  				}),
   531  				rulespb.NewRecordingRule(&rulespb.RecordingRule{
   532  					Name:  "a1",
   533  					Query: "up",
   534  					Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{
   535  						{Name: "replica", Value: "1"},
   536  					}},
   537  					LastEvaluation: time.Unix(1, 0),
   538  				}),
   539  				rulespb.NewRecordingRule(&rulespb.RecordingRule{
   540  					Name:  "a1",
   541  					Query: "up",
   542  					Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{
   543  						{Name: "replica", Value: "3"},
   544  					}},
   545  					LastEvaluation: time.Unix(3, 0),
   546  				}),
   547  				rulespb.NewRecordingRule(&rulespb.RecordingRule{
   548  					Name:           "a1",
   549  					Query:          "up",
   550  					LastEvaluation: time.Unix(2, 0),
   551  				}),
   552  			},
   553  			want: []*rulespb.Rule{
   554  				rulespb.NewRecordingRule(&rulespb.RecordingRule{
   555  					Name:           "a1",
   556  					Query:          "up",
   557  					LastEvaluation: time.Unix(3, 0),
   558  				}),
   559  			},
   560  			replicaLabels: []string{"replica"},
   561  		},
   562  		{
   563  			name: "youngest firing alert",
   564  			rules: []*rulespb.Rule{
   565  				rulespb.NewAlertingRule(&rulespb.Alert{
   566  					Name: "a1",
   567  					Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{
   568  						{Name: "replica", Value: "2"},
   569  					}},
   570  					LastEvaluation: time.Unix(4, 0),
   571  				}),
   572  				rulespb.NewAlertingRule(&rulespb.Alert{
   573  					Name: "a1",
   574  					Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{
   575  						{Name: "replica", Value: "2"},
   576  						{Name: "foo", Value: "bar"},
   577  					}},
   578  					LastEvaluation: time.Unix(2, 0),
   579  				}),
   580  				rulespb.NewAlertingRule(&rulespb.Alert{
   581  					Name:           "a2",
   582  					LastEvaluation: time.Unix(2, 0),
   583  					State:          rulespb.AlertState_PENDING,
   584  				}),
   585  				rulespb.NewAlertingRule(&rulespb.Alert{
   586  					Name: "a1",
   587  					Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{
   588  						{Name: "replica", Value: "1"},
   589  					}},
   590  					LastEvaluation: time.Unix(3, 0),
   591  				}),
   592  				rulespb.NewAlertingRule(&rulespb.Alert{
   593  					Name: "a2",
   594  					Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{
   595  						{Name: "replica", Value: "1"},
   596  					}},
   597  					LastEvaluation: time.Unix(3, 0),
   598  					State:          rulespb.AlertState_PENDING,
   599  				}),
   600  				rulespb.NewAlertingRule(&rulespb.Alert{
   601  					Name: "a1",
   602  					Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{
   603  						{Name: "replica", Value: "3"},
   604  					}},
   605  					LastEvaluation: time.Unix(2, 0),
   606  					State:          rulespb.AlertState_FIRING,
   607  				}),
   608  				rulespb.NewAlertingRule(&rulespb.Alert{
   609  					Name: "a1",
   610  					Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{
   611  						{Name: "foo", Value: "bar"},
   612  					}},
   613  					State:          rulespb.AlertState_FIRING,
   614  					LastEvaluation: time.Unix(1, 0),
   615  				}),
   616  			},
   617  			want: []*rulespb.Rule{
   618  				rulespb.NewAlertingRule(&rulespb.Alert{
   619  					State:          rulespb.AlertState_FIRING,
   620  					Name:           "a1",
   621  					LastEvaluation: time.Unix(2, 0),
   622  				}),
   623  				rulespb.NewAlertingRule(&rulespb.Alert{
   624  					State: rulespb.AlertState_FIRING,
   625  					Name:  "a1",
   626  					Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{
   627  						{Name: "foo", Value: "bar"},
   628  					}},
   629  					LastEvaluation: time.Unix(1, 0),
   630  				}),
   631  				rulespb.NewAlertingRule(&rulespb.Alert{
   632  					State:          rulespb.AlertState_PENDING,
   633  					Name:           "a2",
   634  					LastEvaluation: time.Unix(3, 0),
   635  				}),
   636  			},
   637  			replicaLabels: []string{"replica"},
   638  		},
   639  		{
   640  			name: "alerts with different severity",
   641  			rules: []*rulespb.Rule{
   642  				rulespb.NewAlertingRule(&rulespb.Alert{
   643  					Name: "a1",
   644  					Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{
   645  						{Name: "replica", Value: "1"},
   646  						{Name: "severity", Value: "warning"},
   647  					}},
   648  					LastEvaluation: time.Unix(1, 0),
   649  				}),
   650  				rulespb.NewAlertingRule(&rulespb.Alert{
   651  					Name: "a1",
   652  					Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{
   653  						{Name: "replica", Value: "1"},
   654  						{Name: "severity", Value: "critical"},
   655  					}},
   656  					LastEvaluation: time.Unix(1, 0),
   657  				}),
   658  				rulespb.NewAlertingRule(&rulespb.Alert{
   659  					Name: "a1",
   660  					Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{
   661  						{Name: "replica", Value: "2"},
   662  						{Name: "severity", Value: "warning"},
   663  					}},
   664  					LastEvaluation: time.Unix(1, 0),
   665  				}),
   666  				rulespb.NewAlertingRule(&rulespb.Alert{
   667  					Name: "a1",
   668  					Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{
   669  						{Name: "replica", Value: "2"},
   670  						{Name: "severity", Value: "critical"},
   671  					}},
   672  					LastEvaluation: time.Unix(1, 0),
   673  				}),
   674  			},
   675  			want: []*rulespb.Rule{
   676  				rulespb.NewAlertingRule(&rulespb.Alert{
   677  					Name: "a1",
   678  					Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{
   679  						{Name: "severity", Value: "critical"},
   680  					}},
   681  					LastEvaluation: time.Unix(1, 0),
   682  				}),
   683  				rulespb.NewAlertingRule(&rulespb.Alert{
   684  					Name: "a1",
   685  					Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{
   686  						{Name: "severity", Value: "warning"},
   687  					}},
   688  					LastEvaluation: time.Unix(1, 0),
   689  				}),
   690  			},
   691  			replicaLabels: []string{"replica"},
   692  		},
   693  		{
   694  			name: "alerts with missing replica labels",
   695  			rules: []*rulespb.Rule{
   696  				rulespb.NewAlertingRule(&rulespb.Alert{
   697  					Name: "a1",
   698  					Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{
   699  						{Name: "replica", Value: "1"},
   700  						{Name: "label", Value: "foo"},
   701  					}},
   702  					LastEvaluation: time.Unix(1, 0),
   703  				}),
   704  				rulespb.NewAlertingRule(&rulespb.Alert{
   705  					Name: "a1",
   706  					Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{
   707  						{Name: "replica", Value: "2"},
   708  						{Name: "label", Value: "foo"},
   709  					}},
   710  					LastEvaluation: time.Unix(1, 0),
   711  				}),
   712  				rulespb.NewAlertingRule(&rulespb.Alert{
   713  					Name: "a1",
   714  					Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{
   715  						{Name: "label", Value: "foo"},
   716  					}},
   717  					LastEvaluation: time.Unix(1, 0),
   718  				}),
   719  			},
   720  			want: []*rulespb.Rule{
   721  				rulespb.NewAlertingRule(&rulespb.Alert{
   722  					Name: "a1",
   723  					Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{
   724  						{Name: "label", Value: "foo"},
   725  					}},
   726  					LastEvaluation: time.Unix(1, 0),
   727  				}),
   728  			},
   729  			replicaLabels: []string{"replica"},
   730  		},
   731  	} {
   732  		t.Run(tc.name, func(t *testing.T) {
   733  			replicaLabels := make(map[string]struct{})
   734  			for _, lbl := range tc.replicaLabels {
   735  				replicaLabels[lbl] = struct{}{}
   736  			}
   737  			testutil.Equals(t, tc.want, dedupRules(tc.rules, replicaLabels))
   738  		})
   739  	}
   740  }
   741  
   742  func TestDedupGroups(t *testing.T) {
   743  	for _, tc := range []struct {
   744  		name         string
   745  		groups, want []*rulespb.RuleGroup
   746  	}{
   747  		{
   748  			name:   "no groups",
   749  			groups: nil,
   750  			want:   nil,
   751  		},
   752  		{
   753  			name: "empty group",
   754  			groups: []*rulespb.RuleGroup{
   755  				{Name: "a"},
   756  			},
   757  			want: []*rulespb.RuleGroup{
   758  				{Name: "a"},
   759  			},
   760  		},
   761  		{
   762  			name: "multiple empty groups",
   763  			groups: []*rulespb.RuleGroup{
   764  				{Name: "a"},
   765  				{Name: "b"},
   766  			},
   767  			want: []*rulespb.RuleGroup{
   768  				{Name: "a"},
   769  				{Name: "b"},
   770  			},
   771  		},
   772  		{
   773  			name: "single group",
   774  			groups: []*rulespb.RuleGroup{
   775  				{
   776  					Name: "a",
   777  					Rules: []*rulespb.Rule{
   778  						rulespb.NewRecordingRule(&rulespb.RecordingRule{Name: "a1"}),
   779  						rulespb.NewRecordingRule(&rulespb.RecordingRule{Name: "a2"}),
   780  					},
   781  				},
   782  			},
   783  			want: []*rulespb.RuleGroup{
   784  				{
   785  					Name: "a",
   786  					Rules: []*rulespb.Rule{
   787  						rulespb.NewRecordingRule(&rulespb.RecordingRule{Name: "a1"}),
   788  						rulespb.NewRecordingRule(&rulespb.RecordingRule{Name: "a2"}),
   789  					},
   790  				},
   791  			},
   792  		},
   793  		{
   794  			name: "separate groups",
   795  			groups: []*rulespb.RuleGroup{
   796  				{
   797  					Name: "a",
   798  					Rules: []*rulespb.Rule{
   799  						rulespb.NewRecordingRule(&rulespb.RecordingRule{Name: "a1"}),
   800  						rulespb.NewRecordingRule(&rulespb.RecordingRule{Name: "a2"}),
   801  					},
   802  				},
   803  				{
   804  					Name: "b",
   805  					Rules: []*rulespb.Rule{
   806  						rulespb.NewRecordingRule(&rulespb.RecordingRule{Name: "b1"}),
   807  						rulespb.NewRecordingRule(&rulespb.RecordingRule{Name: "b2"}),
   808  					},
   809  				},
   810  			},
   811  			want: []*rulespb.RuleGroup{
   812  				{
   813  					Name: "a",
   814  					Rules: []*rulespb.Rule{
   815  						rulespb.NewRecordingRule(&rulespb.RecordingRule{Name: "a1"}),
   816  						rulespb.NewRecordingRule(&rulespb.RecordingRule{Name: "a2"}),
   817  					},
   818  				},
   819  				{
   820  					Name: "b",
   821  					Rules: []*rulespb.Rule{
   822  						rulespb.NewRecordingRule(&rulespb.RecordingRule{Name: "b1"}),
   823  						rulespb.NewRecordingRule(&rulespb.RecordingRule{Name: "b2"}),
   824  					},
   825  				},
   826  			},
   827  		},
   828  		{
   829  			name: "duplicate groups",
   830  			groups: []*rulespb.RuleGroup{
   831  				{
   832  					Name: "a",
   833  					Rules: []*rulespb.Rule{
   834  						rulespb.NewRecordingRule(&rulespb.RecordingRule{Name: "a1"}),
   835  						rulespb.NewRecordingRule(&rulespb.RecordingRule{Name: "a2"}),
   836  					},
   837  				},
   838  				{
   839  					Name: "b",
   840  					Rules: []*rulespb.Rule{
   841  						rulespb.NewRecordingRule(&rulespb.RecordingRule{Name: "b1"}),
   842  						rulespb.NewRecordingRule(&rulespb.RecordingRule{Name: "b2"}),
   843  					},
   844  				},
   845  				{
   846  					Name: "c",
   847  				},
   848  				{
   849  					Name: "a",
   850  					Rules: []*rulespb.Rule{
   851  						rulespb.NewRecordingRule(&rulespb.RecordingRule{Name: "a1"}),
   852  						rulespb.NewRecordingRule(&rulespb.RecordingRule{Name: "a2"}),
   853  					},
   854  				},
   855  			},
   856  			want: []*rulespb.RuleGroup{
   857  				{
   858  					Name: "a",
   859  					Rules: []*rulespb.Rule{
   860  						rulespb.NewRecordingRule(&rulespb.RecordingRule{Name: "a1"}),
   861  						rulespb.NewRecordingRule(&rulespb.RecordingRule{Name: "a2"}),
   862  						rulespb.NewRecordingRule(&rulespb.RecordingRule{Name: "a1"}),
   863  						rulespb.NewRecordingRule(&rulespb.RecordingRule{Name: "a2"}),
   864  					},
   865  				},
   866  				{
   867  					Name: "b",
   868  					Rules: []*rulespb.Rule{
   869  						rulespb.NewRecordingRule(&rulespb.RecordingRule{Name: "b1"}),
   870  						rulespb.NewRecordingRule(&rulespb.RecordingRule{Name: "b2"}),
   871  					},
   872  				},
   873  				{
   874  					Name: "c",
   875  				},
   876  			},
   877  		},
   878  		{
   879  			name: "distinct file names",
   880  			groups: []*rulespb.RuleGroup{
   881  				{
   882  					Name: "a",
   883  					File: "foo.yaml",
   884  					Rules: []*rulespb.Rule{
   885  						rulespb.NewRecordingRule(&rulespb.RecordingRule{Name: "a1"}),
   886  						rulespb.NewRecordingRule(&rulespb.RecordingRule{Name: "a2"}),
   887  					},
   888  				},
   889  				{
   890  					Name: "a",
   891  					Rules: []*rulespb.Rule{
   892  						rulespb.NewRecordingRule(&rulespb.RecordingRule{Name: "a1"}),
   893  						rulespb.NewRecordingRule(&rulespb.RecordingRule{Name: "a2"}),
   894  					},
   895  				},
   896  				{
   897  					Name: "b",
   898  					Rules: []*rulespb.Rule{
   899  						rulespb.NewRecordingRule(&rulespb.RecordingRule{Name: "b1"}),
   900  						rulespb.NewRecordingRule(&rulespb.RecordingRule{Name: "b2"}),
   901  					},
   902  				},
   903  				{
   904  					Name: "c",
   905  				},
   906  				{
   907  					Name: "a",
   908  					File: "bar.yaml",
   909  					Rules: []*rulespb.Rule{
   910  						rulespb.NewRecordingRule(&rulespb.RecordingRule{Name: "a1"}),
   911  						rulespb.NewRecordingRule(&rulespb.RecordingRule{Name: "a2"}),
   912  					},
   913  				},
   914  			},
   915  			want: []*rulespb.RuleGroup{
   916  				{
   917  					Name: "a",
   918  					Rules: []*rulespb.Rule{
   919  						rulespb.NewRecordingRule(&rulespb.RecordingRule{Name: "a1"}),
   920  						rulespb.NewRecordingRule(&rulespb.RecordingRule{Name: "a2"}),
   921  					},
   922  				},
   923  				{
   924  					Name: "b",
   925  					Rules: []*rulespb.Rule{
   926  						rulespb.NewRecordingRule(&rulespb.RecordingRule{Name: "b1"}),
   927  						rulespb.NewRecordingRule(&rulespb.RecordingRule{Name: "b2"}),
   928  					},
   929  				},
   930  				{
   931  					Name: "c",
   932  				},
   933  				{
   934  					Name: "a",
   935  					File: "bar.yaml",
   936  					Rules: []*rulespb.Rule{
   937  						rulespb.NewRecordingRule(&rulespb.RecordingRule{Name: "a1"}),
   938  						rulespb.NewRecordingRule(&rulespb.RecordingRule{Name: "a2"}),
   939  					},
   940  				},
   941  				{
   942  					Name: "a",
   943  					File: "foo.yaml",
   944  					Rules: []*rulespb.Rule{
   945  						rulespb.NewRecordingRule(&rulespb.RecordingRule{Name: "a1"}),
   946  						rulespb.NewRecordingRule(&rulespb.RecordingRule{Name: "a2"}),
   947  					},
   948  				},
   949  			},
   950  		},
   951  	} {
   952  		t.Run(tc.name, func(t *testing.T) {
   953  			t.Run(tc.name, func(t *testing.T) {
   954  				testutil.Equals(t, tc.want, dedupGroups(tc.groups))
   955  			})
   956  		})
   957  	}
   958  }
   959  
   960  func TestFilterRules(t *testing.T) {
   961  	for _, tc := range []struct {
   962  		name         string
   963  		matcherSets  [][]*labels.Matcher
   964  		groups, want []*rulespb.RuleGroup
   965  	}{
   966  		{
   967  			name:   "no groups",
   968  			groups: nil,
   969  			want:   nil,
   970  		},
   971  		{
   972  			name: "empty group with no matcher",
   973  			groups: []*rulespb.RuleGroup{
   974  				{Name: "a"},
   975  			},
   976  			want: []*rulespb.RuleGroup{
   977  				{Name: "a"},
   978  			},
   979  		},
   980  		{
   981  			name: "multiple empty groups with no matcher",
   982  			groups: []*rulespb.RuleGroup{
   983  				{Name: "a"},
   984  				{Name: "b"},
   985  			},
   986  			want: []*rulespb.RuleGroup{
   987  				{Name: "a"},
   988  				{Name: "b"},
   989  			},
   990  		},
   991  		{
   992  			name: "single group with labeled rules and no matcher",
   993  			groups: []*rulespb.RuleGroup{
   994  				{
   995  					Name: "a",
   996  					Rules: []*rulespb.Rule{
   997  						rulespb.NewAlertingRule(&rulespb.Alert{
   998  							Name: "a1",
   999  							Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{
  1000  								{Name: "replica", Value: "1"},
  1001  								{Name: "label", Value: "foo"},
  1002  							}},
  1003  						}),
  1004  						rulespb.NewRecordingRule(&rulespb.RecordingRule{
  1005  							Name: "r1", Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{
  1006  								{Name: "label", Value: "foo"},
  1007  							}},
  1008  						}),
  1009  						rulespb.NewRecordingRule(&rulespb.RecordingRule{
  1010  							Name: "r2", Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{
  1011  								{Name: "otherlabel", Value: "bar"},
  1012  							}},
  1013  						}),
  1014  					},
  1015  				},
  1016  			},
  1017  			want: []*rulespb.RuleGroup{
  1018  				{
  1019  					Name: "a",
  1020  					Rules: []*rulespb.Rule{
  1021  						rulespb.NewAlertingRule(&rulespb.Alert{
  1022  							Name: "a1",
  1023  							Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{
  1024  								{Name: "replica", Value: "1"},
  1025  								{Name: "label", Value: "foo"},
  1026  							}},
  1027  						}),
  1028  						rulespb.NewRecordingRule(&rulespb.RecordingRule{
  1029  							Name: "r1", Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{
  1030  								{Name: "label", Value: "foo"},
  1031  							}},
  1032  						}),
  1033  						rulespb.NewRecordingRule(&rulespb.RecordingRule{
  1034  							Name: "r2", Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{
  1035  								{Name: "otherlabel", Value: "bar"},
  1036  							}},
  1037  						}),
  1038  					},
  1039  				},
  1040  			},
  1041  		},
  1042  		{
  1043  			name:        "single group with labeled rules and matcher",
  1044  			matcherSets: [][]*labels.Matcher{{&labels.Matcher{Name: "label", Value: "foo", Type: labels.MatchEqual}}},
  1045  			groups: []*rulespb.RuleGroup{
  1046  				{
  1047  					Name: "a",
  1048  					Rules: []*rulespb.Rule{
  1049  						rulespb.NewAlertingRule(&rulespb.Alert{
  1050  							Name: "a1",
  1051  							Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{
  1052  								{Name: "replica", Value: "1"},
  1053  								{Name: "label", Value: "foo"},
  1054  							}},
  1055  						}),
  1056  						rulespb.NewRecordingRule(&rulespb.RecordingRule{
  1057  							Name: "r1", Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{
  1058  								{Name: "label", Value: "foo"},
  1059  							}},
  1060  						}),
  1061  						rulespb.NewRecordingRule(&rulespb.RecordingRule{
  1062  							Name: "r2", Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{
  1063  								{Name: "otherlabel", Value: "bar"},
  1064  							}},
  1065  						}),
  1066  					},
  1067  				},
  1068  			},
  1069  			want: []*rulespb.RuleGroup{
  1070  				{
  1071  					Name: "a",
  1072  					Rules: []*rulespb.Rule{
  1073  						rulespb.NewAlertingRule(&rulespb.Alert{
  1074  							Name: "a1",
  1075  							Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{
  1076  								{Name: "replica", Value: "1"},
  1077  								{Name: "label", Value: "foo"},
  1078  							}},
  1079  						}),
  1080  						rulespb.NewRecordingRule(&rulespb.RecordingRule{
  1081  							Name: "r1", Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{
  1082  								{Name: "label", Value: "foo"},
  1083  							}},
  1084  						}),
  1085  					},
  1086  				},
  1087  			},
  1088  		},
  1089  		{
  1090  			name:        "single group with no match for matcher",
  1091  			matcherSets: [][]*labels.Matcher{{&labels.Matcher{Name: "foo", Value: "bar", Type: labels.MatchEqual}}},
  1092  			groups: []*rulespb.RuleGroup{
  1093  				{
  1094  					Name: "a",
  1095  					Rules: []*rulespb.Rule{
  1096  						rulespb.NewAlertingRule(&rulespb.Alert{
  1097  							Name: "a1",
  1098  							Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{
  1099  								{Name: "replica", Value: "1"},
  1100  								{Name: "label", Value: "foo"},
  1101  							}},
  1102  						}),
  1103  						rulespb.NewRecordingRule(&rulespb.RecordingRule{
  1104  							Name: "r1", Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{
  1105  								{Name: "label", Value: "foo"},
  1106  							}},
  1107  						}),
  1108  						rulespb.NewRecordingRule(&rulespb.RecordingRule{
  1109  							Name: "r2", Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{
  1110  								{Name: "otherlabel", Value: "bar"},
  1111  							}},
  1112  						}),
  1113  					},
  1114  				},
  1115  			},
  1116  			want: []*rulespb.RuleGroup{},
  1117  		},
  1118  		{
  1119  			name:        "single group with templated labels",
  1120  			matcherSets: [][]*labels.Matcher{{&labels.Matcher{Name: "templatedlabel", Value: "{{ $externalURL }}", Type: labels.MatchEqual}}},
  1121  			groups: []*rulespb.RuleGroup{
  1122  				{
  1123  					Name: "a",
  1124  					Rules: []*rulespb.Rule{
  1125  						rulespb.NewAlertingRule(&rulespb.Alert{
  1126  							Name: "a1",
  1127  							Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{
  1128  								{Name: "label", Value: "foo"},
  1129  								{Name: "templatedlabel", Value: "{{ $externalURL }}"},
  1130  							}},
  1131  						}),
  1132  						rulespb.NewAlertingRule(&rulespb.Alert{
  1133  							Name: "a2",
  1134  							Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{
  1135  								{Name: "label", Value: "foo"},
  1136  							}},
  1137  						}),
  1138  					},
  1139  				},
  1140  			},
  1141  			want: []*rulespb.RuleGroup{},
  1142  		},
  1143  		{
  1144  			name:        "multiple group with labeled rules and matcher",
  1145  			matcherSets: [][]*labels.Matcher{{&labels.Matcher{Name: "label", Value: "foo", Type: labels.MatchEqual}}},
  1146  			groups: []*rulespb.RuleGroup{
  1147  				{
  1148  					Name: "a",
  1149  					Rules: []*rulespb.Rule{
  1150  						rulespb.NewAlertingRule(&rulespb.Alert{
  1151  							Name: "a1a",
  1152  							Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{
  1153  								{Name: "label", Value: "foo"},
  1154  							}},
  1155  						}),
  1156  						rulespb.NewRecordingRule(&rulespb.RecordingRule{
  1157  							Name: "r1a", Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{
  1158  								{Name: "label", Value: "foo"},
  1159  							}},
  1160  						}),
  1161  						rulespb.NewRecordingRule(&rulespb.RecordingRule{
  1162  							Name: "r1b", Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{
  1163  								{Name: "otherlabel", Value: "bar"},
  1164  							}},
  1165  						}),
  1166  					},
  1167  				},
  1168  				{
  1169  					Name: "b",
  1170  					Rules: []*rulespb.Rule{
  1171  						rulespb.NewAlertingRule(&rulespb.Alert{
  1172  							Name: "a1b",
  1173  							Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{
  1174  								{Name: "some", Value: "label"},
  1175  							}},
  1176  						}),
  1177  						rulespb.NewRecordingRule(&rulespb.RecordingRule{
  1178  							Name: "r1b", Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{
  1179  								{Name: "label", Value: "foo"},
  1180  							}},
  1181  						}),
  1182  					},
  1183  				},
  1184  			},
  1185  			want: []*rulespb.RuleGroup{
  1186  				{
  1187  					Name: "a",
  1188  					Rules: []*rulespb.Rule{
  1189  						rulespb.NewAlertingRule(&rulespb.Alert{
  1190  							Name: "a1a",
  1191  							Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{
  1192  								{Name: "label", Value: "foo"},
  1193  							}},
  1194  						}),
  1195  						rulespb.NewRecordingRule(&rulespb.RecordingRule{
  1196  							Name: "r1a", Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{
  1197  								{Name: "label", Value: "foo"},
  1198  							}},
  1199  						}),
  1200  					},
  1201  				},
  1202  				{
  1203  					Name: "b",
  1204  					Rules: []*rulespb.Rule{
  1205  						rulespb.NewRecordingRule(&rulespb.RecordingRule{
  1206  							Name: "r1b", Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{
  1207  								{Name: "label", Value: "foo"},
  1208  							}},
  1209  						}),
  1210  					},
  1211  				},
  1212  			},
  1213  		},
  1214  		{
  1215  			name:        "multiple group with labeled rules and no match",
  1216  			matcherSets: [][]*labels.Matcher{{&labels.Matcher{Name: "foo", Value: "bar", Type: labels.MatchEqual}}},
  1217  			groups: []*rulespb.RuleGroup{
  1218  				{
  1219  					Name: "a",
  1220  					Rules: []*rulespb.Rule{
  1221  						rulespb.NewAlertingRule(&rulespb.Alert{
  1222  							Name: "a1a",
  1223  							Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{
  1224  								{Name: "label", Value: "foo"},
  1225  							}},
  1226  						}),
  1227  						rulespb.NewRecordingRule(&rulespb.RecordingRule{
  1228  							Name: "r1a", Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{
  1229  								{Name: "label", Value: "foo"},
  1230  							}},
  1231  						}),
  1232  						rulespb.NewRecordingRule(&rulespb.RecordingRule{
  1233  							Name: "r1b", Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{
  1234  								{Name: "otherlabel", Value: "bar"},
  1235  							}},
  1236  						}),
  1237  					},
  1238  				},
  1239  				{
  1240  					Name: "b",
  1241  					Rules: []*rulespb.Rule{
  1242  						rulespb.NewAlertingRule(&rulespb.Alert{
  1243  							Name: "a1b",
  1244  							Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{
  1245  								{Name: "some", Value: "label"},
  1246  							}},
  1247  						}),
  1248  						rulespb.NewRecordingRule(&rulespb.RecordingRule{
  1249  							Name: "r1b", Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{
  1250  								{Name: "label", Value: "foo"},
  1251  							}},
  1252  						}),
  1253  					},
  1254  				},
  1255  			},
  1256  			want: []*rulespb.RuleGroup{},
  1257  		},
  1258  		{
  1259  			name:        "multiple group with templated labels",
  1260  			matcherSets: [][]*labels.Matcher{{&labels.Matcher{Name: "templatedlabel", Value: "{{ $externalURL }}", Type: labels.MatchEqual}}},
  1261  			groups: []*rulespb.RuleGroup{
  1262  				{
  1263  					Name: "a",
  1264  					Rules: []*rulespb.Rule{
  1265  						rulespb.NewAlertingRule(&rulespb.Alert{
  1266  							Name: "a1a",
  1267  							Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{
  1268  								{Name: "templatedlabel", Value: "{{ $externalURL }}"},
  1269  							}},
  1270  						}),
  1271  					},
  1272  				},
  1273  				{
  1274  					Name: "b",
  1275  					Rules: []*rulespb.Rule{
  1276  						rulespb.NewAlertingRule(&rulespb.Alert{
  1277  							Name: "a1b",
  1278  							Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{
  1279  								{Name: "templated", Value: "{{ $externalURL }}"},
  1280  							}},
  1281  						}),
  1282  					},
  1283  				},
  1284  			},
  1285  			want: []*rulespb.RuleGroup{},
  1286  		},
  1287  		{
  1288  			name:        "multiple group with templated labels and non templated matcher",
  1289  			matcherSets: [][]*labels.Matcher{{&labels.Matcher{Name: "templatedlabel", Value: "foo", Type: labels.MatchEqual}}},
  1290  			groups: []*rulespb.RuleGroup{
  1291  				{
  1292  					Name: "a",
  1293  					Rules: []*rulespb.Rule{
  1294  						rulespb.NewAlertingRule(&rulespb.Alert{
  1295  							Name: "a1a",
  1296  							Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{
  1297  								{Name: "templatedlabel", Value: "{{ $externalURL }}"},
  1298  							}},
  1299  						}),
  1300  					},
  1301  				},
  1302  				{
  1303  					Name: "b",
  1304  					Rules: []*rulespb.Rule{
  1305  						rulespb.NewAlertingRule(&rulespb.Alert{
  1306  							Name: "a1b",
  1307  							Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{
  1308  								{Name: "templated", Value: "{{ $externalURL }}"},
  1309  							}},
  1310  						}),
  1311  					},
  1312  				},
  1313  			},
  1314  			want: []*rulespb.RuleGroup{},
  1315  		},
  1316  		{
  1317  			name:        "multiple group with regex matcher",
  1318  			matcherSets: [][]*labels.Matcher{{labels.MustNewMatcher(labels.MatchRegexp, "label", "f.*")}},
  1319  			groups: []*rulespb.RuleGroup{
  1320  				{
  1321  					Name: "a",
  1322  					Rules: []*rulespb.Rule{
  1323  						rulespb.NewAlertingRule(&rulespb.Alert{
  1324  							Name: "a1a",
  1325  							Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{
  1326  								{Name: "label", Value: "foo"},
  1327  							}},
  1328  						}),
  1329  						rulespb.NewRecordingRule(&rulespb.RecordingRule{
  1330  							Name: "r1a", Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{
  1331  								{Name: "label", Value: "foo"},
  1332  							}},
  1333  						}),
  1334  						rulespb.NewRecordingRule(&rulespb.RecordingRule{
  1335  							Name: "r1b", Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{
  1336  								{Name: "otherlabel", Value: "bar"},
  1337  							}},
  1338  						}),
  1339  					},
  1340  				},
  1341  				{
  1342  					Name: "b",
  1343  					Rules: []*rulespb.Rule{
  1344  						rulespb.NewAlertingRule(&rulespb.Alert{
  1345  							Name: "a1b",
  1346  							Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{
  1347  								{Name: "some", Value: "label"},
  1348  							}},
  1349  						}),
  1350  						rulespb.NewRecordingRule(&rulespb.RecordingRule{
  1351  							Name: "r1b", Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{
  1352  								{Name: "label", Value: "foo"},
  1353  							}},
  1354  						}),
  1355  					},
  1356  				},
  1357  			},
  1358  			want: []*rulespb.RuleGroup{
  1359  				{
  1360  					Name: "a",
  1361  					Rules: []*rulespb.Rule{
  1362  						rulespb.NewAlertingRule(&rulespb.Alert{
  1363  							Name: "a1a",
  1364  							Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{
  1365  								{Name: "label", Value: "foo"},
  1366  							}},
  1367  						}),
  1368  						rulespb.NewRecordingRule(&rulespb.RecordingRule{
  1369  							Name: "r1a", Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{
  1370  								{Name: "label", Value: "foo"},
  1371  							}},
  1372  						}),
  1373  					},
  1374  				},
  1375  				{
  1376  					Name: "b",
  1377  					Rules: []*rulespb.Rule{
  1378  						rulespb.NewRecordingRule(&rulespb.RecordingRule{
  1379  							Name: "r1b", Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{
  1380  								{Name: "label", Value: "foo"},
  1381  							}},
  1382  						}),
  1383  					},
  1384  				},
  1385  			},
  1386  		},
  1387  	} {
  1388  		t.Run(tc.name, func(t *testing.T) {
  1389  			testutil.Equals(t, tc.want, filterRules(tc.groups, tc.matcherSets))
  1390  		})
  1391  	}
  1392  }