Skip to content

Commit

Permalink
Add response body callback checker (#11)
Browse files Browse the repository at this point in the history
  • Loading branch information
vearutop authored Apr 12, 2023
1 parent ad8f1a6 commit c4f3b08
Show file tree
Hide file tree
Showing 12 changed files with 159 additions and 135 deletions.
21 changes: 21 additions & 0 deletions .github/ISSUE_TEMPLATE/bug_report.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''

---

**Describe the bug**
A clear and concise description of what the bug is.

**To Reproduce**
If feasible/relevant, please provide a code snippet (inline or with Go playground) to reproduce the issue.


**Expected behavior**
A clear and concise description of what you expected to happen.

**Additional context**
Add any other context about the problem here.
10 changes: 10 additions & 0 deletions .github/ISSUE_TEMPLATE/feature_request.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: ''
assignees: ''

---

Please use discussions https://github.com/bool64/httpmock/discussions/categories/ideas to share feature ideas.
10 changes: 10 additions & 0 deletions .github/ISSUE_TEMPLATE/question.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
name: Question
about: Any question about features or usage
title: ''
labels: ''
assignees: ''

---

Please use discussions https://github.com/bool64/httpmock/discussions/categories/q-a to make your question more discoverable by other folks.
11 changes: 5 additions & 6 deletions .github/workflows/cloc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,26 +13,25 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
uses: actions/checkout@v3
with:
path: pr
- name: Checkout base code
uses: actions/checkout@v2
uses: actions/checkout@v3
with:
ref: ${{ github.event.pull_request.base.sha }}
path: base
- name: Count Lines Of Code
- name: Count lines of code
id: loc
run: |
curl -sLO https://github.com/vearutop/sccdiff/releases/download/v1.0.3/linux_amd64.tar.gz && tar xf linux_amd64.tar.gz
sccdiff_hash=$(git hash-object ./sccdiff)
[ "$sccdiff_hash" == "ae8a07b687bd3dba60861584efe724351aa7ff63" ] || (echo "::error::unexpected hash for sccdiff, possible tampering: $sccdiff_hash" && exit 1)
OUTPUT=$(cd pr && ../sccdiff -basedir ../base)
echo "${OUTPUT}"
OUTPUT="${OUTPUT//$'\n'/%0A}"
echo "::set-output name=diff::$OUTPUT"
echo "diff<<EOF" >> $GITHUB_OUTPUT && echo "$OUTPUT" >> $GITHUB_OUTPUT && echo "EOF" >> $GITHUB_OUTPUT
- name: Comment Code Lines
- name: Comment lines of code
continue-on-error: true
uses: marocchino/sticky-pull-request-comment@v2
with:
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/golangci-lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,13 @@ jobs:
steps:
- uses: actions/setup-go@v3
with:
go-version: 1.19.x
go-version: 1.20.x
- uses: actions/checkout@v2
- name: golangci-lint
uses: golangci/golangci-lint-action@v3.2.0
uses: golangci/golangci-lint-action@v3.4.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.50.0
version: v1.51.1

# Optional: working directory, useful for monorepos
# working-directory: somedir
Expand Down
13 changes: 6 additions & 7 deletions .github/workflows/gorelease.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,14 @@ concurrency:
cancel-in-progress: true

env:
GO_VERSION: 1.19.x
GO_VERSION: 1.20.x
jobs:
gorelease:
runs-on: ubuntu-latest
steps:
- name: Install Go stable
if: env.GO_VERSION != 'tip'
uses: actions/setup-go@v3
uses: actions/setup-go@v4
with:
go-version: ${{ env.GO_VERSION }}
- name: Install Go tip
Expand All @@ -29,9 +29,9 @@ jobs:
~/sdk/gotip/bin/go version
echo "PATH=$HOME/go/bin:$HOME/sdk/gotip/bin/:$PATH" >> $GITHUB_ENV
- name: Checkout code
uses: actions/checkout@v2
uses: actions/checkout@v3
- name: Gorelease cache
uses: actions/cache@v2
uses: actions/cache@v3
with:
path: |
~/go/bin/gorelease
Expand All @@ -42,9 +42,8 @@ jobs:
test -e ~/go/bin/gorelease || go install golang.org/x/exp/cmd/gorelease@latest
OUTPUT=$(gorelease 2>&1 || exit 0)
echo "${OUTPUT}"
OUTPUT="${OUTPUT//$'\n'/%0A}"
echo "::set-output name=report::$OUTPUT"
- name: Comment Report
echo "report<<EOF" >> $GITHUB_OUTPUT && echo "$OUTPUT" >> $GITHUB_OUTPUT && echo "EOF" >> $GITHUB_OUTPUT
- name: Comment report
continue-on-error: true
uses: marocchino/sticky-pull-request-comment@v2
with:
Expand Down
22 changes: 10 additions & 12 deletions .github/workflows/test-unit.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,18 @@ concurrency:
env:
GO111MODULE: "on"
RUN_BASE_COVERAGE: "on" # Runs test for PR base in case base test coverage is missing.
COV_GO_VERSION: 1.18.x # Version of Go to collect coverage
COV_GO_VERSION: 1.20.x # Version of Go to collect coverage
TARGET_DELTA_COV: 90 # Target coverage of changed lines, in percents
jobs:
test:
strategy:
matrix:
go-version: [ 1.13.x, 1.14.x, 1.15.x, 1.16.x, 1.17.x, 1.18.x, 1.19.x ]
go-version: [ 1.13.x, 1.19.x, 1.20.x ]
runs-on: ubuntu-latest
steps:
- name: Install Go stable
if: matrix.go-version != 'tip'
uses: actions/setup-go@v3
uses: actions/setup-go@v4
with:
go-version: ${{ matrix.go-version }}

Expand All @@ -41,10 +41,10 @@ jobs:
echo "PATH=$HOME/go/bin:$HOME/sdk/gotip/bin/:$PATH" >> $GITHUB_ENV
- name: Checkout code
uses: actions/checkout@v2
uses: actions/checkout@v3

- name: Go cache
uses: actions/cache@v2
uses: actions/cache@v3
with:
# In order:
# * Module download cache
Expand Down Expand Up @@ -82,7 +82,7 @@ jobs:
go tool cover -func=./unit.coverprofile > unit.txt
TOTAL=$(grep 'total:' unit.txt)
echo "${TOTAL}"
echo "::set-output name=total::$TOTAL"
echo "total=$TOTAL" >> $GITHUB_OUTPUT
- name: Annotate missing test coverage
id: annotate
Expand All @@ -94,16 +94,14 @@ jobs:
git fetch origin master ${{ github.event.pull_request.base.sha }}
REP=$(./gocovdiff -cov unit.coverprofile -gha-annotations gha-unit.txt -delta-cov-file delta-cov-unit.txt -target-delta-cov ${TARGET_DELTA_COV})
echo "${REP}"
REP="${REP//$'\n'/%0A}"
cat gha-unit.txt
DIFF=$(test -e unit-base.txt && ./gocovdiff -func-cov unit.txt -func-base-cov unit-base.txt || echo "Missing base coverage file")
DIFF="${DIFF//$'\n'/%0A}"
TOTAL=$(cat delta-cov-unit.txt)
echo "::set-output name=rep::$REP"
echo "::set-output name=diff::$DIFF"
echo "::set-output name=total::$TOTAL"
echo "rep<<EOF" >> $GITHUB_OUTPUT && echo "$REP" >> $GITHUB_OUTPUT && echo "EOF" >> $GITHUB_OUTPUT
echo "diff<<EOF" >> $GITHUB_OUTPUT && echo "$DIFF" >> $GITHUB_OUTPUT && echo "EOF" >> $GITHUB_OUTPUT
echo "total<<EOF" >> $GITHUB_OUTPUT && echo "$TOTAL" >> $GITHUB_OUTPUT && echo "EOF" >> $GITHUB_OUTPUT
- name: Comment Test Coverage
- name: Comment test coverage
continue-on-error: true
if: matrix.go-version == env.COV_GO_VERSION && github.event.pull_request.base.sha != ''
uses: marocchino/sticky-pull-request-comment@v2
Expand Down
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.50.0" # Optional configuration to pinpoint golangci-lint version.
#GOLANGCI_LINT_VERSION := "v1.51.1" # Optional configuration to pinpoint golangci-lint version.

# The head of Makefile determines location of dev-go to include standard targets.
GO ?= go
Expand Down
87 changes: 68 additions & 19 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -542,9 +542,45 @@ func (c *Client) assertResponseHeader(key, value string, resp *http.Response) er
return c.JSONComparer.FailNotEqual(expected, received)
}

