Skip to content

Commit

Permalink
Expose HTTP details of last run (#17)
Browse files Browse the repository at this point in the history
  • Loading branch information
vearutop authored Oct 30, 2024
1 parent 1eb0b7e commit 7149880
Show file tree
Hide file tree
Showing 9 changed files with 128 additions and 34 deletions.
24 changes: 21 additions & 3 deletions .github/workflows/golangci-lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,28 @@ 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@v6.0.1
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.59.1
version: v1.61.0

# Optional: working directory, useful for monorepos
# working-directory: somedir

# Optional: golangci-lint command line arguments.
# args: --issues-exit-code=0

# Optional: show only new issues if it's a pull request. The default value is `false`.
# only-new-issues: true

# Optional: if set to true then the action will use pre-installed Go.
# skip-go-installation: true

# Optional: if set to true then the action don't cache or restore ~/go/pkg.
# skip-pkg-cache: true

# Optional: if set to true then the action don't cache or restore ~/.cache/go-build.
# skip-build-cache: true
2 changes: 1 addition & 1 deletion .github/workflows/gorelease.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/test-unit.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ jobs:
test:
strategy:
matrix:
go-version: [ 1.13.x, 1.21.x, 1.22.x ]
go-version: [ 1.13.x, 1.22.x, 1.23.x ]
runs-on: ubuntu-latest
steps:
- name: Install Go stable
Expand Down
9 changes: 5 additions & 4 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ linters:
- ireturn
- exhaustruct
- nonamedreturns
- structcheck
- testableexamples
- dupword
- depguard
Expand All @@ -51,21 +50,23 @@ issues:
- linters:
- staticcheck
path: ".go"
text: "\"io/ioutil\" has been deprecated since Go 1.16" # Keeping backwards compatibility with go1.13.
text: "\"io/ioutil\" has been deprecated since" # Keeping backwards compatibility with go1.13.
- linters:
- gomnd
- mnd
- goconst
- goerr113
- noctx
- funlen
- dupl
- structcheck
- unused
- unparam
- nosnakecase
path: "_test.go"
- linters:
- errcheck # Error checking omitted for brevity.
- gosec
path: "example_"
- linters:
- revive
text: "unused-parameter: parameter"

2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#GOLANGCI_LINT_VERSION := "v1.59.1" # 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
Expand Down
94 changes: 79 additions & 15 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,13 @@ type Client struct {

ctx context.Context //nolint:containedctx // Context is configured separately.

req *http.Request
resp *http.Response
respBody []byte

attempt int
retryDelays []time.Duration

reqHeaders map[string]string
reqCookies map[string]string
reqQueryParams url.Values
Expand Down Expand Up @@ -114,6 +118,38 @@ func NewClient(baseURL string) *Client {
return c
}

// HTTPValue contains information about request and response.
type HTTPValue struct {
Req *http.Request
ReqBody []byte

Resp *http.Response
RespBody []byte

OtherResp *http.Response
OtherRespBody []byte

Attempt int
RetryDelays []time.Duration
}

// Details returns HTTP request and response information of last run.
func (c *Client) Details() HTTPValue {
return HTTPValue{
Req: c.req,
ReqBody: c.reqBody,

Resp: c.resp,
RespBody: c.respBody,

OtherResp: c.otherResp,
OtherRespBody: c.otherRespBody,

Attempt: c.attempt,
RetryDelays: c.retryDelays,
}
}

// SetBaseURL changes baseURL configured with constructor.
func (c *Client) SetBaseURL(baseURL string) {
if !strings.HasPrefix(baseURL, "http://") && !strings.HasPrefix(baseURL, "https://") {
Expand All @@ -132,6 +168,8 @@ func (c *Client) Reset() *Client {
c.reqQueryParams = map[string][]string{}
c.reqFormDataParams = map[string][]string{}

c.req = nil

c.resp = nil
c.respBody = nil

Expand All @@ -147,6 +185,9 @@ func (c *Client) Reset() *Client {
c.otherRespBody = nil
c.otherRespExpected = false

c.attempt = 0
c.retryDelays = nil

return c
}

Expand All @@ -158,7 +199,7 @@ func (c *Client) Reset() *Client {
// This method enables context-driven concurrent access to shared base Client.
func (c *Client) Fork(ctx context.Context) (context.Context, *Client) {
// Pointer to current Client is used as context key
// to enable multiple different clients in sam context.
// to enable multiple different clients in same context.
if fc, ok := ctx.Value(c).(*Client); ok {
return ctx, fc
}
Expand Down Expand Up @@ -261,7 +302,9 @@ func (c *Client) WithURLEncodedFormDataParam(name, value string) *Client {
return c
}

func (c *Client) do() (err error) {
func (c *Client) do() (err error) { //nolint:funlen
c.attempt++

if c.reqConcurrency < 1 {
c.reqConcurrency = 1
}
Expand Down Expand Up @@ -289,7 +332,7 @@ func (c *Client) do() (err error) {
wg.Done()
}()

resp, er := c.doOnce()
req, resp, er := c.doOnce()
if er != nil {
return
}
Expand All @@ -305,6 +348,10 @@ func (c *Client) do() (err error) {
}

mu.Lock()
if c.req == nil {
c.req = req
}

if _, ok := statusCodeCount[resp.StatusCode]; !ok {
resps[resp.StatusCode] = resp
bodies[resp.StatusCode] = body
Expand All @@ -315,6 +362,7 @@ func (c *Client) do() (err error) {
mu.Unlock()
}()
}

wg.Wait()

if err != nil {
Expand All @@ -329,6 +377,14 @@ func (c *Client) expectResp(check func() error) (err error) {
return check()
}

if len(c.reqBody) == 0 && len(c.reqFormDataParams) > 0 {
c.reqBody = []byte(c.reqFormDataParams.Encode())

if c.reqMethod == "" {
c.reqMethod = http.MethodPost
}
}

if c.retryBackOff != nil {
for {
if err = c.do(); err == nil {
Expand All @@ -343,6 +399,8 @@ func (c *Client) expectResp(check func() error) (err error) {
return err
}

c.retryDelays = append(c.retryDelays, dur)

time.Sleep(dur)
}
}
Expand Down Expand Up @@ -433,15 +491,17 @@ func (c *Client) buildURI() (string, error) {
return uri, nil
}

type readSeekNopCloser struct {
io.ReadSeeker
}

func (r *readSeekNopCloser) Close() error {
return nil
}

func (c *Client) buildBody() io.Reader {
if len(c.reqBody) > 0 {
return bytes.NewBuffer(c.reqBody)
} else if len(c.reqFormDataParams) > 0 {
if c.reqMethod == "" {
c.reqMethod = http.MethodPost
}

return strings.NewReader(c.reqFormDataParams.Encode())
return &readSeekNopCloser{ReadSeeker: bytes.NewReader(c.reqBody)}
}

return nil
Expand Down Expand Up @@ -486,17 +546,17 @@ func (c *Client) applyCookies(req *http.Request) {
}
}

func (c *Client) doOnce() (*http.Response, error) {
func (c *Client) doOnce() (*http.Request, *http.Response, error) {
uri, err := c.buildURI()
if err != nil {
return nil, err
return nil, nil, err
}

body := c.buildBody()

req, err := http.NewRequestWithContext(c.ctx, c.reqMethod, uri, body)
if err != nil {
return nil, err
return nil, nil, err
}

c.applyHeaders(req)
Expand All @@ -513,10 +573,14 @@ func (c *Client) doOnce() (*http.Response, error) {
cl.Transport = tr
cl.Jar = j

return cl.Do(req)
resp, err := cl.Do(req)

return req, resp, err
}

return tr.RoundTrip(req)
resp, err := tr.RoundTrip(req)

return req, resp, err
}

// ExpectResponseStatus sets expected response status code.
Expand Down
11 changes: 11 additions & 0 deletions client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,17 @@ func TestNewClient(t *testing.T) {
val, found := vars.Get("$var1")
assert.True(t, found)
assert.Equal(t, "abc", val)

details := c.Details()
assert.NotNil(t, details.Req)
assert.NotNil(t, details.Resp)
assert.NotNil(t, details.OtherResp)

assert.Equal(t, http.StatusAccepted, details.Resp.StatusCode)
assert.Equal(t, http.StatusConflict, details.OtherResp.StatusCode)

assert.Equal(t, 1, details.Attempt)
assert.Empty(t, details.RetryDelays)
}

func TestNewClient_failedExpectation(t *testing.T) {
Expand Down
6 changes: 3 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@ module github.com/bool64/httpmock
go 1.18

require (
github.com/bool64/dev v0.2.35
github.com/bool64/dev v0.2.36
github.com/bool64/shared v0.1.5
github.com/stretchr/testify v1.8.2
github.com/swaggest/assertjson v1.8.1
github.com/swaggest/assertjson v1.9.0
)

require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/iancoleman/orderedmap v0.2.0 // indirect
github.com/iancoleman/orderedmap v0.3.0 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect
github.com/nxadm/tail v1.4.8 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
Expand Down
12 changes: 6 additions & 6 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
github.com/bool64/dev v0.2.35 h1:M17TLsO/pV2J7PYI/gpe3Ua26ETkzZGb+dC06eoMqlk=
github.com/bool64/dev v0.2.35/go.mod h1:iJbh1y/HkunEPhgebWRNcs8wfGq7sjvJ6W5iabL8ACg=
github.com/bool64/dev v0.2.36 h1:yU3bbOTujoxhWnt8ig8t94PVmZXIkCaRj9C57OtqJBY=
github.com/bool64/dev v0.2.36/go.mod h1:iJbh1y/HkunEPhgebWRNcs8wfGq7sjvJ6W5iabL8ACg=
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/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/iancoleman/orderedmap v0.2.0 h1:sq1N/TFpYH++aViPcaKjys3bDClUEU7s5B+z6jq8pNA=
github.com/iancoleman/orderedmap v0.2.0/go.mod h1:N0Wam8K1arqPXNWjMo21EXnBPOPp36vB07FNRdD2geA=
github.com/iancoleman/orderedmap v0.3.0 h1:5cbR2grmZR/DiVt+VJopEhtVs9YGInGIxAoMJn+Ichc=
github.com/iancoleman/orderedmap v0.3.0/go.mod h1:XuLcCUkdL5owUCQeF2Ue9uuw1EptkJDkXXS7VoV7XGE=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
Expand All @@ -33,8 +33,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.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/swaggest/assertjson v1.8.1 h1:Be2EHY9S2qwKWV+xWZB747Cd7Y79YK6JLdeyrgFvyMo=
github.com/swaggest/assertjson v1.8.1/go.mod h1:/8kNRmDZAZfavS5VeWYtCimLGebn0Ak1/iErFUi+DEM=
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/yosuke-furukawa/json5 v0.1.2-0.20201207051438-cf7bb3f354ff h1:7YqG491bE4vstXRz1lD38rbSgbXnirvROz1lZiOnPO8=
github.com/yosuke-furukawa/json5 v0.1.2-0.20201207051438-cf7bb3f354ff/go.mod h1:sw49aWDqNdRJ6DYUtIQiaA3xyj2IL9tjeNYmX2ixwcU=
github.com/yudai/gojsondiff v1.0.0 h1:27cbfqXLVEJ1o8I6v3y9lg8Ydm53EKqHXAOMxEGlCOA=
Expand Down

0 comments on commit 7149880

Please sign in to comment.