diff --git a/src/MockHttp/Matchers/Patterns/RegexPatternMatcher.cs b/src/MockHttp/Matchers/Patterns/RegexPatternMatcher.cs index 1b179c01..2ce2116e 100644 --- a/src/MockHttp/Matchers/Patterns/RegexPatternMatcher.cs +++ b/src/MockHttp/Matchers/Patterns/RegexPatternMatcher.cs @@ -5,8 +5,6 @@ namespace MockHttp.Matchers.Patterns; internal class RegexPatternMatcher : PatternMatcher { - private readonly Regex _regex; - public RegexPatternMatcher ( #if NET7_0_OR_GREATER @@ -20,18 +18,20 @@ string regex public RegexPatternMatcher(Regex regex) { - _regex = regex ?? throw new ArgumentNullException(nameof(regex)); + Regex = regex ?? throw new ArgumentNullException(nameof(regex)); } + internal Regex Regex { get; } + /// public override bool IsMatch(string value) { - return _regex.IsMatch(value); + return Regex.IsMatch(value); } /// public override string ToString() { - return _regex.ToString(); + return Regex.ToString(); } } diff --git a/src/MockHttp/Matchers/Patterns/WildcardPatternMatcher.cs b/src/MockHttp/Matchers/Patterns/WildcardPatternMatcher.cs index 941e5425..8ce1e300 100644 --- a/src/MockHttp/Matchers/Patterns/WildcardPatternMatcher.cs +++ b/src/MockHttp/Matchers/Patterns/WildcardPatternMatcher.cs @@ -4,6 +4,8 @@ namespace MockHttp.Matchers.Patterns; internal sealed class WildcardPatternMatcher : RegexPatternMatcher { + private static readonly char[] SpecialRegexChars = ['.', '+', '*', '?', '^', '$', '(', ')', '[', ']', '{', '}', '|', '\\']; + private readonly string _pattern; public WildcardPatternMatcher(string pattern) @@ -47,7 +49,7 @@ private static string GetMatchPattern(string pattern) IEnumerable matchGroups = pattern .Split('*') .Where(s => !string.IsNullOrEmpty(s)) - .Select(s => $"({s})"); + .Select(s => $"({RegexUriEscape(s)})"); sb.Append(string.Join(".+", matchGroups)); @@ -55,4 +57,20 @@ private static string GetMatchPattern(string pattern) return sb.ToString(); } + + private static string RegexUriEscape(string s) + { + var sb = new StringBuilder(); + foreach (char ch in s) + { + if (SpecialRegexChars.Contains(ch)) + { + sb.Append('\\'); + } + + sb.Append(ch); + } + + return sb.ToString(); + } } diff --git a/test/MockHttp.Tests/Matchers/Patterns/WildcardPatternMatcherTests.cs b/test/MockHttp.Tests/Matchers/Patterns/WildcardPatternMatcherTests.cs new file mode 100644 index 00000000..5e1b9754 --- /dev/null +++ b/test/MockHttp.Tests/Matchers/Patterns/WildcardPatternMatcherTests.cs @@ -0,0 +1,46 @@ +namespace MockHttp.Matchers.Patterns; + +public sealed class WildcardPatternMatcherTests +{ + [Theory] + [InlineData("test", "^(test)$")] + [InlineData("test*", "^(test).*")] + [InlineData("*test", ".*(test)$")] + [InlineData("*test*", ".*(test).*")] + [InlineData("*test1*test2*", ".*(test1).+(test2).*")] + [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) + { + var sut = new WildcardPatternMatcher(wildcardPattern); + sut.Regex.ToString().Should().Be(expectedRegex); + } + + [Theory] + [InlineData("test", "test", true)] + [InlineData("test", "testing", false)] + [InlineData("test*", "test", true)] + [InlineData("test*", "testing", true)] + [InlineData("test*", "stress testing", false)] + [InlineData("*test", "test", true)] + [InlineData("*test", "testing", false)] + [InlineData("*test", "stress test", true)] + [InlineData("*test*", "test", true)] + [InlineData("*test*", "testing", true)] + [InlineData("*test*", "stress testing", true)] + [InlineData("*test*", "tes", false)] + [InlineData("*test1*test2*", "test1 test2", true)] + [InlineData("*test1*test2*", "test0 test1 test2 test3", true)] + [InlineData("*test1*test2*", "test test2", false)] + [InlineData("/path?q=*", "/path?q=1", true)] + [InlineData("/path?q=*", "/path?v=1&q=1", false)] + [InlineData("/path/file.jpg?q=*&p=1&m=[a,b]", "/path/file.jpg?q=search%20term&p=1&m=[a,b]", true)] + [InlineData("/path/file.jpg?q=*&p=1&m=[a,b]", "/path/file.jpg?q=search%20term&p=1&m=(a,b)", false)] + [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); + sut.IsMatch(value).Should().Be(expected); + } +}