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
46 changes: 8 additions & 38 deletions src/MockHttp/Extensions/RequestMatchingExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,20 +19,6 @@ namespace MockHttp;
/// </summary>
public static class RequestMatchingExtensions
{
private static bool ContainsWildcard(this string value)
{
if (value is null)
{
throw new ArgumentNullException(nameof(value));
}

#if NETSTANDARD2_0
return value.Contains("*");
#else
return value.Contains('*', StringComparison.InvariantCultureIgnoreCase);
#endif
}

/// <summary>
/// Matches a request by specified <paramref name="requestUri" />.
/// </summary>
Expand All @@ -49,29 +35,7 @@ string requestUri
)
#pragma warning restore CA1054
{
return builder.RequestUri(requestUri, true);
}

/// <summary>
/// Matches a request by specified <paramref name="requestUri" />.
/// </summary>
/// <param name="builder">The request matching builder instance.</param>
/// <param name="requestUri">The request URI or a URI wildcard.</param>
/// <param name="allowWildcards"><see langword="true" /> to allow wildcards, or <see langword="false" /> if exact matching.</param>
/// <returns>The request matching builder instance.</returns>
#pragma warning disable CA1054
// For now, keep this internal. For coverage, and most likely, the API will change so then we'd have more to deprecate (using patterns).
internal static IRequestMatching RequestUri(this IRequestMatching builder, string requestUri, bool allowWildcards)
#pragma warning restore CA1054
{
if (requestUri is null)
{
throw new ArgumentNullException(nameof(requestUri));
}

return allowWildcards && requestUri.ContainsWildcard()
? builder.RequestUri(Matches.Wildcard(requestUri))
: builder.RequestUri(new Uri(requestUri, DotNetRelativeOrAbsolute));
return builder.RequestUri(new Uri(requestUri, DotNetRelativeOrAbsolute));
}

