mirror of
https://github.com/wiremock/WireMock.Net.git
synced 2026-08-19 10:15:28 +02:00
Compare commits
16
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
48792c7b7b | ||
|
|
a31f4768a8 | ||
|
|
42353f035e | ||
|
|
7e9718eab7 | ||
|
|
a0df0916f9 | ||
|
|
7a8582fef5 | ||
|
|
05929bdea6 | ||
|
|
84c32d5b2d | ||
|
|
d39f7c771f | ||
|
|
97c8e4a08a | ||
|
|
f5eb01c580 | ||
|
|
8260dba641 | ||
|
|
5e1c126e34 | ||
|
|
ca4eea808e | ||
|
|
5692498ea3 | ||
|
|
d270ad7f4f |
@@ -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
|
||||
@@ -1,3 +1,19 @@
|
||||
# 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.13.0 (19 July 2026)
|
||||
- [#1488](https://github.com/wiremock/WireMock.Net/pull/1488) - feat(xunit logging): TestOutputHelperWireMockLogger [feature] contributed by [Stepami](https://github.com/Stepami)
|
||||
- [#1490](https://github.com/wiremock/WireMock.Net/pull/1490) - Fix: Fully respect configured custom admin path (merge PR #1483) [feature] contributed by [Copilot](https://github.com/apps/copilot-swe-agent)
|
||||
- [#1482](https://github.com/wiremock/WireMock.Net/issues/1482) - WireMock.Minimal.Owin does not respect custom pathing for determining admin paths [bug]
|
||||
|
||||
# 2.12.0 (07 July 2026)
|
||||
- [#1479](https://github.com/wiremock/WireMock.Net/pull/1479) - Upgrade Microsoft.OpenApi to 3.7.0 and YamlDotNet to 18.1.0 [security] contributed by [StefH](https://github.com/StefH)
|
||||
- [#1480](https://github.com/wiremock/WireMock.Net/pull/1480) - Upgrade RamlToOpenApiConverter.SourceOnly to 0.21.0 [feature] contributed by [StefH](https://github.com/StefH)
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<VersionPrefix>2.12.0</VersionPrefix>
|
||||
<VersionPrefix>2.15.0</VersionPrefix>
|
||||
<PackageIcon>WireMock.Net-Logo.png</PackageIcon>
|
||||
<PackageProjectUrl>https://github.com/wiremock/WireMock.Net</PackageProjectUrl>
|
||||
<PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
|
||||
@@ -51,8 +51,8 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="JetBrains.Annotations" Version="2025.2.4" PrivateAssets="All" />
|
||||
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="10.0.300" PrivateAssets="All" />
|
||||
<PackageReference Include="JetBrains.Annotations" Version="2026.2.0" PrivateAssets="All" />
|
||||
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="10.0.400" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition=" '$(TargetFramework)' == 'net8.0' ">
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
rem https://github.com/StefH/GitHubReleaseNotes
|
||||
|
||||
SET version=2.12.0
|
||||
SET version=2.15.0
|
||||
|
||||
GitHubReleaseNotes --output CHANGELOG.md --skip-empty-releases --exclude-labels wontfix test question invalid doc duplicate example environment --version %version% --token %GH_TOKEN%
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
# 2.12.0 (07 July 2026)
|
||||
- #1479 Upgrade Microsoft.OpenApi to 3.7.0 and YamlDotNet to 18.1.0 [security]
|
||||
- #1480 Upgrade RamlToOpenApiConverter.SourceOnly to 0.21.0 [feature]
|
||||
- #1484 Update Scriban.Signed package version to 7.2.5 [security]
|
||||
- #1487 Response.Create() sets the DateTime to UtcNow [bug]
|
||||
- #1486 ResponseMessage.DateTime defaults to DateTime.MinValue, causing premature log entry purging for *matched* requests [bug]
|
||||
# 2.15.0 (15 August 2026)
|
||||
- #1495 Fix #1494: `WithClientIP(MatchOperator, params IStringMatcher[])` ignores the MatchOperator argument [bug]
|
||||
- #1497 Fix WithParam RejectOnMatch inverting the match result #1496 [bug]
|
||||
- #1499 Update Testcontainers [bug]
|
||||
- #1494 `WithClientIP(MatchOperator, params IStringMatcher[])` ignores the `MatchOperator` argument [bug]
|
||||
- #1496 `WithParam(key, MatchBehaviour.RejectOnMatch, values)` inverts the match result [bug]
|
||||
|
||||
The full release notes can be found here: https://github.com/wiremock/WireMock.Net/blob/master/CHANGELOG.md
|
||||
@@ -1,3 +1,9 @@
|
||||
parameters:
|
||||
- name: runWindowsReleaseToMyGet
|
||||
displayName: Run Windows Release to MyGet
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
variables:
|
||||
Prerelease: 'ci'
|
||||
buildId: "1$(Build.BuildId)"
|
||||
@@ -131,6 +137,7 @@ jobs:
|
||||
|
||||
- job: Windows_Release_to_MyGet
|
||||
dependsOn: Windows_Build_Test
|
||||
condition: and(succeeded(), ${{ parameters.runWindowsReleaseToMyGet }})
|
||||
|
||||
pool:
|
||||
vmImage: 'windows-2025'
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace WireMock.Net.Console.MimePart;
|
||||
|
||||
static class Program
|
||||
{
|
||||
private static readonly ILoggerRepository LogRepository = LogManager.GetRepository(Assembly.GetEntryAssembly());
|
||||
private static readonly ILoggerRepository LogRepository = LogManager.GetRepository(Assembly.GetEntryAssembly()!);
|
||||
private static readonly ILog Log = LogManager.GetLogger(typeof(Program));
|
||||
|
||||
static async Task Main(params string[] args)
|
||||
|
||||
@@ -1115,7 +1115,7 @@ namespace WireMock.Net.ConsoleApplication
|
||||
{
|
||||
BodyData = new BodyData
|
||||
{
|
||||
BodyAsString = "random200or505:" + code + ", HeadersFromRequest = " + string.Join(",", request.Headers),
|
||||
BodyAsString = "random200or505:" + code + ", HeadersFromRequest = " + request.Headers != null ? string.Join(",", request.Headers!.Select(h => $"{h.Key}:{h.Value}")) : "none",
|
||||
DetectedBodyType = BodyType.String,
|
||||
},
|
||||
StatusCode = code
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using log4net;
|
||||
using log4net.Config;
|
||||
using log4net.Repository;
|
||||
@@ -12,8 +10,7 @@ namespace WireMock.Net.Console.NET8;
|
||||
|
||||
static class Program
|
||||
{
|
||||
private static readonly ILoggerRepository LogRepository = LogManager.GetRepository(Assembly.GetEntryAssembly());
|
||||
private static readonly ILog Log = LogManager.GetLogger(typeof(Program));
|
||||
private static readonly ILoggerRepository LogRepository = LogManager.GetRepository(Assembly.GetEntryAssembly()!);
|
||||
|
||||
static async Task Main(params string[] args)
|
||||
{
|
||||
|
||||
+1
-1
@@ -16,7 +16,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="WireMock.Net" Version="2.10.0" />
|
||||
<PackageReference Include="WireMock.Net" Version="2.12.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -36,12 +36,6 @@
|
||||
<StartupObject>Wiremock.Net.Service.Program</StartupObject>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Newtonsoft.Json, Version=13.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Newtonsoft.Json.13.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="SimMetrics.Net, Version=1.0.5.0, Culture=neutral, PublicKeyToken=c58dc06d59f3391b, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\SimMetrics.Net.1.0.5\lib\net45\SimMetrics.Net.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Configuration" />
|
||||
<Reference Include="System.Configuration.Install" />
|
||||
@@ -91,13 +85,13 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="log4net">
|
||||
<Version>3.3.0</Version>
|
||||
<Version>3.3.2</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel.Core">
|
||||
<Version>2.3.9</Version>
|
||||
<Version>2.3.11</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="WireMock.Net">
|
||||
<Version>2.10.0</Version>
|
||||
<Version>2.12.0</Version>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
|
||||
@@ -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).
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System.Linq;
|
||||
|
||||
namespace WireMock.Types;
|
||||
|
||||
/// <summary>
|
||||
/// A special List which overrides the ToString() to return first value.
|
||||
/// A special List which overrides the ToString() to return first value in case of a single element.
|
||||
/// Else it will return a comma separated list of all values.
|
||||
/// If null or empty, it will return an empty string.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The generic type</typeparam>
|
||||
/// <seealso cref="List{T}" />
|
||||
@@ -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>
|
||||
@@ -62,10 +154,10 @@ public class WireMockList<T> : List<T>
|
||||
return strValue;
|
||||
}
|
||||
|
||||
return this[0]?.ToString();
|
||||
return this[0]?.ToString() ?? string.Empty;
|
||||
|
||||
default:
|
||||
var strings = this.Select(x => x as string ?? x?.ToString());
|
||||
var strings = this.Select(x => x as string ?? x?.ToString() ?? string.Empty);
|
||||
return string.Join(", ", strings);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -116,37 +117,51 @@ public class FormUrlEncodedMatcher : IStringMatcher, IIgnoreCaseMatcher
|
||||
var matches = GetMatches(inputNameValueCollection);
|
||||
|
||||
var score = MatchScores.ToScore(matches, MatchOperator);
|
||||
return MatchResult.From(Name, MatchBehaviourHelper.Convert(MatchBehaviour, score));
|
||||
return MatchResult.From(Name, 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 />
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System.Text.RegularExpressions;
|
||||
using WireMock.Matchers;
|
||||
|
||||
namespace WireMock.Owin;
|
||||
|
||||
internal sealed class AdminPaths(string? adminPath) : IAdminPaths
|
||||
{
|
||||
private const string DefaultAdminPathPrefix = "/__admin";
|
||||
|
||||
private readonly string _prefix = adminPath ?? DefaultAdminPathPrefix;
|
||||
|
||||
public string Files => $"{_prefix}/files";
|
||||
|
||||
public string Health => $"{_prefix}/health";
|
||||
|
||||
public string Mappings => $"{_prefix}/mappings";
|
||||
|
||||
public string MappingsCode => $"{_prefix}/mappings/code";
|
||||
|
||||
public string MappingsWireMockOrg => $"{_prefix}/mappings/wiremock.org";
|
||||
|
||||
public string Requests => $"{_prefix}/requests";
|
||||
|
||||
public string Settings => $"{_prefix}/settings";
|
||||
|
||||
public string Scenarios => $"{_prefix}/scenarios";
|
||||
|
||||
public string OpenApi => $"{_prefix}/openapi";
|
||||
|
||||
public string ProtoDefinitions => $"{_prefix}/protodefinitions";
|
||||
|
||||
private string PrefixRegexEscaped => Regex.Escape(_prefix);
|
||||
|
||||
public RegexMatcher MappingsGuidPathMatcher => new($@"^{PrefixRegexEscaped}\/mappings\/([0-9A-Fa-f]{{8}}[-][0-9A-Fa-f]{{4}}[-][0-9A-Fa-f]{{4}}[-][0-9A-Fa-f]{{4}}[-][0-9A-Fa-f]{{12}})$");
|
||||
|
||||
public RegexMatcher MappingsGuidEnablePathMatcher => new($@"^{PrefixRegexEscaped}\/mappings\/([0-9A-Fa-f]{{8}}[-][0-9A-Fa-f]{{4}}[-][0-9A-Fa-f]{{4}}[-][0-9A-Fa-f]{{4}}[-][0-9A-Fa-f]{{12}})\/enable$");
|
||||
|
||||
public RegexMatcher MappingsGuidDisablePathMatcher => new($@"^{PrefixRegexEscaped}\/mappings\/([0-9A-Fa-f]{{8}}[-][0-9A-Fa-f]{{4}}[-][0-9A-Fa-f]{{4}}[-][0-9A-Fa-f]{{4}}[-][0-9A-Fa-f]{{12}})\/disable$");
|
||||
|
||||
public RegexMatcher MappingsCodeGuidPathMatcher => new($@"^{PrefixRegexEscaped}\/mappings\/code\/([0-9A-Fa-f]{{8}}[-][0-9A-Fa-f]{{4}}[-][0-9A-Fa-f]{{4}}[-][0-9A-Fa-f]{{4}}[-][0-9A-Fa-f]{{12}})$");
|
||||
|
||||
public RegexMatcher RequestsGuidPathMatcher => new($@"^{PrefixRegexEscaped}\/requests\/([0-9A-Fa-f]{{8}}[-][0-9A-Fa-f]{{4}}[-][0-9A-Fa-f]{{4}}[-][0-9A-Fa-f]{{4}}[-][0-9A-Fa-f]{{12}})$");
|
||||
|
||||
public RegexMatcher ScenariosNameMatcher => new($@"^{PrefixRegexEscaped}\/scenarios\/.+$");
|
||||
|
||||
public RegexMatcher ScenariosNameWithStateMatcher => new($@"^{PrefixRegexEscaped}\/scenarios\/.+\/state$");
|
||||
|
||||
public RegexMatcher ScenariosNameWithResetMatcher => new($@"^{PrefixRegexEscaped}\/scenarios\/.+\/reset$");
|
||||
|
||||
public RegexMatcher FilesFilenamePathMatcher => new($@"^{PrefixRegexEscaped}\/files\/.+$");
|
||||
|
||||
public RegexMatcher ProtoDefinitionsIdPathMatcher => new($@"^{PrefixRegexEscaped}\/protodefinitions\/.+$");
|
||||
|
||||
public bool Includes(string? path) => path?.StartsWith($"{_prefix}/") ?? false;
|
||||
|
||||
public override string ToString() => _prefix;
|
||||
}
|
||||
@@ -66,6 +66,7 @@ internal partial class AspNetCoreSelfHost
|
||||
.ConfigureServices(services =>
|
||||
{
|
||||
services.AddSingleton(_wireMockMiddlewareOptions);
|
||||
services.AddSingleton(_wireMockMiddlewareOptions.AdminPaths);
|
||||
services.AddSingleton<IMappingMatcher, MappingMatcher>();
|
||||
services.AddSingleton<IRandomizerDoubleBetween0And1, RandomizerDoubleBetween0And1>();
|
||||
services.AddSingleton<IOwinRequestMapper, OwinRequestMapper>();
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
using WireMock.Matchers;
|
||||
|
||||
namespace WireMock.Owin;
|
||||
|
||||
internal interface IAdminPaths
|
||||
{
|
||||
string Files { get; }
|
||||
|
||||
string Health { get; }
|
||||
|
||||
string Mappings { get; }
|
||||
|
||||
string MappingsCode { get; }
|
||||
|
||||
string MappingsWireMockOrg { get; }
|
||||
|
||||
RegexMatcher MappingsGuidPathMatcher { get; }
|
||||
|
||||
RegexMatcher MappingsGuidEnablePathMatcher { get; }
|
||||
|
||||
RegexMatcher MappingsGuidDisablePathMatcher { get; }
|
||||
|
||||
RegexMatcher MappingsCodeGuidPathMatcher { get; }
|
||||
|
||||
RegexMatcher RequestsGuidPathMatcher { get; }
|
||||
|
||||
RegexMatcher ScenariosNameMatcher { get; }
|
||||
|
||||
RegexMatcher ScenariosNameWithStateMatcher { get; }
|
||||
|
||||
RegexMatcher ScenariosNameWithResetMatcher { get; }
|
||||
|
||||
RegexMatcher FilesFilenamePathMatcher { get; }
|
||||
|
||||
RegexMatcher ProtoDefinitionsIdPathMatcher { get; }
|
||||
|
||||
string Requests { get; }
|
||||
|
||||
string Settings { get; }
|
||||
|
||||
string Scenarios { get; }
|
||||
|
||||
string OpenApi { get; }
|
||||
|
||||
string ProtoDefinitions { get; }
|
||||
|
||||
bool Includes(string? path);
|
||||
}
|
||||
@@ -108,4 +108,6 @@ internal interface IWireMockMiddlewareOptions
|
||||
/// Set this property to customize how objects are serialized to and deserialized from JSON during mapping.
|
||||
/// </remarks>
|
||||
IJsonConverter DefaultJsonSerializer { get; set; }
|
||||
|
||||
IAdminPaths AdminPaths { get; set; }
|
||||
}
|
||||
@@ -21,6 +21,7 @@ internal class WireMockMiddleware(
|
||||
RequestDelegate next,
|
||||
#pragma warning restore CS9113 // Parameter is unread.
|
||||
IWireMockMiddlewareOptions options,
|
||||
IAdminPaths adminPaths,
|
||||
IOwinRequestMapper requestMapper,
|
||||
IOwinResponseMapper responseMapper,
|
||||
IMappingMatcher mappingMatcher,
|
||||
@@ -64,7 +65,7 @@ internal class WireMockMiddleware(
|
||||
Activity? activity = null;
|
||||
|
||||
// Check if we should trace this request (optionally exclude admin requests)
|
||||
var shouldTrace = tracingEnabled && !(excludeAdmin && request.Path.StartsWith("/__admin/"));
|
||||
var shouldTrace = tracingEnabled && !(excludeAdmin && adminPaths.Includes(request.Path));
|
||||
|
||||
if (shouldTrace)
|
||||
{
|
||||
|
||||
@@ -12,23 +12,27 @@ internal class WireMockMiddlewareLogger(
|
||||
IWireMockMiddlewareOptions options,
|
||||
LogEntryMapper logEntryMapper,
|
||||
IGuidUtils guidUtils,
|
||||
IAdminPaths adminPaths,
|
||||
IDateTimeUtils dateTimeUtils
|
||||
) : IWireMockMiddlewareLogger
|
||||
{
|
||||
public void LogRequestAndResponse(bool logRequest, RequestMessage request, IResponseMessage? response, MappingMatcherResult? match, MappingMatcherResult? partialMatch, Activity? activity)
|
||||
{
|
||||
var mapping = match?.Mapping;
|
||||
var partialMapping = partialMatch?.Mapping;
|
||||
|
||||
var logEntry = new LogEntry
|
||||
{
|
||||
Guid = guidUtils.NewGuid(),
|
||||
RequestMessage = request,
|
||||
ResponseMessage = response,
|
||||
|
||||
MappingGuid = match?.Mapping?.Guid,
|
||||
MappingTitle = match?.Mapping?.Title,
|
||||
MappingGuid = mapping?.Guid,
|
||||
MappingTitle = mapping?.Title,
|
||||
RequestMatchResult = match?.RequestMatchResult,
|
||||
|
||||
PartialMappingGuid = partialMatch?.Mapping?.Guid,
|
||||
PartialMappingTitle = partialMatch?.Mapping?.Title,
|
||||
PartialMappingGuid = partialMapping?.Guid,
|
||||
PartialMappingTitle = partialMapping?.Title,
|
||||
PartialMatchResult = partialMatch?.RequestMatchResult
|
||||
};
|
||||
|
||||
@@ -53,7 +57,8 @@ internal class WireMockMiddlewareLogger(
|
||||
|
||||
public void LogLogEntry(LogEntry entry, bool addRequest)
|
||||
{
|
||||
options.Logger.DebugRequestResponse(logEntryMapper.Map(entry), entry.RequestMessage?.Path.StartsWith("/__admin/") == true);
|
||||
var isAdminRequest = adminPaths.Includes(entry.RequestMessage?.Path);
|
||||
options.Logger.DebugRequestResponse(logEntryMapper.Map(entry), isAdminRequest);
|
||||
|
||||
// If addRequest is set to true and MaxRequestLogCount is null or does have a value greater than 0, try to add a new request log.
|
||||
if (addRequest && options.MaxRequestLogCount is null or > 0)
|
||||
|
||||
@@ -115,4 +115,7 @@ internal class WireMockMiddlewareOptions : IWireMockMiddlewareOptions
|
||||
|
||||
/// <inheritdoc />
|
||||
public IJsonConverter DefaultJsonSerializer { get; set; } = new NewtonsoftJsonConverter();
|
||||
|
||||
/// <inheritdoc />
|
||||
public IAdminPaths AdminPaths { get; set; } = new AdminPaths(null);
|
||||
}
|
||||
@@ -34,6 +34,7 @@ internal static class WireMockMiddlewareOptionsHelper
|
||||
options.QueryParameterMultipleValueSupport = settings.QueryParameterMultipleValueSupport;
|
||||
options.RequestLogExpirationDuration = settings.RequestLogExpirationDuration;
|
||||
options.SaveUnmatchedRequests = settings.SaveUnmatchedRequests;
|
||||
options.AdminPaths = new AdminPaths(settings.AdminPath);
|
||||
|
||||
#if USE_ASPNETCORE
|
||||
options.AdditionalServiceRegistration = settings.AdditionalServiceRegistration;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -342,7 +342,7 @@ internal class MappingConverter(MatcherMapper mapper)
|
||||
if (clientIPMatcher?.Matchers != null)
|
||||
{
|
||||
var clientIPMatchers = _mapper.Map(clientIPMatcher.Matchers);
|
||||
mappingModel.Request.Path = new ClientIPModel
|
||||
mappingModel.Request.ClientIP = new ClientIPModel
|
||||
{
|
||||
Matchers = clientIPMatchers,
|
||||
MatchOperator = clientIPMatchers?.Length > 1 ? clientIPMatcher.MatchOperator.ToString() : null
|
||||
|
||||
@@ -19,7 +19,6 @@ using WireMock.Owin;
|
||||
using WireMock.RequestBuilders;
|
||||
using WireMock.ResponseProviders;
|
||||
using WireMock.Serialization;
|
||||
using WireMock.Settings;
|
||||
using WireMock.Types;
|
||||
using WireMock.Util;
|
||||
|
||||
@@ -28,50 +27,16 @@ namespace WireMock.Server;
|
||||
public partial class WireMockServer
|
||||
{
|
||||
private const int EnhancedFileSystemWatcherTimeoutMs = 1000;
|
||||
private const string DefaultAdminPathPrefix = "/__admin";
|
||||
private const string QueryParamReloadStaticMappings = "reloadStaticMappings";
|
||||
private static readonly Guid ProxyMappingGuid = new("e59914fd-782e-428e-91c1-4810ffb86567");
|
||||
private static readonly RegexMatcher AdminRequestContentTypeJson = new ContentTypeMatcher(WireMockConstants.ContentTypeJson, true);
|
||||
private EnhancedFileSystemWatcher? _enhancedFileSystemWatcher;
|
||||
private AdminPaths? _adminPaths;
|
||||
|
||||
private sealed class AdminPaths
|
||||
{
|
||||
private readonly string _prefix;
|
||||
private readonly string _prefixEscaped;
|
||||
|
||||
public AdminPaths(WireMockServerSettings settings)
|
||||
{
|
||||
_prefix = settings.AdminPath ?? DefaultAdminPathPrefix;
|
||||
_prefixEscaped = _prefix.Replace("/", "\\/");
|
||||
}
|
||||
|
||||
public string Files => $"{_prefix}/files";
|
||||
public string Health => $"{_prefix}/health";
|
||||
public string Mappings => $"{_prefix}/mappings";
|
||||
public string MappingsCode => $"{_prefix}/mappings/code";
|
||||
public string MappingsWireMockOrg => $"{_prefix}mappings/wiremock.org";
|
||||
public string Requests => $"{_prefix}/requests";
|
||||
public string Settings => $"{_prefix}/settings";
|
||||
public string Scenarios => $"{_prefix}/scenarios";
|
||||
public string OpenApi => $"{_prefix}/openapi";
|
||||
|
||||
public RegexMatcher MappingsGuidPathMatcher => new($"^{_prefixEscaped}\\/mappings\\/([0-9A-Fa-f]{{8}}[-][0-9A-Fa-f]{{4}}[-][0-9A-Fa-f]{{4}}[-][0-9A-Fa-f]{{4}}[-][0-9A-Fa-f]{{12}})$");
|
||||
public RegexMatcher MappingsGuidEnablePathMatcher => new($"^{_prefixEscaped}\\/mappings\\/([0-9A-Fa-f]{{8}}[-][0-9A-Fa-f]{{4}}[-][0-9A-Fa-f]{{4}}[-][0-9A-Fa-f]{{4}}[-][0-9A-Fa-f]{{12}})\\/enable$");
|
||||
public RegexMatcher MappingsGuidDisablePathMatcher => new($"^{_prefixEscaped}\\/mappings\\/([0-9A-Fa-f]{{8}}[-][0-9A-Fa-f]{{4}}[-][0-9A-Fa-f]{{4}}[-][0-9A-Fa-f]{{4}}[-][0-9A-Fa-f]{{12}})\\/disable$");
|
||||
public RegexMatcher MappingsCodeGuidPathMatcher => new($"^{_prefixEscaped}\\/mappings\\/code\\/([0-9A-Fa-f]{{8}}[-][0-9A-Fa-f]{{4}}[-][0-9A-Fa-f]{{4}}[-][0-9A-Fa-f]{{4}}[-][0-9A-Fa-f]{{12}})$");
|
||||
public RegexMatcher RequestsGuidPathMatcher => new($"^{_prefixEscaped}\\/requests\\/([0-9A-Fa-f]{{8}}[-][0-9A-Fa-f]{{4}}[-][0-9A-Fa-f]{{4}}[-][0-9A-Fa-f]{{4}}[-][0-9A-Fa-f]{{12}})$");
|
||||
public RegexMatcher ScenariosNameMatcher => new($"^{_prefixEscaped}\\/scenarios\\/.+$");
|
||||
public RegexMatcher ScenariosNameWithStateMatcher => new($"^{_prefixEscaped}\\/scenarios\\/.+\\/state$");
|
||||
public RegexMatcher ScenariosNameWithResetMatcher => new($"^{_prefixEscaped}\\/scenarios\\/.+\\/reset$");
|
||||
public RegexMatcher FilesFilenamePathMatcher => new($"^{_prefixEscaped}\\/files\\/.+$");
|
||||
public RegexMatcher ProtoDefinitionsIdPathMatcher => new($"^{_prefixEscaped}\\/protodefinitions\\/.+$");
|
||||
}
|
||||
private IAdminPaths? _adminPaths;
|
||||
|
||||
#region InitAdmin
|
||||
private void InitAdmin()
|
||||
{
|
||||
_adminPaths = new AdminPaths(_settings);
|
||||
_adminPaths = _options.AdminPaths;
|
||||
|
||||
// __admin/health
|
||||
Given(Request.Create().WithPath(_adminPaths.Health).UsingGet()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(HealthGet));
|
||||
@@ -631,7 +596,7 @@ public partial class WireMockServer
|
||||
{
|
||||
if (TryParseGuidFromRequestMessage(requestMessage, out var guid))
|
||||
{
|
||||
var entry = LogEntries.SingleOrDefault(r => r.RequestMessage != null && !r.RequestMessage.Path.StartsWith("/__admin/") && r.Guid == guid);
|
||||
var entry = LogEntries.SingleOrDefault(r => r.RequestMessage != null && !_adminPaths!.Includes(r.RequestMessage.Path) && r.Guid == guid);
|
||||
if (entry is { })
|
||||
{
|
||||
var model = new LogEntryMapper(_options).Map(entry);
|
||||
@@ -660,7 +625,7 @@ public partial class WireMockServer
|
||||
{
|
||||
var logEntryMapper = new LogEntryMapper(_options);
|
||||
var result = LogEntries
|
||||
.Where(r => r.RequestMessage != null && !r.RequestMessage.Path.StartsWith("/__admin/"))
|
||||
.Where(r => r.RequestMessage != null && !_adminPaths!.Includes(r.RequestMessage.Path))
|
||||
.Select(logEntryMapper.Map);
|
||||
|
||||
return ToJson(result);
|
||||
@@ -682,7 +647,7 @@ public partial class WireMockServer
|
||||
var request = (Request)InitRequestBuilder(requestModel);
|
||||
|
||||
var dict = new Dictionary<ILogEntry, RequestMatchResult>();
|
||||
foreach (var logEntry in LogEntries.Where(le => le.RequestMessage != null && !le.RequestMessage.Path.StartsWith("/__admin/")))
|
||||
foreach (var logEntry in LogEntries.Where(le => le.RequestMessage != null && !_adminPaths!.Includes(le.RequestMessage.Path)))
|
||||
{
|
||||
var requestMatchResult = new RequestMatchResult();
|
||||
if (request.GetMatchingScore(logEntry.RequestMessage!, requestMatchResult) > MatchScores.AlmostPerfect)
|
||||
@@ -704,7 +669,7 @@ public partial class WireMockServer
|
||||
Guid.TryParse(value.ToString(), out var mappingGuid)
|
||||
)
|
||||
{
|
||||
var logEntries = LogEntries.Where(le => le.RequestMessage != null && !le.RequestMessage.Path.StartsWith("/__admin/") && le.MappingGuid == mappingGuid);
|
||||
var logEntries = LogEntries.Where(le => le.RequestMessage != null && !_adminPaths!.Includes(le.RequestMessage.Path) && le.MappingGuid == mappingGuid);
|
||||
var logEntryMapper = new LogEntryMapper(_options);
|
||||
var result = logEntries.Select(logEntryMapper.Map);
|
||||
return ToJson(result);
|
||||
|
||||
@@ -155,7 +155,8 @@ public partial class WireMockServer
|
||||
var clientIPModel = _settings.DefaultJsonSerializer.ParseJsonToken<ClientIPModel>(requestModel.ClientIP);
|
||||
if (clientIPModel.Matchers != null)
|
||||
{
|
||||
requestBuilder = requestBuilder.WithPath(clientIPModel.Matchers.Select(_matcherMapper.Map).OfType<IStringMatcher>().ToArray());
|
||||
var matchOperator = StringUtils.ParseMatchOperator(clientIPModel.MatchOperator);
|
||||
requestBuilder = requestBuilder.WithClientIP(matchOperator, clientIPModel.Matchers.Select(_matcherMapper.Map).OfType<IStringMatcher>().ToArray());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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(IEnumerable<bool> values, MatchOperator matchOperator)
|
||||
{
|
||||
return ToScore(values.Select(ToScore).ToArray(), matchOperator);
|
||||
}
|
||||
@@ -68,7 +66,7 @@ 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(IEnumerable<double> values, MatchOperator matchOperator)
|
||||
{
|
||||
if (!values.Any())
|
||||
{
|
||||
@@ -82,4 +80,29 @@ 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 rowRange = Enumerable.Range(0, matchers.Length);
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System;
|
||||
using Newtonsoft.Json;
|
||||
using Stef.Validation;
|
||||
using WireMock.Admin.Requests;
|
||||
@@ -14,51 +13,61 @@ namespace WireMock.Net.Xunit;
|
||||
/// </summary>
|
||||
public sealed class TestOutputHelperWireMockLogger : IWireMockLogger
|
||||
{
|
||||
private readonly ITestOutputHelper _testOutputHelper;
|
||||
private readonly Func<ITestOutputHelper?> _testOutputHelperFactory;
|
||||
|
||||
/// <summary>
|
||||
/// Create a new instance on the <see cref="TestOutputHelperWireMockLogger"/>.
|
||||
/// </summary>
|
||||
/// <param name="testOutputHelper">Represents a class which can be used to provide test output.</param>
|
||||
public TestOutputHelperWireMockLogger(ITestOutputHelper testOutputHelper)
|
||||
public TestOutputHelperWireMockLogger(ITestOutputHelper testOutputHelper) :
|
||||
this(() => testOutputHelper)
|
||||
{
|
||||
_testOutputHelper = Guard.NotNull(testOutputHelper);
|
||||
Guard.NotNull(testOutputHelper);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new instance on the <see cref="TestOutputHelperWireMockLogger"/>.
|
||||
/// </summary>
|
||||
/// <param name="testOutputHelperFactory">Represents a factory to provide current test output.</param>
|
||||
public TestOutputHelperWireMockLogger(Func<ITestOutputHelper?> testOutputHelperFactory)
|
||||
{
|
||||
_testOutputHelperFactory = Guard.NotNull(testOutputHelperFactory);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Debug(string formatString, params object[] args)
|
||||
{
|
||||
_testOutputHelper.WriteLine(Format("Debug", formatString, args));
|
||||
_testOutputHelperFactory()?.WriteLine(Format("Debug", formatString, args));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Info(string formatString, params object[] args)
|
||||
{
|
||||
_testOutputHelper.WriteLine(Format("Info", formatString, args));
|
||||
_testOutputHelperFactory()?.WriteLine(Format("Info", formatString, args));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Warn(string formatString, params object[] args)
|
||||
{
|
||||
_testOutputHelper.WriteLine(Format("Warning", formatString, args));
|
||||
_testOutputHelperFactory()?.WriteLine(Format("Warning", formatString, args));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Error(string formatString, params object[] args)
|
||||
{
|
||||
_testOutputHelper.WriteLine(Format("Error", formatString, args));
|
||||
_testOutputHelperFactory()?.WriteLine(Format("Error", formatString, args));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Error(string message, Exception exception)
|
||||
{
|
||||
_testOutputHelper.WriteLine(Format("Error", $"{message} {{0}}", exception));
|
||||
_testOutputHelperFactory()?.WriteLine(Format("Error", $"{message} {{0}}", exception));
|
||||
|
||||
if (exception is AggregateException ae)
|
||||
{
|
||||
ae.Handle(ex =>
|
||||
{
|
||||
_testOutputHelper.WriteLine(Format("Error", "Exception {0}", ex));
|
||||
_testOutputHelperFactory()?.WriteLine(Format("Error", "Exception {0}", ex));
|
||||
return true;
|
||||
});
|
||||
}
|
||||
@@ -72,7 +81,7 @@ public sealed class TestOutputHelperWireMockLogger : IWireMockLogger
|
||||
Formatting = Formatting.Indented,
|
||||
NullValueHandling = NullValueHandling.Ignore
|
||||
});
|
||||
_testOutputHelper.WriteLine(Format("DebugRequestResponse", "Admin[{0}] {1}", isAdminRequest, message));
|
||||
_testOutputHelperFactory()?.WriteLine(Format("DebugRequestResponse", "Admin[{0}] {1}", isAdminRequest, message));
|
||||
}
|
||||
|
||||
private static string Format(string level, string formatString, params object[] args)
|
||||
|
||||
@@ -15,8 +15,8 @@
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.6.0" />
|
||||
<PackageReference Include="WireMock.Net" Version="2.10.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.8.1" />
|
||||
<PackageReference Include="WireMock.Net" Version="2.12.0" />
|
||||
<PackageReference Include="xunit.v3" Version="3.2.2" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.5">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
@@ -29,13 +29,13 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Update="JetBrains.Annotations" Version="2025.2.4" />
|
||||
<PackageReference Include="OpenTelemetry" Version="1.15.3" />
|
||||
<PackageReference Include="OpenTelemetry.Api" Version="1.15.3" />
|
||||
<PackageReference Include="OpenTelemetry.Api.ProviderBuilderExtensions" Version="1.15.3" />
|
||||
<PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.15.3" />
|
||||
<PackageReference Include="OpenTelemetry.Extensions.Hosting" Version="1.15.3" />
|
||||
<PackageReference Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.15.2" />
|
||||
<!--<PackageReference Update="JetBrains.Annotations" Version="2025.2.4" />-->
|
||||
<PackageReference Include="OpenTelemetry" Version="1.17.0" />
|
||||
<PackageReference Include="OpenTelemetry.Api" Version="1.17.0" />
|
||||
<PackageReference Include="OpenTelemetry.Api.ProviderBuilderExtensions" Version="1.17.0" />
|
||||
<PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.17.0" />
|
||||
<PackageReference Include="OpenTelemetry.Extensions.Hosting" Version="1.17.0" />
|
||||
<PackageReference Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.17.0" />
|
||||
<PackageReference Update="SonarAnalyzer.CSharp" Version="10.11.0.117924" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@@ -0,0 +1,616 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using WireMock.Types;
|
||||
|
||||
namespace WireMock.Net.Tests.Abstractions;
|
||||
|
||||
public class WireMockListTests
|
||||
{
|
||||
#region String Generic Type Tests
|
||||
|
||||
[Fact]
|
||||
public void WireMockListOfString_Constructor_Empty_ShouldCreateEmptyList()
|
||||
{
|
||||
// Act
|
||||
var list = new WireMockList<string>();
|
||||
|
||||
// Assert
|
||||
list.Should().BeEmpty();
|
||||
list.Count.Should().Be(0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WireMockListOfString_Constructor_WithSingleString_ShouldCreateListWithOneElement()
|
||||
{
|
||||
// Arrange
|
||||
var value = "test";
|
||||
|
||||
// Act
|
||||
var list = new WireMockList<string>(value);
|
||||
|
||||
// Assert
|
||||
list.Should().HaveCount(1);
|
||||
list[0].Should().Be("test");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WireMockListOfString_Constructor_WithMultipleStrings_ShouldCreateListWithAllElements()
|
||||
{
|
||||
// Arrange
|
||||
var values = new[] { "value1", "value2", "value3" };
|
||||
|
||||
// Act
|
||||
var list = new WireMockList<string>(values);
|
||||
|
||||
// Assert
|
||||
list.Should().HaveCount(3);
|
||||
list.Should().ContainInOrder("value1", "value2", "value3");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WireMockListOfString_Constructor_WithIEnumerable_ShouldCreateListWithAllElements()
|
||||
{
|
||||
// Arrange
|
||||
var values = new List<string> { "a", "b", "c" };
|
||||
|
||||
// Act
|
||||
var list = new WireMockList<string>(values);
|
||||
|
||||
// Assert
|
||||
list.Should().HaveCount(3);
|
||||
list.Should().ContainInOrder("a", "b", "c");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WireMockListOfString_ToString_WhenEmpty_ShouldReturnEmptyString()
|
||||
{
|
||||
// Arrange
|
||||
var list = new WireMockList<string>();
|
||||
|
||||
// Act
|
||||
var result = list.ToString();
|
||||
|
||||
// Assert
|
||||
result.Should().Be(string.Empty);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WireMockListOfString_ToString_WhenSingleElement_ShouldReturnElementValue()
|
||||
{
|
||||
// Arrange
|
||||
var list = new WireMockList<string>("singleValue");
|
||||
|
||||
// Act
|
||||
var result = list.ToString();
|
||||
|
||||
// Assert
|
||||
result.Should().Be("singleValue");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WireMockListOfString_ToString_WhenMultipleElements_ShouldReturnCommaSeparatedValues()
|
||||
{
|
||||
// Arrange
|
||||
var list = new WireMockList<string>("value1", "value2", "value3");
|
||||
|
||||
// Act
|
||||
var result = list.ToString();
|
||||
|
||||
// Assert
|
||||
result.Should().Be("value1, value2, value3");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WireMockListOfString_ImplicitOperator_WithSingleValue_ShouldCreateList()
|
||||
{
|
||||
// Act
|
||||
WireMockList<string> list = "testValue";
|
||||
|
||||
// Assert
|
||||
list.Should().HaveCount(1);
|
||||
list[0].Should().Be("testValue");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WireMockListOfString_ImplicitOperator_WithArray_ShouldCreateList()
|
||||
{
|
||||
// Arrange
|
||||
var values = new[] { "first", "second", "third" };
|
||||
|
||||
// Act
|
||||
WireMockList<string> list = values;
|
||||
|
||||
// Assert
|
||||
list.Should().HaveCount(3);
|
||||
list.Should().ContainInOrder("first", "second", "third");
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Object Generic Type Tests
|
||||
|
||||
[Fact]
|
||||
public void WireMockListOfObject_Constructor_Empty_ShouldCreateEmptyList()
|
||||
{
|
||||
// Act
|
||||
var list = new WireMockList<object>();
|
||||
|
||||
// Assert
|
||||
list.Should().BeEmpty();
|
||||
list.Count.Should().Be(0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WireMockListOfObject_Constructor_WithSingleObject_ShouldCreateListWithOneElement()
|
||||
{
|
||||
// Arrange
|
||||
var obj = new { Name = "Test", Value = 123 };
|
||||
|
||||
// Act
|
||||
var list = new WireMockList<object>(obj);
|
||||
|
||||
// Assert
|
||||
list.Should().HaveCount(1);
|
||||
list[0].Should().Be(obj);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WireMockListOfObject_Constructor_WithMultipleObjects_ShouldCreateListWithAllElements()
|
||||
{
|
||||
// Arrange
|
||||
var obj1 = new object();
|
||||
var obj2 = new object();
|
||||
var obj3 = new object();
|
||||
var values = new[] { obj1, obj2, obj3 };
|
||||
|
||||
// Act
|
||||
var list = new WireMockList<object>(values);
|
||||
|
||||
// Assert
|
||||
list.Should().HaveCount(3);
|
||||
list.Should().ContainInOrder(obj1, obj2, obj3);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WireMockListOfObject_Constructor_WithIEnumerable_ShouldCreateListWithAllElements()
|
||||
{
|
||||
// Arrange
|
||||
var values = new List<object?>
|
||||
{
|
||||
"string",
|
||||
123,
|
||||
45.67,
|
||||
true,
|
||||
null
|
||||
};
|
||||
|
||||
// Act
|
||||
var list = new WireMockList<object?>(values);
|
||||
|
||||
// Assert
|
||||
list.Should().HaveCount(5);
|
||||
list.Should().ContainInOrder("string", 123, 45.67, true, null);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WireMockListOfObject_ToString_WhenEmpty_ShouldReturnEmptyString()
|
||||
{
|
||||
// Arrange
|
||||
var list = new WireMockList<object>();
|
||||
|
||||
// Act
|
||||
var result = list.ToString();
|
||||
|
||||
// Assert
|
||||
result.Should().Be(string.Empty);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WireMockListOfObject_ToString_WhenSingleString_ShouldReturnString()
|
||||
{
|
||||
// Arrange
|
||||
var list = new WireMockList<object>("singleString");
|
||||
|
||||
// Act
|
||||
var result = list.ToString();
|
||||
|
||||
// Assert
|
||||
result.Should().Be("singleString");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WireMockListOfObject_ToString_WhenSingleObject_ShouldReturnObjectToString()
|
||||
{
|
||||
// Arrange
|
||||
var obj = new { Name = "Test" };
|
||||
var list = new WireMockList<object>(obj);
|
||||
|
||||
// Act
|
||||
var result = list.ToString();
|
||||
|
||||
// Assert
|
||||
result.Should().Contain("Name");
|
||||
result.Should().Contain("Test");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WireMockListOfObject_ToString_WhenSingleInt_ShouldReturnIntAsString()
|
||||
{
|
||||
// Arrange
|
||||
var list = new WireMockList<object>(42);
|
||||
|
||||
// Act
|
||||
var result = list.ToString();
|
||||
|
||||
// Assert
|
||||
result.Should().Be("42");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WireMockListOfObject_ToString_WhenMultipleElements_ShouldReturnCommaSeparatedValues()
|
||||
{
|
||||
// Arrange
|
||||
var list = new WireMockList<object>("text", 123, 45.67);
|
||||
|
||||
// Act
|
||||
var result = list.ToString();
|
||||
|
||||
// Assert
|
||||
result.Should().Be($"text, 123, {45.67}");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WireMockListOfObject_ToString_WithMixedTypes_ShouldReturnCommaSeparatedStringRepresentation()
|
||||
{
|
||||
// Arrange
|
||||
var list = new WireMockList<object>
|
||||
{
|
||||
"string",
|
||||
123,
|
||||
45.67,
|
||||
true
|
||||
};
|
||||
|
||||
// Act
|
||||
var result = list.ToString();
|
||||
|
||||
// Assert
|
||||
result.Should().Be($"string, 123, {45.67}, True");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WireMockListOfObject_ToString_WithNullValue_ShouldReturnEmptyStringForNull()
|
||||
{
|
||||
// Arrange
|
||||
var list = new WireMockList<object?>
|
||||
{
|
||||
"value1",
|
||||
null,
|
||||
"value3"
|
||||
};
|
||||
|
||||
// Act
|
||||
var result = list.ToString();
|
||||
|
||||
// Assert
|
||||
result.Should().Be("value1, , value3");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WireMockListOfObject_ImplicitOperator_WithSingleValue_ShouldCreateList()
|
||||
{
|
||||
// Arrange
|
||||
var obj = new { Id = 1 };
|
||||
|
||||
// Act
|
||||
WireMockList<object> list = obj;
|
||||
|
||||
// Assert
|
||||
list.Should().HaveCount(1);
|
||||
list[0].Should().Be(obj);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WireMockListOfObject_ImplicitOperator_WithArray_ShouldCreateList()
|
||||
{
|
||||
// Arrange
|
||||
var values = new object[] { "first", 2, 3.0 };
|
||||
|
||||
// Act
|
||||
WireMockList<object> list = values;
|
||||
|
||||
// Assert
|
||||
list.Should().HaveCount(3);
|
||||
list.Should().ContainInOrder("first", 2, 3.0);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region List Operations Tests
|
||||
|
||||
[Fact]
|
||||
public void WireMockListOfString_Add_ShouldAddElement()
|
||||
{
|
||||
// Arrange
|
||||
var list = new WireMockList<string>("initial");
|
||||
|
||||
// Act
|
||||
list.Add("new");
|
||||
|
||||
// Assert
|
||||
list.Should().HaveCount(2);
|
||||
list[1].Should().Be("new");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WireMockListOfString_Remove_ShouldRemoveElement()
|
||||
{
|
||||
// Arrange
|
||||
var list = new WireMockList<string>("value1", "value2", "value3");
|
||||
|
||||
// Act
|
||||
var removed = list.Remove("value2");
|
||||
|
||||
// Assert
|
||||
removed.Should().BeTrue();
|
||||
list.Should().HaveCount(2);
|
||||
list.Should().ContainInOrder("value1", "value3");
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Edge Cases
|
||||
|
||||
[Fact]
|
||||
public void WireMockListOfString_ToString_WithEmptyString_ShouldReturnEmptyString()
|
||||
{
|
||||
// Arrange
|
||||
var list = new WireMockList<string>("");
|
||||
|
||||
// Act
|
||||
var result = list.ToString();
|
||||
|
||||
// Assert
|
||||
result.Should().Be("");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WireMockListOfString_Constructor_WithNull_ShouldThrow()
|
||||
{
|
||||
// Act & Assert
|
||||
var act = () => new WireMockList<string?>(null!);
|
||||
act.Should().Throw<ArgumentNullException>();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WireMockListOfObject_ImplicitOperator_WithNull_ShouldCreateListWithNullElement()
|
||||
{
|
||||
// Act
|
||||
WireMockList<object> list = (null as object)!;
|
||||
|
||||
// Assert
|
||||
list.Should().HaveCount(1);
|
||||
list[0].Should().BeNull();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WireMockListOfObject_ToString_WhenSingleNullObject_ShouldReturnEmptyString()
|
||||
{
|
||||
// Arrange
|
||||
object? nullObj = null;
|
||||
var list = new WireMockList<object>(nullObj!);
|
||||
|
||||
// Act
|
||||
var result = list.ToString();
|
||||
|
||||
// Assert
|
||||
result.Should().Be(string.Empty);
|
||||
}
|
||||
|
||||
#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");
|
||||
|
||||
// Act & Assert
|
||||
list.Equals(list).Should().BeTrue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WireMockListOfString_Equals_WhenComparedToNull_ShouldBeFalse()
|
||||
{
|
||||
// Arrange
|
||||
var list = new WireMockList<string>("a");
|
||||
|
||||
// Act & Assert
|
||||
list.Equals(null).Should().BeFalse();
|
||||
}
|
||||
|
||||
[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
|
||||
}
|
||||
@@ -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,18 +11,26 @@ 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(
|
||||
@@ -33,28 +40,76 @@ public class FormUrlEncodedMatcherTest
|
||||
]);
|
||||
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
|
||||
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(
|
||||
@@ -64,7 +119,47 @@ public class FormUrlEncodedMatcherTest
|
||||
]);
|
||||
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
|
||||
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_Test_1()
|
||||
{
|
||||
// Arrange
|
||||
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_Test_2()
|
||||
{
|
||||
// Arrange
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,16 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using JsonConverter.Newtonsoft.Json;
|
||||
using JsonConverter.System.Text.Json;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
using Moq;
|
||||
using WireMock.Handlers;
|
||||
using WireMock.Owin;
|
||||
using WireMock.Owin.Mappers;
|
||||
using WireMock.ResponseBuilders;
|
||||
using WireMock.Types;
|
||||
using WireMock.Util;
|
||||
using WireMock.Owin;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
using JsonConverter.Newtonsoft.Json;
|
||||
using JsonConverter.System.Text.Json;
|
||||
|
||||
namespace WireMock.Net.Tests.Owin.Mappers;
|
||||
|
||||
@@ -40,11 +40,11 @@ public class OwinResponseMapperTests
|
||||
|
||||
_headers = new Mock<IHeaderDictionary>();
|
||||
_headers.SetupAllProperties();
|
||||
#if NET452
|
||||
_headers.Setup(h => h.AppendValues(It.IsAny<string>(), It.IsAny<string[]>()));
|
||||
#else
|
||||
|
||||
// Extension methods(here: HeaderDictionaryExtensions.Append) may not be used in setup / verification expressions.
|
||||
#pragma warning disable ASP0019
|
||||
_headers.Setup(h => h.Add(It.IsAny<string>(), It.IsAny<StringValues>()));
|
||||
#endif
|
||||
#pragma warning restore ASP0019
|
||||
|
||||
_responseMock = new Mock<HttpResponse>();
|
||||
_responseMock.SetupAllProperties();
|
||||
@@ -238,12 +238,8 @@ public class OwinResponseMapperTests
|
||||
await _sut.MapAsync(responseMessage, _responseMock.Object);
|
||||
|
||||
// Assert
|
||||
#if NET452
|
||||
_headers.Verify(h => h.AppendValues("h", new string[] { "x", "y" }), Times.Once);
|
||||
#else
|
||||
var v = new StringValues();
|
||||
_headers.Verify(h => h.TryGetValue("h", out v), Times.Once);
|
||||
#endif
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
||||
@@ -50,6 +50,9 @@ public class WireMockMiddlewareTests
|
||||
_guidUtilsMock = new Mock<IGuidUtils>();
|
||||
_guidUtilsMock.Setup(g => g.NewGuid()).Returns(NewGuid);
|
||||
|
||||
var adminPathsMock = new Mock<IAdminPaths>();
|
||||
adminPathsMock.Setup(a => a.Includes(It.IsAny<string?>())).Returns((string? path) => path?.StartsWith("/__admin/") == true);
|
||||
|
||||
_dateTimeUtilsMock = new Mock<IDateTimeUtils>();
|
||||
_dateTimeUtilsMock.Setup(d => d.UtcNow).Returns(UtcNow);
|
||||
|
||||
@@ -89,6 +92,7 @@ public class WireMockMiddlewareTests
|
||||
_sut = new WireMockMiddleware(
|
||||
_ => Task.CompletedTask,
|
||||
_optionsMock.Object,
|
||||
adminPathsMock.Object,
|
||||
_requestMapperMock.Object,
|
||||
_responseMapperMock.Object,
|
||||
_matcherMock.Object,
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
|
||||
using WireMock.Matchers.Request;
|
||||
using WireMock.RequestBuilders;
|
||||
|
||||
@@ -17,7 +16,7 @@ public class RequestBuilderUsingMethodTests
|
||||
// Assert
|
||||
var matchers = requestBuilder.GetPrivateFieldValue<IList<IRequestMatcher>>("_requestMatchers");
|
||||
matchers.Count.Should().Be(1);
|
||||
(matchers[0] as RequestMessageMethodMatcher).Methods.Should().ContainSingle("CONNECT");
|
||||
matchers[0].Should().BeOfType<RequestMessageMethodMatcher>().Which.Methods.Should().ContainSingle("CONNECT");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -29,7 +28,7 @@ public class RequestBuilderUsingMethodTests
|
||||
// Assert
|
||||
var matchers = requestBuilder.GetPrivateFieldValue<IList<IRequestMatcher>>("_requestMatchers");
|
||||
matchers.Count.Should().Be(1);
|
||||
(matchers[0] as RequestMessageMethodMatcher).Methods.Should().ContainSingle("OPTIONS");
|
||||
matchers[0].Should().BeOfType<RequestMessageMethodMatcher>().Which.Methods.Should().ContainSingle("OPTIONS");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -41,7 +40,7 @@ public class RequestBuilderUsingMethodTests
|
||||
// Assert
|
||||
var matchers = requestBuilder.GetPrivateFieldValue<IList<IRequestMatcher>>("_requestMatchers");
|
||||
matchers.Count.Should().Be(1);
|
||||
(matchers[0] as RequestMessageMethodMatcher).Methods.Should().ContainSingle("PATCH");
|
||||
matchers[0].Should().BeOfType<RequestMessageMethodMatcher>().Which.Methods.Should().ContainSingle("PATCH");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -53,7 +52,7 @@ public class RequestBuilderUsingMethodTests
|
||||
// Assert
|
||||
var matchers = requestBuilder.GetPrivateFieldValue<IList<IRequestMatcher>>("_requestMatchers");
|
||||
matchers.Count.Should().Be(1);
|
||||
(matchers[0] as RequestMessageMethodMatcher).Methods.Should().ContainSingle("TRACE");
|
||||
matchers[0].Should().BeOfType<RequestMessageMethodMatcher>().Which.Methods.Should().ContainSingle("TRACE");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
{
|
||||
Guid: Guid_1,
|
||||
UpdatedAt: DateTime_1,
|
||||
Title: ,
|
||||
Description: ,
|
||||
Priority: 42,
|
||||
Request: {
|
||||
ClientIP: {
|
||||
Matchers: [
|
||||
{
|
||||
Name: WildcardMatcher,
|
||||
Pattern: 1.2.3.4,
|
||||
IgnoreCase: false
|
||||
}
|
||||
]
|
||||
},
|
||||
Path: {
|
||||
Matchers: [
|
||||
{
|
||||
Name: WildcardMatcher,
|
||||
Pattern: /foo,
|
||||
IgnoreCase: false
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
Response: {},
|
||||
UseWebhooksFireAndForget: false
|
||||
}
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
Description: ,
|
||||
Priority: 42,
|
||||
Request: {
|
||||
Path: {
|
||||
ClientIP: {
|
||||
Matchers: [
|
||||
{
|
||||
Name: WildcardMatcher,
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
Description: ,
|
||||
Priority: 42,
|
||||
Request: {
|
||||
Path: {
|
||||
ClientIP: {
|
||||
Matchers: [
|
||||
{
|
||||
Name: WildcardMatcher,
|
||||
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
[
|
||||
{
|
||||
Guid: Guid_1,
|
||||
UpdatedAt: DateTime_1,
|
||||
Request: {
|
||||
ClientIP: {
|
||||
Matchers: [
|
||||
{
|
||||
Name: WildcardMatcher,
|
||||
Pattern: 1.2.3.4,
|
||||
IgnoreCase: false
|
||||
}
|
||||
]
|
||||
},
|
||||
Path: {
|
||||
Matchers: [
|
||||
{
|
||||
Name: WildcardMatcher,
|
||||
Pattern: /foo,
|
||||
IgnoreCase: false
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
Response: {
|
||||
StatusCode: 200
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -6,6 +6,7 @@ using WireMock.Models;
|
||||
using WireMock.RequestBuilders;
|
||||
using WireMock.ResponseBuilders;
|
||||
using WireMock.Serialization;
|
||||
using WireMock.Server;
|
||||
using WireMock.Settings;
|
||||
using WireMock.Types;
|
||||
using WireMock.Util;
|
||||
@@ -376,6 +377,47 @@ message HelloReply {
|
||||
return Verify(model);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public Task ToMappingModel_Request_WithClientIP_And_Path_MapsClientIPToClientIPModel_NotPath()
|
||||
{
|
||||
// Arrange: a request that gates on BOTH ClientIP and Path.
|
||||
var request = Request.Create().WithPath("/foo").WithClientIP("1.2.3.4");
|
||||
var response = Response.Create();
|
||||
var mapping = new Mapping(_guid, _updatedAt, string.Empty, string.Empty, null, _settings, request, response, 42, null, null, null, null, null, false, null, null);
|
||||
|
||||
// Act
|
||||
var model = _sut.ToMappingModel(mapping);
|
||||
|
||||
// Assert
|
||||
model.Should().NotBeNull();
|
||||
|
||||
// Verify
|
||||
return Verify(model);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public Task WithClientIP_And_Path_SurvivesMappingModelRoundTrip_AsClientIPMatcher()
|
||||
{
|
||||
// Arrange: a server with a mapping that gates on BOTH ClientIP and Path.
|
||||
using var source = WireMockServer.Start();
|
||||
source
|
||||
.Given(Request.Create().WithPath("/foo").WithClientIP("1.2.3.4"))
|
||||
.RespondWith(Response.Create().WithSuccess());
|
||||
|
||||
var models = source.MappingModels.ToArray();
|
||||
models.Should().ContainSingle();
|
||||
|
||||
// Act
|
||||
using var target = WireMockServer.Start();
|
||||
target.WithMapping(models);
|
||||
|
||||
// Assert
|
||||
var request = (Request)target.Mappings.Single(m => !m.IsAdminInterface).RequestMatcher;
|
||||
|
||||
// Verify
|
||||
return Verify(target.MappingModels);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public Task ToMappingModel_Request_WithHeader_And_Cookie_ReturnsCorrectModel()
|
||||
{
|
||||
|
||||
@@ -7,27 +7,27 @@ namespace WireMock.Net.Tests.Util;
|
||||
|
||||
public class QueryStringParserTests
|
||||
{
|
||||
public static IEnumerable<object?[]> QueryStringTestData => new List<object?[]>
|
||||
public static List<object?[]> QueryStringTestData => new()
|
||||
{
|
||||
new object?[] { null, false, false, null },
|
||||
new object?[] { string.Empty, false, true, new Dictionary<string, string>() },
|
||||
new object?[] { "test", false, true, new Dictionary<string, string>() },
|
||||
new object?[] { "&", false, true, new Dictionary<string, string>() },
|
||||
new object?[] { "&&", false, true, new Dictionary<string, string>() },
|
||||
new object?[] { "a=", false, true, new Dictionary<string, string> { { "a", "" } } },
|
||||
new object?[] { "&a", false, true, new Dictionary<string, string>() },
|
||||
new object?[] { "&a=", false, true, new Dictionary<string, string> { { "a", "" } } },
|
||||
new object?[] { "&key1=value1", false, true, new Dictionary<string, string> { { "key1", "value1" } } },
|
||||
new object?[] { "key1=value1", false, true, new Dictionary<string, string> { { "key1", "value1" } } },
|
||||
new object?[] { "key1=value1&key2=value2", false, true, new Dictionary<string, string> { { "key1", "value1" }, { "key2", "value2" } } },
|
||||
new object?[] { "key1=value1&key2=value2&", false, true, new Dictionary<string, string> { { "key1", "value1" }, { "key2", "value2" } } },
|
||||
new object?[] { "key1=value1&&key2=value2", false, true, new Dictionary<string, string> { { "key1", "value1" }, { "key2", "value2" } } },
|
||||
new object?[] { "&key1=value1&key2=value2&&", false, true, new Dictionary<string, string> { { "key1", "value1" }, { "key2", "value2" } } },
|
||||
new object?[] { string.Empty, false, true, new Dictionary<string, WireMockList<string>>() },
|
||||
new object?[] { "test", false, true, new Dictionary<string, WireMockList<string>>() },
|
||||
new object?[] { "&", false, true, new Dictionary<string, WireMockList<string>>() },
|
||||
new object?[] { "&&", false, true, new Dictionary<string, WireMockList<string>>() },
|
||||
new object?[] { "a=", false, true, new Dictionary<string, WireMockList<string>> { { "a", new WireMockList<string>("") } } },
|
||||
new object?[] { "&a", false, true, new Dictionary<string, WireMockList<string>>() },
|
||||
new object?[] { "&a=", false, true, new Dictionary<string, WireMockList<string>> { { "a", new WireMockList<string>("") } } },
|
||||
new object?[] { "&key1=value1", false, true, new Dictionary<string, WireMockList<string>> { { "key1", new WireMockList<string>("value1") } } },
|
||||
new object?[] { "key1=value1", false, true, new Dictionary<string, WireMockList<string>> { { "key1", new WireMockList<string>("value1") } } },
|
||||
new object?[] { "key1=value1&key2=value2", false, true, new Dictionary<string, WireMockList<string>> { { "key1", new WireMockList<string>("value1") }, { "key2", new WireMockList<string>("value2") } } },
|
||||
new object?[] { "key1=value1&key2=value2&", false, true, new Dictionary<string, WireMockList<string>> { { "key1", new WireMockList<string>("value1") }, { "key2", new WireMockList<string>("value2") } } },
|
||||
new object?[] { "key1=value1&&key2=value2", false, true, new Dictionary<string, WireMockList<string>> { { "key1", new WireMockList<string>("value1") }, { "key2", new WireMockList<string>("value2") } } },
|
||||
new object?[] { "&key1=value1&key2=value2&&", false, true, new Dictionary<string, WireMockList<string>> { { "key1", new WireMockList<string>("value1") }, { "key2", new WireMockList<string>("value2") } } },
|
||||
};
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(QueryStringTestData))]
|
||||
public void TryParse_Should_Parse_QueryString(string queryString, bool caseIgnore, bool expectedResult, IDictionary<string, string> expectedOutput)
|
||||
public void TryParse_Should_Parse_QueryString(string queryString, bool caseIgnore, bool expectedResult, IDictionary<string, WireMockList<string>> expectedOutput)
|
||||
{
|
||||
// Act
|
||||
var result = QueryStringParser.TryParse(queryString, caseIgnore, out var actual);
|
||||
@@ -49,7 +49,7 @@ public class QueryStringParserTests
|
||||
|
||||
// Assert
|
||||
result.Should().BeTrue();
|
||||
actual.Should().BeEquivalentTo(new Dictionary<string, string> { { "x", "rNaCP7hv8UOmS/JcujdvLw==" } });
|
||||
actual.Should().BeEquivalentTo(new Dictionary<string, WireMockList<string>> { { "x", new WireMockList<string>("rNaCP7hv8UOmS/JcujdvLw==") } });
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
||||
@@ -158,4 +158,8 @@
|
||||
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Types\" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -648,4 +648,24 @@ public class WireMockServerAdminTests(ITestOutputHelper output, ITestContextAcce
|
||||
// Assert
|
||||
settings.Should().NotBeNull();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task WireMockServer_WithCustomAdminPath_AdminRequestsNotInLogEntries()
|
||||
{
|
||||
// Arrange
|
||||
var cancellationToken = TestContext.Current.CancellationToken;
|
||||
using var server = WireMockServer.Start(w =>
|
||||
{
|
||||
w.Logger = new TestOutputHelperWireMockLogger(output);
|
||||
w.StartAdminInterface = true;
|
||||
w.AdminPath = "/custom/__admin";
|
||||
});
|
||||
var client = server.CreateClient();
|
||||
|
||||
// Act
|
||||
await client.GetAsync($"{server.Url}/custom/__admin/settings", cancellationToken);
|
||||
|
||||
// Assert
|
||||
server.LogEntries.Should().BeEmpty();
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,7 @@ using WireMock.RequestBuilders;
|
||||
using WireMock.ResponseBuilders;
|
||||
using WireMock.Server;
|
||||
using WireMock.Settings;
|
||||
using WireMock.Types;
|
||||
|
||||
namespace WireMock.Net.Tests;
|
||||
|
||||
@@ -339,7 +340,7 @@ public partial class WireMockServerTests
|
||||
#endif
|
||||
|
||||
[Fact]
|
||||
public async Task WireMockServer_WithBodyAsFormUrlEncoded_Using_PostAsync_And_WithFunc()
|
||||
public async Task WireMockServer_WithBodyAsFormUrlEncoded_Using_PostAsync_And_WithFunc1()
|
||||
{
|
||||
// Arrange
|
||||
using var server = WireMockServer.Start();
|
||||
@@ -364,6 +365,32 @@ public partial class WireMockServerTests
|
||||
server.Stop();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task WireMockServer_WithBodyAsFormUrlEncoded_Using_PostAsync_And_WithFunc2()
|
||||
{
|
||||
// Arrange
|
||||
using var server = WireMockServer.Start();
|
||||
server.Given(
|
||||
Request.Create()
|
||||
.UsingPost()
|
||||
.WithPath("/foo")
|
||||
.WithBody((IDictionary<string, WireMockList<string>>? values) => values != null && values["key1"] == "value1")
|
||||
)
|
||||
.RespondWith(
|
||||
Response.Create()
|
||||
);
|
||||
|
||||
// Act
|
||||
var content = new FormUrlEncodedContent([new KeyValuePair<string, string>("key1", "value1")]);
|
||||
var response = await new HttpClient()
|
||||
.PostAsync($"{server.Url}/foo", content, _ct);
|
||||
|
||||
// Assert
|
||||
response.StatusCode.Should().Be(HttpStatusCode.OK);
|
||||
|
||||
server.Stop();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task WireMockServer_WithBodyAsFormUrlEncoded_Using_PostAsync_And_WithExactMatcher()
|
||||
{
|
||||
|
||||
@@ -93,6 +93,54 @@ public partial class WireMockServerTests
|
||||
server.Stop();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task WireMockServer_WithParam_RejectOnMatch_WithValue_WhenValueMatches_ShouldNotMatch_Returns404()
|
||||
{
|
||||
// Arrange
|
||||
var cancelationToken = TestContext.Current.CancellationToken;
|
||||
var server = WireMockServer.Start();
|
||||
server.Given(
|
||||
Request.Create()
|
||||
.WithPath("/x")
|
||||
.WithParam("k", MatchBehaviour.RejectOnMatch, "abc")
|
||||
.UsingGet()
|
||||
)
|
||||
.ThenRespondWithOK();
|
||||
|
||||
// Act
|
||||
var requestUri = new Uri($"http://localhost:{server.Port}/x?k=abc");
|
||||
var response = await server.CreateClient().GetAsync(requestUri, cancelationToken);
|
||||
|
||||
// Assert
|
||||
response.StatusCode.Should().Be(HttpStatusCode.NotFound);
|
||||
|
||||
server.Stop();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task WireMockServer_WithParam_RejectOnMatch_WithValue_WhenValueDoesNotMatch_ShouldMatch_Returns200()
|
||||
{
|
||||
// Arrange
|
||||
var cancelationToken = TestContext.Current.CancellationToken;
|
||||
var server = WireMockServer.Start();
|
||||
server.Given(
|
||||
Request.Create()
|
||||
.WithPath("/x")
|
||||
.WithParam("k", MatchBehaviour.RejectOnMatch, "abc")
|
||||
.UsingGet()
|
||||
)
|
||||
.ThenRespondWithOK();
|
||||
|
||||
// Act
|
||||
var requestUri = new Uri($"http://localhost:{server.Port}/x?k=xyz");
|
||||
var response = await server.CreateClient().GetAsync(requestUri, cancelationToken);
|
||||
|
||||
// Assert
|
||||
response.StatusCode.Should().Be(HttpStatusCode.OK);
|
||||
|
||||
server.Stop();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task WireMockServer_WithParam_AcceptOnMatch_OnNonMatchingParam_ShouldReturnMappingOk()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user