1 package reference
2
3 import (
4 "regexp"
5 "strings"
6 "testing"
7 )
8
9 type regexpMatch struct {
10 input string
11 match bool
12 subs []string
13 }
14
15 func checkRegexp(t *testing.T, r *regexp.Regexp, m regexpMatch) {
16 t.Helper()
17 matches := r.FindStringSubmatch(m.input)
18 if m.match && matches != nil {
19 if len(matches) != (r.NumSubexp()+1) || matches[0] != m.input {
20 t.Fatalf("Bad match result %#v for %q", matches, m.input)
21 }
22 if len(matches) < (len(m.subs) + 1) {
23 t.Errorf("Expected %d sub matches, only have %d for %q", len(m.subs), len(matches)-1, m.input)
24 }
25 for i := range m.subs {
26 if m.subs[i] != matches[i+1] {
27 t.Errorf("Unexpected submatch %d: %q, expected %q for %q", i+1, matches[i+1], m.subs[i], m.input)
28 }
29 }
30 } else if m.match {
31 t.Errorf("Expected match for %q", m.input)
32 } else if matches != nil {
33 t.Errorf("Unexpected match for %q", m.input)
34 }
35 }
36
37 func TestDomainRegexp(t *testing.T) {
38 t.Parallel()
39 tests := []struct {
40 input string
41 match bool
42 }{
43 {
44 input: "test.com",
45 match: true,
46 },
47 {
48 input: "test.com:10304",
49 match: true,
50 },
51 {
52 input: "test.com:http",
53 match: false,
54 },
55 {
56 input: "localhost",
57 match: true,
58 },
59 {
60 input: "localhost:8080",
61 match: true,
62 },
63 {
64 input: "a",
65 match: true,
66 },
67 {
68 input: "a.b",
69 match: true,
70 },
71 {
72 input: "ab.cd.com",
73 match: true,
74 },
75 {
76 input: "a-b.com",
77 match: true,
78 },
79 {
80 input: "-ab.com",
81 match: false,
82 },
83 {
84 input: "ab-.com",
85 match: false,
86 },
87 {
88 input: "ab.c-om",
89 match: true,
90 },
91 {
92 input: "ab.-com",
93 match: false,
94 },
95 {
96 input: "ab.com-",
97 match: false,
98 },
99 {
100 input: "0101.com",
101 match: true,
102 },
103 {
104 input: "001a.com",
105 match: true,
106 },
107 {
108 input: "b.gbc.io:443",
109 match: true,
110 },
111 {
112 input: "b.gbc.io",
113 match: true,
114 },
115 {
116 input: "xn--n3h.com",
117 match: true,
118 },
119 {
120 input: "Asdf.com",
121 match: true,
122 },
123 {
124 input: "192.168.1.1:75050",
125 match: true,
126 },
127 {
128 input: "192.168.1.1:750050",
129 match: true,
130 },
131 {
132 input: "[fd00:1:2::3]:75050",
133 match: true,
134 },
135 {
136 input: "[fd00:1:2::3]75050",
137 match: false,
138 },
139 {
140 input: "[fd00:1:2::3]::75050",
141 match: false,
142 },
143 {
144 input: "[fd00:1:2::3%eth0]:75050",
145 match: false,
146 },
147 {
148 input: "[fd00123123123]:75050",
149 match: true,
150 },
151 {
152 input: "[2001:0db8:85a3:0000:0000:8a2e:0370:7334]:75050",
153 match: true,
154 },
155 {
156 input: "[2001:0db8:85a3:0000:0000:8a2e:0370:7334]:750505",
157 match: true,
158 },
159 {
160 input: "fd00:1:2::3:75050",
161 match: false,
162 },
163 }
164 r := regexp.MustCompile(`^` + DomainRegexp.String() + `$`)
165 for _, tc := range tests {
166 tc := tc
167 t.Run(tc.input, func(t *testing.T) {
168 t.Parallel()
169 match := r.MatchString(tc.input)
170 if match != tc.match {
171 t.Errorf("Expected match=%t, got %t", tc.match, match)
172 }
173 })
174 }
175 }
176
177 func TestFullNameRegexp(t *testing.T) {
178 t.Parallel()
179 if anchoredNameRegexp.NumSubexp() != 2 {
180 t.Fatalf("anchored name regexp should have two submatches: %v, %v != 2",
181 anchoredNameRegexp, anchoredNameRegexp.NumSubexp())
182 }
183
184 tests := []regexpMatch{
185 {
186 input: "",
187 match: false,
188 },
189 {
190 input: "short",
191 match: true,
192 subs: []string{"", "short"},
193 },
194 {
195 input: "simple/name",
196 match: true,
197 subs: []string{"simple", "name"},
198 },
199 {
200 input: "library/ubuntu",
201 match: true,
202 subs: []string{"library", "ubuntu"},
203 },
204 {
205 input: "docker/stevvooe/app",
206 match: true,
207 subs: []string{"docker", "stevvooe/app"},
208 },
209 {
210 input: "aa/aa/aa/aa/aa/aa/aa/aa/aa/bb/bb/bb/bb/bb/bb",
211 match: true,
212 subs: []string{"aa", "aa/aa/aa/aa/aa/aa/aa/aa/bb/bb/bb/bb/bb/bb"},
213 },
214 {
215 input: "aa/aa/bb/bb/bb",
216 match: true,
217 subs: []string{"aa", "aa/bb/bb/bb"},
218 },
219 {
220 input: "a/a/a/a",
221 match: true,
222 subs: []string{"a", "a/a/a"},
223 },
224 {
225 input: "a/a/a/a/",
226 match: false,
227 },
228 {
229 input: "a//a/a",
230 match: false,
231 },
232 {
233 input: "a",
234 match: true,
235 subs: []string{"", "a"},
236 },
237 {
238 input: "a/aa",
239 match: true,
240 subs: []string{"a", "aa"},
241 },
242 {
243 input: "a/aa/a",
244 match: true,
245 subs: []string{"a", "aa/a"},
246 },
247 {
248 input: "foo.com",
249 match: true,
250 subs: []string{"", "foo.com"},
251 },
252 {
253 input: "foo.com/",
254 match: false,
255 },
256 {
257 input: "foo.com:8080/bar",
258 match: true,
259 subs: []string{"foo.com:8080", "bar"},
260 },
261 {
262 input: "foo.com:http/bar",
263 match: false,
264 },
265 {
266 input: "foo.com/bar",
267 match: true,
268 subs: []string{"foo.com", "bar"},
269 },
270 {
271 input: "foo.com/bar/baz",
272 match: true,
273 subs: []string{"foo.com", "bar/baz"},
274 },
275 {
276 input: "localhost:8080/bar",
277 match: true,
278 subs: []string{"localhost:8080", "bar"},
279 },
280 {
281 input: "sub-dom1.foo.com/bar/baz/quux",
282 match: true,
283 subs: []string{"sub-dom1.foo.com", "bar/baz/quux"},
284 },
285 {
286 input: "blog.foo.com/bar/baz",
287 match: true,
288 subs: []string{"blog.foo.com", "bar/baz"},
289 },
290 {
291 input: "a^a",
292 match: false,
293 },
294 {
295 input: "aa/asdf$$^/aa",
296 match: false,
297 },
298 {
299 input: "asdf$$^/aa",
300 match: false,
301 },
302 {
303 input: "aa-a/a",
304 match: true,
305 subs: []string{"aa-a", "a"},
306 },
307 {
308 input: strings.Repeat("a/", 128) + "a",
309 match: true,
310 subs: []string{"a", strings.Repeat("a/", 127) + "a"},
311 },
312 {
313 input: "a-/a/a/a",
314 match: false,
315 },
316 {
317 input: "foo.com/a-/a/a",
318 match: false,
319 },
320 {
321 input: "-foo/bar",
322 match: false,
323 },
324 {
325 input: "foo/bar-",
326 match: false,
327 },
328 {
329 input: "foo-/bar",
330 match: false,
331 },
332 {
333 input: "foo/-bar",
334 match: false,
335 },
336 {
337 input: "_foo/bar",
338 match: false,
339 },
340 {
341 input: "foo_bar",
342 match: true,
343 subs: []string{"", "foo_bar"},
344 },
345 {
346 input: "foo_bar.com",
347 match: true,
348 subs: []string{"", "foo_bar.com"},
349 },
350 {
351 input: "foo_bar.com:8080",
352 match: false,
353 },
354 {
355 input: "foo_bar.com:8080/app",
356 match: false,
357 },
358 {
359 input: "foo.com/foo_bar",
360 match: true,
361 subs: []string{"foo.com", "foo_bar"},
362 },
363 {
364 input: "____/____",
365 match: false,
366 },
367 {
368 input: "_docker/_docker",
369 match: false,
370 },
371 {
372 input: "docker_/docker_",
373 match: false,
374 },
375 {
376 input: "b.gcr.io/test.example.com/my-app",
377 match: true,
378 subs: []string{"b.gcr.io", "test.example.com/my-app"},
379 },
380 {
381 input: "xn--n3h.com/myimage",
382 match: true,
383 subs: []string{"xn--n3h.com", "myimage"},
384 },
385 {
386 input: "xn--7o8h.com/myimage",
387 match: true,
388 subs: []string{"xn--7o8h.com", "myimage"},
389 },
390 {
391 input: "example.com/xn--7o8h.com/myimage",
392 match: true,
393 subs: []string{"example.com", "xn--7o8h.com/myimage"},
394 },
395 {
396 input: "example.com/some_separator__underscore/myimage",
397 match: true,
398 subs: []string{"example.com", "some_separator__underscore/myimage"},
399 },
400 {
401 input: "example.com/__underscore/myimage",
402 match: false,
403 },
404 {
405 input: "example.com/..dots/myimage",
406 match: false,
407 },
408 {
409 input: "example.com/.dots/myimage",
410 match: false,
411 },
412 {
413 input: "example.com/nodouble..dots/myimage",
414 match: false,
415 },
416 {
417 input: "example.com/nodouble..dots/myimage",
418 match: false,
419 },
420 {
421 input: "docker./docker",
422 match: false,
423 },
424 {
425 input: ".docker/docker",
426 match: false,
427 },
428 {
429 input: "docker-/docker",
430 match: false,
431 },
432 {
433 input: "-docker/docker",
434 match: false,
435 },
436 {
437 input: "do..cker/docker",
438 match: false,
439 },
440 {
441 input: "do__cker:8080/docker",
442 match: false,
443 },
444 {
445 input: "do__cker/docker",
446 match: true,
447 subs: []string{"", "do__cker/docker"},
448 },
449 {
450 input: "b.gcr.io/test.example.com/my-app",
451 match: true,
452 subs: []string{"b.gcr.io", "test.example.com/my-app"},
453 },
454 {
455 input: "registry.io/foo/project--id.module--name.ver---sion--name",
456 match: true,
457 subs: []string{"registry.io", "foo/project--id.module--name.ver---sion--name"},
458 },
459 {
460 input: "Asdf.com/foo/bar",
461 match: true,
462 },
463 {
464 input: "Foo/FarB",
465 match: false,
466 },
467 }
468 for _, tc := range tests {
469 tc := tc
470 t.Run(tc.input, func(t *testing.T) {
471 t.Parallel()
472 checkRegexp(t, anchoredNameRegexp, tc)
473 })
474 }
475 }
476
477 func TestReferenceRegexp(t *testing.T) {
478 t.Parallel()
479 if ReferenceRegexp.NumSubexp() != 3 {
480 t.Fatalf("anchored name regexp should have three submatches: %v, %v != 3",
481 ReferenceRegexp, ReferenceRegexp.NumSubexp())
482 }
483
484 tests := []regexpMatch{
485 {
486 input: "registry.com:8080/myapp:tag",
487 match: true,
488 subs: []string{"registry.com:8080/myapp", "tag", ""},
489 },
490 {
491 input: "registry.com:8080/myapp@sha256:be178c0543eb17f5f3043021c9e5fcf30285e557a4fc309cce97ff9ca6182912",
492 match: true,
493 subs: []string{"registry.com:8080/myapp", "", "sha256:be178c0543eb17f5f3043021c9e5fcf30285e557a4fc309cce97ff9ca6182912"},
494 },
495 {
496 input: "registry.com:8080/myapp:tag2@sha256:be178c0543eb17f5f3043021c9e5fcf30285e557a4fc309cce97ff9ca6182912",
497 match: true,
498 subs: []string{"registry.com:8080/myapp", "tag2", "sha256:be178c0543eb17f5f3043021c9e5fcf30285e557a4fc309cce97ff9ca6182912"},
499 },
500 {
501 input: "registry.com:8080/myapp@sha256:badbadbadbad",
502 match: false,
503 },
504 {
505 input: "registry.com:8080/myapp:invalid~tag",
506 match: false,
507 },
508 {
509 input: "bad_hostname.com:8080/myapp:tag",
510 match: false,
511 },
512 {
513 input:
514 "localhost:8080@sha256:be178c0543eb17f5f3043021c9e5fcf30285e557a4fc309cce97ff9ca6182912",
515 match: true,
516 subs: []string{"localhost", "8080", "sha256:be178c0543eb17f5f3043021c9e5fcf30285e557a4fc309cce97ff9ca6182912"},
517 },
518 {
519 input: "localhost:8080/name@sha256:be178c0543eb17f5f3043021c9e5fcf30285e557a4fc309cce97ff9ca6182912",
520 match: true,
521 subs: []string{"localhost:8080/name", "", "sha256:be178c0543eb17f5f3043021c9e5fcf30285e557a4fc309cce97ff9ca6182912"},
522 },
523 {
524 input: "localhost:http/name@sha256:be178c0543eb17f5f3043021c9e5fcf30285e557a4fc309cce97ff9ca6182912",
525 match: false,
526 },
527 {
528
529 input: "localhost@sha256:be178c0543eb17f5f3043021c9e5fcf30285e557a4fc309cce97ff9ca6182912",
530 match: true,
531 subs: []string{"localhost", "", "sha256:be178c0543eb17f5f3043021c9e5fcf30285e557a4fc309cce97ff9ca6182912"},
532 },
533 {
534 input: "registry.com:8080/myapp@bad",
535 match: false,
536 },
537 {
538 input: "registry.com:8080/myapp@2bad",
539 match: false,
540 },
541 }
542
543 for _, tc := range tests {
544 tc := tc
545 t.Run(tc.input, func(t *testing.T) {
546 t.Parallel()
547 checkRegexp(t, ReferenceRegexp, tc)
548 })
549 }
550 }
551
552 func TestIdentifierRegexp(t *testing.T) {
553 t.Parallel()
554 tests := []struct {
555 input string
556 match bool
557 }{
558 {
559 input: "da304e823d8ca2b9d863a3c897baeb852ba21ea9a9f1414736394ae7fcaf9821",
560 match: true,
561 },
562 {
563 input: "7EC43B381E5AEFE6E04EFB0B3F0693FF2A4A50652D64AEC573905F2DB5889A1C",
564 match: false,
565 },
566 {
567 input: "da304e823d8ca2b9d863a3c897baeb852ba21ea9a9f1414736394ae7fcaf",
568 match: false,
569 },
570 {
571 input: "sha256:da304e823d8ca2b9d863a3c897baeb852ba21ea9a9f1414736394ae7fcaf9821",
572 match: false,
573 },
574 {
575 input: "da304e823d8ca2b9d863a3c897baeb852ba21ea9a9f1414736394ae7fcaf98218482",
576 match: false,
577 },
578 }
579 for _, tc := range tests {
580 tc := tc
581 t.Run(tc.input, func(t *testing.T) {
582 t.Parallel()
583 match := anchoredIdentifierRegexp.MatchString(tc.input)
584 if match != tc.match {
585 t.Errorf("Expected match=%t, got %t", tc.match, match)
586 }
587 })
588 }
589 }
590
View as plain text