Files
trivy/pkg/github/github_test.go
Teppei Fukuda 1b66b77f69 feat: prepare for config scanning (#1005)
* temp: disable config scanning
2021-05-20 09:05:36 +03:00

460 lines
11 KiB
Go

package github
import (
"context"
"fmt"
"io"
"io/ioutil"
"net/http"
"net/http/httptest"
"net/url"
"path"
"strings"
"testing"
"time"
"github.com/google/go-github/v33/github"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"golang.org/x/xerrors"
)
type MockRepository struct {
mock.Mock
}
func (_m *MockRepository) ListReleases(ctx context.Context, opt *github.ListOptions) (
[]*github.RepositoryRelease, *github.Response, error) {
ret := _m.Called(ctx, opt)
ret0 := ret.Get(0)
if ret0 == nil {
return nil, nil, ret.Error(2)
}
releases, ok := ret0.([]*github.RepositoryRelease)
if !ok {
return nil, nil, ret.Error(2)
}
return releases, nil, ret.Error(2)
}
func (_m *MockRepository) DownloadAsset(ctx context.Context, id int64) (io.ReadCloser, string, error) {
ret := _m.Called(ctx, id)
ret0 := ret.Get(0)
if ret0 == nil {
return nil, ret.String(1), ret.Error(2)
}
rc, ok := ret0.(io.ReadCloser)
if !ok {
return nil, ret.String(1), ret.Error(2)
}
return rc, ret.String(1), ret.Error(2)
}
func TestClient_DownloadDB(t *testing.T) {
type listReleasesOutput struct {
releases []*github.RepositoryRelease
response *github.Response
err error
}
type listReleases struct {
input string
output listReleasesOutput
}
type downloadAssetOutput struct {
rc io.ReadCloser
redirectPath string
err error
}
type downloadAsset struct {
input int64
output downloadAssetOutput
}
testCases := []struct {
name string
fileName string
filePaths []string
listReleases []listReleases
downloadAsset []downloadAsset
expectedError error
}{
{
name: "happy path",
fileName: "trivy.db.gz",
listReleases: []listReleases{
{
input: mock.Anything,
output: listReleasesOutput{
releases: []*github.RepositoryRelease{
{
// this release should be skipped due to the wrong prefix of the tag
ID: github.Int64(2),
Name: github.String("v2-2020010101"),
PublishedAt: &github.Timestamp{
Time: time.Date(2020, 1, 1, 1, 59, 59, 0, time.UTC),
},
Assets: []*github.ReleaseAsset{
{
ID: github.Int64(200),
Name: github.String("trivy.db.gz"),
},
},
},
{
ID: github.Int64(1),
Name: github.String("v1-2020123123"),
PublishedAt: &github.Timestamp{
Time: time.Date(2020, 12, 31, 23, 59, 59, 0, time.UTC),
},
Assets: []*github.ReleaseAsset{
{
ID: github.Int64(100),
Name: github.String("trivy.db.gz"),
},
},
},
},
},
},
},
downloadAsset: []downloadAsset{
{
input: 100,
output: downloadAssetOutput{
rc: ioutil.NopCloser(strings.NewReader("foo")),
},
},
},
},
{
name: "happy path with redirect URL",
fileName: "trivy.db.gz",
listReleases: []listReleases{
{
input: mock.Anything,
output: listReleasesOutput{
releases: []*github.RepositoryRelease{
{
ID: github.Int64(1),
Name: github.String("v1-2020123123"),
PublishedAt: &github.Timestamp{
Time: time.Date(2020, 12, 31, 23, 59, 59, 0, time.UTC),
},
Assets: []*github.ReleaseAsset{
{
ID: github.Int64(100),
Name: github.String("trivy.db.gz"),
},
},
},
},
},
},
},
downloadAsset: []downloadAsset{
{
input: 100,
output: downloadAssetOutput{
redirectPath: "/happy",
},
},
},
},
{
name: "happy path with inorder releases",
fileName: "trivy.db.gz",
listReleases: []listReleases{
{
input: mock.Anything,
output: listReleasesOutput{
releases: []*github.RepositoryRelease{
{
ID: github.Int64(1),
Name: github.String("v1-2019100123"),
PublishedAt: &github.Timestamp{
Time: time.Date(2019, 10, 1, 23, 59, 59, 0, time.UTC),
},
Assets: []*github.ReleaseAsset{
{
ID: github.Int64(100),
Name: github.String("trivy.db.gz"),
},
},
},
{
// this release should be used because this is the latest
ID: github.Int64(3),
Name: github.String("v1-2019100200"),
PublishedAt: &github.Timestamp{
Time: time.Date(2019, 10, 2, 0, 59, 59, 0, time.UTC),
},
Assets: []*github.ReleaseAsset{
{
ID: github.Int64(300),
Name: github.String("trivy.db.gz"),
},
},
},
{
ID: github.Int64(2),
Name: github.String("v1-2019100122"),
PublishedAt: &github.Timestamp{
Time: time.Date(2019, 10, 1, 22, 59, 59, 0, time.UTC),
},
Assets: []*github.ReleaseAsset{
{
ID: github.Int64(200),
Name: github.String("trivy.db.gz"),
},
},
},
},
},
},
},
downloadAsset: []downloadAsset{
{
input: 300,
output: downloadAssetOutput{
rc: ioutil.NopCloser(strings.NewReader("foo")),
},
},
},
},
{
name: "happy path with no asset",
fileName: "trivy.db.gz",
listReleases: []listReleases{
{
input: mock.Anything,
output: listReleasesOutput{
releases: []*github.RepositoryRelease{
{
// this release should be skipped due to no asset
ID: github.Int64(1),
Name: github.String("v1-2019100123"),
PublishedAt: &github.Timestamp{
Time: time.Date(2019, 10, 1, 23, 59, 59, 0, time.UTC),
},
},
{
// this release should be skipped due to no asset
ID: github.Int64(3),
Name: github.String("v1-2019100200"),
PublishedAt: &github.Timestamp{
Time: time.Date(2019, 10, 2, 0, 59, 59, 0, time.UTC),
},
},
{
// this release should be used because this release has assets
ID: github.Int64(2),
Name: github.String("v1-2019100122"),
PublishedAt: &github.Timestamp{
Time: time.Date(2019, 10, 1, 22, 59, 59, 0, time.UTC),
},
Assets: []*github.ReleaseAsset{
{
ID: github.Int64(200),
Name: github.String("trivy.db.gz"),
},
},
},
},
},
},
},
downloadAsset: []downloadAsset{
{
input: 200,
output: downloadAssetOutput{
rc: ioutil.NopCloser(strings.NewReader("foo")),
},
},
},
},
{
name: "no asset",
fileName: "trivy.db.gz",
listReleases: []listReleases{
{
input: mock.Anything,
output: listReleasesOutput{
releases: []*github.RepositoryRelease{
{
ID: github.Int64(1),
Name: github.String("v1-2020123000"),
PublishedAt: &github.Timestamp{
Time: time.Date(2020, 12, 31, 23, 59, 59, 0, time.UTC),
},
},
},
},
},
},
expectedError: xerrors.New("DB file not found"),
},
{
name: "the file name doesn't match",
fileName: "trivy-light.db.gz",
listReleases: []listReleases{
{
input: mock.Anything,
output: listReleasesOutput{
releases: []*github.RepositoryRelease{
{
ID: github.Int64(1),
Name: github.String("v1-2020123000"),
PublishedAt: &github.Timestamp{
Time: time.Date(2020, 12, 31, 23, 59, 59, 0, time.UTC),
},
Assets: []*github.ReleaseAsset{
{
ID: github.Int64(100),
Name: github.String("trivy.db.gz"),
},
},
},
},
},
},
},
expectedError: xerrors.New("DB file not found"),
},
{
name: "ListReleases returns error",
fileName: "trivy.db.gz",
listReleases: []listReleases{
{
input: mock.Anything,
output: listReleasesOutput{
err: xerrors.New("something wrong"),
},
},
},
expectedError: xerrors.New("failed to list releases: something wrong"),
},
{
name: "DownloadAsset returns error",
fileName: "trivy.db.gz",
listReleases: []listReleases{
{
input: mock.Anything,
output: listReleasesOutput{
releases: []*github.RepositoryRelease{
{
ID: github.Int64(1),
Name: github.String("v1-2020123000"),
PublishedAt: &github.Timestamp{
Time: time.Date(2020, 12, 31, 23, 59, 59, 0, time.UTC),
},
Assets: []*github.ReleaseAsset{
{
ID: github.Int64(100),
Name: github.String("trivy.db.gz"),
},
},
},
},
},
},
},
downloadAsset: []downloadAsset{
{
input: 100,
output: downloadAssetOutput{
err: xerrors.New("something wrong"),
},
},
},
expectedError: xerrors.New("DB file not found"),
},
{
name: "404 error",
fileName: "trivy.db.gz",
listReleases: []listReleases{
{
input: mock.Anything,
output: listReleasesOutput{
releases: []*github.RepositoryRelease{
{
ID: github.Int64(1),
Name: github.String("v1-2020123000"),
PublishedAt: &github.Timestamp{
Time: time.Date(2020, 12, 31, 23, 59, 59, 0, time.UTC),
},
Assets: []*github.ReleaseAsset{
{
ID: github.Int64(100),
Name: github.String("trivy.db.gz"),
},
},
},
},
},
},
},
downloadAsset: []downloadAsset{
{
input: 100,
output: downloadAssetOutput{
redirectPath: "/not_found",
},
},
},
expectedError: xerrors.New("DB file not found"),
},
}
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch r.URL.Path {
case "/happy":
_, _ = fmt.Fprintf(w, "happy")
case "/not_found":
http.NotFound(w, r)
}
return
},
))
defer ts.Close()
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
mockRepo := new(MockRepository)
for _, lr := range tc.listReleases {
mockRepo.On("ListReleases", mock.Anything, lr.input).Return(
lr.output.releases, lr.output.response, lr.output.err,
)
}
for _, da := range tc.downloadAsset {
var redirectURL string
if da.output.redirectPath != "" {
u, _ := url.Parse(ts.URL)
u.Path = path.Join(u.Path, da.output.redirectPath)
redirectURL = u.String()
}
mockRepo.On("DownloadAsset", mock.Anything, da.input).Return(
da.output.rc, redirectURL, da.output.err,
)
}
client := Client{
Repository: mockRepo,
}
ctx := context.Background()
rc, _, err := client.DownloadDB(ctx, tc.fileName)
switch {
case tc.expectedError != nil:
assert.EqualError(t, err, tc.expectedError.Error(), tc.name)
default:
assert.NoError(t, err, tc.name)
assert.NotNil(t, rc, tc.name)
}
mockRepo.AssertExpectations(t)
})
}
}