Compare commits

...
Author SHA1 Message Date
Stef Heyenrath 601a0773c1 Merge branch 'master' into dependabot/nuget/src/WireMock.Net.AspNetCore.Middleware/System.Text.Json-8.0.5 2026-10-18 09:48:16 +02:00
Mattias SundströmandMattias 0132ec873a Add ScribanRegistrationCallback to register custom Scriban functions (#1515) (#1516)
- Add WireMockServerSettings.ScribanRegistrationCallback
  (Action<object, IFileSystemHandler>?) so code-configured servers can add
  functions, objects or variables to Scriban and Scriban-DotLiquid templates.
- Pass the settings to ScribanContextFactory, which invokes the callback with
  a new ScriptObject per transformer context.
- Push the custom globals beneath the template model in ScribanContext so
  request, response, mapping and data take precedence; keep the original
  Template.Render call when no callback is configured.
- Add the strongly typed WithScribanRegistration extension method.
- Add a Scriban example to the .NET 8 console app.
- Make the proxy tests use a local target server instead of www.google.com,
  and skip the IPv6 loopback test when the OS has no IPv6 support.

Co-authored-by: Mattias <13809209+Quattordici@users.noreply.github.com>
2026-10-18 09:29:11 +02:00
Stef Heyenrath 64734c4f92 <VersionPrefix>2.19.0</VersionPrefix> 2026-10-07 18:25:30 +02:00
Stef Heyenrath a5589e1e2f 2.19.0 2026-10-07 18:22:51 +02:00
Stef HeyenrathandStef Heyenrath cb4503b1a6 Fix DateTime in Response (#1514)
* Fix DateTime in Response

* // Set the DateTime from the Response to the current DateTime

---------

Co-authored-by: Stef Heyenrath <stef.heyenrath@apg.nl>
2026-10-07 17:35:58 +02:00
Stef Heyenrath dd762f218b Use IDateTimeUtils in WireMockWebSocketContext (#1512) 2026-10-06 22:58:33 +02:00
Stef Heyenrath b0f9924933 Add example /test (#1513) 2026-10-06 22:51:17 +02:00
Stef Heyenrath dc4cdfae50 2.18.0 2026-09-22 22:21:50 +02:00
Stef Heyenrath f19d94acae 2.18.0 2026-09-22 22:18:47 +02:00
Stef Heyenrath 90819f031e WireMock.Net.OpenApiParser: fix depencency on RamlToOpenApiConverter.SourceOnly (#1510) 2026-09-22 22:16:13 +02:00
Stef Heyenrath 471745f704 2.17.0 2026-09-21 20:53:22 +02:00
Stef Heyenrath 7db5b562d8 Revert to RamlToOpenApiConverter.SourceOnly (#1509) 2026-09-21 20:50:49 +02:00
Stef Heyenrath e4389333a1 2.16.0 2026-09-19 07:54:10 +02:00
dependabot[bot] 2c33e21339 Bump System.Text.Json from 8.0.4 to 8.0.5
---
updated-dependencies:
- dependency-name: System.Text.Json
  dependency-version: 8.0.5
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-09-10 18:32:41 +00:00
Stef HeyenrathandCopilot Autofix powered by AI f0969e9618 Upgrade dependency RamlToOpenApiConverter to version 0.40.0 (#1505)
* Upgrade dependency RamlToOpenApiConverter to version 0.40.0

* fix code comment

* .

* Potential fix for pull request finding 'Constant condition'

Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>

* Potential fix for pull request finding 'Constant condition'

Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>

* fix

---------

Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
2026-09-10 20:28:43 +02:00
John Call 84f4109a7c Update SharpYaml package version to 2.1.5 (#1504)
Fix https://github.com/xoofx/SharpYaml/issues/130
2026-09-05 08:54:06 +02:00
ee0f890795 Fix FormUrlEncodedMatcher (+ refactor values / matchers logic) (#1503)
* Fix FormUrlEncodedMatcher

* tests

* Apply suggestions from code review

Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* fix comments

---------

Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-08-20 17:31:16 +02:00
Dmytro Nikitin 2b165eb6a1 Fix #1501: Generate WhenStateIs/WillSetStateTo correctly in ToCSharpCode (#1502)
* Fix #1501: Generate WhenStateIs/WillSetStateTo correctly in ToCSharpCode

* Fix matcher to code converters. Refactor changes.

* Adjust test artifacts.

* Return fully qualified class names for matchers
2026-08-19 19:58:47 +02:00
Stef Heyenrath 42353f035e 2.15.0 2026-08-15 09:51:50 +02:00
Stef Heyenrath 7e9718eab7 Update Testcontainers (#1499) 2026-08-14 16:55:57 +02:00
Dmytro Nikitin a0df0916f9 Fix #1496: WithParam RejectOnMatch inverting the match result (#1497) 2026-08-13 20:42:06 +02:00
Dmytro Nikitin 7a8582fef5 Fix #1494: WithClientIP(MatchOperator, IStringMatcher[]) ignores the operator (#1495) 2026-08-09 18:49:13 +02:00
Stef Heyenrath 05929bdea6 remove copilot-setup-steps.yml 2026-08-06 20:15:30 +02:00
121 changed files with 1948 additions and 469 deletions

No files matched your search

-36
View File
@@ -1,36 +0,0 @@
name: "Copilot Setup Steps"
# Automatically run the setup steps when they are changed to allow for easy validation, and
# allow manual testing through the repository's "Actions" tab
on:
workflow_dispatch:
push:
paths:
- .github/workflows/copilot-setup-steps.yml
pull_request:
paths:
- .github/workflows/copilot-setup-steps.yml
jobs:
# The job MUST be called `copilot-setup-steps` or it will not be picked up by Copilot.
copilot-setup-steps:
runs-on: ubuntu-latest
# Set the permissions to the lowest permissions possible needed for your steps.
# Copilot will be given its own token for its operations.
permissions:
# If you want to clone the repository as part of your setup steps, for example to install dependencies, you'll need the `contents: read` permission. If you don't clone the repository in your setup steps, Copilot will do this for you automatically after the steps complete.
contents: read
# You can define any steps you want, and they will run before the agent starts.
# If you do not check out your code, Copilot will do this for you.
steps:
- name: Install .NET 10.x
uses: actions/setup-dotnet@v5
with:
dotnet-version: |
10.x
dotnet-quality: preview
- name: dotnet --info
run: dotnet --info
+27
View File
@@ -1,3 +1,30 @@
# 2.19.0 (07 October 2026)
- [#1512](https://github.com/wiremock/WireMock.Net/pull/1512) - Use IDateTimeUtils in WireMockWebSocketContext [bug] contributed by [StefH](https://github.com/StefH)
- [#1514](https://github.com/wiremock/WireMock.Net/pull/1514) - Fix DateTime in Response [bug] contributed by [StefH](https://github.com/StefH)
- [#1511](https://github.com/wiremock/WireMock.Net/issues/1511) - Incorrect response datetime for mapped response [bug]
# 2.18.0 (22 September 2026)
- [#1510](https://github.com/wiremock/WireMock.Net/pull/1510) - WireMock.Net.OpenApiParser: fix depencency on RamlToOpenApiConverter.SourceOnly [bug] contributed by [StefH](https://github.com/StefH)
- [#1508](https://github.com/wiremock/WireMock.Net/issues/1508) - Transitive Dependency Conflict with Microsoft.OpenApi in 2.16.0 [bug]
# 2.17.0 (21 September 2026)
- [#1509](https://github.com/wiremock/WireMock.Net/pull/1509) - Revert to RamlToOpenApiConverter.SourceOnly [bug] contributed by [StefH](https://github.com/StefH)
# 2.16.0 (19 September 2026)
- [#1502](https://github.com/wiremock/WireMock.Net/pull/1502) - Fix #1501: Generate WhenStateIs/WillSetStateTo correctly in ToCSharpCode contributed by [Bafyn](https://github.com/Bafyn)
- [#1503](https://github.com/wiremock/WireMock.Net/pull/1503) - Fix FormUrlEncodedMatcher (+ refactor values / matchers logic) contributed by [StefH](https://github.com/StefH)
- [#1504](https://github.com/wiremock/WireMock.Net/pull/1504) - Update SharpYaml package to version 2.5.1 [dependencies] contributed by [johnthcall](https://github.com/johnthcall)
- [#1505](https://github.com/wiremock/WireMock.Net/pull/1505) - Upgrade dependency RamlToOpenApiConverter to version 0.40.0 [dependencies] contributed by [StefH](https://github.com/StefH)
- [#1500](https://github.com/wiremock/WireMock.Net/issues/1500) - Sending a form-urlencoded body with a repeated field name returns HTTP 500 [bug]
- [#1501](https://github.com/wiremock/WireMock.Net/issues/1501) - Generated C# code for stateful (Scenario) mappings does not compile and drops the WhenStateIs condition [bug]
# 2.15.0 (15 August 2026)
- [#1495](https://github.com/wiremock/WireMock.Net/pull/1495) - Fix #1494: `WithClientIP(MatchOperator, params IStringMatcher[])` ignores the MatchOperator argument [bug] contributed by [Bafyn](https://github.com/Bafyn)
- [#1497](https://github.com/wiremock/WireMock.Net/pull/1497) - Fix WithParam RejectOnMatch inverting the match result #1496 [bug] contributed by [Bafyn](https://github.com/Bafyn)
- [#1499](https://github.com/wiremock/WireMock.Net/pull/1499) - Update Testcontainers [bug] contributed by [StefH](https://github.com/StefH)
- [#1494](https://github.com/wiremock/WireMock.Net/issues/1494) - `WithClientIP(MatchOperator, params IStringMatcher[])` ignores the `MatchOperator` argument [bug]
- [#1496](https://github.com/wiremock/WireMock.Net/issues/1496) - `WithParam(key, MatchBehaviour.RejectOnMatch, values)` inverts the match result [bug]
# 2.14.0 (06 August 2026)
- [#1493](https://github.com/wiremock/WireMock.Net/pull/1493) - Fix #1492: Serialize WithClientIP matcher to Request.ClientIP [bug] contributed by [Bafyn](https://github.com/Bafyn)
- [#1492](https://github.com/wiremock/WireMock.Net/issues/1492) - `WithClientIP` matcher is lost / corrupted when a mapping is serialized [bug]
+2 -2
View File
@@ -4,7 +4,7 @@
</PropertyGroup>
<PropertyGroup>
<VersionPrefix>2.14.0</VersionPrefix>
<VersionPrefix>2.19.0</VersionPrefix>
<PackageIcon>WireMock.Net-Logo.png</PackageIcon>
<PackageProjectUrl>https://github.com/wiremock/WireMock.Net</PackageProjectUrl>
<PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
@@ -52,7 +52,7 @@
<ItemGroup>
<PackageReference Include="JetBrains.Annotations" Version="2026.2.0" PrivateAssets="All" />
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="10.0.301" PrivateAssets="All" />
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="10.0.401" PrivateAssets="All" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net8.0' ">
+1 -1
View File
@@ -1,6 +1,6 @@
rem https://github.com/StefH/GitHubReleaseNotes
SET version=2.14.0
SET version=2.19.0
GitHubReleaseNotes --output CHANGELOG.md --skip-empty-releases --exclude-labels wontfix test question invalid doc duplicate example environment --version %version% --token %GH_TOKEN%
+4 -3
View File
@@ -1,5 +1,6 @@
# 2.14.0 (06 August 2026)
- #1493 Fix #1492: Serialize WithClientIP matcher to Request.ClientIP [bug]
- #1492 `WithClientIP` matcher is lost / corrupted when a mapping is serialized [bug]
# 2.19.0 (07 October 2026)
- #1512 Use IDateTimeUtils in WireMockWebSocketContext [bug]
- #1514 Fix DateTime in Response [bug]
- #1511 Incorrect response datetime for mapped response [bug]
The full release notes can be found here: https://github.com/wiremock/WireMock.Net/blob/master/CHANGELOG.md
-2
View File
@@ -1,9 +1,7 @@
// Copyright © WireMock.Net
using System;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json;
using RestEase;
using WireMock.Admin.Settings;
@@ -1,6 +1,5 @@
// Copyright © WireMock.Net
using System.IO;
using WireMock.Logging;
using WireMock.Server;
using WireMock.Settings;
+26 -7
View File
@@ -1,14 +1,9 @@
// Copyright © WireMock.Net
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using Newtonsoft.Json;
using SharpYaml.Model;
using Scriban.Runtime;
using WireMock.Logging;
using WireMock.Matchers;
using WireMock.Models;
@@ -291,11 +286,21 @@ namespace WireMock.Net.ConsoleApplication
var server = WireMockServer.Start(new WireMockServerSettings
{
StartAdminInterface = true,
Logger = new WireMockConsoleLogger(),
Port = 9091
});
server
.WhenRequest(r => r
.WithPath("/test")
.UsingAnyMethod()
)
.ThenRespondWith(r => r
.WithStatusCode(HttpStatusCode.OK)
.WithBody("OK")
);
server
.WhenRequest(r => r
.WithPath("/Content-Length")
@@ -405,12 +410,26 @@ namespace WireMock.Net.ConsoleApplication
// handlebarsContext.RegisterHelper(transformer.Name, transformer.Render); TODO
},
ScribanRegistrationCallback = (scriptObject, fileSystemHandler) =>
{
var globals = (ScriptObject)scriptObject;
globals.Import("greet", new Func<string, string>(name => $"Hello {name}"));
},
// Uncomment below if you want to use the CustomFileSystemFileHandler
// FileSystemHandler = new CustomFileSystemFileHandler()
});
System.Console.WriteLine("WireMockServer listening at {0}", string.Join(",", server.Urls));
server.SetBasicAuthentication("a", "b");
server
.Given(Request.Create().WithPath("/scriban/greet").UsingGet())
.RespondWith(Response.Create()
.WithBody("{{ greet 'WireMock.Net' }}")
.WithTransformer(TransformerType.Scriban)
);
//server.SetAzureADAuthentication(Environment.GetEnvironmentVariable("WIREMOCK_AAD_TENANT")!, "api://e083d51a-01a6-446c-8ad5-0c5c7f002208");
//var http = new HttpClient();
@@ -1,9 +1,5 @@
// Copyright © WireMock.Net
using System;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using WireMock.Logging;
using WireMock.Net.StandAlone;
using WireMock.RequestBuilders;
@@ -16,7 +16,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="WireMock.Net" Version="2.12.0" />
<PackageReference Include="WireMock.Net" Version="2.15.0" />
</ItemGroup>
</Project>
@@ -1,7 +1,5 @@
// Copyright © WireMock.Net
using System;
using System.IO;
using WireMock.RequestBuilders;
using WireMock.ResponseBuilders;
@@ -6,15 +6,13 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.OpenApi.YamlReader" Version="3.7.0" />
<ProjectReference Include="..\..\src\WireMock.Net.Abstractions\WireMock.Net.Abstractions.csproj" />
<ProjectReference Include="..\..\src\WireMock.Net.OpenApiParser\WireMock.Net.OpenApiParser.csproj" />
<ProjectReference Include="..\..\src\WireMock.Net\WireMock.Net.csproj" />
</ItemGroup>
<ItemGroup Condition="'$(Configuration)' == 'Release'">
<PackageReference Include="Microsoft.OpenApi" Version="3.7.0" PrivateAssets="All" />
<PackageReference Include="Microsoft.OpenApi" Version="3.10.2" PrivateAssets="All" />
</ItemGroup>
<ItemGroup>
@@ -32,7 +32,7 @@ public interface IRequestMessage
string? ProxyUrl { get; set; }
/// <summary>
/// Gets the DateTime.
/// The DateTime when this request was done.
/// </summary>
DateTime DateTime { get; }
@@ -52,7 +52,7 @@ public interface IResponseMessage
object? StatusCode { get; set; }
/// <summary>
/// Gets the DateTime.
/// The DateTime when this response was sent.
/// </summary>
DateTime DateTime { get; }
@@ -46,7 +46,7 @@ public interface IBodyData
/// <summary>
/// The body as Form UrlEncoded dictionary.
/// </summary>
IDictionary<string, string>? BodyAsFormUrlEncoded { get; set; }
IDictionary<string, WireMockList<string>>? BodyAsFormUrlEncoded { get; set; }
/// <summary>
/// The detected body type (detection based on body content).
@@ -46,6 +46,98 @@ public class WireMockList<T> : List<T>
/// <param name="values">The values to set.</param>
public static implicit operator WireMockList<T>(T[] values) => new(values);
/// <summary>
/// Operator for equality comparison from WireMockList to T
/// </summary>
public static bool operator ==(WireMockList<T>? left, T? right)
{
if (ReferenceEquals(left, right))
{
return true;
}
if (left?.Count == 1 && Equals(left[0], right))
{
return true;
}
return false;
}
/// <summary>
/// Operator for equality comparison from T to WireMockList
/// </summary>
public static bool operator ==(T? left, WireMockList<T>? right)
{
if (ReferenceEquals(left, right))
{
return true;
}
if (right?.Count == 1 && Equals(left, right[0]))
{
return true;
}
return false;
}
/// <summary>
/// Operator for inequality comparison from WireMockList to T
/// </summary>
public static bool operator !=(WireMockList<T>? left, T? right) => !(left == right);
/// <summary>
/// Operator for inequality comparison from T to WireMockList
/// </summary>
public static bool operator !=(T? left, WireMockList<T>? right) => !(left == right);
/// <summary>
/// Determines whether the specified object is equal to the current instance.
/// Two <see cref="WireMockList{T}"/> instances are equal if they contain the same elements in the same order.
/// </summary>
public override bool Equals(object? obj)
{
if (ReferenceEquals(this, obj))
{
return true;
}
if (obj is WireMockList<T> other)
{
if (Count != other.Count)
{
return false;
}
for (var i = 0; i < Count; i++)
{
if (!Equals(this[i], other[i]))
{
return false;
}
}
return true;
}
return false;
}
/// <summary>
/// Returns a hash code for this instance based on its elements.
/// </summary>
public override int GetHashCode()
{
var hashCode = 17;
foreach (var item in this)
{
hashCode = hashCode * 31 + (item?.GetHashCode() ?? 0);
}
return hashCode;
}
/// <summary>
/// Returns a <see cref="string" /> that represents this instance.
/// </summary>
@@ -61,7 +153,7 @@ public class WireMockList<T> : List<T>
{
return strValue;
}
return this[0]?.ToString() ?? string.Empty;
default:
@@ -32,6 +32,7 @@
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.2" />
<PackageReference Include="System.Text.Json" Version="8.0.5" />
</ItemGroup>
<ItemGroup>
@@ -70,7 +70,7 @@ public class CSharpCodeMatcher : ICSharpCodeMatcher
/// <inheritdoc />
public string GetCSharpCodeArguments()
{
return $"new {Name}" +
return $"new {typeof(CSharpCodeMatcher).FullName}" +
$"(" +
$"{MatchBehaviour.GetFullyQualifiedEnumValue()}, " +
$"{MatchOperator.GetFullyQualifiedEnumValue()}, " +
@@ -126,7 +126,7 @@ public class SystemTextJsonPathMatcher : ISystemTextJsonPathMatcher
/// <inheritdoc />
public string GetCSharpCodeArguments()
{
return $"new {Name}" +
return $"new {typeof(SystemTextJsonPathMatcher).FullName}" +
$"(" +
$"{MatchBehaviour.GetFullyQualifiedEnumValue()}, " +
$"{MatchOperator.GetFullyQualifiedEnumValue()}, " +
@@ -0,0 +1,14 @@
// Copyright © WireMock.Net
using System.Diagnostics.CodeAnalysis;
using Microsoft.AspNetCore.Http;
namespace WireMock.Extensions;
internal static class HttpContextExtensions
{
internal static bool TryGet<T>(this HttpContext httpContext, [NotNullWhen(true)] out T? value)
{
return httpContext.Items.TryGetValue(nameof(T), out value);
}
}
@@ -80,10 +80,10 @@ public class ContentTypeMatcher : WildcardMatcher
/// <inheritdoc />
public override string GetCSharpCodeArguments()
{
return $"new {Name}" +
return $"new {typeof(ContentTypeMatcher).FullName}" +
$"(" +
$"{MatchBehaviour.GetFullyQualifiedEnumValue()}, " +
$"{MappingConverterUtils.ToCSharpCodeArguments(_patterns)}, " +
$"new AnyOfTypes.AnyOf<string, WireMock.Models.StringPattern>[] {{ {MappingConverterUtils.ToCSharpCodeArguments(_patterns)} }}, " +
$"{CSharpFormatter.ToCSharpBooleanLiteral(IgnoreCase)}" +
$")";
}
@@ -95,7 +95,7 @@ public class ExactMatcher : IStringMatcher, IIgnoreCaseMatcher
/// <inheritdoc />
public string GetCSharpCodeArguments()
{
return $"new {Name}" +
return $"new {typeof(ExactMatcher).FullName}" +
$"(" +
$"{MatchBehaviour.GetFullyQualifiedEnumValue()}, " +
$"{CSharpFormatter.ToCSharpBooleanLiteral(IgnoreCase)}, " +
@@ -1,10 +1,10 @@
// Copyright © WireMock.Net
using System.Linq;
using AnyOfTypes;
using Stef.Validation;
using WireMock.Extensions;
using WireMock.Models;
using WireMock.Types;
using WireMock.Util;
namespace WireMock.Matchers;
@@ -21,7 +21,7 @@ public class FormUrlEncodedMatcher : IStringMatcher, IIgnoreCaseMatcher
/// <inheritdoc />
public MatchBehaviour MatchBehaviour { get; }
private readonly List<(WildcardMatcher Key, WildcardMatcher? Value)> _pairs = [];
private readonly List<(WildcardMatcher Key, WildcardMatcher[]? Values)> KeyValueMatchers = [];
/// <summary>
/// Initializes a new instance of the <see cref="FormUrlEncodedMatcher"/> class.
@@ -91,9 +91,10 @@ public class FormUrlEncodedMatcher : IStringMatcher, IIgnoreCaseMatcher
{
foreach (var nameValue in nameValueCollection)
{
var keyMatcher = new WildcardMatcher(MatchBehaviour.AcceptOnMatch, [nameValue.Key], ignoreCase, MatchOperator);
var valueMatcher = new WildcardMatcher(MatchBehaviour.AcceptOnMatch, [nameValue.Value], ignoreCase, MatchOperator);
_pairs.Add((keyMatcher, valueMatcher));
var keyMatcher = new WildcardMatcher(MatchBehaviour.AcceptOnMatch, nameValue.Key, ignoreCase);
var valueMatchers = nameValue.Value.Select(value => new WildcardMatcher(MatchBehaviour.AcceptOnMatch, value, ignoreCase)).ToArray();
KeyValueMatchers.Add((keyMatcher, valueMatchers));
}
}
}
@@ -119,34 +120,48 @@ public class FormUrlEncodedMatcher : IStringMatcher, IIgnoreCaseMatcher
return MatchResult.From(Name, MatchBehaviourHelper.Convert(MatchBehaviour, score));
}
private bool[] GetMatches(IDictionary<string, string> inputNameValueCollection)
private List<double> GetMatches(IDictionary<string, WireMockList<string>> inputNameValueCollection)
{
var matches = new List<bool>();
if (_pairs.Count > inputNameValueCollection.Count)
var inputPairs = inputNameValueCollection.ToArray();
var rowCount = inputPairs.Length;
var columnCount = KeyValueMatchers.Count;
if (rowCount == 0 && columnCount == 0)
{
matches.AddRange(Enumerable.Repeat(false, _pairs.Count - inputNameValueCollection.Count));
return [];
}
foreach (var inputKeyValuePair in inputNameValueCollection)
var matrix = new double[rowCount][];
for (var row = 0; row < rowCount; row++)
{
var match = false;
foreach (var pair in _pairs)
matrix[row] = new double[columnCount];
var inputKeyValuePair = inputPairs[row];
var inputKey = inputKeyValuePair.Key;
var inputValues = inputKeyValuePair.Value;
for (var column = 0; column < columnCount; column++)
{
var keyMatchResult = pair.Key.IsMatch(inputKeyValuePair.Key).IsPerfect();
if (keyMatchResult)
{
match = pair.Value?.IsMatch(inputKeyValuePair.Value).IsPerfect() ?? false;
if (match)
{
break;
}
}
}
var (keyMatcher, valuesMatchers) = KeyValueMatchers[column];
matches.Add(match);
var keyScore = keyMatcher.IsMatch(inputKey).Score;
var valueScore = valuesMatchers != null ? MatchScores.ToScore(inputValues, valuesMatchers) : MatchScores.Perfect;
matrix[row][column] = Math.Min(keyScore, valueScore);
}
}
return matches.ToArray();
var rowScores = rowCount == 0 ? [] : matrix.Select(row => row.Length == 0 ? MatchScores.Mismatch : row.Max()).ToList();
var columnScores = new List<double>();
for (var column = 0; column < columnCount; column++)
{
columnScores.Add(rowCount == 0 ? MatchScores.Mismatch : matrix.Max(row => row[column]));
}
rowScores.AddRange(columnScores);
return rowScores;
}
/// <inheritdoc />
@@ -167,10 +182,10 @@ public class FormUrlEncodedMatcher : IStringMatcher, IIgnoreCaseMatcher
/// <inheritdoc />
public string GetCSharpCodeArguments()
{
return $"new {Name}" +
return $"new {typeof(FormUrlEncodedMatcher).FullName}" +
$"(" +
$"{MatchBehaviour.GetFullyQualifiedEnumValue()}, " +
$"{MappingConverterUtils.ToCSharpCodeArguments(_patterns)}, " +
$"new AnyOfTypes.AnyOf<string, WireMock.Models.StringPattern>[] {{ {MappingConverterUtils.ToCSharpCodeArguments(_patterns)} }}, " +
$"{CSharpFormatter.ToCSharpBooleanLiteral(IgnoreCase)}, " +
$"{MatchOperator.GetFullyQualifiedEnumValue()}" +
$")";
@@ -76,7 +76,7 @@ public class FuncMatcher : IFuncMatcher
public string GetCSharpCodeArguments()
{
var funcType = _stringFunc != null ? "Func<string?, bool>" : "Func<byte[]?, bool>";
return $"new {Name}" +
return $"new {typeof(FuncMatcher).FullName}" +
$"(" +
$"/* {funcType} function */, " +
$"{MatchBehaviour.GetFullyQualifiedEnumValue()}" +
@@ -119,7 +119,7 @@ public class JsonPathMatcher : IJsonPathMatcher
/// <inheritdoc />
public string GetCSharpCodeArguments()
{
return $"new {Name}" +
return $"new {typeof(JsonPathMatcher).FullName}" +
$"(" +
$"{MatchBehaviour.GetFullyQualifiedEnumValue()}, " +
$"{MatchOperator.GetFullyQualifiedEnumValue()}, " +
@@ -119,7 +119,7 @@ public class JmesPathMatcher : IStringMatcher, IObjectMatcher
/// <inheritdoc />
public string GetCSharpCodeArguments()
{
return $"new {Name}" +
return $"new {typeof(JmesPathMatcher).FullName}" +
$"(" +
$"{MatchBehaviour.GetFullyQualifiedEnumValue()}, " +
$"{MatchOperator.GetFullyQualifiedEnumValue()}, " +
@@ -110,7 +110,7 @@ public class JsonMatcher : IJsonMatcher
/// <inheritdoc />
public virtual string GetCSharpCodeArguments()
{
return $"new {Name}" +
return $"new {typeof(JsonMatcher).FullName}" +
$"(" +
$"{MatchBehaviour.GetFullyQualifiedEnumValue()}, " +
$"{CSharpFormatter.ConvertToAnonymousObjectDefinition(Value, 3)}, " +
@@ -41,7 +41,7 @@ public class JsonPartialMatcher : AbstractJsonPartialMatcher
/// <inheritdoc />
public override string GetCSharpCodeArguments()
{
return $"new {Name}" +
return $"new {typeof(JsonPartialMatcher).FullName}" +
$"(" +
$"{MatchBehaviour.GetFullyQualifiedEnumValue()}, " +
$"{CSharpFormatter.ConvertToAnonymousObjectDefinition(Value, 3)}, " +
@@ -41,7 +41,7 @@ public class JsonPartialWildcardMatcher : AbstractJsonPartialMatcher
/// <inheritdoc />
public override string GetCSharpCodeArguments()
{
return $"new {Name}" +
return $"new {typeof(JsonPartialWildcardMatcher).FullName}" +
$"(" +
$"{MatchBehaviour.GetFullyQualifiedEnumValue()}, " +
$"{CSharpFormatter.ConvertToAnonymousObjectDefinition(Value, 3)}, " +
@@ -141,7 +141,7 @@ public class LinqMatcher : IObjectMatcher, IStringMatcher
/// <inheritdoc />
public string GetCSharpCodeArguments()
{
return $"new {Name}" +
return $"new {typeof(LinqMatcher).FullName}" +
$"(" +
$"{MatchBehaviour.GetFullyQualifiedEnumValue()}, " +
$"{MatchOperator.GetFullyQualifiedEnumValue()}, " +
@@ -2,6 +2,7 @@
using Stef.Validation;
using WireMock.Matchers.Helpers;
using WireMock.Types;
using WireMock.Util;
namespace WireMock.Matchers.Request;
@@ -34,7 +35,7 @@ public class RequestMessageBodyMatcher : IRequestMatcher
/// <summary>
/// The body data function for FormUrlEncoded
/// </summary>
public Func<IDictionary<string, string>?, bool>? MatchOnBodyAsFormUrlEncodedFunc { get; }
public Func<IDictionary<string, WireMockList<string>>?, bool>? MatchOnBodyAsFormUrlEncodedFunc { get; }
/// <summary>
/// The matchers.
@@ -116,7 +117,7 @@ public class RequestMessageBodyMatcher : IRequestMatcher
/// Initializes a new instance of the <see cref="RequestMessageBodyMatcher"/> class.
/// </summary>
/// <param name="func">The function.</param>
public RequestMessageBodyMatcher(Func<IDictionary<string, string>?, bool> func)
public RequestMessageBodyMatcher(Func<IDictionary<string, WireMockList<string>>?, bool> func)
{
MatchOnBodyAsFormUrlEncodedFunc = Guard.NotNull(func);
}
@@ -1,6 +1,5 @@
// Copyright © WireMock.Net
using System.Linq;
using Stef.Validation;
using WireMock.Types;
@@ -54,7 +53,10 @@ public class RequestMessageParamMatcher : IRequestMatcher
/// <param name="ignoreCase">Defines if the key should be matched using case-ignore.</param>
/// <param name="values">The values.</param>
public RequestMessageParamMatcher(MatchBehaviour matchBehaviour, string key, bool ignoreCase, params string[]? values) :
this(matchBehaviour, key, ignoreCase, values?.Select(value => new ExactMatcher(matchBehaviour, ignoreCase, MatchOperator.And, value)).Cast<IStringMatcher>().ToArray())
// Note: the inner ExactMatcher must use AcceptOnMatch.
// The MatchBehaviour (e.g. RejectOnMatch) is applied once by this matcher's GetMatchingScore.
// Passing matchBehaviour here as well would apply the conversion twice and invert RejectOnMatch.
this(matchBehaviour, key, ignoreCase, values?.Select(value => new ExactMatcher(MatchBehaviour.AcceptOnMatch, ignoreCase, MatchOperator.And, value)).Cast<IStringMatcher>().ToArray())
{
}
@@ -113,36 +115,6 @@ public class RequestMessageParamMatcher : IRequestMatcher
}
// Return the score based on Matchers and valuesPresentInRequestMessage
return CalculateScore(Matchers, valuesPresentInRequestMessage);
}
private static double CalculateScore(IReadOnlyList<IStringMatcher> matchers, WireMockList<string> valuesPresentInRequestMessage)
{
var total = new List<double>();
// If the total patterns in all matchers > values in message, use the matcher as base
if (matchers.Sum(m => m.GetPatterns().Length) > valuesPresentInRequestMessage.Count)
{
foreach (var matcher in matchers)
{
double score = 0d;
foreach (string valuePresentInRequestMessage in valuesPresentInRequestMessage)
{
score += matcher.IsMatch(valuePresentInRequestMessage).Score / matcher.GetPatterns().Length;
}
total.Add(score);
}
}
else
{
foreach (string valuePresentInRequestMessage in valuesPresentInRequestMessage)
{
var score = matchers.Max(m => m.IsMatch(valuePresentInRequestMessage).Score);
total.Add(score);
}
}
return total.Any() ? MatchScores.ToScore(total, MatchOperator.Average) : 0;
return MatchScores.ToScore(valuesPresentInRequestMessage, Matchers.ToArray());
}
}
@@ -1,6 +1,5 @@
// Copyright © WireMock.Net
using System.Linq;
using AnyOfTypes;
using SimMetrics.Net;
using SimMetrics.Net.API;
@@ -92,10 +91,10 @@ public class SimMetricsMatcher : IStringMatcher
/// <inheritdoc />
public virtual string GetCSharpCodeArguments()
{
return $"new {Name}" +
return $"new {typeof(SimMetricsMatcher).FullName}" +
$"(" +
$"{MatchBehaviour.GetFullyQualifiedEnumValue()}, " +
$"{MappingConverterUtils.ToCSharpCodeArguments(_patterns)}, " +
$"new AnyOfTypes.AnyOf<string, WireMock.Models.StringPattern>[] {{ {MappingConverterUtils.ToCSharpCodeArguments(_patterns)} }}, " +
$"{_simMetricType.GetFullyQualifiedEnumValue()}, " +
$"{MatchOperator.GetFullyQualifiedEnumValue()}" +
$")";
@@ -115,7 +115,7 @@ public class SystemTextJsonMatcher : IJsonMatcher
/// <inheritdoc />
public virtual string GetCSharpCodeArguments()
{
return $"new {Name}" +
return $"new {typeof(SystemTextJsonMatcher).FullName}" +
$"(" +
$"{MatchBehaviour.GetFullyQualifiedEnumValue()}, " +
$"{CSharpFormatter.ConvertToAnonymousObjectDefinition(Value, 3)}, " +
@@ -41,7 +41,7 @@ public class SystemTextJsonPartialMatcher : AbstractSystemTextJsonPartialMatcher
/// <inheritdoc />
public override string GetCSharpCodeArguments()
{
return $"new {Name}" +
return $"new {typeof(SystemTextJsonPartialMatcher).FullName}" +
$"(" +
$"{MatchBehaviour.GetFullyQualifiedEnumValue()}, " +
$"{CSharpFormatter.ConvertToAnonymousObjectDefinition(Value, 3)}, " +
@@ -41,7 +41,7 @@ public class SystemTextJsonPartialWildcardMatcher : AbstractSystemTextJsonPartia
/// <inheritdoc />
public override string GetCSharpCodeArguments()
{
return $"new {Name}" +
return $"new {typeof(SystemTextJsonPartialWildcardMatcher).FullName}" +
$"(" +
$"{MatchBehaviour.GetFullyQualifiedEnumValue()}, " +
$"{CSharpFormatter.ConvertToAnonymousObjectDefinition(Value, 3)}, " +
@@ -101,7 +101,7 @@ public class XPathMatcher : IStringMatcher
/// <inheritdoc />
public string GetCSharpCodeArguments()
{
return $"new {Name}" +
return $"new {typeof(XPathMatcher).FullName}" +
$"(" +
$"{MatchBehaviour.GetFullyQualifiedEnumValue()}, " +
$"{MatchOperator.GetFullyQualifiedEnumValue()}, " +
@@ -12,7 +12,7 @@ namespace WireMock.Owin.Mappers;
/// <summary>
/// OwinRequestMapper
/// </summary>
internal class OwinRequestMapper : IOwinRequestMapper
internal class OwinRequestMapper(IDateTimeUtils dateTimeUtils) : IOwinRequestMapper
{
/// <inheritdoc />
public async Task<RequestMessage> MapAsync(HttpContext context, IWireMockMiddlewareOptions options)
@@ -72,7 +72,7 @@ internal class OwinRequestMapper : IOwinRequestMapper
await request.HttpContext.Connection.GetClientCertificateAsync()
)
{
DateTime = DateTime.UtcNow
DateTime = dateTimeUtils.UtcNow
};
}
@@ -171,6 +171,12 @@ internal class WireMockMiddleware(
}
finally
{
// Set the DateTime from the Response to the current DateTime
if (response is ResponseMessage responseMessage)
{
responseMessage.DateTime = dateTimeUtils.UtcNow;
}
logger.LogRequestAndResponse(logRequest, request, response, result.Match, result.Partial, activity);
try
@@ -255,4 +261,4 @@ internal class WireMockMiddleware(
scenario.Finished = mapping.NextState == null;
});
}
}
}
@@ -73,20 +73,20 @@ internal class WireMockMiddlewareLogger(
var logEntries = options.LogEntries.ToList();
foreach (var logEntry in logEntries
.OrderBy(le => le.RequestMessage?.DateTime ?? le.ResponseMessage?.DateTime)
.OrderBy(le => le.RequestMessage?.DateTime)
.Take(logEntries.Count - options.MaxRequestLogCount.Value))
{
TryRemoveLogEntry(logEntry);
}
}
// In case RequestLogExpirationDuration has a value greater than 0, try to delete existing request logs based on the date.
// In case RequestLogExpirationDuration has a value greater than 0, try to delete existing request logs based on the RequestMessage date.
if (options.RequestLogExpirationDuration is > 0)
{
var logEntries = options.LogEntries.ToList();
var checkTime = dateTimeUtils.UtcNow.AddHours(-options.RequestLogExpirationDuration.Value);
foreach (var logEntry in logEntries.Where(le => le.RequestMessage?.DateTime < checkTime || le.ResponseMessage?.DateTime < checkTime))
foreach (var logEntry in logEntries.Where(le => le.RequestMessage?.DateTime < checkTime))
{
TryRemoveLogEntry(logEntry);
}
@@ -19,7 +19,7 @@ public partial class Request
{
Guard.NotNullOrEmpty(matchers);
_requestMatchers.Add(new RequestMessageClientIPMatcher(MatchBehaviour.AcceptOnMatch, MatchOperator.Or, matchers));
_requestMatchers.Add(new RequestMessageClientIPMatcher(MatchBehaviour.AcceptOnMatch, matchOperator, matchers));
return this;
}
@@ -5,6 +5,7 @@
using Stef.Validation;
using WireMock.Matchers;
using WireMock.Matchers.Request;
using WireMock.Types;
using WireMock.Util;
namespace WireMock.RequestBuilders;
@@ -84,7 +85,7 @@ public partial class Request
}
/// <inheritdoc />
public IRequestBuilder WithBody(Func<IDictionary<string, string>?, bool> func)
public IRequestBuilder WithBody(Func<IDictionary<string, WireMockList<string>>?, bool> func)
{
_requestMatchers.Add(new RequestMessageBodyMatcher(Guard.NotNull(func)));
return this;
@@ -87,7 +87,7 @@ public partial class Response : IResponseBuilder
{
var message = responseMessage ?? new ResponseMessage
{
DateTime = DateTime.UtcNow // We set it here to the current time.
DateTime = DateTime.UtcNow // Initially, this is set to the current time, but will be updated to the real time once the response has been sent.
};
return new Response(message);
@@ -2,7 +2,6 @@
// This source file is based on mock4net by Alexandre Victoor which is licensed under the Apache 2.0 License.
// For more details see 'mock4net/LICENSE.txt' and 'mock4net/readme.md' in this project root.
using System.Linq;
using Stef.Validation;
using WireMock.ResponseBuilders;
using WireMock.Types;
@@ -67,7 +67,8 @@ internal class WebSocketResponseProvider(WebSocketBuilder builder, IGuidUtils gu
builder,
options,
logger,
guidUtils
guidUtils,
dateTimeUtils
);
// Add to registry
@@ -146,7 +146,7 @@ internal class MappingConverter(MatcherMapper mapper)
case JsonMatcher jsonMatcher:
{
var matcherType = jsonMatcher.GetType().Name;
var matcherType = jsonMatcher.GetType().FullName;
sb.AppendLine($" .WithBody(new {matcherType}(");
sb.AppendLine($" value: {ConvertToAnonymousObjectDefinition(jsonMatcher.Value, 3)},");
sb.AppendLine($" ignoreCase: {ToCSharpBooleanLiteral(jsonMatcher.IgnoreCase)},");
@@ -162,14 +162,18 @@ internal class MappingConverter(MatcherMapper mapper)
// Guid
sb.AppendLine($" .WithGuid(\"{mapping.Guid}\")");
// Scenario + State
// Scenario, ExecutionConditionState, NextState and StateTimes
if (!string.IsNullOrEmpty(mapping.Scenario))
{
sb.AppendLine($" .InScenario({ToCSharpStringLiteral(mapping.Scenario)})");
}
if (!string.IsNullOrEmpty(mapping.ExecutionConditionState))
{
sb.AppendLine($" .WhenStateIs({ToCSharpStringLiteral(mapping.ExecutionConditionState)})");
}
if (!string.IsNullOrEmpty(mapping.NextState))
{
sb.AppendLine($" .WhenStateIs({ToCSharpStringLiteral(mapping.NextState)}, {ToCSharpIntLiteral(mapping.TimesInSameState)})");
sb.AppendLine($" .WillSetStateTo({ToCSharpStringLiteral(mapping.NextState)}, {ToCSharpIntLiteral(mapping.TimesInSameState)})");
}
if (mapping.Probability != null)
@@ -0,0 +1,40 @@
// Copyright © WireMock.Net
using JetBrains.Annotations;
using Scriban.Runtime;
using Stef.Validation;
using WireMock.Handlers;
namespace WireMock.Settings;
/// <summary>
/// Extension methods for <see cref="WireMockServerSettings"/>.
/// </summary>
public static class WireMockServerSettingsExtensions
{
/// <summary>
/// Adds a strongly typed Scriban registration to <see cref="WireMockServerSettings.ScribanRegistrationCallback"/>.
/// </summary>
/// <remarks>
/// Members added to the <see cref="ScriptObject"/> are available as globals in Scriban and Scriban-DotLiquid templates.
/// When a callback is already configured, it is kept and invoked before <paramref name="registrationCallback"/>.
/// </remarks>
/// <param name="settings">The settings.</param>
/// <param name="registrationCallback">The action which registers functions, objects or variables on the <see cref="ScriptObject"/>.</param>
/// <returns>The same <see cref="WireMockServerSettings"/> instance.</returns>
[PublicAPI]
public static WireMockServerSettings WithScribanRegistration(this WireMockServerSettings settings, Action<ScriptObject, IFileSystemHandler> registrationCallback)
{
Guard.NotNull(settings);
Guard.NotNull(registrationCallback);
var existingCallback = settings.ScribanRegistrationCallback;
settings.ScribanRegistrationCallback = (scriptObject, fileSystemHandler) =>
{
existingCallback?.Invoke(scriptObject, fileSystemHandler);
registrationCallback((ScriptObject)scriptObject, fileSystemHandler);
};
return settings;
}
}
@@ -1,6 +1,7 @@
// Copyright © WireMock.Net
using Scriban;
using Scriban.Runtime;
using Stef.Validation;
using WireMock.Handlers;
using WireMock.Types;
@@ -9,21 +10,40 @@ namespace WireMock.Transformers.Scriban;
internal class ScribanContext : ITransformerContext
{
private static readonly MemberRenamerDelegate MemberRenamer = member => member.Name;
private readonly TransformerType _transformerType;
private readonly ScriptObject? _customGlobals;
public IFileSystemHandler FileSystemHandler { get; }
public ScribanContext(IFileSystemHandler fileSystemHandler, TransformerType transformerType)
public ScribanContext(IFileSystemHandler fileSystemHandler, TransformerType transformerType, ScriptObject? customGlobals = null)
{
FileSystemHandler = Guard.NotNull(fileSystemHandler);
_transformerType = transformerType;
_customGlobals = customGlobals;
}
public string ParseAndRender(string text, object model)
{
var template = _transformerType == TransformerType.ScribanDotLiquid ? Template.ParseLiquid(text) : Template.Parse(text);
var isLiquid = _transformerType == TransformerType.ScribanDotLiquid;
var template = isLiquid ? Template.ParseLiquid(text) : Template.Parse(text);
return template.Render(model, member => member.Name);
if (_customGlobals is null)
{
return template.Render(model, MemberRenamer);
}
// Same as Template.Render(model, MemberRenamer), but with the custom globals pushed beneath the model so that the model takes precedence.
var context = isLiquid ? new LiquidTemplateContext() : new TemplateContext();
context.MemberRenamer = MemberRenamer;
context.PushGlobal(_customGlobals);
var modelGlobals = new ScriptObject();
modelGlobals.Import(model, renamer: MemberRenamer);
context.PushGlobal(modelGlobals);
return template.Render(context);
}
public object? ParseAndEvaluate(string text, object model)
@@ -1,24 +1,32 @@
// Copyright © WireMock.Net
using WireMock.Handlers;
using WireMock.Types;
using Scriban.Runtime;
using Stef.Validation;
using WireMock.Settings;
using WireMock.Types;
namespace WireMock.Transformers.Scriban;
internal class ScribanContextFactory : ITransformerContextFactory
{
private readonly IFileSystemHandler _fileSystemHandler;
private readonly WireMockServerSettings _settings;
private readonly TransformerType _transformerType;
public ScribanContextFactory(IFileSystemHandler fileSystemHandler, TransformerType transformerType)
public ScribanContextFactory(WireMockServerSettings settings, TransformerType transformerType)
{
_fileSystemHandler = Guard.NotNull(fileSystemHandler);
_settings = Guard.NotNull(settings);
_transformerType = Guard.Condition(transformerType, t => t is TransformerType.Scriban or TransformerType.ScribanDotLiquid);
}
public ITransformerContext Create()
{
return new ScribanContext(_fileSystemHandler, _transformerType);
ScriptObject? customGlobals = null;
if (_settings.ScribanRegistrationCallback is { } registrationCallback)
{
customGlobals = new ScriptObject();
registrationCallback(customGlobals, _settings.FileSystemHandler);
}
return new ScribanContext(_settings.FileSystemHandler, _transformerType, customGlobals);
}
}
@@ -19,8 +19,8 @@ internal static class TransformerFactory
case TransformerType.Scriban:
case TransformerType.ScribanDotLiquid:
var factoryDotLiquid = new ScribanContextFactory(settings.FileSystemHandler, transformerType);
return new Transformer(settings, factoryDotLiquid);
var factoryScriban = new ScribanContextFactory(settings, transformerType);
return new Transformer(settings, factoryScriban);
default:
throw new NotSupportedException($"{nameof(TransformerType)} '{transformerType}' is not supported.");
@@ -18,6 +18,8 @@ namespace WireMock.WebSockets;
/// </summary>
public class WireMockWebSocketContext : IWebSocketContext
{
private readonly IDateTimeUtils _dateTimeUtils;
/// <inheritdoc />
public Guid ConnectionId { get; }
@@ -50,7 +52,8 @@ public class WireMockWebSocketContext : IWebSocketContext
WebSocketBuilder builder,
IWireMockMiddlewareOptions options,
IWireMockMiddlewareLogger logger,
IGuidUtils guidUtils
IGuidUtils guidUtils,
IDateTimeUtils dateTimeUtils
)
{
HttpContext = httpContext;
@@ -61,6 +64,7 @@ public class WireMockWebSocketContext : IWebSocketContext
Builder = builder;
Options = options;
Logger = logger;
_dateTimeUtils = dateTimeUtils;
ConnectionId = guidUtils.NewGuid();
}
@@ -169,7 +173,7 @@ public class WireMockWebSocketContext : IWebSocketContext
null
)
{
DateTime = DateTime.UtcNow
DateTime = _dateTimeUtils.UtcNow
};
}
else
@@ -179,7 +183,7 @@ public class WireMockWebSocketContext : IWebSocketContext
{
Method = method,
BodyData = bodyData,
DateTime = DateTime.UtcNow
DateTime = _dateTimeUtils.UtcNow
};
}
@@ -1,10 +1,10 @@
// Copyright © WireMock.Net
#if NETSTANDARD2_0
namespace System.Collections.Generic;
internal static class DictionaryExtensions
{
#if NETSTANDARD2_0
public static bool TryAdd<TKey, TValue>(this Dictionary<TKey, TValue>? dictionary, TKey key, TValue value)
{
if (dictionary is null || dictionary.ContainsKey(key))
@@ -16,5 +16,29 @@ internal static class DictionaryExtensions
return true;
}
}
#endif
#endif
public static bool TryGetFirstValue<TKey, TValue>(this IDictionary<TKey, TValue>? dictionary, out TValue? value)
{
if (dictionary != null && dictionary.Count > 0)
{
value = dictionary.First().Value;
return true;
}
value = default;
return false;
}
public static bool TryGetFirstOrDefault<TKey, TValue>(this IDictionary<TKey, TValue>? dictionary, out KeyValuePair<TKey, TValue> value)
{
if (dictionary != null && dictionary.Count > 0)
{
value = dictionary.First();
return true;
}
value = default;
return false;
}
}
@@ -1,7 +1,5 @@
// Copyright © WireMock.Net
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text.Json;
using System.Text.Json.Nodes;
@@ -56,37 +54,25 @@ internal static class OpenApiSchemaExtensions
public static SchemaFormat GetSchemaFormat(this IOpenApiSchema? schema)
{
switch (schema?.Format)
return (schema?.Format) switch
{
case "float":
return SchemaFormat.Float;
"float" => SchemaFormat.Float,
"double" => SchemaFormat.Double,
"int32" => SchemaFormat.Int32,
"int64" => SchemaFormat.Int64,
"date" => SchemaFormat.Date,
"date-time" => SchemaFormat.DateTime,
"password" => SchemaFormat.Password,
"byte" => SchemaFormat.Byte,
"binary" => SchemaFormat.Binary,
_ => SchemaFormat.Undefined,
};
}
case "double":
return SchemaFormat.Double;
case "int32":
return SchemaFormat.Int32;
case "int64":
return SchemaFormat.Int64;
case "date":
return SchemaFormat.Date;
case "date-time":
return SchemaFormat.DateTime;
case "password":
return SchemaFormat.Password;
case "byte":
return SchemaFormat.Byte;
case "binary":
return SchemaFormat.Binary;
default:
return SchemaFormat.Undefined;
}
internal static JsonNode? FindFirstExample(this IOpenApiSchema? schema)
{
#pragma warning disable CS0618 // Type or member is obsolete
return schema?.Examples?.FirstOrDefault() ?? schema?.Example;
#pragma warning restore CS0618 // Type or member is obsolete
}
}
@@ -1,7 +1,5 @@
// Copyright © WireMock.Net
using System.IO;
using System.Linq;
using JetBrains.Annotations;
using Stef.Validation;
using WireMock.Net.OpenApiParser.Models;
@@ -16,7 +14,8 @@ namespace WireMock.Net.OpenApiParser.Extensions;
public static class WireMockServerExtensions
{
/// <summary>
/// Register the mappings via an OpenAPI (swagger) V2/V3/V3.1 file.
/// Register the mappings via an OpenAPI (swagger) V2/V3/V3.1/V3.2 file.
/// In case of an error, no mappings are registered and <see cref="OpenApiDiagnostic"/> contains the error details.
/// </summary>
/// <param name="server">The WireMockServer instance</param>
/// <param name="path">Path containing OpenAPI file to parse and use the mappings.</param>
@@ -28,7 +27,8 @@ public static class WireMockServerExtensions
}
/// <summary>
/// Register the mappings via an OpenAPI (swagger) V2/V3/V3.1 file.
/// Register the mappings via an OpenAPI (swagger) V2/V3/V3.1/V3.2 file.
/// In case of an error, no mappings are registered and <see cref="OpenApiDiagnostic"/> contains the error details.
/// </summary>
/// <param name="server">The WireMockServer instance</param>
/// <param name="path">Path containing OpenAPI file to parse and use the mappings.</param>
@@ -46,7 +46,8 @@ public static class WireMockServerExtensions
}
/// <summary>
/// Register the mappings via an OpenAPI (swagger) V2/V3/V3.1 stream.
/// Register the mappings via an OpenAPI (swagger) V2/V3/V3.1/V3.2 stream.
/// In case of an error, no mappings are registered and <see cref="OpenApiDiagnostic"/> contains the error details.
/// </summary>
/// <param name="server">The WireMockServer instance</param>
/// <param name="stream">Stream containing OpenAPI description to parse and use the mappings.</param>
@@ -58,7 +59,8 @@ public static class WireMockServerExtensions
}
/// <summary>
/// Register the mappings via an OpenAPI (swagger) V2/V3/V3.1 stream.
/// Register the mappings via an OpenAPI (swagger) V2/V3/V3.1/V3.2 stream.
/// In case of an error, no mappings are registered and <see cref="OpenApiDiagnostic"/> contains the error details.
/// </summary>
/// <param name="server">The WireMockServer instance</param>
/// <param name="stream">Stream containing OpenAPI description to parse and use the mappings.</param>
@@ -77,7 +79,8 @@ public static class WireMockServerExtensions
}
/// <summary>
/// Register the mappings via an OpenAPI (swagger) V2/V3/V3.1 document.
/// Register the mappings via an OpenAPI (swagger) V2/V3/V3.1/V3.2 document.
/// In case of an error, no mappings are registered and <see cref="OpenApiDiagnostic"/> contains the error details.
/// </summary>
/// <param name="server">The WireMockServer instance</param>
/// <param name="document">The OpenAPI document to use as mappings.</param>
@@ -9,11 +9,11 @@
<InputAssemblies Include="@(ReferencePathWithRefAssemblies)" Condition="'%(filename)' == 'Microsoft.OpenApi'" />
<LibraryPath Include="%(ReferencePathWithRefAssemblies.RelativeDir)" />
</ItemGroup>
<ItemGroup>
<DoNotInternalizeAssemblies Include="WireMock.Net.OpenApiParser" />
</ItemGroup>
<ILRepack
Parallel="true"
Internalize="true"
@@ -1,7 +1,5 @@
// Copyright © WireMock.Net
using System.Collections.Generic;
using System.IO;
using WireMock.Admin.Mappings;
using WireMock.Net.OpenApiParser.Models;
using WireMock.Net.OpenApiParser.Settings;
@@ -15,6 +13,7 @@ public interface IWireMockOpenApiParser
{
/// <summary>
/// Generate <see cref="IReadOnlyList{MappingModel}"/> from a file-path.
/// In case of an error, an empty list is returned and <see cref="OpenApiDiagnostic"/> contains the error details.
/// </summary>
/// <param name="path">The path to read the OpenApi/Swagger/V2/V3/V31 or Raml file.</param>
/// <param name="diagnostic">OpenApiDiagnostic output</param>
@@ -23,6 +22,7 @@ public interface IWireMockOpenApiParser
/// <summary>
/// Generate <see cref="IReadOnlyList{MappingModel}"/> from a file-path.
/// In case of an error, an empty list is returned and <see cref="OpenApiDiagnostic"/> contains the error details.
/// </summary>
/// <param name="path">The path to read the OpenApi/Swagger/V2/V3/V31 or Raml file.</param>
/// <param name="settings">Additional settings</param>
@@ -40,6 +40,7 @@ public interface IWireMockOpenApiParser
/// <summary>
/// Generate <see cref="IReadOnlyList{MappingModel}"/> from a <seealso cref="Stream"/>.
/// In case of an error, an empty list is returned and <see cref="OpenApiDiagnostic"/> contains the error details.
/// </summary>
/// <param name="stream">The source stream</param>
/// <param name="diagnostic">OpenApiDiagnostic output</param>
@@ -48,6 +49,7 @@ public interface IWireMockOpenApiParser
/// <summary>
/// Generate <see cref="IReadOnlyList{MappingModel}"/> from a <seealso cref="Stream"/>.
/// In case of an error, an empty list is returned and <see cref="OpenApiDiagnostic"/> contains the error details.
/// </summary>
/// <param name="stream">The source stream</param>
/// <param name="settings">Additional settings</param>
@@ -57,6 +59,7 @@ public interface IWireMockOpenApiParser
/// <summary>
/// Generate <see cref="IReadOnlyList{MappingModel}"/> from a <seealso cref="string"/>.
/// In case of an error, an empty list is returned and <see cref="OpenApiDiagnostic"/> contains the error details.
/// </summary>
/// <param name="text">The source text</param>
/// <param name="diagnostic">OpenApiDiagnostic output</param>
@@ -65,6 +68,7 @@ public interface IWireMockOpenApiParser
/// <summary>
/// Generate <see cref="IReadOnlyList{MappingModel}"/> from a <seealso cref="string"/>.
/// In case of an error, an empty list is returned and <see cref="OpenApiDiagnostic"/> contains the error details.
/// </summary>
/// <param name="text">The source text</param>
/// <param name="settings">Additional settings</param>
@@ -53,7 +53,7 @@ internal class OpenApiPathsMapper(WireMockOpenApiParserSettings settings)
Headers = MapRequestHeaders(requestHeaders),
Body = GetRequestBodyModel(operation.RequestBody)
},
Response = GetResponseModel(operation.Responses?.FirstOrDefault())
Response = operation.Responses?.TryGetFirstOrDefault(out var firstResponse) == true ? GetResponseModel(firstResponse) : new ResponseModel { StatusCode = 200 }
};
}
@@ -70,8 +70,7 @@ internal class OpenApiPathsMapper(WireMockOpenApiParserSettings settings)
var requestExample = requestContent?.Example;
var requestExamples = requestContent?.Examples;
var requestSchemaExample = requestContent?.Schema?.Example;
var requestSchemaExamples = requestContent?.Schema?.Examples;
var requestSchemaExample = requestContent?.Schema.FindFirstExample();
JsonNode? request;
if (requestExample != null)
@@ -86,10 +85,6 @@ internal class OpenApiPathsMapper(WireMockOpenApiParserSettings settings)
{
request = requestExamples.FirstOrDefault().Value.Value;
}
else if (requestSchemaExamples != null)
{
request = requestSchemaExamples.FirstOrDefault();
}
else
{
var requestSchema = content?.FirstOrDefault().Value.Schema;
@@ -99,16 +94,15 @@ internal class OpenApiPathsMapper(WireMockOpenApiParserSettings settings)
return MapRequestBody(request) ?? new BodyModel();
}
private ResponseModel GetResponseModel(KeyValuePair<string, IOpenApiResponse>? openApiResponse)
private ResponseModel GetResponseModel(KeyValuePair<string, IOpenApiResponse> openApiResponse)
{
var content = openApiResponse?.Value.Content;
var content = openApiResponse.Value.Content;
TryGetContent(content, out var responseContent, out var contentType);
var responseExample = responseContent?.Example;
var responseExamples = responseContent?.Examples;
var responseSchemaExample = responseContent?.Schema?.Example;
var responseSchemaExamples = responseContent?.Schema?.Examples;
var responseSchemaExample = responseContent?.Schema.FindFirstExample();
JsonNode? response;
if (responseExample != null)
@@ -119,24 +113,19 @@ internal class OpenApiPathsMapper(WireMockOpenApiParserSettings settings)
{
response = responseSchemaExample;
}
else if (responseExamples != null)
else if (responseExamples.TryGetFirstValue(out var firstResponseFromExamples))
{
response = responseExamples.FirstOrDefault().Value.Value;
}
else if (responseSchemaExamples != null)
{
response = responseSchemaExamples.FirstOrDefault();
response = firstResponseFromExamples?.Value;
}
else
{
var responseSchema = content?.FirstOrDefault().Value?.Schema;
response = MapSchemaToObject(responseSchema);
response = content.TryGetFirstValue(out var firstContent) ? MapSchemaToObject(firstContent?.Schema) : null;
}
return new ResponseModel
{
StatusCode = int.TryParse(openApiResponse?.Key, out var httpStatusCode) ? httpStatusCode : 200,
Headers = MapHeaders(contentType, openApiResponse?.Value.Headers),
StatusCode = int.TryParse(openApiResponse.Key, out var httpStatusCode) ? httpStatusCode : 200,
Headers = MapHeaders(contentType, openApiResponse.Value.Headers),
BodyAsJson = response != null ? JsonConvert.DeserializeObject(SystemTextJsonSerializer.Serialize(response)) : null
};
}
@@ -1,9 +1,6 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
using System.Collections.Generic;
using RamlToOpenApiConverter;
namespace WireMock.Net.OpenApiParser.Models;
/// <summary>
@@ -24,5 +21,5 @@ public class OpenApiDiagnostic
/// <summary>
/// Open API specification version of the document parsed.
/// </summary>
public OpenApiSpecificationVersion SpecificationVersion { get; set; }
public OpenApiSpecVersion SpecificationVersion { get; set; }
}
@@ -1,7 +1,5 @@
// Copyright © WireMock.Net
using System.Linq;
using RamlToOpenApiConverter;
using MicrosoftOpenApiDiagnostic = Microsoft.OpenApi.Reader.OpenApiDiagnostic;
namespace WireMock.Net.OpenApiParser.Models;
@@ -19,7 +17,7 @@ internal static class OpenApiMapper
{
Errors = openApiDiagnostic.Errors.Select(e => new OpenApiError(e.Pointer, e.Message)).ToList(),
Warnings = openApiDiagnostic.Warnings.Select(e => new OpenApiError(e.Pointer, e.Message)).ToList(),
SpecificationVersion = (OpenApiSpecificationVersion)openApiDiagnostic.SpecificationVersion
SpecificationVersion = (OpenApiSpecVersion)openApiDiagnostic.SpecificationVersion
};
}
}
@@ -0,0 +1,29 @@
// Copyright © WireMock.Net
namespace WireMock.Net.OpenApiParser.Models;
/// <summary>
/// Represents versions of OpenAPI specification.
/// </summary>
public enum OpenApiSpecVersion
{
/// <summary>
/// Represents OpenAPI V2.0 spec
/// </summary>
OpenApi2_0,
/// <summary>
/// Represents all patches of OpenAPI V3.0 spec (e.g. 3.0.0, 3.0.1)
/// </summary>
OpenApi3_0,
/// <summary>
/// Represents OpenAPI V3.1 spec
/// </summary>
OpenApi3_1,
/// <summary>
/// Represents OpenAPI V3.2 spec
/// </summary>
OpenApi3_2
}
@@ -1,6 +1,5 @@
// Copyright © WireMock.Net
using System;
using Microsoft.OpenApi;
namespace WireMock.Net.OpenApiParser.Settings;
@@ -1,6 +1,5 @@
// Copyright © WireMock.Net
using System;
using Microsoft.OpenApi;
using RandomDataGenerator.FieldOptions;
using RandomDataGenerator.Randomizers;
@@ -1,6 +1,5 @@
// Copyright © WireMock.Net
using System;
using Microsoft.OpenApi;
namespace WireMock.Net.OpenApiParser.Settings;
@@ -1,6 +1,5 @@
// Copyright © WireMock.Net
using System;
using System.Globalization;
namespace WireMock.Net.OpenApiParser.Utils;
@@ -1,7 +1,5 @@
// Copyright © WireMock.Net
using System;
using System.Linq;
using System.Text.Json.Nodes;
using Microsoft.OpenApi;
using Stef.Validation;
@@ -39,7 +37,7 @@ internal class ExampleValueGenerator
public JsonNode GetExampleValue(IOpenApiSchema? schema)
{
var schemaExample = schema?.Example;
var schemaExample = schema?.FindFirstExample();
var schemaEnum = schema?.Enum?.FirstOrDefault();
_exampleValues.Schema = schema;
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Description>An OpenApi (swagger) parser to generate MappingModel or mapping.json file.</Description>
@@ -23,7 +23,7 @@
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
</PropertyGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard2.0' or '$(TargetFramework)' == 'netstandard2.1' ">
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard2.0' ">
<PackageReference Include="Required" Version="1.0.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
@@ -40,25 +40,29 @@
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.4" />
<PackageReference Include="RamlToOpenApiConverter.SourceOnly" Version="0.21.0" />
<PackageReference Include="YamlDotNet" Version="18.1.0" />
<PackageReference Include="RandomDataGenerator.Net" Version="1.0.19.1" />
<PackageReference Include="Stef.Validation" Version="0.3.0" />
</ItemGroup>
<!-- https://github.com/wiremock/WireMock.Net/issues/1508 -->
<ItemGroup>
<PackageReference Include="RamlToOpenApiConverter.SourceOnly" Version="0.50.0" />
</ItemGroup>
<ItemGroup Condition="'$(Configuration)' == 'Debug'">
<PackageReference Include="Microsoft.OpenApi.YamlReader" Version="3.7.0" />
<PackageReference Include="Microsoft.OpenApi.YamlReader" Version="3.10.2" />
</ItemGroup>
<ItemGroup Condition="'$(Configuration)' == 'Release'">
<!-- Keep at version 2.0.43 (https://github.com/ravibpatel/ILRepack.Lib.MSBuild.Task/issues/80) -->
<PackageReference Include="ILRepack.Lib.MSBuild.Task" Version="2.0.43" PrivateAssets="All" />
<PackageReference Include="Microsoft.OpenApi" Version="3.7.0" PrivateAssets="All" />
<PackageReference Include="Microsoft.OpenApi.YamlReader" Version="3.7.0" PrivateAssets="All" />
<PackageReference Include="SharpYaml" Version="2.1.4" />
<PackageReference Include="Microsoft.OpenApi" Version="3.10.2" PrivateAssets="All" />
<PackageReference Include="Microsoft.OpenApi.YamlReader" Version="3.10.2" PrivateAssets="All" />
<PackageReference Include="SharpYaml" Version="2.1.5" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\WireMock.Net.Abstractions\WireMock.Net.Abstractions.csproj" />
</ItemGroup>
</Project>
</Project>
@@ -1,8 +1,6 @@
// Copyright © WireMock.Net
using System;
using System.Collections.Generic;
using System.IO;
using System.Diagnostics.CodeAnalysis;
using System.Text;
using JetBrains.Annotations;
using Microsoft.OpenApi;
@@ -18,7 +16,7 @@ using OpenApiDiagnostic = WireMock.Net.OpenApiParser.Models.OpenApiDiagnostic;
namespace WireMock.Net.OpenApiParser;
/// <summary>
/// Parse a OpenApi/Swagger/V2/V3/V3.1 to WireMock.Net MappingModels.
/// Parse a OpenApi/Swagger/V2/V3/V3.1/V3.2 to WireMock.Net MappingModels.
/// </summary>
public class WireMockOpenApiParser : IWireMockOpenApiParser
{
@@ -32,8 +30,15 @@ public class WireMockOpenApiParser : IWireMockOpenApiParser
_readerSettings = new OpenApiReaderSettings();
_readerSettings.AddMicrosoftExtensionParsers();
_readerSettings.AddJsonReader();
_readerSettings.TryAddReader(OpenApiConstants.Yaml, new OpenApiYamlReader());
_readerSettings.TryAddReader(OpenApiConstants.Yml, new OpenApiYamlReader());
var openApiYamlReaderSettings = new OpenApiYamlReaderSettings
{
MaxAliasExpansionNodeCount = 1_000_000
};
var openApiYamlReader = new OpenApiYamlReader(openApiYamlReaderSettings);
_readerSettings.TryAddReader(OpenApiConstants.Yaml, openApiYamlReader);
_readerSettings.TryAddReader(OpenApiConstants.Yml, openApiYamlReader);
}
/// <inheritdoc />
@@ -55,7 +60,12 @@ public class WireMockOpenApiParser : IWireMockOpenApiParser
}
else
{
document = Read(File.OpenRead(path), out diagnostic);
if (!TryRead(File.OpenRead(path), out var documentFromYaml, out diagnostic))
{
return [];
}
document = documentFromYaml;
}
return FromDocument(document, settings);
@@ -74,14 +84,24 @@ public class WireMockOpenApiParser : IWireMockOpenApiParser
[PublicAPI]
public IReadOnlyList<MappingModel> FromStream(Stream stream, out OpenApiDiagnostic diagnostic)
{
return FromDocument(Read(stream, out diagnostic));
if (TryRead(stream, out var openApiDocument, out diagnostic))
{
return FromDocument(openApiDocument);
}
return [];
}
/// <inheritdoc />
[PublicAPI]
public IReadOnlyList<MappingModel> FromStream(Stream stream, WireMockOpenApiParserSettings settings, out OpenApiDiagnostic diagnostic)
{
return FromDocument(Read(stream, out diagnostic), settings);
if (TryRead(stream, out var openApiDocument, out diagnostic))
{
return FromDocument(openApiDocument, settings);
}
return [];
}
/// <inheritdoc />
@@ -98,7 +118,7 @@ public class WireMockOpenApiParser : IWireMockOpenApiParser
return FromStream(new MemoryStream(Encoding.UTF8.GetBytes(text)), settings, out diagnostic);
}
private OpenApiDocument Read(Stream stream, out OpenApiDiagnostic diagnostic)
private bool TryRead(Stream stream, [NotNullWhen(true)] out OpenApiDocument? openApiDocument, out OpenApiDiagnostic diagnostic)
{
if (stream is not MemoryStream memoryStream)
{
@@ -108,7 +128,8 @@ public class WireMockOpenApiParser : IWireMockOpenApiParser
var result = OpenApiDocument.Load(memoryStream, settings: _readerSettings);
diagnostic = OpenApiMapper.Map(result.Diagnostic) ?? new OpenApiDiagnostic();
return result.Document ?? throw new InvalidOperationException("The document is null.");
openApiDocument = result.Document;
return openApiDocument != null && !diagnostic.Errors.Any();
}
private static MemoryStream ReadStreamIntoMemoryStream(Stream stream)
@@ -1,7 +1,5 @@
// Copyright © WireMock.Net
using System.Collections.Generic;
using System.Linq;
using AnyOfTypes;
using WireMock.Models;
@@ -1,8 +1,6 @@
// Copyright © WireMock.Net
using System;
using System.Collections.Generic;
using System.Linq;
using WireMock.Types;
namespace WireMock.Matchers;
@@ -57,7 +55,7 @@ public static class MatchScores
/// <param name="values">The values.</param>
/// <param name="matchOperator">The <see cref="MatchOperator"/>.</param>
/// <returns>average score</returns>
public static double ToScore(IReadOnlyCollection<bool> values, MatchOperator matchOperator)
public static double ToScore(IReadOnlyList<bool> values, MatchOperator matchOperator)
{
return ToScore(values.Select(ToScore).ToArray(), matchOperator);
}
@@ -68,9 +66,9 @@ public static class MatchScores
/// <param name="values">The values.</param>
/// <param name="matchOperator"></param>
/// <returns>average score</returns>
public static double ToScore(IReadOnlyCollection<double> values, MatchOperator matchOperator)
public static double ToScore(IReadOnlyList<double> values, MatchOperator matchOperator)
{
if (!values.Any())
if (values.Count == 0)
{
return Mismatch;
}
@@ -82,4 +80,28 @@ public static class MatchScores
_ => values.Average()
};
}
internal static double ToScore(WireMockList<string> values, IStringMatcher[] matchers, MatchOperator matchOperator = MatchOperator.And)
{
// Create a matrix of scores where each row corresponds to a value and each column corresponds to a matcher.
var matrix = values
.Select(value => matchers
.Select(matcher => matcher.IsMatch(value).Score).ToArray()
)
.ToArray();
if (matrix.Length == 0 || matchers.Length == 0)
{
return Mismatch;
}
// For each value, how well was it matched by its best matcher?
var rowScore = matchOperator == MatchOperator.And ? matrix.Average(row => row.Max()) : matrix.Max(row => row.Max());
// For each matcher, how well was it satisfied by its best value?
var columnRange = Enumerable.Range(0, matchers.Length);
var columnScore = matchOperator == MatchOperator.And ? columnRange.Average(column => matrix.Max(row => row[column])) : columnRange.Max(column => matrix.Max(row => row[column]));
return matchOperator == MatchOperator.And ? Math.Min(rowScore, columnScore) : Math.Max(rowScore, columnScore);
}
}
@@ -75,7 +75,7 @@ public class NotNullOrEmptyMatcher : IObjectMatcher, IStringMatcher
/// <inheritdoc />
public string GetCSharpCodeArguments()
{
return $"new {Name}" +
return $"new {typeof(NotNullOrEmptyMatcher).FullName}" +
$"(" +
$"{MatchBehaviour.GetFullyQualifiedEnumValue()}" +
$")";
@@ -1,7 +1,5 @@
// Copyright © WireMock.Net
using System;
using System.Linq;
using System.Text.RegularExpressions;
using AnyOfTypes;
using JetBrains.Annotations;
@@ -132,10 +130,10 @@ public class RegexMatcher : IStringMatcher, IIgnoreCaseMatcher
/// <inheritdoc />
public virtual string GetCSharpCodeArguments()
{
return $"new {Name}" +
return $"new {typeof(RegexMatcher).FullName}" +
$"(" +
$"{MatchBehaviour.GetFullyQualifiedEnumValue()}, " +
$"{MappingConverterUtils.ToCSharpCodeArguments(_patterns)}, " +
$"new AnyOfTypes.AnyOf<string, WireMock.Models.StringPattern>[] {{ {MappingConverterUtils.ToCSharpCodeArguments(_patterns)} }}, " +
$"{CSharpFormatter.ToCSharpBooleanLiteral(IgnoreCase)}, " +
$"{CSharpFormatter.ToCSharpBooleanLiteral(_useRegexExtended)}, " +
$"{MatchOperator.GetFullyQualifiedEnumValue()}" +
@@ -1,6 +1,5 @@
// Copyright © WireMock.Net
using System.Linq;
using System.Text.RegularExpressions;
using AnyOfTypes;
using Stef.Validation;
@@ -74,10 +73,10 @@ public class WildcardMatcher : RegexMatcher
/// <inheritdoc />
public override string GetCSharpCodeArguments()
{
return $"new {Name}" +
return $"new {typeof(WildcardMatcher).FullName}" +
$"(" +
$"{MatchBehaviour.GetFullyQualifiedEnumValue()}, " +
$"{MappingConverterUtils.ToCSharpCodeArguments(_patterns)}, " +
$"new AnyOfTypes.AnyOf<string, WireMock.Models.StringPattern>[] {{ {MappingConverterUtils.ToCSharpCodeArguments(_patterns)} }}, " +
$"{CSharpFormatter.ToCSharpBooleanLiteral(IgnoreCase)}, " +
$"{MatchOperator.GetFullyQualifiedEnumValue()}" +
$")";
+1 -1
View File
@@ -22,7 +22,7 @@ public class BodyData : IBodyData
public string? BodyAsString { get; set; }
/// <inheritdoc />
public IDictionary<string, string>? BodyAsFormUrlEncoded { get; set; }
public IDictionary<string, WireMockList<string>>? BodyAsFormUrlEncoded { get; set; }
/// <inheritdoc />
public object? BodyAsJson { get; set; }
@@ -1,8 +1,7 @@
// Copyright © WireMock.Net
using System;
using System.Collections.Generic;
using WireMock.Matchers;
using WireMock.Types;
using WireMock.Util;
namespace WireMock.RequestBuilders;
@@ -100,5 +99,5 @@ public interface IBodyRequestBuilder : IMultiPartRequestBuilder
/// </summary>
/// <param name="func">The form-urlencoded values.</param>
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
IRequestBuilder WithBody(Func<IDictionary<string, string>?, bool> func);
IRequestBuilder WithBody(Func<IDictionary<string, WireMockList<string>>?, bool> func);
}
@@ -194,6 +194,20 @@ public class WireMockServerSettings
[JsonIgnore]
public Action<IHandlebars, IFileSystemHandler>? HandlebarsRegistrationCallback { get; set; }
/// <summary>
/// Action which can be used to add additional Scriban registrations (functions, objects or variables). [Optional]
/// </summary>
/// <remarks>
/// The first argument is a <c>Scriban.Runtime.ScriptObject</c> from the <c>Scriban.Signed</c> package.
/// It is typed as <see cref="object"/> so that this package does not depend on Scriban.
/// Members added to it are available as globals in <see cref="TransformerType.Scriban"/> and <see cref="TransformerType.ScribanDotLiquid"/> templates.
/// On a name clash the template model (<c>request</c>, <c>response</c>, <c>mapping</c> and <c>data</c>) takes precedence.
/// The action is invoked each time a transformer context is created.
/// </remarks>
[PublicAPI]
[JsonIgnore]
public Action<object, IFileSystemHandler>? ScribanRegistrationCallback { get; set; }
/// <summary>
/// Allow the usage of CSharpCodeMatcher (default is not allowed).
/// </summary>
@@ -1,9 +1,6 @@
// Copyright © WireMock.Net
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Net;
using WireMock.Types;
@@ -16,7 +13,7 @@ internal static class QueryStringParser
{
private static readonly Dictionary<string, WireMockList<string>> Empty = new();
public static bool TryParse(string? queryString, bool caseIgnore, [NotNullWhen(true)] out IDictionary<string, string>? nameValueCollection)
public static bool TryParse(string? queryString, bool caseIgnore, [NotNullWhen(true)] out IDictionary<string, WireMockList<string>>? nameValueCollection)
{
if (queryString == null)
{
@@ -29,12 +26,22 @@ internal static class QueryStringParser
.Select(parameter => parameter.Split('='))
.Distinct();
nameValueCollection = caseIgnore ? new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase) : new Dictionary<string, string>();
nameValueCollection = caseIgnore ? new Dictionary<string, WireMockList<string>>(StringComparer.OrdinalIgnoreCase) : new Dictionary<string, WireMockList<string>>();
foreach (var part in parts)
{
if (part.Length == 2)
{
nameValueCollection.Add(part[0], WebUtility.UrlDecode(part[1]));
var key = part[0];
var value = WebUtility.UrlDecode(part[1]);
if (!nameValueCollection.TryGetValue(key, out var stringList))
{
nameValueCollection.Add(key, value);
}
else
{
stringList.Add(value);
}
}
}
@@ -62,5 +62,6 @@
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="System.Text.Json" Version="8.0.5" />
</ItemGroup>
</Project>
@@ -39,7 +39,7 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Stef.Validation" Version="0.3.0" />
<PackageReference Include="Testcontainers" Version="4.12.0" />
<PackageReference Include="Testcontainers" Version="4.14.0" />
</ItemGroup>
<ItemGroup>
@@ -7,6 +7,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="System.Text.Json" Version="8.0.5" />
<ProjectReference Include="..\..\src\WireMock.Net.AspNetCore.Middleware\WireMock.Net.AspNetCore.Middleware.csproj" />
</ItemGroup>
@@ -408,4 +408,210 @@ public class WireMockListTests
}
#endregion
#region Equality Operator Tests
[Fact]
public void WireMockListOfString_EqualityOperator_ListToSingleValue_WhenSingleElementMatchesValue_ShouldBeTrue()
{
// Arrange
var list = new WireMockList<string>("hello");
// Act & Assert
(list == "hello").Should().BeTrue();
}
[Fact]
public void WireMockListOfString_EqualityOperator_ListToSingleValue_WhenSingleElementDoesNotMatchValue_ShouldBeFalse()
{
// Arrange
var list = new WireMockList<string>("hello");
// Act & Assert
(list == "world").Should().BeFalse();
}
[Fact]
public void WireMockListOfString_EqualityOperator_ListToSingleValue_WhenMultipleElements_ShouldBeFalse()
{
// Arrange
var list = new WireMockList<string>("hello", "world");
// Act & Assert
(list == "hello").Should().BeFalse();
}
[Fact]
public void WireMockListOfString_EqualityOperator_SingleValueToList_WhenSingleElementMatchesValue_ShouldBeTrue()
{
// Arrange
var list = new WireMockList<string>("hello");
// Act & Assert
("hello" == list).Should().BeTrue();
}
[Fact]
public void WireMockListOfString_EqualityOperator_SingleValueToList_WhenSingleElementDoesNotMatchValue_ShouldBeFalse()
{
// Arrange
var list = new WireMockList<string>("hello");
// Act & Assert
("world" == list).Should().BeFalse();
}
[Fact]
public void WireMockListOfString_InequalityOperator_ListToSingleValue_WhenSingleElementMatchesValue_ShouldBeFalse()
{
// Arrange
var list = new WireMockList<string>("hello");
// Act & Assert
(list != "hello").Should().BeFalse();
}
[Fact]
public void WireMockListOfString_InequalityOperator_ListToSingleValue_WhenSingleElementDoesNotMatchValue_ShouldBeTrue()
{
// Arrange
var list = new WireMockList<string>("hello");
// Act & Assert
(list != "world").Should().BeTrue();
}
[Fact]
public void WireMockListOfString_InequalityOperator_SingleValueToList_WhenSingleElementMatchesValue_ShouldBeFalse()
{
// Arrange
var list = new WireMockList<string>("hello");
// Act & Assert
("hello" != list).Should().BeFalse();
}
[Fact]
public void WireMockListOfString_InequalityOperator_SingleValueToList_WhenSingleElementDoesNotMatchValue_ShouldBeTrue()
{
// Arrange
var list = new WireMockList<string>("hello");
// Act & Assert
("world" != list).Should().BeTrue();
}
#endregion
#region Equals and GetHashCode Tests
[Fact]
public void WireMockListOfString_Equals_WhenBothListsHaveSameElements_ShouldBeTrue()
{
// Arrange
var list1 = new WireMockList<string>("a", "b", "c");
var list2 = new WireMockList<string>("a", "b", "c");
// Act & Assert
list1.Equals(list2).Should().BeTrue();
}
[Fact]
public void WireMockListOfString_Equals_WhenListsHaveDifferentElements_ShouldBeFalse()
{
// Arrange
var list1 = new WireMockList<string>("a", "b");
var list2 = new WireMockList<string>("a", "x");
// Act & Assert
list1.Equals(list2).Should().BeFalse();
}
[Fact]
public void WireMockListOfString_Equals_WhenListsHaveDifferentCounts_ShouldBeFalse()
{
// Arrange
var list1 = new WireMockList<string>("a", "b");
var list2 = new WireMockList<string>("a");
// Act & Assert
list1.Equals(list2).Should().BeFalse();
}
[Fact]
public void WireMockListOfString_Equals_WhenBothEmpty_ShouldBeTrue()
{
// Arrange
var list1 = new WireMockList<string>();
var list2 = new WireMockList<string>();
// Act & Assert
list1.Equals(list2).Should().BeTrue();
}
[Fact]
public void WireMockListOfString_Equals_WhenSameReference_ShouldBeTrue()
{
// Arrange
var list = new WireMockList<string>("a", "b");
var sameReference = list;
// Act & Assert
list.Equals(sameReference).Should().BeTrue();
}
[Fact]
public void WireMockListOfString_Equals_WhenComparedToNull_ShouldBeFalse()
{
// Arrange
var list = new WireMockList<string>("a");
// Act & Assert
list.Should().NotBeNull();
}
[Fact]
public void WireMockListOfString_Equals_WhenComparedToNonWireMockList_ShouldBeFalse()
{
// Arrange
var list = new WireMockList<string>("a");
// Act & Assert
list.Equals(new List<string> { "a" }).Should().BeFalse();
}
[Fact]
public void WireMockListOfString_GetHashCode_WhenListsHaveSameElements_ShouldBeEqual()
{
// Arrange
var list1 = new WireMockList<string>("a", "b", "c");
var list2 = new WireMockList<string>("a", "b", "c");
// Act & Assert
list1.GetHashCode().Should().Be(list2.GetHashCode());
}
[Fact]
public void WireMockListOfString_GetHashCode_WhenListsHaveDifferentElements_ShouldNotBeEqual()
{
// Arrange
var list1 = new WireMockList<string>("a", "b");
var list2 = new WireMockList<string>("x", "y");
// Act & Assert
list1.GetHashCode().Should().NotBe(list2.GetHashCode());
}
[Fact]
public void WireMockListOfString_GetHashCode_WhenEmpty_ShouldReturnConsistentValue()
{
// Arrange
var list1 = new WireMockList<string>();
var list2 = new WireMockList<string>();
// Act & Assert
list1.GetHashCode().Should().Be(list2.GetHashCode());
}
#endregion
}
@@ -2,8 +2,8 @@
server
.Given(Request.Create()
.UsingMethod("GET")
.WithPath(new WildcardMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch, "/foo1", false, WireMock.Matchers.MatchOperator.Or))
.WithParam("p1", new ExactMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch, false, WireMock.Matchers.MatchOperator.And, "xyz"))
.WithPath(new WireMock.Matchers.WildcardMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch, new AnyOfTypes.AnyOf<string, WireMock.Models.StringPattern>[] { "/foo1" }, false, WireMock.Matchers.MatchOperator.Or))
.WithParam("p1", new WireMock.Matchers.ExactMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch, false, WireMock.Matchers.MatchOperator.And, "xyz"))
)
.WithGuid("90356dba-b36c-469a-a17e-669cd84f1f05")
.RespondWith(Response.Create()
@@ -2,8 +2,8 @@
server
.Given(Request.Create()
.UsingMethod("POST")
.WithPath(new WildcardMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch, "/users/post2", false, WireMock.Matchers.MatchOperator.Or))
.WithBody(new JsonPartialMatcher(
.WithPath(new WireMock.Matchers.WildcardMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch, new AnyOfTypes.AnyOf<string, WireMock.Models.StringPattern>[] { "/users/post2" }, false, WireMock.Matchers.MatchOperator.Or))
.WithBody(new WireMock.Matchers.JsonPartialMatcher(
value: new
{
city = "City",
@@ -22,8 +22,8 @@ Some ""value"" in Line2")
server
.Given(Request.Create()
.UsingMethod("POST")
.WithPath(new WildcardMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch, "/foo2", false, WireMock.Matchers.MatchOperator.Or))
.WithParam("p2", new ExactMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch, false, WireMock.Matchers.MatchOperator.And, "abc"))
.WithPath(new WireMock.Matchers.WildcardMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch, new AnyOfTypes.AnyOf<string, WireMock.Models.StringPattern>[] { "/foo2" }, false, WireMock.Matchers.MatchOperator.Or))
.WithParam("p2", new WireMock.Matchers.ExactMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch, false, WireMock.Matchers.MatchOperator.And, "abc"))
.WithHeader("h1", "W/\"234f2q3r\"", true)
)
.WithGuid("4126dec8-470b-4eff-93bb-c24f83b8b1fd")
@@ -37,8 +37,8 @@ server
server
.Given(Request.Create()
.UsingMethod("POST")
.WithPath(new WildcardMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch, "/users/post1", false, WireMock.Matchers.MatchOperator.Or))
.WithBody(new JsonMatcher(
.WithPath(new WireMock.Matchers.WildcardMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch, new AnyOfTypes.AnyOf<string, WireMock.Models.StringPattern>[] { "/users/post1" }, false, WireMock.Matchers.MatchOperator.Or))
.WithBody(new WireMock.Matchers.JsonMatcher(
value: new
{
city = "Amsterdam",
@@ -55,7 +55,7 @@ server
server
.Given(Request.Create()
.UsingMethod("DELETE")
.WithUrl(new WildcardMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch, "https://localhost/test", false, WireMock.Matchers.MatchOperator.Or))
.WithUrl(new WireMock.Matchers.WildcardMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch, new AnyOfTypes.AnyOf<string, WireMock.Models.StringPattern>[] { "https://localhost/test" }, false, WireMock.Matchers.MatchOperator.Or))
)
.WithGuid("c9929240-7ae8-4a5d-8ed8-0913479f6eeb")
.RespondWith(Response.Create()
@@ -99,8 +99,8 @@ text
server
.Given(Request.Create()
.UsingMethod("GET")
.WithPath(new WildcardMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch, "/foo1", false, WireMock.Matchers.MatchOperator.Or))
.WithParam("p1", new ExactMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch, false, WireMock.Matchers.MatchOperator.And, "xyz"))
.WithPath(new WireMock.Matchers.WildcardMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch, new AnyOfTypes.AnyOf<string, WireMock.Models.StringPattern>[] { "/foo1" }, false, WireMock.Matchers.MatchOperator.Or))
.WithParam("p1", new WireMock.Matchers.ExactMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch, false, WireMock.Matchers.MatchOperator.And, "xyz"))
)
.WithGuid("f74fd144-df53-404f-8e35-da22a640bd5f")
.RespondWith(Response.Create()
@@ -0,0 +1,29 @@
// Copyright © WireMock.Net
#if NET6_0_OR_GREATER
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using WireMock.Net.Testcontainers.Utils;
namespace WireMock.Net.Tests.Facts;
[ExcludeFromCodeCoverage]
public sealed class RunOnDockerFact : FactAttribute
{
public RunOnDockerFact(
[CallerFilePath] string? sourceFilePath = null,
[CallerLineNumber] int sourceLineNumber = -1) : base(sourceFilePath, sourceLineNumber)
{
OSPlatform currentPlatform;
try
{
currentPlatform = TestcontainersUtils.GetImageOSAsync.Value.Result;
}
catch
{
Skip = $"Only run test when Docker is installed.";
}
}
}
#endif
@@ -15,7 +15,19 @@ public sealed class RunOnDockerPlatformFact : FactAttribute
[CallerFilePath] string? sourceFilePath = null,
[CallerLineNumber] int sourceLineNumber = -1) : base(sourceFilePath, sourceLineNumber)
{
if (TestcontainersUtils.GetImageOSAsync.Value.Result != OSPlatform.Create(platform))
OSPlatform currentPlatform;
try
{
currentPlatform = TestcontainersUtils.GetImageOSAsync.Value.Result;
}
catch
{
Skip = $"Only run test when Docker is installed.";
return;
}
if (currentPlatform != OSPlatform.Create(platform))
{
Skip = $"Only run test when Docker OS Platform {platform} is used.";
}
@@ -2,8 +2,8 @@
builder
.Given(Request.Create()
.UsingMethod("GET")
.WithPath(new WildcardMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch, "/foo", false, WireMock.Matchers.MatchOperator.Or))
.WithParam("test", new ExactMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch, false, WireMock.Matchers.MatchOperator.Or, "abc"))
.WithPath(new WireMock.Matchers.WildcardMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch, new AnyOfTypes.AnyOf<string, WireMock.Models.StringPattern>[] { "/foo" }, false, WireMock.Matchers.MatchOperator.Or))
.WithParam("test", new WireMock.Matchers.ExactMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch, false, WireMock.Matchers.MatchOperator.Or, "abc"))
)
.WithGuid("41372914-1838-4c67-916b-b9aacdd096ce")
.RespondWith(Response.Create()
@@ -13,8 +13,8 @@ builder
builder
.Given(Request.Create()
.UsingMethod("POST")
.WithPath(new WildcardMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch, "/users/post1", false, WireMock.Matchers.MatchOperator.Or))
.WithBody(new JsonMatcher(
.WithPath(new WireMock.Matchers.WildcardMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch, new AnyOfTypes.AnyOf<string, WireMock.Models.StringPattern>[] { "/users/post1" }, false, WireMock.Matchers.MatchOperator.Or))
.WithBody(new WireMock.Matchers.JsonMatcher(
value: new
{
Request = "Hello?"
@@ -30,8 +30,8 @@ builder
builder
.Given(Request.Create()
.UsingMethod("POST")
.WithPath(new WildcardMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch, "/users/post2", false, WireMock.Matchers.MatchOperator.Or))
.WithBody(new JsonMatcher(
.WithPath(new WireMock.Matchers.WildcardMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch, new AnyOfTypes.AnyOf<string, WireMock.Models.StringPattern>[] { "/users/post2" }, false, WireMock.Matchers.MatchOperator.Or))
.WithBody(new WireMock.Matchers.JsonMatcher(
value: new
{
city = "Amsterdam",
@@ -48,7 +48,7 @@ builder
builder
.Given(Request.Create()
.UsingMethod("POST")
.WithPath(new WildcardMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch, "/form-urlencoded", false, WireMock.Matchers.MatchOperator.Or))
.WithPath(new WireMock.Matchers.WildcardMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch, new AnyOfTypes.AnyOf<string, WireMock.Models.StringPattern>[] { "/form-urlencoded" }, false, WireMock.Matchers.MatchOperator.Or))
.WithHeader("Content-Type", "application/x-www-form-urlencoded", true)
.WithBody("name=John Doe")
)
@@ -59,8 +59,8 @@ builder
builder
.Given(Request.Create()
.UsingMethod("GET")
.WithPath(new WildcardMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch, "/regex", false, WireMock.Matchers.MatchOperator.Or))
.WithParam("foo", new RegexMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch, ".*", false, true, WireMock.Matchers.MatchOperator.Or))
.WithPath(new WireMock.Matchers.WildcardMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch, new AnyOfTypes.AnyOf<string, WireMock.Models.StringPattern>[] { "/regex" }, false, WireMock.Matchers.MatchOperator.Or))
.WithParam("foo", new WireMock.Matchers.RegexMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch, new AnyOfTypes.AnyOf<string, WireMock.Models.StringPattern>[] { ".*" }, false, true, WireMock.Matchers.MatchOperator.Or))
)
.WithGuid("98fae52e-76df-47d9-876f-2ee32e931004")
.RespondWith(Response.Create()
@@ -69,11 +69,11 @@ builder
builder
.Given(Request.Create()
.UsingMethod("GET")
.WithPath(new WildcardMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch, "/todo/items", false, WireMock.Matchers.MatchOperator.Or))
.WithPath(new WireMock.Matchers.WildcardMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch, new AnyOfTypes.AnyOf<string, WireMock.Models.StringPattern>[] { "/todo/items" }, false, WireMock.Matchers.MatchOperator.Or))
)
.WithGuid("98fae52e-76df-47d9-876f-2ee32e931005")
.InScenario("To do list")
.WhenStateIs("TodoList State Started", 2)
.WillSetStateTo("TodoList State Started", 2)
.RespondWith(Response.Create()
.WithBody("Buy milk")
);
@@ -81,7 +81,7 @@ builder
builder
.Given(Request.Create()
.UsingMethod("GET")
.WithPath(new WildcardMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch, "/delay", false, WireMock.Matchers.MatchOperator.Or))
.WithPath(new WireMock.Matchers.WildcardMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch, new AnyOfTypes.AnyOf<string, WireMock.Models.StringPattern>[] { "/delay" }, false, WireMock.Matchers.MatchOperator.Or))
)
.WithGuid("98fae52e-76df-47d9-876f-2ee32e931006")
.RespondWith(Response.Create()
@@ -91,7 +91,7 @@ builder
builder
.Given(Request.Create()
.UsingMethod("GET")
.WithPath(new WildcardMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch, "/random-delay", false, WireMock.Matchers.MatchOperator.Or))
.WithPath(new WireMock.Matchers.WildcardMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch, new AnyOfTypes.AnyOf<string, WireMock.Models.StringPattern>[] { "/random-delay" }, false, WireMock.Matchers.MatchOperator.Or))
)
.WithGuid("98fae52e-76df-47d9-876f-2ee32e931007")
.RespondWith(Response.Create()
@@ -101,7 +101,7 @@ builder
builder
.Given(Request.Create()
.UsingMethod("GET")
.WithPath(new WildcardMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch, "/prob", false, WireMock.Matchers.MatchOperator.Or))
.WithPath(new WireMock.Matchers.WildcardMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch, new AnyOfTypes.AnyOf<string, WireMock.Models.StringPattern>[] { "/prob" }, false, WireMock.Matchers.MatchOperator.Or))
)
.WithGuid("98fae52e-76df-47d9-876f-2ee32e931008")
.WithProbability(0.1)
@@ -112,7 +112,7 @@ builder
builder
.Given(Request.Create()
.UsingMethod("GET")
.WithPath(new WildcardMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch, "/prob", false, WireMock.Matchers.MatchOperator.Or))
.WithPath(new WireMock.Matchers.WildcardMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch, new AnyOfTypes.AnyOf<string, WireMock.Models.StringPattern>[] { "/prob" }, false, WireMock.Matchers.MatchOperator.Or))
)
.WithGuid("98fae52e-76df-47d9-876f-2ee32e931009")
.WithProbability(0.9)
@@ -2,8 +2,8 @@
server
.Given(Request.Create()
.UsingMethod("GET")
.WithPath(new WildcardMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch, "/foo", false, WireMock.Matchers.MatchOperator.Or))
.WithParam("test", new ExactMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch, false, WireMock.Matchers.MatchOperator.Or, "abc"))
.WithPath(new WireMock.Matchers.WildcardMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch, new AnyOfTypes.AnyOf<string, WireMock.Models.StringPattern>[] { "/foo" }, false, WireMock.Matchers.MatchOperator.Or))
.WithParam("test", new WireMock.Matchers.ExactMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch, false, WireMock.Matchers.MatchOperator.Or, "abc"))
)
.WithGuid("41372914-1838-4c67-916b-b9aacdd096ce")
.RespondWith(Response.Create()
@@ -13,8 +13,8 @@ server
server
.Given(Request.Create()
.UsingMethod("POST")
.WithPath(new WildcardMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch, "/users/post1", false, WireMock.Matchers.MatchOperator.Or))
.WithBody(new JsonMatcher(
.WithPath(new WireMock.Matchers.WildcardMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch, new AnyOfTypes.AnyOf<string, WireMock.Models.StringPattern>[] { "/users/post1" }, false, WireMock.Matchers.MatchOperator.Or))
.WithBody(new WireMock.Matchers.JsonMatcher(
value: new
{
Request = "Hello?"
@@ -30,8 +30,8 @@ server
server
.Given(Request.Create()
.UsingMethod("POST")
.WithPath(new WildcardMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch, "/users/post2", false, WireMock.Matchers.MatchOperator.Or))
.WithBody(new JsonMatcher(
.WithPath(new WireMock.Matchers.WildcardMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch, new AnyOfTypes.AnyOf<string, WireMock.Models.StringPattern>[] { "/users/post2" }, false, WireMock.Matchers.MatchOperator.Or))
.WithBody(new WireMock.Matchers.JsonMatcher(
value: new
{
city = "Amsterdam",
@@ -48,7 +48,7 @@ server
server
.Given(Request.Create()
.UsingMethod("POST")
.WithPath(new WildcardMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch, "/form-urlencoded", false, WireMock.Matchers.MatchOperator.Or))
.WithPath(new WireMock.Matchers.WildcardMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch, new AnyOfTypes.AnyOf<string, WireMock.Models.StringPattern>[] { "/form-urlencoded" }, false, WireMock.Matchers.MatchOperator.Or))
.WithHeader("Content-Type", "application/x-www-form-urlencoded", true)
.WithBody("name=John Doe")
)
@@ -59,8 +59,8 @@ server
server
.Given(Request.Create()
.UsingMethod("GET")
.WithPath(new WildcardMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch, "/regex", false, WireMock.Matchers.MatchOperator.Or))
.WithParam("foo", new RegexMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch, ".*", false, true, WireMock.Matchers.MatchOperator.Or))
.WithPath(new WireMock.Matchers.WildcardMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch, new AnyOfTypes.AnyOf<string, WireMock.Models.StringPattern>[] { "/regex" }, false, WireMock.Matchers.MatchOperator.Or))
.WithParam("foo", new WireMock.Matchers.RegexMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch, new AnyOfTypes.AnyOf<string, WireMock.Models.StringPattern>[] { ".*" }, false, true, WireMock.Matchers.MatchOperator.Or))
)
.WithGuid("98fae52e-76df-47d9-876f-2ee32e931004")
.RespondWith(Response.Create()
@@ -69,11 +69,11 @@ server
server
.Given(Request.Create()
.UsingMethod("GET")
.WithPath(new WildcardMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch, "/todo/items", false, WireMock.Matchers.MatchOperator.Or))
.WithPath(new WireMock.Matchers.WildcardMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch, new AnyOfTypes.AnyOf<string, WireMock.Models.StringPattern>[] { "/todo/items" }, false, WireMock.Matchers.MatchOperator.Or))
)
.WithGuid("98fae52e-76df-47d9-876f-2ee32e931005")
.InScenario("To do list")
.WhenStateIs("TodoList State Started", 2)
.WillSetStateTo("TodoList State Started", 2)
.RespondWith(Response.Create()
.WithBody("Buy milk")
);
@@ -81,7 +81,7 @@ server
server
.Given(Request.Create()
.UsingMethod("GET")
.WithPath(new WildcardMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch, "/delay", false, WireMock.Matchers.MatchOperator.Or))
.WithPath(new WireMock.Matchers.WildcardMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch, new AnyOfTypes.AnyOf<string, WireMock.Models.StringPattern>[] { "/delay" }, false, WireMock.Matchers.MatchOperator.Or))
)
.WithGuid("98fae52e-76df-47d9-876f-2ee32e931006")
.RespondWith(Response.Create()
@@ -91,7 +91,7 @@ server
server
.Given(Request.Create()
.UsingMethod("GET")
.WithPath(new WildcardMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch, "/random-delay", false, WireMock.Matchers.MatchOperator.Or))
.WithPath(new WireMock.Matchers.WildcardMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch, new AnyOfTypes.AnyOf<string, WireMock.Models.StringPattern>[] { "/random-delay" }, false, WireMock.Matchers.MatchOperator.Or))
)
.WithGuid("98fae52e-76df-47d9-876f-2ee32e931007")
.RespondWith(Response.Create()
@@ -101,7 +101,7 @@ server
server
.Given(Request.Create()
.UsingMethod("GET")
.WithPath(new WildcardMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch, "/prob", false, WireMock.Matchers.MatchOperator.Or))
.WithPath(new WireMock.Matchers.WildcardMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch, new AnyOfTypes.AnyOf<string, WireMock.Models.StringPattern>[] { "/prob" }, false, WireMock.Matchers.MatchOperator.Or))
)
.WithGuid("98fae52e-76df-47d9-876f-2ee32e931008")
.WithProbability(0.1)
@@ -112,7 +112,7 @@ server
server
.Given(Request.Create()
.UsingMethod("GET")
.WithPath(new WildcardMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch, "/prob", false, WireMock.Matchers.MatchOperator.Or))
.WithPath(new WireMock.Matchers.WildcardMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch, new AnyOfTypes.AnyOf<string, WireMock.Models.StringPattern>[] { "/prob" }, false, WireMock.Matchers.MatchOperator.Or))
)
.WithGuid("98fae52e-76df-47d9-876f-2ee32e931009")
.WithProbability(0.9)
@@ -1,9 +1,8 @@
// Copyright © WireMock.Net
using System.Net.Http;
using AnyOfTypes;
using WireMock.Extensions;
using WireMock.Matchers;
using WireMock.Models;
namespace WireMock.Net.Tests.Matchers;
@@ -12,59 +11,155 @@ public class FormUrlEncodedMatcherTest
private readonly CancellationToken _ct = TestContext.Current.CancellationToken;
[Theory]
[InlineData("*=*")]
[InlineData("name=John Doe")]
[InlineData("name=*")]
[InlineData("*=John Doe")]
[InlineData("email=johndoe@example.com")]
[InlineData("email=*")]
[InlineData("*=johndoe@example.com")]
[InlineData("name=John Doe", "email=johndoe@example.com")]
[InlineData("name=John Doe", "email=*")]
[InlineData("name=*", "email=*")]
[InlineData("*=John Doe", "*=johndoe@example.com")]
public async Task FormUrlEncodedMatcher_IsMatch(params string[] patterns)
[InlineData(true, "*=*")]
[InlineData(true, "name=John Doe")]
[InlineData(false, "name=Stef")]
[InlineData(false, "name=John Doe&name=Stef")]
[InlineData(true, "name=*")]
[InlineData(true, "*=John Doe")]
[InlineData(false, "*=Stef")]
[InlineData(false, "*=John Doe&*=Stef")]
[InlineData(true, "email=johndoe@example.com")]
[InlineData(true, "email=*")]
[InlineData(true, "*=johndoe@example.com")]
[InlineData(true, "name=John Doe", "email=johndoe@example.com")]
[InlineData(true, "name=John Doe", "email=*")]
[InlineData(true, "name=John Doe&name=Stef", "email=*")]
[InlineData(true, "name=Stef", "email=*")]
[InlineData(true, "name=*", "email=*")]
[InlineData(true, "*=John Doe", "*=johndoe@example.com")]
[InlineData(true, "*=Stef", "*=johndoe@example.com")]
[InlineData(true, "name=John Doe&name=Stef", "*=johndoe@example.com")]
public async Task FormUrlEncodedMatcher_IsMatch_Single_Or(bool expected, params string[] patterns)
{
// Arrange
var content = new FormUrlEncodedContent(
using var content = new FormUrlEncodedContent(
[
new KeyValuePair<string, string>("name", "John Doe"),
new KeyValuePair<string, string>("email", "johndoe@example.com")
]);
var contentAsString = await content.ReadAsStringAsync(_ct);
var matcher = new FormUrlEncodedMatcher(patterns.Select(p => new AnyOf<string, StringPattern>(p)).ToArray());
var matcher = new FormUrlEncodedMatcher(patterns.ToAnyOfPatterns());
// Act
var score = matcher.IsMatch(contentAsString).IsPerfect();
// Assert
score.Should().BeTrue();
score.Should().Be(expected);
}
[Theory]
[InlineData(true, "*=*")]
[InlineData(false, "name=John Doe")]
[InlineData(false, "name=Stef")]
[InlineData(true, "name=John Doe&name=Stef")]
[InlineData(true, "name=*")]
[InlineData(false, "*=John Doe")]
[InlineData(false, "*=Stef")]
[InlineData(true, "*=John Doe&*=Stef")]
[InlineData(true, "email=johndoe@example.com")]
[InlineData(true, "email=*")]
[InlineData(true, "*=johndoe@example.com")]
[InlineData(true, "name=John Doe", "email=johndoe@example.com")]
[InlineData(true, "name=John Doe", "email=*")]
[InlineData(true, "name=John Doe&name=Stef", "email=*")]
[InlineData(true, "name=Stef", "email=*")]
[InlineData(true, "name=*", "email=*")]
[InlineData(true, "*=John Doe", "*=johndoe@example.com")]
[InlineData(true, "*=Stef", "*=johndoe@example.com")]
[InlineData(true, "name=John Doe&name=Stef", "*=johndoe@example.com")]
public async Task FormUrlEncodedMatcher_IsMatch_Multiple_Or(bool expected, params string[] patterns)
{
// Arrange
using var content = new FormUrlEncodedContent(
[
new KeyValuePair<string, string>("name", "John Doe"),
new KeyValuePair<string, string>("name", "Stef"),
new KeyValuePair<string, string>("email", "johndoe@example.com")
]);
var contentAsString = await content.ReadAsStringAsync(_ct);
var matcher = new FormUrlEncodedMatcher(patterns.ToAnyOfPatterns());
// Act
var score = matcher.IsMatch(contentAsString).IsPerfect();
// Assert
score.Should().Be(expected);
}
[Theory]
[InlineData(true, "*=*")]
[InlineData(false, "name=John Doe")]
[InlineData(false, "name=Stef")]
[InlineData(false, "name=John Doe&name=Stef")]
[InlineData(false, "name=*")]
[InlineData(false, "*=John Doe")]
[InlineData(false, "*=Stef")]
[InlineData(false, "*=John Doe&*=Stef")]
[InlineData(false, "email=johndoe@example.com")]
[InlineData(false, "email=*")]
[InlineData(false, "*=johndoe@example.com")]
[InlineData(true, "name=John Doe", "email=johndoe@example.com")]
[InlineData(true, "name=John Doe", "email=*")]
[InlineData(false, "name=John Doe&name=Stef", "email=*")]
[InlineData(false, "name=Stef", "email=*")]
[InlineData(true, "name=*", "email=*")]
[InlineData(true, "*=John Doe", "*=johndoe@example.com")]
[InlineData(true, "*=*")]
public async Task FormUrlEncodedMatcher_IsMatch_And(bool expected, params string[] patterns)
[InlineData(false, "*=Stef", "*=johndoe@example.com")]
[InlineData(false, "name=John Doe&name=Stef", "*=johndoe@example.com")]
public async Task FormUrlEncodedMatcher_IsMatch_Single_And(bool expected, params string[] patterns)
{
// Arrange
var content = new FormUrlEncodedContent(
using var content = new FormUrlEncodedContent(
[
new KeyValuePair<string, string>("name", "John Doe"),
new KeyValuePair<string, string>("email", "johndoe@example.com")
]);
var contentAsString = await content.ReadAsStringAsync(_ct);
var matcher = new FormUrlEncodedMatcher(patterns.Select(p => new AnyOf<string, StringPattern>(p)).ToArray(), true, MatchOperator.And);
var matcher = new FormUrlEncodedMatcher(patterns.ToAnyOfPatterns(), true, MatchOperator.And);
// Act
var score = matcher.IsMatch(contentAsString).IsPerfect();
// Assert
score.Should().Be(expected);
}
[Theory]
[InlineData(true, "*=*")]
[InlineData(false, "name=John Doe")]
[InlineData(false, "name=Stef")]
[InlineData(false, "name=John Doe&name=Stef")]
[InlineData(false, "name=*")]
[InlineData(false, "*=John Doe")]
[InlineData(false, "*=Stef")]
[InlineData(false, "*=John Doe&*=Stef")]
[InlineData(false, "email=johndoe@example.com")]
[InlineData(false, "email=*")]
[InlineData(false, "*=johndoe@example.com")]
[InlineData(false, "name=John Doe", "email=johndoe@example.com")]
[InlineData(false, "name=John Doe", "email=*")]
[InlineData(true, "name=John Doe&name=Stef", "email=*")]
[InlineData(false, "name=Stef", "email=*")]
[InlineData(true, "name=*", "email=*")]
[InlineData(false, "*=John Doe", "*=johndoe@example.com")]
[InlineData(false, "*=Stef", "*=johndoe@example.com")]
[InlineData(true, "name=John Doe&name=Stef", "*=johndoe@example.com")]
public async Task FormUrlEncodedMatcher_IsMatch_Multiple_And(bool expected, params string[] patterns)
{
// Arrange
using var content = new FormUrlEncodedContent(
[
new KeyValuePair<string, string>("name", "John Doe"),
new KeyValuePair<string, string>("name", "Stef"),
new KeyValuePair<string, string>("email", "johndoe@example.com")
]);
var contentAsString = await content.ReadAsStringAsync(_ct);
var matcher = new FormUrlEncodedMatcher(patterns.ToAnyOfPatterns(), true, MatchOperator.And);
// Act
var score = matcher.IsMatch(contentAsString).IsPerfect();
@@ -74,12 +169,13 @@ public class FormUrlEncodedMatcherTest
}
[Fact]
public async Task FormUrlEncodedMatcher_IsMatch_And_MatchAllProperties()
public async Task FormUrlEncodedMatcher_IsMatch_And_MatchAllProperties_MissingRequiredKey_ShouldNotMatch()
{
// Arrange
var content = new FormUrlEncodedContent(
using var content = new FormUrlEncodedContent(
[
new KeyValuePair<string, string>("name", "John Doe"),
new KeyValuePair<string, string>("name", "Stef"),
new KeyValuePair<string, string>("email", "johndoe@example.com")
]);
var contentAsString = await content.ReadAsStringAsync(_ct);
@@ -93,4 +189,25 @@ public class FormUrlEncodedMatcherTest
// Assert
score.Should().BeFalse();
}
[Fact]
public async Task FormUrlEncodedMatcher_IsMatch_And_MatchAllProperties_ConflictingEmailPatterns_ShouldNotMatch()
{
// Arrange
using var content = new FormUrlEncodedContent(
[
new KeyValuePair<string, string>("name", "John Doe"),
new KeyValuePair<string, string>("name", "Stef"),
new KeyValuePair<string, string>("email", "johndoe@example.com")
]);
var contentAsString = await content.ReadAsStringAsync(_ct);
var matcher = new FormUrlEncodedMatcher(["name=*", "email=*", "email=x"], matchOperator: MatchOperator.And);
// Act
var score = matcher.IsMatch(contentAsString).IsPerfect();
// Assert
score.Should().BeFalse();
}
}
@@ -257,7 +257,7 @@ public class FuncMatcherTests
var code = matcher.GetCSharpCodeArguments();
// Assert
code.Should().Be("new FuncMatcher(/* Func<string?, bool> function */, WireMock.Matchers.MatchBehaviour.AcceptOnMatch)");
code.Should().Be("new WireMock.Matchers.FuncMatcher(/* Func<string?, bool> function */, WireMock.Matchers.MatchBehaviour.AcceptOnMatch)");
}
[Fact]
@@ -271,7 +271,7 @@ public class FuncMatcherTests
var code = matcher.GetCSharpCodeArguments();
// Assert
code.Should().Be("new FuncMatcher(/* Func<byte[]?, bool> function */, WireMock.Matchers.MatchBehaviour.AcceptOnMatch)");
code.Should().Be("new WireMock.Matchers.FuncMatcher(/* Func<byte[]?, bool> function */, WireMock.Matchers.MatchBehaviour.AcceptOnMatch)");
}
[Fact]
@@ -667,7 +667,7 @@ public class JsonMatcherTests
var result = matcher.GetCSharpCodeArguments();
// Assert
result.Should().StartWith($"new JsonMatcher(WireMock.Matchers.MatchBehaviour.{matchBehaviour},");
result.Should().StartWith($"new WireMock.Matchers.JsonMatcher(WireMock.Matchers.MatchBehaviour.{matchBehaviour},");
result.Should().EndWith($", {ignoreCase.ToString().ToLowerInvariant()}, {regex.ToString().ToLowerInvariant()}, {ignoreArrayOrder.ToString().ToLowerInvariant()})");
}
}
@@ -14,7 +14,7 @@ public class MatcherTests
var result = matcher.GetCSharpCodeArguments();
// Assert
result.Should().Be("new ContentTypeMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch, \"application/json\", false)");
result.Should().Be("new WireMock.Matchers.ContentTypeMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch, new AnyOfTypes.AnyOf<string, WireMock.Models.StringPattern>[] { \"application/json\" }, false)");
}
[Fact]
@@ -27,7 +27,7 @@ public class MatcherTests
var result = matcher.GetCSharpCodeArguments();
// Assert
result.Should().Be("new ExactMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch, false, WireMock.Matchers.MatchOperator.Or, \"test\")");
result.Should().Be("new WireMock.Matchers.ExactMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch, false, WireMock.Matchers.MatchOperator.Or, \"test\")");
}
[Fact]
@@ -53,7 +53,7 @@ public class MatcherTests
var result = matcher.GetCSharpCodeArguments();
// Assert
result.Should().Be("new FormUrlEncodedMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch, \"key=value\", false, WireMock.Matchers.MatchOperator.Or)");
result.Should().Be("new WireMock.Matchers.FormUrlEncodedMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch, new AnyOfTypes.AnyOf<string, WireMock.Models.StringPattern>[] { \"key=value\" }, false, WireMock.Matchers.MatchOperator.Or)");
}
[Fact]
@@ -66,7 +66,7 @@ public class MatcherTests
var result = matcher.GetCSharpCodeArguments();
// Assert
result.Should().Be("new JmesPathMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch, WireMock.Matchers.MatchOperator.Or, \"expression\")");
result.Should().Be("new WireMock.Matchers.JmesPathMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch, WireMock.Matchers.MatchOperator.Or, \"expression\")");
}
[Fact]
@@ -79,7 +79,7 @@ public class MatcherTests
var result = matcher.GetCSharpCodeArguments();
// Assert
result.Should().StartWith("new JsonMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch,");
result.Should().StartWith("new WireMock.Matchers.JsonMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch,");
}
[Fact]
@@ -92,7 +92,7 @@ public class MatcherTests
var result = matcher.GetCSharpCodeArguments();
// Assert
result.Should().StartWith("new JsonPartialMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch,");
result.Should().StartWith("new WireMock.Matchers.JsonPartialMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch,");
}
[Fact]
@@ -105,7 +105,7 @@ public class MatcherTests
var result = matcher.GetCSharpCodeArguments();
// Assert
result.Should().StartWith("new JsonPartialWildcardMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch,");
result.Should().StartWith("new WireMock.Matchers.JsonPartialWildcardMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch,");
}
//[Fact]
@@ -118,7 +118,7 @@ public class MatcherTests
// var result = matcher.GetCSharpCodeArguments();
// // Assert
// result.Should().Be("new LinqMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch, WireMock.Matchers.MatchOperator.Or, \"it.Contains(\\\"test\\\"\")");
// result.Should().Be("new WireMock.Matchers.LinqMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch, WireMock.Matchers.MatchOperator.Or, \"it.Contains(\\\"test\\\"\")");
//}
[Fact]
@@ -131,7 +131,7 @@ public class MatcherTests
var result = matcher.GetCSharpCodeArguments();
// Assert
result.Should().Be("new RegexMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch, \"pattern\", false, true, WireMock.Matchers.MatchOperator.Or)");
result.Should().Be("new WireMock.Matchers.RegexMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch, new AnyOfTypes.AnyOf<string, WireMock.Models.StringPattern>[] { \"pattern\" }, false, true, WireMock.Matchers.MatchOperator.Or)");
}
[Fact]
@@ -144,7 +144,7 @@ public class MatcherTests
var result = matcher.GetCSharpCodeArguments();
// Assert
result.Should().Be("new SimMetricsMatcher.Levenstein(WireMock.Matchers.MatchBehaviour.AcceptOnMatch, \"test\", SimMetrics.Net.SimMetricType.Levenstein, WireMock.Matchers.MatchOperator.Average)");
result.Should().Be("new WireMock.Matchers.SimMetricsMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch, new AnyOfTypes.AnyOf<string, WireMock.Models.StringPattern>[] { \"test\" }, SimMetrics.Net.SimMetricType.Levenstein, WireMock.Matchers.MatchOperator.Average)");
}
[Fact]
@@ -157,7 +157,7 @@ public class MatcherTests
var result = matcher.GetCSharpCodeArguments();
// Assert
result.Should().Be("new WildcardMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch, \"pattern\", false, WireMock.Matchers.MatchOperator.Or)");
result.Should().Be("new WireMock.Matchers.WildcardMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch, new AnyOfTypes.AnyOf<string, WireMock.Models.StringPattern>[] { \"pattern\" }, false, WireMock.Matchers.MatchOperator.Or)");
}
[Fact]
@@ -170,6 +170,6 @@ public class MatcherTests
var result = matcher.GetCSharpCodeArguments();
// Assert
result.Should().Be("new XPathMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch, WireMock.Matchers.MatchOperator.Or, null, \"pattern1\")");
result.Should().Be("new WireMock.Matchers.XPathMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch, WireMock.Matchers.MatchOperator.Or, null, \"pattern1\")");
}
}
@@ -478,7 +478,7 @@ public class SystemTextJsonMatcherTests
var result = matcher.GetCSharpCodeArguments();
// Assert
result.Should().StartWith($"new SystemTextJsonMatcher(WireMock.Matchers.MatchBehaviour.{matchBehaviour},");
result.Should().StartWith($"new WireMock.Matchers.SystemTextJsonMatcher(WireMock.Matchers.MatchBehaviour.{matchBehaviour},");
result.Should().EndWith($", {ignoreCase.ToString().ToLowerInvariant()}, {regex.ToString().ToLowerInvariant()}, {ignoreArrayOrder.ToString().ToLowerInvariant()})");
}
}
@@ -36,12 +36,38 @@ public class WireMockOpenApiParserTests
var openApiDocument = File.ReadAllText(Path.Combine("OpenApiParser", "payroc-openapi-spec.yaml"));
// Act
var mappings = _sut.FromText(openApiDocument, settings, out _);
var mappings = _sut.FromText(openApiDocument, settings, out var diagnostic);
// Assert
mappings.Should().NotBeEmpty();
diagnostic.Should().NotBeNull();
diagnostic.Errors.Should().BeEmpty();
// Verify
await Verify(mappings);
}
[Fact]
public void FromText_UsingInvalidYaml_ShouldReturnEmptyMappingsWithDiagnostic()
{
// Arrange
var settings = new WireMockOpenApiParserSettings
{
ExampleValues = _exampleValuesMock.Object
};
var openApiDocument = File.ReadAllText(Path.Combine("OpenApiParser", "invalid.yaml"));
// Act
var mappings = _sut.FromText(openApiDocument, settings, out var diagnostic);
// Assert
mappings.Should().BeEmpty();
diagnostic.Should().NotBeNull();
diagnostic.Errors.Should().HaveCount(1);
diagnostic.Errors[0].Message.Should().Be("Responses must contain at least one response");
}
[Fact]
public async Task FromText_UsingJson_WithPlainTextExample_ShouldReturnMappings()
{
@@ -54,7 +80,12 @@ public class WireMockOpenApiParserTests
var openApiDocument = File.ReadAllText(Path.Combine("OpenApiParser", "oas-content-example.json"));
// Act
var mappings = _sut.FromText(openApiDocument, settings, out _);
var mappings = _sut.FromText(openApiDocument, settings, out var diagnostic);
// Assert
mappings.Should().NotBeEmpty();
diagnostic.Should().NotBeNull();
diagnostic.Errors.Should().BeEmpty();
// Verify
await Verify(mappings);
@@ -0,0 +1,12 @@
openapi: 3.2.0
info:
version: '1'
title: Invalid API
description: Invalid
paths:
/funding-recipients:
summary: Create and manage funding recipients.
post:
tags:
- Funding recipients
@@ -106,6 +106,15 @@ public class WireMockMiddlewareTests
[Fact]
public async Task WireMockMiddleware_Invoke_NoMatch()
{
// Arrange
var inititalDateTime = new DateTime(2027, 1, 1, 0, 0, 0, DateTimeKind.Utc);
var responseDateTime = new DateTime(2027, 1, 1, 0, 0, 42, DateTimeKind.Utc);
_dateTimeUtilsMock.Reset();
_dateTimeUtilsMock.SetupSequence(d => d.UtcNow)
.Returns(inititalDateTime)
.Returns(responseDateTime);
// Act
await _sut.Invoke(_contextMock.Object);
@@ -115,7 +124,7 @@ public class WireMockMiddlewareTests
Expression<Func<ResponseMessage, bool>> match = r =>
(int)r.StatusCode! == 404 &&
((StatusModel)r.BodyData!.BodyAsJson!).Status == "No matching mapping found" &&
r.DateTime == UtcNow;
r.DateTime == responseDateTime;
_responseMapperMock.Verify(m => m.MapAsync(It.Is(match), It.IsAny<HttpResponse>()), Times.Once);
}
@@ -142,12 +142,12 @@ public class RequestBuilderWithBodyTests
public void Request_WithBody_FuncFormUrlEncoded()
{
// Assign
var requestBuilder = Request.Create().UsingAnyMethod().WithBody((IDictionary<string, string>? values) => values != null);
var requestBuilder = Request.Create().UsingAnyMethod().WithBody((IDictionary<string, WireMockList<string>>? values) => values != null);
// Act
var body = new BodyData
{
BodyAsFormUrlEncoded = new Dictionary<string, string>(),
BodyAsFormUrlEncoded = new Dictionary<string, WireMockList<string>>(),
DetectedBodyTypeFromContentType = BodyType.FormUrlEncoded,
DetectedBodyType = BodyType.FormUrlEncoded
};
@@ -64,4 +64,23 @@ public class RequestBuilderWithClientIPTests
var requestMatchResult = new RequestMatchResult();
spec.GetMatchingScore(request, requestMatchResult).Should().Be(1.0);
}
[Fact]
public void Request_WithClientIP_MatchOperator_And_RequiresAllMatchers()
{
// given: two matchers combined with And (the client IP must satisfy BOTH)
var spec = Request.Create().WithClientIP(MatchOperator.And, new WildcardMatcher("1.2.*"), new WildcardMatcher("*.3.4"));
// when: an IP matching both matchers -> perfect match
var matchesBoth = new RequestMessage(new UrlDetails("http://localhost"), "GET", "1.2.3.4");
spec.GetMatchingScore(matchesBoth, new RequestMatchResult()).Should().Be(1.0);
// when: an IP matching only the first matcher -> mismatch
var matchesFirstOnly = new RequestMessage(new UrlDetails("http://localhost"), "GET", "1.2.9.9");
spec.GetMatchingScore(matchesFirstOnly, new RequestMatchResult()).Should().Be(0.0);
// when: an IP matching only the second matcher -> mismatch
var matchesSecondOnly = new RequestMessage(new UrlDetails("http://localhost"), "GET", "9.3.4");
spec.GetMatchingScore(matchesSecondOnly, new RequestMatchResult()).Should().Be(0.0);
}
}
@@ -15,7 +15,7 @@ public class RequestMessageParamMatcherTests
{
// Assign
var requestMessage = new RequestMessage(new UrlDetails("http://localhost?key=test1"), "GET", "127.0.0.1");
var matcher = new RequestMessageParamMatcher(MatchBehaviour.AcceptOnMatch, "KeY", true, new[] { "test1" });
var matcher = new RequestMessageParamMatcher(MatchBehaviour.AcceptOnMatch, "KeY", true, ["test1"]);
// Act
var result = new RequestMatchResult();
@@ -30,7 +30,7 @@ public class RequestMessageParamMatcherTests
{
// Assign
var requestMessage = new RequestMessage(new UrlDetails("http://localhost?key=test1"), "GET", "127.0.0.1");
var matcher = new RequestMessageParamMatcher(MatchBehaviour.AcceptOnMatch, "key", false, new[] { "test1", "test2" });
var matcher = new RequestMessageParamMatcher(MatchBehaviour.AcceptOnMatch, "key", false, ["test1", "test2"]);
// Act
var result = new RequestMatchResult();
@@ -60,7 +60,7 @@ public class RequestMessageParamMatcherTests
{
// Assign
var requestMessage = new RequestMessage(new UrlDetails("http://localhost?key=test1,test2,test3"), "GET", "127.0.0.1");
var matcher = new RequestMessageParamMatcher(MatchBehaviour.AcceptOnMatch, "key", false, new IStringMatcher[] { new ExactMatcher("test1", "test2") });
var matcher = new RequestMessageParamMatcher(MatchBehaviour.AcceptOnMatch, "key", false, [new ExactMatcher("test1", "test2")]);
// Act
var result = new RequestMatchResult();
@@ -71,18 +71,18 @@ public class RequestMessageParamMatcherTests
}
[Fact]
public void RequestMessageParamMatcher_GetMatchingScore_KeyWith2ValuesPresentInUrl_And_With1ExactStringWith3Patterns_Returns0_66()
public void RequestMessageParamMatcher_GetMatchingScore_KeyWith2ValuesPresentInUrl_And_With1ExactStringWith3Patterns_Returns1_0()
{
// Assign
var requestMessage = new RequestMessage(new UrlDetails("http://localhost?key=test1,test2"), "GET", "127.0.0.1");
var matcher = new RequestMessageParamMatcher(MatchBehaviour.AcceptOnMatch, "key", false, new IStringMatcher[] { new ExactMatcher("test1", "test2", "test3") });
var matcher = new RequestMessageParamMatcher(MatchBehaviour.AcceptOnMatch, "key", false, [new ExactMatcher("test1", "test2", "test3")]);
// Act
var result = new RequestMatchResult();
double score = matcher.GetMatchingScore(requestMessage, result);
// Assert
score.Should().BeApproximately(0.66d, 0.1d);
score.Should().Be(1.0d);
}
[Fact]
@@ -90,7 +90,7 @@ public class RequestMessageParamMatcherTests
{
// Assign
var requestMessage = new RequestMessage(new UrlDetails("http://localhost?key=test1,test2"), "GET", "127.0.0.1");
var matcher = new RequestMessageParamMatcher(MatchBehaviour.AcceptOnMatch, "key", false, new[] { "test1", "test2" });
var matcher = new RequestMessageParamMatcher(MatchBehaviour.AcceptOnMatch, "key", false, ["test1", "test2"]);
// Act
var result = new RequestMatchResult();
@@ -105,7 +105,7 @@ public class RequestMessageParamMatcherTests
{
// Assign
var requestMessage = new RequestMessage(new UrlDetails("http://localhost?key=test1,test2"), "GET", "127.0.0.1");
var matcher = new RequestMessageParamMatcher(MatchBehaviour.AcceptOnMatch, "key", false, new IStringMatcher[] { new ExactMatcher("test1"), new ExactMatcher("test2") });
var matcher = new RequestMessageParamMatcher(MatchBehaviour.AcceptOnMatch, "key", false, [new ExactMatcher("test1"), new ExactMatcher("test2")]);
// Act
var result = new RequestMatchResult();
@@ -120,7 +120,7 @@ public class RequestMessageParamMatcherTests
{
// Assign
var requestMessage = new RequestMessage(new UrlDetails("http://localhost?key=test0,test2"), "GET", "127.0.0.1");
var matcher = new RequestMessageParamMatcher(MatchBehaviour.AcceptOnMatch, "key", false, new[] { "test1", "test2" });
var matcher = new RequestMessageParamMatcher(MatchBehaviour.AcceptOnMatch, "key", false, ["test1", "test2"]);
// Act
var result = new RequestMatchResult();
@@ -135,7 +135,7 @@ public class RequestMessageParamMatcherTests
{
// Assign
var requestMessage = new RequestMessage(new UrlDetails("http://localhost?key"), "GET", "127.0.0.1");
var matcher = new RequestMessageParamMatcher(MatchBehaviour.AcceptOnMatch, "key", false, new[] { "test1", "test2" });
var matcher = new RequestMessageParamMatcher(MatchBehaviour.AcceptOnMatch, "key", false, ["test1", "test2"]);
// Act
var result = new RequestMatchResult();
@@ -209,5 +209,79 @@ public class RequestMessageParamMatcherTests
// Assert
score.Should().Be(1.0);
}
}
[Fact]
public void RequestMessageParamMatcher_RejectOnMatch_WhenValuePresentMatchesPattern_ReturnsMismatch()
{
// Assign: the param value in the URL matches the reject pattern -> the mapping must be rejected (score 0.0).
var requestMessage = new RequestMessage(new UrlDetails("http://localhost?key=abc"), "GET", "127.0.0.1");
var matcher = new RequestMessageParamMatcher(MatchBehaviour.RejectOnMatch, "key", false, ["abc"]);
// Act
var result = new RequestMatchResult();
var score = matcher.GetMatchingScore(requestMessage, result);
// Assert
score.Should().Be(0.0d);
}
[Fact]
public void RequestMessageParamMatcher_RejectOnMatch_WhenValuePresentDoesNotMatchPattern_ReturnsPerfect()
{
// Assign: the param value in the URL does NOT match the reject pattern -> the mapping is accepted (score 1.0).
var requestMessage = new RequestMessage(new UrlDetails("http://localhost?key=xyz"), "GET", "127.0.0.1");
var matcher = new RequestMessageParamMatcher(MatchBehaviour.RejectOnMatch, "key", false, ["abc"]);
// Act
var result = new RequestMatchResult();
var score = matcher.GetMatchingScore(requestMessage, result);
// Assert
score.Should().Be(1.0d);
}
[Fact]
public void RequestMessageParamMatcher_RejectOnMatch_WithIgnoreCase_WhenValueMatchesCaseInsensitively_ReturnsMismatch()
{
// Assign: ignoreCase must still be honored on the inner matcher after the fix.
var requestMessage = new RequestMessage(new UrlDetails("http://localhost?key=ABC"), "GET", "127.0.0.1");
var matcher = new RequestMessageParamMatcher(MatchBehaviour.RejectOnMatch, "key", true, ["abc"]);
// Act
var result = new RequestMatchResult();
var score = matcher.GetMatchingScore(requestMessage, result);
// Assert
score.Should().Be(0.0d);
}
[Fact]
public void RequestMessageParamMatcher_AcceptOnMatch_WhenValuePresentMatchesPattern_ReturnsPerfect()
{
// Assign
var requestMessage = new RequestMessage(new UrlDetails("http://localhost?key=abc"), "GET", "127.0.0.1");
var matcher = new RequestMessageParamMatcher(MatchBehaviour.AcceptOnMatch, "key", false, ["abc"]);
// Act
var result = new RequestMatchResult();
var score = matcher.GetMatchingScore(requestMessage, result);
// Assert
score.Should().Be(1.0d);
}
[Fact]
public void RequestMessageParamMatcher_AcceptOnMatch_WhenValuePresentDoesNotMatchPattern_ReturnsMismatch()
{
// Assign
var requestMessage = new RequestMessage(new UrlDetails("http://localhost?key=xyz"), "GET", "127.0.0.1");
var matcher = new RequestMessageParamMatcher(MatchBehaviour.AcceptOnMatch, "key", false, ["abc"]);
// Act
var result = new RequestMatchResult();
var score = matcher.GetMatchingScore(requestMessage, result);
// Assert
score.Should().Be(0.0d);
}
}
@@ -8,6 +8,8 @@ using WireMock.Util;
using WireMock.Handlers;
using Moq;
using Microsoft.AspNetCore.Http;
using Newtonsoft.Json;
using Scriban.Runtime;
namespace WireMock.Net.Tests.ResponseBuilders;
@@ -65,5 +67,50 @@ public class ResponseWithScribanTests
// Assert
response.Message.BodyData.BodyAsString.Should().Be("test http://localhost/foo /foo POSt");
}
[Theory]
[InlineData(TransformerType.Scriban)]
[InlineData(TransformerType.ScribanDotLiquid)]
public async Task Response_ProvideResponse_WithScribanRegistrationCallback_UsesCustomFunctionInBodyAndHeaders(TransformerType transformerType)
{
// Arrange
_settings.ScribanRegistrationCallback = (scriptObject, _) =>
((ScriptObject)scriptObject).Import("fake_name", new Func<string>(() => "Jane Doe"));
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "GET", ClientIp);
var responseBuilder = Response.Create()
.WithBody("{{ fake_name }} {{ request.Path }}")
.WithHeader("x-name", "{{ fake_name }}")
.WithTransformer(transformerType);
// Act
var response = await responseBuilder.ProvideResponseAsync(_mappingMock.Object, Mock.Of<HttpContext>(), request, _settings);
// Assert
response.Message.BodyData!.BodyAsString.Should().Be("Jane Doe /foo");
response.Message.Headers!["x-name"].Should().ContainSingle().Which.Should().Be("Jane Doe");
}
[Theory]
[InlineData(TransformerType.Scriban)]
[InlineData(TransformerType.ScribanDotLiquid)]
public async Task Response_ProvideResponse_WithScribanRegistrationCallback_UsesCustomFunctionInBodyAsJson(TransformerType transformerType)
{
// Arrange
_settings.WithScribanRegistration((scriptObject, _) => scriptObject.Import("fake_name", new Func<string>(() => "Jane Doe")));
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "GET", ClientIp);
var responseBuilder = Response.Create()
.WithBodyAsJson(new { name = "{{ fake_name }}", path = "{{ request.Path }}" })
.WithTransformer(transformerType);
// Act
var response = await responseBuilder.ProvideResponseAsync(_mappingMock.Object, Mock.Of<HttpContext>(), request, _settings);
// Assert
JsonConvert.SerializeObject(response.Message.BodyData!.BodyAsJson).Should().Be("{\"name\":\"Jane Doe\",\"path\":\"/foo\"}");
}
}
@@ -1,5 +1,6 @@
// Copyright © WireMock.Net
using WireMock.Matchers;
using WireMock.Matchers.Request;
using WireMock.Net.Tests.VerifyExtensions;
using WireMock.RequestBuilders;
@@ -97,6 +98,167 @@ public partial class MappingConverterTests
return Verify(code, VerifySettings);
}
[Fact]
public Task ToCSharpCode_With_ScenarioAndState_Emits_WhenStateIs_And_WillSetStateTo()
{
// Assign: a stateful mapping that gates on state "Begin" and moves to "End" after 3 hits.
var mapping = CreateScenarioMapping("Ordering", executionConditionState: "Begin", nextState: "End", timesInSameState: 3);
// Act
var code = _sut.ToCSharpCode(mapping, new MappingConverterSettings
{
AddStart = false,
ConverterType = MappingConverterType.Server
});
// Verify
return Verify(code, VerifySettings);
}
[Fact]
public Task ToCSharpCode_With_NextStateOnly_Emits_WillSetStateTo_Without_Times()
{
// Assign: a start-state mapping that only sets the next state (no execution condition, default times).
var mapping = CreateScenarioMapping("Ordering", executionConditionState: null, nextState: "Begin", timesInSameState: null);
// Act
var code = _sut.ToCSharpCode(mapping, new MappingConverterSettings
{
AddStart = false,
ConverterType = MappingConverterType.Server
});
// Verify
return Verify(code, VerifySettings);
}
[Fact]
public Task ToCSharpCode_WithPath_RegexMatcher_MultiplePatterns()
{
// Assign
var mapping = CreateMappingWithRequest(Request.Create()
.UsingGet()
.WithPath(new RegexMatcher(MatchBehaviour.AcceptOnMatch, ["/a", "/b"])));
// Act
var code = _sut.ToCSharpCode(mapping, new MappingConverterSettings
{
AddStart = false,
ConverterType = MappingConverterType.Server
});
// Verify
return Verify(code, VerifySettings);
}
[Fact]
public Task ToCSharpCode_WithPath_SimMetricsMatcher_MultiplePatterns()
{
// Assign
var mapping = CreateMappingWithRequest(Request.Create()
.UsingGet()
.WithPath(new SimMetricsMatcher(new AnyOfTypes.AnyOf<string, Models.StringPattern>[] { "/a", "/b" })));
// Act
var code = _sut.ToCSharpCode(mapping, new MappingConverterSettings
{
AddStart = false,
ConverterType = MappingConverterType.Server
});
// Verify
return Verify(code, VerifySettings);
}
[Fact]
public Task ToCSharpCode_WithPath_ContentTypeMatcher_MultiplePatterns()
{
// Assign
var mapping = CreateMappingWithRequest(Request.Create()
.UsingGet()
.WithPath(new ContentTypeMatcher(["text/a", "text/b"])));
// Act
var code = _sut.ToCSharpCode(mapping, new MappingConverterSettings
{
AddStart = false,
ConverterType = MappingConverterType.Server
});
// Verify
return Verify(code, VerifySettings);
}
[Fact]
public Task ToCSharpCode_WithPath_FormUrlEncodedMatcher_MultiplePatterns()
{
// Assign
var mapping = CreateMappingWithRequest(Request.Create()
.UsingGet()
.WithPath(new FormUrlEncodedMatcher(["a=1", "b=2"])));
// Act
var code = _sut.ToCSharpCode(mapping, new MappingConverterSettings
{
AddStart = false,
ConverterType = MappingConverterType.Server
});
// Verify
return Verify(code, VerifySettings);
}
private Mapping CreateMappingWithRequest(IRequestBuilder requestBuilder)
{
return new Mapping
(
guid: new Guid("8e7b9ab7-e18e-4502-8bc9-11e6679811cc"),
updatedAt: _updatedAt,
title: string.Empty,
description: string.Empty,
path: null,
settings: _settings,
requestMatcher: (IRequestMatcher)requestBuilder,
provider: Response.Create().WithSuccess(),
priority: 0,
scenario: null,
executionConditionState: null,
nextState: null,
stateTimes: null,
webhooks: null,
useWebhooksFireAndForget: false,
timeSettings: null,
data: null
);
}
private Mapping CreateScenarioMapping(string scenario, string? executionConditionState, string? nextState, int? timesInSameState)
{
var request = Request.Create().UsingGet().WithPath("/test_path");
var response = Response.Create().WithSuccess();
return new Mapping
(
guid: new Guid("8e7b9ab7-e18e-4502-8bc9-11e6679811cc"),
updatedAt: _updatedAt,
title: string.Empty,
description: string.Empty,
path: null,
settings: _settings,
requestMatcher: request,
provider: response,
priority: 0,
scenario: scenario,
executionConditionState: executionConditionState,
nextState: nextState,
stateTimes: timesInSameState,
webhooks: null,
useWebhooksFireAndForget: false,
timeSettings: null,
data: null
);
}
private IMapping CreateMapping()
{
var guid = new Guid("8e7b9ab7-e18e-4502-8bc9-11e6679811cc");
@@ -117,22 +279,22 @@ public partial class MappingConverterTests
return new Mapping
(
guid,
_updatedAt,
string.Empty,
string.Empty,
null,
_settings,
request,
response,
42,
null,
null,
null,
null,
null,
false,
null,
guid: guid,
updatedAt: _updatedAt,
title: string.Empty,
description: string.Empty,
path: null,
settings: _settings,
requestMatcher: request,
provider: response,
priority: 42,
scenario: null,
executionConditionState: null,
nextState: null,
stateTimes: null,
webhooks: null,
useWebhooksFireAndForget: false,
timeSettings: null,
data: null
).WithProbability(0.3);
}
@@ -0,0 +1,9 @@
server
.Given(Request.Create()
.UsingMethod("GET")
.WithPath(new WireMock.Matchers.ContentTypeMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch, new AnyOfTypes.AnyOf<string, WireMock.Models.StringPattern>[] { "text/a", "text/b" }, false))
)
.WithGuid("8e7b9ab7-e18e-4502-8bc9-11e6679811cc")
.RespondWith(Response.Create()
.WithStatusCode(200)
);
Loaded 100 of 121 files, more files were not shown because too many files have changed in this diff. Show more