mirror of
https://github.com/google/go-github.git
synced 2026-10-28 16:34:37 +08:00
339 lines
10 KiB
Go
339 lines
10 KiB
Go
// Copyright 2023 The go-github 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 github
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"testing"
|
|
|
|
"github.com/google/go-cmp/cmp"
|
|
)
|
|
|
|
func TestDependencyGraphService_GetSBOM(t *testing.T) {
|
|
t.Parallel()
|
|
client, mux, _ := setup(t)
|
|
|
|
mux.HandleFunc("/repos/owner/repo/dependency-graph/sbom", func(w http.ResponseWriter, r *http.Request) {
|
|
testMethod(t, r, "GET")
|
|
fmt.Fprint(w, `{
|
|
"sbom":{
|
|
"creationInfo":{
|
|
"created":`+referenceTimeStr+`
|
|
},
|
|
"name":"owner/repo",
|
|
"packages":[
|
|
{
|
|
"name":"rubygems:rails",
|
|
"versionInfo":"1.0.0"
|
|
}
|
|
]
|
|
}
|
|
}`)
|
|
})
|
|
|
|
ctx := t.Context()
|
|
sbom, _, err := client.DependencyGraph.GetSBOM(ctx, "owner", "repo")
|
|
if err != nil {
|
|
t.Errorf("DependencyGraph.GetSBOM returned error: %v", err)
|
|
}
|
|
|
|
want := &SBOM{
|
|
&SBOMInfo{
|
|
CreationInfo: &CreationInfo{
|
|
Created: &referenceTimestamp,
|
|
},
|
|
Name: new("owner/repo"),
|
|
Packages: []*RepoDependencies{
|
|
{
|
|
Name: new("rubygems:rails"),
|
|
VersionInfo: new("1.0.0"),
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
if !cmp.Equal(sbom, want) {
|
|
t.Errorf("DependencyGraph.GetSBOM returned %+v, want %+v", sbom, want)
|
|
}
|
|
|
|
const methodName = "GetSBOM"
|
|
testBadOptions(t, methodName, func() (err error) {
|
|
_, _, err = client.DependencyGraph.GetSBOM(ctx, "\n", "\n")
|
|
return err
|
|
})
|
|
|
|
testNewRequestAndDoFailureCategory(t, methodName, client, DependencySBOMCategory, func() (*Response, error) {
|
|
got, resp, err := client.DependencyGraph.GetSBOM(ctx, "owner", "repo")
|
|
if got != nil {
|
|
t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
|
|
}
|
|
return resp, err
|
|
})
|
|
}
|
|
|
|
func TestDependencyGraphService_GenerateSBOM(t *testing.T) {
|
|
t.Parallel()
|
|
client, mux, _ := setup(t)
|
|
|
|
mux.HandleFunc("/repos/owner/repo/dependency-graph/sbom/generate-report", func(w http.ResponseWriter, r *http.Request) {
|
|
testMethod(t, r, "GET")
|
|
w.WriteHeader(http.StatusCreated)
|
|
fmt.Fprint(w, `{"sbom_url":"https://api.github.com/repos/owner/repo/dependency-graph/sbom/fetch-report/1234"}`)
|
|
})
|
|
|
|
ctx := t.Context()
|
|
generation, resp, err := client.DependencyGraph.GenerateSBOM(ctx, "owner", "repo")
|
|
if err != nil {
|
|
t.Errorf("DependencyGraph.GenerateSBOM returned error: %v", err)
|
|
}
|
|
|
|
if resp.StatusCode != http.StatusCreated {
|
|
t.Errorf("DependencyGraph.GenerateSBOM returned status = %v, want %v", resp.StatusCode, http.StatusCreated)
|
|
}
|
|
|
|
want := &SBOMGeneration{
|
|
SBOMURL: new("https://api.github.com/repos/owner/repo/dependency-graph/sbom/fetch-report/1234"),
|
|
}
|
|
if !cmp.Equal(generation, want) {
|
|
t.Errorf("DependencyGraph.GenerateSBOM returned %+v, want %+v", generation, want)
|
|
}
|
|
|
|
const methodName = "GenerateSBOM"
|
|
testBadOptions(t, methodName, func() (err error) {
|
|
_, _, err = client.DependencyGraph.GenerateSBOM(ctx, "\n", "\n")
|
|
return err
|
|
})
|
|
|
|
testNewRequestAndDoFailureCategory(t, methodName, client, DependencySBOMCategory, func() (*Response, error) {
|
|
got, resp, err := client.DependencyGraph.GenerateSBOM(ctx, "owner", "repo")
|
|
if got != nil {
|
|
t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
|
|
}
|
|
return resp, err
|
|
})
|
|
}
|
|
|
|
func TestDependencyGraphService_FetchSBOM_Download(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
tests := []struct {
|
|
name string
|
|
downloadStatus int
|
|
downloadBody string
|
|
wantSBOM *SBOM
|
|
wantErr error
|
|
}{
|
|
{
|
|
name: "success",
|
|
downloadStatus: http.StatusOK,
|
|
downloadBody: `{"name":"owner/repo"}`,
|
|
wantSBOM: &SBOM{
|
|
SBOM: &SBOMInfo{Name: new("owner/repo")},
|
|
},
|
|
},
|
|
{
|
|
name: "forbidden",
|
|
downloadStatus: http.StatusForbidden,
|
|
wantErr: &ErrorResponse{
|
|
Response: &http.Response{StatusCode: http.StatusForbidden},
|
|
},
|
|
},
|
|
{
|
|
name: "truncated JSON",
|
|
downloadStatus: http.StatusOK,
|
|
downloadBody: `{"name":"owner/`,
|
|
wantErr: io.ErrUnexpectedEOF,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
client, mux, _ := setup(t)
|
|
|
|
client, err := client.Clone(WithAuthToken("secret-token"))
|
|
if err != nil {
|
|
t.Fatalf("failed to clone client: %v", err)
|
|
}
|
|
|
|
downloadServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
testMethod(t, r, "GET")
|
|
testHeader(t, r, "Authorization", "")
|
|
w.WriteHeader(tt.downloadStatus)
|
|
fmt.Fprint(w, tt.downloadBody)
|
|
}))
|
|
t.Cleanup(downloadServer.Close)
|
|
|
|
mux.HandleFunc("/repos/owner/repo/dependency-graph/sbom/fetch-report/1234", func(w http.ResponseWriter, r *http.Request) {
|
|
testMethod(t, r, "GET")
|
|
testHeader(t, r, "Authorization", "Bearer secret-token")
|
|
http.Redirect(w, r, downloadServer.URL, http.StatusFound)
|
|
})
|
|
|
|
sbom, redirectURL, resp, err := client.DependencyGraph.FetchSBOM(t.Context(), "owner", "repo", "1234", http.DefaultClient)
|
|
if !errors.Is(err, tt.wantErr) {
|
|
t.Errorf("DependencyGraph.FetchSBOM error = %#v, want %#v", err, tt.wantErr)
|
|
}
|
|
|
|
if resp == nil || resp.StatusCode != http.StatusFound {
|
|
t.Errorf("DependencyGraph.FetchSBOM response = %v, want status 302", resp)
|
|
}
|
|
if diff := cmp.Diff(tt.wantSBOM, sbom); diff != "" {
|
|
t.Errorf("DependencyGraph.FetchSBOM SBOM mismatch (-want +got):\n%v", diff)
|
|
}
|
|
if redirectURL != "" {
|
|
t.Errorf("DependencyGraph.FetchSBOM redirectURL = %v, want empty", redirectURL)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestDependencyGraphService_FetchSBOM_Redirect(t *testing.T) {
|
|
t.Parallel()
|
|
client, mux, serverURL := setup(t)
|
|
downloadURL := serverURL + baseURLPath + "/download"
|
|
|
|
mux.HandleFunc("/repos/owner/repo/dependency-graph/sbom/fetch-report/1234", func(w http.ResponseWriter, r *http.Request) {
|
|
testMethod(t, r, "GET")
|
|
http.Redirect(w, r, downloadURL, http.StatusFound)
|
|
})
|
|
mux.HandleFunc("/download", func(http.ResponseWriter, *http.Request) {
|
|
t.Error("download URL was fetched with a nil followRedirectsClient")
|
|
})
|
|
|
|
sbom, redirectURL, resp, err := client.DependencyGraph.FetchSBOM(t.Context(), "owner", "repo", "1234", nil)
|
|
if err != nil {
|
|
t.Fatalf("DependencyGraph.FetchSBOM returned error: %v", err)
|
|
}
|
|
|
|
if sbom != nil {
|
|
t.Errorf("DependencyGraph.FetchSBOM SBOM = %v, want nil", sbom)
|
|
}
|
|
if redirectURL != downloadURL {
|
|
t.Errorf("DependencyGraph.FetchSBOM redirectURL = %v, want %v", redirectURL, downloadURL)
|
|
}
|
|
if resp == nil || resp.StatusCode != http.StatusFound {
|
|
t.Errorf("DependencyGraph.FetchSBOM response = %v, want status 302", resp)
|
|
}
|
|
}
|
|
|
|
func TestDependencyGraphService_FetchSBOM_DownloadTransportError(t *testing.T) {
|
|
t.Parallel()
|
|
client, mux, _ := setup(t)
|
|
|
|
const downloadURL = "https://example.com/sbom"
|
|
wantErr := errors.New("download failed")
|
|
downloadClient := &http.Client{
|
|
Transport: roundTripperFunc(func(r *http.Request) (*http.Response, error) {
|
|
testMethod(t, r, "GET")
|
|
if got := r.URL.String(); got != downloadURL {
|
|
t.Errorf("download URL = %v, want %v", got, downloadURL)
|
|
}
|
|
return nil, wantErr
|
|
}),
|
|
}
|
|
|
|
mux.HandleFunc("/repos/owner/repo/dependency-graph/sbom/fetch-report/1234", func(w http.ResponseWriter, r *http.Request) {
|
|
testMethod(t, r, "GET")
|
|
http.Redirect(w, r, downloadURL, http.StatusFound)
|
|
})
|
|
|
|
sbom, redirectURL, resp, err := client.DependencyGraph.FetchSBOM(t.Context(), "owner", "repo", "1234", downloadClient)
|
|
if !errors.Is(err, wantErr) {
|
|
t.Errorf("DependencyGraph.FetchSBOM error = %v, want %v", err, wantErr)
|
|
}
|
|
|
|
if sbom != nil || redirectURL != "" {
|
|
t.Errorf("DependencyGraph.FetchSBOM returned (%v, %v), want (nil, empty)", sbom, redirectURL)
|
|
}
|
|
if resp == nil || resp.StatusCode != http.StatusFound {
|
|
t.Errorf("DependencyGraph.FetchSBOM response = %v, want status 302", resp)
|
|
}
|
|
}
|
|
|
|
func TestDependencyGraphService_FetchSBOM_AcceptedError(t *testing.T) {
|
|
t.Parallel()
|
|
client, mux, _ := setup(t)
|
|
|
|
mux.HandleFunc("/repos/owner/repo/dependency-graph/sbom/fetch-report/1234", func(w http.ResponseWriter, r *http.Request) {
|
|
testMethod(t, r, "GET")
|
|
w.WriteHeader(http.StatusAccepted)
|
|
})
|
|
|
|
sbom, redirectURL, resp, err := client.DependencyGraph.FetchSBOM(t.Context(), "owner", "repo", "1234", http.DefaultClient)
|
|
|
|
if _, ok := errors.AsType[*AcceptedError](err); !ok {
|
|
t.Errorf("DependencyGraph.FetchSBOM error = %v, want *AcceptedError", err)
|
|
}
|
|
|
|
if sbom != nil || redirectURL != "" {
|
|
t.Errorf("DependencyGraph.FetchSBOM returned (%v, %v), want (nil, empty)", sbom, redirectURL)
|
|
}
|
|
if resp == nil || resp.StatusCode != http.StatusAccepted {
|
|
t.Errorf("DependencyGraph.FetchSBOM response = %v, want status 202", resp)
|
|
}
|
|
}
|
|
|
|
func TestDependencyGraphService_FetchSBOM_NoRedirect(t *testing.T) {
|
|
t.Parallel()
|
|
client, mux, _ := setup(t)
|
|
|
|
mux.HandleFunc("/repos/owner/repo/dependency-graph/sbom/fetch-report/1234", func(w http.ResponseWriter, r *http.Request) {
|
|
testMethod(t, r, "GET")
|
|
w.WriteHeader(http.StatusOK)
|
|
})
|
|
|
|
sbom, redirectURL, resp, err := client.DependencyGraph.FetchSBOM(t.Context(), "owner", "repo", "1234", http.DefaultClient)
|
|
if err == nil {
|
|
t.Error("DependencyGraph.FetchSBOM returned no error for a non-redirect response")
|
|
}
|
|
|
|
if sbom != nil || redirectURL != "" {
|
|
t.Errorf("DependencyGraph.FetchSBOM returned (%v, %v), want (nil, empty)", sbom, redirectURL)
|
|
}
|
|
if resp == nil || resp.StatusCode != http.StatusOK {
|
|
t.Errorf("DependencyGraph.FetchSBOM response = %v, want status 200", resp)
|
|
}
|
|
}
|
|
|
|
func TestDependencyGraphService_FetchSBOM_requestErrors(t *testing.T) {
|
|
t.Parallel()
|
|
client, _, _ := setup(t)
|
|
|
|
ctx := t.Context()
|
|
|
|
const methodName = "FetchSBOM"
|
|
testBadOptions(t, methodName, func() (err error) {
|
|
_, _, _, err = client.DependencyGraph.FetchSBOM(ctx, "\n", "\n", "\n", http.DefaultClient)
|
|
return err
|
|
})
|
|
|
|
testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
|
|
sbom, redirectURL, resp, err := client.DependencyGraph.FetchSBOM(ctx, "owner", "repo", "1234", http.DefaultClient)
|
|
if sbom != nil || redirectURL != "" {
|
|
t.Errorf("testNewRequestAndDoFailure %v returned (%v, %v), want (nil, empty)", methodName, sbom, redirectURL)
|
|
}
|
|
return resp, err
|
|
})
|
|
}
|
|
|
|
func TestDependencyGraphService_fetchSBOMFromURL_unparsableURL(t *testing.T) {
|
|
t.Parallel()
|
|
client, _, _ := setup(t)
|
|
|
|
sbom, err := client.DependencyGraph.fetchSBOMFromURL(t.Context(), http.DefaultClient, "\n")
|
|
if err == nil {
|
|
t.Error("DependencyGraph.fetchSBOMFromURL returned no error for an unparsable URL")
|
|
}
|
|
if sbom != nil {
|
|
t.Errorf("DependencyGraph.fetchSBOMFromURL returned %v, want nil", sbom)
|
|
}
|
|
}
|