pact-foundation / pact-js

JS version of Pact. Pact is a contract testing framework for HTTP APIs and non-HTTP asynchronous messaging systems.
https://pact.io
Other
1.59k stars 343 forks source link

Generating `path` with `fromProviderState` and matching `path` using `Regex` #1089

Open agross opened 1 year ago

agross commented 1 year ago

Software versions

Issue Checklist

Please confirm the following:

Expected behaviour

In my project we have a situation where a URL path contains a provider state-generated string (buildingId):

/plans/buildings/${buildingId}/floors

We also use the Pact stub server for client-side development and need to match paths received by the stub server against this URL pattern.

A teammate defined the interaction like this (excerpt):

.withRequest({
          method: "GET",
          path: fromProviderState(
            '/plans/buildings/${buildingId}/floors',
            Matchers.regex(
              /^\/plans\/buildings\/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\/floors$/,
              '/plans/buildings/8e824a37-408c-4341-9c98-38ec9a3953f3/floors'
            )
          ) as Matchers.Path)

This left us with this Pact JSON file (excerpt):

{
      "providerStates": [
        {
          "name": "building with floors",
          "params": {
            "buildingId": "b0be1c67-51e7-4657-8a63-196f2da18e73"
          }
        }
      ],
      "request": {
        "generators": {
          "path": {
            "expression": "/plans/buildings/${buildingId}/floors",
            "type": "ProviderState"
          }
        },
        "matchingRules": {
          "path": {
            "combine": "AND",
            "matchers": [
              {
                "match": "type"
              }
            ]
          }
        },
        "method": "GET",
        "path": "{\"pact:matcher:type\":\"regex\",\"regex\":\"^\\\\/plans\\\\/buildings\\\\/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\\\\/floors$\",\"value\":\"/plans/buildings/8e824a37-408c-4341-9c98-38ec9a3953f3/floors\"}"
      },

...which contains the string representation of the Regex matcher above as the example path. The generator element looks fine and the provider can be verified with a dynamic provider-generated buildingId.

What we rather want is a matchingRules entry that adheres to the URL pattern for the /plans/buildings/${buildingId}/floors path. The JSON above would match any string, essentially capturing all requests.

The following JSON contains the path matcher we wish for was generated from this piece:

        .withRequest({
          method: "GET",
          path: Matchers.regex(
            /^\/plans\/buildings\/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\/floors$/,
            "/plans/buildings/8e824a37-408c-4341-9c98-38ec9a3953f3/floors"
          ),
{
  "description": "A request for loading available floors",
  "providerStates": [
    {
      "name": "building with floors",
      "params": {
        "buildingId": "b0be1c67-51e7-4657-8a63-196f2da18e73",
        "stub": true
      }
    }
  ],
  "request": {
    "matchingRules": {
      "path": {
        "combine": "AND",
        "matchers": [
          {
            "match": "regex",
            "regex": "^\\/plans\\/buildings\\/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\\/floors$"
          }
        ]
      }
    },
    "method": "GET",
    "path": "/plans/buildings/8e824a37-408c-4341-9c98-38ec9a3953f3/floors"
  }

Is this possible? If yes, how?

I imagine this URL pattern is not uncommon, but I could not find any examples. The only Stack Overflow question I could find has been kind of inactive with no solution provided.

mefellows commented 1 year ago

This is a @uglyog question I think. I don't think we currently support this do we?

rholshausen commented 1 year ago

You can't use a regex matcher with the fromProviderState function, it expects two parameters, an expression to replace and an example value.

From the Pact file, you can see it is just taking the second parameter and calling toString() on it

"path": "{\"pact:matcher:type\":\"regex\",\"regex\":\"^\\/plans\\/buildings\\/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\\/floors$\",\"value\":\"/plans/buildings/8e824a37-408c-4341-9c98-38ec9a3953f3/floors\"}"

agross commented 1 year ago

You can't use a regex matcher with the fromProviderState function

Yes, this is what we found and the reason this issue exists.

Are such URL patterns uncommon with Pact users?

rholshausen commented 1 year ago

This is the first time I've seen anyone needing to do this.

To support this, the regex matcher will need to be defined after the fromProviderState function is done, but I don't think the current JS DSL has a way of doing this.

mefellows commented 1 year ago

It's not that the URL pattern is uncommon, it's the specific set of features needed. I'll have to think about how we can support it, perhaps in a V4 interface.