Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// Copyright © WireMock.Net

using System;
using Newtonsoft.Json.Linq;
using Stef.Validation;

namespace WireMock.Matchers.Request;

/// <summary>
/// The request body matcher.
/// </summary>
public class RequestMessageBodyMatcher<T> : IRequestMatcher
{
/// <summary>
/// The body data function for type T
/// </summary>
public Func<T?, bool>? Func { get; }

/// <summary>
/// The <see cref="MatchOperator"/>
/// </summary>
public MatchOperator MatchOperator { get; } = MatchOperator.Or;

/// <summary>
/// Initializes a new instance of the <see cref="RequestMessageBodyMatcher"/> class.
/// </summary>
/// <param name="func">The function.</param>
public RequestMessageBodyMatcher(Func<T?, bool> func)
{
Func = Guard.NotNull(func);
}

/// <inheritdoc />
public double GetMatchingScore(IRequestMessage requestMessage, IRequestMatchResult requestMatchResult)
{
var (score, exception) = CalculateMatchScore(requestMessage).Expand();
return requestMatchResult.AddScore(GetType(), score, exception);
}

private MatchResult CalculateMatchScore(IRequestMessage requestMessage)
{
if (Func != null)
{
if (requestMessage.BodyData?.BodyAsJson is JObject jsonObject)
{
try
{
var bodyAsT = jsonObject.ToObject<T>();
return MatchScores.ToScore(Func(bodyAsT));
}
catch (Exception ex)
{
return new MatchResult(ex);
}
}
}

return default;
}
}
23 changes: 16 additions & 7 deletions src/WireMock.Net.Minimal/RequestBuilders/Request.WithBody.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,6 @@ public IRequestBuilder WithBody(object body, MatchBehaviour matchBehaviour = Mat
return this;
}

/// <inheritdoc />
public IRequestBuilder WithBodyAsJson(object body, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch)
{
var matcher = body as IMatcher ?? new JsonMatcher(matchBehaviour, body);
return WithBody([matcher]);
}

/// <inheritdoc />
public IRequestBuilder WithBody(IMatcher matcher)
{
Expand Down Expand Up @@ -98,4 +91,20 @@ public IRequestBuilder WithBody(Func<IDictionary<string, string>?, bool> func)
_requestMatchers.Add(new RequestMessageBodyMatcher(Guard.NotNull(func)));
return this;
}

/// <inheritdoc />
public IRequestBuilder WithBodyAsJson(object body, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch)
{
var matcher = body as IMatcher ?? new JsonMatcher(matchBehaviour, body);
return WithBody([matcher]);
}

/// <inheritdoc />
public IRequestBuilder WithBodyAsType<T>(Func<T?, bool> func)
{
Guard.NotNull(func);

_requestMatchers.Add(new RequestMessageBodyMatcher<T>(func));
return this;
}
}
2 changes: 1 addition & 1 deletion src/WireMock.Net.Minimal/RequestBuilders/Request.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public partial class Request : RequestMessageCompositeMatcher, IRequestBuilder
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
public static IRequestBuilder Create()
{
return new Request(new List<IRequestMatcher>());
return new Request([]);
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,14 @@ public interface IBodyRequestBuilder : IMultiPartRequestBuilder
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
IRequestBuilder WithBody(Func<object?, bool> func);

/// <summary>
/// WithBody: func (type)
/// </summary>
/// <typeparam name="T">The type.</typeparam>
/// <param name="func">The function.</param>
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
IRequestBuilder WithBodyAsType<T>(Func<T?, bool> func);

/// <summary>
/// WithBody: func (BodyData object)
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
// Copyright © WireMock.Net

using System;
using FluentAssertions;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using FluentAssertions;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using NFluent;
using WireMock.Matchers;
using WireMock.Matchers.Request;
Expand Down Expand Up @@ -72,15 +73,17 @@ public void Request_WithBody_FuncString()
}

[Fact]
public void Request_WithBody_FuncJson()
public void Request_WithBody_FuncObject()
{
// Assign
var requestBuilder = Request.Create().UsingAnyMethod().WithBody(b => b != null);
var requestBuilder = Request.Create()
.UsingAnyMethod()
.WithBody(b => b != null);

// Act
var body = new BodyData
{
BodyAsJson = 123,
BodyAsJson = JObject.Parse("""{ "X": 123, "Y": "a" }"""),
DetectedBodyType = BodyType.Json
};
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "POST", ClientIp, body);
Expand All @@ -90,6 +93,57 @@ public void Request_WithBody_FuncJson()
Check.That(requestBuilder.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0);
}

[Theory]
[InlineData("""{ "X": 123, "Y": "a" }""", 1.0)]
[InlineData("""{ "X": 123, "Y": "b" }""", 0.0)]
public void Request_WithBodyAsType_Func(string json, double expected)
{
// Assign
var requestBuilder = Request.Create()
.UsingAnyMethod()
.WithBodyAsType<FuncType>(ft => ft != null && ft.X == 123 && ft.Y == "a");

// Act
var body = new BodyData
{
BodyAsJson = JObject.Parse(json),
DetectedBodyType = BodyType.Json
};
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "POST", ClientIp, body);

// Assert
var requestMatchResult = new RequestMatchResult();
Check.That(requestBuilder.GetMatchingScore(request, requestMatchResult)).IsEqualTo(expected);
}

[Fact]
public void Request_WithBodyAsType_Func_IncorrectType()
{
// Assign
var requestBuilder = Request.Create()
.UsingAnyMethod()
.WithBodyAsType<Version>(ft => ft != null);

// Act
var body = new BodyData
{
BodyAsJson = JObject.Parse("""{ "X": 123, "Y": "a" }"""),
DetectedBodyType = BodyType.Json
};
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "POST", ClientIp, body);

// Assert
var requestMatchResult = new RequestMatchResult();
Check.That(requestBuilder.GetMatchingScore(request, requestMatchResult)).IsEqualTo(0.0);
}

private class FuncType
{
public int X { get; set; } = 42;

public string Y { get; set; } = string.Empty;
}

[Fact]
public void Request_WithBody_FuncFormUrlEncoded()
{
Expand Down
Loading