Skip to content

Commit

Permalink
feat(wip): migrate to FFI bindings for consumer interface (removes co…
Browse files Browse the repository at this point in the history
…nsumer message pact support)
  • Loading branch information
mefellows committed May 16, 2021
1 parent c872020 commit 0fc423a
Show file tree
Hide file tree
Showing 20 changed files with 868 additions and 2,667 deletions.
110 changes: 57 additions & 53 deletions examples/v3/consumer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,22 +49,25 @@ func TestConsumerV2(t *testing.T) {
WithRequest(v3.Request{
Method: "POST",
Path: v3.Regex("/foobar", `\/foo.*`),
Headers: v3.MapMatcher{"Content-Type": s("application/json"), "Authorization": s("Bearer 1234")},
Headers: v3.MapMatcher{"Content-Type": s("application/json"), "Authorization": v3.Like("Bearer 1234")},
Query: v3.QueryMatcher{
"baz": []interface{}{
"baz": []v3.Matcher{
v3.Regex("bar", "[a-z]+"),
v3.Regex("bat", "[a-z]+"),
v3.Regex("baz", "[a-z]+"),
},
},
// Body: v3.MapMatcher{
// "name": s("billy"),
// "id": v3.Like(27),
// "name": v3.Like("billy"),
// "datetime": v3.Like("2020-01-01'T'08:00:45"),
// "lastName": v3.Like("billy"),
// },
Body: v3.MatchV2(&User{}),
}).
WillRespondWith(v3.Response{
Status: 200,
Headers: v3.MapMatcher{"Content-Type": s("application/json")},
Headers: v3.MapMatcher{"Content-Type": v3.Regex("application/json", "application\\/json")},
// Body: v3.Match(&User{}),
Body: v3.MapMatcher{
"dateTime": v3.Regex("2020-01-01", "[0-9\\-]+"),
Expand Down Expand Up @@ -121,9 +124,10 @@ func TestConsumerV3(t *testing.T) {
// },

// Alternative use MatchV3
Body: v3.MatchV3(&User{}),
// Body: v3.MatchV3(&User{}),
Body: v3.MatchV2(&User{}),
Query: v3.QueryMatcher{
"baz": []interface{}{
"baz": []v3.Matcher{
v3.Regex("bar", "[a-z]+"),
v3.Regex("bat", "[a-z]+"),
v3.Regex("baz", "[a-z]+"),
Expand All @@ -135,15 +139,15 @@ func TestConsumerV3(t *testing.T) {
Headers: v3.MapMatcher{"Content-Type": s("application/json")},
// Body: v3.MatchV3(&User{}),
Body: v3.MapMatcher{
"dateTime": v3.Regex("2020-01-01", "[0-9\\-]+"),
"name": s("FirstName"),
"lastName": s("LastName"),
"superstring": v3.Includes("foo"),
"id": v3.Integer(12),
"accountBalance": v3.Decimal(123.76),
"itemsMinMax": v3.ArrayMinMaxLike(27, 3, 5),
"itemsMin": v3.ArrayMinLike("thereshouldbe3ofthese", 3),
"equality": v3.Equality("a thing"),
"dateTime": v3.Regex("2020-01-01", "[0-9\\-]+"),
"name": s("FirstName"),
"lastName": s("LastName"),
// "superstring": v3.Includes("foo"),
// "id": v3.Integer(12),
// "accountBalance": v3.Decimal(123.76),
// "itemsMinMax": v3.ArrayMinMaxLike(27, 3, 5),
// "itemsMin": v3.ArrayMinLike("thereshouldbe3ofthese", 3),
// "equality": v3.Equality("a thing"),
},
})

Expand All @@ -153,40 +157,39 @@ func TestConsumerV3(t *testing.T) {
}
}

func TestMessagePact(t *testing.T) {
provider, err := v3.NewMessagePactV3(v3.MessageConfig{
Consumer: "V3MessageConsumer",
Provider: "V3MessageProvider", // must be different to the HTTP one, can't mix both interaction styles
SpecificationVersion: v3.V3,
})

if err != nil {
t.Fatal(err)
}

message := provider.AddMessage()
message.
Given(v3.ProviderStateV3{
Name: "User with id 127 exists",
Parameters: map[string]interface{}{
"id": 127,
},
}).
ExpectsToReceive("a user event").
WithMetadata(v3.MapMatcher{
"Content-Type": s("application/json; charset=utf-8"),
}).
// WithContent(v3.MatchV3(&User{})).
WithContent(v3.MapMatcher{
"datetime": v3.Regex("2020-01-01", "[0-9\\-]+"),
"name": s("FirstName"),
"lastName": s("LastName"),
"id": v3.Integer(12),
}).
AsType(&User{})

provider.VerifyMessageConsumer(t, message, userHandlerWrapper)
}
// func TestMessagePact(t *testing.T) {
// provider, err := v3.NewMessagePactV3(v3.MessageConfig{
// Consumer: "V3MessageConsumer",
// Provider: "V3MessageProvider", // must be different to the HTTP one, can't mix both interaction styles
// SpecificationVersion: v3.V3,
// })

// if err != nil {
// t.Fatal(err)
// }

// message := provider.AddMessage()
// message.
// Given(v3.ProviderStateV3{
// Name: "User with id 127 exists",
// Parameters: map[string]interface{}{
// "id": 127,
// },
// }).
// ExpectsToReceive("a user event").
// WithMetadata(v3.MapMatcher{
// "Content-Type": s("application/json; charset=utf-8"),
// }).
// WithContent(v3.MapMatcher{
// "datetime": v3.Regex("2020-01-01", "[0-9\\-]+"),
// "name": s("FirstName"),
// "lastName": s("LastName"),
// "id": v3.Integer(12),
// }).
// AsType(&User{})

// provider.VerifyMessageConsumer(t, message, userHandlerWrapper)
// }

type User struct {
ID int `json:"id" pact:"example=27"`
Expand All @@ -208,10 +211,11 @@ var test = func(config v3.MockServerConfig) error {
req := &http.Request{
Method: "POST",
URL: &url.URL{
Host: fmt.Sprintf("%s:%d", "localhost", config.Port),
Scheme: "https",
Path: "/foobar",
RawQuery: "baz=bat&baz=foo&baz=something", // Default behaviour
Host: fmt.Sprintf("%s:%d", "localhost", config.Port),
Scheme: "https",
Path: "/foobar",
// RawQuery: "baz=foo&baz=foo&baz=foo", // TODO: Currently doesn't support matching rules being sent over the wire, so must have exact values
RawQuery: "baz=bat&baz=foo&baz=something", // Default behaviour, test matching
// RawQuery: "baz[]=bat&baz[]=foo&baz[]=something", // TODO: Rust v3 does not support this syntax
},
Body: ioutil.NopCloser(strings.NewReader(`{"id": 27, "name":"billy", "lastName":"sampson", "datetime":"2020-01-01'T'08:00:45"}`)),
Expand Down
6 changes: 6 additions & 0 deletions v3/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,12 @@ func (p *httpMockProvider) validateConfig() error {
}

p.mockserver = native.NewMockServer(p.config.Consumer, p.config.Provider)
switch p.specificationVersion {
case V2:
p.mockserver.WithSpecificationVersion(native.SPECIFICATION_VERSION_V2)
case V3:
p.mockserver.WithSpecificationVersion(native.SPECIFICATION_VERSION_V3)
}
native.Init()

return nil
Expand Down
10 changes: 9 additions & 1 deletion v3/http_v2.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,16 @@ func NewHTTPMockProviderV2(config MockHTTPProviderConfigV2) (*HTTPMockProviderV2
// with each additional modification to the InteractionV2 object
func (p *HTTPMockProviderV2) AddInteraction() *InteractionV2 {
log.Println("[DEBUG] pact add v2 interaction")
i := &InteractionV2{}
interaction := p.httpMockProvider.mockserver.NewInteraction("")

i := &InteractionV2{
Interaction: Interaction{
interaction: interaction,
},
}

p.httpMockProvider.v2Interactions = append(p.httpMockProvider.v2Interactions, i)

return i
}

Expand Down
9 changes: 8 additions & 1 deletion v3/http_v3.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,14 @@ func NewHTTPMockProviderV3(config MockHTTPProviderConfigV3) (*HTTPMockProviderV3
// required things. Will automatically start a Mock Service if none running.
func (p *HTTPMockProviderV3) AddInteraction() *InteractionV3 {
log.Println("[DEBUG] pact add v3 interaction")
i := &InteractionV3{}
interaction := p.httpMockProvider.mockserver.NewInteraction("")

i := &InteractionV3{
Interaction: Interaction{
interaction: interaction,
},
}

p.httpMockProvider.v3Interactions = append(p.httpMockProvider.v3Interactions, i)
return i
}
43 changes: 43 additions & 0 deletions v3/interaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package v3

import (
"log"

"github.com/pact-foundation/pact-go/v3/internal/native/mockserver"
)

// Interaction is the main implementation of the Pact interface.
Expand All @@ -14,12 +16,16 @@ type Interaction struct {

// Description to be written into the Pact file
Description string `json:"description"`

// Reference to the native rust handle
interaction *mockserver.Interaction
}

// UponReceiving specifies the name of the test case. This becomes the name of
// the consumer/provider pair in the Pact file. Mandatory.
func (i *Interaction) UponReceiving(description string) *Interaction {
i.Description = description
i.interaction.UponReceiving(description)

return i
}
Expand All @@ -30,6 +36,21 @@ func (i *Interaction) UponReceiving(description string) *Interaction {
func (i *Interaction) WithRequest(request Request) *Interaction {
i.Request = request

i.interaction.WithRequest(request.Method, request.Path)
query := make(map[string][]interface{})
for k, values := range request.Query {
for _, v := range values {
query[k] = append(query[k], v)
}
}
i.interaction.WithQuery(query)
headers := make(map[string]interface{})
for k, v := range request.Headers {
headers[k] = v
}
i.interaction.WithRequestHeaders(headers)
i.interaction.WithJSONRequestBody(request.Body)

// Check if someone tried to add an object as a string representation
// as per original allowed implementation, e.g.
// { "foo": "bar", "baz": like("bat") }
Expand All @@ -44,8 +65,30 @@ func (i *Interaction) WithRequest(request Request) *Interaction {

// WillRespondWith specifies the details of the HTTP response that will be used to
// confirm that the Provider must satisfy. Mandatory.
// Defaults to application/json.
// Use WillResponseWithContent to define custom type
func (i *Interaction) WillRespondWith(response Response) *Interaction {
return i.WillRespondWithContent("application/json", response)
}

func (i *Interaction) WillRespondWithContent(contentType string, response Response) *Interaction {
i.Response = response
headers := make(map[string]interface{})
for k, v := range response.Headers {
headers[k] = v.(stringLike).string()
}
i.interaction.WithResponseHeaders(headers)
i.interaction.WithStatus(response.Status)

if contentType == "application/json" {
i.interaction.WithJSONResponseBody(response.Body)
} else {
bodyStr, ok := response.Body.(string)
if !ok {
panic("response body must be a string")
}
i.interaction.WithResponseBody(bodyStr, contentType)
}

return i
}
2 changes: 2 additions & 0 deletions v3/interaction_v2.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,7 @@ type InteractionV2 struct {
func (i *InteractionV2) Given(state string) *InteractionV2 {
i.State = state

i.interaction.Given(state)

return i
}
Loading

0 comments on commit 0fc423a

Please sign in to comment.