cuelang.org/go@v0.10.1/internal/mod/modrequirements/requirements_test.go (about)

     1  package modrequirements
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"testing"
     7  
     8  	"cuelabs.dev/go/oci/ociregistry/ociclient"
     9  	"github.com/go-quicktest/qt"
    10  	"golang.org/x/tools/txtar"
    11  
    12  	"cuelang.org/go/internal/mod/mvs"
    13  	"cuelang.org/go/internal/registrytest"
    14  	"cuelang.org/go/mod/modfile"
    15  	"cuelang.org/go/mod/modregistry"
    16  	"cuelang.org/go/mod/module"
    17  )
    18  
    19  func TestRequirements(t *testing.T) {
    20  	const registryContents = `
    21  -- example.com_v0.0.1/cue.mod/module.cue --
    22  module: "example.com@v0"
    23  language: version: "v0.8.0"
    24  deps: {
    25  	"foo.com/bar/hello@v0": v: "v0.2.3"
    26  	"bar.com@v0": v: "v0.5.0"
    27  }
    28  
    29  -- foo.com_bar_hello_v0.2.3/cue.mod/module.cue --
    30  module: "foo.com/bar/hello@v0"
    31  language: version: "v0.8.0"
    32  deps: {
    33  	"bar.com@v0": v: "v0.0.2"
    34  	"baz.org@v0": v: "v0.10.1"
    35  }
    36  
    37  -- bar.com_v0.0.2/cue.mod/module.cue --
    38  module: "bar.com@v0"
    39  language: version: "v0.8.0"
    40  deps: "baz.org@v0": v: "v0.0.2"
    41  
    42  -- bar.com_v0.5.0/cue.mod/module.cue --
    43  module: "bar.com@v0"
    44  language: version: "v0.8.0"
    45  deps: "baz.org@v0": v: "v0.5.0"
    46  
    47  -- baz.org_v0.0.2/cue.mod/module.cue --
    48  module: "baz.org@v0"
    49  language: version: "v0.8.0"
    50  
    51  -- baz.org_v0.1.2/cue.mod/module.cue --
    52  module: "baz.org@v0"
    53  language: version: "v0.8.0"
    54  
    55  -- baz.org_v0.5.0/cue.mod/module.cue --
    56  module: "baz.org@v0"
    57  language: version: "v0.8.0"
    58  
    59  -- baz.org_v0.10.1/cue.mod/module.cue --
    60  module: "baz.org@v0"
    61  language: version: "v0.8.0"
    62  `
    63  
    64  	ctx := context.Background()
    65  	reg := newRegistry(t, registryContents)
    66  
    67  	rootVersion := mustParseVersion("example.com@v0")
    68  
    69  	rs := NewRequirements(rootVersion.Path(), reg, versions("foo.com/bar/hello@v0.2.3"), nil)
    70  
    71  	v, ok := rs.RootSelected(rootVersion.Path())
    72  	qt.Assert(t, qt.IsTrue(ok))
    73  	qt.Assert(t, qt.Equals(v, ""))
    74  
    75  	v, ok = rs.RootSelected("foo.com/bar/hello@v0")
    76  	qt.Assert(t, qt.IsTrue(ok))
    77  	qt.Assert(t, qt.Equals(v, "v0.2.3"))
    78  
    79  	// Other parts of the graph aren't loaded yet.
    80  	v, ok = rs.RootSelected("bar.com@v0")
    81  	qt.Assert(t, qt.IsFalse(ok))
    82  	qt.Assert(t, qt.Equals(v, ""))
    83  
    84  	mg, err := rs.Graph(ctx)
    85  	qt.Assert(t, qt.IsNil(err))
    86  	_ = mg
    87  	rv, ok := mg.RequiredBy(rootVersion)
    88  	qt.Assert(t, qt.Equals(ok, true))
    89  	qt.Assert(t, qt.DeepEquals(rv, []module.Version{
    90  		module.MustParseVersion("foo.com/bar/hello@v0.2.3"),
    91  	}))
    92  	rv, ok = mg.RequiredBy(module.MustParseVersion("foo.com/bar/hello@v0.2.3"))
    93  	qt.Assert(t, qt.Equals(ok, true))
    94  	qt.Assert(t, qt.DeepEquals(rv, versions("bar.com@v0.0.2", "baz.org@v0.10.1")))
    95  
    96  	qt.Assert(t, qt.DeepEquals(mg.BuildList(), versions(
    97  		"example.com@v0",
    98  		"bar.com@v0.0.2",
    99  		"baz.org@v0.10.1",
   100  		"foo.com/bar/hello@v0.2.3",
   101  	)))
   102  }
   103  
   104  func TestRequirementsErrorFromMissingModule(t *testing.T) {
   105  	const registryContents = `
   106  -- example.com_v0.0.1/cue.mod/module.cue --
   107  module: "example.com@v0"
   108  language: version: "v0.8.0"
   109  deps: "foo.com/bar/hello@v0": v: "v0.2.3"
   110  
   111  -- foo.com_bar_hello_v0.2.3/cue.mod/module.cue --
   112  module: "foo.com/bar/hello@v0"
   113  language: version: "v0.8.0"
   114  deps: "bar.com@v0": v: "v0.0.2"	// doesn't exist
   115  `
   116  	ctx := context.Background()
   117  	reg := newRegistry(t, registryContents)
   118  
   119  	rootVersion := mustParseVersion("example.com@v0")
   120  	rs := NewRequirements(rootVersion.Path(), reg, versions(
   121  		"bar.com@v0.0.2",
   122  		"foo.com/bar/hello@v0.2.3",
   123  	), nil)
   124  	_, err := rs.Graph(ctx)
   125  	qt.Assert(t, qt.ErrorMatches(err, `bar.com@v0.0.2: module bar.com@v0.0.2: module not found`))
   126  	qt.Assert(t, qt.ErrorAs(err, new(*mvs.BuildListError[module.Version])))
   127  }
   128  
   129  func TestRequirementsWithDefaultMajorVersions(t *testing.T) {
   130  	rs := NewRequirements("example.com@v0", nil, versions(
   131  		"bar.com@v0.0.2",
   132  		"bar.com@v1.2.3",
   133  		"bar.com@v2.0.1",
   134  		"baz.org@v0.10.1",
   135  		"baz.org@v1.2.3",
   136  		"foo.com/bar/hello@v0.2.3",
   137  	), map[string]string{
   138  		"bar.com": "v1",
   139  	})
   140  	qt.Assert(t, qt.DeepEquals(rs.DefaultMajorVersions(), map[string]string{
   141  		"bar.com": "v1",
   142  	}))
   143  	tests := []struct {
   144  		mpath       string
   145  		wantVersion string
   146  		wantStatus  MajorVersionDefaultStatus
   147  	}{{
   148  		mpath:       "bar.com",
   149  		wantVersion: "v1",
   150  		wantStatus:  ExplicitDefault,
   151  	}, {
   152  		mpath:      "baz.org",
   153  		wantStatus: AmbiguousDefault,
   154  	}, {
   155  		mpath:       "foo.com/bar/hello",
   156  		wantVersion: "v0",
   157  		wantStatus:  NonExplicitDefault,
   158  	}, {
   159  		mpath:       "other.com",
   160  		wantVersion: "",
   161  		wantStatus:  NoDefault,
   162  	}}
   163  	for _, test := range tests {
   164  		t.Run("", func(t *testing.T) {
   165  			v, st := rs.DefaultMajorVersion(test.mpath)
   166  			qt.Check(t, qt.Equals(v, test.wantVersion))
   167  			qt.Check(t, qt.Equals(st, test.wantStatus))
   168  		})
   169  	}
   170  
   171  }
   172  
   173  type registryImpl struct {
   174  	reg *modregistry.Client
   175  }
   176  
   177  var _ Registry = (*registryImpl)(nil)
   178  
   179  func (r *registryImpl) Requirements(ctx context.Context, mv module.Version) ([]module.Version, error) {
   180  	m, err := r.reg.GetModule(ctx, mv)
   181  	if err != nil {
   182  		return nil, err
   183  	}
   184  	data, err := m.ModuleFile(ctx)
   185  	if err != nil {
   186  		return nil, fmt.Errorf("cannot get module file from %v: %v", m, err)
   187  	}
   188  	mf, err := modfile.Parse(data, mv.String())
   189  	if err != nil {
   190  		return nil, fmt.Errorf("cannot parse module file from %v: %v", m, err)
   191  	}
   192  	return mf.DepVersions(), nil
   193  }
   194  
   195  func versions(vs ...string) []module.Version {
   196  	mvs := make([]module.Version, len(vs))
   197  	for i, v := range vs {
   198  		mvs[i] = mustParseVersion(v)
   199  	}
   200  	return mvs
   201  }
   202  
   203  // mustParseVersion is like module.MustParseVersion except
   204  // that it accepts non-versioned modules too, e.g. foo.com@v0.
   205  func mustParseVersion(s string) module.Version {
   206  	if v, err := module.ParseVersion(s); err == nil {
   207  		return v
   208  	}
   209  	v, err := module.NewVersion(s, "")
   210  	if err != nil {
   211  		panic(err)
   212  	}
   213  	return v
   214  }
   215  
   216  func newRegistry(t *testing.T, registryContents string) Registry {
   217  	regFS, err := txtar.FS(txtar.Parse([]byte(registryContents)))
   218  	qt.Assert(t, qt.IsNil(err))
   219  	regSrv, err := registrytest.New(regFS, "")
   220  	qt.Assert(t, qt.IsNil(err))
   221  	t.Cleanup(regSrv.Close)
   222  	regOCI, err := ociclient.New(regSrv.Host(), &ociclient.Options{
   223  		Insecure: true,
   224  	})
   225  	qt.Assert(t, qt.IsNil(err))
   226  	return &registryImpl{modregistry.NewClient(regOCI)}
   227  }