mirror of
https://github.com/wiremock/WireMock.Net.git
synced 2026-04-27 10:47:01 +02:00
Compare commits
23 Commits
2.3.0
...
SystemText
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1e07f3f7f3 | ||
|
|
64d3e4cbf3 | ||
|
|
47b0bf594f | ||
|
|
31636e5e40 | ||
|
|
32f42105b1 | ||
|
|
8bf42904ab | ||
|
|
0a48b40021 | ||
|
|
1962437dcd | ||
|
|
7d1dcc6fb1 | ||
|
|
82277c7804 | ||
|
|
b105fd3706 | ||
|
|
21cc70e77b | ||
|
|
65bf469906 | ||
|
|
3fcc530d30 | ||
|
|
ebbedda098 | ||
|
|
b55842afae | ||
|
|
8d5f98dc0e | ||
|
|
18d6b4958c | ||
|
|
fece70a9d2 | ||
|
|
ce35fe14af | ||
|
|
6cb41494c8 | ||
|
|
6e402acd80 | ||
|
|
9853f055f2 |
5
.github/copilot-instructions.md
vendored
Normal file
5
.github/copilot-instructions.md
vendored
Normal file
@@ -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.
|
||||||
@@ -1,3 +1,8 @@
|
|||||||
|
# 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)
|
||||||
|
- [#1421](https://github.com/wiremock/WireMock.Net/issues/1421) - Deactivate mapping without deleting it [feature]
|
||||||
|
|
||||||
# 2.3.0 (20 April 2026)
|
# 2.3.0 (20 April 2026)
|
||||||
- [#1436](https://github.com/wiremock/WireMock.Net/pull/1436) - Moving Scenario state change before global response delay is set [feature] contributed by [jayaraman-venkatesan](https://github.com/jayaraman-venkatesan)
|
- [#1436](https://github.com/wiremock/WireMock.Net/pull/1436) - Moving Scenario state change before global response delay is set [feature] contributed by [jayaraman-venkatesan](https://github.com/jayaraman-venkatesan)
|
||||||
- [#1440](https://github.com/wiremock/WireMock.Net/pull/1440) - Bump log4net from 2.0.15 to 3.3.0 [dependencies] contributed by [dependabot[bot]](https://github.com/apps/dependabot)
|
- [#1440](https://github.com/wiremock/WireMock.Net/pull/1440) - Bump log4net from 2.0.15 to 3.3.0 [dependencies] contributed by [dependabot[bot]](https://github.com/apps/dependabot)
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<VersionPrefix>2.3.0</VersionPrefix>
|
<VersionPrefix>2.4.0</VersionPrefix>
|
||||||
<PackageIcon>WireMock.Net-Logo.png</PackageIcon>
|
<PackageIcon>WireMock.Net-Logo.png</PackageIcon>
|
||||||
<PackageProjectUrl>https://github.com/wiremock/WireMock.Net</PackageProjectUrl>
|
<PackageProjectUrl>https://github.com/wiremock/WireMock.Net</PackageProjectUrl>
|
||||||
<PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
|
<PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
rem https://github.com/StefH/GitHubReleaseNotes
|
rem https://github.com/StefH/GitHubReleaseNotes
|
||||||
|
|
||||||
SET version=2.3.0
|
SET version=2.4.0
|
||||||
|
|
||||||
GitHubReleaseNotes --output CHANGELOG.md --skip-empty-releases --exclude-labels wontfix test question invalid doc duplicate example environment --version %version% --token %GH_TOKEN%
|
GitHubReleaseNotes --output CHANGELOG.md --skip-empty-releases --exclude-labels wontfix test question invalid doc duplicate example environment --version %version% --token %GH_TOKEN%
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,6 @@
|
|||||||
# 2.3.0 (20 April 2026)
|
# 2.4.0 (24 April 2026)
|
||||||
- #1436 Moving Scenario state change before global response delay is set [feature]
|
- #1437 Added feature to enable and disable mappings [feature]
|
||||||
- #1440 Bump log4net from 2.0.15 to 3.3.0 [dependencies]
|
- #1450 Bump OpenTelemetry.Exporter.OpenTelemetryProtocol from 1.14.0 to 1.15.x [dependencies, .NET]
|
||||||
- #1443 Fix ExactMatcher and JsonMatcher not working for ISO dates as string [bug]
|
- #1421 Deactivate mapping without deleting it [feature]
|
||||||
- #1444 Update instructions.md [refactor]
|
|
||||||
- #1448 Use DefaultJsonSerializer for BodyAsJson-Response [bug]
|
|
||||||
- #1205 System.Private.Uri 4.3.0 Blackduck security High vulnerability [bug]
|
|
||||||
- #1260 Scenario state change before a response delay timeout ends [bug]
|
|
||||||
- #1441 ExactMatcher and JsonMatcher not working for ISO dates in v2.2.0 [bug]
|
|
||||||
- #1446 WithBodyAsJson does not return correctly formatted json when using SystemTextJsonConverter [bug]
|
|
||||||
|
|
||||||
The full release notes can be found here: https://github.com/wiremock/WireMock.Net/blob/master/CHANGELOG.md
|
The full release notes can be found here: https://github.com/wiremock/WireMock.Net/blob/master/CHANGELOG.md
|
||||||
@@ -64,6 +64,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.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.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.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.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)
|
| **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 +77,7 @@ A C# .NET version based on [mock4net](https://github.com/alexvictoor/mock4net) w
|
|||||||
|
|
||||||
<br />
|
<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*.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
# Visual Studio Version 18
|
# Visual Studio Version 18
|
||||||
VisualStudioVersion = 18.0.11205.157
|
VisualStudioVersion = 18.0.11205.157
|
||||||
@@ -156,6 +155,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WireMock.Net.TestWebApplica
|
|||||||
EndProject
|
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}"
|
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
|
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
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
@@ -850,6 +851,18 @@ Global
|
|||||||
{F4B2B967-98D7-4D93-9A5C-5EF7B84B941A}.Release|x64.Build.0 = Release|Any CPU
|
{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.ActiveCfg = Release|Any CPU
|
||||||
{F4B2B967-98D7-4D93-9A5C-5EF7B84B941A}.Release|x86.Build.0 = 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
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
@@ -914,6 +927,7 @@ Global
|
|||||||
{2CE8E3A6-59CC-FE9C-9399-AD54E1FA862B} = {985E0ADB-D4B4-473A-AA40-567E279B7946}
|
{2CE8E3A6-59CC-FE9C-9399-AD54E1FA862B} = {985E0ADB-D4B4-473A-AA40-567E279B7946}
|
||||||
{3B05CC76-C3CB-8667-6B65-3129DFB25681} = {0BB8B634-407A-4610-A91F-11586990767A}
|
{3B05CC76-C3CB-8667-6B65-3129DFB25681} = {0BB8B634-407A-4610-A91F-11586990767A}
|
||||||
{F4B2B967-98D7-4D93-9A5C-5EF7B84B941A} = {8F890C6F-9ACC-438D-928A-AD61CDA862F2}
|
{F4B2B967-98D7-4D93-9A5C-5EF7B84B941A} = {8F890C6F-9ACC-438D-928A-AD61CDA862F2}
|
||||||
|
{A1B2C3D4-E5F6-7890-ABCD-EF1234567890} = {8F890C6F-9ACC-438D-928A-AD61CDA862F2}
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
SolutionGuid = {DC539027-9852-430C-B19F-FD035D018458}
|
SolutionGuid = {DC539027-9852-430C-B19F-FD035D018458}
|
||||||
|
|||||||
@@ -12,11 +12,11 @@
|
|||||||
|
|
||||||
<PackageReference Include="Microsoft.Extensions.Http.Resilience" Version="8.4.0" />
|
<PackageReference Include="Microsoft.Extensions.Http.Resilience" Version="8.4.0" />
|
||||||
<PackageReference Include="Microsoft.Extensions.ServiceDiscovery" Version="8.0.0" />
|
<PackageReference Include="Microsoft.Extensions.ServiceDiscovery" Version="8.0.0" />
|
||||||
<PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.8.1" />
|
<PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.15.3" />
|
||||||
<PackageReference Include="OpenTelemetry.Extensions.Hosting" Version="1.8.1" />
|
<PackageReference Include="OpenTelemetry.Extensions.Hosting" Version="1.15.3" />
|
||||||
<PackageReference Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.8.1" />
|
<PackageReference Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.15.2" />
|
||||||
<PackageReference Include="OpenTelemetry.Instrumentation.Http" Version="1.8.1" />
|
<PackageReference Include="OpenTelemetry.Instrumentation.Http" Version="1.15.1" />
|
||||||
<PackageReference Include="OpenTelemetry.Instrumentation.Runtime" Version="1.8.0" />
|
<PackageReference Include="OpenTelemetry.Instrumentation.Runtime" Version="1.15.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -8,9 +8,9 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="OpenTelemetry.Exporter.Console" Version="1.14.0" />
|
<PackageReference Include="OpenTelemetry.Exporter.Console" Version="1.15.3" />
|
||||||
<PackageReference Include="OpenTelemetry.Instrumentation.Http" Version="1.14.0" />
|
<PackageReference Include="OpenTelemetry.Instrumentation.Http" Version="1.15.1" />
|
||||||
<PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.14.0" />
|
<PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.15.3" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -55,12 +55,17 @@ public class MappingModel
|
|||||||
/// In case the value is null state will not be changed.
|
/// In case the value is null state will not be changed.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string? SetStateTo { get; set; }
|
public string? SetStateTo { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The number of times this match should be matched before the state will be changed to the specified one.
|
/// The number of times this match should be matched before the state will be changed to the specified one.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int? TimesInSameState { get; set; }
|
public int? TimesInSameState { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Value to determine if the mapping is disabled. Defaults to <c>null</c> (not disabled).
|
||||||
|
/// </summary>
|
||||||
|
public bool? IsDisabled { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The request model.
|
/// The request model.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -100,7 +105,7 @@ public class MappingModel
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public object? Data { get; set; }
|
public object? Data { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The probability when this request should be matched. Value is between 0 and 1. [Optional]
|
/// The probability when this request should be matched. Value is between 0 and 1. [Optional]
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public double? Probability { get; set; }
|
public double? Probability { get; set; }
|
||||||
|
|||||||
@@ -25,7 +25,7 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="JsonConverter.Abstractions" Version="0.9.0" />
|
<PackageReference Include="JsonConverter.Abstractions" Version="0.11.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -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")]
|
||||||
@@ -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
|
// Copyright © WireMock.Net
|
||||||
|
|
||||||
using System;
|
using JsonConverter.Abstractions;
|
||||||
using System.Collections.Generic;
|
using JsonConverter.Newtonsoft.Json;
|
||||||
|
using Newtonsoft.Json;
|
||||||
using WireMock.Matchers.Helpers;
|
using WireMock.Matchers.Helpers;
|
||||||
using WireMock.Models.Mime;
|
using WireMock.Models.Mime;
|
||||||
using WireMock.Util;
|
using WireMock.Util;
|
||||||
@@ -15,6 +16,8 @@ public class MimePartMatcher : IMimePartMatcher
|
|||||||
{
|
{
|
||||||
private readonly IList<(string Name, Func<IMimePartData, MatchResult> func)> _matcherFunctions;
|
private readonly IList<(string Name, Func<IMimePartData, MatchResult> func)> _matcherFunctions;
|
||||||
|
|
||||||
|
private readonly IJsonConverter _jsonConverter;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public string Name => nameof(MimePartMatcher);
|
public string Name => nameof(MimePartMatcher);
|
||||||
|
|
||||||
@@ -41,7 +44,8 @@ public class MimePartMatcher : IMimePartMatcher
|
|||||||
IStringMatcher? contentTypeMatcher,
|
IStringMatcher? contentTypeMatcher,
|
||||||
IStringMatcher? contentDispositionMatcher,
|
IStringMatcher? contentDispositionMatcher,
|
||||||
IStringMatcher? contentTransferEncodingMatcher,
|
IStringMatcher? contentTransferEncodingMatcher,
|
||||||
IMatcher? contentMatcher
|
IMatcher? contentMatcher,
|
||||||
|
IJsonConverter? jsonConverter = null
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
MatchBehaviour = matchBehaviour;
|
MatchBehaviour = matchBehaviour;
|
||||||
@@ -49,6 +53,7 @@ public class MimePartMatcher : IMimePartMatcher
|
|||||||
ContentDispositionMatcher = contentDispositionMatcher;
|
ContentDispositionMatcher = contentDispositionMatcher;
|
||||||
ContentTransferEncodingMatcher = contentTransferEncodingMatcher;
|
ContentTransferEncodingMatcher = contentTransferEncodingMatcher;
|
||||||
ContentMatcher = contentMatcher;
|
ContentMatcher = contentMatcher;
|
||||||
|
_jsonConverter = jsonConverter ?? new NewtonsoftJsonConverter();
|
||||||
|
|
||||||
_matcherFunctions = [];
|
_matcherFunctions = [];
|
||||||
if (ContentTypeMatcher != null)
|
if (ContentTypeMatcher != null)
|
||||||
@@ -107,7 +112,8 @@ public class MimePartMatcher : IMimePartMatcher
|
|||||||
ContentType = GetContentTypeAsString(mimePart.ContentType),
|
ContentType = GetContentTypeAsString(mimePart.ContentType),
|
||||||
DeserializeJson = true,
|
DeserializeJson = true,
|
||||||
ContentEncoding = null, // mimePart.ContentType?.CharsetEncoding.ToString(),
|
ContentEncoding = null, // mimePart.ContentType?.CharsetEncoding.ToString(),
|
||||||
DecompressGZipAndDeflate = true
|
DecompressGZipAndDeflate = true,
|
||||||
|
DefaultJsonConverter = _jsonConverter
|
||||||
};
|
};
|
||||||
|
|
||||||
var bodyData = BodyParser.ParseAsync(bodyParserSettings).ConfigureAwait(false).GetAwaiter().GetResult();
|
var bodyData = BodyParser.ParseAsync(bodyParserSettings).ConfigureAwait(false).GetAwaiter().GetResult();
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
// Copyright © WireMock.Net
|
// Copyright © WireMock.Net
|
||||||
|
|
||||||
using System.Linq;
|
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.Http;
|
using JsonConverter.Abstractions;
|
||||||
using WireMock.Util;
|
using WireMock.Util;
|
||||||
|
|
||||||
namespace WireMock.Http;
|
namespace WireMock.Http;
|
||||||
@@ -15,7 +14,8 @@ internal static class HttpResponseMessageHelper
|
|||||||
Uri originalUri,
|
Uri originalUri,
|
||||||
bool deserializeJson,
|
bool deserializeJson,
|
||||||
bool decompressGzipAndDeflate,
|
bool decompressGzipAndDeflate,
|
||||||
bool deserializeFormUrlEncoded)
|
bool deserializeFormUrlEncoded,
|
||||||
|
IJsonConverter jsonConverter)
|
||||||
{
|
{
|
||||||
var responseMessage = new ResponseMessage { StatusCode = (int)httpResponseMessage.StatusCode };
|
var responseMessage = new ResponseMessage { StatusCode = (int)httpResponseMessage.StatusCode };
|
||||||
|
|
||||||
@@ -45,7 +45,8 @@ internal static class HttpResponseMessageHelper
|
|||||||
DeserializeJson = deserializeJson,
|
DeserializeJson = deserializeJson,
|
||||||
ContentEncoding = contentEncodingHeader?.FirstOrDefault(),
|
ContentEncoding = contentEncodingHeader?.FirstOrDefault(),
|
||||||
DecompressGZipAndDeflate = decompressGzipAndDeflate,
|
DecompressGZipAndDeflate = decompressGzipAndDeflate,
|
||||||
DeserializeFormUrlEncoded = deserializeFormUrlEncoded
|
DeserializeFormUrlEncoded = deserializeFormUrlEncoded,
|
||||||
|
DefaultJsonConverter = jsonConverter
|
||||||
};
|
};
|
||||||
responseMessage.BodyData = await BodyParser.ParseAsync(bodyParserSettings).ConfigureAwait(false);
|
responseMessage.BodyData = await BodyParser.ParseAsync(bodyParserSettings).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -62,6 +62,9 @@ public class Mapping : IMapping
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public bool IsProxy => Provider is ProxyAsyncResponseProvider;
|
public bool IsProxy => Provider is ProxyAsyncResponseProvider;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public bool IsDisabled { get; set; }
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public bool LogMapping => Provider is not (DynamicResponseProvider or DynamicAsyncResponseProvider);
|
public bool LogMapping => Provider is not (DynamicResponseProvider or DynamicAsyncResponseProvider);
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
// Copyright © WireMock.Net
|
// Copyright © WireMock.Net
|
||||||
|
|
||||||
using System.Linq;
|
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
using WireMock.Util;
|
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
|
// Copyright © WireMock.Net
|
||||||
|
|
||||||
using System.Linq;
|
|
||||||
using AnyOfTypes;
|
using AnyOfTypes;
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
using Stef.Validation;
|
using Stef.Validation;
|
||||||
@@ -13,9 +12,8 @@ namespace WireMock.Matchers;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// JsonPathMatcher
|
/// JsonPathMatcher
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <seealso cref="IStringMatcher" />
|
/// <seealso cref="IJsonPathMatcher" />
|
||||||
/// <seealso cref="IObjectMatcher" />
|
public class JsonPathMatcher : IJsonPathMatcher
|
||||||
public class JsonPathMatcher : IStringMatcher, IObjectMatcher
|
|
||||||
{
|
{
|
||||||
private readonly AnyOf<string, StringPattern>[] _patterns;
|
private readonly AnyOf<string, StringPattern>[] _patterns;
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
// Copyright © WireMock.Net
|
// Copyright © WireMock.Net
|
||||||
|
|
||||||
using System.Linq;
|
using System.Collections;
|
||||||
|
using Newtonsoft.Json;
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
using Stef.Validation;
|
using Stef.Validation;
|
||||||
using WireMock.Extensions;
|
using WireMock.Extensions;
|
||||||
|
using WireMock.Serialization;
|
||||||
using WireMock.Util;
|
using WireMock.Util;
|
||||||
using JsonUtils = WireMock.Util.JsonUtils;
|
|
||||||
|
|
||||||
namespace WireMock.Matchers;
|
namespace WireMock.Matchers;
|
||||||
|
|
||||||
@@ -69,7 +70,7 @@ public class JsonMatcher : IJsonMatcher
|
|||||||
Regex = regex;
|
Regex = regex;
|
||||||
|
|
||||||
Value = value;
|
Value = value;
|
||||||
_valueAsJToken = JsonUtils.ConvertValueToJToken(value);
|
_valueAsJToken = ConvertValueToJToken(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@@ -83,7 +84,7 @@ public class JsonMatcher : IJsonMatcher
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var inputAsJToken = JsonUtils.ConvertValueToJToken(input);
|
var inputAsJToken = ConvertValueToJToken(input);
|
||||||
|
|
||||||
var match = IsMatch(RenameJToken(_valueAsJToken), RenameJToken(inputAsJToken));
|
var match = IsMatch(RenameJToken(_valueAsJToken), RenameJToken(inputAsJToken));
|
||||||
score = MatchScores.ToScore(match);
|
score = MatchScores.ToScore(match);
|
||||||
@@ -103,7 +104,7 @@ public class JsonMatcher : IJsonMatcher
|
|||||||
return $"new {Name}" +
|
return $"new {Name}" +
|
||||||
$"(" +
|
$"(" +
|
||||||
$"{MatchBehaviour.GetFullyQualifiedEnumValue()}, " +
|
$"{MatchBehaviour.GetFullyQualifiedEnumValue()}, " +
|
||||||
$"{CSharpFormatter.ConvertToAnonymousObjectDefinition(Value, 3)}, " +
|
$"{CSharpFormatter.ConvertToAnonymousObjectDefinition(Value, 3)}, " +
|
||||||
$"{CSharpFormatter.ToCSharpBooleanLiteral(IgnoreCase)}, " +
|
$"{CSharpFormatter.ToCSharpBooleanLiteral(IgnoreCase)}, " +
|
||||||
$"{CSharpFormatter.ToCSharpBooleanLiteral(Regex)}" +
|
$"{CSharpFormatter.ToCSharpBooleanLiteral(Regex)}" +
|
||||||
$")";
|
$")";
|
||||||
@@ -241,6 +242,18 @@ public class JsonMatcher : IJsonMatcher
|
|||||||
return new JObject(renamedProperties);
|
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)
|
private static string? ToUpper(string? input)
|
||||||
{
|
{
|
||||||
return input?.ToUpperInvariant();
|
return input?.ToUpperInvariant();
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
// Copyright © WireMock.Net
|
// Copyright © WireMock.Net
|
||||||
|
|
||||||
using System.Linq;
|
|
||||||
using Stef.Validation;
|
using Stef.Validation;
|
||||||
using WireMock.Matchers.Helpers;
|
using WireMock.Matchers.Helpers;
|
||||||
using WireMock.Util;
|
using WireMock.Util;
|
||||||
|
|||||||
282
src/WireMock.Net.Minimal/Matchers/SystemTextJsonMatcher.cs
Normal file
282
src/WireMock.Net.Minimal/Matchers/SystemTextJsonMatcher.cs
Normal file
@@ -0,0 +1,282 @@
|
|||||||
|
// 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; }
|
||||||
|
|
||||||
|
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>
|
||||||
|
public SystemTextJsonMatcher(string value, bool ignoreCase = false, bool regex = false)
|
||||||
|
: this(MatchBehaviour.AcceptOnMatch, value, ignoreCase, regex)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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>
|
||||||
|
public SystemTextJsonMatcher(object value, bool ignoreCase = false, bool regex = false)
|
||||||
|
: this(MatchBehaviour.AcceptOnMatch, value, ignoreCase, regex)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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>
|
||||||
|
public SystemTextJsonMatcher(MatchBehaviour matchBehaviour, object value, bool ignoreCase = false, bool regex = false)
|
||||||
|
{
|
||||||
|
Guard.NotNull(value);
|
||||||
|
|
||||||
|
MatchBehaviour = matchBehaviour;
|
||||||
|
IgnoreCase = ignoreCase;
|
||||||
|
Regex = regex;
|
||||||
|
|
||||||
|
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)}" +
|
||||||
|
$")";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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)}" +
|
||||||
|
$")";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -53,7 +53,8 @@ internal class OwinRequestMapper : IOwinRequestMapper
|
|||||||
ContentType = request.ContentType,
|
ContentType = request.ContentType,
|
||||||
DeserializeJson = !options.DisableJsonBodyParsing.GetValueOrDefault(false),
|
DeserializeJson = !options.DisableJsonBodyParsing.GetValueOrDefault(false),
|
||||||
ContentEncoding = contentEncodingHeader?.FirstOrDefault(),
|
ContentEncoding = contentEncodingHeader?.FirstOrDefault(),
|
||||||
DecompressGZipAndDeflate = !options.DisableRequestBodyDecompressing.GetValueOrDefault(false)
|
DecompressGZipAndDeflate = !options.DisableRequestBodyDecompressing.GetValueOrDefault(false),
|
||||||
|
DefaultJsonConverter = options.DefaultJsonSerializer
|
||||||
};
|
};
|
||||||
|
|
||||||
body = await BodyParser.ParseAsync(bodyParserSettings).ConfigureAwait(false);
|
body = await BodyParser.ParseAsync(bodyParserSettings).ConfigureAwait(false);
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ internal class MappingMatcher(IWireMockMiddlewareOptions options, IRandomizerDou
|
|||||||
var possibleMappings = new List<MappingMatcherResult>();
|
var possibleMappings = new List<MappingMatcherResult>();
|
||||||
|
|
||||||
var mappings = _options.Mappings.Values
|
var mappings = _options.Mappings.Values
|
||||||
|
.Where(m => !m.IsDisabled)
|
||||||
.Where(m => m.TimeSettings.IsValid())
|
.Where(m => m.TimeSettings.IsValid())
|
||||||
.Where(m => m.Probability is null || _randomizerDoubleBetween0And1.Generate() <= m.Probability)
|
.Where(m => m.Probability is null || _randomizerDoubleBetween0And1.Generate() <= m.Probability)
|
||||||
.ToArray();
|
.ToArray();
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
// Copyright © WireMock.Net
|
// Copyright © WireMock.Net
|
||||||
|
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
|
||||||
using WireMock.Logging;
|
using WireMock.Logging;
|
||||||
using WireMock.Owin.ActivityTracing;
|
using WireMock.Owin.ActivityTracing;
|
||||||
using WireMock.Serialization;
|
using WireMock.Serialization;
|
||||||
@@ -42,7 +41,7 @@ internal class WireMockMiddlewareLogger(
|
|||||||
if (_options.SaveUnmatchedRequests == true && match?.RequestMatchResult is not { IsPerfectMatch: true })
|
if (_options.SaveUnmatchedRequests == true && match?.RequestMatchResult is not { IsPerfectMatch: true })
|
||||||
{
|
{
|
||||||
var filename = $"{logEntry.Guid}.LogEntry.json";
|
var filename = $"{logEntry.Guid}.LogEntry.json";
|
||||||
_options.FileSystemHandler?.WriteUnmatchedRequest(filename, JsonUtils.Serialize(logEntry));
|
_options.FileSystemHandler?.WriteUnmatchedRequest(filename, _options.DefaultJsonSerializer.Serialize(logEntry));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
|
|||||||
@@ -7,4 +7,4 @@ using System.Runtime.CompilerServices;
|
|||||||
[assembly: InternalsVisibleTo("WireMock.Net.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100e138ec44d93acac565953052636eb8d5e7e9f27ddb030590055cd1a0ab2069a5623f1f77ca907d78e0b37066ca0f6d63da7eecc3fcb65b76aa8ebeccf7ebe1d11264b8404cd9b1cbbf2c83f566e033b3e54129f6ef28daffff776ba7aebbc53c0d635ebad8f45f78eb3f7e0459023c218f003416e080f96a1a3c5ffeb56bee9e")]
|
[assembly: InternalsVisibleTo("WireMock.Net.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100e138ec44d93acac565953052636eb8d5e7e9f27ddb030590055cd1a0ab2069a5623f1f77ca907d78e0b37066ca0f6d63da7eecc3fcb65b76aa8ebeccf7ebe1d11264b8404cd9b1cbbf2c83f566e033b3e54129f6ef28daffff776ba7aebbc53c0d635ebad8f45f78eb3f7e0459023c218f003416e080f96a1a3c5ffeb56bee9e")]
|
||||||
|
|
||||||
// Needed for Moq in the UnitTest project
|
// 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,
|
originalUri,
|
||||||
deserializeJson,
|
deserializeJson,
|
||||||
decompressGzipAndDeflate,
|
decompressGzipAndDeflate,
|
||||||
deserializeFormUrlEncoded
|
deserializeFormUrlEncoded,
|
||||||
|
_settings.DefaultJsonSerializer
|
||||||
).ConfigureAwait(false);
|
).ConfigureAwait(false);
|
||||||
|
|
||||||
IMapping? newMapping = null;
|
IMapping? newMapping = null;
|
||||||
|
|||||||
@@ -197,7 +197,7 @@ public partial class Response : IResponseBuilder
|
|||||||
|
|
||||||
if (ProxyAndRecordSettings != null && _httpClientForProxy != null)
|
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);
|
int place = source.IndexOf(find, StringComparison.OrdinalIgnoreCase);
|
||||||
return place >= 0 ? source.Remove(place, find.Length) : source;
|
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);
|
var decoded = await protoBufMatcher.DecodeAsync(requestMessage.BodyData?.BodyAsBytes).ConfigureAwait(false);
|
||||||
if (decoded != null)
|
if (decoded != null)
|
||||||
{
|
{
|
||||||
requestMessageImplementation.BodyAsJson = JsonUtils.ConvertValueToJToken(decoded);
|
requestMessageImplementation.BodyAsJson = settings.DefaultJsonSerializer.ToJsonToken(decoded);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -275,6 +275,7 @@ internal class MappingConverter(MatcherMapper mapper)
|
|||||||
TimesInSameState = !string.IsNullOrWhiteSpace(mapping.NextState) ? mapping.TimesInSameState : null,
|
TimesInSameState = !string.IsNullOrWhiteSpace(mapping.NextState) ? mapping.TimesInSameState : null,
|
||||||
Data = mapping.Data,
|
Data = mapping.Data,
|
||||||
Probability = mapping.Probability,
|
Probability = mapping.Probability,
|
||||||
|
IsDisabled = mapping.IsDisabled ? true : null,
|
||||||
Request = new RequestModel
|
Request = new RequestModel
|
||||||
{
|
{
|
||||||
Headers = headerMatchers.Any() ? headerMatchers.Select(hm => new HeaderModel
|
Headers = headerMatchers.Any() ? headerMatchers.Select(hm => new HeaderModel
|
||||||
|
|||||||
@@ -1,55 +1,31 @@
|
|||||||
// Copyright © WireMock.Net
|
// Copyright © WireMock.Net
|
||||||
|
|
||||||
using JsonConverter.Abstractions;
|
using JsonConverter.Abstractions;
|
||||||
using Newtonsoft.Json;
|
using JsonConverter.Abstractions.Models;
|
||||||
using Newtonsoft.Json.Linq;
|
|
||||||
#if NETSTANDARD2_0_OR_GREATER || NETCOREAPP3_1_OR_GREATER || NET6_0_OR_GREATER || NET461
|
|
||||||
using System.Text.Json;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace WireMock.Serialization;
|
namespace WireMock.Serialization;
|
||||||
|
|
||||||
internal class MappingSerializer(IJsonConverter jsonConverter)
|
internal class MappingSerializer(IJsonConverter jsonConverter)
|
||||||
{
|
{
|
||||||
private static readonly JsonConverterOptions JsonConverterOptions = new JsonConverterOptions
|
|
||||||
{
|
|
||||||
DateParseHandling = (int) DateParseHandling.None
|
|
||||||
};
|
|
||||||
|
|
||||||
internal T[] DeserializeJsonToArray<T>(string value)
|
internal T[] DeserializeJsonToArray<T>(string value)
|
||||||
{
|
{
|
||||||
// DeserializeObject
|
switch (JsonTypeHelper.GetJsonType(value))
|
||||||
return DeserializeObjectToArray<T>(jsonConverter.Deserialize<object>(value, JsonConverterOptions)!);
|
{
|
||||||
|
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)
|
var json = jsonConverter.Serialize(value, JsonSerializationConstants.JsonConverterOptionsWithDateParsingNone);
|
||||||
{
|
return DeserializeJsonToArray<T>(json);
|
||||||
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.");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -106,9 +106,29 @@ internal class MatcherMapper
|
|||||||
var valueForJsonPartialWildcardMatcher = matcherModel.Pattern ?? matcherModel.Patterns;
|
var valueForJsonPartialWildcardMatcher = matcherModel.Pattern ?? matcherModel.Patterns;
|
||||||
return new JsonPartialWildcardMatcher(matchBehaviour, valueForJsonPartialWildcardMatcher!, ignoreCase, useRegex);
|
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):
|
case nameof(JsonPathMatcher):
|
||||||
return new JsonPathMatcher(matchBehaviour, matchOperator, stringPatterns);
|
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):
|
case nameof(JmesPathMatcher):
|
||||||
return new JmesPathMatcher(matchBehaviour, matchOperator, stringPatterns);
|
return new JmesPathMatcher(matchBehaviour, matchOperator, stringPatterns);
|
||||||
|
|
||||||
@@ -171,6 +191,10 @@ internal class MatcherMapper
|
|||||||
model.Regex = jsonMatcher.Regex;
|
model.Regex = jsonMatcher.Regex;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case SystemTextJsonMatcher stjMatcher:
|
||||||
|
model.Regex = stjMatcher.Regex;
|
||||||
|
break;
|
||||||
|
|
||||||
case XPathMatcher xpathMatcher:
|
case XPathMatcher xpathMatcher:
|
||||||
model.XmlNamespaceMap = xpathMatcher.XmlNamespaceMap;
|
model.XmlNamespaceMap = xpathMatcher.XmlNamespaceMap;
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -234,6 +234,13 @@ public interface IRespondWithAProvider
|
|||||||
/// <returns>The <see cref="IRespondWithAProvider"/>.</returns>
|
/// <returns>The <see cref="IRespondWithAProvider"/>.</returns>
|
||||||
IRespondWithAProvider WithProbability(double probability);
|
IRespondWithAProvider WithProbability(double probability);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Define whether this mapping is disabled. Defaults to <c>false</c>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="isDisabled">Whether this mapping is disabled.</param>
|
||||||
|
/// <returns>The <see cref="IRespondWithAProvider"/>.</returns>
|
||||||
|
IRespondWithAProvider WithIsDisabled(bool isDisabled);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Define a Grpc ProtoDefinition which is used for the request and the response.
|
/// Define a Grpc ProtoDefinition which is used for the request and the response.
|
||||||
/// This can be a ProtoDefinition as a string, or an id when the ProtoDefinitions are defined at the WireMockServer.
|
/// This can be a ProtoDefinition as a string, or an id when the ProtoDefinitions are defined at the WireMockServer.
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ internal class RespondWithAProvider : IRespondWithAProvider
|
|||||||
private int _timesInSameState = 1;
|
private int _timesInSameState = 1;
|
||||||
private bool? _useWebhookFireAndForget;
|
private bool? _useWebhookFireAndForget;
|
||||||
private double? _probability;
|
private double? _probability;
|
||||||
|
private bool _isDisabled = false;
|
||||||
private GraphQLSchemaDetails? _graphQLSchemaDetails; // Future Use.
|
private GraphQLSchemaDetails? _graphQLSchemaDetails; // Future Use.
|
||||||
|
|
||||||
public Guid Guid { get; private set; }
|
public Guid Guid { get; private set; }
|
||||||
@@ -108,6 +109,11 @@ internal class RespondWithAProvider : IRespondWithAProvider
|
|||||||
mapping.WithProbability(_probability.Value);
|
mapping.WithProbability(_probability.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_isDisabled)
|
||||||
|
{
|
||||||
|
mapping.IsDisabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (ProtoDefinition != null)
|
if (ProtoDefinition != null)
|
||||||
{
|
{
|
||||||
mapping.WithProtoDefinition(ProtoDefinition.Value);
|
mapping.WithProtoDefinition(ProtoDefinition.Value);
|
||||||
@@ -354,6 +360,13 @@ internal class RespondWithAProvider : IRespondWithAProvider
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public IRespondWithAProvider WithIsDisabled(bool isDisabled)
|
||||||
|
{
|
||||||
|
_isDisabled = isDisabled;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public IRespondWithAProvider WithProtoDefinition(params string[] protoDefinitionOrId)
|
public IRespondWithAProvider WithProtoDefinition(params string[] protoDefinitionOrId)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -57,6 +57,8 @@ public partial class WireMockServer
|
|||||||
public string OpenApi => $"{_prefix}/openapi";
|
public string OpenApi => $"{_prefix}/openapi";
|
||||||
|
|
||||||
public RegexMatcher MappingsGuidPathMatcher => new($"^{_prefixEscaped}\\/mappings\\/([0-9A-Fa-f]{{8}}[-][0-9A-Fa-f]{{4}}[-][0-9A-Fa-f]{{4}}[-][0-9A-Fa-f]{{4}}[-][0-9A-Fa-f]{{12}})$");
|
public RegexMatcher MappingsGuidPathMatcher => new($"^{_prefixEscaped}\\/mappings\\/([0-9A-Fa-f]{{8}}[-][0-9A-Fa-f]{{4}}[-][0-9A-Fa-f]{{4}}[-][0-9A-Fa-f]{{4}}[-][0-9A-Fa-f]{{12}})$");
|
||||||
|
public RegexMatcher MappingsGuidEnablePathMatcher => new($"^{_prefixEscaped}\\/mappings\\/([0-9A-Fa-f]{{8}}[-][0-9A-Fa-f]{{4}}[-][0-9A-Fa-f]{{4}}[-][0-9A-Fa-f]{{4}}[-][0-9A-Fa-f]{{12}})\\/enable$");
|
||||||
|
public RegexMatcher MappingsGuidDisablePathMatcher => new($"^{_prefixEscaped}\\/mappings\\/([0-9A-Fa-f]{{8}}[-][0-9A-Fa-f]{{4}}[-][0-9A-Fa-f]{{4}}[-][0-9A-Fa-f]{{4}}[-][0-9A-Fa-f]{{12}})\\/disable$");
|
||||||
public RegexMatcher MappingsCodeGuidPathMatcher => new($"^{_prefixEscaped}\\/mappings\\/code\\/([0-9A-Fa-f]{{8}}[-][0-9A-Fa-f]{{4}}[-][0-9A-Fa-f]{{4}}[-][0-9A-Fa-f]{{4}}[-][0-9A-Fa-f]{{12}})$");
|
public RegexMatcher MappingsCodeGuidPathMatcher => new($"^{_prefixEscaped}\\/mappings\\/code\\/([0-9A-Fa-f]{{8}}[-][0-9A-Fa-f]{{4}}[-][0-9A-Fa-f]{{4}}[-][0-9A-Fa-f]{{4}}[-][0-9A-Fa-f]{{12}})$");
|
||||||
public RegexMatcher RequestsGuidPathMatcher => new($"^{_prefixEscaped}\\/requests\\/([0-9A-Fa-f]{{8}}[-][0-9A-Fa-f]{{4}}[-][0-9A-Fa-f]{{4}}[-][0-9A-Fa-f]{{4}}[-][0-9A-Fa-f]{{12}})$");
|
public RegexMatcher RequestsGuidPathMatcher => new($"^{_prefixEscaped}\\/requests\\/([0-9A-Fa-f]{{8}}[-][0-9A-Fa-f]{{4}}[-][0-9A-Fa-f]{{4}}[-][0-9A-Fa-f]{{4}}[-][0-9A-Fa-f]{{12}})$");
|
||||||
public RegexMatcher ScenariosNameMatcher => new($"^{_prefixEscaped}\\/scenarios\\/.+$");
|
public RegexMatcher ScenariosNameMatcher => new($"^{_prefixEscaped}\\/scenarios\\/.+$");
|
||||||
@@ -100,6 +102,12 @@ public partial class WireMockServer
|
|||||||
Given(Request.Create().WithPath(_adminPaths.MappingsGuidPathMatcher).UsingPut().WithHeader(HttpKnownHeaderNames.ContentType, AdminRequestContentTypeJson)).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(MappingPut));
|
Given(Request.Create().WithPath(_adminPaths.MappingsGuidPathMatcher).UsingPut().WithHeader(HttpKnownHeaderNames.ContentType, AdminRequestContentTypeJson)).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(MappingPut));
|
||||||
Given(Request.Create().WithPath(_adminPaths.MappingsGuidPathMatcher).UsingDelete()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(MappingDelete));
|
Given(Request.Create().WithPath(_adminPaths.MappingsGuidPathMatcher).UsingDelete()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(MappingDelete));
|
||||||
|
|
||||||
|
// __admin/mappings/{guid}/enable
|
||||||
|
Given(Request.Create().WithPath(_adminPaths.MappingsGuidEnablePathMatcher).UsingPut()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(MappingEnable));
|
||||||
|
|
||||||
|
// __admin/mappings/{guid}/disable
|
||||||
|
Given(Request.Create().WithPath(_adminPaths.MappingsGuidDisablePathMatcher).UsingPut()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(MappingDisable));
|
||||||
|
|
||||||
// __admin/mappings/code/{guid}
|
// __admin/mappings/code/{guid}
|
||||||
Given(Request.Create().WithPath(_adminPaths.MappingsCodeGuidPathMatcher).UsingGet()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(MappingCodeGet));
|
Given(Request.Create().WithPath(_adminPaths.MappingsCodeGuidPathMatcher).UsingGet()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(MappingCodeGet));
|
||||||
|
|
||||||
@@ -338,7 +346,7 @@ public partial class WireMockServer
|
|||||||
}
|
}
|
||||||
|
|
||||||
o.CorsPolicyOptions = corsPolicyOptions;
|
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;
|
o.AcceptAnyClientCertificate = _settings.AcceptAnyClientCertificate;
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -426,6 +434,47 @@ public partial class WireMockServer
|
|||||||
var lastPart = requestMessage.Path.Split('/').LastOrDefault();
|
var lastPart = requestMessage.Path.Split('/').LastOrDefault();
|
||||||
return Guid.TryParse(lastPart, out guid);
|
return Guid.TryParse(lastPart, out guid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static bool TryParseGuidFromSecondToLastSegment(IRequestMessage requestMessage, out Guid guid)
|
||||||
|
{
|
||||||
|
var parts = requestMessage.Path.Split('/');
|
||||||
|
if (parts.Length >= 2 && Guid.TryParse(parts[parts.Length - 2], out guid))
|
||||||
|
return true;
|
||||||
|
guid = Guid.Empty;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private IResponseMessage MappingEnable(HttpContext _, IRequestMessage requestMessage)
|
||||||
|
{
|
||||||
|
if (TryParseGuidFromSecondToLastSegment(requestMessage, out var guid))
|
||||||
|
{
|
||||||
|
var mapping = Mappings.FirstOrDefault(m => !m.IsAdminInterface && m.Guid == guid);
|
||||||
|
if (mapping != null)
|
||||||
|
{
|
||||||
|
mapping.IsDisabled = false;
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
|
||||||
|
private IResponseMessage MappingDisable(HttpContext _, IRequestMessage requestMessage)
|
||||||
|
{
|
||||||
|
if (TryParseGuidFromSecondToLastSegment(requestMessage, out var guid))
|
||||||
|
{
|
||||||
|
var mapping = Mappings.FirstOrDefault(m => !m.IsAdminInterface && m.Guid == guid);
|
||||||
|
if (mapping != null)
|
||||||
|
{
|
||||||
|
mapping.IsDisabled = true;
|
||||||
|
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");
|
||||||
|
}
|
||||||
#endregion Mapping/{guid}
|
#endregion Mapping/{guid}
|
||||||
|
|
||||||
#region Mappings
|
#region Mappings
|
||||||
@@ -834,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)
|
private static Encoding? ToEncoding(EncodingModel? encodingModel)
|
||||||
{
|
{
|
||||||
return encodingModel != null ? Encoding.GetEncoding(encodingModel.CodePage) : null;
|
return encodingModel != null ? Encoding.GetEncoding(encodingModel.CodePage) : null;
|
||||||
@@ -853,28 +914,16 @@ public partial class WireMockServer
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private static T[] DeserializeRequestMessageToArray<T>(IRequestMessage requestMessage)
|
private T DeserializeObject<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)
|
|
||||||
{
|
{
|
||||||
switch (requestMessage.BodyData?.DetectedBodyType)
|
switch (requestMessage.BodyData?.DetectedBodyType)
|
||||||
{
|
{
|
||||||
case BodyType.String:
|
case BodyType.String when requestMessage.BodyData?.BodyAsString != null:
|
||||||
case BodyType.FormUrlEncoded:
|
case BodyType.FormUrlEncoded when requestMessage.BodyData?.BodyAsString != null:
|
||||||
return JsonUtils.DeserializeObject<T>(requestMessage.BodyData.BodyAsString!);
|
return _settings.DefaultJsonSerializer.Deserialize<T>(requestMessage.BodyData.BodyAsString)!;
|
||||||
|
|
||||||
case BodyType.Json when requestMessage.BodyData?.BodyAsJson != null:
|
case BodyType.Json when requestMessage.BodyData?.BodyAsJson != null:
|
||||||
return ((JObject)requestMessage.BodyData.BodyAsJson).ToObject<T>()!;
|
return _settings.DefaultJsonSerializer.ParseJsonToken<T>(requestMessage.BodyData.BodyAsJson)!;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw new NotSupportedException();
|
throw new NotSupportedException();
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
// Copyright © WireMock.Net
|
// Copyright © WireMock.Net
|
||||||
|
|
||||||
using System.Linq;
|
|
||||||
using Stef.Validation;
|
using Stef.Validation;
|
||||||
using WireMock.Admin.Mappings;
|
using WireMock.Admin.Mappings;
|
||||||
using WireMock.Matchers;
|
using WireMock.Matchers;
|
||||||
@@ -120,6 +119,11 @@ public partial class WireMockServer
|
|||||||
respondProvider.WithProbability(mappingModel.Probability.Value);
|
respondProvider.WithProbability(mappingModel.Probability.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mappingModel.IsDisabled == true)
|
||||||
|
{
|
||||||
|
respondProvider.WithIsDisabled(true);
|
||||||
|
}
|
||||||
|
|
||||||
// ProtoDefinition is defined at Mapping level
|
// ProtoDefinition is defined at Mapping level
|
||||||
if (mappingModel.ProtoDefinition != null)
|
if (mappingModel.ProtoDefinition != null)
|
||||||
{
|
{
|
||||||
@@ -148,7 +152,7 @@ public partial class WireMockServer
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var clientIPModel = JsonUtils.ParseJTokenToObject<ClientIPModel>(requestModel.ClientIP);
|
var clientIPModel = _settings.DefaultJsonSerializer.ParseJsonToken<ClientIPModel>(requestModel.ClientIP);
|
||||||
if (clientIPModel.Matchers != null)
|
if (clientIPModel.Matchers != null)
|
||||||
{
|
{
|
||||||
requestBuilder = requestBuilder.WithPath(clientIPModel.Matchers.Select(_matcherMapper.Map).OfType<IStringMatcher>().ToArray());
|
requestBuilder = requestBuilder.WithPath(clientIPModel.Matchers.Select(_matcherMapper.Map).OfType<IStringMatcher>().ToArray());
|
||||||
@@ -164,7 +168,7 @@ public partial class WireMockServer
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var pathModel = JsonUtils.ParseJTokenToObject<PathModel>(requestModel.Path);
|
var pathModel = _settings.DefaultJsonSerializer.ParseJsonToken<PathModel>(requestModel.Path);
|
||||||
if (pathModel.Matchers != null)
|
if (pathModel.Matchers != null)
|
||||||
{
|
{
|
||||||
var matchOperator = StringUtils.ParseMatchOperator(pathModel.MatchOperator);
|
var matchOperator = StringUtils.ParseMatchOperator(pathModel.MatchOperator);
|
||||||
@@ -180,7 +184,7 @@ public partial class WireMockServer
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var urlModel = JsonUtils.ParseJTokenToObject<UrlModel>(requestModel.Url);
|
var urlModel = _settings.DefaultJsonSerializer.ParseJsonToken<UrlModel>(requestModel.Url);
|
||||||
if (urlModel.Matchers != null)
|
if (urlModel.Matchers != null)
|
||||||
{
|
{
|
||||||
var matchOperator = StringUtils.ParseMatchOperator(urlModel.MatchOperator);
|
var matchOperator = StringUtils.ParseMatchOperator(urlModel.MatchOperator);
|
||||||
@@ -266,7 +270,7 @@ public partial class WireMockServer
|
|||||||
return requestBuilder;
|
return requestBuilder;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static IResponseBuilder InitResponseBuilder(ResponseModel responseModel)
|
private IResponseBuilder InitResponseBuilder(ResponseModel responseModel)
|
||||||
{
|
{
|
||||||
var responseBuilder = Response.Create();
|
var responseBuilder = Response.Create();
|
||||||
|
|
||||||
@@ -331,7 +335,7 @@ public partial class WireMockServer
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var headers = JsonUtils.ParseJTokenToObject<string[]>(entry.Value);
|
var headers = _settings.DefaultJsonSerializer.ParseJsonToken<string[]>(entry.Value);
|
||||||
responseBuilder.WithHeader(entry.Key, headers);
|
responseBuilder.WithHeader(entry.Key, headers);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -357,7 +361,7 @@ public partial class WireMockServer
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var headers = JsonUtils.ParseJTokenToObject<string[]>(entry.Value);
|
var headers = _settings.DefaultJsonSerializer.ParseJsonToken<string[]>(entry.Value);
|
||||||
responseBuilder.WithTrailingHeader(entry.Key, headers);
|
responseBuilder.WithTrailingHeader(entry.Key, headers);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ using WireMock.Constants;
|
|||||||
using WireMock.Logging;
|
using WireMock.Logging;
|
||||||
using WireMock.Models;
|
using WireMock.Models;
|
||||||
using WireMock.Types;
|
using WireMock.Types;
|
||||||
|
using WireMock.Transformers;
|
||||||
using WireMock.Util;
|
using WireMock.Util;
|
||||||
|
|
||||||
namespace WireMock.Settings;
|
namespace WireMock.Settings;
|
||||||
@@ -74,6 +75,8 @@ public static class WireMockServerSettingsParser
|
|||||||
WatchStaticMappingsInSubdirectories = parser.GetBoolValue(nameof(WireMockServerSettings.WatchStaticMappingsInSubdirectories)),
|
WatchStaticMappingsInSubdirectories = parser.GetBoolValue(nameof(WireMockServerSettings.WatchStaticMappingsInSubdirectories)),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
settings.DefaultJsonBodyTransformer = new NewtonsoftJsonBodyTransformer(settings);
|
||||||
|
|
||||||
#if USE_ASPNETCORE
|
#if USE_ASPNETCORE
|
||||||
settings.CorsPolicyOptions = parser.GetEnumValue(nameof(WireMockServerSettings.CorsPolicyOptions), CorsPolicyOptions.None);
|
settings.CorsPolicyOptions = parser.GetEnumValue(nameof(WireMockServerSettings.CorsPolicyOptions), CorsPolicyOptions.None);
|
||||||
settings.ClientCertificateMode = parser.GetEnumValue(nameof(WireMockServerSettings.ClientCertificateMode), ClientCertificateMode.NoCertificate);
|
settings.ClientCertificateMode = parser.GetEnumValue(nameof(WireMockServerSettings.ClientCertificateMode), ClientCertificateMode.NoCertificate);
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
// Copyright © WireMock.Net
|
// Copyright © WireMock.Net
|
||||||
|
|
||||||
using HandlebarsDotNet;
|
using HandlebarsDotNet;
|
||||||
|
using WireMock.Transformers;
|
||||||
|
|
||||||
namespace WireMock.Transformers.Handlebars;
|
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
|
// Copyright © WireMock.Net
|
||||||
|
|
||||||
using System.Collections;
|
|
||||||
using System.Linq;
|
|
||||||
using HandlebarsDotNet.Helpers.Models;
|
|
||||||
using Newtonsoft.Json;
|
|
||||||
using Newtonsoft.Json.Linq;
|
|
||||||
using Stef.Validation;
|
using Stef.Validation;
|
||||||
using WireMock.Settings;
|
using WireMock.Settings;
|
||||||
using WireMock.Types;
|
using WireMock.Types;
|
||||||
@@ -14,17 +9,13 @@ namespace WireMock.Transformers;
|
|||||||
|
|
||||||
internal class Transformer : ITransformer
|
internal class Transformer : ITransformer
|
||||||
{
|
{
|
||||||
private readonly JsonSerializer _jsonSerializer;
|
private readonly IJsonBodyTransformer _jsonBodyTransformer;
|
||||||
private readonly ITransformerContextFactory _factory;
|
private readonly ITransformerContextFactory _factory;
|
||||||
|
|
||||||
public Transformer(WireMockServerSettings settings, ITransformerContextFactory factory)
|
public Transformer(WireMockServerSettings settings, ITransformerContextFactory factory)
|
||||||
{
|
{
|
||||||
_factory = Guard.NotNull(factory);
|
_factory = Guard.NotNull(factory);
|
||||||
|
_jsonBodyTransformer = Guard.NotNull(settings).DefaultJsonBodyTransformer;
|
||||||
_jsonSerializer = new JsonSerializer
|
|
||||||
{
|
|
||||||
Culture = Guard.NotNull(settings).Culture
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public IBodyData? TransformBody(
|
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)
|
switch (original.DetectedBodyType)
|
||||||
{
|
{
|
||||||
case BodyType.Json:
|
case BodyType.Json:
|
||||||
case BodyType.ProtoBuf:
|
case BodyType.ProtoBuf:
|
||||||
return TransformBodyAsJson(transformerContext, options, model, original);
|
return _jsonBodyTransformer.TransformBodyAsJson(
|
||||||
|
transformerContext,
|
||||||
|
options,
|
||||||
|
model,
|
||||||
|
original);
|
||||||
|
|
||||||
case BodyType.File:
|
case BodyType.File:
|
||||||
return TransformBodyAsFile(transformerContext, model, original, useTransformerForBodyAsFile);
|
return TransformBodyAsFile(transformerContext, model, original, useTransformerForBodyAsFile);
|
||||||
@@ -159,185 +154,7 @@ internal class Transformer : ITransformer
|
|||||||
return newHeaders;
|
return newHeaders;
|
||||||
}
|
}
|
||||||
|
|
||||||
private IBodyData TransformBodyAsJson(ITransformerContext transformerContext, ReplaceNodeOptions options, object model, IBodyData original)
|
private static BodyData TransformBodyAsString(ITransformerContext transformerContext, 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)
|
|
||||||
{
|
{
|
||||||
return new BodyData
|
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);
|
var transformedBodyAsFilename = transformerContext.ParseAndRender(original.BodyAsFile!, model);
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<Description>Minimal version from the lightweight Http Mocking Server for .NET</Description>
|
<Description>Minimal version from the lightweight Http Mocking Server for .NET</Description>
|
||||||
<AssemblyTitle>WireMock.Net.Minimal</AssemblyTitle>
|
<AssemblyTitle>WireMock.Net.Minimal</AssemblyTitle>
|
||||||
|
|||||||
@@ -25,7 +25,7 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="JsonConverter.Newtonsoft.Json" Version="0.9.0" />
|
<PackageReference Include="JsonConverter.Newtonsoft.Json" Version="0.11.0" />
|
||||||
<PackageReference Include="NUnit" Version="4.4.0" />
|
<PackageReference Include="NUnit" Version="4.4.0" />
|
||||||
<PackageReference Include="Stef.Validation" Version="0.2.0" />
|
<PackageReference Include="Stef.Validation" Version="0.2.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|||||||
@@ -25,9 +25,9 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="OpenTelemetry.Extensions.Hosting" Version="1.14.0" />
|
<PackageReference Include="OpenTelemetry.Extensions.Hosting" Version="1.15.3" />
|
||||||
<PackageReference Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.14.0" />
|
<PackageReference Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.15.2" />
|
||||||
<PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.14.0" />
|
<PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.15.3" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -163,6 +163,22 @@ public interface IWireMockAdminApi
|
|||||||
[Header("Content-Type", "application/json")]
|
[Header("Content-Type", "application/json")]
|
||||||
Task<StatusModel> PutMappingAsync([Path] Guid guid, [Body] MappingModel mapping, CancellationToken cancellationToken = default);
|
Task<StatusModel> PutMappingAsync([Path] Guid guid, [Body] MappingModel mapping, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Enable a mapping based on the guid.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="guid">The Guid.</param>
|
||||||
|
/// <param name="cancellationToken">The optional cancellationToken.</param>
|
||||||
|
[Put("mappings/{guid}/enable")]
|
||||||
|
Task<StatusModel> EnableMappingAsync([Path] Guid guid, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Disable a mapping based on the guid.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="guid">The Guid.</param>
|
||||||
|
/// <param name="cancellationToken">The optional cancellationToken.</param>
|
||||||
|
[Put("mappings/{guid}/disable")]
|
||||||
|
Task<StatusModel> DisableMappingAsync([Path] Guid guid, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Delete a mapping based on the guid
|
/// Delete a mapping based on the guid
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -33,7 +33,7 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="JsonConverter.Newtonsoft.Json" Version="0.9.0" />
|
<PackageReference Include="JsonConverter.Newtonsoft.Json" Version="0.11.0" />
|
||||||
<PackageReference Include="RestEase" Version="1.6.4" />
|
<PackageReference Include="RestEase" Version="1.6.4" />
|
||||||
<PackageReference Include="Stef.Validation" Version="0.2.0" />
|
<PackageReference Include="Stef.Validation" Version="0.2.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|||||||
@@ -108,6 +108,14 @@ public interface IMapping
|
|||||||
/// </value>
|
/// </value>
|
||||||
bool IsProxy { get; }
|
bool IsProxy { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a value indicating whether this mapping is disabled.
|
||||||
|
/// </summary>
|
||||||
|
/// <value>
|
||||||
|
/// <c>true</c> if this mapping is disabled; otherwise, <c>false</c>.
|
||||||
|
/// </value>
|
||||||
|
bool IsDisabled { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets a value indicating whether this mapping to be logged.
|
/// Gets a value indicating whether this mapping to be logged.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -135,7 +143,7 @@ public interface IMapping
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
object? Data { get; }
|
object? Data { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The probability when this request should be matched. Value is between 0 and 1. [Optional]
|
/// The probability when this request should be matched. Value is between 0 and 1. [Optional]
|
||||||
/// </summary>
|
/// </summary>
|
||||||
double? Probability { get; }
|
double? Probability { get; }
|
||||||
|
|||||||
11
src/WireMock.Net.Shared/Matchers/IJsonPathMatcher.cs
Normal file
11
src/WireMock.Net.Shared/Matchers/IJsonPathMatcher.cs
Normal file
@@ -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.GraphQL, PublicKey=0024000004800000940000000602000000240000525341310004000001000100e138ec44d93acac565953052636eb8d5e7e9f27ddb030590055cd1a0ab2069a5623f1f77ca907d78e0b37066ca0f6d63da7eecc3fcb65b76aa8ebeccf7ebe1d11264b8404cd9b1cbbf2c83f566e033b3e54129f6ef28daffff776ba7aebbc53c0d635ebad8f45f78eb3f7e0459023c218f003416e080f96a1a3c5ffeb56bee9e")]
|
||||||
[assembly: InternalsVisibleTo("WireMock.Net.ProtoBuf, PublicKey=0024000004800000940000000602000000240000525341310004000001000100e138ec44d93acac565953052636eb8d5e7e9f27ddb030590055cd1a0ab2069a5623f1f77ca907d78e0b37066ca0f6d63da7eecc3fcb65b76aa8ebeccf7ebe1d11264b8404cd9b1cbbf2c83f566e033b3e54129f6ef28daffff776ba7aebbc53c0d635ebad8f45f78eb3f7e0459023c218f003416e080f96a1a3c5ffeb56bee9e")]
|
[assembly: InternalsVisibleTo("WireMock.Net.ProtoBuf, PublicKey=0024000004800000940000000602000000240000525341310004000001000100e138ec44d93acac565953052636eb8d5e7e9f27ddb030590055cd1a0ab2069a5623f1f77ca907d78e0b37066ca0f6d63da7eecc3fcb65b76aa8ebeccf7ebe1d11264b8404cd9b1cbbf2c83f566e033b3e54129f6ef28daffff776ba7aebbc53c0d635ebad8f45f78eb3f7e0459023c218f003416e080f96a1a3c5ffeb56bee9e")]
|
||||||
[assembly: InternalsVisibleTo("WireMock.Net.Matchers.CSharpCode, 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.OpenTelemetry, PublicKey=0024000004800000940000000602000000240000525341310004000001000100e138ec44d93acac565953052636eb8d5e7e9f27ddb030590055cd1a0ab2069a5623f1f77ca907d78e0b37066ca0f6d63da7eecc3fcb65b76aa8ebeccf7ebe1d11264b8404cd9b1cbbf2c83f566e033b3e54129f6ef28daffff776ba7aebbc53c0d635ebad8f45f78eb3f7e0459023c218f003416e080f96a1a3c5ffeb56bee9e")]
|
||||||
// [assembly: InternalsVisibleTo("WireMock.Net.StandAlone, PublicKey=0024000004800000940000000602000000240000525341310004000001000100e138ec44d93acac565953052636eb8d5e7e9f27ddb030590055cd1a0ab2069a5623f1f77ca907d78e0b37066ca0f6d63da7eecc3fcb65b76aa8ebeccf7ebe1d11264b8404cd9b1cbbf2c83f566e033b3e54129f6ef28daffff776ba7aebbc53c0d635ebad8f45f78eb3f7e0459023c218f003416e080f96a1a3c5ffeb56bee9e")]
|
// [assembly: InternalsVisibleTo("WireMock.Net.StandAlone, PublicKey=0024000004800000940000000602000000240000525341310004000001000100e138ec44d93acac565953052636eb8d5e7e9f27ddb030590055cd1a0ab2069a5623f1f77ca907d78e0b37066ca0f6d63da7eecc3fcb65b76aa8ebeccf7ebe1d11264b8404cd9b1cbbf2c83f566e033b3e54129f6ef28daffff776ba7aebbc53c0d635ebad8f45f78eb3f7e0459023c218f003416e080f96a1a3c5ffeb56bee9e")]
|
||||||
[assembly: InternalsVisibleTo("WireMock.Net.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100e138ec44d93acac565953052636eb8d5e7e9f27ddb030590055cd1a0ab2069a5623f1f77ca907d78e0b37066ca0f6d63da7eecc3fcb65b76aa8ebeccf7ebe1d11264b8404cd9b1cbbf2c83f566e033b3e54129f6ef28daffff776ba7aebbc53c0d635ebad8f45f78eb3f7e0459023c218f003416e080f96a1a3c5ffeb56bee9e")]
|
[assembly: InternalsVisibleTo("WireMock.Net.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100e138ec44d93acac565953052636eb8d5e7e9f27ddb030590055cd1a0ab2069a5623f1f77ca907d78e0b37066ca0f6d63da7eecc3fcb65b76aa8ebeccf7ebe1d11264b8404cd9b1cbbf2c83f566e033b3e54129f6ef28daffff776ba7aebbc53c0d635ebad8f45f78eb3f7e0459023c218f003416e080f96a1a3c5ffeb56bee9e")]
|
||||||
|
|
||||||
// Needed for Moq in the UnitTest project
|
// Needed for Moq in the UnitTest project
|
||||||
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")]
|
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")]
|
||||||
@@ -14,11 +14,17 @@ internal static class JsonSerializationConstants
|
|||||||
IgnoreNullValues = true
|
IgnoreNullValues = true
|
||||||
};
|
};
|
||||||
|
|
||||||
//internal static readonly JsonSerializerSettings JsonSerializerSettingsDefault = new()
|
internal static readonly JsonConverterOptions JsonConverterOptionsIncludeNullValues = new()
|
||||||
//{
|
{
|
||||||
// Formatting = Formatting.Indented,
|
WriteIndented = true,
|
||||||
// NullValueHandling = NullValueHandling.Ignore
|
IgnoreNullValues = false
|
||||||
//};
|
};
|
||||||
|
|
||||||
|
internal static readonly JsonConverterOptions JsonConverterOptionsWithDateParsingNone = new()
|
||||||
|
{
|
||||||
|
WriteIndented = true,
|
||||||
|
DateParseHandling = 0
|
||||||
|
};
|
||||||
|
|
||||||
internal static readonly JsonSerializerSettings JsonSerializerSettingsIncludeNullValues = new()
|
internal static readonly JsonSerializerSettings JsonSerializerSettingsIncludeNullValues = new()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ using WireMock.Logging;
|
|||||||
using WireMock.Matchers;
|
using WireMock.Matchers;
|
||||||
using WireMock.Models;
|
using WireMock.Models;
|
||||||
using WireMock.RegularExpressions;
|
using WireMock.RegularExpressions;
|
||||||
|
using WireMock.Transformers;
|
||||||
using WireMock.Types;
|
using WireMock.Types;
|
||||||
|
|
||||||
namespace WireMock.Settings;
|
namespace WireMock.Settings;
|
||||||
@@ -362,11 +363,31 @@ public class WireMockServerSettings
|
|||||||
/// Default is <see cref="NewtonsoftJsonConverter"/>.
|
/// Default is <see cref="NewtonsoftJsonConverter"/>.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
[PublicAPI]
|
[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>
|
/// <summary>
|
||||||
/// WebSocket settings.
|
/// WebSocket settings.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[PublicAPI]
|
[PublicAPI]
|
||||||
public WebSocketSettings? WebSocketSettings { get; set; }
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
28
src/WireMock.Net.Shared/Transformers/IJsonBodyTransformer.cs
Normal file
28
src/WireMock.Net.Shared/Transformers/IJsonBodyTransformer.cs
Normal file
@@ -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);
|
||||||
|
}
|
||||||
34
src/WireMock.Net.Shared/Transformers/ITransformerContext.cs
Normal file
34
src/WireMock.Net.Shared/Transformers/ITransformerContext.cs
Normal file
@@ -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,270 @@
|
|||||||
|
// 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 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
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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,207 @@
|
|||||||
|
// 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.Types;
|
||||||
|
using WireMock.Util;
|
||||||
|
|
||||||
|
namespace WireMock.Transformers;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// JSON body transformer implementation based on System.Text.Json.
|
||||||
|
/// </summary>
|
||||||
|
[PublicAPI]
|
||||||
|
public class SystemTextJsonBodyTransformer() : IJsonBodyTransformer
|
||||||
|
{
|
||||||
|
private 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
|
||||||
|
{
|
||||||
|
// Ignore and return as string.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
// Copyright © WireMock.Net
|
||||||
|
|
||||||
using System.Linq;
|
|
||||||
using System.Net.Http.Headers;
|
using System.Net.Http.Headers;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using Stef.Validation;
|
using Stef.Validation;
|
||||||
@@ -129,11 +128,11 @@ internal static class BodyParser
|
|||||||
{
|
{
|
||||||
Guard.NotNull(settings);
|
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
|
var data = new BodyData
|
||||||
{
|
{
|
||||||
BodyAsBytes = bodyWithContentEncoding.Bytes,
|
BodyAsBytes = Bytes,
|
||||||
DetectedCompression = bodyWithContentEncoding.ContentType,
|
DetectedCompression = ContentType,
|
||||||
DetectedBodyType = BodyType.Bytes,
|
DetectedBodyType = BodyType.Bytes,
|
||||||
DetectedBodyTypeFromContentType = DetectBodyTypeFromContentType(settings.ContentType)
|
DetectedBodyTypeFromContentType = DetectBodyTypeFromContentType(settings.ContentType)
|
||||||
};
|
};
|
||||||
@@ -169,17 +168,17 @@ internal static class BodyParser
|
|||||||
data.DetectedBodyType = BodyType.FormUrlEncoded;
|
data.DetectedBodyType = BodyType.FormUrlEncoded;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If string is not null or empty, try to deserialize the string to a JObject
|
// If string is not null or empty, try to deserialize the string
|
||||||
if (settings.DeserializeJson && JsonUtils.IsJson(data.BodyAsString))
|
if (settings.DeserializeJson && settings.DefaultJsonConverter.IsValidJson(data.BodyAsString))
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
data.BodyAsJson = JsonUtils.DeserializeObject(data.BodyAsString);
|
data.BodyAsJson = settings.DefaultJsonConverter.Deserialize<object>(data.BodyAsString);
|
||||||
data.DetectedBodyType = BodyType.Json;
|
data.DetectedBodyType = BodyType.Json;
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
// JsonConvert failed, just ignore.
|
// JsonConverter failed, just ignore.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -202,7 +201,7 @@ internal static class BodyParser
|
|||||||
return (null, data);
|
return (null, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool IsProbablyText(byte[] data)
|
private static bool IsProbablyText(byte[] data)
|
||||||
{
|
{
|
||||||
if (data.Length == 0)
|
if (data.Length == 0)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
// Copyright © WireMock.Net
|
// Copyright © WireMock.Net
|
||||||
|
|
||||||
using System.IO;
|
using JsonConverter.Abstractions;
|
||||||
|
using JsonConverter.Newtonsoft.Json;
|
||||||
|
|
||||||
namespace WireMock.Util;
|
namespace WireMock.Util;
|
||||||
|
|
||||||
@@ -35,4 +36,13 @@ internal class BodyParserSettings
|
|||||||
/// Try to deserialize the body as FormUrlEncoded.
|
/// Try to deserialize the body as FormUrlEncoded.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool DeserializeFormUrlEncoded { get; set; } = true;
|
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,7 +1,5 @@
|
|||||||
// Copyright © WireMock.Net
|
// Copyright © WireMock.Net
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections;
|
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
@@ -35,7 +33,7 @@ internal static class JsonUtils
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Try to convert this string into a JToken
|
// Try to convert this string into a JObject
|
||||||
value = JObject.Parse(strInput!);
|
value = JObject.Parse(strInput!);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -45,28 +43,12 @@ internal static class JsonUtils
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string Serialize(object value)
|
|
||||||
{
|
|
||||||
return JsonConvert.SerializeObject(value, JsonSerializationConstants.JsonSerializerSettingsIncludeNullValues);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static byte[] SerializeAsPactFile(object value)
|
public static byte[] SerializeAsPactFile(object value)
|
||||||
{
|
{
|
||||||
var json = JsonConvert.SerializeObject(value, JsonSerializationConstants.JsonSerializerSettingsPact);
|
var json = JsonConvert.SerializeObject(value, JsonSerializationConstants.JsonSerializerSettingsPact);
|
||||||
return Encoding.UTF8.GetBytes(json);
|
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>
|
/// <summary>
|
||||||
/// Deserializes the JSON to a .NET object.
|
/// Deserializes the JSON to a .NET object.
|
||||||
/// Using : DateParseHandling = DateParseHandling.None
|
/// Using : DateParseHandling = DateParseHandling.None
|
||||||
@@ -100,38 +82,4 @@ internal static class JsonUtils
|
|||||||
return default;
|
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.Collections.Concurrent;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Linq;
|
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using Stef.Validation;
|
using Stef.Validation;
|
||||||
|
|
||||||
|
|||||||
@@ -30,7 +30,8 @@
|
|||||||
<PackageReference Include="Stef.Validation" Version="0.2.0" />
|
<PackageReference Include="Stef.Validation" Version="0.2.0" />
|
||||||
<PackageReference Include="AnyOf" Version="0.5.0.1" />
|
<PackageReference Include="AnyOf" Version="0.5.0.1" />
|
||||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.2" />
|
<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.11.0" />
|
||||||
|
<PackageReference Include="JsonConverter.System.Text.Json" Version="0.11.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -34,5 +34,6 @@
|
|||||||
<ProjectReference Include="../WireMock.Net.GraphQL/WireMock.Net.GraphQL.csproj" />
|
<ProjectReference Include="../WireMock.Net.GraphQL/WireMock.Net.GraphQL.csproj" />
|
||||||
<ProjectReference Include="../WireMock.Net.ProtoBuf/WireMock.Net.ProtoBuf.csproj" />
|
<ProjectReference Include="../WireMock.Net.ProtoBuf/WireMock.Net.ProtoBuf.csproj" />
|
||||||
<ProjectReference Include="../WireMock.Net.OpenTelemetry/WireMock.Net.OpenTelemetry.csproj" />
|
<ProjectReference Include="../WireMock.Net.OpenTelemetry/WireMock.Net.OpenTelemetry.csproj" />
|
||||||
|
<ProjectReference Include="../WireMock.Net.Matchers.SystemTextJsonPath/WireMock.Net.Matchers.SystemTextJsonPath.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
@@ -0,0 +1,134 @@
|
|||||||
|
// Copyright © WireMock.Net
|
||||||
|
|
||||||
|
using RestEase;
|
||||||
|
using WireMock.Admin.Mappings;
|
||||||
|
using WireMock.Client;
|
||||||
|
using WireMock.Server;
|
||||||
|
|
||||||
|
namespace WireMock.Net.Tests.AdminApi;
|
||||||
|
|
||||||
|
public partial class WireMockAdminApiTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public async Task IWireMockAdminApi_PostMappingAsync_WithIsDisabledTrue_DoesNotMatchRequests()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var ct = TestContext.Current.CancellationToken;
|
||||||
|
using var server = WireMockServer.StartWithAdminInterface();
|
||||||
|
var api = RestClient.For<IWireMockAdminApi>(server.Urls[0]);
|
||||||
|
var httpClient = server.CreateClient();
|
||||||
|
|
||||||
|
var model = new MappingModel
|
||||||
|
{
|
||||||
|
Request = new RequestModel { Path = "/foo", Methods = ["GET"] },
|
||||||
|
Response = new ResponseModel { Body = "hello", StatusCode = 200 },
|
||||||
|
IsDisabled = true
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act — POST the disabled mapping
|
||||||
|
var postResult = await api.PostMappingAsync(model, ct);
|
||||||
|
postResult.Should().NotBeNull();
|
||||||
|
|
||||||
|
// Assert — request should not be matched (404)
|
||||||
|
var response = await httpClient.GetAsync("/foo", ct);
|
||||||
|
((int)response.StatusCode).Should().Be(404);
|
||||||
|
|
||||||
|
// Assert — mapping exists but IsDisabled is true
|
||||||
|
server.Mappings.Where(m => !m.IsAdminInterface).Should().ContainSingle(m => m.IsDisabled == true);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task IWireMockAdminApi_DisableMappingAsync_PreventsMatching()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var ct = TestContext.Current.CancellationToken;
|
||||||
|
using var server = WireMockServer.StartWithAdminInterface();
|
||||||
|
var api = RestClient.For<IWireMockAdminApi>(server.Urls[0]);
|
||||||
|
var httpClient = server.CreateClient();
|
||||||
|
|
||||||
|
var model = new MappingModel
|
||||||
|
{
|
||||||
|
Request = new RequestModel { Path = "/bar", Methods = ["GET"] },
|
||||||
|
Response = new ResponseModel { Body = "world", StatusCode = 200 }
|
||||||
|
};
|
||||||
|
var postResult = await api.PostMappingAsync(model, ct);
|
||||||
|
var guid = postResult.Guid!.Value;
|
||||||
|
|
||||||
|
// Assert — mapping matches before disable
|
||||||
|
var before = await httpClient.GetAsync("/bar", ct);
|
||||||
|
((int)before.StatusCode).Should().Be(200);
|
||||||
|
|
||||||
|
// Act — disable
|
||||||
|
var disableResult = await api.DisableMappingAsync(guid, ct);
|
||||||
|
disableResult.Status.Should().Be("Mapping disabled");
|
||||||
|
|
||||||
|
// Assert — no match after disable
|
||||||
|
var after = await httpClient.GetAsync("/bar", ct);
|
||||||
|
((int)after.StatusCode).Should().Be(404);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task IWireMockAdminApi_EnableMappingAsync_ResumesMatching()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var ct = TestContext.Current.CancellationToken;
|
||||||
|
using var server = WireMockServer.StartWithAdminInterface();
|
||||||
|
var api = RestClient.For<IWireMockAdminApi>(server.Urls[0]);
|
||||||
|
var httpClient = server.CreateClient();
|
||||||
|
|
||||||
|
var model = new MappingModel
|
||||||
|
{
|
||||||
|
Request = new RequestModel { Path = "/baz", Methods = ["GET"] },
|
||||||
|
Response = new ResponseModel { Body = "re-enabled", StatusCode = 200 },
|
||||||
|
IsDisabled = true
|
||||||
|
};
|
||||||
|
var postResult = await api.PostMappingAsync(model, ct);
|
||||||
|
var guid = postResult.Guid!.Value;
|
||||||
|
|
||||||
|
// Assert — no match while disabled
|
||||||
|
var before = await httpClient.GetAsync("/baz", ct);
|
||||||
|
((int)before.StatusCode).Should().Be(404);
|
||||||
|
|
||||||
|
// Act — enable
|
||||||
|
var enableResult = await api.EnableMappingAsync(guid, ct);
|
||||||
|
enableResult.Status.Should().Be("Mapping enabled");
|
||||||
|
|
||||||
|
// Assert — mapping matches after enable
|
||||||
|
var after = await httpClient.GetAsync("/baz", ct);
|
||||||
|
((int)after.StatusCode).Should().Be(200);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task IWireMockAdminApi_GetMappingAsync_ReturnsIsDisabledTrue_WhenDisabled()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var ct = TestContext.Current.CancellationToken;
|
||||||
|
using var server = WireMockServer.StartWithAdminInterface();
|
||||||
|
var api = RestClient.For<IWireMockAdminApi>(server.Urls[0]);
|
||||||
|
|
||||||
|
var disabledModel = new MappingModel
|
||||||
|
{
|
||||||
|
Request = new RequestModel { Path = "/check-disabled" },
|
||||||
|
Response = new ResponseModel { Body = "x", StatusCode = 200 },
|
||||||
|
IsDisabled = true
|
||||||
|
};
|
||||||
|
var enabledModel = new MappingModel
|
||||||
|
{
|
||||||
|
Request = new RequestModel { Path = "/check-enabled" },
|
||||||
|
Response = new ResponseModel { Body = "y", StatusCode = 200 }
|
||||||
|
};
|
||||||
|
|
||||||
|
var disabledPost = await api.PostMappingAsync(disabledModel, ct);
|
||||||
|
var enabledPost = await api.PostMappingAsync(enabledModel, ct);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var disabledGot = await api.GetMappingAsync(disabledPost.Guid!.Value, ct);
|
||||||
|
var enabledGot = await api.GetMappingAsync(enabledPost.Guid!.Value, ct);
|
||||||
|
|
||||||
|
// Assert — disabled mapping serializes IsDisabled = true
|
||||||
|
disabledGot.IsDisabled.Should().BeTrue();
|
||||||
|
|
||||||
|
// Assert — enabled mapping omits IsDisabled (null = default not disabled)
|
||||||
|
enabledGot.IsDisabled.Should().BeNull();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,5 +6,5 @@ internal static class Constants
|
|||||||
{
|
{
|
||||||
internal const int NumStaticMappings = 10;
|
internal const int NumStaticMappings = 10;
|
||||||
|
|
||||||
internal const int NumAdminMappings = 37;
|
internal const int NumAdminMappings = 39;
|
||||||
}
|
}
|
||||||
369
test/WireMock.Net.Tests/Matchers/SystemTextJsonMatcherTests.cs
Normal file
369
test/WireMock.Net.Tests/Matchers/SystemTextJsonMatcherTests.cs
Normal file
@@ -0,0 +1,369 @@
|
|||||||
|
// 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("{}");
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = matcher.IsMatch(new MemoryStream());
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,410 @@
|
|||||||
|
// 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("{}");
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = matcher.IsMatch(new MemoryStream());
|
||||||
|
|
||||||
|
// 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,381 @@
|
|||||||
|
// 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
|
||||||
|
var result = matcher.IsMatch(new MemoryStream());
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -56,6 +56,7 @@ public class MappingMatcherTests
|
|||||||
{
|
{
|
||||||
// Assign
|
// Assign
|
||||||
var mappingMock = new Mock<IMapping>();
|
var mappingMock = new Mock<IMapping>();
|
||||||
|
mappingMock.SetupGet(m => m.IsDisabled).Returns(false);
|
||||||
mappingMock.Setup(m => m.GetRequestMatchResult(It.IsAny<RequestMessage>(), It.IsAny<string>())).Throws<Exception>();
|
mappingMock.Setup(m => m.GetRequestMatchResult(It.IsAny<RequestMessage>(), It.IsAny<string>())).Throws<Exception>();
|
||||||
|
|
||||||
var mappings = new ConcurrentDictionary<Guid, IMapping>();
|
var mappings = new ConcurrentDictionary<Guid, IMapping>();
|
||||||
@@ -229,6 +230,35 @@ public class MappingMatcherTests
|
|||||||
result.Match!.Mapping.Guid.Should().Be(withProbability);
|
result.Match!.Mapping.Guid.Should().Be(withProbability);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void MappingMatcher_FindBestMatch_WhenMappingIsDisabled_ShouldReturnNull()
|
||||||
|
{
|
||||||
|
// Assign
|
||||||
|
var guid = Guid.Parse("00000000-0000-0000-0000-000000000001");
|
||||||
|
var mappingMock = new Mock<IMapping>();
|
||||||
|
mappingMock.SetupGet(m => m.Guid).Returns(guid);
|
||||||
|
mappingMock.SetupGet(m => m.IsDisabled).Returns(true);
|
||||||
|
mappingMock.SetupGet(m => m.Probability).Returns((double?)null);
|
||||||
|
|
||||||
|
var matchResult = new RequestMatchResult();
|
||||||
|
matchResult.AddScore(typeof(object), 1.0, null);
|
||||||
|
mappingMock.Setup(m => m.GetRequestMatchResult(It.IsAny<RequestMessage>(), It.IsAny<string>())).Returns(matchResult);
|
||||||
|
|
||||||
|
var mappings = new ConcurrentDictionary<Guid, IMapping>();
|
||||||
|
mappings.TryAdd(guid, mappingMock.Object);
|
||||||
|
_optionsMock.Setup(o => o.Mappings).Returns(mappings);
|
||||||
|
|
||||||
|
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "GET", "::1");
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = _sut.FindBestMatch(request);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Match.Should().BeNull();
|
||||||
|
result.Partial.Should().BeNull();
|
||||||
|
mappingMock.Verify(m => m.GetRequestMatchResult(It.IsAny<RequestMessage>(), It.IsAny<string>()), Times.Never);
|
||||||
|
}
|
||||||
|
|
||||||
private static ConcurrentDictionary<Guid, IMapping> InitMappings(params (Guid guid, double[] scores, double? probability)[] matches)
|
private static ConcurrentDictionary<Guid, IMapping> InitMappings(params (Guid guid, double[] scores, double? probability)[] matches)
|
||||||
{
|
{
|
||||||
var mappings = new ConcurrentDictionary<Guid, IMapping>();
|
var mappings = new ConcurrentDictionary<Guid, IMapping>();
|
||||||
@@ -237,6 +267,7 @@ public class MappingMatcherTests
|
|||||||
{
|
{
|
||||||
var mappingMock = new Mock<IMapping>();
|
var mappingMock = new Mock<IMapping>();
|
||||||
mappingMock.SetupGet(m => m.Guid).Returns(match.guid);
|
mappingMock.SetupGet(m => m.Guid).Returns(match.guid);
|
||||||
|
mappingMock.SetupGet(m => m.IsDisabled).Returns(false);
|
||||||
|
|
||||||
var requestMatchResult = new RequestMatchResult();
|
var requestMatchResult = new RequestMatchResult();
|
||||||
foreach (var score in match.scores)
|
foreach (var score in match.scores)
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ using WireMock.Server;
|
|||||||
|
|
||||||
namespace WireMock.Net.Tests.Pact;
|
namespace WireMock.Net.Tests.Pact;
|
||||||
|
|
||||||
|
[Collection(nameof(PactTests))]
|
||||||
public class PactTests
|
public class PactTests
|
||||||
{
|
{
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
||||||
using WireMock.Matchers;
|
using WireMock.Matchers;
|
||||||
using WireMock.Matchers.Request;
|
using WireMock.Matchers.Request;
|
||||||
using WireMock.Models;
|
using WireMock.Models;
|
||||||
@@ -323,7 +322,7 @@ public class RequestBuilderWithBodyTests
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Request_WithBodyJson_PathMatcher_false()
|
public void Request_WithBodyJson_JsonPathMatcher_false()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var spec = Request.Create().UsingAnyMethod().WithBody(new JsonPathMatcher("$.things[?(@.name == 'RequiredThing')]"));
|
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()
|
public void Request_WithBody_Array_JsonPathMatcher_1()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var spec = Request.Create().UsingAnyMethod().WithBody(new JsonPathMatcher("$..books[?(@.price < 10)]"));
|
var spec = Request.Create().UsingAnyMethod().WithBody(new JsonPathMatcher("$[?(@.Id == 1)]"));
|
||||||
|
|
||||||
// Act
|
// 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
|
var bodyData = new BodyData
|
||||||
{
|
{
|
||||||
BodyAsJson = JsonConvert.DeserializeObject(jsonString),
|
BodyAsJson = JsonConvert.DeserializeObject(jsonString),
|
||||||
@@ -391,10 +390,10 @@ public class RequestBuilderWithBodyTests
|
|||||||
public void Request_WithBody_Array_JsonPathMatcher_2()
|
public void Request_WithBody_Array_JsonPathMatcher_2()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var spec = Request.Create().UsingAnyMethod().WithBody(new JsonPathMatcher("$..[?(@.Id == 1)]"));
|
var spec = Request.Create().UsingAnyMethod().WithBody(new JsonPathMatcher("$.test"));
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
string jsonString = "{ \"Id\": 1, \"Name\": \"Test\" }";
|
string jsonString = "{\"name\": \"PathSelectorTest\", \"test\": \"test\", \"test2\": \"test2\", \"arr\": [{\"line1\": \"line1\"}]}";
|
||||||
var bodyData = new BodyData
|
var bodyData = new BodyData
|
||||||
{
|
{
|
||||||
BodyAsJson = JsonConvert.DeserializeObject(jsonString),
|
BodyAsJson = JsonConvert.DeserializeObject(jsonString),
|
||||||
@@ -479,5 +478,132 @@ public class RequestBuilderWithBodyTests
|
|||||||
var requestMatchResult = new RequestMatchResult();
|
var requestMatchResult = new RequestMatchResult();
|
||||||
requestBuilder.GetMatchingScore(request, requestMatchResult).Should().Be(1.0);
|
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
164
test/WireMock.Net.Tests/Transformers/JsonBodyTransformerTests.cs
Normal file
164
test/WireMock.Net.Tests/Transformers/JsonBodyTransformerTests.cs
Normal file
@@ -0,0 +1,164 @@
|
|||||||
|
// Copyright © WireMock.Net
|
||||||
|
|
||||||
|
using System.Text;
|
||||||
|
using System.Text.Json.Nodes;
|
||||||
|
using Moq;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
using WireMock.Handlers;
|
||||||
|
using WireMock.Settings;
|
||||||
|
using WireMock.Transformers;
|
||||||
|
using WireMock.Types;
|
||||||
|
using WireMock.Util;
|
||||||
|
|
||||||
|
namespace WireMock.Net.Tests.Transformers;
|
||||||
|
|
||||||
|
public class JsonBodyTransformerTests
|
||||||
|
{
|
||||||
|
public static TheoryData<JsonBodyTransformerTestContext> Transformers
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return
|
||||||
|
[
|
||||||
|
new JsonBodyTransformerTestContext(
|
||||||
|
() => new NewtonsoftJsonBodyTransformer(new WireMockServerSettings()),
|
||||||
|
JObject.Parse,
|
||||||
|
body => ((JToken)body).ToString(Formatting.None)),
|
||||||
|
|
||||||
|
new JsonBodyTransformerTestContext(
|
||||||
|
() => new SystemTextJsonBodyTransformer(),
|
||||||
|
json => JsonNode.Parse(json)!,
|
||||||
|
body => ((JsonNode)body).ToJsonString())
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[MemberData(nameof(Transformers))]
|
||||||
|
public void TransformBodyAsJson_Replaces_String_Value_And_Preserves_Original(JsonBodyTransformerTestContext testContext)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var transformer = testContext.CreateTransformer();
|
||||||
|
var originalJson = testContext.ParseJson("{\"value\":\"{{number}}\"}");
|
||||||
|
var bodyData = new BodyData
|
||||||
|
{
|
||||||
|
Encoding = Encoding.UTF8,
|
||||||
|
DetectedBodyType = BodyType.Json,
|
||||||
|
DetectedBodyTypeFromContentType = BodyType.Json,
|
||||||
|
ProtoBufMessageType = "My.Message",
|
||||||
|
BodyAsJson = originalJson
|
||||||
|
};
|
||||||
|
|
||||||
|
var transformerContext = new FakeTransformerContext(
|
||||||
|
text => text,
|
||||||
|
text => text == "{{number}}" ? "123" : text);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = transformer.TransformBodyAsJson(transformerContext, ReplaceNodeOptions.EvaluateAndTryToConvert, new { }, bodyData);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Encoding.Should().Be(Encoding.UTF8);
|
||||||
|
result.DetectedBodyType.Should().Be(BodyType.Json);
|
||||||
|
result.DetectedBodyTypeFromContentType.Should().Be(BodyType.Json);
|
||||||
|
result.ProtoBufMessageType.Should().Be("My.Message");
|
||||||
|
result.BodyAsJson.Should().NotBeNull();
|
||||||
|
testContext.SerializeJson(result.BodyAsJson).Should().Be("{\"value\":123}");
|
||||||
|
testContext.SerializeJson(originalJson).Should().Be("{\"value\":\"{{number}}\"}");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[MemberData(nameof(Transformers))]
|
||||||
|
public void TransformBodyAsJson_With_String_Body_Replaces_Single_Node_With_Object(JsonBodyTransformerTestContext testContext)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var transformer = testContext.CreateTransformer();
|
||||||
|
var bodyData = new BodyData
|
||||||
|
{
|
||||||
|
DetectedBodyType = BodyType.Json,
|
||||||
|
BodyAsJson = "{{json}}"
|
||||||
|
};
|
||||||
|
|
||||||
|
var transformerContext = new FakeTransformerContext(
|
||||||
|
text => text == "{{json}}" ? "{\"name\":\"test\"}" : text,
|
||||||
|
text => text);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = transformer.TransformBodyAsJson(transformerContext, ReplaceNodeOptions.EvaluateAndTryToConvert, new { }, bodyData);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.BodyAsJson.Should().NotBeNull();
|
||||||
|
testContext.SerializeJson(result.BodyAsJson).Should().Be("{\"name\":\"test\"}");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[MemberData(nameof(Transformers))]
|
||||||
|
public void TransformBodyAsJson_Replaces_String_Value_With_WireMockList_As_Array(JsonBodyTransformerTestContext testContext)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var transformer = testContext.CreateTransformer();
|
||||||
|
var bodyData = new BodyData
|
||||||
|
{
|
||||||
|
DetectedBodyType = BodyType.Json,
|
||||||
|
BodyAsJson = testContext.ParseJson("{\"values\":\"{{list}}\"}")
|
||||||
|
};
|
||||||
|
|
||||||
|
var transformerContext = new FakeTransformerContext(
|
||||||
|
text => text,
|
||||||
|
text => text == "{{list}}" ? new WireMockList<string>(["a", "b"]) : text);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = transformer.TransformBodyAsJson(transformerContext, ReplaceNodeOptions.EvaluateAndTryToConvert, new { }, bodyData);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.BodyAsJson.Should().NotBeNull();
|
||||||
|
testContext.SerializeJson(result.BodyAsJson).Should().Be("{\"values\":[\"a\",\"b\"]}");
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class JsonBodyTransformerTestContext
|
||||||
|
{
|
||||||
|
private readonly Func<IJsonBodyTransformer> _createTransformer;
|
||||||
|
private readonly Func<string, object> _parseJson;
|
||||||
|
private readonly Func<object, string> _serializeJson;
|
||||||
|
|
||||||
|
public JsonBodyTransformerTestContext(
|
||||||
|
Func<IJsonBodyTransformer> createTransformer,
|
||||||
|
Func<string, object> parseJson,
|
||||||
|
Func<object, string> serializeJson)
|
||||||
|
{
|
||||||
|
_createTransformer = createTransformer;
|
||||||
|
_parseJson = parseJson;
|
||||||
|
_serializeJson = serializeJson;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IJsonBodyTransformer CreateTransformer()
|
||||||
|
{
|
||||||
|
return _createTransformer();
|
||||||
|
}
|
||||||
|
|
||||||
|
public object ParseJson(string json)
|
||||||
|
{
|
||||||
|
return _parseJson(json);
|
||||||
|
}
|
||||||
|
|
||||||
|
public string SerializeJson(object body)
|
||||||
|
{
|
||||||
|
return _serializeJson(body);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private sealed class FakeTransformerContext(Func<string, string> parseAndRender, Func<string, object> parseAndEvaluate) : ITransformerContext
|
||||||
|
{
|
||||||
|
public IFileSystemHandler FileSystemHandler { get; private set; } = Mock.Of<IFileSystemHandler>();
|
||||||
|
|
||||||
|
public string ParseAndRender(string text, object model)
|
||||||
|
{
|
||||||
|
return parseAndRender(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
public object ParseAndEvaluate(string text, object model)
|
||||||
|
{
|
||||||
|
return parseAndEvaluate(text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -750,6 +750,8 @@ public class WebSocketIntegrationTests(ITestOutputHelper output, ITestContextAcc
|
|||||||
{
|
{
|
||||||
await client.SendAsync(testMessage, cancellationToken: _ct);
|
await client.SendAsync(testMessage, cancellationToken: _ct);
|
||||||
|
|
||||||
|
await Task.Delay(250, _ct);
|
||||||
|
|
||||||
var received = await client.ReceiveAsTextAsync(cancellationToken: _ct);
|
var received = await client.ReceiveAsTextAsync(cancellationToken: _ct);
|
||||||
received.Should().Be(testMessage, $"message '{testMessage}' should be proxied and echoed back");
|
received.Should().Be(testMessage, $"message '{testMessage}' should be proxied and echoed back");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,6 +50,7 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\..\src\WireMock.Net.AwesomeAssertions\WireMock.Net.AwesomeAssertions.csproj" />
|
<ProjectReference Include="..\..\src\WireMock.Net.AwesomeAssertions\WireMock.Net.AwesomeAssertions.csproj" />
|
||||||
<ProjectReference Include="..\..\src\WireMock.Net.Matchers.CSharpCode\WireMock.Net.Matchers.CSharpCode.csproj" />
|
<ProjectReference Include="..\..\src\WireMock.Net.Matchers.CSharpCode\WireMock.Net.Matchers.CSharpCode.csproj" />
|
||||||
|
<ProjectReference Include="..\..\src\WireMock.Net.Matchers.SystemTextJsonPath\WireMock.Net.Matchers.SystemTextJsonPath.csproj" />
|
||||||
<ProjectReference Include="..\..\src\WireMock.Net.RestClient.AwesomeAssertions\WireMock.Net.RestClient.AwesomeAssertions.csproj" />
|
<ProjectReference Include="..\..\src\WireMock.Net.RestClient.AwesomeAssertions\WireMock.Net.RestClient.AwesomeAssertions.csproj" />
|
||||||
<ProjectReference Include="..\..\src\WireMock.Net.RestClient\WireMock.Net.RestClient.csproj" />
|
<ProjectReference Include="..\..\src\WireMock.Net.RestClient\WireMock.Net.RestClient.csproj" />
|
||||||
<ProjectReference Include="..\..\src\WireMock.Net.xUnit.v3\WireMock.Net.xUnit.v3.csproj" />
|
<ProjectReference Include="..\..\src\WireMock.Net.xUnit.v3\WireMock.Net.xUnit.v3.csproj" />
|
||||||
@@ -75,7 +76,6 @@
|
|||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Moq" Version="4.20.72" />
|
<PackageReference Include="Moq" Version="4.20.72" />
|
||||||
<PackageReference Include="SimMetrics.Net" Version="1.0.5" />
|
<PackageReference Include="SimMetrics.Net" Version="1.0.5" />
|
||||||
<PackageReference Include="JsonConverter.System.Text.Json" Version="0.8.0" />
|
|
||||||
<PackageReference Include="Google.Protobuf" Version="3.33.5" />
|
<PackageReference Include="Google.Protobuf" Version="3.33.5" />
|
||||||
<PackageReference Include="Grpc.Net.Client" Version="2.76.0" />
|
<PackageReference Include="Grpc.Net.Client" Version="2.76.0" />
|
||||||
<PackageReference Include="Grpc.Tools" Version="2.78.0">
|
<PackageReference Include="Grpc.Tools" Version="2.78.0">
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ using System.Text;
|
|||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
using JsonConverter.System.Text.Json;
|
using JsonConverter.System.Text.Json;
|
||||||
using WireMock.Matchers;
|
using WireMock.Matchers;
|
||||||
|
using WireMock.Net.Xunit;
|
||||||
using WireMock.RequestBuilders;
|
using WireMock.RequestBuilders;
|
||||||
using WireMock.ResponseBuilders;
|
using WireMock.ResponseBuilders;
|
||||||
using WireMock.Server;
|
using WireMock.Server;
|
||||||
@@ -232,9 +233,13 @@ public partial class WireMockServerTests
|
|||||||
public async Task WireMockServer_WithBodyAsJson_Using_PostAsync_And_JsonPartialWildcardMatcher_And_SystemTextJson_ShouldMatch()
|
public async Task WireMockServer_WithBodyAsJson_Using_PostAsync_And_JsonPartialWildcardMatcher_And_SystemTextJson_ShouldMatch()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
using var server = WireMockServer.Start(x => x.DefaultJsonSerializer = new SystemTextJsonConverter());
|
using var server = WireMockServer.Start(settings =>
|
||||||
|
{
|
||||||
|
settings.Logger = new TestOutputHelperWireMockLogger(testOutputHelper);
|
||||||
|
settings.DefaultJsonSerializer = new SystemTextJsonConverter();
|
||||||
|
});
|
||||||
|
|
||||||
var matcher = new JsonPartialWildcardMatcher(new { id = "^[a-f0-9]{32}-[0-9]$" }, ignoreCase: true, regex: true);
|
var matcher = new SystemTextJsonPartialWildcardMatcher(new { id = "^[a-f0-9]{32}-[0-9]$" }, ignoreCase: true, regex: true);
|
||||||
server.Given(Request.Create()
|
server.Given(Request.Create()
|
||||||
.WithHeader("Content-Type", "application/json*")
|
.WithHeader("Content-Type", "application/json*")
|
||||||
.UsingPost()
|
.UsingPost()
|
||||||
|
|||||||
Reference in New Issue
Block a user