RegexExtended in settings (#700)

* Add extra unittest for RegexExtended

* settings
This commit is contained in:
Stef Heyenrath
2021-12-12 15:40:38 +01:00
committed by GitHub
parent 4a434b5dba
commit 6943b90da6
8 changed files with 71 additions and 24 deletions

View File

@@ -18,7 +18,7 @@ namespace WireMock.Matchers
public class RegexMatcher : IStringMatcher, IIgnoreCaseMatcher public class RegexMatcher : IStringMatcher, IIgnoreCaseMatcher
{ {
private readonly AnyOf<string, StringPattern>[] _patterns; private readonly AnyOf<string, StringPattern>[] _patterns;
private readonly RegexExtended[] _expressions; private readonly Regex[] _expressions;
/// <inheritdoc cref="IMatcher.MatchBehaviour"/> /// <inheritdoc cref="IMatcher.MatchBehaviour"/>
public MatchBehaviour MatchBehaviour { get; } public MatchBehaviour MatchBehaviour { get; }
@@ -31,7 +31,10 @@ namespace WireMock.Matchers
/// </summary> /// </summary>
/// <param name="pattern">The pattern.</param> /// <param name="pattern">The pattern.</param>
/// <param name="ignoreCase">Ignore the case from the pattern.</param> /// <param name="ignoreCase">Ignore the case from the pattern.</param>
public RegexMatcher([NotNull, RegexPattern] AnyOf<string, StringPattern> pattern, bool ignoreCase = false) : this(new[] { pattern }, ignoreCase) /// <param name="throwException">Throw an exception when the internal matching fails because of invalid input.</param>
/// <param name="useRegexExtended">Use RegexExtended (default = true).</param>
public RegexMatcher([NotNull, RegexPattern] AnyOf<string, StringPattern> pattern, bool ignoreCase = false, bool throwException = false, bool useRegexExtended = true) :
this(MatchBehaviour.AcceptOnMatch, new[] { pattern }, ignoreCase, throwException, useRegexExtended)
{ {
} }
@@ -41,16 +44,10 @@ namespace WireMock.Matchers
/// <param name="matchBehaviour">The match behaviour.</param> /// <param name="matchBehaviour">The match behaviour.</param>
/// <param name="pattern">The pattern.</param> /// <param name="pattern">The pattern.</param>
/// <param name="ignoreCase">Ignore the case from the pattern.</param> /// <param name="ignoreCase">Ignore the case from the pattern.</param>
public RegexMatcher(MatchBehaviour matchBehaviour, [NotNull, RegexPattern] AnyOf<string, StringPattern> pattern, bool ignoreCase = false) : this(matchBehaviour, new[] { pattern }, ignoreCase) /// <param name="throwException">Throw an exception when the internal matching fails because of invalid input.</param>
{ /// <param name="useRegexExtended">Use RegexExtended (default = true).</param>
} public RegexMatcher(MatchBehaviour matchBehaviour, [NotNull, RegexPattern] AnyOf<string, StringPattern> pattern, bool ignoreCase = false, bool throwException = false, bool useRegexExtended = true) :
this(matchBehaviour, new[] { pattern }, ignoreCase, throwException, useRegexExtended)
/// <summary>
/// Initializes a new instance of the <see cref="RegexMatcher"/> class.
/// </summary>
/// <param name="patterns">The patterns.</param>
/// <param name="ignoreCase">Ignore the case from the pattern.</param>
public RegexMatcher([NotNull, RegexPattern] AnyOf<string, StringPattern>[] patterns, bool ignoreCase = false) : this(MatchBehaviour.AcceptOnMatch, patterns, ignoreCase)
{ {
} }
@@ -61,7 +58,8 @@ namespace WireMock.Matchers
/// <param name="patterns">The patterns.</param> /// <param name="patterns">The patterns.</param>
/// <param name="ignoreCase">Ignore the case from the pattern.</param> /// <param name="ignoreCase">Ignore the case from the pattern.</param>
/// <param name="throwException">Throw an exception when the internal matching fails because of invalid input.</param> /// <param name="throwException">Throw an exception when the internal matching fails because of invalid input.</param>
public RegexMatcher(MatchBehaviour matchBehaviour, [NotNull, RegexPattern] AnyOf<string, StringPattern>[] patterns, bool ignoreCase = false, bool throwException = false) /// <param name="useRegexExtended">Use RegexExtended (default = true).</param>
public RegexMatcher(MatchBehaviour matchBehaviour, [NotNull, RegexPattern] AnyOf<string, StringPattern>[] patterns, bool ignoreCase = false, bool throwException = false, bool useRegexExtended = true)
{ {
Check.NotNull(patterns, nameof(patterns)); Check.NotNull(patterns, nameof(patterns));
@@ -77,7 +75,7 @@ namespace WireMock.Matchers
options |= RegexOptions.IgnoreCase; options |= RegexOptions.IgnoreCase;
} }
_expressions = patterns.Select(p => new RegexExtended(p.GetPattern(), options)).ToArray(); _expressions = patterns.Select(p => useRegexExtended ? new RegexExtended(p.GetPattern(), options) : new Regex(p.GetPattern(), options)).ToArray();
} }
/// <inheritdoc cref="IStringMatcher.IsMatch"/> /// <inheritdoc cref="IStringMatcher.IsMatch"/>

View File

@@ -16,16 +16,13 @@ namespace WireMock.RegularExpressions
} }
/// <inheritdoc cref="Regex"/> /// <inheritdoc cref="Regex"/>
public RegexExtended(string pattern, public RegexExtended(string pattern, RegexOptions options)
RegexOptions options)
: this(pattern, options, Regex.InfiniteMatchTimeout) : this(pattern, options, Regex.InfiniteMatchTimeout)
{ {
} }
/// <inheritdoc cref="Regex"/> /// <inheritdoc cref="Regex"/>
public RegexExtended(string pattern, public RegexExtended(string pattern, RegexOptions options, TimeSpan matchTimeout)
RegexOptions options,
TimeSpan matchTimeout)
: base(ReplaceGuidPattern(pattern), options, matchTimeout) : base(ReplaceGuidPattern(pattern), options, matchTimeout)
{ {
} }
@@ -35,22 +32,31 @@ namespace WireMock.RegularExpressions
{ {
// Lower case format `B` Guid pattern // Lower case format `B` Guid pattern
{ @"\guidb", @"(\{[a-z0-9]{8}-([a-z0-9]{4}-){3}[a-z0-9]{12}\})" }, { @"\guidb", @"(\{[a-z0-9]{8}-([a-z0-9]{4}-){3}[a-z0-9]{12}\})" },
// Upper case format `B` Guid pattern // Upper case format `B` Guid pattern
{ @"\GUIDB", @"(\{[A-Z0-9]{8}-([A-Z0-9]{4}-){3}[A-Z0-9]{12}\})" }, { @"\GUIDB", @"(\{[A-Z0-9]{8}-([A-Z0-9]{4}-){3}[A-Z0-9]{12}\})" },
// Lower case format `D` Guid pattern // Lower case format `D` Guid pattern
{ @"\guidd", "([a-z0-9]{8}-([a-z0-9]{4}-){3}[a-z0-9]{12})" }, { @"\guidd", "([a-z0-9]{8}-([a-z0-9]{4}-){3}[a-z0-9]{12})" },
// Upper case format `D` Guid pattern // Upper case format `D` Guid pattern
{ @"\GUIDD", "([A-Z0-9]{8}-([A-Z0-9]{4}-){3}[A-Z0-9]{12})" }, { @"\GUIDD", "([A-Z0-9]{8}-([A-Z0-9]{4}-){3}[A-Z0-9]{12})" },
// Lower case format `N` Guid pattern // Lower case format `N` Guid pattern
{ @"\guidn", "([a-z0-9]{32})" }, { @"\guidn", "([a-z0-9]{32})" },
// Upper case format `N` Guid pattern // Upper case format `N` Guid pattern
{ @"\GUIDN", "([A-Z0-9]{32})" }, { @"\GUIDN", "([A-Z0-9]{32})" },
// Lower case format `P` Guid pattern // Lower case format `P` Guid pattern
{ @"\guidp", @"(\([a-z0-9]{8}-([a-z0-9]{4}-){3}[a-z0-9]{12}\))" }, { @"\guidp", @"(\([a-z0-9]{8}-([a-z0-9]{4}-){3}[a-z0-9]{12}\))" },
// Upper case format `P` Guid pattern // Upper case format `P` Guid pattern
{ @"\GUIDP", @"(\([A-Z0-9]{8}-([A-Z0-9]{4}-){3}[A-Z0-9]{12}\))" }, { @"\GUIDP", @"(\([A-Z0-9]{8}-([A-Z0-9]{4}-){3}[A-Z0-9]{12}\))" },
// Lower case format `X` Guid pattern // Lower case format `X` Guid pattern
{ @"\guidx", @"(\{0x[a-f0-9]{8},0x[a-f0-9]{4},0x[a-f0-9]{4},\{(0x[a-f0-9]{2},){7}(0x[a-f0-9]{2})\}\})" }, { @"\guidx", @"(\{0x[a-f0-9]{8},0x[a-f0-9]{4},0x[a-f0-9]{4},\{(0x[a-f0-9]{2},){7}(0x[a-f0-9]{2})\}\})" },
// Upper case format `X` Guid pattern // Upper case format `X` Guid pattern
{ @"\GUIDX", @"(\{0x[A-F0-9]{8},0x[A-F0-9]{4},0x[A-F0-9]{4},\{(0x[A-F0-9]{2},){7}(0x[A-F0-9]{2})\}\})" }, { @"\GUIDX", @"(\{0x[A-F0-9]{8},0x[A-F0-9]{4},0x[A-F0-9]{4},\{(0x[A-F0-9]{2},){7}(0x[A-F0-9]{2})\}\})" },
}; };
@@ -62,11 +68,13 @@ namespace WireMock.RegularExpressions
private static string ReplaceGuidPattern(string pattern) private static string ReplaceGuidPattern(string pattern)
{ {
Check.NotNull(pattern, nameof(pattern)); Check.NotNull(pattern, nameof(pattern));
foreach (var tokenPattern in GuidTokenPatterns) foreach (var tokenPattern in GuidTokenPatterns)
{ {
pattern = pattern.Replace(tokenPattern.Key, tokenPattern.Value); pattern = pattern.Replace(tokenPattern.Key, tokenPattern.Value);
} }
return pattern; return pattern;
} }
} }
} }

