From b7314d2f410ba49bc5a74b40eeb516e510116e13 Mon Sep 17 00:00:00 2001 From: Kairo de Araujo Date: Wed, 20 Dec 2023 15:01:42 +0100 Subject: [PATCH 1/3] tests: Include UT tests to pkg/api/store Add unit tests for `pkg/api/store` coverage as 84% Signed-off-by: Kairo de Araujo --- pkg/api/store_test.go | 209 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 209 insertions(+) create mode 100644 pkg/api/store_test.go diff --git a/pkg/api/store_test.go b/pkg/api/store_test.go new file mode 100644 index 00000000..6611e230 --- /dev/null +++ b/pkg/api/store_test.go @@ -0,0 +1,209 @@ +// Copyright 2023 The Archivista Contributors +// +// 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 api_test + +import ( + "context" + "encoding/json" + "net/http" + "net/http/httptest" + "os" + "testing" + + "github.com/in-toto/archivista/pkg/api" + "github.com/in-toto/go-witness/dsse" + "github.com/stretchr/testify/suite" +) + +// Test Suite: UT APIStore +type UTAPIStoreSuite struct { + suite.Suite +} + +func TestAPIStoreSuite(t *testing.T) { + suite.Run(t, new(UTAPIStoreSuite)) +} + +func (ut *UTAPIStoreSuite) Test_Store() { + + testServer := httptest.NewServer( + http.HandlerFunc( + func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + _, err := w.Write([]byte(`{"gitoid":"test"}`)) + if err != nil { + ut.FailNow(err.Error()) + } + }, + ), + ) + defer testServer.Close() + ctx := context.TODO() + + // load test valid test file and parse the dsse envelop + attFile, err := os.ReadFile("../../test/package.attestation.json") + if err != nil { + ut.FailNow(err.Error()) + } + attEnvelop := &dsse.Envelope{} + err = json.Unmarshal(attFile, &attEnvelop) + if err != nil { + ut.FailNow(err.Error()) + } + + // test api.Store happy flow + resp, err := api.Store(ctx, testServer.URL, *attEnvelop) + if err != nil { + ut.FailNow(err.Error()) + } + + ut.Equal(resp, api.StoreResponse{Gitoid: "test"}) +} + +func (ut *UTAPIStoreSuite) Test_StoreWithReader() { + + // mock server + testServer := httptest.NewServer( + http.HandlerFunc( + func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + _, err := w.Write([]byte(`{"gitoid":"test"}`)) + if err != nil { + ut.FailNow(err.Error()) + } + }, + ), + ) + defer testServer.Close() + + // io.Reader file + attIo, err := os.Open("../../test/package.attestation.json") + if err != nil { + ut.FailNow(err.Error()) + } + + // context + ctx := context.TODO() + + resp, err := api.StoreWithReader(ctx, testServer.URL, attIo) + if err != nil { + ut.FailNow(err.Error()) + } + ut.Equal(resp, api.StoreResponse{Gitoid: "test"}) +} + +func (ut *UTAPIStoreSuite) Test_StoreWithReader_NoServer() { + + // io.Reader file + attIo, err := os.Open("../../test/package.attestation.json") + if err != nil { + ut.FailNow(err.Error()) + } + + // context + ctx := context.TODO() + + resp, err := api.StoreWithReader(ctx, "http://invalid-archivista", attIo) + ut.Error(err) + ut.Equal(resp, api.StoreResponse{}) +} + +func (ut *UTAPIStoreSuite) Test_StoreWithReader_InvalidResponseBody() { + + // mock server + testServer := httptest.NewServer( + http.HandlerFunc( + func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + _, err := w.Write([]byte(`{"invalid":"invalid"}`)) + if err != nil { + ut.FailNow(err.Error()) + } + }, + ), + ) + defer testServer.Close() + + // io.Reader file + attIo, err := os.Open("../../test/package.attestation.json") + if err != nil { + ut.FailNow(err.Error()) + } + + // context + ctx := context.TODO() + + resp, err := api.StoreWithReader(ctx, testServer.URL, attIo) + if err != nil { + ut.FailNow(err.Error()) + } + ut.Equal(resp, api.StoreResponse{}) +} + +func (ut *UTAPIStoreSuite) Test_StoreWithReader_BadStatusCode() { + + // mock server + testServer := httptest.NewServer( + http.HandlerFunc( + func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusInternalServerError) + _, err := w.Write([]byte(`Internal Server Error`)) + if err != nil { + ut.FailNow(err.Error()) + } + }, + ), + ) + defer testServer.Close() + + // io.Reader file + attIo, err := os.Open("../../test/package.attestation.json") + if err != nil { + ut.FailNow(err.Error()) + } + + // context + ctx := context.TODO() + + resp, err := api.StoreWithReader(ctx, testServer.URL, attIo) + ut.ErrorContains(err, "Internal Server Error") + ut.Equal(resp, api.StoreResponse{}) +} + +func (ut *UTAPIStoreSuite) Test_StoreWithReader_BadJSONBody() { + + // mock server + testServer := httptest.NewServer( + http.HandlerFunc( + func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + }, + ), + ) + defer testServer.Close() + + // io.Reader file + attIo, err := os.Open("../../test/package.attestation.json") + if err != nil { + ut.FailNow(err.Error()) + } + + // context + ctx := context.TODO() + + resp, err := api.StoreWithReader(ctx, testServer.URL, attIo) + ut.ErrorContains(err, "unexpected end of JSON input") + ut.Equal(resp, api.StoreResponse{}) +} From 477fe51d365beed544b9178993f156f935681699 Mon Sep 17 00:00:00 2001 From: Kairo de Araujo Date: Wed, 20 Dec 2023 17:00:04 +0100 Subject: [PATCH 2/3] tests: Include UT tests to pkg/api/download Add unit tests for `pkg/api/download` coverage as 85% The UT found a bug in the `DownloadWithWriter`. This bug returns nil instead of error and affects users while downloading envelopes. For example, the `archivistactl retrieve envelope` command, if giving an invalid server or if the server is unavailable, will return no error. In the `archivistactl`, the user will behave the same when the server is available, and there is no envelope with the expected `gitoid`. Signed-off-by: Kairo de Araujo --- cmd/archivistactl/cmd/retrieve_test.go | 5 +- pkg/api/download.go | 2 +- pkg/api/download_test.go | 172 +++++++++++++++++++++++++ 3 files changed, 176 insertions(+), 3 deletions(-) create mode 100644 pkg/api/download_test.go diff --git a/cmd/archivistactl/cmd/retrieve_test.go b/cmd/archivistactl/cmd/retrieve_test.go index 2642ff1b..1c3c587a 100644 --- a/cmd/archivistactl/cmd/retrieve_test.go +++ b/cmd/archivistactl/cmd/retrieve_test.go @@ -55,16 +55,17 @@ func (ut *UTRetrieveSuite) Test_RetrieveEnvelopeMissingArg() { } } -func (ut *UTRetrieveSuite) Test_RetrieveEnvelope() { +func (ut *UTRetrieveSuite) Test_RetrieveEnvelope_NoDB() { output := bytes.NewBufferString("") rootCmd.SetOut(output) rootCmd.SetErr(output) rootCmd.SetArgs([]string{"retrieve", "envelope", "test"}) err := rootCmd.Execute() if err != nil { + ut.ErrorContains(err, "connection refused") + } else { ut.FailNow("Expected: error") } - ut.Equal(output.String(), "") } func (ut *UTRetrieveSuite) Test_RetrieveSubjectsMissingArg() { diff --git a/pkg/api/download.go b/pkg/api/download.go index f6f86149..06c67883 100644 --- a/pkg/api/download.go +++ b/pkg/api/download.go @@ -56,7 +56,7 @@ func DownloadWithWriter(ctx context.Context, baseUrl, gitoid string, dst io.Writ hc := &http.Client{} resp, err := hc.Do(req) if err != nil { - return nil + return err } defer resp.Body.Close() diff --git a/pkg/api/download_test.go b/pkg/api/download_test.go new file mode 100644 index 00000000..1190a218 --- /dev/null +++ b/pkg/api/download_test.go @@ -0,0 +1,172 @@ +// Copyright 2023 The Archivista Contributors +// +// 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 api_test + +import ( + "context" + "encoding/json" + "io" + "net/http" + "net/http/httptest" + "os" + "path" + "testing" + + "github.com/in-toto/archivista/pkg/api" + "github.com/in-toto/go-witness/dsse" + "github.com/stretchr/testify/suite" +) + +// Test Suite: UT APIDownload +type UTAPIDownloadSuite struct { + suite.Suite +} + +func TestAPIDownloadSuite(t *testing.T) { + suite.Run(t, new(UTAPIDownloadSuite)) +} + +func (ut *UTAPIDownloadSuite) Test_Download() { + + testEnvelope, err := os.ReadFile("../../test/package.attestation.json") + if err != nil { + ut.FailNow(err.Error()) + } + expectedEnvelop := dsse.Envelope{} + err = json.Unmarshal(testEnvelope, &expectedEnvelop) + if err != nil { + ut.FailNow(err.Error()) + } + + testServer := httptest.NewServer( + http.HandlerFunc( + func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + _, err = w.Write(testEnvelope) + if err != nil { + ut.FailNow(err.Error()) + } + }, + ), + ) + defer testServer.Close() + ctx := context.TODO() + + // test api.Download happy flow + resp, err := api.Download(ctx, testServer.URL, "gitoid_test") + if err != nil { + ut.FailNow(err.Error()) + } + + ut.Equal(expectedEnvelop, resp) +} + +func (ut *UTAPIDownloadSuite) Test_Download_DecodeFailure() { + testServer := httptest.NewServer( + http.HandlerFunc( + func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + _, err := w.Write([]byte(`invalid`)) + if err != nil { + ut.FailNow(err.Error()) + } + }, + ), + ) + defer testServer.Close() + ctx := context.TODO() + + // test api.Download happy flow + resp, err := api.Download(ctx, testServer.URL, "gitoid_test") + ut.Error(err) + ut.Equal(dsse.Envelope{}, resp) +} + +func (ut *UTAPIDownloadSuite) Test_DownloadWithReader() { + + // mock server + expected := `{"body":"body"}` + testServer := httptest.NewServer( + http.HandlerFunc( + func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + _, err := w.Write([]byte(expected)) + if err != nil { + ut.FailNow(err.Error()) + } + }, + ), + ) + defer testServer.Close() + + // context + ctx := context.TODO() + + // temporary file + tempDir := os.TempDir() + dst, err := os.Create(path.Join(tempDir, "test")) + if err != nil { + ut.FailNow(err.Error()) + } + err = api.DownloadWithWriter(ctx, testServer.URL, "gitoid", dst) + if err != nil { + ut.FailNow(err.Error()) + } + + // validate result + result, err := os.ReadFile(path.Join(tempDir, "test")) + if err != nil { + ut.FailNow(err.Error()) + } + ut.Equal(expected, string(result)) +} + +func (ut *UTAPIDownloadSuite) Test_DownloadWithWriter_NoServer() { + + // context + ctx := context.TODO() + + // dst as stdout + var dst io.Writer = os.Stdout + + err := api.DownloadWithWriter(ctx, "http://invalid-archivista", "gitoid_test", dst) + ut.Error(err) +} + +func (ut *UTAPIDownloadSuite) Test_DownloadWithWriter_BadStatusCode() { + + // mock server + testServer := httptest.NewServer( + http.HandlerFunc( + func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusInternalServerError) + _, err := w.Write([]byte(`Internal Server Error`)) + if err != nil { + ut.FailNow(err.Error()) + } + }, + ), + ) + defer testServer.Close() + + // dst as stdout + var dst io.Writer = os.Stdout + + // context + ctx := context.TODO() + + err := api.DownloadWithWriter(ctx, testServer.URL, "gitoid_test", dst) + ut.ErrorContains(err, "Internal Server Error") +} From 8d28176410d59a89b5ca1a86a04fd16ada261be5 Mon Sep 17 00:00:00 2001 From: Kairo de Araujo Date: Thu, 21 Dec 2023 13:58:36 +0100 Subject: [PATCH 3/3] tests: Include UT tests to pkg/api/graphql Add unit tests for `pkg/api/graphql` coverage as 86% Signed-off-by: Kairo de Araujo --- pkg/api/graphql_test.go | 161 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 161 insertions(+) create mode 100644 pkg/api/graphql_test.go diff --git a/pkg/api/graphql_test.go b/pkg/api/graphql_test.go new file mode 100644 index 00000000..30948db2 --- /dev/null +++ b/pkg/api/graphql_test.go @@ -0,0 +1,161 @@ +// Copyright 2023 The Archivista Contributors +// +// 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 api_test + +import ( + "context" + "net/http" + "net/http/httptest" + "testing" + + "github.com/in-toto/archivista/pkg/api" + "github.com/stretchr/testify/suite" +) + +// Test Suite: UT APIStore +type UTAPIGraphQLSuite struct { + suite.Suite +} + +func TestAPIGraphQLSuite(t *testing.T) { + suite.Run(t, new(UTAPIGraphQLSuite)) +} + +func (ut *UTAPIGraphQLSuite) Test_Store() { + + testServer := httptest.NewServer( + http.HandlerFunc( + func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + _, err := w.Write([]byte(`{"data": {"data": "test"}}`)) + if err != nil { + ut.FailNow(err.Error()) + } + }, + ), + ) + defer testServer.Close() + ctx := context.TODO() + + type testSubjectVar struct { + Gitoid string `json:"gitoid"` + } + + type testSubjectResult struct { + Data string `json:"data"` + } + result, err := api.GraphQlQuery[testSubjectResult](ctx, testServer.URL, `query`, testSubjectVar{Gitoid: "test_Gitoid"}) + ut.NoError(err) + ut.Equal(testSubjectResult{Data: "test"}, result) +} + +func (ut *UTAPIGraphQLSuite) Test_Store_NoServer() { + + ctx := context.TODO() + + type testSubjectVar struct { + Gitoid string `json:"gitoid"` + } + + type testSubjectResult struct { + Data string `json:"data"` + } + result, err := api.GraphQlQuery[testSubjectResult](ctx, "http://invalid-archivista", `query`, testSubjectVar{Gitoid: "test_Gitoid"}) + ut.Error(err) + ut.Equal(testSubjectResult{Data: ""}, result) +} + +func (ut *UTAPIGraphQLSuite) Test_Store_BadStatusCode_NoMsg() { + + testServer := httptest.NewServer( + http.HandlerFunc( + func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusInternalServerError) + }, + ), + ) + defer testServer.Close() + ctx := context.TODO() + + type testSubjectVar struct { + Gitoid string `json:"gitoid"` + } + + type testSubjectResult struct { + Data string `json:"data"` + } + result, err := api.GraphQlQuery[testSubjectResult](ctx, testServer.URL, `query`, testSubjectVar{Gitoid: "test_Gitoid"}) + ut.Error(err) + ut.Equal(testSubjectResult{Data: ""}, result) +} + +func (ut *UTAPIGraphQLSuite) Test_Store_InvalidData() { + + testServer := httptest.NewServer( + http.HandlerFunc( + func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + _, err := w.Write([]byte(``)) + if err != nil { + ut.FailNow(err.Error()) + } + }, + ), + ) + defer testServer.Close() + ctx := context.TODO() + + type testSubjectVar struct { + Gitoid string `json:"gitoid"` + } + + type testSubjectResult struct { + Data string `json:"data"` + } + result, err := api.GraphQlQuery[testSubjectResult](ctx, testServer.URL, `query`, testSubjectVar{Gitoid: "test_Gitoid"}) + ut.Error(err) + ut.Equal(testSubjectResult{Data: ""}, result) +} + +func (ut *UTAPIGraphQLSuite) Test_Store_QLReponseWithErrors() { + + testServer := httptest.NewServer( + http.HandlerFunc( + func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + _, err := w.Write([]byte(`{"data": {"data": "test"}, "errors": [{"message": "test_error"}]}}`)) + if err != nil { + ut.FailNow(err.Error()) + } + }, + ), + ) + defer testServer.Close() + ctx := context.TODO() + + type testSubjectVar struct { + Gitoid string `json:"gitoid"` + } + + type testSubjectResult struct { + Data string `json:"data"` + Errors string `json:"errors"` + } + + result, err := api.GraphQlQuery[testSubjectResult](ctx, testServer.URL, `query`, testSubjectVar{Gitoid: "test_Gitoid"}) + ut.Error(err) + ut.EqualError(err, "graph ql query failed: [{test_error}]") + ut.Equal(testSubjectResult{Data: ""}, result) +}