From cc2278086faef1117e378702ceb094aafb8eb054 Mon Sep 17 00:00:00 2001 From: Michael McGuinness Date: Tue, 27 May 2025 15:28:46 +0000 Subject: [PATCH] Merged in feature/import (pull request #157) Feature/import * importstart * pp * tests * importtests * 100GB * lint * passtests * utc * awsprofile * doublequotes * host --- api/queryAPI/api.gen.go | 185 +++++--- api/queryAPI/client_test.go | 23 +- api/queryAPI/collector_test.go | 2 +- api/queryAPI/controllers.go | 20 +- api/queryAPI/documents.go | 36 ++ api/queryAPI/documents_test.go | 45 +- api/queryAPI/export_test.go | 9 +- api/queryAPI/query_test.go | 36 +- api/queryAPI/status_test.go | 18 +- bitbucket-pipelines.yml | 3 +- cmd/queryAPI/main.go | 46 +- go.mod | 2 - go.sum | 2 - .../00000000000005_documents.up.sql | 10 + internal/database/queries/document.sql | 23 + internal/database/repository/document.sql.go | 87 ++++ internal/database/repository/models.go | 9 + internal/document/upload/get.go | 73 +++ internal/document/upload/get_test.go | 45 ++ internal/document/upload/service.go | 21 + internal/serviceconfig/common.go | 4 +- internal/serviceconfig/objectstore/config.go | 11 + .../serviceconfig/objectstore/config_test.go | 6 +- internal/test/api.go | 4 +- internal/test/container.go | 91 +--- internal/test/container_test.go | 18 - internal/test/ecosystem.go | 9 +- internal/test/objectstore.go | 8 +- internal/test/objectstore_test.go | 3 +- internal/test/queryAPI/service.go | 32 ++ internal/test/runner.go | 11 +- mocks/queryapi/mock_ClientInterface.go | 76 ++++ .../mock_ClientWithResponsesInterface.go | 76 ++++ pkg/queryAPI/api.gen.go | 149 +++++- serviceAPIs/queryAPI.yaml | 50 ++- test/process_test.go | 77 +--- .../github.com/gruntwork-io/terratest/LICENSE | 202 --------- .../github.com/gruntwork-io/terratest/NOTICE | 4 - .../modules/collections/collections.go | 2 - .../terratest/modules/collections/errors.go | 17 - .../terratest/modules/collections/lists.go | 40 -- .../modules/collections/stringslicevalue.go | 27 -- .../terratest/modules/docker/build.go | 222 --------- .../terratest/modules/docker/docker.go | 2 - .../modules/docker/docker_compose.go | 99 ---- .../terratest/modules/docker/host.go | 40 -- .../terratest/modules/docker/images.go | 112 ----- .../terratest/modules/docker/inspect.go | 270 ----------- .../terratest/modules/docker/push.go | 25 -- .../terratest/modules/docker/run.go | 166 ------- .../terratest/modules/docker/stop.go | 59 --- .../terratest/modules/logger/logger.go | 178 -------- .../terratest/modules/shell/command.go | 252 ----------- .../terratest/modules/shell/output.go | 92 ---- .../terratest/modules/shell/shell.go | 2 - .../terratest/modules/testing/types.go | 28 -- vendor/gotest.tools/v3/LICENSE | 13 - vendor/gotest.tools/v3/assert/assert.go | 313 ------------- vendor/gotest.tools/v3/assert/cmp/compare.go | 404 ----------------- vendor/gotest.tools/v3/assert/cmp/result.go | 110 ----- vendor/gotest.tools/v3/icmd/command.go | 295 ------------ vendor/gotest.tools/v3/icmd/exitcode.go | 23 - vendor/gotest.tools/v3/icmd/ops.go | 60 --- .../gotest.tools/v3/internal/assert/assert.go | 161 ------- .../gotest.tools/v3/internal/assert/result.go | 146 ------ .../gotest.tools/v3/internal/difflib/LICENSE | 27 -- .../v3/internal/difflib/difflib.go | 423 ------------------ .../gotest.tools/v3/internal/format/diff.go | 162 ------- .../gotest.tools/v3/internal/format/format.go | 27 -- .../gotest.tools/v3/internal/source/defers.go | 52 --- .../gotest.tools/v3/internal/source/source.go | 148 ------ .../gotest.tools/v3/internal/source/update.go | 171 ------- .../v3/internal/source/version.go | 35 -- vendor/modules.txt | 16 - 74 files changed, 992 insertions(+), 4753 deletions(-) create mode 100644 internal/document/upload/get.go create mode 100644 internal/document/upload/get_test.go create mode 100644 internal/document/upload/service.go delete mode 100644 internal/test/container_test.go delete mode 100644 vendor/github.com/gruntwork-io/terratest/LICENSE delete mode 100644 vendor/github.com/gruntwork-io/terratest/NOTICE delete mode 100644 vendor/github.com/gruntwork-io/terratest/modules/collections/collections.go delete mode 100644 vendor/github.com/gruntwork-io/terratest/modules/collections/errors.go delete mode 100644 vendor/github.com/gruntwork-io/terratest/modules/collections/lists.go delete mode 100644 vendor/github.com/gruntwork-io/terratest/modules/collections/stringslicevalue.go delete mode 100644 vendor/github.com/gruntwork-io/terratest/modules/docker/build.go delete mode 100644 vendor/github.com/gruntwork-io/terratest/modules/docker/docker.go delete mode 100644 vendor/github.com/gruntwork-io/terratest/modules/docker/docker_compose.go delete mode 100644 vendor/github.com/gruntwork-io/terratest/modules/docker/host.go delete mode 100644 vendor/github.com/gruntwork-io/terratest/modules/docker/images.go delete mode 100644 vendor/github.com/gruntwork-io/terratest/modules/docker/inspect.go delete mode 100644 vendor/github.com/gruntwork-io/terratest/modules/docker/push.go delete mode 100644 vendor/github.com/gruntwork-io/terratest/modules/docker/run.go delete mode 100644 vendor/github.com/gruntwork-io/terratest/modules/docker/stop.go delete mode 100644 vendor/github.com/gruntwork-io/terratest/modules/logger/logger.go delete mode 100644 vendor/github.com/gruntwork-io/terratest/modules/shell/command.go delete mode 100644 vendor/github.com/gruntwork-io/terratest/modules/shell/output.go delete mode 100644 vendor/github.com/gruntwork-io/terratest/modules/shell/shell.go delete mode 100644 vendor/github.com/gruntwork-io/terratest/modules/testing/types.go delete mode 100644 vendor/gotest.tools/v3/LICENSE delete mode 100644 vendor/gotest.tools/v3/assert/assert.go delete mode 100644 vendor/gotest.tools/v3/assert/cmp/compare.go delete mode 100644 vendor/gotest.tools/v3/assert/cmp/result.go delete mode 100644 vendor/gotest.tools/v3/icmd/command.go delete mode 100644 vendor/gotest.tools/v3/icmd/exitcode.go delete mode 100644 vendor/gotest.tools/v3/icmd/ops.go delete mode 100644 vendor/gotest.tools/v3/internal/assert/assert.go delete mode 100644 vendor/gotest.tools/v3/internal/assert/result.go delete mode 100644 vendor/gotest.tools/v3/internal/difflib/LICENSE delete mode 100644 vendor/gotest.tools/v3/internal/difflib/difflib.go delete mode 100644 vendor/gotest.tools/v3/internal/format/diff.go delete mode 100644 vendor/gotest.tools/v3/internal/format/format.go delete mode 100644 vendor/gotest.tools/v3/internal/source/defers.go delete mode 100644 vendor/gotest.tools/v3/internal/source/source.go delete mode 100644 vendor/gotest.tools/v3/internal/source/update.go delete mode 100644 vendor/gotest.tools/v3/internal/source/version.go diff --git a/api/queryAPI/api.gen.go b/api/queryAPI/api.gen.go index 6c843e1e..40d326f2 100644 --- a/api/queryAPI/api.gen.go +++ b/api/queryAPI/api.gen.go @@ -351,6 +351,15 @@ type TooManyRequests = ErrorMessage // Unauthorized Description of error type Unauthorized = ErrorMessage +// UploadDocumentMultipartBody defines parameters for UploadDocument. +type UploadDocumentMultipartBody struct { + // File The file to upload + File openapi_types.File `json:"file"` + + // Filename Optional custom filename + Filename *string `json:"filename,omitempty"` +} + // LoginCallbackParams defines parameters for LoginCallback. type LoginCallbackParams struct { // Code Authorization code from Cognito @@ -369,6 +378,9 @@ type UpdateClientJSONRequestBody = ClientUpdate // SetCollectorByClientIdJSONRequestBody defines body for SetCollectorByClientId for application/json ContentType. type SetCollectorByClientIdJSONRequestBody = CollectorSet +// UploadDocumentMultipartRequestBody defines body for UploadDocument for multipart/form-data ContentType. +type UploadDocumentMultipartRequestBody UploadDocumentMultipartBody + // TriggerExportJSONRequestBody defines body for TriggerExport for application/json ContentType. type TriggerExportJSONRequestBody = ExportTrigger @@ -399,8 +411,11 @@ type ServerInterface interface { // (PATCH /client/{id}/collector) SetCollectorByClientId(ctx echo.Context, id ClientID) error // List the documents for a client - // (GET /client/{id}/documents) + // (GET /client/{id}/document) ListDocumentsByClientId(ctx echo.Context, id ClientID) error + // Upload a file + // (POST /client/{id}/document) + UploadDocument(ctx echo.Context, id ClientID) error // Trigger an export // (POST /client/{id}/export) TriggerExport(ctx echo.Context, id ClientID) error @@ -548,6 +563,24 @@ func (w *ServerInterfaceWrapper) ListDocumentsByClientId(ctx echo.Context) error return err } +// UploadDocument converts echo context to params. +func (w *ServerInterfaceWrapper) UploadDocument(ctx echo.Context) error { + var err error + // ------------- Path parameter "id" ------------- + var id ClientID + + err = runtime.BindStyledParameterWithOptions("simple", "id", ctx.Param("id"), &id, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter id: %s", err)) + } + + ctx.Set(JwtAuthScopes, []string{}) + + // Invoke the callback with all the unmarshaled arguments + err = w.Handler.UploadDocument(ctx, id) + return err +} + // TriggerExport converts echo context to params. func (w *ServerInterfaceWrapper) TriggerExport(ctx echo.Context) error { var err error @@ -787,7 +820,8 @@ func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL router.PATCH(baseURL+"/client/:id", wrapper.UpdateClient) router.GET(baseURL+"/client/:id/collector", wrapper.GetCollectorByClientId) router.PATCH(baseURL+"/client/:id/collector", wrapper.SetCollectorByClientId) - router.GET(baseURL+"/client/:id/documents", wrapper.ListDocumentsByClientId) + router.GET(baseURL+"/client/:id/document", wrapper.ListDocumentsByClientId) + router.POST(baseURL+"/client/:id/document", wrapper.UploadDocument) router.POST(baseURL+"/client/:id/export", wrapper.TriggerExport) router.GET(baseURL+"/client/:id/status", wrapper.GetStatusByClientId) router.GET(baseURL+"/document/:id", wrapper.GetDocument) @@ -807,78 +841,81 @@ func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL // Base64 encoded, gzipped, json marshaled Swagger object var swaggerSpec = []string{ - "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", - "8jpSZwAA", + "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", } // GetSwagger returns the content of the embedded swagger specification file diff --git a/api/queryAPI/client_test.go b/api/queryAPI/client_test.go index e3eb0dd7..f7d36ec6 100644 --- a/api/queryAPI/client_test.go +++ b/api/queryAPI/client_test.go @@ -1,16 +1,14 @@ package queryapi_test import ( + "bytes" "encoding/json" "net/http" "net/http/httptest" - "strings" "testing" queryapi "queryorchestration/api/queryAPI" "queryorchestration/internal/database/repository" - "queryorchestration/internal/serviceconfig" - "queryorchestration/internal/serviceconfig/queue/clientsync" "queryorchestration/internal/test" "github.com/labstack/echo/v4" @@ -18,11 +16,6 @@ import ( "github.com/stretchr/testify/require" ) -type ClientConfig struct { - serviceconfig.BaseConfig - clientsync.ClientSyncConfig -} - func TestCreateClient(t *testing.T) { t.Parallel() cfg := &ControllerConfig{} @@ -36,7 +29,7 @@ func TestCreateClient(t *testing.T) { Id: "external_id", } - ctx, rec := createContextWithBody(t, body) + ctx, rec := createContextWithJSONBody(t, body) err := cons.CreateClient(ctx) require.NoError(t, err) @@ -105,7 +98,7 @@ func TestUpdateClient(t *testing.T) { Name: &newClientName, } - ctx, rec := createContextWithBody(t, body) + ctx, rec := createContextWithJSONBody(t, body) err = cons.UpdateClient(ctx, id) require.NoError(t, err) @@ -118,15 +111,19 @@ func TestUpdateClient(t *testing.T) { } func createContext(t testing.TB) (echo.Context, *httptest.ResponseRecorder) { - return createContextWithBody(t, struct{}{}) + return createContextWithJSONBody(t, struct{}{}) } -func createContextWithBody(t testing.TB, body interface{}) (echo.Context, *httptest.ResponseRecorder) { +func createContextWithJSONBody(t testing.TB, body interface{}) (echo.Context, *httptest.ResponseRecorder) { bodyBytes, err := json.Marshal(body) require.NoError(t, err) + return createContextWithBody(t, bodyBytes) +} + +func createContextWithBody(t testing.TB, body []byte) (echo.Context, *httptest.ResponseRecorder) { e := echo.New() - req := httptest.NewRequest(http.MethodPost, "/", strings.NewReader(string(bodyBytes))) + req := httptest.NewRequest(http.MethodPost, "/", bytes.NewReader(body)) req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON) rec := httptest.NewRecorder() return e.NewContext(req, rec), rec diff --git a/api/queryAPI/collector_test.go b/api/queryAPI/collector_test.go index fd14c43a..b4658205 100644 --- a/api/queryAPI/collector_test.go +++ b/api/queryAPI/collector_test.go @@ -48,7 +48,7 @@ func TestSetCollector(t *testing.T) { }, }, } - ctx, rec := createContextWithBody(t, body) + ctx, rec := createContextWithJSONBody(t, body) err = cons.SetCollectorByClientId(ctx, id) require.NoError(t, err) diff --git a/api/queryAPI/controllers.go b/api/queryAPI/controllers.go index ea1c8b5e..dbf26738 100644 --- a/api/queryAPI/controllers.go +++ b/api/queryAPI/controllers.go @@ -6,6 +6,7 @@ import ( "queryorchestration/internal/collector" collectorset "queryorchestration/internal/collector/set" "queryorchestration/internal/document" + documentupload "queryorchestration/internal/document/upload" "queryorchestration/internal/export" "queryorchestration/internal/query" querytest "queryorchestration/internal/query/test" @@ -15,15 +16,16 @@ import ( const Name = "queryAPI" type Services struct { - Export *export.Service - Collector *collector.Service - CollectorSet *collectorset.Service - Query *query.Service - QueryUpdate *queryupdate.Service - QueryTest *querytest.Service - Client *client.Service - ClientUpdate *clientupdate.Service - Document *document.Service + Export *export.Service + Collector *collector.Service + CollectorSet *collectorset.Service + Query *query.Service + QueryUpdate *queryupdate.Service + QueryTest *querytest.Service + Client *client.Service + ClientUpdate *clientupdate.Service + Document *document.Service + DocumentUpload *documentupload.Service } type Controllers struct { diff --git a/api/queryAPI/documents.go b/api/queryAPI/documents.go index 3aded9aa..d294b184 100644 --- a/api/queryAPI/documents.go +++ b/api/queryAPI/documents.go @@ -2,11 +2,47 @@ package queryapi import ( "fmt" + "log/slog" "net/http" + documentupload "queryorchestration/internal/document/upload" + "github.com/labstack/echo/v4" ) +func (s *Controllers) UploadDocument(ctx echo.Context, clientId ClientID) error { + form, err := ctx.MultipartForm() + if err != nil { + slog.Error("unable to get form data", "error", err) + return echo.NewHTTPError(http.StatusBadRequest, "unable to get form data") + } + + files := form.File["file"] + if len(files) == 0 { + return echo.NewHTTPError(http.StatusBadRequest, "missing file") + } else if len(files) > 1 { + return echo.NewHTTPError(http.StatusBadRequest, "too many files") + } + file := files[0] + + src, err := file.Open() + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, "unable to read file") + } + defer src.Close() + + err = s.svc.DocumentUpload.Upload(ctx.Request().Context(), documentupload.File{ + ClientID: clientId, + Content: src, + }) + if err != nil { + slog.Error("unable to get form data", "error", err) + return echo.NewHTTPError(http.StatusBadRequest, "unable to upload file") + } + + return ctx.NoContent(http.StatusOK) +} + func (s *Controllers) ListDocumentsByClientId(ctx echo.Context, clientId ClientID) error { documents, err := s.svc.Document.ListByClient(ctx.Request().Context(), clientId) if err != nil { diff --git a/api/queryAPI/documents_test.go b/api/queryAPI/documents_test.go index d7c29e89..12380f63 100644 --- a/api/queryAPI/documents_test.go +++ b/api/queryAPI/documents_test.go @@ -1,17 +1,60 @@ package queryapi_test import ( + "bytes" + "mime/multipart" "net/http" + "path/filepath" "testing" - queryapi "queryorchestration/api/queryAPI" "queryorchestration/internal/database/repository" "queryorchestration/internal/test" + queryapi "queryorchestration/api/queryAPI" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) +func TestUploadDocument(t *testing.T) { + t.Parallel() + cfg := &ControllerConfig{} + test.CreateDB(t, cfg) + acfg := test.CreateAWSContainer(t, cfg) + + test.SetStoreClient(t, t.Context(), cfg, acfg.ExternalEndpoint) + test.CreateBucket(t, cfg) + + svc := createControllerServices(cfg) + cons := queryapi.NewControllers(svc) + + err := cfg.GetDBQueries().CreateClient(t.Context(), &repository.CreateClientParams{ + Clientid: "client_id", + Name: "client_name", + }) + require.NoError(t, err) + + var buf bytes.Buffer + writer := multipart.NewWriter(&buf) + + fileWriter, err := writer.CreateFormFile("file", filepath.Base("../../assets/original/helloWorld.pdf")) + require.NoError(t, err) + + _, err = fileWriter.Write([]byte("hi")) + require.NoError(t, err) + + err = writer.Close() + require.NoError(t, err) + + ctx, rec := createContextWithBody(t, buf.Bytes()) + + ctx.Request().Header.Set("Content-Type", writer.FormDataContentType()) + + err = cons.UploadDocument(ctx, "client_id") + require.NoError(t, err) + assert.Equal(t, http.StatusOK, rec.Code) +} + func TestListDocumentsByClientId(t *testing.T) { t.Parallel() cfg := &ControllerConfig{} diff --git a/api/queryAPI/export_test.go b/api/queryAPI/export_test.go index 21fd6c8c..d60cda22 100644 --- a/api/queryAPI/export_test.go +++ b/api/queryAPI/export_test.go @@ -13,8 +13,10 @@ import ( func TestTriggerExport(t *testing.T) { ctx, rec := createContext(t) + cfg := &ControllerConfig{} - cons := queryapi.NewControllers(&queryapi.Services{}) + svc := createControllerServices(cfg) + cons := queryapi.NewControllers(svc) err := cons.TriggerExport(ctx, "clientid") require.NoError(t, err) @@ -25,7 +27,10 @@ func TestTriggerExport(t *testing.T) { func TestExportState(t *testing.T) { ctx, rec := createContext(t) - cons := queryapi.NewControllers(&queryapi.Services{}) + cfg := &ControllerConfig{} + + svc := createControllerServices(cfg) + cons := queryapi.NewControllers(svc) id := uuid.New() diff --git a/api/queryAPI/query_test.go b/api/queryAPI/query_test.go index de48e28a..0e4e3e9f 100644 --- a/api/queryAPI/query_test.go +++ b/api/queryAPI/query_test.go @@ -7,19 +7,13 @@ import ( "testing" "time" - queryapi "queryorchestration/api/queryAPI" "queryorchestration/internal/client" - clientupdate "queryorchestration/internal/client/update" "queryorchestration/internal/collector" - collectorset "queryorchestration/internal/collector/set" "queryorchestration/internal/database/repository" "queryorchestration/internal/document" "queryorchestration/internal/export" "queryorchestration/internal/query" "queryorchestration/internal/query/result" - resultprocessor "queryorchestration/internal/query/result/processor" - querytest "queryorchestration/internal/query/test" - queryupdate "queryorchestration/internal/query/update" "queryorchestration/internal/serviceconfig" "queryorchestration/internal/serviceconfig/objectstore" "queryorchestration/internal/serviceconfig/queue" @@ -27,6 +21,18 @@ import ( "queryorchestration/internal/serviceconfig/queue/queryversionsync" "queryorchestration/internal/test" + queryapi "queryorchestration/api/queryAPI" + + clientupdate "queryorchestration/internal/client/update" + + collectorset "queryorchestration/internal/collector/set" + + documentupload "queryorchestration/internal/document/upload" + + resultprocessor "queryorchestration/internal/query/result/processor" + querytest "queryorchestration/internal/query/test" + queryupdate "queryorchestration/internal/query/update" + "github.com/jackc/pgx/v5/pgtype" "github.com/stretchr/testify/require" @@ -36,17 +42,16 @@ import ( func TestCreateQuery(t *testing.T) { t.Parallel() - cfg := &serviceconfig.BaseConfig{} + cfg := &ControllerConfig{} test.CreateDB(t, cfg) - cons := queryapi.NewControllers(&queryapi.Services{ - Query: query.New(cfg), - }) + svc := createControllerServices(cfg) + cons := queryapi.NewControllers(svc) body := queryapi.QueryCreate{ Type: queryapi.CONTEXTFULL, } - ctx, rec := createContextWithBody(t, body) + ctx, rec := createContextWithJSONBody(t, body) err := cons.CreateQuery(ctx) require.NoError(t, err) @@ -128,7 +133,7 @@ func TestUpdateQuery(t *testing.T) { ActiveVersion: &av, Config: &c, } - ctx, rec := createContextWithBody(t, body) + ctx, rec := createContextWithJSONBody(t, body) err = cons.UpdateQuery(ctx, id) require.NoError(t, err) @@ -172,7 +177,8 @@ func createControllerServices(cfg *ControllerConfig) *queryapi.Services { ClientUpdate: clientupdate.New(cfg, &clientupdate.Services{ Client: cli, }), - Document: docsvc, + Document: docsvc, + DocumentUpload: documentupload.New(cfg), } } @@ -269,7 +275,7 @@ func TestTestQuery(t *testing.T) { DocumentId: docId, QueryVersion: 1, } - ctx, rec := createContextWithBody(t, body) + ctx, rec := createContextWithJSONBody(t, body) err = cons.TestQuery(ctx, queryId) require.NoError(t, err) @@ -282,7 +288,7 @@ func TestTestQuery(t *testing.T) { DocumentId: docId, QueryVersion: 2, } - ctx, rec = createContextWithBody(t, body) + ctx, rec = createContextWithJSONBody(t, body) err = cons.TestQuery(ctx, queryId) require.NoError(t, err) diff --git a/api/queryAPI/status_test.go b/api/queryAPI/status_test.go index f1877ed8..0e76dc83 100644 --- a/api/queryAPI/status_test.go +++ b/api/queryAPI/status_test.go @@ -5,9 +5,7 @@ import ( "testing" queryapi "queryorchestration/api/queryAPI" - "queryorchestration/internal/client" "queryorchestration/internal/database/repository" - "queryorchestration/internal/export" "queryorchestration/internal/test" "github.com/stretchr/testify/assert" @@ -16,13 +14,11 @@ import ( func TestGetClientStatus(t *testing.T) { t.Parallel() - cfg := &ClientConfig{} + cfg := &ControllerConfig{} test.CreateDB(t, cfg) - cons := queryapi.NewControllers(&queryapi.Services{ - Client: client.New(cfg), - Export: export.New(), - }) + svc := createControllerServices(cfg) + cons := queryapi.NewControllers(svc) ctx, rec := createContext(t) @@ -48,13 +44,11 @@ func TestGetClientStatus(t *testing.T) { } func BenchmarkGetClientStatus(b *testing.B) { - cfg := &ClientConfig{} + cfg := &ControllerConfig{} test.CreateDB(b, cfg) - cons := queryapi.NewControllers(&queryapi.Services{ - Client: client.New(cfg), - Export: export.New(), - }) + svc := createControllerServices(cfg) + cons := queryapi.NewControllers(svc) ctx, _ := createContext(b) diff --git a/bitbucket-pipelines.yml b/bitbucket-pipelines.yml index cb1edd69..c6f0ca75 100644 --- a/bitbucket-pipelines.yml +++ b/bitbucket-pipelines.yml @@ -23,6 +23,7 @@ definitions: - apt-cache script: - export DOCKER_BUILDKIT=0 + - export DOCKER_HOST=tcp://localhost:2375 - export TESTCONTAINERS_DOCKER_SOCKET_OVERRIDE=tcp://localhost:2375 - export TESTCONTAINERS_RYUK_DISABLED=true @@ -34,7 +35,7 @@ definitions: - touch .env - devbox install - - devbox run -- task fullsuite + - devbox run -- AWS_PROFILE="" task fullsuite pipelines: branches: diff --git a/cmd/queryAPI/main.go b/cmd/queryAPI/main.go index 2ce726c4..2b9c376b 100644 --- a/cmd/queryAPI/main.go +++ b/cmd/queryAPI/main.go @@ -19,23 +19,28 @@ import ( "log/slog" "os" - queryapi "queryorchestration/api/queryAPI" - "queryorchestration/internal/serviceconfig" - "queryorchestration/internal/client" - clientupdate "queryorchestration/internal/client/update" "queryorchestration/internal/collector" - collectorset "queryorchestration/internal/collector/set" "queryorchestration/internal/document" "queryorchestration/internal/export" "queryorchestration/internal/query" "queryorchestration/internal/query/result" - querytest "queryorchestration/internal/query/test" - queryupdate "queryorchestration/internal/query/update" "queryorchestration/internal/server/api" + "queryorchestration/internal/serviceconfig" + "queryorchestration/internal/serviceconfig/objectstore" "queryorchestration/internal/serviceconfig/queue/clientsync" "queryorchestration/internal/serviceconfig/queue/queryversionsync" + queryapi "queryorchestration/api/queryAPI" + documentupload "queryorchestration/internal/document/upload" + + clientupdate "queryorchestration/internal/client/update" + + collectorset "queryorchestration/internal/collector/set" + + querytest "queryorchestration/internal/query/test" + queryupdate "queryorchestration/internal/query/update" + "github.com/getkin/kin-openapi/openapi3" _ "github.com/lib/pq" ) @@ -44,6 +49,7 @@ type QueryAPIConfig struct { api.BaseConfig clientsync.ClientSyncConfig queryversionsync.QueryVersionSyncConfig + objectstore.ObjectStoreConfig } func main() { @@ -66,6 +72,7 @@ func main() { Client: cli, }) doc := document.New(cfg) + docup := documentupload.New(cfg) quetest := querytest.New(cfg, &querytest.Services{ Collector: col, Result: res, @@ -76,15 +83,16 @@ func main() { }) services := &queryapi.Services{ - Export: exp, - Collector: col, - CollectorSet: colupdate, - Query: que, - QueryUpdate: qupdate, - QueryTest: quetest, - Client: cli, - ClientUpdate: cliUpdate, - Document: doc, + Export: exp, + Collector: col, + CollectorSet: colupdate, + Query: que, + QueryUpdate: qupdate, + QueryTest: quetest, + Client: cli, + ClientUpdate: cliUpdate, + Document: doc, + DocumentUpload: docup, } cons := queryapi.NewControllers(services) @@ -126,5 +134,11 @@ func main() { os.Exit(1) } + err = cfg.SetStoreClient(ctx) + if err != nil { + slog.Error(err.Error()) + os.Exit(1) + } + server.Listen() } diff --git a/go.mod b/go.mod index 50761561..3a982405 100644 --- a/go.mod +++ b/go.mod @@ -15,7 +15,6 @@ require ( github.com/go-playground/validator/v10 v10.25.0 github.com/golang-migrate/migrate/v4 v4.18.2 github.com/google/go-cmp v0.7.0 - github.com/gruntwork-io/terratest v0.48.2 github.com/jackc/pgx/v5 v5.7.2 github.com/joho/godotenv v1.5.1 github.com/labstack/echo-contrib v0.17.2 @@ -134,7 +133,6 @@ require ( google.golang.org/protobuf v1.36.5 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect - gotest.tools/v3 v3.5.1 // indirect ) require ( diff --git a/go.sum b/go.sum index 86e93544..765cf585 100644 --- a/go.sum +++ b/go.sum @@ -149,8 +149,6 @@ github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 h1:5ZPtiqj0JL5oKWmcsq4VMaAW5ukBEgSGXEN89zeH1Jo= github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3/go.mod h1:ndYquD05frm2vACXE1nsccT4oJzjhw2arTS2cpUD1PI= -github.com/gruntwork-io/terratest v0.48.2 h1:+VwfODchq8jxZZWD+s8gBlhD1z6/C4bFLNrhpm9ONrs= -github.com/gruntwork-io/terratest v0.48.2/go.mod h1:Y5ETyD4ZQ2MZhasPno272fWuCpKwvTPYDi8Y0tIMqTE= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= diff --git a/internal/database/migrations/00000000000005_documents.up.sql b/internal/database/migrations/00000000000005_documents.up.sql index 075d9d6e..9d9ef069 100644 --- a/internal/database/migrations/00000000000005_documents.up.sql +++ b/internal/database/migrations/00000000000005_documents.up.sql @@ -6,6 +6,16 @@ CREATE TABLE documents ( unique(clientId, hash) ); +CREATE TABLE documentUploads ( + id uuid primary key DEFAULT uuid_generate_v7(), + bucket text not null, + key text not null, + clientId varchar(255) not null, + part unsignedsmallint not null, + createdAt timestamp not null, + foreign key (clientId) references clients(clientId) +); + CREATE TABLE documentEntries ( id uuid primary key DEFAULT uuid_generate_v7(), documentId uuid not null, diff --git a/internal/database/queries/document.sql b/internal/database/queries/document.sql index 3abb737d..c476bfd5 100644 --- a/internal/database/queries/document.sql +++ b/internal/database/queries/document.sql @@ -44,3 +44,26 @@ SELECT id FROM documents WHERE hash = $1 and clientId = $2; -- name: ListDocumentIDsBatch :many SELECT id, totalCount FROM listDocumentIDs(@clientId, @batchSize, @pageOffset); + +-- name: AddDocumentUpload :exec +INSERT INTO documentUploads (id, clientId, bucket, key, part, createdAt) VALUES ($1, $2, $3, $4, $5, $6); + +-- name: GetDocumentUploadCurrentPart :one +WITH client as ( + select clientId from clients where clientId = @clientId +), +parts as ( + SELECT docs.part + FROM client as c + JOIN documentUploads as docs on docs.clientId = c.clientId + WHERE date(docs.createdAt) = date(@queryDate) +), +max_part as ( + SELECT COALESCE(max(part), 0) as max_part_num FROM parts +) +SELECT + COALESCE(p.part, 0)::unsignedsmallint as part, + COUNT(p.part) as count +FROM parts p +RIGHT JOIN max_part mp ON p.part = mp.max_part_num +GROUP BY p.part; diff --git a/internal/database/repository/document.sql.go b/internal/database/repository/document.sql.go index 9ad9ca66..084a616a 100644 --- a/internal/database/repository/document.sql.go +++ b/internal/database/repository/document.sql.go @@ -9,6 +9,7 @@ import ( "context" "github.com/google/uuid" + "github.com/jackc/pgx/v5/pgtype" ) const addDocumentEntry = `-- name: AddDocumentEntry :exec @@ -29,6 +30,34 @@ func (q *Queries) AddDocumentEntry(ctx context.Context, arg *AddDocumentEntryPar return err } +const addDocumentUpload = `-- name: AddDocumentUpload :exec +INSERT INTO documentUploads (id, clientId, bucket, key, part, createdAt) VALUES ($1, $2, $3, $4, $5, $6) +` + +type AddDocumentUploadParams struct { + ID uuid.UUID `db:"id"` + Clientid string `db:"clientid"` + Bucket string `db:"bucket"` + Key string `db:"key"` + Part uint16 `db:"part"` + Createdat pgtype.Timestamp `db:"createdat"` +} + +// AddDocumentUpload +// +// INSERT INTO documentUploads (id, clientId, bucket, key, part, createdAt) VALUES ($1, $2, $3, $4, $5, $6) +func (q *Queries) AddDocumentUpload(ctx context.Context, arg *AddDocumentUploadParams) error { + _, err := q.db.Exec(ctx, addDocumentUpload, + arg.ID, + arg.Clientid, + arg.Bucket, + arg.Key, + arg.Part, + arg.Createdat, + ) + return err +} + const createDocument = `-- name: CreateDocument :one INSERT INTO documents (clientId, hash) VALUES ($1, $2) RETURNING id ` @@ -173,6 +202,64 @@ func (q *Queries) GetDocumentSummary(ctx context.Context, id uuid.UUID) (*Docume return &i, err } +const getDocumentUploadCurrentPart = `-- name: GetDocumentUploadCurrentPart :one +WITH client as ( + select clientId from clients where clientId = $1 +), +parts as ( + SELECT docs.part + FROM client as c + JOIN documentUploads as docs on docs.clientId = c.clientId + WHERE date(docs.createdAt) = date($2) +), +max_part as ( + SELECT COALESCE(max(part), 0) as max_part_num FROM parts +) +SELECT + COALESCE(p.part, 0)::unsignedsmallint as part, + COUNT(p.part) as count +FROM parts p +RIGHT JOIN max_part mp ON p.part = mp.max_part_num +GROUP BY p.part +` + +type GetDocumentUploadCurrentPartParams struct { + Clientid *string `db:"clientid"` + Querydate pgtype.Timestamptz `db:"querydate"` +} + +type GetDocumentUploadCurrentPartRow struct { + Part uint16 `db:"part"` + Count int64 `db:"count"` +} + +// GetDocumentUploadCurrentPart +// +// WITH client as ( +// select clientId from clients where clientId = $1 +// ), +// parts as ( +// SELECT docs.part +// FROM client as c +// JOIN documentUploads as docs on docs.clientId = c.clientId +// WHERE date(docs.createdAt) = date($2) +// ), +// max_part as ( +// SELECT COALESCE(max(part), 0) as max_part_num FROM parts +// ) +// SELECT +// COALESCE(p.part, 0)::unsignedsmallint as part, +// COUNT(p.part) as count +// FROM parts p +// RIGHT JOIN max_part mp ON p.part = mp.max_part_num +// GROUP BY p.part +func (q *Queries) GetDocumentUploadCurrentPart(ctx context.Context, arg *GetDocumentUploadCurrentPartParams) (*GetDocumentUploadCurrentPartRow, error) { + row := q.db.QueryRow(ctx, getDocumentUploadCurrentPart, arg.Clientid, arg.Querydate) + var i GetDocumentUploadCurrentPartRow + err := row.Scan(&i.Part, &i.Count) + return &i, err +} + const listDocumentIDsBatch = `-- name: ListDocumentIDsBatch :many SELECT id, totalCount FROM listDocumentIDs($1, $2, $3) ` diff --git a/internal/database/repository/models.go b/internal/database/repository/models.go index 0c6f0c43..0f9d9372 100644 --- a/internal/database/repository/models.go +++ b/internal/database/repository/models.go @@ -336,6 +336,15 @@ type Documenttextextractionentry struct { Version int64 `db:"version"` } +type Documentupload struct { + ID uuid.UUID `db:"id"` + Bucket string `db:"bucket"` + Key string `db:"key"` + Clientid string `db:"clientid"` + Part uint16 `db:"part"` + Createdat pgtype.Timestamp `db:"createdat"` +} + type Fullactivecollector struct { Clientid string `db:"clientid"` Mincleanversion int64 `db:"mincleanversion"` diff --git a/internal/document/upload/get.go b/internal/document/upload/get.go new file mode 100644 index 00000000..4fdbc438 --- /dev/null +++ b/internal/document/upload/get.go @@ -0,0 +1,73 @@ +package documentupload + +import ( + "context" + "io" + "time" + + "queryorchestration/internal/database/repository" + "queryorchestration/internal/serviceconfig/objectstore" + + "github.com/aws/aws-sdk-go-v2/service/s3" + "github.com/google/uuid" + "github.com/jackc/pgx/v5/pgtype" +) + +type File struct { + ClientID string + Content io.Reader +} + +func (s *Service) Upload(ctx context.Context, file File) error { + return s.cfg.ExecuteDBTransaction(ctx, func(ctx context.Context, q *repository.Queries) error { + now := time.Now().UTC() + part, err := s.cfg.GetDBQueries().GetDocumentUploadCurrentPart(ctx, &repository.GetDocumentUploadCurrentPartParams{ + Clientid: &file.ClientID, + Querydate: pgtype.Timestamptz{ + Valid: true, + Time: now, + }, + }) + if err != nil { + return err + } + + uploadId := uuid.New() + newPart := s.cfg.GetDirectoryPart(part.Part, part.Count) + key := objectstore.BucketKey{ + ClientID: file.ClientID, + Location: objectstore.Import, + CreatedAt: now, + EntityID: uploadId, + Part: &newPart, + } + keyStr := key.String() + bucket := s.cfg.GetBucket() + + err = q.AddDocumentUpload(ctx, &repository.AddDocumentUploadParams{ + ID: uploadId, + Clientid: file.ClientID, + Bucket: bucket, + Key: keyStr, + Part: newPart, + Createdat: pgtype.Timestamp{ + Valid: true, + Time: now, + }, + }) + if err != nil { + return err + } + + _, err = s.cfg.GetStoreClient().PutObject(ctx, &s3.PutObjectInput{ + Bucket: &bucket, + Key: &keyStr, + Body: file.Content, + }) + if err != nil { + return err + } + + return nil + }) +} diff --git a/internal/document/upload/get_test.go b/internal/document/upload/get_test.go new file mode 100644 index 00000000..660c75b6 --- /dev/null +++ b/internal/document/upload/get_test.go @@ -0,0 +1,45 @@ +package documentupload_test + +import ( + "strings" + "testing" + + "queryorchestration/internal/database/repository" + "queryorchestration/internal/serviceconfig" + "queryorchestration/internal/serviceconfig/objectstore" + "queryorchestration/internal/test" + + documentupload "queryorchestration/internal/document/upload" + + "github.com/stretchr/testify/require" +) + +type Config struct { + serviceconfig.BaseConfig + objectstore.ObjectStoreConfig +} + +func TestUpload(t *testing.T) { + cfg := &Config{} + test.CreateDB(t, cfg) + acfg := test.CreateAWSContainer(t, cfg) + + test.SetStoreClient(t, t.Context(), cfg, acfg.ExternalEndpoint) + test.CreateBucket(t, cfg) + + err := cfg.GetDBQueries().CreateClient(t.Context(), &repository.CreateClientParams{ + Clientid: "client_id", + Name: "client", + }) + require.NoError(t, err) + + svc := documentupload.New(cfg) + + file := documentupload.File{ + ClientID: "client_id", + Content: strings.NewReader("aaaaaaaaaaaa"), + } + + err = svc.Upload(t.Context(), file) + require.NoError(t, err) +} diff --git a/internal/document/upload/service.go b/internal/document/upload/service.go new file mode 100644 index 00000000..226a9fd7 --- /dev/null +++ b/internal/document/upload/service.go @@ -0,0 +1,21 @@ +package documentupload + +import ( + "queryorchestration/internal/serviceconfig" + "queryorchestration/internal/serviceconfig/objectstore" +) + +type ConfigProvider interface { + serviceconfig.ConfigProvider + objectstore.ConfigProvider +} + +type Service struct { + cfg ConfigProvider +} + +func New(cfg ConfigProvider) *Service { + return &Service{ + cfg, + } +} diff --git a/internal/serviceconfig/common.go b/internal/serviceconfig/common.go index ed68c7d7..4cb9200a 100644 --- a/internal/serviceconfig/common.go +++ b/internal/serviceconfig/common.go @@ -4,13 +4,11 @@ import ( "errors" "fmt" "log/slog" - - "queryorchestration/internal/serviceconfig/auth" - "reflect" "strings" "sync" + "queryorchestration/internal/serviceconfig/auth" "queryorchestration/internal/serviceconfig/aws" "queryorchestration/internal/serviceconfig/database" "queryorchestration/internal/serviceconfig/logger" diff --git a/internal/serviceconfig/objectstore/config.go b/internal/serviceconfig/objectstore/config.go index dfed5469..4c91adc9 100644 --- a/internal/serviceconfig/objectstore/config.go +++ b/internal/serviceconfig/objectstore/config.go @@ -18,6 +18,7 @@ import ( type ObjectStoreConfig struct { AWSEndpointUrlS3 string `env:"AWS_ENDPOINT_URL_S3"` AWSS3UsePathStyle bool `env:"AWS_S3_USE_PATH_STYLE" envDefault:"false"` + Bucket string `env:"BUCKET,required,notEmpty"` StoreClient S3Client ChunkSize int MaxDirSize int64 @@ -35,6 +36,16 @@ type ConfigProvider interface { SetS3UsePathStyle(bool) CalculateETag(context.Context, io.Reader) (string, error) GetDirectoryPart(uint16, int64) uint16 + GetBucket() string + SetBucket(string) +} + +func (c *ObjectStoreConfig) GetBucket() string { + return c.Bucket +} + +func (c *ObjectStoreConfig) SetBucket(name string) { + c.Bucket = name } func (c *ObjectStoreConfig) SetStoreClientAndPingByName(ctx context.Context, name string) error { diff --git a/internal/serviceconfig/objectstore/config_test.go b/internal/serviceconfig/objectstore/config_test.go index 15231189..cf86cb30 100644 --- a/internal/serviceconfig/objectstore/config_test.go +++ b/internal/serviceconfig/objectstore/config_test.go @@ -9,8 +9,10 @@ import ( "time" "queryorchestration/internal/serviceconfig" - objectstore "queryorchestration/internal/serviceconfig/objectstore" "queryorchestration/internal/test" + + objectstore "queryorchestration/internal/serviceconfig/objectstore" + objectstoremock "queryorchestration/mocks/objectstore" awsc "queryorchestration/internal/serviceconfig/aws" @@ -97,7 +99,7 @@ func TestCalculateAndCompareETag(t *testing.T) { acfg := test.CreateAWSContainer(t, cfg) test.SetStoreClient(t, ctx, cfg, acfg.ExternalEndpoint) - test.CreateBucket(t, ctx, cfg) + test.CreateBucket(t, cfg) testContent := "hi" hashReader := strings.NewReader(testContent) diff --git a/internal/test/api.go b/internal/test/api.go index 33d9c855..acc1e1a6 100644 --- a/internal/test/api.go +++ b/internal/test/api.go @@ -7,8 +7,6 @@ import ( "net/http" "testing" - "queryorchestration/internal/serviceconfig" - "github.com/docker/go-connections/nat" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -30,7 +28,7 @@ type APIConfig struct { MockHTTP string } -func CreateAPI(t testing.TB, ctx context.Context, cfg serviceconfig.ConfigProvider, network string, config *APIConfig) (*Container, func()) { +func CreateAPI(t testing.TB, ctx context.Context, cfg ContainerConfigProvider, network string, config *APIConfig) (*Container, func()) { port, err := nat.NewPort("tcp", "8080") require.NoError(t, err) diff --git a/internal/test/container.go b/internal/test/container.go index b8edea90..8127fdd5 100644 --- a/internal/test/container.go +++ b/internal/test/container.go @@ -5,17 +5,12 @@ import ( "context" "fmt" "log/slog" - "os" - "path/filepath" "strconv" - "sync" "testing" "queryorchestration/internal/serviceconfig" + "queryorchestration/internal/serviceconfig/objectstore" - "github.com/docker/docker/api/types" - "github.com/docker/docker/client" - "github.com/docker/docker/pkg/archive" "github.com/docker/go-connections/nat" "github.com/stretchr/testify/require" "github.com/testcontainers/testcontainers-go" @@ -27,9 +22,14 @@ type Container struct { URI string } +type ContainerConfigProvider interface { + serviceconfig.ConfigProvider + objectstore.ConfigProvider +} + type containerConfig struct { Name string - Cfg serviceconfig.ConfigProvider + Cfg ContainerConfigProvider DownstreamQueues []RunnerName Env map[string]string WaitForMsg string @@ -42,77 +42,14 @@ const ( dockerfilePath = "build/Dockerfile" ) -var imageBuild sync.Once - -func buildImage(t testing.TB, ctx context.Context) { - imageBuild.Do(func() { - dockerContextPath, err := os.Getwd() - require.NoError(t, err, "Failed to get current directory") - - for { - candidatePath := filepath.Join(dockerContextPath, dockerfilePath) - - t.Logf("Checking for Dockerfile at: %s", dockerContextPath) - _, err := os.Stat(candidatePath) - if err == nil { - break - } - - dockerContextPath = filepath.Join(dockerContextPath, "..") - } - - cli, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation()) - require.NoError(t, err) - - excludePatterns := []string{ - "LICENSE", - "README.md", - "**/*_test.go", - "cmd/*_test/", - ".bitbucket/", - ".devbox/", - ".vscode/", - "deployments/", - "mocks/", - "out/", - "serviceAPIs/", - "test/", - "docs/", - "assets/", - "pkg/", - "internal/test/", - "Taskfile.yml", - "scripts/", - ".gitattributes", - ".gitignore", - "devbox.*", - "sqlc.yml", - "vaccum.conf.yaml", - ".*.yml", - "**/*.cast", - "**/*.http", - } - - buildContext, err := archive.TarWithOptions(dockerContextPath, &archive.TarOptions{ - ExcludePatterns: excludePatterns, - }) - require.NoError(t, err) - defer buildContext.Close() - - out, err := cli.ImageBuild(ctx, buildContext, types.ImageBuildOptions{ - Dockerfile: dockerfilePath, - Tags: []string{imageTag}, - }) - require.NoError(t, err) - defer func() { - err := out.Body.Close() - require.NoError(t, err) - }() - }) -} - func createContainer(t testing.TB, ctx context.Context, network string, cfg *containerConfig) (testcontainers.Container, func()) { + bucket := cfg.Cfg.GetBucket() + if bucket == "" { + bucket = "invalid-example" + } + env := map[string]string{ + "BUCKET": bucket, "PGUSER": cfg.Cfg.GetDBUser(), "PGPASSWORD": cfg.Cfg.GetDBSecret(), "PGHOST": dbAlias, @@ -145,8 +82,6 @@ func createContainer(t testing.TB, ctx context.Context, network string, cfg *con env[string(GetRunnerEnvFromName(e))] = GetQueueURL(t, cfg.Cfg, e) } - buildImage(t, ctx) - req := testcontainers.ContainerRequest{ Image: imageTag, Env: env, diff --git a/internal/test/container_test.go b/internal/test/container_test.go deleted file mode 100644 index 2eddf65c..00000000 --- a/internal/test/container_test.go +++ /dev/null @@ -1,18 +0,0 @@ -package test - -import ( - "testing" - - "github.com/gruntwork-io/terratest/modules/docker" - "github.com/stretchr/testify/assert" -) - -func TestBuildImage(t *testing.T) { - if testing.Short() { - t.SkipNow() - } - buildImage(t, t.Context()) - - exists := docker.DoesImageExist(t, imageTag, nil) - assert.True(t, exists) -} diff --git a/internal/test/ecosystem.go b/internal/test/ecosystem.go index 8228604b..606c791a 100644 --- a/internal/test/ecosystem.go +++ b/internal/test/ecosystem.go @@ -9,6 +9,7 @@ import ( "queryorchestration/internal/serviceconfig" "queryorchestration/internal/serviceconfig/objectstore" + queryapi "queryorchestration/pkg/queryAPI" "github.com/stretchr/testify/require" @@ -67,12 +68,6 @@ func CreateFullNetwork(t testing.TB, ctx context.Context, cfg FullDependenciesCo }() } - wg.Add(1) - go func() { - buildImage(t, ctx) - wg.Done() - }() - wg.Wait() qService, err := queryapi.NewClientWithResponses(apiContainers[QueryAPIName].URI) @@ -166,7 +161,7 @@ func CreateFullDependenciesWithParams(t testing.TB, cfg FullDependenciesConfig, if !params.NoObjectStore { SetStoreClient(t, t.Context(), cfg, deps.AWSConfig.ExternalEndpoint) - CreateBucket(t, t.Context(), cfg) + CreateBucket(t, cfg) SetBucketNotifs(t, t.Context(), cfg) } diff --git a/internal/test/objectstore.go b/internal/test/objectstore.go index fac1eea4..5a11419f 100644 --- a/internal/test/objectstore.go +++ b/internal/test/objectstore.go @@ -22,17 +22,19 @@ func GetBucketName(t testing.TB) string { return GetAlias(t, "bucket") } -func CreateBucket(t testing.TB, ctx context.Context, cfg objectstore.ConfigProvider) { +func CreateBucket(t testing.TB, cfg objectstore.ConfigProvider) { t.Helper() bucketName := GetBucketName(t) - _, err := cfg.GetStoreClient().CreateBucket(ctx, &s3.CreateBucketInput{ + _, err := cfg.GetStoreClient().CreateBucket(t.Context(), &s3.CreateBucketInput{ Bucket: aws.String(bucketName), }) require.NoError(t, err) slog.Info("create bucket", "bucket", bucketName) - err = cfg.PingStoreByName(ctx, bucketName) + err = cfg.PingStoreByName(t.Context(), bucketName) require.NoError(t, err) + + cfg.SetBucket(bucketName) } func SetBucketNotifs(t testing.TB, ctx context.Context, cfg objectstore.ConfigProvider) { diff --git a/internal/test/objectstore_test.go b/internal/test/objectstore_test.go index 58170ced..b27113cc 100644 --- a/internal/test/objectstore_test.go +++ b/internal/test/objectstore_test.go @@ -7,6 +7,7 @@ import ( "queryorchestration/internal/serviceconfig" "queryorchestration/internal/serviceconfig/aws" + objectstore "queryorchestration/internal/serviceconfig/objectstore" objectstoremock "queryorchestration/mocks/objectstore" @@ -32,7 +33,7 @@ func TestCreateBucket(t *testing.T) { acfg := CreateAWSContainer(t, cfg) SetStoreClient(t, ctx, cfg, acfg.ExternalEndpoint) - CreateBucket(t, ctx, cfg) + CreateBucket(t, cfg) } func TestPutObject(t *testing.T) { diff --git a/internal/test/queryAPI/service.go b/internal/test/queryAPI/service.go index 22cc7003..4fe1590a 100644 --- a/internal/test/queryAPI/service.go +++ b/internal/test/queryAPI/service.go @@ -1,14 +1,17 @@ package queryapitest import ( + "bytes" "context" "fmt" "log/slog" + "mime/multipart" "net/http" "testing" "time" "queryorchestration/internal/test" + queryapi "queryorchestration/pkg/queryAPI" "github.com/google/uuid" @@ -17,6 +20,35 @@ import ( "github.com/stretchr/testify/require" ) +type File struct { + ClientID queryapi.ClientID + Filename string + Content []byte +} + +func CreateFile(t testing.TB, client queryapi.ClientWithResponsesInterface, file File) { + var buf bytes.Buffer + fileBody := multipart.NewWriter(&buf) + + name := file.Filename + if name == "" { + name = "helloworld" + } + + write, err := fileBody.CreateFormFile("file", name) + require.NoError(t, err) + _, err = write.Write(file.Content) + require.NoError(t, err) + err = fileBody.Close() + require.NoError(t, err) + + body := bytes.NewReader(buf.Bytes()) + + uploadRes, err := client.UploadDocumentWithBodyWithResponse(t.Context(), file.ClientID, fileBody.FormDataContentType(), body) + require.NoError(t, err) + require.Equal(t, http.StatusOK, uploadRes.StatusCode()) +} + func CreateClientWithSync(t testing.TB, client queryapi.ClientWithResponsesInterface) queryapi.ClientID { t.Helper() diff --git a/internal/test/runner.go b/internal/test/runner.go index 310f0246..50cc9994 100644 --- a/internal/test/runner.go +++ b/internal/test/runner.go @@ -4,7 +4,6 @@ import ( "context" "testing" - "queryorchestration/internal/serviceconfig" "queryorchestration/internal/serviceconfig/queue/clientsync" "queryorchestration/internal/serviceconfig/queue/documentclean" "queryorchestration/internal/serviceconfig/queue/documentinit" @@ -76,7 +75,7 @@ type RunnerConfig struct { MockHTTP string } -func CreateRunner(t testing.TB, ctx context.Context, cfg serviceconfig.ConfigProvider, network string, config *RunnerConfig) (*Container, func()) { +func CreateRunner(t testing.TB, ctx context.Context, cfg ContainerConfigProvider, network string, config *RunnerConfig) (*Container, func()) { env := map[string]string{} url := GetQueueURL(t, cfg, config.Runner.Name) env["QUEUE_URL"] = url @@ -100,34 +99,42 @@ var StoreEventRunner = Runner{ Name: StoreEventRunnerName, DownstreamQueues: []RunnerName{DocInitRunnerName}, } + var DocInitRunner = Runner{ Name: DocInitRunnerName, DownstreamQueues: []RunnerName{DocSyncRunnerName}, } + var DocSyncRunner = Runner{ Name: DocSyncRunnerName, DownstreamQueues: []RunnerName{DocCleanRunnerName}, } + var DocCleanRunner = Runner{ Name: DocCleanRunnerName, DownstreamQueues: []RunnerName{DocTextRunnerName}, } + var DocTextRunner = Runner{ Name: DocTextRunnerName, DownstreamQueues: []RunnerName{QuerySyncRunnerName}, } + var QuerySyncRunner = Runner{ Name: QuerySyncRunnerName, DownstreamQueues: []RunnerName{QueryRunnerName}, } + var QueryRunner = Runner{ Name: QueryRunnerName, DownstreamQueues: []RunnerName{QueryRunnerName}, } + var ClientSyncRunner = Runner{ Name: ClientSyncRunnerName, DownstreamQueues: []RunnerName{DocSyncRunnerName}, } + var QueryVersionSyncRunner = Runner{ Name: QueryVersionSyncRunnerName, DownstreamQueues: []RunnerName{ClientSyncRunnerName}, diff --git a/mocks/queryapi/mock_ClientInterface.go b/mocks/queryapi/mock_ClientInterface.go index 7f1c41fd..b91d0427 100644 --- a/mocks/queryapi/mock_ClientInterface.go +++ b/mocks/queryapi/mock_ClientInterface.go @@ -1963,6 +1963,82 @@ func (_c *MockClientInterface_UpdateQueryWithBody_Call) RunAndReturn(run func(co return _c } +// UploadDocumentWithBody provides a mock function with given fields: ctx, id, contentType, body, reqEditors +func (_m *MockClientInterface) UploadDocumentWithBody(ctx context.Context, id queryapi.ClientID, contentType string, body io.Reader, reqEditors ...queryapi.RequestEditorFn) (*http.Response, error) { + _va := make([]interface{}, len(reqEditors)) + for _i := range reqEditors { + _va[_i] = reqEditors[_i] + } + var _ca []interface{} + _ca = append(_ca, ctx, id, contentType, body) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) + + if len(ret) == 0 { + panic("no return value specified for UploadDocumentWithBody") + } + + var r0 *http.Response + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, queryapi.ClientID, string, io.Reader, ...queryapi.RequestEditorFn) (*http.Response, error)); ok { + return rf(ctx, id, contentType, body, reqEditors...) + } + if rf, ok := ret.Get(0).(func(context.Context, queryapi.ClientID, string, io.Reader, ...queryapi.RequestEditorFn) *http.Response); ok { + r0 = rf(ctx, id, contentType, body, reqEditors...) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*http.Response) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, queryapi.ClientID, string, io.Reader, ...queryapi.RequestEditorFn) error); ok { + r1 = rf(ctx, id, contentType, body, reqEditors...) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// MockClientInterface_UploadDocumentWithBody_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UploadDocumentWithBody' +type MockClientInterface_UploadDocumentWithBody_Call struct { + *mock.Call +} + +// UploadDocumentWithBody is a helper method to define mock.On call +// - ctx context.Context +// - id queryapi.ClientID +// - contentType string +// - body io.Reader +// - reqEditors ...queryapi.RequestEditorFn +func (_e *MockClientInterface_Expecter) UploadDocumentWithBody(ctx interface{}, id interface{}, contentType interface{}, body interface{}, reqEditors ...interface{}) *MockClientInterface_UploadDocumentWithBody_Call { + return &MockClientInterface_UploadDocumentWithBody_Call{Call: _e.mock.On("UploadDocumentWithBody", + append([]interface{}{ctx, id, contentType, body}, reqEditors...)...)} +} + +func (_c *MockClientInterface_UploadDocumentWithBody_Call) Run(run func(ctx context.Context, id queryapi.ClientID, contentType string, body io.Reader, reqEditors ...queryapi.RequestEditorFn)) *MockClientInterface_UploadDocumentWithBody_Call { + _c.Call.Run(func(args mock.Arguments) { + variadicArgs := make([]queryapi.RequestEditorFn, len(args)-4) + for i, a := range args[4:] { + if a != nil { + variadicArgs[i] = a.(queryapi.RequestEditorFn) + } + } + run(args[0].(context.Context), args[1].(queryapi.ClientID), args[2].(string), args[3].(io.Reader), variadicArgs...) + }) + return _c +} + +func (_c *MockClientInterface_UploadDocumentWithBody_Call) Return(_a0 *http.Response, _a1 error) *MockClientInterface_UploadDocumentWithBody_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *MockClientInterface_UploadDocumentWithBody_Call) RunAndReturn(run func(context.Context, queryapi.ClientID, string, io.Reader, ...queryapi.RequestEditorFn) (*http.Response, error)) *MockClientInterface_UploadDocumentWithBody_Call { + _c.Call.Return(run) + return _c +} + // NewMockClientInterface creates a new instance of MockClientInterface. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. // The first argument is typically a *testing.T value. func NewMockClientInterface(t interface { diff --git a/mocks/queryapi/mock_ClientWithResponsesInterface.go b/mocks/queryapi/mock_ClientWithResponsesInterface.go index 79937968..83ace41e 100644 --- a/mocks/queryapi/mock_ClientWithResponsesInterface.go +++ b/mocks/queryapi/mock_ClientWithResponsesInterface.go @@ -1961,6 +1961,82 @@ func (_c *MockClientWithResponsesInterface_UpdateQueryWithResponse_Call) RunAndR return _c } +// UploadDocumentWithBodyWithResponse provides a mock function with given fields: ctx, id, contentType, body, reqEditors +func (_m *MockClientWithResponsesInterface) UploadDocumentWithBodyWithResponse(ctx context.Context, id queryapi.ClientID, contentType string, body io.Reader, reqEditors ...queryapi.RequestEditorFn) (*queryapi.UploadDocumentResponse, error) { + _va := make([]interface{}, len(reqEditors)) + for _i := range reqEditors { + _va[_i] = reqEditors[_i] + } + var _ca []interface{} + _ca = append(_ca, ctx, id, contentType, body) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) + + if len(ret) == 0 { + panic("no return value specified for UploadDocumentWithBodyWithResponse") + } + + var r0 *queryapi.UploadDocumentResponse + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, queryapi.ClientID, string, io.Reader, ...queryapi.RequestEditorFn) (*queryapi.UploadDocumentResponse, error)); ok { + return rf(ctx, id, contentType, body, reqEditors...) + } + if rf, ok := ret.Get(0).(func(context.Context, queryapi.ClientID, string, io.Reader, ...queryapi.RequestEditorFn) *queryapi.UploadDocumentResponse); ok { + r0 = rf(ctx, id, contentType, body, reqEditors...) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*queryapi.UploadDocumentResponse) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, queryapi.ClientID, string, io.Reader, ...queryapi.RequestEditorFn) error); ok { + r1 = rf(ctx, id, contentType, body, reqEditors...) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// MockClientWithResponsesInterface_UploadDocumentWithBodyWithResponse_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UploadDocumentWithBodyWithResponse' +type MockClientWithResponsesInterface_UploadDocumentWithBodyWithResponse_Call struct { + *mock.Call +} + +// UploadDocumentWithBodyWithResponse is a helper method to define mock.On call +// - ctx context.Context +// - id queryapi.ClientID +// - contentType string +// - body io.Reader +// - reqEditors ...queryapi.RequestEditorFn +func (_e *MockClientWithResponsesInterface_Expecter) UploadDocumentWithBodyWithResponse(ctx interface{}, id interface{}, contentType interface{}, body interface{}, reqEditors ...interface{}) *MockClientWithResponsesInterface_UploadDocumentWithBodyWithResponse_Call { + return &MockClientWithResponsesInterface_UploadDocumentWithBodyWithResponse_Call{Call: _e.mock.On("UploadDocumentWithBodyWithResponse", + append([]interface{}{ctx, id, contentType, body}, reqEditors...)...)} +} + +func (_c *MockClientWithResponsesInterface_UploadDocumentWithBodyWithResponse_Call) Run(run func(ctx context.Context, id queryapi.ClientID, contentType string, body io.Reader, reqEditors ...queryapi.RequestEditorFn)) *MockClientWithResponsesInterface_UploadDocumentWithBodyWithResponse_Call { + _c.Call.Run(func(args mock.Arguments) { + variadicArgs := make([]queryapi.RequestEditorFn, len(args)-4) + for i, a := range args[4:] { + if a != nil { + variadicArgs[i] = a.(queryapi.RequestEditorFn) + } + } + run(args[0].(context.Context), args[1].(queryapi.ClientID), args[2].(string), args[3].(io.Reader), variadicArgs...) + }) + return _c +} + +func (_c *MockClientWithResponsesInterface_UploadDocumentWithBodyWithResponse_Call) Return(_a0 *queryapi.UploadDocumentResponse, _a1 error) *MockClientWithResponsesInterface_UploadDocumentWithBodyWithResponse_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *MockClientWithResponsesInterface_UploadDocumentWithBodyWithResponse_Call) RunAndReturn(run func(context.Context, queryapi.ClientID, string, io.Reader, ...queryapi.RequestEditorFn) (*queryapi.UploadDocumentResponse, error)) *MockClientWithResponsesInterface_UploadDocumentWithBodyWithResponse_Call { + _c.Call.Return(run) + return _c +} + // NewMockClientWithResponsesInterface creates a new instance of MockClientWithResponsesInterface. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. // The first argument is typically a *testing.T value. func NewMockClientWithResponsesInterface(t interface { diff --git a/pkg/queryAPI/api.gen.go b/pkg/queryAPI/api.gen.go index 5bbf141e..0a7d999e 100644 --- a/pkg/queryAPI/api.gen.go +++ b/pkg/queryAPI/api.gen.go @@ -349,6 +349,15 @@ type TooManyRequests = ErrorMessage // Unauthorized Description of error type Unauthorized = ErrorMessage +// UploadDocumentMultipartBody defines parameters for UploadDocument. +type UploadDocumentMultipartBody struct { + // File The file to upload + File openapi_types.File `json:"file"` + + // Filename Optional custom filename + Filename *string `json:"filename,omitempty"` +} + // LoginCallbackParams defines parameters for LoginCallback. type LoginCallbackParams struct { // Code Authorization code from Cognito @@ -367,6 +376,9 @@ type UpdateClientJSONRequestBody = ClientUpdate // SetCollectorByClientIdJSONRequestBody defines body for SetCollectorByClientId for application/json ContentType. type SetCollectorByClientIdJSONRequestBody = CollectorSet +// UploadDocumentMultipartRequestBody defines body for UploadDocument for multipart/form-data ContentType. +type UploadDocumentMultipartRequestBody UploadDocumentMultipartBody + // TriggerExportJSONRequestBody defines body for TriggerExport for application/json ContentType. type TriggerExportJSONRequestBody = ExportTrigger @@ -476,6 +488,9 @@ type ClientInterface interface { // ListDocumentsByClientId request ListDocumentsByClientId(ctx context.Context, id ClientID, reqEditors ...RequestEditorFn) (*http.Response, error) + // UploadDocumentWithBody request with any body + UploadDocumentWithBody(ctx context.Context, id ClientID, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + // TriggerExportWithBody request with any body TriggerExportWithBody(ctx context.Context, id ClientID, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) @@ -632,6 +647,18 @@ func (c *Client) ListDocumentsByClientId(ctx context.Context, id ClientID, reqEd return c.Client.Do(req) } +func (c *Client) UploadDocumentWithBody(ctx context.Context, id ClientID, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewUploadDocumentRequestWithBody(c.Server, id, contentType, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + func (c *Client) TriggerExportWithBody(ctx context.Context, id ClientID, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewTriggerExportRequestWithBody(c.Server, id, contentType, body) if err != nil { @@ -1054,7 +1081,7 @@ func NewListDocumentsByClientIdRequest(server string, id ClientID) (*http.Reques return nil, err } - operationPath := fmt.Sprintf("/client/%s/documents", pathParam0) + operationPath := fmt.Sprintf("/client/%s/document", pathParam0) if operationPath[0] == '/' { operationPath = "." + operationPath } @@ -1072,6 +1099,42 @@ func NewListDocumentsByClientIdRequest(server string, id ClientID) (*http.Reques return req, nil } +// NewUploadDocumentRequestWithBody generates requests for UploadDocument with any type of body +func NewUploadDocumentRequestWithBody(server string, id ClientID, contentType string, body io.Reader) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithLocation("simple", false, "id", runtime.ParamLocationPath, id) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/client/%s/document", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("POST", queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + // NewTriggerExportRequest calls the generic TriggerExport builder with application/json body func NewTriggerExportRequest(server string, id ClientID, body TriggerExportJSONRequestBody) (*http.Request, error) { var bodyReader io.Reader @@ -1621,6 +1684,9 @@ type ClientWithResponsesInterface interface { // ListDocumentsByClientIdWithResponse request ListDocumentsByClientIdWithResponse(ctx context.Context, id ClientID, reqEditors ...RequestEditorFn) (*ListDocumentsByClientIdResponse, error) + // UploadDocumentWithBodyWithResponse request with any body + UploadDocumentWithBodyWithResponse(ctx context.Context, id ClientID, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*UploadDocumentResponse, error) + // TriggerExportWithBodyWithResponse request with any body TriggerExportWithBodyWithResponse(ctx context.Context, id ClientID, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*TriggerExportResponse, error) @@ -1823,6 +1889,31 @@ func (r ListDocumentsByClientIdResponse) StatusCode() int { return 0 } +type UploadDocumentResponse struct { + Body []byte + HTTPResponse *http.Response + JSON400 *InvalidRequest + JSON401 *Unauthorized + JSON429 *TooManyRequests + JSON500 *InternalError +} + +// Status returns HTTPResponse.Status +func (r UploadDocumentResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r UploadDocumentResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + type TriggerExportResponse struct { Body []byte HTTPResponse *http.Response @@ -2234,6 +2325,15 @@ func (c *ClientWithResponses) ListDocumentsByClientIdWithResponse(ctx context.Co return ParseListDocumentsByClientIdResponse(rsp) } +// UploadDocumentWithBodyWithResponse request with arbitrary body returning *UploadDocumentResponse +func (c *ClientWithResponses) UploadDocumentWithBodyWithResponse(ctx context.Context, id ClientID, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*UploadDocumentResponse, error) { + rsp, err := c.UploadDocumentWithBody(ctx, id, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseUploadDocumentResponse(rsp) +} + // TriggerExportWithBodyWithResponse request with arbitrary body returning *TriggerExportResponse func (c *ClientWithResponses) TriggerExportWithBodyWithResponse(ctx context.Context, id ClientID, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*TriggerExportResponse, error) { rsp, err := c.TriggerExportWithBody(ctx, id, contentType, body, reqEditors...) @@ -2693,6 +2793,53 @@ func ParseListDocumentsByClientIdResponse(rsp *http.Response) (*ListDocumentsByC return response, nil } +// ParseUploadDocumentResponse parses an HTTP response from a UploadDocumentWithResponse call +func ParseUploadDocumentResponse(rsp *http.Response) (*UploadDocumentResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &UploadDocumentResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400: + var dest InvalidRequest + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON400 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 401: + var dest Unauthorized + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON401 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 429: + var dest TooManyRequests + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON429 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: + var dest InternalError + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + } + + return response, nil +} + // ParseTriggerExportResponse parses an HTTP response from a TriggerExportWithResponse call func ParseTriggerExportResponse(rsp *http.Response) (*TriggerExportResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) diff --git a/serviceAPIs/queryAPI.yaml b/serviceAPIs/queryAPI.yaml index 1582c3a7..46d453e7 100644 --- a/serviceAPIs/queryAPI.yaml +++ b/serviceAPIs/queryAPI.yaml @@ -369,7 +369,7 @@ paths: "500": $ref: "#/components/responses/InternalError" - /client/{id}/documents: + /client/{id}/document: parameters: - $ref: "#/components/parameters/ClientID" get: @@ -398,6 +398,54 @@ paths: $ref: "#/components/responses/TooManyRequests" "500": $ref: "#/components/responses/InternalError" + post: + operationId: uploadDocument + tags: + - DocumentsService + summary: Upload a file + description: Upload a single file to the server with optional metadata + security: + - jwtAuth: [] + requestBody: + required: true + description: upload file body + content: + multipart/form-data: + schema: + type: object + required: + - file + properties: + file: + type: string + format: binary + maxLength: 107374182400 + description: The file to upload + filename: + type: string + pattern: ".+" + maxLength: 4096 + description: Optional custom filename + example: "document.pdf" + encoding: + file: + contentType: application/pdf + style: form + explode: false + responses: + "200": + description: Document uploaded. + headers: + RateLimit: + $ref: "#/components/headers/RateLimit" + "400": + $ref: "#/components/responses/InvalidRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "429": + $ref: "#/components/responses/TooManyRequests" + "500": + $ref: "#/components/responses/InternalError" /document/{id}: parameters: diff --git a/test/process_test.go b/test/process_test.go index 50a1ce2b..09003c26 100644 --- a/test/process_test.go +++ b/test/process_test.go @@ -10,6 +10,7 @@ import ( "queryorchestration/internal/serviceconfig/aws" "queryorchestration/internal/serviceconfig/objectstore" "queryorchestration/internal/test" + queryapitest "queryorchestration/internal/test/queryAPI" queryapi "queryorchestration/pkg/queryAPI" @@ -59,21 +60,15 @@ func TestProcess(t *testing.T) { queryapitest.SetQueryForClient(t, net.Client, clientId, jsonId) - part := uint16(0) - importKey := objectstore.BucketKey{ - ClientID: clientId, - EntityID: uuid.New(), - Location: objectstore.Import, - CreatedAt: time.Now().UTC(), - Part: &part, - } - inputFile := strings.NewReader(pdfHelloWorld) - test.PutObject(t, t.Context(), cfg, test.PutObjectParams{ - File: inputFile, - Key: importKey, + queryapitest.CreateFile(t, net.Client, queryapitest.File{ + Filename: "helloworld.pdf", + Content: []byte(pdfHelloWorld), + ClientID: clientId, }) test.WaitForMockEndpoint(t, net.Dependencies.MockServer, textractExpectation.Request) + + part := uint16(0) textKey := objectstore.BucketKey{ ClientID: clientId, EntityID: uuid.New(), @@ -81,7 +76,7 @@ func TestProcess(t *testing.T) { CreatedAt: time.Now().UTC(), Part: &part, } - inputFile = strings.NewReader(textractBody) + inputFile := strings.NewReader(textractBody) test.PutObject(t, t.Context(), cfg, test.PutObjectParams{ File: inputFile, Key: textKey, @@ -144,33 +139,13 @@ func TestProcess(t *testing.T) { queryapitest.SetQueryForClient(t, net.Client, clientId, jsonId) - part := uint16(0) - importKey := objectstore.BucketKey{ - ClientID: clientId, - EntityID: uuid.New(), - Location: objectstore.Import, - CreatedAt: time.Now().UTC(), - Part: &part, - } - inputFile := strings.NewReader(pdfHelloWorld) - test.PutObject(t, t.Context(), cfg, test.PutObjectParams{ - File: inputFile, - Key: importKey, + queryapitest.CreateFile(t, net.Client, queryapitest.File{ + Filename: "helloworld.pdf", + Content: []byte(pdfHelloWorld), + ClientID: clientId, }) test.WaitForMockEndpoint(t, net.Dependencies.MockServer, textractExpectation.Request) - textKey := objectstore.BucketKey{ - ClientID: clientId, - EntityID: uuid.New(), - Location: objectstore.Text, - CreatedAt: time.Now().UTC(), - Part: &part, - } - inputFile = strings.NewReader(textractBody) - test.PutObject(t, t.Context(), cfg, test.PutObjectParams{ - File: inputFile, - Key: textKey, - }) queryapitest.WaitForClientStatus(t, t.Context(), net.Client, clientId, queryapi.INSYNC) @@ -245,33 +220,13 @@ func TestProcess(t *testing.T) { queryapitest.SetQueryForClient(t, net.Client, clientId, jsonId) - part := uint16(0) - importKey := objectstore.BucketKey{ - ClientID: clientId, - EntityID: uuid.New(), - Location: objectstore.Import, - CreatedAt: time.Now().UTC(), - Part: &part, - } - inputFile := strings.NewReader(pdfHelloWorld) - test.PutObject(t, t.Context(), cfg, test.PutObjectParams{ - File: inputFile, - Key: importKey, + queryapitest.CreateFile(t, net.Client, queryapitest.File{ + Filename: "helloworld.pdf", + Content: []byte(pdfHelloWorld), + ClientID: clientId, }) test.WaitForMockEndpoint(t, net.Dependencies.MockServer, textractExpectation.Request) - textKey := objectstore.BucketKey{ - ClientID: clientId, - EntityID: uuid.New(), - Location: objectstore.Text, - CreatedAt: time.Now().UTC(), - Part: &part, - } - inputFile = strings.NewReader(textractBody) - test.PutObject(t, t.Context(), cfg, test.PutObjectParams{ - File: inputFile, - Key: textKey, - }) queryapitest.WaitForClientStatus(t, t.Context(), net.Client, clientId, queryapi.INSYNC) diff --git a/vendor/github.com/gruntwork-io/terratest/LICENSE b/vendor/github.com/gruntwork-io/terratest/LICENSE deleted file mode 100644 index 7a4a3ea2..00000000 --- a/vendor/github.com/gruntwork-io/terratest/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. \ No newline at end of file diff --git a/vendor/github.com/gruntwork-io/terratest/NOTICE b/vendor/github.com/gruntwork-io/terratest/NOTICE deleted file mode 100644 index 88b1d1a6..00000000 --- a/vendor/github.com/gruntwork-io/terratest/NOTICE +++ /dev/null @@ -1,4 +0,0 @@ -terratest -Copyright 2018 Gruntwork, Inc. - -This product includes software developed at Gruntwork (https://www.gruntwork.io/). \ No newline at end of file diff --git a/vendor/github.com/gruntwork-io/terratest/modules/collections/collections.go b/vendor/github.com/gruntwork-io/terratest/modules/collections/collections.go deleted file mode 100644 index 6967d2f6..00000000 --- a/vendor/github.com/gruntwork-io/terratest/modules/collections/collections.go +++ /dev/null @@ -1,2 +0,0 @@ -// Package collections allows to interact with lists of things. -package collections diff --git a/vendor/github.com/gruntwork-io/terratest/modules/collections/errors.go b/vendor/github.com/gruntwork-io/terratest/modules/collections/errors.go deleted file mode 100644 index 56afccea..00000000 --- a/vendor/github.com/gruntwork-io/terratest/modules/collections/errors.go +++ /dev/null @@ -1,17 +0,0 @@ -package collections - -import "fmt" - -// SliceValueNotFoundError is returned when a provided values file input is not found on the host path. -type SliceValueNotFoundError struct { - sourceString string -} - -func (err SliceValueNotFoundError) Error() string { - return fmt.Sprintf("Could not resolve requested slice value from string %s", err.sourceString) -} - -// NewSliceValueNotFoundError creates a new slice found error -func NewSliceValueNotFoundError(sourceString string) SliceValueNotFoundError { - return SliceValueNotFoundError{sourceString} -} diff --git a/vendor/github.com/gruntwork-io/terratest/modules/collections/lists.go b/vendor/github.com/gruntwork-io/terratest/modules/collections/lists.go deleted file mode 100644 index ec468d38..00000000 --- a/vendor/github.com/gruntwork-io/terratest/modules/collections/lists.go +++ /dev/null @@ -1,40 +0,0 @@ -package collections - -// ListIntersection returns all the items in both list1 and list2. Note that this will dedup the items so that the -// output is more predictable. Otherwise, the end list depends on which list was used as the base. -func ListIntersection(list1 []string, list2 []string) []string { - out := []string{} - - // Only need to iterate list1, because we want items in both lists, not union. - for _, item := range list1 { - if ListContains(list2, item) && !ListContains(out, item) { - out = append(out, item) - } - } - - return out -} - -// ListSubtract removes all the items in list2 from list1. -func ListSubtract(list1 []string, list2 []string) []string { - out := []string{} - - for _, item := range list1 { - if !ListContains(list2, item) { - out = append(out, item) - } - } - - return out -} - -// ListContains returns true if the given list of strings (haystack) contains the given string (needle). -func ListContains(haystack []string, needle string) bool { - for _, str := range haystack { - if needle == str { - return true - } - } - - return false -} diff --git a/vendor/github.com/gruntwork-io/terratest/modules/collections/stringslicevalue.go b/vendor/github.com/gruntwork-io/terratest/modules/collections/stringslicevalue.go deleted file mode 100644 index 0fede5e4..00000000 --- a/vendor/github.com/gruntwork-io/terratest/modules/collections/stringslicevalue.go +++ /dev/null @@ -1,27 +0,0 @@ -package collections - -import ( - "strings" -) - -// GetSliceLastValueE will take a source string and returns the last value when split by the separator char. -func GetSliceLastValueE(source string, separator string) (string, error) { - if len(source) > 0 && len(separator) > 0 && strings.Contains(source, separator) { - tmp := strings.Split(source, separator) - return tmp[len(tmp)-1], nil - } - return "", NewSliceValueNotFoundError(source) -} - -// GetSliceIndexValueE will take a source string and returns the value at the given index when split by -// the separator char. -func GetSliceIndexValueE(source string, separator string, index int) (string, error) { - if len(source) > 0 && len(separator) > 0 && strings.Contains(source, separator) && index >= 0 { - tmp := strings.Split(source, separator) - if index > len(tmp) { - return "", NewSliceValueNotFoundError(source) - } - return tmp[index], nil - } - return "", NewSliceValueNotFoundError(source) -} diff --git a/vendor/github.com/gruntwork-io/terratest/modules/docker/build.go b/vendor/github.com/gruntwork-io/terratest/modules/docker/build.go deleted file mode 100644 index 82c29508..00000000 --- a/vendor/github.com/gruntwork-io/terratest/modules/docker/build.go +++ /dev/null @@ -1,222 +0,0 @@ -package docker - -import ( - "os" - "path/filepath" - "strings" - - "github.com/gruntwork-io/terratest/modules/logger" - "github.com/gruntwork-io/terratest/modules/shell" - "github.com/gruntwork-io/terratest/modules/testing" - "github.com/hashicorp/go-multierror" - "github.com/stretchr/testify/require" -) - -// BuildOptions defines options that can be passed to the 'docker build' command. -type BuildOptions struct { - // Tags for the Docker image - Tags []string - - // Build args to pass the 'docker build' command - BuildArgs []string - - // Target build arg to pass to the 'docker build' command - Target string - - // All architectures to target in a multiarch build. Configuring this variable will cause terratest to use docker - // buildx to construct multiarch images. - // You can read more about multiarch docker builds in the official documentation for buildx: - // https://docs.docker.com/buildx/working-with-buildx/ - // NOTE: This list does not automatically include the current platform. For example, if you are building images on - // an Apple Silicon based MacBook, and you configure this variable to []string{"linux/amd64"} to build an amd64 - // image, the buildx command will not automatically include linux/arm64 - you must include that explicitly. - Architectures []string - - // Whether or not to push images directly to the registry on build. Note that for multiarch images (Architectures is - // not empty), this must be true to ensure availability of all architectures - only the image for the current - // platform will be loaded into the daemon (due to a limitation of the docker daemon), so you won't be able to run a - // `docker push` command later to push the multiarch image. - // See https://github.com/moby/moby/pull/38738 for more info on the limitation of multiarch images in docker daemon. - Push bool - - // Whether or not to load the image into the docker daemon at the end of a multiarch build so that it can be used - // locally. Note that this is only used when Architectures is set, and assumes the current architecture is already - // included in the Architectures list. - Load bool - - // Custom CLI options that will be passed as-is to the 'docker build' command. This is an "escape hatch" that allows - // Terratest to not have to support every single command-line option offered by the 'docker build' command, and - // solely focus on the most important ones. - OtherOptions []string - - // Whether ot not to enable buildkit. You can find more information about buildkit here https://docs.docker.com/build/buildkit/#getting-started. - EnableBuildKit bool - - // Additional environment variables to pass in when running docker build command. - Env map[string]string - - // Set a logger that should be used. See the logger package for more info. - Logger *logger.Logger -} - -// Build runs the 'docker build' command at the given path with the given options and fails the test if there are any -// errors. -func Build(t testing.TestingT, path string, options *BuildOptions) { - require.NoError(t, BuildE(t, path, options)) -} - -// BuildE runs the 'docker build' command at the given path with the given options and returns any errors. -func BuildE(t testing.TestingT, path string, options *BuildOptions) error { - options.Logger.Logf(t, "Running 'docker build' in %s", path) - - env := make(map[string]string) - if options.Env != nil { - env = options.Env - } - - if options.EnableBuildKit { - env["DOCKER_BUILDKIT"] = "1" - } - - cmd := shell.Command{ - Command: "docker", - Args: formatDockerBuildArgs(path, options), - Logger: options.Logger, - Env: env, - } - - if err := shell.RunCommandE(t, cmd); err != nil { - return err - } - - // For non multiarch images, we need to call docker push for each tag since build does not have a push option like - // buildx. - if len(options.Architectures) == 0 && options.Push { - var errorsOccurred = new(multierror.Error) - for _, tag := range options.Tags { - if err := PushE(t, options.Logger, tag); err != nil { - options.Logger.Logf(t, "ERROR: error pushing tag %s", tag) - errorsOccurred = multierror.Append(err) - } - } - return errorsOccurred.ErrorOrNil() - } - - // For multiarch images, if a load is requested call the load command to export the built image into the daemon. - if len(options.Architectures) > 0 && options.Load { - loadCmd := shell.Command{ - Command: "docker", - Args: formatDockerBuildxLoadArgs(path, options), - Logger: options.Logger, - } - return shell.RunCommandE(t, loadCmd) - } - - return nil -} - -// GitCloneAndBuild builds a new Docker image from a given Git repo. This function will clone the given repo at the -// specified ref, and call the docker build command on the cloned repo from the given relative path (relative to repo -// root). This will fail the test if there are any errors. -func GitCloneAndBuild( - t testing.TestingT, - repo string, - ref string, - path string, - dockerBuildOpts *BuildOptions, -) { - require.NoError(t, GitCloneAndBuildE(t, repo, ref, path, dockerBuildOpts)) -} - -// GitCloneAndBuildE builds a new Docker image from a given Git repo. This function will clone the given repo at the -// specified ref, and call the docker build command on the cloned repo from the given relative path (relative to repo -// root). -func GitCloneAndBuildE( - t testing.TestingT, - repo string, - ref string, - path string, - dockerBuildOpts *BuildOptions, -) error { - workingDir, err := os.MkdirTemp("", "") - if err != nil { - return err - } - defer os.RemoveAll(workingDir) - - cloneCmd := shell.Command{ - Command: "git", - Args: []string{"clone", repo, workingDir}, - } - if err := shell.RunCommandE(t, cloneCmd); err != nil { - return err - } - - checkoutCmd := shell.Command{ - Command: "git", - Args: []string{"checkout", ref}, - WorkingDir: workingDir, - } - if err := shell.RunCommandE(t, checkoutCmd); err != nil { - return err - } - - contextPath := filepath.Join(workingDir, path) - if err := BuildE(t, contextPath, dockerBuildOpts); err != nil { - return err - } - return nil -} - -// formatDockerBuildArgs formats the arguments for the 'docker build' command. -func formatDockerBuildArgs(path string, options *BuildOptions) []string { - args := []string{} - - if len(options.Architectures) > 0 { - args = append( - args, - "buildx", - "build", - "--platform", - strings.Join(options.Architectures, ","), - ) - if options.Push { - args = append(args, "--push") - } - } else { - args = append(args, "build") - } - - return append(args, formatDockerBuildBaseArgs(path, options)...) -} - -// formatDockerBuildxLoadArgs formats the arguments for calling load on the 'docker buildx' command. -func formatDockerBuildxLoadArgs(path string, options *BuildOptions) []string { - args := []string{ - "buildx", - "build", - "--load", - } - return append(args, formatDockerBuildBaseArgs(path, options)...) -} - -// formatDockerBuildBaseArgs formats the common args for the build command, both for `build` and `buildx`. -func formatDockerBuildBaseArgs(path string, options *BuildOptions) []string { - args := []string{} - for _, tag := range options.Tags { - args = append(args, "--tag", tag) - } - - for _, arg := range options.BuildArgs { - args = append(args, "--build-arg", arg) - } - - if len(options.Target) > 0 { - args = append(args, "--target", options.Target) - } - - args = append(args, options.OtherOptions...) - - args = append(args, path) - return args -} diff --git a/vendor/github.com/gruntwork-io/terratest/modules/docker/docker.go b/vendor/github.com/gruntwork-io/terratest/modules/docker/docker.go deleted file mode 100644 index 28d9c965..00000000 --- a/vendor/github.com/gruntwork-io/terratest/modules/docker/docker.go +++ /dev/null @@ -1,2 +0,0 @@ -// Package docker allows to interact with Docker and docker compose resources. -package docker diff --git a/vendor/github.com/gruntwork-io/terratest/modules/docker/docker_compose.go b/vendor/github.com/gruntwork-io/terratest/modules/docker/docker_compose.go deleted file mode 100644 index 330309d1..00000000 --- a/vendor/github.com/gruntwork-io/terratest/modules/docker/docker_compose.go +++ /dev/null @@ -1,99 +0,0 @@ -package docker - -import ( - "regexp" - "strings" - - "github.com/gruntwork-io/terratest/modules/logger" - "github.com/gruntwork-io/terratest/modules/shell" - "github.com/gruntwork-io/terratest/modules/testing" - "github.com/stretchr/testify/require" - "gotest.tools/v3/icmd" -) - -// Options are Docker options. -type Options struct { - WorkingDir string - EnvVars map[string]string - - // Whether ot not to enable buildkit. You can find more information about buildkit here https://docs.docker.com/build/buildkit/#getting-started. - EnableBuildKit bool - - // Set a logger that should be used. See the logger package for more info. - Logger *logger.Logger - ProjectName string -} - -// RunDockerCompose runs docker compose with the given arguments and options and return stdout/stderr. -func RunDockerCompose(t testing.TestingT, options *Options, args ...string) string { - out, err := runDockerComposeE(t, false, options, args...) - if err != nil { - t.Fatal(err) - } - return out -} - -// RunDockerComposeAndGetStdout runs docker compose with the given arguments and options and returns only stdout. -func RunDockerComposeAndGetStdOut(t testing.TestingT, options *Options, args ...string) string { - out, err := runDockerComposeE(t, true, options, args...) - require.NoError(t, err) - return out -} - -// RunDockerComposeE runs docker compose with the given arguments and options and return stdout/stderr. -func RunDockerComposeE(t testing.TestingT, options *Options, args ...string) (string, error) { - return runDockerComposeE(t, false, options, args...) -} - -func runDockerComposeE(t testing.TestingT, stdout bool, options *Options, args ...string) (string, error) { - var cmd shell.Command - - projectName := options.ProjectName - if len(projectName) <= 0 { - projectName = strings.ToLower(t.Name()) - } - - dockerComposeVersionCmd := icmd.Command("docker", "compose", "version") - result := icmd.RunCmd(dockerComposeVersionCmd) - - if options.EnableBuildKit { - if options.EnvVars == nil { - options.EnvVars = make(map[string]string) - } - - options.EnvVars["DOCKER_BUILDKIT"] = "1" - options.EnvVars["COMPOSE_DOCKER_CLI_BUILD"] = "1" - } - - if result.ExitCode == 0 { - cmd = shell.Command{ - Command: "docker", - Args: append([]string{"compose", "--project-name", generateValidDockerComposeProjectName(projectName)}, args...), - WorkingDir: options.WorkingDir, - Env: options.EnvVars, - Logger: options.Logger, - } - } else { - cmd = shell.Command{ - Command: "docker-compose", - // We append --project-name to ensure containers from multiple different tests using Docker Compose don't end - // up in the same project and end up conflicting with each other. - Args: append([]string{"--project-name", generateValidDockerComposeProjectName(projectName)}, args...), - WorkingDir: options.WorkingDir, - Env: options.EnvVars, - Logger: options.Logger, - } - } - - if stdout { - return shell.RunCommandAndGetStdOut(t, cmd), nil - } - - return shell.RunCommandAndGetOutputE(t, cmd) -} - -// Note: docker-compose command doesn't like lower case or special characters, other than -. -func generateValidDockerComposeProjectName(str string) string { - lower_str := strings.ToLower(str) - return regexp.MustCompile(`[^a-zA-Z0-9 ]+`).ReplaceAllString(lower_str, "-") -} diff --git a/vendor/github.com/gruntwork-io/terratest/modules/docker/host.go b/vendor/github.com/gruntwork-io/terratest/modules/docker/host.go deleted file mode 100644 index d8142b12..00000000 --- a/vendor/github.com/gruntwork-io/terratest/modules/docker/host.go +++ /dev/null @@ -1,40 +0,0 @@ -package docker - -import ( - "os" - "strings" -) - -// GetDockerHost returns the name or address of the host on which the Docker engine is running. -func GetDockerHost() string { - return getDockerHostFromEnv(os.Environ()) -} - -func getDockerHostFromEnv(env []string) string { - // Parses the DOCKER_HOST environment variable to find the address - // - // For valid formats see: - // https://github.com/docker/cli/blob/6916b427a0b07e8581d121967633235ced6db9a1/opts/hosts.go#L69 - var dockerUrl []string - - for _, item := range env { - envVar := strings.Split(item, "=") - if len(envVar) == 2 && envVar[0] == "DOCKER_HOST" { - dockerUrl = strings.Split(envVar[1], ":") - break - } - } - - if len(dockerUrl) < 2 { - // DOCKER_HOST was empty, not present or not a valid URL - return "localhost" - } - - switch dockerUrl[0] { - case "tcp", "ssh", "fd": - return strings.TrimPrefix(dockerUrl[1], "//") - default: - // if DOCKER_HOST is not in one of the formats listed above, return default - return "localhost" - } -} diff --git a/vendor/github.com/gruntwork-io/terratest/modules/docker/images.go b/vendor/github.com/gruntwork-io/terratest/modules/docker/images.go deleted file mode 100644 index a4f63940..00000000 --- a/vendor/github.com/gruntwork-io/terratest/modules/docker/images.go +++ /dev/null @@ -1,112 +0,0 @@ -package docker - -import ( - "bufio" - "encoding/json" - "fmt" - "strings" - - "github.com/gruntwork-io/terratest/modules/collections" - "github.com/gruntwork-io/terratest/modules/logger" - "github.com/gruntwork-io/terratest/modules/shell" - "github.com/gruntwork-io/terratest/modules/testing" - "github.com/stretchr/testify/require" -) - -// Image represents a docker image, and exports all the fields that the docker images command returns for the -// image. -type Image struct { - // ID is the image ID in docker, and can be used to identify the image in place of the repo and tag. - ID string - - // Repository is the image repository. - Repository string - - // Tag is the image tag wichin the repository. - Tag string - - // CreatedAt represents a timestamp for when the image was created. - CreatedAt string - - // CreatedSince is a diff between when the image was created to now. - CreatedSince string - - // SharedSize is the amount of space that an image shares with another one (i.e. their common data). - SharedSize string - - // UniqueSize is the amount of space that is only used by a given image. - UniqueSize string - - // VirtualSize is the total size of the image, combining SharedSize and UniqueSize. - VirtualSize string - - // Containers represents the list of containers that are using the image. - Containers string - - // Digest is the hash digest of the image, if requested. - Digest string -} - -func (image Image) String() string { - return fmt.Sprintf("%s:%s", image.Repository, image.Tag) -} - -// DeleteImage removes a docker image using the Docker CLI. This will fail the test if there is an error. -func DeleteImage(t testing.TestingT, img string, logger *logger.Logger) { - require.NoError(t, DeleteImageE(t, img, logger)) -} - -// DeleteImageE removes a docker image using the Docker CLI. -func DeleteImageE(t testing.TestingT, img string, logger *logger.Logger) error { - cmd := shell.Command{ - Command: "docker", - Args: []string{"rmi", img}, - Logger: logger, - } - return shell.RunCommandE(t, cmd) -} - -// ListImages calls docker images using the Docker CLI to list the available images on the local docker daemon. -func ListImages(t testing.TestingT, logger *logger.Logger) []Image { - out, err := ListImagesE(t, logger) - require.NoError(t, err) - return out -} - -// ListImagesE calls docker images using the Docker CLI to list the available images on the local docker daemon. -func ListImagesE(t testing.TestingT, logger *logger.Logger) ([]Image, error) { - cmd := shell.Command{ - Command: "docker", - Args: []string{"images", "--format", "{{ json . }}"}, - Logger: logger, - } - out, err := shell.RunCommandAndGetOutputE(t, cmd) - if err != nil { - return nil, err - } - - // Parse and return the list of image objects. - images := []Image{} - scanner := bufio.NewScanner(strings.NewReader(out)) - for scanner.Scan() { - line := scanner.Text() - var image Image - err := json.Unmarshal([]byte(line), &image) - if err != nil { - return nil, err - } - images = append(images, image) - } - return images, nil -} - -// DoesImageExist lists the images in the docker daemon and returns true if the given image label (repo:tag) exists. -// This will fail the test if there is an error. -func DoesImageExist(t testing.TestingT, imgLabel string, logger *logger.Logger) bool { - images := ListImages(t, logger) - imageTags := []string{} - for _, image := range images { - imageTags = append(imageTags, image.String()) - } - return collections.ListContains(imageTags, imgLabel) -} diff --git a/vendor/github.com/gruntwork-io/terratest/modules/docker/inspect.go b/vendor/github.com/gruntwork-io/terratest/modules/docker/inspect.go deleted file mode 100644 index 6d058d1b..00000000 --- a/vendor/github.com/gruntwork-io/terratest/modules/docker/inspect.go +++ /dev/null @@ -1,270 +0,0 @@ -package docker - -import ( - "encoding/json" - "fmt" - "strconv" - "strings" - "testing" - "time" - - "github.com/gruntwork-io/terratest/modules/logger" - "github.com/gruntwork-io/terratest/modules/shell" - "github.com/stretchr/testify/require" -) - -// ContainerInspect defines the output of the Inspect method, with the options returned by 'docker inspect' -// converted into a more friendly and testable interface -type ContainerInspect struct { - // ID of the inspected container - ID string - - // Name of the inspected container - Name string - - // time.Time that the container was created - Created time.Time - - // String representing the container's status - Status string - - // Whether the container is currently running or not - Running bool - - // Container's exit code - ExitCode uint8 - - // String with the container's error message, if there is any - Error string - - // Ports exposed by the container - Ports []Port - - // Volume bindings made to the container - Binds []VolumeBind - - // Health check - Health HealthCheck -} - -// Port represents a single port mapping exported by the container -type Port struct { - HostPort uint16 - ContainerPort uint16 - Protocol string -} - -// VolumeBind represents a single volume binding made to the container -type VolumeBind struct { - Source string - Destination string -} - -// HealthCheck represents the current health history of the container -type HealthCheck struct { - // Health check status - Status string - - // Current count of failing health checks - FailingStreak uint8 - - // Log of failures - Log []HealthLog -} - -// HealthLog represents the output of a single Health check of the container -type HealthLog struct { - // Start time of health check - Start string - - // End time of health check - End string - - // Exit code of health check - ExitCode uint8 - - // Output of health check - Output string -} - -// inspectOutput defines options that will be returned by 'docker inspect', in JSON format. -// Not all options are included here, only the ones that we might need -type inspectOutput struct { - Id string - Created string - Name string - State struct { - Health HealthCheck - Status string - Running bool - ExitCode uint8 - Error string - } - NetworkSettings struct { - Ports map[string][]struct { - HostIp string - HostPort string - } - } - HostConfig struct { - Binds []string - } -} - -// Inspect runs the 'docker inspect {container id}' command and returns a ContainerInspect -// struct, converted from the output JSON, along with any errors -func Inspect(t *testing.T, id string) *ContainerInspect { - out, err := InspectE(t, id) - require.NoError(t, err) - - return out -} - -// InspectE runs the 'docker inspect {container id}' command and returns a ContainerInspect -// struct, converted from the output JSON, along with any errors -func InspectE(t *testing.T, id string) (*ContainerInspect, error) { - cmd := shell.Command{ - Command: "docker", - Args: []string{"container", "inspect", id}, - // inspect is a short-running command, don't print the output. - Logger: logger.Discard, - } - - out, err := shell.RunCommandAndGetStdOutE(t, cmd) - if err != nil { - return nil, err - } - - var containers []inspectOutput - err = json.Unmarshal([]byte(out), &containers) - if err != nil { - return nil, err - } - - if len(containers) == 0 { - return nil, fmt.Errorf("no container found with ID %s", id) - } - - container := containers[0] - - return transformContainer(t, container) -} - -// transformContainerPorts converts 'docker inspect' output JSON into a more friendly and testable format -func transformContainer(t *testing.T, container inspectOutput) (*ContainerInspect, error) { - name := strings.TrimLeft(container.Name, "/") - - ports, err := transformContainerPorts(container) - if err != nil { - return nil, err - } - - volumes := transformContainerVolumes(container) - - created, err := time.Parse(time.RFC3339Nano, container.Created) - if err != nil { - return nil, err - } - - inspect := ContainerInspect{ - ID: container.Id, - Name: name, - Created: created, - Status: container.State.Status, - Running: container.State.Running, - ExitCode: container.State.ExitCode, - Error: container.State.Error, - Ports: ports, - Binds: volumes, - Health: HealthCheck{ - Status: container.State.Health.Status, - FailingStreak: container.State.Health.FailingStreak, - Log: container.State.Health.Log, - }, - } - - return &inspect, nil -} - -// transformContainerPorts converts Docker's ports from the following json into a more testable format -// -// { -// "80/tcp": [ -// { -// "HostIp": "" -// "HostPort": "8080" -// } -// ] -// } -func transformContainerPorts(container inspectOutput) ([]Port, error) { - var ports []Port - - cPorts := container.NetworkSettings.Ports - - for key, portBinding := range cPorts { - split := strings.Split(key, "/") - - containerPort, err := strconv.ParseUint(split[0], 10, 16) - if err != nil { - return nil, err - } - - var protocol string - if len(split) > 1 { - protocol = split[1] - } - - for _, port := range portBinding { - hostPort, err := strconv.ParseUint(port.HostPort, 10, 16) - if err != nil { - return nil, err - } - - ports = append(ports, Port{ - HostPort: uint16(hostPort), - ContainerPort: uint16(containerPort), - Protocol: protocol, - }) - } - } - - return ports, nil -} - -// GetExposedHostPort returns an exposed host port according to requested container port. Returns 0 if the requested port is not exposed. -func (inspectOutput ContainerInspect) GetExposedHostPort(containerPort uint16) uint16 { - for _, port := range inspectOutput.Ports { - if port.ContainerPort == containerPort { - return port.HostPort - } - } - return uint16(0) -} - -// transformContainerVolumes converts Docker's volume bindings from the -// format "/foo/bar:/foo/baz" into a more testable one -func transformContainerVolumes(container inspectOutput) []VolumeBind { - binds := container.HostConfig.Binds - volumes := make([]VolumeBind, 0, len(binds)) - - for _, bind := range binds { - var source, dest string - - split := strings.Split(bind, ":") - - // Considering it as an unbound volume - dest = split[0] - - if len(split) == 2 { - source = split[0] - dest = split[1] - } - - volumes = append(volumes, VolumeBind{ - Source: source, - Destination: dest, - }) - } - - return volumes -} diff --git a/vendor/github.com/gruntwork-io/terratest/modules/docker/push.go b/vendor/github.com/gruntwork-io/terratest/modules/docker/push.go deleted file mode 100644 index af0659b5..00000000 --- a/vendor/github.com/gruntwork-io/terratest/modules/docker/push.go +++ /dev/null @@ -1,25 +0,0 @@ -package docker - -import ( - "github.com/gruntwork-io/terratest/modules/logger" - "github.com/gruntwork-io/terratest/modules/shell" - "github.com/gruntwork-io/terratest/modules/testing" - "github.com/stretchr/testify/require" -) - -// Push runs the 'docker push' command to push the given tag. This will fail the test if there are any errors. -func Push(t testing.TestingT, logger *logger.Logger, tag string) { - require.NoError(t, PushE(t, logger, tag)) -} - -// PushE runs the 'docker push' command to push the given tag. -func PushE(t testing.TestingT, logger *logger.Logger, tag string) error { - logger.Logf(t, "Running 'docker push' for tag %s", tag) - - cmd := shell.Command{ - Command: "docker", - Args: []string{"push", tag}, - Logger: logger, - } - return shell.RunCommandE(t, cmd) -} diff --git a/vendor/github.com/gruntwork-io/terratest/modules/docker/run.go b/vendor/github.com/gruntwork-io/terratest/modules/docker/run.go deleted file mode 100644 index d57a4bb0..00000000 --- a/vendor/github.com/gruntwork-io/terratest/modules/docker/run.go +++ /dev/null @@ -1,166 +0,0 @@ -package docker - -import ( - "github.com/gruntwork-io/terratest/modules/logger" - "github.com/gruntwork-io/terratest/modules/shell" - "github.com/gruntwork-io/terratest/modules/testing" - "github.com/stretchr/testify/require" -) - -// RunOptions defines options that can be passed to the 'docker run' command. -type RunOptions struct { - // Override the default COMMAND of the Docker image - Command []string - - // If set to true, pass the --detach flag to 'docker run' to run the container in the background - Detach bool - - // Override the default ENTRYPOINT of the Docker image - Entrypoint string - - // Set environment variables - EnvironmentVariables []string - - // If set to true, pass the --init flag to 'docker run' to run an init inside the container that forwards signals - // and reaps processes - Init bool - - // Assign a name to the container - Name string - - // Set platform - Platform string - - // If set to true, pass the --privileged flag to 'docker run' to give extended privileges to the container - Privileged bool - - // If set to true, pass the --rm flag to 'docker run' to automatically remove the container when it exits - Remove bool - - // If set to true, pass the -tty flag to 'docker run' to allocate a pseudo-TTY - Tty bool - - // Username or UID - User string - - // Bind mount these volume(s) when running the container - Volumes []string - - // Custom CLI options that will be passed as-is to the 'docker run' command. This is an "escape hatch" that allows - // Terratest to not have to support every single command-line option offered by the 'docker run' command, and - // solely focus on the most important ones. - OtherOptions []string - - // Set a logger that should be used. See the logger package for more info. - Logger *logger.Logger -} - -// Run runs the 'docker run' command on the given image with the given options and return stdout/stderr. This method -// fails the test if there are any errors. -func Run(t testing.TestingT, image string, options *RunOptions) string { - out, err := RunE(t, image, options) - require.NoError(t, err) - return out -} - -// RunE runs the 'docker run' command on the given image with the given options and return stdout/stderr, or any error. -func RunE(t testing.TestingT, image string, options *RunOptions) (string, error) { - options.Logger.Logf(t, "Running 'docker run' on image '%s'", image) - - args, err := formatDockerRunArgs(image, options) - if err != nil { - return "", err - } - - cmd := shell.Command{ - Command: "docker", - Args: args, - Logger: options.Logger, - } - - return shell.RunCommandAndGetOutputE(t, cmd) -} - -// RunAndGetID runs the 'docker run' command on the given image with the given options and returns the container ID -// that is returned in stdout. This method fails the test if there are any errors. -func RunAndGetID(t testing.TestingT, image string, options *RunOptions) string { - out, err := RunAndGetIDE(t, image, options) - require.NoError(t, err) - return out -} - -// RunAndGetIDE runs the 'docker run' command on the given image with the given options and returns the container ID -// that is returned in stdout, or any error. -func RunAndGetIDE(t testing.TestingT, image string, options *RunOptions) (string, error) { - options.Logger.Logf(t, "Running 'docker run' on image '%s', returning stdout", image) - - args, err := formatDockerRunArgs(image, options) - if err != nil { - return "", err - } - - cmd := shell.Command{ - Command: "docker", - Args: args, - Logger: options.Logger, - } - - return shell.RunCommandAndGetStdOutE(t, cmd) -} - -// formatDockerRunArgs formats the arguments for the 'docker run' command. -func formatDockerRunArgs(image string, options *RunOptions) ([]string, error) { - args := []string{"run"} - - if options.Detach { - args = append(args, "--detach") - } - - if options.Entrypoint != "" { - args = append(args, "--entrypoint", options.Entrypoint) - } - - for _, envVar := range options.EnvironmentVariables { - args = append(args, "--env", envVar) - } - - if options.Init { - args = append(args, "--init") - } - - if options.Name != "" { - args = append(args, "--name", options.Name) - } - - if options.Platform != "" { - args = append(args, "--platform", options.Platform) - } - - if options.Privileged { - args = append(args, "--privileged") - } - - if options.Remove { - args = append(args, "--rm") - } - - if options.Tty { - args = append(args, "--tty") - } - - if options.User != "" { - args = append(args, "--user", options.User) - } - - for _, volume := range options.Volumes { - args = append(args, "--volume", volume) - } - - args = append(args, options.OtherOptions...) - - args = append(args, image) - - args = append(args, options.Command...) - - return args, nil -} diff --git a/vendor/github.com/gruntwork-io/terratest/modules/docker/stop.go b/vendor/github.com/gruntwork-io/terratest/modules/docker/stop.go deleted file mode 100644 index e5a43c60..00000000 --- a/vendor/github.com/gruntwork-io/terratest/modules/docker/stop.go +++ /dev/null @@ -1,59 +0,0 @@ -package docker - -import ( - "strconv" - - "github.com/gruntwork-io/terratest/modules/logger" - "github.com/gruntwork-io/terratest/modules/shell" - "github.com/gruntwork-io/terratest/modules/testing" - "github.com/stretchr/testify/require" -) - -// StopOptions defines the options that can be passed to the 'docker stop' command -type StopOptions struct { - // Seconds to wait for stop before killing the container (default 10) - Time int - - // Set a logger that should be used. See the logger package for more info. - Logger *logger.Logger -} - -// Stop runs the 'docker stop' command for the given containers and return the stdout/stderr. This method fails -// the test if there are any errors -func Stop(t testing.TestingT, containers []string, options *StopOptions) string { - out, err := StopE(t, containers, options) - require.NoError(t, err) - return out -} - -// StopE runs the 'docker stop' command for the given containers and returns any errors. -func StopE(t testing.TestingT, containers []string, options *StopOptions) (string, error) { - options.Logger.Logf(t, "Running 'docker stop' on containers '%s'", containers) - - args, err := formatDockerStopArgs(containers, options) - if err != nil { - return "", err - } - - cmd := shell.Command{ - Command: "docker", - Args: args, - Logger: options.Logger, - } - - return shell.RunCommandAndGetOutputE(t, cmd) - -} - -// formatDockerStopArgs formats the arguments for the 'docker stop' command -func formatDockerStopArgs(containers []string, options *StopOptions) ([]string, error) { - args := []string{"stop"} - - if options.Time != 0 { - args = append(args, "--time", strconv.Itoa(options.Time)) - } - - args = append(args, containers...) - - return args, nil -} diff --git a/vendor/github.com/gruntwork-io/terratest/modules/logger/logger.go b/vendor/github.com/gruntwork-io/terratest/modules/logger/logger.go deleted file mode 100644 index abc8c14b..00000000 --- a/vendor/github.com/gruntwork-io/terratest/modules/logger/logger.go +++ /dev/null @@ -1,178 +0,0 @@ -// Package logger contains different methods to log. -package logger - -import ( - "fmt" - "io" - "os" - "runtime" - "strings" - "sync" - gotesting "testing" - "time" - - "github.com/gruntwork-io/terratest/modules/testing" -) - -var ( - // Default is the default logger that is used for the Logf function, if no one is provided. It uses the - // TerratestLogger to log messages. This can be overwritten to change the logging globally. - Default = New(terratestLogger{}) - // Discard discards all logging. - Discard = New(discardLogger{}) - // Terratest logs the given format and arguments, formatted using fmt.Sprintf, to stdout, along with a timestamp and - // information about what test and file is doing the logging. Before Go 1.14, this is an alternative to t.Logf as it - // logs to stdout immediately, rather than buffering all log output and only displaying it at the very end of the test. - // This is useful because: - // - // 1. It allows you to iterate faster locally, as you get feedback on whether your code changes are working as expected - // right away, rather than at the very end of the test run. - // - // 2. If you have a bug in your code that causes a test to never complete or if the test code crashes, t.Logf would - // show you no log output whatsoever, making debugging very hard, where as this method will show you all the log - // output available. - // - // 3. If you have a test that takes a long time to complete, some CI systems will kill the test suite prematurely - // because there is no log output with t.Logf (e.g., CircleCI kills tests after 10 minutes of no log output). With - // this log method, you get log output continuously. - // - Terratest = New(terratestLogger{}) - // TestingT can be used to use Go's testing.T to log. If this is used, but no testing.T is provided, it will fallback - // to Default. - TestingT = New(testingT{}) -) - -type TestLogger interface { - Logf(t testing.TestingT, format string, args ...interface{}) -} - -type Logger struct { - l TestLogger -} - -func New(l TestLogger) *Logger { - return &Logger{ - l, - } -} - -func (l *Logger) Logf(t testing.TestingT, format string, args ...interface{}) { - if tt, ok := t.(helper); ok { - tt.Helper() - } - - // methods can be called on (typed) nil pointers. In this case, use the Default function to log. This enables the - // caller to do `var l *Logger` and then use the logger already. - if l == nil || l.l == nil { - Default.Logf(t, format, args...) - return - } - - l.l.Logf(t, format, args...) -} - -// helper is used to mark this library as a "helper", and thus not appearing in the line numbers. testing.T implements -// this interface, for example. -type helper interface { - Helper() -} - -type discardLogger struct{} - -func (_ discardLogger) Logf(_ testing.TestingT, format string, args ...interface{}) {} - -type testingT struct{} - -func (_ testingT) Logf(t testing.TestingT, format string, args ...interface{}) { - // this should never fail - tt, ok := t.(*gotesting.T) - if !ok { - // fallback - DoLog(t, 2, os.Stdout, fmt.Sprintf(format, args...)) - return - } - - tt.Helper() - tt.Logf(format, args...) - return -} - -type terratestLogger struct{} - -func (_ terratestLogger) Logf(t testing.TestingT, format string, args ...interface{}) { - DoLog(t, 3, os.Stdout, fmt.Sprintf(format, args...)) -} - -// Deprecated: use Logger instead, as it provides more flexibility on logging. -// Logf logs the given format and arguments, formatted using fmt.Sprintf, to stdout, along with a timestamp and information -// about what test and file is doing the logging. Before Go 1.14, this is an alternative to t.Logf as it logs to stdout -// immediately, rather than buffering all log output and only displaying it at the very end of the test. This is useful -// because: -// -// 1. It allows you to iterate faster locally, as you get feedback on whether your code changes are working as expected -// right away, rather than at the very end of the test run. -// -// 2. If you have a bug in your code that causes a test to never complete or if the test code crashes, t.Logf would -// show you no log output whatsoever, making debugging very hard, where as this method will show you all the log -// output available. -// -// 3. If you have a test that takes a long time to complete, some CI systems will kill the test suite prematurely -// because there is no log output with t.Logf (e.g., CircleCI kills tests after 10 minutes of no log output). With -// this log method, you get log output continuously. -// -// Although t.Logf now supports streaming output since Go 1.14, this is kept for compatibility purposes. -func Logf(t testing.TestingT, format string, args ...interface{}) { - if tt, ok := t.(helper); ok { - tt.Helper() - } - - mutexStdout.Lock() - defer mutexStdout.Unlock() - DoLog(t, 2, os.Stdout, fmt.Sprintf(format, args...)) -} - -// Log logs the given arguments to stdout, along with a timestamp and information about what test and file is doing the -// logging. This is an alternative to t.Logf that logs to stdout immediately, rather than buffering all log output and -// only displaying it at the very end of the test. See the Logf method for more info. -func Log(t testing.TestingT, args ...interface{}) { - if tt, ok := t.(helper); ok { - tt.Helper() - } - - mutexStdout.Lock() - defer mutexStdout.Unlock() - DoLog(t, 2, os.Stdout, args...) -} - -var mutexStdout sync.Mutex - -// DoLog logs the given arguments to the given writer, along with a timestamp and information about what test and file is -// doing the logging. -func DoLog(t testing.TestingT, callDepth int, writer io.Writer, args ...interface{}) { - date := time.Now() - prefix := fmt.Sprintf("%s %s %s:", t.Name(), date.Format(time.RFC3339), CallerPrefix(callDepth+1)) - allArgs := append([]interface{}{prefix}, args...) - fmt.Fprintln(writer, allArgs...) -} - -// CallerPrefix returns the file and line number information about the methods that called this method, based on the current -// goroutine's stack. The argument callDepth is the number of stack frames to ascend, with 0 identifying the method -// that called CallerPrefix, 1 identifying the method that called that method, and so on. -// -// This code is adapted from testing.go, where it is in a private method called decorate. -func CallerPrefix(callDepth int) string { - _, file, line, ok := runtime.Caller(callDepth) - if ok { - // Truncate file name at last file name separator. - if index := strings.LastIndex(file, "/"); index >= 0 { - file = file[index+1:] - } else if index = strings.LastIndex(file, "\\"); index >= 0 { - file = file[index+1:] - } - } else { - file = "???" - line = 1 - } - - return fmt.Sprintf("%s:%d", file, line) -} diff --git a/vendor/github.com/gruntwork-io/terratest/modules/shell/command.go b/vendor/github.com/gruntwork-io/terratest/modules/shell/command.go deleted file mode 100644 index db964069..00000000 --- a/vendor/github.com/gruntwork-io/terratest/modules/shell/command.go +++ /dev/null @@ -1,252 +0,0 @@ -package shell - -import ( - "bufio" - "errors" - "fmt" - "io" - "os" - "os/exec" - "strings" - "sync" - "syscall" - - "github.com/gruntwork-io/terratest/modules/logger" - "github.com/gruntwork-io/terratest/modules/testing" - "github.com/stretchr/testify/require" -) - -// Command is a simpler struct for defining commands than Go's built-in Cmd. -type Command struct { - Command string // The command to run - Args []string // The args to pass to the command - WorkingDir string // The working directory - Env map[string]string // Additional environment variables to set - // Use the specified logger for the command's output. Use logger.Discard to not print the output while executing the command. - Logger *logger.Logger -} - -// RunCommand runs a shell command and redirects its stdout and stderr to the stdout of the atomic script itself. If -// there are any errors, fail the test. -func RunCommand(t testing.TestingT, command Command) { - err := RunCommandE(t, command) - require.NoError(t, err) -} - -// RunCommandE runs a shell command and redirects its stdout and stderr to the stdout of the atomic script itself. Any -// returned error will be of type ErrWithCmdOutput, containing the output streams and the underlying error. -func RunCommandE(t testing.TestingT, command Command) error { - output, err := runCommand(t, command) - if err != nil { - return &ErrWithCmdOutput{err, output} - } - return nil -} - -// RunCommandAndGetOutput runs a shell command and returns its stdout and stderr as a string. The stdout and stderr of -// that command will also be logged with Command.Log to make debugging easier. If there are any errors, fail the test. -func RunCommandAndGetOutput(t testing.TestingT, command Command) string { - out, err := RunCommandAndGetOutputE(t, command) - require.NoError(t, err) - return out -} - -// RunCommandAndGetOutputE runs a shell command and returns its stdout and stderr as a string. The stdout and stderr of -// that command will also be logged with Command.Log to make debugging easier. Any returned error will be of type -// ErrWithCmdOutput, containing the output streams and the underlying error. -func RunCommandAndGetOutputE(t testing.TestingT, command Command) (string, error) { - output, err := runCommand(t, command) - if err != nil { - return output.Combined(), &ErrWithCmdOutput{err, output} - } - - return output.Combined(), nil -} - -// RunCommandAndGetStdOut runs a shell command and returns solely its stdout (but not stderr) as a string. The stdout and -// stderr of that command will also be logged with Command.Log to make debugging easier. If there are any errors, fail -// the test. -func RunCommandAndGetStdOut(t testing.TestingT, command Command) string { - output, err := RunCommandAndGetStdOutE(t, command) - require.NoError(t, err) - return output -} - -// RunCommandAndGetStdOutE runs a shell command and returns solely its stdout (but not stderr) as a string. The stdout -// and stderr of that command will also be printed to the stdout and stderr of this Go program to make debugging easier. -// Any returned error will be of type ErrWithCmdOutput, containing the output streams and the underlying error. -func RunCommandAndGetStdOutE(t testing.TestingT, command Command) (string, error) { - output, err := runCommand(t, command) - if err != nil { - return output.Stdout(), &ErrWithCmdOutput{err, output} - } - - return output.Stdout(), nil -} - -// RunCommandAndGetStdOutErr runs a shell command and returns solely its stdout and stderr as a string. The stdout and -// stderr of that command will also be logged with Command.Log to make debugging easier. If there are any errors, fail -// the test. -func RunCommandAndGetStdOutErr(t testing.TestingT, command Command) (stdout string, stderr string) { - stdout, stderr, err := RunCommandAndGetStdOutErrE(t, command) - require.NoError(t, err) - return stdout, stderr -} - -// RunCommandAndGetStdOutErrE runs a shell command and returns solely its stdout and stderr as a string. The stdout -// and stderr of that command will also be printed to the stdout and stderr of this Go program to make debugging easier. -// Any returned error will be of type ErrWithCmdOutput, containing the output streams and the underlying error. -func RunCommandAndGetStdOutErrE(t testing.TestingT, command Command) (stdout string, stderr string, err error) { - output, err := runCommand(t, command) - if err != nil { - return output.Stdout(), output.Stderr(), &ErrWithCmdOutput{err, output} - } - - return output.Stdout(), output.Stderr(), nil -} - -type ErrWithCmdOutput struct { - Underlying error - Output *output -} - -func (e *ErrWithCmdOutput) Error() string { - return fmt.Sprintf("error while running command: %v; %s", e.Underlying, e.Output.Stderr()) -} - -// runCommand runs a shell command and stores each line from stdout and stderr in Output. Depending on the logger, the -// stdout and stderr of that command will also be printed to the stdout and stderr of this Go program to make debugging -// easier. -func runCommand(t testing.TestingT, command Command) (*output, error) { - command.Logger.Logf(t, "Running command %s with args %s", command.Command, command.Args) - - cmd := exec.Command(command.Command, command.Args...) - cmd.Dir = command.WorkingDir - cmd.Stdin = os.Stdin - cmd.Env = formatEnvVars(command) - - stdout, err := cmd.StdoutPipe() - if err != nil { - return nil, err - } - - stderr, err := cmd.StderrPipe() - if err != nil { - return nil, err - } - - err = cmd.Start() - if err != nil { - return nil, err - } - - output, err := readStdoutAndStderr(t, command.Logger, stdout, stderr) - if err != nil { - return output, err - } - - return output, cmd.Wait() -} - -// This function captures stdout and stderr into the given variables while still printing it to the stdout and stderr -// of this Go program -func readStdoutAndStderr(t testing.TestingT, log *logger.Logger, stdout, stderr io.ReadCloser) (*output, error) { - out := newOutput() - stdoutReader := bufio.NewReader(stdout) - stderrReader := bufio.NewReader(stderr) - - wg := &sync.WaitGroup{} - - wg.Add(2) - var stdoutErr, stderrErr error - go func() { - defer wg.Done() - stdoutErr = readData(t, log, stdoutReader, out.stdout) - }() - go func() { - defer wg.Done() - stderrErr = readData(t, log, stderrReader, out.stderr) - }() - wg.Wait() - - if stdoutErr != nil { - return out, stdoutErr - } - if stderrErr != nil { - return out, stderrErr - } - - return out, nil -} - -func readData(t testing.TestingT, log *logger.Logger, reader *bufio.Reader, writer io.StringWriter) error { - var line string - var readErr error - for { - line, readErr = reader.ReadString('\n') - - // remove newline, our output is in a slice, - // one element per line. - line = strings.TrimSuffix(line, "\n") - - // only return early if the line does not have - // any contents. We could have a line that does - // not not have a newline before io.EOF, we still - // need to add it to the output. - if len(line) == 0 && readErr == io.EOF { - break - } - - // logger.Logger has a Logf method, but not a Log method. - // We have to use the format string indirection to avoid - // interpreting any possible formatting characters in - // the line. - // - // See https://github.com/gruntwork-io/terratest/issues/982. - log.Logf(t, "%s", line) - - if _, err := writer.WriteString(line); err != nil { - return err - } - - if readErr != nil { - break - } - } - if readErr != io.EOF { - return readErr - } - return nil -} - -// GetExitCodeForRunCommandError tries to read the exit code for the error object returned from running a shell command. This is a bit tricky to do -// in a way that works across platforms. -func GetExitCodeForRunCommandError(err error) (int, error) { - if errWithOutput, ok := err.(*ErrWithCmdOutput); ok { - err = errWithOutput.Underlying - } - - // http://stackoverflow.com/a/10385867/483528 - if exitErr, ok := err.(*exec.ExitError); ok { - // The program has exited with an exit code != 0 - - // This works on both Unix and Windows. Although package - // syscall is generally platform dependent, WaitStatus is - // defined for both Unix and Windows and in both cases has - // an ExitStatus() method with the same signature. - if status, ok := exitErr.Sys().(syscall.WaitStatus); ok { - return status.ExitStatus(), nil - } - return 1, errors.New("could not determine exit code") - } - - return 0, nil -} - -func formatEnvVars(command Command) []string { - env := os.Environ() - for key, value := range command.Env { - env = append(env, fmt.Sprintf("%s=%s", key, value)) - } - return env -} diff --git a/vendor/github.com/gruntwork-io/terratest/modules/shell/output.go b/vendor/github.com/gruntwork-io/terratest/modules/shell/output.go deleted file mode 100644 index 616f02a5..00000000 --- a/vendor/github.com/gruntwork-io/terratest/modules/shell/output.go +++ /dev/null @@ -1,92 +0,0 @@ -package shell - -import ( - "strings" - "sync" -) - -// output contains the output after runnig a command. -type output struct { - stdout *outputStream - stderr *outputStream - // merged contains stdout and stderr merged into one stream. - merged *merged -} - -func newOutput() *output { - m := new(merged) - return &output{ - merged: m, - stdout: &outputStream{ - merged: m, - }, - stderr: &outputStream{ - merged: m, - }, - } -} - -func (o *output) Stdout() string { - if o == nil { - return "" - } - - return o.stdout.String() -} - -func (o *output) Stderr() string { - if o == nil { - return "" - } - - return o.stderr.String() -} - -func (o *output) Combined() string { - if o == nil { - return "" - } - - return o.merged.String() -} - -type outputStream struct { - Lines []string - *merged -} - -func (st *outputStream) WriteString(s string) (n int, err error) { - st.Lines = append(st.Lines, string(s)) - return st.merged.WriteString(s) -} - -func (st *outputStream) String() string { - if st == nil { - return "" - } - - return strings.Join(st.Lines, "\n") -} - -type merged struct { - // ensure that there are no parallel writes - sync.Mutex - Lines []string -} - -func (m *merged) String() string { - if m == nil { - return "" - } - - return strings.Join(m.Lines, "\n") -} - -func (m *merged) WriteString(s string) (n int, err error) { - m.Lock() - defer m.Unlock() - - m.Lines = append(m.Lines, string(s)) - - return len(s), nil -} diff --git a/vendor/github.com/gruntwork-io/terratest/modules/shell/shell.go b/vendor/github.com/gruntwork-io/terratest/modules/shell/shell.go deleted file mode 100644 index 5bbccbc8..00000000 --- a/vendor/github.com/gruntwork-io/terratest/modules/shell/shell.go +++ /dev/null @@ -1,2 +0,0 @@ -// Package shell allows to run commands in a shell. -package shell diff --git a/vendor/github.com/gruntwork-io/terratest/modules/testing/types.go b/vendor/github.com/gruntwork-io/terratest/modules/testing/types.go deleted file mode 100644 index 13c5d902..00000000 --- a/vendor/github.com/gruntwork-io/terratest/modules/testing/types.go +++ /dev/null @@ -1,28 +0,0 @@ -package testing - -// TestingT is an interface that describes the implementation of the testing object -// that the majority of Terratest functions accept as first argument. -// Using an interface that describes testing.T instead of the actual implementation -// makes terratest usable in a wider variety of contexts (e.g. use with ginkgo : https://godoc.org/github.com/onsi/ginkgo#GinkgoT) -type TestingT interface { - //Fail marks the function as having failed but continues execution. - Fail() - // FailNow marks the function as having failed and stops its execution - // by calling runtime.Goexit (which then runs all deferred calls in the - // current goroutine). - // Execution will continue at the next test or benchmark. - // FailNow must be called from the goroutine running the - // test or benchmark function, not from other goroutines - // created during the test. Calling FailNow does not stop - // those other goroutines. - FailNow() - Fatal(args ...interface{}) - // Fatalf is equivalent to Logf followed by FailNow. - Fatalf(format string, args ...interface{}) - // Error is equivalent to Log followed by Fail. - Error(args ...interface{}) - // Errorf is equivalent to Logf followed by Fail. - Errorf(format string, args ...interface{}) - // Name returns the name of the running test or benchmark. - Name() string -} diff --git a/vendor/gotest.tools/v3/LICENSE b/vendor/gotest.tools/v3/LICENSE deleted file mode 100644 index aeaa2fac..00000000 --- a/vendor/gotest.tools/v3/LICENSE +++ /dev/null @@ -1,13 +0,0 @@ -Copyright 2018 gotest.tools 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. diff --git a/vendor/gotest.tools/v3/assert/assert.go b/vendor/gotest.tools/v3/assert/assert.go deleted file mode 100644 index c418bd07..00000000 --- a/vendor/gotest.tools/v3/assert/assert.go +++ /dev/null @@ -1,313 +0,0 @@ -/* -Package assert provides assertions for comparing expected values to actual -values in tests. When an assertion fails a helpful error message is printed. - -# Example usage - -All the assertions in this package use [testing.T.Helper] to mark themselves as -test helpers. This allows the testing package to print the filename and line -number of the file function that failed. - - assert.NilError(t, err) - // filename_test.go:212: assertion failed: error is not nil: file not found - -If any assertion is called from a helper function, make sure to call t.Helper -from the helper function so that the filename and line number remain correct. - -The examples below show assert used with some common types and the failure -messages it produces. The filename and line number portion of the failure -message is omitted from these examples for brevity. - - // booleans - - assert.Assert(t, ok) - // assertion failed: ok is false - assert.Assert(t, !missing) - // assertion failed: missing is true - - // primitives - - assert.Equal(t, count, 1) - // assertion failed: 0 (count int) != 1 (int) - assert.Equal(t, msg, "the message") - // assertion failed: my message (msg string) != the message (string) - assert.Assert(t, total != 10) // use Assert for NotEqual - // assertion failed: total is 10 - assert.Assert(t, count > 20, "count=%v", count) - // assertion failed: count is <= 20: count=1 - - // errors - - assert.NilError(t, closer.Close()) - // assertion failed: error is not nil: close /file: errno 11 - assert.Error(t, err, "the exact error message") - // assertion failed: expected error "the exact error message", got "oops" - assert.ErrorContains(t, err, "includes this") - // assertion failed: expected error to contain "includes this", got "oops" - assert.ErrorIs(t, err, os.ErrNotExist) - // assertion failed: error is "oops", not "file does not exist" (os.ErrNotExist) - - // complex types - - assert.DeepEqual(t, result, myStruct{Name: "title"}) - // assertion failed: ... (diff of the two structs) - assert.Assert(t, is.Len(items, 3)) - // assertion failed: expected [] (length 0) to have length 3 - assert.Assert(t, len(sequence) != 0) // use Assert for NotEmpty - // assertion failed: len(sequence) is 0 - assert.Assert(t, is.Contains(mapping, "key")) - // assertion failed: map[other:1] does not contain key - - // pointers and interface - - assert.Assert(t, ref == nil) - // assertion failed: ref is not nil - assert.Assert(t, ref != nil) // use Assert for NotNil - // assertion failed: ref is nil - -# Assert and Check - -[Assert] and [Check] are very similar, they both accept a [cmp.Comparison], and fail -the test when the comparison fails. The one difference is that Assert uses -[testing.T.FailNow] to fail the test, which will end the test execution immediately. -Check uses [testing.T.Fail] to fail the test, which allows it to return the -result of the comparison, then proceed with the rest of the test case. - -Like [testing.T.FailNow], [Assert] must be called from the goroutine running the test, -not from other goroutines created during the test. [Check] is safe to use from any -goroutine. - -# Comparisons - -Package [gotest.tools/v3/assert/cmp] provides -many common comparisons. Additional comparisons can be written to compare -values in other ways. See the example Assert (CustomComparison). - -# Automated migration from testify - -gty-migrate-from-testify is a command which translates Go source code from -testify assertions to the assertions provided by this package. - -See http://pkg.go.dev/gotest.tools/v3/assert/cmd/gty-migrate-from-testify. -*/ -package assert // import "gotest.tools/v3/assert" - -import ( - gocmp "github.com/google/go-cmp/cmp" - "gotest.tools/v3/assert/cmp" - "gotest.tools/v3/internal/assert" -) - -// BoolOrComparison can be a bool, [cmp.Comparison], or error. See [Assert] for -// details about how this type is used. -type BoolOrComparison interface{} - -// TestingT is the subset of [testing.T] (see also [testing.TB]) used by the assert package. -type TestingT interface { - FailNow() - Fail() - Log(args ...interface{}) -} - -type helperT interface { - Helper() -} - -// Assert performs a comparison. If the comparison fails, the test is marked as -// failed, a failure message is logged, and execution is stopped immediately. -// -// The comparison argument may be one of three types: -// -// bool -// True is success. False is a failure. The failure message will contain -// the literal source code of the expression. -// -// cmp.Comparison -// Uses cmp.Result.Success() to check for success or failure. -// The comparison is responsible for producing a helpful failure message. -// http://pkg.go.dev/gotest.tools/v3/assert/cmp provides many common comparisons. -// -// error -// A nil value is considered success, and a non-nil error is a failure. -// The return value of error.Error is used as the failure message. -// -// Extra details can be added to the failure message using msgAndArgs. msgAndArgs -// may be either a single string, or a format string and args that will be -// passed to [fmt.Sprintf]. -// -// Assert uses [testing.TB.FailNow] to fail the test. Like t.FailNow, Assert must be called -// from the goroutine running the test function, not from other -// goroutines created during the test. Use [Check] from other goroutines. -func Assert(t TestingT, comparison BoolOrComparison, msgAndArgs ...interface{}) { - if ht, ok := t.(helperT); ok { - ht.Helper() - } - if !assert.Eval(t, assert.ArgsFromComparisonCall, comparison, msgAndArgs...) { - t.FailNow() - } -} - -// Check performs a comparison. If the comparison fails the test is marked as -// failed, a failure message is printed, and Check returns false. If the comparison -// is successful Check returns true. Check may be called from any goroutine. -// -// See [Assert] for details about the comparison arg and failure messages. -func Check(t TestingT, comparison BoolOrComparison, msgAndArgs ...interface{}) bool { - if ht, ok := t.(helperT); ok { - ht.Helper() - } - if !assert.Eval(t, assert.ArgsFromComparisonCall, comparison, msgAndArgs...) { - t.Fail() - return false - } - return true -} - -// NilError fails the test immediately if err is not nil, and includes err.Error -// in the failure message. -// -// NilError uses [testing.TB.FailNow] to fail the test. Like t.FailNow, NilError must be -// called from the goroutine running the test function, not from other -// goroutines created during the test. Use [Check] from other goroutines. -func NilError(t TestingT, err error, msgAndArgs ...interface{}) { - if ht, ok := t.(helperT); ok { - ht.Helper() - } - if !assert.Eval(t, assert.ArgsAfterT, err, msgAndArgs...) { - t.FailNow() - } -} - -// Equal uses the == operator to assert two values are equal and fails the test -// if they are not equal. -// -// If the comparison fails Equal will use the variable names and types of -// x and y as part of the failure message to identify the actual and expected -// values. -// -// assert.Equal(t, actual, expected) -// // main_test.go:41: assertion failed: 1 (actual int) != 21 (expected int32) -// -// If either x or y are a multi-line string the failure message will include a -// unified diff of the two values. If the values only differ by whitespace -// the unified diff will be augmented by replacing whitespace characters with -// visible characters to identify the whitespace difference. -// -// Equal uses [testing.T.FailNow] to fail the test. Like t.FailNow, Equal must be -// called from the goroutine running the test function, not from other -// goroutines created during the test. Use [Check] with [cmp.Equal] from other -// goroutines. -func Equal(t TestingT, x, y interface{}, msgAndArgs ...interface{}) { - if ht, ok := t.(helperT); ok { - ht.Helper() - } - if !assert.Eval(t, assert.ArgsAfterT, cmp.Equal(x, y), msgAndArgs...) { - t.FailNow() - } -} - -// DeepEqual uses [github.com/google/go-cmp/cmp] -// to assert two values are equal and fails the test if they are not equal. -// -// Package [gotest.tools/v3/assert/opt] provides some additional -// commonly used Options. -// -// DeepEqual uses [testing.T.FailNow] to fail the test. Like t.FailNow, DeepEqual must be -// called from the goroutine running the test function, not from other -// goroutines created during the test. Use [Check] with [cmp.DeepEqual] from other -// goroutines. -func DeepEqual(t TestingT, x, y interface{}, opts ...gocmp.Option) { - if ht, ok := t.(helperT); ok { - ht.Helper() - } - if !assert.Eval(t, assert.ArgsAfterT, cmp.DeepEqual(x, y, opts...)) { - t.FailNow() - } -} - -// Error fails the test if err is nil, or if err.Error is not equal to expected. -// Both err.Error and expected will be included in the failure message. -// Error performs an exact match of the error text. Use [ErrorContains] if only -// part of the error message is relevant. Use [ErrorType] or [ErrorIs] to compare -// errors by type. -// -// Error uses [testing.T.FailNow] to fail the test. Like t.FailNow, Error must be -// called from the goroutine running the test function, not from other -// goroutines created during the test. Use [Check] with [cmp.Error] from other -// goroutines. -func Error(t TestingT, err error, expected string, msgAndArgs ...interface{}) { - if ht, ok := t.(helperT); ok { - ht.Helper() - } - if !assert.Eval(t, assert.ArgsAfterT, cmp.Error(err, expected), msgAndArgs...) { - t.FailNow() - } -} - -// ErrorContains fails the test if err is nil, or if err.Error does not -// contain the expected substring. Both err.Error and the expected substring -// will be included in the failure message. -// -// ErrorContains uses [testing.T.FailNow] to fail the test. Like t.FailNow, ErrorContains -// must be called from the goroutine running the test function, not from other -// goroutines created during the test. Use [Check] with [cmp.ErrorContains] from other -// goroutines. -func ErrorContains(t TestingT, err error, substring string, msgAndArgs ...interface{}) { - if ht, ok := t.(helperT); ok { - ht.Helper() - } - if !assert.Eval(t, assert.ArgsAfterT, cmp.ErrorContains(err, substring), msgAndArgs...) { - t.FailNow() - } -} - -// ErrorType fails the test if err is nil, or err is not the expected type. -// New code should use ErrorIs instead. -// -// Expected can be one of: -// -// func(error) bool -// The function should return true if the error is the expected type. -// -// struct{} or *struct{} -// A struct or a pointer to a struct. The assertion fails if the error is -// not of the same type. -// -// *interface{} -// A pointer to an interface type. The assertion fails if err does not -// implement the interface. -// -// reflect.Type -// The assertion fails if err does not implement the reflect.Type. -// -// ErrorType uses [testing.T.FailNow] to fail the test. Like t.FailNow, ErrorType -// must be called from the goroutine running the test function, not from other -// goroutines created during the test. Use [Check] with [cmp.ErrorType] from other -// goroutines. -// -// Deprecated: Use [ErrorIs] -func ErrorType(t TestingT, err error, expected interface{}, msgAndArgs ...interface{}) { - if ht, ok := t.(helperT); ok { - ht.Helper() - } - if !assert.Eval(t, assert.ArgsAfterT, cmp.ErrorType(err, expected), msgAndArgs...) { - t.FailNow() - } -} - -// ErrorIs fails the test if err is nil, or the error does not match expected -// when compared using errors.Is. See [errors.Is] for -// accepted arguments. -// -// ErrorIs uses [testing.T.FailNow] to fail the test. Like t.FailNow, ErrorIs -// must be called from the goroutine running the test function, not from other -// goroutines created during the test. Use [Check] with [cmp.ErrorIs] from other -// goroutines. -func ErrorIs(t TestingT, err error, expected error, msgAndArgs ...interface{}) { - if ht, ok := t.(helperT); ok { - ht.Helper() - } - if !assert.Eval(t, assert.ArgsAfterT, cmp.ErrorIs(err, expected), msgAndArgs...) { - t.FailNow() - } -} diff --git a/vendor/gotest.tools/v3/assert/cmp/compare.go b/vendor/gotest.tools/v3/assert/cmp/compare.go deleted file mode 100644 index 118844f3..00000000 --- a/vendor/gotest.tools/v3/assert/cmp/compare.go +++ /dev/null @@ -1,404 +0,0 @@ -/*Package cmp provides Comparisons for Assert and Check*/ -package cmp // import "gotest.tools/v3/assert/cmp" - -import ( - "errors" - "fmt" - "reflect" - "regexp" - "strings" - - "github.com/google/go-cmp/cmp" - "gotest.tools/v3/internal/format" -) - -// Comparison is a function which compares values and returns [ResultSuccess] if -// the actual value matches the expected value. If the values do not match the -// [Result] will contain a message about why it failed. -type Comparison func() Result - -// DeepEqual compares two values using [github.com/google/go-cmp/cmp] -// and succeeds if the values are equal. -// -// The comparison can be customized using comparison Options. -// Package [gotest.tools/v3/assert/opt] provides some additional -// commonly used Options. -func DeepEqual(x, y interface{}, opts ...cmp.Option) Comparison { - return func() (result Result) { - defer func() { - if panicmsg, handled := handleCmpPanic(recover()); handled { - result = ResultFailure(panicmsg) - } - }() - diff := cmp.Diff(x, y, opts...) - if diff == "" { - return ResultSuccess - } - return multiLineDiffResult(diff, x, y) - } -} - -func handleCmpPanic(r interface{}) (string, bool) { - if r == nil { - return "", false - } - panicmsg, ok := r.(string) - if !ok { - panic(r) - } - switch { - case strings.HasPrefix(panicmsg, "cannot handle unexported field"): - return panicmsg, true - } - panic(r) -} - -func toResult(success bool, msg string) Result { - if success { - return ResultSuccess - } - return ResultFailure(msg) -} - -// RegexOrPattern may be either a [*regexp.Regexp] or a string that is a valid -// regexp pattern. -type RegexOrPattern interface{} - -// Regexp succeeds if value v matches regular expression re. -// -// Example: -// -// assert.Assert(t, cmp.Regexp("^[0-9a-f]{32}$", str)) -// r := regexp.MustCompile("^[0-9a-f]{32}$") -// assert.Assert(t, cmp.Regexp(r, str)) -func Regexp(re RegexOrPattern, v string) Comparison { - match := func(re *regexp.Regexp) Result { - return toResult( - re.MatchString(v), - fmt.Sprintf("value %q does not match regexp %q", v, re.String())) - } - - return func() Result { - switch regex := re.(type) { - case *regexp.Regexp: - return match(regex) - case string: - re, err := regexp.Compile(regex) - if err != nil { - return ResultFailure(err.Error()) - } - return match(re) - default: - return ResultFailure(fmt.Sprintf("invalid type %T for regex pattern", regex)) - } - } -} - -// Equal succeeds if x == y. See [gotest.tools/v3/assert.Equal] for full documentation. -func Equal(x, y interface{}) Comparison { - return func() Result { - switch { - case x == y: - return ResultSuccess - case isMultiLineStringCompare(x, y): - diff := format.UnifiedDiff(format.DiffConfig{A: x.(string), B: y.(string)}) - return multiLineDiffResult(diff, x, y) - } - return ResultFailureTemplate(` - {{- printf "%v" .Data.x}} ( - {{- with callArg 0 }}{{ formatNode . }} {{end -}} - {{- printf "%T" .Data.x -}} - ) != {{ printf "%v" .Data.y}} ( - {{- with callArg 1 }}{{ formatNode . }} {{end -}} - {{- printf "%T" .Data.y -}} - )`, - map[string]interface{}{"x": x, "y": y}) - } -} - -func isMultiLineStringCompare(x, y interface{}) bool { - strX, ok := x.(string) - if !ok { - return false - } - strY, ok := y.(string) - if !ok { - return false - } - return strings.Contains(strX, "\n") || strings.Contains(strY, "\n") -} - -func multiLineDiffResult(diff string, x, y interface{}) Result { - return ResultFailureTemplate(` ---- {{ with callArg 0 }}{{ formatNode . }}{{else}}←{{end}} -+++ {{ with callArg 1 }}{{ formatNode . }}{{else}}→{{end}} -{{ .Data.diff }}`, - map[string]interface{}{"diff": diff, "x": x, "y": y}) -} - -// Len succeeds if the sequence has the expected length. -func Len(seq interface{}, expected int) Comparison { - return func() (result Result) { - defer func() { - if e := recover(); e != nil { - result = ResultFailure(fmt.Sprintf("type %T does not have a length", seq)) - } - }() - value := reflect.ValueOf(seq) - length := value.Len() - if length == expected { - return ResultSuccess - } - msg := fmt.Sprintf("expected %s (length %d) to have length %d", seq, length, expected) - return ResultFailure(msg) - } -} - -// Contains succeeds if item is in collection. Collection may be a string, map, -// slice, or array. -// -// If collection is a string, item must also be a string, and is compared using -// [strings.Contains]. -// If collection is a Map, contains will succeed if item is a key in the map. -// If collection is a slice or array, item is compared to each item in the -// sequence using [reflect.DeepEqual]. -func Contains(collection interface{}, item interface{}) Comparison { - return func() Result { - colValue := reflect.ValueOf(collection) - if !colValue.IsValid() { - return ResultFailure("nil does not contain items") - } - msg := fmt.Sprintf("%v does not contain %v", collection, item) - - itemValue := reflect.ValueOf(item) - switch colValue.Type().Kind() { - case reflect.String: - if itemValue.Type().Kind() != reflect.String { - return ResultFailure("string may only contain strings") - } - return toResult( - strings.Contains(colValue.String(), itemValue.String()), - fmt.Sprintf("string %q does not contain %q", collection, item)) - - case reflect.Map: - if itemValue.Type() != colValue.Type().Key() { - return ResultFailure(fmt.Sprintf( - "%v can not contain a %v key", colValue.Type(), itemValue.Type())) - } - return toResult(colValue.MapIndex(itemValue).IsValid(), msg) - - case reflect.Slice, reflect.Array: - for i := 0; i < colValue.Len(); i++ { - if reflect.DeepEqual(colValue.Index(i).Interface(), item) { - return ResultSuccess - } - } - return ResultFailure(msg) - default: - return ResultFailure(fmt.Sprintf("type %T does not contain items", collection)) - } - } -} - -// Panics succeeds if f() panics. -func Panics(f func()) Comparison { - return func() (result Result) { - defer func() { - if err := recover(); err != nil { - result = ResultSuccess - } - }() - f() - return ResultFailure("did not panic") - } -} - -// Error succeeds if err is a non-nil error, and the error message equals the -// expected message. -func Error(err error, message string) Comparison { - return func() Result { - switch { - case err == nil: - return ResultFailure("expected an error, got nil") - case err.Error() != message: - return ResultFailure(fmt.Sprintf( - "expected error %q, got %s", message, formatErrorMessage(err))) - } - return ResultSuccess - } -} - -// ErrorContains succeeds if err is a non-nil error, and the error message contains -// the expected substring. -func ErrorContains(err error, substring string) Comparison { - return func() Result { - switch { - case err == nil: - return ResultFailure("expected an error, got nil") - case !strings.Contains(err.Error(), substring): - return ResultFailure(fmt.Sprintf( - "expected error to contain %q, got %s", substring, formatErrorMessage(err))) - } - return ResultSuccess - } -} - -type causer interface { - Cause() error -} - -func formatErrorMessage(err error) string { - //nolint:errorlint // unwrapping is not appropriate here - if _, ok := err.(causer); ok { - return fmt.Sprintf("%q\n%+v", err, err) - } - // This error was not wrapped with github.com/pkg/errors - return fmt.Sprintf("%q", err) -} - -// Nil succeeds if obj is a nil interface, pointer, or function. -// -// Use [gotest.tools/v3/assert.NilError] for comparing errors. Use Len(obj, 0) for comparing slices, -// maps, and channels. -func Nil(obj interface{}) Comparison { - msgFunc := func(value reflect.Value) string { - return fmt.Sprintf("%v (type %s) is not nil", reflect.Indirect(value), value.Type()) - } - return isNil(obj, msgFunc) -} - -func isNil(obj interface{}, msgFunc func(reflect.Value) string) Comparison { - return func() Result { - if obj == nil { - return ResultSuccess - } - value := reflect.ValueOf(obj) - kind := value.Type().Kind() - if kind >= reflect.Chan && kind <= reflect.Slice { - if value.IsNil() { - return ResultSuccess - } - return ResultFailure(msgFunc(value)) - } - - return ResultFailure(fmt.Sprintf("%v (type %s) can not be nil", value, value.Type())) - } -} - -// ErrorType succeeds if err is not nil and is of the expected type. -// -// Expected can be one of: -// -// func(error) bool -// -// Function should return true if the error is the expected type. -// -// type struct{}, type &struct{} -// -// A struct or a pointer to a struct. -// Fails if the error is not of the same type as expected. -// -// type &interface{} -// -// A pointer to an interface type. -// Fails if err does not implement the interface. -// -// reflect.Type -// -// Fails if err does not implement the [reflect.Type]. -// -// Deprecated: Use [ErrorIs] -func ErrorType(err error, expected interface{}) Comparison { - return func() Result { - switch expectedType := expected.(type) { - case func(error) bool: - return cmpErrorTypeFunc(err, expectedType) - case reflect.Type: - if expectedType.Kind() == reflect.Interface { - return cmpErrorTypeImplementsType(err, expectedType) - } - return cmpErrorTypeEqualType(err, expectedType) - case nil: - return ResultFailure("invalid type for expected: nil") - } - - expectedType := reflect.TypeOf(expected) - switch { - case expectedType.Kind() == reflect.Struct, isPtrToStruct(expectedType): - return cmpErrorTypeEqualType(err, expectedType) - case isPtrToInterface(expectedType): - return cmpErrorTypeImplementsType(err, expectedType.Elem()) - } - return ResultFailure(fmt.Sprintf("invalid type for expected: %T", expected)) - } -} - -func cmpErrorTypeFunc(err error, f func(error) bool) Result { - if f(err) { - return ResultSuccess - } - actual := "nil" - if err != nil { - actual = fmt.Sprintf("%s (%T)", err, err) - } - return ResultFailureTemplate(`error is {{ .Data.actual }} - {{- with callArg 1 }}, not {{ formatNode . }}{{end -}}`, - map[string]interface{}{"actual": actual}) -} - -func cmpErrorTypeEqualType(err error, expectedType reflect.Type) Result { - if err == nil { - return ResultFailure(fmt.Sprintf("error is nil, not %s", expectedType)) - } - errValue := reflect.ValueOf(err) - if errValue.Type() == expectedType { - return ResultSuccess - } - return ResultFailure(fmt.Sprintf("error is %s (%T), not %s", err, err, expectedType)) -} - -func cmpErrorTypeImplementsType(err error, expectedType reflect.Type) Result { - if err == nil { - return ResultFailure(fmt.Sprintf("error is nil, not %s", expectedType)) - } - errValue := reflect.ValueOf(err) - if errValue.Type().Implements(expectedType) { - return ResultSuccess - } - return ResultFailure(fmt.Sprintf("error is %s (%T), not %s", err, err, expectedType)) -} - -func isPtrToInterface(typ reflect.Type) bool { - return typ.Kind() == reflect.Ptr && typ.Elem().Kind() == reflect.Interface -} - -func isPtrToStruct(typ reflect.Type) bool { - return typ.Kind() == reflect.Ptr && typ.Elem().Kind() == reflect.Struct -} - -var ( - stdlibErrorNewType = reflect.TypeOf(errors.New("")) - stdlibFmtErrorType = reflect.TypeOf(fmt.Errorf("%w", fmt.Errorf(""))) -) - -// ErrorIs succeeds if errors.Is(actual, expected) returns true. See -// [errors.Is] for accepted argument values. -func ErrorIs(actual error, expected error) Comparison { - return func() Result { - if errors.Is(actual, expected) { - return ResultSuccess - } - - // The type of stdlib errors is excluded because the type is not relevant - // in those cases. The type is only important when it is a user defined - // custom error type. - return ResultFailureTemplate(`error is - {{- if not .Data.a }} nil,{{ else }} - {{- printf " \"%v\"" .Data.a }} - {{- if notStdlibErrorType .Data.a }} ({{ printf "%T" .Data.a }}){{ end }}, - {{- end }} not {{ printf "\"%v\"" .Data.x }} ( - {{- with callArg 1 }}{{ formatNode . }}{{ end }} - {{- if notStdlibErrorType .Data.x }}{{ printf " %T" .Data.x }}{{ end }})`, - map[string]interface{}{"a": actual, "x": expected}) - } -} diff --git a/vendor/gotest.tools/v3/assert/cmp/result.go b/vendor/gotest.tools/v3/assert/cmp/result.go deleted file mode 100644 index 9992ede5..00000000 --- a/vendor/gotest.tools/v3/assert/cmp/result.go +++ /dev/null @@ -1,110 +0,0 @@ -package cmp - -import ( - "bytes" - "fmt" - "go/ast" - "reflect" - "text/template" - - "gotest.tools/v3/internal/source" -) - -// A Result of a [Comparison]. -type Result interface { - Success() bool -} - -// StringResult is an implementation of [Result] that reports the error message -// string verbatim and does not provide any templating or formatting of the -// message. -type StringResult struct { - success bool - message string -} - -// Success returns true if the comparison was successful. -func (r StringResult) Success() bool { - return r.success -} - -// FailureMessage returns the message used to provide additional information -// about the failure. -func (r StringResult) FailureMessage() string { - return r.message -} - -// ResultSuccess is a constant which is returned by a [Comparison] to -// indicate success. -var ResultSuccess = StringResult{success: true} - -// ResultFailure returns a failed [Result] with a failure message. -func ResultFailure(message string) StringResult { - return StringResult{message: message} -} - -// ResultFromError returns [ResultSuccess] if err is nil. Otherwise [ResultFailure] -// is returned with the error message as the failure message. -func ResultFromError(err error) Result { - if err == nil { - return ResultSuccess - } - return ResultFailure(err.Error()) -} - -type templatedResult struct { - template string - data map[string]interface{} -} - -func (r templatedResult) Success() bool { - return false -} - -func (r templatedResult) FailureMessage(args []ast.Expr) string { - msg, err := renderMessage(r, args) - if err != nil { - return fmt.Sprintf("failed to render failure message: %s", err) - } - return msg -} - -func (r templatedResult) UpdatedExpected(stackIndex int) error { - // TODO: would be nice to have structured data instead of a map - return source.UpdateExpectedValue(stackIndex+1, r.data["x"], r.data["y"]) -} - -// ResultFailureTemplate returns a [Result] with a template string and data which -// can be used to format a failure message. The template may access data from .Data, -// the comparison args with the callArg function, and the formatNode function may -// be used to format the call args. -func ResultFailureTemplate(template string, data map[string]interface{}) Result { - return templatedResult{template: template, data: data} -} - -func renderMessage(result templatedResult, args []ast.Expr) (string, error) { - tmpl := template.New("failure").Funcs(template.FuncMap{ - "formatNode": source.FormatNode, - "callArg": func(index int) ast.Expr { - if index >= len(args) { - return nil - } - return args[index] - }, - // TODO: any way to include this from ErrorIS instead of here? - "notStdlibErrorType": func(typ interface{}) bool { - r := reflect.TypeOf(typ) - return r != stdlibFmtErrorType && r != stdlibErrorNewType - }, - }) - var err error - tmpl, err = tmpl.Parse(result.template) - if err != nil { - return "", err - } - buf := new(bytes.Buffer) - err = tmpl.Execute(buf, map[string]interface{}{ - "Data": result.data, - }) - return buf.String(), err -} diff --git a/vendor/gotest.tools/v3/icmd/command.go b/vendor/gotest.tools/v3/icmd/command.go deleted file mode 100644 index a3e167a0..00000000 --- a/vendor/gotest.tools/v3/icmd/command.go +++ /dev/null @@ -1,295 +0,0 @@ -/*Package icmd executes binaries and provides convenient assertions for testing the results. - */ -package icmd // import "gotest.tools/v3/icmd" - -import ( - "bytes" - "fmt" - "io" - "os" - "os/exec" - "strings" - "sync" - "time" - - "gotest.tools/v3/assert" - "gotest.tools/v3/assert/cmp" -) - -type helperT interface { - Helper() -} - -// None is a token to inform Result.Assert that the output should be empty -const None = "[NOTHING]" - -type lockedBuffer struct { - m sync.RWMutex - buf bytes.Buffer -} - -func (buf *lockedBuffer) Write(b []byte) (int, error) { - buf.m.Lock() - defer buf.m.Unlock() - return buf.buf.Write(b) -} - -func (buf *lockedBuffer) String() string { - buf.m.RLock() - defer buf.m.RUnlock() - return buf.buf.String() -} - -// Result stores the result of running a command -type Result struct { - Cmd *exec.Cmd - ExitCode int - Error error - // Timeout is true if the command was killed because it ran for too long - Timeout bool - outBuffer *lockedBuffer - errBuffer *lockedBuffer -} - -// Assert compares the Result against the Expected struct, and fails the test if -// any of the expectations are not met. -// -// This function is equivalent to assert.Assert(t, result.Equal(exp)). -func (r *Result) Assert(t assert.TestingT, exp Expected) *Result { - if ht, ok := t.(helperT); ok { - ht.Helper() - } - assert.Assert(t, r.Equal(exp)) - return r -} - -// Equal compares the result to Expected. If the result doesn't match expected -// returns a formatted failure message with the command, stdout, stderr, exit code, -// and any failed expectations. -func (r *Result) Equal(exp Expected) cmp.Comparison { - return func() cmp.Result { - return cmp.ResultFromError(r.match(exp)) - } -} - -// Compare the result to Expected and return an error if they do not match. -func (r *Result) Compare(exp Expected) error { - return r.match(exp) -} - -func (r *Result) match(exp Expected) error { - errors := []string{} - add := func(format string, args ...interface{}) { - errors = append(errors, fmt.Sprintf(format, args...)) - } - - if exp.ExitCode != r.ExitCode { - add("ExitCode was %d expected %d", r.ExitCode, exp.ExitCode) - } - if exp.Timeout != r.Timeout { - if exp.Timeout { - add("Expected command to timeout") - } else { - add("Expected command to finish, but it hit the timeout") - } - } - if !matchOutput(exp.Out, r.Stdout()) { - add("Expected stdout to contain %q", exp.Out) - } - if !matchOutput(exp.Err, r.Stderr()) { - add("Expected stderr to contain %q", exp.Err) - } - switch { - // If a non-zero exit code is expected there is going to be an error. - // Don't require an error message as well as an exit code because the - // error message is going to be "exit status which is not useful - case exp.Error == "" && exp.ExitCode != 0: - case exp.Error == "" && r.Error != nil: - add("Expected no error") - case exp.Error != "" && r.Error == nil: - add("Expected error to contain %q, but there was no error", exp.Error) - case exp.Error != "" && !strings.Contains(r.Error.Error(), exp.Error): - add("Expected error to contain %q", exp.Error) - } - - if len(errors) == 0 { - return nil - } - return fmt.Errorf("%s\nFailures:\n%s", r, strings.Join(errors, "\n")) -} - -func matchOutput(expected string, actual string) bool { - switch expected { - case None: - return actual == "" - default: - return strings.Contains(actual, expected) - } -} - -func (r *Result) String() string { - var timeout string - if r.Timeout { - timeout = " (timeout)" - } - var errString string - if r.Error != nil { - errString = "\nError: " + r.Error.Error() - } - - return fmt.Sprintf(` -Command: %s -ExitCode: %d%s%s -Stdout: %v -Stderr: %v -`, - strings.Join(r.Cmd.Args, " "), - r.ExitCode, - timeout, - errString, - r.Stdout(), - r.Stderr()) -} - -// Expected is the expected output from a Command. This struct is compared to a -// Result struct by Result.Assert(). -type Expected struct { - ExitCode int - Timeout bool - Error string - Out string - Err string -} - -// Success is the default expected result. A Success result is one with a 0 -// ExitCode. -var Success = Expected{} - -// Stdout returns the stdout of the process as a string -func (r *Result) Stdout() string { - return r.outBuffer.String() -} - -// Stderr returns the stderr of the process as a string -func (r *Result) Stderr() string { - return r.errBuffer.String() -} - -// Combined returns the stdout and stderr combined into a single string -func (r *Result) Combined() string { - return r.outBuffer.String() + r.errBuffer.String() -} - -func (r *Result) setExitError(err error) { - if err == nil { - return - } - r.Error = err - r.ExitCode = processExitCode(err) -} - -// Cmd contains the arguments and options for a process to run as part of a test -// suite. -type Cmd struct { - Command []string - Timeout time.Duration - Stdin io.Reader - Stdout io.Writer - Stderr io.Writer - Dir string - Env []string - ExtraFiles []*os.File -} - -// Command create a simple Cmd with the specified command and arguments -func Command(command string, args ...string) Cmd { - return Cmd{Command: append([]string{command}, args...)} -} - -// RunCmd runs a command and returns a Result -func RunCmd(cmd Cmd, cmdOperators ...CmdOp) *Result { - result := StartCmd(cmd, cmdOperators...) - if result.Error != nil { - return result - } - return WaitOnCmd(cmd.Timeout, result) -} - -// RunCommand runs a command with default options, and returns a result -func RunCommand(command string, args ...string) *Result { - return RunCmd(Command(command, args...)) -} - -// StartCmd starts a command, but doesn't wait for it to finish -func StartCmd(cmd Cmd, cmdOperators ...CmdOp) *Result { - for _, op := range cmdOperators { - op(&cmd) - } - result := buildCmd(cmd) - if result.Error != nil { - return result - } - result.setExitError(result.Cmd.Start()) - return result -} - -// TODO: support exec.CommandContext -func buildCmd(cmd Cmd) *Result { - var execCmd *exec.Cmd - switch len(cmd.Command) { - case 1: - execCmd = exec.Command(cmd.Command[0]) - default: - execCmd = exec.Command(cmd.Command[0], cmd.Command[1:]...) - } - outBuffer := new(lockedBuffer) - errBuffer := new(lockedBuffer) - - execCmd.Stdin = cmd.Stdin - execCmd.Dir = cmd.Dir - execCmd.Env = cmd.Env - if cmd.Stdout != nil { - execCmd.Stdout = io.MultiWriter(outBuffer, cmd.Stdout) - } else { - execCmd.Stdout = outBuffer - } - if cmd.Stderr != nil { - execCmd.Stderr = io.MultiWriter(errBuffer, cmd.Stderr) - } else { - execCmd.Stderr = errBuffer - } - execCmd.ExtraFiles = cmd.ExtraFiles - - return &Result{ - Cmd: execCmd, - outBuffer: outBuffer, - errBuffer: errBuffer, - } -} - -// WaitOnCmd waits for a command to complete. If timeout is non-nil then -// only wait until the timeout. -func WaitOnCmd(timeout time.Duration, result *Result) *Result { - if timeout == time.Duration(0) { - result.setExitError(result.Cmd.Wait()) - return result - } - - done := make(chan error, 1) - // Wait for command to exit in a goroutine - go func() { - done <- result.Cmd.Wait() - }() - - select { - case <-time.After(timeout): - killErr := result.Cmd.Process.Kill() - if killErr != nil { - fmt.Printf("failed to kill (pid=%d): %v\n", result.Cmd.Process.Pid, killErr) - } - result.Timeout = true - case err := <-done: - result.setExitError(err) - } - return result -} diff --git a/vendor/gotest.tools/v3/icmd/exitcode.go b/vendor/gotest.tools/v3/icmd/exitcode.go deleted file mode 100644 index 4e48fc4f..00000000 --- a/vendor/gotest.tools/v3/icmd/exitcode.go +++ /dev/null @@ -1,23 +0,0 @@ -package icmd - -import ( - "errors" - "os/exec" -) - -func processExitCode(err error) int { - if err == nil { - return 0 - } - - var exitErr *exec.ExitError - if errors.As(err, &exitErr) { - if exitErr.ProcessState == nil { - return 0 - } - if code := exitErr.ProcessState.ExitCode(); code != -1 { - return code - } - } - return 127 -} diff --git a/vendor/gotest.tools/v3/icmd/ops.go b/vendor/gotest.tools/v3/icmd/ops.go deleted file mode 100644 index aa3bc1e8..00000000 --- a/vendor/gotest.tools/v3/icmd/ops.go +++ /dev/null @@ -1,60 +0,0 @@ -package icmd - -import ( - "io" - "os" - "time" -) - -// CmdOp is an operation which modified a Cmd structure used to execute commands -type CmdOp func(*Cmd) - -// WithTimeout sets the timeout duration of the command -func WithTimeout(timeout time.Duration) CmdOp { - return func(c *Cmd) { - c.Timeout = timeout - } -} - -// WithEnv sets the environment variable of the command. -// Each arguments are in the form of KEY=VALUE -func WithEnv(env ...string) CmdOp { - return func(c *Cmd) { - c.Env = env - } -} - -// Dir sets the working directory of the command -func Dir(path string) CmdOp { - return func(c *Cmd) { - c.Dir = path - } -} - -// WithStdin sets the standard input of the command to the specified reader -func WithStdin(r io.Reader) CmdOp { - return func(c *Cmd) { - c.Stdin = r - } -} - -// WithStdout sets the standard output of the command to the specified writer -func WithStdout(w io.Writer) CmdOp { - return func(c *Cmd) { - c.Stdout = w - } -} - -// WithStderr sets the standard error of the command to the specified writer -func WithStderr(w io.Writer) CmdOp { - return func(c *Cmd) { - c.Stderr = w - } -} - -// WithExtraFile adds a file descriptor to the command -func WithExtraFile(f *os.File) CmdOp { - return func(c *Cmd) { - c.ExtraFiles = append(c.ExtraFiles, f) - } -} diff --git a/vendor/gotest.tools/v3/internal/assert/assert.go b/vendor/gotest.tools/v3/internal/assert/assert.go deleted file mode 100644 index 2dd80255..00000000 --- a/vendor/gotest.tools/v3/internal/assert/assert.go +++ /dev/null @@ -1,161 +0,0 @@ -// Package assert provides internal utilties for assertions. -package assert - -import ( - "fmt" - "go/ast" - "go/token" - "reflect" - - "gotest.tools/v3/assert/cmp" - "gotest.tools/v3/internal/format" - "gotest.tools/v3/internal/source" -) - -// LogT is the subset of testing.T used by the assert package. -type LogT interface { - Log(args ...interface{}) -} - -type helperT interface { - Helper() -} - -const failureMessage = "assertion failed: " - -// Eval the comparison and print a failure messages if the comparison has failed. -func Eval( - t LogT, - argSelector argSelector, - comparison interface{}, - msgAndArgs ...interface{}, -) bool { - if ht, ok := t.(helperT); ok { - ht.Helper() - } - var success bool - switch check := comparison.(type) { - case bool: - if check { - return true - } - logFailureFromBool(t, msgAndArgs...) - - // Undocumented legacy comparison without Result type - case func() (success bool, message string): - success = runCompareFunc(t, check, msgAndArgs...) - - case nil: - return true - - case error: - msg := failureMsgFromError(check) - t.Log(format.WithCustomMessage(failureMessage+msg, msgAndArgs...)) - - case cmp.Comparison: - success = RunComparison(t, argSelector, check, msgAndArgs...) - - case func() cmp.Result: - success = RunComparison(t, argSelector, check, msgAndArgs...) - - default: - t.Log(fmt.Sprintf("invalid Comparison: %v (%T)", check, check)) - } - return success -} - -func runCompareFunc( - t LogT, - f func() (success bool, message string), - msgAndArgs ...interface{}, -) bool { - if ht, ok := t.(helperT); ok { - ht.Helper() - } - if success, message := f(); !success { - t.Log(format.WithCustomMessage(failureMessage+message, msgAndArgs...)) - return false - } - return true -} - -func logFailureFromBool(t LogT, msgAndArgs ...interface{}) { - if ht, ok := t.(helperT); ok { - ht.Helper() - } - const stackIndex = 3 // Assert()/Check(), assert(), logFailureFromBool() - args, err := source.CallExprArgs(stackIndex) - if err != nil { - t.Log(err.Error()) - return - } - - const comparisonArgIndex = 1 // Assert(t, comparison) - if len(args) <= comparisonArgIndex { - t.Log(failureMessage + "but assert failed to find the expression to print") - return - } - - msg, err := boolFailureMessage(args[comparisonArgIndex]) - if err != nil { - t.Log(err.Error()) - msg = "expression is false" - } - - t.Log(format.WithCustomMessage(failureMessage+msg, msgAndArgs...)) -} - -func failureMsgFromError(err error) string { - // Handle errors with non-nil types - v := reflect.ValueOf(err) - if v.Kind() == reflect.Ptr && v.IsNil() { - return fmt.Sprintf("error is not nil: error has type %T", err) - } - return "error is not nil: " + err.Error() -} - -func boolFailureMessage(expr ast.Expr) (string, error) { - if binaryExpr, ok := expr.(*ast.BinaryExpr); ok { - x, err := source.FormatNode(binaryExpr.X) - if err != nil { - return "", err - } - y, err := source.FormatNode(binaryExpr.Y) - if err != nil { - return "", err - } - - switch binaryExpr.Op { - case token.NEQ: - return x + " is " + y, nil - case token.EQL: - return x + " is not " + y, nil - case token.GTR: - return x + " is <= " + y, nil - case token.LSS: - return x + " is >= " + y, nil - case token.GEQ: - return x + " is less than " + y, nil - case token.LEQ: - return x + " is greater than " + y, nil - } - } - - if unaryExpr, ok := expr.(*ast.UnaryExpr); ok && unaryExpr.Op == token.NOT { - x, err := source.FormatNode(unaryExpr.X) - if err != nil { - return "", err - } - return x + " is true", nil - } - - if ident, ok := expr.(*ast.Ident); ok { - return ident.Name + " is false", nil - } - - formatted, err := source.FormatNode(expr) - if err != nil { - return "", err - } - return "expression is false: " + formatted, nil -} diff --git a/vendor/gotest.tools/v3/internal/assert/result.go b/vendor/gotest.tools/v3/internal/assert/result.go deleted file mode 100644 index bb8741eb..00000000 --- a/vendor/gotest.tools/v3/internal/assert/result.go +++ /dev/null @@ -1,146 +0,0 @@ -package assert - -import ( - "errors" - "fmt" - "go/ast" - - "gotest.tools/v3/assert/cmp" - "gotest.tools/v3/internal/format" - "gotest.tools/v3/internal/source" -) - -// RunComparison and return Comparison.Success. If the comparison fails a messages -// will be printed using t.Log. -func RunComparison( - t LogT, - argSelector argSelector, - f cmp.Comparison, - msgAndArgs ...interface{}, -) bool { - if ht, ok := t.(helperT); ok { - ht.Helper() - } - result := f() - if result.Success() { - return true - } - - if source.IsUpdate() { - if updater, ok := result.(updateExpected); ok { - const stackIndex = 3 // Assert/Check, assert, RunComparison - err := updater.UpdatedExpected(stackIndex) - switch { - case err == nil: - return true - case errors.Is(err, source.ErrNotFound): - // do nothing, fallthrough to regular failure message - default: - t.Log("failed to update source", err) - return false - } - } - } - - var message string - switch typed := result.(type) { - case resultWithComparisonArgs: - const stackIndex = 3 // Assert/Check, assert, RunComparison - args, err := source.CallExprArgs(stackIndex) - if err != nil { - t.Log(err.Error()) - } - message = typed.FailureMessage(filterPrintableExpr(argSelector(args))) - case resultBasic: - message = typed.FailureMessage() - default: - message = fmt.Sprintf("comparison returned invalid Result type: %T", result) - } - - t.Log(format.WithCustomMessage(failureMessage+message, msgAndArgs...)) - return false -} - -type resultWithComparisonArgs interface { - FailureMessage(args []ast.Expr) string -} - -type resultBasic interface { - FailureMessage() string -} - -type updateExpected interface { - UpdatedExpected(stackIndex int) error -} - -// filterPrintableExpr filters the ast.Expr slice to only include Expr that are -// easy to read when printed and contain relevant information to an assertion. -// -// Ident and SelectorExpr are included because they print nicely and the variable -// names may provide additional context to their values. -// BasicLit and CompositeLit are excluded because their source is equivalent to -// their value, which is already available. -// Other types are ignored for now, but could be added if they are relevant. -func filterPrintableExpr(args []ast.Expr) []ast.Expr { - result := make([]ast.Expr, len(args)) - for i, arg := range args { - if isShortPrintableExpr(arg) { - result[i] = arg - continue - } - - if starExpr, ok := arg.(*ast.StarExpr); ok { - result[i] = starExpr.X - continue - } - } - return result -} - -func isShortPrintableExpr(expr ast.Expr) bool { - switch expr.(type) { - case *ast.Ident, *ast.SelectorExpr, *ast.IndexExpr, *ast.SliceExpr: - return true - case *ast.BinaryExpr, *ast.UnaryExpr: - return true - default: - // CallExpr, ParenExpr, TypeAssertExpr, KeyValueExpr, StarExpr - return false - } -} - -type argSelector func([]ast.Expr) []ast.Expr - -// ArgsAfterT selects args starting at position 1. Used when the caller has a -// testing.T as the first argument, and the args to select should follow it. -func ArgsAfterT(args []ast.Expr) []ast.Expr { - if len(args) < 1 { - return nil - } - return args[1:] -} - -// ArgsFromComparisonCall selects args from the CallExpression at position 1. -// Used when the caller has a testing.T as the first argument, and the args to -// select are passed to the cmp.Comparison at position 1. -func ArgsFromComparisonCall(args []ast.Expr) []ast.Expr { - if len(args) <= 1 { - return nil - } - if callExpr, ok := args[1].(*ast.CallExpr); ok { - return callExpr.Args - } - return nil -} - -// ArgsAtZeroIndex selects args from the CallExpression at position 1. -// Used when the caller accepts a single cmp.Comparison argument. -func ArgsAtZeroIndex(args []ast.Expr) []ast.Expr { - if len(args) == 0 { - return nil - } - if callExpr, ok := args[0].(*ast.CallExpr); ok { - return callExpr.Args - } - return nil -} diff --git a/vendor/gotest.tools/v3/internal/difflib/LICENSE b/vendor/gotest.tools/v3/internal/difflib/LICENSE deleted file mode 100644 index c67dad61..00000000 --- a/vendor/gotest.tools/v3/internal/difflib/LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -Copyright (c) 2013, Patrick Mezard -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright -notice, this list of conditions and the following disclaimer in the -documentation and/or other materials provided with the distribution. - The names of its contributors may not be used to endorse or promote -products derived from this software without specific prior written -permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS -IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED -TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A -PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED -TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/gotest.tools/v3/internal/difflib/difflib.go b/vendor/gotest.tools/v3/internal/difflib/difflib.go deleted file mode 100644 index 9bf506b6..00000000 --- a/vendor/gotest.tools/v3/internal/difflib/difflib.go +++ /dev/null @@ -1,423 +0,0 @@ -/*Package difflib is a partial port of Python difflib module. - -Original source: https://github.com/pmezard/go-difflib - -This file is trimmed to only the parts used by this repository. -*/ -package difflib // import "gotest.tools/v3/internal/difflib" - -func min(a, b int) int { - if a < b { - return a - } - return b -} - -func max(a, b int) int { - if a > b { - return a - } - return b -} - -// Match stores line numbers of size of match -type Match struct { - A int - B int - Size int -} - -// OpCode identifies the type of diff -type OpCode struct { - Tag byte - I1 int - I2 int - J1 int - J2 int -} - -// SequenceMatcher compares sequence of strings. The basic -// algorithm predates, and is a little fancier than, an algorithm -// published in the late 1980's by Ratcliff and Obershelp under the -// hyperbolic name "gestalt pattern matching". The basic idea is to find -// the longest contiguous matching subsequence that contains no "junk" -// elements (R-O doesn't address junk). The same idea is then applied -// recursively to the pieces of the sequences to the left and to the right -// of the matching subsequence. This does not yield minimal edit -// sequences, but does tend to yield matches that "look right" to people. -// -// SequenceMatcher tries to compute a "human-friendly diff" between two -// sequences. Unlike e.g. UNIX(tm) diff, the fundamental notion is the -// longest *contiguous* & junk-free matching subsequence. That's what -// catches peoples' eyes. The Windows(tm) windiff has another interesting -// notion, pairing up elements that appear uniquely in each sequence. -// That, and the method here, appear to yield more intuitive difference -// reports than does diff. This method appears to be the least vulnerable -// to synching up on blocks of "junk lines", though (like blank lines in -// ordinary text files, or maybe "

" lines in HTML files). That may be -// because this is the only method of the 3 that has a *concept* of -// "junk" . -// -// Timing: Basic R-O is cubic time worst case and quadratic time expected -// case. SequenceMatcher is quadratic time for the worst case and has -// expected-case behavior dependent in a complicated way on how many -// elements the sequences have in common; best case time is linear. -type SequenceMatcher struct { - a []string - b []string - b2j map[string][]int - IsJunk func(string) bool - autoJunk bool - bJunk map[string]struct{} - matchingBlocks []Match - fullBCount map[string]int - bPopular map[string]struct{} - opCodes []OpCode -} - -// NewMatcher returns a new SequenceMatcher -func NewMatcher(a, b []string) *SequenceMatcher { - m := SequenceMatcher{autoJunk: true} - m.SetSeqs(a, b) - return &m -} - -// SetSeqs sets two sequences to be compared. -func (m *SequenceMatcher) SetSeqs(a, b []string) { - m.SetSeq1(a) - m.SetSeq2(b) -} - -// SetSeq1 sets the first sequence to be compared. The second sequence to be compared is -// not changed. -// -// SequenceMatcher computes and caches detailed information about the second -// sequence, so if you want to compare one sequence S against many sequences, -// use .SetSeq2(s) once and call .SetSeq1(x) repeatedly for each of the other -// sequences. -// -// See also SetSeqs() and SetSeq2(). -func (m *SequenceMatcher) SetSeq1(a []string) { - if &a == &m.a { - return - } - m.a = a - m.matchingBlocks = nil - m.opCodes = nil -} - -// SetSeq2 sets the second sequence to be compared. The first sequence to be compared is -// not changed. -func (m *SequenceMatcher) SetSeq2(b []string) { - if &b == &m.b { - return - } - m.b = b - m.matchingBlocks = nil - m.opCodes = nil - m.fullBCount = nil - m.chainB() -} - -func (m *SequenceMatcher) chainB() { - // Populate line -> index mapping - b2j := map[string][]int{} - for i, s := range m.b { - indices := b2j[s] - indices = append(indices, i) - b2j[s] = indices - } - - // Purge junk elements - m.bJunk = map[string]struct{}{} - if m.IsJunk != nil { - junk := m.bJunk - for s := range b2j { - if m.IsJunk(s) { - junk[s] = struct{}{} - } - } - for s := range junk { - delete(b2j, s) - } - } - - // Purge remaining popular elements - popular := map[string]struct{}{} - n := len(m.b) - if m.autoJunk && n >= 200 { - ntest := n/100 + 1 - for s, indices := range b2j { - if len(indices) > ntest { - popular[s] = struct{}{} - } - } - for s := range popular { - delete(b2j, s) - } - } - m.bPopular = popular - m.b2j = b2j -} - -func (m *SequenceMatcher) isBJunk(s string) bool { - _, ok := m.bJunk[s] - return ok -} - -// Find longest matching block in a[alo:ahi] and b[blo:bhi]. -// -// If IsJunk is not defined: -// -// Return (i,j,k) such that a[i:i+k] is equal to b[j:j+k], where -// alo <= i <= i+k <= ahi -// blo <= j <= j+k <= bhi -// and for all (i',j',k') meeting those conditions, -// k >= k' -// i <= i' -// and if i == i', j <= j' -// -// In other words, of all maximal matching blocks, return one that -// starts earliest in a, and of all those maximal matching blocks that -// start earliest in a, return the one that starts earliest in b. -// -// If IsJunk is defined, first the longest matching block is -// determined as above, but with the additional restriction that no -// junk element appears in the block. Then that block is extended as -// far as possible by matching (only) junk elements on both sides. So -// the resulting block never matches on junk except as identical junk -// happens to be adjacent to an "interesting" match. -// -// If no blocks match, return (alo, blo, 0). -func (m *SequenceMatcher) findLongestMatch(alo, ahi, blo, bhi int) Match { - // CAUTION: stripping common prefix or suffix would be incorrect. - // E.g., - // ab - // acab - // Longest matching block is "ab", but if common prefix is - // stripped, it's "a" (tied with "b"). UNIX(tm) diff does so - // strip, so ends up claiming that ab is changed to acab by - // inserting "ca" in the middle. That's minimal but unintuitive: - // "it's obvious" that someone inserted "ac" at the front. - // Windiff ends up at the same place as diff, but by pairing up - // the unique 'b's and then matching the first two 'a's. - besti, bestj, bestsize := alo, blo, 0 - - // find longest junk-free match - // during an iteration of the loop, j2len[j] = length of longest - // junk-free match ending with a[i-1] and b[j] - j2len := map[int]int{} - for i := alo; i != ahi; i++ { - // look at all instances of a[i] in b; note that because - // b2j has no junk keys, the loop is skipped if a[i] is junk - newj2len := map[int]int{} - for _, j := range m.b2j[m.a[i]] { - // a[i] matches b[j] - if j < blo { - continue - } - if j >= bhi { - break - } - k := j2len[j-1] + 1 - newj2len[j] = k - if k > bestsize { - besti, bestj, bestsize = i-k+1, j-k+1, k - } - } - j2len = newj2len - } - - // Extend the best by non-junk elements on each end. In particular, - // "popular" non-junk elements aren't in b2j, which greatly speeds - // the inner loop above, but also means "the best" match so far - // doesn't contain any junk *or* popular non-junk elements. - for besti > alo && bestj > blo && !m.isBJunk(m.b[bestj-1]) && - m.a[besti-1] == m.b[bestj-1] { - besti, bestj, bestsize = besti-1, bestj-1, bestsize+1 - } - for besti+bestsize < ahi && bestj+bestsize < bhi && - !m.isBJunk(m.b[bestj+bestsize]) && - m.a[besti+bestsize] == m.b[bestj+bestsize] { - bestsize += 1 - } - - // Now that we have a wholly interesting match (albeit possibly - // empty!), we may as well suck up the matching junk on each - // side of it too. Can't think of a good reason not to, and it - // saves post-processing the (possibly considerable) expense of - // figuring out what to do with it. In the case of an empty - // interesting match, this is clearly the right thing to do, - // because no other kind of match is possible in the regions. - for besti > alo && bestj > blo && m.isBJunk(m.b[bestj-1]) && - m.a[besti-1] == m.b[bestj-1] { - besti, bestj, bestsize = besti-1, bestj-1, bestsize+1 - } - for besti+bestsize < ahi && bestj+bestsize < bhi && - m.isBJunk(m.b[bestj+bestsize]) && - m.a[besti+bestsize] == m.b[bestj+bestsize] { - bestsize += 1 - } - - return Match{A: besti, B: bestj, Size: bestsize} -} - -// GetMatchingBlocks returns a list of triples describing matching subsequences. -// -// Each triple is of the form (i, j, n), and means that -// a[i:i+n] == b[j:j+n]. The triples are monotonically increasing in -// i and in j. It's also guaranteed that if (i, j, n) and (i', j', n') are -// adjacent triples in the list, and the second is not the last triple in the -// list, then i+n != i' or j+n != j'. IOW, adjacent triples never describe -// adjacent equal blocks. -// -// The last triple is a dummy, (len(a), len(b), 0), and is the only -// triple with n==0. -func (m *SequenceMatcher) GetMatchingBlocks() []Match { - if m.matchingBlocks != nil { - return m.matchingBlocks - } - - var matchBlocks func(alo, ahi, blo, bhi int, matched []Match) []Match - matchBlocks = func(alo, ahi, blo, bhi int, matched []Match) []Match { - match := m.findLongestMatch(alo, ahi, blo, bhi) - i, j, k := match.A, match.B, match.Size - if match.Size > 0 { - if alo < i && blo < j { - matched = matchBlocks(alo, i, blo, j, matched) - } - matched = append(matched, match) - if i+k < ahi && j+k < bhi { - matched = matchBlocks(i+k, ahi, j+k, bhi, matched) - } - } - return matched - } - matched := matchBlocks(0, len(m.a), 0, len(m.b), nil) - - // It's possible that we have adjacent equal blocks in the - // matching_blocks list now. - nonAdjacent := []Match{} - i1, j1, k1 := 0, 0, 0 - for _, b := range matched { - // Is this block adjacent to i1, j1, k1? - i2, j2, k2 := b.A, b.B, b.Size - if i1+k1 == i2 && j1+k1 == j2 { - // Yes, so collapse them -- this just increases the length of - // the first block by the length of the second, and the first - // block so lengthened remains the block to compare against. - k1 += k2 - } else { - // Not adjacent. Remember the first block (k1==0 means it's - // the dummy we started with), and make the second block the - // new block to compare against. - if k1 > 0 { - nonAdjacent = append(nonAdjacent, Match{i1, j1, k1}) - } - i1, j1, k1 = i2, j2, k2 - } - } - if k1 > 0 { - nonAdjacent = append(nonAdjacent, Match{i1, j1, k1}) - } - - nonAdjacent = append(nonAdjacent, Match{len(m.a), len(m.b), 0}) - m.matchingBlocks = nonAdjacent - return m.matchingBlocks -} - -// GetOpCodes returns a list of 5-tuples describing how to turn a into b. -// -// Each tuple is of the form (tag, i1, i2, j1, j2). The first tuple -// has i1 == j1 == 0, and remaining tuples have i1 == the i2 from the -// tuple preceding it, and likewise for j1 == the previous j2. -// -// The tags are characters, with these meanings: -// -// 'r' (replace): a[i1:i2] should be replaced by b[j1:j2] -// -// 'd' (delete): a[i1:i2] should be deleted, j1==j2 in this case. -// -// 'i' (insert): b[j1:j2] should be inserted at a[i1:i1], i1==i2 in this case. -// -// 'e' (equal): a[i1:i2] == b[j1:j2] -func (m *SequenceMatcher) GetOpCodes() []OpCode { - if m.opCodes != nil { - return m.opCodes - } - i, j := 0, 0 - matching := m.GetMatchingBlocks() - opCodes := make([]OpCode, 0, len(matching)) - for _, m := range matching { - // invariant: we've pumped out correct diffs to change - // a[:i] into b[:j], and the next matching block is - // a[ai:ai+size] == b[bj:bj+size]. So we need to pump - // out a diff to change a[i:ai] into b[j:bj], pump out - // the matching block, and move (i,j) beyond the match - ai, bj, size := m.A, m.B, m.Size - tag := byte(0) - if i < ai && j < bj { - tag = 'r' - } else if i < ai { - tag = 'd' - } else if j < bj { - tag = 'i' - } - if tag > 0 { - opCodes = append(opCodes, OpCode{tag, i, ai, j, bj}) - } - i, j = ai+size, bj+size - // the list of matching blocks is terminated by a - // sentinel with size 0 - if size > 0 { - opCodes = append(opCodes, OpCode{'e', ai, i, bj, j}) - } - } - m.opCodes = opCodes - return m.opCodes -} - -// GetGroupedOpCodes isolates change clusters by eliminating ranges with no changes. -// -// Return a generator of groups with up to n lines of context. -// Each group is in the same format as returned by GetOpCodes(). -func (m *SequenceMatcher) GetGroupedOpCodes(n int) [][]OpCode { - if n < 0 { - n = 3 - } - codes := m.GetOpCodes() - if len(codes) == 0 { - codes = []OpCode{{'e', 0, 1, 0, 1}} - } - // Fixup leading and trailing groups if they show no changes. - if codes[0].Tag == 'e' { - c := codes[0] - i1, i2, j1, j2 := c.I1, c.I2, c.J1, c.J2 - codes[0] = OpCode{c.Tag, max(i1, i2-n), i2, max(j1, j2-n), j2} - } - if codes[len(codes)-1].Tag == 'e' { - c := codes[len(codes)-1] - i1, i2, j1, j2 := c.I1, c.I2, c.J1, c.J2 - codes[len(codes)-1] = OpCode{c.Tag, i1, min(i2, i1+n), j1, min(j2, j1+n)} - } - nn := n + n - groups := [][]OpCode{} - group := []OpCode{} - for _, c := range codes { - i1, i2, j1, j2 := c.I1, c.I2, c.J1, c.J2 - // End the current group and start a new one whenever - // there is a large range with no changes. - if c.Tag == 'e' && i2-i1 > nn { - group = append(group, OpCode{c.Tag, i1, min(i2, i1+n), - j1, min(j2, j1+n)}) - groups = append(groups, group) - group = []OpCode{} - i1, j1 = max(i1, i2-n), max(j1, j2-n) - } - group = append(group, OpCode{c.Tag, i1, i2, j1, j2}) - } - if len(group) > 0 && !(len(group) == 1 && group[0].Tag == 'e') { - groups = append(groups, group) - } - return groups -} diff --git a/vendor/gotest.tools/v3/internal/format/diff.go b/vendor/gotest.tools/v3/internal/format/diff.go deleted file mode 100644 index 4f6c07a3..00000000 --- a/vendor/gotest.tools/v3/internal/format/diff.go +++ /dev/null @@ -1,162 +0,0 @@ -// Package format provides utilities for formatting diffs and messages. -package format - -import ( - "bytes" - "fmt" - "strings" - "unicode" - - "gotest.tools/v3/internal/difflib" -) - -const ( - contextLines = 2 -) - -// DiffConfig for a unified diff -type DiffConfig struct { - A string - B string - From string - To string -} - -// UnifiedDiff is a modified version of difflib.WriteUnifiedDiff with better -// support for showing the whitespace differences. -func UnifiedDiff(conf DiffConfig) string { - a := strings.SplitAfter(conf.A, "\n") - b := strings.SplitAfter(conf.B, "\n") - groups := difflib.NewMatcher(a, b).GetGroupedOpCodes(contextLines) - if len(groups) == 0 { - return "" - } - - buf := new(bytes.Buffer) - writeFormat := func(format string, args ...interface{}) { - buf.WriteString(fmt.Sprintf(format, args...)) - } - writeLine := func(prefix string, s string) { - buf.WriteString(prefix + s) - } - if hasWhitespaceDiffLines(groups, a, b) { - writeLine = visibleWhitespaceLine(writeLine) - } - formatHeader(writeFormat, conf) - for _, group := range groups { - formatRangeLine(writeFormat, group) - for _, opCode := range group { - in, out := a[opCode.I1:opCode.I2], b[opCode.J1:opCode.J2] - switch opCode.Tag { - case 'e': - formatLines(writeLine, " ", in) - case 'r': - formatLines(writeLine, "-", in) - formatLines(writeLine, "+", out) - case 'd': - formatLines(writeLine, "-", in) - case 'i': - formatLines(writeLine, "+", out) - } - } - } - return buf.String() -} - -// hasWhitespaceDiffLines returns true if any diff groups is only different -// because of whitespace characters. -func hasWhitespaceDiffLines(groups [][]difflib.OpCode, a, b []string) bool { - for _, group := range groups { - in, out := new(bytes.Buffer), new(bytes.Buffer) - for _, opCode := range group { - if opCode.Tag == 'e' { - continue - } - for _, line := range a[opCode.I1:opCode.I2] { - in.WriteString(line) - } - for _, line := range b[opCode.J1:opCode.J2] { - out.WriteString(line) - } - } - if removeWhitespace(in.String()) == removeWhitespace(out.String()) { - return true - } - } - return false -} - -func removeWhitespace(s string) string { - var result []rune - for _, r := range s { - if !unicode.IsSpace(r) { - result = append(result, r) - } - } - return string(result) -} - -func visibleWhitespaceLine(ws func(string, string)) func(string, string) { - mapToVisibleSpace := func(r rune) rune { - switch r { - case '\n': - case ' ': - return '·' - case '\t': - return '▷' - case '\v': - return '▽' - case '\r': - return '↵' - case '\f': - return '↓' - default: - if unicode.IsSpace(r) { - return '�' - } - } - return r - } - return func(prefix, s string) { - ws(prefix, strings.Map(mapToVisibleSpace, s)) - } -} - -func formatHeader(wf func(string, ...interface{}), conf DiffConfig) { - if conf.From != "" || conf.To != "" { - wf("--- %s\n", conf.From) - wf("+++ %s\n", conf.To) - } -} - -func formatRangeLine(wf func(string, ...interface{}), group []difflib.OpCode) { - first, last := group[0], group[len(group)-1] - range1 := formatRangeUnified(first.I1, last.I2) - range2 := formatRangeUnified(first.J1, last.J2) - wf("@@ -%s +%s @@\n", range1, range2) -} - -// Convert range to the "ed" format -func formatRangeUnified(start, stop int) string { - // Per the diff spec at http://www.unix.org/single_unix_specification/ - beginning := start + 1 // lines start numbering with one - length := stop - start - if length == 1 { - return fmt.Sprintf("%d", beginning) - } - if length == 0 { - beginning-- // empty ranges begin at line just before the range - } - return fmt.Sprintf("%d,%d", beginning, length) -} - -func formatLines(writeLine func(string, string), prefix string, lines []string) { - for _, line := range lines { - writeLine(prefix, line) - } - // Add a newline if the last line is missing one so that the diff displays - // properly. - if !strings.HasSuffix(lines[len(lines)-1], "\n") { - writeLine("", "\n") - } -} diff --git a/vendor/gotest.tools/v3/internal/format/format.go b/vendor/gotest.tools/v3/internal/format/format.go deleted file mode 100644 index 5097e4bd..00000000 --- a/vendor/gotest.tools/v3/internal/format/format.go +++ /dev/null @@ -1,27 +0,0 @@ -package format // import "gotest.tools/v3/internal/format" - -import "fmt" - -// Message accepts a msgAndArgs varargs and formats it using fmt.Sprintf -func Message(msgAndArgs ...interface{}) string { - switch len(msgAndArgs) { - case 0: - return "" - case 1: - return fmt.Sprintf("%v", msgAndArgs[0]) - default: - return fmt.Sprintf(msgAndArgs[0].(string), msgAndArgs[1:]...) - } -} - -// WithCustomMessage accepts one or two messages and formats them appropriately -func WithCustomMessage(source string, msgAndArgs ...interface{}) string { - custom := Message(msgAndArgs...) - switch { - case custom == "": - return source - case source == "": - return custom - } - return fmt.Sprintf("%s: %s", source, custom) -} diff --git a/vendor/gotest.tools/v3/internal/source/defers.go b/vendor/gotest.tools/v3/internal/source/defers.go deleted file mode 100644 index 392d9fe0..00000000 --- a/vendor/gotest.tools/v3/internal/source/defers.go +++ /dev/null @@ -1,52 +0,0 @@ -package source - -import ( - "fmt" - "go/ast" - "go/token" -) - -func scanToDeferLine(fileset *token.FileSet, node ast.Node, lineNum int) ast.Node { - var matchedNode ast.Node - ast.Inspect(node, func(node ast.Node) bool { - switch { - case node == nil || matchedNode != nil: - return false - case fileset.Position(node.End()).Line == lineNum: - if funcLit, ok := node.(*ast.FuncLit); ok { - matchedNode = funcLit - return false - } - } - return true - }) - debug("defer line node: %s", debugFormatNode{matchedNode}) - return matchedNode -} - -func guessDefer(node ast.Node) (ast.Node, error) { - defers := collectDefers(node) - switch len(defers) { - case 0: - return nil, fmt.Errorf("failed to find expression in defer") - case 1: - return defers[0].Call, nil - default: - return nil, fmt.Errorf( - "ambiguous call expression: multiple (%d) defers in call block", - len(defers)) - } -} - -func collectDefers(node ast.Node) []*ast.DeferStmt { - var defers []*ast.DeferStmt - ast.Inspect(node, func(node ast.Node) bool { - if d, ok := node.(*ast.DeferStmt); ok { - defers = append(defers, d) - debug("defer: %s", debugFormatNode{d}) - return false - } - return true - }) - return defers -} diff --git a/vendor/gotest.tools/v3/internal/source/source.go b/vendor/gotest.tools/v3/internal/source/source.go deleted file mode 100644 index a4fc24ee..00000000 --- a/vendor/gotest.tools/v3/internal/source/source.go +++ /dev/null @@ -1,148 +0,0 @@ -// Package source provides utilities for handling source-code. -package source // import "gotest.tools/v3/internal/source" - -import ( - "bytes" - "errors" - "fmt" - "go/ast" - "go/format" - "go/parser" - "go/token" - "os" - "runtime" -) - -// FormattedCallExprArg returns the argument from an ast.CallExpr at the -// index in the call stack. The argument is formatted using FormatNode. -func FormattedCallExprArg(stackIndex int, argPos int) (string, error) { - args, err := CallExprArgs(stackIndex + 1) - if err != nil { - return "", err - } - if argPos >= len(args) { - return "", errors.New("failed to find expression") - } - return FormatNode(args[argPos]) -} - -// CallExprArgs returns the ast.Expr slice for the args of an ast.CallExpr at -// the index in the call stack. -func CallExprArgs(stackIndex int) ([]ast.Expr, error) { - _, filename, line, ok := runtime.Caller(stackIndex + 1) - if !ok { - return nil, errors.New("failed to get call stack") - } - debug("call stack position: %s:%d", filename, line) - - fileset := token.NewFileSet() - astFile, err := parser.ParseFile(fileset, filename, nil, parser.AllErrors) - if err != nil { - return nil, fmt.Errorf("failed to parse source file %s: %w", filename, err) - } - - expr, err := getCallExprArgs(fileset, astFile, line) - if err != nil { - return nil, fmt.Errorf("call from %s:%d: %w", filename, line, err) - } - return expr, nil -} - -func getNodeAtLine(fileset *token.FileSet, astFile ast.Node, lineNum int) (ast.Node, error) { - if node := scanToLine(fileset, astFile, lineNum); node != nil { - return node, nil - } - if node := scanToDeferLine(fileset, astFile, lineNum); node != nil { - node, err := guessDefer(node) - if err != nil || node != nil { - return node, err - } - } - return nil, nil -} - -func scanToLine(fileset *token.FileSet, node ast.Node, lineNum int) ast.Node { - var matchedNode ast.Node - ast.Inspect(node, func(node ast.Node) bool { - switch { - case node == nil || matchedNode != nil: - return false - case fileset.Position(node.Pos()).Line == lineNum: - matchedNode = node - return false - } - return true - }) - return matchedNode -} - -func getCallExprArgs(fileset *token.FileSet, astFile ast.Node, line int) ([]ast.Expr, error) { - node, err := getNodeAtLine(fileset, astFile, line) - switch { - case err != nil: - return nil, err - case node == nil: - return nil, fmt.Errorf("failed to find an expression") - } - - debug("found node: %s", debugFormatNode{node}) - - visitor := &callExprVisitor{} - ast.Walk(visitor, node) - if visitor.expr == nil { - return nil, errors.New("failed to find call expression") - } - debug("callExpr: %s", debugFormatNode{visitor.expr}) - return visitor.expr.Args, nil -} - -type callExprVisitor struct { - expr *ast.CallExpr -} - -func (v *callExprVisitor) Visit(node ast.Node) ast.Visitor { - if v.expr != nil || node == nil { - return nil - } - debug("visit: %s", debugFormatNode{node}) - - switch typed := node.(type) { - case *ast.CallExpr: - v.expr = typed - return nil - case *ast.DeferStmt: - ast.Walk(v, typed.Call.Fun) - return nil - } - return v -} - -// FormatNode using go/format.Node and return the result as a string -func FormatNode(node ast.Node) (string, error) { - buf := new(bytes.Buffer) - err := format.Node(buf, token.NewFileSet(), node) - return buf.String(), err -} - -var debugEnabled = os.Getenv("GOTESTTOOLS_DEBUG") != "" - -func debug(format string, args ...interface{}) { - if debugEnabled { - fmt.Fprintf(os.Stderr, "DEBUG: "+format+"\n", args...) - } -} - -type debugFormatNode struct { - ast.Node -} - -func (n debugFormatNode) String() string { - if n.Node == nil { - return "none" - } - out, err := FormatNode(n.Node) - if err != nil { - return fmt.Sprintf("failed to format %s: %s", n.Node, err) - } - return fmt.Sprintf("(%T) %s", n.Node, out) -} diff --git a/vendor/gotest.tools/v3/internal/source/update.go b/vendor/gotest.tools/v3/internal/source/update.go deleted file mode 100644 index 5591bffd..00000000 --- a/vendor/gotest.tools/v3/internal/source/update.go +++ /dev/null @@ -1,171 +0,0 @@ -package source - -import ( - "bytes" - "errors" - "flag" - "fmt" - "go/ast" - "go/format" - "go/parser" - "go/token" - "os" - "runtime" - "strings" -) - -// IsUpdate is returns true if the -update flag is set. It indicates the user -// running the tests would like to update any golden values. -func IsUpdate() bool { - if Update { - return true - } - return flag.Lookup("update").Value.(flag.Getter).Get().(bool) -} - -// Update is a shim for testing, and for compatibility with the old -update-golden -// flag. -var Update bool - -func init() { - if f := flag.Lookup("update"); f != nil { - getter, ok := f.Value.(flag.Getter) - msg := "some other package defined an incompatible -update flag, expected a flag.Bool" - if !ok { - panic(msg) - } - if _, ok := getter.Get().(bool); !ok { - panic(msg) - } - return - } - flag.Bool("update", false, "update golden values") -} - -// ErrNotFound indicates that UpdateExpectedValue failed to find the -// variable to update, likely because it is not a package level variable. -var ErrNotFound = fmt.Errorf("failed to find variable for update of golden value") - -// UpdateExpectedValue looks for a package-level variable with a name that -// starts with expected in the arguments to the caller. If the variable is -// found, the value of the variable will be updated to value of the other -// argument to the caller. -func UpdateExpectedValue(stackIndex int, x, y interface{}) error { - _, filename, line, ok := runtime.Caller(stackIndex + 1) - if !ok { - return errors.New("failed to get call stack") - } - debug("call stack position: %s:%d", filename, line) - - fileset := token.NewFileSet() - astFile, err := parser.ParseFile(fileset, filename, nil, parser.AllErrors|parser.ParseComments) - if err != nil { - return fmt.Errorf("failed to parse source file %s: %w", filename, err) - } - - expr, err := getCallExprArgs(fileset, astFile, line) - if err != nil { - return fmt.Errorf("call from %s:%d: %w", filename, line, err) - } - - if len(expr) < 3 { - debug("not enough arguments %d: %v", - len(expr), debugFormatNode{Node: &ast.CallExpr{Args: expr}}) - return ErrNotFound - } - - argIndex, ident := getIdentForExpectedValueArg(expr) - if argIndex < 0 || ident == nil { - debug("no arguments started with the word 'expected': %v", - debugFormatNode{Node: &ast.CallExpr{Args: expr}}) - return ErrNotFound - } - - value := x - if argIndex == 1 { - value = y - } - - strValue, ok := value.(string) - if !ok { - debug("value must be type string, got %T", value) - return ErrNotFound - } - return UpdateVariable(filename, fileset, astFile, ident, strValue) -} - -// UpdateVariable writes to filename the contents of astFile with the value of -// the variable updated to value. -func UpdateVariable( - filename string, - fileset *token.FileSet, - astFile *ast.File, - ident *ast.Ident, - value string, -) error { - obj := ident.Obj - if obj == nil { - return ErrNotFound - } - if obj.Kind != ast.Con && obj.Kind != ast.Var { - debug("can only update var and const, found %v", obj.Kind) - return ErrNotFound - } - - switch decl := obj.Decl.(type) { - case *ast.ValueSpec: - if len(decl.Names) != 1 { - debug("more than one name in ast.ValueSpec") - return ErrNotFound - } - - decl.Values[0] = &ast.BasicLit{ - Kind: token.STRING, - Value: "`" + value + "`", - } - - case *ast.AssignStmt: - if len(decl.Lhs) != 1 { - debug("more than one name in ast.AssignStmt") - return ErrNotFound - } - - decl.Rhs[0] = &ast.BasicLit{ - Kind: token.STRING, - Value: "`" + value + "`", - } - - default: - debug("can only update *ast.ValueSpec, found %T", obj.Decl) - return ErrNotFound - } - - var buf bytes.Buffer - if err := format.Node(&buf, fileset, astFile); err != nil { - return fmt.Errorf("failed to format file after update: %w", err) - } - - fh, err := os.Create(filename) - if err != nil { - return fmt.Errorf("failed to open file %v: %w", filename, err) - } - if _, err = fh.Write(buf.Bytes()); err != nil { - return fmt.Errorf("failed to write file %v: %w", filename, err) - } - if err := fh.Sync(); err != nil { - return fmt.Errorf("failed to sync file %v: %w", filename, err) - } - return nil -} - -func getIdentForExpectedValueArg(expr []ast.Expr) (int, *ast.Ident) { - for i := 1; i < 3; i++ { - switch e := expr[i].(type) { - case *ast.Ident: - if strings.HasPrefix(strings.ToLower(e.Name), "expected") { - return i, e - } - } - } - return -1, nil -} diff --git a/vendor/gotest.tools/v3/internal/source/version.go b/vendor/gotest.tools/v3/internal/source/version.go deleted file mode 100644 index 5fa8a903..00000000 --- a/vendor/gotest.tools/v3/internal/source/version.go +++ /dev/null @@ -1,35 +0,0 @@ -package source - -import ( - "runtime" - "strconv" - "strings" -) - -// GoVersionLessThan returns true if runtime.Version() is semantically less than -// version major.minor. Returns false if a release version can not be parsed from -// runtime.Version(). -func GoVersionLessThan(major, minor int64) bool { - version := runtime.Version() - // not a release version - if !strings.HasPrefix(version, "go") { - return false - } - version = strings.TrimPrefix(version, "go") - parts := strings.Split(version, ".") - if len(parts) < 2 { - return false - } - rMajor, err := strconv.ParseInt(parts[0], 10, 32) - if err != nil { - return false - } - if rMajor != major { - return rMajor < major - } - rMinor, err := strconv.ParseInt(parts[1], 10, 32) - if err != nil { - return false - } - return rMinor < minor -} diff --git a/vendor/modules.txt b/vendor/modules.txt index 5e858c66..c6fa4d15 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -344,13 +344,6 @@ github.com/gorilla/mux github.com/grpc-ecosystem/grpc-gateway/v2/internal/httprule github.com/grpc-ecosystem/grpc-gateway/v2/runtime github.com/grpc-ecosystem/grpc-gateway/v2/utilities -# github.com/gruntwork-io/terratest v0.48.2 -## explicit; go 1.21 -github.com/gruntwork-io/terratest/modules/collections -github.com/gruntwork-io/terratest/modules/docker -github.com/gruntwork-io/terratest/modules/logger -github.com/gruntwork-io/terratest/modules/shell -github.com/gruntwork-io/terratest/modules/testing # github.com/hashicorp/errwrap v1.1.0 ## explicit github.com/hashicorp/errwrap @@ -1053,12 +1046,3 @@ gopkg.in/yaml.v2 # gopkg.in/yaml.v3 v3.0.1 ## explicit gopkg.in/yaml.v3 -# gotest.tools/v3 v3.5.1 -## explicit; go 1.17 -gotest.tools/v3/assert -gotest.tools/v3/assert/cmp -gotest.tools/v3/icmd -gotest.tools/v3/internal/assert -gotest.tools/v3/internal/difflib -gotest.tools/v3/internal/format -gotest.tools/v3/internal/source