blob: 7b09998b4a00f45bc2cc41c930d3c1ff0de15853 [file] [log] [blame]
Julie Qiuedd14bf2020-07-09 23:55:17 -04001// Copyright 2019 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package postgres
6
7import (
8 "context"
Jonathan Amsterdameab19172021-03-11 09:36:33 -05009 "database/sql"
Julie Qiuedd14bf2020-07-09 23:55:17 -040010 "fmt"
11 "testing"
12
13 "github.com/google/go-cmp/cmp"
14 "golang.org/x/pkgsite/internal"
15 "golang.org/x/pkgsite/internal/source"
Jamal Carvalho0c5cd202020-07-15 19:43:08 -040016 "golang.org/x/pkgsite/internal/stdlib"
Julie Qiuedd14bf2020-07-09 23:55:17 -040017 "golang.org/x/pkgsite/internal/testing/sample"
18)
19
Jamal Carvalho0c5cd202020-07-15 19:43:08 -040020func TestGetVersions(t *testing.T) {
Jonathan Amsterdamf4412b42021-02-12 07:25:21 -050021 t.Parallel()
Jamal Carvalho0c5cd202020-07-15 19:43:08 -040022 var (
23 taggedAndPseudoModule = "path.to/foo"
24 taggedModuleV2 = "path.to/foo/v2"
25 taggedModuleV3 = "path.to/foo/v3"
26 pseudoModule = "golang.org/x/tools"
27 otherModule = "path.to/other"
28 incompatibleModule = "path.to/incompatible"
29 rootModule = "golang.org/foo/bar"
30 nestedModule = "golang.org/foo/bar/api"
31 testModules = []*internal.Module{
Julie Qiu1c1c6662020-10-25 23:07:59 -040032 sample.Module(stdlib.ModulePath, "v1.15.0-beta.1", "cmd/go"),
33 sample.Module(stdlib.ModulePath, "v1.14.6", "cmd/go"),
34 sample.Module(taggedModuleV3, "v3.2.0-beta", "bar"),
35 sample.Module(taggedModuleV3, "v3.2.0-alpha.2", "bar"),
36 sample.Module(taggedModuleV3, "v3.2.0-alpha.1", "bar"),
37 sample.Module(taggedModuleV3, "v3.1.0", "bar"),
38 sample.Module(taggedModuleV3, "v3.0.0", "bar"),
39 sample.Module(taggedModuleV2, "v2.0.1", "bar"),
40 sample.Module(taggedAndPseudoModule, "v1.5.3-pre1", "bar"),
41 sample.Module(taggedAndPseudoModule, "v1.5.2", "bar"),
42 sample.Module(taggedAndPseudoModule, "v0.0.0-20200101120000-000000000000", "bar"),
43 sample.Module(otherModule, "v3.0.0", "thing"),
44 sample.Module(incompatibleModule, "v2.0.0+incompatible", "module"),
45 sample.Module(incompatibleModule, "v0.0.0", "module"),
46 sample.Module(rootModule, "v1.0.3", "api"),
47 sample.Module(rootModule, "v0.11.6", "api"),
48 sample.Module(nestedModule, "v1.0.4", "api"),
49 sample.Module(nestedModule, "v1.0.3", "api"),
Jamal Carvalho0c5cd202020-07-15 19:43:08 -040050 }
51 )
52
53 // Add 12 pseudo versions to the test modules. Below we only
54 // expect to return the 10 most recent.
55 for i := 1; i <= 12; i++ {
Julie Qiu1c1c6662020-10-25 23:07:59 -040056 testModules = append(testModules, sample.Module(pseudoModule, fmt.Sprintf("v0.0.0-202001011200%02d-000000000000", i), "blog"))
Jamal Carvalho0c5cd202020-07-15 19:43:08 -040057 }
58
Jonathan Amsterdamf4412b42021-02-12 07:25:21 -050059 testDB, release := acquire(t)
60 defer release()
61 ctx, cancel := context.WithTimeout(context.Background(), testTimeout)
62 defer cancel()
63
Jamal Carvalho0c5cd202020-07-15 19:43:08 -040064 for _, m := range testModules {
Jonathan Amsterdamd5da0382021-04-07 14:54:25 -040065 goMod := "module " + m.ModulePath
66 if m.ModulePath == rootModule {
67 goMod = `
68 module golang.org/foo/bar // Deprecated: use other
69 retract v1.0.3 // security flaw
70 `
71 }
72 MustInsertModuleGoMod(ctx, t, testDB, m, goMod)
Jamal Carvalho0c5cd202020-07-15 19:43:08 -040073 }
74
75 stdModuleVersions := []*internal.ModuleInfo{
76 {
77 ModulePath: stdlib.ModulePath,
78 Version: "v1.15.0-beta.1",
79 },
80 {
81 ModulePath: stdlib.ModulePath,
82 Version: "v1.14.6",
83 },
84 }
85
86 fooModuleVersions := []*internal.ModuleInfo{
87 {
88 ModulePath: taggedModuleV3,
89 Version: "v3.2.0-beta",
90 },
91 {
92 ModulePath: taggedModuleV3,
93 Version: "v3.2.0-alpha.2",
94 },
95 {
96 ModulePath: taggedModuleV3,
97 Version: "v3.2.0-alpha.1",
98 },
99 {
100 ModulePath: taggedModuleV3,
101 Version: "v3.1.0",
102 },
103 {
104 ModulePath: taggedModuleV3,
105 Version: "v3.0.0",
106 },
107 {
108 ModulePath: taggedModuleV2,
109 Version: "v2.0.1",
110 },
111 {
112 ModulePath: taggedAndPseudoModule,
113 Version: "v1.5.3-pre1",
114 },
115 {
116 ModulePath: taggedAndPseudoModule,
117 Version: "v1.5.2",
118 },
119 }
120
121 testCases := []struct {
122 name, path string
123 want []*internal.ModuleInfo
124 }{
125 {
126 name: "std_module",
127 path: stdlib.ModulePath,
128 want: stdModuleVersions,
129 },
130 {
131 name: "stdlib_path",
132 path: "cmd",
133 want: stdModuleVersions,
134 },
135 {
136 name: "stdlib_package",
137 path: "cmd/go",
138 want: stdModuleVersions,
139 },
140 {
141 name: "want_tagged_versions_only",
142 path: "path.to/foo/bar",
143 want: fooModuleVersions,
144 },
145 {
146 name: "want_all_tagged_versions_at_v2_path",
147 path: "path.to/foo/v2/bar",
148 want: fooModuleVersions,
149 },
150 {
151 name: "want_pseudo_versions_only",
152 path: "golang.org/x/tools/blog",
153 want: func() []*internal.ModuleInfo {
154 versions := []*internal.ModuleInfo{}
155 // Expect the 10 most recent in DESC order
156 for i := 12; i > 2; i-- {
157 versions = append(versions, &internal.ModuleInfo{
158 ModulePath: pseudoModule,
159 Version: fmt.Sprintf("v0.0.0-202001011200%02d-000000000000", i),
160 })
161 }
162 return versions
163 }(),
164 },
165 {
166 name: "want_tagged_versions_includes_incompatible",
167 path: "path.to/incompatible/module",
168 want: []*internal.ModuleInfo{
169 {
170 ModulePath: incompatibleModule,
Miguel Aceroa1d3b212020-08-17 15:29:36 -0400171 Version: "v0.0.0",
Jamal Carvalho0c5cd202020-07-15 19:43:08 -0400172 },
173 {
174 ModulePath: incompatibleModule,
Miguel Aceroa1d3b212020-08-17 15:29:36 -0400175 Version: "v2.0.0+incompatible",
Jamal Carvalho0c5cd202020-07-15 19:43:08 -0400176 },
177 },
178 },
179 {
180 name: "nested_module_path",
181 path: "golang.org/foo/bar/api",
182 want: []*internal.ModuleInfo{
183 {
184 ModulePath: nestedModule,
185 Version: "v1.0.4",
186 },
187 {
188 ModulePath: nestedModule,
189 Version: "v1.0.3",
190 },
191 {
Jonathan Amsterdam32228ff2021-02-24 10:14:36 -0500192 ModulePath: rootModule,
193 Version: "v1.0.3",
194 Deprecated: true,
195 DeprecationComment: "use other",
196 Retracted: true,
197 RetractionRationale: "security flaw",
Jamal Carvalho0c5cd202020-07-15 19:43:08 -0400198 },
199 {
Jonathan Amsterdam32228ff2021-02-24 10:14:36 -0500200 ModulePath: rootModule,
201 Version: "v0.11.6",
202 Deprecated: true,
203 DeprecationComment: "use other",
Jamal Carvalho0c5cd202020-07-15 19:43:08 -0400204 },
205 },
206 },
207 {
Jonathan Amsterdam32228ff2021-02-24 10:14:36 -0500208 name: "root module path",
Jamal Carvalho0c5cd202020-07-15 19:43:08 -0400209 path: "golang.org/foo/bar",
210 want: []*internal.ModuleInfo{
211 {
Jonathan Amsterdam32228ff2021-02-24 10:14:36 -0500212 ModulePath: rootModule,
213 Version: "v1.0.3",
214 Deprecated: true,
215 DeprecationComment: "use other",
216 Retracted: true,
217 RetractionRationale: "security flaw",
Jamal Carvalho0c5cd202020-07-15 19:43:08 -0400218 },
219 {
Jonathan Amsterdam32228ff2021-02-24 10:14:36 -0500220 ModulePath: rootModule,
221 Version: "v0.11.6",
222 Deprecated: true,
223 DeprecationComment: "use other",
Jamal Carvalho0c5cd202020-07-15 19:43:08 -0400224 },
225 },
226 },
227 {
228 name: "want_zero_results_in_non_empty_db",
229 path: "not.a/real/path",
230 },
231 }
232
Julie Qiuff4efb12020-11-19 23:09:05 -0500233 for _, test := range testCases {
234 t.Run(test.name, func(t *testing.T) {
Julie Qiuff4efb12020-11-19 23:09:05 -0500235 for _, w := range test.want {
Jamal Carvalho0c5cd202020-07-15 19:43:08 -0400236 mod := sample.ModuleInfo(w.ModulePath, w.Version)
Jonathan Amsterdam32228ff2021-02-24 10:14:36 -0500237 w.CommitTime = mod.CommitTime
238 w.IsRedistributable = mod.IsRedistributable
239 w.HasGoMod = mod.HasGoMod
240 w.SourceInfo = mod.SourceInfo
Jamal Carvalho0c5cd202020-07-15 19:43:08 -0400241 }
242
Julie Qiuff4efb12020-11-19 23:09:05 -0500243 got, err := testDB.GetVersionsForPath(ctx, test.path)
Jamal Carvalho0c5cd202020-07-15 19:43:08 -0400244 if err != nil {
245 t.Fatal(err)
246 }
Jonathan Amsterdam32228ff2021-02-24 10:14:36 -0500247 if diff := cmp.Diff(test.want, got, cmp.AllowUnexported(source.Info{})); diff != "" {
Julie Qiuff4efb12020-11-19 23:09:05 -0500248 t.Errorf("testDB.GetVersionsForPath(%q) mismatch (-want +got):\n%s", test.path, diff)
Jamal Carvalho0c5cd202020-07-15 19:43:08 -0400249 }
250 })
251 }
252}
Jonathan Amsterdam67c71892020-10-28 06:59:47 -0400253
Jonathan Amsterdam96b5eb82020-12-22 15:50:13 -0500254func TestGetLatestInfo(t *testing.T) {
Jonathan Amsterdamf4412b42021-02-12 07:25:21 -0500255 t.Parallel()
256 testDB, release := acquire(t)
257 defer release()
Miguel Aceroa457b9e2020-08-26 16:56:55 -0400258 ctx, cancel := context.WithTimeout(context.Background(), testTimeout)
259 defer cancel()
Jonathan Amsterdam96b5eb82020-12-22 15:50:13 -0500260
Charlotte Brandhorst-Satzkorn69b3ffd2020-12-02 12:32:20 +0000261 for _, m := range []*internal.Module{
Jonathan Amsterdam668263d2021-04-07 08:50:53 -0400262 sample.Module("a.com/M", "v99.0.0+incompatible", "all", "most"),
Jonathan Amsterdam96b5eb82020-12-22 15:50:13 -0500263 sample.Module("a.com/M", "v1.1.1", "all", "most", "some", "one", "D/other"),
264 sample.Module("a.com/M", "v1.2.0", "all", "most"),
265 sample.Module("a.com/M/v2", "v2.0.5", "all", "most"),
266 sample.Module("a.com/M/v3", "v3.0.1", "all", "some"),
267 sample.Module("a.com/M/D", "v1.3.0", "other"),
Jonathan Amsterdam7b9ec912021-01-25 16:04:54 -0500268 sample.Module("b.com/M/v9", "v9.0.0", ""),
269 sample.Module("b.com/M/v10", "v10.0.0", ""),
Jonathan Amsterdambd41dbe2021-03-18 11:12:54 -0400270 sample.Module("gopkg.in/M.v1", "v1.0.0", ""),
271 sample.Module("gopkg.in/M.v2", "v2.0.0-pre", ""),
272 sample.Module("gopkg.in/M.v3", "v3.0.0-20200602140019-6ec2bf8d378b", ""),
273 sample.Module("c.com/M", "v0.0.0-20200602140019-6ec2bf8d378b", ""),
Miguel Aceroa457b9e2020-08-26 16:56:55 -0400274 } {
Jonathan Amsterdamd025eb32021-04-07 20:16:23 -0400275 MustInsertModule(ctx, t, testDB, m)
Miguel Aceroa457b9e2020-08-26 16:56:55 -0400276 }
277
Julie Qiuff4efb12020-11-19 23:09:05 -0500278 for _, test := range []struct {
Jonathan Amsterdam7b9ec912021-01-25 16:04:54 -0500279 unit, module string
280 want internal.LatestInfo
Miguel Aceroa457b9e2020-08-26 16:56:55 -0400281 }{
282 {
Jonathan Amsterdam96b5eb82020-12-22 15:50:13 -0500283 // A unit that is the module.
Jonathan Amsterdam7b9ec912021-01-25 16:04:54 -0500284 "a.com/M", "a.com/M",
285
Jonathan Amsterdam96b5eb82020-12-22 15:50:13 -0500286 internal.LatestInfo{
287 MinorVersion: "v1.2.0",
288 MinorModulePath: "a.com/M",
289 UnitExistsAtMinor: true,
290 MajorModulePath: "a.com/M/v3",
291 MajorUnitPath: "a.com/M/v3",
292 },
Miguel Aceroa457b9e2020-08-26 16:56:55 -0400293 },
294 {
Jonathan Amsterdam96b5eb82020-12-22 15:50:13 -0500295 // A unit that exists in all versions of the module.
Jonathan Amsterdam7b9ec912021-01-25 16:04:54 -0500296 "a.com/M/all", "a.com/M",
297
Jonathan Amsterdam96b5eb82020-12-22 15:50:13 -0500298 internal.LatestInfo{
299 MinorVersion: "v1.2.0",
300 MinorModulePath: "a.com/M",
301 UnitExistsAtMinor: true,
302 MajorModulePath: "a.com/M/v3",
303 MajorUnitPath: "a.com/M/v3/all",
304 },
Miguel Aceroa457b9e2020-08-26 16:56:55 -0400305 },
306 {
Jonathan Amsterdam96b5eb82020-12-22 15:50:13 -0500307 // A unit that exists in most versions, but not the latest major.
Jonathan Amsterdam7b9ec912021-01-25 16:04:54 -0500308 "a.com/M/most", "a.com/M",
309
Jonathan Amsterdam96b5eb82020-12-22 15:50:13 -0500310 internal.LatestInfo{
311 MinorVersion: "v1.2.0",
312 MinorModulePath: "a.com/M",
313 UnitExistsAtMinor: true,
314 MajorModulePath: "a.com/M/v3",
315 MajorUnitPath: "a.com/M/v3",
316 },
Miguel Aceroa457b9e2020-08-26 16:56:55 -0400317 },
Charlotte Brandhorst-Satzkorn69b3ffd2020-12-02 12:32:20 +0000318 {
Jonathan Amsterdam96b5eb82020-12-22 15:50:13 -0500319 // A unit that does not exist at the latest minor version, but does at the latest major.
Jonathan Amsterdam7b9ec912021-01-25 16:04:54 -0500320 "a.com/M/some", "a.com/M",
321
Jonathan Amsterdam96b5eb82020-12-22 15:50:13 -0500322 internal.LatestInfo{
323 MinorVersion: "v1.1.1",
324 MinorModulePath: "a.com/M",
325 UnitExistsAtMinor: false,
326 MajorModulePath: "a.com/M/v3",
327 MajorUnitPath: "a.com/M/v3/some",
328 },
Charlotte Brandhorst-Satzkorn69b3ffd2020-12-02 12:32:20 +0000329 },
330 {
Jonathan Amsterdam96b5eb82020-12-22 15:50:13 -0500331 // A unit that does not exist at the latest minor or major versions.
Jonathan Amsterdam7b9ec912021-01-25 16:04:54 -0500332 "a.com/M/one", "a.com/M",
333
Jonathan Amsterdam96b5eb82020-12-22 15:50:13 -0500334 internal.LatestInfo{
335 MinorVersion: "v1.1.1",
336 MinorModulePath: "a.com/M",
337 UnitExistsAtMinor: false,
338 MajorModulePath: "a.com/M/v3",
339 MajorUnitPath: "a.com/M/v3",
340 },
341 },
342 {
343 // A unit whose latest minor version is in a different module.
Jonathan Amsterdam7b9ec912021-01-25 16:04:54 -0500344 "a.com/M/D/other", "a.com/M",
345
Jonathan Amsterdam96b5eb82020-12-22 15:50:13 -0500346 internal.LatestInfo{
347 MinorVersion: "v1.3.0",
348 MinorModulePath: "a.com/M/D",
349 UnitExistsAtMinor: false,
350 MajorModulePath: "a.com/M/v3",
351 MajorUnitPath: "a.com/M/v3",
352 },
Charlotte Brandhorst-Satzkorn69b3ffd2020-12-02 12:32:20 +0000353 },
Jonathan Amsterdam7b9ec912021-01-25 16:04:54 -0500354 {
355 // A module with v9 and v10 versions.
356 "b.com/M/v9", "b.com/M/v9",
357 internal.LatestInfo{
358 MinorVersion: "v9.0.0",
359 MinorModulePath: "b.com/M/v9",
360 UnitExistsAtMinor: true,
361 MajorModulePath: "b.com/M/v10",
362 MajorUnitPath: "b.com/M/v10",
363 },
364 },
Jonathan Amsterdambd41dbe2021-03-18 11:12:54 -0400365 {
366 // gopkg.in module, highest version is not tagged
367 "gopkg.in/M.v1", "gopkg.in/M.v1",
368 internal.LatestInfo{
369 MinorVersion: "v1.0.0",
370 MinorModulePath: "gopkg.in/M.v1",
371 UnitExistsAtMinor: true,
372 MajorModulePath: "gopkg.in/M.v2",
373 MajorUnitPath: "gopkg.in/M.v2",
374 },
375 },
376 {
377 // no tagged versions
378 "c.com/M", "c.com/M",
379 internal.LatestInfo{
380 MinorVersion: "v0.0.0-20200602140019-6ec2bf8d378b",
381 MinorModulePath: "c.com/M",
382 UnitExistsAtMinor: true,
383 MajorModulePath: "",
384 MajorUnitPath: "",
385 },
386 },
Miguel Aceroa457b9e2020-08-26 16:56:55 -0400387 } {
Jonathan Amsterdam96b5eb82020-12-22 15:50:13 -0500388 t.Run(test.unit, func(t *testing.T) {
Jonathan Amsterdam23453442021-04-01 17:16:29 -0400389 got, err := testDB.GetLatestInfo(ctx, test.unit, test.module, nil)
Jonathan Amsterdam96b5eb82020-12-22 15:50:13 -0500390 if err != nil {
391 t.Fatal(err)
Miguel Aceroa457b9e2020-08-26 16:56:55 -0400392 }
Jonathan Amsterdam96b5eb82020-12-22 15:50:13 -0500393 if diff := cmp.Diff(test.want, got); diff != "" {
394 t.Errorf("mismatch (-want, +got):\n%s", diff)
Miguel Aceroa457b9e2020-08-26 16:56:55 -0400395 }
Jonathan Amsterdam96b5eb82020-12-22 15:50:13 -0500396 })
Jonathan Amsterdamc9f5aa42020-12-22 07:47:06 -0500397 }
398}
Jonathan Amsterdam0e2b9782021-02-23 09:41:34 -0500399
Jonathan Amsterdam38233972021-03-12 15:18:32 -0500400func TestRawIsMoreRecent(t *testing.T) {
Jonathan Amsterdam0e2b9782021-02-23 09:41:34 -0500401 for _, test := range []struct {
402 new, cur string
403 want bool
404 }{
405 {"v1.2.0", "v1.0.0", true},
406 {"v1.2.0", "v1.2.0", false},
407 {"v1.2.0", "v1.3.0", false},
408 {"v1.0.0", "v1.9.9-pre", true}, // release beats prerelease
409 {"v1.0.0", "v2.3.4+incompatible", true}, // compatible beats incompatible
410 } {
Jonathan Amsterdam38233972021-03-12 15:18:32 -0500411 got := rawIsMoreRecent(test.new, test.cur)
Jonathan Amsterdam0e2b9782021-02-23 09:41:34 -0500412 if got != test.want {
Jonathan Amsterdam38233972021-03-12 15:18:32 -0500413 t.Errorf("rawIsMoreRecent(%q, %q) = %t, want %t", test.new, test.cur, got, test.want)
Jonathan Amsterdam0e2b9782021-02-23 09:41:34 -0500414 }
415 }
416}
Jonathan Amsterdamb0b8b702021-02-24 14:56:41 -0500417
Jonathan Amsterdamc01b8192021-03-03 07:59:49 -0500418func TestGetLatestGoodVersion(t *testing.T) {
419 t.Parallel()
420 testDB, release := acquire(t)
421 defer release()
422 ctx := context.Background()
423
424 const (
425 modulePath = "example.com/m"
426 modFile = `
427 module example.com/m
428 retract v1.3.0
429 `
430 )
431
432 lmv, err := internal.NewLatestModuleVersions(modulePath, "", "", "", []byte(modFile))
433 if err != nil {
434 t.Fatal(err)
435 }
436 for _, v := range []string{"v2.0.0+incompatible", "v1.4.0-pre", "v1.3.0", "v1.2.0", "v1.1.0"} {
Jonathan Amsterdamd025eb32021-04-07 20:16:23 -0400437 MustInsertModule(ctx, t, testDB, sample.Module(modulePath, v, "pkg"))
Jonathan Amsterdamc01b8192021-03-03 07:59:49 -0500438 }
439
440 for _, test := range []struct {
441 name string
Jonathan Amsterdam38233972021-03-12 15:18:32 -0500442 cooked string // cooked latest version; empty means nil lmv
Jonathan Amsterdamc01b8192021-03-03 07:59:49 -0500443 want string
444 }{
445 {
446 name: "compatible",
447 cooked: "v1.3.0",
448 want: "v1.2.0", // v1.3.0 is retracted
449 },
450 {
451 name: "incompatible",
452 cooked: "v2.0.0+incompatible",
453 want: "v2.0.0+incompatible",
454 },
455 {
456 name: "bad", // cooked version not in modules table
457 cooked: "v1.4.0",
458 want: "v1.2.0",
459 },
Jonathan Amsterdam38233972021-03-12 15:18:32 -0500460 {
461 name: "nil",
462 cooked: "", // no latest-version info
463 want: "v2.0.0+incompatible",
464 },
Jonathan Amsterdamc01b8192021-03-03 07:59:49 -0500465 } {
466 t.Run(test.name, func(t *testing.T) {
Jonathan Amsterdam38233972021-03-12 15:18:32 -0500467 var lm *internal.LatestModuleVersions
468 if test.cooked != "" {
469 lmv.CookedVersion = test.cooked
470 lm = lmv
471 }
472 got, err := getLatestGoodVersion(ctx, testDB.db, modulePath, lm)
Jonathan Amsterdamc01b8192021-03-03 07:59:49 -0500473 if err != nil {
474 t.Fatal(err)
475 }
476 if got != test.want {
477 t.Errorf("got %q, want %q", got, test.want)
478 }
479 })
480 }
481}
482
483func TestLatestModuleVersions(t *testing.T) {
484 t.Parallel()
485 testDB, release := acquire(t)
486 defer release()
487 ctx := context.Background()
488
489 const (
490 modulePath = "example.com/m"
491 modFile = `
492 module example.com/m
493 retract v1.3.0
494 `
495 incompatible = "v2.0.0+incompatible"
496 )
497 modBytes := []byte(modFile)
498
Jonathan Amsterdamc01b8192021-03-03 07:59:49 -0500499 type versions struct {
Jonathan Amsterdam38233972021-03-12 15:18:32 -0500500 raw, cooked string
Jonathan Amsterdamc01b8192021-03-03 07:59:49 -0500501 }
502 // These tests form a sequence. Each test's want versions are in the DB for the next test.
503 for _, test := range []struct {
504 name string
505 in, want versions
506 }{
507 {
508 name: "initial",
Jonathan Amsterdam38233972021-03-12 15:18:32 -0500509 in: versions{"v1.3.0", "v1.2.0"},
510 want: versions{"v1.3.0", "v1.2.0"},
Jonathan Amsterdamc01b8192021-03-03 07:59:49 -0500511 },
512 {
513 name: "older", // older incoming info doesn't cause an update
Jonathan Amsterdam38233972021-03-12 15:18:32 -0500514 in: versions{"v1.2.0", "v1.2.0"},
515 want: versions{"v1.3.0", "v1.2.0"},
Jonathan Amsterdamc01b8192021-03-03 07:59:49 -0500516 },
517 {
518 name: "newer bad", // a newer version, not in modules table
Jonathan Amsterdam38233972021-03-12 15:18:32 -0500519 in: versions{"v1.4.0", "v1.4.0"},
520 want: versions{"v1.4.0", "v1.4.0"},
Jonathan Amsterdamc01b8192021-03-03 07:59:49 -0500521 },
522 {
523 name: "incompatible",
Jonathan Amsterdam38233972021-03-12 15:18:32 -0500524 in: versions{incompatible, incompatible},
525 want: versions{incompatible, incompatible},
Jonathan Amsterdamc01b8192021-03-03 07:59:49 -0500526 },
527 {
528 name: "compatible", // "downgrade" from incompatible to compatible will update
Jonathan Amsterdam38233972021-03-12 15:18:32 -0500529 in: versions{"v1.3.0", "v1.2.0"},
530 want: versions{"v1.3.0", "v1.2.0"},
Jonathan Amsterdamc01b8192021-03-03 07:59:49 -0500531 },
532 } {
533 t.Run(test.name, func(t *testing.T) {
534 vNew, err := internal.NewLatestModuleVersions(modulePath, test.in.raw, test.in.cooked, "", modBytes)
535 if err != nil {
536 t.Fatal(err)
537 }
Jonathan Amsterdam38233972021-03-12 15:18:32 -0500538 vGot, err := testDB.UpdateLatestModuleVersions(ctx, vNew)
Jonathan Amsterdamc01b8192021-03-03 07:59:49 -0500539 if err != nil {
540 t.Fatal(err)
541 }
542 if vGot == nil {
543 t.Fatal("got nothing")
544 }
Jonathan Amsterdam38233972021-03-12 15:18:32 -0500545 got := versions{vGot.RawVersion, vGot.CookedVersion}
Jonathan Amsterdamc01b8192021-03-03 07:59:49 -0500546 if got != test.want {
547 t.Fatalf("got %q, want %q", got, test.want)
548 }
549 })
550 }
551}
Jonathan Amsterdameab19172021-03-11 09:36:33 -0500552
553func TestLatestModuleVersionsBadStatus(t *testing.T) {
554 t.Parallel()
555 testDB, release := acquire(t)
556 defer release()
557 ctx := context.Background()
558
559 const modulePath = "example.com/m"
560
561 getStatus := func() int {
562 var s int
563 err := testDB.db.QueryRow(ctx, `
564 SELECT status
565 FROM latest_module_versions l
566 INNER JOIN paths p ON (p.id=l.module_path_id)
567 WHERE p.path = $1`,
568 modulePath).Scan(&s)
569 if err != nil && err != sql.ErrNoRows {
570 t.Fatal(err)
571 }
572 return s
573 }
574
575 // Insert a failure status.
576 newStatus := 410
577 if err := testDB.UpdateLatestModuleVersionsStatus(ctx, modulePath, newStatus); err != nil {
578 t.Fatal(err)
579 }
580 if got := getStatus(); got != newStatus {
581 t.Errorf("got %d, want %d", got, newStatus)
582 }
583
584 // GetLatestModuleVersions should return nil.
585 got, err := testDB.GetLatestModuleVersions(ctx, modulePath)
586 if err != nil {
587 t.Fatal(err)
588 }
589 if got != nil {
590 t.Errorf("got %v, want nil", got)
591 }
592
593 // A new failure status should overwrite.
594 newStatus = 404
595 if err := testDB.UpdateLatestModuleVersionsStatus(ctx, modulePath, newStatus); err != nil {
596 t.Fatal(err)
597 }
598 if got := getStatus(); got != newStatus {
599 t.Errorf("got %d, want %d", got, newStatus)
600 }
601
602 // Good information overwrites.
603 lmv, err := internal.NewLatestModuleVersions(modulePath, "v1.2.3", "v1.2.3", "", []byte(`module example.com/m`))
604 if err != nil {
605 t.Fatal(err)
606 }
Jonathan Amsterdam38233972021-03-12 15:18:32 -0500607 if _, err := testDB.UpdateLatestModuleVersions(ctx, lmv); err != nil {
Jonathan Amsterdameab19172021-03-11 09:36:33 -0500608 t.Fatal(err)
609 }
610 if got := getStatus(); got != 200 {
611 t.Errorf("got %d, want %d", got, 200)
612 }
613
614 // Once we have good information, a bad status won't remove it.
615 if err := testDB.UpdateLatestModuleVersionsStatus(ctx, modulePath, 500); err != nil {
616 t.Fatal(err)
617 }
618 got, err = testDB.GetLatestModuleVersions(ctx, modulePath)
619 if err != nil {
620 t.Fatal(err)
621 }
622 if got == nil {
623 t.Error("got nil, want non-nil")
624 }
625}
Jonathan Amsterdam02c6e842021-03-19 13:28:36 -0400626
627func TestLatestModuleVersionsGood(t *testing.T) {
628 // Verify that the good latest version is updated properly.
629 t.Parallel()
630 testDB, release := acquire(t)
631 defer release()
632 ctx := context.Background()
633
634 const modulePath = "example.com/m"
635
636 check := func(want string) {
637 t.Helper()
638 got, err := testDB.GetLatestModuleVersions(ctx, modulePath)
639 if err != nil {
640 t.Fatal(err)
641 }
642 if got.GoodVersion != want {
643 t.Fatalf("got %q, want %q", got.GoodVersion, want)
644 }
645 }
646
647 // Add two good versions.
Jonathan Amsterdamd189d9d2021-04-07 19:59:52 -0400648 const v1 = "v1.1.0"
Jonathan Amsterdamd025eb32021-04-07 20:16:23 -0400649 MustInsertModule(ctx, t, testDB, sample.Module(modulePath, v1, "pkg"))
Jonathan Amsterdam02c6e842021-03-19 13:28:36 -0400650 check(v1)
651
652 // Good version should be updated.
Jonathan Amsterdamd189d9d2021-04-07 19:59:52 -0400653 const v2 = "v1.2.0"
Jonathan Amsterdamd025eb32021-04-07 20:16:23 -0400654 MustInsertModule(ctx, t, testDB, sample.Module(modulePath, v2, "pkg"))
Jonathan Amsterdam02c6e842021-03-19 13:28:36 -0400655 check(v2)
656
Jonathan Amsterdamd189d9d2021-04-07 19:59:52 -0400657 // New latest-version info retracts v2 (and itself); good version should switch to v1.
658 MustInsertModuleGoMod(ctx, t, testDB, sample.Module(modulePath, "v1.3.0", "pkg"), fmt.Sprintf(`
659 module %s
660 retract v1.3.0
661 retract %s
662 `, modulePath, v2))
Jonathan Amsterdam02c6e842021-03-19 13:28:36 -0400663 check(v1)
664}