Skip to content

Commit 50c9820

Browse files
committed
Merge branch 'master' into issue-1217
2 parents 3bec9be + 485f7ad commit 50c9820

File tree

12 files changed

+166
-40
lines changed

12 files changed

+166
-40
lines changed

src/WireMock.Net.Abstractions/Server/IWireMockServer.cs

+9-3
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using System.Collections.Specialized;
66
using WireMock.Admin.Mappings;
77
using WireMock.Logging;
8+
using WireMock.Matchers.Request;
89
using WireMock.Types;
910

1011
namespace WireMock.Server;
@@ -27,12 +28,12 @@ public interface IWireMockServer : IDisposable
2728
/// <summary>
2829
/// Gets the request logs.
2930
/// </summary>
30-
IEnumerable<ILogEntry> LogEntries { get; }
31+
IReadOnlyList<ILogEntry> LogEntries { get; }
3132

3233
/// <summary>
3334
/// Gets the mappings as MappingModels.
3435
/// </summary>
35-
IEnumerable<MappingModel> MappingModels { get; }
36+
IReadOnlyList<MappingModel> MappingModels { get; }
3637

3738
// <summary>
3839
// Gets the mappings.
@@ -109,7 +110,12 @@ public interface IWireMockServer : IDisposable
109110
/// <param name="guid">The unique identifier.</param>
110111
bool DeleteMapping(Guid guid);
111112

112-
//IEnumerable<LogEntry> FindLogEntries([NotNull] params IRequestMatcher[] matchers);
113+
/// <summary>
114+
/// Search log-entries based on matchers.
115+
/// </summary>
116+
/// <param name="matchers">The request matchers to use.</param>
117+
/// <returns>The <see cref="IReadOnlyList{ILogEntry}"/>.</returns>
118+
IReadOnlyList<ILogEntry> FindLogEntries(params IRequestMatcher[] matchers);
113119

114120
// IRespondWithAProvider Given(IRequestMatcher requestMatcher, bool saveToFile = false);
115121

src/WireMock.Net.FluentAssertions/Assertions/WireMockAssertions.AtUrl.cs

+26-5
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
// Copyright © WireMock.Net
22

33
#pragma warning disable CS1591
4-
using System;
4+
using WireMock.Extensions;
5+
using WireMock.Matchers;
56

