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") +} 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) +} 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{}) +}