pact-foundation / pact-jvm

JVM version of Pact. Enables consumer driven contract testing, providing a mock service and DSL for the consumer project, and interaction playback and verification for the service provider project.
https://docs.pact.io
Apache License 2.0
1.08k stars 479 forks source link

Challenges in migrating pact V3 to pact V4 #1676

Open mjureczko opened 1 year ago

mjureczko commented 1 year ago

Once we migrated the pact-jvm to the 4.5.0 version in a consumer service, we’ve started publishing pacts with version V4 (pactSpecificaiton.version = 4.0). The provider was fetching the interactions without any issues and as it turns out they were tagged correctly (with the master tag). But pact reported that it wasn’t able to find any Pact files. Why? Because they were being filtered out. What caused our issues was this annotation @PactFilter(value = "/v1/products/price", filter = InteractionFilter.ByRequestPath.class) , InteractionFilter.ByRequestPath.class to be precise. Inside of this filter there is an if statement that filters out all the interaction that are not RequestResponseInteraction (my understanding is, it just filters out message interactions) using this if statement:

 if(interaction instanceof RequestResponseInteraction){...}else{return false} .

When we switched to V4 pacts, we started using the V4Interaction . It does not implement RequestResponseInteraction , however it implements directly SynchronousRequestResponse (which is an interface that is implemented also by RequestResponseInteraction ). So at the end, all the V4 interactions were filtered out, so the interaction with the latest tag master was not found by pact. We’ve reimplemented the filter on our own where instead of relying on the types hierarchy, we use available methods (isSynchronousRequestResponse and asSynchronousRequestResponse) to identify the type of interaction. As of now, it was tested successfully on a feature branch and it works just fine with both V3 and V4 interactions:

public static class V4CompatibleByRequestPathInteractionFilter<I extends Interaction> implements InteractionFilter<I> {
    @Override
    public Predicate<I> buildPredicate(String[] values) {
        return interaction -> {
            if (interaction.isSynchronousRequestResponse()) {
                var requestPath = interaction.asSynchronousRequestResponse().getRequest().getPath();
                return Arrays.stream(values).anyMatch(requestPath::matches);
            } else {
                return false;
            }
        };
    }
}

Later, I found another discrepancy between pact-jvm:4.5.0 and the previous version of it. This one happens when generating Pact JSON files, headers section to be precise. When using V3 pacts, in RequestResponseInteraction.requestToMap we can see that the headers “section” is flattened with:

map["headers"] = request.headers.entries.associate { (key, value) -> key to value.joinToString(COMMA) }

However, in V4 pacts in V4HttpParts , there is just:

map["headers"] = headers (headers is of type MutableMap<String, List<String>>)

As a result, in V4 we get:

"headers": {
          "Accept-Language": [
            "en-GB"
          ],
          "BannerId": [
            "326764e2-9640-40f1-9cef-308527c96e1f"
          ],
          "Content-Type": [
            "application/json"
          ],
          "RequestId": [
            "5abf04ba-b5ae-4dc4-a52e-979ebbd3ca08"
          ]
        }

and in V3 it was:

"headers": {
          "Accept-Language": "en-GB",
          "BannerId": "326764e2-9640-40f1-9cef-308527c96e1f",
          "Content-Type": "application/json",
          "RequestId": "5abf04ba-b5ae-4dc4-a52e-979ebbd3ca08"
        }
rholshausen commented 1 year ago

Duplicate of #1673