From 5cb39f86e65bf8d2f7554d40745627d32625b390 Mon Sep 17 00:00:00 2001
From: skwasjer <11424653+skwasjer@users.noreply.github.com>
Date: Sun, 8 Sep 2024 20:00:55 +0200
Subject: [PATCH] refactor: add new Pattern type that replaces
IPatternMatcher<> making the API a lot simpler.
---
src/Directory.Build.targets | 8 +++
.../Extensions/RequestMatchingExtensions.cs | 56 +++++++++++----
.../Http/HttpHeaderEqualityComparer.cs | 12 ++--
src/MockHttp/Matchers/HttpHeadersMatcher.cs | 9 ++-
.../Matchers/Patterns/IPatternMatcher.cs | 11 ---
.../Matchers/Patterns/RegexPatternMatcher.cs | 37 ----------
.../RelativeOrAbsoluteUriPatternMatcher.cs | 34 ---------
.../Patterns/UriStringPatternMatcher.cs | 19 -----
src/MockHttp/Matchers/UriMatcher.cs | 29 +++++---
src/MockHttp/Patterns/IPattern.cs | 7 ++
src/MockHttp/Patterns/Pattern.cs | 22 ++++++
src/MockHttp/Patterns/RegexPattern.cs | 53 ++++++++++++++
.../WildcardPattern.cs} | 48 +++++++++----
.../RequestMatchingExtensionsTests.cs | 12 ++++
.../Patterns/RegexPatternTests.cs | 71 +++++++++++++++++++
.../WildcardPatternTests.cs} | 45 ++++++++++--
16 files changed, 318 insertions(+), 155 deletions(-)
delete mode 100644 src/MockHttp/Matchers/Patterns/IPatternMatcher.cs
delete mode 100644 src/MockHttp/Matchers/Patterns/RegexPatternMatcher.cs
delete mode 100644 src/MockHttp/Matchers/Patterns/RelativeOrAbsoluteUriPatternMatcher.cs
delete mode 100644 src/MockHttp/Matchers/Patterns/UriStringPatternMatcher.cs
create mode 100644 src/MockHttp/Patterns/IPattern.cs
create mode 100644 src/MockHttp/Patterns/Pattern.cs
create mode 100644 src/MockHttp/Patterns/RegexPattern.cs
rename src/MockHttp/{Matchers/Patterns/WildcardPatternMatcher.cs => Patterns/WildcardPattern.cs} (54%)
create mode 100644 test/MockHttp.Tests/Patterns/RegexPatternTests.cs
rename test/MockHttp.Tests/{Matchers/Patterns/WildcardPatternMatcherTests.cs => Patterns/WildcardPatternTests.cs} (60%)
diff --git a/src/Directory.Build.targets b/src/Directory.Build.targets
index 7180fa4b..a1be6125 100644
--- a/src/Directory.Build.targets
+++ b/src/Directory.Build.targets
@@ -17,6 +17,14 @@
all
runtime; build; native; contentfiles; analyzers; buildtransitive
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
diff --git a/src/MockHttp/Extensions/RequestMatchingExtensions.cs b/src/MockHttp/Extensions/RequestMatchingExtensions.cs
index 7038c877..c78883fe 100644
--- a/src/MockHttp/Extensions/RequestMatchingExtensions.cs
+++ b/src/MockHttp/Extensions/RequestMatchingExtensions.cs
@@ -6,7 +6,7 @@
using System.Text;
using MockHttp.Http;
using MockHttp.Matchers;
-using MockHttp.Matchers.Patterns;
+using MockHttp.Patterns;
using static MockHttp.Http.UriExtensions;
namespace MockHttp;
@@ -41,18 +41,13 @@ private static bool ContainsWildcard(this string value)
public static RequestMatching RequestUri(this RequestMatching builder, string requestUri, bool allowWildcards = true)
#pragma warning restore CA1054
{
- if (builder is null)
- {
- throw new ArgumentNullException(nameof(builder));
- }
-
if (requestUri is null)
{
throw new ArgumentNullException(nameof(requestUri));
}
return allowWildcards && requestUri.ContainsWildcard()
- ? builder.With(new UriMatcher(new UriStringPatternMatcher(uri => uri.ToString(), new WildcardPatternMatcher(requestUri)), requestUri))
+ ? builder.RequestUri(WildcardPattern.Create(requestUri))
: builder.RequestUri(new Uri(requestUri, DotNetRelativeOrAbsolute));
}
@@ -64,17 +59,54 @@ public static RequestMatching RequestUri(this RequestMatching builder, string re
/// The request matching builder instance.
public static RequestMatching RequestUri(this RequestMatching builder, Uri requestUri)
{
- if (builder is null)
+ if (requestUri is null)
{
- throw new ArgumentNullException(nameof(builder));
+ throw new ArgumentNullException(nameof(requestUri));
}
- if (requestUri is null)
+ return builder.RequestUri(GetAbsoluteOrRelativePattern(requestUri));
+
+ static Pattern GetAbsoluteOrRelativePattern(Uri pattern)
{
- throw new ArgumentNullException(nameof(requestUri));
+ Uri patternRooted = pattern.EnsureIsRooted();
+ return new Pattern
+ {
+ Value = pattern.ToString(),
+ IsMatch = input =>
+ {
+ var uri = new Uri(input, DotNetRelativeOrAbsolute);
+ return IsAbsoluteUriMatch(patternRooted, uri) || IsRelativeUriMatch(patternRooted, uri);
+ }
+ };
+
+ static bool IsAbsoluteUriMatch(Uri pattern, Uri uri)
+ {
+ return pattern.IsAbsoluteUri && uri.Equals(pattern);
+ }
+
+ static bool IsRelativeUriMatch(Uri pattern, Uri uri)
+ {
+ return !pattern.IsAbsoluteUri
+ && uri.IsBaseOf(pattern)
+ && uri.ToString().EndsWith(pattern.ToString(), StringComparison.Ordinal);
+ }
+ }
+ }
+
+ ///
+ /// Matches a request by specified .
+ ///
+ /// The request matching builder instance.
+ /// The pattern that must match the request URI.
+ /// The request matching builder instance.
+ private static RequestMatching RequestUri(this RequestMatching builder, Pattern pattern)
+ {
+ if (builder is null)
+ {
+ throw new ArgumentNullException(nameof(builder));
}
- return builder.With(new UriMatcher(new RelativeOrAbsoluteUriPatternMatcher(requestUri), requestUri.ToString()));
+ return builder.With(new UriMatcher(pattern));
}
///
diff --git a/src/MockHttp/Http/HttpHeaderEqualityComparer.cs b/src/MockHttp/Http/HttpHeaderEqualityComparer.cs
index 8abed384..97d8eb1a 100644
--- a/src/MockHttp/Http/HttpHeaderEqualityComparer.cs
+++ b/src/MockHttp/Http/HttpHeaderEqualityComparer.cs
@@ -1,5 +1,5 @@
using System.ComponentModel;
-using MockHttp.Matchers.Patterns;
+using MockHttp.Patterns;
namespace MockHttp.Http;
@@ -22,7 +22,7 @@ internal enum HttpHeaderMatchType
internal sealed class HttpHeaderEqualityComparer : IEqualityComparer>>
{
private readonly HttpHeaderMatchType? _matchType;
- private readonly IPatternMatcher? _valuePatternMatcher;
+ private readonly Pattern? _valuePatternMatcher;
public HttpHeaderEqualityComparer(HttpHeaderMatchType matchType)
{
@@ -34,9 +34,9 @@ public HttpHeaderEqualityComparer(HttpHeaderMatchType matchType)
_matchType = matchType;
}
- public HttpHeaderEqualityComparer(IPatternMatcher valuePatternMatcher)
+ public HttpHeaderEqualityComparer(Pattern valuePatternMatcher)
{
- _valuePatternMatcher = valuePatternMatcher ?? throw new ArgumentNullException(nameof(valuePatternMatcher));
+ _valuePatternMatcher = valuePatternMatcher;
}
public bool Equals(KeyValuePair> x, KeyValuePair> y)
@@ -62,8 +62,8 @@ public bool Equals(KeyValuePair> x, KeyValuePair
{
string[] headerValues = HttpHeadersCollection.ParseHttpHeaderValue(yValue).ToArray();
- return _valuePatternMatcher is null && headerValues.Contains(xValue)
- || (_valuePatternMatcher is not null && headerValues.Any(_valuePatternMatcher.IsMatch));
+ return !_valuePatternMatcher.HasValue && headerValues.Contains(xValue)
+ || (_valuePatternMatcher.HasValue && headerValues.Any(_valuePatternMatcher.Value.IsMatch));
})
))
{
diff --git a/src/MockHttp/Matchers/HttpHeadersMatcher.cs b/src/MockHttp/Matchers/HttpHeadersMatcher.cs
index c4ddb85b..918abfc9 100644
--- a/src/MockHttp/Matchers/HttpHeadersMatcher.cs
+++ b/src/MockHttp/Matchers/HttpHeadersMatcher.cs
@@ -1,6 +1,6 @@
using System.Net.Http.Headers;
using MockHttp.Http;
-using MockHttp.Matchers.Patterns;
+using MockHttp.Patterns;
using MockHttp.Responses;
namespace MockHttp.Matchers;
@@ -41,10 +41,9 @@ public HttpHeadersMatcher(string name, string value, bool allowWildcards = false
throw new ArgumentNullException(nameof(name));
}
- WildcardPatternMatcher? patternMatcher = allowWildcards ? new WildcardPatternMatcher(value) : null;
- _equalityComparer = patternMatcher is null
- ? new HttpHeaderEqualityComparer(HttpHeaderMatchType.HeaderNameAndPartialValues)
- : new HttpHeaderEqualityComparer(patternMatcher);
+ _equalityComparer = allowWildcards
+ ? new HttpHeaderEqualityComparer(WildcardPattern.Create(value))
+ : new HttpHeaderEqualityComparer(HttpHeaderMatchType.HeaderNameAndPartialValues);
Value.Add(name, value);
}
diff --git a/src/MockHttp/Matchers/Patterns/IPatternMatcher.cs b/src/MockHttp/Matchers/Patterns/IPatternMatcher.cs
deleted file mode 100644
index b2560b68..00000000
--- a/src/MockHttp/Matchers/Patterns/IPatternMatcher.cs
+++ /dev/null
@@ -1,11 +0,0 @@
-namespace MockHttp.Matchers.Patterns;
-
-internal interface IPatternMatcher
-{
- ///
- /// Tests if the specified matches the pattern.
- ///
- /// The value to test.
- /// Returns true if the value matches the pattern; otherwise returns false.
- bool IsMatch(T value);
-}
diff --git a/src/MockHttp/Matchers/Patterns/RegexPatternMatcher.cs b/src/MockHttp/Matchers/Patterns/RegexPatternMatcher.cs
deleted file mode 100644
index 7456a64c..00000000
--- a/src/MockHttp/Matchers/Patterns/RegexPatternMatcher.cs
+++ /dev/null
@@ -1,37 +0,0 @@
-using System.Diagnostics.CodeAnalysis;
-using System.Text.RegularExpressions;
-
-namespace MockHttp.Matchers.Patterns;
-
-internal class RegexPatternMatcher : IPatternMatcher
-{
- public RegexPatternMatcher
- (
-#if NET8_0_OR_GREATER
- [StringSyntax(StringSyntaxAttribute.Regex)]
-#endif
- string regex
- )
- : this(new Regex(regex, RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.Singleline))
- {
- }
-
- public RegexPatternMatcher(Regex regex)
- {
- Regex = regex ?? throw new ArgumentNullException(nameof(regex));
- }
-
- internal Regex Regex { get; }
-
- ///
- public virtual bool IsMatch(string value)
- {
- return Regex.IsMatch(value);
- }
-
- ///
- public override string ToString()
- {
- return Regex.ToString();
- }
-}
diff --git a/src/MockHttp/Matchers/Patterns/RelativeOrAbsoluteUriPatternMatcher.cs b/src/MockHttp/Matchers/Patterns/RelativeOrAbsoluteUriPatternMatcher.cs
deleted file mode 100644
index fb84d4b5..00000000
--- a/src/MockHttp/Matchers/Patterns/RelativeOrAbsoluteUriPatternMatcher.cs
+++ /dev/null
@@ -1,34 +0,0 @@
-using System.Diagnostics;
-using MockHttp.Http;
-
-namespace MockHttp.Matchers.Patterns;
-
-[DebuggerDisplay($"{{{nameof(_originalUri)}}}")]
-internal sealed class RelativeOrAbsoluteUriPatternMatcher : IPatternMatcher
-{
- private readonly Uri _originalUri;
- private readonly Uri _uri;
-
- public RelativeOrAbsoluteUriPatternMatcher(Uri uri)
- {
- _originalUri = uri;
- _uri = uri.EnsureIsRooted();
- }
-
- public bool IsMatch(Uri value)
- {
- return IsAbsoluteUriMatch(value) || IsRelativeUriMatch(value);
- }
-
- private bool IsAbsoluteUriMatch(Uri uri)
- {
- return _uri.IsAbsoluteUri && uri.Equals(_uri);
- }
-
- private bool IsRelativeUriMatch(Uri uri)
- {
- return !_uri.IsAbsoluteUri
- && uri.IsBaseOf(_uri)
- && uri.ToString().EndsWith(_uri.ToString(), StringComparison.Ordinal);
- }
-}
diff --git a/src/MockHttp/Matchers/Patterns/UriStringPatternMatcher.cs b/src/MockHttp/Matchers/Patterns/UriStringPatternMatcher.cs
deleted file mode 100644
index 8a677e73..00000000
--- a/src/MockHttp/Matchers/Patterns/UriStringPatternMatcher.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-namespace MockHttp.Matchers.Patterns;
-
-internal class UriStringPatternMatcher : IPatternMatcher
-{
- private readonly Func _selector;
- private readonly IPatternMatcher _stringPatternMatcher;
-
- public UriStringPatternMatcher(Func selector, IPatternMatcher stringPatternMatcher)
- {
- _selector = selector ?? throw new ArgumentNullException(nameof(selector));
- _stringPatternMatcher = stringPatternMatcher ?? throw new ArgumentNullException(nameof(stringPatternMatcher));
- }
-
- public bool IsMatch(Uri value)
- {
- string v = _selector(value);
- return _stringPatternMatcher.IsMatch(v);
- }
-}
diff --git a/src/MockHttp/Matchers/UriMatcher.cs b/src/MockHttp/Matchers/UriMatcher.cs
index 866c4ed0..4716672d 100644
--- a/src/MockHttp/Matchers/UriMatcher.cs
+++ b/src/MockHttp/Matchers/UriMatcher.cs
@@ -1,5 +1,6 @@
-using System.Runtime.CompilerServices;
-using MockHttp.Matchers.Patterns;
+using System.Linq.Expressions;
+using System.Runtime.CompilerServices;
+using MockHttp.Patterns;
using MockHttp.Responses;
namespace MockHttp.Matchers;
@@ -9,26 +10,28 @@ namespace MockHttp.Matchers;
///
internal class UriMatcher : HttpRequestMatcher
{
- private readonly IPatternMatcher _patternMatcher;
private readonly string _name;
+ private readonly Pattern _pattern;
private readonly string _patternDescription;
+ private readonly Func? _selectorFn;
///
/// Initializes a new instance of the class.
///
- /// A matcher implementation that validates the URI.
- /// A description of the pattern.
+ /// The pattern to match.
+ /// An expression to extract the part of the URI to match against. If , uses the complete URI.
/// The name of this matcher.
/// Thrown when a required argument is .
internal UriMatcher
(
- IPatternMatcher patternMatcher,
- string patternDescription,
+ Pattern pattern,
+ Expression>? selector = null,
[CallerMemberName] string? name = null
)
{
- _patternMatcher = patternMatcher ?? throw new ArgumentNullException(nameof(patternMatcher));
- _patternDescription = patternDescription ?? throw new ArgumentNullException(nameof(patternDescription));
+ _pattern = pattern;
+ _selectorFn = selector?.Compile();
+ _patternDescription = pattern.Value;
name ??= GetType().Name;
if (name.EndsWith("Matcher", StringComparison.Ordinal))
@@ -48,7 +51,13 @@ public override bool IsMatch(MockHttpRequestContext requestContext)
}
Uri? uri = requestContext.Request.RequestUri;
- return uri is not null && _patternMatcher.IsMatch(uri);
+ if (uri is null)
+ {
+ return false;
+ }
+
+ string value = _selectorFn?.Invoke(uri) ?? uri.ToString();
+ return _pattern.IsMatch(value);
}
///
diff --git a/src/MockHttp/Patterns/IPattern.cs b/src/MockHttp/Patterns/IPattern.cs
new file mode 100644
index 00000000..2e66d21a
--- /dev/null
+++ b/src/MockHttp/Patterns/IPattern.cs
@@ -0,0 +1,7 @@
+namespace MockHttp.Patterns;
+
+internal interface IPattern
+{
+ string Value { get; }
+ Func IsMatch { get; }
+}
diff --git a/src/MockHttp/Patterns/Pattern.cs b/src/MockHttp/Patterns/Pattern.cs
new file mode 100644
index 00000000..053b582d
--- /dev/null
+++ b/src/MockHttp/Patterns/Pattern.cs
@@ -0,0 +1,22 @@
+namespace MockHttp.Patterns;
+
+internal readonly record struct Pattern : IPattern
+{
+ public required string Value { get; internal init; }
+ public required Func IsMatch { get; internal init; }
+
+ ///
+ public override string ToString()
+ {
+ return Value;
+ }
+
+ public static Pattern MatchExactly(string value)
+ {
+ return new Pattern
+ {
+ Value = value,
+ IsMatch = input => StringComparer.Ordinal.Equals(value, input)
+ };
+ }
+}
diff --git a/src/MockHttp/Patterns/RegexPattern.cs b/src/MockHttp/Patterns/RegexPattern.cs
new file mode 100644
index 00000000..ecbee42e
--- /dev/null
+++ b/src/MockHttp/Patterns/RegexPattern.cs
@@ -0,0 +1,53 @@
+using System.Text.RegularExpressions;
+
+namespace MockHttp.Patterns;
+
+internal readonly record struct RegexPattern : IPattern
+{
+ public required string Value { get; internal init; }
+ public required Func IsMatch { get; internal init; }
+
+ ///
+ public override string ToString()
+ {
+ return Value;
+ }
+
+ public static implicit operator Pattern(RegexPattern pattern)
+ {
+ return new Pattern
+ {
+ Value = pattern.Value,
+ IsMatch = pattern.IsMatch
+ };
+ }
+
+ public static RegexPattern Create
+ (
+#if NET8_0_OR_GREATER
+ [System.Diagnostics.CodeAnalysis.StringSyntax(System.Diagnostics.CodeAnalysis.StringSyntaxAttribute.Regex)]
+#endif
+ string pattern
+ )
+ {
+ return Create(new Regex(
+ pattern,
+ RegexOptions.CultureInvariant | RegexOptions.Singleline,
+ TimeSpan.FromSeconds(5))
+ );
+ }
+
+ public static RegexPattern Create(Regex pattern)
+ {
+ if (pattern is null)
+ {
+ throw new ArgumentNullException(nameof(pattern));
+ }
+
+ return new RegexPattern
+ {
+ Value = pattern.ToString(),
+ IsMatch = pattern.IsMatch
+ };
+ }
+}
diff --git a/src/MockHttp/Matchers/Patterns/WildcardPatternMatcher.cs b/src/MockHttp/Patterns/WildcardPattern.cs
similarity index 54%
rename from src/MockHttp/Matchers/Patterns/WildcardPatternMatcher.cs
rename to src/MockHttp/Patterns/WildcardPattern.cs
index 8ce1e300..86bda616 100644
--- a/src/MockHttp/Matchers/Patterns/WildcardPatternMatcher.cs
+++ b/src/MockHttp/Patterns/WildcardPattern.cs
@@ -1,37 +1,53 @@
using System.Text;
-namespace MockHttp.Matchers.Patterns;
+namespace MockHttp.Patterns;
-internal sealed class WildcardPatternMatcher : RegexPatternMatcher
+internal readonly record struct WildcardPattern : IPattern
{
private static readonly char[] SpecialRegexChars = ['.', '+', '*', '?', '^', '$', '(', ')', '[', ']', '{', '}', '|', '\\'];
- private readonly string _pattern;
+ internal RegexPattern RegexPattern { get; private init; }
+ public required string Value { get; internal init; }
+ public required Func IsMatch { get; internal init; }
- public WildcardPatternMatcher(string pattern)
- : base(GetMatchPattern(pattern))
+ ///
+ public override string ToString()
{
- _pattern = pattern;
+ return Value;
}
- ///
- public override string ToString()
+ public static implicit operator Pattern(WildcardPattern pattern)
{
- return _pattern;
+ return new Pattern
+ {
+ Value = pattern.Value,
+ IsMatch = pattern.IsMatch
+ };
}
- private static string GetMatchPattern(string pattern)
+ public static WildcardPattern Create(string pattern)
{
if (pattern is null)
{
throw new ArgumentNullException(nameof(pattern));
}
+ var regexPattern = RegexPattern.Create(GetMatchPattern(pattern));
+ return new WildcardPattern
+ {
+ RegexPattern = regexPattern,
+ Value = pattern,
+ IsMatch = regexPattern.IsMatch
+ };
+ }
+
+ private static string GetMatchPattern(string pattern)
+ {
var sb = new StringBuilder();
bool startsWithWildcard = pattern[0] == '*';
if (startsWithWildcard)
{
- pattern = pattern.TrimStart('*');
+ pattern = pattern.Substring(1);
sb.Append(".*");
}
else
@@ -49,7 +65,7 @@ private static string GetMatchPattern(string pattern)
IEnumerable matchGroups = pattern
.Split('*')
.Where(s => !string.IsNullOrEmpty(s))
- .Select(s => $"({RegexUriEscape(s)})");
+ .Select(s => $"({EscapeSpecialRegexChars(s)})");
sb.Append(string.Join(".+", matchGroups));
@@ -58,19 +74,21 @@ private static string GetMatchPattern(string pattern)
return sb.ToString();
}
- private static string RegexUriEscape(string s)
+ private static string EscapeSpecialRegexChars(string s)
{
- var sb = new StringBuilder();
+ bool isStrModified = false;
+ var sb = new StringBuilder(Math.Min(s.Length, 128));
foreach (char ch in s)
{
if (SpecialRegexChars.Contains(ch))
{
sb.Append('\\');
+ isStrModified = true;
}
sb.Append(ch);
}
- return sb.ToString();
+ return isStrModified ? sb.ToString() : s;
}
}
diff --git a/test/MockHttp.Tests/Extensions/RequestMatchingExtensionsTests.cs b/test/MockHttp.Tests/Extensions/RequestMatchingExtensionsTests.cs
index bd876f64..ecf526dc 100644
--- a/test/MockHttp.Tests/Extensions/RequestMatchingExtensionsTests.cs
+++ b/test/MockHttp.Tests/Extensions/RequestMatchingExtensionsTests.cs
@@ -23,6 +23,15 @@ 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)]
@@ -134,7 +143,10 @@ public async Task When_configuring_requestUri_as_string_it_should_match(string u
}
[Theory]
+ [InlineData("", UriKind.Relative, "http://127.0.0.1", true)]
[InlineData("", UriKind.Relative, "http://127.0.0.1/", true)]
+ [InlineData("/", UriKind.Relative, "http://127.0.0.1", true)]
+ [InlineData("/", UriKind.Relative, "http://127.0.0.1/", true)]
[InlineData("relative.htm", UriKind.Relative, "http://127.0.0.1/relative.htm", true)]
[InlineData("/folder/relative.htm", UriKind.Relative, "http://127.0.0.1/relative.htm", false)]
[InlineData("relative.htm", UriKind.Relative, "http://127.0.0.1/folder/relative.htm", false)]
diff --git a/test/MockHttp.Tests/Patterns/RegexPatternTests.cs b/test/MockHttp.Tests/Patterns/RegexPatternTests.cs
new file mode 100644
index 00000000..19cda88d
--- /dev/null
+++ b/test/MockHttp.Tests/Patterns/RegexPatternTests.cs
@@ -0,0 +1,71 @@
+using System.Text.RegularExpressions;
+
+namespace MockHttp.Patterns;
+
+public sealed class RegexPatternTests
+{
+ [Fact]
+ public void Given_that_pattern_is_null_when_creating_instance_it_should_throw()
+ {
+ string? pattern = null;
+
+ // Act
+ Func act = () => RegexPattern.Create(pattern!);
+
+ // Assert
+ act.Should()
+ .Throw()
+ .WithParameterName(nameof(pattern));
+ }
+
+ [Fact]
+ public void Given_that_regex_is_null_when_creating_instance_it_should_throw()
+ {
+ Regex? pattern = null;
+
+ // Act
+ Func act = () => RegexPattern.Create(pattern!);
+
+ // Assert
+ act.Should()
+ .Throw()
+ .WithParameterName(nameof(pattern));
+ }
+
+ [Theory]
+ [InlineData("test")]
+ [InlineData("^t.+st.*")]
+ public void When_creating_pattern_it_should_return_expected_instance(string regexPattern)
+ {
+ // Act
+ var sut = RegexPattern.Create(regexPattern);
+
+ // Assert
+ sut.Value.Should()
+ .Be(sut.ToString())
+ .And.Be(regexPattern);
+ }
+
+ [Theory]
+ [InlineData("^123$", "123", true)]
+ [InlineData("^123$", "1234", false)]
+ [InlineData("^123", "1234", true)]
+ public void Given_that_value_matches_pattern_when_matching_it_should_pass(string regexPattern, string value, bool expected)
+ {
+ var sut = RegexPattern.Create(regexPattern);
+ sut.IsMatch(value).Should().Be(expected);
+ }
+
+ [Fact]
+ public void When_casting_to_pattern_it_should_return_expected_instance()
+ {
+ var sut = RegexPattern.Create("test");
+
+ // Act
+ Pattern actual = sut;
+
+ // Assert
+ actual.Value.Should().BeSameAs(sut.Value);
+ actual.IsMatch.Should().BeSameAs(sut.IsMatch);
+ }
+}
diff --git a/test/MockHttp.Tests/Matchers/Patterns/WildcardPatternMatcherTests.cs b/test/MockHttp.Tests/Patterns/WildcardPatternTests.cs
similarity index 60%
rename from test/MockHttp.Tests/Matchers/Patterns/WildcardPatternMatcherTests.cs
rename to test/MockHttp.Tests/Patterns/WildcardPatternTests.cs
index 5e1b9754..60bffacc 100644
--- a/test/MockHttp.Tests/Matchers/Patterns/WildcardPatternMatcherTests.cs
+++ b/test/MockHttp.Tests/Patterns/WildcardPatternTests.cs
@@ -1,7 +1,21 @@
-namespace MockHttp.Matchers.Patterns;
+namespace MockHttp.Patterns;
-public sealed class WildcardPatternMatcherTests
+public sealed class WildcardPatternTests
{
+ [Fact]
+ public void Given_that_pattern_is_null_when_creating_instance_it_should_throw()
+ {
+ string? pattern = null;
+
+ // Act
+ Func act = () => WildcardPattern.Create(pattern!);
+
+ // Assert
+ act.Should()
+ .Throw()
+ .WithParameterName(nameof(pattern));
+ }
+
[Theory]
[InlineData("test", "^(test)$")]
[InlineData("test*", "^(test).*")]
@@ -11,10 +25,16 @@ public sealed class WildcardPatternMatcherTests
[InlineData("/path?*", "^(/path\\?).*")]
[InlineData("/path/file.jpg?q=*&p=1&m=[a,b]", "^(/path/file\\.jpg\\?q=).+(&p=1&m=\\[a,b\\])$")]
[InlineData(".+?^$()[]{}|\\", "^(\\.\\+\\?\\^\\$\\(\\)\\[\\]\\{\\}\\|\\\\)$")]
- public void When_creating_matcher_it_should_generate_expected_regex(string wildcardPattern, string expectedRegex)
+ public void When_creating_pattern_it_should_return_expected_instance(string wildcardPattern, string expectedRegex)
{
- var sut = new WildcardPatternMatcher(wildcardPattern);
- sut.Regex.ToString().Should().Be(expectedRegex);
+ // Act
+ var sut = WildcardPattern.Create(wildcardPattern);
+
+ // Assert
+ sut.RegexPattern.Value.Should().Be(expectedRegex);
+ sut.Value.Should()
+ .Be(sut.ToString())
+ .And.Be(wildcardPattern);
}
[Theory]
@@ -40,7 +60,20 @@ public void When_creating_matcher_it_should_generate_expected_regex(string wildc
[InlineData("/path/file.jpg?q=*&p=*&m=*", "/path/file.jpg?q=search%20term&p=1&m=[a,b]", true)]
public void Given_that_value_matches_pattern_when_matching_it_should_pass(string wildcardPattern, string value, bool expected)
{
- var sut = new WildcardPatternMatcher(wildcardPattern);
+ var sut = WildcardPattern.Create(wildcardPattern);
sut.IsMatch(value).Should().Be(expected);
}
+
+ [Fact]
+ public void When_casting_to_pattern_it_should_return_expected_instance()
+ {
+ var sut = WildcardPattern.Create("te*st");
+
+ // Act
+ Pattern actual = sut;
+
+ // Assert
+ actual.Value.Should().BeSameAs(sut.Value);
+ actual.IsMatch.Should().BeSameAs(sut.IsMatch);
+ }
}