Skip to content

Commit

Permalink
feat: update consumer version selectors
Browse files Browse the repository at this point in the history
  • Loading branch information
mefellows committed Aug 9, 2021
1 parent 3d3cd4c commit 40cbc80
Show file tree
Hide file tree
Showing 9 changed files with 88 additions and 80 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ pact: clean install #docker

publish:
@echo "-- 📃 Publishing pacts"
@"${PACT_CLI}" publish ${PWD}/examples/pacts --consumer-app-version ${APP_SHA} --tag ${APP_BRANCH}
@"${PACT_CLI}" publish ${PWD}/examples/pacts --consumer-app-version ${APP_SHA} --tag ${APP_BRANCH} --tag prod

release:
echo "--- 🚀 Releasing it"
Expand Down
2 changes: 1 addition & 1 deletion docs/consumer.md
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,6 @@ For V3 tests, these methods may be called multiple times, resulting in more than

## Publishing pacts to a Broker

We recommend publishing the contracts to a Pact Broker (or https://pactflow.io) using the [CLI Tools]()https://docs.pact.io/implementation_guides/cli/#pact-cli.
We recommend publishing the contracts to a [Pact Broker](https://docs.pact.io/pact_broker) using the [CLI Tools](https://docs.pact.io/implementation_guides/cli/#pact-cli).

[Read more](https://docs.pact.io/pact_broker/publishing_and_retrieving_pacts/) about publishing pacts.
65 changes: 46 additions & 19 deletions docs/provider.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,15 +137,59 @@ for different test cases:

_Important Note_: You should only use this feature for things that can not be persisted in the pact file. By modifying the request, you are potentially modifying the contract from the consumer tests!

### Pending Pacts
### Connecting to a Pact Broker

In most cases, you will want to use a [Pact Broker](https://docs.pact.io/pact_broker) to manage your contracts.

The first part of the configuration is to tell Pact how to connect to your broker:

```golang
Provider: "V3Provider",
ProviderVersion: os.Getenv("APP_SHA"),
BrokerURL: os.Getenv("PACT_BROKER_BASE_URL"),
```

The provider name uniquely identifies the application and automatically discovers consumer contracts for this provider. The [version information](https://docs.pact.io/getting_started/versioning_in_the_pact_broker) is extremely important, and is used to send compatibility information back to the broker. This should be unique per build, and is recommended to be the Git SHA.

#### Selecting pacts to verify

Once connected to the broker, you need to configure which pacts you care about verifying. Consumer version selectors are how we do this. For example, in the following setup we collect all contracts where the tag is `master` or `prod`:

```golang
ConsumerVersionSelectors: []Selector{
&ConsumerVersionSelector{
Tag: "master",
},
&ConsumerVersionSelector{
Tag: "prod",
},
},
```

Read more on [selectors](https://docs.pact.io/pact_broker/advanced_topics/consumer_version_selectors/)

#### Publishing test results

Lastly, you will want to send the verification results so that consumers can query if they are safe
to release. In your broker, it may look like this:

![screenshot of verification result](https://cloud.githubusercontent.com/assets/53900/25884085/2066d98e-3593-11e7-82af-3b41a20af8e5.png)

You need to specify the following in your verification options:

```go
PublishVerificationResults: true, // recommended only in CI
```

#### Pending Pacts

Pending pacts is a feature that allows consumers to publish new contracts or changes to existing contracts without breaking Provider's builds. It does so by flagging the contract as "unverified" in the Pact Broker the first time a contract is published. A Provider can then enable a behaviour (via `EnablePending: true`) that will still perform a verification (and thus share the results back to the broker) but _not_ fail the verification step itself.

This enables safe introduction of new contracts into the system, without breaking Provider builds, whilst still providing feedback to Consumers as per before.

See the [docs](https://docs.pact.io/pending) and this [article](http://blog.pact.io/2020/02/24/how-we-have-fixed-the-biggest-problem-with-the-pact-workflow/) for more background.

### WIP Pacts
#### WIP Pacts

WIP Pacts builds upon pending pacts, enabling provider tests to pull in _any_ contracts applicable to the provider regardless of the `tag` it was given. This is useful, because often times consumers won't follow the exact same tagging convention and so their workflow would be interrupted. This feature enables any pacts determined to be "work in progress" to be verified by the Provider, without causing a build failure. You can enable this behaviour by specifying a valid `time.Time` field for `IncludeWIPPactsSince`. This sets the start window for which new WIP pacts will be pulled down for verification, regardless of the tag.

Expand All @@ -158,20 +202,3 @@ For each _interaction_ in a pact file, the order of execution is as follows:
`BeforeEach` -> `StateHandler` -> `RequestFilter (pre)` -> `Execute Provider Test` -> `RequestFilter (post)` -> `AfterEach`

If any of the middleware or hooks fail, the tests will also fail.

### Publishing Provider Verification Results to a Pact Broker

If you're using a Pact Broker (e.g. a hosted one at https://pactflow.io), you can
publish your verification results so that consumers can query if they are safe
to release.

It looks like this:

![screenshot of verification result](https://cloud.githubusercontent.com/assets/53900/25884085/2066d98e-3593-11e7-82af-3b41a20af8e5.png)

You need to specify the following in your verification options:

```go
PublishVerificationResults: true,
ProviderVersion: "1.0.0",
```
11 changes: 10 additions & 1 deletion examples/provider_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,16 @@ func TestV3HTTPProvider(t *testing.T) {
filepath.ToSlash(fmt.Sprintf("%s/PactGoV3Consumer-V3Provider.json", pactDir)),
filepath.ToSlash(fmt.Sprintf("%s/PactGoV2ConsumerMatch-V2ProviderMatch.json", pactDir)),
},
RequestFilter: f,
ConsumerVersionSelectors: []Selector{
&ConsumerVersionSelector{
Tag: "master",
},
&ConsumerVersionSelector{
Tag: "prod",
},
},
PublishVerificationResults: true,
RequestFilter: f,
BeforeEach: func() error {
log.Println("[DEBUG] HOOK before each")
return nil
Expand Down
43 changes: 24 additions & 19 deletions provider/consumer_version_selector.go
Original file line number Diff line number Diff line change
@@ -1,31 +1,36 @@
package provider

import "fmt"

// ConsumerVersionSelector are the way we specify which pacticipants and
// versions we want to use when configuring verifications
// See https://docs.pact.io/selectors for more
//
// Where a new selector is available in the broker but not yet supported here,
// you may use the UntypedConsumerVersionSelector to pass in arbitrary key/values
//
// Definitive list: https://github.com/pact-foundation/pact_broker/blob/master/spec/lib/pact_broker/api/contracts/verifiable_pacts_json_query_schema_combinations_spec.rb
type ConsumerVersionSelector struct {
Pacticipant string `json:"pacticipant"`
Tag string `json:"tag"`
Version string `json:"version"`
Latest bool `json:"latest"`
All bool `json:"all"`
Tag string `json:"tag,omitempty"`
FallbackTag string `json:"fallbackTag,omitempty"`
Latest bool `json:"latest,omitempty"`
Consumer string `json:"consumer,omitempty"`
DeployedOrReleased bool `json:"deployedOrReleased,omitempty"`
Deployed bool `json:"deployed,omitempty"`
Released bool `json:"released,omitempty"`
Environment string `json:"environment,omitempty"`
MainBranch bool `json:"mainBranch,omitempty"`
Branch string `json:"branch,omitempty"`
}

// Validate the selector configuration
func (c *ConsumerVersionSelector) Validate() error {
if c.All && c.Pacticipant == "" {
return fmt.Errorf("must provide a Pacticpant")
}
// Type marker
func (c *ConsumerVersionSelector) IsSelector() {
}

if c.Pacticipant != "" && c.Tag == "" {
return fmt.Errorf("must provide at least a Tag if Pacticpant specified")
}
type UntypedConsumerVersionSelector map[string]interface{}

if c.All && c.Latest {
return fmt.Errorf("cannot select both All and Latest")
}
// Type marker
func (c *UntypedConsumerVersionSelector) IsSelector() {
}

return nil
type Selector interface {
IsSelector()
}
32 changes: 0 additions & 32 deletions provider/consumer_version_selector_test.go

This file was deleted.

7 changes: 2 additions & 5 deletions provider/verify_request.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ type VerifyRequest struct {
// Selectors are the way we specify which pacticipants and
// versions we want to use when configuring verifications
// See https://docs.pact.io/selectors for more
ConsumerVersionSelectors []ConsumerVersionSelector
ConsumerVersionSelectors []Selector

// Retrieve the latest pacts with this consumer version tag
Tags []string
Expand Down Expand Up @@ -164,15 +164,12 @@ func (v *VerifyRequest) validate() error {

if len(v.ConsumerVersionSelectors) != 0 {
for _, selector := range v.ConsumerVersionSelectors {
if err := selector.Validate(); err != nil {
return fmt.Errorf("invalid consumer version selector specified: %v", err)
}
body, err := json.Marshal(selector)
if err != nil {
return fmt.Errorf("invalid consumer version selector specified: %v", err)
}

v.args = append(v.args, "--consumer-version-selector", string(body))
v.args = append(v.args, "--consumer-version-selectors", string(body))
}
}

Expand Down
4 changes: 2 additions & 2 deletions provider/verify_request_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,12 +81,12 @@ func TestVerifyRequestValidate(t *testing.T) {
{name: "no pacticipant", request: VerifyRequest{
PactURLs: []string{"http://localhost:1234/path/to/pact"},
ProviderBaseURL: "http://localhost:8080",
ConsumerVersionSelectors: []ConsumerVersionSelector{{}},
ConsumerVersionSelectors: []Selector{&ConsumerVersionSelector{}},
}, err: false},
{name: "pacticipant only", request: VerifyRequest{
PactURLs: []string{"http://localhost:1234/path/to/pact"},
ProviderBaseURL: "http://localhost:8080",
ConsumerVersionSelectors: []ConsumerVersionSelector{{Pacticipant: "foo", Tag: "test"}},
ConsumerVersionSelectors: []Selector{&ConsumerVersionSelector{Consumer: "foo", Tag: "test"}},
}, err: false},
}
for _, tt := range tests {
Expand Down
2 changes: 2 additions & 0 deletions sugar/sugar.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ type VerifyRequest = provider.VerifyRequest
type AsynchronousMessage = message.AsynchronousMessage
type ProviderStateV3Response = models.ProviderStateV3Response
type StateHandlers = models.StateHandlers
type ConsumerVersionSelector = provider.ConsumerVersionSelector
type Selector = provider.Selector

// V2
var Like = matchers.Like
Expand Down

0 comments on commit 40cbc80

Please sign in to comment.