Giter VIP home page Giter VIP logo

com-pact's Introduction

ComPact

An alternative Pact implementation for .NET with support for Pact Specification v3.

Introduction

Why another Pact implementation?

  1. Most importantly, it's a fun project which allows me to learn a lot about the details of the Pact Specification.
  2. My impression is that while the idea to reuse the same (Ruby) core logic for almost all Pact implementations has a lot going for it, in practice it adds quite some accidental complexity and as a result the project isn't moving forward as fast as it could.
  3. I think it's healthy for a standard/specification to have more independent implementations of it.

Status

ComPact is already being used by various people and organisations, and as more people experiment with different scenarios, some bugs are still being ironed out. I try to respond quickly to issues. Pull requests are welcome.

What's not supported

This implementation will not support:

  • Specification versions lower than 2.0.
  • "Body is present, but is null"-semantics. Due to the practicalities of .NET, no distiction will be made between a body that is non-existent and one that is null.
  • Matching rules on requests.

In the future it might support:

  • Data formats other than JSON (i.e. XML). For now the semantics of content-type headers and message metadata will be ignored.
  • Example generators.

Also note that the DSL to define a contract will not allow you to express everything that is valid within the Pact Specification. The goal is not to be complete, but to be simple and user friendly in such a way that it makes the right thing easy to do, and the wrong thing hard.

Usage

As an API consumer

As the consumer of an API, you'll be the one to generate the Pact contract, so first you have to decide which version of the Pact Specification you want to use. To use V3, start with the following using statement in your test class:

using ComPact.Builders.V3;

Create a builder, and provide the base URL where the builder will create a mock provider service that you can call to verify your consumer code:

var url = "http://localhost:9393";
var builder = new PactBuilder("test-consumer", "test-provider", url);

Set up an interaction, which simply tells the mock provider that when it receives the specified request, it should return the specified response. Notice that the response body is described using a DSL specific for ComPact.

builder.SetUp(
	Pact.Interaction
        .UponReceiving("a request")
        .With(Pact.Request
            .WithHeader("Accept", "application/json")
            .WithMethod(Method.GET)
            .WithPath("/testpath"))
        .WillRespondWith(Pact.Response
            .WithStatus(200)
            .WithHeader("Content-Type", "application/json")
            .WithBody(Pact.JsonContent.With(Some.Element.WithTheExactValue("test")))));

To test the interaction, use your actual client code to call the mock provider and check if it can correctly handle the response. If the actual request your code produces cannot be matched to a request that has been set up, the mock provider will return a 400 response with some explanation in the response body.

Set up any number of interactions and verify them. Finally, when you're done with your tests, create the pact contract:

await builder.BuildAsync();

This will verify that all interactions that have been set up have actually been triggered, and will then write a Pact json file to the project directory (by default).

As an API provider

When you are the provider of an API, create a PactVerifier that will act as a "mock consumer". It will send you requests as specified in a Pact contract and compare the actual response your code produces to the expected response.

Start by providing the PactVerifier with the base url where your actual API can be reached:

var url = "http://localhost:9494";

var pactVerifier = new PactVerifier(new PactVerifierConfig { ProviderBaseUrl = url });

Run your own API in such a way that it can be reached by the mock consumer, for example like this:

var cts = new CancellationTokenSource();
var hostTask = WebHost.CreateDefaultBuilder()
                .UseKestrel()
                .UseUrls(url)
                .UseStartup<TestStartup>()
                .Build().RunAsync(cts.Token);

If you have the Pact file your want to verify stored locally, call VerifyPactAsync with the file path as a parameter. The PactVerifier will throw an exception when the actual response does not match the expected response.

var buildDirectory = AppContext.BaseDirectory;
var pactDir = Path.GetFullPath($"{buildDirectory}{Path.DirectorySeparatorChar}..{Path.DirectorySeparatorChar}..{Path.DirectorySeparatorChar}..{Path.DirectorySeparatorChar}pacts{Path.DirectorySeparatorChar}");
await pactVerifier.VerifyPactAsync(pactDir + "recipe-consumer-recipe-service.json");

