mirror of
https://github.com/wiremock/WireMock.Net.git
synced 2026-06-02 20:10:54 +02:00
Compare commits
17 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6caa3df521 | |||
| 1264df4a72 | |||
| 5b5d68be86 | |||
| e92041783c | |||
| 6912d52faf | |||
| 3685fccfaf | |||
| 36b0a93a6c | |||
| fed1c87663 | |||
| e2e83abeb5 | |||
| 3aef0ad7a2 | |||
| e68a73c3d5 | |||
| bd83a630ff | |||
| 52ac7e37dc | |||
| 0eccf43a8e | |||
| fa423370b1 | |||
| 1806ae39f8 | |||
| 67acdcf1d3 |
@@ -0,0 +1,5 @@
|
||||
# Copilot Instructions
|
||||
|
||||
## Project Guidelines
|
||||
- When running tests in this workspace, do not run tests for the net48 target framework.
|
||||
- When changing System.Text.Json code in this workspace, verify API availability for netstandard2.0 and netstandard2.1 instead of assuming newer APIs exist.
|
||||
@@ -0,0 +1,40 @@
|
||||
name: Publish to NuGet
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
name: Build, Pack, and Publish
|
||||
runs-on: windows-2022
|
||||
permissions:
|
||||
id-token: write # enable GitHub OIDC token issuance for this job
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Build projects
|
||||
shell: pwsh
|
||||
run: |
|
||||
Get-ChildItem ./src -Recurse -Filter *.csproj |
|
||||
ForEach-Object {
|
||||
dotnet build $_.FullName -c Release
|
||||
}
|
||||
|
||||
- name: Pack projects
|
||||
shell: pwsh
|
||||
run: |
|
||||
Get-ChildItem ./src -Recurse -Filter *.csproj |
|
||||
ForEach-Object {
|
||||
dotnet pack $_.FullName -c Release --no-build -o ./packages
|
||||
}
|
||||
|
||||
- name: NuGet login (OIDC → temp API key)
|
||||
uses: NuGet/login@v1
|
||||
id: login
|
||||
with:
|
||||
user: ${{ secrets.NUGET_USER }}
|
||||
|
||||
- name: Push to NuGet
|
||||
run: dotnet nuget push "**/packages/*.nupkg" --api-key ${{steps.login.outputs.NUGET_API_KEY}} --source "https://api.nuget.org/v3/index.json" --skip-duplicate
|
||||
+13
-2
@@ -1,12 +1,23 @@
|
||||
# 2.7.0 (24 May 2026)
|
||||
- [#1457](https://github.com/wiremock/WireMock.Net/pull/1457) - Update OpenTelemetry.Api from 1.14.0 to 1.15.3 in unit test project [dependencies] contributed by [dependabot[bot]](https://github.com/apps/dependabot)
|
||||
- [#1459](https://github.com/wiremock/WireMock.Net/pull/1459) - chore: update Handlebars 2.5.2 to 2.5.5 [dependencies] contributed by [kamisoft-fr](https://github.com/kamisoft-fr)
|
||||
- [#1461](https://github.com/wiremock/WireMock.Net/pull/1461) - Update Testcontainers nuget package to 4.12.0 [dependencies] contributed by [MD-V](https://github.com/MD-V)
|
||||
- [#1462](https://github.com/wiremock/WireMock.Net/pull/1462) - Update Scriban.Signed to latest [dependencies] contributed by [StefH](https://github.com/StefH)
|
||||
- [#1458](https://github.com/wiremock/WireMock.Net/issues/1458) - update Handlebars Humanizer package version [feature]
|
||||
|
||||
# 2.6.0 (11 May 2026)
|
||||
- [#1455](https://github.com/wiremock/WireMock.Net/pull/1455) - Fix request storing when RequestLogExpirationDuration is set [bug] [bug] contributed by [pbenko-xitaso](https://github.com/pbenko-xitaso)
|
||||
- [#1454](https://github.com/wiremock/WireMock.Net/issues/1454) - No requests stored in Standalone when RequestLogExpirationDuration is set [bug]
|
||||
|
||||
# 2.5.0 (04 May 2026)
|
||||
- [#1451](https://github.com/wiremock/WireMock.Net/pull/1451) - Feature/early mismatch [feature] contributed by [Stepami](https://github.com/Stepami)
|
||||
- [#1452](https://github.com/wiremock/WireMock.Net/pull/1452) - Bump log4net from 2.0.15 to 3.3.0 in example console app [dependencies, .NET] contributed by [dependabot[bot]](https://github.com/apps/dependabot)
|
||||
- [#1452](https://github.com/wiremock/WireMock.Net/pull/1452) - Bump log4net from 2.0.15 to 3.3.0 in example console app [dependencies] contributed by [dependabot[bot]](https://github.com/apps/dependabot)
|
||||
- [#1453](https://github.com/wiremock/WireMock.Net/pull/1453) - Fix CVE-2026-40021: upgrade log4net to 3.3.0 in examples/WireMock.Net.Service/packages.config [dependencies] contributed by [Copilot](https://github.com/apps/copilot-swe-agent)
|
||||
- [#1442](https://github.com/wiremock/WireMock.Net/issues/1442) - Bug: [grpc] WithBodyAsProtoBuf exception on match [bug]
|
||||
|
||||
# 2.4.0 (24 April 2026)
|
||||
- [#1437](https://github.com/wiremock/WireMock.Net/pull/1437) - Added feature to enable and disable mappings [feature] contributed by [jayaraman-venkatesan](https://github.com/jayaraman-venkatesan)
|
||||
- [#1450](https://github.com/wiremock/WireMock.Net/pull/1450) - Bump OpenTelemetry.Exporter.OpenTelemetryProtocol from 1.14.0 to 1.15.x [dependencies, .NET] contributed by [dependabot[bot]](https://github.com/apps/dependabot)
|
||||
- [#1450](https://github.com/wiremock/WireMock.Net/pull/1450) - Bump OpenTelemetry.Exporter.OpenTelemetryProtocol from 1.14.0 to 1.15.x [dependencies] contributed by [dependabot[bot]](https://github.com/apps/dependabot)
|
||||
- [#1421](https://github.com/wiremock/WireMock.Net/issues/1421) - Deactivate mapping without deleting it [feature]
|
||||
|
||||
# 2.3.0 (20 April 2026)
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<VersionPrefix>2.5.0</VersionPrefix>
|
||||
<VersionPrefix>2.8.0</VersionPrefix>
|
||||
<PackageIcon>WireMock.Net-Logo.png</PackageIcon>
|
||||
<PackageProjectUrl>https://github.com/wiremock/WireMock.Net</PackageProjectUrl>
|
||||
<PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
|
||||
@@ -52,7 +52,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="JetBrains.Annotations" Version="2025.2.4" PrivateAssets="All" />
|
||||
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="10.0.103" PrivateAssets="All" />
|
||||
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="10.0.300" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition=" '$(TargetFramework)' == 'net8.0' ">
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
rem https://github.com/StefH/GitHubReleaseNotes
|
||||
|
||||
SET version=2.5.0
|
||||
SET version=2.8.0
|
||||
|
||||
GitHubReleaseNotes --output CHANGELOG.md --skip-empty-releases --exclude-labels wontfix test question invalid doc duplicate example environment --version %version% --token %GH_TOKEN%
|
||||
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
# 2.5.0 (04 May 2026)
|
||||
- #1451 Feature/early mismatch [feature]
|
||||
- #1452 Bump log4net from 2.0.15 to 3.3.0 in example console app [dependencies, .NET]
|
||||
- #1453 Fix CVE-2026-40021: upgrade log4net to 3.3.0 in examples/WireMock.Net.Service/packages.config [dependencies]
|
||||
- #1442 Bug: [grpc] WithBodyAsProtoBuf exception on match [bug]
|
||||
# 2.7.0 (24 May 2026)
|
||||
- #1457 Update OpenTelemetry.Api from 1.14.0 to 1.15.3 in unit test project [dependencies]
|
||||
- #1459 chore: update Handlebars 2.5.2 to 2.5.5 [dependencies]
|
||||
- #1461 Update Testcontainers nuget package to 4.12.0 [dependencies]
|
||||
- #1462 Update Scriban.Signed to latest [dependencies]
|
||||
- #1458 update Handlebars Humanizer package version [feature]
|
||||
|
||||
The full release notes can be found here: https://github.com/wiremock/WireMock.Net/blob/master/CHANGELOG.md
|
||||
@@ -39,8 +39,9 @@ A C# .NET version based on [mock4net](https://github.com/alexvictoor/mock4net) w
|
||||
| | |
|
||||
| ***Quality*** | |
|
||||
| **Build Azure** | [](https://stef.visualstudio.com/WireMock.Net/_build/latest?definitionId=61) |
|
||||
| **Build GitHub** | [](https://github.com/wiremock/WireMock.Net/actions/workflows/ci.yml)
|
||||
| **Quality** | [](https://sonarcloud.io/project/issues?id=WireMock-Net_WireMock.Net) [](https://www.codefactor.io/repository/github/wiremock/wiremock.net) |
|
||||
| **Sonar Bugs** | [](https://sonarcloud.io/project/issues?id=WireMock-Net_WireMock.Net&resolved=false&types=BUG) [](https://sonarcloud.io/project/issues?id=WireMock-Net_WireMock.Net&resolved=false&types=CODE_SMELL) |
|
||||
| **SonarQube** | [](https://sonarcloud.io/project/issues?id=WireMock-Net_WireMock.Net&resolved=false&types=BUG) [](https://sonarcloud.io/project/issues?id=WireMock-Net_WireMock.Net&resolved=false&types=CODE_SMELL) |
|
||||
| **Coverage** | [](https://sonarcloud.io/component_measures?id=WireMock-Net_WireMock.Net&metric=coverage) [](https://codecov.io/gh/wiremock/WireMock.Net)|
|
||||
| **TIOBE** | [TIOBE Quality Indicator](https://ticsdemo.tiobe.com/tiobeweb/DEMO/TqiDashboard.html#axes=Project(WireMock.Net),Sub()&metric=tqi)
|
||||
|
||||
@@ -64,6 +65,7 @@ A C# .NET version based on [mock4net](https://github.com/alexvictoor/mock4net) w
|
||||
| | | |
|
||||
| **WireMock.Net.Extensions.Routing** | [](https://www.nuget.org/packages/WireMock.Net.Extensions.Routing) | [](https://www.myget.org/feed/wiremock-net/package/nuget/WireMock.Net.Extensions.Routing)
|
||||
| **WireMock.Net.Matchers.CSharpCode** | [](https://www.nuget.org/packages/WireMock.Net.Matchers.CSharpCode) | [](https://www.myget.org/feed/wiremock-net/package/nuget/WireMock.Net.Matchers.CSharpCode)
|
||||
| **WireMock.Net.Matchers.SystemTextJsonPath** | [](https://www.nuget.org/packages/WireMock.Net.Matchers.SystemTextJsonPath) | [](https://www.myget.org/feed/wiremock-net/package/nuget/WireMock.Net.Matchers.SystemTextJsonPath)
|
||||
| **WireMock.Net.OpenApiParser** | [](https://www.nuget.org/packages/WireMock.Net.OpenApiParser) | [](https://www.myget.org/feed/wiremock-net/package/nuget/WireMock.Net.OpenApiParser)
|
||||
| **WireMock.Net.MimePart** | [](https://www.nuget.org/packages/WireMock.Net.MimePart) | [](https://www.myget.org/feed/wiremock-net/package/nuget/WireMock.Net.MimePart)
|
||||
| **WireMock.Net.GraphQL** | [](https://www.nuget.org/packages/WireMock.Net.GraphQL) | [](https://www.myget.org/feed/wiremock-net/package/nuget/WireMock.Net.GraphQL)
|
||||
@@ -76,7 +78,7 @@ A C# .NET version based on [mock4net](https://github.com/alexvictoor/mock4net) w
|
||||
|
||||
<br />
|
||||
|
||||
🔺 **WireMock.Net.Minimal** does not include *WireMock.Net.MimePart*, *WireMock.Net.GraphQL*, *WireMock.Net.ProtoBuf* and *WireMock.Net.OpenTelemetry*.
|
||||
🔺 **WireMock.Net.Minimal** does not include *WireMock.Net.MimePart*, *WireMock.Net.GraphQL*, *WireMock.Net.ProtoBuf*, *WireMock.Net.OpenTelemetry* and *WireMock.Net.Matchers.SystemTextJsonPath*.
|
||||
|
||||
---
|
||||
|
||||
|
||||
+30
-33
@@ -1,4 +1,3 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 18
|
||||
VisualStudioVersion = 18.0.11205.157
|
||||
@@ -38,8 +37,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WireMock.Net.Client", "examples\WireMock.Net.Client\WireMock.Net.Client.csproj", "{74D91AD0-D96D-4FD2-AEC5-CC49D38346C0}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WireMock.Net.Service", "examples\Wiremock.Net.Service\WireMock.Net.Service.csproj", "{7F0B2446-0363-4720-AF46-F47F83B557DC}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WireMock.Net.Console.RequestLogTest", "examples\WireMock.Net.Console.RequestLogTest\WireMock.Net.Console.RequestLogTest.csproj", "{A9D039B9-7509-4CF1-9EFD-87EB82998575}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WireMock.Net.OpenApiParser.ConsoleApp", "examples\WireMock.Net.OpenApiParser.ConsoleApp\WireMock.Net.OpenApiParser.ConsoleApp.csproj", "{5C09FB93-1535-4F92-AF26-21E8A061EE4A}"
|
||||
@@ -76,10 +73,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WireMock.Net.Testcontainers
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WireMock.Net.TestcontainersExample", "examples\WireMock.Net.TestcontainersExample\WireMock.Net.TestcontainersExample.csproj", "{56A38798-C48B-4A4A-B805-071E05C02CE1}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tools", "tools", "{0147029F-FA4A-44B3-B79A-3C3574054EE4}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MultipartUploader", "tools\MultipartUploader\MultipartUploader.csproj", "{07C30227-ADEC-4BDE-8CDC-849D85A690BB}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WireMock.Net.Console.NET8", "examples\WireMock.Net.Console.NET8\WireMock.Net.Console.NET8.csproj", "{1EA72C0F-92E9-486B-8FFE-53F992BFC4AA}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WireMockAzureQueueProxy", "examples\WireMockAzureQueueProxy\WireMockAzureQueueProxy.csproj", "{7FC0B409-2682-40EE-B3B9-3930D6769D01}"
|
||||
@@ -156,6 +149,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WireMock.Net.TestWebApplica
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WireMock.Net.RestClient.AwesomeAssertions", "src\WireMock.Net.RestClient.AwesomeAssertions\WireMock.Net.RestClient.AwesomeAssertions.csproj", "{F4B2B967-98D7-4D93-9A5C-5EF7B84B941A}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WireMock.Net.Matchers.SystemTextJsonPath", "src\WireMock.Net.Matchers.SystemTextJsonPath\WireMock.Net.Matchers.SystemTextJsonPath.csproj", "{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WireMock.Net.Service", "examples\WireMock.Net.Service\WireMock.Net.Service.csproj", "{7F0B2446-0363-4720-AF46-F47F83B557DC}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
@@ -238,18 +235,6 @@ Global
|
||||
{74D91AD0-D96D-4FD2-AEC5-CC49D38346C0}.Release|x64.Build.0 = Release|Any CPU
|
||||
{74D91AD0-D96D-4FD2-AEC5-CC49D38346C0}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{74D91AD0-D96D-4FD2-AEC5-CC49D38346C0}.Release|x86.Build.0 = Release|Any CPU
|
||||
{7F0B2446-0363-4720-AF46-F47F83B557DC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{7F0B2446-0363-4720-AF46-F47F83B557DC}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{7F0B2446-0363-4720-AF46-F47F83B557DC}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{7F0B2446-0363-4720-AF46-F47F83B557DC}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{7F0B2446-0363-4720-AF46-F47F83B557DC}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{7F0B2446-0363-4720-AF46-F47F83B557DC}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{7F0B2446-0363-4720-AF46-F47F83B557DC}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{7F0B2446-0363-4720-AF46-F47F83B557DC}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{7F0B2446-0363-4720-AF46-F47F83B557DC}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{7F0B2446-0363-4720-AF46-F47F83B557DC}.Release|x64.Build.0 = Release|Any CPU
|
||||
{7F0B2446-0363-4720-AF46-F47F83B557DC}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{7F0B2446-0363-4720-AF46-F47F83B557DC}.Release|x86.Build.0 = Release|Any CPU
|
||||
{A9D039B9-7509-4CF1-9EFD-87EB82998575}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{A9D039B9-7509-4CF1-9EFD-87EB82998575}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{A9D039B9-7509-4CF1-9EFD-87EB82998575}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
@@ -394,18 +379,6 @@ Global
|
||||
{56A38798-C48B-4A4A-B805-071E05C02CE1}.Release|x64.Build.0 = Release|Any CPU
|
||||
{56A38798-C48B-4A4A-B805-071E05C02CE1}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{56A38798-C48B-4A4A-B805-071E05C02CE1}.Release|x86.Build.0 = Release|Any CPU
|
||||
{07C30227-ADEC-4BDE-8CDC-849D85A690BB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{07C30227-ADEC-4BDE-8CDC-849D85A690BB}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{07C30227-ADEC-4BDE-8CDC-849D85A690BB}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{07C30227-ADEC-4BDE-8CDC-849D85A690BB}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{07C30227-ADEC-4BDE-8CDC-849D85A690BB}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{07C30227-ADEC-4BDE-8CDC-849D85A690BB}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{07C30227-ADEC-4BDE-8CDC-849D85A690BB}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{07C30227-ADEC-4BDE-8CDC-849D85A690BB}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{07C30227-ADEC-4BDE-8CDC-849D85A690BB}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{07C30227-ADEC-4BDE-8CDC-849D85A690BB}.Release|x64.Build.0 = Release|Any CPU
|
||||
{07C30227-ADEC-4BDE-8CDC-849D85A690BB}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{07C30227-ADEC-4BDE-8CDC-849D85A690BB}.Release|x86.Build.0 = Release|Any CPU
|
||||
{1EA72C0F-92E9-486B-8FFE-53F992BFC4AA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{1EA72C0F-92E9-486B-8FFE-53F992BFC4AA}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{1EA72C0F-92E9-486B-8FFE-53F992BFC4AA}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
@@ -850,6 +823,30 @@ Global
|
||||
{F4B2B967-98D7-4D93-9A5C-5EF7B84B941A}.Release|x64.Build.0 = Release|Any CPU
|
||||
{F4B2B967-98D7-4D93-9A5C-5EF7B84B941A}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{F4B2B967-98D7-4D93-9A5C-5EF7B84B941A}.Release|x86.Build.0 = Release|Any CPU
|
||||
{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Release|x64.Build.0 = Release|Any CPU
|
||||
{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Release|x86.Build.0 = Release|Any CPU
|
||||
{7F0B2446-0363-4720-AF46-F47F83B557DC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{7F0B2446-0363-4720-AF46-F47F83B557DC}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{7F0B2446-0363-4720-AF46-F47F83B557DC}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{7F0B2446-0363-4720-AF46-F47F83B557DC}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{7F0B2446-0363-4720-AF46-F47F83B557DC}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{7F0B2446-0363-4720-AF46-F47F83B557DC}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{7F0B2446-0363-4720-AF46-F47F83B557DC}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{7F0B2446-0363-4720-AF46-F47F83B557DC}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{7F0B2446-0363-4720-AF46-F47F83B557DC}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{7F0B2446-0363-4720-AF46-F47F83B557DC}.Release|x64.Build.0 = Release|Any CPU
|
||||
{7F0B2446-0363-4720-AF46-F47F83B557DC}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{7F0B2446-0363-4720-AF46-F47F83B557DC}.Release|x86.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
@@ -861,7 +858,6 @@ Global
|
||||
{B6269AAC-170A-43D5-8B9A-579DED3D9A95} = {8F890C6F-9ACC-438D-928A-AD61CDA862F2}
|
||||
{31DC2EF8-C3FE-467D-84BE-FB5D956E612E} = {0BB8B634-407A-4610-A91F-11586990767A}
|
||||
{74D91AD0-D96D-4FD2-AEC5-CC49D38346C0} = {985E0ADB-D4B4-473A-AA40-567E279B7946}
|
||||
{7F0B2446-0363-4720-AF46-F47F83B557DC} = {985E0ADB-D4B4-473A-AA40-567E279B7946}
|
||||
{A9D039B9-7509-4CF1-9EFD-87EB82998575} = {985E0ADB-D4B4-473A-AA40-567E279B7946}
|
||||
{5C09FB93-1535-4F92-AF26-21E8A061EE4A} = {985E0ADB-D4B4-473A-AA40-567E279B7946}
|
||||
{B6269AAC-170A-4346-8B9A-579DED3D9A95} = {8F890C6F-9ACC-438D-928A-AD61CDA862F2}
|
||||
@@ -876,7 +872,6 @@ Global
|
||||
{BAA9EC2A-874B-45CE-8E51-A73622DC7F3D} = {985E0ADB-D4B4-473A-AA40-567E279B7946}
|
||||
{12B016A5-9D8B-4EFE-96C2-CA51BE43367D} = {8F890C6F-9ACC-438D-928A-AD61CDA862F2}
|
||||
{56A38798-C48B-4A4A-B805-071E05C02CE1} = {985E0ADB-D4B4-473A-AA40-567E279B7946}
|
||||
{07C30227-ADEC-4BDE-8CDC-849D85A690BB} = {0147029F-FA4A-44B3-B79A-3C3574054EE4}
|
||||
{1EA72C0F-92E9-486B-8FFE-53F992BFC4AA} = {985E0ADB-D4B4-473A-AA40-567E279B7946}
|
||||
{7FC0B409-2682-40EE-B3B9-3930D6769D01} = {985E0ADB-D4B4-473A-AA40-567E279B7946}
|
||||
{B1580A38-84E7-44BE-8FE7-3EE5031D74A1} = {985E0ADB-D4B4-473A-AA40-567E279B7946}
|
||||
@@ -914,6 +909,8 @@ Global
|
||||
{2CE8E3A6-59CC-FE9C-9399-AD54E1FA862B} = {985E0ADB-D4B4-473A-AA40-567E279B7946}
|
||||
{3B05CC76-C3CB-8667-6B65-3129DFB25681} = {0BB8B634-407A-4610-A91F-11586990767A}
|
||||
{F4B2B967-98D7-4D93-9A5C-5EF7B84B941A} = {8F890C6F-9ACC-438D-928A-AD61CDA862F2}
|
||||
{A1B2C3D4-E5F6-7890-ABCD-EF1234567890} = {8F890C6F-9ACC-438D-928A-AD61CDA862F2}
|
||||
{7F0B2446-0363-4720-AF46-F47F83B557DC} = {985E0ADB-D4B4-473A-AA40-567E279B7946}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {DC539027-9852-430C-B19F-FD035D018458}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<Sdk Name="Aspire.AppHost.Sdk" Version="13.1.0" />
|
||||
<Sdk Name="Aspire.AppHost.Sdk" Version="13.4.0" />
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
@@ -18,7 +18,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Aspire.Hosting.AppHost" Version="13.1.0" />
|
||||
<PackageReference Include="Aspire.Hosting.AppHost" Version="13.4.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<Sdk Name="Aspire.AppHost.Sdk" Version="13.1.0" />
|
||||
<Sdk Name="Aspire.AppHost.Sdk" Version="13.4.0" />
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
@@ -15,7 +15,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Aspire.Hosting.AppHost" Version="13.1.0" />
|
||||
<PackageReference Include="Aspire.Hosting.AppHost" Version="13.4.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
<PackageReference Include="Aspire.Hosting.Testing" Version="13.1.0" />
|
||||
<PackageReference Include="coverlet.collector" Version="6.0.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
|
||||
<PackageReference Include="System.Net.Http" Version="4.3.4" />
|
||||
<PackageReference Include="System.Text.RegularExpressions" Version="4.3.1" />
|
||||
<PackageReference Include="xunit" Version="2.5.3" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.3" />
|
||||
</ItemGroup>
|
||||
|
||||
+1
-1
@@ -16,7 +16,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="WireMock.Net" Version="1.25.0" />
|
||||
<PackageReference Include="WireMock.Net" Version="2.6.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -32,6 +32,9 @@
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<StartupObject>Wiremock.Net.Service.Program</StartupObject>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Newtonsoft.Json, Version=13.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Newtonsoft.Json.13.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||
|
||||
@@ -246,8 +246,8 @@ internal class Program
|
||||
throw new InvalidOperationException($"The {nameof(TestcontainersSettings.OS.DockerEndpointAuthConfig)} is null. Check if Docker is started.");
|
||||
}
|
||||
|
||||
using var dockerClientConfig = TestcontainersSettings.OS.DockerEndpointAuthConfig.GetDockerClientConfiguration();
|
||||
using var dockerClient = dockerClientConfig.CreateClient();
|
||||
var dockerClientBuilder = TestcontainersSettings.OS.DockerEndpointAuthConfig.GetDockerClientBuilder();
|
||||
using var dockerClient = dockerClientBuilder.Build();
|
||||
|
||||
var version = await dockerClient.System.GetVersionAsync();
|
||||
return version.Os.IndexOf("Windows", StringComparison.OrdinalIgnoreCase) >= 0 ? OSPlatform.Windows : OSPlatform.Linux;
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Aspire.Hosting" Version="13.1.0" />
|
||||
<PackageReference Include="Aspire.Hosting" Version="13.4.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -21,11 +21,6 @@ public class WireMockHealthCheck(WireMockServerResource resource) : IHealthCheck
|
||||
return HealthCheckResult.Unhealthy("WireMock.Net is not healthy");
|
||||
}
|
||||
|
||||
if (resource.ApiMappingState == WireMockMappingState.NotSubmitted)
|
||||
{
|
||||
return HealthCheckResult.Unhealthy("WireMock.Net has not received mappings");
|
||||
}
|
||||
|
||||
return HealthCheckResult.Healthy();
|
||||
}
|
||||
|
||||
@@ -41,4 +36,4 @@ public class WireMockHealthCheck(WireMockServerResource resource) : IHealthCheck
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -21,7 +21,13 @@ internal class WireMockLifecycleSubscriber(ILoggerFactory loggerFactory) : IDist
|
||||
|
||||
var endpoint = wireMockServerResource.GetEndpoint();
|
||||
Debug.Assert(endpoint.IsAllocated);
|
||||
}
|
||||
});
|
||||
|
||||
eventing.Subscribe<ResourceReadyEvent>(async (@event, ct) =>
|
||||
{
|
||||
if (@event.Resource is WireMockServerResource wireMockServerResource)
|
||||
{
|
||||
await wireMockServerResource.WaitForHealthAsync(ct);
|
||||
|
||||
await wireMockServerResource.CallAddProtoDefinitionsAsync(ct);
|
||||
|
||||
@@ -31,19 +31,19 @@ public partial class WireMockAssertions
|
||||
}
|
||||
|
||||
[CustomAssertion]
|
||||
public AndConstraint<WireMockAssertions> WithBodyAsJson(object body, string because = "", params object[] becauseArgs)
|
||||
public AndConstraint<WireMockAssertions> WithBodyAsJson(object body, IJsonMatcher? jsonMatcher = null, string because = "", params object[] becauseArgs)
|
||||
{
|
||||
return WithBodyAsJson(new JsonMatcher(body), because, becauseArgs);
|
||||
return WithBodyAsJson(jsonMatcher ?? new JsonMatcher(body), because, becauseArgs);
|
||||
}
|
||||
|
||||
[CustomAssertion]
|
||||
public AndConstraint<WireMockAssertions> WithBodyAsJson(string body, string because = "", params object[] becauseArgs)
|
||||
public AndConstraint<WireMockAssertions> WithBodyAsJson(string body, IJsonMatcher? jsonMatcher = null, string because = "", params object[] becauseArgs)
|
||||
{
|
||||
return WithBodyAsJson(new JsonMatcher(body), because, becauseArgs);
|
||||
return WithBodyAsJson(jsonMatcher ?? new JsonMatcher(body), because, becauseArgs);
|
||||
}
|
||||
|
||||
[CustomAssertion]
|
||||
public AndConstraint<WireMockAssertions> WithBodyAsJson(IObjectMatcher matcher, string because = "", params object[] becauseArgs)
|
||||
public AndConstraint<WireMockAssertions> WithBodyAsJson(IJsonMatcher matcher, string because = "", params object[] becauseArgs)
|
||||
{
|
||||
var (filter, condition) = BuildFilterAndCondition(r => r.BodyAsJson, matcher);
|
||||
|
||||
@@ -126,15 +126,44 @@ public partial class WireMockAssertions
|
||||
|
||||
private static string? FormatBody(object? body)
|
||||
{
|
||||
return body switch
|
||||
if (body == null)
|
||||
{
|
||||
null => null,
|
||||
string str => str,
|
||||
AnyOf<string, StringPattern>[] stringPatterns => FormatBodies(stringPatterns.Select(p => p.GetPattern())),
|
||||
byte[] bytes => $"byte[{bytes.Length}] {{...}}",
|
||||
JToken jToken => jToken.ToString(Formatting.None),
|
||||
_ => JToken.FromObject(body).ToString(Formatting.None)
|
||||
};
|
||||
return null;
|
||||
}
|
||||
|
||||
if (body is string str)
|
||||
{
|
||||
return str;
|
||||
}
|
||||
|
||||
if (body is AnyOf<string, StringPattern>[] stringPatterns)
|
||||
{
|
||||
return FormatBodies(stringPatterns.Select(p => p.GetPattern()));
|
||||
}
|
||||
|
||||
if (body is byte[] bytes)
|
||||
{
|
||||
return $"byte[{bytes.Length}] {{...}}";
|
||||
}
|
||||
|
||||
if (body is JToken jToken)
|
||||
{
|
||||
return jToken.ToString(Formatting.None);
|
||||
}
|
||||
|
||||
// System.IO.FileNotFoundException : Could not load file or assembly 'System.Text.Json, Version=10.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51'. The system cannot find the file specified.
|
||||
var typeName = body.GetType().FullName;
|
||||
if (typeName == "System.Text.Json.JsonElement")
|
||||
{
|
||||
return ((dynamic)body).GetRawText();
|
||||
}
|
||||
|
||||
if (typeName == "System.Text.Json.JsonDocument")
|
||||
{
|
||||
return ((dynamic)body).RootElement.GetRawText();
|
||||
}
|
||||
|
||||
return JToken.FromObject(body).ToString(Formatting.None);
|
||||
}
|
||||
|
||||
private static string? FormatBodies(IEnumerable<object?> bodies)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<Description>WireMock.Net.Routing extends WireMock.Net with modern, minimal-API-style routing for .NET</Description>
|
||||
<Authors>Gennadii Saltyshchak</Authors>
|
||||
@@ -25,7 +25,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="JsonConverter.Abstractions" Version="0.9.0" />
|
||||
<PackageReference Include="JsonConverter.Abstractions" Version="0.12.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -31,19 +31,19 @@ public partial class WireMockAssertions
|
||||
}
|
||||
|
||||
[CustomAssertion]
|
||||
public AndConstraint<WireMockAssertions> WithBodyAsJson(object body, string because = "", params object[] becauseArgs)
|
||||
public AndConstraint<WireMockAssertions> WithBodyAsJson(object body, IJsonMatcher? jsonMatcher = null, string because = "", params object[] becauseArgs)
|
||||
{
|
||||
return WithBodyAsJson(new JsonMatcher(body), because, becauseArgs);
|
||||
return WithBodyAsJson(jsonMatcher ?? new JsonMatcher(body), because, becauseArgs);
|
||||
}
|
||||
|
||||
[CustomAssertion]
|
||||
public AndConstraint<WireMockAssertions> WithBodyAsJson(string body, string because = "", params object[] becauseArgs)
|
||||
public AndConstraint<WireMockAssertions> WithBodyAsJson(string body, IJsonMatcher? jsonMatcher = null, string because = "", params object[] becauseArgs)
|
||||
{
|
||||
return WithBodyAsJson(new JsonMatcher(body), because, becauseArgs);
|
||||
return WithBodyAsJson(jsonMatcher ?? new JsonMatcher(body), because, becauseArgs);
|
||||
}
|
||||
|
||||
[CustomAssertion]
|
||||
public AndConstraint<WireMockAssertions> WithBodyAsJson(IObjectMatcher matcher, string because = "", params object[] becauseArgs)
|
||||
public AndConstraint<WireMockAssertions> WithBodyAsJson(IJsonMatcher matcher, string because = "", params object[] becauseArgs)
|
||||
{
|
||||
var (filter, condition) = BuildFilterAndCondition(r => r.BodyAsJson, matcher);
|
||||
|
||||
@@ -126,15 +126,44 @@ public partial class WireMockAssertions
|
||||
|
||||
private static string? FormatBody(object? body)
|
||||
{
|
||||
return body switch
|
||||
if (body == null)
|
||||
{
|
||||
null => null,
|
||||
string str => str,
|
||||
AnyOf<string, StringPattern>[] stringPatterns => FormatBodies(stringPatterns.Select(p => p.GetPattern())),
|
||||
byte[] bytes => $"byte[{bytes.Length}] {{...}}",
|
||||
JToken jToken => jToken.ToString(Formatting.None),
|
||||
_ => JToken.FromObject(body).ToString(Formatting.None)
|
||||
};
|
||||
return null;
|
||||
}
|
||||
|
||||
if (body is string str)
|
||||
{
|
||||
return str;
|
||||
}
|
||||
|
||||
if (body is AnyOf<string, StringPattern>[] stringPatterns)
|
||||
{
|
||||
return FormatBodies(stringPatterns.Select(p => p.GetPattern()));
|
||||
}
|
||||
|
||||
if (body is byte[] bytes)
|
||||
{
|
||||
return $"byte[{bytes.Length}] {{...}}";
|
||||
}
|
||||
|
||||
if (body is JToken jToken)
|
||||
{
|
||||
return jToken.ToString(Formatting.None);
|
||||
}
|
||||
|
||||
// System.IO.FileNotFoundException : Could not load file or assembly 'System.Text.Json, Version=10.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51'. The system cannot find the file specified.
|
||||
var typeName = body.GetType().FullName;
|
||||
if (typeName == "System.Text.Json.JsonElement")
|
||||
{
|
||||
return ((dynamic)body).GetRawText();
|
||||
}
|
||||
|
||||
if (typeName == "System.Text.Json.JsonDocument")
|
||||
{
|
||||
return ((dynamic)body).RootElement.GetRawText();
|
||||
}
|
||||
|
||||
return JToken.FromObject(body).ToString(Formatting.None);
|
||||
}
|
||||
|
||||
private static string? FormatBodies(IEnumerable<object?> bodies)
|
||||
|
||||
@@ -0,0 +1,184 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Nodes;
|
||||
using AnyOfTypes;
|
||||
using Json.Path;
|
||||
using Stef.Validation;
|
||||
using WireMock.Extensions;
|
||||
using WireMock.Models;
|
||||
using WireMock.Util;
|
||||
|
||||
namespace WireMock.Matchers;
|
||||
|
||||
/// <summary>
|
||||
/// SystemTextJsonPathMatcher - behaves the same as JsonPathMatcher but uses System.Text.Json and Json.Path instead of Newtonsoft.Json.
|
||||
/// </summary>
|
||||
/// <seealso cref="ISystemTextJsonPathMatcher" />
|
||||
public class SystemTextJsonPathMatcher : ISystemTextJsonPathMatcher
|
||||
{
|
||||
private readonly AnyOf<string, StringPattern>[] _patterns;
|
||||
|
||||
/// <inheritdoc />
|
||||
public MatchBehaviour MatchBehaviour { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public object Value { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="SystemTextJsonPathMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="patterns">The patterns.</param>
|
||||
public SystemTextJsonPathMatcher(params string[] patterns)
|
||||
: this(MatchBehaviour.AcceptOnMatch, MatchOperator.Or, patterns.ToAnyOfPatterns())
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="SystemTextJsonPathMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="patterns">The patterns.</param>
|
||||
public SystemTextJsonPathMatcher(params AnyOf<string, StringPattern>[] patterns)
|
||||
: this(MatchBehaviour.AcceptOnMatch, MatchOperator.Or, patterns)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="SystemTextJsonPathMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="matchBehaviour">The match behaviour.</param>
|
||||
/// <param name="matchOperator">The <see cref="Matchers.MatchOperator"/> to use. (default = "Or")</param>
|
||||
/// <param name="patterns">The patterns.</param>
|
||||
public SystemTextJsonPathMatcher(
|
||||
MatchBehaviour matchBehaviour,
|
||||
MatchOperator matchOperator = MatchOperator.Or,
|
||||
params AnyOf<string, StringPattern>[] patterns)
|
||||
{
|
||||
_patterns = Guard.NotNull(patterns);
|
||||
MatchBehaviour = matchBehaviour;
|
||||
MatchOperator = matchOperator;
|
||||
Value = patterns;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public MatchResult IsMatch(string? input)
|
||||
{
|
||||
var score = MatchScores.Mismatch;
|
||||
Exception? exception = null;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(input))
|
||||
{
|
||||
try
|
||||
{
|
||||
var node = JsonNode.Parse(input!);
|
||||
score = IsMatchInternal(node);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
exception = ex;
|
||||
}
|
||||
}
|
||||
|
||||
return MatchResult.From(Name, MatchBehaviourHelper.Convert(MatchBehaviour, score), exception);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public MatchResult IsMatch(object? input)
|
||||
{
|
||||
var score = MatchScores.Mismatch;
|
||||
Exception? exception = null;
|
||||
|
||||
// When input is null or byte[], return Mismatch.
|
||||
if (input != null && input is not byte[])
|
||||
{
|
||||
try
|
||||
{
|
||||
JsonNode? node = input switch
|
||||
{
|
||||
JsonNode jsonNode => jsonNode,
|
||||
string str => JsonNode.Parse(str),
|
||||
_ => JsonNode.Parse(JsonSerializer.Serialize(input))
|
||||
};
|
||||
|
||||
score = IsMatchInternal(node);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
exception = ex;
|
||||
}
|
||||
}
|
||||
|
||||
return MatchResult.From(Name, MatchBehaviourHelper.Convert(MatchBehaviour, score), exception);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public AnyOf<string, StringPattern>[] GetPatterns()
|
||||
{
|
||||
return _patterns;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public MatchOperator MatchOperator { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Name => nameof(SystemTextJsonPathMatcher);
|
||||
|
||||
/// <inheritdoc />
|
||||
public string GetCSharpCodeArguments()
|
||||
{
|
||||
return $"new {Name}" +
|
||||
$"(" +
|
||||
$"{MatchBehaviour.GetFullyQualifiedEnumValue()}, " +
|
||||
$"{MatchOperator.GetFullyQualifiedEnumValue()}, " +
|
||||
$"{MappingConverterUtils.ToCSharpCodeArguments(_patterns)}" +
|
||||
$")";
|
||||
}
|
||||
|
||||
private double IsMatchInternal(JsonNode? node)
|
||||
{
|
||||
// JsonPath.Net requires the node to be inside an object or array for filter expressions.
|
||||
// Similar to JsonPathMatcher's ConvertJTokenToJArrayIfNeeded, wrap a plain object in an array
|
||||
// when it's an object with a single non-array child property.
|
||||
var evaluationNode = WrapIfNeeded(node);
|
||||
|
||||
var values = _patterns
|
||||
.Select(pattern =>
|
||||
{
|
||||
var path = JsonPath.Parse(pattern.GetPattern());
|
||||
var result = path.Evaluate(evaluationNode);
|
||||
return result.Matches is { Count: > 0 };
|
||||
})
|
||||
.ToArray();
|
||||
|
||||
return MatchScores.ToScore(values, MatchOperator);
|
||||
}
|
||||
|
||||
// Mirrors JsonPathMatcher.ConvertJTokenToJArrayIfNeeded:
|
||||
// If the node is an object with exactly one property whose value is not already an array,
|
||||
// wrap that value in an array so that filter expressions (e.g. [?(@.x == y)]) can match.
|
||||
private static JsonNode? WrapIfNeeded(JsonNode? node)
|
||||
{
|
||||
if (node is not JsonObject obj)
|
||||
{
|
||||
return node;
|
||||
}
|
||||
|
||||
var properties = obj.ToList();
|
||||
if (properties.Count != 1)
|
||||
{
|
||||
return node;
|
||||
}
|
||||
|
||||
var single = properties[0];
|
||||
if (single.Value is JsonArray)
|
||||
{
|
||||
return node;
|
||||
}
|
||||
|
||||
var clonedValue = JsonNode.Parse(single.Value?.ToJsonString() ?? "null");
|
||||
return new JsonObject
|
||||
{
|
||||
[single.Key] = new JsonArray(clonedValue)
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
[assembly: InternalsVisibleTo("WireMock.Net.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100e138ec44d93acac565953052636eb8d5e7e9f27ddb030590055cd1a0ab2069a5623f1f77ca907d78e0b37066ca0f6d63da7eecc3fcb65b76aa8ebeccf7ebe1d11264b8404cd9b1cbbf2c83f566e033b3e54129f6ef28daffff776ba7aebbc53c0d635ebad8f45f78eb3f7e0459023c218f003416e080f96a1a3c5ffeb56bee9e")]
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<Description>A SystemTextJsonPathMatcher which can be used to match WireMock.Net Requests using JsonPath.Net.</Description>
|
||||
<AssemblyTitle>WireMock.Net.Matchers.SystemTextJsonPath</AssemblyTitle>
|
||||
<Authors>Stef Heyenrath</Authors>
|
||||
<TargetFrameworks>netstandard2.0;net8.0</TargetFrameworks>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
<PackageTags>wiremock;matchers;matcher;jsonpath;systemtextjson</PackageTags>
|
||||
<RootNamespace>WireMock</RootNamespace>
|
||||
<PackageId>WireMock.Net.Matchers.SystemTextJsonPath</PackageId>
|
||||
<ProjectGuid>{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}</ProjectGuid>
|
||||
<PublishRepositoryUrl>true</PublishRepositoryUrl>
|
||||
<AllowedOutputExtensionsInPackageBuildOutputFolder>$(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb</AllowedOutputExtensionsInPackageBuildOutputFolder>
|
||||
<EmbedUntrackedSources>true</EmbedUntrackedSources>
|
||||
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
|
||||
<GenerateBindingRedirectsOutputType>true</GenerateBindingRedirectsOutputType>
|
||||
<SignAssembly>true</SignAssembly>
|
||||
<AssemblyOriginatorKeyFile>../WireMock.Net/WireMock.Net.snk</AssemblyOriginatorKeyFile>
|
||||
<!--<DelaySign>true</DelaySign>-->
|
||||
<PublicSign Condition=" '$(OS)' != 'Windows_NT' ">true</PublicSign>
|
||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)' == 'Debug - Sonar'">
|
||||
<CodeAnalysisRuleSet>../WireMock.Net/WireMock.Net.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
|
||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\WireMock.Net.Shared\WireMock.Net.Shared.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="JsonPath.Net" Version="3.0.2" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -1,7 +1,8 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using JsonConverter.Abstractions;
|
||||
using JsonConverter.Newtonsoft.Json;
|
||||
using Newtonsoft.Json;
|
||||
using WireMock.Matchers.Helpers;
|
||||
using WireMock.Models.Mime;
|
||||
using WireMock.Util;
|
||||
@@ -15,6 +16,8 @@ public class MimePartMatcher : IMimePartMatcher
|
||||
{
|
||||
private readonly IList<(string Name, Func<IMimePartData, MatchResult> func)> _matcherFunctions;
|
||||
|
||||
private readonly IJsonConverter _jsonConverter;
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Name => nameof(MimePartMatcher);
|
||||
|
||||
@@ -41,7 +44,8 @@ public class MimePartMatcher : IMimePartMatcher
|
||||
IStringMatcher? contentTypeMatcher,
|
||||
IStringMatcher? contentDispositionMatcher,
|
||||
IStringMatcher? contentTransferEncodingMatcher,
|
||||
IMatcher? contentMatcher
|
||||
IMatcher? contentMatcher,
|
||||
IJsonConverter? jsonConverter = null
|
||||
)
|
||||
{
|
||||
MatchBehaviour = matchBehaviour;
|
||||
@@ -49,6 +53,7 @@ public class MimePartMatcher : IMimePartMatcher
|
||||
ContentDispositionMatcher = contentDispositionMatcher;
|
||||
ContentTransferEncodingMatcher = contentTransferEncodingMatcher;
|
||||
ContentMatcher = contentMatcher;
|
||||
_jsonConverter = jsonConverter ?? new NewtonsoftJsonConverter();
|
||||
|
||||
_matcherFunctions = [];
|
||||
if (ContentTypeMatcher != null)
|
||||
@@ -107,7 +112,8 @@ public class MimePartMatcher : IMimePartMatcher
|
||||
ContentType = GetContentTypeAsString(mimePart.ContentType),
|
||||
DeserializeJson = true,
|
||||
ContentEncoding = null, // mimePart.ContentType?.CharsetEncoding.ToString(),
|
||||
DecompressGZipAndDeflate = true
|
||||
DecompressGZipAndDeflate = true,
|
||||
DefaultJsonConverter = _jsonConverter
|
||||
};
|
||||
|
||||
var bodyData = BodyParser.ParseAsync(bodyParserSettings).ConfigureAwait(false).GetAwaiter().GetResult();
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using JsonConverter.Abstractions;
|
||||
using WireMock.Util;
|
||||
|
||||
namespace WireMock.Http;
|
||||
@@ -15,7 +14,8 @@ internal static class HttpResponseMessageHelper
|
||||
Uri originalUri,
|
||||
bool deserializeJson,
|
||||
bool decompressGzipAndDeflate,
|
||||
bool deserializeFormUrlEncoded)
|
||||
bool deserializeFormUrlEncoded,
|
||||
IJsonConverter jsonConverter)
|
||||
{
|
||||
var responseMessage = new ResponseMessage { StatusCode = (int)httpResponseMessage.StatusCode };
|
||||
|
||||
@@ -45,7 +45,8 @@ internal static class HttpResponseMessageHelper
|
||||
DeserializeJson = deserializeJson,
|
||||
ContentEncoding = contentEncodingHeader?.FirstOrDefault(),
|
||||
DecompressGZipAndDeflate = decompressGzipAndDeflate,
|
||||
DeserializeFormUrlEncoded = deserializeFormUrlEncoded
|
||||
DeserializeFormUrlEncoded = deserializeFormUrlEncoded,
|
||||
DefaultJsonConverter = jsonConverter
|
||||
};
|
||||
responseMessage.BodyData = await BodyParser.ParseAsync(bodyParserSettings).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System.Net;
|
||||
|
||||
namespace WireMock;
|
||||
|
||||
internal interface IResponseMessageBuilder
|
||||
{
|
||||
ResponseMessage Create(HttpStatusCode statusCode, string? status, Guid? guid = null);
|
||||
|
||||
ResponseMessage Create(int statusCode, string? status, Guid? guid = null);
|
||||
|
||||
ResponseMessage Create(int statusCode, string? status, string? error, Guid? guid = null);
|
||||
|
||||
ResponseMessage Create(HttpStatusCode statusCode);
|
||||
}
|
||||
@@ -27,6 +27,7 @@ public class MappingBuilder : IMappingBuilder
|
||||
private readonly MappingToFileSaver _mappingToFileSaver;
|
||||
private readonly IGuidUtils _guidUtils;
|
||||
private readonly IDateTimeUtils _dateTimeUtils;
|
||||
private readonly IResponseMessageBuilder _responseMessageBuilder;
|
||||
|
||||
/// <summary>
|
||||
/// Create a MappingBuilder
|
||||
@@ -43,6 +44,7 @@ public class MappingBuilder : IMappingBuilder
|
||||
|
||||
_guidUtils = new GuidUtils();
|
||||
_dateTimeUtils = new DateTimeUtils();
|
||||
_responseMessageBuilder = new ResponseMessageBuilder(_dateTimeUtils);
|
||||
}
|
||||
|
||||
internal MappingBuilder(
|
||||
@@ -51,7 +53,8 @@ public class MappingBuilder : IMappingBuilder
|
||||
MappingConverter mappingConverter,
|
||||
MappingToFileSaver mappingToFileSaver,
|
||||
IGuidUtils guidUtils,
|
||||
IDateTimeUtils dateTimeUtils
|
||||
IDateTimeUtils dateTimeUtils,
|
||||
IResponseMessageBuilder responseMessageBuilder
|
||||
)
|
||||
{
|
||||
_settings = Guard.NotNull(settings);
|
||||
@@ -60,12 +63,13 @@ public class MappingBuilder : IMappingBuilder
|
||||
_mappingToFileSaver = Guard.NotNull(mappingToFileSaver);
|
||||
_guidUtils = Guard.NotNull(guidUtils);
|
||||
_dateTimeUtils = Guard.NotNull(dateTimeUtils);
|
||||
_responseMessageBuilder = Guard.NotNull(responseMessageBuilder);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IRespondWithAProvider Given(IRequestMatcher requestMatcher, bool saveToFile = false)
|
||||
{
|
||||
return new RespondWithAProvider(RegisterMapping, Guard.NotNull(requestMatcher), _settings, _guidUtils, _dateTimeUtils, saveToFile);
|
||||
return new RespondWithAProvider(RegisterMapping, Guard.NotNull(requestMatcher), _settings, _guidUtils, _dateTimeUtils, _responseMessageBuilder, saveToFile);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System.Linq;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using WireMock.Util;
|
||||
|
||||
|
||||
@@ -0,0 +1,173 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System.Text.Json;
|
||||
using WireMock.Util;
|
||||
|
||||
namespace WireMock.Matchers;
|
||||
|
||||
/// <summary>
|
||||
/// Generic AbstractSystemTextJsonPartialMatcher - uses System.Text.Json instead of Newtonsoft.Json.
|
||||
/// </summary>
|
||||
public abstract class AbstractSystemTextJsonPartialMatcher : SystemTextJsonMatcher
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AbstractSystemTextJsonPartialMatcher"/> class.
|
||||
/// </summary>
|
||||
protected AbstractSystemTextJsonPartialMatcher(string value, bool ignoreCase = false, bool regex = false)
|
||||
: base(value, ignoreCase, regex)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AbstractSystemTextJsonPartialMatcher"/> class.
|
||||
/// </summary>
|
||||
protected AbstractSystemTextJsonPartialMatcher(object value, bool ignoreCase = false, bool regex = false)
|
||||
: base(value, ignoreCase, regex)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AbstractSystemTextJsonPartialMatcher"/> class.
|
||||
/// </summary>
|
||||
protected AbstractSystemTextJsonPartialMatcher(MatchBehaviour matchBehaviour, object value, bool ignoreCase = false, bool regex = false)
|
||||
: base(matchBehaviour, value, ignoreCase, regex)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override bool IsMatch(JsonElement value, JsonElement? input)
|
||||
{
|
||||
if (input == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var inputElement = input.Value;
|
||||
|
||||
// Regex on a string value
|
||||
if (Regex && value.ValueKind == JsonValueKind.String)
|
||||
{
|
||||
var valueAsString = value.GetString()!;
|
||||
var inputAsString = GetStringValue(inputElement);
|
||||
|
||||
var (valid, result) = RegexUtils.MatchRegex(valueAsString, inputAsString);
|
||||
if (valid)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
// Guid comparison: both strings, both parseable as Guid
|
||||
if (value.ValueKind == JsonValueKind.String && inputElement.ValueKind == JsonValueKind.String)
|
||||
{
|
||||
var valueStr = value.GetString()!;
|
||||
var inputStr = inputElement.GetString()!;
|
||||
if (Guid.TryParse(valueStr, out var vg) && Guid.TryParse(inputStr, out var ig))
|
||||
{
|
||||
return IsMatch(vg.ToString(), ig.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
// Type mismatch (after regex/guid checks)
|
||||
if (value.ValueKind != inputElement.ValueKind)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (value.ValueKind)
|
||||
{
|
||||
case JsonValueKind.Object:
|
||||
{
|
||||
var nestedValues = value.EnumerateObject().ToArray();
|
||||
if (nestedValues.Length == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return nestedValues.All(pair =>
|
||||
{
|
||||
var selected = SelectElement(inputElement, pair.Name);
|
||||
return selected != null && IsMatch(pair.Value, selected.Value);
|
||||
});
|
||||
}
|
||||
|
||||
case JsonValueKind.Array:
|
||||
{
|
||||
var valuesArray = value.EnumerateArray().ToArray();
|
||||
if (valuesArray.Length == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
var tokenArray = inputElement.EnumerateArray().ToArray();
|
||||
if (tokenArray.Length == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return valuesArray.All(subFilter => tokenArray.Any(subToken => IsMatch(subFilter, subToken)));
|
||||
}
|
||||
|
||||
default:
|
||||
return IsMatch(GetStringValue(value), GetStringValue(inputElement));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if two strings are a match (matching can be done exact or wildcard).
|
||||
/// </summary>
|
||||
protected abstract bool IsMatch(string value, string input);
|
||||
|
||||
/// <summary>
|
||||
/// Selects a <see cref="JsonElement"/> from an object using a key that may be a plain property name,
|
||||
/// a dotted path (e.g. "a.b.c") or bracket notation (e.g. "['name.with.dot']"),
|
||||
/// mirroring Newtonsoft's <c>SelectToken</c> + direct indexer fallback.
|
||||
/// </summary>
|
||||
private static JsonElement? SelectElement(JsonElement input, string key)
|
||||
{
|
||||
if (input.ValueKind != JsonValueKind.Object)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// Direct property access (also handles keys containing colons or dots that are literal property names)
|
||||
if (input.TryGetProperty(key, out var direct))
|
||||
{
|
||||
return direct;
|
||||
}
|
||||
|
||||
// Bracket notation: ['property.name.with.dots']
|
||||
if (key.StartsWith("['") && key.EndsWith("']"))
|
||||
{
|
||||
var propertyName = key.Substring(2, key.Length - 4);
|
||||
return input.TryGetProperty(propertyName, out var bracketValue) ? bracketValue : null;
|
||||
}
|
||||
|
||||
// Dotted path: a.b.c
|
||||
if (key.Contains('.'))
|
||||
{
|
||||
var parts = key.Split('.');
|
||||
var current = input;
|
||||
foreach (var part in parts)
|
||||
{
|
||||
if (current.ValueKind != JsonValueKind.Object || !current.TryGetProperty(part, out var next))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
current = next;
|
||||
}
|
||||
|
||||
return current;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static string GetStringValue(JsonElement element)
|
||||
{
|
||||
return element.ValueKind == JsonValueKind.String
|
||||
? element.GetString()!
|
||||
: element.GetRawText();
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System.Linq;
|
||||
using AnyOfTypes;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Stef.Validation;
|
||||
@@ -13,9 +12,8 @@ namespace WireMock.Matchers;
|
||||
/// <summary>
|
||||
/// JsonPathMatcher
|
||||
/// </summary>
|
||||
/// <seealso cref="IStringMatcher" />
|
||||
/// <seealso cref="IObjectMatcher" />
|
||||
public class JsonPathMatcher : IStringMatcher, IObjectMatcher
|
||||
/// <seealso cref="IJsonPathMatcher" />
|
||||
public class JsonPathMatcher : IJsonPathMatcher
|
||||
{
|
||||
private readonly AnyOf<string, StringPattern>[] _patterns;
|
||||
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System.Linq;
|
||||
using System.Collections;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Stef.Validation;
|
||||
using WireMock.Extensions;
|
||||
using WireMock.Serialization;
|
||||
using WireMock.Util;
|
||||
using JsonUtils = WireMock.Util.JsonUtils;
|
||||
|
||||
namespace WireMock.Matchers;
|
||||
|
||||
@@ -31,6 +32,11 @@ public class JsonMatcher : IJsonMatcher
|
||||
/// </summary>
|
||||
public bool Regex { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Ignore array order when comparing
|
||||
/// </summary>
|
||||
public bool IgnoreArrayOrder { get; }
|
||||
|
||||
private readonly JToken _valueAsJToken;
|
||||
|
||||
/// <summary>
|
||||
@@ -39,7 +45,8 @@ public class JsonMatcher : IJsonMatcher
|
||||
/// <param name="value">The string value to check for equality.</param>
|
||||
/// <param name="ignoreCase">Ignore the case from the PropertyName and PropertyValue (string only).</param>
|
||||
/// <param name="regex">Support Regex.</param>
|
||||
public JsonMatcher(string value, bool ignoreCase = false, bool regex = false) : this(MatchBehaviour.AcceptOnMatch, value, ignoreCase, regex)
|
||||
/// <param name="ignoreArrayOrder">Ignore array element order when comparing.</param>
|
||||
public JsonMatcher(string value, bool ignoreCase = false, bool regex = false, bool ignoreArrayOrder = false) : this(MatchBehaviour.AcceptOnMatch, value, ignoreCase, regex, ignoreArrayOrder)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -49,7 +56,8 @@ public class JsonMatcher : IJsonMatcher
|
||||
/// <param name="value">The object value to check for equality.</param>
|
||||
/// <param name="ignoreCase">Ignore the case from the PropertyName and PropertyValue (string only).</param>
|
||||
/// <param name="regex">Support Regex.</param>
|
||||
public JsonMatcher(object value, bool ignoreCase = false, bool regex = false) : this(MatchBehaviour.AcceptOnMatch, value, ignoreCase, regex)
|
||||
/// <param name="ignoreArrayOrder">Ignore array element order when comparing.</param>
|
||||
public JsonMatcher(object value, bool ignoreCase = false, bool regex = false, bool ignoreArrayOrder = false) : this(MatchBehaviour.AcceptOnMatch, value, ignoreCase, regex, ignoreArrayOrder)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -60,16 +68,18 @@ public class JsonMatcher : IJsonMatcher
|
||||
/// <param name="value">The value to check for equality.</param>
|
||||
/// <param name="ignoreCase">Ignore the case from the PropertyName and PropertyValue (string only).</param>
|
||||
/// <param name="regex">Support Regex.</param>
|
||||
public JsonMatcher(MatchBehaviour matchBehaviour, object value, bool ignoreCase = false, bool regex = false)
|
||||
/// <param name="ignoreArrayOrder">Ignore array element order when comparing.</param>
|
||||
public JsonMatcher(MatchBehaviour matchBehaviour, object value, bool ignoreCase = false, bool regex = false, bool ignoreArrayOrder = false)
|
||||
{
|
||||
Guard.NotNull(value);
|
||||
|
||||
MatchBehaviour = matchBehaviour;
|
||||
IgnoreCase = ignoreCase;
|
||||
Regex = regex;
|
||||
IgnoreArrayOrder = ignoreArrayOrder;
|
||||
|
||||
Value = value;
|
||||
_valueAsJToken = JsonUtils.ConvertValueToJToken(value);
|
||||
_valueAsJToken = ConvertValueToJToken(value);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -83,7 +93,7 @@ public class JsonMatcher : IJsonMatcher
|
||||
{
|
||||
try
|
||||
{
|
||||
var inputAsJToken = JsonUtils.ConvertValueToJToken(input);
|
||||
var inputAsJToken = ConvertValueToJToken(input);
|
||||
|
||||
var match = IsMatch(RenameJToken(_valueAsJToken), RenameJToken(inputAsJToken));
|
||||
score = MatchScores.ToScore(match);
|
||||
@@ -103,9 +113,10 @@ public class JsonMatcher : IJsonMatcher
|
||||
return $"new {Name}" +
|
||||
$"(" +
|
||||
$"{MatchBehaviour.GetFullyQualifiedEnumValue()}, " +
|
||||
$"{CSharpFormatter.ConvertToAnonymousObjectDefinition(Value, 3)}, " +
|
||||
$"{CSharpFormatter.ConvertToAnonymousObjectDefinition(Value, 3)}, " +
|
||||
$"{CSharpFormatter.ToCSharpBooleanLiteral(IgnoreCase)}, " +
|
||||
$"{CSharpFormatter.ToCSharpBooleanLiteral(Regex)}" +
|
||||
$"{CSharpFormatter.ToCSharpBooleanLiteral(Regex)}, " +
|
||||
$"{CSharpFormatter.ToCSharpBooleanLiteral(IgnoreArrayOrder)}" +
|
||||
$")";
|
||||
}
|
||||
|
||||
@@ -182,6 +193,13 @@ public class JsonMatcher : IJsonMatcher
|
||||
return false;
|
||||
}
|
||||
|
||||
if (IgnoreArrayOrder)
|
||||
{
|
||||
// Sort both arrays by their string representation and compare
|
||||
valueArray = valueArray.OrderBy(t => t.ToString()).ToArray();
|
||||
inputArray = inputArray.OrderBy(t => t.ToString()).ToArray();
|
||||
}
|
||||
|
||||
return !valueArray.Where((valueToken, index) => !IsMatch(valueToken, inputArray[index])).Any();
|
||||
|
||||
default:
|
||||
@@ -241,6 +259,18 @@ public class JsonMatcher : IJsonMatcher
|
||||
return new JObject(renamedProperties);
|
||||
}
|
||||
|
||||
private static JToken ConvertValueToJToken(object value)
|
||||
{
|
||||
// Check if JToken, string, IEnumerable or object
|
||||
return value switch
|
||||
{
|
||||
JToken tokenValue => tokenValue,
|
||||
string stringValue => JsonConvert.DeserializeObject<JToken>(stringValue, JsonSerializationConstants.JsonDeserializerSettingsWithDateParsingNone)!,
|
||||
IEnumerable enumerableValue => JArray.FromObject(enumerableValue),
|
||||
_ => JObject.FromObject(value),
|
||||
};
|
||||
}
|
||||
|
||||
private static string? ToUpper(string? input)
|
||||
{
|
||||
return input?.ToUpperInvariant();
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System.Linq;
|
||||
using Stef.Validation;
|
||||
using WireMock.Matchers.Helpers;
|
||||
using WireMock.Util;
|
||||
|
||||
@@ -0,0 +1,299 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System.Collections;
|
||||
using System.Text.Json;
|
||||
using Stef.Validation;
|
||||
using WireMock.Extensions;
|
||||
using WireMock.Util;
|
||||
|
||||
namespace WireMock.Matchers;
|
||||
|
||||
/// <summary>
|
||||
/// SystemTextJsonMatcher - behaves the same as <see cref="JsonMatcher"/> but uses System.Text.Json instead of Newtonsoft.Json.
|
||||
/// </summary>
|
||||
public class SystemTextJsonMatcher : IJsonMatcher
|
||||
{
|
||||
private static readonly JsonSerializerOptions DefaultSerializerOptions = new()
|
||||
{
|
||||
PropertyNameCaseInsensitive = false
|
||||
};
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual string Name => nameof(SystemTextJsonMatcher);
|
||||
|
||||
/// <inheritdoc />
|
||||
public object Value { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public MatchBehaviour MatchBehaviour { get; }
|
||||
|
||||
/// <inheritdoc cref="IIgnoreCaseMatcher.IgnoreCase"/>
|
||||
public bool IgnoreCase { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Support Regex
|
||||
/// </summary>
|
||||
public bool Regex { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Ignore array order when comparing
|
||||
/// </summary>
|
||||
public bool IgnoreArrayOrder { get; }
|
||||
|
||||
private readonly JsonElement _valueAsJsonElement;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="SystemTextJsonMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="value">The string value to check for equality.</param>
|
||||
/// <param name="ignoreCase">Ignore the case from the PropertyName and PropertyValue (string only).</param>
|
||||
/// <param name="regex">Support Regex.</param>
|
||||
/// <param name="ignoreArrayOrder">Ignore array element order when comparing.</param>
|
||||
public SystemTextJsonMatcher(string value, bool ignoreCase = false, bool regex = false, bool ignoreArrayOrder = false)
|
||||
: this(MatchBehaviour.AcceptOnMatch, value, ignoreCase, regex, ignoreArrayOrder)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="SystemTextJsonMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="value">The object value to check for equality.</param>
|
||||
/// <param name="ignoreCase">Ignore the case from the PropertyName and PropertyValue (string only).</param>
|
||||
/// <param name="regex">Support Regex.</param>
|
||||
/// <param name="ignoreArrayOrder">Ignore array element order when comparing.</param>
|
||||
public SystemTextJsonMatcher(object value, bool ignoreCase = false, bool regex = false, bool ignoreArrayOrder = false)
|
||||
: this(MatchBehaviour.AcceptOnMatch, value, ignoreCase, regex, ignoreArrayOrder)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="SystemTextJsonMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="matchBehaviour">The match behaviour.</param>
|
||||
/// <param name="value">The value to check for equality.</param>
|
||||
/// <param name="ignoreCase">Ignore the case from the PropertyName and PropertyValue (string only).</param>
|
||||
/// <param name="regex">Support Regex.</param>
|
||||
/// <param name="ignoreArrayOrder">Ignore array element order when comparing.</param>
|
||||
public SystemTextJsonMatcher(MatchBehaviour matchBehaviour, object value, bool ignoreCase = false, bool regex = false, bool ignoreArrayOrder = false)
|
||||
{
|
||||
Guard.NotNull(value);
|
||||
|
||||
MatchBehaviour = matchBehaviour;
|
||||
IgnoreCase = ignoreCase;
|
||||
Regex = regex;
|
||||
IgnoreArrayOrder = ignoreArrayOrder;
|
||||
|
||||
Value = value;
|
||||
_valueAsJsonElement = ConvertToJsonElement(value);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public MatchResult IsMatch(object? input)
|
||||
{
|
||||
var score = MatchScores.Mismatch;
|
||||
Exception? error = null;
|
||||
|
||||
// When input is null or byte[], return Mismatch.
|
||||
if (input != null && input is not byte[])
|
||||
{
|
||||
try
|
||||
{
|
||||
var inputAsJsonElement = ConvertToJsonElement(input);
|
||||
|
||||
var match = IsMatch(NormalizeElement(_valueAsJsonElement), NormalizeElement(inputAsJsonElement));
|
||||
score = MatchScores.ToScore(match);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
error = ex;
|
||||
}
|
||||
}
|
||||
|
||||
return MatchResult.From(Name, MatchBehaviourHelper.Convert(MatchBehaviour, score), error);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual string GetCSharpCodeArguments()
|
||||
{
|
||||
return $"new {Name}" +
|
||||
$"(" +
|
||||
$"{MatchBehaviour.GetFullyQualifiedEnumValue()}, " +
|
||||
$"{CSharpFormatter.ConvertToAnonymousObjectDefinition(Value, 3)}, " +
|
||||
$"{CSharpFormatter.ToCSharpBooleanLiteral(IgnoreCase)}, " +
|
||||
$"{CSharpFormatter.ToCSharpBooleanLiteral(Regex)}, " +
|
||||
$"{CSharpFormatter.ToCSharpBooleanLiteral(IgnoreArrayOrder)}" +
|
||||
$")";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares the input against the matcher value
|
||||
/// </summary>
|
||||
protected virtual bool IsMatch(JsonElement value, JsonElement? input)
|
||||
{
|
||||
if (input == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var inputElement = input.Value;
|
||||
|
||||
// If using Regex and the value is a string, use the MatchRegex method.
|
||||
if (Regex && value.ValueKind == JsonValueKind.String)
|
||||
{
|
||||
var valueAsString = value.GetString()!;
|
||||
var inputAsString = inputElement.ValueKind == JsonValueKind.String
|
||||
? inputElement.GetString()!
|
||||
: inputElement.GetRawText();
|
||||
|
||||
var (valid, result) = RegexUtils.MatchRegex(valueAsString, inputAsString);
|
||||
if (valid)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
// If the value is a Guid (string) and input is a string, or vice versa, compare as strings.
|
||||
if (value.ValueKind == JsonValueKind.String && inputElement.ValueKind == JsonValueKind.String)
|
||||
{
|
||||
var valueStr = value.GetString()!;
|
||||
var inputStr = inputElement.GetString()!;
|
||||
|
||||
if (Guid.TryParse(valueStr, out var valueGuid) && Guid.TryParse(inputStr, out var inputGuid))
|
||||
{
|
||||
return valueGuid == inputGuid;
|
||||
}
|
||||
}
|
||||
|
||||
switch (value.ValueKind)
|
||||
{
|
||||
case JsonValueKind.Object:
|
||||
{
|
||||
if (inputElement.ValueKind != JsonValueKind.Object)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var valueProperties = value.EnumerateObject().ToDictionary(p => p.Name, p => p.Value);
|
||||
var inputProperties = inputElement.EnumerateObject().ToDictionary(p => p.Name, p => p.Value);
|
||||
|
||||
if (valueProperties.Count != inputProperties.Count)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach (var pair in valueProperties)
|
||||
{
|
||||
if (!inputProperties.TryGetValue(pair.Key, out var inputPropValue))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!IsMatch(pair.Value, inputPropValue))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
case JsonValueKind.Array:
|
||||
{
|
||||
if (inputElement.ValueKind != JsonValueKind.Array)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var valueArray = value.EnumerateArray().ToArray();
|
||||
var inputArray = inputElement.EnumerateArray().ToArray();
|
||||
|
||||
if (valueArray.Length != inputArray.Length)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (IgnoreArrayOrder)
|
||||
{
|
||||
// Sort both arrays by their string representation and compare
|
||||
valueArray = valueArray.OrderBy(e => e.GetRawText()).ToArray();
|
||||
inputArray = inputArray.OrderBy(e => e.GetRawText()).ToArray();
|
||||
}
|
||||
|
||||
return !valueArray.Where((valueToken, index) => !IsMatch(valueToken, inputArray[index])).Any();
|
||||
}
|
||||
|
||||
default:
|
||||
return value.GetRawText() == inputElement.GetRawText();
|
||||
}
|
||||
}
|
||||
|
||||
private JsonElement NormalizeElement(JsonElement element)
|
||||
{
|
||||
if (!IgnoreCase)
|
||||
{
|
||||
return element;
|
||||
}
|
||||
|
||||
var normalized = NormalizeValue(element);
|
||||
return ConvertToJsonElement(normalized);
|
||||
}
|
||||
|
||||
private object NormalizeValue(JsonElement element)
|
||||
{
|
||||
switch (element.ValueKind)
|
||||
{
|
||||
case JsonValueKind.Object:
|
||||
{
|
||||
var dict = new Dictionary<string, object?>();
|
||||
foreach (var prop in element.EnumerateObject())
|
||||
{
|
||||
var normalizedKey = prop.Name.ToUpperInvariant();
|
||||
dict[normalizedKey] = NormalizeValue(prop.Value);
|
||||
}
|
||||
|
||||
return dict;
|
||||
}
|
||||
|
||||
case JsonValueKind.Array:
|
||||
{
|
||||
if (Regex)
|
||||
{
|
||||
return element.EnumerateArray().Select(e => (object)e.GetRawText()).ToArray();
|
||||
}
|
||||
|
||||
return element.EnumerateArray().Select(NormalizeValue).ToArray();
|
||||
}
|
||||
|
||||
case JsonValueKind.String:
|
||||
{
|
||||
var str = element.GetString()!;
|
||||
return Regex ? str : str.ToUpperInvariant();
|
||||
}
|
||||
|
||||
default:
|
||||
return element.GetRawText();
|
||||
}
|
||||
}
|
||||
|
||||
private static JsonElement ConvertToJsonElement(object value)
|
||||
{
|
||||
switch (value)
|
||||
{
|
||||
case JsonElement jsonElement:
|
||||
return jsonElement;
|
||||
|
||||
case JsonDocument jsonDocument:
|
||||
return jsonDocument.RootElement;
|
||||
|
||||
case string stringValue:
|
||||
return JsonDocument.Parse(stringValue).RootElement;
|
||||
|
||||
case IEnumerable enumerableValue when value is not string:
|
||||
return JsonSerializer.SerializeToElement(enumerableValue, DefaultSerializerOptions);
|
||||
|
||||
default:
|
||||
var json = JsonSerializer.Serialize(value, DefaultSerializerOptions);
|
||||
return JsonDocument.Parse(json).RootElement;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using WireMock.Extensions;
|
||||
using WireMock.Util;
|
||||
|
||||
namespace WireMock.Matchers;
|
||||
|
||||
/// <summary>
|
||||
/// SystemTextJsonPartialMatcher - uses System.Text.Json instead of Newtonsoft.Json.
|
||||
/// </summary>
|
||||
public class SystemTextJsonPartialMatcher : AbstractSystemTextJsonPartialMatcher
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override string Name => nameof(SystemTextJsonPartialMatcher);
|
||||
|
||||
/// <inheritdoc />
|
||||
public SystemTextJsonPartialMatcher(string value, bool ignoreCase = false, bool regex = false)
|
||||
: base(value, ignoreCase, regex)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public SystemTextJsonPartialMatcher(object value, bool ignoreCase = false, bool regex = false)
|
||||
: base(value, ignoreCase, regex)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public SystemTextJsonPartialMatcher(MatchBehaviour matchBehaviour, object value, bool ignoreCase = false, bool regex = false)
|
||||
: base(matchBehaviour, value, ignoreCase, regex)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override bool IsMatch(string value, string input)
|
||||
{
|
||||
var exactStringMatcher = new ExactMatcher(MatchBehaviour.AcceptOnMatch, IgnoreCase, MatchOperator.Or, value);
|
||||
return exactStringMatcher.IsMatch(input).IsPerfect();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string GetCSharpCodeArguments()
|
||||
{
|
||||
return $"new {Name}" +
|
||||
$"(" +
|
||||
$"{MatchBehaviour.GetFullyQualifiedEnumValue()}, " +
|
||||
$"{CSharpFormatter.ConvertToAnonymousObjectDefinition(Value, 3)}, " +
|
||||
$"{CSharpFormatter.ToCSharpBooleanLiteral(IgnoreCase)}, " +
|
||||
$"{CSharpFormatter.ToCSharpBooleanLiteral(Regex)}" +
|
||||
$")";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using WireMock.Extensions;
|
||||
using WireMock.Util;
|
||||
|
||||
namespace WireMock.Matchers;
|
||||
|
||||
/// <summary>
|
||||
/// SystemTextJsonPartialWildcardMatcher - uses System.Text.Json instead of Newtonsoft.Json.
|
||||
/// </summary>
|
||||
public class SystemTextJsonPartialWildcardMatcher : AbstractSystemTextJsonPartialMatcher
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override string Name => nameof(SystemTextJsonPartialWildcardMatcher);
|
||||
|
||||
/// <inheritdoc />
|
||||
public SystemTextJsonPartialWildcardMatcher(string value, bool ignoreCase = false, bool regex = false)
|
||||
: base(value, ignoreCase, regex)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public SystemTextJsonPartialWildcardMatcher(object value, bool ignoreCase = false, bool regex = false)
|
||||
: base(value, ignoreCase, regex)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public SystemTextJsonPartialWildcardMatcher(MatchBehaviour matchBehaviour, object value, bool ignoreCase = false, bool regex = false)
|
||||
: base(matchBehaviour, value, ignoreCase, regex)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override bool IsMatch(string value, string input)
|
||||
{
|
||||
var wildcardStringMatcher = new WildcardMatcher(MatchBehaviour.AcceptOnMatch, value, IgnoreCase);
|
||||
return wildcardStringMatcher.IsMatch(input).IsPerfect();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string GetCSharpCodeArguments()
|
||||
{
|
||||
return $"new {Name}" +
|
||||
$"(" +
|
||||
$"{MatchBehaviour.GetFullyQualifiedEnumValue()}, " +
|
||||
$"{CSharpFormatter.ConvertToAnonymousObjectDefinition(Value, 3)}, " +
|
||||
$"{CSharpFormatter.ToCSharpBooleanLiteral(IgnoreCase)}, " +
|
||||
$"{CSharpFormatter.ToCSharpBooleanLiteral(Regex)}" +
|
||||
$")";
|
||||
}
|
||||
}
|
||||
@@ -72,6 +72,7 @@ internal partial class AspNetCoreSelfHost
|
||||
services.AddSingleton<IOwinResponseMapper, OwinResponseMapper>();
|
||||
services.AddSingleton<IGuidUtils, GuidUtils>();
|
||||
services.AddSingleton<IDateTimeUtils, DateTimeUtils>();
|
||||
services.AddSingleton<IResponseMessageBuilder, ResponseMessageBuilder>();
|
||||
services.AddSingleton<LogEntryMapper>();
|
||||
services.AddSingleton<IWireMockMiddlewareLogger, WireMockMiddlewareLogger>();
|
||||
|
||||
|
||||
@@ -11,12 +11,14 @@ internal class GlobalExceptionMiddleware
|
||||
{
|
||||
private readonly IWireMockMiddlewareOptions _options;
|
||||
private readonly IOwinResponseMapper _responseMapper;
|
||||
private readonly IResponseMessageBuilder _responseMessageBuilder;
|
||||
|
||||
public GlobalExceptionMiddleware(RequestDelegate next, IWireMockMiddlewareOptions options, IOwinResponseMapper responseMapper)
|
||||
public GlobalExceptionMiddleware(RequestDelegate next, IWireMockMiddlewareOptions options, IOwinResponseMapper responseMapper, IResponseMessageBuilder responseMessageBuilder)
|
||||
{
|
||||
Next = next;
|
||||
_options = Guard.NotNull(options);
|
||||
_responseMapper = Guard.NotNull(responseMapper);
|
||||
_responseMessageBuilder = Guard.NotNull(responseMessageBuilder);
|
||||
}
|
||||
|
||||
public RequestDelegate Next { get; }
|
||||
@@ -35,7 +37,7 @@ internal class GlobalExceptionMiddleware
|
||||
catch (Exception ex)
|
||||
{
|
||||
_options.Logger.Error("HttpStatusCode set to 500 {0}", ex);
|
||||
await _responseMapper.MapAsync(ResponseMessageBuilder.Create(500, JsonConvert.SerializeObject(ex)), ctx.Response).ConfigureAwait(false);
|
||||
await _responseMapper.MapAsync(_responseMessageBuilder.Create(500, JsonConvert.SerializeObject(ex)), ctx.Response).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -53,7 +53,8 @@ internal class OwinRequestMapper : IOwinRequestMapper
|
||||
ContentType = request.ContentType,
|
||||
DeserializeJson = !options.DisableJsonBodyParsing.GetValueOrDefault(false),
|
||||
ContentEncoding = contentEncodingHeader?.FirstOrDefault(),
|
||||
DecompressGZipAndDeflate = !options.DisableRequestBodyDecompressing.GetValueOrDefault(false)
|
||||
DecompressGZipAndDeflate = !options.DisableRequestBodyDecompressing.GetValueOrDefault(false),
|
||||
DefaultJsonConverter = options.DefaultJsonSerializer
|
||||
};
|
||||
|
||||
body = await BodyParser.ParseAsync(bodyParserSettings).ConfigureAwait(false);
|
||||
|
||||
@@ -26,7 +26,8 @@ internal class WireMockMiddleware(
|
||||
IMappingMatcher mappingMatcher,
|
||||
IWireMockMiddlewareLogger logger,
|
||||
IGuidUtils guidUtils,
|
||||
IDateTimeUtils dateTimeUtils
|
||||
IDateTimeUtils dateTimeUtils,
|
||||
IResponseMessageBuilder responseMessageBuilder
|
||||
)
|
||||
{
|
||||
private readonly object _lock = new();
|
||||
@@ -97,7 +98,7 @@ internal class WireMockMiddleware(
|
||||
{
|
||||
logRequest = true;
|
||||
options.Logger.Warn("HttpStatusCode set to 404 : No matching mapping found");
|
||||
response = ResponseMessageBuilder.Create(HttpStatusCode.NotFound, WireMockConstants.NoMatchingFound);
|
||||
response = responseMessageBuilder.Create(HttpStatusCode.NotFound, WireMockConstants.NoMatchingFound);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -109,7 +110,7 @@ internal class WireMockMiddleware(
|
||||
if (!authorizationHeaderPresent)
|
||||
{
|
||||
options.Logger.Error("HttpStatusCode set to 401, authorization header is missing.");
|
||||
response = ResponseMessageBuilder.Create(HttpStatusCode.Unauthorized, null);
|
||||
response = responseMessageBuilder.Create(HttpStatusCode.Unauthorized, null);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -117,7 +118,7 @@ internal class WireMockMiddleware(
|
||||
if (!MatchScores.IsPerfect(authorizationHeaderMatchResult.Score))
|
||||
{
|
||||
options.Logger.Error("HttpStatusCode set to 401, authentication failed.", authorizationHeaderMatchResult.Exception ?? throw new WireMockException("Authentication failed"));
|
||||
response = ResponseMessageBuilder.Create(HttpStatusCode.Unauthorized, null);
|
||||
response = responseMessageBuilder.Create(HttpStatusCode.Unauthorized, null);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -165,7 +166,7 @@ internal class WireMockMiddleware(
|
||||
options.Logger.Error($"Providing a Response for Mapping '{result.Match?.Mapping.Guid}' failed. HttpStatusCode set to 500. Exception: {ex}");
|
||||
WireMockActivitySource.RecordException(activity, ex);
|
||||
|
||||
response = ResponseMessageBuilder.Create(500, ex.Message);
|
||||
response = responseMessageBuilder.Create(500, ex.Message);
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -179,7 +180,7 @@ internal class WireMockMiddleware(
|
||||
{
|
||||
options.Logger.Error("HttpStatusCode set to 404 : No matching mapping found", ex);
|
||||
|
||||
var notFoundResponse = ResponseMessageBuilder.Create(HttpStatusCode.NotFound, WireMockConstants.NoMatchingFound);
|
||||
var notFoundResponse = responseMessageBuilder.Create(HttpStatusCode.NotFound, WireMockConstants.NoMatchingFound);
|
||||
await responseMapper.MapAsync(notFoundResponse, ctx.Response).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using WireMock.Logging;
|
||||
using WireMock.Owin.ActivityTracing;
|
||||
using WireMock.Serialization;
|
||||
@@ -42,7 +41,7 @@ internal class WireMockMiddlewareLogger(
|
||||
if (_options.SaveUnmatchedRequests == true && match?.RequestMatchResult is not { IsPerfectMatch: true })
|
||||
{
|
||||
var filename = $"{logEntry.Guid}.LogEntry.json";
|
||||
_options.FileSystemHandler?.WriteUnmatchedRequest(filename, JsonUtils.Serialize(logEntry));
|
||||
_options.FileSystemHandler?.WriteUnmatchedRequest(filename, _options.DefaultJsonSerializer.Serialize(logEntry));
|
||||
}
|
||||
}
|
||||
catch
|
||||
|
||||
@@ -7,4 +7,4 @@ using System.Runtime.CompilerServices;
|
||||
[assembly: InternalsVisibleTo("WireMock.Net.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100e138ec44d93acac565953052636eb8d5e7e9f27ddb030590055cd1a0ab2069a5623f1f77ca907d78e0b37066ca0f6d63da7eecc3fcb65b76aa8ebeccf7ebe1d11264b8404cd9b1cbbf2c83f566e033b3e54129f6ef28daffff776ba7aebbc53c0d635ebad8f45f78eb3f7e0459023c218f003416e080f96a1a3c5ffeb56bee9e")]
|
||||
|
||||
// Needed for Moq in the UnitTest project
|
||||
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")]
|
||||
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")]
|
||||
@@ -48,7 +48,8 @@ internal class ProxyHelper(WireMockServerSettings settings)
|
||||
originalUri,
|
||||
deserializeJson,
|
||||
decompressGzipAndDeflate,
|
||||
deserializeFormUrlEncoded
|
||||
deserializeFormUrlEncoded,
|
||||
_settings.DefaultJsonSerializer
|
||||
).ConfigureAwait(false);
|
||||
|
||||
IMapping? newMapping = null;
|
||||
|
||||
@@ -2,8 +2,10 @@
|
||||
|
||||
using System.Text;
|
||||
using JsonConverter.Abstractions;
|
||||
using JsonConverter.Newtonsoft.Json;
|
||||
using Stef.Validation;
|
||||
using WireMock.Models;
|
||||
using WireMock.Serialization;
|
||||
using WireMock.Types;
|
||||
using WireMock.Util;
|
||||
|
||||
@@ -119,11 +121,13 @@ public partial class Response
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IResponseBuilder WithBody(string body, string? destination = BodyDestinationFormat.SameAsSource, Encoding? encoding = null)
|
||||
public IResponseBuilder WithBody(string body, string? destination = BodyDestinationFormat.SameAsSource, Encoding? encoding = null, IJsonConverter? jsonConverter = null, JsonConverterOptions? options = null)
|
||||
{
|
||||
Guard.NotNull(body);
|
||||
|
||||
encoding ??= Encoding.UTF8;
|
||||
jsonConverter ??= new NewtonsoftJsonConverter();
|
||||
options ??= JsonSerializationConstants.JsonConverterOptionsWithDateParsingNone;
|
||||
|
||||
ResponseMessage.BodyDestination = destination;
|
||||
ResponseMessage.BodyData = new BodyData
|
||||
@@ -140,7 +144,7 @@ public partial class Response
|
||||
|
||||
case BodyDestinationFormat.Json:
|
||||
ResponseMessage.BodyData.DetectedBodyType = BodyType.Json;
|
||||
ResponseMessage.BodyData.BodyAsJson = JsonUtils.DeserializeObject(body);
|
||||
ResponseMessage.BodyData.BodyAsJson = jsonConverter.Deserialize<object>(body, options);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
@@ -197,7 +197,7 @@ public partial class Response : IResponseBuilder
|
||||
|
||||
if (ProxyAndRecordSettings != null && _httpClientForProxy != null)
|
||||
{
|
||||
string RemoveFirstOccurrence(string source, string find)
|
||||
static string RemoveFirstOccurrence(string source, string find)
|
||||
{
|
||||
int place = source.IndexOf(find, StringComparison.OrdinalIgnoreCase);
|
||||
return place >= 0 ? source.Remove(place, find.Length) : source;
|
||||
@@ -265,7 +265,7 @@ public partial class Response : IResponseBuilder
|
||||
var decoded = await protoBufMatcher.DecodeAsync(requestMessage.BodyData?.BodyAsBytes).ConfigureAwait(false);
|
||||
if (decoded != null)
|
||||
{
|
||||
requestMessageImplementation.BodyAsJson = JsonUtils.ConvertValueToJToken(decoded);
|
||||
requestMessageImplementation.BodyAsJson = settings.DefaultJsonSerializer.ToJsonToken(decoded);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,29 +9,30 @@ using WireMock.Util;
|
||||
|
||||
namespace WireMock;
|
||||
|
||||
internal static class ResponseMessageBuilder
|
||||
internal class ResponseMessageBuilder(IDateTimeUtils dateTimeUtils) : IResponseMessageBuilder
|
||||
{
|
||||
private static readonly IDictionary<string, WireMockList<string>> ContentTypeJsonHeaders = new Dictionary<string, WireMockList<string>>
|
||||
{
|
||||
{ HttpKnownHeaderNames.ContentType, new WireMockList<string> { WireMockConstants.ContentTypeJson } }
|
||||
};
|
||||
|
||||
internal static ResponseMessage Create(HttpStatusCode statusCode, string? status, Guid? guid = null)
|
||||
public ResponseMessage Create(HttpStatusCode statusCode, string? status, Guid? guid = null)
|
||||
{
|
||||
return Create((int)statusCode, status, guid);
|
||||
return Create((int)statusCode, status, null, guid);
|
||||
}
|
||||
|
||||
internal static ResponseMessage Create(int statusCode, string? status, Guid? guid = null)
|
||||
public ResponseMessage Create(int statusCode, string? status, Guid? guid = null)
|
||||
{
|
||||
return Create(statusCode, status, null, guid);
|
||||
}
|
||||
|
||||
internal static ResponseMessage Create(int statusCode, string? status, string? error, Guid? guid = null)
|
||||
public ResponseMessage Create(int statusCode, string? status, string? error, Guid? guid = null)
|
||||
{
|
||||
var response = new ResponseMessage
|
||||
{
|
||||
StatusCode = statusCode,
|
||||
Headers = ContentTypeJsonHeaders
|
||||
Headers = ContentTypeJsonHeaders,
|
||||
DateTime = dateTimeUtils.UtcNow
|
||||
};
|
||||
|
||||
if (status != null || error != null)
|
||||
@@ -51,7 +52,7 @@ internal static class ResponseMessageBuilder
|
||||
return response;
|
||||
}
|
||||
|
||||
internal static ResponseMessage Create(HttpStatusCode statusCode)
|
||||
public ResponseMessage Create(HttpStatusCode statusCode)
|
||||
{
|
||||
return new ResponseMessage
|
||||
{
|
||||
|
||||
@@ -16,7 +16,7 @@ using WireMock.WebSockets;
|
||||
|
||||
namespace WireMock.ResponseProviders;
|
||||
|
||||
internal class WebSocketResponseProvider(WebSocketBuilder builder, IGuidUtils guidUtils, IDateTimeUtils dateTimeUtils) : IResponseProvider
|
||||
internal class WebSocketResponseProvider(WebSocketBuilder builder, IGuidUtils guidUtils, IDateTimeUtils dateTimeUtils, IResponseMessageBuilder responseMessageBuilder) : IResponseProvider
|
||||
{
|
||||
public async Task<(IResponseMessage Message, IMapping? Mapping)> ProvideResponseAsync(
|
||||
IMapping mapping,
|
||||
@@ -27,7 +27,7 @@ internal class WebSocketResponseProvider(WebSocketBuilder builder, IGuidUtils gu
|
||||
// Check if this is a WebSocket upgrade request
|
||||
if (!context.WebSockets.IsWebSocketRequest)
|
||||
{
|
||||
return (ResponseMessageBuilder.Create(HttpStatusCode.BadRequest, "Bad Request: Not a WebSocket upgrade request"), null);
|
||||
return (responseMessageBuilder.Create(HttpStatusCode.BadRequest, "Bad Request: Not a WebSocket upgrade request"), null);
|
||||
}
|
||||
|
||||
if (!context.Items.TryGetValue<IWireMockMiddlewareOptions>(nameof(IWireMockMiddlewareOptions), out var options))
|
||||
@@ -110,7 +110,7 @@ internal class WebSocketResponseProvider(WebSocketBuilder builder, IGuidUtils gu
|
||||
// If we haven't upgraded yet, we can return HTTP error
|
||||
if (!context.Response.HasStarted)
|
||||
{
|
||||
return (ResponseMessageBuilder.Create(HttpStatusCode.InternalServerError, $"WebSocket error: {ex.Message}"), null);
|
||||
return (responseMessageBuilder.Create(HttpStatusCode.InternalServerError, $"WebSocket error: {ex.Message}"), null);
|
||||
}
|
||||
|
||||
// Already upgraded - return marker
|
||||
|
||||
@@ -1,55 +1,31 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using JsonConverter.Abstractions;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
#if NETSTANDARD2_0_OR_GREATER || NETCOREAPP3_1_OR_GREATER || NET6_0_OR_GREATER || NET461
|
||||
using System.Text.Json;
|
||||
#endif
|
||||
using JsonConverter.Abstractions.Models;
|
||||
|
||||
namespace WireMock.Serialization;
|
||||
|
||||
internal class MappingSerializer(IJsonConverter jsonConverter)
|
||||
{
|
||||
private static readonly JsonConverterOptions JsonConverterOptions = new JsonConverterOptions
|
||||
{
|
||||
DateParseHandling = (int) DateParseHandling.None
|
||||
};
|
||||
|
||||
internal T[] DeserializeJsonToArray<T>(string value)
|
||||
{
|
||||
// DeserializeObject
|
||||
return DeserializeObjectToArray<T>(jsonConverter.Deserialize<object>(value, JsonConverterOptions)!);
|
||||
switch (JsonTypeHelper.GetJsonType(value))
|
||||
{
|
||||
case JsonType.Array:
|
||||
return jsonConverter.Deserialize<T[]>(value, JsonSerializationConstants.JsonConverterOptionsWithDateParsingNone)!;
|
||||
|
||||
case JsonType.Object:
|
||||
var singleResult = jsonConverter.Deserialize<T>(value, JsonSerializationConstants.JsonConverterOptionsWithDateParsingNone);
|
||||
return [singleResult!];
|
||||
|
||||
default:
|
||||
throw new InvalidOperationException("Cannot deserialize the provided value to an array or object.");
|
||||
}
|
||||
}
|
||||
|
||||
internal static T[] DeserializeObjectToArray<T>(object value)
|
||||
internal T[] DeserializeObjectToArray<T>(object value)
|
||||
{
|
||||
if (value is JArray jArray)
|
||||
{
|
||||
return jArray.ToObject<T[]>()!;
|
||||
}
|
||||
|
||||
if (value is JObject jObject)
|
||||
{
|
||||
var singleResult = jObject.ToObject<T>();
|
||||
return [singleResult!];
|
||||
}
|
||||
|
||||
#if NETSTANDARD2_0_OR_GREATER || NETCOREAPP3_1_OR_GREATER || NET6_0_OR_GREATER || NET461
|
||||
if (value is JsonElement jElement)
|
||||
{
|
||||
if (jElement.ValueKind == JsonValueKind.Array)
|
||||
{
|
||||
return jElement.Deserialize<T[]>()!;
|
||||
}
|
||||
|
||||
if (jElement.ValueKind == JsonValueKind.Object)
|
||||
{
|
||||
var singleResult = jElement.Deserialize<T>();
|
||||
return [singleResult!];
|
||||
}
|
||||
}
|
||||
#endif
|
||||
throw new InvalidOperationException("Cannot deserialize the provided value to an array or object.");
|
||||
var json = jsonConverter.Serialize(value, JsonSerializationConstants.JsonConverterOptionsWithDateParsingNone);
|
||||
return DeserializeJsonToArray<T>(json);
|
||||
}
|
||||
}
|
||||
@@ -106,9 +106,29 @@ internal class MatcherMapper
|
||||
var valueForJsonPartialWildcardMatcher = matcherModel.Pattern ?? matcherModel.Patterns;
|
||||
return new JsonPartialWildcardMatcher(matchBehaviour, valueForJsonPartialWildcardMatcher!, ignoreCase, useRegex);
|
||||
|
||||
case nameof(SystemTextJsonMatcher):
|
||||
var valueForSystemTextJsonMatcher = matcherModel.Pattern ?? matcherModel.Patterns;
|
||||
return new SystemTextJsonMatcher(matchBehaviour, valueForSystemTextJsonMatcher!, ignoreCase, useRegex);
|
||||
|
||||
case nameof(SystemTextJsonPartialMatcher):
|
||||
var valueForSystemTextJsonPartialMatcher = matcherModel.Pattern ?? matcherModel.Patterns;
|
||||
return new SystemTextJsonPartialMatcher(matchBehaviour, valueForSystemTextJsonPartialMatcher!, ignoreCase, useRegex);
|
||||
|
||||
case nameof(SystemTextJsonPartialWildcardMatcher):
|
||||
var valueForSystemTextJsonPartialWildcardMatcher = matcherModel.Pattern ?? matcherModel.Patterns;
|
||||
return new SystemTextJsonPartialWildcardMatcher(matchBehaviour, valueForSystemTextJsonPartialWildcardMatcher!, ignoreCase, useRegex);
|
||||
|
||||
case nameof(JsonPathMatcher):
|
||||
return new JsonPathMatcher(matchBehaviour, matchOperator, stringPatterns);
|
||||
|
||||
case "SystemTextJsonPathMatcher":
|
||||
if (TypeLoader.TryLoadNewInstance<ISystemTextJsonPathMatcher>(out var systemTextJsonPathMatcher, matchBehaviour, matchOperator, stringPatterns))
|
||||
{
|
||||
return systemTextJsonPathMatcher;
|
||||
}
|
||||
|
||||
throw new InvalidOperationException("The 'SystemTextJsonPathMatcher' cannot be loaded. Please install the WireMock.Net.Matchers.SystemTextJsonPath package.");
|
||||
|
||||
case nameof(JmesPathMatcher):
|
||||
return new JmesPathMatcher(matchBehaviour, matchOperator, stringPatterns);
|
||||
|
||||
@@ -171,6 +191,10 @@ internal class MatcherMapper
|
||||
model.Regex = jsonMatcher.Regex;
|
||||
break;
|
||||
|
||||
case SystemTextJsonMatcher stjMatcher:
|
||||
model.Regex = stjMatcher.Regex;
|
||||
break;
|
||||
|
||||
case XPathMatcher xpathMatcher:
|
||||
model.XmlNamespaceMap = xpathMatcher.XmlNamespaceMap;
|
||||
break;
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Newtonsoft.Json;
|
||||
using WireMock.Admin.Mappings;
|
||||
using WireMock.Extensions;
|
||||
using WireMock.Pact.Models.V2;
|
||||
@@ -49,7 +51,7 @@ internal static class PactMapper
|
||||
pact.Interactions.Add(interaction);
|
||||
}
|
||||
|
||||
return (filename, JsonUtils.SerializeAsPactFile(pact));
|
||||
return (filename, SerializeAsPactFile(pact));
|
||||
}
|
||||
|
||||
private static PactRequest MapRequest(RequestModel request, string path)
|
||||
@@ -152,7 +154,7 @@ internal static class PactMapper
|
||||
/// </summary>
|
||||
private static object? TryDeserializeJsonStringAsObject(string? value)
|
||||
{
|
||||
return value != null ? JsonUtils.TryDeserializeObject<object?>(value) ?? value : null;
|
||||
return value != null ? TryDeserializeObject<object?>(value) ?? value : null;
|
||||
}
|
||||
|
||||
//private static string GetPatternAsStringFromMatchers(MatcherModel[]? matchers, string defaultValue)
|
||||
@@ -164,4 +166,22 @@ internal static class PactMapper
|
||||
|
||||
// return defaultValue;
|
||||
//}
|
||||
|
||||
private static byte[] SerializeAsPactFile(object value)
|
||||
{
|
||||
var json = JsonConvert.SerializeObject(value, JsonSerializationConstants.JsonSerializerSettingsPact);
|
||||
return Encoding.UTF8.GetBytes(json);
|
||||
}
|
||||
|
||||
private static T? TryDeserializeObject<T>(string json)
|
||||
{
|
||||
try
|
||||
{
|
||||
return JsonConvert.DeserializeObject<T>(json);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return default;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,8 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System.Linq;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using NJsonSchema;
|
||||
using NJsonSchema.Extensions;
|
||||
using NSwag;
|
||||
@@ -281,7 +282,7 @@ internal static class SwaggerMapper
|
||||
if (matcher is { Name: nameof(JsonMatcher) })
|
||||
{
|
||||
var pattern = GetPatternAsStringFromMatcher(matcher);
|
||||
if (JsonUtils.TryParseAsJObject(pattern, out var jObject))
|
||||
if (TryParseAsJObject(pattern, out var jObject))
|
||||
{
|
||||
return jObject;
|
||||
}
|
||||
@@ -292,6 +293,39 @@ internal static class SwaggerMapper
|
||||
return null;
|
||||
}
|
||||
|
||||
private static bool IsJson(string? value)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(value))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
value = value!.Trim();
|
||||
|
||||
return (value.StartsWith("{") && value.EndsWith("}")) || (value.StartsWith("[") && value.EndsWith("]"));
|
||||
}
|
||||
|
||||
private static bool TryParseAsJObject(string? strInput, [NotNullWhen(true)] out JObject? value)
|
||||
{
|
||||
value = null;
|
||||
|
||||
if (!IsJson(strInput))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// Try to convert this string into a JObject
|
||||
value = JObject.Parse(strInput!);
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static string GetContentType(RequestModel request)
|
||||
{
|
||||
var contentType = request.Headers?.FirstOrDefault(h => h.Name == "Content-Type");
|
||||
|
||||
@@ -24,6 +24,7 @@ internal class RespondWithAProvider : IRespondWithAProvider
|
||||
private readonly WireMockServerSettings _settings;
|
||||
private readonly IDateTimeUtils _dateTimeUtils;
|
||||
private readonly IGuidUtils _guidUtils;
|
||||
private readonly IResponseMessageBuilder _responseMessageBuilder;
|
||||
|
||||
private readonly bool _saveToFile;
|
||||
|
||||
@@ -56,6 +57,7 @@ internal class RespondWithAProvider : IRespondWithAProvider
|
||||
WireMockServerSettings settings,
|
||||
IGuidUtils guidUtils,
|
||||
IDateTimeUtils dateTimeUtils,
|
||||
IResponseMessageBuilder responseMessageBuilder,
|
||||
bool saveToFile = false
|
||||
)
|
||||
{
|
||||
@@ -64,6 +66,7 @@ internal class RespondWithAProvider : IRespondWithAProvider
|
||||
_settings = Guard.NotNull(settings);
|
||||
_dateTimeUtils = Guard.NotNull(dateTimeUtils);
|
||||
_guidUtils = Guard.NotNull(guidUtils);
|
||||
_responseMessageBuilder = Guard.NotNull(responseMessageBuilder);
|
||||
|
||||
_saveToFile = saveToFile;
|
||||
|
||||
@@ -79,7 +82,8 @@ internal class RespondWithAProvider : IRespondWithAProvider
|
||||
provider = new WebSocketResponseProvider(
|
||||
response.WebSocketBuilder,
|
||||
_guidUtils,
|
||||
_dateTimeUtils
|
||||
_dateTimeUtils,
|
||||
_responseMessageBuilder
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -346,11 +346,11 @@ public partial class WireMockServer
|
||||
}
|
||||
|
||||
o.CorsPolicyOptions = corsPolicyOptions;
|
||||
o.ClientCertificateMode = (Microsoft.AspNetCore.Server.Kestrel.Https.ClientCertificateMode) _settings.ClientCertificateMode;
|
||||
o.ClientCertificateMode = (Microsoft.AspNetCore.Server.Kestrel.Https.ClientCertificateMode)_settings.ClientCertificateMode;
|
||||
o.AcceptAnyClientCertificate = _settings.AcceptAnyClientCertificate;
|
||||
});
|
||||
|
||||
return ResponseMessageBuilder.Create(200, "Settings updated");
|
||||
return _responseMessageBuilder.Create(200, "Settings updated");
|
||||
}
|
||||
#endregion Settings
|
||||
|
||||
@@ -361,7 +361,7 @@ public partial class WireMockServer
|
||||
if (mapping == null)
|
||||
{
|
||||
_settings.Logger.Warn("HttpStatusCode set to 404 : Mapping not found");
|
||||
return ResponseMessageBuilder.Create(HttpStatusCode.NotFound, "Mapping not found");
|
||||
return _responseMessageBuilder.Create(HttpStatusCode.NotFound, "Mapping not found");
|
||||
}
|
||||
|
||||
var model = _mappingConverter.ToMappingModel(mapping);
|
||||
@@ -377,14 +377,14 @@ public partial class WireMockServer
|
||||
if (code is null)
|
||||
{
|
||||
_settings.Logger.Warn("HttpStatusCode set to 404 : Mapping not found");
|
||||
return ResponseMessageBuilder.Create(HttpStatusCode.NotFound, "Mapping not found");
|
||||
return _responseMessageBuilder.Create(HttpStatusCode.NotFound, "Mapping not found");
|
||||
}
|
||||
|
||||
return ToResponseMessage(code);
|
||||
}
|
||||
|
||||
_settings.Logger.Warn("HttpStatusCode set to 400");
|
||||
return ResponseMessageBuilder.Create(HttpStatusCode.BadRequest, "GUID is missing");
|
||||
return _responseMessageBuilder.Create(HttpStatusCode.BadRequest, "GUID is missing");
|
||||
}
|
||||
|
||||
private static TEnum GetEnumFromQuery<TEnum>(IRequestMessage requestMessage, TEnum defaultValue)
|
||||
@@ -411,22 +411,22 @@ public partial class WireMockServer
|
||||
var mappingModel = DeserializeObject<MappingModel>(requestMessage);
|
||||
var guidFromPut = ConvertMappingAndRegisterAsRespondProvider(mappingModel, guid);
|
||||
|
||||
return ResponseMessageBuilder.Create(HttpStatusCode.OK, "Mapping added or updated", guidFromPut);
|
||||
return _responseMessageBuilder.Create(HttpStatusCode.OK, "Mapping added or updated", guidFromPut);
|
||||
}
|
||||
|
||||
_settings.Logger.Warn("HttpStatusCode set to 404 : Mapping not found");
|
||||
return ResponseMessageBuilder.Create(HttpStatusCode.NotFound, "Mapping not found");
|
||||
return _responseMessageBuilder.Create(HttpStatusCode.NotFound, "Mapping not found");
|
||||
}
|
||||
|
||||
private IResponseMessage MappingDelete(HttpContext _, IRequestMessage requestMessage)
|
||||
{
|
||||
if (TryParseGuidFromRequestMessage(requestMessage, out var guid) && DeleteMapping(guid))
|
||||
{
|
||||
return ResponseMessageBuilder.Create(HttpStatusCode.OK, "Mapping removed", guid);
|
||||
return _responseMessageBuilder.Create(HttpStatusCode.OK, "Mapping removed", guid);
|
||||
}
|
||||
|
||||
_settings.Logger.Warn("HttpStatusCode set to 404 : Mapping not found");
|
||||
return ResponseMessageBuilder.Create(HttpStatusCode.NotFound, "Mapping not found");
|
||||
return _responseMessageBuilder.Create(HttpStatusCode.NotFound, "Mapping not found");
|
||||
}
|
||||
|
||||
private static bool TryParseGuidFromRequestMessage(IRequestMessage requestMessage, out Guid guid)
|
||||
@@ -452,12 +452,12 @@ public partial class WireMockServer
|
||||
if (mapping != null)
|
||||
{
|
||||
mapping.IsDisabled = false;
|
||||
return ResponseMessageBuilder.Create(HttpStatusCode.OK, "Mapping enabled", guid);
|
||||
return _responseMessageBuilder.Create(HttpStatusCode.OK, "Mapping enabled", guid);
|
||||
}
|
||||
}
|
||||
|
||||
_settings.Logger.Warn("HttpStatusCode set to 404 : Mapping not found");
|
||||
return ResponseMessageBuilder.Create(HttpStatusCode.NotFound, "Mapping not found");
|
||||
return _responseMessageBuilder.Create(HttpStatusCode.NotFound, "Mapping not found");
|
||||
}
|
||||
|
||||
private IResponseMessage MappingDisable(HttpContext _, IRequestMessage requestMessage)
|
||||
@@ -468,12 +468,12 @@ public partial class WireMockServer
|
||||
if (mapping != null)
|
||||
{
|
||||
mapping.IsDisabled = true;
|
||||
return ResponseMessageBuilder.Create(HttpStatusCode.OK, "Mapping disabled", guid);
|
||||
return _responseMessageBuilder.Create(HttpStatusCode.OK, "Mapping disabled", guid);
|
||||
}
|
||||
}
|
||||
|
||||
_settings.Logger.Warn("HttpStatusCode set to 404 : Mapping not found");
|
||||
return ResponseMessageBuilder.Create(HttpStatusCode.NotFound, "Mapping not found");
|
||||
return _responseMessageBuilder.Create(HttpStatusCode.NotFound, "Mapping not found");
|
||||
}
|
||||
#endregion Mapping/{guid}
|
||||
|
||||
@@ -496,7 +496,7 @@ public partial class WireMockServer
|
||||
{
|
||||
SaveStaticMappings();
|
||||
|
||||
return ResponseMessageBuilder.Create(200, "Mappings saved to disk");
|
||||
return _responseMessageBuilder.Create(200, "Mappings saved to disk");
|
||||
}
|
||||
|
||||
private MappingModel[] ToMappingModels()
|
||||
@@ -526,22 +526,22 @@ public partial class WireMockServer
|
||||
if (mappingModels.Length == 1)
|
||||
{
|
||||
var guid = ConvertMappingAndRegisterAsRespondProvider(mappingModels[0]);
|
||||
return ResponseMessageBuilder.Create(201, "Mapping added", guid);
|
||||
return _responseMessageBuilder.Create(201, "Mapping added", guid);
|
||||
}
|
||||
|
||||
ConvertMappingsAndRegisterAsRespondProvider(mappingModels);
|
||||
|
||||
return ResponseMessageBuilder.Create(201, "Mappings added");
|
||||
return _responseMessageBuilder.Create(201, "Mappings added");
|
||||
}
|
||||
catch (ArgumentException a)
|
||||
{
|
||||
_settings.Logger.Error("HttpStatusCode set to 400 {0}", a);
|
||||
return ResponseMessageBuilder.Create(400, a.Message);
|
||||
return _responseMessageBuilder.Create(400, a.Message);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_settings.Logger.Error("HttpStatusCode set to 500 {0}", e);
|
||||
return ResponseMessageBuilder.Create(500, e.ToString());
|
||||
return _responseMessageBuilder.Create(500, e.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -552,18 +552,18 @@ public partial class WireMockServer
|
||||
var deletedGuids = MappingsDeleteMappingFromBody(requestMessage);
|
||||
if (deletedGuids != null)
|
||||
{
|
||||
return ResponseMessageBuilder.Create(200, $"Mappings deleted. Affected GUIDs: [{string.Join(", ", deletedGuids.ToArray())}]");
|
||||
return _responseMessageBuilder.Create(200, $"Mappings deleted. Affected GUIDs: [{string.Join(", ", deletedGuids.ToArray())}]");
|
||||
}
|
||||
|
||||
// return bad request
|
||||
return ResponseMessageBuilder.Create(400, "Poorly formed mapping JSON.");
|
||||
return _responseMessageBuilder.Create(400, "Poorly formed mapping JSON.");
|
||||
}
|
||||
|
||||
ResetMappings();
|
||||
|
||||
ResetScenarios();
|
||||
|
||||
return ResponseMessageBuilder.Create(200, "Mappings deleted");
|
||||
return _responseMessageBuilder.Create(200, "Mappings deleted");
|
||||
}
|
||||
|
||||
private IEnumerable<Guid>? MappingsDeleteMappingFromBody(IRequestMessage requestMessage)
|
||||
@@ -615,14 +615,14 @@ public partial class WireMockServer
|
||||
message += " and static mappings reloaded";
|
||||
}
|
||||
|
||||
return ResponseMessageBuilder.Create(200, message);
|
||||
return _responseMessageBuilder.Create(200, message);
|
||||
}
|
||||
|
||||
private IResponseMessage ReloadStaticMappings(HttpContext _, IRequestMessage __)
|
||||
{
|
||||
ReadStaticMappings();
|
||||
|
||||
return ResponseMessageBuilder.Create(200, "Static Mappings reloaded");
|
||||
return _responseMessageBuilder.Create(200, "Static Mappings reloaded");
|
||||
}
|
||||
#endregion Mappings
|
||||
|
||||
@@ -640,18 +640,18 @@ public partial class WireMockServer
|
||||
}
|
||||
|
||||
_settings.Logger.Warn("HttpStatusCode set to 404 : Request not found");
|
||||
return ResponseMessageBuilder.Create(HttpStatusCode.NotFound, "Request not found");
|
||||
return _responseMessageBuilder.Create(HttpStatusCode.NotFound, "Request not found");
|
||||
}
|
||||
|
||||
private IResponseMessage RequestDelete(HttpContext _, IRequestMessage requestMessage)
|
||||
{
|
||||
if (TryParseGuidFromRequestMessage(requestMessage, out var guid) && DeleteLogEntry(guid))
|
||||
{
|
||||
return ResponseMessageBuilder.Create(200, "Request removed");
|
||||
return _responseMessageBuilder.Create(200, "Request removed");
|
||||
}
|
||||
|
||||
_settings.Logger.Warn("HttpStatusCode set to 404 : Request not found");
|
||||
return ResponseMessageBuilder.Create(HttpStatusCode.NotFound, "Request not found");
|
||||
return _responseMessageBuilder.Create(HttpStatusCode.NotFound, "Request not found");
|
||||
}
|
||||
#endregion Request/{guid}
|
||||
|
||||
@@ -670,7 +670,7 @@ public partial class WireMockServer
|
||||
{
|
||||
ResetLogEntries();
|
||||
|
||||
return ResponseMessageBuilder.Create(200, "Requests deleted");
|
||||
return _responseMessageBuilder.Create(200, "Requests deleted");
|
||||
}
|
||||
#endregion Requests
|
||||
|
||||
@@ -710,7 +710,7 @@ public partial class WireMockServer
|
||||
return ToJson(result);
|
||||
}
|
||||
|
||||
return ResponseMessageBuilder.Create(HttpStatusCode.BadRequest);
|
||||
return _responseMessageBuilder.Create(HttpStatusCode.BadRequest);
|
||||
}
|
||||
#endregion Requests/find
|
||||
|
||||
@@ -733,7 +733,7 @@ public partial class WireMockServer
|
||||
{
|
||||
ResetScenarios();
|
||||
|
||||
return ResponseMessageBuilder.Create(200, "Scenarios reset");
|
||||
return _responseMessageBuilder.Create(200, "Scenarios reset");
|
||||
}
|
||||
|
||||
private IResponseMessage ScenarioReset(HttpContext _, IRequestMessage requestMessage)
|
||||
@@ -743,8 +743,8 @@ public partial class WireMockServer
|
||||
Enumerable.Reverse(requestMessage.Path.Split('/')).Skip(1).First();
|
||||
|
||||
return ResetScenario(name) ?
|
||||
ResponseMessageBuilder.Create(200, "Scenario reset") :
|
||||
ResponseMessageBuilder.Create(HttpStatusCode.NotFound, $"No scenario found by name '{name}'.");
|
||||
_responseMessageBuilder.Create(200, "Scenario reset") :
|
||||
_responseMessageBuilder.Create(HttpStatusCode.NotFound, $"No scenario found by name '{name}'.");
|
||||
}
|
||||
|
||||
private IResponseMessage ScenariosSetState(HttpContext _, IRequestMessage requestMessage)
|
||||
@@ -752,14 +752,14 @@ public partial class WireMockServer
|
||||
var name = Enumerable.Reverse(requestMessage.Path.Split('/')).Skip(1).First();
|
||||
if (!_options.ScenarioStateStore.ContainsKey(name))
|
||||
{
|
||||
ResponseMessageBuilder.Create(HttpStatusCode.NotFound, $"No scenario found by name '{name}'.");
|
||||
_responseMessageBuilder.Create(HttpStatusCode.NotFound, $"No scenario found by name '{name}'.");
|
||||
}
|
||||
|
||||
var update = DeserializeObject<ScenarioStateUpdateModel>(requestMessage);
|
||||
|
||||
return SetScenarioState(name, update.State) ?
|
||||
ResponseMessageBuilder.Create(200, $"Scenario state set to '{update.State}'") :
|
||||
ResponseMessageBuilder.Create(HttpStatusCode.NotFound, $"No scenario found by name '{name}'.");
|
||||
_responseMessageBuilder.Create(200, $"Scenario state set to '{update.State}'") :
|
||||
_responseMessageBuilder.Create(HttpStatusCode.NotFound, $"No scenario found by name '{name}'.");
|
||||
}
|
||||
#endregion
|
||||
|
||||
@@ -883,6 +883,18 @@ public partial class WireMockServer
|
||||
};
|
||||
}
|
||||
|
||||
private T[] DeserializeRequestMessageToArray<T>(IRequestMessage requestMessage)
|
||||
{
|
||||
if (requestMessage.BodyData?.DetectedBodyType == BodyType.Json && requestMessage.BodyData.BodyAsJson != null)
|
||||
{
|
||||
var bodyAsJson = requestMessage.BodyData.BodyAsJson!;
|
||||
|
||||
return _mappingSerializer.DeserializeObjectToArray<T>(bodyAsJson);
|
||||
}
|
||||
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
private static Encoding? ToEncoding(EncodingModel? encodingModel)
|
||||
{
|
||||
return encodingModel != null ? Encoding.GetEncoding(encodingModel.CodePage) : null;
|
||||
@@ -902,28 +914,16 @@ public partial class WireMockServer
|
||||
};
|
||||
}
|
||||
|
||||
private static T[] DeserializeRequestMessageToArray<T>(IRequestMessage requestMessage)
|
||||
{
|
||||
if (requestMessage.BodyData?.DetectedBodyType == BodyType.Json && requestMessage.BodyData.BodyAsJson != null)
|
||||
{
|
||||
var bodyAsJson = requestMessage.BodyData.BodyAsJson!;
|
||||
|
||||
return MappingSerializer.DeserializeObjectToArray<T>(bodyAsJson);
|
||||
}
|
||||
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
private static T DeserializeObject<T>(IRequestMessage requestMessage)
|
||||
private T DeserializeObject<T>(IRequestMessage requestMessage)
|
||||
{
|
||||
switch (requestMessage.BodyData?.DetectedBodyType)
|
||||
{
|
||||
case BodyType.String:
|
||||
case BodyType.FormUrlEncoded:
|
||||
return JsonUtils.DeserializeObject<T>(requestMessage.BodyData.BodyAsString!);
|
||||
case BodyType.String when requestMessage.BodyData?.BodyAsString != null:
|
||||
case BodyType.FormUrlEncoded when requestMessage.BodyData?.BodyAsString != null:
|
||||
return _settings.DefaultJsonSerializer.Deserialize<T>(requestMessage.BodyData.BodyAsString)!;
|
||||
|
||||
case BodyType.Json when requestMessage.BodyData?.BodyAsJson != null:
|
||||
return ((JObject)requestMessage.BodyData.BodyAsJson).ToObject<T>()!;
|
||||
return _settings.DefaultJsonSerializer.ParseJsonToken<T>(requestMessage.BodyData.BodyAsJson)!;
|
||||
|
||||
default:
|
||||
throw new NotSupportedException();
|
||||
|
||||
@@ -18,14 +18,14 @@ public partial class WireMockServer
|
||||
{
|
||||
if (requestMessage.Body is null)
|
||||
{
|
||||
return ResponseMessageBuilder.Create(HttpStatusCode.BadRequest, "Body is null");
|
||||
return _responseMessageBuilder.Create(HttpStatusCode.BadRequest, "Body is null");
|
||||
}
|
||||
|
||||
var id = requestMessage.Path.Split('/').Last();
|
||||
|
||||
AddProtoDefinition(id, requestMessage.Body);
|
||||
|
||||
return ResponseMessageBuilder.Create(HttpStatusCode.OK, "ProtoDefinition added");
|
||||
return _responseMessageBuilder.Create(HttpStatusCode.OK, "ProtoDefinition added");
|
||||
}
|
||||
#endregion
|
||||
|
||||
@@ -34,7 +34,7 @@ public partial class WireMockServer
|
||||
{
|
||||
if (requestMessage.BodyAsBytes is null)
|
||||
{
|
||||
return ResponseMessageBuilder.Create(HttpStatusCode.BadRequest, "Body is null");
|
||||
return _responseMessageBuilder.Create(HttpStatusCode.BadRequest, "Body is null");
|
||||
}
|
||||
|
||||
var filename = GetFileNameFromRequestMessage(requestMessage);
|
||||
@@ -47,14 +47,14 @@ public partial class WireMockServer
|
||||
|
||||
_settings.FileSystemHandler.WriteFile(filename, requestMessage.BodyAsBytes);
|
||||
|
||||
return ResponseMessageBuilder.Create(HttpStatusCode.OK, "File created");
|
||||
return _responseMessageBuilder.Create(HttpStatusCode.OK, "File created");
|
||||
}
|
||||
|
||||
private IResponseMessage FilePut(HttpContext _, IRequestMessage requestMessage)
|
||||
{
|
||||
if (requestMessage.BodyAsBytes is null)
|
||||
{
|
||||
return ResponseMessageBuilder.Create(HttpStatusCode.BadRequest, "Body is null");
|
||||
return _responseMessageBuilder.Create(HttpStatusCode.BadRequest, "Body is null");
|
||||
}
|
||||
|
||||
var filename = GetFileNameFromRequestMessage(requestMessage);
|
||||
@@ -62,12 +62,12 @@ public partial class WireMockServer
|
||||
if (!_settings.FileSystemHandler.FileExists(filename))
|
||||
{
|
||||
_settings.Logger.Info("The file '{0}' does not exist, updating file will be skipped.", filename);
|
||||
return ResponseMessageBuilder.Create(HttpStatusCode.NotFound, "File is not found");
|
||||
return _responseMessageBuilder.Create(HttpStatusCode.NotFound, "File is not found");
|
||||
}
|
||||
|
||||
_settings.FileSystemHandler.WriteFile(filename, requestMessage.BodyAsBytes);
|
||||
|
||||
return ResponseMessageBuilder.Create(HttpStatusCode.OK, "File updated");
|
||||
return _responseMessageBuilder.Create(HttpStatusCode.OK, "File updated");
|
||||
}
|
||||
|
||||
private IResponseMessage FileGet(HttpContext _, IRequestMessage requestMessage)
|
||||
@@ -77,7 +77,7 @@ public partial class WireMockServer
|
||||
if (!_settings.FileSystemHandler.FileExists(filename))
|
||||
{
|
||||
_settings.Logger.Info("The file '{0}' does not exist.", filename);
|
||||
return ResponseMessageBuilder.Create(HttpStatusCode.NotFound, "File is not found");
|
||||
return _responseMessageBuilder.Create(HttpStatusCode.NotFound, "File is not found");
|
||||
}
|
||||
|
||||
var bytes = _settings.FileSystemHandler.ReadFile(filename);
|
||||
@@ -112,10 +112,10 @@ public partial class WireMockServer
|
||||
if (!_settings.FileSystemHandler.FileExists(filename))
|
||||
{
|
||||
_settings.Logger.Info("The file '{0}' does not exist.", filename);
|
||||
return ResponseMessageBuilder.Create(HttpStatusCode.NotFound);
|
||||
return _responseMessageBuilder.Create(HttpStatusCode.NotFound);
|
||||
}
|
||||
|
||||
return ResponseMessageBuilder.Create(HttpStatusCode.NoContent);
|
||||
return _responseMessageBuilder.Create(HttpStatusCode.NoContent);
|
||||
}
|
||||
|
||||
private IResponseMessage FileDelete(HttpContext _, IRequestMessage requestMessage)
|
||||
@@ -125,11 +125,11 @@ public partial class WireMockServer
|
||||
if (!_settings.FileSystemHandler.FileExists(filename))
|
||||
{
|
||||
_settings.Logger.Info("The file '{0}' does not exist.", filename);
|
||||
return ResponseMessageBuilder.Create(HttpStatusCode.NotFound, "File is not deleted");
|
||||
return _responseMessageBuilder.Create(HttpStatusCode.NotFound, "File is not deleted");
|
||||
}
|
||||
|
||||
_settings.FileSystemHandler.DeleteFile(filename);
|
||||
return ResponseMessageBuilder.Create(HttpStatusCode.OK, "File deleted.");
|
||||
return _responseMessageBuilder.Create(HttpStatusCode.OK, "File deleted.");
|
||||
}
|
||||
|
||||
private string GetFileNameFromRequestMessage(IRequestMessage requestMessage)
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System.Linq;
|
||||
using Stef.Validation;
|
||||
using WireMock.Admin.Mappings;
|
||||
using WireMock.Matchers;
|
||||
@@ -153,7 +152,7 @@ public partial class WireMockServer
|
||||
}
|
||||
else
|
||||
{
|
||||
var clientIPModel = JsonUtils.ParseJTokenToObject<ClientIPModel>(requestModel.ClientIP);
|
||||
var clientIPModel = _settings.DefaultJsonSerializer.ParseJsonToken<ClientIPModel>(requestModel.ClientIP);
|
||||
if (clientIPModel.Matchers != null)
|
||||
{
|
||||
requestBuilder = requestBuilder.WithPath(clientIPModel.Matchers.Select(_matcherMapper.Map).OfType<IStringMatcher>().ToArray());
|
||||
@@ -169,7 +168,7 @@ public partial class WireMockServer
|
||||
}
|
||||
else
|
||||
{
|
||||
var pathModel = JsonUtils.ParseJTokenToObject<PathModel>(requestModel.Path);
|
||||
var pathModel = _settings.DefaultJsonSerializer.ParseJsonToken<PathModel>(requestModel.Path);
|
||||
if (pathModel.Matchers != null)
|
||||
{
|
||||
var matchOperator = StringUtils.ParseMatchOperator(pathModel.MatchOperator);
|
||||
@@ -185,7 +184,7 @@ public partial class WireMockServer
|
||||
}
|
||||
else
|
||||
{
|
||||
var urlModel = JsonUtils.ParseJTokenToObject<UrlModel>(requestModel.Url);
|
||||
var urlModel = _settings.DefaultJsonSerializer.ParseJsonToken<UrlModel>(requestModel.Url);
|
||||
if (urlModel.Matchers != null)
|
||||
{
|
||||
var matchOperator = StringUtils.ParseMatchOperator(urlModel.MatchOperator);
|
||||
@@ -273,7 +272,7 @@ public partial class WireMockServer
|
||||
return requestBuilder;
|
||||
}
|
||||
|
||||
private static IResponseBuilder InitResponseBuilder(ResponseModel responseModel)
|
||||
private IResponseBuilder InitResponseBuilder(ResponseModel responseModel)
|
||||
{
|
||||
var responseBuilder = Response.Create();
|
||||
|
||||
@@ -338,7 +337,7 @@ public partial class WireMockServer
|
||||
}
|
||||
else
|
||||
{
|
||||
var headers = JsonUtils.ParseJTokenToObject<string[]>(entry.Value);
|
||||
var headers = _settings.DefaultJsonSerializer.ParseJsonToken<string[]>(entry.Value);
|
||||
responseBuilder.WithHeader(entry.Key, headers);
|
||||
}
|
||||
}
|
||||
@@ -364,7 +363,7 @@ public partial class WireMockServer
|
||||
}
|
||||
else
|
||||
{
|
||||
var headers = JsonUtils.ParseJTokenToObject<string[]>(entry.Value);
|
||||
var headers = _settings.DefaultJsonSerializer.ParseJsonToken<string[]>(entry.Value);
|
||||
responseBuilder.WithTrailingHeader(entry.Key, headers);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ public partial class WireMockServer
|
||||
if (mappingModels.Length == 1)
|
||||
{
|
||||
var guid = ConvertWireMockOrgMappingAndRegisterAsRespondProvider(mappingModels[0]);
|
||||
return ResponseMessageBuilder.Create(201, "Mapping added", guid);
|
||||
return _responseMessageBuilder.Create(201, "Mapping added", guid);
|
||||
}
|
||||
|
||||
foreach (var mappingModel in mappingModels)
|
||||
@@ -60,17 +60,17 @@ public partial class WireMockServer
|
||||
ConvertWireMockOrgMappingAndRegisterAsRespondProvider(mappingModel);
|
||||
}
|
||||
|
||||
return ResponseMessageBuilder.Create(201, "Mappings added");
|
||||
return _responseMessageBuilder.Create(201, "Mappings added");
|
||||
}
|
||||
catch (ArgumentException a)
|
||||
{
|
||||
_settings.Logger.Error("HttpStatusCode set to 400 {0}", a);
|
||||
return ResponseMessageBuilder.Create(400, a.Message);
|
||||
return _responseMessageBuilder.Create(400, a.Message);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_settings.Logger.Error("HttpStatusCode set to 500 {0}", e);
|
||||
return ResponseMessageBuilder.Create(500, e.ToString());
|
||||
return _responseMessageBuilder.Create(500, e.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ public partial class WireMockServer
|
||||
catch (Exception e)
|
||||
{
|
||||
_settings.Logger.Error("HttpStatusCode set to {0} {1}", HttpStatusCode.BadRequest, e);
|
||||
return ResponseMessageBuilder.Create(HttpStatusCode.BadRequest, e.Message);
|
||||
return _responseMessageBuilder.Create(HttpStatusCode.BadRequest, e.Message);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,12 +35,12 @@ public partial class WireMockServer
|
||||
|
||||
ConvertMappingsAndRegisterAsRespondProvider(mappingModels);
|
||||
|
||||
return ResponseMessageBuilder.Create(HttpStatusCode.Created, "OpenApi document converted to Mappings");
|
||||
return _responseMessageBuilder.Create(HttpStatusCode.Created, "OpenApi document converted to Mappings");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_settings.Logger.Error("HttpStatusCode set to {0} {1}", HttpStatusCode.BadRequest, e);
|
||||
return ResponseMessageBuilder.Create(HttpStatusCode.BadRequest, e.Message);
|
||||
return _responseMessageBuilder.Create(HttpStatusCode.BadRequest, e.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -42,6 +42,7 @@ public partial class WireMockServer : IWireMockServer
|
||||
private readonly MappingBuilder _mappingBuilder;
|
||||
private readonly IGuidUtils _guidUtils = new GuidUtils();
|
||||
private readonly IDateTimeUtils _dateTimeUtils = new DateTimeUtils();
|
||||
private readonly IResponseMessageBuilder _responseMessageBuilder;
|
||||
private readonly MappingSerializer _mappingSerializer;
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -354,6 +355,8 @@ public partial class WireMockServer : IWireMockServer
|
||||
{
|
||||
_settings = Guard.NotNull(settings);
|
||||
|
||||
_responseMessageBuilder = new ResponseMessageBuilder(_dateTimeUtils);
|
||||
|
||||
_mappingSerializer = new MappingSerializer(settings.DefaultJsonSerializer ?? new NewtonsoftJsonConverter());
|
||||
|
||||
// Set default values if not provided
|
||||
@@ -407,7 +410,8 @@ public partial class WireMockServer : IWireMockServer
|
||||
_mappingConverter,
|
||||
_mappingToFileSaver,
|
||||
_guidUtils,
|
||||
_dateTimeUtils
|
||||
_dateTimeUtils,
|
||||
_responseMessageBuilder
|
||||
);
|
||||
|
||||
_options.AdditionalServiceRegistration = _settings.AdditionalServiceRegistration;
|
||||
@@ -471,7 +475,7 @@ public partial class WireMockServer : IWireMockServer
|
||||
Given(Request.Create().WithPath("/*").UsingAnyMethod())
|
||||
.WithGuid(Guid.Parse("90008000-0000-4444-a17e-669cd84f1f05"))
|
||||
.AtPriority(1000)
|
||||
.RespondWith(new DynamicResponseProvider((_, _) => ResponseMessageBuilder.Create(HttpStatusCode.NotFound, WireMockConstants.NoMatchingFound)));
|
||||
.RespondWith(new DynamicResponseProvider((_, _) => _responseMessageBuilder.Create(HttpStatusCode.NotFound, WireMockConstants.NoMatchingFound)));
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IWireMockServer.Reset" />
|
||||
|
||||
@@ -3,12 +3,14 @@
|
||||
using System.Collections;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using JetBrains.Annotations;
|
||||
using JsonConverter.Newtonsoft.Json;
|
||||
using JsonConverter.System.Text.Json;
|
||||
using Stef.Validation;
|
||||
using WireMock.Constants;
|
||||
using WireMock.Logging;
|
||||
using WireMock.Models;
|
||||
using WireMock.Transformers;
|
||||
using WireMock.Types;
|
||||
using WireMock.Util;
|
||||
|
||||
@@ -57,11 +59,9 @@ public static class WireMockServerSettingsParser
|
||||
DisableRequestBodyDecompressing = parser.GetBoolValue(nameof(WireMockServerSettings.DisableRequestBodyDecompressing)),
|
||||
DisableDeserializeFormUrlEncoded = parser.GetBoolValue(nameof(WireMockServerSettings.DisableDeserializeFormUrlEncoded)),
|
||||
DoNotSaveDynamicResponseInLogEntry = parser.GetBoolValue(nameof(WireMockServerSettings.DoNotSaveDynamicResponseInLogEntry)),
|
||||
GraphQLSchemas = parser.GetObjectValueFromJson<Dictionary<string, GraphQLSchemaDetails>>(nameof(settings.GraphQLSchemas)),
|
||||
HandleRequestsSynchronously = parser.GetBoolValue(nameof(WireMockServerSettings.HandleRequestsSynchronously)),
|
||||
HostingScheme = parser.GetEnumValue<HostingScheme>(nameof(WireMockServerSettings.HostingScheme)),
|
||||
MaxRequestLogCount = parser.GetIntValue(nameof(WireMockServerSettings.MaxRequestLogCount)),
|
||||
ProtoDefinitions = parser.GetObjectValueFromJson<Dictionary<string, string[]>>(nameof(settings.ProtoDefinitions)),
|
||||
QueryParameterMultipleValueSupport = parser.GetEnumValue<QueryParameterMultipleValueSupport>(nameof(WireMockServerSettings.QueryParameterMultipleValueSupport)),
|
||||
ReadStaticMappings = parser.GetBoolValue(nameof(WireMockServerSettings.ReadStaticMappings)),
|
||||
RequestLogExpirationDuration = parser.GetIntValue(nameof(WireMockServerSettings.RequestLogExpirationDuration)),
|
||||
@@ -80,6 +80,7 @@ public static class WireMockServerSettingsParser
|
||||
settings.AcceptAnyClientCertificate = parser.GetBoolValue(nameof(WireMockServerSettings.AcceptAnyClientCertificate));
|
||||
#endif
|
||||
|
||||
ParseJsonSerializerSettings(settings, parser);
|
||||
ParseLoggerSettings(settings, logger, parser);
|
||||
ParsePortSettings(settings, parser);
|
||||
ParseProxyAndRecordSettings(settings, parser);
|
||||
@@ -88,6 +89,9 @@ public static class WireMockServerSettingsParser
|
||||
ParseActivityTracingSettings(settings, parser);
|
||||
ParseWebSocketSettings(settings, parser);
|
||||
|
||||
settings.GraphQLSchemas = parser.GetObjectValueFromJson<Dictionary<string, GraphQLSchemaDetails>>(nameof(settings.GraphQLSchemas), settings.DefaultJsonSerializer);
|
||||
settings.ProtoDefinitions = parser.GetObjectValueFromJson<Dictionary<string, string[]>>(nameof(settings.ProtoDefinitions), settings.DefaultJsonSerializer);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -259,4 +263,21 @@ public static class WireMockServerSettingsParser
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private static void ParseJsonSerializerSettings(WireMockServerSettings settings, SimpleSettingsParser parser)
|
||||
{
|
||||
var defaultJsonSerializer = parser.GetStringValue(nameof(WireMockServerSettings.DefaultJsonSerializer));
|
||||
settings.DefaultJsonSerializer = defaultJsonSerializer switch
|
||||
{
|
||||
nameof(SystemTextJsonConverter) => new SystemTextJsonConverter(),
|
||||
_ => new NewtonsoftJsonConverter(),
|
||||
};
|
||||
|
||||
var defaultJsonBodyTransformer = parser.GetStringValue(nameof(WireMockServerSettings.DefaultJsonBodyTransformer));
|
||||
settings.DefaultJsonBodyTransformer = defaultJsonBodyTransformer switch
|
||||
{
|
||||
nameof(SystemTextJsonBodyTransformer) => new SystemTextJsonBodyTransformer(settings),
|
||||
_ => new NewtonsoftJsonBodyTransformer(settings),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using HandlebarsDotNet;
|
||||
using WireMock.Transformers;
|
||||
|
||||
namespace WireMock.Transformers.Handlebars;
|
||||
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using WireMock.Handlers;
|
||||
|
||||
namespace WireMock.Transformers;
|
||||
|
||||
internal interface ITransformerContext
|
||||
{
|
||||
IFileSystemHandler FileSystemHandler { get; }
|
||||
|
||||
string ParseAndRender(string text, object model);
|
||||
|
||||
object? ParseAndEvaluate(string text, object model);
|
||||
}
|
||||
@@ -1,10 +1,5 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System.Collections;
|
||||
using System.Linq;
|
||||
using HandlebarsDotNet.Helpers.Models;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Stef.Validation;
|
||||
using WireMock.Settings;
|
||||
using WireMock.Types;
|
||||
@@ -14,17 +9,13 @@ namespace WireMock.Transformers;
|
||||
|
||||
internal class Transformer : ITransformer
|
||||
{
|
||||
private readonly JsonSerializer _jsonSerializer;
|
||||
private readonly IJsonBodyTransformer _jsonBodyTransformer;
|
||||
private readonly ITransformerContextFactory _factory;
|
||||
|
||||
public Transformer(WireMockServerSettings settings, ITransformerContextFactory factory)
|
||||
{
|
||||
_factory = Guard.NotNull(factory);
|
||||
|
||||
_jsonSerializer = new JsonSerializer
|
||||
{
|
||||
Culture = Guard.NotNull(settings).Culture
|
||||
};
|
||||
_jsonBodyTransformer = Guard.NotNull(settings).DefaultJsonBodyTransformer;
|
||||
}
|
||||
|
||||
public IBodyData? TransformBody(
|
||||
@@ -121,13 +112,17 @@ internal class Transformer : ITransformer
|
||||
});
|
||||
}
|
||||
|
||||
private IBodyData? TransformBodyData(ITransformerContext transformerContext, ReplaceNodeOptions options, TransformModel model, IBodyData original, bool useTransformerForBodyAsFile)
|
||||
private BodyData? TransformBodyData(ITransformerContext transformerContext, ReplaceNodeOptions options, TransformModel model, IBodyData original, bool useTransformerForBodyAsFile)
|
||||
{
|
||||
switch (original.DetectedBodyType)
|
||||
{
|
||||
case BodyType.Json:
|
||||
case BodyType.ProtoBuf:
|
||||
return TransformBodyAsJson(transformerContext, options, model, original);
|
||||
return _jsonBodyTransformer.TransformBodyAsJson(
|
||||
transformerContext,
|
||||
options,
|
||||
model,
|
||||
original);
|
||||
|
||||
case BodyType.File:
|
||||
return TransformBodyAsFile(transformerContext, model, original, useTransformerForBodyAsFile);
|
||||
@@ -159,185 +154,7 @@ internal class Transformer : ITransformer
|
||||
return newHeaders;
|
||||
}
|
||||
|
||||
private IBodyData TransformBodyAsJson(ITransformerContext transformerContext, ReplaceNodeOptions options, object model, IBodyData original)
|
||||
{
|
||||
JToken? jToken = null;
|
||||
switch (original.BodyAsJson)
|
||||
{
|
||||
case JObject bodyAsJObject:
|
||||
jToken = bodyAsJObject.DeepClone();
|
||||
WalkNode(transformerContext, options, jToken, model);
|
||||
break;
|
||||
|
||||
case JArray bodyAsJArray:
|
||||
jToken = bodyAsJArray.DeepClone();
|
||||
WalkNode(transformerContext, options, jToken, model);
|
||||
break;
|
||||
|
||||
case var bodyAsEnumerable when bodyAsEnumerable is IEnumerable and not string:
|
||||
jToken = JArray.FromObject(bodyAsEnumerable, _jsonSerializer);
|
||||
WalkNode(transformerContext, options, jToken, model);
|
||||
break;
|
||||
|
||||
case string bodyAsString:
|
||||
jToken = ReplaceSingleNode(transformerContext, options, bodyAsString, model);
|
||||
break;
|
||||
|
||||
case not null:
|
||||
jToken = JObject.FromObject(original.BodyAsJson, _jsonSerializer);
|
||||
WalkNode(transformerContext, options, jToken, model);
|
||||
break;
|
||||
}
|
||||
|
||||
return new BodyData
|
||||
{
|
||||
Encoding = original.Encoding,
|
||||
DetectedBodyType = original.DetectedBodyType,
|
||||
DetectedBodyTypeFromContentType = original.DetectedBodyTypeFromContentType,
|
||||
ProtoDefinition = original.ProtoDefinition,
|
||||
ProtoBufMessageType = original.ProtoBufMessageType,
|
||||
BodyAsJson = jToken
|
||||
};
|
||||
}
|
||||
|
||||
private JToken ReplaceSingleNode(ITransformerContext transformerContext, ReplaceNodeOptions options, string stringValue, object model)
|
||||
{
|
||||
var transformedString = transformerContext.ParseAndRender(stringValue, model);
|
||||
|
||||
if (!string.Equals(stringValue, transformedString))
|
||||
{
|
||||
const string property = "_";
|
||||
JObject dummy = JObject.Parse($"{{ \"{property}\": null }}");
|
||||
if (dummy[property] == null)
|
||||
{
|
||||
// TODO: check if just returning null is fine
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
JToken node = dummy[property]!;
|
||||
|
||||
ReplaceNodeValue(options, node, transformedString);
|
||||
|
||||
return dummy[property]!;
|
||||
}
|
||||
|
||||
return stringValue;
|
||||
}
|
||||
|
||||
private void WalkNode(ITransformerContext transformerContext, ReplaceNodeOptions options, JToken node, object model)
|
||||
{
|
||||
switch (node.Type)
|
||||
{
|
||||
case JTokenType.Object:
|
||||
// In case of Object, loop all children. Do a ToArray() to avoid `Collection was modified` exceptions.
|
||||
foreach (var child in node.Children<JProperty>().ToArray())
|
||||
{
|
||||
WalkNode(transformerContext, options, child.Value, model);
|
||||
}
|
||||
break;
|
||||
|
||||
case JTokenType.Array:
|
||||
// In case of Array, loop all items. Do a ToArray() to avoid `Collection was modified` exceptions.
|
||||
foreach (var child in node.Children().ToArray())
|
||||
{
|
||||
WalkNode(transformerContext, options, child, model);
|
||||
}
|
||||
break;
|
||||
|
||||
case JTokenType.String:
|
||||
// In case of string, try to transform the value.
|
||||
var stringValue = node.Value<string>();
|
||||
if (string.IsNullOrEmpty(stringValue))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var transformed = transformerContext.ParseAndEvaluate(stringValue!, model);
|
||||
if (!Equals(stringValue, transformed))
|
||||
{
|
||||
ReplaceNodeValue(options, node, transformed);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// ReSharper disable once UnusedParameter.Local
|
||||
private void ReplaceNodeValue(ReplaceNodeOptions options, JToken node, object? transformedValue)
|
||||
{
|
||||
switch (transformedValue)
|
||||
{
|
||||
case JValue jValue:
|
||||
node.Replace(jValue);
|
||||
return;
|
||||
|
||||
case string transformedString:
|
||||
var (isConvertedFromString, convertedValueFromString) = TryConvert(options, transformedString);
|
||||
if (isConvertedFromString)
|
||||
{
|
||||
node.Replace(JToken.FromObject(convertedValueFromString, _jsonSerializer));
|
||||
}
|
||||
else
|
||||
{
|
||||
node.Replace(ParseAsJObject(transformedString));
|
||||
}
|
||||
break;
|
||||
|
||||
case WireMockList<string> strings:
|
||||
switch (strings.Count)
|
||||
{
|
||||
case 1:
|
||||
node.Replace(ParseAsJObject(strings[0]));
|
||||
return;
|
||||
|
||||
case > 1:
|
||||
node.Replace(JToken.FromObject(strings.ToArray(), _jsonSerializer));
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
case { }:
|
||||
var (isConverted, convertedValue) = TryConvert(options, transformedValue);
|
||||
if (isConverted)
|
||||
{
|
||||
node.Replace(JToken.FromObject(convertedValue, _jsonSerializer));
|
||||
}
|
||||
return;
|
||||
|
||||
default: // It's null, skip it. Maybe remove it ?
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private static JToken ParseAsJObject(string stringValue)
|
||||
{
|
||||
return JsonUtils.TryParseAsJObject(stringValue, out var parsedAsjObject) ? parsedAsjObject : stringValue;
|
||||
}
|
||||
|
||||
private static (bool IsConverted, object ConvertedValue) TryConvert(ReplaceNodeOptions options, object value)
|
||||
{
|
||||
var valueAsString = value as string;
|
||||
|
||||
if (options == ReplaceNodeOptions.Evaluate)
|
||||
{
|
||||
if (valueAsString != null && WrappedString.TryDecode(valueAsString, out var decoded))
|
||||
{
|
||||
return (true, decoded);
|
||||
}
|
||||
|
||||
return (false, value);
|
||||
}
|
||||
|
||||
if (valueAsString != null)
|
||||
{
|
||||
return WrappedString.TryDecode(valueAsString, out var decoded) ?
|
||||
(true, decoded) :
|
||||
StringUtils.TryConvertToKnownType(valueAsString);
|
||||
}
|
||||
|
||||
return (false, value);
|
||||
}
|
||||
|
||||
private static IBodyData TransformBodyAsString(ITransformerContext transformerContext, object model, IBodyData original)
|
||||
private static BodyData TransformBodyAsString(ITransformerContext transformerContext, object model, IBodyData original)
|
||||
{
|
||||
return new BodyData
|
||||
{
|
||||
@@ -348,7 +165,7 @@ internal class Transformer : ITransformer
|
||||
};
|
||||
}
|
||||
|
||||
private static IBodyData TransformBodyAsFile(ITransformerContext transformerContext, object model, IBodyData original, bool useTransformerForBodyAsFile)
|
||||
private static BodyData TransformBodyAsFile(ITransformerContext transformerContext, object model, IBodyData original, bool useTransformerForBodyAsFile)
|
||||
{
|
||||
var transformedBodyAsFilename = transformerContext.ParseAndRender(original.BodyAsFile!, model);
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<Description>Minimal version from the lightweight Http Mocking Server for .NET</Description>
|
||||
<AssemblyTitle>WireMock.Net.Minimal</AssemblyTitle>
|
||||
@@ -43,7 +43,7 @@
|
||||
<PackageReference Include="SimMetrics.Net" Version="1.0.5" />
|
||||
<PackageReference Include="TinyMapper.Signed" Version="4.0.0" />
|
||||
<PackageReference Include="Microsoft.IdentityModel.Protocols.OpenIdConnect" Version="6.34.0" />
|
||||
<PackageReference Include="Scriban.Signed" Version="7.0.6" />
|
||||
<PackageReference Include="Scriban.Signed" Version="7.2.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<Description>Some extensions for NUnit</Description>
|
||||
<AssemblyTitle>WireMock.Net.NUnit</AssemblyTitle>
|
||||
@@ -25,7 +25,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="JsonConverter.Newtonsoft.Json" Version="0.9.0" />
|
||||
<PackageReference Include="JsonConverter.Newtonsoft.Json" Version="0.12.0" />
|
||||
<PackageReference Include="NUnit" Version="4.4.0" />
|
||||
<PackageReference Include="Stef.Validation" Version="0.2.0" />
|
||||
</ItemGroup>
|
||||
|
||||
+41
-12
@@ -32,15 +32,15 @@ public partial class WireMockAdminApiAssertions
|
||||
}
|
||||
|
||||
[CustomAssertion]
|
||||
public AndConstraint<WireMockAdminApiAssertions> WithBodyAsJson(object body, string because = "", params object[] becauseArgs)
|
||||
public AndConstraint<WireMockAdminApiAssertions> WithBodyAsJson(object body, IJsonMatcher? jsonMatcher = null, string because = "", params object[] becauseArgs)
|
||||
{
|
||||
return WithBodyAsJson(new JsonMatcher(body), because, becauseArgs);
|
||||
return WithBodyAsJson(jsonMatcher ?? new JsonMatcher(body), because, becauseArgs);
|
||||
}
|
||||
|
||||
[CustomAssertion]
|
||||
public AndConstraint<WireMockAdminApiAssertions> WithBodyAsJson(string body, string because = "", params object[] becauseArgs)
|
||||
public AndConstraint<WireMockAdminApiAssertions> WithBodyAsJson(string body, IJsonMatcher? jsonMatcher = null, string because = "", params object[] becauseArgs)
|
||||
{
|
||||
return WithBodyAsJson(new JsonMatcher(body), because, becauseArgs);
|
||||
return WithBodyAsJson(jsonMatcher ?? new JsonMatcher(body), because, becauseArgs);
|
||||
}
|
||||
|
||||
[CustomAssertion]
|
||||
@@ -127,15 +127,44 @@ public partial class WireMockAdminApiAssertions
|
||||
|
||||
private static string? FormatBody(object? body)
|
||||
{
|
||||
return body switch
|
||||
if (body == null)
|
||||
{
|
||||
null => null,
|
||||
string str => str,
|
||||
AnyOf<string, StringPattern>[] stringPatterns => FormatBodies(stringPatterns.Select(p => p.GetPattern())),
|
||||
byte[] bytes => $"byte[{bytes.Length}] {{...}}",
|
||||
JToken jToken => jToken.ToString(Formatting.None),
|
||||
_ => JToken.FromObject(body).ToString(Formatting.None)
|
||||
};
|
||||
return null;
|
||||
}
|
||||
|
||||
if (body is string str)
|
||||
{
|
||||
return str;
|
||||
}
|
||||
|
||||
if (body is AnyOf<string, StringPattern>[] stringPatterns)
|
||||
{
|
||||
return FormatBodies(stringPatterns.Select(p => p.GetPattern()));
|
||||
}
|
||||
|
||||
if (body is byte[] bytes)
|
||||
{
|
||||
return $"byte[{bytes.Length}] {{...}}";
|
||||
}
|
||||
|
||||
if (body is JToken jToken)
|
||||
{
|
||||
return jToken.ToString(Formatting.None);
|
||||
}
|
||||
|
||||
// System.IO.FileNotFoundException : Could not load file or assembly 'System.Text.Json, Version=10.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51'. The system cannot find the file specified.
|
||||
var typeName = body.GetType().FullName;
|
||||
if (typeName == "System.Text.Json.JsonElement")
|
||||
{
|
||||
return ((dynamic)body).GetRawText();
|
||||
}
|
||||
|
||||
if (typeName == "System.Text.Json.JsonDocument")
|
||||
{
|
||||
return ((dynamic)body).RootElement.GetRawText();
|
||||
}
|
||||
|
||||
return JToken.FromObject(body).ToString(Formatting.None);
|
||||
}
|
||||
|
||||
private static string? FormatBodies(IEnumerable<object?> bodies)
|
||||
|
||||
@@ -65,6 +65,7 @@ public static class WireMockAdminApiExtensions
|
||||
waitTime = (int)(InitialWaitingTimeInMilliSeconds * Math.Pow(2, retries));
|
||||
await Task.Delay(waitTime, cancellationToken);
|
||||
isHealthy = await IsHealthyAsync(adminApi, cancellationToken);
|
||||
|
||||
retries++;
|
||||
totalWaitTime += waitTime;
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="JsonConverter.Newtonsoft.Json" Version="0.9.0" />
|
||||
<PackageReference Include="JsonConverter.Newtonsoft.Json" Version="0.12.0" />
|
||||
<PackageReference Include="RestEase" Version="1.6.4" />
|
||||
<PackageReference Include="Stef.Validation" Version="0.2.0" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
namespace WireMock.Matchers;
|
||||
|
||||
/// <summary>
|
||||
/// IJsonPathMatcher
|
||||
/// <seealso cref="IStringMatcher"/> and <seealso cref="IObjectMatcher"/>.
|
||||
/// </summary>
|
||||
public interface IJsonPathMatcher : IStringMatcher, IObjectMatcher
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
namespace WireMock.Matchers;
|
||||
|
||||
/// <summary>
|
||||
/// ISystemTextJsonPathMatcher
|
||||
/// <seealso cref="IJsonPathMatcher"/>.
|
||||
/// </summary>
|
||||
public interface ISystemTextJsonPathMatcher : IJsonPathMatcher
|
||||
{
|
||||
}
|
||||
@@ -7,9 +7,10 @@ using System.Runtime.CompilerServices;
|
||||
[assembly: InternalsVisibleTo("WireMock.Net.GraphQL, PublicKey=0024000004800000940000000602000000240000525341310004000001000100e138ec44d93acac565953052636eb8d5e7e9f27ddb030590055cd1a0ab2069a5623f1f77ca907d78e0b37066ca0f6d63da7eecc3fcb65b76aa8ebeccf7ebe1d11264b8404cd9b1cbbf2c83f566e033b3e54129f6ef28daffff776ba7aebbc53c0d635ebad8f45f78eb3f7e0459023c218f003416e080f96a1a3c5ffeb56bee9e")]
|
||||
[assembly: InternalsVisibleTo("WireMock.Net.ProtoBuf, PublicKey=0024000004800000940000000602000000240000525341310004000001000100e138ec44d93acac565953052636eb8d5e7e9f27ddb030590055cd1a0ab2069a5623f1f77ca907d78e0b37066ca0f6d63da7eecc3fcb65b76aa8ebeccf7ebe1d11264b8404cd9b1cbbf2c83f566e033b3e54129f6ef28daffff776ba7aebbc53c0d635ebad8f45f78eb3f7e0459023c218f003416e080f96a1a3c5ffeb56bee9e")]
|
||||
[assembly: InternalsVisibleTo("WireMock.Net.Matchers.CSharpCode, PublicKey=0024000004800000940000000602000000240000525341310004000001000100e138ec44d93acac565953052636eb8d5e7e9f27ddb030590055cd1a0ab2069a5623f1f77ca907d78e0b37066ca0f6d63da7eecc3fcb65b76aa8ebeccf7ebe1d11264b8404cd9b1cbbf2c83f566e033b3e54129f6ef28daffff776ba7aebbc53c0d635ebad8f45f78eb3f7e0459023c218f003416e080f96a1a3c5ffeb56bee9e")]
|
||||
[assembly: InternalsVisibleTo("WireMock.Net.Matchers.SystemTextJsonPath, PublicKey=0024000004800000940000000602000000240000525341310004000001000100e138ec44d93acac565953052636eb8d5e7e9f27ddb030590055cd1a0ab2069a5623f1f77ca907d78e0b37066ca0f6d63da7eecc3fcb65b76aa8ebeccf7ebe1d11264b8404cd9b1cbbf2c83f566e033b3e54129f6ef28daffff776ba7aebbc53c0d635ebad8f45f78eb3f7e0459023c218f003416e080f96a1a3c5ffeb56bee9e")]
|
||||
[assembly: InternalsVisibleTo("WireMock.Net.OpenTelemetry, PublicKey=0024000004800000940000000602000000240000525341310004000001000100e138ec44d93acac565953052636eb8d5e7e9f27ddb030590055cd1a0ab2069a5623f1f77ca907d78e0b37066ca0f6d63da7eecc3fcb65b76aa8ebeccf7ebe1d11264b8404cd9b1cbbf2c83f566e033b3e54129f6ef28daffff776ba7aebbc53c0d635ebad8f45f78eb3f7e0459023c218f003416e080f96a1a3c5ffeb56bee9e")]
|
||||
// [assembly: InternalsVisibleTo("WireMock.Net.StandAlone, PublicKey=0024000004800000940000000602000000240000525341310004000001000100e138ec44d93acac565953052636eb8d5e7e9f27ddb030590055cd1a0ab2069a5623f1f77ca907d78e0b37066ca0f6d63da7eecc3fcb65b76aa8ebeccf7ebe1d11264b8404cd9b1cbbf2c83f566e033b3e54129f6ef28daffff776ba7aebbc53c0d635ebad8f45f78eb3f7e0459023c218f003416e080f96a1a3c5ffeb56bee9e")]
|
||||
[assembly: InternalsVisibleTo("WireMock.Net.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100e138ec44d93acac565953052636eb8d5e7e9f27ddb030590055cd1a0ab2069a5623f1f77ca907d78e0b37066ca0f6d63da7eecc3fcb65b76aa8ebeccf7ebe1d11264b8404cd9b1cbbf2c83f566e033b3e54129f6ef28daffff776ba7aebbc53c0d635ebad8f45f78eb3f7e0459023c218f003416e080f96a1a3c5ffeb56bee9e")]
|
||||
|
||||
// Needed for Moq in the UnitTest project
|
||||
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")]
|
||||
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")]
|
||||
@@ -1,8 +1,6 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using JsonConverter.Abstractions;
|
||||
using WireMock.Models;
|
||||
|
||||
@@ -19,8 +17,10 @@ public interface IBodyResponseBuilder : IFaultResponseBuilder
|
||||
/// <param name="body">The body.</param>
|
||||
/// <param name="destination">The Body Destination format (SameAsSource, String or Bytes).</param>
|
||||
/// <param name="encoding">The body encoding.</param>
|
||||
/// <param name="jsonConverter">The JSON converter.</param>
|
||||
/// <param name="options">The JSON converter options.</param>
|
||||
/// <returns>A <see cref="IResponseBuilder"/>.</returns>
|
||||
IResponseBuilder WithBody(string body, string? destination = BodyDestinationFormat.SameAsSource, Encoding? encoding = null);
|
||||
IResponseBuilder WithBody(string body, string? destination = BodyDestinationFormat.SameAsSource, Encoding? encoding = null, IJsonConverter? jsonConverter = null, JsonConverterOptions? options = null);
|
||||
|
||||
/// <summary>
|
||||
/// WithBody : Create a ... response based on a callback function.
|
||||
|
||||
@@ -14,16 +14,16 @@ internal static class JsonSerializationConstants
|
||||
IgnoreNullValues = true
|
||||
};
|
||||
|
||||
//internal static readonly JsonSerializerSettings JsonSerializerSettingsDefault = new()
|
||||
//{
|
||||
// Formatting = Formatting.Indented,
|
||||
// NullValueHandling = NullValueHandling.Ignore
|
||||
//};
|
||||
|
||||
internal static readonly JsonSerializerSettings JsonSerializerSettingsIncludeNullValues = new()
|
||||
internal static readonly JsonConverterOptions JsonConverterOptionsIncludeNullValues = new()
|
||||
{
|
||||
Formatting = Formatting.Indented,
|
||||
NullValueHandling = NullValueHandling.Include
|
||||
WriteIndented = true,
|
||||
IgnoreNullValues = false
|
||||
};
|
||||
|
||||
internal static readonly JsonConverterOptions JsonConverterOptionsWithDateParsingNone = new()
|
||||
{
|
||||
WriteIndented = true,
|
||||
DateParseHandling = 0
|
||||
};
|
||||
|
||||
internal static readonly JsonSerializerSettings JsonDeserializerSettingsWithDateParsingNone = new()
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System.Collections;
|
||||
using JsonConverter.Abstractions;
|
||||
using WireMock.Extensions;
|
||||
using WireMock.Util;
|
||||
|
||||
@@ -191,9 +192,9 @@ internal class SimpleSettingsParser
|
||||
return GetValue(name, values => values.FirstOrDefault());
|
||||
}
|
||||
|
||||
public T? GetObjectValueFromJson<T>(string name)
|
||||
public T? GetObjectValueFromJson<T>(string name, IJsonConverter jsonConverter)
|
||||
{
|
||||
var value = GetValue(name, values => values.FirstOrDefault());
|
||||
return string.IsNullOrWhiteSpace(value) ? default : JsonUtils.DeserializeObject<T>(value!);
|
||||
return string.IsNullOrWhiteSpace(value) ? default : jsonConverter.Deserialize<T>(value!);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ using WireMock.Logging;
|
||||
using WireMock.Matchers;
|
||||
using WireMock.Models;
|
||||
using WireMock.RegularExpressions;
|
||||
using WireMock.Transformers;
|
||||
using WireMock.Types;
|
||||
|
||||
namespace WireMock.Settings;
|
||||
@@ -362,11 +363,31 @@ public class WireMockServerSettings
|
||||
/// Default is <see cref="NewtonsoftJsonConverter"/>.
|
||||
/// </remarks>
|
||||
[PublicAPI]
|
||||
public IJsonConverter DefaultJsonSerializer { get; set; } = new NewtonsoftJsonConverter();
|
||||
public IJsonConverter DefaultJsonSerializer { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the default JSON body transformer used for template-based JSON body transformations.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Set this property to provide a custom implementation for transforming JSON and ProtoBuf body content.
|
||||
/// Default is <see cref="NewtonsoftJsonBodyTransformer"/>.
|
||||
/// </remarks>
|
||||
[PublicAPI]
|
||||
[JsonIgnore]
|
||||
public IJsonBodyTransformer DefaultJsonBodyTransformer { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// WebSocket settings.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public WebSocketSettings? WebSocketSettings { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="WireMockServerSettings"/> class.
|
||||
/// </summary>
|
||||
public WireMockServerSettings()
|
||||
{
|
||||
DefaultJsonSerializer = new NewtonsoftJsonConverter();
|
||||
DefaultJsonBodyTransformer = new NewtonsoftJsonBodyTransformer(this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using JetBrains.Annotations;
|
||||
using WireMock.Types;
|
||||
using WireMock.Util;
|
||||
|
||||
namespace WireMock.Transformers;
|
||||
|
||||
/// <summary>
|
||||
/// Defines the contract for transforming JSON-like body data using a transformer context.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public interface IJsonBodyTransformer
|
||||
{
|
||||
/// <summary>
|
||||
/// Transforms the JSON body using the provided transformer context and model.
|
||||
/// </summary>
|
||||
/// <param name="transformerContext">The transformer context used to render and evaluate template values.</param>
|
||||
/// <param name="options">The JSON node replacement behavior to apply during transformation.</param>
|
||||
/// <param name="model">The model used when rendering or evaluating template values.</param>
|
||||
/// <param name="original">The original body data to transform.</param>
|
||||
/// <returns>The transformed JSON body data.</returns>
|
||||
BodyData TransformBodyAsJson(
|
||||
ITransformerContext transformerContext,
|
||||
ReplaceNodeOptions options,
|
||||
object model,
|
||||
IBodyData original);
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using JetBrains.Annotations;
|
||||
using WireMock.Handlers;
|
||||
|
||||
namespace WireMock.Transformers;
|
||||
|
||||
/// <summary>
|
||||
/// Defines the transformer context used to render and evaluate templates during response transformation.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public interface ITransformerContext
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the file system handler used by the current transformer context.
|
||||
/// </summary>
|
||||
IFileSystemHandler FileSystemHandler { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Renders the specified template text using the supplied model.
|
||||
/// </summary>
|
||||
/// <param name="text">The template text to render.</param>
|
||||
/// <param name="model">The model used during rendering.</param>
|
||||
/// <returns>The rendered text.</returns>
|
||||
string ParseAndRender(string text, object model);
|
||||
|
||||
/// <summary>
|
||||
/// Evaluates the specified template text using the supplied model.
|
||||
/// </summary>
|
||||
/// <param name="text">The template text to evaluate.</param>
|
||||
/// <param name="model">The model used during evaluation.</param>
|
||||
/// <returns>The evaluated value.</returns>
|
||||
object? ParseAndEvaluate(string text, object model);
|
||||
}
|
||||
@@ -0,0 +1,271 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System.Collections;
|
||||
using HandlebarsDotNet.Helpers.Models;
|
||||
using JetBrains.Annotations;
|
||||
using JsonConverter.Newtonsoft.Json;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using WireMock.Settings;
|
||||
using WireMock.Types;
|
||||
using WireMock.Util;
|
||||
|
||||
namespace WireMock.Transformers;
|
||||
|
||||
/// <summary>
|
||||
/// Default JSON body transformer implementation based on Newtonsoft.Json.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Initializes a new instance of the <see cref="NewtonsoftJsonBodyTransformer"/> class.
|
||||
/// </remarks>
|
||||
/// <param name="settings">The server settings used to configure JSON transformation behavior.</param>
|
||||
[PublicAPI]
|
||||
public class NewtonsoftJsonBodyTransformer(WireMockServerSettings settings) : IJsonBodyTransformer
|
||||
{
|
||||
private static readonly NewtonsoftJsonConverter _jsonConverter = new();
|
||||
|
||||
/// <inheritdoc />
|
||||
public BodyData TransformBodyAsJson(
|
||||
ITransformerContext transformerContext,
|
||||
ReplaceNodeOptions options,
|
||||
object model,
|
||||
IBodyData original)
|
||||
{
|
||||
var jsonSerializer = new JsonSerializer
|
||||
{
|
||||
Culture = settings.Culture
|
||||
};
|
||||
|
||||
JToken? jToken = null;
|
||||
switch (original.BodyAsJson)
|
||||
{
|
||||
case JObject bodyAsJObject:
|
||||
jToken = bodyAsJObject.DeepClone();
|
||||
WalkNode(transformerContext, jsonSerializer, options, jToken, model);
|
||||
break;
|
||||
|
||||
case JArray bodyAsJArray:
|
||||
jToken = bodyAsJArray.DeepClone();
|
||||
WalkNode(transformerContext, jsonSerializer, options, jToken, model);
|
||||
break;
|
||||
|
||||
case var bodyAsEnumerable when bodyAsEnumerable is IEnumerable and not string:
|
||||
jToken = JArray.FromObject(bodyAsEnumerable, jsonSerializer);
|
||||
WalkNode(transformerContext, jsonSerializer, options, jToken, model);
|
||||
break;
|
||||
|
||||
case string bodyAsString:
|
||||
jToken = ReplaceSingleNode(transformerContext, jsonSerializer, options, bodyAsString, model);
|
||||
break;
|
||||
|
||||
case not null:
|
||||
jToken = JObject.FromObject(original.BodyAsJson, jsonSerializer);
|
||||
WalkNode(transformerContext, jsonSerializer, options, jToken, model);
|
||||
break;
|
||||
}
|
||||
|
||||
return new BodyData
|
||||
{
|
||||
Encoding = original.Encoding,
|
||||
DetectedBodyType = original.DetectedBodyType,
|
||||
DetectedBodyTypeFromContentType = original.DetectedBodyTypeFromContentType,
|
||||
ProtoDefinition = original.ProtoDefinition,
|
||||
ProtoBufMessageType = original.ProtoBufMessageType,
|
||||
BodyAsJson = jToken
|
||||
};
|
||||
}
|
||||
|
||||
private JToken ParseAsJObject(string stringValue)
|
||||
{
|
||||
if (_jsonConverter.IsValidJson(stringValue))
|
||||
{
|
||||
try
|
||||
{
|
||||
// Try to convert this string into a JObject
|
||||
return JObject.Parse(stringValue!);
|
||||
}
|
||||
catch
|
||||
{
|
||||
settings.Logger.Warn("Failed to parse string ''{0}'' as JSON. Returning the original string value.", stringValue);
|
||||
}
|
||||
}
|
||||
|
||||
return stringValue;
|
||||
}
|
||||
|
||||
private JToken ReplaceSingleNode(ITransformerContext transformerContext, JsonSerializer jsonSerializer, ReplaceNodeOptions options, string stringValue, object model)
|
||||
{
|
||||
var transformedString = transformerContext.ParseAndRender(stringValue, model);
|
||||
|
||||
if (!string.Equals(stringValue, transformedString))
|
||||
{
|
||||
const string property = "_";
|
||||
JObject dummy = JObject.Parse($"{{ \"{property}\": null }}");
|
||||
if (dummy[property] == null)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
JToken node = dummy[property]!;
|
||||
|
||||
ReplaceNodeValue(jsonSerializer, options, node, transformedString);
|
||||
|
||||
return dummy[property]!;
|
||||
}
|
||||
|
||||
return stringValue;
|
||||
}
|
||||
|
||||
private void WalkNode(ITransformerContext transformerContext, JsonSerializer jsonSerializer, ReplaceNodeOptions options, JToken node, object model)
|
||||
{
|
||||
switch (node.Type)
|
||||
{
|
||||
case JTokenType.Object:
|
||||
foreach (var child in node.Children<JProperty>().ToArray())
|
||||
{
|
||||
WalkNode(transformerContext, jsonSerializer, options, child.Value, model);
|
||||
}
|
||||
break;
|
||||
|
||||
case JTokenType.Array:
|
||||
foreach (var child in node.Children().ToArray())
|
||||
{
|
||||
WalkNode(transformerContext, jsonSerializer, options, child, model);
|
||||
}
|
||||
break;
|
||||
|
||||
case JTokenType.String:
|
||||
var stringValue = node.Value<string>();
|
||||
if (string.IsNullOrEmpty(stringValue))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var transformed = transformerContext.ParseAndEvaluate(stringValue!, model);
|
||||
if (!Equals(stringValue, transformed))
|
||||
{
|
||||
ReplaceNodeValue(jsonSerializer, options, node, transformed);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void ReplaceNodeValue(JsonSerializer jsonSerializer, ReplaceNodeOptions options, JToken node, object? transformedValue)
|
||||
{
|
||||
switch (transformedValue)
|
||||
{
|
||||
case JValue jValue:
|
||||
node.Replace(jValue);
|
||||
return;
|
||||
|
||||
case string transformedString:
|
||||
var (isConvertedFromString, convertedValueFromString) = TryConvert(options, transformedString);
|
||||
if (isConvertedFromString)
|
||||
{
|
||||
node.Replace(JToken.FromObject(convertedValueFromString, jsonSerializer));
|
||||
}
|
||||
else
|
||||
{
|
||||
node.Replace(ParseAsJObject(transformedString));
|
||||
}
|
||||
break;
|
||||
|
||||
case WireMockList<string> strings:
|
||||
switch (strings.Count)
|
||||
{
|
||||
case 1:
|
||||
node.Replace(ParseAsJObject(strings[0]));
|
||||
return;
|
||||
|
||||
case > 1:
|
||||
node.Replace(JToken.FromObject(strings.ToArray(), jsonSerializer));
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
case { }:
|
||||
var (isConverted, convertedValue) = TryConvert(options, transformedValue);
|
||||
if (isConverted)
|
||||
{
|
||||
node.Replace(JToken.FromObject(convertedValue, jsonSerializer));
|
||||
}
|
||||
return;
|
||||
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private static (bool IsConverted, object ConvertedValue) TryConvert(ReplaceNodeOptions options, object value)
|
||||
{
|
||||
var valueAsString = value as string;
|
||||
|
||||
if (options == ReplaceNodeOptions.Evaluate)
|
||||
{
|
||||
if (valueAsString != null && WrappedString.TryDecode(valueAsString, out var decoded))
|
||||
{
|
||||
return (true, decoded);
|
||||
}
|
||||
|
||||
return (false, value);
|
||||
}
|
||||
|
||||
if (valueAsString != null)
|
||||
{
|
||||
return WrappedString.TryDecode(valueAsString, out var decoded)
|
||||
? (true, decoded)
|
||||
: TryConvertToKnownType(valueAsString);
|
||||
}
|
||||
|
||||
return (false, value);
|
||||
}
|
||||
|
||||
internal static (bool IsConverted, object ConvertedValue) TryConvertToKnownType(string value)
|
||||
{
|
||||
if (bool.TryParse(value, out var boolResult))
|
||||
{
|
||||
return (true, boolResult);
|
||||
}
|
||||
|
||||
if (int.TryParse(value, out var intResult))
|
||||
{
|
||||
return (true, intResult);
|
||||
}
|
||||
|
||||
if (long.TryParse(value, out var longResult))
|
||||
{
|
||||
return (true, longResult);
|
||||
}
|
||||
|
||||
if (double.TryParse(value, out var doubleResult))
|
||||
{
|
||||
return (true, doubleResult);
|
||||
}
|
||||
|
||||
if (Guid.TryParseExact(value, "D", out var guidResult))
|
||||
{
|
||||
return (true, guidResult);
|
||||
}
|
||||
|
||||
if (TimeSpan.TryParse(value, out var timeSpanResult))
|
||||
{
|
||||
return (true, timeSpanResult);
|
||||
}
|
||||
|
||||
if (DateTime.TryParse(value, out var dateTimeResult))
|
||||
{
|
||||
return (true, dateTimeResult);
|
||||
}
|
||||
|
||||
if ((value.StartsWith("ftp://", StringComparison.OrdinalIgnoreCase) ||
|
||||
value.StartsWith("ftps://", StringComparison.OrdinalIgnoreCase) ||
|
||||
value.StartsWith("http://", StringComparison.OrdinalIgnoreCase) ||
|
||||
value.StartsWith("https://", StringComparison.OrdinalIgnoreCase)) &&
|
||||
Uri.TryCreate(value, UriKind.RelativeOrAbsolute, out var uriResult))
|
||||
{
|
||||
return (true, uriResult);
|
||||
}
|
||||
|
||||
return (false, value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,208 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System.Collections;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Nodes;
|
||||
using HandlebarsDotNet.Helpers.Models;
|
||||
using JetBrains.Annotations;
|
||||
using JsonConverter.System.Text.Json;
|
||||
using WireMock.Settings;
|
||||
using WireMock.Types;
|
||||
using WireMock.Util;
|
||||
|
||||
namespace WireMock.Transformers;
|
||||
|
||||
/// <summary>
|
||||
/// JSON body transformer implementation based on System.Text.Json.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public class SystemTextJsonBodyTransformer(WireMockServerSettings settings) : IJsonBodyTransformer
|
||||
{
|
||||
private static readonly SystemTextJsonConverter _jsonConverter = new();
|
||||
|
||||
/// <inheritdoc />
|
||||
public BodyData TransformBodyAsJson(
|
||||
ITransformerContext transformerContext,
|
||||
ReplaceNodeOptions options,
|
||||
object model,
|
||||
IBodyData original)
|
||||
{
|
||||
JsonNode? jsonNode = null;
|
||||
switch (original.BodyAsJson)
|
||||
{
|
||||
case JsonObject bodyAsJsonObject:
|
||||
jsonNode = CloneNode(bodyAsJsonObject);
|
||||
jsonNode = WalkNode(transformerContext, options, jsonNode, model);
|
||||
break;
|
||||
|
||||
case JsonArray bodyAsJsonArray:
|
||||
jsonNode = CloneNode(bodyAsJsonArray);
|
||||
jsonNode = WalkNode(transformerContext, options, jsonNode, model);
|
||||
break;
|
||||
|
||||
case var bodyAsEnumerable when bodyAsEnumerable is IEnumerable and not string:
|
||||
jsonNode = JsonSerializer.SerializeToNode(bodyAsEnumerable);
|
||||
if (jsonNode != null)
|
||||
{
|
||||
jsonNode = WalkNode(transformerContext, options, jsonNode, model);
|
||||
}
|
||||
break;
|
||||
|
||||
case string bodyAsString:
|
||||
jsonNode = ReplaceSingleNode(transformerContext, options, bodyAsString, model);
|
||||
break;
|
||||
|
||||
case not null:
|
||||
jsonNode = JsonSerializer.SerializeToNode(original.BodyAsJson);
|
||||
if (jsonNode != null)
|
||||
{
|
||||
jsonNode = WalkNode(transformerContext, options, jsonNode, model);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return new BodyData
|
||||
{
|
||||
Encoding = original.Encoding,
|
||||
DetectedBodyType = original.DetectedBodyType,
|
||||
DetectedBodyTypeFromContentType = original.DetectedBodyTypeFromContentType,
|
||||
ProtoDefinition = original.ProtoDefinition,
|
||||
ProtoBufMessageType = original.ProtoBufMessageType,
|
||||
BodyAsJson = jsonNode
|
||||
};
|
||||
}
|
||||
|
||||
private JsonNode ParseAsJsonObject(string stringValue)
|
||||
{
|
||||
if (_jsonConverter.IsValidJson(stringValue))
|
||||
{
|
||||
try
|
||||
{
|
||||
var parsed = JsonNode.Parse(stringValue);
|
||||
if (parsed is JsonObject)
|
||||
{
|
||||
return parsed;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
settings.Logger.Warn("Failed to parse string ''{0}'' as JSON. Returning the original string value.", stringValue);
|
||||
}
|
||||
}
|
||||
|
||||
return JsonValue.Create(stringValue)!;
|
||||
}
|
||||
|
||||
private JsonNode? ReplaceSingleNode(ITransformerContext transformerContext, ReplaceNodeOptions options, string stringValue, object model)
|
||||
{
|
||||
var transformedString = transformerContext.ParseAndRender(stringValue, model);
|
||||
|
||||
if (!string.Equals(stringValue, transformedString))
|
||||
{
|
||||
return ReplaceNodeValue(options, transformedString);
|
||||
}
|
||||
|
||||
return JsonValue.Create(stringValue);
|
||||
}
|
||||
|
||||
private JsonNode? WalkNode(ITransformerContext transformerContext, ReplaceNodeOptions options, JsonNode? node, object model)
|
||||
{
|
||||
switch (node)
|
||||
{
|
||||
case JsonObject jsonObject:
|
||||
foreach (var property in jsonObject.ToArray())
|
||||
{
|
||||
jsonObject[property.Key] = WalkNode(transformerContext, options, property.Value, model);
|
||||
}
|
||||
return jsonObject;
|
||||
|
||||
case JsonArray jsonArray:
|
||||
for (var i = 0; i < jsonArray.Count; i++)
|
||||
{
|
||||
jsonArray[i] = WalkNode(transformerContext, options, jsonArray[i], model);
|
||||
}
|
||||
return jsonArray;
|
||||
|
||||
case JsonValue jsonValue when jsonValue.TryGetValue<string>(out var stringValue):
|
||||
if (string.IsNullOrEmpty(stringValue))
|
||||
{
|
||||
return jsonValue;
|
||||
}
|
||||
|
||||
var transformed = transformerContext.ParseAndEvaluate(stringValue!, model);
|
||||
return !Equals(stringValue, transformed) ? ReplaceNodeValue(options, transformed) ?? jsonValue : jsonValue;
|
||||
|
||||
default:
|
||||
return node;
|
||||
}
|
||||
}
|
||||
|
||||
private JsonNode? ReplaceNodeValue(ReplaceNodeOptions options, object? transformedValue)
|
||||
{
|
||||
switch (transformedValue)
|
||||
{
|
||||
case JsonNode jsonNode:
|
||||
return CloneNode(jsonNode);
|
||||
|
||||
case string transformedString:
|
||||
var (isConvertedFromString, convertedValueFromString) = TryConvert(options, transformedString);
|
||||
return isConvertedFromString
|
||||
? JsonSerializer.SerializeToNode(convertedValueFromString)
|
||||
: ParseAsJsonObject(transformedString);
|
||||
|
||||
case WireMockList<string> strings:
|
||||
switch (strings.Count)
|
||||
{
|
||||
case 1:
|
||||
return ParseAsJsonObject(strings[0]);
|
||||
|
||||
case > 1:
|
||||
return JsonSerializer.SerializeToNode(strings.ToArray());
|
||||
}
|
||||
break;
|
||||
|
||||
case { }:
|
||||
var (isConverted, convertedValue) = TryConvert(options, transformedValue);
|
||||
if (isConverted)
|
||||
{
|
||||
return JsonSerializer.SerializeToNode(convertedValue);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static JsonNode? CloneNode(JsonNode? node)
|
||||
{
|
||||
#if NET8_0_OR_GREATER
|
||||
return node?.DeepClone();
|
||||
#else
|
||||
return node == null ? null : JsonNode.Parse(node.ToJsonString());
|
||||
#endif
|
||||
}
|
||||
|
||||
private static (bool IsConverted, object ConvertedValue) TryConvert(ReplaceNodeOptions options, object value)
|
||||
{
|
||||
var valueAsString = value as string;
|
||||
|
||||
if (options == ReplaceNodeOptions.Evaluate)
|
||||
{
|
||||
if (valueAsString != null && WrappedString.TryDecode(valueAsString, out var decoded))
|
||||
{
|
||||
return (true, decoded);
|
||||
}
|
||||
|
||||
return (false, value);
|
||||
}
|
||||
|
||||
if (valueAsString != null)
|
||||
{
|
||||
return WrappedString.TryDecode(valueAsString, out var decoded)
|
||||
? (true, decoded)
|
||||
: NewtonsoftJsonBodyTransformer.TryConvertToKnownType(valueAsString);
|
||||
}
|
||||
|
||||
return (false, value);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System.Linq;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Text;
|
||||
using Stef.Validation;
|
||||
@@ -129,11 +128,11 @@ internal static class BodyParser
|
||||
{
|
||||
Guard.NotNull(settings);
|
||||
|
||||
var bodyWithContentEncoding = await ReadBytesAsync(settings.Stream, settings.ContentEncoding, settings.DecompressGZipAndDeflate).ConfigureAwait(false);
|
||||
var (ContentType, Bytes) = await ReadBytesAsync(settings.Stream, settings.ContentEncoding, settings.DecompressGZipAndDeflate).ConfigureAwait(false);
|
||||
var data = new BodyData
|
||||
{
|
||||
BodyAsBytes = bodyWithContentEncoding.Bytes,
|
||||
DetectedCompression = bodyWithContentEncoding.ContentType,
|
||||
BodyAsBytes = Bytes,
|
||||
DetectedCompression = ContentType,
|
||||
DetectedBodyType = BodyType.Bytes,
|
||||
DetectedBodyTypeFromContentType = DetectBodyTypeFromContentType(settings.ContentType)
|
||||
};
|
||||
@@ -169,17 +168,17 @@ internal static class BodyParser
|
||||
data.DetectedBodyType = BodyType.FormUrlEncoded;
|
||||
}
|
||||
|
||||
// If string is not null or empty, try to deserialize the string to a JObject
|
||||
if (settings.DeserializeJson && JsonUtils.IsJson(data.BodyAsString))
|
||||
// If string is not null or empty, try to deserialize the string
|
||||
if (settings.DeserializeJson && settings.DefaultJsonConverter.IsValidJson(data.BodyAsString))
|
||||
{
|
||||
try
|
||||
{
|
||||
data.BodyAsJson = JsonUtils.DeserializeObject(data.BodyAsString);
|
||||
data.BodyAsJson = settings.DefaultJsonConverter.Deserialize<object>(data.BodyAsString);
|
||||
data.DetectedBodyType = BodyType.Json;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// JsonConvert failed, just ignore.
|
||||
// JsonConverter failed, just ignore.
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -202,7 +201,7 @@ internal static class BodyParser
|
||||
return (null, data);
|
||||
}
|
||||
|
||||
public static bool IsProbablyText(byte[] data)
|
||||
private static bool IsProbablyText(byte[] data)
|
||||
{
|
||||
if (data.Length == 0)
|
||||
{
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System.IO;
|
||||
using JsonConverter.Abstractions;
|
||||
using JsonConverter.Newtonsoft.Json;
|
||||
|
||||
namespace WireMock.Util;
|
||||
|
||||
@@ -35,4 +36,13 @@ internal class BodyParserSettings
|
||||
/// Try to deserialize the body as FormUrlEncoded.
|
||||
/// </summary>
|
||||
public bool DeserializeFormUrlEncoded { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the default JSON converter used for deserialization.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Set this property to customize how objects are serialized to and deserialized from JSON during mapping.
|
||||
/// Default is <see cref="NewtonsoftJsonConverter"/>.
|
||||
/// </remarks>
|
||||
public IJsonConverter DefaultJsonConverter { get; set; } = new NewtonsoftJsonConverter();
|
||||
}
|
||||
@@ -1,137 +0,0 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Text;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using WireMock.Serialization;
|
||||
|
||||
namespace WireMock.Util;
|
||||
|
||||
internal static class JsonUtils
|
||||
{
|
||||
public static bool IsJson(string? value)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(value))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
value = value!.Trim();
|
||||
|
||||
return (value.StartsWith("{") && value.EndsWith("}")) || (value.StartsWith("[") && value.EndsWith("]"));
|
||||
}
|
||||
|
||||
public static bool TryParseAsJObject(string? strInput, [NotNullWhen(true)] out JObject? value)
|
||||
{
|
||||
value = null;
|
||||
|
||||
if (!IsJson(strInput))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// Try to convert this string into a JToken
|
||||
value = JObject.Parse(strInput!);
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static string Serialize(object value)
|
||||
{
|
||||
return JsonConvert.SerializeObject(value, JsonSerializationConstants.JsonSerializerSettingsIncludeNullValues);
|
||||
}
|
||||
|
||||
public static byte[] SerializeAsPactFile(object value)
|
||||
{
|
||||
var json = JsonConvert.SerializeObject(value, JsonSerializationConstants.JsonSerializerSettingsPact);
|
||||
return Encoding.UTF8.GetBytes(json);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Load a Newtonsoft.Json.Linq.JObject from a string that contains JSON.
|
||||
/// Using : DateParseHandling = DateParseHandling.None
|
||||
/// </summary>
|
||||
/// <param name="json">A System.String that contains JSON.</param>
|
||||
/// <returns>A Newtonsoft.Json.Linq.JToken populated from the string that contains JSON.</returns>
|
||||
public static JToken Parse(string json)
|
||||
{
|
||||
return JsonConvert.DeserializeObject<JToken>(json, JsonSerializationConstants.JsonDeserializerSettingsWithDateParsingNone)!;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deserializes the JSON to a .NET object.
|
||||
/// Using : DateParseHandling = DateParseHandling.None
|
||||
/// </summary>
|
||||
/// <param name="json">A System.String that contains JSON.</param>
|
||||
/// <returns>The deserialized object from the JSON string.</returns>
|
||||
public static object DeserializeObject(string json)
|
||||
{
|
||||
return JsonConvert.DeserializeObject(json, JsonSerializationConstants.JsonDeserializerSettingsWithDateParsingNone)!;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deserializes the JSON to the specified .NET type.
|
||||
/// Using : DateParseHandling = DateParseHandling.None
|
||||
/// </summary>
|
||||
/// <param name="json">A System.String that contains JSON.</param>
|
||||
/// <returns>The deserialized object from the JSON string.</returns>
|
||||
public static T DeserializeObject<T>(string json)
|
||||
{
|
||||
return JsonConvert.DeserializeObject<T>(json, JsonSerializationConstants.JsonDeserializerSettingsWithDateParsingNone)!;
|
||||
}
|
||||
|
||||
public static T? TryDeserializeObject<T>(string json)
|
||||
{
|
||||
try
|
||||
{
|
||||
return JsonConvert.DeserializeObject<T>(json);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return default;
|
||||
}
|
||||
}
|
||||
|
||||
public static T ParseJTokenToObject<T>(object? value)
|
||||
{
|
||||
if (value != null && value.GetType() == typeof(T))
|
||||
{
|
||||
return (T)value;
|
||||
}
|
||||
|
||||
return value switch
|
||||
{
|
||||
JToken tokenValue => tokenValue.ToObject<T>()!,
|
||||
|
||||
_ => throw new NotSupportedException($"Unable to convert value to {typeof(T)}.")
|
||||
};
|
||||
}
|
||||
|
||||
public static JToken ConvertValueToJToken(object value)
|
||||
{
|
||||
// Check if JToken, string, IEnumerable or object
|
||||
switch (value)
|
||||
{
|
||||
case JToken tokenValue:
|
||||
return tokenValue;
|
||||
|
||||
case string stringValue:
|
||||
return Parse(stringValue);
|
||||
|
||||
case IEnumerable enumerableValue:
|
||||
return JArray.FromObject(enumerableValue);
|
||||
|
||||
default:
|
||||
return JObject.FromObject(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,6 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Stef.Validation;
|
||||
|
||||
|
||||
@@ -30,18 +30,19 @@
|
||||
<PackageReference Include="Stef.Validation" Version="0.2.0" />
|
||||
<PackageReference Include="AnyOf" Version="0.5.0.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.2" />
|
||||
<PackageReference Include="JsonConverter.Newtonsoft.Json" Version="0.9.0" />
|
||||
<PackageReference Include="JsonConverter.Newtonsoft.Json" Version="0.12.0" />
|
||||
<PackageReference Include="JsonConverter.System.Text.Json" Version="0.12.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Handlebars.Net.Helpers" Version="2.5.2" />
|
||||
<PackageReference Include="Handlebars.Net.Helpers" Version="2.5.5" />
|
||||
<!--<PackageReference Include="Handlebars.Net.Helpers.DynamicLinq" Version="2.5.2" />-->
|
||||
<PackageReference Include="Handlebars.Net.Helpers.Humanizer" Version="2.5.2" />
|
||||
<PackageReference Include="Handlebars.Net.Helpers.Json" Version="2.5.2" />
|
||||
<PackageReference Include="Handlebars.Net.Helpers.Random" Version="2.5.2" />
|
||||
<PackageReference Include="Handlebars.Net.Helpers.Xeger" Version="2.5.2" />
|
||||
<PackageReference Include="Handlebars.Net.Helpers.XPath" Version="2.5.2" />
|
||||
<PackageReference Include="Handlebars.Net.Helpers.Xslt" Version="2.5.2" />
|
||||
<PackageReference Include="Handlebars.Net.Helpers.Humanizer" Version="2.5.5" />
|
||||
<PackageReference Include="Handlebars.Net.Helpers.Json" Version="2.5.5" />
|
||||
<PackageReference Include="Handlebars.Net.Helpers.Random" Version="2.5.5" />
|
||||
<PackageReference Include="Handlebars.Net.Helpers.Xeger" Version="2.5.5" />
|
||||
<PackageReference Include="Handlebars.Net.Helpers.XPath" Version="2.5.5" />
|
||||
<PackageReference Include="Handlebars.Net.Helpers.Xslt" Version="2.5.5" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -22,8 +22,8 @@ public static class TestcontainersUtils
|
||||
throw new InvalidOperationException($"The {nameof(TestcontainersSettings.OS.DockerEndpointAuthConfig)} is null. Check if Docker is started.");
|
||||
}
|
||||
|
||||
using var dockerClientConfig = TestcontainersSettings.OS.DockerEndpointAuthConfig.GetDockerClientConfiguration();
|
||||
using var dockerClient = dockerClientConfig.CreateClient();
|
||||
var dockerClientBuilder = TestcontainersSettings.OS.DockerEndpointAuthConfig.GetDockerClientBuilder();
|
||||
using var dockerClient = dockerClientBuilder.Build();
|
||||
|
||||
var version = await dockerClient.System.GetVersionAsync();
|
||||
return version.Os.IndexOf("Windows", StringComparison.OrdinalIgnoreCase) >= 0 ? OSPlatform.Windows : OSPlatform.Linux;
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Stef.Validation" Version="0.2.0" />
|
||||
<PackageReference Include="Testcontainers" Version="4.10.0" />
|
||||
<PackageReference Include="Testcontainers" Version="4.12.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -34,5 +34,6 @@
|
||||
<ProjectReference Include="../WireMock.Net.GraphQL/WireMock.Net.GraphQL.csproj" />
|
||||
<ProjectReference Include="../WireMock.Net.ProtoBuf/WireMock.Net.ProtoBuf.csproj" />
|
||||
<ProjectReference Include="../WireMock.Net.OpenTelemetry/WireMock.Net.OpenTelemetry.csproj" />
|
||||
<ProjectReference Include="../WireMock.Net.Matchers.SystemTextJsonPath/WireMock.Net.Matchers.SystemTextJsonPath.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -1,6 +1,6 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<Sdk Name="Aspire.AppHost.Sdk" Version="13.1.0" />
|
||||
<Sdk Name="Aspire.AppHost.Sdk" Version="13.4.0" />
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
@@ -19,7 +19,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Aspire.Hosting.AppHost" Version="13.1.1" />
|
||||
<PackageReference Include="Aspire.Hosting.AppHost" Version="13.4.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Aspire.Hosting.Testing" Version="13.1.1" />
|
||||
<PackageReference Include="Aspire.Hosting.Testing" Version="13.4.0" />
|
||||
<PackageReference Include="Codecov" Version="1.13.0" />
|
||||
<PackageReference Include="coverlet.msbuild" Version="6.0.4">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
@@ -25,7 +25,7 @@
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="AwesomeAssertions" Version="9.4.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.3.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.6.0" />
|
||||
<PackageReference Include="Moq" Version="4.20.72" />
|
||||
<PackageReference Include="xunit.v3" Version="3.2.2" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.5">
|
||||
|
||||
@@ -92,17 +92,11 @@ public class WireMockServerBuilderExtensionsTests
|
||||
Tag = "latest"
|
||||
});
|
||||
|
||||
var endpointAnnotation = wiremock.Resource.Annotations.OfType<EndpointAnnotation>().FirstOrDefault();
|
||||
endpointAnnotation.Should().BeEquivalentTo(new EndpointAnnotation(
|
||||
protocol: ProtocolType.Tcp,
|
||||
uriScheme: "http",
|
||||
transport: null,
|
||||
name: null,
|
||||
port: port,
|
||||
targetPort: 80,
|
||||
isExternal: null,
|
||||
isProxied: true
|
||||
));
|
||||
var endpointAnnotation = wiremock.Resource.Annotations.OfType<EndpointAnnotation>().First();
|
||||
endpointAnnotation.Protocol.Should().Be(ProtocolType.Tcp);
|
||||
endpointAnnotation.UriScheme.Should().Be("http");
|
||||
endpointAnnotation.Port.Should().Be(port);
|
||||
endpointAnnotation.TargetPort.Should().Be(80);
|
||||
|
||||
wiremock.Resource.Annotations.OfType<EnvironmentCallbackAnnotation>().FirstOrDefault().Should().NotBeNull();
|
||||
wiremock.Resource.Annotations.OfType<CommandLineArgsCallbackAnnotation>().FirstOrDefault().Should().NotBeNull();
|
||||
@@ -153,39 +147,24 @@ public class WireMockServerBuilderExtensionsTests
|
||||
endpointAnnotations.Should().HaveCount(3);
|
||||
|
||||
var endpointAnnotationForHttp80 = endpointAnnotations[0];
|
||||
endpointAnnotationForHttp80.Should().BeEquivalentTo(new EndpointAnnotation(
|
||||
protocol: ProtocolType.Tcp,
|
||||
uriScheme: "http",
|
||||
transport: null,
|
||||
name: null,
|
||||
port: null,
|
||||
targetPort: 80,
|
||||
isExternal: null,
|
||||
isProxied: true
|
||||
));
|
||||
endpointAnnotationForHttp80.Protocol.Should().Be(ProtocolType.Tcp);
|
||||
endpointAnnotationForHttp80.UriScheme.Should().Be("http");
|
||||
endpointAnnotationForHttp80.Port.Should().BeNull();
|
||||
endpointAnnotationForHttp80.TargetPort.Should().Be(80);
|
||||
|
||||
var endpointAnnotationForHttpFreePort = endpointAnnotations[1];
|
||||
endpointAnnotationForHttpFreePort.Should().BeEquivalentTo(new EndpointAnnotation(
|
||||
protocol: ProtocolType.Tcp,
|
||||
uriScheme: "http",
|
||||
transport: null,
|
||||
name: $"http-{freePorts[0]}",
|
||||
port: freePorts[0],
|
||||
targetPort: freePorts[0],
|
||||
isExternal: null,
|
||||
isProxied: true
|
||||
));
|
||||
endpointAnnotationForHttpFreePort.Protocol.Should().Be(ProtocolType.Tcp);
|
||||
endpointAnnotationForHttpFreePort.UriScheme.Should().Be("http");
|
||||
endpointAnnotationForHttpFreePort.Name.Should().Be($"http-{freePorts[0]}");
|
||||
endpointAnnotationForHttpFreePort.Port.Should().Be(freePorts[0]);
|
||||
endpointAnnotationForHttpFreePort.TargetPort.Should().Be(freePorts[0]);
|
||||
|
||||
var endpointAnnotationForGrpcFreePort = endpointAnnotations[2];
|
||||
endpointAnnotationForGrpcFreePort.Should().BeEquivalentTo(new EndpointAnnotation(
|
||||
protocol: ProtocolType.Tcp,
|
||||
uriScheme: "grpc",
|
||||
transport: null,
|
||||
name: $"grpc-{freePorts[1]}",
|
||||
port: freePorts[1],
|
||||
targetPort: freePorts[1],
|
||||
isExternal: null,
|
||||
isProxied: true
|
||||
));
|
||||
endpointAnnotationForGrpcFreePort.Protocol.Should().Be(ProtocolType.Tcp);
|
||||
endpointAnnotationForGrpcFreePort.UriScheme.Should().Be("grpc");
|
||||
endpointAnnotationForGrpcFreePort.Name.Should().Be($"grpc-{freePorts[1]}");
|
||||
endpointAnnotationForGrpcFreePort.Port.Should().Be(freePorts[1]);
|
||||
endpointAnnotationForGrpcFreePort.TargetPort.Should().Be(freePorts[1]);
|
||||
|
||||
wiremock.Resource.Annotations.OfType<EnvironmentCallbackAnnotation>().FirstOrDefault().Should().NotBeNull();
|
||||
wiremock.Resource.Annotations.OfType<CommandLineArgsCallbackAnnotation>().FirstOrDefault().Should().NotBeNull();
|
||||
|
||||
@@ -30,6 +30,12 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Update="JetBrains.Annotations" Version="2025.2.4" />
|
||||
<PackageReference Include="OpenTelemetry" Version="1.15.3" />
|
||||
<PackageReference Include="OpenTelemetry.Api" Version="1.15.3" />
|
||||
<PackageReference Include="OpenTelemetry.Api.ProviderBuilderExtensions" Version="1.15.3" />
|
||||
<PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.15.3" />
|
||||
<PackageReference Include="OpenTelemetry.Extensions.Hosting" Version="1.15.3" />
|
||||
<PackageReference Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.15.2" />
|
||||
<PackageReference Update="SonarAnalyzer.CSharp" Version="10.11.0.117924" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@@ -59,7 +59,8 @@ public class MappingBuilderTests
|
||||
mappingConverter,
|
||||
mappingToFileSaver,
|
||||
guidUtilsMock.Object,
|
||||
dateTimeUtilsMock.Object
|
||||
dateTimeUtilsMock.Object,
|
||||
new ResponseMessageBuilder(dateTimeUtilsMock.Object)
|
||||
);
|
||||
|
||||
_sut.Given(Request.Create()
|
||||
|
||||
@@ -523,4 +523,151 @@ public class JsonMatcherTests
|
||||
// Assert
|
||||
Assert.Equal(1.0, score);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void JsonMatcher_IsMatch_Array_WithIgnoreArrayOrderTrue_DifferentOrder_ShouldMatch()
|
||||
{
|
||||
// Assign
|
||||
var matcher = new JsonMatcher(new[] { "a", "b", "c" }, ignoreArrayOrder: true);
|
||||
|
||||
// Act
|
||||
var jArray = new JArray
|
||||
{
|
||||
"c",
|
||||
"a",
|
||||
"b"
|
||||
};
|
||||
var score = matcher.IsMatch(jArray).Score;
|
||||
|
||||
// Assert
|
||||
Assert.Equal(1.0, score);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void JsonMatcher_IsMatch_Array_WithIgnoreArrayOrderFalse_DifferentOrder_ShouldNotMatch()
|
||||
{
|
||||
// Assign
|
||||
var matcher = new JsonMatcher(new[] { "a", "b", "c" }, ignoreArrayOrder: false);
|
||||
|
||||
// Act
|
||||
var jArray = new JArray
|
||||
{
|
||||
"c",
|
||||
"a",
|
||||
"b"
|
||||
};
|
||||
var score = matcher.IsMatch(jArray).Score;
|
||||
|
||||
// Assert
|
||||
Assert.Equal(MatchScores.Mismatch, score);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void JsonMatcher_IsMatch_Array_WithIgnoreArrayOrderTrue_SameOrder_ShouldMatch()
|
||||
{
|
||||
// Assign
|
||||
var matcher = new JsonMatcher(new[] { "a", "b", "c" }, ignoreArrayOrder: true);
|
||||
|
||||
// Act
|
||||
var jArray = new JArray
|
||||
{
|
||||
"a",
|
||||
"b",
|
||||
"c"
|
||||
};
|
||||
var score = matcher.IsMatch(jArray).Score;
|
||||
|
||||
// Assert
|
||||
Assert.Equal(1.0, score);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void JsonMatcher_IsMatch_Array_WithIgnoreArrayOrderTrue_DifferentLength_ShouldNotMatch()
|
||||
{
|
||||
// Assign
|
||||
var matcher = new JsonMatcher(new[] { "a", "b", "c" }, ignoreArrayOrder: true);
|
||||
|
||||
// Act
|
||||
var jArray = new JArray
|
||||
{
|
||||
"a",
|
||||
"b"
|
||||
};
|
||||
var score = matcher.IsMatch(jArray).Score;
|
||||
|
||||
// Assert
|
||||
Assert.Equal(MatchScores.Mismatch, score);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void JsonMatcher_IsMatch_ObjectWithArray_WithIgnoreArrayOrderTrue_DifferentOrder_ShouldMatch()
|
||||
{
|
||||
// Assign
|
||||
var matcher = new JsonMatcher(new { Items = new[] { "x", "y", "z" } }, ignoreArrayOrder: true);
|
||||
|
||||
// Act
|
||||
var jObject = new JObject
|
||||
{
|
||||
{ "Items", new JArray("z", "x", "y") }
|
||||
};
|
||||
var score = matcher.IsMatch(jObject).Score;
|
||||
|
||||
// Assert
|
||||
Assert.Equal(1.0, score);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void JsonMatcher_IsMatch_ArrayAsString_WithIgnoreArrayOrderTrue_DifferentOrder_ShouldMatch()
|
||||
{
|
||||
// Assign
|
||||
var matcher = new JsonMatcher("[ \"a\", \"b\", \"c\" ]", ignoreArrayOrder: true);
|
||||
|
||||
// Act
|
||||
var jArray = new JArray
|
||||
{
|
||||
"c",
|
||||
"b",
|
||||
"a"
|
||||
};
|
||||
var score = matcher.IsMatch(jArray).Score;
|
||||
|
||||
// Assert
|
||||
Assert.Equal(1.0, score);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void JsonMatcher_IsMatch_ArrayOfNumbers_WithIgnoreArrayOrderTrue_DifferentOrder_ShouldMatch()
|
||||
{
|
||||
// Assign
|
||||
var matcher = new JsonMatcher(new[] { 1, 2, 3 }, ignoreArrayOrder: true);
|
||||
|
||||
// Act
|
||||
var jArray = new JArray
|
||||
{
|
||||
3,
|
||||
1,
|
||||
2
|
||||
};
|
||||
var score = matcher.IsMatch(jArray).Score;
|
||||
|
||||
// Assert
|
||||
Assert.Equal(1.0, score);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(MatchBehaviour.AcceptOnMatch, false, false, false)]
|
||||
[InlineData(MatchBehaviour.AcceptOnMatch, true, false, true)]
|
||||
[InlineData(MatchBehaviour.RejectOnMatch, true, true, false)]
|
||||
public void JsonMatcher_GetCSharpCodeArguments_ShouldIncludeAllConstructorArguments(MatchBehaviour matchBehaviour, bool ignoreCase, bool regex, bool ignoreArrayOrder)
|
||||
{
|
||||
// Assign
|
||||
var matcher = new JsonMatcher(matchBehaviour, "{ \"id\": 1 }", ignoreCase, regex, ignoreArrayOrder);
|
||||
|
||||
// Act
|
||||
var result = matcher.GetCSharpCodeArguments();
|
||||
|
||||
// Assert
|
||||
result.Should().StartWith($"new JsonMatcher(WireMock.Matchers.MatchBehaviour.{matchBehaviour},");
|
||||
result.Should().EndWith($", {ignoreCase.ToString().ToLowerInvariant()}, {regex.ToString().ToLowerInvariant()}, {ignoreArrayOrder.ToString().ToLowerInvariant()})");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,484 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System.Text.Json;
|
||||
using WireMock.Matchers;
|
||||
|
||||
namespace WireMock.Net.Tests.Matchers;
|
||||
|
||||
public class SystemTextJsonMatcherTests
|
||||
{
|
||||
public enum NormalEnumStj
|
||||
{
|
||||
Abc
|
||||
}
|
||||
|
||||
public class Test1Stj
|
||||
{
|
||||
public NormalEnumStj NormalEnum { get; set; }
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SystemTextJsonMatcher_GetName()
|
||||
{
|
||||
// Assign
|
||||
var matcher = new SystemTextJsonMatcher("{}");
|
||||
|
||||
// Act
|
||||
var name = matcher.Name;
|
||||
|
||||
// Assert
|
||||
name.Should().Be("SystemTextJsonMatcher");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SystemTextJsonMatcher_GetValue()
|
||||
{
|
||||
// Assign
|
||||
var matcher = new SystemTextJsonMatcher("{}");
|
||||
|
||||
// Act
|
||||
var value = matcher.Value;
|
||||
|
||||
// Assert
|
||||
value.Should().Be("{}");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SystemTextJsonMatcher_WithInvalidStringValue_Should_ThrowException()
|
||||
{
|
||||
// Act
|
||||
Action action = () => new SystemTextJsonMatcher(MatchBehaviour.AcceptOnMatch, "{ \"Id\"");
|
||||
|
||||
// Assert
|
||||
action.Should().Throw<JsonException>();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SystemTextJsonMatcher_WithInvalidObjectValue_Should_ThrowException()
|
||||
{
|
||||
// Act
|
||||
Action action = () => new SystemTextJsonMatcher(MatchBehaviour.AcceptOnMatch, new MemoryStream());
|
||||
|
||||
// Assert
|
||||
action.Should().Throw<Exception>();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SystemTextJsonMatcher_IsMatch_WithInvalidValue_Should_ReturnMismatch_And_Exception_ShouldBeSet()
|
||||
{
|
||||
// Assign
|
||||
var matcher = new SystemTextJsonMatcher("{}");
|
||||
using var stream = new MemoryStream();
|
||||
|
||||
// Act
|
||||
var result = matcher.IsMatch(stream);
|
||||
|
||||
// Assert
|
||||
result.Score.Should().Be(MatchScores.Mismatch);
|
||||
result.Exception.Should().NotBeNull();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SystemTextJsonMatcher_IsMatch_ByteArray()
|
||||
{
|
||||
// Assign
|
||||
var bytes = new byte[0];
|
||||
var matcher = new SystemTextJsonMatcher("{}");
|
||||
|
||||
// Act
|
||||
var match = matcher.IsMatch(bytes).Score;
|
||||
|
||||
// Assert
|
||||
match.Should().Be(0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SystemTextJsonMatcher_IsMatch_NullString()
|
||||
{
|
||||
// Assign
|
||||
string? s = null;
|
||||
var matcher = new SystemTextJsonMatcher("{}");
|
||||
|
||||
// Act
|
||||
var match = matcher.IsMatch(s).Score;
|
||||
|
||||
// Assert
|
||||
match.Should().Be(0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SystemTextJsonMatcher_IsMatch_NullObject()
|
||||
{
|
||||
// Assign
|
||||
object? o = null;
|
||||
var matcher = new SystemTextJsonMatcher("{}");
|
||||
|
||||
// Act
|
||||
var match = matcher.IsMatch(o).Score;
|
||||
|
||||
// Assert
|
||||
match.Should().Be(0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SystemTextJsonMatcher_IsMatch_JsonArrayAsString()
|
||||
{
|
||||
// Assign
|
||||
var matcher = new SystemTextJsonMatcher("[ \"x\", \"y\" ]");
|
||||
|
||||
// Act
|
||||
var jsonElement = JsonDocument.Parse("[ \"x\", \"y\" ]").RootElement;
|
||||
var match = matcher.IsMatch(jsonElement).Score;
|
||||
|
||||
// Assert
|
||||
Assert.Equal(1.0, match);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SystemTextJsonMatcher_IsMatch_JsonObjectAsString_ShouldMatch()
|
||||
{
|
||||
// Assign
|
||||
var matcher = new SystemTextJsonMatcher("{ \"Id\" : 1, \"Name\" : \"Test\" }");
|
||||
|
||||
// Act
|
||||
var jsonElement = JsonDocument.Parse("{ \"Id\" : 1, \"Name\" : \"Test\" }").RootElement;
|
||||
var match = matcher.IsMatch(jsonElement).Score;
|
||||
|
||||
// Assert
|
||||
Assert.Equal(1.0, match);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SystemTextJsonMatcher_IsMatch_AnonymousObject_ShouldMatch()
|
||||
{
|
||||
// Assign
|
||||
var matcher = new SystemTextJsonMatcher(new { Id = 1, Name = "Test" });
|
||||
|
||||
// Act
|
||||
var match = matcher.IsMatch("{ \"Id\" : 1, \"Name\" : \"Test\" }").Score;
|
||||
|
||||
// Assert
|
||||
Assert.Equal(1.0, match);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SystemTextJsonMatcher_IsMatch_AnonymousObject_ShouldNotMatch()
|
||||
{
|
||||
// Assign
|
||||
var matcher = new SystemTextJsonMatcher(new { Id = 1, Name = "Test" });
|
||||
|
||||
// Act
|
||||
var match = matcher.IsMatch("{ \"Id\" : 1, \"Name\" : \"Test\", \"Other\" : \"abc\" }").Score;
|
||||
|
||||
// Assert
|
||||
Assert.Equal(MatchScores.Mismatch, match);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SystemTextJsonMatcher_IsMatch_WithIgnoreCaseTrue_JsonObject()
|
||||
{
|
||||
// Assign
|
||||
var matcher = new SystemTextJsonMatcher(new { id = 1, Name = "test" }, true);
|
||||
|
||||
// Act
|
||||
var match = matcher.IsMatch("{ \"Id\" : 1, \"NaMe\" : \"Test\" }").Score;
|
||||
|
||||
// Assert
|
||||
Assert.Equal(1.0, match);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SystemTextJsonMatcher_IsMatch_WithIgnoreCaseTrue_JsonObjectParsed()
|
||||
{
|
||||
// Assign
|
||||
var matcher = new SystemTextJsonMatcher(new { Id = 1, Name = "TESt" }, true);
|
||||
|
||||
// Act
|
||||
var match = matcher.IsMatch("{ \"Id\" : 1, \"Name\" : \"Test\" }").Score;
|
||||
|
||||
// Assert
|
||||
Assert.Equal(1.0, match);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SystemTextJsonMatcher_IsMatch_JsonObjectAsString_RejectOnMatch()
|
||||
{
|
||||
// Assign
|
||||
var matcher = new SystemTextJsonMatcher(MatchBehaviour.RejectOnMatch, "{ \"Id\" : 1, \"Name\" : \"Test\" }");
|
||||
|
||||
// Act
|
||||
var match = matcher.IsMatch("{ \"Id\" : 1, \"Name\" : \"Test\" }").Score;
|
||||
|
||||
// Assert
|
||||
Assert.Equal(0.0, match);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SystemTextJsonMatcher_IsMatch_JsonObjectWithDateTimeOffsetAsString()
|
||||
{
|
||||
// Assign
|
||||
var matcher = new SystemTextJsonMatcher("{ \"preferredAt\" : \"2019-11-21T10:32:53.2210009+00:00\" }");
|
||||
|
||||
// Act
|
||||
var match = matcher.IsMatch("{ \"preferredAt\" : \"2019-11-21T10:32:53.2210009+00:00\" }").Score;
|
||||
|
||||
// Assert
|
||||
Assert.Equal(1.0, match);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SystemTextJsonMatcher_IsMatch_NormalEnum()
|
||||
{
|
||||
// Assign
|
||||
var matcher = new SystemTextJsonMatcher(new Test1Stj { NormalEnum = NormalEnumStj.Abc });
|
||||
|
||||
// Act
|
||||
var match = matcher.IsMatch("{ \"NormalEnum\" : 0 }").Score;
|
||||
|
||||
// Assert
|
||||
match.Should().Be(1.0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SystemTextJsonMatcher_IsMatch_WithRegexTrue_ShouldMatch()
|
||||
{
|
||||
// Assign
|
||||
var matcher = new SystemTextJsonMatcher(new { Id = "^\\d+$", Name = "Test" }, regex: true);
|
||||
|
||||
// Act
|
||||
var match = matcher.IsMatch("{ \"Id\" : \"42\", \"Name\" : \"Test\" }").Score;
|
||||
|
||||
// Assert
|
||||
Assert.Equal(1.0, match);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SystemTextJsonMatcher_IsMatch_WithRegexTrue_Complex_ShouldMatch()
|
||||
{
|
||||
// Assign
|
||||
var matcher = new SystemTextJsonMatcher(new
|
||||
{
|
||||
Complex = new
|
||||
{
|
||||
Id = "^\\d+$",
|
||||
Name = ".*"
|
||||
}
|
||||
}, regex: true);
|
||||
|
||||
// Act
|
||||
var match = matcher.IsMatch("{ \"Complex\" : { \"Id\" : \"42\", \"Name\" : \"Test\" } }").Score;
|
||||
|
||||
// Assert
|
||||
Assert.Equal(1.0, match);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SystemTextJsonMatcher_IsMatch_WithRegexTrue_Complex_ShouldNotMatch()
|
||||
{
|
||||
// Assign
|
||||
var matcher = new SystemTextJsonMatcher(new
|
||||
{
|
||||
Complex = new
|
||||
{
|
||||
Id = "^\\d+$",
|
||||
Name = ".*"
|
||||
}
|
||||
}, regex: true);
|
||||
|
||||
// Act
|
||||
var match = matcher.IsMatch("{ \"Complex\" : { \"Id\" : \"42\", \"Name\" : \"Test\", \"Other\" : \"Other\" } }").Score;
|
||||
|
||||
// Assert
|
||||
Assert.Equal(MatchScores.Mismatch, match);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SystemTextJsonMatcher_IsMatch_WithRegexTrue_Array_ShouldMatch()
|
||||
{
|
||||
// Assign
|
||||
var matcher = new SystemTextJsonMatcher(new
|
||||
{
|
||||
Array = new[] { "^\\d+$", ".*" }
|
||||
}, regex: true);
|
||||
|
||||
// Act
|
||||
var match = matcher.IsMatch("{ \"Array\" : [ \"42\", \"test\" ] }").Score;
|
||||
|
||||
// Assert
|
||||
Assert.Equal(1.0, match);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SystemTextJsonMatcher_IsMatch_WithRegexTrue_Array_ShouldNotMatch()
|
||||
{
|
||||
// Assign
|
||||
var matcher = new SystemTextJsonMatcher(new
|
||||
{
|
||||
Array = new[] { "^\\d+$", ".*" }
|
||||
}, regex: true);
|
||||
|
||||
// Act
|
||||
var match = matcher.IsMatch("{ \"Array\" : [ \"42\", \"test\", \"other\" ] }").Score;
|
||||
|
||||
// Assert
|
||||
Assert.Equal(MatchScores.Mismatch, match);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SystemTextJsonMatcher_IsMatch_GuidAndString()
|
||||
{
|
||||
// Assign
|
||||
var id = Guid.NewGuid();
|
||||
var idAsString = id.ToString();
|
||||
var matcher = new SystemTextJsonMatcher(new { Id = id });
|
||||
|
||||
// Act
|
||||
var match = matcher.IsMatch($"{{ \"Id\" : \"{idAsString}\" }}").Score;
|
||||
|
||||
// Assert
|
||||
Assert.Equal(1.0, match);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SystemTextJsonMatcher_IsMatch_StringAndGuid()
|
||||
{
|
||||
// Assign
|
||||
var id = Guid.NewGuid();
|
||||
var idAsString = id.ToString();
|
||||
var matcher = new SystemTextJsonMatcher(new { Id = idAsString });
|
||||
|
||||
// Act
|
||||
var match = matcher.IsMatch($"{{ \"Id\" : \"{id}\" }}").Score;
|
||||
|
||||
// Assert
|
||||
Assert.Equal(1.0, match);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SystemTextJsonMatcher_IsMatch_JsonElement_ShouldMatch()
|
||||
{
|
||||
// Assign
|
||||
var matcher = new SystemTextJsonMatcher(new { Id = 1, Name = "Test" });
|
||||
|
||||
// Act
|
||||
var jsonElement = JsonDocument.Parse("{ \"Id\" : 1, \"Name\" : \"Test\" }").RootElement;
|
||||
var match = matcher.IsMatch(jsonElement).Score;
|
||||
|
||||
// Assert
|
||||
Assert.Equal(1.0, match);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SystemTextJsonMatcher_IsMatch_Array_WithIgnoreArrayOrderTrue_DifferentOrder_ShouldMatch()
|
||||
{
|
||||
// Assign
|
||||
var matcher = new SystemTextJsonMatcher(new[] { "a", "b", "c" }, ignoreArrayOrder: true);
|
||||
|
||||
// Act
|
||||
var jsonElement = JsonDocument.Parse("[ \"c\", \"a\", \"b\" ]").RootElement;
|
||||
var score = matcher.IsMatch(jsonElement).Score;
|
||||
|
||||
// Assert
|
||||
Assert.Equal(1.0, score);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SystemTextJsonMatcher_IsMatch_Array_WithIgnoreArrayOrderFalse_DifferentOrder_ShouldNotMatch()
|
||||
{
|
||||
// Assign
|
||||
var matcher = new SystemTextJsonMatcher(new[] { "a", "b", "c" }, ignoreArrayOrder: false);
|
||||
|
||||
// Act
|
||||
var jsonElement = JsonDocument.Parse("[ \"c\", \"a\", \"b\" ]").RootElement;
|
||||
var score = matcher.IsMatch(jsonElement).Score;
|
||||
|
||||
// Assert
|
||||
Assert.Equal(MatchScores.Mismatch, score);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SystemTextJsonMatcher_IsMatch_Array_WithIgnoreArrayOrderTrue_SameOrder_ShouldMatch()
|
||||
{
|
||||
// Assign
|
||||
var matcher = new SystemTextJsonMatcher(new[] { "a", "b", "c" }, ignoreArrayOrder: true);
|
||||
|
||||
// Act
|
||||
var jsonElement = JsonDocument.Parse("[ \"a\", \"b\", \"c\" ]").RootElement;
|
||||
var score = matcher.IsMatch(jsonElement).Score;
|
||||
|
||||
// Assert
|
||||
Assert.Equal(1.0, score);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SystemTextJsonMatcher_IsMatch_Array_WithIgnoreArrayOrderTrue_DifferentLength_ShouldNotMatch()
|
||||
{
|
||||
// Assign
|
||||
var matcher = new SystemTextJsonMatcher(new[] { "a", "b", "c" }, ignoreArrayOrder: true);
|
||||
|
||||
// Act
|
||||
var jsonElement = JsonDocument.Parse("[ \"a\", \"b\" ]").RootElement;
|
||||
var score = matcher.IsMatch(jsonElement).Score;
|
||||
|
||||
// Assert
|
||||
Assert.Equal(MatchScores.Mismatch, score);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SystemTextJsonMatcher_IsMatch_ObjectWithArray_WithIgnoreArrayOrderTrue_DifferentOrder_ShouldMatch()
|
||||
{
|
||||
// Assign
|
||||
var matcher = new SystemTextJsonMatcher(new { Items = new[] { "x", "y", "z" } }, ignoreArrayOrder: true);
|
||||
|
||||
// Act
|
||||
var match = matcher.IsMatch("{ \"Items\" : [ \"z\", \"x\", \"y\" ] }").Score;
|
||||
|
||||
// Assert
|
||||
Assert.Equal(1.0, match);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SystemTextJsonMatcher_IsMatch_ArrayAsString_WithIgnoreArrayOrderTrue_DifferentOrder_ShouldMatch()
|
||||
{
|
||||
// Assign
|
||||
var matcher = new SystemTextJsonMatcher("[ \"a\", \"b\", \"c\" ]", ignoreArrayOrder: true);
|
||||
|
||||
// Act
|
||||
var jsonElement = JsonDocument.Parse("[ \"c\", \"b\", \"a\" ]").RootElement;
|
||||
var score = matcher.IsMatch(jsonElement).Score;
|
||||
|
||||
// Assert
|
||||
Assert.Equal(1.0, score);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SystemTextJsonMatcher_IsMatch_ArrayOfNumbers_WithIgnoreArrayOrderTrue_DifferentOrder_ShouldMatch()
|
||||
{
|
||||
// Assign
|
||||
var matcher = new SystemTextJsonMatcher(new[] { 1, 2, 3 }, ignoreArrayOrder: true);
|
||||
|
||||
// Act
|
||||
var jsonElement = JsonDocument.Parse("[ 3, 1, 2 ]").RootElement;
|
||||
var score = matcher.IsMatch(jsonElement).Score;
|
||||
|
||||
// Assert
|
||||
Assert.Equal(1.0, score);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(MatchBehaviour.AcceptOnMatch, false, false, false)]
|
||||
[InlineData(MatchBehaviour.AcceptOnMatch, true, false, true)]
|
||||
[InlineData(MatchBehaviour.RejectOnMatch, true, true, false)]
|
||||
public void SystemTextJsonMatcher_GetCSharpCodeArguments_ShouldIncludeAllConstructorArguments(MatchBehaviour matchBehaviour, bool ignoreCase, bool regex, bool ignoreArrayOrder)
|
||||
{
|
||||
// Assign
|
||||
var matcher = new SystemTextJsonMatcher(matchBehaviour, "{ \"id\": 1 }", ignoreCase, regex, ignoreArrayOrder);
|
||||
|
||||
// Act
|
||||
var result = matcher.GetCSharpCodeArguments();
|
||||
|
||||
// Assert
|
||||
result.Should().StartWith($"new SystemTextJsonMatcher(WireMock.Matchers.MatchBehaviour.{matchBehaviour},");
|
||||
result.Should().EndWith($", {ignoreCase.ToString().ToLowerInvariant()}, {regex.ToString().ToLowerInvariant()}, {ignoreArrayOrder.ToString().ToLowerInvariant()})");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,411 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System.Text.Json;
|
||||
using WireMock.Matchers;
|
||||
|
||||
namespace WireMock.Net.Tests.Matchers;
|
||||
|
||||
public class SystemTextJsonPartialMatcherTests
|
||||
{
|
||||
[Fact]
|
||||
public void SystemTextJsonPartialMatcher_GetName()
|
||||
{
|
||||
// Assign
|
||||
var matcher = new SystemTextJsonPartialMatcher("{}");
|
||||
|
||||
// Act
|
||||
string name = matcher.Name;
|
||||
|
||||
// Assert
|
||||
name.Should().Be("SystemTextJsonPartialMatcher");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SystemTextJsonPartialMatcher_GetValue()
|
||||
{
|
||||
// Assign
|
||||
var matcher = new SystemTextJsonPartialMatcher("{}");
|
||||
|
||||
// Act
|
||||
object value = matcher.Value;
|
||||
|
||||
// Assert
|
||||
value.Should().Be("{}");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SystemTextJsonPartialMatcher_WithInvalidStringValue_Should_ThrowException()
|
||||
{
|
||||
// Act
|
||||
Action action = () => new SystemTextJsonPartialMatcher(MatchBehaviour.AcceptOnMatch, "{ \"Id\"");
|
||||
|
||||
// Assert
|
||||
action.Should().Throw<JsonException>();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SystemTextJsonPartialMatcher_WithInvalidObjectValue_Should_ThrowException()
|
||||
{
|
||||
// Act
|
||||
Action action = () => new SystemTextJsonPartialMatcher(MatchBehaviour.AcceptOnMatch, new MemoryStream());
|
||||
|
||||
// Assert
|
||||
action.Should().Throw<Exception>();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SystemTextJsonPartialMatcher_IsMatch_WithInvalidValue_Should_ReturnMismatch_And_Exception_ShouldBeSet()
|
||||
{
|
||||
// Assign
|
||||
var matcher = new SystemTextJsonPartialMatcher("{}");
|
||||
using var stream = new MemoryStream();
|
||||
|
||||
// Act
|
||||
var result = matcher.IsMatch(stream);
|
||||
|
||||
// Assert
|
||||
result.Score.Should().Be(MatchScores.Mismatch);
|
||||
result.Exception.Should().NotBeNull();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SystemTextJsonPartialMatcher_IsMatch_ByteArray()
|
||||
{
|
||||
// Assign
|
||||
var bytes = new byte[0];
|
||||
var matcher = new SystemTextJsonPartialMatcher("{}");
|
||||
|
||||
// Act
|
||||
double match = matcher.IsMatch(bytes).Score;
|
||||
|
||||
// Assert
|
||||
match.Should().Be(0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SystemTextJsonPartialMatcher_IsMatch_NullString()
|
||||
{
|
||||
// Assign
|
||||
string? s = null;
|
||||
var matcher = new SystemTextJsonPartialMatcher("{}");
|
||||
|
||||
// Act
|
||||
double match = matcher.IsMatch(s).Score;
|
||||
|
||||
// Assert
|
||||
match.Should().Be(0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SystemTextJsonPartialMatcher_IsMatch_NullObject()
|
||||
{
|
||||
// Assign
|
||||
object? o = null;
|
||||
var matcher = new SystemTextJsonPartialMatcher("{}");
|
||||
|
||||
// Act
|
||||
double match = matcher.IsMatch(o).Score;
|
||||
|
||||
// Assert
|
||||
match.Should().Be(0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SystemTextJsonPartialMatcher_IsMatch_JsonArray()
|
||||
{
|
||||
// Assign
|
||||
var matcher = new SystemTextJsonPartialMatcher(new[] { "x", "y" });
|
||||
|
||||
// Act
|
||||
double match = matcher.IsMatch("[ \"x\", \"y\" ]").Score;
|
||||
|
||||
// Assert
|
||||
Assert.Equal(1.0, match);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SystemTextJsonPartialMatcher_IsMatch_JsonObject()
|
||||
{
|
||||
// Assign
|
||||
var matcher = new SystemTextJsonPartialMatcher(new { Id = 1, Name = "Test" });
|
||||
|
||||
// Act
|
||||
double match = matcher.IsMatch("{ \"Id\" : 1, \"Name\" : \"Test\" }").Score;
|
||||
|
||||
// Assert
|
||||
Assert.Equal(1.0, match);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SystemTextJsonPartialMatcher_IsMatch_WithRegexTrue()
|
||||
{
|
||||
// Assign
|
||||
var matcher = new SystemTextJsonPartialMatcher(new { Id = "^\\d+$", Name = "Test" }, false, true);
|
||||
|
||||
// Act
|
||||
double match = matcher.IsMatch("{ \"Id\" : \"1\", \"Name\" : \"Test\" }").Score;
|
||||
|
||||
// Assert
|
||||
Assert.Equal(1.0, match);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SystemTextJsonPartialMatcher_IsMatch_WithRegexFalse()
|
||||
{
|
||||
// Assign
|
||||
var matcher = new SystemTextJsonPartialMatcher(new { Id = "^\\d+$", Name = "Test" });
|
||||
|
||||
// Act
|
||||
double match = matcher.IsMatch("{ \"Id\" : 1, \"Name\" : \"Test\" }").Score;
|
||||
|
||||
// Assert
|
||||
Assert.Equal(0.0, match);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SystemTextJsonPartialMatcher_IsMatch_GuidAsString_UsingRegex()
|
||||
{
|
||||
var guid = new Guid("1111238e-b775-44a9-a263-95e570135c94");
|
||||
var matcher = new SystemTextJsonPartialMatcher(new
|
||||
{
|
||||
Id = 1,
|
||||
Name = "^1111[a-fA-F0-9]{4}(-[a-fA-F0-9]{4}){3}-[a-fA-F0-9]{12}$"
|
||||
}, false, true);
|
||||
|
||||
// Act
|
||||
double match = matcher.IsMatch($"{{ \"Id\" : 1, \"Name\" : \"{guid}\" }}").Score;
|
||||
|
||||
// Assert
|
||||
Assert.Equal(1.0, match);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SystemTextJsonPartialMatcher_IsMatch_WithIgnoreCaseTrue_JsonObject()
|
||||
{
|
||||
// Assign
|
||||
var matcher = new SystemTextJsonPartialMatcher(new { id = 1, Name = "test" }, true);
|
||||
|
||||
// Act
|
||||
double match = matcher.IsMatch("{ \"Id\" : 1, \"NaMe\" : \"Test\" }").Score;
|
||||
|
||||
// Assert
|
||||
Assert.Equal(1.0, match);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SystemTextJsonPartialMatcher_IsMatch_JsonObjectParsed()
|
||||
{
|
||||
// Assign
|
||||
var matcher = new SystemTextJsonPartialMatcher(new { Id = 1, Name = "Test" });
|
||||
|
||||
// Act
|
||||
double match = matcher.IsMatch("{ \"Id\" : 1, \"Name\" : \"Test\" }").Score;
|
||||
|
||||
// Assert
|
||||
Assert.Equal(1.0, match);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SystemTextJsonPartialMatcher_IsMatch_WithIgnoreCaseTrue_JsonObjectParsed()
|
||||
{
|
||||
// Assign
|
||||
var matcher = new SystemTextJsonPartialMatcher(new { Id = 1, Name = "TESt" }, true);
|
||||
|
||||
// Act
|
||||
double match = matcher.IsMatch("{ \"Id\" : 1, \"Name\" : \"Test\" }").Score;
|
||||
|
||||
// Assert
|
||||
Assert.Equal(1.0, match);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SystemTextJsonPartialMatcher_IsMatch_JsonArrayAsString()
|
||||
{
|
||||
// Assign
|
||||
var matcher = new SystemTextJsonPartialMatcher("[ \"x\", \"y\" ]");
|
||||
|
||||
// Act
|
||||
double match = matcher.IsMatch("[ \"x\", \"y\" ]").Score;
|
||||
|
||||
// Assert
|
||||
Assert.Equal(1.0, match);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SystemTextJsonPartialMatcher_IsMatch_JsonObjectAsString()
|
||||
{
|
||||
// Assign
|
||||
var matcher = new SystemTextJsonPartialMatcher("{ \"Id\" : 1, \"Name\" : \"Test\" }");
|
||||
|
||||
// Act
|
||||
double match = matcher.IsMatch("{ \"Id\" : 1, \"Name\" : \"Test\" }").Score;
|
||||
|
||||
// Assert
|
||||
Assert.Equal(1.0, match);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SystemTextJsonPartialMatcher_IsMatch_JsonObjectAsStringWithDottedPropertyName()
|
||||
{
|
||||
// Assign
|
||||
var matcher = new SystemTextJsonPartialMatcher("{ \"urn:ietf:params:scim:schemas:extension:enterprise:2.0:User\" : \"Test\" }");
|
||||
|
||||
// Act
|
||||
double match = matcher.IsMatch("{ \"urn:ietf:params:scim:schemas:extension:enterprise:2.0:User\" : \"Test\" }").Score;
|
||||
|
||||
// Assert
|
||||
Assert.Equal(1.0, match);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SystemTextJsonPartialMatcher_IsMatch_GuidAsString()
|
||||
{
|
||||
// Assign
|
||||
var guid = Guid.NewGuid();
|
||||
var matcher = new SystemTextJsonPartialMatcher(new { Id = 1, Name = guid });
|
||||
|
||||
// Act
|
||||
double match = matcher.IsMatch($"{{ \"Id\" : 1, \"Name\" : \"{guid}\" }}").Score;
|
||||
|
||||
// Assert
|
||||
Assert.Equal(1.0, match);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SystemTextJsonPartialMatcher_IsMatch_WithIgnoreCaseTrue_JsonObjectAsString()
|
||||
{
|
||||
// Assign
|
||||
var matcher = new SystemTextJsonPartialMatcher("{ \"Id\" : 1, \"Name\" : \"test\" }", true);
|
||||
|
||||
// Act
|
||||
double match = matcher.IsMatch("{ \"Id\" : 1, \"Name\" : \"Test\" }").Score;
|
||||
|
||||
// Assert
|
||||
Assert.Equal(1.0, match);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SystemTextJsonPartialMatcher_IsMatch_JsonObjectAsString_RejectOnMatch()
|
||||
{
|
||||
// Assign
|
||||
var matcher = new SystemTextJsonPartialMatcher(MatchBehaviour.RejectOnMatch, "{ \"Id\" : 1, \"Name\" : \"Test\" }");
|
||||
|
||||
// Act
|
||||
double match = matcher.IsMatch("{ \"Id\" : 1, \"Name\" : \"Test\" }").Score;
|
||||
|
||||
// Assert
|
||||
Assert.Equal(0.0, match);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SystemTextJsonPartialMatcher_IsMatch_JsonObjectWithDateTimeOffsetAsString()
|
||||
{
|
||||
// Assign
|
||||
var matcher = new SystemTextJsonPartialMatcher("{ \"preferredAt\" : \"2019-11-21T10:32:53.2210009+00:00\" }");
|
||||
|
||||
// Act
|
||||
double match = matcher.IsMatch("{ \"preferredAt\" : \"2019-11-21T10:32:53.2210009+00:00\" }").Score;
|
||||
|
||||
// Assert
|
||||
Assert.Equal(1.0, match);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("{\"test\":\"abc\"}", "{\"test\":\"abc\",\"other\":\"xyz\"}")]
|
||||
[InlineData("\"test\"", "\"test\"")]
|
||||
[InlineData("123", "123")]
|
||||
[InlineData("[\"test\"]", "[\"test\"]")]
|
||||
[InlineData("[\"test\"]", "[\"test\", \"other\"]")]
|
||||
[InlineData("[123]", "[123]")]
|
||||
[InlineData("[123]", "[123, 456]")]
|
||||
[InlineData("{ \"test\":\"value\" }", "{\"test\":\"value\",\"other\":123}")]
|
||||
[InlineData("{ \"test\":\"value\" }", "{\"test\":\"value\"}")]
|
||||
[InlineData("{\"test\":{\"nested\":\"value\"}}", "{\"test\":{\"nested\":\"value\"}}")]
|
||||
public void SystemTextJsonPartialMatcher_IsMatch_StringInputValidMatch(string value, string input)
|
||||
{
|
||||
// Assign
|
||||
var matcher = new SystemTextJsonPartialMatcher(value);
|
||||
|
||||
// Act
|
||||
double match = matcher.IsMatch(input).Score;
|
||||
|
||||
// Assert
|
||||
Assert.Equal(1.0, match);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("\"test\"", null)]
|
||||
[InlineData("\"test1\"", "\"test2\"")]
|
||||
[InlineData("123", "1234")]
|
||||
[InlineData("[\"test\"]", "[\"test1\"]")]
|
||||
[InlineData("[\"test\"]", "[\"test1\", \"test2\"]")]
|
||||
[InlineData("[123]", "[1234]")]
|
||||
[InlineData("{}", "\"test\"")]
|
||||
[InlineData("{ \"test\":\"value\" }", "{\"test\":\"value2\"}")]
|
||||
[InlineData("{ \"test.nested\":\"value\" }", "{\"test\":{\"nested\":\"value1\"}}")]
|
||||
[InlineData("{\"test\":{\"test1\":\"value\"}}", "{\"test\":{\"test1\":\"value1\"}}")]
|
||||
[InlineData("[{ \"test.nested\":\"value\" }]", "[{\"test\":{\"nested\":\"value1\"}}]")]
|
||||
public void SystemTextJsonPartialMatcher_IsMatch_StringInputWithInvalidMatch(string value, string? input)
|
||||
{
|
||||
// Assign
|
||||
var matcher = new SystemTextJsonPartialMatcher(value);
|
||||
|
||||
// Act
|
||||
double match = matcher.IsMatch(input).Score;
|
||||
|
||||
// Assert
|
||||
Assert.Equal(0.0, match);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("{ \"test.nested\":123 }", "{\"test\":{\"nested\":123}}")]
|
||||
[InlineData("{ \"test.nested\":[123, 456] }", "{\"test\":{\"nested\":[123, 456]}}")]
|
||||
[InlineData("{ \"test.nested\":\"value\" }", "{\"test\":{\"nested\":\"value\"}}")]
|
||||
[InlineData("{ \"['name.with.dot']\":\"value\" }", "{\"name.with.dot\":\"value\"}")]
|
||||
[InlineData("[{ \"test.nested\":\"value\" }]", "[{\"test\":{\"nested\":\"value\"}}]")]
|
||||
[InlineData("[{ \"['name.with.dot']\":\"value\" }]", "[{\"name.with.dot\":\"value\"}]")]
|
||||
public void SystemTextJsonPartialMatcher_IsMatch_ValueAsPathValidMatch(string value, string input)
|
||||
{
|
||||
// Assign
|
||||
var matcher = new SystemTextJsonPartialMatcher(value);
|
||||
|
||||
// Act
|
||||
double match = matcher.IsMatch(input).Score;
|
||||
|
||||
// Assert
|
||||
Assert.Equal(1.0, match);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("{ \"test.nested\":123 }", "{\"test\":{\"nested\":456}}")]
|
||||
[InlineData("{ \"test.nested\":[123, 456] }", "{\"test\":{\"nested\":[1, 2]}}")]
|
||||
[InlineData("{ \"test.nested\":\"value\" }", "{\"test\":{\"nested\":\"value1\"}}")]
|
||||
[InlineData("{ \"['name.with.dot']\":\"value\" }", "{\"name.with.dot\":\"value1\"}")]
|
||||
[InlineData("[{ \"test.nested\":\"value\" }]", "[{\"test\":{\"nested\":\"value1\"}}]")]
|
||||
[InlineData("[{ \"['name.with.dot']\":\"value\" }]", "[{\"name.with.dot\":\"value1\"}]")]
|
||||
public void SystemTextJsonPartialMatcher_IsMatch_ValueAsPathInvalidMatch(string value, string input)
|
||||
{
|
||||
// Assign
|
||||
var matcher = new SystemTextJsonPartialMatcher(value);
|
||||
|
||||
// Act
|
||||
double match = matcher.IsMatch(input).Score;
|
||||
|
||||
// Assert
|
||||
Assert.Equal(0.0, match);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SystemTextJsonPartialMatcher_IsMatch_JsonElement_ShouldMatch()
|
||||
{
|
||||
// Assign
|
||||
var matcher = new SystemTextJsonPartialMatcher(new { Id = 1, Name = "Test" });
|
||||
|
||||
// Act
|
||||
var jsonElement = JsonDocument.Parse("{ \"Id\" : 1, \"Name\" : \"Test\", \"Extra\" : \"value\" }").RootElement;
|
||||
double match = matcher.IsMatch(jsonElement).Score;
|
||||
|
||||
// Assert
|
||||
Assert.Equal(1.0, match);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,382 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System.Text.Json;
|
||||
using WireMock.Matchers;
|
||||
|
||||
namespace WireMock.Net.Tests.Matchers;
|
||||
|
||||
public class SystemTextJsonPartialWildcardMatcherTests
|
||||
{
|
||||
[Fact]
|
||||
public void SystemTextJsonPartialWildcardMatcher_GetName()
|
||||
{
|
||||
// Assign
|
||||
var matcher = new SystemTextJsonPartialWildcardMatcher("{}");
|
||||
|
||||
// Act
|
||||
var name = matcher.Name;
|
||||
|
||||
// Assert
|
||||
name.Should().Be("SystemTextJsonPartialWildcardMatcher");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SystemTextJsonPartialWildcardMatcher_GetValue()
|
||||
{
|
||||
// Assign
|
||||
var matcher = new SystemTextJsonPartialWildcardMatcher("{}");
|
||||
|
||||
// Act
|
||||
var value = matcher.Value;
|
||||
|
||||
// Assert
|
||||
value.Should().Be("{}");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SystemTextJsonPartialWildcardMatcher_WithInvalidStringValue_Should_ThrowException()
|
||||
{
|
||||
// Act
|
||||
Action action = () => new SystemTextJsonPartialWildcardMatcher(MatchBehaviour.AcceptOnMatch, "{ \"Id\"");
|
||||
|
||||
// Assert
|
||||
action.Should().Throw<JsonException>();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SystemTextJsonPartialWildcardMatcher_WithInvalidObjectValue_Should_ThrowException()
|
||||
{
|
||||
// Act
|
||||
Action action = () => new SystemTextJsonPartialWildcardMatcher(MatchBehaviour.AcceptOnMatch, new MemoryStream());
|
||||
|
||||
// Assert
|
||||
action.Should().Throw<Exception>();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SystemTextJsonPartialWildcardMatcher_IsMatch_WithInvalidValue_Should_ReturnMismatch_And_Exception_ShouldBeSet()
|
||||
{
|
||||
// Assign
|
||||
var matcher = new SystemTextJsonPartialWildcardMatcher("{}");
|
||||
|
||||
// Act
|
||||
using var stream = new MemoryStream();
|
||||
var result = matcher.IsMatch(stream);
|
||||
|
||||
// Assert
|
||||
result.Score.Should().Be(MatchScores.Mismatch);
|
||||
result.Exception.Should().NotBeNull();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SystemTextJsonPartialWildcardMatcher_IsMatch_ByteArray()
|
||||
{
|
||||
// Assign
|
||||
var bytes = new byte[0];
|
||||
var matcher = new SystemTextJsonPartialWildcardMatcher("{}");
|
||||
|
||||
// Act
|
||||
var match = matcher.IsMatch(bytes).Score;
|
||||
|
||||
// Assert
|
||||
match.Should().Be(0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SystemTextJsonPartialWildcardMatcher_IsMatch_NullString()
|
||||
{
|
||||
// Assign
|
||||
string? s = null;
|
||||
var matcher = new SystemTextJsonPartialWildcardMatcher("{}");
|
||||
|
||||
// Act
|
||||
var match = matcher.IsMatch(s).Score;
|
||||
|
||||
// Assert
|
||||
match.Should().Be(0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SystemTextJsonPartialWildcardMatcher_IsMatch_NullObject()
|
||||
{
|
||||
// Assign
|
||||
object? o = null;
|
||||
var matcher = new SystemTextJsonPartialWildcardMatcher("{}");
|
||||
|
||||
// Act
|
||||
var match = matcher.IsMatch(o).Score;
|
||||
|
||||
// Assert
|
||||
match.Should().Be(0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SystemTextJsonPartialWildcardMatcher_IsMatch_JsonArray()
|
||||
{
|
||||
// Assign
|
||||
var matcher = new SystemTextJsonPartialWildcardMatcher(new[] { "x", "y" });
|
||||
|
||||
// Act
|
||||
var match = matcher.IsMatch("[ \"x\", \"y\" ]").Score;
|
||||
|
||||
// Assert
|
||||
Assert.Equal(1.0, match);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SystemTextJsonPartialWildcardMatcher_IsMatch_JsonObject()
|
||||
{
|
||||
// Assign
|
||||
var matcher = new SystemTextJsonPartialWildcardMatcher(new { Id = 1, Name = "Test" });
|
||||
|
||||
// Act
|
||||
var match = matcher.IsMatch("{ \"Id\" : 1, \"Name\" : \"Test\" }").Score;
|
||||
|
||||
// Assert
|
||||
Assert.Equal(1.0, match);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SystemTextJsonPartialWildcardMatcher_IsMatch_WithIgnoreCaseTrue_JsonObject()
|
||||
{
|
||||
// Assign
|
||||
var matcher = new SystemTextJsonPartialWildcardMatcher(new { id = 1, Name = "test" }, true);
|
||||
|
||||
// Act
|
||||
var match = matcher.IsMatch("{ \"Id\" : 1, \"NaMe\" : \"Test\" }").Score;
|
||||
|
||||
// Assert
|
||||
Assert.Equal(1.0, match);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SystemTextJsonPartialWildcardMatcher_IsMatch_JsonObjectParsed()
|
||||
{
|
||||
// Assign
|
||||
var matcher = new SystemTextJsonPartialWildcardMatcher(new { Id = 1, Name = "Test" });
|
||||
|
||||
// Act
|
||||
var match = matcher.IsMatch("{ \"Id\" : 1, \"Name\" : \"Test\" }").Score;
|
||||
|
||||
// Assert
|
||||
Assert.Equal(1.0, match);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SystemTextJsonPartialWildcardMatcher_IsMatch_WithIgnoreCaseTrue_JsonObjectParsed()
|
||||
{
|
||||
// Assign
|
||||
var matcher = new SystemTextJsonPartialWildcardMatcher(new { Id = 1, Name = "TESt" }, true);
|
||||
|
||||
// Act
|
||||
var match = matcher.IsMatch("{ \"Id\" : 1, \"Name\" : \"Test\" }").Score;
|
||||
|
||||
// Assert
|
||||
Assert.Equal(1.0, match);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SystemTextJsonPartialWildcardMatcher_IsMatch_JsonArrayAsString()
|
||||
{
|
||||
// Assign
|
||||
var matcher = new SystemTextJsonPartialWildcardMatcher("[ \"x\", \"y\" ]");
|
||||
|
||||
// Act
|
||||
var match = matcher.IsMatch("[ \"x\", \"y\" ]").Score;
|
||||
|
||||
// Assert
|
||||
Assert.Equal(1.0, match);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SystemTextJsonPartialWildcardMatcher_IsMatch_JsonObjectAsString()
|
||||
{
|
||||
// Assign
|
||||
var matcher = new SystemTextJsonPartialWildcardMatcher("{ \"Id\" : 1, \"Name\" : \"Test\" }");
|
||||
|
||||
// Act
|
||||
var match = matcher.IsMatch("{ \"Id\" : 1, \"Name\" : \"Test\" }").Score;
|
||||
|
||||
// Assert
|
||||
Assert.Equal(1.0, match);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SystemTextJsonPartialWildcardMatcher_IsMatch_WithIgnoreCaseTrue_JsonObjectAsString()
|
||||
{
|
||||
// Assign
|
||||
var matcher = new SystemTextJsonPartialWildcardMatcher("{ \"Id\" : 1, \"Name\" : \"test\" }", true);
|
||||
|
||||
// Act
|
||||
var match = matcher.IsMatch("{ \"Id\" : 1, \"Name\" : \"Test\" }").Score;
|
||||
|
||||
// Assert
|
||||
Assert.Equal(1.0, match);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SystemTextJsonPartialWildcardMatcher_IsMatch_JsonObjectAsString_RejectOnMatch()
|
||||
{
|
||||
// Assign
|
||||
var matcher = new SystemTextJsonPartialWildcardMatcher(MatchBehaviour.RejectOnMatch, "{ \"Id\" : 1, \"Name\" : \"Test\" }");
|
||||
|
||||
// Act
|
||||
var match = matcher.IsMatch("{ \"Id\" : 1, \"Name\" : \"Test\" }").Score;
|
||||
|
||||
// Assert
|
||||
Assert.Equal(0.0, match);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SystemTextJsonPartialWildcardMatcher_IsMatch_JsonObjectWithDateTimeOffsetAsString()
|
||||
{
|
||||
// Assign
|
||||
var matcher = new SystemTextJsonPartialWildcardMatcher("{ \"preferredAt\" : \"2019-11-21T10:32:53.2210009+00:00\" }");
|
||||
|
||||
// Act
|
||||
var match = matcher.IsMatch("{ \"preferredAt\" : \"2019-11-21T10:32:53.2210009+00:00\" }").Score;
|
||||
|
||||
// Assert
|
||||
Assert.Equal(1.0, match);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("{\"test\":\"abc\"}", "{\"test\":\"abc\",\"other\":\"xyz\"}")]
|
||||
[InlineData("\"test\"", "\"test\"")]
|
||||
[InlineData("123", "123")]
|
||||
[InlineData("[\"test\"]", "[\"test\"]")]
|
||||
[InlineData("[\"test\"]", "[\"test\", \"other\"]")]
|
||||
[InlineData("[123]", "[123]")]
|
||||
[InlineData("[123]", "[123, 456]")]
|
||||
[InlineData("{ \"test\":\"value\" }", "{\"test\":\"value\",\"other\":123}")]
|
||||
[InlineData("{ \"test\":\"value\" }", "{\"test\":\"value\"}")]
|
||||
[InlineData("{\"test\":{\"nested\":\"value\"}}", "{\"test\":{\"nested\":\"value\"}}")]
|
||||
public void SystemTextJsonPartialWildcardMatcher_IsMatch_StringInput_IsValidMatch(string value, string input)
|
||||
{
|
||||
// Assign
|
||||
var matcher = new SystemTextJsonPartialWildcardMatcher(value);
|
||||
|
||||
// Act
|
||||
var match = matcher.IsMatch(input).Score;
|
||||
|
||||
// Assert
|
||||
Assert.Equal(1.0, match);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("{\"test\":\"*\"}", "{\"test\":\"xxx\",\"other\":\"xyz\"}")]
|
||||
[InlineData("\"t*t\"", "\"test\"")]
|
||||
public void SystemTextJsonPartialWildcardMatcher_IsMatch_StringInputWithWildcard_IsValidMatch(string value, string input)
|
||||
{
|
||||
// Assign
|
||||
var matcher = new SystemTextJsonPartialWildcardMatcher(value);
|
||||
|
||||
// Act
|
||||
var match = matcher.IsMatch(input).Score;
|
||||
|
||||
// Assert
|
||||
match.Should().Be(1.0);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("\"test\"", null)]
|
||||
[InlineData("\"test1\"", "\"test2\"")]
|
||||
[InlineData("123", "1234")]
|
||||
[InlineData("[\"test\"]", "[\"test1\"]")]
|
||||
[InlineData("[\"test\"]", "[\"test1\", \"test2\"]")]
|
||||
[InlineData("[123]", "[1234]")]
|
||||
[InlineData("{}", "\"test\"")]
|
||||
[InlineData("{ \"test\":\"value\" }", "{\"test\":\"value2\"}")]
|
||||
[InlineData("{ \"test.nested\":\"value\" }", "{\"test\":{\"nested\":\"value1\"}}")]
|
||||
[InlineData("{\"test\":{\"test1\":\"value\"}}", "{\"test\":{\"test1\":\"value1\"}}")]
|
||||
[InlineData("[{ \"test.nested\":\"value\" }]", "[{\"test\":{\"nested\":\"value1\"}}]")]
|
||||
public void SystemTextJsonPartialWildcardMatcher_IsMatch_StringInputWithInvalidMatch(string value, string? input)
|
||||
{
|
||||
// Assign
|
||||
var matcher = new SystemTextJsonPartialWildcardMatcher(value);
|
||||
|
||||
// Act
|
||||
var match = matcher.IsMatch(input).Score;
|
||||
|
||||
// Assert
|
||||
Assert.Equal(0.0, match);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("{ \"test.nested\":123 }", "{\"test\":{\"nested\":123}}")]
|
||||
[InlineData("{ \"test.nested\":[123, 456] }", "{\"test\":{\"nested\":[123, 456]}}")]
|
||||
[InlineData("{ \"test.nested\":\"value\" }", "{\"test\":{\"nested\":\"value\"}}")]
|
||||
[InlineData("{ \"['name.with.dot']\":\"value\" }", "{\"name.with.dot\":\"value\"}")]
|
||||
[InlineData("[{ \"test.nested\":\"value\" }]", "[{\"test\":{\"nested\":\"value\"}}]")]
|
||||
[InlineData("[{ \"['name.with.dot']\":\"value\" }]", "[{\"name.with.dot\":\"value\"}]")]
|
||||
public void SystemTextJsonPartialWildcardMatcher_IsMatch_ValueAsPathValidMatch(string value, string input)
|
||||
{
|
||||
// Assign
|
||||
var matcher = new SystemTextJsonPartialWildcardMatcher(value);
|
||||
|
||||
// Act
|
||||
var match = matcher.IsMatch(input).Score;
|
||||
|
||||
// Assert
|
||||
Assert.Equal(1.0, match);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("{ \"test.nested\":123 }", "{\"test\":{\"nested\":456}}")]
|
||||
[InlineData("{ \"test.nested\":[123, 456] }", "{\"test\":{\"nested\":[1, 2]}}")]
|
||||
[InlineData("{ \"test.nested\":\"value\" }", "{\"test\":{\"nested\":\"value1\"}}")]
|
||||
[InlineData("{ \"['name.with.dot']\":\"value\" }", "{\"name.with.dot\":\"value1\"}")]
|
||||
[InlineData("[{ \"test.nested\":\"value\" }]", "[{\"test\":{\"nested\":\"value1\"}}]")]
|
||||
[InlineData("[{ \"['name.with.dot']\":\"value\" }]", "[{\"name.with.dot\":\"value1\"}]")]
|
||||
public void SystemTextJsonPartialWildcardMatcher_IsMatch_ValueAsPathInvalidMatch(string value, string input)
|
||||
{
|
||||
// Assign
|
||||
var matcher = new SystemTextJsonPartialWildcardMatcher(value);
|
||||
|
||||
// Act
|
||||
var match = matcher.IsMatch(input).Score;
|
||||
|
||||
// Assert
|
||||
Assert.Equal(0.0, match);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SystemTextJsonPartialWildcardMatcher_IsMatch_WithIgnoreCaseTrueAndRegexTrue_JsonObject1()
|
||||
{
|
||||
// Assign
|
||||
var matcher = new SystemTextJsonPartialWildcardMatcher(new { id = 1, Number = "^\\d+$" }, ignoreCase: true, regex: true);
|
||||
|
||||
// Act
|
||||
var match = matcher.IsMatch("{ \"Id\" : 1, \"Number\" : \"42\" }").Score;
|
||||
|
||||
// Assert
|
||||
Assert.Equal(1.0, match);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SystemTextJsonPartialWildcardMatcher_IsMatch_WithIgnoreCaseTrueAndRegexTrue_JsonObject2()
|
||||
{
|
||||
// Assign
|
||||
var matcher = new SystemTextJsonPartialWildcardMatcher(new { method = "initialize", id = "^[a-f0-9]{32}-[0-9]$" }, ignoreCase: true, regex: true);
|
||||
|
||||
// Act
|
||||
var match = matcher.IsMatch("{\"jsonrpc\":\"2.0\",\"id\":\"ec475f56d4694b48bc737500ba575b35-1\",\"method\":\"initialize\",\"params\":{\"protocolVersion\":\"2024-11-05\",\"capabilities\":{},\"clientInfo\":{\"name\":\"GitHub Test\",\"version\":\"1.0.0\"}}}").Score;
|
||||
|
||||
// Assert
|
||||
Assert.Equal(1.0, match);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SystemTextJsonPartialWildcardMatcher_IsMatch_JsonElement_ShouldMatch()
|
||||
{
|
||||
// Assign
|
||||
var matcher = new SystemTextJsonPartialWildcardMatcher(new { Id = 1, Name = "Test" });
|
||||
|
||||
// Act
|
||||
var jsonElement = JsonDocument.Parse("{ \"Id\" : 1, \"Name\" : \"Test\", \"Extra\" : \"value\" }").RootElement;
|
||||
var match = matcher.IsMatch(jsonElement).Score;
|
||||
|
||||
// Assert
|
||||
Assert.Equal(1.0, match);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,400 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System.Text.Json.Nodes;
|
||||
using WireMock.Matchers;
|
||||
|
||||
namespace WireMock.Net.Tests.Matchers;
|
||||
|
||||
public class SystemTextJsonPathMatcherTests
|
||||
{
|
||||
[Fact]
|
||||
public void SystemTextJsonPathMatcher_GetName()
|
||||
{
|
||||
// Arrange
|
||||
var matcher = new SystemTextJsonPathMatcher("X");
|
||||
|
||||
// Act
|
||||
string name = matcher.Name;
|
||||
|
||||
// Assert
|
||||
name.Should().Be("SystemTextJsonPathMatcher");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SystemTextJsonPathMatcher_GetPatterns()
|
||||
{
|
||||
// Arrange
|
||||
var matcher = new SystemTextJsonPathMatcher("X");
|
||||
|
||||
// Act
|
||||
var patterns = matcher.GetPatterns();
|
||||
|
||||
// Assert
|
||||
patterns.Should().ContainSingle("X");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SystemTextJsonPathMatcher_IsMatch_ByteArray()
|
||||
{
|
||||
// Arrange
|
||||
var bytes = new byte[0];
|
||||
var matcher = new SystemTextJsonPathMatcher("$.Id");
|
||||
|
||||
// Act
|
||||
double match = matcher.IsMatch(bytes).Score;
|
||||
|
||||
// Assert
|
||||
match.Should().Be(0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SystemTextJsonPathMatcher_IsMatch_NullString()
|
||||
{
|
||||
// Arrange
|
||||
var matcher = new SystemTextJsonPathMatcher("$.Id");
|
||||
|
||||
// Act
|
||||
double match = matcher.IsMatch(null).Score;
|
||||
|
||||
// Assert
|
||||
match.Should().Be(0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SystemTextJsonPathMatcher_IsMatch_EmptyString()
|
||||
{
|
||||
// Arrange
|
||||
var matcher = new SystemTextJsonPathMatcher("$.Id");
|
||||
|
||||
// Act
|
||||
double match = matcher.IsMatch(string.Empty).Score;
|
||||
|
||||
// Assert
|
||||
match.Should().Be(0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SystemTextJsonPathMatcher_IsMatch_NullObject()
|
||||
{
|
||||
// Arrange
|
||||
object? o = null;
|
||||
var matcher = new SystemTextJsonPathMatcher("$.Id");
|
||||
|
||||
// Act
|
||||
double match = matcher.IsMatch(o).Score;
|
||||
|
||||
// Assert
|
||||
match.Should().Be(0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SystemTextJsonPathMatcher_IsMatch_String_Exception_Mismatch()
|
||||
{
|
||||
// Arrange
|
||||
var matcher = new SystemTextJsonPathMatcher("$.Id");
|
||||
|
||||
// Act
|
||||
double match = matcher.IsMatch("not-json").Score;
|
||||
|
||||
// Assert
|
||||
match.Should().Be(0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SystemTextJsonPathMatcher_IsMatch_AnonymousObject()
|
||||
{
|
||||
// Arrange - RFC 9535: filter expression requires an array context
|
||||
var matcher = new SystemTextJsonPathMatcher("$[?(@.Id == 1)]");
|
||||
|
||||
// Act
|
||||
double match = matcher.IsMatch(new[] { new { Id = 1, Name = "Test" } }).Score;
|
||||
|
||||
// Assert
|
||||
match.Should().Be(1);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SystemTextJsonPathMatcher_IsMatch_AnonymousObject_WithNestedObject()
|
||||
{
|
||||
// Arrange
|
||||
var matcher = new SystemTextJsonPathMatcher("$.things[?(@.name == 'x')]");
|
||||
|
||||
// Act
|
||||
double match = matcher.IsMatch(new { things = new { name = "x" } }).Score;
|
||||
|
||||
// Assert
|
||||
match.Should().Be(1);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SystemTextJsonPathMatcher_IsMatch_String_WithNestedObject()
|
||||
{
|
||||
// Arrange
|
||||
var json = "{ \"things\": { \"name\": \"x\" } }";
|
||||
var matcher = new SystemTextJsonPathMatcher("$.things[?(@.name == 'x')]");
|
||||
|
||||
// Act
|
||||
double match = matcher.IsMatch(json).Score;
|
||||
|
||||
// Assert
|
||||
match.Should().Be(1);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SystemTextJsonPathMatcher_IsNoMatch_String_WithNestedObject()
|
||||
{
|
||||
// Arrange
|
||||
var json = "{ \"things\": { \"name\": \"y\" } }";
|
||||
var matcher = new SystemTextJsonPathMatcher("$.things[?(@.name == 'x')]");
|
||||
|
||||
// Act
|
||||
double match = matcher.IsMatch(json).Score;
|
||||
|
||||
// Assert
|
||||
match.Should().Be(0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SystemTextJsonPathMatcher_IsMatch_JsonNode()
|
||||
{
|
||||
// Arrange - RFC 9535: filter expression requires an array context
|
||||
string[] patterns = { "$[?(@.Id == 1)]" };
|
||||
var matcher = new SystemTextJsonPathMatcher(patterns);
|
||||
|
||||
// Act
|
||||
var node = JsonNode.Parse("[{\"Id\":1,\"Name\":\"Test\"}]");
|
||||
double match = matcher.IsMatch(node).Score;
|
||||
|
||||
// Assert
|
||||
match.Should().Be(1);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SystemTextJsonPathMatcher_IsMatch_JsonNode_Parsed()
|
||||
{
|
||||
// Arrange - RFC 9535: filter expression requires an array context
|
||||
var matcher = new SystemTextJsonPathMatcher("$[?(@.Id == 1)]");
|
||||
|
||||
// Act
|
||||
double match = matcher.IsMatch(JsonNode.Parse("[{\"Id\":1,\"Name\":\"Test\"}]")).Score;
|
||||
|
||||
// Assert
|
||||
match.Should().Be(1);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SystemTextJsonPathMatcher_IsMatch_RejectOnMatch()
|
||||
{
|
||||
// Arrange - RFC 9535: filter expression requires an array context
|
||||
var matcher = new SystemTextJsonPathMatcher(MatchBehaviour.RejectOnMatch, MatchOperator.Or, "$[?(@.Id == 1)]");
|
||||
|
||||
// Act
|
||||
double match = matcher.IsMatch(JsonNode.Parse("[{\"Id\":1,\"Name\":\"Test\"}]")).Score;
|
||||
|
||||
// Assert
|
||||
match.Should().Be(0.0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SystemTextJsonPathMatcher_IsMatch_ArrayOneLevel()
|
||||
{
|
||||
// Arrange
|
||||
var matcher = new SystemTextJsonPathMatcher("$.arr[0].line1");
|
||||
|
||||
// Act
|
||||
double match = matcher.IsMatch(JsonNode.Parse(@"{
|
||||
""name"": ""PathSelectorTest"",
|
||||
""test"": ""test"",
|
||||
""test2"": ""test2"",
|
||||
""arr"": [{
|
||||
""line1"": ""line1""
|
||||
}]
|
||||
}")).Score;
|
||||
|
||||
// Assert
|
||||
match.Should().Be(1.0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SystemTextJsonPathMatcher_IsMatch_ObjectMatch()
|
||||
{
|
||||
// Arrange
|
||||
var matcher = new SystemTextJsonPathMatcher("$.test");
|
||||
|
||||
// Act
|
||||
double match = matcher.IsMatch(JsonNode.Parse(@"{
|
||||
""name"": ""PathSelectorTest"",
|
||||
""test"": ""test"",
|
||||
""test2"": ""test2"",
|
||||
""arr"": [
|
||||
{
|
||||
""line1"": ""line1""
|
||||
}
|
||||
]
|
||||
}")).Score;
|
||||
|
||||
// Assert
|
||||
match.Should().Be(1.0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SystemTextJsonPathMatcher_IsMatch_DoesntMatch()
|
||||
{
|
||||
// Arrange
|
||||
var matcher = new SystemTextJsonPathMatcher("$.test3");
|
||||
|
||||
// Act
|
||||
double match = matcher.IsMatch(JsonNode.Parse(@"{
|
||||
""name"": ""PathSelectorTest"",
|
||||
""test"": ""test"",
|
||||
""test2"": ""test2"",
|
||||
""arr"": [
|
||||
{
|
||||
""line1"": ""line1""
|
||||
}
|
||||
]
|
||||
}")).Score;
|
||||
|
||||
// Assert
|
||||
match.Should().Be(0.0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SystemTextJsonPathMatcher_IsMatch_DoesntMatchInArray()
|
||||
{
|
||||
// Arrange
|
||||
var matcher = new SystemTextJsonPathMatcher("$arr[0].line1");
|
||||
|
||||
// Act
|
||||
double match = matcher.IsMatch(JsonNode.Parse(@"{
|
||||
""name"": ""PathSelectorTest"",
|
||||
""test"": ""test"",
|
||||
""test2"": ""test2"",
|
||||
""arr"": []
|
||||
}")).Score;
|
||||
|
||||
// Assert
|
||||
match.Should().Be(0.0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SystemTextJsonPathMatcher_IsMatch_DoesntMatchNoObjectsInArray()
|
||||
{
|
||||
// Arrange
|
||||
var matcher = new SystemTextJsonPathMatcher("$arr[2].line1");
|
||||
|
||||
// Act
|
||||
double match = matcher.IsMatch(JsonNode.Parse(@"{
|
||||
""name"": ""PathSelectorTest"",
|
||||
""test"": ""test"",
|
||||
""test2"": ""test2"",
|
||||
""arr"": []
|
||||
}")).Score;
|
||||
|
||||
// Assert
|
||||
match.Should().Be(0.0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SystemTextJsonPathMatcher_IsMatch_NestedArrays()
|
||||
{
|
||||
// Arrange
|
||||
var matcher = new SystemTextJsonPathMatcher("$.arr[0].sub[0].subline1");
|
||||
|
||||
// Act
|
||||
double match = matcher.IsMatch(JsonNode.Parse(@"{
|
||||
""name"": ""PathSelectorTest"",
|
||||
""test"": ""test"",
|
||||
""test2"": ""test2"",
|
||||
""arr"": [{
|
||||
""line1"": ""line1"",
|
||||
""sub"":[
|
||||
{
|
||||
""subline1"":""subline1""
|
||||
}]
|
||||
}]
|
||||
}")).Score;
|
||||
|
||||
// Assert
|
||||
match.Should().Be(1.0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SystemTextJsonPathMatcher_IsMatch_MultiplePatternsUsingMatchOperatorAnd()
|
||||
{
|
||||
// Arrange
|
||||
var matcher = new SystemTextJsonPathMatcher(MatchBehaviour.AcceptOnMatch, MatchOperator.And, "$.arr[0].sub[0].subline1", "$.arr[0].line2");
|
||||
|
||||
// Act
|
||||
double match = matcher.IsMatch(JsonNode.Parse(@"{
|
||||
""name"": ""PathSelectorTest"",
|
||||
""test"": ""test"",
|
||||
""test2"": ""test2"",
|
||||
""arr"": [{
|
||||
""line1"": ""line1"",
|
||||
""sub"":[
|
||||
{
|
||||
""subline1"":""subline1""
|
||||
}]
|
||||
}]
|
||||
}")).Score;
|
||||
|
||||
// Assert
|
||||
match.Should().Be(0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SystemTextJsonPathMatcher_IsMatch_MultiplePatternsUsingMatchOperatorOr()
|
||||
{
|
||||
// Arrange
|
||||
var matcher = new SystemTextJsonPathMatcher(MatchBehaviour.AcceptOnMatch, MatchOperator.Or, "$.arr[0].sub[0].subline2", "$.arr[0].line1");
|
||||
|
||||
// Act
|
||||
double match = matcher.IsMatch(JsonNode.Parse(@"{
|
||||
""name"": ""PathSelectorTest"",
|
||||
""test"": ""test"",
|
||||
""test2"": ""test2"",
|
||||
""arr"": [{
|
||||
""line1"": ""line1"",
|
||||
""sub"":[
|
||||
{
|
||||
""subline1"":""subline1""
|
||||
}]
|
||||
}]
|
||||
}")).Score;
|
||||
|
||||
// Assert
|
||||
match.Should().Be(1);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SystemTextJsonPathMatcher_IsMatch_String_ArrayOneLevel()
|
||||
{
|
||||
// Arrange
|
||||
var matcher = new SystemTextJsonPathMatcher("$.arr[0].line1");
|
||||
|
||||
// Act
|
||||
double match = matcher.IsMatch(@"{
|
||||
""name"": ""PathSelectorTest"",
|
||||
""arr"": [{
|
||||
""line1"": ""line1""
|
||||
}]
|
||||
}").Score;
|
||||
|
||||
// Assert
|
||||
match.Should().Be(1.0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SystemTextJsonPathMatcher_IsMatch_String_DoesntMatch()
|
||||
{
|
||||
// Arrange
|
||||
var matcher = new SystemTextJsonPathMatcher("$.test3");
|
||||
|
||||
// Act
|
||||
double match = matcher.IsMatch(@"{ ""test"": ""test"" }").Score;
|
||||
|
||||
// Assert
|
||||
match.Should().Be(0.0);
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ using Moq;
|
||||
using WireMock.Logging;
|
||||
using WireMock.Owin;
|
||||
using WireMock.Owin.Mappers;
|
||||
using WireMock.Util;
|
||||
|
||||
namespace WireMock.Net.Tests.Owin;
|
||||
|
||||
@@ -12,6 +13,7 @@ public class GlobalExceptionMiddlewareTests
|
||||
{
|
||||
private readonly Mock<IWireMockMiddlewareOptions> _optionsMock;
|
||||
private readonly Mock<IOwinResponseMapper> _responseMapperMock;
|
||||
private readonly IResponseMessageBuilder _responseMessageBuilder;
|
||||
|
||||
private readonly GlobalExceptionMiddleware _sut;
|
||||
|
||||
@@ -23,7 +25,9 @@ public class GlobalExceptionMiddlewareTests
|
||||
_responseMapperMock = new Mock<IOwinResponseMapper>();
|
||||
_responseMapperMock.Setup(m => m.MapAsync(It.IsAny<ResponseMessage?>(), It.IsAny<HttpResponse>())).Returns(Task.FromResult(true));
|
||||
|
||||
_sut = new GlobalExceptionMiddleware(_ => Task.CompletedTask, _optionsMock.Object, _responseMapperMock.Object);
|
||||
_responseMessageBuilder = new ResponseMessageBuilder(new DateTimeUtils());
|
||||
|
||||
_sut = new GlobalExceptionMiddleware(_ => Task.CompletedTask, _optionsMock.Object, _responseMapperMock.Object, _responseMessageBuilder);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -37,7 +41,7 @@ public class GlobalExceptionMiddlewareTests
|
||||
public void GlobalExceptionMiddleware_Invoke_InvalidNext_ShouldCallResponseMapperWith500()
|
||||
{
|
||||
// Arrange
|
||||
var sut = new GlobalExceptionMiddleware(_ => throw new ArgumentException(), _optionsMock.Object, _responseMapperMock.Object);
|
||||
var sut = new GlobalExceptionMiddleware(_ => throw new ArgumentException(), _optionsMock.Object, _responseMapperMock.Object, _responseMessageBuilder);
|
||||
|
||||
// Act
|
||||
sut.Invoke(Mock.Of<HttpContext>());
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Diagnostics;
|
||||
using System.Linq.Expressions;
|
||||
using System.Net;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Moq;
|
||||
using WireMock.Admin.Mappings;
|
||||
@@ -38,6 +39,7 @@ public class WireMockMiddlewareTests
|
||||
private readonly Mock<HttpContext> _contextMock;
|
||||
private readonly Mock<IGuidUtils> _guidUtilsMock;
|
||||
private readonly Mock<IDateTimeUtils> _dateTimeUtilsMock;
|
||||
private readonly IResponseMessageBuilder _responseMessageBuilderMock;
|
||||
|
||||
private readonly WireMockMiddleware _sut;
|
||||
|
||||
@@ -51,6 +53,8 @@ public class WireMockMiddlewareTests
|
||||
_dateTimeUtilsMock = new Mock<IDateTimeUtils>();
|
||||
_dateTimeUtilsMock.Setup(d => d.UtcNow).Returns(UtcNow);
|
||||
|
||||
_responseMessageBuilderMock = new ResponseMessageBuilder(_dateTimeUtilsMock.Object);
|
||||
|
||||
_optionsMock = new Mock<IWireMockMiddlewareOptions>();
|
||||
_optionsMock.SetupAllProperties();
|
||||
_optionsMock.Setup(o => o.Mappings).Returns(_mappings);
|
||||
@@ -90,7 +94,8 @@ public class WireMockMiddlewareTests
|
||||
_matcherMock.Object,
|
||||
wireMockMiddlewareLoggerMock.Object,
|
||||
_guidUtilsMock.Object,
|
||||
_dateTimeUtilsMock.Object
|
||||
_dateTimeUtilsMock.Object,
|
||||
_responseMessageBuilderMock
|
||||
);
|
||||
}
|
||||
|
||||
@@ -103,7 +108,10 @@ public class WireMockMiddlewareTests
|
||||
// Assert and Verify
|
||||
_optionsMock.Verify(o => o.Logger.Warn(It.IsAny<string>(), It.IsAny<object[]>()), Times.Once);
|
||||
|
||||
Expression<Func<ResponseMessage, bool>> match = r => (int)r.StatusCode! == 404 && ((StatusModel)r.BodyData!.BodyAsJson!).Status == "No matching mapping found";
|
||||
Expression<Func<ResponseMessage, bool>> match = r =>
|
||||
(int)r.StatusCode! == 404 &&
|
||||
((StatusModel)r.BodyData!.BodyAsJson!).Status == "No matching mapping found" &&
|
||||
r.DateTime == UtcNow;
|
||||
_responseMapperMock.Verify(m => m.MapAsync(It.Is(match), It.IsAny<HttpResponse>()), Times.Once);
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ using WireMock.Server;
|
||||
|
||||
namespace WireMock.Net.Tests.Pact;
|
||||
|
||||
[Collection(nameof(PactTests))]
|
||||
public class PactTests
|
||||
{
|
||||
[Fact]
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
using System.Text;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
using WireMock.Matchers;
|
||||
using WireMock.Matchers.Request;
|
||||
using WireMock.Models;
|
||||
@@ -323,7 +322,7 @@ public class RequestBuilderWithBodyTests
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Request_WithBodyJson_PathMatcher_false()
|
||||
public void Request_WithBodyJson_JsonPathMatcher_false()
|
||||
{
|
||||
// Arrange
|
||||
var spec = Request.Create().UsingAnyMethod().WithBody(new JsonPathMatcher("$.things[?(@.name == 'RequiredThing')]"));
|
||||
@@ -368,10 +367,10 @@ public class RequestBuilderWithBodyTests
|
||||
public void Request_WithBody_Array_JsonPathMatcher_1()
|
||||
{
|
||||
// Arrange
|
||||
var spec = Request.Create().UsingAnyMethod().WithBody(new JsonPathMatcher("$..books[?(@.price < 10)]"));
|
||||
var spec = Request.Create().UsingAnyMethod().WithBody(new JsonPathMatcher("$[?(@.Id == 1)]"));
|
||||
|
||||
// Act
|
||||
string jsonString = "{ \"books\": [ { \"category\": \"test1\", \"price\": 8.95 }, { \"category\": \"test2\", \"price\": 20 } ] }";
|
||||
string jsonString = "[{\"Id\": 1, \"Name\": \"Test\"}, {\"Id\": 2, \"Name\": \"Test2\"}]";
|
||||
var bodyData = new BodyData
|
||||
{
|
||||
BodyAsJson = JsonConvert.DeserializeObject(jsonString),
|
||||
@@ -391,10 +390,10 @@ public class RequestBuilderWithBodyTests
|
||||
public void Request_WithBody_Array_JsonPathMatcher_2()
|
||||
{
|
||||
// Arrange
|
||||
var spec = Request.Create().UsingAnyMethod().WithBody(new JsonPathMatcher("$..[?(@.Id == 1)]"));
|
||||
var spec = Request.Create().UsingAnyMethod().WithBody(new JsonPathMatcher("$.test"));
|
||||
|
||||
// Act
|
||||
string jsonString = "{ \"Id\": 1, \"Name\": \"Test\" }";
|
||||
string jsonString = "{\"name\": \"PathSelectorTest\", \"test\": \"test\", \"test2\": \"test2\", \"arr\": [{\"line1\": \"line1\"}]}";
|
||||
var bodyData = new BodyData
|
||||
{
|
||||
BodyAsJson = JsonConvert.DeserializeObject(jsonString),
|
||||
@@ -479,5 +478,132 @@ public class RequestBuilderWithBodyTests
|
||||
var requestMatchResult = new RequestMatchResult();
|
||||
requestBuilder.GetMatchingScore(request, requestMatchResult).Should().Be(1.0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Request_WithBody_SystemTextJsonPathMatcher_true()
|
||||
{
|
||||
// Arrange
|
||||
var spec = Request.Create().UsingAnyMethod().WithBody(new SystemTextJsonPathMatcher("$..things[?(@.name == 'RequiredThing')]"));
|
||||
|
||||
// Act
|
||||
var body = new BodyData
|
||||
{
|
||||
BodyAsString = "{ \"things\": [ { \"name\": \"RequiredThing\" }, { \"name\": \"Wiremock\" } ] }",
|
||||
DetectedBodyType = BodyType.String
|
||||
};
|
||||
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "PUT", ClientIp, body);
|
||||
|
||||
// Assert
|
||||
var requestMatchResult = new RequestMatchResult();
|
||||
spec.GetMatchingScore(request, requestMatchResult).Should().Be(1.0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Request_WithBodyJson_SystemTextJsonPathMatcher_false()
|
||||
{
|
||||
// Arrange
|
||||
var spec = Request.Create().UsingAnyMethod().WithBody(new SystemTextJsonPathMatcher("$.things[?(@.name == 'RequiredThing')]"));
|
||||
|
||||
// Act
|
||||
var body = new BodyData
|
||||
{
|
||||
BodyAsString = "{ \"things\": { \"name\": \"Wiremock\" } }",
|
||||
DetectedBodyType = BodyType.String
|
||||
};
|
||||
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "PUT", ClientIp, body);
|
||||
|
||||
// Assert
|
||||
var requestMatchResult = new RequestMatchResult();
|
||||
spec.GetMatchingScore(request, requestMatchResult).Should().NotBe(1.0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Request_WithBody_Object_SystemTextJsonPathMatcher_true()
|
||||
{
|
||||
// Arrange
|
||||
var spec = Request.Create().UsingAnyMethod().WithBody(new SystemTextJsonPathMatcher("$.arr[0].line1"));
|
||||
|
||||
// Act
|
||||
string jsonString = "{\"name\": \"PathSelectorTest\", \"test\": \"test\", \"test2\": \"test2\", \"arr\": [{\"line1\": \"line1\"}]}";
|
||||
var bodyData = new BodyData
|
||||
{
|
||||
BodyAsString = jsonString,
|
||||
Encoding = Encoding.UTF8,
|
||||
DetectedBodyType = BodyType.String
|
||||
};
|
||||
|
||||
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "PUT", ClientIp, bodyData);
|
||||
|
||||
// Assert
|
||||
var requestMatchResult = new RequestMatchResult();
|
||||
spec.GetMatchingScore(request, requestMatchResult).Should().Be(1.0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Request_WithBody_Array_SystemTextJsonPathMatcher_1()
|
||||
{
|
||||
// Arrange - RFC 9535: filter expression requires an array context
|
||||
var spec = Request.Create().UsingAnyMethod().WithBody(new SystemTextJsonPathMatcher("$[?(@.Id == 1)]"));
|
||||
|
||||
// Act
|
||||
string jsonString = "[{\"Id\": 1, \"Name\": \"Test\"}, {\"Id\": 2, \"Name\": \"Test2\"}]";
|
||||
var bodyData = new BodyData
|
||||
{
|
||||
BodyAsString = jsonString,
|
||||
Encoding = Encoding.UTF8,
|
||||
DetectedBodyType = BodyType.String
|
||||
};
|
||||
|
||||
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "PUT", ClientIp, bodyData);
|
||||
|
||||
// Assert
|
||||
var requestMatchResult = new RequestMatchResult();
|
||||
spec.GetMatchingScore(request, requestMatchResult).Should().Be(1.0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Request_WithBody_Array_SystemTextJsonPathMatcher_2()
|
||||
{
|
||||
// Arrange
|
||||
var spec = Request.Create().UsingAnyMethod().WithBody(new SystemTextJsonPathMatcher("$.test"));
|
||||
|
||||
// Act
|
||||
string jsonString = "{\"name\": \"PathSelectorTest\", \"test\": \"test\", \"test2\": \"test2\", \"arr\": [{\"line1\": \"line1\"}]}";
|
||||
var bodyData = new BodyData
|
||||
{
|
||||
BodyAsString = jsonString,
|
||||
Encoding = Encoding.UTF8,
|
||||
DetectedBodyType = BodyType.String
|
||||
};
|
||||
|
||||
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "PUT", ClientIp, bodyData);
|
||||
|
||||
// Assert
|
||||
var requestMatchResult = new RequestMatchResult();
|
||||
double result = spec.GetMatchingScore(request, requestMatchResult);
|
||||
result.Should().Be(1.0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Request_WithBody_SystemTextJsonPathMatcher_false()
|
||||
{
|
||||
// Arrange
|
||||
var spec = Request.Create().UsingAnyMethod().WithBody(new SystemTextJsonPathMatcher("$.nonexistent"));
|
||||
|
||||
// Act
|
||||
string jsonString = "{\"name\": \"Test\", \"arr\": [{\"line1\": \"line1\"}]}";
|
||||
var bodyData = new BodyData
|
||||
{
|
||||
BodyAsString = jsonString,
|
||||
Encoding = Encoding.UTF8,
|
||||
DetectedBodyType = BodyType.String
|
||||
};
|
||||
|
||||
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "PUT", ClientIp, bodyData);
|
||||
|
||||
// Assert
|
||||
var requestMatchResult = new RequestMatchResult();
|
||||
spec.GetMatchingScore(request, requestMatchResult).Should().NotBe(1.0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user