diff --git a/src/MockHttp/Matchers/Patterns/IPatternMatcher.cs b/src/MockHttp/Matchers/Patterns/IPatternMatcher.cs index cd99da36..b2560b68 100644 --- a/src/MockHttp/Matchers/Patterns/IPatternMatcher.cs +++ b/src/MockHttp/Matchers/Patterns/IPatternMatcher.cs @@ -2,5 +2,10 @@ 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/PatternMatcher.cs b/src/MockHttp/Matchers/Patterns/PatternMatcher.cs deleted file mode 100644 index c76d89dc..00000000 --- a/src/MockHttp/Matchers/Patterns/PatternMatcher.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace MockHttp.Matchers.Patterns; - -internal abstract class PatternMatcher : IPatternMatcher -{ - /// - /// Tests if the specified matches the pattern. - /// - /// The value to test. - /// Returns true if the value matches the pattern; otherwise returns false. - public abstract bool IsMatch(string value); - - /// - public abstract override string ToString(); -} diff --git a/src/MockHttp/Matchers/Patterns/RegexPatternMatcher.cs b/src/MockHttp/Matchers/Patterns/RegexPatternMatcher.cs index 56b93f06..7456a64c 100644 --- a/src/MockHttp/Matchers/Patterns/RegexPatternMatcher.cs +++ b/src/MockHttp/Matchers/Patterns/RegexPatternMatcher.cs @@ -3,7 +3,7 @@ namespace MockHttp.Matchers.Patterns; -internal class RegexPatternMatcher : PatternMatcher +internal class RegexPatternMatcher : IPatternMatcher { public RegexPatternMatcher ( @@ -24,7 +24,7 @@ public RegexPatternMatcher(Regex regex) internal Regex Regex { get; } /// - public override bool IsMatch(string value) + public virtual bool IsMatch(string value) { return Regex.IsMatch(value); } diff --git a/src/MockHttp/Matchers/Patterns/RelativeOrAbsoluteUriPatternMatcher.cs b/src/MockHttp/Matchers/Patterns/RelativeOrAbsoluteUriPatternMatcher.cs new file mode 100644 index 00000000..7ba0afbc --- /dev/null +++ b/src/MockHttp/Matchers/Patterns/RelativeOrAbsoluteUriPatternMatcher.cs @@ -0,0 +1,34 @@ +using System.Diagnostics; +using MockHttp.Http; + +namespace MockHttp.Matchers.Patterns; + +[DebuggerDisplay($"{{{nameof(_originalUri)}}}")] +internal sealed class RelativeOrAbsoluteUriPatternMatcher : UriPatternMatcher +{ + private readonly Uri _originalUri; + private readonly Uri _uri; + + public RelativeOrAbsoluteUriPatternMatcher(Uri uri) + { + _originalUri = uri; + _uri = uri.EnsureIsRooted(); + } + + public override 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/UriPatternMatcher.cs b/src/MockHttp/Matchers/Patterns/UriPatternMatcher.cs new file mode 100644 index 00000000..4b1026fc --- /dev/null +++ b/src/MockHttp/Matchers/Patterns/UriPatternMatcher.cs @@ -0,0 +1,6 @@ +namespace MockHttp.Matchers.Patterns; + +internal abstract class UriPatternMatcher : IPatternMatcher +{ + public abstract bool IsMatch(Uri value); +} diff --git a/src/MockHttp/Matchers/Patterns/UriStringPatternMatcher.cs b/src/MockHttp/Matchers/Patterns/UriStringPatternMatcher.cs new file mode 100644 index 00000000..93c381d4 --- /dev/null +++ b/src/MockHttp/Matchers/Patterns/UriStringPatternMatcher.cs @@ -0,0 +1,19 @@ +namespace MockHttp.Matchers.Patterns; + +internal class UriStringPatternMatcher : UriPatternMatcher +{ + 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 override bool IsMatch(Uri value) + { + string v = _selector(value); + return _stringPatternMatcher.IsMatch(v); + } +} diff --git a/src/MockHttp/Matchers/RequestUriMatcher.cs b/src/MockHttp/Matchers/RequestUriMatcher.cs index 2e36e934..782e810c 100644 --- a/src/MockHttp/Matchers/RequestUriMatcher.cs +++ b/src/MockHttp/Matchers/RequestUriMatcher.cs @@ -16,7 +16,7 @@ public class RequestUriMatcher : HttpRequestMatcher [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly string _formattedUri; [DebuggerBrowsable(DebuggerBrowsableState.Never)] - private readonly PatternMatcher? _uriPatternMatcher; + private readonly WildcardPatternMatcher? _uriPatternMatcher; /// /// Initializes a new instance of the class using specified . diff --git a/src/MockHttp/Matchers/UriMatcher.cs b/src/MockHttp/Matchers/UriMatcher.cs new file mode 100644 index 00000000..f757d049 --- /dev/null +++ b/src/MockHttp/Matchers/UriMatcher.cs @@ -0,0 +1,59 @@ +using System.Runtime.CompilerServices; +using MockHttp.Matchers.Patterns; +using MockHttp.Responses; + +namespace MockHttp.Matchers; + +/// +/// Matches a request by the URI. +/// +public class UriMatcher : HttpRequestMatcher +{ + private readonly IPatternMatcher _patternMatcher; + private readonly string _name; + private readonly string _patternDescription; + + /// + /// Initializes a new instance of the class. + /// + /// A matcher implementation that validates the URI. + /// A description of the pattern. + /// The name of this matcher. + /// Thrown when a required argument is . + internal UriMatcher + ( + IPatternMatcher patternMatcher, + string patternDescription, + [CallerMemberName] string? name = null + ) + { + _patternMatcher = patternMatcher ?? throw new ArgumentNullException(nameof(patternMatcher)); + _patternDescription = patternDescription ?? throw new ArgumentNullException(nameof(patternDescription)); + + name ??= GetType().Name; + if (name.EndsWith("Matcher", StringComparison.Ordinal)) + { + name = name.Remove(name.Length - "Matcher".Length); + } + + _name = name; + } + + /// + public override bool IsMatch(MockHttpRequestContext requestContext) + { + if (requestContext is null) + { + throw new ArgumentNullException(nameof(requestContext)); + } + + Uri? uri = requestContext.Request.RequestUri; + return uri is not null && _patternMatcher.IsMatch(uri); + } + + /// + public override string ToString() + { + return $"{_name}: '{_patternDescription}'"; + } +}