1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package ociref
16
17 import (
18 _ "crypto/sha256"
19 _ "crypto/sha512"
20 "fmt"
21 "regexp"
22 "strings"
23 "testing"
24
25 "github.com/go-quicktest/qt"
26 )
27
28 var parseReferenceTests = []struct {
29 testName string
30
31 input string
32
33 wantErr string
34 wantRef Reference
35 }{
36 {
37 input: "test_com",
38 wantRef: Reference{
39 Repository: "test_com",
40 },
41 },
42 {
43 input: "test.com:tag",
44 wantRef: Reference{
45 Repository: "test.com",
46 Tag: "tag",
47 },
48 },
49 {
50 input: "test.com:5000",
51 wantRef: Reference{
52 Repository: "test.com",
53 Tag: "5000",
54 },
55 },
56 {
57 input: "test.com/repo:tag",
58 wantRef: Reference{
59 Host: "test.com",
60 Repository: "repo",
61 Tag: "tag",
62 },
63 },
64 {
65 input: "test:5000/repo",
66 wantRef: Reference{
67 Host: "test:5000",
68 Repository: "repo",
69 },
70 },
71 {
72 input: "test:5000/repo:tag",
73 wantRef: Reference{
74 Host: "test:5000",
75 Repository: "repo",
76 Tag: "tag",
77 },
78 },
79 {
80 input: "test:5000/repo@sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
81 wantRef: Reference{
82 Host: "test:5000",
83 Repository: "repo",
84 Digest: "sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
85 },
86 },
87 {
88 input: "test:5000/repo:tag@sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
89 wantRef: Reference{
90 Host: "test:5000",
91 Repository: "repo",
92 Tag: "tag",
93 Digest: "sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
94 },
95 },
96 {
97 input: "test:5000/repo",
98 wantRef: Reference{
99 Host: "test:5000",
100 Repository: "repo",
101 },
102 },
103 {
104 testName: "EmptyString",
105 input: "",
106 wantErr: `invalid reference syntax`,
107 },
108 {
109 input: ":justtag",
110 wantErr: `invalid reference syntax`,
111 },
112 {
113 input: "@sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
114 wantErr: `invalid reference syntax`,
115 },
116 {
117 input: "repo@sha256:ffffffffffffffffffffffffffffffffff",
118 wantErr: `invalid digest "sha256:ffffffffffffffffffffffffffffffffff": invalid checksum digest length`,
119 },
120 {
121 input: "validname@invalidDigest:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
122 wantErr: `invalid digest "invalidDigest:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff": invalid checksum digest format`,
123 },
124 {
125 input: "Uppercase:tag",
126 wantErr: `invalid reference syntax`,
127 },
128
129
130
131
132
133
134 {
135 input: "test:5000/Uppercase/lowercase:tag",
136 wantErr: `tag "5000/Uppercase/lowercase:tag" contains invalid invalid character '/'`,
137 },
138 {
139 input: "lowercase:Uppercase",
140 wantRef: Reference{
141 Repository: "lowercase",
142 Tag: "Uppercase",
143 },
144 },
145 {
146 testName: "RepoTooLong",
147 input: strings.Repeat("a/", 128) + "a:tag",
148 wantErr: `repository name too long`,
149 },
150 {
151 testName: "RepoAlmostTooLong",
152 input: strings.Repeat("a/", 127) + "a:tag-puts-this-over-max",
153 wantRef: Reference{
154
155 Repository: strings.Repeat("a/", 127) + "a",
156 Tag: "tag-puts-this-over-max",
157 },
158 },
159 {
160 input: "aa/asdf$$^/aa",
161 wantErr: `invalid reference syntax`,
162 },
163 {
164 input: "sub-dom1.foo.com/bar/baz/quux",
165 wantRef: Reference{
166 Host: "sub-dom1.foo.com",
167 Repository: "bar/baz/quux",
168 },
169 },
170 {
171 input: "sub-dom1.foo.com/bar/baz/quux:some-long-tag",
172 wantRef: Reference{
173 Host: "sub-dom1.foo.com",
174 Repository: "bar/baz/quux",
175 Tag: "some-long-tag",
176 },
177 },
178 {
179 input: "b.gcr.io/test.example.com/my-app:test.example.com",
180 wantRef: Reference{
181 Host: "b.gcr.io",
182 Repository: "test.example.com/my-app",
183 Tag: "test.example.com",
184 },
185 },
186 {
187 input: "xn--n3h.com/myimage:xn--n3h.com",
188 wantRef: Reference{
189 Host: "xn--n3h.com",
190 Repository: "myimage",
191 Tag: "xn--n3h.com",
192 },
193 },
194 {
195 input: "xn--7o8h.com/myimage:xn--7o8h.com@sha512:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
196 wantRef: Reference{
197 Host: "xn--7o8h.com",
198 Repository: "myimage",
199 Tag: "xn--7o8h.com",
200 Digest: "sha512:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
201 },
202 },
203 {
204 input: "foo_bar.com:8080",
205 wantRef: Reference{
206 Repository: "foo_bar.com",
207 Tag: "8080",
208 },
209 },
210 {
211 input: "foo.com/bar:8080",
212 wantRef: Reference{
213 Host: "foo.com",
214 Repository: "bar",
215 Tag: "8080",
216 },
217 },
218 {
219 input: "foo/foo_bar.com:8080",
220 wantRef: Reference{
221 Repository: "foo/foo_bar.com",
222 Tag: "8080",
223 },
224 },
225 {
226 input: "192.168.1.1",
227 wantRef: Reference{
228 Repository: "192.168.1.1",
229 },
230 },
231 {
232 input: "192.168.1.1:tag",
233 wantRef: Reference{
234 Repository: "192.168.1.1",
235 Tag: "tag",
236 },
237 },
238 {
239 input: "192.168.1.1:5000",
240 wantRef: Reference{
241 Repository: "192.168.1.1",
242 Tag: "5000",
243 },
244 },
245 {
246 input: "192.168.1.1/repo",
247 wantRef: Reference{
248 Host: "192.168.1.1",
249 Repository: "repo",
250 },
251 },
252 {
253 input: "192.168.1.1:5000/repo",
254 wantRef: Reference{
255 Host: "192.168.1.1:5000",
256 Repository: "repo",
257 },
258 },
259 {
260 input: "192.168.1.1:5000/repo:5050",
261 wantRef: Reference{
262 Host: "192.168.1.1:5000",
263 Repository: "repo",
264 Tag: "5050",
265 },
266 },
267 {
268 input: "[2001:db8::1]",
269 wantErr: `invalid reference syntax`,
270 },
271 {
272 input: "[2001:db8::1]:5000",
273 wantErr: `invalid reference syntax`,
274 },
275 {
276 input: "[2001:db8::1]:tag",
277 wantErr: `invalid reference syntax`,
278 },
279 {
280 input: "[2001:db8::1]/repo",
281 wantRef: Reference{
282 Host: "[2001:db8::1]",
283 Repository: "repo",
284 },
285 },
286 {
287 input: "[2001:db8:1:2:3:4:5:6]/repo:tag",
288 wantRef: Reference{
289 Host: "[2001:db8:1:2:3:4:5:6]",
290 Repository: "repo",
291 Tag: "tag",
292 },
293 },
294 {
295 input: "[2001:db8::1]:5000/repo",
296 wantRef: Reference{
297 Host: "[2001:db8::1]:5000",
298 Repository: "repo",
299 },
300 },
301 {
302 input: "[2001:db8::1]:5000/repo:tag",
303 wantRef: Reference{
304 Host: "[2001:db8::1]:5000",
305 Repository: "repo",
306 Tag: "tag",
307 },
308 },
309 {
310 input: "[2001:db8::1]:5000/repo@sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
311 wantRef: Reference{
312 Host: "[2001:db8::1]:5000",
313 Repository: "repo",
314 Digest: "sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
315 },
316 },
317 {
318 input: "[2001:db8::1]:5000/repo:tag@sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
319 wantRef: Reference{
320 Host: "[2001:db8::1]:5000",
321 Repository: "repo",
322 Tag: "tag",
323 Digest: "sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
324 },
325 },
326 {
327 input: "[2001:db8::]:5000/repo",
328 wantRef: Reference{
329 Host: "[2001:db8::]:5000",
330 Repository: "repo",
331 },
332 },
333 {
334 input: "[::1]:5000/repo",
335 wantRef: Reference{
336 Host: "[::1]:5000",
337 Repository: "repo",
338 },
339 },
340 {
341 input: "[fe80::1%eth0]:5000/repo",
342 wantErr: `invalid reference syntax`,
343 },
344 {
345 input: "[fe80::1%@invalidzone]:5000/repo",
346 wantErr: `invalid reference syntax`,
347 },
348 }
349
350 func TestParseReference(t *testing.T) {
351 for _, test := range parseReferenceTests {
352 if test.testName == "" {
353 test.testName = test.input
354 }
355 t.Run(test.testName, func(t *testing.T) {
356 ref, err := ParseRelative(test.input)
357 t.Logf("ref: %#v", ref)
358 if test.wantErr != "" {
359 if test.wantErr == "invalid reference syntax" {
360 test.wantErr += regexp.QuoteMeta(fmt.Sprintf(" (%q)", test.input))
361 }
362 qt.Assert(t, qt.ErrorMatches(err, test.wantErr))
363 return
364 }
365 qt.Assert(t, qt.IsNil(err))
366 qt.Check(t, qt.Equals(ref, test.wantRef))
367 qt.Check(t, qt.Equals(ref.String(), test.input))
368 if test.wantRef.Host != "" {
369 ref1, err := Parse(test.input)
370 qt.Assert(t, qt.IsNil(err))
371 qt.Check(t, qt.Equals(ref1, test.wantRef))
372 } else {
373 _, err := Parse(test.input)
374 qt.Assert(t, qt.ErrorMatches(err, `reference does not contain host name`))
375 }
376 })
377 }
378 }
379
380 var isValidHostTests = []struct {
381 host string
382 want bool
383 }{{
384 host: "foo.com:5000",
385 want: true,
386 }, {
387 host: "foo.com",
388 want: true,
389 }, {
390 host: "localhost:1234",
391 want: true,
392 }, {
393 host: "localhost",
394 want: false,
395 }, {
396 host: "foo",
397 want: false,
398 }, {
399 host: "foo..com",
400 want: false,
401 }, {
402 host: "[::1]",
403 want: true,
404 }, {
405 host: "[::1]:3456",
406 want: true,
407 }}
408
409 func TestIsValidHost(t *testing.T) {
410 for _, test := range isValidHostTests {
411 t.Run(test.host, func(t *testing.T) {
412 qt.Assert(t, qt.Equals(IsValidHost(test.host), test.want))
413 })
414 }
415 }
416
417 var isValidRepositoryTests = []struct {
418 repo string
419 want bool
420 }{{
421 repo: "foo",
422 want: true,
423 }, {
424 repo: "00123456789abcdefghijklmnopqrstuvwxyz",
425 want: true,
426 }, {
427 repo: "a-b---c",
428 want: true,
429 }, {
430 repo: "a.b.c9",
431 want: true,
432 }, {
433 repo: "a..b",
434 want: false,
435 }, {
436 repo: ".a",
437 want: false,
438 }, {
439 repo: "a.",
440 want: false,
441 }, {
442 repo: "-a",
443 want: false,
444 }, {
445 repo: "a-",
446 want: false,
447 }, {
448 repo: "A",
449 want: false,
450 }, {
451 repo: "_a",
452 want: false,
453 }, {
454 repo: "a_",
455 want: false,
456 }, {
457 repo: "foo/bar/baz",
458 want: true,
459 }, {
460 repo: "café",
461 want: false,
462 }, {
463 repo: "foo@bar",
464 want: false,
465 }}
466
467 func TestIsValidRepository(t *testing.T) {
468 for _, test := range isValidRepositoryTests {
469 t.Run(test.repo, func(t *testing.T) {
470 qt.Assert(t, qt.Equals(IsValidRepository(test.repo), test.want))
471 })
472 }
473 }
474
475 var isValidTagTests = []struct {
476 tag string
477 want bool
478 }{{
479 tag: "hello",
480 want: true,
481 }, {
482 tag: "v1.2.3-alpha.0",
483 want: true,
484 }, {
485 tag: "foo",
486 want: true,
487 }, {
488 tag: "_",
489 want: true,
490 }, {
491 tag: "_x",
492 want: true,
493 }, {
494 tag: "___",
495 want: true,
496 }, {
497 tag: "ABC_D",
498 want: true,
499 }, {
500 tag: strings.Repeat("a", 128),
501 want: true,
502 }, {
503 tag: strings.Repeat("a", 129),
504 want: false,
505 }, {
506 tag: "foo....___---",
507 want: true,
508 }, {
509 tag: "café",
510 want: false,
511 }, {
512 tag: "foo@bar",
513 want: false,
514 }, {
515 tag: "v4.3+something",
516 want: false,
517 }, {
518 tag: "xxx",
519 want: true,
520 }}
521
522 func TestIsValidTag(t *testing.T) {
523 for _, test := range isValidTagTests {
524 t.Run(test.tag, func(t *testing.T) {
525 qt.Assert(t, qt.Equals(IsValidTag(test.tag), test.want))
526 })
527 }
528 }
529
View as plain text