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