Compare commits

..

1 Commits

Author SHA1 Message Date
Stef Heyenrath
7880bfcc63 [WIP] - Riok.Mapperly 2024-08-15 13:51:44 +02:00
19 changed files with 294 additions and 365 deletions

View File

@@ -1,17 +1,3 @@
# 1.6.1 (22 August 2024)
- [#1160](https://github.com/WireMock-Net/WireMock.Net/pull/1160) - Use default timeout for Regex [bug] contributed by [StefH](https://github.com/StefH)
- [#1159](https://github.com/WireMock-Net/WireMock.Net/issues/1159) - RegexMatchTimeoutException when trying to parse HTTP version [bug]
# 1.6.0 (16 August 2024)
- [#1042](https://github.com/WireMock-Net/WireMock.Net/pull/1042) - Update + add fluent builder methods [feature] contributed by [StefH](https://github.com/StefH)
- [#1109](https://github.com/WireMock-Net/WireMock.Net/pull/1109) - Add Aspire Extension [feature] contributed by [StefH](https://github.com/StefH)
- [#1148](https://github.com/WireMock-Net/WireMock.Net/pull/1148) - Use Guid.TryParseExact with format "D" contributed by [StefH](https://github.com/StefH)
- [#1157](https://github.com/WireMock-Net/WireMock.Net/pull/1157) - Fix FormUrlEncodedMatcher (MatchOperator.And) [bug] contributed by [StefH](https://github.com/StefH)
- [#1158](https://github.com/WireMock-Net/WireMock.Net/pull/1158) - Allow setting Content-Length header on the response [feature] contributed by [StefH](https://github.com/StefH)
- [#720](https://github.com/WireMock-Net/WireMock.Net/issues/720) - Response Header Content-Length not available when call HEAD Method [feature]
- [#1145](https://github.com/WireMock-Net/WireMock.Net/issues/1145) - Response is auto converting string to guid [bug]
- [#1156](https://github.com/WireMock-Net/WireMock.Net/issues/1156) - FormUrlEncodedMatcher is not requiring to match all properties when MatchOperator.And [bug]
# 1.5.62 (27 July 2024) # 1.5.62 (27 July 2024)
- [#1147](https://github.com/WireMock-Net/WireMock.Net/pull/1147) - Add FormUrlEncodedMatcher [feature] contributed by [StefH](https://github.com/StefH) - [#1147](https://github.com/WireMock-Net/WireMock.Net/pull/1147) - Add FormUrlEncodedMatcher [feature] contributed by [StefH](https://github.com/StefH)
- [#1143](https://github.com/WireMock-Net/WireMock.Net/issues/1143) - FormEncoded Request fails (404 Not Found) if key value pairs order in mapping is different from request body order [bug] - [#1143](https://github.com/WireMock-Net/WireMock.Net/issues/1143) - FormEncoded Request fails (404 Not Found) if key value pairs order in mapping is different from request body order [bug]

View File

@@ -4,7 +4,7 @@
</PropertyGroup> </PropertyGroup>
<PropertyGroup> <PropertyGroup>
<VersionPrefix>1.6.1</VersionPrefix> <VersionPrefix>1.5.62</VersionPrefix>
<PackageIcon>WireMock.Net-Logo.png</PackageIcon> <PackageIcon>WireMock.Net-Logo.png</PackageIcon>
<PackageProjectUrl>https://github.com/WireMock-Net/WireMock.Net</PackageProjectUrl> <PackageProjectUrl>https://github.com/WireMock-Net/WireMock.Net</PackageProjectUrl>
<PackageLicenseExpression>Apache-2.0</PackageLicenseExpression> <PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>

View File

@@ -1,6 +1,6 @@
rem https://github.com/StefH/GitHubReleaseNotes rem https://github.com/StefH/GitHubReleaseNotes
SET version=1.6.1 SET version=1.5.62
GitHubReleaseNotes --output CHANGELOG.md --skip-empty-releases --exclude-labels question invalid doc duplicate example environment --version %version% --token %GH_TOKEN% GitHubReleaseNotes --output CHANGELOG.md --skip-empty-releases --exclude-labels question invalid doc duplicate example environment --version %version% --token %GH_TOKEN%

View File

@@ -1,5 +1,5 @@
# 1.6.1 (22 August 2024) # 1.5.62 (27 July 2024)
- #1160 Use default timeout for Regex [bug] - #1147 Add FormUrlEncodedMatcher [feature]
- #1159 RegexMatchTimeoutException when trying to parse HTTP version [bug] - #1143 FormEncoded Request fails (404 Not Found) if key value pairs order in mapping is different from request body order [bug]
The full release notes can be found here: https://github.com/WireMock-Net/WireMock.Net/blob/master/CHANGELOG.md The full release notes can be found here: https://github.com/WireMock-Net/WireMock.Net/blob/master/CHANGELOG.md

View File

@@ -137,6 +137,7 @@ message HelloReply {
public static void Run() public static void Run()
{ {
RunOnLocal(); RunOnLocal();
return;
var mappingBuilder = new MappingBuilder(); var mappingBuilder = new MappingBuilder();
mappingBuilder mappingBuilder
@@ -307,6 +308,17 @@ message HelloReply {
.RespondWith(Response.Create() .RespondWith(Response.Create()
.WithBody("GraphQL is ok") .WithBody("GraphQL is ok")
); );
//server
// .AddGraphQLSchema("my-graphql", TestSchema, customScalars)
// .Given(Request.Create()
// .WithPath("/graphql2")
// .UsingPost()
// )
// .WithGraphQLSchema("my-graphql")
// .RespondWith(Response.Create()
// .WithBody("GraphQL is ok")
// );
#endif #endif
#if MIMEKIT #if MIMEKIT
@@ -365,15 +377,6 @@ message HelloReply {
.WithHeader("Content-Type", "text/plain") .WithHeader("Content-Type", "text/plain")
); );
server
.Given(Request.Create()
.UsingHead()
.WithPath("/cl")
)
.RespondWith(Response.Create()
.WithHeader("Content-Length", "42")
);
server server
.Given(Request.Create() .Given(Request.Create()
.UsingMethod("GET") .UsingMethod("GET")

View File

@@ -10,7 +10,6 @@ using Microsoft.IdentityModel.Protocols;
using Microsoft.IdentityModel.Protocols.OpenIdConnect; using Microsoft.IdentityModel.Protocols.OpenIdConnect;
using Microsoft.IdentityModel.Tokens; using Microsoft.IdentityModel.Tokens;
using Stef.Validation; using Stef.Validation;
using WireMock.Constants;
using WireMock.Matchers; using WireMock.Matchers;
using WireMock.Models; using WireMock.Models;
@@ -51,7 +50,7 @@ internal class AzureADAuthenticationMatcher : IStringMatcher
return MatchScores.Mismatch; return MatchScores.Mismatch;
} }
var token = Regex.Replace(input, BearerPrefix, string.Empty, RegexOptions.IgnoreCase, WireMockConstants.DefaultRegexTimeout); var token = Regex.Replace(input, BearerPrefix, string.Empty, RegexOptions.IgnoreCase);
try try
{ {

View File

@@ -6,9 +6,12 @@ using WireMock.Matchers;
namespace WireMock.Authentication; namespace WireMock.Authentication;
internal class BasicAuthenticationMatcher(string username, string password) internal class BasicAuthenticationMatcher : RegexMatcher
: RegexMatcher(BuildPattern(username, password))
{ {
public BasicAuthenticationMatcher(string username, string password) : base(BuildPattern(username, password))
{
}
public override string Name => nameof(BasicAuthenticationMatcher); public override string Name => nameof(BasicAuthenticationMatcher);
private static string BuildPattern(string username, string password) private static string BuildPattern(string username, string password)

View File

@@ -2,7 +2,6 @@
#if NET451 || NET452 || NET46 || NET451 || NET461 || NETSTANDARD1_3 || NETSTANDARD2_0 #if NET451 || NET452 || NET46 || NET451 || NET461 || NETSTANDARD1_3 || NETSTANDARD2_0
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using WireMock.Constants;
// ReSharper disable once CheckNamespace // ReSharper disable once CheckNamespace
namespace System; namespace System;
@@ -12,7 +11,7 @@ internal static class StringExtensions
public static string Replace(this string text, string oldValue, string newValue, StringComparison stringComparison) public static string Replace(this string text, string oldValue, string newValue, StringComparison stringComparison)
{ {
var options = stringComparison == StringComparison.OrdinalIgnoreCase ? RegexOptions.IgnoreCase : RegexOptions.None; var options = stringComparison == StringComparison.OrdinalIgnoreCase ? RegexOptions.IgnoreCase : RegexOptions.None;
return Regex.Replace(text, oldValue, newValue, options, WireMockConstants.DefaultRegexTimeout); return Regex.Replace(text, oldValue, newValue, options);
} }
} }
#endif #endif

View File

@@ -1,19 +1,15 @@
// Copyright © WireMock.Net // Copyright © WireMock.Net
using System;
namespace WireMock.Constants; namespace WireMock.Constants;
internal static class WireMockConstants internal static class WireMockConstants
{ {
internal static readonly TimeSpan DefaultRegexTimeout = TimeSpan.FromSeconds(10); public const int AdminPriority = int.MinValue;
public const int MinPriority = -1_000_000;
internal const int AdminPriority = int.MinValue; public const int ProxyPriority = -2_000_000;
internal const int MinPriority = -1_000_000;
internal const int ProxyPriority = -2_000_000;
internal const string ContentTypeJson = "application/json"; public const string ContentTypeJson = "application/json";
internal const string ContentTypeTextPlain = "text/plain"; public const string ContentTypeTextPlain = "text/plain";
internal const string NoMatchingFound = "No matching mapping found"; public const string NoMatchingFound = "No matching mapping found";
} }

View File

@@ -14,12 +14,12 @@ namespace WireMock.Http;
/// </summary> /// </summary>
internal static class HttpKnownHeaderNames internal static class HttpKnownHeaderNames
{ {
// - https://docs.microsoft.com/en-us/dotnet/api/system.net.webheadercollection.isrestricted // https://docs.microsoft.com/en-us/dotnet/api/system.net.webheadercollection.isrestricted
// - ContentLength is allowed per #720
private static readonly string[] RestrictedResponseHeaders = private static readonly string[] RestrictedResponseHeaders =
{ {
Accept, Accept,
Connection, Connection,
ContentLength,
ContentType, ContentType,
Date, // RFC1123Pattern Date, // RFC1123Pattern
Expect, Expect,

View File

@@ -5,11 +5,10 @@ using System.Linq;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using AnyOfTypes; using AnyOfTypes;
using JetBrains.Annotations; using JetBrains.Annotations;
using Stef.Validation;
using WireMock.Constants;
using WireMock.Extensions; using WireMock.Extensions;
using WireMock.Models; using WireMock.Models;
using WireMock.RegularExpressions; using WireMock.RegularExpressions;
using Stef.Validation;
namespace WireMock.Matchers; namespace WireMock.Matchers;
@@ -38,7 +37,7 @@ public class RegexMatcher : IStringMatcher, IIgnoreCaseMatcher
bool ignoreCase = false, bool ignoreCase = false,
bool useRegexExtended = true, bool useRegexExtended = true,
MatchOperator matchOperator = MatchOperator.Or) : MatchOperator matchOperator = MatchOperator.Or) :
this(MatchBehaviour.AcceptOnMatch, [pattern], ignoreCase, useRegexExtended, matchOperator) this(MatchBehaviour.AcceptOnMatch, new[] { pattern }, ignoreCase, useRegexExtended, matchOperator)
{ {
} }
@@ -56,7 +55,7 @@ public class RegexMatcher : IStringMatcher, IIgnoreCaseMatcher
bool ignoreCase = false, bool ignoreCase = false,
bool useRegexExtended = true, bool useRegexExtended = true,
MatchOperator matchOperator = MatchOperator.Or) : MatchOperator matchOperator = MatchOperator.Or) :
this(matchBehaviour, [pattern], ignoreCase, useRegexExtended, matchOperator) this(matchBehaviour, new[] { pattern }, ignoreCase, useRegexExtended, matchOperator)
{ {
} }
@@ -87,7 +86,7 @@ public class RegexMatcher : IStringMatcher, IIgnoreCaseMatcher
options |= RegexOptions.IgnoreCase; options |= RegexOptions.IgnoreCase;
} }
_expressions = patterns.Select(p => useRegexExtended ? new RegexExtended(p.GetPattern(), options) : new Regex(p.GetPattern(), options, WireMockConstants.DefaultRegexTimeout)).ToArray(); _expressions = patterns.Select(p => useRegexExtended ? new RegexExtended(p.GetPattern(), options) : new Regex(p.GetPattern(), options)).ToArray();
} }
/// <inheritdoc /> /// <inheritdoc />

View File

@@ -37,20 +37,13 @@ namespace WireMock.Owin.Mappers
private readonly Encoding _utf8NoBom = new UTF8Encoding(false); private readonly Encoding _utf8NoBom = new UTF8Encoding(false);
// https://msdn.microsoft.com/en-us/library/78h415ay(v=vs.110).aspx // https://msdn.microsoft.com/en-us/library/78h415ay(v=vs.110).aspx
private static readonly IDictionary<string, Action<IResponse, bool, WireMockList<string>>> ResponseHeadersToFix = #if !USE_ASPNETCORE
new Dictionary<string, Action<IResponse, bool, WireMockList<string>>>(StringComparer.OrdinalIgnoreCase) private static readonly IDictionary<string, Action<IResponse, WireMockList<string>>> ResponseHeadersToFix = new Dictionary<string, Action<IResponse, WireMockList<string>>>(StringComparer.OrdinalIgnoreCase) {
{ #else
{ HttpKnownHeaderNames.ContentType, (r, _, v) => r.ContentType = v.FirstOrDefault() }, private static readonly IDictionary<string, Action<IResponse, WireMockList<string>>> ResponseHeadersToFix = new Dictionary<string, Action<IResponse, WireMockList<string>>>(StringComparer.OrdinalIgnoreCase) {
{ HttpKnownHeaderNames.ContentLength, (r, hasBody, v) => #endif
{ { HttpKnownHeaderNames.ContentType, (r, v) => r.ContentType = v.FirstOrDefault() }
// Only set the Content-Length header if the response does not have a body };
if (!hasBody && long.TryParse(v.FirstOrDefault(), out var contentLength))
{
r.ContentLength = contentLength;
}
}
}
};
/// <summary> /// <summary>
/// Constructor /// Constructor
@@ -90,21 +83,23 @@ namespace WireMock.Owin.Mappers
} }
var statusCodeType = responseMessage.StatusCode?.GetType(); var statusCodeType = responseMessage.StatusCode?.GetType();
if (statusCodeType != null) switch (statusCodeType)
{ {
if (statusCodeType == typeof(int) || statusCodeType == typeof(int?) || statusCodeType.GetTypeInfo().IsEnum) case { } when statusCodeType == typeof(int) || statusCodeType == typeof(int?) || statusCodeType.GetTypeInfo().IsEnum:
{
response.StatusCode = MapStatusCode((int)responseMessage.StatusCode!); response.StatusCode = MapStatusCode((int)responseMessage.StatusCode!);
} break;
else if (statusCodeType == typeof(string))
{ case { } when statusCodeType == typeof(string):
// Note: this case will also match on null // Note: this case will also match on null
int.TryParse(responseMessage.StatusCode as string, out var statusCodeTypeAsInt); int.TryParse(responseMessage.StatusCode as string, out var result);
response.StatusCode = MapStatusCode(statusCodeTypeAsInt); response.StatusCode = MapStatusCode(result);
} break;
default:
break;
} }
SetResponseHeaders(responseMessage, bytes, response); SetResponseHeaders(responseMessage, response);
if (bytes != null) if (bytes != null)
{ {
@@ -165,7 +160,7 @@ namespace WireMock.Owin.Mappers
return null; return null;
} }
private static void SetResponseHeaders(IResponseMessage responseMessage, byte[]? bytes, IResponse response) private static void SetResponseHeaders(IResponseMessage responseMessage, IResponse response)
{ {
// Force setting the Date header (#577) // Force setting the Date header (#577)
AppendResponseHeader( AppendResponseHeader(
@@ -184,11 +179,11 @@ namespace WireMock.Owin.Mappers
var value = item.Value; var value = item.Value;
if (ResponseHeadersToFix.TryGetValue(headerName, out var action)) if (ResponseHeadersToFix.TryGetValue(headerName, out var action))
{ {
action?.Invoke(response, bytes != null, value); action?.Invoke(response, value);
} }
else else
{ {
// Check if this response header can be added (#148, #227 and #720) // Check if this response header can be added (#148 and #227)
if (!HttpKnownHeaderNames.IsRestrictedResponseHeader(headerName)) if (!HttpKnownHeaderNames.IsRestrictedResponseHeader(headerName))
{ {
AppendResponseHeader(response, headerName, value.ToArray()); AppendResponseHeader(response, headerName, value.ToArray());
@@ -211,7 +206,7 @@ namespace WireMock.Owin.Mappers
var value = item.Value; var value = item.Value;
if (ResponseHeadersToFix.TryGetValue(headerName, out var action)) if (ResponseHeadersToFix.TryGetValue(headerName, out var action))
{ {
action?.Invoke(response, false, value); action?.Invoke(response, value);
} }
else else
{ {

View File

@@ -1,8 +1,8 @@
// Copyright © WireMock.Net // Copyright © WireMock.Net
using System;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using Stef.Validation; using Stef.Validation;
using WireMock.Constants;
namespace WireMock.Util; namespace WireMock.Util;
@@ -11,7 +11,7 @@ namespace WireMock.Util;
/// </summary> /// </summary>
internal static class HttpVersionParser internal static class HttpVersionParser
{ {
private static readonly Regex HttpVersionRegex = new(@"HTTP/(\d+(\.\d+)?(?!\.))", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant | RegexOptions.Compiled, WireMockConstants.DefaultRegexTimeout); private static readonly Regex HttpVersionRegex = new(@"HTTP/(\d+(\.\d+)?(?!\.))", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant | RegexOptions.Compiled, TimeSpan.FromMilliseconds(100));
/// <summary> /// <summary>
/// Try to extract the version (as a string) from the protocol. /// Try to extract the version (as a string) from the protocol.

View File

@@ -5,7 +5,6 @@ using System.Diagnostics.CodeAnalysis;
using System.Net; using System.Net;
using System.Net.Sockets; using System.Net.Sockets;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using WireMock.Constants;
namespace WireMock.Util; namespace WireMock.Util;
@@ -14,7 +13,7 @@ namespace WireMock.Util;
/// </summary> /// </summary>
internal static class PortUtils internal static class PortUtils
{ {
private static readonly Regex UrlDetailsRegex = new(@"^((?<proto>\w+)://)(?<host>[^/]+?):(?<port>\d+)\/?$", RegexOptions.Compiled, WireMockConstants.DefaultRegexTimeout); private static readonly Regex UrlDetailsRegex = new(@"^((?<proto>\w+)://)(?<host>[^/]+?):(?<port>\d+)\/?$", RegexOptions.Compiled);
/// <summary> /// <summary>
/// Finds a free TCP port. /// Finds a free TCP port.

View File

@@ -1,14 +1,16 @@
// Copyright © WireMock.Net // Copyright © WireMock.Net
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using WireMock.Constants;
using WireMock.RegularExpressions; using WireMock.RegularExpressions;
namespace WireMock.Util; namespace WireMock.Util;
internal static class RegexUtils internal static class RegexUtils
{ {
private static readonly TimeSpan RegexTimeOut = new(0, 0, 10);
public static Dictionary<string, string> GetNamedGroups(Regex regex, string input) public static Dictionary<string, string> GetNamedGroups(Regex regex, string input)
{ {
var namedGroupsDictionary = new Dictionary<string, string>(); var namedGroupsDictionary = new Dictionary<string, string>();
@@ -36,11 +38,11 @@ internal static class RegexUtils
{ {
if (useRegexExtended) if (useRegexExtended)
{ {
var regexExtended = new RegexExtended(pattern!, RegexOptions.None, WireMockConstants.DefaultRegexTimeout); var regexExtended = new RegexExtended(pattern!, RegexOptions.None, RegexTimeOut);
return (true, regexExtended.IsMatch(input)); return (true, regexExtended.IsMatch(input));
} }
var regex = new Regex(pattern, RegexOptions.None, WireMockConstants.DefaultRegexTimeout); var regex = new Regex(pattern, RegexOptions.None, RegexTimeOut);
return (true, regex.IsMatch(input)); return (true, regex.IsMatch(input));
} }
catch catch

View File

@@ -1,56 +1,24 @@
// Copyright © WireMock.Net // Copyright © WireMock.Net
using Nelibur.ObjectMapper; using Riok.Mapperly.Abstractions;
using WireMock.Admin.Mappings; using WireMock.Admin.Mappings;
using WireMock.Admin.Settings; using WireMock.Admin.Settings;
using WireMock.Settings; using WireMock.Settings;
namespace WireMock.Util; namespace WireMock.Util;
internal sealed class TinyMapperUtils [Mapper]
internal static partial class TinyMapperUtils
{ {
public static TinyMapperUtils Instance { get; } = new(); public static partial ProxyAndRecordSettingsModel? Map(ProxyAndRecordSettings? instance);
private TinyMapperUtils() public static partial ProxyAndRecordSettings? Map(ProxyAndRecordSettingsModel? model);
{
TinyMapper.Bind<ProxyAndRecordSettings, ProxyAndRecordSettingsModel>();
TinyMapper.Bind<WebProxySettings, WebProxySettingsModel>();
TinyMapper.Bind<WebProxySettings, WebProxyModel>();
TinyMapper.Bind<ProxyUrlReplaceSettings, ProxyUrlReplaceSettingsModel>();
TinyMapper.Bind<ProxyAndRecordSettingsModel, ProxyAndRecordSettings>(); public static partial ProxyUrlReplaceSettingsModel? Map(ProxyUrlReplaceSettings? instance);
TinyMapper.Bind<WebProxySettingsModel, WebProxySettings>();
TinyMapper.Bind<WebProxyModel, WebProxySettings>();
TinyMapper.Bind<ProxyUrlReplaceSettingsModel, ProxyUrlReplaceSettings>();
}
public ProxyAndRecordSettingsModel? Map(ProxyAndRecordSettings? instance) public static partial ProxyUrlReplaceSettings? Map(ProxyUrlReplaceSettingsModel? model);
{
return instance == null ? null : TinyMapper.Map<ProxyAndRecordSettingsModel>(instance);
}
public ProxyAndRecordSettings? Map(ProxyAndRecordSettingsModel? model) public static partial WebProxyModel? Map(WebProxySettings? instance);
{
return model == null ? null : TinyMapper.Map<ProxyAndRecordSettings>(model);
}
public ProxyUrlReplaceSettingsModel? Map(ProxyUrlReplaceSettings? instance) public static partial WebProxySettings? Map(WebProxyModel? model);
{
return instance == null ? null : TinyMapper.Map<ProxyUrlReplaceSettingsModel>(instance);
}
public ProxyUrlReplaceSettings? Map(ProxyUrlReplaceSettingsModel? model)
{
return model == null ? null : TinyMapper.Map<ProxyUrlReplaceSettings>(model);
}
public WebProxyModel? Map(WebProxySettings? instance)
{
return instance == null ? null : TinyMapper.Map<WebProxyModel>(instance);
}
public WebProxySettings? Map(WebProxyModel? model)
{
return model == null ? null : TinyMapper.Map<WebProxySettings>(model);
}
} }

View File

@@ -63,10 +63,10 @@
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="NJsonSchema.Extensions" Version="0.1.0" /> <PackageReference Include="NJsonSchema.Extensions" Version="0.1.0" />
<PackageReference Include="NSwag.Core" Version="13.16.1" /> <PackageReference Include="NSwag.Core" Version="13.16.1" />
<PackageReference Include="Riok.Mapperly" Version="3.6.0" />
<PackageReference Include="SimMetrics.Net" Version="1.0.5" /> <PackageReference Include="SimMetrics.Net" Version="1.0.5" />
<PackageReference Include="JmesPath.Net" Version="1.0.125" /> <PackageReference Include="JmesPath.Net" Version="1.0.125" />
<PackageReference Include="AnyOf" Version="0.3.0" /> <PackageReference Include="AnyOf" Version="0.3.0" />
<PackageReference Include="TinyMapper" Version="3.0.3" />
</ItemGroup> </ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' != 'netstandard1.3' "> <ItemGroup Condition=" '$(TargetFramework)' != 'netstandard1.3' ">

View File

@@ -25,277 +25,278 @@ using Response = Microsoft.AspNetCore.Http.HttpResponse;
using Microsoft.Extensions.Primitives; using Microsoft.Extensions.Primitives;
#endif #endif
namespace WireMock.Net.Tests.Owin.Mappers; namespace WireMock.Net.Tests.Owin.Mappers
public class OwinResponseMapperTests
{ {
private static readonly Task CompletedTask = Task.FromResult(true); public class OwinResponseMapperTests
private readonly OwinResponseMapper _sut;
private readonly Mock<IResponse> _responseMock;
private readonly Mock<Stream> _stream;
private readonly Mock<IHeaderDictionary> _headers;
private readonly Mock<IFileSystemHandler> _fileSystemHandlerMock;
private readonly Mock<IWireMockMiddlewareOptions> _optionsMock;
public OwinResponseMapperTests()
{ {
_stream = new Mock<Stream>(); private static readonly Task CompletedTask = Task.FromResult(true);
_stream.SetupAllProperties(); private readonly OwinResponseMapper _sut;
_stream.Setup(s => s.WriteAsync(It.IsAny<byte[]>(), It.IsAny<int>(), It.IsAny<int>(), It.IsAny<CancellationToken>())).Returns(CompletedTask); private readonly Mock<IResponse> _responseMock;
private readonly Mock<Stream> _stream;
private readonly Mock<IHeaderDictionary> _headers;
private readonly Mock<IFileSystemHandler> _fileSystemHandlerMock;
private readonly Mock<IWireMockMiddlewareOptions> _optionsMock;
_fileSystemHandlerMock = new Mock<IFileSystemHandler>(); public OwinResponseMapperTests()
_fileSystemHandlerMock.SetupAllProperties(); {
_stream = new Mock<Stream>();
_stream.SetupAllProperties();
_stream.Setup(s => s.WriteAsync(It.IsAny<byte[]>(), It.IsAny<int>(), It.IsAny<int>(), It.IsAny<CancellationToken>())).Returns(CompletedTask);
_optionsMock = new Mock<IWireMockMiddlewareOptions>(); _fileSystemHandlerMock = new Mock<IFileSystemHandler>();
_optionsMock.SetupAllProperties(); _fileSystemHandlerMock.SetupAllProperties();
_optionsMock.SetupGet(o => o.FileSystemHandler).Returns(_fileSystemHandlerMock.Object);
_headers = new Mock<IHeaderDictionary>(); _optionsMock = new Mock<IWireMockMiddlewareOptions>();
_headers.SetupAllProperties(); _optionsMock.SetupAllProperties();
_optionsMock.SetupGet(o => o.FileSystemHandler).Returns(_fileSystemHandlerMock.Object);
_headers = new Mock<IHeaderDictionary>();
_headers.SetupAllProperties();
#if NET452 #if NET452
_headers.Setup(h => h.AppendValues(It.IsAny<string>(), It.IsAny<string[]>())); _headers.Setup(h => h.AppendValues(It.IsAny<string>(), It.IsAny<string[]>()));
#else #else
_headers.Setup(h => h.Add(It.IsAny<string>(), It.IsAny<StringValues>())); _headers.Setup(h => h.Add(It.IsAny<string>(), It.IsAny<StringValues>()));
#endif #endif
_responseMock = new Mock<IResponse>(); _responseMock = new Mock<IResponse>();
_responseMock.SetupAllProperties(); _responseMock.SetupAllProperties();
_responseMock.SetupGet(r => r.Body).Returns(_stream.Object); _responseMock.SetupGet(r => r.Body).Returns(_stream.Object);
_responseMock.SetupGet(r => r.Headers).Returns(_headers.Object); _responseMock.SetupGet(r => r.Headers).Returns(_headers.Object);
_sut = new OwinResponseMapper(_optionsMock.Object); _sut = new OwinResponseMapper(_optionsMock.Object);
} }
[Fact] [Fact]
public async Task OwinResponseMapper_MapAsync_Null() public async Task OwinResponseMapper_MapAsync_Null()
{
// Act
await _sut.MapAsync(null, _responseMock.Object).ConfigureAwait(false);
}
[Theory]
[InlineData(300, 300)]
[InlineData(500, 500)]
public async Task OwinResponseMapper_MapAsync_Valid_StatusCode(object code, int expected)
{
// Arrange
var responseMessage = new ResponseMessage
{ {
StatusCode = code // Act
}; await _sut.MapAsync(null, _responseMock.Object).ConfigureAwait(false);
}
// Act [Theory]
await _sut.MapAsync(responseMessage, _responseMock.Object).ConfigureAwait(false); [InlineData(300, 300)]
[InlineData(500, 500)]
// Assert public async Task OwinResponseMapper_MapAsync_Valid_StatusCode(object code, int expected)
_responseMock.VerifySet(r => r.StatusCode = expected, Times.Once);
}
[Theory]
[InlineData(0, 200)]
[InlineData(-1, 200)]
[InlineData(10000, 200)]
[InlineData(300, 300)]
public async Task OwinResponseMapper_MapAsync_Invalid_StatusCode_When_AllowOnlyDefinedHttpStatusCodeInResponseSet_Is_True(object code, int expected)
{
// Arrange
_optionsMock.SetupGet(o => o.AllowOnlyDefinedHttpStatusCodeInResponse).Returns(true);
var responseMessage = new ResponseMessage
{ {
StatusCode = code // Arrange
}; var responseMessage = new ResponseMessage
{
StatusCode = code
};
// Act // Act
await _sut.MapAsync(responseMessage, _responseMock.Object).ConfigureAwait(false); await _sut.MapAsync(responseMessage, _responseMock.Object).ConfigureAwait(false);
// Assert // Assert
_responseMock.VerifySet(r => r.StatusCode = expected, Times.Once); _responseMock.VerifySet(r => r.StatusCode = expected, Times.Once);
} }
[Fact] [Theory]
public async Task OwinResponseMapper_MapAsync_StatusCode_Is_Null() [InlineData(0, 200)]
{ [InlineData(-1, 200)]
// Arrange [InlineData(10000, 200)]
var responseMessage = new ResponseMessage [InlineData(300, 300)]
public async Task OwinResponseMapper_MapAsync_Invalid_StatusCode_When_AllowOnlyDefinedHttpStatusCodeInResponseSet_Is_True(object code, int expected)
{ {
StatusCode = null // Arrange
}; _optionsMock.SetupGet(o => o.AllowOnlyDefinedHttpStatusCodeInResponse).Returns(true);
var responseMessage = new ResponseMessage
{
StatusCode = code
};
// Act // Act
await _sut.MapAsync(responseMessage, _responseMock.Object).ConfigureAwait(false); await _sut.MapAsync(responseMessage, _responseMock.Object).ConfigureAwait(false);
// Assert // Assert
_responseMock.VerifySet(r => r.StatusCode = It.IsAny<int>(), Times.Never); _responseMock.VerifySet(r => r.StatusCode = expected, Times.Once);
} }
[Theory] [Fact]
[InlineData(0, 0)] public async Task OwinResponseMapper_MapAsync_StatusCode_Is_Null()
[InlineData(-1, -1)]
[InlineData(10000, 10000)]
[InlineData(300, 300)]
public async Task OwinResponseMapper_MapAsync_StatusCode_Is_NotInEnumRange(object code, int expected)
{
// Arrange
var responseMessage = new ResponseMessage
{ {
StatusCode = code // Arrange
}; var responseMessage = new ResponseMessage
{
StatusCode = null
};
// Act // Act
await _sut.MapAsync(responseMessage, _responseMock.Object).ConfigureAwait(false); await _sut.MapAsync(responseMessage, _responseMock.Object).ConfigureAwait(false);
// Assert // Assert
_responseMock.VerifySet(r => r.StatusCode = expected, Times.Once); _responseMock.VerifySet(r => r.StatusCode = It.IsAny<int>(), Times.Never);
} }
[Fact] [Theory]
public async Task OwinResponseMapper_MapAsync_NoBody() [InlineData(0, 0)]
{ [InlineData(-1, -1)]
// Arrange [InlineData(10000, 10000)]
var responseMessage = new ResponseMessage [InlineData(300, 300)]
public async Task OwinResponseMapper_MapAsync_StatusCode_Is_NotInEnumRange(object code, int expected)
{ {
Headers = new Dictionary<string, WireMockList<string>>() // Arrange
}; var responseMessage = new ResponseMessage
{
StatusCode = code
};
// Act // Act
await _sut.MapAsync(responseMessage, _responseMock.Object).ConfigureAwait(false); await _sut.MapAsync(responseMessage, _responseMock.Object).ConfigureAwait(false);
// Assert // Assert
_stream.Verify(s => s.WriteAsync(It.IsAny<byte[]>(), It.IsAny<int>(), It.IsAny<int>(), It.IsAny<CancellationToken>()), Times.Never); _responseMock.VerifySet(r => r.StatusCode = expected, Times.Once);
} }
[Fact] [Fact]
public async Task OwinResponseMapper_MapAsync_Body() public async Task OwinResponseMapper_MapAsync_NoBody()
{
// Arrange
string body = "abcd";
var responseMessage = new ResponseMessage
{ {
Headers = new Dictionary<string, WireMockList<string>>(), // Arrange
BodyData = new BodyData { DetectedBodyType = BodyType.String, BodyAsString = body } var responseMessage = new ResponseMessage
}; {
Headers = new Dictionary<string, WireMockList<string>>()
};
// Act // Act
await _sut.MapAsync(responseMessage, _responseMock.Object).ConfigureAwait(false); await _sut.MapAsync(responseMessage, _responseMock.Object).ConfigureAwait(false);
// Assert // Assert
_stream.Verify(s => s.WriteAsync(new byte[] { 97, 98, 99, 100 }, 0, 4, It.IsAny<CancellationToken>()), Times.Once); _stream.Verify(s => s.WriteAsync(It.IsAny<byte[]>(), It.IsAny<int>(), It.IsAny<int>(), It.IsAny<CancellationToken>()), Times.Never);
} }
[Fact] [Fact]
public async Task OwinResponseMapper_MapAsync_BodyAsBytes() public async Task OwinResponseMapper_MapAsync_Body()
{
// Arrange
var bytes = new byte[] { 48, 49 };
var responseMessage = new ResponseMessage
{ {
Headers = new Dictionary<string, WireMockList<string>>(), // Arrange
BodyData = new BodyData { DetectedBodyType = BodyType.Bytes, BodyAsBytes = bytes } string body = "abcd";
}; var responseMessage = new ResponseMessage
{
Headers = new Dictionary<string, WireMockList<string>>(),
BodyData = new BodyData { DetectedBodyType = BodyType.String, BodyAsString = body }
};
// Act // Act
await _sut.MapAsync(responseMessage, _responseMock.Object).ConfigureAwait(false); await _sut.MapAsync(responseMessage, _responseMock.Object).ConfigureAwait(false);
// Assert // Assert
_stream.Verify(s => s.WriteAsync(bytes, 0, bytes.Length, It.IsAny<CancellationToken>()), Times.Once); _stream.Verify(s => s.WriteAsync(new byte[] { 97, 98, 99, 100 }, 0, 4, It.IsAny<CancellationToken>()), Times.Once);
} }
[Fact] [Fact]
public async Task OwinResponseMapper_MapAsync_BodyAsJson() public async Task OwinResponseMapper_MapAsync_BodyAsBytes()
{
// Arrange
var json = new { t = "x", i = (string?)null };
var responseMessage = new ResponseMessage
{ {
Headers = new Dictionary<string, WireMockList<string>>(), // Arrange
BodyData = new BodyData { DetectedBodyType = BodyType.Json, BodyAsJson = json, BodyAsJsonIndented = false } var bytes = new byte[] { 48, 49 };
}; var responseMessage = new ResponseMessage
{
Headers = new Dictionary<string, WireMockList<string>>(),
BodyData = new BodyData { DetectedBodyType = BodyType.Bytes, BodyAsBytes = bytes }
};
// Act // Act
await _sut.MapAsync(responseMessage, _responseMock.Object).ConfigureAwait(false); await _sut.MapAsync(responseMessage, _responseMock.Object).ConfigureAwait(false);
// Assert // Assert
_stream.Verify(s => s.WriteAsync(new byte[] { 123, 34, 116, 34, 58, 34, 120, 34, 125 }, 0, 9, It.IsAny<CancellationToken>()), Times.Once); _stream.Verify(s => s.WriteAsync(bytes, 0, bytes.Length, It.IsAny<CancellationToken>()), Times.Once);
} }
[Fact] [Fact]
public async Task OwinResponseMapper_MapAsync_SetResponseHeaders() public async Task OwinResponseMapper_MapAsync_BodyAsJson()
{
// Arrange
var responseMessage = new ResponseMessage
{ {
Headers = new Dictionary<string, WireMockList<string>> { { "h", new WireMockList<string>("x", "y") } } // Arrange
}; var json = new { t = "x", i = (string?)null };
var responseMessage = new ResponseMessage
{
Headers = new Dictionary<string, WireMockList<string>>(),
BodyData = new BodyData { DetectedBodyType = BodyType.Json, BodyAsJson = json, BodyAsJsonIndented = false }
};
// Act // Act
await _sut.MapAsync(responseMessage, _responseMock.Object).ConfigureAwait(false); await _sut.MapAsync(responseMessage, _responseMock.Object).ConfigureAwait(false);
// Assert // Assert
_stream.Verify(s => s.WriteAsync(new byte[] { 123, 34, 116, 34, 58, 34, 120, 34, 125 }, 0, 9, It.IsAny<CancellationToken>()), Times.Once);
}
[Fact]
public async Task OwinResponseMapper_MapAsync_SetResponseHeaders()
{
// Arrange
var responseMessage = new ResponseMessage
{
Headers = new Dictionary<string, WireMockList<string>> { { "h", new WireMockList<string>("x", "y") } }
};
// Act
await _sut.MapAsync(responseMessage, _responseMock.Object).ConfigureAwait(false);
// Assert
#if NET452 #if NET452
_headers.Verify(h => h.AppendValues("h", new string[] { "x", "y" }), Times.Once); _headers.Verify(h => h.AppendValues("h", new string[] { "x", "y" }), Times.Once);
#else #else
var v = new StringValues(); var v = new StringValues();
_headers.Verify(h => h.TryGetValue("h", out v), Times.Once); _headers.Verify(h => h.TryGetValue("h", out v), Times.Once);
#endif #endif
} }
[Fact] [Fact]
public void OwinResponseMapper_MapAsync_BodyAsFile_ThrowsException() public void OwinResponseMapper_MapAsync_BodyAsFile_ThrowsException()
{
// Arrange
var responseMessage = new ResponseMessage
{ {
Headers = new Dictionary<string, WireMockList<string>>(), // Arrange
BodyData = new BodyData { DetectedBodyType = BodyType.File, BodyAsFile = string.Empty } var responseMessage = new ResponseMessage
}; {
_fileSystemHandlerMock.Setup(f => f.ReadResponseBodyAsFile(It.IsAny<string>())).Throws<FileNotFoundException>(); Headers = new Dictionary<string, WireMockList<string>>(),
BodyData = new BodyData { DetectedBodyType = BodyType.File, BodyAsFile = string.Empty }
};
_fileSystemHandlerMock.Setup(f => f.ReadResponseBodyAsFile(It.IsAny<string>())).Throws<FileNotFoundException>();
// Act // Act
Func<Task> action = () => _sut.MapAsync(responseMessage, _responseMock.Object); Func<Task> action = () => _sut.MapAsync(responseMessage, _responseMock.Object);
// Assert // Assert
action.Should().ThrowAsync<FileNotFoundException>(); action.Should().ThrowAsync<FileNotFoundException>();
} }
[Fact] [Fact]
public async Task OwinResponseMapper_MapAsync_WithFault_EMPTY_RESPONSE() public async Task OwinResponseMapper_MapAsync_WithFault_EMPTY_RESPONSE()
{
// Arrange
string body = "abc";
var responseMessage = new ResponseMessage
{ {
Headers = new Dictionary<string, WireMockList<string>>(), // Arrange
BodyData = new BodyData { DetectedBodyType = BodyType.String, BodyAsString = body }, string body = "abc";
FaultType = FaultType.EMPTY_RESPONSE var responseMessage = new ResponseMessage
}; {
Headers = new Dictionary<string, WireMockList<string>>(),
BodyData = new BodyData { DetectedBodyType = BodyType.String, BodyAsString = body },
FaultType = FaultType.EMPTY_RESPONSE
};
// Act // Act
await _sut.MapAsync(responseMessage, _responseMock.Object).ConfigureAwait(false); await _sut.MapAsync(responseMessage, _responseMock.Object).ConfigureAwait(false);
// Assert // Assert
_stream.Verify(s => s.WriteAsync(EmptyArray<byte>.Value, 0, 0, It.IsAny<CancellationToken>()), Times.Once); _stream.Verify(s => s.WriteAsync(EmptyArray<byte>.Value, 0, 0, It.IsAny<CancellationToken>()), Times.Once);
} }
[Theory] [Theory]
[InlineData("abcd", BodyType.String)] [InlineData("abcd", BodyType.String)]
[InlineData("", BodyType.String)] [InlineData("", BodyType.String)]
[InlineData(null, BodyType.None)] [InlineData(null, BodyType.None)]
public async Task OwinResponseMapper_MapAsync_WithFault_MALFORMED_RESPONSE_CHUNK(string body, BodyType detected) public async Task OwinResponseMapper_MapAsync_WithFault_MALFORMED_RESPONSE_CHUNK(string body, BodyType detected)
{
// Arrange
var responseMessage = new ResponseMessage
{ {
Headers = new Dictionary<string, WireMockList<string>>(), // Arrange
BodyData = new BodyData { DetectedBodyType = detected, BodyAsString = body }, var responseMessage = new ResponseMessage
StatusCode = 100, {
FaultType = FaultType.MALFORMED_RESPONSE_CHUNK Headers = new Dictionary<string, WireMockList<string>>(),
}; BodyData = new BodyData { DetectedBodyType = detected, BodyAsString = body },
StatusCode = 100,
FaultType = FaultType.MALFORMED_RESPONSE_CHUNK
};
// Act // Act
await _sut.MapAsync(responseMessage, _responseMock.Object).ConfigureAwait(false); await _sut.MapAsync(responseMessage, _responseMock.Object).ConfigureAwait(false);
// Assert // Assert
_responseMock.VerifySet(r => r.StatusCode = 100, Times.Once); _responseMock.VerifySet(r => r.StatusCode = 100, Times.Once);
_stream.Verify(s => s.WriteAsync(It.IsAny<byte[]>(), 0, It.Is<int>(count => count >= 0), It.IsAny<CancellationToken>()), Times.Once); _stream.Verify(s => s.WriteAsync(It.IsAny<byte[]>(), 0, It.Is<int>(count => count >= 0), It.IsAny<CancellationToken>()), Times.Once);
}
} }
} }

View File

@@ -14,7 +14,6 @@ using FluentAssertions;
using Newtonsoft.Json; using Newtonsoft.Json;
using NFluent; using NFluent;
using WireMock.Admin.Mappings; using WireMock.Admin.Mappings;
using WireMock.Http;
using WireMock.Matchers; using WireMock.Matchers;
using WireMock.Net.Tests.Serialization; using WireMock.Net.Tests.Serialization;
using WireMock.Net.Xunit; using WireMock.Net.Xunit;
@@ -352,7 +351,7 @@ public partial class WireMockServerTests
//} //}
[Fact] [Fact]
public async Task WireMockServer_Should_Exclude_RestrictedResponseHeader() public async Task WireMockServer_Should_exclude_restrictedResponseHeader()
{ {
// Assign // Assign
string path = $"/foo_{Guid.NewGuid()}"; string path = $"/foo_{Guid.NewGuid()}";
@@ -372,26 +371,6 @@ public partial class WireMockServerTests
server.Stop(); server.Stop();
} }
[Fact] // #720
public async Task WireMockServer_Should_AllowResponseHeaderContentLength_For_HEAD()
{
// Assign
const string length = "42";
var path = $"/cl_{Guid.NewGuid()}";
using var server = WireMockServer.Start();
server
.Given(Request.Create().WithPath(path).UsingHead())
.RespondWith(Response.Create().WithHeader(HttpKnownHeaderNames.ContentLength, length));
// Act
var httpRequestMessage = new HttpRequestMessage(HttpMethod.Head, path);
var response = await server.CreateClient().SendAsync(httpRequestMessage).ConfigureAwait(false);
// Assert
response.Content.Headers.GetValues(HttpKnownHeaderNames.ContentLength).Should().Contain(length);
}
#if !NET452 && !NET461 #if !NET452 && !NET461
[Theory] [Theory]
[InlineData("TRACE")] [InlineData("TRACE")]