Julie Qiu | edd14bf | 2020-07-09 23:55:17 -0400 | [diff] [blame] | 1 | // 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 | |
| 5 | package postgres |
| 6 | |
| 7 | import ( |
| 8 | "context" |
Jonathan Amsterdam | eab1917 | 2021-03-11 09:36:33 -0500 | [diff] [blame] | 9 | "database/sql" |
Julie Qiu | edd14bf | 2020-07-09 23:55:17 -0400 | [diff] [blame] | 10 | "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 Carvalho | 0c5cd20 | 2020-07-15 19:43:08 -0400 | [diff] [blame] | 16 | "golang.org/x/pkgsite/internal/stdlib" |
Julie Qiu | edd14bf | 2020-07-09 23:55:17 -0400 | [diff] [blame] | 17 | "golang.org/x/pkgsite/internal/testing/sample" |
| 18 | ) |
| 19 | |
Jamal Carvalho | 0c5cd20 | 2020-07-15 19:43:08 -0400 | [diff] [blame] | 20 | func TestGetVersions(t *testing.T) { |
Jonathan Amsterdam | f4412b4 | 2021-02-12 07:25:21 -0500 | [diff] [blame] | 21 | t.Parallel() |
Jamal Carvalho | 0c5cd20 | 2020-07-15 19:43:08 -0400 | [diff] [blame] | 22 | 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 Qiu | 1c1c666 | 2020-10-25 23:07:59 -0400 | [diff] [blame] | 32 | 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 Carvalho | 0c5cd20 | 2020-07-15 19:43:08 -0400 | [diff] [blame] | 50 | } |
| 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 Qiu | 1c1c666 | 2020-10-25 23:07:59 -0400 | [diff] [blame] | 56 | testModules = append(testModules, sample.Module(pseudoModule, fmt.Sprintf("v0.0.0-202001011200%02d-000000000000", i), "blog")) |
Jamal Carvalho | 0c5cd20 | 2020-07-15 19:43:08 -0400 | [diff] [blame] | 57 | } |
| 58 | |
Jonathan Amsterdam | f4412b4 | 2021-02-12 07:25:21 -0500 | [diff] [blame] | 59 | testDB, release := acquire(t) |
| 60 | defer release() |
| 61 | ctx, cancel := context.WithTimeout(context.Background(), testTimeout) |
| 62 | defer cancel() |
| 63 | |
Jamal Carvalho | 0c5cd20 | 2020-07-15 19:43:08 -0400 | [diff] [blame] | 64 | for _, m := range testModules { |
Jonathan Amsterdam | d5da038 | 2021-04-07 14:54:25 -0400 | [diff] [blame] | 65 | 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 Carvalho | 0c5cd20 | 2020-07-15 19:43:08 -0400 | [diff] [blame] | 73 | } |
| 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 Acero | a1d3b21 | 2020-08-17 15:29:36 -0400 | [diff] [blame] | 171 | Version: "v0.0.0", |
Jamal Carvalho | 0c5cd20 | 2020-07-15 19:43:08 -0400 | [diff] [blame] | 172 | }, |
| 173 | { |
| 174 | ModulePath: incompatibleModule, |
Miguel Acero | a1d3b21 | 2020-08-17 15:29:36 -0400 | [diff] [blame] | 175 | Version: "v2.0.0+incompatible", |
Jamal Carvalho | 0c5cd20 | 2020-07-15 19:43:08 -0400 | [diff] [blame] | 176 | }, |
| 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 Amsterdam | 32228ff | 2021-02-24 10:14:36 -0500 | [diff] [blame] | 192 | ModulePath: rootModule, |
| 193 | Version: "v1.0.3", |
| 194 | Deprecated: true, |
| 195 | DeprecationComment: "use other", |
| 196 | Retracted: true, |
| 197 | RetractionRationale: "security flaw", |
Jamal Carvalho | 0c5cd20 | 2020-07-15 19:43:08 -0400 | [diff] [blame] | 198 | }, |
| 199 | { |
Jonathan Amsterdam | 32228ff | 2021-02-24 10:14:36 -0500 | [diff] [blame] | 200 | ModulePath: rootModule, |
| 201 | Version: "v0.11.6", |
| 202 | Deprecated: true, |
| 203 | DeprecationComment: "use other", |
Jamal Carvalho | 0c5cd20 | 2020-07-15 19:43:08 -0400 | [diff] [blame] | 204 | }, |
| 205 | }, |
| 206 | }, |
| 207 | { |
Jonathan Amsterdam | 32228ff | 2021-02-24 10:14:36 -0500 | [diff] [blame] | 208 | name: "root module path", |
Jamal Carvalho | 0c5cd20 | 2020-07-15 19:43:08 -0400 | [diff] [blame] | 209 | path: "golang.org/foo/bar", |
| 210 | want: []*internal.ModuleInfo{ |
| 211 | { |
Jonathan Amsterdam | 32228ff | 2021-02-24 10:14:36 -0500 | [diff] [blame] | 212 | ModulePath: rootModule, |
| 213 | Version: "v1.0.3", |
| 214 | Deprecated: true, |
| 215 | DeprecationComment: "use other", |
| 216 | Retracted: true, |
| 217 | RetractionRationale: "security flaw", |
Jamal Carvalho | 0c5cd20 | 2020-07-15 19:43:08 -0400 | [diff] [blame] | 218 | }, |
| 219 | { |
Jonathan Amsterdam | 32228ff | 2021-02-24 10:14:36 -0500 | [diff] [blame] | 220 | ModulePath: rootModule, |
| 221 | Version: "v0.11.6", |
| 222 | Deprecated: true, |
| 223 | DeprecationComment: "use other", |
Jamal Carvalho | 0c5cd20 | 2020-07-15 19:43:08 -0400 | [diff] [blame] | 224 | }, |
| 225 | }, |
| 226 | }, |
| 227 | { |
| 228 | name: "want_zero_results_in_non_empty_db", |
| 229 | path: "not.a/real/path", |
| 230 | }, |
| 231 | } |
| 232 | |
Julie Qiu | ff4efb1 | 2020-11-19 23:09:05 -0500 | [diff] [blame] | 233 | for _, test := range testCases { |
| 234 | t.Run(test.name, func(t *testing.T) { |
Julie Qiu | ff4efb1 | 2020-11-19 23:09:05 -0500 | [diff] [blame] | 235 | for _, w := range test.want { |
Jamal Carvalho | 0c5cd20 | 2020-07-15 19:43:08 -0400 | [diff] [blame] | 236 | mod := sample.ModuleInfo(w.ModulePath, w.Version) |
Jonathan Amsterdam | 32228ff | 2021-02-24 10:14:36 -0500 | [diff] [blame] | 237 | w.CommitTime = mod.CommitTime |
| 238 | w.IsRedistributable = mod.IsRedistributable |
| 239 | w.HasGoMod = mod.HasGoMod |
| 240 | w.SourceInfo = mod.SourceInfo |
Jamal Carvalho | 0c5cd20 | 2020-07-15 19:43:08 -0400 | [diff] [blame] | 241 | } |
| 242 | |
Julie Qiu | ff4efb1 | 2020-11-19 23:09:05 -0500 | [diff] [blame] | 243 | got, err := testDB.GetVersionsForPath(ctx, test.path) |
Jamal Carvalho | 0c5cd20 | 2020-07-15 19:43:08 -0400 | [diff] [blame] | 244 | if err != nil { |
| 245 | t.Fatal(err) |
| 246 | } |
Jonathan Amsterdam | 32228ff | 2021-02-24 10:14:36 -0500 | [diff] [blame] | 247 | if diff := cmp.Diff(test.want, got, cmp.AllowUnexported(source.Info{})); diff != "" { |
Julie Qiu | ff4efb1 | 2020-11-19 23:09:05 -0500 | [diff] [blame] | 248 | t.Errorf("testDB.GetVersionsForPath(%q) mismatch (-want +got):\n%s", test.path, diff) |
Jamal Carvalho | 0c5cd20 | 2020-07-15 19:43:08 -0400 | [diff] [blame] | 249 | } |
| 250 | }) |
| 251 | } |
| 252 | } |
Jonathan Amsterdam | 67c7189 | 2020-10-28 06:59:47 -0400 | [diff] [blame] | 253 | |
Jonathan Amsterdam | 96b5eb8 | 2020-12-22 15:50:13 -0500 | [diff] [blame] | 254 | func TestGetLatestInfo(t *testing.T) { |
Jonathan Amsterdam | f4412b4 | 2021-02-12 07:25:21 -0500 | [diff] [blame] | 255 | t.Parallel() |
| 256 | testDB, release := acquire(t) |
| 257 | defer release() |
Miguel Acero | a457b9e | 2020-08-26 16:56:55 -0400 | [diff] [blame] | 258 | ctx, cancel := context.WithTimeout(context.Background(), testTimeout) |
| 259 | defer cancel() |
Jonathan Amsterdam | 96b5eb8 | 2020-12-22 15:50:13 -0500 | [diff] [blame] | 260 | |
Charlotte Brandhorst-Satzkorn | 69b3ffd | 2020-12-02 12:32:20 +0000 | [diff] [blame] | 261 | for _, m := range []*internal.Module{ |
Jonathan Amsterdam | 668263d | 2021-04-07 08:50:53 -0400 | [diff] [blame] | 262 | sample.Module("a.com/M", "v99.0.0+incompatible", "all", "most"), |
Jonathan Amsterdam | 96b5eb8 | 2020-12-22 15:50:13 -0500 | [diff] [blame] | 263 | 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 Amsterdam | 7b9ec91 | 2021-01-25 16:04:54 -0500 | [diff] [blame] | 268 | sample.Module("b.com/M/v9", "v9.0.0", ""), |
| 269 | sample.Module("b.com/M/v10", "v10.0.0", ""), |
Jonathan Amsterdam | bd41dbe | 2021-03-18 11:12:54 -0400 | [diff] [blame] | 270 | 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 Acero | a457b9e | 2020-08-26 16:56:55 -0400 | [diff] [blame] | 274 | } { |
Jonathan Amsterdam | d025eb3 | 2021-04-07 20:16:23 -0400 | [diff] [blame] | 275 | MustInsertModule(ctx, t, testDB, m) |
Miguel Acero | a457b9e | 2020-08-26 16:56:55 -0400 | [diff] [blame] | 276 | } |
| 277 | |
Julie Qiu | ff4efb1 | 2020-11-19 23:09:05 -0500 | [diff] [blame] | 278 | for _, test := range []struct { |
Jonathan Amsterdam | 7b9ec91 | 2021-01-25 16:04:54 -0500 | [diff] [blame] | 279 | unit, module string |
| 280 | want internal.LatestInfo |
Miguel Acero | a457b9e | 2020-08-26 16:56:55 -0400 | [diff] [blame] | 281 | }{ |
| 282 | { |
Jonathan Amsterdam | 96b5eb8 | 2020-12-22 15:50:13 -0500 | [diff] [blame] | 283 | // A unit that is the module. |
Jonathan Amsterdam | 7b9ec91 | 2021-01-25 16:04:54 -0500 | [diff] [blame] | 284 | "a.com/M", "a.com/M", |
| 285 | |
Jonathan Amsterdam | 96b5eb8 | 2020-12-22 15:50:13 -0500 | [diff] [blame] | 286 | 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 Acero | a457b9e | 2020-08-26 16:56:55 -0400 | [diff] [blame] | 293 | }, |
| 294 | { |
Jonathan Amsterdam | 96b5eb8 | 2020-12-22 15:50:13 -0500 | [diff] [blame] | 295 | // A unit that exists in all versions of the module. |
Jonathan Amsterdam | 7b9ec91 | 2021-01-25 16:04:54 -0500 | [diff] [blame] | 296 | "a.com/M/all", "a.com/M", |
| 297 | |
Jonathan Amsterdam | 96b5eb8 | 2020-12-22 15:50:13 -0500 | [diff] [blame] | 298 | 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 Acero | a457b9e | 2020-08-26 16:56:55 -0400 | [diff] [blame] | 305 | }, |
| 306 | { |
Jonathan Amsterdam | 96b5eb8 | 2020-12-22 15:50:13 -0500 | [diff] [blame] | 307 | // A unit that exists in most versions, but not the latest major. |
Jonathan Amsterdam | 7b9ec91 | 2021-01-25 16:04:54 -0500 | [diff] [blame] | 308 | "a.com/M/most", "a.com/M", |
| 309 | |
Jonathan Amsterdam | 96b5eb8 | 2020-12-22 15:50:13 -0500 | [diff] [blame] | 310 | 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 Acero | a457b9e | 2020-08-26 16:56:55 -0400 | [diff] [blame] | 317 | }, |
Charlotte Brandhorst-Satzkorn | 69b3ffd | 2020-12-02 12:32:20 +0000 | [diff] [blame] | 318 | { |
Jonathan Amsterdam | 96b5eb8 | 2020-12-22 15:50:13 -0500 | [diff] [blame] | 319 | // A unit that does not exist at the latest minor version, but does at the latest major. |
Jonathan Amsterdam | 7b9ec91 | 2021-01-25 16:04:54 -0500 | [diff] [blame] | 320 | "a.com/M/some", "a.com/M", |
| 321 | |
Jonathan Amsterdam | 96b5eb8 | 2020-12-22 15:50:13 -0500 | [diff] [blame] | 322 | 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-Satzkorn | 69b3ffd | 2020-12-02 12:32:20 +0000 | [diff] [blame] | 329 | }, |
| 330 | { |
Jonathan Amsterdam | 96b5eb8 | 2020-12-22 15:50:13 -0500 | [diff] [blame] | 331 | // A unit that does not exist at the latest minor or major versions. |
Jonathan Amsterdam | 7b9ec91 | 2021-01-25 16:04:54 -0500 | [diff] [blame] | 332 | "a.com/M/one", "a.com/M", |
| 333 | |
Jonathan Amsterdam | 96b5eb8 | 2020-12-22 15:50:13 -0500 | [diff] [blame] | 334 | 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 Amsterdam | 7b9ec91 | 2021-01-25 16:04:54 -0500 | [diff] [blame] | 344 | "a.com/M/D/other", "a.com/M", |
| 345 | |
Jonathan Amsterdam | 96b5eb8 | 2020-12-22 15:50:13 -0500 | [diff] [blame] | 346 | 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-Satzkorn | 69b3ffd | 2020-12-02 12:32:20 +0000 | [diff] [blame] | 353 | }, |
Jonathan Amsterdam | 7b9ec91 | 2021-01-25 16:04:54 -0500 | [diff] [blame] | 354 | { |
| 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 Amsterdam | bd41dbe | 2021-03-18 11:12:54 -0400 | [diff] [blame] | 365 | { |
| 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 Acero | a457b9e | 2020-08-26 16:56:55 -0400 | [diff] [blame] | 387 | } { |
Jonathan Amsterdam | 96b5eb8 | 2020-12-22 15:50:13 -0500 | [diff] [blame] | 388 | t.Run(test.unit, func(t *testing.T) { |
Jonathan Amsterdam | 2345344 | 2021-04-01 17:16:29 -0400 | [diff] [blame] | 389 | got, err := testDB.GetLatestInfo(ctx, test.unit, test.module, nil) |
Jonathan Amsterdam | 96b5eb8 | 2020-12-22 15:50:13 -0500 | [diff] [blame] | 390 | if err != nil { |
| 391 | t.Fatal(err) |
Miguel Acero | a457b9e | 2020-08-26 16:56:55 -0400 | [diff] [blame] | 392 | } |
Jonathan Amsterdam | 96b5eb8 | 2020-12-22 15:50:13 -0500 | [diff] [blame] | 393 | if diff := cmp.Diff(test.want, got); diff != "" { |
| 394 | t.Errorf("mismatch (-want, +got):\n%s", diff) |
Miguel Acero | a457b9e | 2020-08-26 16:56:55 -0400 | [diff] [blame] | 395 | } |
Jonathan Amsterdam | 96b5eb8 | 2020-12-22 15:50:13 -0500 | [diff] [blame] | 396 | }) |
Jonathan Amsterdam | c9f5aa4 | 2020-12-22 07:47:06 -0500 | [diff] [blame] | 397 | } |
| 398 | } |
Jonathan Amsterdam | 0e2b978 | 2021-02-23 09:41:34 -0500 | [diff] [blame] | 399 | |
Jonathan Amsterdam | 3823397 | 2021-03-12 15:18:32 -0500 | [diff] [blame] | 400 | func TestRawIsMoreRecent(t *testing.T) { |
Jonathan Amsterdam | 0e2b978 | 2021-02-23 09:41:34 -0500 | [diff] [blame] | 401 | 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 Amsterdam | 3823397 | 2021-03-12 15:18:32 -0500 | [diff] [blame] | 411 | got := rawIsMoreRecent(test.new, test.cur) |
Jonathan Amsterdam | 0e2b978 | 2021-02-23 09:41:34 -0500 | [diff] [blame] | 412 | if got != test.want { |
Jonathan Amsterdam | 3823397 | 2021-03-12 15:18:32 -0500 | [diff] [blame] | 413 | t.Errorf("rawIsMoreRecent(%q, %q) = %t, want %t", test.new, test.cur, got, test.want) |
Jonathan Amsterdam | 0e2b978 | 2021-02-23 09:41:34 -0500 | [diff] [blame] | 414 | } |
| 415 | } |
| 416 | } |
Jonathan Amsterdam | b0b8b70 | 2021-02-24 14:56:41 -0500 | [diff] [blame] | 417 | |
Jonathan Amsterdam | c01b819 | 2021-03-03 07:59:49 -0500 | [diff] [blame] | 418 | func 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 Amsterdam | d025eb3 | 2021-04-07 20:16:23 -0400 | [diff] [blame] | 437 | MustInsertModule(ctx, t, testDB, sample.Module(modulePath, v, "pkg")) |
Jonathan Amsterdam | c01b819 | 2021-03-03 07:59:49 -0500 | [diff] [blame] | 438 | } |
| 439 | |
| 440 | for _, test := range []struct { |
| 441 | name string |
Jonathan Amsterdam | 3823397 | 2021-03-12 15:18:32 -0500 | [diff] [blame] | 442 | cooked string // cooked latest version; empty means nil lmv |
Jonathan Amsterdam | c01b819 | 2021-03-03 07:59:49 -0500 | [diff] [blame] | 443 | 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 Amsterdam | 3823397 | 2021-03-12 15:18:32 -0500 | [diff] [blame] | 460 | { |
| 461 | name: "nil", |
| 462 | cooked: "", // no latest-version info |
| 463 | want: "v2.0.0+incompatible", |
| 464 | }, |
Jonathan Amsterdam | c01b819 | 2021-03-03 07:59:49 -0500 | [diff] [blame] | 465 | } { |
| 466 | t.Run(test.name, func(t *testing.T) { |
Jonathan Amsterdam | 3823397 | 2021-03-12 15:18:32 -0500 | [diff] [blame] | 467 | 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 Amsterdam | c01b819 | 2021-03-03 07:59:49 -0500 | [diff] [blame] | 473 | 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 | |
| 483 | func 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 Amsterdam | c01b819 | 2021-03-03 07:59:49 -0500 | [diff] [blame] | 499 | type versions struct { |
Jonathan Amsterdam | 3823397 | 2021-03-12 15:18:32 -0500 | [diff] [blame] | 500 | raw, cooked string |
Jonathan Amsterdam | c01b819 | 2021-03-03 07:59:49 -0500 | [diff] [blame] | 501 | } |
| 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 Amsterdam | 3823397 | 2021-03-12 15:18:32 -0500 | [diff] [blame] | 509 | in: versions{"v1.3.0", "v1.2.0"}, |
| 510 | want: versions{"v1.3.0", "v1.2.0"}, |
Jonathan Amsterdam | c01b819 | 2021-03-03 07:59:49 -0500 | [diff] [blame] | 511 | }, |
| 512 | { |
| 513 | name: "older", // older incoming info doesn't cause an update |
Jonathan Amsterdam | 3823397 | 2021-03-12 15:18:32 -0500 | [diff] [blame] | 514 | in: versions{"v1.2.0", "v1.2.0"}, |
| 515 | want: versions{"v1.3.0", "v1.2.0"}, |
Jonathan Amsterdam | c01b819 | 2021-03-03 07:59:49 -0500 | [diff] [blame] | 516 | }, |
| 517 | { |
| 518 | name: "newer bad", // a newer version, not in modules table |
Jonathan Amsterdam | 3823397 | 2021-03-12 15:18:32 -0500 | [diff] [blame] | 519 | in: versions{"v1.4.0", "v1.4.0"}, |
| 520 | want: versions{"v1.4.0", "v1.4.0"}, |
Jonathan Amsterdam | c01b819 | 2021-03-03 07:59:49 -0500 | [diff] [blame] | 521 | }, |
| 522 | { |
| 523 | name: "incompatible", |
Jonathan Amsterdam | 3823397 | 2021-03-12 15:18:32 -0500 | [diff] [blame] | 524 | in: versions{incompatible, incompatible}, |
| 525 | want: versions{incompatible, incompatible}, |
Jonathan Amsterdam | c01b819 | 2021-03-03 07:59:49 -0500 | [diff] [blame] | 526 | }, |
| 527 | { |
| 528 | name: "compatible", // "downgrade" from incompatible to compatible will update |
Jonathan Amsterdam | 3823397 | 2021-03-12 15:18:32 -0500 | [diff] [blame] | 529 | in: versions{"v1.3.0", "v1.2.0"}, |
| 530 | want: versions{"v1.3.0", "v1.2.0"}, |
Jonathan Amsterdam | c01b819 | 2021-03-03 07:59:49 -0500 | [diff] [blame] | 531 | }, |
| 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 Amsterdam | 3823397 | 2021-03-12 15:18:32 -0500 | [diff] [blame] | 538 | vGot, err := testDB.UpdateLatestModuleVersions(ctx, vNew) |
Jonathan Amsterdam | c01b819 | 2021-03-03 07:59:49 -0500 | [diff] [blame] | 539 | if err != nil { |
| 540 | t.Fatal(err) |
| 541 | } |
| 542 | if vGot == nil { |
| 543 | t.Fatal("got nothing") |
| 544 | } |
Jonathan Amsterdam | 3823397 | 2021-03-12 15:18:32 -0500 | [diff] [blame] | 545 | got := versions{vGot.RawVersion, vGot.CookedVersion} |
Jonathan Amsterdam | c01b819 | 2021-03-03 07:59:49 -0500 | [diff] [blame] | 546 | if got != test.want { |
| 547 | t.Fatalf("got %q, want %q", got, test.want) |
| 548 | } |
| 549 | }) |
| 550 | } |
| 551 | } |
Jonathan Amsterdam | eab1917 | 2021-03-11 09:36:33 -0500 | [diff] [blame] | 552 | |
| 553 | func 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 Amsterdam | 3823397 | 2021-03-12 15:18:32 -0500 | [diff] [blame] | 607 | if _, err := testDB.UpdateLatestModuleVersions(ctx, lmv); err != nil { |
Jonathan Amsterdam | eab1917 | 2021-03-11 09:36:33 -0500 | [diff] [blame] | 608 | 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 Amsterdam | 02c6e84 | 2021-03-19 13:28:36 -0400 | [diff] [blame] | 626 | |
| 627 | func 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 Amsterdam | d189d9d | 2021-04-07 19:59:52 -0400 | [diff] [blame] | 648 | const v1 = "v1.1.0" |
Jonathan Amsterdam | d025eb3 | 2021-04-07 20:16:23 -0400 | [diff] [blame] | 649 | MustInsertModule(ctx, t, testDB, sample.Module(modulePath, v1, "pkg")) |
Jonathan Amsterdam | 02c6e84 | 2021-03-19 13:28:36 -0400 | [diff] [blame] | 650 | check(v1) |
| 651 | |
| 652 | // Good version should be updated. |
Jonathan Amsterdam | d189d9d | 2021-04-07 19:59:52 -0400 | [diff] [blame] | 653 | const v2 = "v1.2.0" |
Jonathan Amsterdam | d025eb3 | 2021-04-07 20:16:23 -0400 | [diff] [blame] | 654 | MustInsertModule(ctx, t, testDB, sample.Module(modulePath, v2, "pkg")) |
Jonathan Amsterdam | 02c6e84 | 2021-03-19 13:28:36 -0400 | [diff] [blame] | 655 | check(v2) |
| 656 | |
Jonathan Amsterdam | d189d9d | 2021-04-07 19:59:52 -0400 | [diff] [blame] | 657 | // 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 Amsterdam | 02c6e84 | 2021-03-19 13:28:36 -0400 | [diff] [blame] | 663 | check(v1) |
| 664 | } |