...

Source file src/github.com/distribution/reference/reference_test.go

Documentation: github.com/distribution/reference

     1  package reference
     2  
     3  import (
     4  	_ "crypto/sha256"
     5  	_ "crypto/sha512"
     6  	"encoding/json"
     7  	"strings"
     8  	"testing"
     9  
    10  	"github.com/opencontainers/go-digest"
    11  )
    12  
    13  func TestReferenceParse(t *testing.T) {
    14  	t.Parallel()
    15  	// tests is a unified set of testcases for
    16  	// testing the parsing of references
    17  	tests := []struct {
    18  		// input is the repository name or name component testcase
    19  		input string
    20  		// err is the error expected from Parse, or nil
    21  		err error
    22  		// repository is the string representation for the reference
    23  		repository string
    24  		// domain is the domain expected in the reference
    25  		domain string
    26  		// tag is the tag for the reference
    27  		tag string
    28  		// digest is the digest for the reference (enforces digest reference)
    29  		digest string
    30  	}{
    31  		{
    32  			input:      "test_com",
    33  			repository: "test_com",
    34  		},
    35  		{
    36  			input:      "test.com:tag",
    37  			repository: "test.com",
    38  			tag:        "tag",
    39  		},
    40  		{
    41  			input:      "test.com:5000",
    42  			repository: "test.com",
    43  			tag:        "5000",
    44  		},
    45  		{
    46  			input:      "test.com/repo:tag",
    47  			domain:     "test.com",
    48  			repository: "test.com/repo",
    49  			tag:        "tag",
    50  		},
    51  		{
    52  			input:      "test:5000/repo",
    53  			domain:     "test:5000",
    54  			repository: "test:5000/repo",
    55  		},
    56  		{
    57  			input:      "test:5000/repo:tag",
    58  			domain:     "test:5000",
    59  			repository: "test:5000/repo",
    60  			tag:        "tag",
    61  		},
    62  		{
    63  			input:      "test:5000/repo@sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
    64  			domain:     "test:5000",
    65  			repository: "test:5000/repo",
    66  			digest:     "sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
    67  		},
    68  		{
    69  			input:      "test:5000/repo:tag@sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
    70  			domain:     "test:5000",
    71  			repository: "test:5000/repo",
    72  			tag:        "tag",
    73  			digest:     "sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
    74  		},
    75  		{
    76  			input:      "test:5000/repo",
    77  			domain:     "test:5000",
    78  			repository: "test:5000/repo",
    79  		},
    80  		{
    81  			input: "",
    82  			err:   ErrNameEmpty,
    83  		},
    84  		{
    85  			input: ":justtag",
    86  			err:   ErrReferenceInvalidFormat,
    87  		},
    88  		{
    89  			input: "@sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
    90  			err:   ErrReferenceInvalidFormat,
    91  		},
    92  		{
    93  			input: "repo@sha256:ffffffffffffffffffffffffffffffffff",
    94  			err:   digest.ErrDigestInvalidLength,
    95  		},
    96  		{
    97  			input: "validname@invaliddigest:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
    98  			err:   digest.ErrDigestUnsupported,
    99  		},
   100  		{
   101  			input: "Uppercase:tag",
   102  			err:   ErrNameContainsUppercase,
   103  		},
   104  		// FIXME "Uppercase" is incorrectly handled as a domain-name here, therefore passes.
   105  		// See https://github.com/distribution/distribution/pull/1778, and https://github.com/docker/docker/pull/20175
   106  		// {
   107  		//	input: "Uppercase/lowercase:tag",
   108  		//	err:   ErrNameContainsUppercase,
   109  		// },
   110  		{
   111  			input: "test:5000/Uppercase/lowercase:tag",
   112  			err:   ErrNameContainsUppercase,
   113  		},
   114  		{
   115  			input:      "lowercase:Uppercase",
   116  			repository: "lowercase",
   117  			tag:        "Uppercase",
   118  		},
   119  		{
   120  			input: strings.Repeat("a/", 128) + "a:tag",
   121  			err:   ErrNameTooLong,
   122  		},
   123  		{
   124  			input:      strings.Repeat("a/", 127) + "a:tag-puts-this-over-max",
   125  			domain:     "a",
   126  			repository: strings.Repeat("a/", 127) + "a",
   127  			tag:        "tag-puts-this-over-max",
   128  		},
   129  		{
   130  			input: "aa/asdf$$^/aa",
   131  			err:   ErrReferenceInvalidFormat,
   132  		},
   133  		{
   134  			input:      "sub-dom1.foo.com/bar/baz/quux",
   135  			domain:     "sub-dom1.foo.com",
   136  			repository: "sub-dom1.foo.com/bar/baz/quux",
   137  		},
   138  		{
   139  			input:      "sub-dom1.foo.com/bar/baz/quux:some-long-tag",
   140  			domain:     "sub-dom1.foo.com",
   141  			repository: "sub-dom1.foo.com/bar/baz/quux",
   142  			tag:        "some-long-tag",
   143  		},
   144  		{
   145  			input:      "b.gcr.io/test.example.com/my-app:test.example.com",
   146  			domain:     "b.gcr.io",
   147  			repository: "b.gcr.io/test.example.com/my-app",
   148  			tag:        "test.example.com",
   149  		},
   150  		{
   151  			input:      "xn--n3h.com/myimage:xn--n3h.com", // ☃.com in punycode
   152  			domain:     "xn--n3h.com",
   153  			repository: "xn--n3h.com/myimage",
   154  			tag:        "xn--n3h.com",
   155  		},
   156  		{
   157  			input:      "xn--7o8h.com/myimage:xn--7o8h.com@sha512:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", // 🐳.com in punycode
   158  			domain:     "xn--7o8h.com",
   159  			repository: "xn--7o8h.com/myimage",
   160  			tag:        "xn--7o8h.com",
   161  			digest:     "sha512:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
   162  		},
   163  		{
   164  			input:      "foo_bar.com:8080",
   165  			repository: "foo_bar.com",
   166  			tag:        "8080",
   167  		},
   168  		{
   169  			input:      "foo/foo_bar.com:8080",
   170  			domain:     "foo",
   171  			repository: "foo/foo_bar.com",
   172  			tag:        "8080",
   173  		},
   174  		{
   175  			input:      "192.168.1.1",
   176  			repository: "192.168.1.1",
   177  		},
   178  		{
   179  			input:      "192.168.1.1:tag",
   180  			repository: "192.168.1.1",
   181  			tag:        "tag",
   182  		},
   183  		{
   184  			input:      "192.168.1.1:5000",
   185  			repository: "192.168.1.1",
   186  			tag:        "5000",
   187  		},
   188  		{
   189  			input:      "192.168.1.1/repo",
   190  			domain:     "192.168.1.1",
   191  			repository: "192.168.1.1/repo",
   192  		},
   193  		{
   194  			input:      "192.168.1.1:5000/repo",
   195  			domain:     "192.168.1.1:5000",
   196  			repository: "192.168.1.1:5000/repo",
   197  		},
   198  		{
   199  			input:      "192.168.1.1:5000/repo:5050",
   200  			domain:     "192.168.1.1:5000",
   201  			repository: "192.168.1.1:5000/repo",
   202  			tag:        "5050",
   203  		},
   204  		{
   205  			input: "[2001:db8::1]",
   206  			err:   ErrReferenceInvalidFormat,
   207  		},
   208  		{
   209  			input: "[2001:db8::1]:5000",
   210  			err:   ErrReferenceInvalidFormat,
   211  		},
   212  		{
   213  			input: "[2001:db8::1]:tag",
   214  			err:   ErrReferenceInvalidFormat,
   215  		},
   216  		{
   217  			input:      "[2001:db8::1]/repo",
   218  			domain:     "[2001:db8::1]",
   219  			repository: "[2001:db8::1]/repo",
   220  		},
   221  		{
   222  			input:      "[2001:db8:1:2:3:4:5:6]/repo:tag",
   223  			domain:     "[2001:db8:1:2:3:4:5:6]",
   224  			repository: "[2001:db8:1:2:3:4:5:6]/repo",
   225  			tag:        "tag",
   226  		},
   227  		{
   228  			input:      "[2001:db8::1]:5000/repo",
   229  			domain:     "[2001:db8::1]:5000",
   230  			repository: "[2001:db8::1]:5000/repo",
   231  		},
   232  		{
   233  			input:      "[2001:db8::1]:5000/repo:tag",
   234  			domain:     "[2001:db8::1]:5000",
   235  			repository: "[2001:db8::1]:5000/repo",
   236  			tag:        "tag",
   237  		},
   238  		{
   239  			input:      "[2001:db8::1]:5000/repo@sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
   240  			domain:     "[2001:db8::1]:5000",
   241  			repository: "[2001:db8::1]:5000/repo",
   242  			digest:     "sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
   243  		},
   244  		{
   245  			input:      "[2001:db8::1]:5000/repo:tag@sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
   246  			domain:     "[2001:db8::1]:5000",
   247  			repository: "[2001:db8::1]:5000/repo",
   248  			tag:        "tag",
   249  			digest:     "sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
   250  		},
   251  		{
   252  			input:      "[2001:db8::]:5000/repo",
   253  			domain:     "[2001:db8::]:5000",
   254  			repository: "[2001:db8::]:5000/repo",
   255  		},
   256  		{
   257  			input:      "[::1]:5000/repo",
   258  			domain:     "[::1]:5000",
   259  			repository: "[::1]:5000/repo",
   260  		},
   261  		{
   262  			input: "[fe80::1%eth0]:5000/repo",
   263  			err:   ErrReferenceInvalidFormat,
   264  		},
   265  		{
   266  			input: "[fe80::1%@invalidzone]:5000/repo",
   267  			err:   ErrReferenceInvalidFormat,
   268  		},
   269  	}
   270  	for _, tc := range tests {
   271  		tc := tc
   272  		t.Run(tc.input, func(t *testing.T) {
   273  			t.Parallel()
   274  			repo, err := Parse(tc.input)
   275  			if tc.err != nil {
   276  				if err == nil {
   277  					t.Errorf("missing expected error: %v", tc.err)
   278  				} else if tc.err != err {
   279  					t.Errorf("mismatched error: got %v, expected %v", err, tc.err)
   280  				}
   281  				return
   282  			} else if err != nil {
   283  				t.Errorf("unexpected parse error: %v", err)
   284  				return
   285  			}
   286  			if repo.String() != tc.input {
   287  				t.Errorf("mismatched repo: got %q, expected %q", repo.String(), tc.input)
   288  			}
   289  
   290  			if named, ok := repo.(Named); ok {
   291  				if named.Name() != tc.repository {
   292  					t.Errorf("unexpected repository: got %q, expected %q", named.Name(), tc.repository)
   293  				}
   294  				domain, _ := SplitHostname(named)
   295  				if domain != tc.domain {
   296  					t.Errorf("unexpected domain: got %q, expected %q", domain, tc.domain)
   297  				}
   298  			} else if tc.repository != "" || tc.domain != "" {
   299  				t.Errorf("expected named type, got %T", repo)
   300  			}
   301  
   302  			tagged, ok := repo.(Tagged)
   303  			if tc.tag != "" {
   304  				if ok {
   305  					if tagged.Tag() != tc.tag {
   306  						t.Errorf("unexpected tag: got %q, expected %q", tagged.Tag(), tc.tag)
   307  					}
   308  				} else {
   309  					t.Errorf("expected tagged type, got %T", repo)
   310  				}
   311  			} else if ok {
   312  				t.Errorf("unexpected tagged type")
   313  			}
   314  
   315  			digested, ok := repo.(Digested)
   316  			if tc.digest != "" {
   317  				if ok {
   318  					if digested.Digest().String() != tc.digest {
   319  						t.Errorf("unexpected digest: got %q, expected %q", digested.Digest().String(), tc.digest)
   320  					}
   321  				} else {
   322  					t.Errorf("expected digested type, got %T", repo)
   323  				}
   324  			} else if ok {
   325  				t.Errorf("unexpected digested type")
   326  			}
   327  		})
   328  	}
   329  }
   330  
   331  // TestWithNameFailure tests cases where WithName should fail. Cases where it
   332  // should succeed are covered by TestSplitHostname, below.
   333  func TestWithNameFailure(t *testing.T) {
   334  	t.Parallel()
   335  	tests := []struct {
   336  		input string
   337  		err   error
   338  	}{
   339  		{
   340  			input: "",
   341  			err:   ErrNameEmpty,
   342  		},
   343  		{
   344  			input: ":justtag",
   345  			err:   ErrReferenceInvalidFormat,
   346  		},
   347  		{
   348  			input: "@sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
   349  			err:   ErrReferenceInvalidFormat,
   350  		},
   351  		{
   352  			input: "validname@invaliddigest:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
   353  			err:   ErrReferenceInvalidFormat,
   354  		},
   355  		{
   356  			input: strings.Repeat("a/", 128) + "a:tag",
   357  			err:   ErrNameTooLong,
   358  		},
   359  		{
   360  			input: "aa/asdf$$^/aa",
   361  			err:   ErrReferenceInvalidFormat,
   362  		},
   363  	}
   364  	for _, tc := range tests {
   365  		tc := tc
   366  		t.Run(tc.input, func(t *testing.T) {
   367  			t.Parallel()
   368  			_, err := WithName(tc.input)
   369  			if err == nil {
   370  				t.Errorf("no error parsing name. expected: %s", tc.err)
   371  			}
   372  		})
   373  	}
   374  }
   375  
   376  func TestSplitHostname(t *testing.T) {
   377  	t.Parallel()
   378  	tests := []struct {
   379  		input  string
   380  		domain string
   381  		name   string
   382  	}{
   383  		{
   384  			input:  "test.com/foo",
   385  			domain: "test.com",
   386  			name:   "foo",
   387  		},
   388  		{
   389  			input:  "test_com/foo",
   390  			domain: "",
   391  			name:   "test_com/foo",
   392  		},
   393  		{
   394  			input:  "test:8080/foo",
   395  			domain: "test:8080",
   396  			name:   "foo",
   397  		},
   398  		{
   399  			input:  "test.com:8080/foo",
   400  			domain: "test.com:8080",
   401  			name:   "foo",
   402  		},
   403  		{
   404  			input:  "test-com:8080/foo",
   405  			domain: "test-com:8080",
   406  			name:   "foo",
   407  		},
   408  		{
   409  			input:  "xn--n3h.com:18080/foo",
   410  			domain: "xn--n3h.com:18080",
   411  			name:   "foo",
   412  		},
   413  	}
   414  	for _, tc := range tests {
   415  		tc := tc
   416  		t.Run(tc.input, func(t *testing.T) {
   417  			t.Parallel()
   418  			named, err := WithName(tc.input)
   419  			if err != nil {
   420  				t.Errorf("error parsing name: %s", err)
   421  			}
   422  			domain, name := SplitHostname(named)
   423  			if domain != tc.domain {
   424  				t.Errorf("unexpected domain: got %q, expected %q", domain, tc.domain)
   425  			}
   426  			if name != tc.name {
   427  				t.Errorf("unexpected name: got %q, expected %q", name, tc.name)
   428  			}
   429  		})
   430  	}
   431  }
   432  
   433  type serializationType struct {
   434  	Description string
   435  	Field       Field
   436  }
   437  
   438  func TestSerialization(t *testing.T) {
   439  	t.Parallel()
   440  	tests := []struct {
   441  		description string
   442  		input       string
   443  		name        string
   444  		tag         string
   445  		digest      string
   446  		err         error
   447  	}{
   448  		{
   449  			description: "empty value",
   450  			err:         ErrNameEmpty,
   451  		},
   452  		{
   453  			description: "just a name",
   454  			input:       "example.com:8000/named",
   455  			name:        "example.com:8000/named",
   456  		},
   457  		{
   458  			description: "name with a tag",
   459  			input:       "example.com:8000/named:tagged",
   460  			name:        "example.com:8000/named",
   461  			tag:         "tagged",
   462  		},
   463  		{
   464  			description: "name with digest",
   465  			input:       "other.com/named@sha256:1234567890098765432112345667890098765432112345667890098765432112",
   466  			name:        "other.com/named",
   467  			digest:      "sha256:1234567890098765432112345667890098765432112345667890098765432112",
   468  		},
   469  	}
   470  	for _, tc := range tests {
   471  		tc := tc
   472  		t.Run(tc.description, func(t *testing.T) {
   473  			t.Parallel()
   474  			m := map[string]string{
   475  				"Description": tc.description,
   476  				"Field":       tc.input,
   477  			}
   478  			b, err := json.Marshal(m)
   479  			if err != nil {
   480  				t.Errorf("error marshalling: %v", err)
   481  			}
   482  			st := serializationType{}
   483  
   484  			if err := json.Unmarshal(b, &st); err != nil {
   485  				if tc.err == nil {
   486  					t.Errorf("error unmarshalling: %v", err)
   487  				}
   488  				if err != tc.err {
   489  					t.Errorf("wrong error, expected %v, got %v", tc.err, err)
   490  				}
   491  
   492  				return
   493  			} else if tc.err != nil {
   494  				t.Errorf("expected error unmarshalling: %v", tc.err)
   495  			}
   496  
   497  			if st.Description != tc.description {
   498  				t.Errorf("wrong description, expected %q, got %q", tc.description, st.Description)
   499  			}
   500  
   501  			ref := st.Field.Reference()
   502  
   503  			if named, ok := ref.(Named); ok {
   504  				if named.Name() != tc.name {
   505  					t.Errorf("unexpected repository: got %q, expected %q", named.Name(), tc.name)
   506  				}
   507  			} else if tc.name != "" {
   508  				t.Errorf("expected named type, got %T", ref)
   509  			}
   510  
   511  			tagged, ok := ref.(Tagged)
   512  			if tc.tag != "" {
   513  				if ok {
   514  					if tagged.Tag() != tc.tag {
   515  						t.Errorf("unexpected tag: got %q, expected %q", tagged.Tag(), tc.tag)
   516  					}
   517  				} else {
   518  					t.Errorf("expected tagged type, got %T", ref)
   519  				}
   520  			} else if ok {
   521  				t.Errorf("unexpected tagged type")
   522  			}
   523  
   524  			digested, ok := ref.(Digested)
   525  			if tc.digest != "" {
   526  				if ok {
   527  					if digested.Digest().String() != tc.digest {
   528  						t.Errorf("unexpected digest: got %q, expected %q", digested.Digest().String(), tc.digest)
   529  					}
   530  				} else {
   531  					t.Errorf("expected digested type, got %T", ref)
   532  				}
   533  			} else if ok {
   534  				t.Errorf("unexpected digested type")
   535  			}
   536  
   537  			st = serializationType{
   538  				Description: tc.description,
   539  				Field:       AsField(ref),
   540  			}
   541  
   542  			b2, err := json.Marshal(st)
   543  			if err != nil {
   544  				t.Errorf("error marshing serialization type: %v", err)
   545  			}
   546  
   547  			if string(b) != string(b2) {
   548  				t.Errorf("unexpected serialized value: expected %q, got %q", string(b), string(b2))
   549  			}
   550  
   551  			// Ensure st.Field is not implementing "Reference" directly, getting
   552  			// around the Reference type system
   553  			var fieldInterface interface{} = st.Field
   554  			if _, ok := fieldInterface.(Reference); ok {
   555  				t.Errorf("field should not implement Reference interface")
   556  			}
   557  		})
   558  	}
   559  }
   560  
   561  func TestWithTag(t *testing.T) {
   562  	t.Parallel()
   563  	tests := []struct {
   564  		name     string
   565  		digest   digest.Digest
   566  		tag      string
   567  		combined string
   568  	}{
   569  		{
   570  			name:     "test.com/foo",
   571  			tag:      "tag",
   572  			combined: "test.com/foo:tag",
   573  		},
   574  		{
   575  			name:     "foo",
   576  			tag:      "tag2",
   577  			combined: "foo:tag2",
   578  		},
   579  		{
   580  			name:     "test.com:8000/foo",
   581  			tag:      "tag4",
   582  			combined: "test.com:8000/foo:tag4",
   583  		},
   584  		{
   585  			name:     "test.com:8000/foo",
   586  			tag:      "TAG5",
   587  			combined: "test.com:8000/foo:TAG5",
   588  		},
   589  		{
   590  			name:     "test.com:8000/foo",
   591  			digest:   "sha256:1234567890098765432112345667890098765",
   592  			tag:      "TAG5",
   593  			combined: "test.com:8000/foo:TAG5@sha256:1234567890098765432112345667890098765",
   594  		},
   595  	}
   596  	for _, tc := range tests {
   597  		tc := tc
   598  		t.Run(tc.combined, func(t *testing.T) {
   599  			t.Parallel()
   600  			named, err := WithName(tc.name)
   601  			if err != nil {
   602  				t.Errorf("error parsing name: %s", err)
   603  			}
   604  			if tc.digest != "" {
   605  				canonical, err := WithDigest(named, tc.digest)
   606  				if err != nil {
   607  					t.Errorf("error adding digest")
   608  				}
   609  				named = canonical
   610  			}
   611  
   612  			tagged, err := WithTag(named, tc.tag)
   613  			if err != nil {
   614  				t.Errorf("WithTag failed: %s", err)
   615  			}
   616  			if tagged.String() != tc.combined {
   617  				t.Errorf("unexpected: got %q, expected %q", tagged.String(), tc.combined)
   618  			}
   619  		})
   620  	}
   621  }
   622  
   623  func TestWithDigest(t *testing.T) {
   624  	t.Parallel()
   625  	tests := []struct {
   626  		name     string
   627  		digest   digest.Digest
   628  		tag      string
   629  		combined string
   630  	}{
   631  		{
   632  			name:     "test.com/foo",
   633  			digest:   "sha256:1234567890098765432112345667890098765",
   634  			combined: "test.com/foo@sha256:1234567890098765432112345667890098765",
   635  		},
   636  		{
   637  			name:     "foo",
   638  			digest:   "sha256:1234567890098765432112345667890098765",
   639  			combined: "foo@sha256:1234567890098765432112345667890098765",
   640  		},
   641  		{
   642  			name:     "test.com:8000/foo",
   643  			digest:   "sha256:1234567890098765432112345667890098765",
   644  			combined: "test.com:8000/foo@sha256:1234567890098765432112345667890098765",
   645  		},
   646  		{
   647  			name:     "test.com:8000/foo",
   648  			digest:   "sha256:1234567890098765432112345667890098765",
   649  			tag:      "latest",
   650  			combined: "test.com:8000/foo:latest@sha256:1234567890098765432112345667890098765",
   651  		},
   652  	}
   653  	for _, tc := range tests {
   654  		tc := tc
   655  		t.Run(tc.combined, func(t *testing.T) {
   656  			t.Parallel()
   657  			named, err := WithName(tc.name)
   658  			if err != nil {
   659  				t.Errorf("error parsing name: %s", err)
   660  			}
   661  			if tc.tag != "" {
   662  				tagged, err := WithTag(named, tc.tag)
   663  				if err != nil {
   664  					t.Errorf("error adding tag")
   665  				}
   666  				named = tagged
   667  			}
   668  			digested, err := WithDigest(named, tc.digest)
   669  			if err != nil {
   670  				t.Errorf("WithDigest failed: %s", err)
   671  			}
   672  			if digested.String() != tc.combined {
   673  				t.Errorf("unexpected: got %q, expected %q", digested.String(), tc.combined)
   674  			}
   675  		})
   676  	}
   677  }
   678  
   679  func TestParseNamed(t *testing.T) {
   680  	t.Parallel()
   681  	tests := []struct {
   682  		input  string
   683  		domain string
   684  		name   string
   685  		err    error
   686  	}{
   687  		{
   688  			input:  "test.com/foo",
   689  			domain: "test.com",
   690  			name:   "foo",
   691  		},
   692  		{
   693  			input:  "test:8080/foo",
   694  			domain: "test:8080",
   695  			name:   "foo",
   696  		},
   697  		{
   698  			input: "test_com/foo",
   699  			err:   ErrNameNotCanonical,
   700  		},
   701  		{
   702  			input: "test.com",
   703  			err:   ErrNameNotCanonical,
   704  		},
   705  		{
   706  			input: "foo",
   707  			err:   ErrNameNotCanonical,
   708  		},
   709  		{
   710  			input: "library/foo",
   711  			err:   ErrNameNotCanonical,
   712  		},
   713  		{
   714  			input:  "docker.io/library/foo",
   715  			domain: "docker.io",
   716  			name:   "library/foo",
   717  		},
   718  		// Ambiguous case, parser will add "library/" to foo
   719  		{
   720  			input: "docker.io/foo",
   721  			err:   ErrNameNotCanonical,
   722  		},
   723  	}
   724  	for _, tc := range tests {
   725  		tc := tc
   726  		t.Run(tc.input, func(t *testing.T) {
   727  			t.Parallel()
   728  			named, err := ParseNamed(tc.input)
   729  			if err != nil && tc.err == nil {
   730  				t.Errorf("error parsing name: %s", err)
   731  				return
   732  			} else if err == nil && tc.err != nil {
   733  				t.Errorf("parsing succeeded: expected error %v", tc.err)
   734  				return
   735  			} else if err != tc.err {
   736  				t.Errorf("unexpected error %v, expected %v", err, tc.err)
   737  				return
   738  			} else if err != nil {
   739  				return
   740  			}
   741  
   742  			domain, name := SplitHostname(named)
   743  			if domain != tc.domain {
   744  				t.Errorf("unexpected domain: got %q, expected %q", domain, tc.domain)
   745  			}
   746  			if name != tc.name {
   747  				t.Errorf("unexpected name: got %q, expected %q", name, tc.name)
   748  			}
   749  		})
   750  	}
   751  }
   752  

View as plain text