Search code examples
gobad-requestpact

How to return a bad request (400, 500) with Pact in Go?


I'm working in the adoption of Pact in my company, but on Golang, we hit a hurdle on the basic case where a consumer as 2 states for one endpoint:

  • Given("A product with id 1 exists").
  • Given("A product with id 2 doesn't exists").

Our trouble is on the doesn't exists case.

Consumer

mockProvider.AddInteraction().
            Given("The product with ID 66 doesn't exists").
            UponReceiving("a request Product 66").
            WithRequest(http.MethodGet, S("/api/v1/product/66")).
            WillRespondWith(http.StatusNotFound).

Provider

func TestContract(t *testing.T) {

    SetLogLevel("TRACE")
    verifier := HTTPVerifier{}

    err := verifier.VerifyProvider(t, VerifyRequest{
        ProviderBaseURL:            "http://localhost:8080",
        Provider:                   "ms.pact-provider-example-for-go",
        ProviderVersion:            "example",                                            // os.Getenv("APP_SHA"),
        BrokerURL:                  "https://…", // os.Getenv("PACT_BROKER_BASE_URL"),
        PublishVerificationResults: false,
        StateHandlers: StateHandlers{
            "A product with id 1 exists": func(setup bool, s ProviderStateV3) (ProviderStateV3Response, error) {
                …
                return response, nil
            },
            "A product with id 2 doesn't exists": func(setup bool, s ProviderStateV3) (ProviderStateV3Response, error) {
                // ???
            },
        },
    })

    require.NoError(t, err)
}

Question

How can we return a bad request reponse as ProviderStateV3Response is a map interface?


Solution

  • StateHandlers don't exist to change the response directly (that could impact the validity of the test), they exist to modify the internal state of the provider for the current test. The use the state name (and optionally, parameters) to determine what state should be configured.

    When the test executes, the provider should execute its usual code with that state in place, and respond accordingly.

            StateHandlers: StateHandlers{
                "A product with id 1 exists": func(setup bool, s ProviderStateV3) (ProviderStateV3Response, error) {
                    // modify internal state of the provider, so that product with ID 1 exists in the database
                    return response, nil
                },
                "A product with id 2 doesn't exists": func(setup bool, s ProviderStateV3) (ProviderStateV3Response, error) {
                    // modify internal state of the provider, so that product with ID 2 does not exist in the database
                },
            },
    

    There are examples in the repository, such as https://github.com/pact-foundation/pact-go/blob/master/examples/mux/provider/user_service_test.go#L94-L120.

    States are abstract - it doesn't imply how the state is configured. It could achieve the state transition in multiple ways such as updating a database or configuring a stub etc.