// Copyright Project Harbor Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package v1 import ( "encoding/json" "fmt" "net/http" "net/http/httptest" "strings" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" ) // ClientTestSuite tests the v1 client type ClientTestSuite struct { suite.Suite testServer *httptest.Server client Client } // TestClient is the entry of ClientTestSuite func TestClient(t *testing.T) { suite.Run(t, new(ClientTestSuite)) } // SetupSuite prepares the test suite env func (suite *ClientTestSuite) SetupSuite() { suite.testServer = httptest.NewServer(&mockHandler{}) c, err := NewClient(suite.testServer.URL, "", "", true) require.NoError(suite.T(), err) require.NotNil(suite.T(), c) suite.client = c } // TestClientMetadata tests the metadata of the client func (suite *ClientTestSuite) TestClientMetadata() { m, err := suite.client.GetMetadata() require.NoError(suite.T(), err) require.NotNil(suite.T(), m) assert.Equal(suite.T(), m.Scanner.Name, "Trivy") assert.Equal(suite.T(), m.Capabilities[0].Type, "sbom") } // TestClientSubmitScan tests the scan submission of client func (suite *ClientTestSuite) TestClientSubmitScan() { res, err := suite.client.SubmitScan(&ScanRequest{}) require.NoError(suite.T(), err) require.NotNil(suite.T(), res) assert.Equal(suite.T(), res.ID, "123456789") } // TestClientGetScanReportError tests getting report failed func (suite *ClientTestSuite) TestClientGetScanReportError() { _, err := suite.client.GetScanReport("id1", MimeTypeNativeReport, "") require.Error(suite.T(), err) assert.Condition(suite.T(), func() (success bool) { success = strings.Index(err.Error(), "error") != -1 return }) } // TestClientGetScanReport tests getting report func (suite *ClientTestSuite) TestClientGetScanReport() { res, err := suite.client.GetScanReport("id2", MimeTypeNativeReport, "") require.NoError(suite.T(), err) require.NotEmpty(suite.T(), res) } // TestClientGetScanReportNotReady tests the case that the report is not ready func (suite *ClientTestSuite) TestClientGetScanReportNotReady() { _, err := suite.client.GetScanReport("id3", MimeTypeNativeReport, "") require.Error(suite.T(), err) require.Condition(suite.T(), func() (success bool) { _, success = err.(*ReportNotReadyError) return }) assert.Equal(suite.T(), 10, err.(*ReportNotReadyError).RetryAfter) } // TearDownSuite clears the test suite env func (suite *ClientTestSuite) TearDownSuite() { suite.testServer.Close() } type mockHandler struct{} // ServeHTTP ... func (mh *mockHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { switch r.RequestURI { case "/api/v1/metadata": if r.Method != http.MethodGet { w.WriteHeader(http.StatusForbidden) return } m := &ScannerAdapterMetadata{ Scanner: &Scanner{ Name: "Trivy", Vendor: "Harbor", Version: "0.1.0", }, Capabilities: []*ScannerCapability{{ Type: "sbom", ConsumesMimeTypes: []string{ MimeTypeOCIArtifact, MimeTypeDockerArtifact, }, ProducesMimeTypes: []string{ MimeTypeNativeReport, MimeTypeRawReport, MimeTypeGenericVulnerabilityReport, }, }}, Properties: ScannerProperties{ "extra": "testing", }, } data, _ := json.Marshal(m) w.WriteHeader(http.StatusOK) _, _ = w.Write(data) break case "/api/v1/scan": if r.Method != http.MethodPost { w.WriteHeader(http.StatusForbidden) return } res := &ScanResponse{} res.ID = "123456789" data, _ := json.Marshal(res) w.WriteHeader(http.StatusAccepted) _, _ = w.Write(data) break case "/api/v1/scan/id1/report": if r.Method != http.MethodGet { w.WriteHeader(http.StatusForbidden) return } e := &ErrorResponse{ &Error{ Message: "error", }, } data, _ := json.Marshal(e) w.WriteHeader(http.StatusNotFound) _, _ = w.Write(data) break case "/api/v1/scan/id2/report": if r.Method != http.MethodGet { w.WriteHeader(http.StatusForbidden) return } w.WriteHeader(http.StatusOK) _, _ = w.Write([]byte("{}")) break case "/api/v1/scan/id3/report": if r.Method != http.MethodGet { w.WriteHeader(http.StatusForbidden) return } w.Header().Add(refreshAfterHeader, fmt.Sprintf("%d", 10)) w.Header().Add("Location", "/scan/id3/report") w.WriteHeader(http.StatusFound) break } }