pact-foundation / pact-net

.NET 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://pact.io
MIT License
848 stars 234 forks source link

MinType always returning just one element #522

Open javiplay opened 1 month ago

javiplay commented 1 month ago

Previous issues Have you searched the issue tracker to ensure this hasn't been discussed before? Yes but I did not find any discussion on this.

Version information:

Describe the bug When creating contract using MinType to return at least a number of elements in array property only one element is returned by the fake server.

Steps To Reproduce

 pact
     .UponReceiving("A request to get some things")
     .Given("There are some things")
     .WithRequest(HttpMethod.Get, "/api/things")
     .WithHeader("Accept", "application/json")
 .WillRespond()
     .WithStatus(HttpStatusCode.OK)
     .WithHeader("Content-Type", "application/json; charset=utf-8")
     .WithJsonBody((
         Match.MinType(new
         {
             thingId = Match.Regex("2", "^\\d+$"),
             thingName = Match.Regex("SecondThing", "^\\w{1,12}$")
         }, 3)));

 await pact.VerifyAsync(async ctx => {
     var thingsRepository = new HttpThingsRepository(ctx.MockServerUri.ToString());
     var things = await thingsRepository.GetThings();
     Assert.Equal(3, things.Count);
 });

Expected behavior I would expect the server to return 3 elements instead of one.

Log Output This is the generated contract (after forcing test to pass by expecting one element)

{
  "consumer": {
    "name": "systemtestconsumer-win32nt"
  },
  "interactions": [
    {
      "description": "A request to get some things",
      "providerStates": [
        {
          "name": "There are some things"
        }
      ],
      "request": {
        "headers": {
          "Accept": "application/json"
        },
        "method": "GET",
        "path": "/api/things"
      },
      "response": {
        "body": [
          {
            "thingId": "2",
            "thingName": "SecondThing"
          }
        ],
        "headers": {
          "Content-Type": "application/json; charset=utf-8"
        },
        "matchingRules": {
          "body": {
            "$": {
              "combine": "AND",
              "matchers": [
                {
                  "match": "type",
                  "min": 3
                }
              ]
            },
            "$[*].thingId": {
              "combine": "AND",
              "matchers": [
                {
                  "match": "regex",
                  "regex": "^\\d+$"
                }
              ]
            },
            "$[*].thingName": {
              "combine": "AND",
              "matchers": [
                {
                  "match": "regex",
                  "regex": "^\\w{1,12}$"
                }
              ]
            }
          },
          "header": {}
        },
        "status": 200
      }
    }
  ],
  "metadata": {
    "pactRust": {
      "ffi": "0.4.0",
      "models": "1.0.4"
    },
    "pactSpecification": {
      "version": "3.0.0"
    }
  },
  "provider": {
    "name": "systemthingsserver-win32nt"
  }
}

Additional context In previous version it was returning the number of items specified in the contract, this is how it was before:

{
  "consumer": {
    "name": "systemtestconsumer"
  },
  "provider": {
    "name": "systemthingsserver"
  },
  "interactions": [
    {
      "description": "A Get request to '/api/things'",
      "providerState": "There are some things",
      "request": {
        "method": "get",
        "path": "/api/things",
        "headers": {
        }
      },
      "response": {
        "status": 200,
        "headers": {
          "Content-Type": "application/json; charset=utf-8"
        },
        "body": [
          {
            "thingId": "2",
            "thingName": "SecondThing"
          },
          {
            "thingId": "2",
            "thingName": "SecondThing"
          },
          {
            "thingId": "2",
            "thingName": "SecondThing"
          }
        ],
        "matchingRules": {
          "$.body": {
            "min": 3
          },
          "$.body[*].*": {
            "match": "type"
          },
          "$.body[*].thingId": {
            "match": "regex",
            "regex": "^\\d+$"
          },
          "$.body[*].thingName": {
            "match": "regex",
            "regex": "^\\w{1,12}$"
          }
        }
      }
    }
  ],
  "metadata": {
    "pactSpecification": {
      "version": "2.0.0"
    }
  }
}
adamrodger commented 1 month ago

I think this is correct behaviour - your matcher is saying "should return at least 3 elements, and each element looks like ".

Pact is about testing the contract "shape" rather than any particular behaviour details.

adamrodger commented 1 month ago

@mefellows Would you be able to comment on the change between versions?

I would expect that to be an implementation detail which doesn't affect the contract semantics, but that would be something inside the FFI anyway.

mefellows commented 1 month ago

@mefellows Would you be able to comment on the change between versions?

I'd need to check the FFI updates, but I would consider this a bug - it should return the minimum IMO.

I would expect that to be an implementation detail which doesn't affect the contract semantics, but that would be something inside the FFI anyway.

Yes, I think that's correct.

mefellows commented 1 month ago

P.S. thank you @javiplay for the nice bug report :)