1 package opts
2
3 import (
4 "fmt"
5 "strings"
6 "testing"
7
8 "gotest.tools/v3/assert"
9 is "gotest.tools/v3/assert/cmp"
10 )
11
12 func TestValidateIPAddress(t *testing.T) {
13 tests := []struct {
14 doc string
15 input string
16 expectedOut string
17 expectedErr string
18 }{
19 {
20 doc: "IPv4 loopback",
21 input: `127.0.0.1`,
22 expectedOut: `127.0.0.1`,
23 },
24 {
25 doc: "IPv4 loopback with whitespace",
26 input: ` 127.0.0.1 `,
27 expectedOut: `127.0.0.1`,
28 },
29 {
30 doc: "IPv6 loopback long form",
31 input: `0:0:0:0:0:0:0:1`,
32 expectedOut: `::1`,
33 },
34 {
35 doc: "IPv6 loopback",
36 input: `::1`,
37 expectedOut: `::1`,
38 },
39 {
40 doc: "IPv6 loopback with whitespace",
41 input: ` ::1 `,
42 expectedOut: `::1`,
43 },
44 {
45 doc: "IPv6 lowercase",
46 input: `2001:db8::68`,
47 expectedOut: `2001:db8::68`,
48 },
49 {
50 doc: "IPv6 uppercase",
51 input: `2001:DB8::68`,
52 expectedOut: `2001:db8::68`,
53 },
54 {
55 doc: "IPv6 with brackets",
56 input: `[::1]`,
57 expectedErr: `IP address is not correctly formatted: [::1]`,
58 },
59 {
60 doc: "IPv4 partial",
61 input: `127`,
62 expectedErr: `IP address is not correctly formatted: 127`,
63 },
64 {
65 doc: "random invalid string",
66 input: `random invalid string`,
67 expectedErr: `IP address is not correctly formatted: random invalid string`,
68 },
69 }
70
71 for _, tc := range tests {
72 tc := tc
73 t.Run(tc.input, func(t *testing.T) {
74 actualOut, actualErr := ValidateIPAddress(tc.input)
75 assert.Check(t, is.Equal(tc.expectedOut, actualOut))
76 if tc.expectedErr == "" {
77 assert.Check(t, actualErr)
78 } else {
79 assert.Check(t, is.Error(actualErr, tc.expectedErr))
80 }
81 })
82 }
83 }
84
85 func TestMapOpts(t *testing.T) {
86 tmpMap := make(map[string]string)
87 o := NewMapOpts(tmpMap, sampleValidator)
88 err := o.Set("valid-option=1")
89 if err != nil {
90 t.Error(err)
91 }
92 if o.String() != "map[valid-option:1]" {
93 t.Errorf("%s != [map[valid-option:1]", o.String())
94 }
95
96 err = o.Set("valid-option2=2")
97 if err != nil {
98 t.Error(err)
99 }
100 if len(tmpMap) != 2 {
101 t.Errorf("map length %d != 2", len(tmpMap))
102 }
103
104 if tmpMap["valid-option"] != "1" {
105 t.Errorf("valid-option = %s != 1", tmpMap["valid-option"])
106 }
107 if tmpMap["valid-option2"] != "2" {
108 t.Errorf("valid-option2 = %s != 2", tmpMap["valid-option2"])
109 }
110
111 if o.Set("dummy-val=3") == nil {
112 t.Error("validator is not being called")
113 }
114 }
115
116 func TestListOptsWithoutValidator(t *testing.T) {
117 o := NewListOpts(nil)
118 err := o.Set("foo")
119 if err != nil {
120 t.Error(err)
121 }
122 if o.String() != "[foo]" {
123 t.Errorf("%s != [foo]", o.String())
124 }
125 err = o.Set("bar")
126 if err != nil {
127 t.Error(err)
128 }
129 if o.Len() != 2 {
130 t.Errorf("%d != 2", o.Len())
131 }
132 err = o.Set("bar")
133 if err != nil {
134 t.Error(err)
135 }
136 if o.Len() != 3 {
137 t.Errorf("%d != 3", o.Len())
138 }
139 if !o.Get("bar") {
140 t.Error("o.Get(\"bar\") == false")
141 }
142 if o.Get("baz") {
143 t.Error("o.Get(\"baz\") == true")
144 }
145 o.Delete("foo")
146 if o.String() != "[bar bar]" {
147 t.Errorf("%s != [bar bar]", o.String())
148 }
149 listOpts := o.GetAll()
150 if len(listOpts) != 2 || listOpts[0] != "bar" || listOpts[1] != "bar" {
151 t.Errorf("Expected [[bar bar]], got [%v]", listOpts)
152 }
153 mapListOpts := o.GetMap()
154 if len(mapListOpts) != 1 {
155 t.Errorf("Expected [map[bar:{}]], got [%v]", mapListOpts)
156 }
157 }
158
159 func TestListOptsWithValidator(t *testing.T) {
160 o := NewListOpts(sampleValidator)
161 err := o.Set("foo")
162 if err == nil {
163 t.Error(err)
164 }
165 if o.String() != "" {
166 t.Errorf(`%s != ""`, o.String())
167 }
168 err = o.Set("foo=bar")
169 if err == nil {
170 t.Error(err)
171 }
172 if o.String() != "" {
173 t.Errorf(`%s != ""`, o.String())
174 }
175 err = o.Set("valid-option2=2")
176 if err != nil {
177 t.Error(err)
178 }
179 if o.Len() != 1 {
180 t.Errorf("%d != 1", o.Len())
181 }
182 if !o.Get("valid-option2=2") {
183 t.Error(`o.Get("valid-option2=2") == false`)
184 }
185 if o.Get("baz") {
186 t.Error(`o.Get("baz") == true`)
187 }
188 o.Delete("valid-option2=2")
189 if o.String() != "" {
190 t.Errorf(`%s != ""`, o.String())
191 }
192 }
193
194
195 func TestValidateDNSSearch(t *testing.T) {
196 valid := []string{
197 `.`,
198 `a`,
199 `a.`,
200 `1.foo`,
201 `17.foo`,
202 `foo.bar`,
203 `foo.bar.baz`,
204 `foo.bar.`,
205 `foo.bar.baz`,
206 `foo1.bar2`,
207 `foo1.bar2.baz`,
208 `1foo.2bar.`,
209 `1foo.2bar.baz`,
210 `foo-1.bar-2`,
211 `foo-1.bar-2.baz`,
212 `foo-1.bar-2.`,
213 `foo-1.bar-2.baz`,
214 `1-foo.2-bar`,
215 `1-foo.2-bar.baz`,
216 `1-foo.2-bar.`,
217 `1-foo.2-bar.baz`,
218 }
219
220 invalid := []string{
221 ``,
222 ` `,
223 ` `,
224 `17`,
225 `17.`,
226 `.17`,
227 `17-.`,
228 `17-.foo`,
229 `.foo`,
230 `foo-.bar`,
231 `-foo.bar`,
232 `foo.bar-`,
233 `foo.bar-.baz`,
234 `foo.-bar`,
235 `foo.-bar.baz`,
236 `foo.bar.baz.this.should.fail.on.long.name.because.it.is.longer.thanisshouldbethis.should.fail.on.long.name.because.it.is.longer.thanisshouldbethis.should.fail.on.long.name.because.it.is.longer.thanisshouldbethis.should.fail.on.long.name.because.it.is.longer.thanisshouldbe`,
237 }
238
239 for _, domain := range valid {
240 if ret, err := ValidateDNSSearch(domain); err != nil || ret == "" {
241 t.Fatalf("ValidateDNSSearch(`"+domain+"`) got %s %s", ret, err)
242 }
243 }
244
245 for _, domain := range invalid {
246 if ret, err := ValidateDNSSearch(domain); err == nil || ret != "" {
247 t.Fatalf("ValidateDNSSearch(`"+domain+"`) got %s %s", ret, err)
248 }
249 }
250 }
251
252 func TestValidateLabel(t *testing.T) {
253 tests := []struct {
254 name string
255 value string
256 expectedErr string
257 }{
258 {
259 name: "empty",
260 expectedErr: `invalid label '': empty name`,
261 },
262 {
263 name: "whitespace only ",
264 value: " ",
265 expectedErr: `invalid label ' ': empty name`,
266 },
267 {
268 name: "whitespace around equal-sign",
269 value: " = ",
270 expectedErr: `invalid label ' = ': empty name`,
271 },
272 {
273 name: "leading whitespace",
274 value: " label=value",
275 },
276 {
277 name: "whitespaces in key without value",
278 value: "this is a label without value",
279 expectedErr: `label 'this is a label without value' contains whitespaces`,
280 },
281 {
282 name: "whitespaces in key",
283 value: "this is a label=value",
284 expectedErr: `label 'this is a label' contains whitespaces`,
285 },
286 {
287 name: "whitespaces in value",
288 value: "label=a value that has whitespace",
289 },
290 {
291 name: "trailing whitespace in value",
292 value: "label=value ",
293 },
294 {
295 name: "leading whitespace in value",
296 value: "label= value",
297 },
298 {
299 name: "no value",
300 value: "label",
301 },
302 {
303 name: "no key",
304 value: "=label",
305 expectedErr: `invalid label '=label': empty name`,
306 },
307 {
308 name: "empty value",
309 value: "label=",
310 },
311 {
312 name: "key value",
313 value: "key1=value1",
314 },
315 {
316 name: "double equal-signs",
317 value: "key1=value1=value2",
318 },
319 {
320 name: "multiple equal-signs",
321 value: "key1=value1=value2=value",
322 },
323 {
324 name: "double quotes in key and value",
325 value: `key"with"quotes={"hello"}`,
326 },
327 {
328 name: "double quotes around key and value",
329 value: `"quoted-label"="quoted value"`,
330 },
331 {
332 name: "single quotes in key and value",
333 value: `key'with'quotes=hello'with'quotes`,
334 },
335 {
336 name: "single quotes around key and value",
337 value: `'quoted-label'='quoted value''`,
338 },
339 }
340
341 for _, tc := range tests {
342 tc := tc
343 t.Run(tc.name, func(t *testing.T) {
344 val, err := ValidateLabel(tc.value)
345 if tc.expectedErr != "" {
346 assert.Error(t, err, tc.expectedErr)
347 return
348 }
349 assert.NilError(t, err)
350 assert.Equal(t, val, tc.value)
351 })
352 }
353 }
354
355 func sampleValidator(val string) (string, error) {
356 allowedKeys := map[string]string{"valid-option": "1", "valid-option2": "2"}
357 k, _, _ := strings.Cut(val, "=")
358 if allowedKeys[k] != "" {
359 return val, nil
360 }
361 return "", fmt.Errorf("invalid key %s", k)
362 }
363
364 func TestNamedListOpts(t *testing.T) {
365 var v []string
366 o := NewNamedListOptsRef("foo-name", &v, nil)
367
368 o.Set("foo")
369 if o.String() != "[foo]" {
370 t.Errorf("%s != [foo]", o.String())
371 }
372 if o.Name() != "foo-name" {
373 t.Errorf("%s != foo-name", o.Name())
374 }
375 if len(v) != 1 {
376 t.Errorf("expected foo to be in the values, got %v", v)
377 }
378 }
379
380 func TestNamedMapOpts(t *testing.T) {
381 tmpMap := make(map[string]string)
382 o := NewNamedMapOpts("max-name", tmpMap, nil)
383
384 o.Set("max-size=1")
385 if o.String() != "map[max-size:1]" {
386 t.Errorf("%s != [map[max-size:1]", o.String())
387 }
388 if o.Name() != "max-name" {
389 t.Errorf("%s != max-name", o.Name())
390 }
391 if _, exist := tmpMap["max-size"]; !exist {
392 t.Errorf("expected map-size to be in the values, got %v", tmpMap)
393 }
394 }
395
396 func TestValidateMACAddress(t *testing.T) {
397 if _, err := ValidateMACAddress(`92:d0:c6:0a:29:33`); err != nil {
398 t.Fatalf("ValidateMACAddress(`92:d0:c6:0a:29:33`) got %s", err)
399 }
400
401 if _, err := ValidateMACAddress(`92:d0:c6:0a:33`); err == nil {
402 t.Fatalf("ValidateMACAddress(`92:d0:c6:0a:33`) succeeded; expected failure on invalid MAC")
403 }
404
405 if _, err := ValidateMACAddress(`random invalid string`); err == nil {
406 t.Fatalf("ValidateMACAddress(`random invalid string`) succeeded; expected failure on invalid MAC")
407 }
408 }
409
410 func TestValidateLink(t *testing.T) {
411 valid := []string{
412 "name",
413 "dcdfbe62ecd0:alias",
414 "7a67485460b7642516a4ad82ecefe7f57d0c4916f530561b71a50a3f9c4e33da",
415 "angry_torvalds:linus",
416 }
417 invalid := map[string]string{
418 "": "empty string specified for links",
419 "too:much:of:it": "bad format for links: too:much:of:it",
420 }
421
422 for _, link := range valid {
423 if _, err := ValidateLink(link); err != nil {
424 t.Fatalf("ValidateLink(`%q`) should succeed: error %q", link, err)
425 }
426 }
427
428 for link, expectedError := range invalid {
429 if _, err := ValidateLink(link); err == nil {
430 t.Fatalf("ValidateLink(`%q`) should have failed validation", link)
431 } else if !strings.Contains(err.Error(), expectedError) {
432 t.Fatalf("ValidateLink(`%q`) error should contain %q", link, expectedError)
433 }
434 }
435 }
436
437 func TestParseLink(t *testing.T) {
438 name, alias, err := ParseLink("name:alias")
439 if err != nil {
440 t.Fatalf("Expected not to error out on a valid name:alias format but got: %v", err)
441 }
442 if name != "name" {
443 t.Fatalf("Link name should have been name, got %s instead", name)
444 }
445 if alias != "alias" {
446 t.Fatalf("Link alias should have been alias, got %s instead", alias)
447 }
448
449 name, alias, err = ParseLink("name")
450 if err != nil {
451 t.Fatalf("Expected not to error out on a valid name only format but got: %v", err)
452 }
453 if name != "name" {
454 t.Fatalf("Link name should have been name, got %s instead", name)
455 }
456 if alias != "name" {
457 t.Fatalf("Link alias should have been name, got %s instead", alias)
458 }
459
460 if _, _, err := ParseLink(""); err == nil || !strings.Contains(err.Error(), "empty string specified for links") {
461 t.Fatalf("Expected error 'empty string specified for links' but got: %v", err)
462 }
463
464 if _, _, err := ParseLink("link:alias:wrong"); err == nil || !strings.Contains(err.Error(), "bad format for links: link:alias:wrong") {
465 t.Fatalf("Expected error 'bad format for links: link:alias:wrong' but got: %v", err)
466 }
467 }
468
View as plain text