// ExpectResponseBodyCallback sets expectation for response body to be received as JSON payload.
//
// In concurrent mode such response must be met only once or for all calls.
func (c *Client) ExpectResponseBodyCallback(cb func(received []byte) error) error {
if c.resp == nil {
err := c.do()
if err != nil {
return err
}
}

return c.checkBody(nil, c.respBody, cb)
}

// ExpectOtherResponsesBodyCallback sets expectation for response body to be received one or more times during concurrent
// calling.
//
// For example, it may describe "Not Found" response on multiple DELETE or "Conflict" response on multiple POST.
// Does not affect single (non-concurrent) calls.
func (c *Client) ExpectOtherResponsesBodyCallback(cb func(received []byte) error) error {
c.otherRespExpected = true

if c.resp == nil {
err := c.do()
if err != nil {
return err
}
}

if c.otherResp == nil {
return errNoOtherResponses
}

return c.checkBody(nil, c.otherRespBody, cb)
}

// ExpectResponseBody sets expectation for response body to be received.
//
// In concurrent mode such response mush be met only once or for all calls.
// In concurrent mode such response must be met only once or for all calls.
func (c *Client) ExpectResponseBody(body []byte) error {
if c.resp == nil {
err := c.do()
Expand All @@ -553,7 +589,7 @@ func (c *Client) ExpectResponseBody(body []byte) error {
}
}

return c.checkBody(body, c.respBody)
return c.checkBody(body, c.respBody, nil)
}

