This commit is contained in:
Stef Heyenrath
2026-02-21 19:23:57 +01:00
parent ba4eb4b804
commit d0ed0b852e
3 changed files with 55 additions and 25 deletions

View File

@@ -1,7 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<Version>0.0.1-preview-01</Version>
<Description>AwesomeAssertions extensions for WireMock.Net RestClient</Description> <Description>AwesomeAssertions extensions for WireMock.Net RestClient</Description>
<AssemblyTitle>WireMock.Net.RestClient.AwesomeAssertions</AssemblyTitle> <AssemblyTitle>WireMock.Net.RestClient.AwesomeAssertions</AssemblyTitle>
<Authors>Stef Heyenrath</Authors> <Authors>Stef Heyenrath</Authors>

View File

@@ -1,11 +1,7 @@
// Copyright © WireMock.Net // Copyright © WireMock.Net
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Http.Headers; using System.Net.Http.Headers;
using System.Text; using System.Text;
using System.Threading.Tasks;
using Stef.Validation; using Stef.Validation;
using WireMock.Constants; using WireMock.Constants;
using WireMock.Matchers; using WireMock.Matchers;
@@ -16,7 +12,7 @@ namespace WireMock.Util;
internal static class BodyParser internal static class BodyParser
{ {
private static readonly Encoding DefaultEncoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true); private static readonly Encoding DefaultEncoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true);
private static readonly Encoding[] SupportedBodyAsStringEncodingForMultipart = [ DefaultEncoding, Encoding.ASCII ]; private static readonly Encoding[] SupportedBodyAsStringEncodingForMultipart = [DefaultEncoding, Encoding.ASCII];
/* /*
HEAD - No defined body semantics. HEAD - No defined body semantics.
@@ -156,7 +152,7 @@ internal static class BodyParser
} }
// Try to get the body as String, FormUrlEncoded or Json // Try to get the body as String, FormUrlEncoded or Json
try if (IsProbablyText(data.BodyAsBytes))
{ {
data.BodyAsString = DefaultEncoding.GetString(data.BodyAsBytes); data.BodyAsString = DefaultEncoding.GetString(data.BodyAsBytes);
data.Encoding = DefaultEncoding; data.Encoding = DefaultEncoding;
@@ -168,15 +164,8 @@ internal static class BodyParser
QueryStringParser.TryParse(data.BodyAsString, false, out var nameValueCollection) QueryStringParser.TryParse(data.BodyAsString, false, out var nameValueCollection)
) )
{ {
try data.BodyAsFormUrlEncoded = nameValueCollection;
{ data.DetectedBodyType = BodyType.FormUrlEncoded;
data.BodyAsFormUrlEncoded = nameValueCollection;
data.DetectedBodyType = BodyType.FormUrlEncoded;
}
catch
{
// Deserialize FormUrlEncoded failed, just ignore.
}
} }
// If string is not null or empty, try to deserialize the string to a JObject // If string is not null or empty, try to deserialize the string to a JObject
@@ -193,14 +182,10 @@ internal static class BodyParser
} }
} }
} }
catch
{
// Reading as string failed, just ignore
}
return data; return data;
} }
private static async Task<(string? ContentType, byte[] Bytes)> ReadBytesAsync(Stream stream, string? contentEncoding = null, bool decompressGZipAndDeflate = true) private static async Task<(string? ContentType, byte[] Bytes)> ReadBytesAsync(Stream stream, string? contentEncoding = null, bool decompressGZipAndDeflate = true)
{ {
using var memoryStream = new MemoryStream(); using var memoryStream = new MemoryStream();
@@ -215,4 +200,49 @@ internal static class BodyParser
return (null, data); return (null, data);
} }
public static bool IsProbablyText(byte[] data)
{
if (data.Length == 0)
{
return true;
}
;
// 1) Quick binary detection
for (int i = 0; i < data.Length; i++)
{
if (data[i] == 0)
{
return false;
}
}
// 2) Validate UTF-8
string text;
try
{
text = DefaultEncoding.GetString(data);
}
catch
{
return false;
}
// 3) Count printable characters
int printable = 0;
foreach (char c in text)
{
if (!char.IsControl(c) || char.IsWhiteSpace(c))
{
printable++;
}
}
var ratio = (double)printable / text.Length;
// Threshold commonly used by tools like git
return ratio > 0.85;
}
} }

View File

@@ -1002,7 +1002,7 @@ public class WireMockAdminApiAssertionsTests : IDisposable
act.Should() act.Should()
.Throw<Exception>() .Throw<Exception>()
.WithMessage("Expected wiremockadminapi to have been called using body \"byte[1] {...}\", but didn't find it among the body/bodies <null>."); .WithMessage("Expected wiremockadminapi to have been called using body \"byte[1] {...}\", but didn't find it among the body/bodies \"byte[1] {...}\".");
} }
[Fact] [Fact]
@@ -1016,21 +1016,22 @@ public class WireMockAdminApiAssertionsTests : IDisposable
}); });
var adminApi = RestClient.For<IWireMockAdminApi>(server.Url); var adminApi = RestClient.For<IWireMockAdminApi>(server.Url);
var bytes = new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
server server
.Given(Request.Create().WithPath("/a").UsingPut().WithBody([100])) .Given(Request.Create().WithPath("/binary").UsingPut().WithBody(bytes))
.RespondWith(Response.Create().WithBody("A response")); .RespondWith(Response.Create().WithBody("A response"));
// Act // Act
using var httpClient = new HttpClient(); using var httpClient = new HttpClient();
await httpClient.PutAsync($"{server.Url}/a", new ByteArrayContent([100]), _ct); await httpClient.PutAsync($"{server.Url}/binary", new ByteArrayContent(bytes), _ct);
// Assert // Assert
adminApi adminApi
.Should() .Should()
.HaveReceived(1) .HaveReceived(1)
.Calls() .Calls()
.WithBodyAsBytes([100]) .WithBodyAsBytes(bytes)
.And .And
.UsingPut(); .UsingPut();