master
go 638 lines 20.3 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 package relabel
4
5 import (
6 "testing"
7
8 "github.com/grafana/regexp"
9 commonmodel "github.com/prometheus/common/model"
10 "github.com/prometheus/prometheus/model/labels"
11 "github.com/stretchr/testify/assert"
12 "github.com/stretchr/testify/require"
13
14 prompkg "github.com/netdata/netdata/go/plugins/pkg/prometheus"
15 )
16
17 func TestRegexp_String(t *testing.T) {
18 tests := map[string]struct {
19 re Regexp
20 want string
21 }{
22 "NewRegexp returns the un-anchored source": {re: MustNewRegexp("(.*)_total"), want: "(.*)_total"},
23 "NewRegexp with empty source": {re: MustNewRegexp(""), want: ""},
24 "zero value": {re: Regexp{}, want: ""},
25 "raw non-anchored value is safe (no panic)": {re: Regexp{Regexp: regexp.MustCompile("x")}, want: ""},
26 }
27 for name, tc := range tests {
28 t.Run(name, func(t *testing.T) {
29 var got string
30 require.NotPanics(t, func() { got = tc.re.String() })
31 assert.Equal(t, tc.want, got)
32 })
33 }
34 }
35
36 func TestProcessor_Apply(t *testing.T) {
37 tests := map[string]struct {
38 cfgs []Config
39 in prompkg.Sample
40 want prompkg.Sample
41 keep bool
42 sameLabelBacking bool
43 }{
44 "labelmap matching __name__ maps it once, not its own output": {
45 cfgs: []Config{{
46 Regex: MustNewRegexp("(.+)"),
47 Replacement: "copy_${1}",
48 Action: LabelMap,
49 }},
50 in: sample("m", map[string]string{"job": "api"}, 1, prompkg.SampleKindScalar, commonmodel.MetricTypeGauge),
51 want: sample("m", map[string]string{
52 "job": "api",
53 "copy___name__": "m",
54 "copy_job": "api",
55 }, 1, prompkg.SampleKindScalar, commonmodel.MetricTypeGauge),
56 keep: true,
57 },
58 "explicit empty separator joins without the default ';'": {
59 cfgs: []Config{{
60 SourceLabels: []string{"a", "b"},
61 Separator: "",
62 separatorSet: true,
63 TargetLabel: "c",
64 Action: Replace,
65 }},
66 in: sample("m", map[string]string{"a": "foo", "b": "bar"}, 1, prompkg.SampleKindScalar, commonmodel.MetricTypeGauge),
67 want: sample("m", map[string]string{"a": "foo", "b": "bar", "c": "foobar"}, 1, prompkg.SampleKindScalar, commonmodel.MetricTypeGauge),
68 keep: true,
69 },
70 "non-canonical action is canonicalized (KEEP -> keep)": {
71 cfgs: []Config{{
72 SourceLabels: []string{"a"},
73 Regex: MustNewRegexp("foo"),
74 Action: Action("KEEP"),
75 }},
76 in: sample("m", map[string]string{"a": "foo"}, 1, prompkg.SampleKindScalar, commonmodel.MetricTypeGauge),
77 want: sample("m", map[string]string{"a": "foo"}, 1, prompkg.SampleKindScalar, commonmodel.MetricTypeGauge),
78 keep: true,
79 },
80 "zero rules pass through without rematerializing labels": {
81 in: sample("test_metric", map[string]string{"a": "foo"}, 1, prompkg.SampleKindScalar, commonmodel.MetricTypeGauge),
82 want: sample("test_metric", map[string]string{"a": "foo"}, 1, prompkg.SampleKindScalar, commonmodel.MetricTypeGauge),
83 keep: true,
84 sameLabelBacking: true,
85 },
86 "replace rewrites label and preserves sample payload": {
87 cfgs: []Config{
88 {
89 SourceLabels: []string{"a"},
90 Regex: MustNewRegexp("f(.*)"),
91 TargetLabel: "b",
92 Replacement: "ch${1}",
93 Action: Replace,
94 },
95 },
96 in: sample("test_total", map[string]string{"a": "foo"}, 1.5, prompkg.SampleKindScalar, commonmodel.MetricTypeCounter),
97 want: sample("test_total", map[string]string{
98 "a": "foo",
99 "b": "choo",
100 }, 1.5, prompkg.SampleKindScalar, commonmodel.MetricTypeCounter),
101 keep: true,
102 },
103 "replace can rewrite __name__": {
104 cfgs: []Config{
105 {
106 SourceLabels: []string{commonmodel.MetricNameLabel},
107 Regex: MustNewRegexp("(.*)_total"),
108 TargetLabel: commonmodel.MetricNameLabel,
109 Replacement: "${1}",
110 Action: Replace,
111 },
112 },
113 in: sample("http_requests_total", map[string]string{"method": "GET"}, 1, prompkg.SampleKindScalar, commonmodel.MetricTypeCounter),
114 want: sample("http_requests", map[string]string{"method": "GET"}, 1, prompkg.SampleKindScalar, commonmodel.MetricTypeCounter),
115 keep: true,
116 },
117 "label-only relabel preserves existing utf8 metric name": {
118 cfgs: []Config{
119 {
120 SourceLabels: []string{"method"},
121 TargetLabel: "method_upper",
122 Action: Uppercase,
123 },
124 },
125 in: sample("http.requests_total", map[string]string{"method": "get"}, 1, prompkg.SampleKindScalar, commonmodel.MetricTypeCounter),
126 want: sample("http.requests_total", map[string]string{
127 "method": "get",
128 "method_upper": "GET",
129 }, 1, prompkg.SampleKindScalar, commonmodel.MetricTypeCounter),
130 keep: true,
131 },
132 "replace rewrites __name__ with explicit utf8 scheme at runtime": {
133 cfgs: []Config{
134 {
135 SourceLabels: []string{commonmodel.MetricNameLabel},
136 Regex: MustNewRegexp("(.*)_total"),
137 TargetLabel: commonmodel.MetricNameLabel,
138 Replacement: "${1}.total",
139 Action: Replace,
140 NameScheme: commonmodel.UTF8Validation,
141 },
142 },
143 in: sample("http_requests_total", map[string]string{"method": "GET"}, 1, prompkg.SampleKindScalar, commonmodel.MetricTypeCounter),
144 want: sample("http_requests.total", map[string]string{"method": "GET"}, 1, prompkg.SampleKindScalar, commonmodel.MetricTypeCounter),
145 keep: true,
146 sameLabelBacking: true,
147 },
148 "histogram bucket relabel preserves kind and family type": {
149 cfgs: []Config{
150 {
151 SourceLabels: []string{commonmodel.MetricNameLabel},
152 Regex: MustNewRegexp("(.*)"),
153 TargetLabel: commonmodel.MetricNameLabel,
154 Replacement: "nginx_${1}",
155 Action: Replace,
156 },
157 },
158 in: sample("request_duration_seconds_bucket", map[string]string{
159 "le": "0.5",
160 "method": "GET",
161 }, 42, prompkg.SampleKindHistogramBucket, commonmodel.MetricTypeHistogram),
162 want: sample("nginx_request_duration_seconds_bucket", map[string]string{
163 "le": "0.5",
164 "method": "GET",
165 }, 42, prompkg.SampleKindHistogramBucket, commonmodel.MetricTypeHistogram),
166 keep: true,
167 sameLabelBacking: true,
168 },
169 "summary quantile relabel preserves kind and family type": {
170 cfgs: []Config{
171 {
172 SourceLabels: []string{commonmodel.MetricNameLabel},
173 Regex: MustNewRegexp("(.*)"),
174 TargetLabel: commonmodel.MetricNameLabel,
175 Replacement: "nginx_${1}",
176 Action: Replace,
177 },
178 },
179 in: sample("request_duration_seconds", map[string]string{
180 "method": "GET",
181 "quantile": "0.9",
182 }, 7, prompkg.SampleKindSummaryQuantile, commonmodel.MetricTypeSummary),
183 want: sample("nginx_request_duration_seconds", map[string]string{
184 "method": "GET",
185 "quantile": "0.9",
186 }, 7, prompkg.SampleKindSummaryQuantile, commonmodel.MetricTypeSummary),
187 keep: true,
188 sameLabelBacking: true,
189 },
190 "drop drops matching input": {
191 cfgs: []Config{
192 {
193 SourceLabels: []string{"a"},
194 Regex: MustNewRegexp(".*o.*"),
195 Action: Drop,
196 },
197 },
198 in: sample("test_metric", map[string]string{"a": "foo"}, 1, prompkg.SampleKindScalar, commonmodel.MetricTypeGauge),
199 keep: false,
200 },
201 "drop uses anchored regex semantics": {
202 cfgs: []Config{
203 {
204 SourceLabels: []string{"a"},
205 Regex: MustNewRegexp("f|o"),
206 Action: Drop,
207 },
208 },
209 in: sample("test_metric", map[string]string{"a": "foo"}, 1, prompkg.SampleKindScalar, commonmodel.MetricTypeGauge),
210 want: sample("test_metric", map[string]string{"a": "foo"}, 1, prompkg.SampleKindScalar, commonmodel.MetricTypeGauge),
211 keep: true,
212 sameLabelBacking: true,
213 },
214 "keep drops non matching input": {
215 cfgs: []Config{
216 {
217 SourceLabels: []string{"a"},
218 Regex: MustNewRegexp("no-match"),
219 Action: Keep,
220 },
221 },
222 in: sample("test_metric", map[string]string{"a": "foo"}, 1, prompkg.SampleKindScalar, commonmodel.MetricTypeGauge),
223 keep: false,
224 },
225 "ordered rules see prior __name__ rewrite": {
226 cfgs: []Config{
227 {
228 SourceLabels: []string{"rename_to"},
229 TargetLabel: commonmodel.MetricNameLabel,
230 Replacement: "$1",
231 Action: Replace,
232 NameScheme: commonmodel.UTF8Validation,
233 },
234 {
235 SourceLabels: []string{commonmodel.MetricNameLabel},
236 TargetLabel: "seen_name",
237 Replacement: "prefix_$1",
238 Action: Replace,
239 },
240 },
241 in: sample("request_total", map[string]string{
242 "rename_to": "request.total",
243 }, 1, prompkg.SampleKindScalar, commonmodel.MetricTypeCounter),
244 want: sample("request.total", map[string]string{
245 "rename_to": "request.total",
246 "seen_name": "prefix_request.total",
247 }, 1, prompkg.SampleKindScalar, commonmodel.MetricTypeCounter),
248 keep: true,
249 },
250 "keepequal keeps when values match": {
251 cfgs: []Config{
252 {
253 SourceLabels: []string{"__tmp_port"},
254 TargetLabel: "__port1",
255 Action: KeepEqual,
256 },
257 },
258 in: sample("test_metric", map[string]string{"__tmp_port": "1234", "__port1": "1234"}, 1, prompkg.SampleKindScalar, commonmodel.MetricTypeGauge),
259 want: sample("test_metric", map[string]string{
260 "__tmp_port": "1234",
261 "__port1": "1234",
262 }, 1, prompkg.SampleKindScalar, commonmodel.MetricTypeGauge),
263 keep: true,
264 },
265 "dropequal drops when values match": {
266 cfgs: []Config{
267 {
268 SourceLabels: []string{"__tmp_port"},
269 TargetLabel: "__port1",
270 Action: DropEqual,
271 },
272 },
273 in: sample("test_metric", map[string]string{"__tmp_port": "1234", "__port1": "1234"}, 1, prompkg.SampleKindScalar, commonmodel.MetricTypeGauge),
274 keep: false,
275 },
276 "hashmod matches upstream example": {
277 cfgs: []Config{
278 {
279 SourceLabels: []string{"c"},
280 TargetLabel: "d",
281 Action: HashMod,
282 Modulus: 1000,
283 },
284 },
285 in: sample("test_metric", map[string]string{"a": "foo", "b": "bar", "c": "baz"}, 1, prompkg.SampleKindScalar, commonmodel.MetricTypeGauge),
286 want: sample("test_metric", map[string]string{
287 "a": "foo",
288 "b": "bar",
289 "c": "baz",
290 "d": "976",
291 }, 1, prompkg.SampleKindScalar, commonmodel.MetricTypeGauge),
292 keep: true,
293 },
294 "labelmap copies matching labels": {
295 cfgs: []Config{
296 {
297 Regex: MustNewRegexp("(b.*)"),
298 Replacement: "bar_${1}",
299 Action: LabelMap,
300 },
301 },
302 in: sample("test_metric", map[string]string{"a": "foo", "b1": "bar", "b2": "baz"}, 1, prompkg.SampleKindScalar, commonmodel.MetricTypeGauge),
303 want: sample("test_metric", map[string]string{
304 "a": "foo",
305 "b1": "bar",
306 "b2": "baz",
307 "bar_b1": "bar",
308 "bar_b2": "baz",
309 }, 1, prompkg.SampleKindScalar, commonmodel.MetricTypeGauge),
310 keep: true,
311 },
312 "labeldrop removes matching labels": {
313 cfgs: []Config{
314 {
315 Regex: MustNewRegexp("(b.*)"),
316 Action: LabelDrop,
317 },
318 },
319 in: sample("test_metric", map[string]string{"a": "foo", "b1": "bar", "b2": "baz"}, 1, prompkg.SampleKindScalar, commonmodel.MetricTypeGauge),
320 want: sample("test_metric", map[string]string{
321 "a": "foo",
322 }, 1, prompkg.SampleKindScalar, commonmodel.MetricTypeGauge),
323 keep: true,
324 },
325 "labelkeep can drop __name__ and therefore drop the sample": {
326 cfgs: []Config{
327 {
328 Regex: MustNewRegexp("(b.*)"),
329 Action: LabelKeep,
330 },
331 },
332 in: sample("test_metric", map[string]string{"b1": "bar"}, 1, prompkg.SampleKindScalar, commonmodel.MetricTypeGauge),
333 keep: false,
334 },
335 "lowercase and uppercase write derived labels": {
336 cfgs: []Config{
337 {
338 SourceLabels: []string{"foo"},
339 TargetLabel: "foo_uppercase",
340 Action: Uppercase,
341 },
342 {
343 SourceLabels: []string{"foo"},
344 TargetLabel: "foo_lowercase",
345 Action: Lowercase,
346 },
347 },
348 in: sample("test_metric", map[string]string{"foo": "bAr123Foo"}, 1, prompkg.SampleKindScalar, commonmodel.MetricTypeGauge),
349 want: sample("test_metric", map[string]string{
350 "foo": "bAr123Foo",
351 "foo_lowercase": "bar123foo",
352 "foo_uppercase": "BAR123FOO",
353 }, 1, prompkg.SampleKindScalar, commonmodel.MetricTypeGauge),
354 keep: true,
355 },
356 "explicit empty replacement deletes target label": {
357 cfgs: []Config{
358 {
359 SourceLabels: []string{"a"},
360 TargetLabel: "b",
361 Replacement: "",
362 Action: Replace,
363 replacementSet: true,
364 },
365 },
366 in: sample("test_metric", map[string]string{"a": "foo", "b": "bar"}, 1, prompkg.SampleKindScalar, commonmodel.MetricTypeGauge),
367 want: sample("test_metric", map[string]string{
368 "a": "foo",
369 }, 1, prompkg.SampleKindScalar, commonmodel.MetricTypeGauge),
370 keep: true,
371 },
372 "invalid final metric name drops the sample": {
373 cfgs: []Config{
374 {
375 SourceLabels: []string{commonmodel.MetricNameLabel},
376 TargetLabel: commonmodel.MetricNameLabel,
377 Replacement: "",
378 Action: Replace,
379 replacementSet: true,
380 },
381 },
382 in: sample("test_metric", map[string]string{"a": "foo"}, 1, prompkg.SampleKindScalar, commonmodel.MetricTypeGauge),
383 keep: false,
384 },
385 }
386
387 for name, test := range tests {
388 t.Run(name, func(t *testing.T) {
389 p, err := New(test.cfgs)
390 require.NoError(t, err)
391
392 got, drop := p.Apply(test.in)
393 require.Equal(t, test.keep, !drop.Dropped())
394 if !drop.Dropped() {
395 assert.Equal(t, test.want, got)
396 if test.sameLabelBacking && len(test.in.Labels) > 0 {
397 require.NotEmpty(t, got.Labels)
398 assert.True(t, &got.Labels[0] == &test.in.Labels[0])
399 }
400 }
401 })
402 }
403 }
404
405 func TestNew_Validate(t *testing.T) {
406 tests := map[string]struct {
407 cfgs []Config
408 wantErrText string
409 }{
410 "rejects labeldrop with explicit empty source_labels": {
411 cfgs: []Config{{
412 Action: LabelDrop,
413 Regex: MustNewRegexp("foo"),
414 sourceLabelsSet: true,
415 }},
416 wantErrText: `requires only 'regex'`,
417 },
418 "rejects replace with invalid regex-var target label under legacy": {
419 cfgs: []Config{{
420 Action: Replace,
421 SourceLabels: []string{"a"},
422 TargetLabel: "${1}.x",
423 NameScheme: commonmodel.LegacyValidation,
424 }},
425 wantErrText: `invalid 'target_label'`,
426 },
427 "rejects labelmap with invalid replacement under legacy": {
428 cfgs: []Config{{
429 Action: LabelMap,
430 Regex: MustNewRegexp("(.+)"),
431 Replacement: "bad.name",
432 NameScheme: commonmodel.LegacyValidation,
433 }},
434 wantErrText: `invalid 'replacement'`,
435 },
436 "rejects unknown action": {
437 cfgs: []Config{{Action: Action("wat")}},
438 wantErrText: `unknown relabel action "wat"`,
439 },
440 "rejects missing target label for replace": {
441 cfgs: []Config{{Action: Replace}},
442 wantErrText: `requires 'target_label' value`,
443 },
444 "rejects hashmod without modulus": {
445 cfgs: []Config{{
446 Action: HashMod,
447 TargetLabel: "d",
448 }},
449 wantErrText: `requires non-zero modulus`,
450 },
451 "rejects labeldrop extra fields": {
452 cfgs: []Config{{
453 Action: LabelDrop,
454 Regex: MustNewRegexp("foo"),
455 Replacement: "bar",
456 replacementSet: true,
457 }},
458 wantErrText: `requires only 'regex'`,
459 },
460 "rejects keepequal with replacement": {
461 cfgs: []Config{{
462 Action: KeepEqual,
463 TargetLabel: "__port1",
464 Replacement: "bar",
465 replacementSet: true,
466 }},
467 wantErrText: `'replacement' can not be set for keepequal action`,
468 },
469 "rejects legacy invalid target label": {
470 cfgs: []Config{{
471 Action: Lowercase,
472 TargetLabel: "${3}",
473 NameScheme: commonmodel.LegacyValidation,
474 }},
475 wantErrText: `"${3}" is invalid 'target_label' for lowercase action`,
476 },
477 "accepts utf8 target label for lowercase": {
478 cfgs: []Config{{
479 Action: Lowercase,
480 TargetLabel: "${3}",
481 NameScheme: commonmodel.UTF8Validation,
482 }},
483 },
484 "defaults to utf8 validation when unset (accepts a target legacy would reject)": {
485 cfgs: []Config{{
486 Action: Lowercase,
487 TargetLabel: "${3}",
488 }},
489 },
490 "rejects invalid name scheme": {
491 cfgs: []Config{{
492 Action: Lowercase,
493 TargetLabel: "foo",
494 NameScheme: commonmodel.ValidationScheme(99),
495 }},
496 wantErrText: `unknown relabel config name validation method specified`,
497 },
498 }
499
500 for name, test := range tests {
501 t.Run(name, func(t *testing.T) {
502 _, err := New(test.cfgs)
503 if test.wantErrText == "" {
504 require.NoError(t, err)
505 return
506 }
507
508 require.ErrorContains(t, err, test.wantErrText)
509 })
510 }
511 }
512
513 func sample(name string, lbs map[string]string, value float64, kind prompkg.SampleKind, familyType commonmodel.MetricType) prompkg.Sample {
514 return prompkg.Sample{
515 Name: name,
516 Labels: labels.FromMap(lbs),
517 Value: value,
518 Kind: kind,
519 FamilyType: familyType,
520 }
521 }
522
523 func TestProcessor_Apply_dropInfo(t *testing.T) {
524 tests := map[string]struct {
525 cfgs []Config
526 in prompkg.Sample
527 wantReason DropReason
528 wantRule int
529 wantAction Action
530 }{
531 "drop rule matched": {
532 cfgs: []Config{{SourceLabels: []string{"a"}, Regex: MustNewRegexp("foo"), Action: Drop}},
533 in: sample("m", map[string]string{"a": "foo"}, 1, prompkg.SampleKindScalar, commonmodel.MetricTypeGauge),
534 wantReason: DropReasonDropRuleMatched,
535 wantRule: 0,
536 wantAction: Drop,
537 },
538 "dropequal matched": {
539 cfgs: []Config{{SourceLabels: []string{"a"}, TargetLabel: "b", Action: DropEqual}},
540 in: sample("m", map[string]string{"a": "x", "b": "x"}, 1, prompkg.SampleKindScalar, commonmodel.MetricTypeGauge),
541 wantReason: DropReasonDropEqualMatched,
542 wantRule: 0,
543 wantAction: DropEqual,
544 },
545 "keepequal did not match": {
546 cfgs: []Config{{SourceLabels: []string{"a"}, TargetLabel: "b", Action: KeepEqual}},
547 in: sample("m", map[string]string{"a": "x", "b": "y"}, 1, prompkg.SampleKindScalar, commonmodel.MetricTypeGauge),
548 wantReason: DropReasonKeepEqualMismatch,
549 wantRule: 0,
550 wantAction: KeepEqual,
551 },
552 "keep rule did not match": {
553 cfgs: []Config{{SourceLabels: []string{"a"}, Regex: MustNewRegexp("foo"), Action: Keep}},
554 in: sample("m", map[string]string{"a": "bar"}, 1, prompkg.SampleKindScalar, commonmodel.MetricTypeGauge),
555 wantReason: DropReasonKeepRuleMismatch,
556 wantRule: 0,
557 wantAction: Keep,
558 },
559 "invalid metric name (labelkeep drops __name__)": {
560 cfgs: []Config{{Regex: MustNewRegexp("keep"), Action: LabelKeep}},
561 in: sample("m", map[string]string{"keep": "v", "other": "x"}, 1, prompkg.SampleKindScalar, commonmodel.MetricTypeGauge),
562 wantReason: DropReasonInvalidMetricName,
563 wantRule: -1,
564 wantAction: "",
565 },
566 }
567
568 for name, test := range tests {
569 t.Run(name, func(t *testing.T) {
570 p, err := New(test.cfgs)
571 require.NoError(t, err)
572
573 got, drop := p.Apply(test.in)
574 require.True(t, drop.Dropped())
575 assert.Equal(t, test.wantReason, drop.Reason)
576 assert.Equal(t, test.wantRule, drop.RuleIndex)
577 assert.Equal(t, test.wantAction, drop.Action)
578 // Apply returns the original sample on drop so the caller can log it.
579 assert.Equal(t, test.in, got)
580 })
581 }
582 }
583
584 func TestNewTransform(t *testing.T) {
585 gauge := func() prompkg.Sample {
586 return sample("m", map[string]string{"a": "foo"}, 1, prompkg.SampleKindScalar, commonmodel.MetricTypeGauge)
587 }
588
589 t.Run("nil transform when there are no rules", func(t *testing.T) {
590 tr, err := NewTransform(nil, nil)
591 require.NoError(t, err)
592 assert.Nil(t, tr)
593 })
594
595 t.Run("invalid rules return an error", func(t *testing.T) {
596 _, err := NewTransform([]Config{{Action: HashMod, TargetLabel: "x"}}, nil)
597 assert.Error(t, err)
598 })
599
600 t.Run("applies rules and keeps", func(t *testing.T) {
601 tr, err := NewTransform([]Config{{
602 SourceLabels: []string{"a"},
603 Regex: MustNewRegexp("f(.*)"),
604 TargetLabel: "b",
605 Replacement: "x${1}",
606 Action: Replace,
607 }}, nil)
608 require.NoError(t, err)
609 require.NotNil(t, tr)
610
611 out, keep, err := tr(gauge())
612 require.NoError(t, err)
613 assert.True(t, keep)
614 assert.Equal(t, "xoo", out.Labels.Get("b"))
615 })
616
617 t.Run("drop calls onDrop with the original sample and no scrape error", func(t *testing.T) {
618 var observed []DropInfo
619 var observedSample prompkg.Sample
620 tr, err := NewTransform([]Config{{
621 SourceLabels: []string{"a"},
622 Regex: MustNewRegexp("foo"),
623 Action: Drop,
624 }}, func(s prompkg.Sample, d DropInfo) {
625 observedSample = s
626 observed = append(observed, d)
627 })
628 require.NoError(t, err)
629
630 in := gauge()
631 _, keep, err := tr(in)
632 require.NoError(t, err) // a drop is not a scrape error
633 assert.False(t, keep)
634 require.Len(t, observed, 1)
635 assert.Equal(t, DropReasonDropRuleMatched, observed[0].Reason)
636 assert.Equal(t, in, observedSample)
637 })
638 }