diff --git a/.cirrus.yml b/.cirrus.yml index c0cab564..43d58f3b 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -15,17 +15,17 @@ BUILD_TEST_TASK_TEMPLATE: &BUILD_TEST_TASK_TEMPLATE linux_arm64_task: arm_container: - image: mcr.microsoft.com/dotnet/sdk:7.0 + image: mcr.microsoft.com/dotnet/sdk:8.0 << : *BUILD_TEST_TASK_TEMPLATE linux_amd64_task: container: - image: mcr.microsoft.com/dotnet/sdk:7.0 + image: mcr.microsoft.com/dotnet/sdk:8.0 << : *BUILD_TEST_TASK_TEMPLATE linux_arm64_alpine_task: arm_container: - image: mcr.microsoft.com/dotnet/sdk:7.0-alpine + image: mcr.microsoft.com/dotnet/sdk:8.0-alpine setup_alpine_script: apk add --no-cache curl bash gzip # patch_cs_proj_script: | # # cat src/PactNet/PactNet.csproj | grep musl @@ -35,7 +35,7 @@ linux_arm64_alpine_task: linux_amd64_alpine_task: container: - image: mcr.microsoft.com/dotnet/sdk:7.0-alpine + image: mcr.microsoft.com/dotnet/sdk:8.0-alpine setup_alpine_script: apk add --no-cache curl bash gzip # patch_cs_proj_script: sed -Ei "s|x86_64\\\libpact_ffi.so|x86_64-musl\\\libpact_ffi.so|" src/PactNet/PactNet.csproj << : *BUILD_TEST_TASK_TEMPLATE \ No newline at end of file diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..50f231ee --- /dev/null +++ b/.gitattributes @@ -0,0 +1,4 @@ +* text=auto + +*.sh text eol=lf +*.sln text eol=crlf diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 00000000..c80bc2a8 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,32 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: bug, triage +assignees: '' + +--- + +**Previous issues** +Have you searched the issue tracker to ensure this hasn't been discussed before? + +**Version information:** + - OS: (e.g. MacOS ARM, Windows x64) + - PactNet Version: + - .Net Version: + - Pact Broker Version (if applicable): + +**Describe the bug** +A clear and concise description of what the bug is. + +**Steps To Reproduce** +Steps to reproduce the issue, including code snippets or preferably a link to a repository which reproduces the issue. + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Log Output** +Applicable log output. Please ensure you remove any sensitive information. + +**Additional context** +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 00000000..425d2329 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,29 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: 'RFC: ' +labels: triage +assignees: '' + +--- + +**Previous issues** +Have you searched the issue tracker to ensure this hasn't been discussed before? + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Breaking Changes** +Does your proposed change include anything which would require a major version bump? If so, please detail them here. Please read the SemVer spec if you are unsure what this means. + +**Potential Downsides/Caveats** +A clear and concise description of any downsides or caveats your change would introduce. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3afd594a..5c6349dc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,27 +19,36 @@ jobs: - ubuntu-latest - macos-12 alpine: [false] - arch: ["x86_64"] include: + - arch: x64 + - arch: ARM64 + os: macos-14 - os: ubuntu-latest alpine: true arch: x86_64 - os: ubuntu-latest alpine: true arch: aarch64 - - os: macos-14 - alpine: false - arch: aarch64 runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup .NET Core SDK - uses: actions/setup-dotnet@v3 + uses: actions/setup-dotnet@v4 with: - dotnet-version: "7.0.x" # runners already have .Net Framework installed as well - + dotnet-version: "8.0.x" # runners already have .Net Framework installed as well + + - name: Cache FFI dependencies + id: cache + uses: actions/cache@v4 + with: + key: cache-ffi-${{ hashFiles('build/download-native-libs.sh') }} + enableCrossOsArchive: true + path: | + build/linux + build/osx + build/windows - name: Set up QEMU if: matrix.alpine == true uses: docker/setup-qemu-action@v3 @@ -47,7 +56,7 @@ jobs: if: matrix.alpine == true uses: docker/setup-buildx-action@v3 - name: Pull interop dependencies - if: matrix.alpine != true + if: matrix.alpine != true && ${{ steps.cache.outputs.cache-hit != 'true' }} run: bash -c "build/download-native-libs.sh" - name: Restore @@ -60,7 +69,7 @@ jobs: - name: Test if: matrix.alpine != true - run: dotnet test --no-build --verbosity normal + run: dotnet test --no-build --verbosity normal -- RunConfiguration.TargetPlatform=${{matrix.arch}} # - name: Patch PactNet.csproj # if: matrix.alpine == true @@ -69,11 +78,11 @@ jobs: - name: test linux amd64 musl if: matrix.alpine == true && matrix.arch == 'x86_64' run: | - docker run --platform=linux/amd64 --rm -v $PWD:/app mcr.microsoft.com/dotnet/sdk:7.0-alpine /bin/sh -c 'apk add --no-cache curl bash gzip && cd /app && build/download-native-libs.sh && dotnet restore && dotnet build --no-restore && dotnet test --no-build --verbosity normal' + docker run --platform=linux/amd64 --rm -v $PWD:/app mcr.microsoft.com/dotnet/sdk:8.0-alpine /bin/sh -c 'apk add --no-cache curl bash gzip && cd /app && build/download-native-libs.sh && dotnet restore && dotnet build --no-restore && dotnet test --no-build --verbosity normal' - name: test linux arm64 musl if: matrix.alpine == true && matrix.arch == 'aarch64' run: | - docker run --platform=linux/arm64 --rm -v $PWD:/app mcr.microsoft.com/dotnet/sdk:7.0-alpine /bin/sh -c 'apk add --no-cache curl bash gzip && cd /app && build/download-native-libs.sh && dotnet restore && dotnet build --no-restore && dotnet test --no-build --verbosity normal' + docker run --platform=linux/arm64 --rm -v $PWD:/app mcr.microsoft.com/dotnet/sdk:8.0-alpine /bin/sh -c 'apk add --no-cache curl bash gzip && cd /app && build/download-native-libs.sh && dotnet restore && dotnet build --no-restore && dotnet test --no-build --verbosity normal' - name: Pack if: matrix.os == 'windows-latest' @@ -81,7 +90,7 @@ jobs: - name: Upload Artifact if: matrix.os == 'windows-latest' - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: nupkgs path: ./dist/*.* @@ -91,12 +100,12 @@ jobs: if: github.ref_type == 'tag' runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup .NET Core - uses: actions/setup-dotnet@v3 + uses: actions/setup-dotnet@v4 with: - dotnet-version: "7.0.x" # runners already have .Net Framework installed as well + dotnet-version: "8.0.x" # runners already have .Net Framework installed as well - name: Pull interop dependencies run: bash -c "build/download-native-libs.sh" diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..29ddb293 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,86 @@ +Contributing to PactNet +======================= + +Raising Issues +-------------- + +Please don't raise issues for general queries (e.g. usage queries) and instead check the [Pact Docs] site or ask on the [Pact Foundation Slack]. + +Before raising any issues, please make as much effort as you can to rule out issues in your own environment as much as possible. +For example, if you are using a self-hosted Pact Broker instance and PactNet is failing to connect, please ensure things like the +authentication token and SSL certificate are valid. + +If you are sure that the issye is with PactNet then please raise an issue, including as many of the following details as you can: + +- PactNet version +- Your operating system and version +- .Net version +- Log output +- Steps to reproduce and/or a repository link which reproduces the issue +- Expected outcome +- Actual outcome + +Due to the way that PactNet works, often issues that are found are not in PactNet itself but instead in the [pact-reference] +native FFI libraries. If this is the case then an upstream issue will be raised in the FFI repository and PactNet will have to +wait until the fix is available in a new FFI release. + +Raising Pull Requests +--------------------- + +For new contributors it is recommended that you start a discussion with a core maintainer prior to raising a PR. This +is for your own benefit so that you don't waste time implementing changes which don't align with the project in general +or will require significant changes afterwards. + +The best way to achieve this is to open an issue detailing: + +- The problem you see at the moment +- The solution you propose to fix this problem (e.g. adding a new feature, refactoring an existing API, etc) +- Any downsides you can foresee as a result of this change + +If an issue already exists for the change you wish to contribute, please comment on the existing issue. + +After raising your PR, a core maintainer will review your change and may request/suggest changes. Please take the +feedback in the spirit intended so that the PR can be merged as quickly as possible whilst still meeting established +conventions within the project such as architecture, code style, test style/coverage and API evolution. + +In particular it is much harder to make any changes which involve a breaking change, so please set expectations accordingly +if your change requires a new major version. A large and/or potentially disruptive change should typically take the form of +an RFC issue. + +A good example of an RFC issue preceding a major change is [PR 457]. + +Building PactNet +---------------- + +In order to build PactNet you must first download the native Rust FFI libraries. You can pull the current supported +version by executing the script in Bash (or Git Bash on Windows): + +```bash +build/download-native-libs.sh +``` + +Alternatively you can download a particular FFI version from the [pact-referece] releases or build your own version +locally, and then copy the artifacts into the folders: + +``` +build/ + linux/ + x86_64/ + libpact_ffi.so + osx/ + aarch64-apple-darwin/ + libpact_ffi.dylib + x86_64/ + libpact_ffi.dylib + windows/ + x86_64/ + pact_ffi.dll +``` + +After the native libraries are in the expected places then the solution can be built in your IDE or on the command +line using `dotnet build` as normal. + +[pact-reference]: https://github.com/pact-foundation/pact-reference/releases +[Pact Docs]: https://docs.pact.io/ +[Pact Foundation Slack]: https://pact-foundation.slack.com/ +[PR 457]: https://github.com/pact-foundation/pact-net/issues/457 diff --git a/README.md b/README.md index e5dce135..e257eb31 100644 --- a/README.md +++ b/README.md @@ -241,7 +241,7 @@ Due to using a shared native library instead of C# for the main Pact logic only | Linux (libc) | x64 | ✔️ Yes | | Linux (musl) | Any | ❌ [No](https://github.com/pact-foundation/pact-net/issues/374) | | OSX | x64 | ✔️ Yes | -| OSX | ARM (M1/M2) | ⚠️ [Alpha](https://github.com/pact-foundation/pact-net/issues/451) | +| OSX | ARM (M1/M2) | ✔️ Yes | ### Pact Specification diff --git a/build/download-native-libs.sh b/build/download-native-libs.sh index a4da8aa2..081d1207 100755 --- a/build/download-native-libs.sh +++ b/build/download-native-libs.sh @@ -1,8 +1,8 @@ #!/usr/bin/env bash set -euo pipefail -FFI_VERSION="0.4.16" -FFI_BASE_URL="https://github.com/you54f/pact-reference/releases/download/libpact_ffi-v$FFI_VERSION" +FFI_VERSION="0.4.17" +FFI_BASE_URL="https://github.com/pact-foundation/pact-reference/releases/download/libpact_ffi-v$FFI_VERSION" GREEN="\e[32m" YELLOW="\e[33m" @@ -47,6 +47,7 @@ download_native() { else sed -Ei "s|../release_artifacts/.+$|$path/$dest_file|" "$path/$dest_file.sha256" # sha256sum -c -s "$path/$dest_file.sha256" + sha256sum --check --quiet "$path/$dest_file.sha256" fi rm "$path/$dest_file.sha256" diff --git a/docs/messaging-pacts.md b/docs/messaging-pacts.md index a371b445..902d9261 100644 --- a/docs/messaging-pacts.md +++ b/docs/messaging-pacts.md @@ -13,7 +13,7 @@ types use two different names, for example "Stock Broker API" and "Stock Broker Sample ------ -See the [sample](../samples/Messaging/) for additional detail. +See the [sample](../samples/OrdersApi/) for additional detail. Consumer Tests -------------- @@ -26,16 +26,16 @@ In code, this is: ```csharp public class StockEventProcessorTests { - private readonly IMessagePactBuilderV3 messagePact; + private readonly IMessagePactBuilderV4 messagePact; public StockEventProcessorTests(ITestOutputHelper output) { - IPactV3 v3 = Pact.V3("Stock Event Consumer", "Stock Event Producer", new PactConfig + IPactV4 v4 = Pact.V4("Stock Event Consumer", "Stock Event Producer", new PactConfig { PactDir = "../../../pacts/", - DefaultJsonSettings = new JsonSerializerSettings + DefaultJsonSettings = new JsonSerializerOptions { - ContractResolver = new CamelCasePropertyNamesContractResolver() + PropertyNamingPolicy = JsonNamingPolicy.CamelCase }, Outputters = new[] { @@ -43,7 +43,7 @@ public class StockEventProcessorTests } }); - this.messagePact = v3.WithMessageInteractions(); + this.messagePact = v4.WithMessageInteractions(); } [Fact] @@ -119,16 +119,13 @@ public class StockEventGeneratorTests : IDisposable "pacts", "Stock Event Consumer-Stock Event Producer.json"); - var defaultSettings = new JsonSerializerSettings + var defaultSettings = new JsonSerializerOptions { - ContractResolver = new CamelCasePropertyNamesContractResolver(), - DefaultValueHandling = DefaultValueHandling.Ignore, - NullValueHandling = NullValueHandling.Ignore, - Formatting = Formatting.Indented + PropertyNamingPolicy = JsonNamingPolicy.CamelCase }; this.verifier - .MessagingProvider(scenarios => + .WithMessages(scenarios => { // register the responses to each interaction // the descriptions must match those in the pact file(s) diff --git a/docs/upgrading-to-4.md b/docs/upgrading-to-4.md index 769e0e6d..a393f3ba 100644 --- a/docs/upgrading-to-4.md +++ b/docs/upgrading-to-4.md @@ -34,7 +34,7 @@ v4.x is: ```csharp public class ConsumerTests { - private readonly IPactBuilderV3 pact; + private readonly IPactBuilderV4 pact; public ConsumerTests(ITestOutputHelper output) { @@ -45,14 +45,14 @@ public class ConsumerTests { new XUnitOutput(output) }, - DefaultJsonSettings = new JsonSerializerSettings + DefaultJsonSettings = new JsonSerializerOptions { - ContractResolver = new CamelCasePropertyNamesContractResolver() + PropertyNamingPolicy = JsonNamingPolicy.CamelCase, } }; - // you select which specification version you wish to use by calling either V2 or V3 - IPactV3 pact = Pact.V3("My Consumer", "My Provider", config); + // you select which specification version you wish to use by calling V2, V3 or V4 + IPactV4 pact = Pact.V4("My Consumer", "My Provider", config); // the pact builder is created in the constructor so it's unique to each test this.pact = pact.UsingNativeBackend(); @@ -171,18 +171,16 @@ public class EventApiTests : IClassFixture<ProviderFixture> string branch = Environment.GetEnvironmentVariable("BRANCH"); string buildUri = Environment.GetEnvironmentVariable("BUILD_URL"); - var config = new PactVerifierConfig + var verifier = new PactVerifier("My Provider", new PactVerifierConfig { LogLevel = PactLogLevel.Information, Outputters = new List<IOutput> { new XUnitOutput(this.output) } - }; - - IPactVerifier verifier = new PactVerifier(config); + }); - verifier.ServiceProvider("My Provider", this.fixture.ServerUri) + verifier.WithHttpEndpoint(this.fixture.ServerUri) .WithPactBrokerSource(new Uri("https://broker.example.org"), options => { options.ConsumerVersionSelectors(new ConsumerVersionSelector { MainBranch = true, Latest = true }) diff --git a/docs/upgrading-to-5.md b/docs/upgrading-to-5.md index 3ce50e8f..8e9d4dd0 100644 --- a/docs/upgrading-to-5.md +++ b/docs/upgrading-to-5.md @@ -68,3 +68,67 @@ verifier .WithFileSource(new FileInfo(@"...")) .Verify(); ``` + +Replace Newtonsoft with System.Text.Json +---------------------------------------- + +See: [RFC 458](https://github.com/pact-foundation/pact-net/issues/458) + +The dominant web framework in .Net is now ASP.Net Core, which uses the `System.Text.Json` serialiser by default instead of the +previously-favoured `Newtonsoft.Json` library. In order to reduce friction when using the now-default JSON serialiser, PactNet +now uses `System.Text.Json` for serialisation. + +The main implication of this change is that any API previously referencing `JsonSerializerSettings` will now use `JsonSerializerOptions` +instead. Whenever PactNet serialises one of your types (e.g. during message interaction definitions) it will now be serialised using +`System.text.Json` instead of `Newtonsoft`, and so you may need to update any annotations on your types. Previously you likely had to +annotate your types with both types of annotation so that both ASP.Net Core and PactNet could serialise them properly, whereas now only +the `System.Text.Json` annotations will be needed in the vast majority of cases. + +For example, the following messaging interaction test has the same API as before but is a breaking change because it will now use +`System.Text.Json` to deserialise: + +```csharp +[Fact] +public async Task OnMessageAsync_OrderCreated_HandlesMessage() +{ + await this.pact + .ExpectsToReceive("an event indicating that an order has been created") + // BREAKING CHANGE: This will be serialised to the Pact file using System.Text.Json + .WithJsonContent(new + { + Id = Match.Integer(1) + }) + // BREAKING CHANGE: OrderCreatedEvent will now be deserialised from the message definition above using System.Text.Json + .VerifyAsync<OrderCreatedEvent>(async message => + { + await this.consumer.OnMessageAsync(message); + + this.mockService.Verify(s => s.FulfilOrderAsync(message.Id)); + }); +} +``` + +When verifying messages, the serialisation will also use `System.Text.Json`: + +```csharp +var verifier = new PactVerifier("My API"); + +verifier.WithMessages(scenarios => + { + // BREAKING CHANGE: The messaging response body will be serialised using System.Text.Json + scenarios.Add<MyEvent>("an event happens") + }) + .WithFileSource(new FileInfo(@"...")) + .Verify(); +``` + +Minimum Supported .Net Framework Version +---------------------------------------- + +The minimum supported version of .Net Framework is now 4.6.2 instead of 4.6.1 in line with the minimum supported version in +`System.Text.Json`. + +MacOS ARM64 Full Support +------------------------ + +MacOS now has full x86-64 and ARM support. diff --git a/samples/OrdersApi/Consumer.Tests/Consumer.Tests.csproj b/samples/OrdersApi/Consumer.Tests/Consumer.Tests.csproj index 12820d7e..a88109dd 100644 --- a/samples/OrdersApi/Consumer.Tests/Consumer.Tests.csproj +++ b/samples/OrdersApi/Consumer.Tests/Consumer.Tests.csproj @@ -1,14 +1,14 @@ <Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> - <TargetFramework>net7.0</TargetFramework> + <TargetFramework>net8.0</TargetFramework> <IsPackable>false</IsPackable> </PropertyGroup> <ItemGroup> - <PackageReference Include="FluentAssertions" Version="6.11.0" /> - <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.6.3" /> - <PackageReference Include="Moq" Version="4.18.4" /> - <PackageReference Include="xunit" Version="2.4.1" /> - <PackageReference Include="xunit.runner.visualstudio" Version="2.4.3"> + <PackageReference Include="FluentAssertions" Version="6.12.0" /> + <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0" /> + <PackageReference Include="Moq" Version="4.20.70" /> + <PackageReference Include="xunit" Version="2.6.6" /> + <PackageReference Include="xunit.runner.visualstudio" Version="2.5.6"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> </PackageReference> diff --git a/samples/OrdersApi/Consumer.Tests/OrderCreatedConsumerTests.cs b/samples/OrdersApi/Consumer.Tests/OrderCreatedConsumerTests.cs index d862e910..14784ecc 100644 --- a/samples/OrdersApi/Consumer.Tests/OrderCreatedConsumerTests.cs +++ b/samples/OrdersApi/Consumer.Tests/OrderCreatedConsumerTests.cs @@ -1,7 +1,6 @@ -using System.Threading.Tasks; +using System.Text.Json; +using System.Threading.Tasks; using Moq; -using Newtonsoft.Json; -using Newtonsoft.Json.Serialization; using PactNet; using PactNet.Output.Xunit; using Xunit; @@ -29,9 +28,10 @@ public OrderCreatedConsumerTests(ITestOutputHelper output) { new XunitOutput(output) }, - DefaultJsonSettings = new JsonSerializerSettings + DefaultJsonSettings = new JsonSerializerOptions { - ContractResolver = new CamelCasePropertyNamesContractResolver() + PropertyNamingPolicy = JsonNamingPolicy.CamelCase, + PropertyNameCaseInsensitive = true } }; diff --git a/samples/OrdersApi/Consumer.Tests/OrdersClientTests.cs b/samples/OrdersApi/Consumer.Tests/OrdersClientTests.cs index a134fc27..a9127c1b 100644 --- a/samples/OrdersApi/Consumer.Tests/OrdersClientTests.cs +++ b/samples/OrdersApi/Consumer.Tests/OrdersClientTests.cs @@ -3,12 +3,11 @@ using System.Net; using System.Net.Http; using System.Net.Http.Headers; +using System.Text.Json; +using System.Text.Json.Serialization; using System.Threading.Tasks; using FluentAssertions; using Moq; -using Newtonsoft.Json; -using Newtonsoft.Json.Converters; -using Newtonsoft.Json.Serialization; using PactNet; using PactNet.Output.Xunit; using Xunit; @@ -33,10 +32,11 @@ public OrdersClientTests(ITestOutputHelper output) { new XunitOutput(output) }, - DefaultJsonSettings = new JsonSerializerSettings + DefaultJsonSettings = new JsonSerializerOptions { - ContractResolver = new CamelCasePropertyNamesContractResolver(), - Converters = new JsonConverter[] { new StringEnumConverter() } + PropertyNamingPolicy = JsonNamingPolicy.CamelCase, + PropertyNameCaseInsensitive = true, + Converters = { new JsonStringEnumConverter() } }, LogLevel = PactLogLevel.Debug }; diff --git a/samples/OrdersApi/Consumer.Tests/pacts/Fulfilment API-Orders API.json b/samples/OrdersApi/Consumer.Tests/pacts/Fulfilment API-Orders API.json index 3ba0dd51..6f9f4dd6 100644 --- a/samples/OrdersApi/Consumer.Tests/pacts/Fulfilment API-Orders API.json +++ b/samples/OrdersApi/Consumer.Tests/pacts/Fulfilment API-Orders API.json @@ -158,7 +158,7 @@ ], "metadata": { "pactRust": { - "ffi": "0.4.5", + "ffi": "0.4.17", "models": "1.1.19" }, "pactSpecification": { diff --git a/samples/OrdersApi/Consumer/Consumer.csproj b/samples/OrdersApi/Consumer/Consumer.csproj index 3d605098..dd76b54a 100644 --- a/samples/OrdersApi/Consumer/Consumer.csproj +++ b/samples/OrdersApi/Consumer/Consumer.csproj @@ -1,9 +1,9 @@ <Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> - <TargetFramework>net7.0</TargetFramework> + <TargetFramework>net8.0</TargetFramework> <IsPackable>false</IsPackable> </PropertyGroup> <ItemGroup> - <PackageReference Include="Microsoft.Extensions.Http" Version="7.0.0" /> + <PackageReference Include="Microsoft.Extensions.Http" Version="8.0.0" /> </ItemGroup> </Project> diff --git a/samples/OrdersApi/Provider.Tests/Provider.Tests.csproj b/samples/OrdersApi/Provider.Tests/Provider.Tests.csproj index 58da5a5e..1517e0f4 100644 --- a/samples/OrdersApi/Provider.Tests/Provider.Tests.csproj +++ b/samples/OrdersApi/Provider.Tests/Provider.Tests.csproj @@ -1,13 +1,13 @@ <Project Sdk="Microsoft.NET.Sdk.Web"> <PropertyGroup> - <TargetFramework>net7.0</TargetFramework> + <TargetFramework>net8.0</TargetFramework> <IsPackable>false</IsPackable> </PropertyGroup> <ItemGroup> - <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.6.3" /> - <PackageReference Include="xunit" Version="2.4.1" /> - <PackageReference Include="xunit.runner.visualstudio" Version="2.4.3"> + <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0" /> + <PackageReference Include="xunit" Version="2.6.6" /> + <PackageReference Include="xunit.runner.visualstudio" Version="2.5.6"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> </PackageReference> diff --git a/samples/OrdersApi/Provider.Tests/ProviderTests.cs b/samples/OrdersApi/Provider.Tests/ProviderTests.cs index e508eafc..1feb302e 100644 --- a/samples/OrdersApi/Provider.Tests/ProviderTests.cs +++ b/samples/OrdersApi/Provider.Tests/ProviderTests.cs @@ -1,10 +1,9 @@ using System; using System.Collections.Generic; using System.IO; +using System.Text.Json; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Hosting; -using Newtonsoft.Json; -using Newtonsoft.Json.Serialization; using PactNet; using PactNet.Infrastructure.Outputters; using PactNet.Output.Xunit; @@ -19,9 +18,10 @@ public class ProviderTests : IDisposable { private static readonly Uri ProviderUri = new("http://localhost:5000"); - private static readonly JsonSerializerSettings Options = new() + private static readonly JsonSerializerOptions Options = new() { - ContractResolver = new CamelCasePropertyNamesContractResolver() + PropertyNamingPolicy = JsonNamingPolicy.CamelCase, + PropertyNameCaseInsensitive = true }; private readonly IHost server; diff --git a/samples/OrdersApi/Provider/Orders/OrdersController.cs b/samples/OrdersApi/Provider/Orders/OrdersController.cs index ef62664e..3bfb5a7b 100644 --- a/samples/OrdersApi/Provider/Orders/OrdersController.cs +++ b/samples/OrdersApi/Provider/Orders/OrdersController.cs @@ -31,7 +31,7 @@ public OrdersController(IOrderRepository orders) /// <returns>Order</returns> /// <response code="200">Order</response> /// <response code="404">Unknown order</response> - [HttpGet("{id}")] + [HttpGet("{id}", Name = "get")] [ProducesResponseType(typeof(OrderDto), StatusCodes.Status200OK)] public async Task<IActionResult> GetByIdAsync(int id) { @@ -60,7 +60,7 @@ public async Task<IActionResult> CreateAsync() await this.orders.InsertAsync(order); - return this.CreatedAtAction(nameof(GetByIdAsync), new { id = order.Id }, order); + return this.CreatedAtRoute("get", new { id = order.Id }, order); } /// <summary> diff --git a/samples/OrdersApi/Provider/Provider.csproj b/samples/OrdersApi/Provider/Provider.csproj index 6b479978..aa473797 100644 --- a/samples/OrdersApi/Provider/Provider.csproj +++ b/samples/OrdersApi/Provider/Provider.csproj @@ -1,7 +1,7 @@ <Project Sdk="Microsoft.NET.Sdk.Web"> <PropertyGroup> - <TargetFramework>net7.0</TargetFramework> + <TargetFramework>net8.0</TargetFramework> <IsPackable>false</IsPackable> </PropertyGroup> diff --git a/src/NuGet.targets b/src/NuGet.targets index 5d6cfaa0..03437512 100644 --- a/src/NuGet.targets +++ b/src/NuGet.targets @@ -17,10 +17,14 @@ <VersionPrefix>5.0.0</VersionPrefix> <PackageReleaseNotes> v5.0.0 - - Add Pact Specification v4 support - - BREAKING CHANGE: Remove obsolete WithNativeBackend calls - - BREAKING CHANGE: Remove obsolete IMessagePact and MessagePact - - BREAKING CHANGE: Refactor verifier to support verifying combined HTTP and message pacts + - BREAKING CHANGE: Remove obsolete WithNativeBackend calls + - BREAKING CHANGE: Remove obsolete IMessagePact and MessagePact + - BREAKING CHANGE: Refactor verifier to support verifying combined HTTP and message pacts + - BREAKING CHANGE: Replace Newtonsoft with System.Text.Json + - BREAKING CHANGE: Minimum supported .Net Framework version is now 4.6.2 instead of 4.6.1 + - feat: Add Pact Specification v4 support + - feat: MacOS ARM64 target is now fully supported + - feat: More efficient and robust messaging interaction verification </PackageReleaseNotes> </PropertyGroup> <PropertyGroup Condition="'$(GITHUB_ACTIONS)' == 'true'"> @@ -28,7 +32,7 @@ <EmbedUntrackedSources>true</EmbedUntrackedSources> </PropertyGroup> <ItemGroup> - <PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1" PrivateAssets="All" /> + <PackageReference Include="Microsoft.SourceLink.GitHub" Version="8.0.0" PrivateAssets="All" /> </ItemGroup> <ItemGroup> <None Include="..\..\docs\logo-128x128.png" Pack="true" PackagePath="\" Visible="false" /> diff --git a/src/PactNet.Abstractions/IMessageBuilder.cs b/src/PactNet.Abstractions/IMessageBuilder.cs index 81a97029..22ddacf7 100644 --- a/src/PactNet.Abstractions/IMessageBuilder.cs +++ b/src/PactNet.Abstractions/IMessageBuilder.cs @@ -1,5 +1,5 @@ using System.Collections.Generic; -using Newtonsoft.Json; +using System.Text.Json; namespace PactNet { @@ -44,7 +44,7 @@ public interface IMessageBuilderV3 /// <param name="body">Message body</param> /// <param name="settings">Custom JSON serializer settings</param> /// <returns>Configured message</returns> - IConfiguredMessageVerifier WithJsonContent(dynamic body, JsonSerializerSettings settings); + IConfiguredMessageVerifier WithJsonContent(dynamic body, JsonSerializerOptions settings); } /// <summary> @@ -88,6 +88,6 @@ public interface IMessageBuilderV4 /// <param name="body">Message body</param> /// <param name="settings">Custom JSON serializer settings</param> /// <returns>Configured message</returns> - IConfiguredMessageVerifier WithJsonContent(dynamic body, JsonSerializerSettings settings); + IConfiguredMessageVerifier WithJsonContent(dynamic body, JsonSerializerOptions settings); } } diff --git a/src/PactNet.Abstractions/IRequestBuilder.cs b/src/PactNet.Abstractions/IRequestBuilder.cs index c4bf4196..357ea8ab 100644 --- a/src/PactNet.Abstractions/IRequestBuilder.cs +++ b/src/PactNet.Abstractions/IRequestBuilder.cs @@ -1,6 +1,6 @@ using System.Collections.Generic; using System.Net.Http; -using Newtonsoft.Json; +using System.Text.Json; using PactNet.Matchers; namespace PactNet @@ -79,7 +79,7 @@ public interface IRequestBuilderV2 /// <param name="body">Request body</param> /// <param name="settings">Custom JSON serializer settings</param> /// <returns>Fluent builder</returns> - IRequestBuilderV2 WithJsonBody(dynamic body, JsonSerializerSettings settings); + IRequestBuilderV2 WithJsonBody(dynamic body, JsonSerializerOptions settings); /// <summary> /// Set a body which is serialised as JSON @@ -88,7 +88,7 @@ public interface IRequestBuilderV2 /// <param name="settings">Custom JSON serializer settings</param> /// <param name="contentType">Content type override</param> /// <returns>Fluent builder</returns> - IRequestBuilderV2 WithJsonBody(dynamic body, JsonSerializerSettings settings, string contentType); + IRequestBuilderV2 WithJsonBody(dynamic body, JsonSerializerOptions settings, string contentType); /// <summary> /// A pre-formatted body which should be used as-is for the request @@ -196,7 +196,7 @@ public interface IRequestBuilderV3 /// <param name="body">Request body</param> /// <param name="settings">Custom JSON serializer settings</param> /// <returns>Fluent builder</returns> - IRequestBuilderV3 WithJsonBody(dynamic body, JsonSerializerSettings settings); + IRequestBuilderV3 WithJsonBody(dynamic body, JsonSerializerOptions settings); /// <summary> /// Set a body which is serialised as JSON @@ -205,7 +205,7 @@ public interface IRequestBuilderV3 /// <param name="settings">Custom JSON serializer settings</param> /// <param name="contentType">Content type override</param> /// <returns>Fluent builder</returns> - IRequestBuilderV3 WithJsonBody(dynamic body, JsonSerializerSettings settings, string contentType); + IRequestBuilderV3 WithJsonBody(dynamic body, JsonSerializerOptions settings, string contentType); /// <summary> /// A pre-formatted body which should be used as-is for the request @@ -315,7 +315,7 @@ public interface IRequestBuilderV4 /// <param name="body">Request body</param> /// <param name="settings">Custom JSON serializer settings</param> /// <returns>Fluent builder</returns> - IRequestBuilderV4 WithJsonBody(dynamic body, JsonSerializerSettings settings); + IRequestBuilderV4 WithJsonBody(dynamic body, JsonSerializerOptions settings); /// <summary> /// Set a body which is serialised as JSON @@ -324,7 +324,7 @@ public interface IRequestBuilderV4 /// <param name="settings">Custom JSON serializer settings</param> /// <param name="contentType">Content type override</param> /// <returns>Fluent builder</returns> - IRequestBuilderV4 WithJsonBody(dynamic body, JsonSerializerSettings settings, string contentType); + IRequestBuilderV4 WithJsonBody(dynamic body, JsonSerializerOptions settings, string contentType); /// <summary> /// A pre-formatted body which should be used as-is for the request diff --git a/src/PactNet.Abstractions/IResponseBuilder.cs b/src/PactNet.Abstractions/IResponseBuilder.cs index 2b4cbc3a..c15e1713 100644 --- a/src/PactNet.Abstractions/IResponseBuilder.cs +++ b/src/PactNet.Abstractions/IResponseBuilder.cs @@ -1,5 +1,5 @@ using System.Net; -using Newtonsoft.Json; +using System.Text.Json; using PactNet.Matchers; namespace PactNet @@ -52,7 +52,7 @@ public interface IResponseBuilderV2 /// <param name="body">Response body</param> /// <param name="settings">Custom JSON serializer settings</param> /// <returns>Fluent builder</returns> - IResponseBuilderV2 WithJsonBody(dynamic body, JsonSerializerSettings settings); + IResponseBuilderV2 WithJsonBody(dynamic body, JsonSerializerOptions settings); /// <summary> /// A pre-formatted body which should be used as-is for the response @@ -111,7 +111,7 @@ public interface IResponseBuilderV3 /// <param name="body">Response body</param> /// <param name="settings">Custom JSON serializer settings</param> /// <returns>Fluent builder</returns> - IResponseBuilderV3 WithJsonBody(dynamic body, JsonSerializerSettings settings); + IResponseBuilderV3 WithJsonBody(dynamic body, JsonSerializerOptions settings); /// <summary> /// A pre-formatted body which should be used as-is for the response @@ -170,7 +170,7 @@ public interface IResponseBuilderV4 /// <param name="body">Response body</param> /// <param name="settings">Custom JSON serializer settings</param> /// <returns>Fluent builder</returns> - IResponseBuilderV4 WithJsonBody(dynamic body, JsonSerializerSettings settings); + IResponseBuilderV4 WithJsonBody(dynamic body, JsonSerializerOptions settings); /// <summary> /// A pre-formatted body which should be used as-is for the response diff --git a/src/PactNet.Abstractions/Matchers/DecimalMatcher.cs b/src/PactNet.Abstractions/Matchers/DecimalMatcher.cs index 030460ef..e2be5a03 100644 --- a/src/PactNet.Abstractions/Matchers/DecimalMatcher.cs +++ b/src/PactNet.Abstractions/Matchers/DecimalMatcher.cs @@ -1,3 +1,5 @@ +using System.Text.Json.Serialization; + namespace PactNet.Matchers { /// <summary> @@ -8,11 +10,13 @@ public class DecimalMatcher : IMatcher /// <summary> /// Type of the matcher /// </summary> + [JsonPropertyName("pact:matcher:type")] public string Type => "decimal"; /// <summary> /// Matcher value /// </summary> + [JsonPropertyName("value")] public dynamic Value { get; } /// <summary> diff --git a/src/PactNet.Abstractions/Matchers/EqualityMatcher.cs b/src/PactNet.Abstractions/Matchers/EqualityMatcher.cs index b07c9c30..b6d9a116 100644 --- a/src/PactNet.Abstractions/Matchers/EqualityMatcher.cs +++ b/src/PactNet.Abstractions/Matchers/EqualityMatcher.cs @@ -1,3 +1,5 @@ +using System.Text.Json.Serialization; + namespace PactNet.Matchers { /// <summary> @@ -8,11 +10,13 @@ public class EqualityMatcher : IMatcher /// <summary> /// Type of the matcher /// </summary> + [JsonPropertyName("pact:matcher:type")] public string Type => "equality"; /// <summary> /// Matcher value /// </summary> + [JsonPropertyName("value")] public dynamic Value { get; } /// <summary> diff --git a/src/PactNet.Abstractions/Matchers/IMatcher.cs b/src/PactNet.Abstractions/Matchers/IMatcher.cs index 01368682..550f7c6e 100644 --- a/src/PactNet.Abstractions/Matchers/IMatcher.cs +++ b/src/PactNet.Abstractions/Matchers/IMatcher.cs @@ -1,22 +1,25 @@ -using Newtonsoft.Json; +using System.Text.Json.Serialization; namespace PactNet.Matchers { /// <summary> /// Matcher /// </summary> + [JsonConverter(typeof(MatcherConverter))] public interface IMatcher { /// <summary> /// Type of the matcher /// </summary> - [JsonProperty("pact:matcher:type")] + + [JsonPropertyName("pact:matcher:type")] string Type { get; } /// <summary> /// Matcher value /// </summary> - [JsonProperty("value")] + + [JsonPropertyName("value")] dynamic Value { get; } } } diff --git a/src/PactNet.Abstractions/Matchers/IncludeMatcher.cs b/src/PactNet.Abstractions/Matchers/IncludeMatcher.cs index 43a089a5..80b292fe 100644 --- a/src/PactNet.Abstractions/Matchers/IncludeMatcher.cs +++ b/src/PactNet.Abstractions/Matchers/IncludeMatcher.cs @@ -1,3 +1,5 @@ +using System.Text.Json.Serialization; + namespace PactNet.Matchers { /// <summary> @@ -8,11 +10,13 @@ public class IncludeMatcher : IMatcher /// <summary> /// Type of the matcher /// </summary> + [JsonPropertyName("pact:matcher:type")] public string Type => "include"; /// <summary> /// Matcher value /// </summary> + [JsonPropertyName("value")] public dynamic Value { get; } /// <summary> diff --git a/src/PactNet.Abstractions/Matchers/IntegerMatcher.cs b/src/PactNet.Abstractions/Matchers/IntegerMatcher.cs index dfc4c9d7..b23b6a2d 100644 --- a/src/PactNet.Abstractions/Matchers/IntegerMatcher.cs +++ b/src/PactNet.Abstractions/Matchers/IntegerMatcher.cs @@ -1,3 +1,5 @@ +using System.Text.Json.Serialization; + namespace PactNet.Matchers { /// <summary> @@ -8,11 +10,13 @@ public class IntegerMatcher : IMatcher /// <summary> /// Type of the matcher /// </summary> + [JsonPropertyName("pact:matcher:type")] public string Type => "integer"; /// <summary> /// Matcher value /// </summary> + [JsonPropertyName("value")] public dynamic Value { get; } /// <summary> @@ -24,4 +28,4 @@ internal IntegerMatcher(int value) this.Value = value; } } -} \ No newline at end of file +} diff --git a/src/PactNet.Abstractions/Matchers/Match.cs b/src/PactNet.Abstractions/Matchers/Match.cs index 6ac0b679..7531fb29 100644 --- a/src/PactNet.Abstractions/Matchers/Match.cs +++ b/src/PactNet.Abstractions/Matchers/Match.cs @@ -1,5 +1,8 @@ namespace PactNet.Matchers { + /// <summary> + /// Supported Pact matchers + /// </summary> public static class Match { /// <summary> diff --git a/src/PactNet.Abstractions/Matchers/MatcherConverter.cs b/src/PactNet.Abstractions/Matchers/MatcherConverter.cs new file mode 100644 index 00000000..e6493de3 --- /dev/null +++ b/src/PactNet.Abstractions/Matchers/MatcherConverter.cs @@ -0,0 +1,60 @@ +using System; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace PactNet.Matchers +{ + /// <summary> + /// Converter for <see cref="IMatcher"/> + /// </summary> + internal class MatcherConverter : JsonConverter<IMatcher> + { + /// <summary>Reads and converts the JSON to type <see cref="IMatcher"/>.</summary> + /// <param name="reader">The reader.</param> + /// <param name="typeToConvert">The type to convert.</param> + /// <param name="options">An object that specifies serialization options to use.</param> + /// <returns>The converted value.</returns> + public override IMatcher Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + => throw new NotSupportedException("Matchers can only be written, not read"); + + /// <summary>Writes a specified value as JSON.</summary> + /// <param name="writer">The writer to write to.</param> + /// <param name="value">The value to convert to JSON.</param> + /// <param name="options">An object that specifies serialization options to use.</param> + public override void Write(Utf8JsonWriter writer, IMatcher value, JsonSerializerOptions options) + { + switch (value) + { + case DecimalMatcher matcher: + JsonSerializer.Serialize(writer, matcher, options); + break; + case EqualityMatcher matcher: + JsonSerializer.Serialize(writer, matcher, options); + break; + case IncludeMatcher matcher: + JsonSerializer.Serialize(writer, matcher, options); + break; + case IntegerMatcher matcher: + JsonSerializer.Serialize(writer, matcher, options); + break; + case MinMaxTypeMatcher matcher: + JsonSerializer.Serialize(writer, matcher, options); + break; + case NullMatcher matcher: + JsonSerializer.Serialize(writer, matcher, options); + break; + case NumericMatcher matcher: + JsonSerializer.Serialize(writer, matcher, options); + break; + case RegexMatcher matcher: + JsonSerializer.Serialize(writer, matcher, options); + break; + case TypeMatcher matcher: + JsonSerializer.Serialize(writer, matcher, options); + break; + default: + throw new ArgumentOutOfRangeException($"Unsupported matcher: {value.GetType()}"); + } + } + } +} diff --git a/src/PactNet.Abstractions/Matchers/MinMaxTypeMatcher.cs b/src/PactNet.Abstractions/Matchers/MinMaxTypeMatcher.cs index 53394d3b..6895d35f 100644 --- a/src/PactNet.Abstractions/Matchers/MinMaxTypeMatcher.cs +++ b/src/PactNet.Abstractions/Matchers/MinMaxTypeMatcher.cs @@ -1,5 +1,5 @@ using System; -using Newtonsoft.Json; +using System.Text.Json.Serialization; namespace PactNet.Matchers { @@ -8,14 +8,30 @@ namespace PactNet.Matchers /// </summary> public class MinMaxTypeMatcher : IMatcher { + /// <summary> + /// Type of the matcher + /// </summary> + [JsonPropertyName("pact:matcher:type")] public string Type => "type"; + /// <summary> + /// Matcher value + /// </summary> + [JsonPropertyName("value")] public dynamic Value { get; } - [JsonProperty(PropertyName = "min", DefaultValueHandling = DefaultValueHandling.Ignore)] + /// <summary> + /// Minimum items + /// </summary> + [JsonPropertyName("min")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public int Min { get; set; } - [JsonProperty(PropertyName = "max", DefaultValueHandling = DefaultValueHandling.Ignore)] + /// <summary> + /// Maximum items + /// </summary> + [JsonPropertyName("max")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public int Max { get; set; } /// <summary> diff --git a/src/PactNet.Abstractions/Matchers/NullMatcher.cs b/src/PactNet.Abstractions/Matchers/NullMatcher.cs index 122af7db..69c7c737 100644 --- a/src/PactNet.Abstractions/Matchers/NullMatcher.cs +++ b/src/PactNet.Abstractions/Matchers/NullMatcher.cs @@ -1,4 +1,4 @@ -using Newtonsoft.Json; +using System.Text.Json.Serialization; namespace PactNet.Matchers { @@ -10,6 +10,7 @@ public class NullMatcher : IMatcher /// <summary> /// Type of the matcher /// </summary> + [JsonPropertyName("pact:matcher:type")] public string Type => "null"; /// <summary> diff --git a/src/PactNet.Abstractions/Matchers/NumericMatcher.cs b/src/PactNet.Abstractions/Matchers/NumericMatcher.cs index 6ab4af95..102dd155 100644 --- a/src/PactNet.Abstractions/Matchers/NumericMatcher.cs +++ b/src/PactNet.Abstractions/Matchers/NumericMatcher.cs @@ -1,3 +1,5 @@ +using System.Text.Json.Serialization; + namespace PactNet.Matchers { /// <summary> @@ -8,11 +10,13 @@ public class NumericMatcher : IMatcher /// <summary> /// Type of the matcher /// </summary> + [JsonPropertyName("pact:matcher:type")] public string Type => "number"; /// <summary> /// Matcher value /// </summary> + [JsonPropertyName("value")] public dynamic Value { get; } /// <summary> diff --git a/src/PactNet.Abstractions/Matchers/RegexMatcher.cs b/src/PactNet.Abstractions/Matchers/RegexMatcher.cs index 97ece050..77f2a720 100644 --- a/src/PactNet.Abstractions/Matchers/RegexMatcher.cs +++ b/src/PactNet.Abstractions/Matchers/RegexMatcher.cs @@ -1,20 +1,39 @@ -using Newtonsoft.Json; +using System.Text.Json.Serialization; namespace PactNet.Matchers { + /// <summary> + /// Match a string by regex + /// </summary> public class RegexMatcher : IMatcher { + /// <summary> + /// Type of the matcher + /// </summary> + [JsonPropertyName("pact:matcher:type")] public string Type => "regex"; + /// <summary> + /// Matcher value + /// </summary> + [JsonPropertyName("value")] public dynamic Value { get; } - [JsonProperty("regex")] + /// <summary> + /// Matcher regex + /// </summary> + [JsonPropertyName("regex")] public string Regex { get; } + /// <summary> + /// Initialises a new instance of the <see cref="RegexMatcher"/> class. + /// </summary> + /// <param name="example">Example value</param> + /// <param name="regex">Regex</param> internal RegexMatcher(string example, string regex) { this.Regex = regex; this.Value = example; } } -} \ No newline at end of file +} diff --git a/src/PactNet.Abstractions/Matchers/TypeMatcher.cs b/src/PactNet.Abstractions/Matchers/TypeMatcher.cs index e41e4f41..b8c7cb64 100644 --- a/src/PactNet.Abstractions/Matchers/TypeMatcher.cs +++ b/src/PactNet.Abstractions/Matchers/TypeMatcher.cs @@ -1,11 +1,28 @@ +using System.Text.Json.Serialization; + namespace PactNet.Matchers { + /// <summary> + /// Match a property by type + /// </summary> public class TypeMatcher : IMatcher { + /// <summary> + /// Type of the matcher + /// </summary> + [JsonPropertyName("pact:matcher:type")] public string Type => "type"; + /// <summary> + /// Matcher value + /// </summary> + [JsonPropertyName("value")] public dynamic Value { get; } + /// <summary> + /// Initialises a new instance of the <see cref="TypeMatcher"/> class. + /// </summary> + /// <param name="example">Example value</param> public TypeMatcher(dynamic example) { this.Value = example; diff --git a/src/PactNet.Abstractions/PactConfig.cs b/src/PactNet.Abstractions/PactConfig.cs index f4c46313..ab0ee9e3 100644 --- a/src/PactNet.Abstractions/PactConfig.cs +++ b/src/PactNet.Abstractions/PactConfig.cs @@ -1,6 +1,6 @@ using System.Collections.Generic; using System.IO; -using Newtonsoft.Json; +using System.Text.Json; using PactNet.Infrastructure.Outputters; namespace PactNet @@ -37,7 +37,7 @@ public string PactDir /// <summary> /// Default JSON serializer settings /// </summary> - public JsonSerializerSettings DefaultJsonSettings { get; set; } = new JsonSerializerSettings(); + public JsonSerializerOptions DefaultJsonSettings { get; set; } = new JsonSerializerOptions(); /// <summary> /// Initialises a new instance of the <see cref="PactConfig"/> class. diff --git a/src/PactNet.Abstractions/PactNet.Abstractions.csproj b/src/PactNet.Abstractions/PactNet.Abstractions.csproj index 53be1a56..8baeb749 100644 --- a/src/PactNet.Abstractions/PactNet.Abstractions.csproj +++ b/src/PactNet.Abstractions/PactNet.Abstractions.csproj @@ -1,4 +1,4 @@ -<Project Sdk="Microsoft.NET.Sdk"> +<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <TargetFramework>netstandard2.0</TargetFramework> <LangVersion>latest</LangVersion> @@ -11,7 +11,8 @@ <Import Project="../NuGet.targets" /> <ItemGroup> - <PackageReference Include="Newtonsoft.Json" Version="10.0.3" /> + <PackageReference Include="Microsoft.CSharp" Version="4.7.0" /> <PackageReference Include="System.Net.Http" Version="4.3.4" /> + <PackageReference Include="System.Text.Json" Version="8.0.2" /> </ItemGroup> </Project> diff --git a/src/PactNet.Abstractions/Verifier/IPactVerifier.cs b/src/PactNet.Abstractions/Verifier/IPactVerifier.cs index 65688a77..9feaa126 100644 --- a/src/PactNet.Abstractions/Verifier/IPactVerifier.cs +++ b/src/PactNet.Abstractions/Verifier/IPactVerifier.cs @@ -1,6 +1,6 @@ using System; using System.IO; -using Newtonsoft.Json; +using System.Text.Json; using PactNet.Verifier.Messaging; namespace PactNet.Verifier @@ -30,7 +30,7 @@ public interface IPactVerifier /// <param name="configure">Configure message scenarios</param> /// <param name="settings">Settings for serialising messages</param> /// <returns>Fluent builder</returns> - IPactVerifier WithMessages(Action<IMessageScenarios> configure, JsonSerializerSettings settings); + IPactVerifier WithMessages(Action<IMessageScenarios> configure, JsonSerializerOptions settings); /// <summary> /// Verify a pact file directly diff --git a/src/PactNet.Abstractions/Verifier/Messaging/IMessageScenarioBuilder.cs b/src/PactNet.Abstractions/Verifier/Messaging/IMessageScenarioBuilder.cs index 6b8933be..a225aaad 100644 --- a/src/PactNet.Abstractions/Verifier/Messaging/IMessageScenarioBuilder.cs +++ b/src/PactNet.Abstractions/Verifier/Messaging/IMessageScenarioBuilder.cs @@ -1,6 +1,6 @@ using System; +using System.Text.Json; using System.Threading.Tasks; -using Newtonsoft.Json; namespace PactNet.Verifier.Messaging { @@ -27,7 +27,7 @@ public interface IMessageScenarioBuilder /// </summary> /// <param name="factory">Content factory</param> /// <param name="settings">Custom JSON serializer settings</param> - void WithContent(Func<dynamic> factory, JsonSerializerSettings settings); + void WithContent(Func<dynamic> factory, JsonSerializerOptions settings); /// <summary> /// Set the action of the scenario @@ -40,6 +40,6 @@ public interface IMessageScenarioBuilder /// </summary> /// <param name="factory">Content factory</param> /// <param name="settings">Custom JSON serializer settings</param> - Task WithContentAsync(Func<Task<dynamic>> factory, JsonSerializerSettings settings); + Task WithContentAsync(Func<Task<dynamic>> factory, JsonSerializerOptions settings); } } diff --git a/src/PactNet.Abstractions/Verifier/Messaging/IMessagingProvider.cs b/src/PactNet.Abstractions/Verifier/Messaging/IMessagingProvider.cs index 31faffc5..5aa61d00 100644 --- a/src/PactNet.Abstractions/Verifier/Messaging/IMessagingProvider.cs +++ b/src/PactNet.Abstractions/Verifier/Messaging/IMessagingProvider.cs @@ -1,5 +1,5 @@ using System; -using Newtonsoft.Json; +using System.Text.Json; namespace PactNet.Verifier.Messaging { @@ -18,6 +18,6 @@ public interface IMessagingProvider : IDisposable /// </summary> /// <param name="settings">Default JSON serializer settings</param> /// <returns>URI of the started service</returns> - Uri Start(JsonSerializerSettings settings); + Uri Start(JsonSerializerOptions settings); } } diff --git a/src/PactNet.Abstractions/Verifier/Messaging/Scenario.cs b/src/PactNet.Abstractions/Verifier/Messaging/Scenario.cs index 0105909d..1fe7e041 100644 --- a/src/PactNet.Abstractions/Verifier/Messaging/Scenario.cs +++ b/src/PactNet.Abstractions/Verifier/Messaging/Scenario.cs @@ -1,5 +1,5 @@ using System; -using Newtonsoft.Json; +using System.Text.Json; namespace PactNet.Verifier.Messaging { @@ -23,7 +23,7 @@ public class Scenario /// <summary> /// Custom JSON serializer settings /// </summary> - public JsonSerializerSettings JsonSettings { get; } + public JsonSerializerOptions JsonSettings { get; } /// <summary> /// Creates an instance of <see cref="Scenario"/> @@ -43,7 +43,7 @@ public Scenario(string description, Func<dynamic> factory) /// <param name="factory">Message content factory</param> /// <param name="metadata">the metadata</param> /// <param name="settings">Custom JSON serializer settings</param> - public Scenario(string description, Func<dynamic> factory, dynamic metadata, JsonSerializerSettings settings) + public Scenario(string description, Func<dynamic> factory, dynamic metadata, JsonSerializerOptions settings) : this(description, factory) { this.Metadata = metadata; diff --git a/src/PactNet/ConfiguredMessageVerifier.cs b/src/PactNet/ConfiguredMessageVerifier.cs index 6f791427..45f125d2 100644 --- a/src/PactNet/ConfiguredMessageVerifier.cs +++ b/src/PactNet/ConfiguredMessageVerifier.cs @@ -1,8 +1,6 @@ using System; +using System.Text.Json; using System.Threading.Tasks; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using Newtonsoft.Json.Serialization; using PactNet.Drivers; using PactNet.Exceptions; using PactNet.Interop; @@ -17,9 +15,9 @@ internal class ConfiguredMessageVerifier : IConfiguredMessageVerifier { // the native message returned from the FFI always uses camel case property // names, but the inner content may use different settings supplied by the user - private static readonly JsonSerializerSettings NativeMessageSettings = new() + private static readonly JsonSerializerOptions NativeMessageSettings = new() { - ContractResolver = new CamelCasePropertyNamesContractResolver() + PropertyNamingPolicy = JsonNamingPolicy.CamelCase }; private readonly IMessageInteractionDriver driver; @@ -87,16 +85,16 @@ public async Task VerifyAsync<T>(Func<T, Task> handler) private T MessageReified<T>() { string reified = this.driver.Reify(); - NativeMessage content = JsonConvert.DeserializeObject<NativeMessage>(reified, NativeMessageSettings); + NativeMessage content = JsonSerializer.Deserialize<NativeMessage>(reified, NativeMessageSettings); string contentString = this.version switch { - PactSpecification.V3 => ((JToken)content.Contents).ToString(Formatting.None), - PactSpecification.V4 => ((JToken)content.Contents)["content"].ToString(Formatting.None), + PactSpecification.V3 => ((JsonElement)content.Contents).GetRawText(), + PactSpecification.V4 => ((JsonElement)content.Contents).GetProperty("content").GetRawText(), _ => throw new ArgumentOutOfRangeException(nameof(version), this.version, "Unsupported specification version") }; - T messageReified = JsonConvert.DeserializeObject<T>(contentString, this.config.DefaultJsonSettings); + T messageReified = JsonSerializer.Deserialize<T>(contentString, this.config.DefaultJsonSettings); return messageReified; } diff --git a/src/PactNet/Drivers/HttpPactDriver.cs b/src/PactNet/Drivers/HttpPactDriver.cs index 8c010144..52026bff 100644 --- a/src/PactNet/Drivers/HttpPactDriver.cs +++ b/src/PactNet/Drivers/HttpPactDriver.cs @@ -40,8 +40,7 @@ public IHttpInteractionDriver NewHttpInteraction(string description) /// <exception cref="InvalidOperationException">Failed to start mock server</exception> public IMockServerDriver CreateMockServer(string host, int? port, bool tls) { - string addrStr = $"{host}:{port.GetValueOrDefault(0)}"; - int result = NativeInterop.CreateMockServerForPact(this.pact, addrStr, tls); + int result = NativeInterop.CreateMockServerForTransport(this.pact, host, (ushort)port.GetValueOrDefault(0), "http", null); if (result > 0) { diff --git a/src/PactNet/Interop/NativeInterop.cs b/src/PactNet/Interop/NativeInterop.cs index 71aeabff..92bdc911 100644 --- a/src/PactNet/Interop/NativeInterop.cs +++ b/src/PactNet/Interop/NativeInterop.cs @@ -15,8 +15,8 @@ internal static class NativeInterop #region Http Interop Support - [DllImport(DllName, EntryPoint = "pactffi_create_mock_server_for_pact")] - public static extern int CreateMockServerForPact(PactHandle pact, string addrStr, bool tls); + [DllImport(DllName, EntryPoint = "pactffi_create_mock_server_for_transport")] + public static extern int CreateMockServerForTransport(PactHandle pact, string addrStr, ushort port, string transport, string transportConfig); [DllImport(DllName, EntryPoint = "pactffi_mock_server_mismatches")] public static extern IntPtr MockServerMismatches(int mockServerPort); diff --git a/src/PactNet/MessageBuilder.cs b/src/PactNet/MessageBuilder.cs index 62f41b2f..83c7cffe 100644 --- a/src/PactNet/MessageBuilder.cs +++ b/src/PactNet/MessageBuilder.cs @@ -1,6 +1,6 @@ using System; using System.Collections.Generic; -using Newtonsoft.Json; +using System.Text.Json; using PactNet.Drivers; using PactNet.Interop; @@ -47,7 +47,7 @@ IConfiguredMessageVerifier IMessageBuilderV3.WithJsonContent(dynamic content) => WithJsonContent(content); /// <inheritdoc cref="IMessageBuilderV3"/> - IConfiguredMessageVerifier IMessageBuilderV3.WithJsonContent(dynamic content, JsonSerializerSettings settings) + IConfiguredMessageVerifier IMessageBuilderV3.WithJsonContent(dynamic content, JsonSerializerOptions settings) => WithJsonContent(content, settings); #endregion @@ -71,7 +71,7 @@ IConfiguredMessageVerifier IMessageBuilderV4.WithJsonContent(dynamic content) => WithJsonContent(content); /// <inheritdoc cref="IMessageBuilderV4"/> - IConfiguredMessageVerifier IMessageBuilderV4.WithJsonContent(dynamic content, JsonSerializerSettings settings) + IConfiguredMessageVerifier IMessageBuilderV4.WithJsonContent(dynamic content, JsonSerializerOptions settings) => WithJsonContent(content, settings); #endregion @@ -132,9 +132,9 @@ internal MessageBuilder WithMetadata(string key, string value) /// <param name="body">Request body</param> /// <param name="settings">Custom JSON serializer settings</param> /// <returns>Fluent builder</returns> - internal ConfiguredMessageVerifier WithJsonContent(dynamic body, JsonSerializerSettings settings) + internal ConfiguredMessageVerifier WithJsonContent(dynamic body, JsonSerializerOptions settings) { - string serialised = JsonConvert.SerializeObject(body, settings); + string serialised = JsonSerializer.Serialize(body, settings); this.driver.WithContents("application/json", serialised, 0); diff --git a/src/PactNet/PactNet.csproj b/src/PactNet/PactNet.csproj index 4147e846..ea7a032a 100644 --- a/src/PactNet/PactNet.csproj +++ b/src/PactNet/PactNet.csproj @@ -78,21 +78,17 @@ <ItemGroup> <Content Include="$(MSBuildProjectDirectory)\..\..\build\PactNet.targets"> - <PackagePath>build/net461/</PackagePath> + <PackagePath>build/net462/</PackagePath> <Pack>true</Pack> <Visible>false</Visible> </Content> <Content Include="$(MSBuildProjectDirectory)\..\..\build\PactNet.targets"> - <PackagePath>buildTransitive/net461/</PackagePath> + <PackagePath>buildTransitive/net462/</PackagePath> <Pack>true</Pack> <Visible>false</Visible> </Content> </ItemGroup> - <ItemGroup> - <PackageReference Include="System.Net.Http" Version="4.3.4" /> - </ItemGroup> - <ItemGroup> <ProjectReference Include="..\PactNet.Abstractions\PactNet.Abstractions.csproj" /> </ItemGroup> diff --git a/src/PactNet/RequestBuilder.cs b/src/PactNet/RequestBuilder.cs index 4ccb1a29..d245cfbf 100644 --- a/src/PactNet/RequestBuilder.cs +++ b/src/PactNet/RequestBuilder.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; using System.Net.Http; -using Newtonsoft.Json; +using System.Text.Json; using PactNet.Drivers; using PactNet.Matchers; @@ -13,7 +13,7 @@ namespace PactNet internal class RequestBuilder : IRequestBuilderV2, IRequestBuilderV3, IRequestBuilderV4 { private readonly IHttpInteractionDriver driver; - private readonly JsonSerializerSettings defaultSettings; + private readonly JsonSerializerOptions defaultSettings; private readonly Dictionary<string, uint> queryCounts; private readonly Dictionary<string, uint> headerCounts; @@ -24,7 +24,7 @@ internal class RequestBuilder : IRequestBuilderV2, IRequestBuilderV3, IRequestBu /// </summary> /// <param name="driver">Interaction driver</param> /// <param name="defaultSettings">Default JSON serializer settings</param> - internal RequestBuilder(IHttpInteractionDriver driver, JsonSerializerSettings defaultSettings) + internal RequestBuilder(IHttpInteractionDriver driver, JsonSerializerOptions defaultSettings) { this.driver = driver; this.defaultSettings = defaultSettings; @@ -111,7 +111,7 @@ IRequestBuilderV2 IRequestBuilderV2.WithJsonBody(dynamic body, string contentTyp /// <param name="body">Request body</param> /// <param name="settings">Custom JSON serializer settings</param> /// <returns>Fluent builder</returns> - IRequestBuilderV2 IRequestBuilderV2.WithJsonBody(dynamic body, JsonSerializerSettings settings) + IRequestBuilderV2 IRequestBuilderV2.WithJsonBody(dynamic body, JsonSerializerOptions settings) => this.WithJsonBody(body, settings); /// <summary> @@ -121,7 +121,7 @@ IRequestBuilderV2 IRequestBuilderV2.WithJsonBody(dynamic body, JsonSerializerSet /// <param name="settings">Custom JSON serializer settings</param> /// <param name="contentType">Content type override</param> /// <returns>Fluent builder</returns> - IRequestBuilderV2 IRequestBuilderV2.WithJsonBody(dynamic body, JsonSerializerSettings settings, string contentType) + IRequestBuilderV2 IRequestBuilderV2.WithJsonBody(dynamic body, JsonSerializerOptions settings, string contentType) => this.WithJsonBody(body, settings, contentType); /// <summary> @@ -240,7 +240,7 @@ IRequestBuilderV3 IRequestBuilderV3.WithJsonBody(dynamic body, string contentTyp /// <param name="body">Request body</param> /// <param name="settings">Custom JSON serializer settings</param> /// <returns>Fluent builder</returns> - IRequestBuilderV3 IRequestBuilderV3.WithJsonBody(dynamic body, JsonSerializerSettings settings) + IRequestBuilderV3 IRequestBuilderV3.WithJsonBody(dynamic body, JsonSerializerOptions settings) => this.WithJsonBody(body, settings); /// <summary> @@ -250,7 +250,7 @@ IRequestBuilderV3 IRequestBuilderV3.WithJsonBody(dynamic body, JsonSerializerSet /// <param name="settings">Custom JSON serializer settings</param> /// <param name="contentType">Content type override</param> /// <returns>Fluent builder</returns> - IRequestBuilderV3 IRequestBuilderV3.WithJsonBody(dynamic body, JsonSerializerSettings settings, string contentType) + IRequestBuilderV3 IRequestBuilderV3.WithJsonBody(dynamic body, JsonSerializerOptions settings, string contentType) => this.WithJsonBody(body, settings, contentType); /// <summary> @@ -369,7 +369,7 @@ IRequestBuilderV4 IRequestBuilderV4.WithJsonBody(dynamic body, string contentTyp /// <param name="body">Request body</param> /// <param name="settings">Custom JSON serializer settings</param> /// <returns>Fluent builder</returns> - IRequestBuilderV4 IRequestBuilderV4.WithJsonBody(dynamic body, JsonSerializerSettings settings) + IRequestBuilderV4 IRequestBuilderV4.WithJsonBody(dynamic body, JsonSerializerOptions settings) => this.WithJsonBody(body, settings); /// <summary> @@ -379,7 +379,7 @@ IRequestBuilderV4 IRequestBuilderV4.WithJsonBody(dynamic body, JsonSerializerSet /// <param name="settings">Custom JSON serializer settings</param> /// <param name="contentType">Content type override</param> /// <returns>Fluent builder</returns> - IRequestBuilderV4 IRequestBuilderV4.WithJsonBody(dynamic body, JsonSerializerSettings settings, string contentType) + IRequestBuilderV4 IRequestBuilderV4.WithJsonBody(dynamic body, JsonSerializerOptions settings, string contentType) => this.WithJsonBody(body, settings, contentType); /// <summary> @@ -475,7 +475,7 @@ internal RequestBuilder WithQuery(string key, string value) /// <remarks>You can add a query parameter with the same key multiple times</remarks> internal RequestBuilder WithQuery(string key, IMatcher matcher) { - var serialised = JsonConvert.SerializeObject(matcher, this.defaultSettings); + var serialised = JsonSerializer.Serialize(matcher, this.defaultSettings); return this.WithQuery(key, serialised); } @@ -504,7 +504,7 @@ internal RequestBuilder WithHeader(string key, string value) /// <returns>Fluent builder</returns> internal RequestBuilder WithHeader(string key, IMatcher matcher) { - var serialised = JsonConvert.SerializeObject(matcher, this.defaultSettings); + var serialised = JsonSerializer.Serialize(matcher, this.defaultSettings); return this.WithHeader(key, serialised); } @@ -530,7 +530,7 @@ internal RequestBuilder WithHeader(string key, IMatcher matcher) /// <param name="body">Request body</param> /// <param name="settings">Custom JSON serializer settings</param> /// <returns>Fluent builder</returns> - internal RequestBuilder WithJsonBody(dynamic body, JsonSerializerSettings settings) => WithJsonBody(body, settings, "application/json"); + internal RequestBuilder WithJsonBody(dynamic body, JsonSerializerOptions settings) => WithJsonBody(body, settings, "application/json"); /// <summary> /// Set a body which is serialised as JSON @@ -539,9 +539,9 @@ internal RequestBuilder WithHeader(string key, IMatcher matcher) /// <param name="settings">Custom JSON serializer settings</param> /// <param name="contentType">Content type override</param> /// <returns>Fluent builder</returns> - internal RequestBuilder WithJsonBody(dynamic body, JsonSerializerSettings settings, string contentType) + internal RequestBuilder WithJsonBody(dynamic body, JsonSerializerOptions settings, string contentType) { - string serialised = JsonConvert.SerializeObject(body, settings); + string serialised = JsonSerializer.Serialize(body, settings); return this.WithBody(serialised, contentType); } diff --git a/src/PactNet/ResponseBuilder.cs b/src/PactNet/ResponseBuilder.cs index 1cd0088d..badb0e96 100644 --- a/src/PactNet/ResponseBuilder.cs +++ b/src/PactNet/ResponseBuilder.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; using System.Net; -using Newtonsoft.Json; +using System.Text.Json; using PactNet.Drivers; using PactNet.Matchers; @@ -13,7 +13,7 @@ namespace PactNet internal class ResponseBuilder : IResponseBuilderV2, IResponseBuilderV3, IResponseBuilderV4 { private readonly IHttpInteractionDriver driver; - private readonly JsonSerializerSettings defaultSettings; + private readonly JsonSerializerOptions defaultSettings; private readonly Dictionary<string, uint> headerCounts; /// <summary> @@ -21,7 +21,7 @@ internal class ResponseBuilder : IResponseBuilderV2, IResponseBuilderV3, IRespon /// </summary> /// <param name="driver">Interaction driver</param> /// <param name="defaultSettings">Default JSON serializer settings</param> - internal ResponseBuilder(IHttpInteractionDriver driver, JsonSerializerSettings defaultSettings) + internal ResponseBuilder(IHttpInteractionDriver driver, JsonSerializerOptions defaultSettings) { this.driver = driver; this.defaultSettings = defaultSettings; @@ -78,7 +78,7 @@ IResponseBuilderV2 IResponseBuilderV2.WithJsonBody(dynamic body) /// <param name="body">Request body</param> /// <param name="settings">Custom JSON serializer settings</param> /// <returns>Fluent builder</returns> - IResponseBuilderV2 IResponseBuilderV2.WithJsonBody(dynamic body, JsonSerializerSettings settings) + IResponseBuilderV2 IResponseBuilderV2.WithJsonBody(dynamic body, JsonSerializerOptions settings) => this.WithJsonBody(body, settings); /// <summary> @@ -142,7 +142,7 @@ IResponseBuilderV3 IResponseBuilderV3.WithJsonBody(dynamic body) /// <param name="body">Request body</param> /// <param name="settings">Custom JSON serializer settings</param> /// <returns>Fluent builder</returns> - IResponseBuilderV3 IResponseBuilderV3.WithJsonBody(dynamic body, JsonSerializerSettings settings) + IResponseBuilderV3 IResponseBuilderV3.WithJsonBody(dynamic body, JsonSerializerOptions settings) => this.WithJsonBody(body, settings); /// <summary> @@ -206,7 +206,7 @@ IResponseBuilderV4 IResponseBuilderV4.WithJsonBody(dynamic body) /// <param name="body">Request body</param> /// <param name="settings">Custom JSON serializer settings</param> /// <returns>Fluent builder</returns> - IResponseBuilderV4 IResponseBuilderV4.WithJsonBody(dynamic body, JsonSerializerSettings settings) + IResponseBuilderV4 IResponseBuilderV4.WithJsonBody(dynamic body, JsonSerializerOptions settings) => this.WithJsonBody(body, settings); /// <summary> @@ -268,7 +268,7 @@ internal ResponseBuilder WithHeader(string key, string value) /// <returns>Fluent builder</returns> internal ResponseBuilder WithHeader(string key, IMatcher matcher) { - var serialised = JsonConvert.SerializeObject(matcher, this.defaultSettings); + var serialised = JsonSerializer.Serialize(matcher, this.defaultSettings); return this.WithHeader(key, serialised); } @@ -287,9 +287,9 @@ internal ResponseBuilder WithJsonBody(dynamic body) /// <param name="body">Request body</param> /// <param name="settings">Custom JSON serializer settings</param> /// <returns>Fluent builder</returns> - internal ResponseBuilder WithJsonBody(dynamic body, JsonSerializerSettings settings) + internal ResponseBuilder WithJsonBody(dynamic body, JsonSerializerOptions settings) { - string serialised = JsonConvert.SerializeObject(body, settings); + string serialised = JsonSerializer.Serialize(body, settings); return this.WithBody(serialised, "application/json"); } diff --git a/src/PactNet/Verifier/Messaging/MessageScenarioBuilder.cs b/src/PactNet/Verifier/Messaging/MessageScenarioBuilder.cs index 9d8dc3c1..ac901da0 100644 --- a/src/PactNet/Verifier/Messaging/MessageScenarioBuilder.cs +++ b/src/PactNet/Verifier/Messaging/MessageScenarioBuilder.cs @@ -1,6 +1,6 @@ using System; +using System.Text.Json; using System.Threading.Tasks; -using Newtonsoft.Json; namespace PactNet.Verifier.Messaging { @@ -12,7 +12,7 @@ internal class MessageScenarioBuilder : IMessageScenarioBuilder private readonly string description; private Func<dynamic> factory; private dynamic metadata = new { ContentType = "application/json" }; - private JsonSerializerSettings settings; + private JsonSerializerOptions settings; /// <summary> /// Initialises a new instance of the <see cref="MessageScenarioBuilder"/> class. @@ -48,7 +48,7 @@ public void WithContent(Func<dynamic> factory) /// </summary> /// <param name="factory">Content factory</param> /// <param name="settings">Custom JSON serializer settings</param> - public void WithContent(Func<dynamic> factory, JsonSerializerSettings settings) + public void WithContent(Func<dynamic> factory, JsonSerializerOptions settings) { this.factory = factory ?? throw new ArgumentNullException(nameof(factory)); this.settings = settings ?? throw new ArgumentNullException(nameof(settings)); @@ -69,7 +69,7 @@ public async Task WithContentAsync(Func<Task<dynamic>> factory) /// </summary> /// <param name="factory">Content factory</param> /// <param name="settings">Custom JSON serializer settings</param> - public async Task WithContentAsync(Func<Task<dynamic>> factory, JsonSerializerSettings settings) + public async Task WithContentAsync(Func<Task<dynamic>> factory, JsonSerializerOptions settings) { dynamic value = await factory(); this.factory = () => value; diff --git a/src/PactNet/Verifier/Messaging/MessagingProvider.cs b/src/PactNet/Verifier/Messaging/MessagingProvider.cs index 7fbd597f..5b70cef5 100644 --- a/src/PactNet/Verifier/Messaging/MessagingProvider.cs +++ b/src/PactNet/Verifier/Messaging/MessagingProvider.cs @@ -5,9 +5,8 @@ using System.Net; using System.Net.NetworkInformation; using System.Text; +using System.Text.Json; using System.Threading; -using Newtonsoft.Json; -using Newtonsoft.Json.Serialization; using PactNet.Exceptions; using PactNet.Internal; @@ -21,16 +20,17 @@ internal class MessagingProvider : IMessagingProvider private const int MinimumPort = 49152; private const int MaximumPort = 65535; - private static readonly JsonSerializerSettings InteractionSettings = new JsonSerializerSettings + private static readonly JsonSerializerOptions InteractionSettings = new JsonSerializerOptions { - ContractResolver = new CamelCasePropertyNamesContractResolver() + PropertyNamingPolicy = JsonNamingPolicy.CamelCase, + PropertyNameCaseInsensitive = true }; private readonly PactVerifierConfig config; - private readonly HttpListener server; private readonly Thread thread; - private JsonSerializerSettings defaultSettings; + private HttpListener server; + private JsonSerializerOptions defaultSettings; /// <summary> /// Scenarios configured for the provider @@ -46,7 +46,6 @@ public MessagingProvider(PactVerifierConfig config, IMessageScenarios scenarios) { this.config = config; this.Scenarios = scenarios; - this.server = new HttpListener(); this.thread = new Thread(this.HandleRequest); } @@ -55,28 +54,40 @@ public MessagingProvider(PactVerifierConfig config, IMessageScenarios scenarios) /// </summary> /// <param name="settings">Default JSON serializer settings</param> /// <returns>URI of the started service</returns> - public Uri Start(JsonSerializerSettings settings) + public Uri Start(JsonSerializerOptions settings) { Guard.NotNull(settings, nameof(settings)); this.defaultSettings = settings; - try + while (true) { - int port = FindUnusedPort(); - var uri = new Uri($"http://localhost:{port}/pact-messages/"); + Uri uri; - this.config.WriteLine($"Starting messaging provider at {uri}"); - this.server.Prefixes.Add(uri.AbsoluteUri); + try + { + int port = FindUnusedPort(); + uri = new Uri($"http://localhost:{port}/pact-messages/"); - this.server.Start(); - this.thread.Start(); + this.config.WriteLine($"Starting messaging provider at {uri}"); + + this.server = new HttpListener(); + this.server.Prefixes.Add(uri.AbsoluteUri); + this.server.Start(); + } + catch (HttpListenerException e) when (e.Message == "Address already in use") + { + // handle intermittent race condition, mostly on MacOS, where a port says it's unused but still throws when you try to use it + this.config.WriteLine("Failed to start messaging provider as the port is already in use, retrying..."); + continue; + } + catch (Exception e) + { + throw new PactFailureException("Unable to start the internal messaging server", e); + } + this.thread.Start(); return uri; } - catch (Exception e) - { - throw new PactFailureException("Unable to start the internal messaging server", e); - } } /// <summary> @@ -137,7 +148,7 @@ private void HandleRequest() { var reader = new StreamReader(context.Request.InputStream); string body = reader.ReadToEnd(); - interaction = JsonConvert.DeserializeObject<MessageInteraction>(body, InteractionSettings); + interaction = JsonSerializer.Deserialize<MessageInteraction>(body, InteractionSettings); if (string.IsNullOrWhiteSpace(interaction.Description)) { @@ -172,11 +183,11 @@ private void HandleInteraction(HttpListenerContext context, MessageInteraction i return; } - JsonSerializerSettings settings = scenario.JsonSettings ?? this.defaultSettings; + JsonSerializerOptions settings = scenario.JsonSettings ?? this.defaultSettings; if (scenario.Metadata != null) { - string stringifyMetadata = JsonConvert.SerializeObject(scenario.Metadata, settings); + string stringifyMetadata = JsonSerializer.Serialize(scenario.Metadata, settings); string metadataBase64 = Convert.ToBase64String(Encoding.UTF8.GetBytes(stringifyMetadata)); context.Response.AddHeader("Pact-Message-Metadata", metadataBase64); @@ -184,7 +195,7 @@ private void HandleInteraction(HttpListenerContext context, MessageInteraction i } dynamic content = scenario.Invoke(); - string response = JsonConvert.SerializeObject(content, settings); + string response = JsonSerializer.Serialize(content, settings); this.OkResponse(context, response); this.config.WriteLine($"Successfully simulated message with description: {interaction.Description}"); @@ -273,8 +284,8 @@ public void Dispose() try { - this.server.Stop(); - this.server.Close(); + this.server?.Stop(); + this.server?.Close(); } catch { diff --git a/src/PactNet/Verifier/PactBrokerOptions.cs b/src/PactNet/Verifier/PactBrokerOptions.cs index 66d41640..987fd1e3 100644 --- a/src/PactNet/Verifier/PactBrokerOptions.cs +++ b/src/PactNet/Verifier/PactBrokerOptions.cs @@ -1,8 +1,8 @@ using System; using System.Collections.Generic; using System.Linq; -using Newtonsoft.Json; -using Newtonsoft.Json.Serialization; +using System.Text.Json; +using System.Text.Json.Serialization; using PactNet.Internal; namespace PactNet.Verifier @@ -12,11 +12,11 @@ namespace PactNet.Verifier /// </summary> internal class PactBrokerOptions : IPactBrokerOptions { - private static readonly JsonSerializerSettings ConsumerSelectorSettings = new() + private static readonly JsonSerializerOptions ConsumerSelectorSettings = new() { - DefaultValueHandling = DefaultValueHandling.Ignore, - NullValueHandling = NullValueHandling.Ignore, - ContractResolver = new CamelCasePropertyNamesContractResolver() + PropertyNamingPolicy = JsonNamingPolicy.CamelCase, + PropertyNameCaseInsensitive = true, + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingDefault, }; private readonly IVerifierProvider provider; @@ -127,7 +127,7 @@ public IPactBrokerOptions ConsumerTags(params string[] tags) /// <remarks>See <see href="https://docs.pact.io/pact_broker/advanced_topics/consumer_version_selectors"/></remarks> public IPactBrokerOptions ConsumerVersionSelectors(ICollection<ConsumerVersionSelector> selectors) { - string[] serialised = selectors.Select(s => JsonConvert.SerializeObject(s, ConsumerSelectorSettings)).ToArray(); + string[] serialised = selectors.Select(s => JsonSerializer.Serialize(s, ConsumerSelectorSettings)).ToArray(); this.consumerVersionSelectors = serialised; diff --git a/src/PactNet/Verifier/PactVerifier.cs b/src/PactNet/Verifier/PactVerifier.cs index 985dd7c8..09d7a0c5 100644 --- a/src/PactNet/Verifier/PactVerifier.cs +++ b/src/PactNet/Verifier/PactVerifier.cs @@ -1,6 +1,6 @@ using System; using System.IO; -using Newtonsoft.Json; +using System.Text.Json; using PactNet.Internal; using PactNet.Verifier.Messaging; @@ -90,7 +90,7 @@ public IPactVerifier WithHttpEndpoint(Uri pactUri) /// </summary> /// <param name="configure">Configure message scenarios</param> /// <returns>Fluent builder</returns> - public IPactVerifier WithMessages(Action<IMessageScenarios> configure) => WithMessages(configure, new JsonSerializerSettings()); + public IPactVerifier WithMessages(Action<IMessageScenarios> configure) => WithMessages(configure, new JsonSerializerOptions()); /// <summary> /// Define messages for verifying pacts containing asynchronous message interactions @@ -98,7 +98,7 @@ public IPactVerifier WithHttpEndpoint(Uri pactUri) /// <param name="configure">Configure message scenarios</param> /// <param name="settings">Settings for serialising messages</param> /// <returns>Fluent builder</returns> - public IPactVerifier WithMessages(Action<IMessageScenarios> configure, JsonSerializerSettings settings) + public IPactVerifier WithMessages(Action<IMessageScenarios> configure, JsonSerializerOptions settings) { Guard.NotNull(settings, nameof(settings)); diff --git a/tests/PactNet.Abstractions.Tests/IntegrationTests/FailureIntegrationTestsMyApiPact.cs b/tests/PactNet.Abstractions.Tests/IntegrationTests/FailureIntegrationTestsMyApiPact.cs index 69dbf247..bd914f00 100644 --- a/tests/PactNet.Abstractions.Tests/IntegrationTests/FailureIntegrationTestsMyApiPact.cs +++ b/tests/PactNet.Abstractions.Tests/IntegrationTests/FailureIntegrationTestsMyApiPact.cs @@ -17,11 +17,11 @@ public FailureIntegrationTestsMyApiPact() var pactConfig = new PactConfig(); PactBuilder = new PactBuilder( - (port, enableSsl, consumerName, providerName, host, jsonSerializerSettings, sslCert, sslKey) => + (port, enableSsl, consumerName, providerName, host, JsonSerializerOptions, sslCert, sslKey) => new MockProviderService( baseUri => new RubyHttpHost(baseUri, "MyConsumer", "MyApi", pactConfig, host), port, enableSsl, - baseUri => new AdminHttpClient(baseUri, jsonSerializerSettings))) + baseUri => new AdminHttpClient(baseUri, JsonSerializerOptions))) .ServiceConsumer("FailureIntegrationTests") .HasPactWith("MyApi"); diff --git a/tests/PactNet.Abstractions.Tests/IntegrationTests/IntegrationTestsMyApiPact.cs b/tests/PactNet.Abstractions.Tests/IntegrationTests/IntegrationTestsMyApiPact.cs index de27d59e..63661fe0 100644 --- a/tests/PactNet.Abstractions.Tests/IntegrationTests/IntegrationTestsMyApiPact.cs +++ b/tests/PactNet.Abstractions.Tests/IntegrationTests/IntegrationTestsMyApiPact.cs @@ -17,11 +17,11 @@ public IntegrationTestsMyApiPact() var pactConfig = new PactConfig(); PactBuilder = new PactBuilder( - (port, enableSsl, consumerName, providerName, host, jsonSerializerSettings, sslCert, sslKey) => + (port, enableSsl, consumerName, providerName, host, JsonSerializerOptions, sslCert, sslKey) => new MockProviderService( baseUri => new RubyHttpHost(baseUri, "MyConsumer", "MyApi", pactConfig, host), port, enableSsl, - baseUri => new AdminHttpClient(baseUri, jsonSerializerSettings))) + baseUri => new AdminHttpClient(baseUri, JsonSerializerOptions))) .ServiceConsumer("IntegrationTests") .HasPactWith("MyApi"); diff --git a/tests/PactNet.Abstractions.Tests/Matchers/DecimalMatcherTests.cs b/tests/PactNet.Abstractions.Tests/Matchers/DecimalMatcherTests.cs index e5a51a6b..46d1572b 100644 --- a/tests/PactNet.Abstractions.Tests/Matchers/DecimalMatcherTests.cs +++ b/tests/PactNet.Abstractions.Tests/Matchers/DecimalMatcherTests.cs @@ -1,8 +1,6 @@ +using System.Text.Json; using FluentAssertions; -using Newtonsoft.Json; using PactNet.Matchers; -using System; -using System.Globalization; using Xunit; namespace PactNet.Abstractions.Tests.Matchers @@ -16,10 +14,10 @@ public void Ctor_Float_SerialisesCorrectly() var matcher = new DecimalMatcher(example); - string actual = JsonConvert.SerializeObject(matcher, new JsonSerializerSettings() { Culture = CultureInfo.InvariantCulture }); - FormattableString expected = $@"{{""pact:matcher:type"":""decimal"",""value"":{example}}}"; + string actual = JsonSerializer.Serialize(matcher); + string expected = $@"{{""pact:matcher:type"":""decimal"",""value"":{JsonSerializer.Serialize(example)}}}"; - actual.Should().BeEquivalentTo(expected.ToString(CultureInfo.InvariantCulture)); + actual.Should().Be(expected); } [Fact] @@ -29,23 +27,23 @@ public void Ctor_Double_SerialisesCorrectly() var matcher = new DecimalMatcher(example); - string actual = JsonConvert.SerializeObject(matcher, new JsonSerializerSettings() { Culture = CultureInfo.InvariantCulture }); - FormattableString expected = $@"{{""pact:matcher:type"":""decimal"",""value"":{example}}}"; + string actual = JsonSerializer.Serialize(matcher); + string expected = $@"{{""pact:matcher:type"":""decimal"",""value"":{JsonSerializer.Serialize(example)}}}"; - actual.Should().BeEquivalentTo(expected.ToString(CultureInfo.InvariantCulture)); + actual.Should().Be(expected); } [Fact] public void Ctor_Decimal_SerialisesCorrectly() { - const decimal example = 3.14m; + const decimal example = 3.1m; var matcher = new DecimalMatcher(example); - string actual = JsonConvert.SerializeObject(matcher, new JsonSerializerSettings() { Culture = CultureInfo.InvariantCulture }); - FormattableString expected = $@"{{""pact:matcher:type"":""decimal"",""value"":{example}}}"; + string actual = JsonSerializer.Serialize(matcher); + string expected = $@"{{""pact:matcher:type"":""decimal"",""value"":{JsonSerializer.Serialize(example)}}}"; - actual.Should().BeEquivalentTo(expected.ToString(CultureInfo.InvariantCulture)); + actual.Should().Be(expected); } } } diff --git a/tests/PactNet.Abstractions.Tests/Matchers/EqualityMatcherTests.cs b/tests/PactNet.Abstractions.Tests/Matchers/EqualityMatcherTests.cs index 6390221d..dd24c41b 100644 --- a/tests/PactNet.Abstractions.Tests/Matchers/EqualityMatcherTests.cs +++ b/tests/PactNet.Abstractions.Tests/Matchers/EqualityMatcherTests.cs @@ -1,5 +1,5 @@ +using System.Text.Json; using FluentAssertions; -using Newtonsoft.Json; using PactNet.Matchers; using Xunit; @@ -14,7 +14,7 @@ public void Ctor_WhenCalled_SerialisesCorrectly() var matcher = new EqualityMatcher(example); - string actual = JsonConvert.SerializeObject(matcher); + string actual = JsonSerializer.Serialize(matcher); string expected = $@"{{""pact:matcher:type"":""equality"",""value"":""{example}""}}"; actual.Should().BeEquivalentTo(expected); diff --git a/tests/PactNet.Abstractions.Tests/Matchers/IncludeMatcherTests.cs b/tests/PactNet.Abstractions.Tests/Matchers/IncludeMatcherTests.cs index 9b2f8317..cd6258af 100644 --- a/tests/PactNet.Abstractions.Tests/Matchers/IncludeMatcherTests.cs +++ b/tests/PactNet.Abstractions.Tests/Matchers/IncludeMatcherTests.cs @@ -1,5 +1,5 @@ +using System.Text.Json; using FluentAssertions; -using Newtonsoft.Json; using PactNet.Matchers; using Xunit; @@ -14,7 +14,7 @@ public void Ctor_WhenCalled_SerialisesCorrectly() var matcher = new IncludeMatcher(example); - string actual = JsonConvert.SerializeObject(matcher); + string actual = JsonSerializer.Serialize(matcher); string expected = $@"{{""pact:matcher:type"":""include"",""value"":""{example}""}}"; actual.Should().BeEquivalentTo(expected); diff --git a/tests/PactNet.Abstractions.Tests/Matchers/IntegerMatchesTests].cs b/tests/PactNet.Abstractions.Tests/Matchers/IntegerMatchesTests].cs index 06938217..e9c3865b 100644 --- a/tests/PactNet.Abstractions.Tests/Matchers/IntegerMatchesTests].cs +++ b/tests/PactNet.Abstractions.Tests/Matchers/IntegerMatchesTests].cs @@ -1,5 +1,5 @@ +using System.Text.Json; using FluentAssertions; -using Newtonsoft.Json; using PactNet.Matchers; using Xunit; @@ -14,7 +14,7 @@ public void Ctor_WhenCalled_SerialisesCorrectly() var matcher = new IntegerMatcher(example); - string actual = JsonConvert.SerializeObject(matcher); + string actual = JsonSerializer.Serialize(matcher); string expected = $@"{{""pact:matcher:type"":""integer"",""value"":{example}}}"; actual.Should().BeEquivalentTo(expected); diff --git a/tests/PactNet.Abstractions.Tests/Matchers/MatcherConverterTests.cs b/tests/PactNet.Abstractions.Tests/Matchers/MatcherConverterTests.cs new file mode 100644 index 00000000..d25366d9 --- /dev/null +++ b/tests/PactNet.Abstractions.Tests/Matchers/MatcherConverterTests.cs @@ -0,0 +1,105 @@ +using System.Text.Json; +using FluentAssertions; +using PactNet.Matchers; +using Xunit; + +namespace PactNet.Abstractions.Tests.Matchers +{ + public class MatcherConverterTests + { + private static readonly JsonSerializerOptions Options = new JsonSerializerOptions() + { + PropertyNamingPolicy = JsonNamingPolicy.CamelCase + }; + + [Fact] + public void Serialise_Decimal_SerialisesCorrectly() + { + IMatcher matcher = Match.Decimal(42.1m); + + string actual = JsonSerializer.Serialize(matcher, Options); + + actual.Should().Be(@"{""pact:matcher:type"":""decimal"",""value"":42.1}"); + } + + [Fact] + public void Serialise_Equality_SerialisesCorrectly() + { + IMatcher matcher = Match.Equality("foo"); + + string actual = JsonSerializer.Serialize(matcher, Options); + + actual.Should().Be(@"{""pact:matcher:type"":""equality"",""value"":""foo""}"); + } + + [Fact] + public void Serialise_Include_SerialisesCorrectly() + { + IMatcher matcher = Match.Include("foo"); + + string actual = JsonSerializer.Serialize(matcher, Options); + + actual.Should().Be(@"{""pact:matcher:type"":""include"",""value"":""foo""}"); + } + + [Fact] + public void Serialise_Integer_SerialisesCorrectly() + { + IMatcher matcher = Match.Integer(42); + + string actual = JsonSerializer.Serialize(matcher, Options); + + actual.Should().Be(@"{""pact:matcher:type"":""integer"",""value"":42}"); + } + + [Fact] + public void Serialise_MinMax_SerialisesCorrectly() + { + IMatcher matcher = Match.MinMaxType("foo", 1, 2); + + string actual = JsonSerializer.Serialize(matcher, Options); + + actual.Should().Be(@"{""pact:matcher:type"":""type"",""value"":[""foo""],""min"":1,""max"":2}"); + } + + [Fact] + public void Serialise_Null_SerialisesCorrectly() + { + IMatcher matcher = Match.Null(); + + string actual = JsonSerializer.Serialize(matcher, Options); + + actual.Should().Be(@"{""pact:matcher:type"":""null""}"); + } + + [Fact] + public void Serialise_Numeric_SerialisesCorrectly() + { + IMatcher matcher = Match.Number(42); + + string actual = JsonSerializer.Serialize(matcher, Options); + + actual.Should().Be(@"{""pact:matcher:type"":""number"",""value"":42}"); + } + + [Fact] + public void Serialise_Regex_SerialisesCorrectly() + { + IMatcher matcher = Match.Regex("foo", "^foo$"); + + string actual = JsonSerializer.Serialize(matcher, Options); + + actual.Should().Be(@"{""pact:matcher:type"":""regex"",""value"":""foo"",""regex"":""^foo$""}"); + } + + [Fact] + public void Serialise_Type_SerialisesCorrectly() + { + IMatcher matcher = Match.Type("foo"); + + string actual = JsonSerializer.Serialize(matcher, Options); + + actual.Should().Be(@"{""pact:matcher:type"":""type"",""value"":""foo""}"); + } + } +} diff --git a/tests/PactNet.Abstractions.Tests/Matchers/MinMaxTypeMatcherTests.cs b/tests/PactNet.Abstractions.Tests/Matchers/MinMaxTypeMatcherTests.cs index 58c7fb80..9aaffd87 100644 --- a/tests/PactNet.Abstractions.Tests/Matchers/MinMaxTypeMatcherTests.cs +++ b/tests/PactNet.Abstractions.Tests/Matchers/MinMaxTypeMatcherTests.cs @@ -1,6 +1,6 @@ using System; +using System.Text.Json; using FluentAssertions; -using Newtonsoft.Json; using PactNet.Matchers; using Xunit; @@ -17,7 +17,7 @@ public void Ctor_Min_SerialisesCorrectly() var matcher = new MinMaxTypeMatcher(example, min); - string actual = JsonConvert.SerializeObject(matcher); + string actual = JsonSerializer.Serialize(matcher); string expected = @"{""pact:matcher:type"":""type"",""value"":[42],""min"":2}"; actual.Should().BeEquivalentTo(expected); @@ -31,7 +31,7 @@ public void Ctor_Max_SerialisesCorrectly() var matcher = new MinMaxTypeMatcher(example, max: max); - string actual = JsonConvert.SerializeObject(matcher); + string actual = JsonSerializer.Serialize(matcher); string expected = @"{""pact:matcher:type"":""type"",""value"":[42],""max"":2}"; actual.Should().BeEquivalentTo(expected); @@ -46,7 +46,7 @@ public void Ctor_MinMax_SerialisesCorrectly() var matcher = new MinMaxTypeMatcher(example, min, max); - string actual = JsonConvert.SerializeObject(matcher); + string actual = JsonSerializer.Serialize(matcher); string expected = @"{""pact:matcher:type"":""type"",""value"":[42],""min"":1,""max"":2}"; actual.Should().BeEquivalentTo(expected); diff --git a/tests/PactNet.Abstractions.Tests/Matchers/NullMatcherTests.cs b/tests/PactNet.Abstractions.Tests/Matchers/NullMatcherTests.cs index 6c471b8d..c6b62e41 100644 --- a/tests/PactNet.Abstractions.Tests/Matchers/NullMatcherTests.cs +++ b/tests/PactNet.Abstractions.Tests/Matchers/NullMatcherTests.cs @@ -1,5 +1,5 @@ +using System.Text.Json; using FluentAssertions; -using Newtonsoft.Json; using PactNet.Matchers; using Xunit; @@ -12,7 +12,7 @@ public void Ctor_WhenCalled_SerialisesCorrectly() { var matcher = new NullMatcher(); - string actual = JsonConvert.SerializeObject(matcher); + string actual = JsonSerializer.Serialize(matcher); string expected = $@"{{""pact:matcher:type"":""null""}}"; actual.Should().BeEquivalentTo(expected); diff --git a/tests/PactNet.Abstractions.Tests/Matchers/NumericMatcherTests.cs b/tests/PactNet.Abstractions.Tests/Matchers/NumericMatcherTests.cs index 861b4297..7ec28757 100644 --- a/tests/PactNet.Abstractions.Tests/Matchers/NumericMatcherTests.cs +++ b/tests/PactNet.Abstractions.Tests/Matchers/NumericMatcherTests.cs @@ -1,8 +1,7 @@ +using System.Globalization; +using System.Text.Json; using FluentAssertions; -using Newtonsoft.Json; using PactNet.Matchers; -using System; -using System.Globalization; using Xunit; namespace PactNet.Abstractions.Tests.Matchers @@ -16,8 +15,8 @@ public void Ctor_Int_SerialisesCorrectly() var matcher = new NumericMatcher(example); - string actual = JsonConvert.SerializeObject(matcher); - string expected = $@"{{""pact:matcher:type"":""number"",""value"":{example}}}"; + string actual = JsonSerializer.Serialize(matcher); + string expected = $@"{{""pact:matcher:type"":""number"",""value"":{JsonSerializer.Serialize(example)}}}"; actual.Should().BeEquivalentTo(expected); } @@ -29,10 +28,10 @@ public void Ctor_Float_SerialisesCorrectly() var matcher = new NumericMatcher(example); - string actual = JsonConvert.SerializeObject(matcher, new JsonSerializerSettings() { Culture = CultureInfo.InvariantCulture }); - FormattableString expected = $@"{{""pact:matcher:type"":""number"",""value"":{example}}}"; + string actual = JsonSerializer.Serialize(matcher); + string expected = $@"{{""pact:matcher:type"":""number"",""value"":{JsonSerializer.Serialize(example)}}}"; - actual.Should().BeEquivalentTo(expected.ToString(CultureInfo.InvariantCulture)); + actual.Should().Be(expected); } [Fact] @@ -42,10 +41,10 @@ public void Ctor_Double_SerialisesCorrectly() var matcher = new NumericMatcher(example); - string actual = JsonConvert.SerializeObject(matcher, new JsonSerializerSettings() { Culture = CultureInfo.InvariantCulture }); - FormattableString expected = $@"{{""pact:matcher:type"":""number"",""value"":{example}}}"; + string actual = JsonSerializer.Serialize(matcher); + string expected = $@"{{""pact:matcher:type"":""number"",""value"":{JsonSerializer.Serialize(example)}}}"; - actual.Should().BeEquivalentTo(expected.ToString(CultureInfo.InvariantCulture)); + actual.Should().Be(expected); } [Fact] @@ -55,10 +54,10 @@ public void Ctor_Numeric_SerialisesCorrectly() var matcher = new NumericMatcher(example); - string actual = JsonConvert.SerializeObject(matcher, new JsonSerializerSettings() { Culture = CultureInfo.InvariantCulture }); - FormattableString expected = $@"{{""pact:matcher:type"":""number"",""value"":{example}}}"; + string actual = JsonSerializer.Serialize(matcher); + string expected = $@"{{""pact:matcher:type"":""number"",""value"":{JsonSerializer.Serialize(example)}}}"; - actual.Should().BeEquivalentTo(expected.ToString(CultureInfo.InvariantCulture)); + actual.Should().Be(expected.ToString(CultureInfo.InvariantCulture)); } } } diff --git a/tests/PactNet.Abstractions.Tests/Matchers/RegexMatcherTests.cs b/tests/PactNet.Abstractions.Tests/Matchers/RegexMatcherTests.cs index 2f0f003f..66e7bef2 100644 --- a/tests/PactNet.Abstractions.Tests/Matchers/RegexMatcherTests.cs +++ b/tests/PactNet.Abstractions.Tests/Matchers/RegexMatcherTests.cs @@ -1,5 +1,5 @@ +using System.Text.Json; using FluentAssertions; -using Newtonsoft.Json; using PactNet.Matchers; using Xunit; @@ -15,8 +15,8 @@ public void Ctor_WhenCalled_SerialisesCorrectly() var matcher = new RegexMatcher(example, regex); - string actual = JsonConvert.SerializeObject(matcher); - string expected = $@"{{""pact:matcher:type"":""regex"",""value"":""{example}"",""regex"":{JsonConvert.SerializeObject(regex)}}}"; + string actual = JsonSerializer.Serialize(matcher); + string expected = $@"{{""pact:matcher:type"":""regex"",""value"":""{example}"",""regex"":{JsonSerializer.Serialize(regex)}}}"; actual.Should().BeEquivalentTo(expected); } diff --git a/tests/PactNet.Abstractions.Tests/Matchers/TypeMatcherTests.cs b/tests/PactNet.Abstractions.Tests/Matchers/TypeMatcherTests.cs index a75273e2..ad60df92 100644 --- a/tests/PactNet.Abstractions.Tests/Matchers/TypeMatcherTests.cs +++ b/tests/PactNet.Abstractions.Tests/Matchers/TypeMatcherTests.cs @@ -1,5 +1,5 @@ +using System.Text.Json; using FluentAssertions; -using Newtonsoft.Json; using PactNet.Matchers; using Xunit; @@ -14,7 +14,7 @@ public void Ctor_WhenCalled_SerialisesCorrectly() var matcher = new TypeMatcher(example); - string actual = JsonConvert.SerializeObject(matcher); + string actual = JsonSerializer.Serialize(matcher); string expected = $@"{{""pact:matcher:type"":""type"",""value"":""{example}""}}"; actual.Should().BeEquivalentTo(expected); diff --git a/tests/PactNet.Abstractions.Tests/PactNet.Abstractions.Tests.csproj b/tests/PactNet.Abstractions.Tests/PactNet.Abstractions.Tests.csproj index 50e62ed3..33213fec 100644 --- a/tests/PactNet.Abstractions.Tests/PactNet.Abstractions.Tests.csproj +++ b/tests/PactNet.Abstractions.Tests/PactNet.Abstractions.Tests.csproj @@ -1,10 +1,10 @@ <Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <TargetFrameworks Condition=" '$(OS)' != 'Windows_NT' "> - net7.0 + net8.0 </TargetFrameworks> <TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' "> - net7.0;net461 + net8.0;net462 </TargetFrameworks> </PropertyGroup> <ItemGroup Condition="$(TargetFramework.StartsWith('net4'))"> @@ -12,14 +12,14 @@ <Reference Include="System.Net.Http" /> </ItemGroup> <ItemGroup> - <PackageReference Include="FluentAssertions" Version="6.11.0" /> - <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.6.3" /> - <PackageReference Include="xunit" Version="2.4.1" /> - <PackageReference Include="xunit.runner.visualstudio" Version="2.4.3"> + <PackageReference Include="FluentAssertions" Version="6.12.0" /> + <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0" /> + <PackageReference Include="xunit" Version="2.6.6" /> + <PackageReference Include="xunit.runner.visualstudio" Version="2.5.6"> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <PrivateAssets>all</PrivateAssets> </PackageReference> - <PackageReference Include="coverlet.collector" Version="3.0.3"> + <PackageReference Include="coverlet.collector" Version="6.0.0"> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <PrivateAssets>all</PrivateAssets> </PackageReference> diff --git a/tests/PactNet.Tests/ConfiguredMessageVerifierTests.cs b/tests/PactNet.Tests/ConfiguredMessageVerifierTests.cs index b5eb1c8f..6b9bb0a7 100644 --- a/tests/PactNet.Tests/ConfiguredMessageVerifierTests.cs +++ b/tests/PactNet.Tests/ConfiguredMessageVerifierTests.cs @@ -1,10 +1,9 @@ using System; +using System.Text.Json; +using System.Text.Json.Nodes; using System.Threading.Tasks; using FluentAssertions; using Moq; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using Newtonsoft.Json.Serialization; using PactNet.Drivers; using PactNet.Exceptions; using PactNet.Interop; @@ -15,17 +14,14 @@ namespace PactNet.Tests { public class ConfiguredMessageVerifierTests { - private static readonly JsonSerializerSettings CamelCase = new() + private static readonly JsonSerializerOptions CamelCase = new() { - ContractResolver = new CamelCasePropertyNamesContractResolver() + PropertyNamingPolicy = JsonNamingPolicy.CamelCase }; - private static readonly JsonSerializerSettings SnakeCase = new() + private static readonly JsonSerializerOptions SnakeCase = new() { - ContractResolver = new DefaultContractResolver() - { - NamingStrategy = new SnakeCaseNamingStrategy() - } + PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower }; private readonly Mock<IMessageInteractionDriver> mockDriver; @@ -139,7 +135,7 @@ internal async Task VerifyAsync_FailedToVerifyAsync_DoesNotWritePactFile(PactSpe this.mockDriver.Verify(s => s.WritePactFile(It.IsAny<string>()), Times.Never); } - private (ConfiguredMessageVerifier Verifier, Message Message) SetupMessage(PactSpecification version, JsonSerializerSettings contentSettings = null) + private (ConfiguredMessageVerifier Verifier, Message Message) SetupMessage(PactSpecification version, JsonSerializerOptions contentSettings = null) { var verifier = new ConfiguredMessageVerifier(this.mockDriver.Object, this.config, version); this.config.DefaultJsonSettings = contentSettings ?? CamelCase; @@ -147,12 +143,12 @@ internal async Task VerifyAsync_FailedToVerifyAsync_DoesNotWritePactFile(PactSpe // this simulates what the FFI library does - the content uses user-supplied JSON settings // then they are interpreted literally to a JToken var contents = new Message { FooBar = 42 }; - string serialised = JsonConvert.SerializeObject(contents, this.config.DefaultJsonSettings); + string serialised = JsonSerializer.Serialize(contents, this.config.DefaultJsonSettings); - JObject token = version switch + JsonNode token = version switch { - PactSpecification.V3 => JObject.Parse(serialised), - PactSpecification.V4 => JObject.Parse(@$"{{""content"":{serialised},""contentType"":""application/json"",""encoded"":false}}"), + PactSpecification.V3 => JsonSerializer.Deserialize<JsonNode>(serialised), + PactSpecification.V4 => JsonSerializer.Deserialize<JsonNode>(@$"{{""content"":{serialised},""contentType"":""application/json"",""encoded"":false}}"), _ => throw new ArgumentOutOfRangeException(nameof(version), version, "Unsupported version") }; @@ -165,7 +161,7 @@ internal async Task VerifyAsync_FailedToVerifyAsync_DoesNotWritePactFile(PactSpe // the native message returned from the FFI is always camel-cased this.mockDriver .Setup(s => s.Reify()) - .Returns(JsonConvert.SerializeObject(native, CamelCase)); + .Returns(JsonSerializer.Serialize(native, CamelCase)); return (verifier, contents); } diff --git a/tests/PactNet.Tests/MessageBuilderTests.cs b/tests/PactNet.Tests/MessageBuilderTests.cs index 82fa466d..4fa72c5c 100644 --- a/tests/PactNet.Tests/MessageBuilderTests.cs +++ b/tests/PactNet.Tests/MessageBuilderTests.cs @@ -1,11 +1,11 @@ using System; using System.Collections.Generic; +using System.Text.Json; using Moq; -using Newtonsoft.Json; -using Newtonsoft.Json.Serialization; using PactNet.Drivers; using PactNet.Interop; using Xunit; +using Match = PactNet.Matchers.Match; namespace PactNet.Tests { @@ -21,7 +21,7 @@ public MessageBuilderTests() { this.mockDriver = new Mock<IMessageInteractionDriver>(); - this.config = new PactConfig { DefaultJsonSettings = new JsonSerializerSettings() }; + this.config = new PactConfig { DefaultJsonSettings = new JsonSerializerOptions() }; this.builder = new MessageBuilder(this.mockDriver.Object, this.config, PactSpecification.V4); } @@ -83,9 +83,23 @@ public void WithJsonContent_WithCustomSettings_AddsContentWithOverriddenSettings var content = new { Id = 1, Desc = "description" }; const string expected = @"{""id"":1,""desc"":""description""}"; - this.builder.WithJsonContent(content, new JsonSerializerSettings + this.builder.WithJsonContent(content, new JsonSerializerOptions { - ContractResolver = new CamelCasePropertyNamesContractResolver() + PropertyNamingPolicy = JsonNamingPolicy.CamelCase + }); + + this.mockDriver.Verify(s => s.WithContents("application/json", expected, 0)); + } + + [Fact] + public void WithJsonContent_MatcherProperties_AddsContent() + { + dynamic content = new { Matcher = Match.Integer(42) }; + const string expected = @"{""matcher"":{""pact:matcher:type"":""integer"",""value"":42}}"; + + this.builder.WithJsonContent(content, new JsonSerializerOptions + { + PropertyNamingPolicy = JsonNamingPolicy.CamelCase }); this.mockDriver.Verify(s => s.WithContents("application/json", expected, 0)); diff --git a/tests/PactNet.Tests/PactExtensionsTests.cs b/tests/PactNet.Tests/PactExtensionsTests.cs index e917b5cc..160205b9 100644 --- a/tests/PactNet.Tests/PactExtensionsTests.cs +++ b/tests/PactNet.Tests/PactExtensionsTests.cs @@ -4,10 +4,10 @@ using System.Net; using System.Net.Http; using System.Text; +using System.Text.Json; +using System.Text.Json.Serialization; using System.Threading.Tasks; using FluentAssertions; -using Newtonsoft.Json; -using Newtonsoft.Json.Serialization; using PactNet.Matchers; using PactNet.Output.Xunit; using Xunit; @@ -52,9 +52,9 @@ public class PactExtensionsTests public PactExtensionsTests(ITestOutputHelper output) { - var jsonSettings = new JsonSerializerSettings + var jsonSettings = new JsonSerializerOptions { - ContractResolver = new CamelCasePropertyNamesContractResolver() + PropertyNamingPolicy = JsonNamingPolicy.CamelCase }; this.config = new PactConfig @@ -286,7 +286,7 @@ await http.VerifyAsync(async ctx => actualPact.Should().Be(expectedPact); } - private static async Task PerformRequestAsync(IConsumerContext context, TestData body, JsonSerializerSettings jsonSettings) + private static async Task PerformRequestAsync(IConsumerContext context, TestData body, JsonSerializerOptions jsonSettings) { var client = new HttpClient { @@ -294,7 +294,7 @@ private static async Task PerformRequestAsync(IConsumerContext context, TestData }; client.DefaultRequestHeaders.Add("X-Request", new[] { "request1", "request2" }); - string content = JsonConvert.SerializeObject(body, jsonSettings); + string content = JsonSerializer.Serialize(body, jsonSettings); HttpResponseMessage response = await client.PostAsync("/things?param=value1¶m=value2", new StringContent(content, Encoding.UTF8, "application/json")); @@ -303,7 +303,7 @@ private static async Task PerformRequestAsync(IConsumerContext context, TestData response.Headers.GetValues("X-Response").Should().BeEquivalentTo(new[] { "response1", "response2" }); string responseContent = await response.Content.ReadAsStringAsync(); - TestData responseData = JsonConvert.DeserializeObject<TestData>(responseContent, jsonSettings); + TestData responseData = JsonSerializer.Deserialize<TestData>(responseContent, jsonSettings); responseData.Should().BeEquivalentTo(body); } @@ -312,7 +312,7 @@ public class TestData public bool Bool { get; set; } public int Int { get; set; } public string String { get; set; } - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public ICollection<TestData> Children { get; set; } } } diff --git a/tests/PactNet.Tests/PactNet.Tests.csproj b/tests/PactNet.Tests/PactNet.Tests.csproj index 84e2391c..98f360d6 100644 --- a/tests/PactNet.Tests/PactNet.Tests.csproj +++ b/tests/PactNet.Tests/PactNet.Tests.csproj @@ -2,10 +2,10 @@ <PropertyGroup> <TargetFrameworks Condition=" '$(OS)' != 'Windows_NT' "> - net7.0 + net8.0 </TargetFrameworks> <TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' "> - net7.0;net461 + net8.0;net462 </TargetFrameworks> <IsPackable>false</IsPackable> <LangVersion>latest</LangVersion> @@ -23,16 +23,16 @@ </ItemGroup> <ItemGroup> - <PackageReference Include="AutoFixture" Version="4.17.0" /> - <PackageReference Include="FluentAssertions" Version="6.11.0" /> - <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.6.3" /> - <PackageReference Include="Moq" Version="4.18.4" /> - <PackageReference Include="xunit" Version="2.4.1" /> - <PackageReference Include="xunit.runner.visualstudio" Version="2.4.3"> + <PackageReference Include="AutoFixture" Version="4.18.1" /> + <PackageReference Include="FluentAssertions" Version="6.12.0" /> + <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0" /> + <PackageReference Include="Moq" Version="4.20.70" /> + <PackageReference Include="xunit" Version="2.6.6" /> + <PackageReference Include="xunit.runner.visualstudio" Version="2.5.6"> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <PrivateAssets>all</PrivateAssets> </PackageReference> - <PackageReference Include="coverlet.collector" Version="3.0.3"> + <PackageReference Include="coverlet.collector" Version="6.0.0"> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <PrivateAssets>all</PrivateAssets> </PackageReference> diff --git a/tests/PactNet.Tests/RequestBuilderTests.cs b/tests/PactNet.Tests/RequestBuilderTests.cs index dbad909f..d4be9624 100644 --- a/tests/PactNet.Tests/RequestBuilderTests.cs +++ b/tests/PactNet.Tests/RequestBuilderTests.cs @@ -1,10 +1,9 @@ using System; using System.Collections.Generic; using System.Net.Http; +using System.Text.Json; using FluentAssertions; using Moq; -using Newtonsoft.Json; -using Newtonsoft.Json.Serialization; using PactNet.Drivers; using Xunit; using Match = PactNet.Matchers.Match; @@ -17,13 +16,13 @@ public class RequestBuilderTests private readonly Mock<IHttpInteractionDriver> mockDriver; - private readonly JsonSerializerSettings settings; + private readonly JsonSerializerOptions settings; public RequestBuilderTests() { this.mockDriver = new Mock<IHttpInteractionDriver>(); - this.settings = new JsonSerializerSettings(); + this.settings = new JsonSerializerOptions(); this.builder = new RequestBuilder(this.mockDriver.Object, this.settings); } @@ -162,7 +161,7 @@ public void WithJsonBody_OverrideContentType_AddsRequestBodyWithOverriddenConten public void WithJsonBody_OverrideJsonSettings_AddsRequestBodyWithOverriddenSettings() { this.builder.WithJsonBody(new { Foo = 42 }, - new JsonSerializerSettings { ContractResolver = new CamelCasePropertyNamesContractResolver() }); + new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }); this.mockDriver.Verify(s => s.WithRequestBody("application/json", @"{""foo"":42}")); } @@ -171,10 +170,7 @@ public void WithJsonBody_OverrideJsonSettings_AddsRequestBodyWithOverriddenSetti public void WithJsonBody_OverrideContentTypeAndSettings_AddsRequestBodyWithOverriddenContentTypeAndSettings() { this.builder.WithJsonBody(new { Foo = 42 }, - new JsonSerializerSettings - { - ContractResolver = new CamelCasePropertyNamesContractResolver() - }, + new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }, "application/json-patch+json"); this.mockDriver.Verify(s => s.WithRequestBody("application/json-patch+json", @"{""foo"":42}")); diff --git a/tests/PactNet.Tests/ResponseBuilderTests.cs b/tests/PactNet.Tests/ResponseBuilderTests.cs index 60fe9fc4..d92cee71 100644 --- a/tests/PactNet.Tests/ResponseBuilderTests.cs +++ b/tests/PactNet.Tests/ResponseBuilderTests.cs @@ -1,7 +1,6 @@ using System.Net; +using System.Text.Json; using Moq; -using Newtonsoft.Json; -using Newtonsoft.Json.Serialization; using PactNet.Drivers; using Xunit; using Match = PactNet.Matchers.Match; @@ -14,13 +13,13 @@ public class ResponseBuilderTests private readonly Mock<IHttpInteractionDriver> mockDriver; - private readonly JsonSerializerSettings settings; + private readonly JsonSerializerOptions settings; public ResponseBuilderTests() { this.mockDriver = new Mock<IHttpInteractionDriver>(); - this.settings = new JsonSerializerSettings(); + this.settings = new JsonSerializerOptions(); this.builder = new ResponseBuilder(this.mockDriver.Object, this.settings); } @@ -99,7 +98,7 @@ public void WithJsonBody_WithoutCustomSettings_AddsRequestBodyWithDefaultSetting public void WithJsonBody_WithCustomSettings_AddsRequestBodyWithOverriddenSettings() { this.builder.WithJsonBody(new { Foo = 42 }, - new JsonSerializerSettings { ContractResolver = new CamelCasePropertyNamesContractResolver() }); + new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }); this.mockDriver.Verify(s => s.WithResponseBody("application/json", @"{""foo"":42}")); } diff --git a/tests/PactNet.Tests/Verifier/Messaging/MessageScenarioBuilderTests.cs b/tests/PactNet.Tests/Verifier/Messaging/MessageScenarioBuilderTests.cs index a5e993e4..68a319f5 100644 --- a/tests/PactNet.Tests/Verifier/Messaging/MessageScenarioBuilderTests.cs +++ b/tests/PactNet.Tests/Verifier/Messaging/MessageScenarioBuilderTests.cs @@ -1,7 +1,7 @@ using System; +using System.Text.Json; using System.Threading.Tasks; using FluentAssertions; -using Newtonsoft.Json; using PactNet.Verifier.Messaging; using Xunit; @@ -55,7 +55,7 @@ public void WithContent_WhenCalled_SetsContent() [Fact] public void WithContent_WithCustomSettings_SetsSettings() { - var expected = new JsonSerializerSettings(); + var expected = new JsonSerializerOptions(); this.builder.WithContent(() => "foo", expected); var actual = this.builder.Build().JsonSettings; @@ -77,7 +77,7 @@ public async Task WithContentAsync_WhenCalled_SetsContent() [Fact] public async Task WithContentAsync_WithCustomSettings_SetsSettings() { - var expected = new JsonSerializerSettings(); + var expected = new JsonSerializerOptions(); await this.builder.WithContentAsync(() => Task.FromResult<dynamic>(new { Foo = "Bar" }), expected); var actual = this.builder.Build().JsonSettings; diff --git a/tests/PactNet.Tests/Verifier/Messaging/MessageScenariosTests.cs b/tests/PactNet.Tests/Verifier/Messaging/MessageScenariosTests.cs index 1dc1d376..5f1c46ec 100644 --- a/tests/PactNet.Tests/Verifier/Messaging/MessageScenariosTests.cs +++ b/tests/PactNet.Tests/Verifier/Messaging/MessageScenariosTests.cs @@ -1,8 +1,8 @@ using System; using System.Collections.Generic; +using System.Text.Json; using System.Threading.Tasks; using FluentAssertions; -using Newtonsoft.Json; using PactNet.Verifier.Messaging; using Xunit; @@ -35,7 +35,7 @@ public void Add_SyncBuilder_AddsScenario() { object metadata = new { Key = "value" }; Func<dynamic> factory = () => new { Foo = 42 }; - JsonSerializerSettings settings = new JsonSerializerSettings(); + JsonSerializerOptions settings = new JsonSerializerOptions(); this.scenarios.Add("description", builder => builder.WithMetadata(metadata).WithContent(factory, settings)); @@ -50,7 +50,7 @@ public void Add_AsyncBuilder_AddsScenario() { object metadata = new { Key = "value" }; Func<Task<dynamic>> factory = () => Task.FromResult<dynamic>(new { Foo = 42 }); - JsonSerializerSettings settings = new JsonSerializerSettings(); + JsonSerializerOptions settings = new JsonSerializerOptions(); this.scenarios.Add("description", async builder => await builder.WithMetadata(metadata).WithContentAsync(factory, settings)); diff --git a/tests/PactNet.Tests/Verifier/Messaging/MessagingProviderTests.cs b/tests/PactNet.Tests/Verifier/Messaging/MessagingProviderTests.cs index 246079eb..db41fdd4 100644 --- a/tests/PactNet.Tests/Verifier/Messaging/MessagingProviderTests.cs +++ b/tests/PactNet.Tests/Verifier/Messaging/MessagingProviderTests.cs @@ -3,11 +3,11 @@ using System.Collections.ObjectModel; using System.Net; using System.Net.Http; +using System.Text.Json; +using System.Text.Json.Serialization; using System.Threading.Tasks; using FluentAssertions; using Moq; -using Newtonsoft.Json; -using Newtonsoft.Json.Serialization; using PactNet.Output.Xunit; using PactNet.Verifier; using PactNet.Verifier.Messaging; @@ -18,11 +18,10 @@ namespace PactNet.Tests.Verifier.Messaging { public class MessagingProviderTests : IDisposable { - private static readonly JsonSerializerSettings Settings = new JsonSerializerSettings + private static readonly JsonSerializerOptions Settings = new JsonSerializerOptions { - ContractResolver = new CamelCasePropertyNamesContractResolver(), - NullValueHandling = NullValueHandling.Ignore, - DefaultValueHandling = DefaultValueHandling.Ignore + PropertyNamingPolicy = JsonNamingPolicy.CamelCase, + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingDefault }; private readonly MessagingProvider provider; @@ -107,9 +106,9 @@ public async Task HandleMessage_ValidInteraction_ReturnsMetadataHeader() ["a message"] = new Scenario("a message", factory, metadata, - new JsonSerializerSettings + new JsonSerializerOptions { - ContractResolver = new CamelCasePropertyNamesContractResolver() + PropertyNamingPolicy = JsonNamingPolicy.CamelCase }) })); @@ -133,9 +132,9 @@ public async Task HandleMessage_ValidInteraction_ReturnsResponseBody() ["a message"] = new Scenario("a message", factory, null, - new JsonSerializerSettings + new JsonSerializerOptions { - ContractResolver = new CamelCasePropertyNamesContractResolver() + PropertyNamingPolicy = JsonNamingPolicy.CamelCase }) })); diff --git a/tests/PactNet.Tests/Verifier/PactVerifierTests.cs b/tests/PactNet.Tests/Verifier/PactVerifierTests.cs index 72efde3d..90007f50 100644 --- a/tests/PactNet.Tests/Verifier/PactVerifierTests.cs +++ b/tests/PactNet.Tests/Verifier/PactVerifierTests.cs @@ -1,10 +1,10 @@ using System; using System.Collections.Generic; using System.IO; +using System.Text.Json; using FluentAssertions; using FluentAssertions.Extensions; using Moq; -using Newtonsoft.Json; using PactNet.Output.Xunit; using PactNet.Verifier; using PactNet.Verifier.Messaging; @@ -62,7 +62,7 @@ public void WithHttpEndpoint_CalledTwice_Throws() [Fact] public void WithMessages_WhenCalled_SetsProviderInfo() { - var settings = new JsonSerializerSettings(); + var settings = new JsonSerializerOptions(); this.mockMessaging .Setup(m => m.Start(settings)) .Returns(new Uri("https://localhost:1234/pact-messaging/")); @@ -76,7 +76,7 @@ public void WithMessages_WhenCalled_SetsProviderInfo() public void WithMessages_CalledTwice_Throws() { this.mockMessaging - .Setup(m => m.Start(It.IsAny<JsonSerializerSettings>())) + .Setup(m => m.Start(It.IsAny<JsonSerializerOptions>())) .Returns(new Uri("https://localhost:1234/pact-messaging/")); Action action = () => this.verifier.WithMessages(_ => { }).WithMessages(_ => { }); @@ -178,7 +178,7 @@ public void PactBrokerSource() { // arrange this.mockMessaging - .Setup(m => m.Start(It.IsAny<JsonSerializerSettings>())) + .Setup(m => m.Start(It.IsAny<JsonSerializerOptions>())) .Returns(new Uri("https://localhost:1234/pact-messaging/")); this.mockMessaging