View File

@@ -41,6 +41,7 @@ namespace WireMock.Serialization
var matchBehaviour = matcher.RejectOnMatch == true ? MatchBehaviour.RejectOnMatch : MatchBehaviour.AcceptOnMatch; var matchBehaviour = matcher.RejectOnMatch == true ? MatchBehaviour.RejectOnMatch : MatchBehaviour.AcceptOnMatch;
bool ignoreCase = matcher.IgnoreCase == true; bool ignoreCase = matcher.IgnoreCase == true;
bool throwExceptionWhenMatcherFails = _settings.ThrowExceptionWhenMatcherFails == true; bool throwExceptionWhenMatcherFails = _settings.ThrowExceptionWhenMatcherFails == true;
bool useRegexExtended = _settings.UseRegexExtended == true;
switch (matcherName) switch (matcherName)
{ {
@@ -65,7 +66,7 @@ namespace WireMock.Serialization
return CreateExactObjectMatcher(matchBehaviour, stringPatterns[0], throwExceptionWhenMatcherFails); return CreateExactObjectMatcher(matchBehaviour, stringPatterns[0], throwExceptionWhenMatcherFails);
case nameof(RegexMatcher): case nameof(RegexMatcher):
return new RegexMatcher(matchBehaviour, stringPatterns, ignoreCase, throwExceptionWhenMatcherFails); return new RegexMatcher(matchBehaviour, stringPatterns, ignoreCase, throwExceptionWhenMatcherFails, useRegexExtended);
case nameof(JsonMatcher): case nameof(JsonMatcher):
object valueForJsonMatcher = matcher.Pattern ?? matcher.Patterns; object valueForJsonMatcher = matcher.Pattern ?? matcher.Patterns;

View File

@@ -1,4 +1,4 @@
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using WireMock.Matchers; using WireMock.Matchers;
@@ -9,7 +9,7 @@ namespace WireMock.Server
{ {
public partial class WireMockServer public partial class WireMockServer
{ {
private readonly RegexMatcher _adminFilesFilenamePathMatcher = new RegexMatcher(MatchBehaviour.AcceptOnMatch, @"^\/__admin\/files\/.*$"); private readonly RegexMatcher _adminFilesFilenamePathMatcher = new RegexMatcher(@"^\/__admin\/files\/.*$");
private static readonly Encoding[] FileBodyIsString = { Encoding.UTF8, Encoding.ASCII }; private static readonly Encoding[] FileBodyIsString = { Encoding.UTF8, Encoding.ASCII };
#region Files/{filename} #region Files/{filename}

View File

@@ -1,9 +1,11 @@
using System; using System;
using System.Text.RegularExpressions;
using HandlebarsDotNet; using HandlebarsDotNet;
using JetBrains.Annotations; using JetBrains.Annotations;
using WireMock.Handlers; using WireMock.Handlers;
using WireMock.Logging; using WireMock.Logging;
using WireMock.Matchers; using WireMock.Matchers;
using WireMock.RegularExpressions;
#if USE_ASPNETCORE #if USE_ASPNETCORE
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
#endif #endif
@@ -215,5 +217,11 @@ namespace WireMock.Settings
/// </summary> /// </summary>
[PublicAPI] [PublicAPI]
IWebhookSettings WebhookSettings { get; set; } IWebhookSettings WebhookSettings { get; set; }
/// <summary>
/// Use the <see cref="RegexExtended"/> instead of the default <see cref="Regex"/>.
/// </summary>
[PublicAPI]
bool? UseRegexExtended { get; }
} }
} }

View File

@@ -151,5 +151,9 @@ namespace WireMock.Settings
/// <inheritdoc cref="IWireMockServerSettings.WebhookSettings"/> /// <inheritdoc cref="IWireMockServerSettings.WebhookSettings"/>
[PublicAPI] [PublicAPI]
public IWebhookSettings WebhookSettings { get; set; } public IWebhookSettings WebhookSettings { get; set; }
/// <inheritdoc cref="IWireMockServerSettings.UseRegexExtended"/>
[PublicAPI]
public bool? UseRegexExtended { get; set; } = true;
} }
} }

