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  }