Skip to content

Commit f8e2c7e

Browse files
authored
Add WithBodyAsType to RequestMatcher (#1388)
* Add WithBody<T> * . * t * t2
1 parent c25d8f3 commit f8e2c7e

File tree

5 files changed

+143
-12
lines changed

5 files changed

+143
-12
lines changed
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
// Copyright © WireMock.Net
2+
3+
using System;
4+
using Newtonsoft.Json.Linq;
5+
using Stef.Validation;
6+
7+
namespace WireMock.Matchers.Request;
8+
9+
/// <summary>
10+
/// The request body matcher.
11+
/// </summary>
12+
public class RequestMessageBodyMatcher<T> : IRequestMatcher
13+
{
14+
/// <summary>
15+
/// The body data function for type T
16+
/// </summary>
17+
public Func<T?, bool>? Func { get; }
18+
19+
/// <summary>
20+
/// The <see cref="MatchOperator"/>
21+
/// </summary>
22+
public MatchOperator MatchOperator { get; } = MatchOperator.Or;
23+
24+
/// <summary>
25+
/// Initializes a new instance of the <see cref="RequestMessageBodyMatcher"/> class.
26+
/// </summary>
27+
/// <param name="func">The function.</param>
28+
public RequestMessageBodyMatcher(Func<T?, bool> func)
29+
{
30+
Func = Guard.NotNull(func);
31+
}
32+
33+
/// <inheritdoc />
34+
public double GetMatchingScore(IRequestMessage requestMessage, IRequestMatchResult requestMatchResult)
35+
{
36+
var (score, exception) = CalculateMatchScore(requestMessage).Expand();
37+
return requestMatchResult.AddScore(GetType(), score, exception);
38+
}
39+
40+
private MatchResult CalculateMatchScore(IRequestMessage requestMessage)
41+
{
42+
if (Func != null)
43+
{
44+
if (requestMessage.BodyData?.BodyAsJson is JObject jsonObject)
45+
{
46+
try
47+
{
48+
var bodyAsT = jsonObject.ToObject<T>();
49+
return MatchScores.ToScore(Func(bodyAsT));
50+
}
51+
catch (Exception ex)
52+
{
53+
return new MatchResult(ex);
54+
}
55+
}
56+
}
57+
58+
return default;
59+
}
60+
}

src/WireMock.Net.Minimal/RequestBuilders/Request.WithBody.cs

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,6 @@ public IRequestBuilder WithBody(object body, MatchBehaviour matchBehaviour = Mat
3434
return this;
3535
}
3636

37-
/// <inheritdoc />
38-
public IRequestBuilder WithBodyAsJson(object body, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch)
39-
{
40-
var matcher = body as IMatcher ?? new JsonMatcher(matchBehaviour, body);
41-
return WithBody([matcher]);
42-
}
43-
4437
/// <inheritdoc />
4538
public IRequestBuilder WithBody(IMatcher matcher)
4639
{
@@ -98,4 +91,20 @@ public IRequestBuilder WithBody(Func<IDictionary<string, string>?, bool> func)
9891
_requestMatchers.Add(new RequestMessageBodyMatcher(Guard.NotNull(func)));
9992
return this;
10093
}
94+
95+
/// <inheritdoc />
96+
public IRequestBuilder WithBodyAsJson(object body, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch)
97+
{
98+
var matcher = body as IMatcher ?? new JsonMatcher(matchBehaviour, body);
99+
return WithBody([matcher]);
100+
}
101+
102+
/// <inheritdoc />
103+
public IRequestBuilder WithBodyAsType<T>(Func<T?, bool> func)
104+
{
105+
Guard.NotNull(func);
106+
107+
_requestMatchers.Add(new RequestMessageBodyMatcher<T>(func));
108+
return this;
109+
}
101110
}

src/WireMock.Net.Minimal/RequestBuilders/Request.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ public partial class Request : RequestMessageCompositeMatcher, IRequestBuilder
2929
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
3030
public static IRequestBuilder Create()
3131
{
32-
return new Request(new List<IRequestMatcher>());
32+
return new Request([]);
3333
}
3434

3535
/// <summary>