View File

@@ -1,3 +1,5 @@
using System;
using FluentAssertions;
using NFluent; using NFluent;
using WireMock.Matchers; using WireMock.Matchers;
using Xunit; using Xunit;
@@ -70,6 +72,32 @@ namespace WireMock.Net.Tests.Matchers
Check.That(result).IsEqualTo(0.0d); Check.That(result).IsEqualTo(0.0d);
} }
[Fact]
public void RegexMatcher_IsMatch_RegexExtended_Guid()
{
// Assign
var matcher = new RegexMatcher(@"\GUIDB", true);
// Act
double result = matcher.IsMatch(Guid.NewGuid().ToString("B"));
// Assert
result.Should().Be(1.0);
}
[Fact]
public void RegexMatcher_IsMatch_Regex_Guid()
{
// Assign
var matcher = new RegexMatcher(@"\GUIDB", true, false, false);
// Act
double result = matcher.IsMatch(Guid.NewGuid().ToString("B"));
// Assert
result.Should().Be(0);
}
[Fact] [Fact]
public void RegexMatcher_IsMatch_IgnoreCase() public void RegexMatcher_IsMatch_IgnoreCase()
{ {

View File

@@ -10,7 +10,7 @@ namespace WireMock.Net.Tests.RegularExpressions
/// <summary> /// <summary>
/// Input guid used for testing /// Input guid used for testing
/// </summary> /// </summary>
public Guid InputGuid { get; } = Guid.NewGuid(); public Guid InputGuid => Guid.NewGuid();
[Fact] [Fact]
public void RegexExtended_GuidB_Pattern() public void RegexExtended_GuidB_Pattern()