1// Copyright 2017 The Prometheus Authors
2// Licensed under the Apache License, Version 2.0 (the "License");
3// you may not use this file except in compliance with the License.
4// You may obtain a copy of the License at
5//
6// http://www.apache.org/licenses/LICENSE-2.0
7//
8// Unless required by applicable law or agreed to in writing, software
9// distributed under the License is distributed on an "AS IS" BASIS,
10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11// See the License for the specific language governing permissions and
12// limitations under the License.
13
14package labels
15
16import (
17	"encoding/json"
18	"testing"
19)
20
21func mustNewMatcher(t *testing.T, mType MatchType, value string) *Matcher {
22	m, err := NewMatcher(mType, "", value)
23	if err != nil {
24		t.Fatal(err)
25	}
26	return m
27}
28
29func TestMatcher(t *testing.T) {
30	tests := []struct {
31		matcher *Matcher
32		value   string
33		match   bool
34	}{
35		{
36			matcher: mustNewMatcher(t, MatchEqual, "bar"),
37			value:   "bar",
38			match:   true,
39		},
40		{
41			matcher: mustNewMatcher(t, MatchEqual, "bar"),
42			value:   "foo-bar",
43			match:   false,
44		},
45		{
46			matcher: mustNewMatcher(t, MatchNotEqual, "bar"),
47			value:   "bar",
48			match:   false,
49		},
50		{
51			matcher: mustNewMatcher(t, MatchNotEqual, "bar"),
52			value:   "foo-bar",
53			match:   true,
54		},
55		{
56			matcher: mustNewMatcher(t, MatchRegexp, "bar"),
57			value:   "bar",
58			match:   true,
59		},
60		{
61			matcher: mustNewMatcher(t, MatchRegexp, "bar"),
62			value:   "foo-bar",
63			match:   false,
64		},
65		{
66			matcher: mustNewMatcher(t, MatchRegexp, ".*bar"),
67			value:   "foo-bar",
68			match:   true,
69		},
70		{
71			matcher: mustNewMatcher(t, MatchNotRegexp, "bar"),
72			value:   "bar",
73			match:   false,
74		},
75		{
76			matcher: mustNewMatcher(t, MatchNotRegexp, "bar"),
77			value:   "foo-bar",
78			match:   true,
79		},
80		{
81			matcher: mustNewMatcher(t, MatchNotRegexp, ".*bar"),
82			value:   "foo-bar",
83			match:   false,
84		},
85		{
86			matcher: mustNewMatcher(t, MatchRegexp, `foo.bar`),
87			value:   "foo-bar",
88			match:   true,
89		},
90		{
91			matcher: mustNewMatcher(t, MatchRegexp, `foo\.bar`),
92			value:   "foo-bar",
93			match:   false,
94		},
95		{
96			matcher: mustNewMatcher(t, MatchRegexp, `foo\.bar`),
97			value:   "foo.bar",
98			match:   true,
99		},
100		{
101			matcher: mustNewMatcher(t, MatchEqual, "foo\nbar"),
102			value:   "foo\nbar",
103			match:   true,
104		},
105		{
106			matcher: mustNewMatcher(t, MatchRegexp, "foo.bar"),
107			value:   "foo\nbar",
108			match:   false,
109		},
110		{
111			matcher: mustNewMatcher(t, MatchRegexp, "(?s)foo.bar"),
112			value:   "foo\nbar",
113			match:   true,
114		},
115		{
116			matcher: mustNewMatcher(t, MatchEqual, "~!=\""),
117			value:   "~!=\"",
118			match:   true,
119		},
120	}
121
122	for _, test := range tests {
123		if test.matcher.Matches(test.value) != test.match {
124			t.Fatalf("Unexpected match result for matcher %v and value %q; want %v, got %v", test.matcher, test.value, test.match, !test.match)
125		}
126	}
127}
128
129func TestMatcherString(t *testing.T) {
130	tests := []struct {
131		name  string
132		op    MatchType
133		value string
134		want  string
135	}{
136		{
137			name:  `foo`,
138			op:    MatchEqual,
139			value: `bar`,
140			want:  `foo="bar"`,
141		},
142		{
143			name:  `foo`,
144			op:    MatchNotEqual,
145			value: `bar`,
146			want:  `foo!="bar"`,
147		},
148		{
149			name:  `foo`,
150			op:    MatchRegexp,
151			value: `bar`,
152			want:  `foo=~"bar"`,
153		},
154		{
155			name:  `foo`,
156			op:    MatchNotRegexp,
157			value: `bar`,
158			want:  `foo!~"bar"`,
159		},
160		{
161			name:  `foo`,
162			op:    MatchEqual,
163			value: `back\slash`,
164			want:  `foo="back\\slash"`,
165		},
166		{
167			name:  `foo`,
168			op:    MatchEqual,
169			value: `double"quote`,
170			want:  `foo="double\"quote"`,
171		},
172		{
173			name: `foo`,
174			op:   MatchEqual,
175			value: `new
176line`,
177			want: `foo="new\nline"`,
178		},
179		{
180			name: `foo`,
181			op:   MatchEqual,
182			value: `tab	stop`,
183			want: `foo="tab	stop"`,
184		},
185	}
186
187	for _, test := range tests {
188		m, err := NewMatcher(test.op, test.name, test.value)
189		if err != nil {
190			t.Fatal(err)
191		}
192		if got := m.String(); got != test.want {
193			t.Errorf("Unexpected string representation of matcher; want %v, got %v", test.want, got)
194		}
195	}
196}
197
198func TestMatcherJSONMarshal(t *testing.T) {
199	tests := []struct {
200		name  string
201		op    MatchType
202		value string
203		want  string
204	}{
205		{
206			name:  `foo`,
207			op:    MatchEqual,
208			value: `bar`,
209			want:  `{"name":"foo","value":"bar","isRegex":false,"isEqual":true}`,
210		},
211		{
212			name:  `foo`,
213			op:    MatchNotEqual,
214			value: `bar`,
215			want:  `{"name":"foo","value":"bar","isRegex":false,"isEqual":false}`,
216		},
217		{
218			name:  `foo`,
219			op:    MatchRegexp,
220			value: `bar`,
221			want:  `{"name":"foo","value":"bar","isRegex":true,"isEqual":true}`,
222		},
223		{
224			name:  `foo`,
225			op:    MatchNotRegexp,
226			value: `bar`,
227			want:  `{"name":"foo","value":"bar","isRegex":true,"isEqual":false}`,
228		},
229	}
230
231	cmp := func(m1, m2 Matcher) bool {
232		return m1.Name == m2.Name && m1.Value == m2.Value && m1.Type == m2.Type
233	}
234
235	for _, test := range tests {
236		m, err := NewMatcher(test.op, test.name, test.value)
237		if err != nil {
238			t.Fatal(err)
239		}
240
241		b, err := json.Marshal(m)
242		if err != nil {
243			t.Fatal(err)
244		}
245		if got := string(b); got != test.want {
246			t.Errorf("Unexpected JSON representation of matcher:\nwant:\t%v\ngot:\t%v", test.want, got)
247		}
248
249		var m2 Matcher
250		if err := json.Unmarshal(b, &m2); err != nil {
251			t.Fatal(err)
252		}
253		if !cmp(*m, m2) {
254			t.Errorf("Doing Marshal and Unmarshal seems to be losing data; before %#v, after %#v", m, m2)
255		}
256	}
257}
258
259func TestMatcherJSONUnmarshal(t *testing.T) {
260	tests := []struct {
261		name  string
262		op    MatchType
263		value string
264		want  string
265	}{
266		{
267			name:  "foo",
268			op:    MatchEqual,
269			value: "bar",
270			want:  `{"name":"foo","value":"bar","isRegex":false}`,
271		},
272		{
273			name:  `foo`,
274			op:    MatchEqual,
275			value: `bar`,
276			want:  `{"name":"foo","value":"bar","isRegex":false,"isEqual":true}`,
277		},
278		{
279			name:  `foo`,
280			op:    MatchNotEqual,
281			value: `bar`,
282			want:  `{"name":"foo","value":"bar","isRegex":false,"isEqual":false}`,
283		},
284		{
285			name:  `foo`,
286			op:    MatchRegexp,
287			value: `bar`,
288			want:  `{"name":"foo","value":"bar","isRegex":true,"isEqual":true}`,
289		},
290		{
291			name:  `foo`,
292			op:    MatchNotRegexp,
293			value: `bar`,
294			want:  `{"name":"foo","value":"bar","isRegex":true,"isEqual":false}`,
295		},
296	}
297
298	cmp := func(m1, m2 Matcher) bool {
299		return m1.Name == m2.Name && m1.Value == m2.Value && m1.Type == m2.Type
300	}
301
302	for _, test := range tests {
303		var m Matcher
304		if err := json.Unmarshal([]byte(test.want), &m); err != nil {
305			t.Fatal(err)
306		}
307
308		m2, err := NewMatcher(test.op, test.name, test.value)
309		if err != nil {
310			t.Fatal(err)
311		}
312
313		if !cmp(m, *m2) {
314			t.Errorf("Unmarshaling seems to be producing unexpected matchers; got %#v, expected %#v", m, m2)
315		}
316	}
317}
318