github.com/yankunsam/loki/v2@v2.6.3-0.20220817130409-389df5235c27/pkg/logql/log/fmt_test.go (about) 1 package log 2 3 import ( 4 "fmt" 5 "sort" 6 "testing" 7 8 "github.com/prometheus/prometheus/model/labels" 9 "github.com/stretchr/testify/require" 10 11 "github.com/grafana/loki/pkg/logqlmodel" 12 ) 13 14 func Test_lineFormatter_Format(t *testing.T) { 15 tests := []struct { 16 name string 17 fmter *LineFormatter 18 lbs labels.Labels 19 ts int64 20 21 want []byte 22 wantLbs labels.Labels 23 in []byte 24 }{ 25 { 26 "combining", 27 newMustLineFormatter("foo{{.foo}}buzz{{ .bar }}"), 28 labels.Labels{{Name: "foo", Value: "blip"}, {Name: "bar", Value: "blop"}}, 29 0, 30 []byte("fooblipbuzzblop"), 31 labels.Labels{{Name: "foo", Value: "blip"}, {Name: "bar", Value: "blop"}}, 32 nil, 33 }, 34 { 35 "Replace", 36 newMustLineFormatter(`foo{{.foo}}buzz{{ Replace .bar "blop" "bar" -1 }}`), 37 labels.Labels{{Name: "foo", Value: "blip"}, {Name: "bar", Value: "blop"}}, 38 0, 39 []byte("fooblipbuzzbar"), 40 labels.Labels{{Name: "foo", Value: "blip"}, {Name: "bar", Value: "blop"}}, 41 nil, 42 }, 43 { 44 "replace", 45 newMustLineFormatter(`foo{{.foo}}buzz{{ .bar | replace "blop" "bar" }}`), 46 labels.Labels{{Name: "foo", Value: "blip"}, {Name: "bar", Value: "blop"}}, 47 0, 48 []byte("fooblipbuzzbar"), 49 labels.Labels{{Name: "foo", Value: "blip"}, {Name: "bar", Value: "blop"}}, 50 nil, 51 }, 52 { 53 "title", 54 newMustLineFormatter(`{{.foo | title }}`), 55 labels.Labels{{Name: "foo", Value: "blip"}, {Name: "bar", Value: "blop"}}, 56 0, 57 []byte("Blip"), 58 labels.Labels{{Name: "foo", Value: "blip"}, {Name: "bar", Value: "blop"}}, 59 nil, 60 }, 61 { 62 "substr and trunc", 63 newMustLineFormatter( 64 `{{.foo | substr 1 3 }} {{ .bar | trunc 1 }} {{ .bar | trunc 3 }}`, 65 ), 66 labels.Labels{{Name: "foo", Value: "blip"}, {Name: "bar", Value: "blop"}}, 67 0, 68 []byte("li b blo"), 69 labels.Labels{{Name: "foo", Value: "blip"}, {Name: "bar", Value: "blop"}}, 70 nil, 71 }, 72 { 73 "trim", 74 newMustLineFormatter( 75 `{{.foo | trim }} {{ .bar | trimAll "op" }} {{ .bar | trimPrefix "b" }} {{ .bar | trimSuffix "p" }}`, 76 ), 77 labels.Labels{{Name: "foo", Value: " blip "}, {Name: "bar", Value: "blop"}}, 78 0, 79 []byte("blip bl lop blo"), 80 labels.Labels{{Name: "foo", Value: " blip "}, {Name: "bar", Value: "blop"}}, 81 nil, 82 }, 83 { 84 "lower and upper", 85 newMustLineFormatter(`{{.foo | lower }} {{ .bar | upper }}`), 86 labels.Labels{{Name: "foo", Value: "BLIp"}, {Name: "bar", Value: "blop"}}, 87 0, 88 []byte("blip BLOP"), 89 labels.Labels{{Name: "foo", Value: "BLIp"}, {Name: "bar", Value: "blop"}}, 90 nil, 91 }, 92 { 93 "repeat", 94 newMustLineFormatter(`{{ "foo" | repeat 3 }}`), 95 labels.Labels{{Name: "foo", Value: "BLIp"}, {Name: "bar", Value: "blop"}}, 96 0, 97 []byte("foofoofoo"), 98 labels.Labels{{Name: "foo", Value: "BLIp"}, {Name: "bar", Value: "blop"}}, 99 nil, 100 }, 101 { 102 "indent", 103 newMustLineFormatter(`{{ "foo\n bar" | indent 4 }}`), 104 labels.Labels{{Name: "foo", Value: "BLIp"}, {Name: "bar", Value: "blop"}}, 105 0, 106 []byte(" foo\n bar"), 107 labels.Labels{{Name: "foo", Value: "BLIp"}, {Name: "bar", Value: "blop"}}, 108 nil, 109 }, 110 { 111 "nindent", 112 newMustLineFormatter(`{{ "foo" | nindent 2 }}`), 113 labels.Labels{{Name: "foo", Value: "BLIp"}, {Name: "bar", Value: "blop"}}, 114 0, 115 []byte("\n foo"), 116 labels.Labels{{Name: "foo", Value: "BLIp"}, {Name: "bar", Value: "blop"}}, 117 nil, 118 }, 119 { 120 "contains", 121 newMustLineFormatter(`{{ if .foo | contains "p"}}yes{{end}}-{{ if .foo | contains "z"}}no{{end}}`), 122 labels.Labels{{Name: "foo", Value: "BLIp"}, {Name: "bar", Value: "blop"}}, 123 0, 124 []byte("yes-"), 125 labels.Labels{{Name: "foo", Value: "BLIp"}, {Name: "bar", Value: "blop"}}, 126 nil, 127 }, 128 { 129 "hasPrefix", 130 newMustLineFormatter(`{{ if .foo | hasPrefix "BL" }}yes{{end}}-{{ if .foo | hasPrefix "p"}}no{{end}}`), 131 labels.Labels{{Name: "foo", Value: "BLIp"}, {Name: "bar", Value: "blop"}}, 132 0, 133 []byte("yes-"), 134 labels.Labels{{Name: "foo", Value: "BLIp"}, {Name: "bar", Value: "blop"}}, 135 nil, 136 }, 137 { 138 "hasSuffix", 139 newMustLineFormatter(`{{ if .foo | hasSuffix "Ip" }}yes{{end}}-{{ if .foo | hasSuffix "pw"}}no{{end}}`), 140 labels.Labels{{Name: "foo", Value: "BLIp"}, {Name: "bar", Value: "blop"}}, 141 0, 142 []byte("yes-"), 143 labels.Labels{{Name: "foo", Value: "BLIp"}, {Name: "bar", Value: "blop"}}, 144 nil, 145 }, 146 { 147 "regexReplaceAll", 148 newMustLineFormatter(`{{ regexReplaceAll "(p)" .foo "t" }}`), 149 labels.Labels{{Name: "foo", Value: "BLIp"}, {Name: "bar", Value: "blop"}}, 150 0, 151 []byte("BLIt"), 152 labels.Labels{{Name: "foo", Value: "BLIp"}, {Name: "bar", Value: "blop"}}, 153 nil, 154 }, 155 { 156 "regexReplaceAllLiteral", 157 newMustLineFormatter(`{{ regexReplaceAllLiteral "(p)" .foo "${1}" }}`), 158 labels.Labels{{Name: "foo", Value: "BLIp"}, {Name: "bar", Value: "blop"}}, 159 0, 160 []byte("BLI${1}"), 161 labels.Labels{{Name: "foo", Value: "BLIp"}, {Name: "bar", Value: "blop"}}, 162 nil, 163 }, 164 { 165 "err", 166 newMustLineFormatter(`{{.foo Replace "foo"}}`), 167 labels.Labels{{Name: "foo", Value: "blip"}, {Name: "bar", Value: "blop"}}, 168 0, 169 nil, 170 labels.Labels{ 171 {Name: "__error__", Value: "TemplateFormatErr"}, 172 {Name: "foo", Value: "blip"}, {Name: "bar", Value: "blop"}, 173 {Name: "__error_details__", Value: "template: line:1:2: executing \"line\" at <.foo>: foo is not a method but has arguments"}, 174 }, 175 nil, 176 }, 177 { 178 "missing", 179 newMustLineFormatter("foo {{.foo}}buzz{{ .bar }}"), 180 labels.Labels{{Name: "bar", Value: "blop"}}, 181 0, 182 []byte("foo buzzblop"), 183 labels.Labels{{Name: "bar", Value: "blop"}}, 184 nil, 185 }, 186 { 187 "function", 188 newMustLineFormatter("foo {{.foo | ToUpper }} buzz{{ .bar }}"), 189 labels.Labels{{Name: "foo", Value: "blip"}, {Name: "bar", Value: "blop"}}, 190 0, 191 []byte("foo BLIP buzzblop"), 192 labels.Labels{{Name: "foo", Value: "blip"}, {Name: "bar", Value: "blop"}}, 193 nil, 194 }, 195 { 196 "mathint", 197 newMustLineFormatter("{{ add .foo 1 | sub .bar | mul .baz | div .bazz}}"), 198 labels.Labels{{Name: "foo", Value: "1"}, {Name: "bar", Value: "3"}, {Name: "baz", Value: "10"}, {Name: "bazz", Value: "20"}}, 199 0, 200 []byte("2"), 201 labels.Labels{{Name: "foo", Value: "1"}, {Name: "bar", Value: "3"}, {Name: "baz", Value: "10"}, {Name: "bazz", Value: "20"}}, 202 nil, 203 }, 204 { 205 "mathfloat", 206 newMustLineFormatter("{{ addf .foo 1.5 | subf .bar 1.5 | mulf .baz | divf .bazz }}"), 207 labels.Labels{{Name: "foo", Value: "1.5"}, {Name: "bar", Value: "5"}, {Name: "baz", Value: "10.5"}, {Name: "bazz", Value: "20.2"}}, 208 0, 209 []byte("3.8476190476190477"), 210 labels.Labels{{Name: "foo", Value: "1.5"}, {Name: "bar", Value: "5"}, {Name: "baz", Value: "10.5"}, {Name: "bazz", Value: "20.2"}}, 211 nil, 212 }, 213 { 214 "mathfloatround", 215 newMustLineFormatter("{{ round (addf .foo 1.5 | subf .bar | mulf .baz | divf .bazz) 5 .2}}"), 216 labels.Labels{{Name: "foo", Value: "1.5"}, {Name: "bar", Value: "3.5"}, {Name: "baz", Value: "10.5"}, {Name: "bazz", Value: "20.4"}}, 217 0, 218 []byte("3.88572"), 219 labels.Labels{{Name: "foo", Value: "1.5"}, {Name: "bar", Value: "3.5"}, {Name: "baz", Value: "10.5"}, {Name: "bazz", Value: "20.4"}}, 220 nil, 221 }, 222 { 223 "min", 224 newMustLineFormatter("min is {{ min .foo .bar .baz }} and max is {{ max .foo .bar .baz }}"), 225 labels.Labels{{Name: "foo", Value: "5"}, {Name: "bar", Value: "10"}, {Name: "baz", Value: "15"}}, 226 0, 227 []byte("min is 5 and max is 15"), 228 labels.Labels{{Name: "foo", Value: "5"}, {Name: "bar", Value: "10"}, {Name: "baz", Value: "15"}}, 229 nil, 230 }, 231 { 232 "max", 233 newMustLineFormatter("minf is {{ minf .foo .bar .baz }} and maxf is {{maxf .foo .bar .baz}}"), 234 labels.Labels{{Name: "foo", Value: "5.3"}, {Name: "bar", Value: "10.5"}, {Name: "baz", Value: "15.2"}}, 235 0, 236 []byte("minf is 5.3 and maxf is 15.2"), 237 labels.Labels{{Name: "foo", Value: "5.3"}, {Name: "bar", Value: "10.5"}, {Name: "baz", Value: "15.2"}}, 238 nil, 239 }, 240 { 241 "ceilfloor", 242 newMustLineFormatter("ceil is {{ ceil .foo }} and floor is {{floor .foo }}"), 243 labels.Labels{{Name: "foo", Value: "5.3"}}, 244 0, 245 []byte("ceil is 6 and floor is 5"), 246 labels.Labels{{Name: "foo", Value: "5.3"}}, 247 nil, 248 }, 249 { 250 "mod", 251 newMustLineFormatter("mod is {{ mod .foo 3 }}"), 252 labels.Labels{{Name: "foo", Value: "20"}}, 253 0, 254 []byte("mod is 2"), 255 labels.Labels{{Name: "foo", Value: "20"}}, 256 nil, 257 }, 258 { 259 "float64int", 260 newMustLineFormatter("{{ \"2.5\" | float64 | int | add 10}}"), 261 labels.Labels{{Name: "foo", Value: "2.5"}}, 262 0, 263 []byte("12"), 264 labels.Labels{{Name: "foo", Value: "2.5"}}, 265 nil, 266 }, 267 { 268 "datetime", 269 newMustLineFormatter("{{ sub (unixEpoch (toDate \"2006-01-02\" \"2021-11-02\")) (unixEpoch (toDate \"2006-01-02\" \"2021-11-01\")) }}"), 270 labels.Labels{}, 271 0, 272 []byte("86400"), 273 labels.Labels{}, 274 nil, 275 }, 276 { 277 "dateformat", 278 newMustLineFormatter("{{ date \"2006-01-02\" (toDate \"2006-01-02\" \"2021-11-02\") }}"), 279 labels.Labels{}, 280 0, 281 []byte("2021-11-02"), 282 labels.Labels{}, 283 nil, 284 }, 285 { 286 "now", 287 newMustLineFormatter("{{ div (unixEpoch now) (unixEpoch now) }}"), 288 labels.Labels{}, 289 0, 290 []byte("1"), 291 labels.Labels{}, 292 nil, 293 }, 294 { 295 "line", 296 newMustLineFormatter("{{ __line__ }} bar {{ .bar }}"), 297 labels.Labels{{Name: "bar", Value: "2"}}, 298 0, 299 []byte("1 bar 2"), 300 labels.Labels{{Name: "bar", Value: "2"}}, 301 []byte("1"), 302 }, 303 { 304 "default", 305 newMustLineFormatter(`{{.foo | default "-" }}{{.bar | default "-"}}{{.unknown | default "-"}}`), 306 labels.Labels{{Name: "foo", Value: "blip"}, {Name: "bar", Value: ""}}, 307 0, 308 []byte("blip--"), 309 labels.Labels{{Name: "foo", Value: "blip"}, {Name: "bar", Value: ""}}, 310 nil, 311 }, 312 { 313 "timestamp", 314 newMustLineFormatter("{{ __timestamp__ | date \"2006-01-02\" }} bar {{ .bar }}"), 315 labels.Labels{{Name: "bar", Value: "2"}}, 316 1656353124120000000, 317 []byte("2022-06-27 bar 2"), 318 labels.Labels{{Name: "bar", Value: "2"}}, 319 []byte("1"), 320 }, 321 { 322 "timestamp_unix", 323 newMustLineFormatter("{{ __timestamp__ | unixEpoch }} bar {{ .bar }}"), 324 labels.Labels{{Name: "bar", Value: "2"}}, 325 1656353124120000000, 326 []byte("1656353124 bar 2"), 327 labels.Labels{{Name: "bar", Value: "2"}}, 328 []byte("1"), 329 }, 330 { 331 "template_error", 332 newMustLineFormatter("{{.foo | now}}"), 333 labels.Labels{{Name: "foo", Value: "blip"}, {Name: "bar", Value: "blop"}}, 334 0, 335 nil, 336 labels.Labels{ 337 {Name: "foo", Value: "blip"}, 338 {Name: "bar", Value: "blop"}, 339 {Name: "__error__", Value: "TemplateFormatErr"}, 340 {Name: "__error_details__", Value: "template: line:1:9: executing \"line\" at <now>: wrong number of args for now: want 0 got 1"}, 341 }, 342 nil, 343 }, 344 } 345 for _, tt := range tests { 346 t.Run(tt.name, func(t *testing.T) { 347 sort.Sort(tt.lbs) 348 sort.Sort(tt.wantLbs) 349 builder := NewBaseLabelsBuilder().ForLabels(tt.lbs, tt.lbs.Hash()) 350 builder.Reset() 351 outLine, _ := tt.fmter.Process(tt.ts, tt.in, builder) 352 require.Equal(t, tt.want, outLine) 353 require.Equal(t, tt.wantLbs, builder.LabelsResult().Labels()) 354 }) 355 } 356 } 357 358 func newMustLineFormatter(tmpl string) *LineFormatter { 359 l, err := NewFormatter(tmpl) 360 if err != nil { 361 panic(err) 362 } 363 return l 364 } 365 366 func Test_labelsFormatter_Format(t *testing.T) { 367 tests := []struct { 368 name string 369 fmter *LabelsFormatter 370 371 in labels.Labels 372 want labels.Labels 373 }{ 374 { 375 "combined with template", 376 mustNewLabelsFormatter([]LabelFmt{NewTemplateLabelFmt("foo", "{{.foo}} and {{.bar}}")}), 377 labels.Labels{{Name: "foo", Value: "blip"}, {Name: "bar", Value: "blop"}}, 378 labels.Labels{{Name: "foo", Value: "blip and blop"}, {Name: "bar", Value: "blop"}}, 379 }, 380 { 381 "combined with template and rename", 382 mustNewLabelsFormatter([]LabelFmt{ 383 NewTemplateLabelFmt("blip", "{{.foo}} and {{.bar}}"), 384 NewRenameLabelFmt("bar", "foo"), 385 }), 386 labels.Labels{{Name: "foo", Value: "blip"}, {Name: "bar", Value: "blop"}}, 387 labels.Labels{{Name: "blip", Value: "blip and blop"}, {Name: "bar", Value: "blip"}}, 388 }, 389 { 390 "fn", 391 mustNewLabelsFormatter([]LabelFmt{ 392 NewTemplateLabelFmt("blip", "{{.foo | ToUpper }} and {{.bar}}"), 393 NewRenameLabelFmt("bar", "foo"), 394 }), 395 labels.Labels{{Name: "foo", Value: "blip"}, {Name: "bar", Value: "blop"}}, 396 labels.Labels{{Name: "blip", Value: "BLIP and blop"}, {Name: "bar", Value: "blip"}}, 397 }, 398 { 399 "math", 400 mustNewLabelsFormatter([]LabelFmt{NewTemplateLabelFmt("status", "{{div .status 100 }}")}), 401 labels.Labels{{Name: "status", Value: "200"}}, 402 labels.Labels{{Name: "status", Value: "2"}}, 403 }, 404 { 405 "default", 406 mustNewLabelsFormatter([]LabelFmt{ 407 NewTemplateLabelFmt("blip", `{{.foo | default "-" }} and {{.bar}}`), 408 }), 409 labels.Labels{{Name: "bar", Value: "blop"}}, 410 labels.Labels{{Name: "blip", Value: "- and blop"}, {Name: "bar", Value: "blop"}}, 411 }, 412 { 413 "template error", 414 mustNewLabelsFormatter([]LabelFmt{NewTemplateLabelFmt("bar", "{{replace \"test\" .foo}}")}), 415 labels.Labels{{Name: "foo", Value: "blip"}, {Name: "bar", Value: "blop"}}, 416 labels.Labels{ 417 {Name: "foo", Value: "blip"}, 418 {Name: "bar", Value: "blop"}, 419 {Name: "__error__", Value: "TemplateFormatErr"}, 420 {Name: "__error_details__", Value: "template: label:1:2: executing \"label\" at <replace>: wrong number of args for replace: want 3 got 2"}, 421 }, 422 }, 423 } 424 425 for _, tt := range tests { 426 t.Run(tt.name, func(t *testing.T) { 427 builder := NewBaseLabelsBuilder().ForLabels(tt.in, tt.in.Hash()) 428 builder.Reset() 429 _, _ = tt.fmter.Process(0, nil, builder) 430 sort.Sort(tt.want) 431 require.Equal(t, tt.want, builder.LabelsResult().Labels()) 432 }) 433 } 434 } 435 436 func mustNewLabelsFormatter(fmts []LabelFmt) *LabelsFormatter { 437 lf, err := NewLabelsFormatter(fmts) 438 if err != nil { 439 panic(err) 440 } 441 return lf 442 } 443 444 func Test_validate(t *testing.T) { 445 tests := []struct { 446 name string 447 fmts []LabelFmt 448 wantErr bool 449 }{ 450 {"no dup", []LabelFmt{NewRenameLabelFmt("foo", "bar"), NewRenameLabelFmt("bar", "foo")}, false}, 451 {"dup", []LabelFmt{NewRenameLabelFmt("foo", "bar"), NewRenameLabelFmt("foo", "blip")}, true}, 452 {"no error", []LabelFmt{NewRenameLabelFmt(logqlmodel.ErrorLabel, "bar")}, true}, 453 } 454 for _, tt := range tests { 455 t.Run(tt.name, func(t *testing.T) { 456 if err := validate(tt.fmts); (err != nil) != tt.wantErr { 457 t.Errorf("validate() error = %v, wantErr %v", err, tt.wantErr) 458 } 459 }) 460 } 461 } 462 463 func Test_trunc(t *testing.T) { 464 tests := []struct { 465 s string 466 c int 467 want string 468 }{ 469 {"Hello, 世界", -1, "界"}, 470 {"Hello, 世界", 1, "H"}, 471 {"Hello, 世界", 0, ""}, 472 {"Hello, 世界", 20, "Hello, 世界"}, 473 {"Hello, 世界", -20, "Hello, 世界"}, 474 } 475 for _, tt := range tests { 476 t.Run(fmt.Sprintf("%s%d", tt.s, tt.c), func(t *testing.T) { 477 if got := trunc(tt.c, tt.s); got != tt.want { 478 t.Errorf("trunc() = %v, want %v", got, tt.want) 479 } 480 }) 481 } 482 } 483 484 func Test_substring(t *testing.T) { 485 tests := []struct { 486 start int 487 end int 488 s string 489 want string 490 }{ 491 {1, 8, "Hello, 世界", "ello, 世"}, 492 {-10, 8, "Hello, 世界", "Hello, 世"}, 493 {1, 10, "Hello, 世界", "ello, 世界"}, 494 {-1, 10, "Hello, 世界", "Hello, 世界"}, 495 {-1, 1, "Hello, 世界", "H"}, 496 {-1, -1, "Hello, 世界", ""}, 497 {20, -1, "Hello, 世界", ""}, 498 {1, 1, "Hello, 世界", ""}, 499 {5, 1, "Hello, 世界", ""}, 500 {3, -1, "Hello, 世界", "lo, 世界"}, 501 } 502 for _, tt := range tests { 503 t.Run(tt.s, func(t *testing.T) { 504 if got := substring(tt.start, tt.end, tt.s); got != tt.want { 505 t.Errorf("substring() = %v, want %v", got, tt.want) 506 } 507 }) 508 } 509 } 510 511 func TestLineFormatter_RequiredLabelNames(t *testing.T) { 512 tests := []struct { 513 fmt string 514 want []string 515 }{ 516 {`{{.foo}} and {{.bar}}`, []string{"foo", "bar"}}, 517 {`{{ .foo | ToUpper | .buzz }} and {{.bar}}`, []string{"foo", "buzz", "bar"}}, 518 {`{{ regexReplaceAllLiteral "(p)" .foo "${1}" }}`, []string{"foo"}}, 519 {`{{ if .foo | hasSuffix "Ip" }} {{.bar}} {{end}}-{{ if .foo | hasSuffix "pw"}}no{{end}}`, []string{"foo", "bar"}}, 520 {`{{with .foo}}{{printf "%q" .}} {{end}}`, []string{"foo"}}, 521 {`{{with .foo}}{{printf "%q" .}} {{else}} {{ .buzz | lower }} {{end}}`, []string{"foo", "buzz"}}, 522 } 523 for _, tt := range tests { 524 t.Run(tt.fmt, func(t *testing.T) { 525 require.Equal(t, tt.want, newMustLineFormatter(tt.fmt).RequiredLabelNames()) 526 }) 527 } 528 } 529 530 func TestLabelFormatter_RequiredLabelNames(t *testing.T) { 531 tests := []struct { 532 name string 533 fmts []LabelFmt 534 want []string 535 }{ 536 {"rename", []LabelFmt{NewRenameLabelFmt("foo", "bar")}, []string{"bar"}}, 537 {"rename and fmt", []LabelFmt{NewRenameLabelFmt("fuzz", "bar"), NewTemplateLabelFmt("1", "{{ .foo | ToUpper | .buzz }} and {{.bar}}")}, []string{"bar", "foo", "buzz"}}, 538 {"fmt", []LabelFmt{NewTemplateLabelFmt("1", "{{.blip}}"), NewTemplateLabelFmt("2", "{{ .foo | ToUpper | .buzz }} and {{.bar}}")}, []string{"blip", "foo", "buzz", "bar"}}, 539 } 540 for _, tt := range tests { 541 t.Run(tt.name, func(t *testing.T) { 542 require.Equal(t, tt.want, mustNewLabelsFormatter(tt.fmts).RequiredLabelNames()) 543 }) 544 } 545 }