At the end of your test, clean up your hosted API.

cts.Cancel();
await hostTask;

If the interactions defined in the Pact include Provider States, the PactVerifier will first, for each provider state, invoke a ProviderStateHandler that you should provide to the PactVerifierConfig. In your ProviderStateHandler you should do everything that is necessary to set up the correct test data.

The handler could look something like this:

public class ProviderStateHandler
    {
        public void Handle(ProviderState providerState)
        {
            ...

Which would be then be passed to PactVerifierConfig like this:

new PactVerifierConfig { ProviderBaseUrl = url, ProviderStateHandler = ProviderStateHandler.Handle }

As a Message consumer

Because Message Pacts are only supported in V3, start with:

using ComPact.Builders.V3;

Create a builder. You don't have to provide a URL because there is no HTTP communication involved:

var builder = new MessagePactBuilder("test-consumer", "test-provider");

Set up a message and verify it by calling VerifyConsumer. You have to provide the VerifyConsumer method with the type of the message you expect as well the code that should handle it. ComPact will only check that the message you set up can actually be deserialized to the provided message type, and that the handling code doesn't throw an exception. Anything else that you want to verify is up to you.

builder.SetUp(
	Pact.Message
        .Given(new ProviderState { 
            Name = "A new recipe has been added.",
            Params = new Dictionary<string, string> { { "recipeId", "7169de6d-df9b-4cf5-8cdc-2654062e5cdc" } } 
            })
        .ShouldSend("a RecipeAdded event.")
        .With(Pact.JsonContent.With(
            Some.String.Named("eventId").LikeGuid("f84fe18f-d871-4dad-9723-65b6dc9b0578"),
            Some.Object.Named("recipe").With(
                Some.Element.Named("name").Like("A Recipe"),
                Some.Element.Named("instructions").Like("Mix it up"),
                Some.Array.Named("ingredients").InWhichEveryElementIs(ingredient)
            )))
        .VerifyConsumer<RecipeAdded>(m => handler.Handle(m)));

Set up any number of messages and verify them. Finally, when you're done with your tests, create the pact contract:

await builder.BuildAsync();

As a Message provider

Verifying a Message Pact is very similar to verifying an API Pact, but instead of connecting to your API, ComPact will instead invoke a MessageProducer function that you supply in the PactVerifierConfig:

var config = new PactVerifierConfig
{
    ProviderStateHandler = providerStateHandler.Handle,
    MessageProducer = messageSender.Send
};

This function should receive the description of the message as a parameter, and based on that description (and possibly the Provider State) it should return the actual message that your application produces. ComPact will compare this message with the expected message as defined by the contract.

Pact Broker integration

It is highly recommended that you share Pact contracts using the Pact Broker. As a consumer ComPact allows you to publish the contracts you create, and as a provider ComPact allows you to publish the verification results.

To publish contracts to the Pact Broker, you should provide a PactPublisher to the PactBuilder. To allow you maximum flexibility how to connect to your own Pact Broker, it's up to you to provide a HttpClient that can be used by ComPact. You should also configure the version of your consumer application and optionally a tag for this version:

var publisher = new PactPublisher(new HttpClient() { BaseAddress = new Uri("http://your-pact-broker") }, "1.0", "local");

var builder = new MessagePactBuilder("messageConsumer", "messageProvider", publisher);

To publish the verification results as a provider, a similar configuration can be added to the PactVerifierConfig:

var config = new PactVerifierConfig
{
    ProviderVersion = "1.0",
    PublishVerificationResults = true,
    PactBrokerClient = new HttpClient() { BaseAddress = new Uri("http://your-pact-broker")}
};

Please note that once you configure a PactBrokerClient, ComPact will no longer try to read Pact files from your local disk, but will instead try to retrieve them from the Pact Broker, so you should provide the path as a parameter to VerifyPactAsync:

await pactVerifier.VerifyPactAsync("pacts/provider/messageProvider/consumer/messageConsumer/latest");

Pact Content DSL

To describe the contents of a message or the body of a response, ComPact uses a domain specific language as a fluent interface. The purpose of this is to make it easy to create a correct and useful Pact contract.

To define some JSON content with the accompanying matching rules, start by typing Pact.JsonContent.With(...

Then describe which elements the content consists of. For example, if the content is just a single string (yes, this is valid JSON) it would look like this: Some.Element.Like("Hello world!"). By using Like() a "type" matching rule will get generated, which means that you consider any string to be a valid response, and "Hello world!" is just an example.

To describe a JSON member (i.e. a name-value pair), you can use either Some.Element.Named("greeting").Like("Hello world!") or Some.Element.Like("Hello world!").Named("greeting"). This will result in the following JSON: { "greeting": "Hello world!" }.

Pact.JsonContent.With() JSON Matching Rules
Some.Element.Like("Hello world!") "Hello world" $ -> { "match": "type" }
Some.Element.Named("greeting").Like("Hello world!") { "greeting": "Hello world!" } $.greeting -> { "match": "type" }
Some.Element.WithTheExactValue("Hello world!") "Hello world" -
Some.String.LikeRegex("Hello world", "Hello.*") "Hello world" $ -> { "match": "regex", "regex": "Hello.*" }
Some.String.LikeGuid("f3b5978d-944d-46f2-8663-0c81f27bc4da") "f3b5978d-944d-46f2-8663-0c81f27bc4da" $ -> { "match": "regex", "regex": "[0-9a-fA-F]{8}\\-[0-9a-fA-F]{4}\\-[0-9a-fA-F]{4}\\-[0-9a-fA-F]{4}\\-[0-9a-fA-F]{12}" }
Some.String.LikeDateTime("2020-06-01T13:05:30") "2020-06-01T13:05:30" $ -> { "match": "regex", "regex": "^([\\+-]?\\d{4}(?!\\d..." }
Some.Array.Named("anArray") .Of(Some.Element.Like("Hello world")) { "anArray": [ "Hello world!" ] } $.anArray[0] -> { "match": "type" }
Some.Array.Named("anArray") .InWhichEveryElementIs(Some.Element.Like("Hello world")) { "anArray": [ "Hello world!" ] } $.anArray -> { "match": "type", "min": 1 }, $.anArray[*] -> { "match": "type" }
Some.Array.Named("anArray").ContainingAtLeast(2) .Of(Some.Element.Like("Hello world")) { "anArray": [ "Hello world!" ] } $.anArray -> { "match": "type", "min": 2 }, $.anArray[0] -> { "match": "type" }
Some.Array.Named("anArray").ContainingAtLeast(2) .InWhichEveryElementIs(Some.Element.Like("Hello world")) { "anArray": [ "Hello world!" ] } $.anArray -> { "match": "type", "min": 2 }, $.anArray[*] -> { "match": "type" }
Some.Object.Named("anObject") .With(Some.Element.Named("aNumber").Like(1))) { "anObject": { "aNumber": 1 } } $.anObject.aNumber -> { "match": "type" }
Some.Integer.Like(1) 1 $ -> { "match": "integer" }
Some.Decimal.Like(1.1) 1.1 $ -> { "match": "decimal" }

com-pact's People

Contributors

bartschotten avatar daghsentinel avatar sfiodorov avatar stasvi123 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

com-pact's Issues

ComPact can be used/referenced in .NET Standard

It's not possible to use (parts of) ComPact from a .NET Standard library.

Quick testing shows that changing the NuGet references:

- Microsoft.AspNetCore
- Microsoft.AspNetCore.Hosting
+ Microsoft.AspNetCore.Hosting.Abstractions
+ Microsoft.AspNetCore.Http.Abstractions

almost resolves everything, except for the code actually using the WebHost. Maybe you can isolate this code in a separate NuGet package?

Also, as a library you might considering lowering the dependencies to the lowest possible version (f.e. 2.1.0 instead of 2.2.7) to prevent forcing consumers to upgrade to at least those versions when they want to use your project. (Basically only require higher versions if bugfixes/used features require this)

ArgumentNullException when matchingRules are empty

Hi, I just discovered your library and wanted to give it a try since the official one does not support messages.

We have some NodeJS backends that consume messages via AMQP and wanted to create Pact tests for it. So we use the official pact-js package. This works for other backends that are also NodeJS but unfortunately I can not get it to work in our Net 5 backend with your library.

Pact-js creates pacts where the matchingRules property is just an empty object. This however seems to break the Verifier with an ArgumentNullException. I have debugged a bit and it seems this is caused by this line

foreach (var path in Body.Select(b => b.Key))

Is there anything that I can do to make this work with a consumer using the pact-js module and a provider using this libary?
I also tried to use the 10.0.0-beta.29 and set the specVersion to 3 but it still comes with empty matchingRules object.

Strings like "true"/"false" get deserialized to DateTime instead of string

Hey.
Issue similar to #14
Pact:

"$.Test": {
            "matchers": [
              {
                "match": "regex",
                "regex": "^([Tt]rue|[Ff]alse)$"
              }
            ]
          }

Message:

{
"Test":"False"
}

BodyMatcher:CompareExpectedTokenWithActual throws, becouse expected type is string and actual is Boolean.
Think it comes from seriazier that forces the type of "True" to be boolean.

Make difference clear between infrastructure and verification exceptions

Currently, all exception during validation return a PactException.
However, there is a difference between exceptions caused by the validation, or exception that are thrown because running the validation is not possible, for example because the endpoint is not available, the provider state can't be generated, etc.

I would suggest:
PactException for infrastructure exceptions, like failing to post provider state or other exception running the verification
PactVerificationException for the actual verification exceptions

Question: Work with matchers in content

I am trying to set up Pact for AMQP messages between a .NET provider and a NodeJS consumer. On the Node side the library PactJS is used, one the .NET side I am using this awesome library.

I have successfully make that work with a plain object, however I am struggling to make it work with matchers. I have tried the latest beta version with their new MatcherV3 API which is generating a json pact file that looks like this:

{
  "consumer": {
    "name": "NodeSide"
  },
  "provider": {
    "name": "NetSide"
  },
  "messages": [
    {
      "description": "a password reset mail",
      "providerStates": [],
      "contents": {
        "type": "mail:send",
        "props": {
          "profile": "PASSWORD_RESET",
          "from": {
            "pact:matcher:type": "type",
            "value": "[email protected]"
          },
          "to": {
            "pact:matcher:type": "type",
            "value": "[email protected]"
          },
          "subject": "Password reset",
          "resetUrl": {
            "pact:matcher:type": "type",
            "value": "https://reset.your.password"
          }
        }
      },
      "matchingRules": {},
      "metaData": {
        "correlationId": "some-string",
        "type": "mail:send",
        "appId": "app:pact-test",
        "contentType": "application/json"
      }
    }
  ],
  "metadata": {
    "pactSpecification": {
      "version": "3.0.0"
    }
  }
}

When I try to validate the .NET provider against this, ComPact is treating the values literally, meaning it is complaining that for example the reference object has no props.from.pact:matcher:type property but expected one.

For reference here is the plain object without matchers that is working:

{
  "consumer": {
    "name": "NodeSide"
  },
  "provider": {
    "name": "NetSide"
  },
  "messages": [
    {
      "description": "a password reset mail",
      "providerStates": [],
      "contents": {
        "type": "mail:send",
        "props": {
          "profile": "PASSWORD_RESET",
          "from": "[email protected]",
          "to": "[email protected]",
          "subject": "Password reset",
          "resetUrl": "https://reset.your.password"
        }
      },
      "matchingRules": {
      },
      "metaData": {
        "correlationId": "some-string",
        "type": "mail:send",
        "appId": "app:pact-test",
        "contentType": "application/json"
      }
    }
  ],
  "metadata": {
    "pactSpecification": {
      "version": "3.0.0"
    }
  }
}

My first guess was, that the Node side is somewhat broken and those rules should be in matchingRules instead. I asked if there is any way to make the matchers appear in matchingRules instead - since when I manually move the matchers to the matchingRules object it kinda works. (For reference: pact-foundation/pact-js#615)

However they indicated that instead this library is not compliant to the Pact spec. So I am asking here, should the above work and the problem is somewhere else? Can I make the above json work with Compact?
Thank you.

Feature Request: WithBody Pass Object

If dealing with a complex object, it would make it easier writing tests if WithBody() as part of a response (WillRepondWith) chain could accept an object to be passed as the expected response for the verification. This would allow Com-Pact to operate similar to Pact.Net. Of course it's better to be very explicit with what is expected but allowing an object to be used as the expected response would allow for tests to be written much faster.

var expectedResponse = new ApiResponse { Id = "1234", SomeData = "Blah"};

_pactBuilder.SetUp(Pact.Interaction.Given(
                    $"An request for data")
                .UponReceiving("A get request for data")
                .With(Pact.Request.WithMethod(Method.GET)
                    .WithPath("/data"))
                .WillRespondWith(Pact.Response
                    .WithStatus((int) HttpStatusCode.OK)
                    .WithHeader("Content-Type", "application/json; charset=utf-8")
                    .WithBody(expectedResponse)));

PactWriter writes to wrong path

Have MessagePactBuilder with pactDir set to @"c:\pactDir"
Call Builder.BuildAsync().Wait();
Expect : file created at c:\pactDir
Result: c:\pactDir empty and file created at c:\ with directory as prefix.

Solution:
Fix PactWriter line 34 from:
File.WriteAllText($"{pactDir}{pact.Consumer.Name}-{pact.Provider.Name}.json",
to:
File.WriteAllText($"{pactDir}{Path.DirectorySeparatorChar}{pact.Consumer.Name}-{pact.Provider.Name}.json",

PS. It will be nice to open repository for subbranhcing , so we will be able so submit pr's

Support asynchronous MessageBuilder VerifyConsumer

Hi @bartschotten .
What do you think about MessageBuilder:VerifyConsumer that supports async pattern?
Most of our calls to consumers are async
It will be nice to have additional
MessageBuilder VerifyConsumerAsync<T>(Func<Task,T> messageHandler)
Together with MessageBuilder:VerifyConsumerAsync<T>
Currently, im using Task.Wait/Task.Result inside the VerifyConsumer and async pattern will fit better.

Matching seems to fail when JToken evaluates types other than what's in the spec

Thanks for a great library, @bartschotten!

If I try to evaluate a provider message with a contract that has a matching rule on a field, and my test data evaluates into a type that is not supported by Pact v3 (like boolean or DateTime for example), matching always fail on type difference.

Given a consumer contract of

ComPact.Builders.V3.Pact.JsonContent.With(
  Some.Element.Named("MyField".WithTheExactValue(true.ToString())

matching rules always fail with

Property 'MyField' has a different type in the actual response. Expected value: True, actual value: True

probably because the JTokenType is evaluated into Boolean, but there is no boolean type in Pact v3. For JToken types that do not have their Pact equivalence, type matching should not be performed.

The same is true with DateTime types (JTokenType: Date).

Or have I misunderstood something somewhere?

HTTP 500 From Mock Server For V2 Consumer Tests

Hi, i'm trying to generate a V2 pact file as a consumer but i'm getting an exception when making a call to the mocked server and the client is getting back HTTP 500.

Any idea what's wrong? Thanks!

Exception:

System.Exception : Unable to get data
   at MyApp.PactTests.ApiClient.Get() in C:\Temp\Example\MyApp.PactTests\ConsumerTests\ApiDataTests.cs:line 81
   at MyApp.PactTests.ApiTests.get_api_data() in C:\Temp\Example\MyApp.PactTests\ConsumerTests\ApiDataTests.cs:line 58
--- End of stack trace from previous location where exception was thrown ---

[Test Class Cleanup Failure (MyApp.PactTests.ApiTests)]: System.ArgumentNullException : Value cannot be null. (Parameter 'source')
   at System.Linq.ThrowHelper.ThrowArgumentNullException(ExceptionArgument argument)
   at System.Linq.Enumerable.Any[TSource](IEnumerable`1 source)
   at ComPact.Models.V2.Response.SetEmptyValuesToNull() in C:\Temp\ComPact\Models\V2\Response.cs:line 25
   at ComPact.Models.V2.Interaction.SetEmptyValuesToNull() in C:\Temp\ComPact\Models\V2\Interaction.cs:line 29
   at ComPact.Models.V2.Contract.<>c.<SetEmptyValuesToNull>b__16_0(Interaction i) in C:\Temp\ComPact\Models\V2\Contract.cs:line 18
   at System.Collections.Generic.List`1.ForEach(Action`1 action)
   at ComPact.Models.V2.Contract.SetEmptyValuesToNull() in C:\Temp\ComPact\Models\V2\Contract.cs:line 18
   at ComPact.Builders.PactWriter.Write(IContract pact, String pactDir) in C:\Temp\ComPact\Builders\PactWriter.cs:line 24
   at ComPact.Builders.PactWriter.Write(IContract pact) in C:\Temp\ComPact\Builders\PactWriter.cs:line 19
   at ComPact.Builders.PactBuilderBase.BuildAsync(IContract pact) in C:\Temp\ComPact\Builders\PactBuilderBase.cs:line 74
   at ComPact.Builders.V2.PactBuilder.BuildAsync() in C:\Temp\ComPact\Builders\V2\PactBuilder.cs:line 46
   at MyApp.PactTests.ApiConsumerFixture.Dispose() in C:\Temp\Example\MyApp.PactTests\ConsumerTests\ApiDataTests.cs:line 27

Here's an simple example i've built of what i'm trying to do:

using System;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using ComPact.Builders.V2;
using ComPact.Models;
using Newtonsoft.Json;
using Xunit;

namespace MyApp.PactTests
{
    public class ApiConsumerFixture : IDisposable
    {
        public ApiClient _apiClient;

        public PactBuilder PactBuilder { get; }

        public ApiConsumerFixture()
        {
            const string mockServerBaseUrl = "http://localhost:5000";

            PactBuilder = new PactBuilder("my-consumer", "my-api", mockServerBaseUrl);

            _apiClient = new ApiClient(new HttpClient { BaseAddress = new Uri(mockServerBaseUrl) });
        }

        public void Dispose() => PactBuilder.BuildAsync().GetAwaiter().GetResult();
    }

    public class ApiTests : IClassFixture<ApiConsumerFixture>
    {
        private readonly PactBuilder _pactBuilder;
        private readonly ApiClient _apiClient;

        public ApiTests(ApiConsumerFixture fixture)
        {
            _apiClient = fixture._apiClient;

            _pactBuilder = fixture.PactBuilder;
            _pactBuilder.ClearInteractions();
        }

        [Fact]
        public async Task get_api_data()
        {
            _pactBuilder.SetUp(Pact.Interaction.Given(
                    $"An request for data")
                .UponReceiving("A get request for data")
                .With(Pact.Request.WithMethod(Method.GET)
                    .WithPath("/data"))
                .WillRespondWith(Pact.Response
                    .WithStatus((int) HttpStatusCode.OK)
                    .WithHeader("Content-Type", "application/json; charset=utf-8")
                    .WithBody(Pact.JsonContent
                        .With(Some.Element.Named("Id").WithTheExactValue("123456"))
                        .With(Some.Element.Named("SomeInt").WithTheExactValue("98765")))));

            await _apiClient.Get();
        }
    }

    public class ApiClient
    {
        private readonly HttpClient _httpClient;

        public ApiClient(HttpClient httpClient)
        {
            _httpClient = httpClient;
        }

        public async Task<ApiResponse> Get()
        {
            var request = new HttpRequestMessage(HttpMethod.Get, "/data");
            var response = await _httpClient.SendAsync(request);

            if (!response.IsSuccessStatusCode)
            {
                throw new Exception("Unable to get data");
            }

            var responseBody = await response.Content.ReadAsStringAsync();
            return JsonConvert.DeserializeObject<ApiResponse>(responseBody);
        }
    }

    public class ApiResponse
    {
        public string Id { get; set; }

        public int SomeInt { get; set; }
    }
}

Provider interactions failing when verifying body

Verifying interactions that check the body of the response seem to be failing when using .NET 3.1. Below is an exception from the test, that states the test passed, but then also failed. Debugging and stepping through, does show that the provider (controller) is returning this data during the test.

ComPact.Exceptions.PactVerificationException : A get request for data (passed)
A get request for data (failed):
- Property 'Id' was not present in the actual response.
- Property 'SomeInt' was not present in the actual response.
   at ComPact.Verifier.PactVerifier.VerifyPactAsync(String path)
   at DataApi.PactTests.ConsumerContractTests.VerifyInteractionWithClient() in C:\temp\Com-Pact-Demo\DataApi.PactTests\ContractConsumerTests.cs:line 43
--- End of stack trace from previous location where exception was thrown ---

I've put up an example project here which demostrates the issue: https://github.com/AshleyPoole/Com-Pact-Demo

The DataApi.PactTests (provider) are failing with the above exception. Tests that don't verify the body seem to pass.

Strings formatted as ISO datetimes get deserialized to DateTime instead of string

Serialization on producer creates json string with "mydate":"2020-04-01T00:00:00"
But when string deserialized back to object it will be DateTime mydate, so it will fail on type compare.
We can not change the deserialization , since it is hidden.
So, its not so clear for me, how to manage it from produce side.
The only way i see it by changing the contract from:
"$.ToDateUtc": { "matchers": [ { "match": "regex", "regex": "(\\d+)(?:-(\\d+))?" } ] }

to:

$.ToDateUtc": {
            "matchers": [
              {
                "match": "type"
              }
            ]
          }

In such case i will lose the regex validation but this is ok becouse it will try to serialize the string to DateTime and if string is not valid then it will fail.

Originally posted by @sfiodorov in #9 (comment)

Error message renders type instead of any relevant value

When setting the provider state failes, the exception message is

Could not set providerState 'System.Collections.Generic.List`1[ComPact.Models.ProviderState]'.
Got a NotFound response.

System.Collections.Generic.List`1[ComPact.Models.ProviderState] is not very helpful here.

Source:

throw new PactException($"Could not set providerState '{interaction.ProviderStates}'. Got a {providerStateResponse.StatusCode} response.");

Using Matchers in request body

Hello
I am trying to implement the API Consumer using matchers in the POST request body but the mock server is always responding with 500 (body is always comes as empty in the expected response) . Can anyone please guide me how can I implement the api consumer and provider using the Matchers in the request and response body ?

Thanks

Failed to call MockProvider due to synchronized I/O call

When trying to call the mock provider (for api consumer tests) I get the following error:

System.InvalidOperationException: Synchronous operations are disallowed. Call ReadAsync or set AllowSynchronousIO to true instead.
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpRequestStream.Read(Byte[] buffer, Int32 offset, Int32 count)
at System.IO.StreamReader.ReadBuffer()
at System.IO.StreamReader.ReadToEnd()
at ComPact.Models.V3.Request..ctor(HttpRequest request)
at ComPact.MockProvider.RequestResponseMatcher.MatchRequestAndReturnResponseAsync(HttpRequest httpRequest, HttpResponse httpResponseToReturn)
at ComPact.MockProvider.ProviderWebHost.<>c__DisplayClass0_0.<b__1>d.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol.ProcessRequests[TContext](IHttpApplication`1 application)

This is probably due to changes in .net core 3.0 that prohibit this behavior.

The problematic code is in the V3.Request ctor:

using (var streamReader = new StreamReader(request.Body, Encoding.UTF8))
{
                var serializedBody = streamReader.ReadToEnd();
                Body = JsonConvert.DeserializeObject<dynamic>(serializedBody);
}

The call to ReadToEnd is the culprit.
Can it be fixed to an async call ? Or set the AllowSynchronousIO to true on the WebHost ?

Thanks,
Rotem

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.