// ExpectOtherResponsesBody sets expectation for response body to be received one or more times during concurrent
Expand All @@ -575,12 +611,12 @@ func (c *Client) ExpectOtherResponsesBody(body []byte) error {
return errNoOtherResponses
}

return c.checkBody(body, c.otherRespBody)
return c.checkBody(body, c.otherRespBody, nil)
}

func (c *Client) checkBody(expected, received []byte) (err error) {
func (c *Client) checkBody(expected, received []byte, cb func(received []byte) error) (err error) {
if len(received) == 0 {
if len(expected) == 0 {
if len(expected) == 0 && expected != nil {
return nil
}

Expand All @@ -593,28 +629,41 @@ func (c *Client) checkBody(expected, received []byte) (err error) {
}
}()

if json5.Valid(expected) && json5.Valid(received) {
expected, err := json5.Downgrade(expected)
if (expected == nil || json5.Valid(expected)) && json5.Valid(received) {
return c.checkJSONBody(expected, received, cb)
}

if cb != nil {
return cb(received)
}

if !bytes.Equal(expected, received) {
return fmt.Errorf("%w, expected: %q, received: %q",
errUnexpectedBody, string(expected), string(received))
}

return nil
}

func (c *Client) checkJSONBody(expected, received []byte, cb func(received []byte) error) (err error) {
if cb != nil {
err = cb(received)
} else {
expected, err = json5.Downgrade(expected)
if err != nil {
return err
}

err = c.JSONComparer.FailNotEqual(expected, received)
if err != nil {
recCompact, cerr := assertjson.MarshalIndentCompact(json.RawMessage(received), "", " ", 100)
if cerr == nil {
received = recCompact
}
}

return fmt.Errorf("%w\nreceived:\n%s ", err, string(received))
if err != nil {
recCompact, cerr := assertjson.MarshalIndentCompact(json.RawMessage(received), "", " ", 100)
if cerr == nil {
received = recCompact
}

return nil
}

if !bytes.Equal(expected, received) {
return fmt.Errorf("%w, expected: %q, received: %q",
errUnexpectedBody, string(expected), string(received))
return fmt.Errorf("%w\nreceived:\n%s ", err, string(received))
}

return nil
Expand Down
12 changes: 12 additions & 0 deletions client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,9 +77,21 @@ func TestNewClient(t *testing.T) {

assert.NoError(t, c.ExpectResponseStatus(http.StatusAccepted))
assert.NoError(t, c.ExpectResponseBody([]byte(`{"bar":"foo","dyn":"$var1"}`)))
assert.NoError(t, c.ExpectResponseBodyCallback(func(received []byte) error {
return c.JSONComparer.FailMismatch([]byte(`{"bar":"foo"}`), received)
}))
assert.Error(t, c.ExpectResponseBodyCallback(func(received []byte) error {
return c.JSONComparer.FailMismatch([]byte(`{"bar":"foo2"}`), received)
}))
assert.NoError(t, c.ExpectResponseHeader("Content-Type", "application/json"))
assert.NoError(t, c.ExpectOtherResponsesStatus(http.StatusConflict))
assert.NoError(t, c.ExpectOtherResponsesBody([]byte(`{"error":"conflict"}`)))
assert.NoError(t, c.ExpectOtherResponsesBodyCallback(func(received []byte) error {
return c.JSONComparer.FailMismatch([]byte(`{"error":"conflict"}`), received)
}))
assert.Error(t, c.ExpectOtherResponsesBodyCallback(func(received []byte) error {
return c.JSONComparer.FailMismatch([]byte(`{"error":"conflict2"}`), received)
}))
assert.NoError(t, c.ExpectOtherResponsesHeader("Content-Type", "application/json"))
assert.NoError(t, c.CheckUnexpectedOtherResponses())
assert.EqualError(t, c.ExpectNoOtherResponses(), "unexpected response status, expected: 202 (Accepted), received: 409 (Conflict)")
Expand Down
10 changes: 5 additions & 5 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
module github.com/bool64/httpmock

go 1.17
go 1.18

require (
github.com/bool64/dev v0.2.22
github.com/bool64/dev v0.2.27
github.com/bool64/shared v0.1.5
github.com/stretchr/testify v1.8.1
github.com/swaggest/assertjson v1.7.0
github.com/stretchr/testify v1.8.2
github.com/swaggest/assertjson v1.8.0
)

require (
Expand All @@ -15,7 +15,7 @@ require (
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
github.com/sergi/go-diff v1.2.0 // indirect
github.com/sergi/go-diff v1.3.1 // indirect
github.com/yosuke-furukawa/json5 v0.1.2-0.20201207051438-cf7bb3f354ff // indirect
github.com/yudai/gojsondiff v1.0.0 // indirect
github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 // indirect
Expand Down
Loading

0 comments on commit c4f3b08

Please sign in to comment.