src/WireMock.Net.Shared/RequestBuilders/IBodyRequestBuilder.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,14 @@ public interface IBodyRequestBuilder : IMultiPartRequestBuilder
8080
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
8181
IRequestBuilder WithBody(Func<object?, bool> func);
8282

83+
/// <summary>
84+
/// WithBody: func (type)
85+
/// </summary>
86+
/// <typeparam name="T">The type.</typeparam>
87+
/// <param name="func">The function.</param>
88+
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
89+
IRequestBuilder WithBodyAsType<T>(Func<T?, bool> func);
90+
8391
/// <summary>
8492
/// WithBody: func (BodyData object)
8593
/// </summary>

test/WireMock.Net.Tests/RequestBuilders/RequestBuilderWithBodyTests.cs

Lines changed: 58 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
// Copyright © WireMock.Net
22

33
using System;
4-
using FluentAssertions;
54
using System.Collections.Generic;
65
using System.Linq;
76
using System.Text;
7+
using FluentAssertions;
88
using Newtonsoft.Json;
9+
using Newtonsoft.Json.Linq;
910
using NFluent;
1011
using WireMock.Matchers;
1112
using WireMock.Matchers.Request;
@@ -72,15 +73,17 @@ public void Request_WithBody_FuncString()
7273
}
7374

7475
[Fact]
75-
public void Request_WithBody_FuncJson()
76+
public void Request_WithBody_FuncObject()
7677
{
7778
// Assign
78-
var requestBuilder = Request.Create().UsingAnyMethod().WithBody(b => b != null);
79+
var requestBuilder = Request.Create()
80+
.UsingAnyMethod()
81+
.WithBody(b => b != null);
7982

8083
// Act
8184
var body = new BodyData
8285
{
83-
BodyAsJson = 123,
86+
BodyAsJson = JObject.Parse("""{ "X": 123, "Y": "a" }"""),
8487
DetectedBodyType = BodyType.Json
8588
};
8689
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "POST", ClientIp, body);
@@ -90,6 +93,57 @@ public void Request_WithBody_FuncJson()
9093
Check.That(requestBuilder.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0);
9194
}
9295

96+
[Theory]
97+
[InlineData("""{ "X": 123, "Y": "a" }""", 1.0)]
98+
[InlineData("""{ "X": 123, "Y": "b" }""", 0.0)]
99+
public void Request_WithBodyAsType_Func(string json, double expected)
100+
{
101+
// Assign
102+
var requestBuilder = Request.Create()
103+
.UsingAnyMethod()
104+
.WithBodyAsType<FuncType>(ft => ft != null && ft.X == 123 && ft.Y == "a");
105+
106+
// Act
107+
var body = new BodyData
108+
{
109+
BodyAsJson = JObject.Parse(json),
110+
DetectedBodyType = BodyType.Json
111+
};
112+
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "POST", ClientIp, body);
113+
114+
// Assert
115+
var requestMatchResult = new RequestMatchResult();
116+
Check.That(requestBuilder.GetMatchingScore(request, requestMatchResult)).IsEqualTo(expected);
117+
}
118+
119+
[Fact]
120+
public void Request_WithBodyAsType_Func_IncorrectType()
121+
{
122+
// Assign
123+
var requestBuilder = Request.Create()
124+
.UsingAnyMethod()
125+
.WithBodyAsType<Version>(ft => ft != null);
126+
127+
// Act
128+
var body = new BodyData
129+
{
130+
BodyAsJson = JObject.Parse("""{ "X": 123, "Y": "a" }"""),
131+
DetectedBodyType = BodyType.Json
132+
};
133+
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "POST", ClientIp, body);
134+
135+
// Assert
136+
var requestMatchResult = new RequestMatchResult();
137+
Check.That(requestBuilder.GetMatchingScore(request, requestMatchResult)).IsEqualTo(0.0);
138+
}
139+
140+
private class FuncType
141+
{
142+
public int X { get; set; } = 42;
143+
144+
public string Y { get; set; } = string.Empty;
145+
}
146+
93147
[Fact]
94148
public void Request_WithBody_FuncFormUrlEncoded()
95149
{

0 commit comments

Comments
 (0)