| // Copyright 2020 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package postgres |
| |
| import ( |
| "context" |
| "errors" |
| "sort" |
| "testing" |
| |
| "github.com/google/go-cmp/cmp" |
| "github.com/google/go-cmp/cmp/cmpopts" |
| "golang.org/x/pkgsite/internal" |
| "golang.org/x/pkgsite/internal/licenses" |
| "golang.org/x/pkgsite/internal/stdlib" |
| "golang.org/x/pkgsite/internal/testing/sample" |
| ) |
| |
| func TestGetLicenses(t *testing.T) { |
| testModule := sample.Module(sample.ModulePath, "v1.2.3", "A/B") |
| stdlibModule := sample.Module(stdlib.ModulePath, "v1.13.0", "cmd/go") |
| mit := &licenses.Metadata{Types: []string{"MIT"}, FilePath: "LICENSE"} |
| bsd := &licenses.Metadata{Types: []string{"BSD-3-Clause"}, FilePath: "A/B/LICENSE"} |
| |
| mitLicense := &licenses.License{Metadata: mit} |
| bsdLicense := &licenses.License{Metadata: bsd} |
| testModule.Licenses = []*licenses.License{bsdLicense, mitLicense} |
| sort.Slice(testModule.Units, func(i, j int) bool { |
| return testModule.Units[i].Path < testModule.Units[j].Path |
| }) |
| |
| // github.com/valid/module_name |
| testModule.Units[0].Licenses = []*licenses.Metadata{mit} |
| // github.com/valid/module_name/A |
| testModule.Units[1].Licenses = []*licenses.Metadata{mit} |
| // github.com/valid/module_name/A/B |
| testModule.Units[2].Licenses = []*licenses.Metadata{mit, bsd} |
| |
| defer ResetTestDB(testDB, t) |
| ctx, cancel := context.WithTimeout(context.Background(), testTimeout*5) |
| defer cancel() |
| if err := testDB.InsertModule(ctx, testModule); err != nil { |
| t.Fatal(err) |
| } |
| if err := testDB.InsertModule(ctx, stdlibModule); err != nil { |
| t.Fatal(err) |
| } |
| for _, test := range []struct { |
| err error |
| name, fullPath, modulePath, version string |
| want []*licenses.License |
| }{ |
| { |
| name: "module root", |
| fullPath: sample.ModulePath, |
| modulePath: sample.ModulePath, |
| version: testModule.Version, |
| want: []*licenses.License{testModule.Licenses[1]}, |
| }, |
| { |
| name: "package without license", |
| fullPath: sample.ModulePath + "/A", |
| modulePath: sample.ModulePath, |
| version: testModule.Version, |
| want: []*licenses.License{testModule.Licenses[1]}, |
| }, |
| { |
| name: "package with additional license", |
| fullPath: sample.ModulePath + "/A/B", |
| modulePath: sample.ModulePath, |
| version: testModule.Version, |
| want: testModule.Licenses, |
| }, |
| { |
| name: "stdlib directory", |
| fullPath: "cmd", |
| modulePath: stdlib.ModulePath, |
| version: stdlibModule.Version, |
| want: stdlibModule.Licenses, |
| }, |
| { |
| name: "stdlib package", |
| fullPath: "cmd/go", |
| modulePath: stdlib.ModulePath, |
| version: stdlibModule.Version, |
| want: stdlibModule.Licenses, |
| }, |
| { |
| name: "stdlib module", |
| fullPath: stdlib.ModulePath, |
| modulePath: stdlib.ModulePath, |
| version: stdlibModule.Version, |
| want: stdlibModule.Licenses, |
| }, |
| } { |
| t.Run(test.name, func(t *testing.T) { |
| u, err := testDB.GetUnit(ctx, &internal.UnitMeta{Path: test.fullPath, ModulePath: test.modulePath, Version: test.version}, internal.WithLicenses) |
| if !errors.Is(err, test.err) { |
| t.Fatal(err) |
| } |
| got := u.LicenseContents |
| sort.Slice(got, func(i, j int) bool { |
| return got[i].FilePath < got[j].FilePath |
| }) |
| sort.Slice(test.want, func(i, j int) bool { |
| return test.want[i].FilePath < test.want[j].FilePath |
| }) |
| for i := range got { |
| sort.Strings(got[i].Types) |
| } |
| for i := range test.want { |
| sort.Strings(test.want[i].Types) |
| } |
| cmpopt := cmpopts.IgnoreFields(licenses.License{}, "Contents") |
| if diff := cmp.Diff(test.want, got, cmpopt); diff != "" { |
| t.Errorf("mismatch (-want +got):\n%s", diff) |
| } |
| }) |
| } |
| } |
| |
| func TestGetModuleLicenses(t *testing.T) { |
| modulePath := "test.module" |
| testModule := sample.Module(modulePath, "v1.2.3", "", "foo", "bar") |
| testModule.Packages()[0].Licenses = []*licenses.Metadata{{Types: []string{"ISC"}, FilePath: "LICENSE"}} |
| testModule.Packages()[1].Licenses = []*licenses.Metadata{{Types: []string{"MIT"}, FilePath: "foo/LICENSE"}} |
| testModule.Packages()[2].Licenses = []*licenses.Metadata{{Types: []string{"GPL2"}, FilePath: "bar/LICENSE.txt"}} |
| |
| defer ResetTestDB(testDB, t) |
| ctx, cancel := context.WithTimeout(context.Background(), testTimeout) |
| defer cancel() |
| |
| testModule.Licenses = nil |
| for _, p := range testModule.Packages() { |
| testModule.Licenses = append(testModule.Licenses, &licenses.License{ |
| Metadata: p.Licenses[0], |
| Contents: []byte(`Lorem Ipsum`), |
| }) |
| } |
| |
| if err := testDB.InsertModule(ctx, testModule); err != nil { |
| t.Fatal(err) |
| } |
| |
| var moduleID int |
| query := ` |
| SELECT m.id |
| FROM modules m |
| WHERE |
| m.module_path = $1 |
| AND m.version = $2;` |
| if err := testDB.db.QueryRow(ctx, query, modulePath, testModule.Version).Scan(&moduleID); err != nil { |
| t.Fatal(err) |
| } |
| got, err := testDB.getModuleLicenses(ctx, moduleID) |
| if err != nil { |
| t.Fatal(err) |
| } |
| // We only want the top-level license. |
| wantLicenses := []*licenses.License{testModule.Licenses[0]} |
| if diff := cmp.Diff(wantLicenses, got); diff != "" { |
| t.Errorf("testDB.getModuleLicenses(ctx, %q, %q) mismatch (-want +got):\n%s", modulePath, testModule.Version, diff) |
| } |
| } |
| |
| func TestGetLicensesBypass(t *testing.T) { |
| ctx, cancel := context.WithTimeout(context.Background(), testTimeout) |
| defer cancel() |
| defer ResetTestDB(testDB, t) |
| |
| bypassDB := NewBypassingLicenseCheck(testDB.db) |
| |
| // Insert with non-redistributable license contents. |
| m := nonRedistributableModule() |
| if err := bypassDB.InsertModule(ctx, m); err != nil { |
| t.Fatal(err) |
| } |
| |
| // check reads and the second license in the module and compares it with want. |
| check := func(bypass bool, want *licenses.License) { |
| t.Helper() |
| db := testDB |
| if bypass { |
| db = bypassDB |
| } |
| u, err := db.GetUnit(ctx, &internal.UnitMeta{Path: sample.ModulePath, ModulePath: sample.ModulePath, Version: m.Version}, internal.WithLicenses) |
| if err != nil { |
| t.Fatal(err) |
| } |
| lics := u.LicenseContents |
| if len(lics) != 2 { |
| t.Fatal("did not read two licenses") |
| } |
| if diff := cmp.Diff(want, lics[1]); diff != "" { |
| t.Errorf("mismatch (-want, +got):\n%s", diff) |
| } |
| } |
| |
| // Read with license bypass includes non-redistributable license contents. |
| check(true, sample.NonRedistributableLicense) |
| |
| // Read without license bypass does not include non-redistributable license contents. |
| nonRedist := *sample.NonRedistributableLicense |
| nonRedist.Contents = nil |
| check(false, &nonRedist) |
| } |
| |
| func nonRedistributableModule() *internal.Module { |
| m := sample.Module(sample.ModulePath, "v1.2.3", "") |
| sample.AddLicense(m, sample.NonRedistributableLicense) |
| m.IsRedistributable = false |
| m.Packages()[0].IsRedistributable = false |
| m.Units[0].IsRedistributable = false |
| return m |
| } |
| |
| // makeModuleNonRedistributable mutates the passed-in module by marking it |
| // non-redistributable along with each of its packages and units. It allows |
| // us to re-use existing test data without defining a non-redistributable |
| // counterpart to each. |
| func makeModuleNonRedistributable(m *internal.Module) { |
| sample.AddLicense(m, sample.NonRedistributableLicense) |
| m.IsRedistributable = false |
| |
| for _, p := range m.Packages() { |
| p.IsRedistributable = false |
| } |
| |
| for i := range m.Units { |
| m.Units[i].IsRedistributable = false |
| } |
| } |