67
// ReSharper disable once CheckNamespace
78
namespace WireMock.FluentAssertions;
@@ -11,7 +12,17 @@ public partial class WireMockAssertions
1112
[CustomAssertion]
1213
public AndWhichConstraint<WireMockAssertions, string> AtAbsoluteUrl(string absoluteUrl, string because = "", params object[] becauseArgs)
1314
{
14-
var (filter, condition) = BuildFilterAndCondition(request => string.Equals(request.AbsoluteUrl, absoluteUrl, StringComparison.OrdinalIgnoreCase));
15+
_ = AtAbsoluteUrl(new ExactMatcher(true, absoluteUrl), because, becauseArgs);
16+
17+
return new AndWhichConstraint<WireMockAssertions, string>(this, absoluteUrl);
18+
}
19+
20+
[CustomAssertion]
21+
public AndWhichConstraint<WireMockAssertions, IStringMatcher> AtAbsoluteUrl(IStringMatcher absoluteUrlMatcher, string because = "", params object[] becauseArgs)
22+
{
23+
var (filter, condition) = BuildFilterAndCondition(request => absoluteUrlMatcher.IsPerfectMatch(request.AbsoluteUrl));
24+
25+
var absoluteUrl = absoluteUrlMatcher.GetPatterns().FirstOrDefault().GetPattern();
1526

1627
Execute.Assertion
1728
.BecauseOf(because, becauseArgs)
@@ -31,13 +42,23 @@ public AndWhichConstraint<WireMockAssertions, string> AtAbsoluteUrl(string absol
3142

3243
FilterRequestMessages(filter);
3344

34-
return new AndWhichConstraint<WireMockAssertions, string>(this, absoluteUrl);
45+
return new AndWhichConstraint<WireMockAssertions, IStringMatcher>(this, absoluteUrlMatcher);
3546
}
3647

3748
[CustomAssertion]
3849
public AndWhichConstraint<WireMockAssertions, string> AtUrl(string url, string because = "", params object[] becauseArgs)
3950
{
40-
var (filter, condition) = BuildFilterAndCondition(request => string.Equals(request.Url, url, StringComparison.OrdinalIgnoreCase));
51+
_ = AtUrl(new ExactMatcher(true, url), because, becauseArgs);
52+
53+
return new AndWhichConstraint<WireMockAssertions, string>(this, url);
54+
}
55+
56+
[CustomAssertion]
57+
public AndWhichConstraint<WireMockAssertions, IStringMatcher> AtUrl(IStringMatcher urlMatcher, string because = "", params object[] becauseArgs)
58+
{
59+
var (filter, condition) = BuildFilterAndCondition(request => urlMatcher.IsPerfectMatch(request.Url));
60+
61+
var url = urlMatcher.GetPatterns().FirstOrDefault().GetPattern();
4162

4263
Execute.Assertion
4364
.BecauseOf(because, becauseArgs)
@@ -57,6 +78,6 @@ public AndWhichConstraint<WireMockAssertions, string> AtUrl(string url, string b
5778

5879
FilterRequestMessages(filter);
5980

60-
return new AndWhichConstraint<WireMockAssertions, string>(this, url);
81+
return new AndWhichConstraint<WireMockAssertions, IStringMatcher>(this, urlMatcher);
6182
}
6283
}

src/WireMock.Net/Http/WebhookSender.cs

+7-5
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ IResponseMessage originalResponseMessage
4646

4747
IBodyData? bodyData;
4848
IDictionary<string, WireMockList<string>>? headers;
49-
string webhookRequestUrl;
49+
string requestUrl;
5050
if (webhookRequest.UseTransformer == true)
5151
{
5252
ITransformer transformer;
@@ -69,18 +69,20 @@ IResponseMessage originalResponseMessage
6969

7070
bodyData = transformer.TransformBody(mapping, originalRequestMessage, originalResponseMessage, webhookRequest.BodyData, webhookRequest.TransformerReplaceNodeOptions);
7171
headers = transformer.TransformHeaders(mapping, originalRequestMessage, originalResponseMessage, webhookRequest.Headers);
72-
webhookRequestUrl = transformer.TransformString(mapping, originalRequestMessage, originalResponseMessage, webhookRequest.Url);
72+
requestUrl = transformer.TransformString(mapping, originalRequestMessage, originalResponseMessage, webhookRequest.Url);
73+
74+
mapping.Settings.WebhookSettings?.PostTransform(mapping, requestUrl, bodyData, headers);
7375
}
7476
else
7577
{
7678
bodyData = webhookRequest.BodyData;
7779
headers = webhookRequest.Headers;
78-
webhookRequestUrl = webhookRequest.Url;
80+
requestUrl = webhookRequest.Url;
7981
}
8082

8183
// Create RequestMessage
8284
var requestMessage = new RequestMessage(
83-
new UrlDetails(webhookRequestUrl),
85+
new UrlDetails(requestUrl),
8486
webhookRequest.Method,
8587
ClientIp,
8688
bodyData,
@@ -91,7 +93,7 @@ IResponseMessage originalResponseMessage
9193
};
9294

9395
// Create HttpRequestMessage
94-
var httpRequestMessage = HttpRequestMessageHelper.Create(requestMessage, webhookRequestUrl);
96+
var httpRequestMessage = HttpRequestMessageHelper.Create(requestMessage, requestUrl);
9597

9698
// Delay (if required)
9799
if (TryGetDelay(webhookRequest, out var delay))
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Copyright © WireMock.Net
2+
3+
namespace WireMock.Matchers;
4+
5+
/// <summary>
6+
/// Provides some extension methods for matchers.
7+
/// </summary>
8+
public static class MatcherExtensions
9+
{
10+
/// <summary>
11+
/// Determines if the match result is a perfect match.
12+
/// </summary>
13+
/// <param name="matcher">The string matcher.</param>
14+
/// <param name="input">The input string to match.</param>
15+
/// <returns><c>true</c>> if the match is perfect; otherwise, <c>false</c>>.</returns>
16+
public static bool IsPerfectMatch(this IStringMatcher matcher, string? input)
17+
{
18+
return matcher.IsMatch(input).IsPerfect();
19+
}
20+
}

src/WireMock.Net/Matchers/Request/RequestMessageMethodMatcher.cs

+8
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,14 @@ internal class RequestMessageMethodMatcher : IRequestMatcher
2626
/// </summary>
2727
public string[] Methods { get; }
2828

29+
/// <summary>
30+
/// Initializes a new instance of the <see cref="RequestMessageMethodMatcher"/> class.
31+
/// </summary>
32+
/// <param name="methods">The methods.</param>
33+
public RequestMessageMethodMatcher(params string[] methods) : this(MatchBehaviour.AcceptOnMatch, MatchOperator.Or, methods)
34+
{
35+
}
36+
2937
/// <summary>
3038
/// Initializes a new instance of the <see cref="RequestMessageMethodMatcher"/> class.
3139
/// </summary>

src/WireMock.Net/Server/WireMockServer.AdminFiles.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ namespace WireMock.Server;
1111

1212
public partial class WireMockServer
1313
{
14-
private static readonly Encoding[] FileBodyIsString = { Encoding.UTF8, Encoding.ASCII };
14+
private static readonly Encoding[] FileBodyIsString = [Encoding.UTF8, Encoding.ASCII];
1515

1616
#region Files/{filename}
1717
private IResponseMessage FilePost(IRequestMessage requestMessage)

src/WireMock.Net/Server/WireMockServer.ConvertMapping.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -308,7 +308,7 @@ private static IResponseBuilder InitResponseBuilder(ResponseModel responseModel)
308308
}
309309
else if (responseModel.HeadersRaw != null)
310310
{
311-
foreach (string headerLine in responseModel.HeadersRaw.Split(new[] { "\n", "\r\n" }, StringSplitOptions.RemoveEmptyEntries))
311+
foreach (string headerLine in responseModel.HeadersRaw.Split(["\n", "\r\n"], StringSplitOptions.RemoveEmptyEntries))
312312
{
313313
int indexColon = headerLine.IndexOf(":", StringComparison.Ordinal);
314314
string key = headerLine.Substring(0, indexColon).TrimStart(' ', '\t');

src/WireMock.Net/Server/WireMockServer.LogEntries.cs

+10-12
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
// Copyright © WireMock.Net
22

33
using System;
4-
using System.Collections;
54
using System.Collections.Generic;
6-
using System.Collections.ObjectModel;
75
using System.Collections.Specialized;
86
using System.Linq;
97
using JetBrains.Annotations;
@@ -24,23 +22,20 @@ public event NotifyCollectionChangedEventHandler LogEntriesChanged
2422
remove => _logEntriesChanged -= value;
2523
}
2624

27-
/// <inheritdoc cref="IWireMockServer.LogEntries" />
25+
/// <inheritdoc />
2826
[PublicAPI]
29-
public IEnumerable<ILogEntry> LogEntries => new ReadOnlyCollection<LogEntry>(_options.LogEntries.ToList());
27+
public IReadOnlyList<ILogEntry> LogEntries => _options.LogEntries.ToArray();
28+
3029

31-
/// <summary>
32-
/// The search log-entries based on matchers.
33-
/// </summary>
34-
/// <param name="matchers">The matchers.</param>
35-
/// <returns>The <see cref="IEnumerable"/>.</returns>
30+
/// <inheritdoc />
3631
[PublicAPI]
37-
public IEnumerable<LogEntry> FindLogEntries(params IRequestMatcher[] matchers)
32+
public IReadOnlyList<ILogEntry> FindLogEntries(params IRequestMatcher[] matchers)
3833
{
3934
Guard.NotNull(matchers);
4035

4136
var results = new Dictionary<LogEntry, RequestMatchResult>();
4237

43-
foreach (var log in _options.LogEntries.ToList())
38+
foreach (var log in _options.LogEntries.ToArray())
4439
{
4540
var requestMatchResult = new RequestMatchResult();
4641
foreach (var matcher in matchers)
@@ -54,7 +49,10 @@ public IEnumerable<LogEntry> FindLogEntries(params IRequestMatcher[] matchers)
5449
}
5550
}
5651

57-
return new ReadOnlyCollection<LogEntry>(results.OrderBy(x => x.Value).Select(x => x.Key).ToList());
52+
return results
53+
.OrderBy(x => x.Value)
54+
.Select(x => x.Key)
55+
.ToArray();
5856
}
5957

6058
/// <inheritdoc cref="IWireMockServer.ResetLogEntries" />

src/WireMock.Net/Server/WireMockServer.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -84,11 +84,11 @@ public partial class WireMockServer : IWireMockServer
8484
/// Gets the mappings.
8585
/// </summary>
8686
[PublicAPI]
87-
public IEnumerable<IMapping> Mappings => _options.Mappings.Values.ToArray();
87+
public IReadOnlyList<IMapping> Mappings => _options.Mappings.Values.ToArray();
8888

8989
/// <inheritdoc cref="IWireMockServer.MappingModels" />
9090
[PublicAPI]
91-
public IEnumerable<MappingModel> MappingModels => ToMappingModels();
91+
public IReadOnlyList<MappingModel> MappingModels => ToMappingModels();
9292

9393
/// <summary>
9494
/// Gets the scenarios.
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,24 @@
11
// Copyright © WireMock.Net
22

3+
using System.Collections.Generic;
4+
using WireMock.Types;
5+
using WireMock.Util;
6+
37
namespace WireMock.Settings;
48

59
/// <summary>
610
/// WebhookSettings
711
/// </summary>
812
public class WebhookSettings : HttpClientSettings
913
{
14+
/// <summary>
15+
/// Executes an action after the transformation of the request body.
16+
/// </summary>
17+
/// <param name="mapping">The mapping used for the request.</param>
18+
/// <param name="requestUrl">The request Url.</param>
19+
/// <param name="bodyData">The body data of the request. [Optional]</param>
20+
/// <param name="headers">The headers of the request. [Optional]</param>
21+
public virtual void PostTransform(IMapping mapping, string requestUrl, IBodyData? bodyData = null, IDictionary<string, WireMockList<string>>? headers = null)
22+
{
23+
}
1024
}

test/WireMock.Net.Tests/FluentAssertions/WireMockAssertionsTests.cs

+38-10
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,16 @@ public async Task HaveReceivedACall_AtAbsoluteUrl_WhenACallWasMadeToAbsoluteUrl_
102102
{
103103
await _httpClient.GetAsync("anyurl").ConfigureAwait(false);
104104

105+
_server.Should()
106+
.HaveReceivedACall()
107+
.AtAbsoluteUrl(new WildcardMatcher($"http://localhost:{_portUsed}/any*"));
108+
}
109+
110+
[Fact]
111+
public async Task HaveReceivedACall_AtAbsoluteUrlWilcardMAtcher_WhenACallWasMadeToAbsoluteUrl_Should_BeOK()
112+
{
113+
await _httpClient.GetAsync("anyurl").ConfigureAwait(false);
114+
105115
_server.Should()
106116
.HaveReceivedACall()
107117
.AtAbsoluteUrl($"http://localhost:{_portUsed}/anyurl");
@@ -231,7 +241,7 @@ public async Task HaveReceivedACall_WithHeader_ShouldCheckAllRequests()
231241
using var client2 = server.CreateClient(handler);
232242

233243
// Act 1
234-
await client1.SendAsync(new HttpRequestMessage(HttpMethod.Get, "/")
244+
var task1 = client1.SendAsync(new HttpRequestMessage(HttpMethod.Get, "/")
235245
{
236246
Headers =
237247
{
@@ -240,14 +250,16 @@ await client1.SendAsync(new HttpRequestMessage(HttpMethod.Get, "/")
240250
});
241251

242252
// Act 2
243-
await client2.SendAsync(new HttpRequestMessage(HttpMethod.Get, "/")
253+
var task2 = client2.SendAsync(new HttpRequestMessage(HttpMethod.Get, "/")
244254
{
245255
Headers =
246256
{
247257
Authorization = new AuthenticationHeaderValue("Bearer", "validToken")
248258
}
249259
});
250260

261+
await Task.WhenAll(task1, task2);
262+
251263
// Assert
252264
server.Should()
253265
.HaveReceivedACall()
@@ -268,6 +280,16 @@ public async Task HaveReceivedACall_AtUrl_WhenACallWasMadeToUrl_Should_BeOK()
268280
.AtUrl($"http://localhost:{_portUsed}/anyurl");
269281
}
270282

283+
[Fact]
284+
public async Task HaveReceivedACall_AtUrlWildcardMatcher_WhenACallWasMadeToUrl_Should_BeOK()
285+
{
286+
await _httpClient.GetAsync("anyurl").ConfigureAwait(false);
287+
288+
_server.Should()
289+
.HaveReceivedACall()
290+
.AtUrl(new WildcardMatcher($"http://localhost:{_portUsed}/AN*", true));
291+
}
292+
271293
[Fact]
272294
public void HaveReceivedACall_AtUrl_Should_ThrowWhenNoCallsWereMade()
273295
{
@@ -393,11 +415,14 @@ public async Task HaveReceivedNoCalls_UsingPost_WhenACallWasNotMadeUsingPost_Sho
393415
[Fact]
394416
public async Task HaveReceived2Calls_UsingDelete_WhenACallWasMadeUsingDelete_Should_BeOK()
395417
{
396-
await _httpClient.DeleteAsync("anyurl").ConfigureAwait(false);
397-
398-
await _httpClient.DeleteAsync("anyurl").ConfigureAwait(false);
418+
var tasks = new[]
419+
{
420+
_httpClient.DeleteAsync("anyurl"),
421+
_httpClient.DeleteAsync("anyurl"),
422+
_httpClient.GetAsync("anyurl")
423+
};
399424

400-
await _httpClient.GetAsync("anyurl").ConfigureAwait(false);
425+
await Task.WhenAll(tasks);
401426

402427
_server.Should()
403428
.HaveReceived(2).Calls()
@@ -521,11 +546,14 @@ public async Task HaveReceived1Calls_AtAbsoluteUrlUsingPost_ShouldChain()
521546
// Act
522547
var httpClient = new HttpClient();
523548

524-
await httpClient.GetAsync($"{server.Url}/a");
525-
526-
await httpClient.PostAsync($"{server.Url}/b", new StringContent("B"));
549+
var tasks = new[]
550+
{
551+
httpClient.GetAsync($"{server.Url}/a"),
552+
httpClient.PostAsync($"{server.Url}/b", new StringContent("B")),
553+
httpClient.PostAsync($"{server.Url}/c", new StringContent("C"))
554+
};
527555

528-
await httpClient.PostAsync($"{server.Url}/c", new StringContent("C"));
556+
await Task.WhenAll(tasks);
529557

530558
// Assert
531559
server

0 commit comments

Comments
 (0)