Compare commits

...

3 Commits

Author SHA1 Message Date
Stef Heyenrath
d2b53893db 1.6.0 2024-08-16 13:19:07 +02:00
Stef Heyenrath
088444024f Allow setting Content-Length header on the response (#1158)
* Allow setting Content-Length header on the response

* fix?
2024-08-16 12:14:42 +02:00
Stef Heyenrath
7e162a00ab Fix FormUrlEncodedMatcher (MatchOperator.And) (#1157) 2024-08-15 11:01:37 +02:00
12 changed files with 337 additions and 266 deletions

View File

@@ -1,3 +1,13 @@
# 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)
- [#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]

View File

@@ -4,7 +4,7 @@
</PropertyGroup>
<PropertyGroup>
<VersionPrefix>1.5.62</VersionPrefix>
<VersionPrefix>1.6.0</VersionPrefix>
<PackageIcon>WireMock.Net-Logo.png</PackageIcon>
<PackageProjectUrl>https://github.com/WireMock-Net/WireMock.Net</PackageProjectUrl>
<PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
@@ -45,7 +45,7 @@
<ItemGroup>
<PackageReference Include="JetBrains.Annotations" Version="2023.3.0" PrivateAssets="All" />
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1" PrivateAssets="All" />
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="8.0.0" PrivateAssets="All" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net8.0' ">

View File

@@ -1,6 +1,6 @@
rem https://github.com/StefH/GitHubReleaseNotes
SET version=1.5.62
SET version=1.6.0
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,11 @@
# 1.5.62 (27 July 2024)
- #1147 Add FormUrlEncodedMatcher [feature]
- #1143 FormEncoded Request fails (404 Not Found) if key value pairs order in mapping is different from request body order [bug]
# 1.6.0 (16 August 2024)
- #1042 Update + add fluent builder methods [feature]
- #1109 Add Aspire Extension [feature]
- #1148 Use Guid.TryParseExact with format &quot;D&quot;
- #1157 Fix FormUrlEncodedMatcher (MatchOperator.And) [bug]
- #1158 Allow setting Content-Length header on the response [feature]
- #720 Response Header Content-Length not available when call HEAD Method [feature]
- #1145 Response is auto converting string to guid [bug]
- #1156 FormUrlEncodedMatcher is not requiring to match all properties when MatchOperator.And [bug]
The full release notes can be found here: https://github.com/WireMock-Net/WireMock.Net/blob/master/CHANGELOG.md

View File

@@ -137,7 +137,6 @@ message HelloReply {
public static void Run()
{
RunOnLocal();
return;
var mappingBuilder = new MappingBuilder();
mappingBuilder
@@ -308,17 +307,6 @@ message HelloReply {
.RespondWith(Response.Create()
.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
#if MIMEKIT
@@ -377,6 +365,15 @@ message HelloReply {
.WithHeader("Content-Type", "text/plain")
);
server
.Given(Request.Create()
.UsingHead()
.WithPath("/cl")
)
.RespondWith(Response.Create()
.WithHeader("Content-Length", "42")
);
server
.Given(Request.Create()
.UsingMethod("GET")

View File

@@ -37,7 +37,6 @@
<ItemGroup>
<PackageReference Include="Aspire.Hosting" Version="8.0.0" />
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="8.0.0" PrivateAssets="All" />
</ItemGroup>
<ItemGroup>

View File

@@ -14,12 +14,12 @@ namespace WireMock.Http;
/// </summary>
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 =
{
Accept,
Connection,
ContentLength,
ContentType,
Date, // RFC1123Pattern
Expect,

View File

@@ -1,6 +1,7 @@
// Copyright © WireMock.Net
using System.Collections.Generic;
using System.Linq;
using AnyOfTypes;
using Stef.Validation;
using WireMock.Models;
@@ -27,7 +28,7 @@ public class FormUrlEncodedMatcher : IStringMatcher, IIgnoreCaseMatcher
/// </summary>
/// <param name="pattern">The pattern.</param>
/// <param name="ignoreCase">Ignore the case from the pattern.</param>
/// <param name="matchOperator">The <see cref="Matchers.MatchOperator"/> to use. (default = "Or")</param>
/// <param name="matchOperator">The <see cref="MatchOperator"/> to use. (default = "Or")</param>
public FormUrlEncodedMatcher(
AnyOf<string, StringPattern> pattern,
bool ignoreCase = false,
@@ -42,7 +43,7 @@ public class FormUrlEncodedMatcher : IStringMatcher, IIgnoreCaseMatcher
/// <param name="matchBehaviour">The match behaviour.</param>
/// <param name="pattern">The pattern.</param>
/// <param name="ignoreCase">Ignore the case from the pattern.</param>
/// <param name="matchOperator">The <see cref="Matchers.MatchOperator"/> to use. (default = "Or")</param>
/// <param name="matchOperator">The <see cref="MatchOperator"/> to use. (default = "Or")</param>
public FormUrlEncodedMatcher(
MatchBehaviour matchBehaviour,
AnyOf<string, StringPattern> pattern,
@@ -57,7 +58,7 @@ public class FormUrlEncodedMatcher : IStringMatcher, IIgnoreCaseMatcher
/// </summary>
/// <param name="patterns">The patterns.</param>
/// <param name="ignoreCase">Ignore the case from the pattern.</param>
/// <param name="matchOperator">The <see cref="Matchers.MatchOperator"/> to use. (default = "Or")</param>
/// <param name="matchOperator">The <see cref="MatchOperator"/> to use. (default = "Or")</param>
public FormUrlEncodedMatcher(
AnyOf<string, StringPattern>[] patterns,
bool ignoreCase = false,
@@ -72,7 +73,7 @@ public class FormUrlEncodedMatcher : IStringMatcher, IIgnoreCaseMatcher
/// <param name="matchBehaviour">The match behaviour.</param>
/// <param name="patterns">The patterns.</param>
/// <param name="ignoreCase">Ignore the case from the pattern.</param>
/// <param name="matchOperator">The <see cref="Matchers.MatchOperator"/> to use. (default = "Or")</param>
/// <param name="matchOperator">The <see cref="MatchOperator"/> to use. (default = "Or")</param>
public FormUrlEncodedMatcher(
MatchBehaviour matchBehaviour,
AnyOf<string, StringPattern>[] patterns,
@@ -112,7 +113,20 @@ public class FormUrlEncodedMatcher : IStringMatcher, IIgnoreCaseMatcher
return new MatchResult(MatchScores.Mismatch);
}
var matches = GetMatches(inputNameValueCollection);
var score = MatchScores.ToScore(matches, MatchOperator);
return new MatchResult(MatchBehaviourHelper.Convert(MatchBehaviour, score));
}
private bool[] GetMatches(IDictionary<string, string> inputNameValueCollection)
{
var matches = new List<bool>();
if (_pairs.Count > inputNameValueCollection.Count)
{
matches.AddRange(Enumerable.Repeat(false, _pairs.Count - inputNameValueCollection.Count));
}
foreach (var inputKeyValuePair in inputNameValueCollection)
{
var match = false;
@@ -132,8 +146,7 @@ public class FormUrlEncodedMatcher : IStringMatcher, IIgnoreCaseMatcher
matches.Add(match);
}
var score = MatchScores.ToScore(matches.ToArray(), MatchOperator);
return new MatchResult(MatchBehaviourHelper.Convert(MatchBehaviour, score));
return matches.ToArray();
}
/// <inheritdoc />

View File

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

View File

@@ -75,4 +75,25 @@ public class FormUrlEncodedMatcherTest
// Assert
score.Should().Be(expected);
}
[Fact]
public async Task FormUrlEncodedMatcher_IsMatch_And_MatchAllProperties()
{
// Arrange
var content = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("name", "John Doe"),
new KeyValuePair<string, string>("email", "johndoe@example.com")
});
var contentAsString = await content.ReadAsStringAsync();
// The expectation is that the matcher requires all properties to be present in the content.
var matcher = new FormUrlEncodedMatcher(["name=*", "email=*", "required=*"], matchOperator: MatchOperator.And);
// Act
var score = matcher.IsMatch(contentAsString).IsPerfect();
// Assert
score.Should().BeFalse();
}
}

View File

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

View File

@@ -14,6 +14,7 @@ using FluentAssertions;
using Newtonsoft.Json;
using NFluent;
using WireMock.Admin.Mappings;
using WireMock.Http;
using WireMock.Matchers;
using WireMock.Net.Tests.Serialization;
using WireMock.Net.Xunit;
@@ -351,7 +352,7 @@ public partial class WireMockServerTests
//}
[Fact]
public async Task WireMockServer_Should_exclude_restrictedResponseHeader()
public async Task WireMockServer_Should_Exclude_RestrictedResponseHeader()
{
// Assign
string path = $"/foo_{Guid.NewGuid()}";
@@ -371,6 +372,26 @@ public partial class WireMockServerTests
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
[Theory]
[InlineData("TRACE")]