...

Source file src/cuelang.org/go/internal/mod/modresolve/resolve_test.go

Documentation: cuelang.org/go/internal/mod/modresolve

     1  package modresolve
     2  
     3  import (
     4  	"crypto/sha256"
     5  	"fmt"
     6  	"strings"
     7  	"testing"
     8  
     9  	"cuelang.org/go/cue"
    10  	"cuelang.org/go/cue/cuecontext"
    11  	"github.com/go-quicktest/qt"
    12  )
    13  
    14  func TestRegistryConfigSchema(t *testing.T) {
    15  	schema := RegistryConfigSchema()
    16  	// Sanity check that it parses OK as CUE and can
    17  	// validate a legitimate schema.
    18  	ctx := cuecontext.New()
    19  	v := ctx.CompileString(schema)
    20  	fileSchema := v.LookupPath(cue.MakePath(cue.Def("#file")))
    21  	qt.Assert(t, qt.IsNil(fileSchema.Err()))
    22  	cfgVal := ctx.CompileString(`defaultRegistry: registry: "something.example"`)
    23  	qt.Assert(t, qt.IsNil(cfgVal.Err()))
    24  	cfgVal = cfgVal.Unify(fileSchema)
    25  	qt.Assert(t, qt.IsNil(cfgVal.Err()))
    26  }
    27  
    28  func TestParseCUERegistry(t *testing.T) {
    29  	testCases := []struct {
    30  		testName        string
    31  		in              string
    32  		catchAllDefault string
    33  		err             string
    34  		wantAllHosts    []Host
    35  		lookups         map[string]*Location
    36  	}{{
    37  		testName: "MultipleFallbacks",
    38  		in:       "registry.somewhere,registry.other",
    39  		err:      "duplicate catch-all registry",
    40  	}, {
    41  		testName:        "NoRegistryOrDefault",
    42  		catchAllDefault: "",
    43  		err:             "no catch-all registry or default",
    44  	}, {
    45  		testName: "InvalidRegistry",
    46  		in:       "$#foo",
    47  		err:      `invalid registry "\$#foo": invalid host name "\$#foo" in registry`,
    48  	}, {
    49  		testName: "InvalidSecuritySuffix",
    50  		in:       "foo.com+bogus",
    51  		err:      `invalid registry "foo.com\+bogus": unknown suffix \("\+bogus"\), need \+insecure, \+secure or no suffix\)`,
    52  	}, {
    53  		testName: "IPV6AddrWithoutBrackets",
    54  		in:       "::1",
    55  		err:      `invalid registry "::1": invalid host name "::1" in registry`,
    56  	}, {
    57  		testName: "EmptyElement",
    58  		in:       "foo.com,",
    59  		err:      `empty registry part`,
    60  	}, {
    61  		testName: "MissingPrefix",
    62  		in:       "=foo.com",
    63  		err:      `empty module prefix`,
    64  	}, {
    65  		testName: "MissingRegistry",
    66  		in:       "x.com=",
    67  		err:      `empty registry reference`,
    68  	}, {
    69  		testName: "InvalidModulePrefix",
    70  		in:       "foo#=foo.com",
    71  		err:      `invalid module path "foo#": invalid char '#'`,
    72  	}, {
    73  		testName: "DuplicateModulePrefix",
    74  		in:       "x.com=r.org,x.com=q.org",
    75  		err:      `duplicate module prefix "x.com"`,
    76  	}, {
    77  		testName: "NoDefaultCatchAll",
    78  		in:       "x.com=r.org",
    79  		err:      `no default catch-all registry provided`,
    80  	}, {
    81  		testName:        "InvalidCatchAll",
    82  		in:              "x.com=r.org",
    83  		catchAllDefault: "bogus",
    84  		err:             `invalid catch-all registry "bogus": invalid host name "bogus" in registry`,
    85  	}, {
    86  		testName: "InvalidRegistryRef",
    87  		in:       "foo.com//bar",
    88  		err:      `invalid registry "foo.com//bar": invalid reference syntax \("foo.com//bar"\)`,
    89  	}, {
    90  		testName: "RegistryRefWithDigest",
    91  		in:       "foo.com/bar@sha256:f3c16f525a1b7c204fc953d6d7db7168d84ebf4902f83c3a37d113b18c28981f",
    92  		err:      `invalid registry "foo.com/bar@sha256:f3c16f525a1b7c204fc953d6d7db7168d84ebf4902f83c3a37d113b18c28981f": cannot have an associated tag or digest`,
    93  	}, {
    94  		testName: "RegistryRefWithTag",
    95  		in:       "foo.com/bar:sometag",
    96  		err:      `invalid registry "foo.com/bar:sometag": cannot have an associated tag or digest`,
    97  	}, {
    98  		testName: "MismatchedSecurity",
    99  		in:       "foo.com/bar+secure,other.example=foo.com/bar+insecure",
   100  		err:      `registry host "foo.com" is specified both as secure and insecure`,
   101  	}, {
   102  		testName:        "SingleCatchAll",
   103  		catchAllDefault: "registry.somewhere",
   104  		wantAllHosts:    []Host{{"registry.somewhere", false}},
   105  		lookups: map[string]*Location{
   106  			"fruit.com/apple": {
   107  				Host:       "registry.somewhere",
   108  				Repository: "fruit.com/apple",
   109  			},
   110  		},
   111  	}, {
   112  		testName:     "CatchAllWithNoDefault",
   113  		in:           "registry.somewhere",
   114  		wantAllHosts: []Host{{"registry.somewhere", false}},
   115  		lookups: map[string]*Location{
   116  			"fruit.com/apple": {
   117  				Host:       "registry.somewhere",
   118  				Repository: "fruit.com/apple",
   119  			},
   120  		},
   121  	}, {
   122  		testName:        "CatchAllWithDefault",
   123  		in:              "registry.somewhere",
   124  		catchAllDefault: "other.cue.somewhere",
   125  		wantAllHosts:    []Host{{"registry.somewhere", false}},
   126  		lookups: map[string]*Location{
   127  			"fruit.com/apple": {
   128  				Host:       "registry.somewhere",
   129  				Repository: "fruit.com/apple",
   130  			},
   131  			"": nil,
   132  		},
   133  	}, {
   134  		testName:     "PrefixWithCatchAllNoDefault",
   135  		in:           "example.com=registry.example.com/offset,registry.somewhere",
   136  		wantAllHosts: []Host{{"registry.example.com", false}, {"registry.somewhere", false}},
   137  		lookups: map[string]*Location{
   138  			"fruit.com/apple": {
   139  				Host:       "registry.somewhere",
   140  				Repository: "fruit.com/apple",
   141  			},
   142  			"example.com/blah": {
   143  				Host:       "registry.example.com",
   144  				Repository: "offset/example.com/blah",
   145  			},
   146  			"example.com": {
   147  				Host:       "registry.example.com",
   148  				Repository: "offset/example.com",
   149  			},
   150  		},
   151  	}, {
   152  		testName:        "PrefixWithCatchAllDefault",
   153  		in:              "example.com=registry.example.com/offset",
   154  		catchAllDefault: "registry.somewhere",
   155  		wantAllHosts:    []Host{{"registry.example.com", false}, {"registry.somewhere", false}},
   156  		lookups: map[string]*Location{
   157  			"fruit.com/apple": {
   158  				Host:       "registry.somewhere",
   159  				Repository: "fruit.com/apple",
   160  			},
   161  			"example.com/blah": {
   162  				Host:       "registry.example.com",
   163  				Repository: "offset/example.com/blah",
   164  			},
   165  		},
   166  	}, {
   167  		testName:     "LocalhostIsInsecure",
   168  		in:           "localhost:5000",
   169  		wantAllHosts: []Host{{"localhost:5000", true}},
   170  		lookups: map[string]*Location{
   171  			"fruit.com/apple": {
   172  				Host:       "localhost:5000",
   173  				Insecure:   true,
   174  				Repository: "fruit.com/apple",
   175  			},
   176  		},
   177  	}, {
   178  		testName:     "SecureLocalhost",
   179  		in:           "localhost:1234+secure",
   180  		wantAllHosts: []Host{{"localhost:1234", false}},
   181  		lookups: map[string]*Location{
   182  			"fruit.com/apple": {
   183  				Host:       "localhost:1234",
   184  				Repository: "fruit.com/apple",
   185  			},
   186  		},
   187  	}, {
   188  		testName:     "127.0.0.1IsInsecure",
   189  		in:           "127.0.0.1",
   190  		wantAllHosts: []Host{{"127.0.0.1", true}},
   191  		lookups: map[string]*Location{
   192  			"fruit.com/apple": {
   193  				Host:       "127.0.0.1",
   194  				Insecure:   true,
   195  				Repository: "fruit.com/apple",
   196  			},
   197  		},
   198  	}, {
   199  		testName:     "[::1]IsInsecure",
   200  		in:           "[::1]",
   201  		wantAllHosts: []Host{{"[::1]", true}},
   202  		lookups: map[string]*Location{
   203  			"fruit.com/apple": {
   204  				Host:       "[::1]",
   205  				Insecure:   true,
   206  				Repository: "fruit.com/apple",
   207  			},
   208  		},
   209  	}, {
   210  		testName:     "[0:0::1]IsInsecure",
   211  		in:           "[0:0::1]",
   212  		wantAllHosts: []Host{{"[0:0::1]", true}},
   213  		lookups: map[string]*Location{
   214  			"fruit.com/apple": {
   215  				Host:       "[0:0::1]",
   216  				Insecure:   true,
   217  				Repository: "fruit.com/apple",
   218  			},
   219  		},
   220  	}}
   221  
   222  	for _, tc := range testCases {
   223  		t.Run(tc.testName, func(t *testing.T) {
   224  			r, err := ParseCUERegistry(tc.in, tc.catchAllDefault)
   225  			if tc.err != "" {
   226  				qt.Assert(t, qt.ErrorMatches(err, tc.err))
   227  				return
   228  			}
   229  			qt.Assert(t, qt.IsNil(err))
   230  			qt.Check(t, qt.DeepEquals(r.AllHosts(), tc.wantAllHosts))
   231  			testLookups(t, r, tc.lookups)
   232  		})
   233  	}
   234  }
   235  
   236  func TestParseConfig(t *testing.T) {
   237  	testCases := []struct {
   238  		testName        string
   239  		in              string
   240  		catchAllDefault string
   241  		err             string
   242  		wantAllHosts    []Host
   243  		lookups         map[string]*Location
   244  	}{{
   245  		testName:        "NoRegistryOrDefault",
   246  		catchAllDefault: "",
   247  		err:             "no default catch-all registry provided",
   248  	}, {
   249  		testName: "InvalidRegistry",
   250  		in: `
   251  defaultRegistry: registry: "$#foo"
   252  `,
   253  		err: `invalid default registry configuration: invalid host name "\$#foo" in registry`,
   254  	}, {
   255  		testName: "EncHashAsRepo",
   256  		in: `
   257  defaultRegistry: {
   258  	registry: "registry.somewhere/hello"
   259  	pathEncoding: "hashAsRepo"
   260  	prefixForTags: "mod-"
   261  }
   262  `,
   263  		wantAllHosts: []Host{{"registry.somewhere", false}},
   264  		lookups: map[string]*Location{
   265  			"foo.com/bar v1.2.3": {
   266  				Host:       "registry.somewhere",
   267  				Repository: "hello/" + hashOf("foo.com/bar"),
   268  				Tag:        "mod-v1.2.3",
   269  			},
   270  		},
   271  	}, {
   272  		testName: "EncHashAsTag",
   273  		in: `
   274  defaultRegistry: {
   275  	registry: "registry.somewhere/hello"
   276  	pathEncoding: "hashAsTag"
   277  	prefixForTags: "mod-"
   278  }
   279  `,
   280  		wantAllHosts: []Host{{"registry.somewhere", false}},
   281  		lookups: map[string]*Location{
   282  			"foo.com/bar v1.2.3": {
   283  				Host:       "registry.somewhere",
   284  				Repository: "hello",
   285  				Tag:        "mod-" + hashOf("foo.com/bar") + "-v1.2.3",
   286  			},
   287  		},
   288  	}, {
   289  		testName: "DefaultRegistryWithModuleRegistries",
   290  		in: `
   291  defaultRegistry: {
   292  	registry: "registry.somewhere"
   293  }
   294  moduleRegistries: {
   295  	"a.com": {
   296  		registry: "registry.otherwhere"
   297  	}
   298  }
   299  `,
   300  		wantAllHosts: []Host{{"registry.otherwhere", false}, {"registry.somewhere", false}},
   301  		lookups: map[string]*Location{
   302  			"a.com v0.0.1": {
   303  				Host:       "registry.otherwhere",
   304  				Repository: "a.com",
   305  				Tag:        "v0.0.1",
   306  			},
   307  			"b.com v0.0.1": {
   308  				Host:       "registry.somewhere",
   309  				Repository: "b.com",
   310  				Tag:        "v0.0.1",
   311  			},
   312  		},
   313  	}, {
   314  		testName:        "DiverseRegistries",
   315  		catchAllDefault: "default.example/foo",
   316  		in: `
   317  moduleRegistries: {
   318  	"a.com": {
   319  		registry: "r1.example/a/b+insecure"
   320  	}
   321  	"a.com/foo/bar": {
   322  		registry: "r2.example/xxx"
   323  		pathEncoding: "hashAsRepo"
   324  		prefixForTags: "cue-"
   325  	}
   326  	"a.com/foo": {
   327  		registry: "r1.example/hello+insecure"
   328  	}
   329  	"stripped.org/bar": {
   330  		registry: "r3.example/repo"
   331  		stripPrefix: true
   332  	}
   333  }
   334  `,
   335  		wantAllHosts: []Host{{
   336  			Name: "default.example",
   337  		}, {
   338  			Name:     "r1.example",
   339  			Insecure: true,
   340  		}, {
   341  			Name: "r2.example",
   342  		}, {
   343  			Name: "r3.example",
   344  		}},
   345  		lookups: map[string]*Location{
   346  			"a.com/other/bar/baz v0.0.1": {
   347  				Host:       "r1.example",
   348  				Insecure:   true,
   349  				Repository: "a/b/a.com/other/bar/baz",
   350  				Tag:        "v0.0.1",
   351  			},
   352  			"a.com/foo/bar v0.0.1": {
   353  				Host:       "r2.example",
   354  				Repository: "xxx/" + hashOf("a.com/foo/bar"),
   355  				Tag:        "cue-v0.0.1",
   356  			},
   357  			"a.com/foo/bar": {
   358  				Host:       "r2.example",
   359  				Repository: "xxx/" + hashOf("a.com/foo/bar"),
   360  				Tag:        "cue-",
   361  			},
   362  			"a.com/foo/baz v0.0.1": {
   363  				Host:       "r1.example",
   364  				Insecure:   true,
   365  				Repository: "hello/a.com/foo/baz",
   366  				Tag:        "v0.0.1",
   367  			},
   368  			"a.com/food v0.0.1": {
   369  				Host:       "r1.example",
   370  				Insecure:   true,
   371  				Repository: "a/b/a.com/food",
   372  				Tag:        "v0.0.1",
   373  			},
   374  			"b.com v0.0.1": {
   375  				Host:       "default.example",
   376  				Repository: "foo/b.com",
   377  				Tag:        "v0.0.1",
   378  			},
   379  			"stripped.org/bar/one/two/three v0.0.1": {
   380  				Host:       "r3.example",
   381  				Repository: "repo/one/two/three",
   382  				Tag:        "v0.0.1",
   383  			},
   384  			"stripped.org/bar v0.0.1": {
   385  				Host:       "r3.example",
   386  				Repository: "repo",
   387  				Tag:        "v0.0.1",
   388  			},
   389  		},
   390  	}, {
   391  		testName: "InvalidModulePath",
   392  		in: `
   393  moduleRegistries: "bad+module": {
   394  	registry: "foo.com"
   395  }
   396  `,
   397  		err: `invalid module path "bad\+module": invalid char '\+'`,
   398  	}, {
   399  		testName: "InvalidHost",
   400  		in: `
   401  moduleRegistries: "foo.example": {
   402  		registry: "badhost:"
   403  }
   404  `,
   405  		err: `invalid registry configuration in "foo.example": invalid host name "badhost:" in registry`,
   406  	}, {
   407  		testName: "InvalidRepository",
   408  		in: `
   409  moduleRegistries: "foo.example": {
   410  		registry: "ok.com/A"
   411  }
   412  `,
   413  		err: `invalid registry configuration in "foo.example": invalid reference syntax \("ok.com/A"\)`,
   414  	}, {
   415  		testName: "UnknownField",
   416  		in: `
   417  registiries: "foo.example": {
   418  		registry: "ok.com/A",
   419  }
   420  `,
   421  		err: `invalid configuration file: registiries: field not allowed`,
   422  	}, {
   423  		testName:        "MismatchedSecurity",
   424  		catchAllDefault: "c.example",
   425  		in: `
   426  moduleRegistries: {
   427  	"a.example": {
   428  		registry: "ok.com+insecure"
   429  	}
   430  	"b.example": {
   431  		registry: "ok.com"
   432  	}
   433  }
   434  `,
   435  		err: `registry host "ok.com" is specified both as secure and insecure`,
   436  	}, {
   437  		testName:        "StripPrefixWithNoRepo",
   438  		catchAllDefault: "c.example",
   439  		in: `
   440  moduleRegistries: {
   441  	"a.example/foo": {
   442  		registry: "foo.example"
   443  		stripPrefix: true
   444  	}
   445  }
   446  `,
   447  		err: `invalid registry configuration in "a.example/foo": use of stripPrefix requires a non-empty repository within the registry`,
   448  	}}
   449  
   450  	for _, tc := range testCases {
   451  		t.Run(tc.testName, func(t *testing.T) {
   452  			r, err := ParseConfig([]byte(tc.in), "somefile.cue", tc.catchAllDefault)
   453  			if tc.err != "" {
   454  				qt.Assert(t, qt.ErrorMatches(err, tc.err))
   455  				return
   456  			}
   457  			qt.Assert(t, qt.IsNil(err))
   458  			qt.Check(t, qt.DeepEquals(r.AllHosts(), tc.wantAllHosts))
   459  			testLookups(t, r, tc.lookups)
   460  		})
   461  	}
   462  }
   463  
   464  func testLookups(t *testing.T, r LocationResolver, lookups map[string]*Location) {
   465  	for key, want := range lookups {
   466  		t.Run(key, func(t *testing.T) {
   467  			m, v, _ := strings.Cut(key, " ")
   468  			got, ok := r.ResolveToLocation(m, v)
   469  			if want == nil {
   470  				qt.Assert(t, qt.IsFalse(ok))
   471  			} else {
   472  				qt.Assert(t, qt.DeepEquals(&got, want))
   473  			}
   474  		})
   475  	}
   476  }
   477  
   478  func hashOf(s string) string {
   479  	return fmt.Sprintf("%x", sha256.Sum256([]byte(s)))
   480  }
   481  

View as plain text