blob: 277a69e0364b322c8c51186b09bcdbac422bbe67 [file] [log] [blame] [edit]
// Copyright 2022 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 govulncheck
import (
"context"
"fmt"
"testing"
"time"
bq "cloud.google.com/go/bigquery"
"github.com/google/go-cmp/cmp"
"golang.org/x/pkgsite-metrics/internal/bigquery"
"golang.org/x/pkgsite-metrics/internal/fstore"
"golang.org/x/pkgsite-metrics/internal/govulncheckapi"
test "golang.org/x/pkgsite-metrics/internal/testing"
"google.golang.org/api/iterator"
)
func TestConvertGovulncheckFinding(t *testing.T) {
var (
osvID = "GO-YYYY-XXXX"
vuln1 = &govulncheckapi.Finding{
OSV: osvID,
Trace: []*govulncheckapi.Frame{
{
Module: "example.com/repo/module",
Version: "v0.0.1",
Package: "example.com/repo/module/package",
Function: "func",
Position: &govulncheckapi.Position{},
},
},
}
vuln2 = &govulncheckapi.Finding{
OSV: osvID,
FixedVersion: "",
Trace: []*govulncheckapi.Frame{
{
Module: "example.com/repo/module",
Version: "v1.0.0",
Package: "example.com/repo/module/package",
Position: nil,
},
},
}
)
tests := []struct {
name string
vuln *govulncheckapi.Finding
wantVuln *Vuln
}{
{
name: "called",
vuln: vuln1,
wantVuln: &Vuln{
ID: "GO-YYYY-XXXX",
PackagePath: "example.com/repo/module/package",
ModulePath: "example.com/repo/module",
Version: "v0.0.1",
},
},
{
name: "Not called",
vuln: vuln2,
wantVuln: &Vuln{
ID: "GO-YYYY-XXXX",
PackagePath: "example.com/repo/module/package",
ModulePath: "example.com/repo/module",
Version: "v1.0.0",
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if diff := cmp.Diff(ConvertGovulncheckFinding(tt.vuln, nil), tt.wantVuln, cmp.AllowUnexported(Vuln{})); diff != "" {
t.Errorf("mismatch (-got, +want): %s", diff)
}
})
}
}
func TestIntegration(t *testing.T) {
test.NeedsIntegrationEnv(t)
must := func(err error) {
t.Helper()
if err != nil {
t.Fatal(err)
}
}
ctx := context.Background()
const projectID = "go-ecosystem"
// Create a new dataset ID to avoid problems with re-using existing tables.
dsID := fmt.Sprintf("test_%s", time.Now().Format("20060102T030405"))
t.Logf("using dataset %s", dsID)
client, err := bigquery.NewClientCreate(ctx, projectID, dsID)
if err != nil {
t.Fatal(err)
}
defer func() {
must(client.Dataset().Delete(ctx))
}()
if _, err := client.CreateOrUpdateTable(ctx, TableName); err != nil {
t.Fatal(err)
}
defer func() { must(client.Table(TableName).Delete(ctx)) }()
tm := time.Date(2022, 7, 21, 0, 0, 0, 0, time.UTC)
ws := &WorkState{
WorkVersion: &WorkVersion{
GoVersion: "go1.19.6",
WorkerVersion: "1",
SchemaVersion: "s",
VulnDBLastModified: tm,
},
ErrorCategory: "SOME ERROR",
}
row := &Result{
ModulePath: "m",
Version: "v",
SortVersion: "sv",
ImportedBy: 10,
WorkVersion: *ws.WorkVersion,
ErrorCategory: ws.ErrorCategory,
}
t.Run("upload", func(t *testing.T) {
must(client.Upload(ctx, TableName, row))
// Round, strip monotonic data and convert to UTC.
// Discrepancies of a few microseconds have been seen, so round to seconds
// just to be safe.
row.CreatedAt = row.CreatedAt.Round(time.Second).UTC()
gots, err := readTable[Result](ctx, client.Table(TableName), nil)
if err != nil {
t.Fatal(err)
}
if g, w := len(gots), 1; g != w {
t.Fatalf("got %d, rows, wanted %d", g, w)
}
got := gots[0]
got.CreatedAt = got.CreatedAt.Round(time.Second)
if diff := cmp.Diff(row, got); diff != "" {
t.Errorf("mismatch (-want, +got):\n%s", diff)
}
})
t.Run("work states", func(t *testing.T) {
ns, err := fstore.OpenNamespace(ctx, projectID, "testing")
if err != nil {
t.Fatal(err)
}
if err := SetWorkState(ctx, ns, "example.com/mod", "v1.0.0", ws); err != nil {
t.Fatal(err)
}
got, err := GetWorkState(ctx, ns, "example.com/mod", "v1.0.0")
if err != nil {
t.Fatal(err)
}
if !cmp.Equal(got, ws) {
t.Errorf("got %+v\nwant %+v", got, ws)
}
// GetWorkState returns nil if the WorkState doesn't exist.
got, err = GetWorkState(ctx, ns, "example.com/mod", "v1.2.3")
if got != nil || err != nil {
t.Errorf("got (%v, %v), want (nil, nil)", got, err)
}
})
}
func readTable[T any](ctx context.Context, table *bq.Table, newT func() *T) ([]*T, error) {
var ts []*T
if newT == nil {
newT = func() *T { return new(T) }
}
iter := table.Read(ctx)
for {
tp := newT()
err := iter.Next(tp)
if err == iterator.Done {
break
}
if err != nil {
return nil, err
}
ts = append(ts, tp)
}
return ts, nil
}