1// Copyright 2018 Prometheus Team 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 "reflect" 18 "testing" 19) 20 21func TestMatchers(t *testing.T) { 22 for _, tc := range []struct { 23 input string 24 want []*Matcher 25 err string 26 }{ 27 { 28 input: `{foo="bar"}`, 29 want: func() []*Matcher { 30 ms := []*Matcher{} 31 m, _ := NewMatcher(MatchEqual, "foo", "bar") 32 return append(ms, m) 33 }(), 34 }, 35 { 36 input: `{foo=~"bar.*"}`, 37 want: func() []*Matcher { 38 ms := []*Matcher{} 39 m, _ := NewMatcher(MatchRegexp, "foo", "bar.*") 40 return append(ms, m) 41 }(), 42 }, 43 { 44 input: `{foo!="bar"}`, 45 want: func() []*Matcher { 46 ms := []*Matcher{} 47 m, _ := NewMatcher(MatchNotEqual, "foo", "bar") 48 return append(ms, m) 49 }(), 50 }, 51 { 52 input: `{foo!~"bar.*"}`, 53 want: func() []*Matcher { 54 ms := []*Matcher{} 55 m, _ := NewMatcher(MatchNotRegexp, "foo", "bar.*") 56 return append(ms, m) 57 }(), 58 }, 59 { 60 input: `{foo="bar", baz!="quux"}`, 61 want: func() []*Matcher { 62 ms := []*Matcher{} 63 m, _ := NewMatcher(MatchEqual, "foo", "bar") 64 m2, _ := NewMatcher(MatchNotEqual, "baz", "quux") 65 return append(ms, m, m2) 66 }(), 67 }, 68 { 69 input: `{foo="bar", baz!~"quux.*"}`, 70 want: func() []*Matcher { 71 ms := []*Matcher{} 72 m, _ := NewMatcher(MatchEqual, "foo", "bar") 73 m2, _ := NewMatcher(MatchNotRegexp, "baz", "quux.*") 74 return append(ms, m, m2) 75 }(), 76 }, 77 { 78 input: `{foo="bar",baz!~".*quux", derp="wat"}`, 79 want: func() []*Matcher { 80 ms := []*Matcher{} 81 m, _ := NewMatcher(MatchEqual, "foo", "bar") 82 m2, _ := NewMatcher(MatchNotRegexp, "baz", ".*quux") 83 m3, _ := NewMatcher(MatchEqual, "derp", "wat") 84 return append(ms, m, m2, m3) 85 }(), 86 }, 87 { 88 input: `{foo="bar", baz!="quux", derp="wat"}`, 89 want: func() []*Matcher { 90 ms := []*Matcher{} 91 m, _ := NewMatcher(MatchEqual, "foo", "bar") 92 m2, _ := NewMatcher(MatchNotEqual, "baz", "quux") 93 m3, _ := NewMatcher(MatchEqual, "derp", "wat") 94 return append(ms, m, m2, m3) 95 }(), 96 }, 97 { 98 input: `{foo="bar", baz!~".*quux.*", derp="wat"}`, 99 want: func() []*Matcher { 100 ms := []*Matcher{} 101 m, _ := NewMatcher(MatchEqual, "foo", "bar") 102 m2, _ := NewMatcher(MatchNotRegexp, "baz", ".*quux.*") 103 m3, _ := NewMatcher(MatchEqual, "derp", "wat") 104 return append(ms, m, m2, m3) 105 }(), 106 }, 107 { 108 input: `{foo="bar", instance=~"some-api.*"}`, 109 want: func() []*Matcher { 110 ms := []*Matcher{} 111 m, _ := NewMatcher(MatchEqual, "foo", "bar") 112 m2, _ := NewMatcher(MatchRegexp, "instance", "some-api.*") 113 return append(ms, m, m2) 114 }(), 115 }, 116 { 117 input: `{foo=""}`, 118 want: func() []*Matcher { 119 ms := []*Matcher{} 120 m, _ := NewMatcher(MatchEqual, "foo", "") 121 return append(ms, m) 122 }(), 123 }, 124 { 125 input: `{foo="bar,quux", job="job1"}`, 126 want: func() []*Matcher { 127 ms := []*Matcher{} 128 m, _ := NewMatcher(MatchEqual, "foo", "bar,quux") 129 m2, _ := NewMatcher(MatchEqual, "job", "job1") 130 return append(ms, m, m2) 131 }(), 132 }, 133 { 134 input: `{foo = "bar", dings != "bums", }`, 135 want: func() []*Matcher { 136 ms := []*Matcher{} 137 m, _ := NewMatcher(MatchEqual, "foo", "bar") 138 m2, _ := NewMatcher(MatchNotEqual, "dings", "bums") 139 return append(ms, m, m2) 140 }(), 141 }, 142 { 143 input: `foo=bar,dings!=bums`, 144 want: func() []*Matcher { 145 ms := []*Matcher{} 146 m, _ := NewMatcher(MatchEqual, "foo", "bar") 147 m2, _ := NewMatcher(MatchNotEqual, "dings", "bums") 148 return append(ms, m, m2) 149 }(), 150 }, 151 { 152 input: `{quote="She said: \"Hi, ladies! That's gender-neutral…\""}`, 153 want: func() []*Matcher { 154 ms := []*Matcher{} 155 m, _ := NewMatcher(MatchEqual, "quote", `She said: "Hi, ladies! That's gender-neutral…"`) 156 return append(ms, m) 157 }(), 158 }, 159 { 160 input: `statuscode=~"5.."`, 161 want: func() []*Matcher { 162 ms := []*Matcher{} 163 m, _ := NewMatcher(MatchRegexp, "statuscode", "5..") 164 return append(ms, m) 165 }(), 166 }, 167 { 168 input: `tricky=~~~`, 169 want: func() []*Matcher { 170 ms := []*Matcher{} 171 m, _ := NewMatcher(MatchRegexp, "tricky", "~~") 172 return append(ms, m) 173 }(), 174 }, 175 { 176 input: `trickier==\\=\=\"`, 177 want: func() []*Matcher { 178 ms := []*Matcher{} 179 m, _ := NewMatcher(MatchEqual, "trickier", `=\=\="`) 180 return append(ms, m) 181 }(), 182 }, 183 { 184 input: `contains_quote != "\"" , contains_comma !~ "foo,bar" , `, 185 want: func() []*Matcher { 186 ms := []*Matcher{} 187 m, _ := NewMatcher(MatchNotEqual, "contains_quote", `"`) 188 m2, _ := NewMatcher(MatchNotRegexp, "contains_comma", "foo,bar") 189 return append(ms, m, m2) 190 }(), 191 }, 192 { 193 input: `job="value`, 194 err: `matcher value contains unescaped double quote: "value`, 195 }, 196 { 197 input: `job=value"`, 198 err: `matcher value contains unescaped double quote: value"`, 199 }, 200 { 201 input: `trickier==\\=\=\""`, 202 err: `matcher value contains unescaped double quote: =\\=\=\""`, 203 }, 204 { 205 input: `contains_unescaped_quote = foo"bar`, 206 err: `matcher value contains unescaped double quote: foo"bar`, 207 }, 208 { 209 input: `{invalid-name = "valid label"}`, 210 err: `bad matcher format: invalid-name = "valid label"`, 211 }, 212 { 213 input: `{foo=~"invalid[regexp"}`, 214 err: "error parsing regexp: missing closing ]: `[regexp)$`", 215 }, 216 // Double escaped strings. 217 { 218 input: `"{foo=\"bar"}`, 219 err: `bad matcher format: "{foo=\"bar"`, 220 }, 221 { 222 input: `"foo=\"bar"`, 223 err: `bad matcher format: "foo=\"bar"`, 224 }, 225 { 226 input: `"foo=\"bar\""`, 227 err: `bad matcher format: "foo=\"bar\""`, 228 }, 229 { 230 input: `"foo=\"bar\"`, 231 err: `bad matcher format: "foo=\"bar\"`, 232 }, 233 { 234 input: `"{foo=\"bar\"}"`, 235 err: `bad matcher format: "{foo=\"bar\"}"`, 236 }, 237 { 238 input: `"foo="bar""`, 239 err: `bad matcher format: "foo="bar""`, 240 }, 241 } { 242 t.Run(tc.input, func(t *testing.T) { 243 got, err := ParseMatchers(tc.input) 244 if err != nil && tc.err == "" { 245 t.Fatalf("got error where none expected: %v", err) 246 } 247 if err == nil && tc.err != "" { 248 t.Fatalf("expected error but got none: %v", tc.err) 249 } 250 if err != nil && err.Error() != tc.err { 251 t.Fatalf("error not equal:\ngot %v\nwant %v", err, tc.err) 252 } 253 if !reflect.DeepEqual(got, tc.want) { 254 t.Fatalf("labels not equal:\ngot %v\nwant %v", got, tc.want) 255 } 256 }) 257 } 258 259} 260