Skip to content

Commit c4f3b08

Browse files
authored
Add response body callback checker (#11)
1 parent ad8f1a6 commit c4f3b08

File tree

12 files changed

+159
-135
lines changed

12 files changed

+159
-135
lines changed
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
---
2+
name: Bug report
3+
about: Create a report to help us improve
4+
title: ''
5+
labels: ''
6+
assignees: ''
7+
8+
---
9+
10+
**Describe the bug**
11+
A clear and concise description of what the bug is.
12+
13+
**To Reproduce**
14+
If feasible/relevant, please provide a code snippet (inline or with Go playground) to reproduce the issue.
15+
16+
17+
**Expected behavior**
18+
A clear and concise description of what you expected to happen.
19+
20+
**Additional context**
21+
Add any other context about the problem here.
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
name: Feature request
3+
about: Suggest an idea for this project
4+
title: ''
5+
labels: ''
6+
assignees: ''
7+
8+
---
9+
10+
Please use discussions https://github.com/bool64/httpmock/discussions/categories/ideas to share feature ideas.

.github/ISSUE_TEMPLATE/question.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
name: Question
3+
about: Any question about features or usage
4+
title: ''
5+
labels: ''
6+
assignees: ''
7+
8+
---
9+
10+
Please use discussions https://github.com/bool64/httpmock/discussions/categories/q-a to make your question more discoverable by other folks.

.github/workflows/cloc.yml

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,26 +13,25 @@ jobs:
1313
runs-on: ubuntu-latest
1414
steps:
1515
- name: Checkout code
16-
uses: actions/checkout@v2
16+
uses: actions/checkout@v3
1717
with:
1818
path: pr
1919
- name: Checkout base code
20-
uses: actions/checkout@v2
20+
uses: actions/checkout@v3
2121
with:
2222
ref: ${{ github.event.pull_request.base.sha }}
2323
path: base
24-
- name: Count Lines Of Code
24+
- name: Count lines of code
2525
id: loc
2626
run: |
2727
curl -sLO https://github.com/vearutop/sccdiff/releases/download/v1.0.3/linux_amd64.tar.gz && tar xf linux_amd64.tar.gz
2828
sccdiff_hash=$(git hash-object ./sccdiff)
2929
[ "$sccdiff_hash" == "ae8a07b687bd3dba60861584efe724351aa7ff63" ] || (echo "::error::unexpected hash for sccdiff, possible tampering: $sccdiff_hash" && exit 1)
3030
OUTPUT=$(cd pr && ../sccdiff -basedir ../base)
3131
echo "${OUTPUT}"
32-
OUTPUT="${OUTPUT//$'\n'/%0A}"
33-
echo "::set-output name=diff::$OUTPUT"
32+
echo "diff<<EOF" >> $GITHUB_OUTPUT && echo "$OUTPUT" >> $GITHUB_OUTPUT && echo "EOF" >> $GITHUB_OUTPUT
3433
35-
- name: Comment Code Lines
34+
- name: Comment lines of code
3635
continue-on-error: true
3736
uses: marocchino/sticky-pull-request-comment@v2
3837
with:

.github/workflows/golangci-lint.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,13 @@ jobs:
2121
steps:
2222
- uses: actions/setup-go@v3
2323
with:
24-
go-version: 1.19.x
24+
go-version: 1.20.x
2525
- uses: actions/checkout@v2
2626
- name: golangci-lint
27-
uses: golangci/golangci-lint-action@v3.2.0
27+
uses: golangci/golangci-lint-action@v3.4.0
2828
with:
2929
# Required: the version of golangci-lint is required and must be specified without patch version: we always use the latest patch version.
30-
version: v1.50.0
30+
version: v1.51.1
3131

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

.github/workflows/gorelease.yml

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,14 @@ concurrency:
99
cancel-in-progress: true
1010

1111
env:
12-
GO_VERSION: 1.19.x
12+
GO_VERSION: 1.20.x
1313
jobs:
1414
gorelease:
1515
runs-on: ubuntu-latest
1616
steps:
1717
- name: Install Go stable
1818
if: env.GO_VERSION != 'tip'
19-
uses: actions/setup-go@v3
19+
uses: actions/setup-go@v4
2020
with:
2121
go-version: ${{ env.GO_VERSION }}
2222
- name: Install Go tip
@@ -29,9 +29,9 @@ jobs:
2929
~/sdk/gotip/bin/go version
3030
echo "PATH=$HOME/go/bin:$HOME/sdk/gotip/bin/:$PATH" >> $GITHUB_ENV
3131
- name: Checkout code
32-
uses: actions/checkout@v2
32+
uses: actions/checkout@v3
3333
- name: Gorelease cache
34-
uses: actions/cache@v2
34+
uses: actions/cache@v3
3535
with:
3636
path: |
3737
~/go/bin/gorelease
@@ -42,9 +42,8 @@ jobs:
4242
test -e ~/go/bin/gorelease || go install golang.org/x/exp/cmd/gorelease@latest
4343
OUTPUT=$(gorelease 2>&1 || exit 0)
4444
echo "${OUTPUT}"
45-
OUTPUT="${OUTPUT//$'\n'/%0A}"
46-
echo "::set-output name=report::$OUTPUT"
47-
- name: Comment Report
45+
echo "report<<EOF" >> $GITHUB_OUTPUT && echo "$OUTPUT" >> $GITHUB_OUTPUT && echo "EOF" >> $GITHUB_OUTPUT
46+
- name: Comment report
4847
continue-on-error: true
4948
uses: marocchino/sticky-pull-request-comment@v2
5049
with:

.github/workflows/test-unit.yml

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -15,18 +15,18 @@ concurrency:
1515
env:
1616
GO111MODULE: "on"
1717
RUN_BASE_COVERAGE: "on" # Runs test for PR base in case base test coverage is missing.
18-
COV_GO_VERSION: 1.18.x # Version of Go to collect coverage
18+
COV_GO_VERSION: 1.20.x # Version of Go to collect coverage
1919
TARGET_DELTA_COV: 90 # Target coverage of changed lines, in percents
2020
jobs:
2121
test:
2222
strategy:
2323
matrix:
24-
go-version: [ 1.13.x, 1.14.x, 1.15.x, 1.16.x, 1.17.x, 1.18.x, 1.19.x ]
24+
go-version: [ 1.13.x, 1.19.x, 1.20.x ]
2525
runs-on: ubuntu-latest
2626
steps:
2727
- name: Install Go stable
2828
if: matrix.go-version != 'tip'
29-
uses: actions/setup-go@v3
29+
uses: actions/setup-go@v4
3030
with:
3131
go-version: ${{ matrix.go-version }}
3232

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

4646
- name: Go cache
47-
uses: actions/cache@v2
47+
uses: actions/cache@v3
4848
with:
4949
# In order:
5050
# * Module download cache
@@ -82,7 +82,7 @@ jobs:
8282
go tool cover -func=./unit.coverprofile > unit.txt
8383
TOTAL=$(grep 'total:' unit.txt)
8484
echo "${TOTAL}"
85-
echo "::set-output name=total::$TOTAL"
85+
echo "total=$TOTAL" >> $GITHUB_OUTPUT
8686
8787
- name: Annotate missing test coverage
8888
id: annotate
@@ -94,16 +94,14 @@ jobs:
9494
git fetch origin master ${{ github.event.pull_request.base.sha }}
9595
REP=$(./gocovdiff -cov unit.coverprofile -gha-annotations gha-unit.txt -delta-cov-file delta-cov-unit.txt -target-delta-cov ${TARGET_DELTA_COV})
9696
echo "${REP}"
97-
REP="${REP//$'\n'/%0A}"
9897
cat gha-unit.txt
9998
DIFF=$(test -e unit-base.txt && ./gocovdiff -func-cov unit.txt -func-base-cov unit-base.txt || echo "Missing base coverage file")
100-
DIFF="${DIFF//$'\n'/%0A}"
10199
TOTAL=$(cat delta-cov-unit.txt)
102-
echo "::set-output name=rep::$REP"
103-
echo "::set-output name=diff::$DIFF"
104-
echo "::set-output name=total::$TOTAL"
100+
echo "rep<<EOF" >> $GITHUB_OUTPUT && echo "$REP" >> $GITHUB_OUTPUT && echo "EOF" >> $GITHUB_OUTPUT
101+
echo "diff<<EOF" >> $GITHUB_OUTPUT && echo "$DIFF" >> $GITHUB_OUTPUT && echo "EOF" >> $GITHUB_OUTPUT
102+
echo "total<<EOF" >> $GITHUB_OUTPUT && echo "$TOTAL" >> $GITHUB_OUTPUT && echo "EOF" >> $GITHUB_OUTPUT
105103
106-
- name: Comment Test Coverage
104+
- name: Comment test coverage
107105
continue-on-error: true
108106
if: matrix.go-version == env.COV_GO_VERSION && github.event.pull_request.base.sha != ''
109107
uses: marocchino/sticky-pull-request-comment@v2

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#GOLANGCI_LINT_VERSION := "v1.50.0" # Optional configuration to pinpoint golangci-lint version.
1+
#GOLANGCI_LINT_VERSION := "v1.51.1" # Optional configuration to pinpoint golangci-lint version.
22

33
# The head of Makefile determines location of dev-go to include standard targets.
44
GO ?= go

client.go

Lines changed: 68 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -542,9 +542,45 @@ func (c *Client) assertResponseHeader(key, value string, resp *http.Response) er
542542
return c.JSONComparer.FailNotEqual(expected, received)
543543
}
544544

545+
// ExpectResponseBodyCallback sets expectation for response body to be received as JSON payload.
546+
//
547+
// In concurrent mode such response must be met only once or for all calls.
548+
func (c *Client) ExpectResponseBodyCallback(cb func(received []byte) error) error {
549+
if c.resp == nil {
550+
err := c.do()
551+
if err != nil {
552+
return err
553+
}
554+
}
555+
556+
return c.checkBody(nil, c.respBody, cb)
557+
}
558+
559+
// ExpectOtherResponsesBodyCallback sets expectation for response body to be received one or more times during concurrent
560+
// calling.
561+
//
562+
// For example, it may describe "Not Found" response on multiple DELETE or "Conflict" response on multiple POST.
563+
// Does not affect single (non-concurrent) calls.
564+
func (c *Client) ExpectOtherResponsesBodyCallback(cb func(received []byte) error) error {
565+
c.otherRespExpected = true
566+
567+
if c.resp == nil {
568+
err := c.do()
569+
if err != nil {
570+
return err
571+
}
572+
}
573+
574+
if c.otherResp == nil {
575+
return errNoOtherResponses
576+
}
577+
578+
return c.checkBody(nil, c.otherRespBody, cb)
579+
}
580+
545581
// ExpectResponseBody sets expectation for response body to be received.
546582
//
547-
// In concurrent mode such response mush be met only once or for all calls.
583+
// In concurrent mode such response must be met only once or for all calls.
548584
func (c *Client) ExpectResponseBody(body []byte) error {
549585
if c.resp == nil {
550586
err := c.do()
@@ -553,7 +589,7 @@ func (c *Client) ExpectResponseBody(body []byte) error {
553589
}
554590
}
555591

556-
return c.checkBody(body, c.respBody)
592+
return c.checkBody(body, c.respBody, nil)
557593
}
558594

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

578-
return c.checkBody(body, c.otherRespBody)
614+
return c.checkBody(body, c.otherRespBody, nil)
579615
}
580616

581-
func (c *Client) checkBody(expected, received []byte) (err error) {
617+
func (c *Client) checkBody(expected, received []byte, cb func(received []byte) error) (err error) {
582618
if len(received) == 0 {
583-
if len(expected) == 0 {
619+
if len(expected) == 0 && expected != nil {
584620
return nil
585621
}
586622

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

596-
if json5.Valid(expected) && json5.Valid(received) {
597-
expected, err := json5.Downgrade(expected)
632+
if (expected == nil || json5.Valid(expected)) && json5.Valid(received) {
633+
return c.checkJSONBody(expected, received, cb)
634+
}
635+
636+
if cb != nil {
637+
return cb(received)
638+
}
639+
640+
if !bytes.Equal(expected, received) {
641+
return fmt.Errorf("%w, expected: %q, received: %q",
642+
errUnexpectedBody, string(expected), string(received))
643+
}
644+
645+
return nil
646+
}
647+
648+
func (c *Client) checkJSONBody(expected, received []byte, cb func(received []byte) error) (err error) {
649+
if cb != nil {
650+
err = cb(received)
651+
} else {
652+
expected, err = json5.Downgrade(expected)
598653
if err != nil {
599654
return err
600655
}
601656

602657
err = c.JSONComparer.FailNotEqual(expected, received)
603-
if err != nil {
604-
recCompact, cerr := assertjson.MarshalIndentCompact(json.RawMessage(received), "", " ", 100)
605-
if cerr == nil {
606-
received = recCompact
607-
}
658+
}
608659

609-
return fmt.Errorf("%w\nreceived:\n%s ", err, string(received))
660+
if err != nil {
661+
recCompact, cerr := assertjson.MarshalIndentCompact(json.RawMessage(received), "", " ", 100)
662+
if cerr == nil {
663+
received = recCompact
610664
}
611665

612-
return nil
613-
}
614-
615-
if !bytes.Equal(expected, received) {
616-
return fmt.Errorf("%w, expected: %q, received: %q",
617-
errUnexpectedBody, string(expected), string(received))
666+
return fmt.Errorf("%w\nreceived:\n%s ", err, string(received))
618667
}
619668

620669
return nil

client_test.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,9 +77,21 @@ func TestNewClient(t *testing.T) {
7777

7878
assert.NoError(t, c.ExpectResponseStatus(http.StatusAccepted))
7979
assert.NoError(t, c.ExpectResponseBody([]byte(`{"bar":"foo","dyn":"$var1"}`)))
80+
assert.NoError(t, c.ExpectResponseBodyCallback(func(received []byte) error {
81+
return c.JSONComparer.FailMismatch([]byte(`{"bar":"foo"}`), received)
82+
}))
83+
assert.Error(t, c.ExpectResponseBodyCallback(func(received []byte) error {
84+
return c.JSONComparer.FailMismatch([]byte(`{"bar":"foo2"}`), received)
85+
}))
8086
assert.NoError(t, c.ExpectResponseHeader("Content-Type", "application/json"))
8187
assert.NoError(t, c.ExpectOtherResponsesStatus(http.StatusConflict))
8288
assert.NoError(t, c.ExpectOtherResponsesBody([]byte(`{"error":"conflict"}`)))
89+
assert.NoError(t, c.ExpectOtherResponsesBodyCallback(func(received []byte) error {
90+
return c.JSONComparer.FailMismatch([]byte(`{"error":"conflict"}`), received)
91+
}))
92+
assert.Error(t, c.ExpectOtherResponsesBodyCallback(func(received []byte) error {
93+
return c.JSONComparer.FailMismatch([]byte(`{"error":"conflict2"}`), received)
94+
}))
8395
assert.NoError(t, c.ExpectOtherResponsesHeader("Content-Type", "application/json"))
8496
assert.NoError(t, c.CheckUnexpectedOtherResponses())
8597
assert.EqualError(t, c.ExpectNoOtherResponses(), "unexpected response status, expected: 202 (Accepted), received: 409 (Conflict)")

0 commit comments

Comments
 (0)