/// <summary>
Expand Down Expand Up @@ -119,7 +83,13 @@ static bool IsRelativeUriMatch(Uri expectedUri, Uri uri)
/// <param name="builder">The request matching builder instance.</param>
/// <param name="requestUri">The string matcher to use to match the request URI.</param>
/// <returns>The request matching builder instance.</returns>
private static IRequestMatching RequestUri(this IRequestMatching builder, Matches requestUri)
public static IRequestMatching RequestUri(
this IRequestMatching builder,
#if NET8_0_OR_GREATER
[StringSyntax(StringSyntaxAttribute.Uri)]
#endif
Matches requestUri
)
{
if (builder is null)
{
Expand Down
2 changes: 1 addition & 1 deletion src/MockHttp/Matchers/Matches.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ namespace MockHttp.Matchers;
/// <summary>
/// A string matcher encapsulating a delegate and 'pretty' name for debug/display needs when reporting errors to the user.
/// </summary>
internal readonly record struct Matches : IStringMatcher
public readonly record struct Matches : IStringMatcher
{
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
private readonly string _value;
Expand Down
3 changes: 2 additions & 1 deletion test/MockHttp.Server.Tests/MockHttpServerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using MockHttp.Fixtures;
using MockHttp.FluentAssertions;
using MockHttp.Http;
using MockHttp.Matchers;
using Xunit.Abstractions;

namespace MockHttp;
Expand Down Expand Up @@ -107,7 +108,7 @@ public static IEnumerable<object[]> RequestResponseTestCases()
[
(Action<MockHttpHandler>)(m => m
.When(matching => matching
.RequestUri("*path/child*")
.RequestUri(Matches.Wildcard("*path/child*"))
.QueryString("?key=value")
)
.Respond(with => with.Body("has content"))
Expand Down
5 changes: 4 additions & 1 deletion test/MockHttp.Testing/Specs/PublicApiSpec.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@ public abstract class PublicApiSpec
ExcludeAttributes =
[
typeof(InternalsVisibleToAttribute).FullName!,
typeof(ExcludeFromCodeCoverageAttribute).FullName!
typeof(ExcludeFromCodeCoverageAttribute).FullName!,
#if NET8_0_OR_GREATER
typeof(MemberNotNullAttribute).FullName!
#endif
],
DenyNamespacePrefixes = []
};
Expand Down
149 changes: 31 additions & 118 deletions test/MockHttp.Tests/Extensions/RequestMatchingExtensionsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,118 +23,36 @@ protected RequestMatchingExtensionsTests()
public class RequestUri : RequestMatchingExtensionsTests
{
[Theory]
[InlineData("", false, "http://127.0.0.1", true)]
[InlineData("", false, "http://127.0.0.1/", true)]
[InlineData("", true, "http://127.0.0.1", true)]
[InlineData("", true, "http://127.0.0.1/", true)]
[InlineData("/", false, "http://127.0.0.1", true)]
[InlineData("/", false, "http://127.0.0.1/", true)]
[InlineData("/", true, "http://127.0.0.1", true)]
[InlineData("/", true, "http://127.0.0.1/", true)]

[InlineData("relative.htm", true, "http://127.0.0.1/relative.htm", true)]
[InlineData("relative.htm", true, "http://127.0.0.1/relative.htm?query=string", false)]
[InlineData("relative.htm", false, "http://127.0.0.1/relative.htm", true)]
[InlineData("relative.htm", false, "http://127.0.0.1/relative.htm?query=string", false)]

[InlineData("/relative.htm", true, "http://127.0.0.1/relative.htm", true)]
[InlineData("/relative.htm", true, "http://127.0.0.1/relative.htm?query=string", false)]
[InlineData("/relative.htm", false, "http://127.0.0.1/relative.htm", true)]
[InlineData("/relative.htm", false, "http://127.0.0.1/relative.htm?query=string", false)]

[InlineData("relative.htm?query=string", true, "http://127.0.0.1/relative.htm?query=string", true)]
[InlineData("relative.htm?query=string", false, "http://127.0.0.1/relative.htm?query=string", true)]
[InlineData("http://127.0.0.1/absolute.htm?query=string", true, "http://127.0.0.1/absolute.htm?query=string", true)]
[InlineData("http://127.0.0.1/absolute.htm?query=string", false, "http://127.0.0.1/absolute.htm?query=string", true)]

[InlineData("/folder/relative.htm", true, "http://127.0.0.1/relative.htm", false)]
[InlineData("/folder/relative.htm", true, "http://127.0.0.1/relative.htm?query=string", false)]
[InlineData("/folder/relative.htm", false, "http://127.0.0.1/relative.htm", false)]
[InlineData("/folder/relative.htm", false, "http://127.0.0.1/relative.htm?query=string", false)]

[InlineData("relative.htm", true, "http://127.0.0.1/folder/relative.htm", false)]
[InlineData("relative.htm", true, "http://127.0.0.1/folder/relative.htm?query=string", false)]
[InlineData("relative.htm", false, "http://127.0.0.1/folder/relative.htm", false)]
[InlineData("relative.htm", false, "http://127.0.0.1/folder/relative.htm?query=string", false)]

[InlineData("folder/relative.htm", true, "http://127.0.0.1/folder/relative.htm", true)]
[InlineData("folder/relative.htm", true, "http://127.0.0.1/folder/relative.htm?query=string", false)]
[InlineData("folder/relative.htm", false, "http://127.0.0.1/folder/relative.htm", true)]
[InlineData("folder/relative.htm", false, "http://127.0.0.1/folder/relative.htm?query=string", false)]

[InlineData("/folder/relative.htm", true, "http://127.0.0.1/folder/relative.htm", true)]
[InlineData("/folder/relative.htm", true, "http://127.0.0.1/folder/relative.htm?query=string", false)]
[InlineData("/folder/relative.htm", false, "http://127.0.0.1/folder/relative.htm", true)]
[InlineData("/folder/relative.htm", false, "http://127.0.0.1/folder/relative.htm?query=string", false)]

[InlineData("http://127.0.0.1/absolute.htm", true, "http://127.0.0.1/absolute.htm", true)]
[InlineData("http://127.0.0.1/absolute.htm", true, "http://127.0.0.1/absolute.htm?query=string", false)]
[InlineData("http://127.0.0.1/absolute.htm", false, "http://127.0.0.1/absolute.htm", true)]
[InlineData("http://127.0.0.1/absolute.htm", false, "http://127.0.0.1/absolute.htm?query=string", false)]

[InlineData("http://127.0.0.1/absolute.htm", true, "http://127.0.0.1/folder/absolute.htm", false)]
[InlineData("http://127.0.0.1/absolute.htm", true, "http://127.0.0.1/folder/absolute.htm?query=string", false)]
[InlineData("http://127.0.0.1/absolute.htm", false, "http://127.0.0.1/folder/absolute.htm", false)]
[InlineData("http://127.0.0.1/absolute.htm", false, "http://127.0.0.1/folder/absolute.htm?query=string", false)]

[InlineData("http://127.0.0.1/folder/absolute.htm", true, "http://127.0.0.1/folder/absolute.htm", true)]
[InlineData("http://127.0.0.1/folder/absolute.htm", true, "http://127.0.0.1/folder/absolute.htm?query=string", false)]
[InlineData("http://127.0.0.1/folder/absolute.htm", false, "http://127.0.0.1/folder/absolute.htm", true)]
[InlineData("http://127.0.0.1/folder/absolute.htm", false, "http://127.0.0.1/folder/absolute.htm?query=string", false)]

[InlineData("*.htm", true, "http://127.0.0.1/relative.htm", true)]
[InlineData("*.htm", true, "http://127.0.0.1/relative.htm?query=string", false)]
[InlineData("*.htm", false, "http://127.0.0.1/relative.htm", false)]

[InlineData("*/relative.htm", true, "http://127.0.0.1/relative.htm", true)]
[InlineData("*/relative.htm", true, "http://127.0.0.1/relative.htm?query=string", false)]
[InlineData("*/relative.htm", false, "http://127.0.0.1/relative.htm", false)]

[InlineData("/*/relative.htm", true, "http://127.0.0.1/folder/relative.htm", false)]
[InlineData("/*/relative.htm", true, "http://127.0.0.1/folder/relative.htm?query=string", false)]
[InlineData("/*/relative.htm", false, "http://127.0.0.1/folder/relative.htm", false)]

[InlineData("/*/relative.htm", true, "http://127.0.0.1/relative.htm", false)]
[InlineData("/*/relative.htm", true, "http://127.0.0.1/relative.htm?query=string", false)]
[InlineData("/*/relative.htm", false, "http://127.0.0.1/relative.htm", false)]

[InlineData("/folder/*.htm", true, "http://127.0.0.1/folder/relative.htm", false)]
[InlineData("/folder/*.htm", true, "http://127.0.0.1/folder/relative.htm?query=string", false)]
[InlineData("/folder/*.htm", false, "http://127.0.0.1/folder/relative.htm", false)]

[InlineData("*/folder/*.htm", true, "http://127.0.0.1/folder/relative.htm", true)]
[InlineData("*/folder/*.htm", true, "http://127.0.0.1/folder/relative.htm?query=string", false)]
[InlineData("*/folder/*.htm", false, "http://127.0.0.1/folder/relative.htm", false)]

[InlineData("/folder/*.htm", true, "http://127.0.0.1/relative.htm", false)]
[InlineData("/folder/*.htm", true, "http://127.0.0.1/relative.htm?query=string", false)]
[InlineData("/folder/*.htm", false, "http://127.0.0.1/relative.htm", false)]

[InlineData("/*/*/relative.*", true, "http://127.0.0.1/folder1/folder2/relative.htm", false)]
[InlineData("/*/*/relative.*", true, "http://127.0.0.1/folder1/folder2/relative.htm?query=string", false)]
[InlineData("/*/*/relative.*", false, "http://127.0.0.1/folder1/folder2/relative.htm", false)]

[InlineData("*/folder1/*/relative.*", true, "http://127.0.0.1/folder1/folder2/relative.htm", true)]
[InlineData("*/folder1/*/relative.*", true, "http://127.0.0.1/folder1/folder2/relative.htm?query=string", true)]
[InlineData("*/folder1/*/relative.*", false, "http://127.0.0.1/folder1/folder2/relative.htm", false)]

[InlineData("/*/*/relative.*", true, "http://127.0.0.1/folder1/relative.htm", false)]
[InlineData("/*/*/relative.*", true, "http://127.0.0.1/folder1/relative.htm?query=string", false)]
[InlineData("/*/*/relative.*", false, "http://127.0.0.1/folder1/relative.htm", false)]

[InlineData("http://127.0.0.1/*.htm", true, "http://127.0.0.1/absolute.htm", true)]
[InlineData("http://127.0.0.1/*.htm", true, "http://127.0.0.1/absolute.htm?query=string", false)]
[InlineData("http://127.0.0.1/*.htm", false, "http://127.0.0.1/absolute.htm", false)]

[InlineData("http://127.0.0.1/*.htm", true, "http://127.0.0.1/folder/absolute.htm", true)]
[InlineData("http://127.0.0.1/*.htm", true, "http://127.0.0.1/folder/absolute.htm?query=string", false)]
[InlineData("http://127.0.0.1/*.htm", false, "http://127.0.0.1/folder/absolute.htm", false)]
public async Task When_configuring_requestUri_as_string_it_should_match(string uriString, bool allowWildcards, string requestUri, bool isMatch)
[InlineData("", "http://127.0.0.1", true)]
[InlineData("", "http://127.0.0.1/", true)]
[InlineData("/", "http://127.0.0.1", true)]
[InlineData("/", "http://127.0.0.1/", true)]
[InlineData("relative.htm", "http://127.0.0.1/relative.htm", true)]
[InlineData("relative.htm", "http://127.0.0.1/relative.htm?query=string", false)]
[InlineData("/relative.htm", "http://127.0.0.1/relative.htm", true)]
[InlineData("/relative.htm", "http://127.0.0.1/relative.htm?query=string", false)]
[InlineData("relative.htm?query=string", "http://127.0.0.1/relative.htm?query=string", true)]
[InlineData("http://127.0.0.1/absolute.htm?query=string", "http://127.0.0.1/absolute.htm?query=string", true)]
[InlineData("/folder/relative.htm", "http://127.0.0.1/relative.htm", false)]
[InlineData("/folder/relative.htm", "http://127.0.0.1/relative.htm?query=string", false)]
[InlineData("relative.htm", "http://127.0.0.1/folder/relative.htm", false)]
[InlineData("relative.htm", "http://127.0.0.1/folder/relative.htm?query=string", false)]
[InlineData("folder/relative.htm", "http://127.0.0.1/folder/relative.htm", true)]
[InlineData("folder/relative.htm", "http://127.0.0.1/folder/relative.htm?query=string", false)]
[InlineData("/folder/relative.htm", "http://127.0.0.1/folder/relative.htm", true)]
[InlineData("/folder/relative.htm", "http://127.0.0.1/folder/relative.htm?query=string", false)]
[InlineData("http://127.0.0.1/absolute.htm", "http://127.0.0.1/absolute.htm", true)]
[InlineData("http://127.0.0.1/absolute.htm", "http://127.0.0.1/absolute.htm?query=string", false)]
[InlineData("http://127.0.0.1/absolute.htm", "http://127.0.0.1/folder/absolute.htm", false)]
[InlineData("http://127.0.0.1/absolute.htm", "http://127.0.0.1/folder/absolute.htm?query=string", false)]
[InlineData("http://127.0.0.1/folder/absolute.htm", "http://127.0.0.1/folder/absolute.htm", true)]
[InlineData("http://127.0.0.1/folder/absolute.htm", "http://127.0.0.1/folder/absolute.htm?query=string", false)]
public async Task When_configuring_requestUri_as_string_it_should_match(string uriString, string requestUri, bool isMatch)
{
var request = new HttpRequestMessage { RequestUri = new Uri(requestUri, UriKind.Absolute) };

// Act
_sut.RequestUri(uriString, allowWildcards);
_sut.RequestUri(uriString);
IReadOnlyCollection<IAsyncHttpRequestMatcher> matchers = _sut.Build();

// Assert
Expand Down Expand Up @@ -169,18 +87,14 @@ public async Task When_configuring_requestUri_as_uri_it_should_match(string matc
}

[Theory]
[InlineData("*/controller/*", false)]
[InlineData("*/controller/*", true)]
[InlineData("file.jpg", true)]
[InlineData("file.jpg", false)]
[InlineData("http://0.0.0.0/path/file.jpg", true)]
[InlineData("http://0.0.0.0/path/file.jpg", false)]
public void When_formatting_uriString_matcher_it_should_return_human_readable_representation(string uriString, bool allowWildcards)
[InlineData("file.jpg")]
[InlineData("http://0.0.0.0/path/file.jpg")]
public void When_formatting_uriString_matcher_it_should_return_human_readable_representation(string uriString)
{
string expectedText = $"RequestUri: '{uriString}'";

// Act
_sut.RequestUri(uriString, allowWildcards);
_sut.RequestUri(uriString);
IReadOnlyCollection<IAsyncHttpRequestMatcher> matchers = _sut.Build();
string displayText = matchers.Should()
.ContainSingle()
Expand Down Expand Up @@ -859,8 +773,7 @@ public void Given_null_argument_when_executing_method_it_should_throw(params obj
DelegateTestCase.Create(
RequestMatchingExtensions.RequestUri,
instance,
uri.ToString(),
true
Matches.Empty
),
DelegateTestCase.Create(
RequestMatchingExtensions.RequestUri,
Expand Down
13 changes: 8 additions & 5 deletions test/MockHttp.Tests/MockHttpHandlerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using MockHttp.Http;
using MockHttp.Language;
using MockHttp.Language.Flow;
using MockHttp.Matchers;
using Newtonsoft.Json;

namespace MockHttp;
Expand Down Expand Up @@ -251,7 +252,7 @@ public async Task Given_number_of_requests_match_expected_callCount_when_verifyi

// Act
Action verifyGet = () => _sut.Verify(
matching => matching.RequestUri("**/url"),
matching => matching.RequestUri(Matches.Wildcard("**/url")),
isSent
);

Expand Down Expand Up @@ -293,7 +294,7 @@ public async Task Given_number_of_requests_does_not_match_expected_callCount_whe

// Act
Action verifyGet = () => _sut.Verify(
matching => matching.RequestUri("**/url"),
matching => matching.RequestUri(Matches.Wildcard("**/url")),
isSent
);

Expand Down Expand Up @@ -367,7 +368,7 @@ public async Task Given_a_request_expectation_when_sending_requests_it_should_co

_sut
.When(matching => matching
.RequestUri("http://0.0.0.1/*/action*")
.RequestUri(Matches.Wildcard("http://0.0.0.1/*/action*"))
.QueryString("test", "$%^&*")
.QueryString("test2=value")
.Method("POST")
Expand All @@ -380,7 +381,7 @@ public async Task Given_a_request_expectation_when_sending_requests_it_should_co
.Version(version)
.Any(any => any
.RequestUri("not-matching")
.RequestUri("**controller**")
.RequestUri(Matches.Wildcard("**controller**"))
)
.Where(r => 0 < r.Version.Major)
)
Expand Down Expand Up @@ -418,7 +419,9 @@ public async Task Given_a_request_expectation_when_sending_requests_it_should_co
HttpResponseMessage response = await _httpClient.SendAsync(req);

// Assert
_sut.Verify(matching => matching.RequestUri("**/controller/**"), IsSent.Exactly(2), "we sent it");
#pragma warning disable S6966
_sut.Verify(matching => matching.RequestUri(Matches.Wildcard("**/controller/**")), IsSent.Exactly(2), "we sent it");
#pragma warning restore S6966
#if !NETFRAMEWORK
await _sut.VerifyAsync(matching => matching.Body(jsonPostContent), IsSent.Once, "we sent it");
#endif
Expand Down
Loading