pact-go
pact-go copied to clipboard
Provider verifier not working correctly when consumer uses matchers.Decimal
Software versions
- OS: e.g. Mac OS Monterey 12.6.8
- Consumer Pact library: pact-go v2.0.1
- Provider Pact library: pact-go v2.0.1
- Golang Version: Go v1.20
-
Golang environment: Provide output of
go env
GO111MODULE=""
GOARCH="arm64"
GOBIN=""
GOCACHE="/Users/some.user/Library/Caches/go-build"
GOENV="/Users/some.user/Library/Application Support/go/env"
GOEXE=""
GOEXPERIMENT=""
GOFLAGS=""
GOHOSTARCH="arm64"
GOHOSTOS="darwin"
GOINSECURE=""
GOMODCACHE="/Users/some.user/go/pkg/mod"
GOOS="darwin"
GOPATH="/Users/some.user/go"
GOPROXY="https://proxy.golang.org,direct"
GOROOT="/Users/some.user/go/go1.20.6"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="/Users/some.user/go/go1.20.6/pkg/tool/darwin_arm64"
GOVCS=""
GOVERSION="go1.20.6"
GCCGO="gccgo"
AR="ar"
CC="clang"
CXX="clang++"
CGO_ENABLED="1"
GOMOD="/dev/null"
GOWORK=""
CGO_CFLAGS="-O2 -g"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-O2 -g"
CGO_FFLAGS="-O2 -g"
CGO_LDFLAGS="-O2 -g"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -arch arm64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=/var/folders/03/m_znf89s593bpkdgwytb9tzjdh18pw/T/go-build578168331=/tmp/go-build -gno-record-gcc-switches -fno-common"
Expected behaviour
5.0
should be a valid decimal value when running provider verifier
Actual behaviour
5.0
is not considered a valid decimal value when running provider verifier
Steps to reproduce
- Create package
consumer
and its test.
// this is consumer.go
package consumer
import (
"encoding/json"
"fmt"
"io"
"net/http"
)
type ProductConsumer struct {
host string
}
func NewProductConsumer(host string) *ProductConsumer {
return &ProductConsumer{
host: host,
}
}
func (c *ProductConsumer) GetProduct(id int) (*Product, error) {
url := fmt.Sprintf("%s/products/%d", c.host, id)
resp, err := http.DefaultClient.Get(url)
if err != nil {
return nil, err
}
defer resp.Body.Close()
b, _ := io.ReadAll(resp.Body)
var p Product
_ = json.Unmarshal(b, &p)
return &p, nil
}
type Product struct {
ID int `json:"id"`
Price float64 `json:"price"`
}
// this is consumer_test.go
package consumer
import (
"fmt"
"testing"
"github.com/pact-foundation/pact-go/v2/consumer"
"github.com/pact-foundation/pact-go/v2/matchers"
"github.com/stretchr/testify/assert"
)
func TestProductConsumer(t *testing.T) {
mockProvider, err := consumer.NewV4Pact(consumer.MockHTTPProviderConfig{
Consumer: "product-consumer",
Provider: "product-provider",
Port: 8089,
})
err = mockProvider.
AddInteraction().
Given("product #1 exists").
UponReceiving("a request to get product #1").
WithRequest("GET", "/products/1").
WillRespondWith(200, func(b *consumer.V4ResponseBuilder) {
b.JSONBody(matchers.Map{
"id": matchers.Integer(1),
"price": matchers.Decimal(5.0),
})
}).
ExecuteTest(t, func(config consumer.MockServerConfig) error {
c := NewProductConsumer(fmt.Sprintf("http://%s:%d", config.Host, config.Port))
p, err := c.GetProduct(1)
assert.NoError(t, err)
assert.Equal(t, 1, p.ID)
return nil
})
assert.NoError(t, err)
}
- Run the test, get the output of the pact
{
"consumer": {
"name": "product-consumer"
},
"interactions": [
{
"description": "a request to get product #1",
"pending": false,
"providerStates": [
{
"name": "product #1 exists"
}
],
"request": {
"method": "GET",
"path": "/products/1"
},
"response": {
"body": {
"content": {
"id": 1,
"price": 5
},
"contentType": "application/json",
"encoded": false
},
"headers": {
"Content-Type": [
"application/json"
]
},
"matchingRules": {
"body": {
"$.id": {
"combine": "AND",
"matchers": [
{
"match": "integer"
}
]
},
"$.price": {
"combine": "AND",
"matchers": [
{
"match": "decimal"
}
]
}
}
},
"status": 200
},
"transport": "http",
"type": "Synchronous/HTTP"
}
],
"metadata": {
"pactRust": {
"ffi": "0.4.5",
"mockserver": "1.1.1",
"models": "1.1.2"
},
"pactSpecification": {
"version": "4.0"
}
},
"provider": {
"name": "product-provider"
}
}
- Create a provider test
package productprovider
import (
"encoding/json"
"net/http"
"testing"
"github.com/pact-foundation/pact-go/v2/models"
"github.com/pact-foundation/pact-go/v2/provider"
"github.com/stretchr/testify/assert"
)
func startServer() {
type product struct {
ID int `json:"id"`
Price float64 `json:"price"`
}
http.HandleFunc("/products/1", func(writer http.ResponseWriter, request *http.Request) {
d := product{
ID: 1,
Price: 5.0,
}
b, _ := json.Marshal(d)
writer.Header().Set("Content-Type", "application/json")
writer.Write(b)
})
http.ListenAndServe("localhost:8080", nil)
}
func TestProductProvider(t *testing.T) {
go startServer()
v := provider.NewVerifier()
err := v.VerifyProvider(t, provider.VerifyRequest{
ProviderBaseURL: "http://localhost:8080",
Provider: "product-provider",
ProviderVersion: "product-provider-v1.0",
PactDirs: []string{"/path/to/consumer/pacts"},
StateHandlers: models.StateHandlers{
"product #1 exists": func(setup bool, state models.ProviderState) (models.ProviderStateResponse, error) {
return models.ProviderStateResponse{}, nil
},
},
})
assert.NoError(t, err)
}
- Run the provider test, it fails with this log
=== RUN TestProductProvider
2023-08-05T14:11:11.107350Z INFO ThreadId(11) pact_verifier: Running setup provider state change handler 'product #1 exists' for 'a request to get product #1'
2023/08/05 21:11:11 [INFO] executing state handler middleware
2023-08-05T14:11:11.287108Z INFO ThreadId(11) pact_verifier: Running provider verification for 'a request to get product #1'
2023-08-05T14:11:11.287166Z INFO ThreadId(11) pact_verifier::provider_client: Sending request to provider at http://localhost:52872/
2023-08-05T14:11:11.287168Z INFO ThreadId(11) pact_verifier::provider_client: Sending request HTTP Request ( method: GET, path: /products/1, query: None, headers: None, body: Missing )
2023-08-05T14:11:11.288802Z INFO ThreadId(11) pact_verifier::provider_client: Received response: HTTP Response ( status: 200, headers: Some({"content-length": ["18"], "content-type": ["application/json"], "date": ["Sat, 05 Aug 2023 14:11:11 GMT"]}), body: Present(18 bytes, application/json) )
2023-08-05T14:11:11.288821Z INFO ThreadId(11) pact_matching: comparing to expected response: HTTP Response ( status: 200, headers: Some({"Content-Type": ["application/json"]}), body: Present(18 bytes, application/json) )
2023-08-05T14:11:11.290069Z INFO ThreadId(11) pact_verifier: Running teardown provider state change handler 'product #1 exists' for 'a request to get product #1'
2023/08/05 21:11:11 [INFO] executing state handler middleware
Verifying a pact between product-consumer and product-provider
a request to get product #1 (0s loading, 522ms verification)
Given product #1 exists
returns a response which
has status code 200 (OK)
includes headers
"Content-Type" with value "application/json" (OK)
has a matching body (FAILED)
Failures:
1) Verifying a pact between product-consumer and product-provider Given product #1 exists - a request to get product #1
1.1) has a matching body
$.price -> Expected '5' to be a decimal value
There were 1 pact failures
productprovider_test.go:52:
Error Trace: /Users/some.user/path/to/productprovider/productprovider_test.go:52
Error: Received unexpected error:
the verifier failed to successfully verify the pacts, this indicates an issue with the provider API
Test: TestProductProvider
--- FAIL: TestProductProvider (0.58s)
=== RUN TestProductProvider/Provider_pact_verification
verifier.go:184: the verifier failed to successfully verify the pacts, this indicates an issue with the provider API
--- FAIL: TestProductProvider/Provider_pact_verification (0.00s)
FAIL
Process finished with the exit code 1
Relevent log files
N/A
Please note that if I change the dummy value in provider server from 5.0
to 5.1
then the provider verifier test works.