diff --git a/.github/workflows/golangci-lint.yml b/.github/workflows/golangci-lint.yml index 6239219..9acf314 100644 --- a/.github/workflows/golangci-lint.yml +++ b/.github/workflows/golangci-lint.yml @@ -21,13 +21,13 @@ jobs: steps: - uses: actions/setup-go@v3 with: - go-version: 1.22.x + go-version: 1.23.x - uses: actions/checkout@v2 - name: golangci-lint - uses: golangci/golangci-lint-action@v4.0.0 + uses: golangci/golangci-lint-action@v6.1.0 with: # Required: the version of golangci-lint is required and must be specified without patch version: we always use the latest patch version. - version: v1.56.2 + version: v1.61.0 # Optional: working directory, useful for monorepos # working-directory: somedir diff --git a/.github/workflows/gorelease.yml b/.github/workflows/gorelease.yml index 6356a9d..c031db4 100644 --- a/.github/workflows/gorelease.yml +++ b/.github/workflows/gorelease.yml @@ -9,7 +9,7 @@ concurrency: cancel-in-progress: true env: - GO_VERSION: 1.22.x + GO_VERSION: 1.23.x jobs: gorelease: runs-on: ubuntu-latest diff --git a/.github/workflows/test-unit.yml b/.github/workflows/test-unit.yml index 9b6e93d..cc984ab 100644 --- a/.github/workflows/test-unit.yml +++ b/.github/workflows/test-unit.yml @@ -21,7 +21,7 @@ jobs: test: strategy: matrix: - go-version: [ 1.20.x, 1.21.x, 1.22.x ] + go-version: [ 1.18.x, 1.22.x, 1.23.x ] runs-on: ubuntu-latest steps: - name: Install Go stable diff --git a/.golangci.yml b/.golangci.yml index a035208..7ee9f9b 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -21,50 +21,46 @@ linters: enable-all: true disable: - lll - - maligned - gochecknoglobals - gomnd - wrapcheck - paralleltest - forbidigo - - exhaustivestruct - - interfacer # deprecated - forcetypeassert - - scopelint # deprecated - - ifshort # too many false positives - - golint # deprecated - varnamelen - tagliatelle - errname - ireturn - exhaustruct - nonamedreturns - - nosnakecase - - structcheck - - varcheck - - deadcode - testableexamples - dupword - depguard - tagalign + - execinquery + - mnd + - testifylint issues: exclude-use-default: false exclude-rules: - linters: - gomnd + - mnd - goconst - - goerr113 - noctx - funlen - dupl - structcheck - unused - unparam - - nosnakecase + - err113 path: "_test.go" - linters: - errcheck # Error checking omitted for brevity. - gosec path: "example_" + - linters: + - revive + text: "unused-parameter: parameter" diff --git a/Makefile b/Makefile index 6164656..853cc8c 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -#GOLANGCI_LINT_VERSION := "v1.56.2" # Optional configuration to pinpoint golangci-lint version. +#GOLANGCI_LINT_VERSION := "v1.61.0" # Optional configuration to pinpoint golangci-lint version. # The head of Makefile determines location of dev-go to include standard targets. GO ?= go diff --git a/example_test.go b/example_test.go index 5b71aab..d3051c6 100644 --- a/example_test.go +++ b/example_test.go @@ -2,7 +2,7 @@ package httpsteps_test import ( "fmt" - "io/ioutil" + "io" "net/http" "net/http/httptest" @@ -17,7 +17,7 @@ func ExampleNewLocalClient() { h := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { req, _ := http.NewRequest(http.MethodGet, templateService+"/template/hello", nil) resp, _ := http.DefaultTransport.RoundTrip(req) - tpl, _ := ioutil.ReadAll(resp.Body) + tpl, _ := io.ReadAll(resp.Body) _ = resp.Body.Close() _, _ = w.Write([]byte(fmt.Sprintf(string(tpl), r.URL.Query().Get("name")))) @@ -37,7 +37,7 @@ func ExampleNewLocalClient() { Format: "pretty", Strict: true, Paths: []string{"_testdata/Example.feature"}, - Output: ioutil.Discard, + Output: io.Discard, }, } diff --git a/external_server_test.go b/external_server_test.go index 612a7f0..9691db4 100644 --- a/external_server_test.go +++ b/external_server_test.go @@ -3,7 +3,6 @@ package httpsteps_test import ( "bytes" "io" - "io/ioutil" "net/http" "sync" "testing" @@ -65,7 +64,7 @@ func callServices(t *testing.T, someServiceURL, anotherServiceURL string) func() assert.Equal(t, "foo", resp.Header.Get("X-Bar")) - respBody, err := ioutil.ReadAll(resp.Body) + respBody, err := io.ReadAll(resp.Body) require.NoError(t, resp.Body.Close()) require.NoError(t, err) @@ -92,6 +91,7 @@ func callServices(t *testing.T, someServiceURL, anotherServiceURL string) func() assertjson.Equal(t, []byte(`{"theFooWas":"bar"}`), respBody) }() } + wg.Wait() // Hitting `"some-service" responds with status "OK"`. @@ -101,7 +101,7 @@ func callServices(t *testing.T, someServiceURL, anotherServiceURL string) func() resp, err = http.DefaultTransport.RoundTrip(req) require.NoError(t, err) - respBody, err = ioutil.ReadAll(resp.Body) + respBody, err = io.ReadAll(resp.Body) require.NoError(t, resp.Body.Close()) require.NoError(t, err) require.Empty(t, respBody) @@ -113,7 +113,7 @@ func callServices(t *testing.T, someServiceURL, anotherServiceURL string) func() resp, err = http.DefaultTransport.RoundTrip(req) require.NoError(t, err) - respBody, err = ioutil.ReadAll(resp.Body) + respBody, err = io.ReadAll(resp.Body) require.NoError(t, resp.Body.Close()) require.NoError(t, err) diff --git a/go.mod b/go.mod index b2367ba..ecd7fe2 100644 --- a/go.mod +++ b/go.mod @@ -3,14 +3,14 @@ module github.com/godogx/httpsteps go 1.18 require ( - github.com/bool64/dev v0.2.34 - github.com/bool64/httpmock v0.1.13 + github.com/bool64/dev v0.2.36 + github.com/bool64/httpmock v0.1.15 github.com/bool64/shared v0.1.5 github.com/cenkalti/backoff/v4 v4.2.1 - github.com/cucumber/godog v0.14.0 + github.com/cucumber/godog v0.15.0 github.com/godogx/resource v0.1.1 - github.com/godogx/vars v0.1.7 - github.com/stretchr/testify v1.8.4 + github.com/godogx/vars v0.1.8 + github.com/stretchr/testify v1.9.0 github.com/swaggest/assertjson v1.9.0 ) diff --git a/go.sum b/go.sum index adfca13..15b564e 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,7 @@ -github.com/bool64/dev v0.2.34 h1:P9n315P8LdpxusnYQ0X7MP1CZXwBK5ae5RZrd+GdSZE= -github.com/bool64/dev v0.2.34/go.mod h1:iJbh1y/HkunEPhgebWRNcs8wfGq7sjvJ6W5iabL8ACg= -github.com/bool64/httpmock v0.1.13 h1:3QpRXQ5kwHLW8xnVT8+Ug7VS6RerhdEFV+RWYC61aVo= -github.com/bool64/httpmock v0.1.13/go.mod h1:YMTLaypQ3o5DAx78eA/kDRSLec0f+42sLMDmHdmeY+E= +github.com/bool64/dev v0.2.36 h1:yU3bbOTujoxhWnt8ig8t94PVmZXIkCaRj9C57OtqJBY= +github.com/bool64/dev v0.2.36/go.mod h1:iJbh1y/HkunEPhgebWRNcs8wfGq7sjvJ6W5iabL8ACg= +github.com/bool64/httpmock v0.1.15 h1:PWvuqpew/FEigT7cvv03/t9G+UeE3wD2QP8PVyBBUwc= +github.com/bool64/httpmock v0.1.15/go.mod h1:Ht82vaR7YJu8YHcJoK5mDi5OG6NJfv/MmYfdfod1huQ= github.com/bool64/shared v0.1.5 h1:fp3eUhBsrSjNCQPcSdQqZxxh9bBwrYiZ+zOKFkM0/2E= github.com/bool64/shared v0.1.5/go.mod h1:081yz68YC9jeFB3+Bbmno2RFWvGKv1lPKkMP6MHJlPs= github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= @@ -9,8 +9,8 @@ github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyY github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/cucumber/gherkin/go/v26 v26.2.0 h1:EgIjePLWiPeslwIWmNQ3XHcypPsWAHoMCz/YEBKP4GI= github.com/cucumber/gherkin/go/v26 v26.2.0/go.mod h1:t2GAPnB8maCT4lkHL99BDCVNzCh1d7dBhCLt150Nr/0= -github.com/cucumber/godog v0.14.0 h1:h/K4t7XBxsFBF+UJEahNqJ1/2VHVepRXCSq3WWWnehs= -github.com/cucumber/godog v0.14.0/go.mod h1:FX3rzIDybWABU4kuIXLZ/qtqEe1Ac5RdXmqvACJOces= +github.com/cucumber/godog v0.15.0 h1:51AL8lBXF3f0cyA5CV4TnJFCTHpgiy+1x1Hb3TtZUmo= +github.com/cucumber/godog v0.15.0/go.mod h1:FX3rzIDybWABU4kuIXLZ/qtqEe1Ac5RdXmqvACJOces= github.com/cucumber/messages/go/v21 v21.0.1 h1:wzA0LxwjlWQYZd32VTlAVDTkW6inOFmSM+RuOwHZiMI= github.com/cucumber/messages/go/v21 v21.0.1/go.mod h1:zheH/2HS9JLVFukdrsPWoPdmUtmYQAQPLk7w5vWsk5s= github.com/cucumber/messages/go/v22 v22.0.0/go.mod h1:aZipXTKc0JnjCsXrJnuZpWhtay93k7Rn3Dee7iyPJjs= @@ -21,8 +21,8 @@ github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWo github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/godogx/resource v0.1.1 h1:1vbznIn1mUCP+9TzJp9v8QKm54kAMTe38CBLnFBk0j8= github.com/godogx/resource v0.1.1/go.mod h1:OYaiyttuq2KaiJp2yOMekyOFjZJFz3w/D7WPioUVC4Y= -github.com/godogx/vars v0.1.7 h1:ddsLouhCSr21k7hqc7Az1GrOVMLMZ/B5uUq7LaNGvvw= -github.com/godogx/vars v0.1.7/go.mod h1:ALGgGk/Lu+APce8uanQrHuO2Ur5ap1OwRIDfFleTBnE= +github.com/godogx/vars v0.1.8 h1:UtAnTBpbZ5E9hm+SeEtk3atgrNlKd4D0gG4r4sZgE3w= +github.com/godogx/vars v0.1.8/go.mod h1:dVfTTDjNvAul6VqXgSogRTw+dvh8+v2lWrCdXOqUFEY= github.com/gofrs/uuid v4.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gofrs/uuid v4.3.1+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA= @@ -69,8 +69,8 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/swaggest/assertjson v1.9.0 h1:dKu0BfJkIxv/xe//mkCrK5yZbs79jL7OVf9Ija7o2xQ= github.com/swaggest/assertjson v1.9.0/go.mod h1:b+ZKX2VRiUjxfUIal0HDN85W0nHPAYUbYH5WkkSsFsU= github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0 h1:6fRhSjgLCkTD3JnJxvaJ4Sj+TYblw757bqYgZaOq5ZY= diff --git a/local_client.go b/local_client.go index 9ab307c..d3d30bb 100644 --- a/local_client.go +++ b/local_client.go @@ -7,9 +7,9 @@ import ( "errors" "fmt" "io" - "io/ioutil" "mime/multipart" "net/http" + "net/http/httputil" "net/url" "os" "path/filepath" @@ -44,7 +44,8 @@ func NewLocalClient(defaultBaseURL string, options ...func(*httpmock.Client)) *L defaultBaseURL = strings.TrimRight(defaultBaseURL, "/") l := LocalClient{ - options: options, + options: options, + ExposeHTTPDetails: DefaultExposeHTTPDetails, } l.AddService(Default, defaultBaseURL) @@ -62,6 +63,18 @@ type LocalClient struct { VS *vars.Steps RetryBackOff func(ctx context.Context, maxElapsedTime time.Duration) (context.Context, httpmock.RetryBackOff) + + // ExposeHTTPDetails enables godog.Attachment for request and response data. + // Has DefaultExposeHTTPDetails by default. + ExposeHTTPDetails func(ctx context.Context, d httpmock.HTTPValue) (context.Context, error) +} + +// HTTPValue grants access to a HTTP request and response. +type HTTPValue struct { + Sequence int + Request *http.Request + Response *http.Response + Error error } // AddService registers a URL for named service. @@ -215,13 +228,9 @@ func (l *LocalClient) RegisterSteps(s *godog.ScenarioContext) { s.After(l.afterScenario) } -func (l *LocalClient) afterScenario(ctx context.Context, _ *godog.Scenario, err error) (context.Context, error) { +func (l *LocalClient) afterScenario(ctx context.Context, _ *godog.Scenario, _ error) (context.Context, error) { var errs []string - if err != nil { - errs = append(errs, err.Error()) - } - for service := range l.services { client, _, err := l.Service(ctx, service) if err != nil { @@ -270,7 +279,7 @@ func (l *LocalClient) iRequestWithMethodAndURI(ctx context.Context, service, met // // Deprecated: use github.com/godogx/vars.(*Steps).ReplaceFile. func LoadBodyFromFile(filePath string, vars *shared.Vars) ([]byte, error) { - body, err := ioutil.ReadFile(filePath) //nolint // File inclusion via variable during tests. + body, err := os.ReadFile(filePath) //nolint // File inclusion via variable during tests. if err != nil { return nil, err } @@ -401,9 +410,11 @@ func (l *LocalClient) tableSetup( var rv []byte + ctx = l.VS.PrepareContext(ctx) + for key, values := range m { for _, value := range values { - ctx, rv, err = l.VS.Replace(ctx, []byte(value)) + _, rv, err = l.VS.Replace(ctx, []byte(value)) if err != nil { return ctx, fmt.Errorf("failed to replace vars in %s %s: %w", receiverName, key, err) } @@ -571,238 +582,260 @@ func (l *LocalClient) iShouldHaveOtherResponsesWithStatus(ctx context.Context, s return ctx, c.ExpectOtherResponsesStatus(code) } -func (l *LocalClient) iShouldHaveResponseWithStatus(ctx context.Context, service, statusOrCode string) (context.Context, error) { - c, ctx, err := l.Service(ctx, service) - if err != nil { - return ctx, err +// DefaultExposeHTTPDetails instruments context with godog.Attachment items of HTTP transaction. +func DefaultExposeHTTPDetails(ctx context.Context, d httpmock.HTTPValue) (context.Context, error) { + d.Req.Body = io.NopCloser(bytes.NewReader(d.ReqBody)) + + if s, ok := d.Req.Body.(io.Seeker); ok { + if _, err := s.Seek(0, io.SeekStart); err != nil { + return ctx, err + } } - code, err := statusCode(statusOrCode) + req, err := httputil.DumpRequest(d.Req, true) if err != nil { return ctx, err } - return ctx, c.ExpectResponseStatus(code) -} + ctx = godog.Attach(ctx, godog.Attachment{ + Body: req, + FileName: "request", + MediaType: "text/plain", + }) -func (l *LocalClient) iShouldHaveOtherResponsesWithHeader(ctx context.Context, service, key, value string) (context.Context, error) { - c, ctx, err := l.Service(ctx, service) - if err != nil { - return ctx, err - } + if d.Resp != nil { + d.Resp.Body = io.NopCloser(bytes.NewReader(d.RespBody)) - return ctx, c.ExpectOtherResponsesHeader(key, value) -} + resp, err := httputil.DumpResponse(d.Resp, true) + if err != nil { + return ctx, err + } -func (l *LocalClient) iShouldHaveOtherResponsesWithHeaders(ctx context.Context, service string, data *godog.Table) (context.Context, error) { - c, ctx, err := l.Service(ctx, service) - if err != nil { - return ctx, err + ctx = godog.Attach(ctx, godog.Attachment{ + Body: resp, + FileName: "response", + MediaType: "text/plain", + }) } - m, err := mapOfData(data) - if err != nil { - return ctx, err - } + if d.OtherResp != nil { + d.OtherResp.Body = io.NopCloser(bytes.NewReader(d.OtherRespBody)) - for key, values := range m { - for _, value := range values { - if err := c.ExpectOtherResponsesHeader(key, value); err != nil { - return ctx, fmt.Errorf("failed to assert response header %s: %w", key, err) - } + resp, err := httputil.DumpResponse(d.OtherResp, true) + if err != nil { + return ctx, err } + + ctx = godog.Attach(ctx, godog.Attachment{ + Body: resp, + FileName: "other responses", + MediaType: "text/plain", + }) + } + + if d.Attempt > 1 { + ctx = godog.Attach(ctx, godog.Attachment{ + FileName: "retries", + Body: []byte(fmt.Sprintf("Attempt: %d, Retry Delays: %v", d.Attempt, d.RetryDelays)), + MediaType: "text/plain", + }) } return ctx, nil } -func (l *LocalClient) iShouldHaveResponseWithHeader(ctx context.Context, service, key, value string) (context.Context, error) { +func (l *LocalClient) expectResponse(ctx context.Context, service string, expect func(c *httpmock.Client) error) (context.Context, error) { c, ctx, err := l.Service(ctx, service) if err != nil { return ctx, err } - return ctx, c.ExpectResponseHeader(key, value) -} + expErr := expect(c) -func (l *LocalClient) iShouldHaveResponseWithHeaders(ctx context.Context, service string, data *godog.Table) (context.Context, error) { - c, ctx, err := l.Service(ctx, service) - if err != nil { - return ctx, err - } + d := c.Details() - m, err := mapOfData(data) - if err != nil { - return ctx, err + if l.ExposeHTTPDetails != nil && d.Req != nil && !d.AlreadyRequested { + ctx, err = l.ExposeHTTPDetails(ctx, d) } - for key, values := range m { - for _, value := range values { - if err := c.ExpectResponseHeader(key, value); err != nil { - return ctx, fmt.Errorf("failed to assert response header %s: %w", key, err) - } + if expErr != nil { + if err == nil { + err = expErr + } else { + err = fmt.Errorf("%w (%s)", expErr, err.Error()) } } - return ctx, nil + return ctx, err } -func (l *LocalClient) iShouldHaveResponseWithBody(ctx context.Context, service, bodyDoc string) (context.Context, error) { - c, ctx, err := l.Service(ctx, service) - if err != nil { - return ctx, err - } - - err = c.ExpectResponseBodyCallback(func(received []byte) error { - ctx, err = augmentBodyErr(l.VS.Assert(ctx, []byte(bodyDoc), received, false)) +func (l *LocalClient) iShouldHaveResponseWithStatus(ctx context.Context, service, statusOrCode string) (context.Context, error) { + return l.expectResponse(ctx, service, func(c *httpmock.Client) error { + code, err := statusCode(statusOrCode) + if err != nil { + return err + } - return err + return c.ExpectResponseStatus(code) }) +} - return ctx, err +func (l *LocalClient) iShouldHaveOtherResponsesWithHeader(ctx context.Context, service, key, value string) (context.Context, error) { + return l.expectResponse(ctx, service, func(c *httpmock.Client) error { + return c.ExpectOtherResponsesHeader(key, value) + }) } -func (l *LocalClient) iShouldHaveResponseWithBodyFromFile(ctx context.Context, service, filePath string) (context.Context, error) { - c, ctx, err := l.Service(ctx, service) - if err != nil { - return ctx, err - } +func (l *LocalClient) iShouldHaveOtherResponsesWithHeaders(ctx context.Context, service string, data *godog.Table) (context.Context, error) { + return l.expectResponse(ctx, service, func(c *httpmock.Client) error { + m, err := mapOfData(data) + if err != nil { + return err + } - err = c.ExpectResponseBodyCallback(func(received []byte) error { - ctx, err = augmentBodyErr(l.VS.AssertFile(ctx, filePath, received, false)) + for key, values := range m { + for _, value := range values { + if err := c.ExpectOtherResponsesHeader(key, value); err != nil { + return fmt.Errorf("failed to assert response header %s: %w", key, err) + } + } + } - return err + return nil }) +} - return ctx, err +func (l *LocalClient) iShouldHaveResponseWithHeader(ctx context.Context, service, key, value string) (context.Context, error) { + return l.expectResponse(ctx, service, func(c *httpmock.Client) error { + return c.ExpectResponseHeader(key, value) + }) } -func (l *LocalClient) iShouldHaveResponseWithBodyThatMatchesJSON(ctx context.Context, service, bodyDoc string) (context.Context, error) { - c, ctx, err := l.Service(ctx, service) - if err != nil { - return ctx, err - } +func (l *LocalClient) iShouldHaveResponseWithHeaders(ctx context.Context, service string, data *godog.Table) (context.Context, error) { + return l.expectResponse(ctx, service, func(c *httpmock.Client) error { + m, err := mapOfData(data) + if err != nil { + return err + } - err = c.ExpectResponseBodyCallback(func(received []byte) error { - ctx, err = augmentBodyErr(l.VS.Assert(ctx, []byte(bodyDoc), received, true)) + for key, values := range m { + for _, value := range values { + if err := c.ExpectResponseHeader(key, value); err != nil { + return fmt.Errorf("failed to assert response header %s: %w", key, err) + } + } + } - return err + return nil }) - - return ctx, err } -func augmentBodyErr(ctx context.Context, err error) (context.Context, error) { - if err != nil { - return ctx, fmt.Errorf("%w %s", errUnexpectedBody, err.Error()) - } +func (l *LocalClient) iShouldHaveResponseWithBody(ctx context.Context, service, bodyDoc string) (context.Context, error) { + ctx = l.VS.PrepareContext(ctx) - return ctx, nil + return l.expectResponse(ctx, service, func(c *httpmock.Client) error { + return c.ExpectResponseBodyCallback(func(received []byte) error { + return augmentBodyErr(l.VS.Assert(ctx, []byte(bodyDoc), received, false)) + }) + }) } -func (l *LocalClient) iShouldHaveResponseWithBodyThatMatchesJSONPaths(ctx context.Context, service string, jsonPaths *godog.Table) (context.Context, error) { - c, ctx, err := l.Service(ctx, service) - if err != nil { - return ctx, err - } - - err = c.ExpectResponseBodyCallback(func(received []byte) error { - ctx, err = augmentBodyErr(l.VS.AssertJSONPaths(ctx, jsonPaths, received, true)) +func (l *LocalClient) iShouldHaveResponseWithBodyFromFile(ctx context.Context, service, filePath string) (context.Context, error) { + ctx = l.VS.PrepareContext(ctx) - return err + return l.expectResponse(ctx, service, func(c *httpmock.Client) error { + return c.ExpectResponseBodyCallback(func(received []byte) error { + return augmentBodyErr(l.VS.AssertFile(ctx, filePath, received, false)) + }) }) +} - return ctx, err +func (l *LocalClient) iShouldHaveResponseWithBodyThatMatchesJSON(ctx context.Context, service, bodyDoc string) (context.Context, error) { + ctx = l.VS.PrepareContext(ctx) + + return l.expectResponse(ctx, service, func(c *httpmock.Client) error { + return c.ExpectResponseBodyCallback(func(received []byte) error { + return augmentBodyErr(l.VS.Assert(ctx, []byte(bodyDoc), received, true)) + }) + }) } -func (l *LocalClient) iShouldHaveResponseWithBodyThatMatchesJSONFromFile(ctx context.Context, service, filePath string) (context.Context, error) { - c, ctx, err := l.Service(ctx, service) +func augmentBodyErr(_ context.Context, err error) error { if err != nil { - return ctx, err + return fmt.Errorf("%w %s", errUnexpectedBody, err.Error()) } - err = c.ExpectResponseBodyCallback(func(received []byte) error { - ctx, err = augmentBodyErr(l.VS.AssertFile(ctx, filePath, received, true)) + return nil +} + +func (l *LocalClient) iShouldHaveResponseWithBodyThatMatchesJSONPaths(ctx context.Context, service string, jsonPaths *godog.Table) (context.Context, error) { + ctx = l.VS.PrepareContext(ctx) - return err + return l.expectResponse(ctx, service, func(c *httpmock.Client) error { + return c.ExpectResponseBodyCallback(func(received []byte) error { + return augmentBodyErr(l.VS.AssertJSONPaths(ctx, jsonPaths, received, true)) + }) }) +} - return ctx, err +func (l *LocalClient) iShouldHaveResponseWithBodyThatMatchesJSONFromFile(ctx context.Context, service, filePath string) (context.Context, error) { + ctx = l.VS.PrepareContext(ctx) + + return l.expectResponse(ctx, service, func(c *httpmock.Client) error { + return c.ExpectResponseBodyCallback(func(received []byte) error { + return augmentBodyErr(l.VS.AssertFile(ctx, filePath, received, true)) + }) + }) } func (l *LocalClient) iShouldHaveOtherResponsesWithBody(ctx context.Context, service, bodyDoc string) (context.Context, error) { - c, ctx, err := l.Service(ctx, service) - if err != nil { - return ctx, err - } + ctx = l.VS.PrepareContext(ctx) - err = c.ExpectOtherResponsesBodyCallback(func(received []byte) error { - ctx, err = augmentBodyErr(l.VS.Assert(ctx, []byte(bodyDoc), received, false)) - - return err + return l.expectResponse(ctx, service, func(c *httpmock.Client) error { + return c.ExpectOtherResponsesBodyCallback(func(received []byte) error { + return augmentBodyErr(l.VS.Assert(ctx, []byte(bodyDoc), received, false)) + }) }) - - return ctx, err } func (l *LocalClient) iShouldHaveOtherResponsesWithBodyFromFile(ctx context.Context, service, filePath string) (context.Context, error) { - c, ctx, err := l.Service(ctx, service) - if err != nil { - return ctx, err - } + ctx = l.VS.PrepareContext(ctx) - err = c.ExpectOtherResponsesBodyCallback(func(received []byte) error { - ctx, err = augmentBodyErr(l.VS.AssertFile(ctx, filePath, received, false)) - - return err + return l.expectResponse(ctx, service, func(c *httpmock.Client) error { + return c.ExpectOtherResponsesBodyCallback(func(received []byte) error { + return augmentBodyErr(l.VS.AssertFile(ctx, filePath, received, false)) + }) }) - - return ctx, err } func (l *LocalClient) iShouldHaveOtherResponsesWithBodyThatMatchesJSON(ctx context.Context, service, bodyDoc string) (context.Context, error) { - c, ctx, err := l.Service(ctx, service) - if err != nil { - return ctx, err - } - - err = c.ExpectOtherResponsesBodyCallback(func(received []byte) error { - ctx, err = augmentBodyErr(l.VS.Assert(ctx, []byte(bodyDoc), received, true)) + ctx = l.VS.PrepareContext(ctx) - return err + return l.expectResponse(ctx, service, func(c *httpmock.Client) error { + return c.ExpectOtherResponsesBodyCallback(func(received []byte) error { + return augmentBodyErr(l.VS.Assert(ctx, []byte(bodyDoc), received, true)) + }) }) - - return ctx, err } func (l *LocalClient) iShouldHaveOtherResponsesWithBodyThatMatchesJSONPaths(ctx context.Context, service string, jsonPaths *godog.Table) (context.Context, error) { - c, ctx, err := l.Service(ctx, service) - if err != nil { - return ctx, err - } + ctx = l.VS.PrepareContext(ctx) - err = c.ExpectOtherResponsesBodyCallback(func(received []byte) error { - ctx, err = augmentBodyErr(l.VS.AssertJSONPaths(ctx, jsonPaths, received, true)) - - return err + return l.expectResponse(ctx, service, func(c *httpmock.Client) error { + return c.ExpectOtherResponsesBodyCallback(func(received []byte) error { + return augmentBodyErr(l.VS.AssertJSONPaths(ctx, jsonPaths, received, true)) + }) }) - - return ctx, err } func (l *LocalClient) iShouldHaveOtherResponsesWithBodyThatMatchesJSONFromFile(ctx context.Context, service, filePath string) (context.Context, error) { - c, ctx, err := l.Service(ctx, service) - if err != nil { - return ctx, err - } - - err = c.ExpectOtherResponsesBodyCallback(func(received []byte) error { - ctx, err = augmentBodyErr(l.VS.AssertFile(ctx, filePath, received, false)) + ctx = l.VS.PrepareContext(ctx) - return err + return l.expectResponse(ctx, service, func(c *httpmock.Client) error { + return c.ExpectOtherResponsesBodyCallback(func(received []byte) error { + return augmentBodyErr(l.VS.AssertFile(ctx, filePath, received, false)) + }) }) - - return ctx, err } func (l *LocalClient) iFollowRedirects(ctx context.Context, service string) (context.Context, error) { diff --git a/local_client_concurrency_test.go b/local_client_concurrency_test.go index b74ec55..f4cacf9 100644 --- a/local_client_concurrency_test.go +++ b/local_client_concurrency_test.go @@ -63,7 +63,9 @@ func TestLocalClient_RegisterSteps_concurrencyNonBlocked(t *testing.T) { local := NewLocalClient(srvURL, func(client *httpmock.Client) { client.ConcurrencyLevel = concurrency + client.Transport = http.DefaultTransport }) + local.AddService("service-one", srvURL) local.AddService("service-two", srvURL) diff --git a/local_client_test.go b/local_client_test.go index 180e52f..e1280f8 100644 --- a/local_client_test.go +++ b/local_client_test.go @@ -4,7 +4,6 @@ import ( "bytes" "fmt" "io" - "io/ioutil" "net/http" "net/http/httptest" "testing" @@ -207,7 +206,7 @@ func TestLocal_RegisterSteps_dynamic(t *testing.T) { } if r.URL.Path == "/order/12345/" && r.URL.Query().Get("user_id") == "12345" { - assert.Equal(t, "12345", r.Header.Get("X-UserId")) + assert.Equal(t, "12345", r.Header.Get("X-Userid")) cookie, err := r.Cookie("user_id") require.NoError(t, err) @@ -266,7 +265,7 @@ func TestLocal_RegisterSteps_AttachmentFile(t *testing.T) { const maxBufferSize = 1024 * 512 reader := io.LimitReader(file, maxBufferSize) - content, err := ioutil.ReadAll(reader) + content, err := io.ReadAll(reader) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError)