Skip to content

Wrong response or 404 with XML body matcher and concurrency #1796

Open
@benjamin-ratier

Description

@benjamin-ratier

MockServer version

5.15.0

To Reproduce

  • Declare 2 endpoints, with a XML body matcher, and two different bodies/responses
  • Call these endpoints concurrently
  • Sometimes, the correct response is returned for the call. Sometimes, the other response is returned, sometimes we get a 404

Expected behaviour

Always have the expected response.

Other info

It looks like the issue has already been encountered, but without enough info: #901

It only happens with XML body matcher. Other matchers seem OK with concurrency.

Reproduction test

package testing

import (
	"bytes"
	"fmt"
	"net/http"
	"sync"
	"testing"
	"time"

	"github.com/go-resty/resty/v2"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

type RequestMatcher struct {
	Method string      `json:"method,omitempty"`
	Path   string      `json:"path,omitempty"`
	Body   BodyMatcher `json:"body,omitempty"`
}

type BodyMatcher struct {
	Type string `json:"type,omitempty"`
	XML  string `json:"xml,omitempty"`
}

type Expectation struct {
	Request  RequestMatcher `json:"httpRequest,omitempty"`
	Response Response       `json:"httpResponse,omitempty"`
	Priority int32          `json:"priority,omitempty"`
}

type Response struct {
	Body       string              `json:"body,omitempty"`
	Headers    map[string][]string `json:"headers,omitempty"`
	StatusCode int32               `json:"statusCode,omitempty"`
}

func resetExpectations(t *testing.T) {
	t.Helper()

	c := resty.New().SetBaseURL(fmt.Sprintf("http://%s:%d", "mockserver", 46311))
	resp, err := c.NewRequest().
		Put("mockserver/reset")
	assert.NoError(t, err, "")
	assert.Equal(t, http.StatusOK, resp.StatusCode())
}

func createExpectations(t *testing.T) {
	t.Helper()

	c := resty.New().SetBaseURL(fmt.Sprintf("http://%s:%d", "mockserver", 46311))
	c.NewRequest().
		SetDoNotParseResponse(true).
		SetBody(Expectation{
			Request: RequestMatcher{
				Method: "POST",
				Path:   "/Test",
				Body: BodyMatcher{
					Type: "XML",
					XML:  "<a>1</a>",
				},
			},
			Response: Response{
				Body: "<r>1</r>",
			},
		}).Put("/mockserver/expectation")
	c.NewRequest().
		SetDoNotParseResponse(true).
		SetBody(Expectation{
			Request: RequestMatcher{
				Method: "POST",
				Path:   "/Test",
				Body: BodyMatcher{
					Type: "XML",
					XML:  "<a>2</a>",
				},
			},
			Response: Response{
				Body: "<r>2</r>",
			},
		}).Put("/mockserver/expectation")
}

func TestCall(t *testing.T) {
	resetExpectations(t)
	createExpectations(t)

	c := resty.New().SetBaseURL(fmt.Sprintf("http://%s:%d", "mockserver", 46311)).SetDoNotParseResponse(true)

	call := func(source string) {

		resp, err := c.NewRequest().SetBody("<a>1</a>").Post("/Test")
		require.NoError(t, err)

		buf := new(bytes.Buffer)
		buf.ReadFrom(resp.RawResponse.Body)

		assert.Equal(t, "<r>1</r>", buf.String(), source)

		// ===========

		resp, err = c.NewRequest().SetBody("<a>2</a>").Post("/Test")
		require.NoError(t, err)

		buf = new(bytes.Buffer)
		buf.ReadFrom(resp.RawResponse.Body)

		assert.Equal(t, "<r>2</r>", buf.String(), source)
	}

	iterations := 10

	// This loop won't fail
	for i := 0; i < iterations; i++ {
		call("sequential")
	}

	var wg sync.WaitGroup
	wg.Add(iterations)

	// This loop will fail
	for i := 0; i < iterations; i++ {
		time.Sleep(10 * time.Millisecond)

		go func() {
			call("parallel")
			wg.Done()
		}()
	}

	wg.Wait()
}

Result and log file

I joined the Go test output, and the mockserver logs for iterations := 5 (we have a 404 and a confusion)

log.txt
test_output.txt

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions