Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature Request: Support UUID fields when using Match #179

Open
brento1 opened this issue Sep 27, 2021 · 2 comments
Open

Feature Request: Support UUID fields when using Match #179

brento1 opened this issue Sep 27, 2021 · 2 comments

Comments

@brento1
Copy link

brento1 commented Sep 27, 2021

Software versions

  • OS: e.g. Mac OSX 11.6
  • Consumer Pact library: Pact go v1.6.4
  • Provider Pact library: Pact go v1.6.4
  • Golang Version: go1.14.2 darwin/amd64
  • Golang environment:
GO111MODULE="on"
GOARCH="amd64"
GOBIN=""
GOCACHE="/Users/admin/Library/Caches/go-build"
GOENV="/Users/admin/Library/Application Support/go/env"
GOEXE=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="darwin"
GOINSECURE=""
GONOPROXY=""
GONOSUMDB=""
GOOS="darwin"
GOPATH="/Users/admin/dev/go"
GOPRIVATE=""
GOPROXY="direct"
GOROOT="/usr/local/go"
GOSUMDB="off"
GOTMPDIR=""
GOTOOLDIR="/usr/local/go/pkg/tool/darwin_amd64"
GCCGO="gccgo"
AR="ar"
CC="clang"
CXX="clang++"
CGO_ENABLED="1"
GOMOD="/Users/admin/dev/my-project/go.mod"
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -m64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=/var/folders/q8/g6cp7z_93q18l9_6gbbz798m0000gp/T/go-build038959026=/tmp/go-build -gno-record-gcc-switches -fno-common"

Expected behaviour

When generating a Pact from a Golang Struct that contains a UUID field, I expect the resulting Pact to contain a String version of the UUID.

Actual behaviour

The resulting Pact contains an array of bytes, which is the UUID fields underlying representation.

Steps to reproduce

The following code is an example on how to reproduce, and the resulting Pact file is below (note the body - ID field)

import (
	"fmt"
	"github.com/google/uuid"
	"github.com/pact-foundation/pact-go/dsl"
	"log"
	"net/http"
	"testing"
)

type Foo struct {
	ID          uuid.UUID `json:"id"`
	Name        string    `json:"name"`
	Description string    `json:"description"`
}

func TestGet(t *testing.T) {
	// Create Pact connecting to local Daemon
	pact := &dsl.Pact{
		Consumer: "my-consumer",
		Provider: "my-provider",
		Host:     "localhost",
	}
	defer pact.Teardown()

	// Pass in test case. This is the component that makes the external HTTP call
	var test = func() (err error) {
		u := fmt.Sprintf("http://localhost:%d/foobar", pact.Server.Port)
		req, err := http.NewRequest("GET", u, nil)
		if err != nil {
			return
		}

		// NOTE: by default, request bodies are expected to be sent with a Content-Type
		// of application/json. If you don't explicitly set the content-type, you
		// will get a mismatch during Verification.
		req.Header.Set("Content-Type", "application/json")

		_, err = http.DefaultClient.Do(req)
		return
	}

	// Set up our expected interactions.
	pact.
		AddInteraction().
		Given("Data exists").
		UponReceiving("A request to get").
		WithRequest(dsl.Request{
			Method:  "get",
			Path:    dsl.String("/foobar"),
			Headers: dsl.MapMatcher{"Content-Type": dsl.String("application/json")},
		}).
		WillRespondWith(dsl.Response{
			Status:  200,
			Headers: dsl.MapMatcher{"Content-Type": dsl.String("application/json")},
			Body:    dsl.Match(&Foo{}),
		})

	// Run the test, verify it did what we expected and capture the contract
	if err := pact.Verify(test); err != nil {
		log.Fatalf("Error on Verify: %v", err)
	}

}
{
  "consumer": {
    "name": "my-consumer"
  },
  "provider": {
    "name": "my-provider"
  },
  "interactions": [
    {
      "description": "A request to get all ",
      "providerState": "Data exists",
      "request": {
        "method": "get",
        "path": "/foobar",
        "headers": {
          "Content-Type": "application/json"
        }
      },
      "response": {
        "status": 200,
        "headers": {
          "Content-Type": "application/json"
        },
        "body": {
          "description": "string",
          "id": [
            1
          ],
          "name": "string"
        },
        "matchingRules": {
          "$.body.description": {
            "match": "type"
          },
          "$.body.id": {
            "min": 1
          },
          "$.body.id[*].*": {
            "match": "type"
          },
          "$.body.id[*]": {
            "match": "type"
          },
          "$.body.name": {
            "match": "type"
          }
        }
      }
    }
  ],
  "metadata": {
    "pactSpecification": {
      "version": "2.0.0"
    }
  }
}
@mefellows
Copy link
Member

Thanks. I don't think it's appropriate to support the UUID directly, but I think the ability to override the default serialisation for non primitives would be better e.g.

type Foo struct {
	ID          uuid.UUID `json:"id", pact:"match=type,example=27cacc3f-a6ca-4b6d-98f9-c9f7e4e78c07`
	Name        string    `json:"name"`
	Description string    `json:"description"`
}

Would that work?

In the meantime, you can just fall back to standard matchers, e.g. something like the below:

...
		Body: map[string]interface{}{
			"uuid":   dsl.Like("27cacc3f-a6ca-4b6d-98f9-c9f7e4e78c07"),
			"name": dsl.Like("Baz"),
			"description": dsl.Like("some user called Baz"),
                 }

@brento1
Copy link
Author

brento1 commented Sep 27, 2021

@mefellows Yep totally fine with that option, makes it usable for other non-primitive types that people might be deserializing to.
Ideally just want something where I can re-use the same struct in both my Pact tests and my core code, but will use the workaround in the meantime.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants