From 9f66dd20582b37c6a48e00d290f8954be534fdbd Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Sat, 17 Aug 2019 15:56:02 +0200 Subject: [PATCH 01/13] wip --- WireMock.Net Solution.sln.DotSettings | 1 + .../Matchers/BaseScriptMatcher.cs | 71 ++++++++++ src/WireMock.Net/Matchers/CSScriptMatcher.cs | 43 ++++++ .../Matchers/CSharpCodeMatcher.cs | 133 ++++++++++++++++++ .../Serialization/MatcherMapper.cs | 12 +- .../Settings/FluentMockServerSettings.cs | 4 + .../Settings/IFluentMockServerSettings.cs | 11 +- src/WireMock.Net/WireMock.Net.csproj | 15 +- .../Matchers/CSScriptMatcherTests.cs | 112 +++++++++++++++ .../Matchers/CSharpCodeMatcherTests.cs | 112 +++++++++++++++ .../WireMock.Net.Tests.csproj | 4 + 11 files changed, 511 insertions(+), 7 deletions(-) create mode 100644 src/WireMock.Net/Matchers/BaseScriptMatcher.cs create mode 100644 src/WireMock.Net/Matchers/CSScriptMatcher.cs create mode 100644 src/WireMock.Net/Matchers/CSharpCodeMatcher.cs create mode 100644 test/WireMock.Net.Tests/Matchers/CSScriptMatcherTests.cs create mode 100644 test/WireMock.Net.Tests/Matchers/CSharpCodeMatcherTests.cs diff --git a/WireMock.Net Solution.sln.DotSettings b/WireMock.Net Solution.sln.DotSettings index 8f9aa5d2d..84b8ed70b 100644 --- a/WireMock.Net Solution.sln.DotSettings +++ b/WireMock.Net Solution.sln.DotSettings @@ -1,4 +1,5 @@  + CS ID IP MD5 diff --git a/src/WireMock.Net/Matchers/BaseScriptMatcher.cs b/src/WireMock.Net/Matchers/BaseScriptMatcher.cs new file mode 100644 index 000000000..16d5e3bc1 --- /dev/null +++ b/src/WireMock.Net/Matchers/BaseScriptMatcher.cs @@ -0,0 +1,71 @@ +using System.Linq; +using JetBrains.Annotations; +using WireMock.Validation; + +namespace WireMock.Matchers +{ + internal abstract class BaseScriptMatcher : IStringMatcher + { + protected const string Framework = "{0}; namespace {1} {{ public class CodeHelper {{ public bool IsMatch(string it) {{ {1} }} }} }}"; + + private readonly string[] _usings = + { + "System", + "System.Linq", + "System.Collections.Generic" + }; + + public MatchBehaviour MatchBehaviour { get; } + + protected readonly string[] Patterns; + + /// + /// Initializes a new instance of the class. + /// + /// The patterns. + protected BaseScriptMatcher([NotNull] params string[] patterns) : this(MatchBehaviour.AcceptOnMatch, patterns) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The match behaviour. + /// The patterns. + protected BaseScriptMatcher(MatchBehaviour matchBehaviour, [NotNull] params string[] patterns) + { + Check.NotNull(patterns, nameof(patterns)); + + MatchBehaviour = matchBehaviour; + Patterns = patterns; + } + + public double IsMatch(string input) + { + double match = MatchScores.Mismatch; + + if (input != null) + { + match = MatchScores.ToScore(Patterns.Select(p => IsMatch(input, p))); + } + + return MatchBehaviourHelper.Convert(MatchBehaviour, match); + } + + /// + public string[] GetPatterns() + { + return Patterns; + } + + /// + public abstract string Name { get; } + + protected abstract bool IsMatch(string input, string pattern); + + protected string GetSource(string pattern) + { + return string.Format(Framework, string.Join(";", _usings.Distinct().Select(u => "using " + u)), pattern); + } + } +} \ No newline at end of file diff --git a/src/WireMock.Net/Matchers/CSScriptMatcher.cs b/src/WireMock.Net/Matchers/CSScriptMatcher.cs new file mode 100644 index 000000000..82ef5f194 --- /dev/null +++ b/src/WireMock.Net/Matchers/CSScriptMatcher.cs @@ -0,0 +1,43 @@ +#if USE_CSSCRIPT +#if NET46 +using CSScriptLibrary; +#else +using csscript; +using CSScriptLib; +#endif +using JetBrains.Annotations; + +namespace WireMock.Matchers +{ + internal class CSScriptMatcher : BaseScriptMatcher + { + /// + /// Initializes a new instance of the class. + /// + /// The patterns. + public CSScriptMatcher([NotNull] params string[] patterns) : this(MatchBehaviour.AcceptOnMatch, patterns) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The match behaviour. + /// The patterns. + public CSScriptMatcher(MatchBehaviour matchBehaviour, [NotNull] params string[] patterns) : base(matchBehaviour, patterns) + { + } + + protected override bool IsMatch(string input, string pattern) + { + string source = GetSource(pattern); + + dynamic script = CSScript.Evaluator.CompileCode(source).CreateObject("*"); + return (bool)script.IsMatch(pattern); + } + + /// + public override string Name => "CSScriptMatcher"; + } +} +#endif \ No newline at end of file diff --git a/src/WireMock.Net/Matchers/CSharpCodeMatcher.cs b/src/WireMock.Net/Matchers/CSharpCodeMatcher.cs new file mode 100644 index 000000000..35711a062 --- /dev/null +++ b/src/WireMock.Net/Matchers/CSharpCodeMatcher.cs @@ -0,0 +1,133 @@ +using System.Linq; +using JetBrains.Annotations; +using WireMock.Exceptions; +using WireMock.Validation; + +namespace WireMock.Matchers +{ + internal class CSharpCodeMatcher : IStringMatcher + { + protected const string Framework = "{0}; namespace {1} {{ public class CodeHelper {{ public bool IsMatch(string it) {{ {1} }} }} }}"; + + private readonly string[] _usings = + { + "System", + "System.Linq", + "System.Collections.Generic" + }; + + public MatchBehaviour MatchBehaviour { get; } + + private readonly string[] _patterns; + + /// + /// Initializes a new instance of the class. + /// + /// The patterns. + public CSharpCodeMatcher([NotNull] params string[] patterns) : this(MatchBehaviour.AcceptOnMatch, patterns) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The match behaviour. + /// The patterns. + public CSharpCodeMatcher(MatchBehaviour matchBehaviour, [NotNull] params string[] patterns) + { + Check.NotNull(patterns, nameof(patterns)); + + MatchBehaviour = matchBehaviour; + _patterns = patterns; + } + + public double IsMatch(string input) + { + double match = MatchScores.Mismatch; + + if (input != null) + { + match = MatchScores.ToScore(_patterns.Select(p => IsMatch(input, p))); + } + + return MatchBehaviourHelper.Convert(MatchBehaviour, match); + } + + private bool IsMatch(string input, string pattern) + { + string source = GetSource(pattern); + +#if NET451 || NET452 + var compilerParams = new System.CodeDom.Compiler.CompilerParameters + { + GenerateInMemory = true, + GenerateExecutable = false, + ReferencedAssemblies = + { + "System.dll" + } + }; + + using (var codeProvider = new Microsoft.CSharp.CSharpCodeProvider()) + { + var compilerResults = codeProvider.CompileAssemblyFromSource(compilerParams, source); + + if (compilerResults.Errors.Count != 0) + { + var errors = from object er in compilerResults.Errors select er.ToString(); + throw new WireMockException(string.Join(", ", errors)); + } + + object helper = compilerResults.CompiledAssembly.CreateInstance("WireMock.CodeHelper"); + if (helper == null) + { + throw new WireMockException("Unable to create instance from WireMock.CodeHelper"); + } + + var methodInfo = helper.GetType().GetMethod("IsMatch"); + if (methodInfo == null) + { + throw new WireMockException("Unable to find method 'IsMatch' in WireMock.CodeHelper"); + } + + return (bool)methodInfo.Invoke(helper, new object[] { input }); + } +#elif NET46 || NET461 + try + { + dynamic script = CSScriptLibrary.CSScript.Evaluator.CompileCode(source).CreateObject("*"); + return (bool)script.IsMatch(input); + } + catch + { + throw new WireMockException("Unable to create compile and execute code from WireMock.CodeHelper"); + } +#else + try + { + var assembly = CSScriptLib.CSScript.Evaluator.CompileCode(source); + dynamic script = csscript.GenericExtensions.CreateObject(assembly, "*"); + return (bool)script.IsMatch(input); + } + catch + { + throw new WireMockException("Unable to create compile and execute code from WireMock.CodeHelper"); + } +#endif + } + + private string GetSource(string pattern) + { + return string.Format(Framework, string.Join(";", _usings.Distinct().Select(u => "using " + u)), pattern); + } + + /// + public string[] GetPatterns() + { + return _patterns; + } + + /// + public string Name => "CSharpCodeMatcher"; + } +} \ No newline at end of file diff --git a/src/WireMock.Net/Serialization/MatcherMapper.cs b/src/WireMock.Net/Serialization/MatcherMapper.cs index 331b9fce0..d437ed3ff 100644 --- a/src/WireMock.Net/Serialization/MatcherMapper.cs +++ b/src/WireMock.Net/Serialization/MatcherMapper.cs @@ -1,8 +1,8 @@ -using JetBrains.Annotations; -using SimMetrics.Net; -using System; +using System; using System.Collections.Generic; using System.Linq; +using JetBrains.Annotations; +using SimMetrics.Net; using WireMock.Admin.Mappings; using WireMock.Matchers; @@ -31,6 +31,12 @@ public static IMatcher Map([CanBeNull] MatcherModel matcher) switch (matcherName) { + case "CSharpCodeMatcher": +//#if USE_CSHARPCODEPROVIDER +// return new CSharpCodeMatcher(matchBehaviour, stringPatterns); +//#else + throw new NotSupportedException("The 'CSharpCodeMatcher' cannot be used in netstandard 1.3 or netstandard 2.0"); +//#endif case "LinqMatcher": return new LinqMatcher(matchBehaviour, stringPatterns); diff --git a/src/WireMock.Net/Settings/FluentMockServerSettings.cs b/src/WireMock.Net/Settings/FluentMockServerSettings.cs index 700ea5840..7c94a7c04 100644 --- a/src/WireMock.Net/Settings/FluentMockServerSettings.cs +++ b/src/WireMock.Net/Settings/FluentMockServerSettings.cs @@ -89,5 +89,9 @@ public class FluentMockServerSettings : IFluentMockServerSettings [PublicAPI] [JsonIgnore] public Action HandlebarsRegistrationCallback { get; set; } + + /// + [PublicAPI] + public bool? AllowCSharpCode { get; set; } } } \ No newline at end of file diff --git a/src/WireMock.Net/Settings/IFluentMockServerSettings.cs b/src/WireMock.Net/Settings/IFluentMockServerSettings.cs index 205115edc..7c08264bd 100644 --- a/src/WireMock.Net/Settings/IFluentMockServerSettings.cs +++ b/src/WireMock.Net/Settings/IFluentMockServerSettings.cs @@ -1,6 +1,6 @@ -using HandlebarsDotNet; +using System; +using HandlebarsDotNet; using JetBrains.Annotations; -using System; using WireMock.Handlers; using WireMock.Logging; @@ -115,9 +115,14 @@ public interface IFluentMockServerSettings IFileSystemHandler FileSystemHandler { get; set; } /// - /// Action which can be used to add additional is Handlebar registrations. [Optional] + /// Action which can be used to add additional Handlebars registrations. [Optional] /// [PublicAPI] Action HandlebarsRegistrationCallback { get; set; } + + /// + /// Allow the usage of CSharpCodeMatcher and CSScriptMatcher (default is not allowed). + /// + bool? AllowCSharpCode { get; set; } } } \ No newline at end of file diff --git a/src/WireMock.Net/WireMock.Net.csproj b/src/WireMock.Net/WireMock.Net.csproj index 815e5f4c9..8a19c1994 100644 --- a/src/WireMock.Net/WireMock.Net.csproj +++ b/src/WireMock.Net/WireMock.Net.csproj @@ -3,6 +3,7 @@ Lightweight Http Mocking Server for .Net, inspired by WireMock from the Java landscape. WireMock.Net Stef Heyenrath + net451;net452;net46;net461;netstandard1.3;netstandard2.0 true WireMock.Net @@ -40,11 +41,17 @@ NETSTANDARD;USE_ASPNETCORE + + USE_ASPNETCORE + + USE_ASPNETCORE;NET46 + + @@ -54,6 +61,7 @@ All + @@ -71,10 +79,12 @@ + + @@ -104,10 +114,12 @@ + + @@ -118,7 +130,8 @@ - + + \ No newline at end of file diff --git a/test/WireMock.Net.Tests/Matchers/CSScriptMatcherTests.cs b/test/WireMock.Net.Tests/Matchers/CSScriptMatcherTests.cs new file mode 100644 index 000000000..ec5534241 --- /dev/null +++ b/test/WireMock.Net.Tests/Matchers/CSScriptMatcherTests.cs @@ -0,0 +1,112 @@ +#if !NET452 +using NFluent; +using WireMock.Matchers; +using Xunit; + +namespace WireMock.Net.Tests.Matchers +{ + public class CSScriptMatcherTests + { + [Fact] + public void CSScriptMatcher_For_String_SinglePattern_IsMatch_Positive() + { + // Assign + string input = "x"; + + // Act + var matcher = new CSScriptMatcher("return it == \"x\";"); + + // Assert + Check.That(matcher.IsMatch(input)).IsEqualTo(1.0d); + } + + [Fact] + public void CSScriptMatcher_For_String_IsMatch_Negative() + { + // Assign + string input = "y"; + + // Act + var matcher = new CSScriptMatcher("return it == \"x\";"); + + // Assert + Check.That(matcher.IsMatch(input)).IsEqualTo(0.0d); + } + + [Fact] + public void CSScriptMatcher_For_String_IsMatch_RejectOnMatch() + { + // Assign + string input = "x"; + + // Act + var matcher = new CSScriptMatcher(MatchBehaviour.RejectOnMatch, "return it == \"x\";"); + + // Assert + Check.That(matcher.IsMatch(input)).IsEqualTo(0.0d); + } + + //[Fact] + //public void CSScriptMatcher_For_Object_IsMatch() + //{ + // // Assign + // var input = new + // { + // Id = 9, + // Name = "Test" + // }; + + // // Act + // var matcher = new CSScriptMatcher("Id > 1 AND Name == \"Test\""); + // double match = matcher.IsMatch(input); + + // // Assert + // Assert.Equal(1.0, match); + //} + + //[Fact] + //public void CSScriptMatcher_For_JObject_IsMatch() + //{ + // // Assign + // var input = new JObject + // { + // { "Id", new JValue(9) }, + // { "Name", new JValue("Test") } + // }; + + // // Act + // var matcher = new CSScriptMatcher("Id > 1 AND Name == \"Test\""); + // double match = matcher.IsMatch(input); + + // // Assert + // Assert.Equal(1.0, match); + //} + + [Fact] + public void CSScriptMatcher_GetName() + { + // Assign + var matcher = new CSScriptMatcher("x"); + + // Act + string name = matcher.Name; + + // Assert + Check.That(name).Equals("CSScriptMatcher"); + } + + [Fact] + public void CSScriptMatcher_GetPatterns() + { + // Assign + var matcher = new CSScriptMatcher("x"); + + // Act + string[] patterns = matcher.GetPatterns(); + + // Assert + Check.That(patterns).ContainsExactly("x"); + } + } +} +#endif \ No newline at end of file diff --git a/test/WireMock.Net.Tests/Matchers/CSharpCodeMatcherTests.cs b/test/WireMock.Net.Tests/Matchers/CSharpCodeMatcherTests.cs new file mode 100644 index 000000000..f67787f61 --- /dev/null +++ b/test/WireMock.Net.Tests/Matchers/CSharpCodeMatcherTests.cs @@ -0,0 +1,112 @@ +//#if NET452 +using NFluent; +using WireMock.Matchers; +using Xunit; + +namespace WireMock.Net.Tests.Matchers +{ + public class CSharpCodeMatcherTests + { + [Fact] + public void CSharpCodeMatcher_For_String_SinglePattern_IsMatch_Positive() + { + // Assign + string input = "x"; + + // Act + var matcher = new CSharpCodeMatcher("return it == \"x\";"); + + // Assert + Check.That(matcher.IsMatch(input)).IsEqualTo(1.0d); + } + + [Fact] + public void CSharpCodeMatcher_For_String_IsMatch_Negative() + { + // Assign + string input = "y"; + + // Act + var matcher = new CSharpCodeMatcher("return it == \"x\";"); + + // Assert + Check.That(matcher.IsMatch(input)).IsEqualTo(0.0d); + } + + [Fact] + public void CSharpCodeMatcher_For_String_IsMatch_RejectOnMatch() + { + // Assign + string input = "x"; + + // Act + var matcher = new CSharpCodeMatcher(MatchBehaviour.RejectOnMatch, "return it == \"x\";"); + + // Assert + Check.That(matcher.IsMatch(input)).IsEqualTo(0.0d); + } + + //[Fact] + //public void CSharpCodeMatcher_For_Object_IsMatch() + //{ + // // Assign + // var input = new + // { + // Id = 9, + // Name = "Test" + // }; + + // // Act + // var matcher = new CSharpCodeMatcher("Id > 1 AND Name == \"Test\""); + // double match = matcher.IsMatch(input); + + // // Assert + // Assert.Equal(1.0, match); + //} + + //[Fact] + //public void CSharpCodeMatcher_For_JObject_IsMatch() + //{ + // // Assign + // var input = new JObject + // { + // { "Id", new JValue(9) }, + // { "Name", new JValue("Test") } + // }; + + // // Act + // var matcher = new CSharpCodeMatcher("Id > 1 AND Name == \"Test\""); + // double match = matcher.IsMatch(input); + + // // Assert + // Assert.Equal(1.0, match); + //} + + [Fact] + public void CSharpCodeMatcher_GetName() + { + // Assign + var matcher = new CSharpCodeMatcher("x"); + + // Act + string name = matcher.Name; + + // Assert + Check.That(name).Equals("CSharpCodeMatcher"); + } + + [Fact] + public void CSharpCodeMatcher_GetPatterns() + { + // Assign + var matcher = new CSharpCodeMatcher("x"); + + // Act + string[] patterns = matcher.GetPatterns(); + + // Assert + Check.That(patterns).ContainsExactly("x"); + } + } +} +//#endif \ No newline at end of file diff --git a/test/WireMock.Net.Tests/WireMock.Net.Tests.csproj b/test/WireMock.Net.Tests/WireMock.Net.Tests.csproj index 26b90ea4e..7639edebc 100644 --- a/test/WireMock.Net.Tests/WireMock.Net.Tests.csproj +++ b/test/WireMock.Net.Tests/WireMock.Net.Tests.csproj @@ -23,6 +23,10 @@ + + + + From 4889528024f4a1ad6f3b3a98c7d25d955cb37df7 Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Sat, 17 Aug 2019 16:30:51 +0200 Subject: [PATCH 02/13] fix --- .../Matchers/BaseScriptMatcher.cs | 71 ------------------- src/WireMock.Net/Matchers/CSScriptMatcher.cs | 43 ----------- .../Matchers/CSharpCodeMatcher.cs | 25 ++++--- src/WireMock.Net/WireMock.Net.csproj | 6 +- 4 files changed, 17 insertions(+), 128 deletions(-) delete mode 100644 src/WireMock.Net/Matchers/BaseScriptMatcher.cs delete mode 100644 src/WireMock.Net/Matchers/CSScriptMatcher.cs diff --git a/src/WireMock.Net/Matchers/BaseScriptMatcher.cs b/src/WireMock.Net/Matchers/BaseScriptMatcher.cs deleted file mode 100644 index 16d5e3bc1..000000000 --- a/src/WireMock.Net/Matchers/BaseScriptMatcher.cs +++ /dev/null @@ -1,71 +0,0 @@ -using System.Linq; -using JetBrains.Annotations; -using WireMock.Validation; - -namespace WireMock.Matchers -{ - internal abstract class BaseScriptMatcher : IStringMatcher - { - protected const string Framework = "{0}; namespace {1} {{ public class CodeHelper {{ public bool IsMatch(string it) {{ {1} }} }} }}"; - - private readonly string[] _usings = - { - "System", - "System.Linq", - "System.Collections.Generic" - }; - - public MatchBehaviour MatchBehaviour { get; } - - protected readonly string[] Patterns; - - /// - /// Initializes a new instance of the class. - /// - /// The patterns. - protected BaseScriptMatcher([NotNull] params string[] patterns) : this(MatchBehaviour.AcceptOnMatch, patterns) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The match behaviour. - /// The patterns. - protected BaseScriptMatcher(MatchBehaviour matchBehaviour, [NotNull] params string[] patterns) - { - Check.NotNull(patterns, nameof(patterns)); - - MatchBehaviour = matchBehaviour; - Patterns = patterns; - } - - public double IsMatch(string input) - { - double match = MatchScores.Mismatch; - - if (input != null) - { - match = MatchScores.ToScore(Patterns.Select(p => IsMatch(input, p))); - } - - return MatchBehaviourHelper.Convert(MatchBehaviour, match); - } - - /// - public string[] GetPatterns() - { - return Patterns; - } - - /// - public abstract string Name { get; } - - protected abstract bool IsMatch(string input, string pattern); - - protected string GetSource(string pattern) - { - return string.Format(Framework, string.Join(";", _usings.Distinct().Select(u => "using " + u)), pattern); - } - } -} \ No newline at end of file diff --git a/src/WireMock.Net/Matchers/CSScriptMatcher.cs b/src/WireMock.Net/Matchers/CSScriptMatcher.cs deleted file mode 100644 index 82ef5f194..000000000 --- a/src/WireMock.Net/Matchers/CSScriptMatcher.cs +++ /dev/null @@ -1,43 +0,0 @@ -#if USE_CSSCRIPT -#if NET46 -using CSScriptLibrary; -#else -using csscript; -using CSScriptLib; -#endif -using JetBrains.Annotations; - -namespace WireMock.Matchers -{ - internal class CSScriptMatcher : BaseScriptMatcher - { - /// - /// Initializes a new instance of the class. - /// - /// The patterns. - public CSScriptMatcher([NotNull] params string[] patterns) : this(MatchBehaviour.AcceptOnMatch, patterns) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The match behaviour. - /// The patterns. - public CSScriptMatcher(MatchBehaviour matchBehaviour, [NotNull] params string[] patterns) : base(matchBehaviour, patterns) - { - } - - protected override bool IsMatch(string input, string pattern) - { - string source = GetSource(pattern); - - dynamic script = CSScript.Evaluator.CompileCode(source).CreateObject("*"); - return (bool)script.IsMatch(pattern); - } - - /// - public override string Name => "CSScriptMatcher"; - } -} -#endif \ No newline at end of file diff --git a/src/WireMock.Net/Matchers/CSharpCodeMatcher.cs b/src/WireMock.Net/Matchers/CSharpCodeMatcher.cs index 35711a062..ffc6c788a 100644 --- a/src/WireMock.Net/Matchers/CSharpCodeMatcher.cs +++ b/src/WireMock.Net/Matchers/CSharpCodeMatcher.cs @@ -1,4 +1,5 @@ -using System.Linq; +using System; +using System.Linq; using JetBrains.Annotations; using WireMock.Exceptions; using WireMock.Validation; @@ -7,7 +8,8 @@ namespace WireMock.Matchers { internal class CSharpCodeMatcher : IStringMatcher { - protected const string Framework = "{0}; namespace {1} {{ public class CodeHelper {{ public bool IsMatch(string it) {{ {1} }} }} }}"; + // protected const string Framework = "{0}; namespace WireMock {{ public class CodeHelper {{ public bool IsMatch(string it) {{ {1} }} }} }}"; + protected const string Framework = "{0} public class CodeHelper {{ public bool IsMatch(string it) {{ {1} }} }}"; private readonly string[] _usings = { @@ -64,7 +66,8 @@ private bool IsMatch(string input, string pattern) GenerateExecutable = false, ReferencedAssemblies = { - "System.dll" + "System.dll", + "System.Core.dll" } }; @@ -78,16 +81,16 @@ private bool IsMatch(string input, string pattern) throw new WireMockException(string.Join(", ", errors)); } - object helper = compilerResults.CompiledAssembly.CreateInstance("WireMock.CodeHelper"); + object helper = compilerResults.CompiledAssembly.CreateInstance("CodeHelper"); if (helper == null) { - throw new WireMockException("Unable to create instance from WireMock.CodeHelper"); + throw new WireMockException("Unable to create instance from CodeHelper"); } var methodInfo = helper.GetType().GetMethod("IsMatch"); if (methodInfo == null) { - throw new WireMockException("Unable to find method 'IsMatch' in WireMock.CodeHelper"); + throw new WireMockException("Unable to find method 'IsMatch' in CodeHelper"); } return (bool)methodInfo.Invoke(helper, new object[] { input }); @@ -102,23 +105,25 @@ private bool IsMatch(string input, string pattern) { throw new WireMockException("Unable to create compile and execute code from WireMock.CodeHelper"); } -#else +#elif NETSTANDARD2_0 try { var assembly = CSScriptLib.CSScript.Evaluator.CompileCode(source); dynamic script = csscript.GenericExtensions.CreateObject(assembly, "*"); return (bool)script.IsMatch(input); } - catch + catch (Exception ex) { - throw new WireMockException("Unable to create compile and execute code from WireMock.CodeHelper"); + throw new WireMockException("Unable to create compile and execute code from WireMock.CodeHelper", ex); } +#else + throw new NotSupportedException("The 'CSharpCodeMatcher' cannot be used in netstandard 1.3"); #endif } private string GetSource(string pattern) { - return string.Format(Framework, string.Join(";", _usings.Distinct().Select(u => "using " + u)), pattern); + return string.Format(Framework, string.Join(Environment.NewLine, _usings.Select(u => $"using {u};")), pattern); } /// diff --git a/src/WireMock.Net/WireMock.Net.csproj b/src/WireMock.Net/WireMock.Net.csproj index 8a19c1994..246a0e7c7 100644 --- a/src/WireMock.Net/WireMock.Net.csproj +++ b/src/WireMock.Net/WireMock.Net.csproj @@ -50,8 +50,6 @@ - - @@ -79,12 +77,10 @@ - - @@ -97,6 +93,7 @@ + @@ -105,6 +102,7 @@ + From bafb2a1c27aa2247697620bf1cd646019f920ea7 Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Sat, 17 Aug 2019 17:12:33 +0200 Subject: [PATCH 03/13] . --- src/WireMock.Net/Matchers/CSharpCodeMatcher.cs | 5 ++--- src/WireMock.Net/Serialization/MatcherMapper.cs | 7 ++----- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/WireMock.Net/Matchers/CSharpCodeMatcher.cs b/src/WireMock.Net/Matchers/CSharpCodeMatcher.cs index ffc6c788a..4c03a0813 100644 --- a/src/WireMock.Net/Matchers/CSharpCodeMatcher.cs +++ b/src/WireMock.Net/Matchers/CSharpCodeMatcher.cs @@ -8,8 +8,7 @@ namespace WireMock.Matchers { internal class CSharpCodeMatcher : IStringMatcher { - // protected const string Framework = "{0}; namespace WireMock {{ public class CodeHelper {{ public bool IsMatch(string it) {{ {1} }} }} }}"; - protected const string Framework = "{0} public class CodeHelper {{ public bool IsMatch(string it) {{ {1} }} }}"; + private const string Framework = "{0} public class CodeHelper {{ public bool IsMatch(string it) {{ {1} }} }}"; private readonly string[] _usings = { @@ -49,7 +48,7 @@ public double IsMatch(string input) if (input != null) { - match = MatchScores.ToScore(_patterns.Select(p => IsMatch(input, p))); + match = MatchScores.ToScore(_patterns.Select(pattern => IsMatch(input, pattern))); } return MatchBehaviourHelper.Convert(MatchBehaviour, match); diff --git a/src/WireMock.Net/Serialization/MatcherMapper.cs b/src/WireMock.Net/Serialization/MatcherMapper.cs index d437ed3ff..4bbd4b80d 100644 --- a/src/WireMock.Net/Serialization/MatcherMapper.cs +++ b/src/WireMock.Net/Serialization/MatcherMapper.cs @@ -32,11 +32,8 @@ public static IMatcher Map([CanBeNull] MatcherModel matcher) switch (matcherName) { case "CSharpCodeMatcher": -//#if USE_CSHARPCODEPROVIDER -// return new CSharpCodeMatcher(matchBehaviour, stringPatterns); -//#else - throw new NotSupportedException("The 'CSharpCodeMatcher' cannot be used in netstandard 1.3 or netstandard 2.0"); -//#endif + return new CSharpCodeMatcher(matchBehaviour, stringPatterns); + case "LinqMatcher": return new LinqMatcher(matchBehaviour, stringPatterns); From d23694d6d371f87f0a34618f5b7a045dc0a98480 Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Sat, 17 Aug 2019 17:59:31 +0200 Subject: [PATCH 04/13] windows-2019 --- azure-pipelines.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 4c58c934e..fe91460e5 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -1,5 +1,5 @@ pool: - vmImage: 'vs2017-win2016' + vmImage: 'windows-2019' variables: Prerelease: 'ci' From 772b898a6513753e1db84bb7a39c926127b2194b Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Sat, 17 Aug 2019 18:14:35 +0200 Subject: [PATCH 05/13] --- src/WireMock.Net/WireMock.Net.csproj | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/WireMock.Net/WireMock.Net.csproj b/src/WireMock.Net/WireMock.Net.csproj index 246a0e7c7..6cb396b75 100644 --- a/src/WireMock.Net/WireMock.Net.csproj +++ b/src/WireMock.Net/WireMock.Net.csproj @@ -32,6 +32,8 @@ true + + $(MSBuildProjectDirectory)=/ true From 71d479ed6e783f88de3b1710643dcc5e7264cba5 Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Sat, 17 Aug 2019 18:23:11 +0200 Subject: [PATCH 06/13] --- src/WireMock.Net/WireMock.Net.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/src/WireMock.Net/WireMock.Net.csproj b/src/WireMock.Net/WireMock.Net.csproj index 6cb396b75..14516b8e7 100644 --- a/src/WireMock.Net/WireMock.Net.csproj +++ b/src/WireMock.Net/WireMock.Net.csproj @@ -32,6 +32,7 @@ true + From 5376c9b82cd3ee398b4b1bcf94738e60a0454c22 Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Sat, 17 Aug 2019 19:19:34 +0200 Subject: [PATCH 07/13] AllowCSharpCodeMatcher --- examples/WireMock.Net.Console.Net452.Classic/MainApp.cs | 1 + src/WireMock.Net.StandAlone/StandAloneApp.cs | 5 +++-- src/WireMock.Net/Serialization/MatcherMapper.cs | 7 ++++++- src/WireMock.Net/Settings/FluentMockServerSettings.cs | 4 ++-- src/WireMock.Net/Settings/IFluentMockServerSettings.cs | 4 ++-- 5 files changed, 14 insertions(+), 7 deletions(-) diff --git a/examples/WireMock.Net.Console.Net452.Classic/MainApp.cs b/examples/WireMock.Net.Console.Net452.Classic/MainApp.cs index 8ea9ded1f..bf75786f3 100644 --- a/examples/WireMock.Net.Console.Net452.Classic/MainApp.cs +++ b/examples/WireMock.Net.Console.Net452.Classic/MainApp.cs @@ -21,6 +21,7 @@ public static void Run() var server = FluentMockServer.Start(new FluentMockServerSettings { + AllowCSharpCodeMatcher = true, Urls = new[] { url1, url2, url3 }, StartAdminInterface = true, ReadStaticMappings = true, diff --git a/src/WireMock.Net.StandAlone/StandAloneApp.cs b/src/WireMock.Net.StandAlone/StandAloneApp.cs index 17105b75d..d215767de 100644 --- a/src/WireMock.Net.StandAlone/StandAloneApp.cs +++ b/src/WireMock.Net.StandAlone/StandAloneApp.cs @@ -46,11 +46,12 @@ public static FluentMockServer Start([NotNull] string[] args, [CanBeNull] IWireM StartAdminInterface = parser.GetBoolValue("StartAdminInterface", true), ReadStaticMappings = parser.GetBoolValue("ReadStaticMappings"), WatchStaticMappings = parser.GetBoolValue("WatchStaticMappings"), - AllowPartialMapping = parser.GetBoolValue("AllowPartialMapping", false), + AllowPartialMapping = parser.GetBoolValue("AllowPartialMapping"), AdminUsername = parser.GetStringValue("AdminUsername"), AdminPassword = parser.GetStringValue("AdminPassword"), MaxRequestLogCount = parser.GetIntValue("MaxRequestLogCount"), - RequestLogExpirationDuration = parser.GetIntValue("RequestLogExpirationDuration") + RequestLogExpirationDuration = parser.GetIntValue("RequestLogExpirationDuration"), + AllowCSharpCodeMatcher = parser.GetBoolValue("AllowCSharpCodeMatcher"), }; if (logger != null) diff --git a/src/WireMock.Net/Serialization/MatcherMapper.cs b/src/WireMock.Net/Serialization/MatcherMapper.cs index 2463db2c2..855a79b87 100644 --- a/src/WireMock.Net/Serialization/MatcherMapper.cs +++ b/src/WireMock.Net/Serialization/MatcherMapper.cs @@ -42,7 +42,12 @@ public IMatcher Map([CanBeNull] MatcherModel matcher) switch (matcherName) { case "CSharpCodeMatcher": - return new CSharpCodeMatcher(matchBehaviour, stringPatterns); + if (_settings.AllowCSharpCodeMatcher == true) + { + return new CSharpCodeMatcher(matchBehaviour, stringPatterns); + } + + throw new NotSupportedException("It's not allowed to use the 'CSharpCodeMatcher' because FluentMockServerSettings.AllowCSharpCodeMatcher is not set to 'true'."); case "LinqMatcher": return new LinqMatcher(matchBehaviour, stringPatterns); diff --git a/src/WireMock.Net/Settings/FluentMockServerSettings.cs b/src/WireMock.Net/Settings/FluentMockServerSettings.cs index 7c94a7c04..d4b71be09 100644 --- a/src/WireMock.Net/Settings/FluentMockServerSettings.cs +++ b/src/WireMock.Net/Settings/FluentMockServerSettings.cs @@ -90,8 +90,8 @@ public class FluentMockServerSettings : IFluentMockServerSettings [JsonIgnore] public Action HandlebarsRegistrationCallback { get; set; } - /// + /// [PublicAPI] - public bool? AllowCSharpCode { get; set; } + public bool? AllowCSharpCodeMatcher { get; set; } } } \ No newline at end of file diff --git a/src/WireMock.Net/Settings/IFluentMockServerSettings.cs b/src/WireMock.Net/Settings/IFluentMockServerSettings.cs index 7c08264bd..240a4e4b9 100644 --- a/src/WireMock.Net/Settings/IFluentMockServerSettings.cs +++ b/src/WireMock.Net/Settings/IFluentMockServerSettings.cs @@ -121,8 +121,8 @@ public interface IFluentMockServerSettings Action HandlebarsRegistrationCallback { get; set; } /// - /// Allow the usage of CSharpCodeMatcher and CSScriptMatcher (default is not allowed). + /// Allow the usage of CSharpCodeMatcher (default is not allowed). /// - bool? AllowCSharpCode { get; set; } + bool? AllowCSharpCodeMatcher { get; set; } } } \ No newline at end of file From 947ed8bf68a177646df87a40a1f53c7d794f0f99 Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Sat, 17 Aug 2019 23:20:57 +0200 Subject: [PATCH 08/13] CSharpCodeMatcher : IObjectMatcher --- .../Matchers/CSharpCodeMatcher.cs | 48 +++++--- src/WireMock.Net/Matchers/LinqMatcher.cs | 2 +- .../Matchers/CSScriptMatcherTests.cs | 112 ------------------ .../Matchers/CSharpCodeMatcherTests.cs | 58 +++------ 4 files changed, 54 insertions(+), 166 deletions(-) delete mode 100644 test/WireMock.Net.Tests/Matchers/CSScriptMatcherTests.cs diff --git a/src/WireMock.Net/Matchers/CSharpCodeMatcher.cs b/src/WireMock.Net/Matchers/CSharpCodeMatcher.cs index 4c03a0813..d890e476f 100644 --- a/src/WireMock.Net/Matchers/CSharpCodeMatcher.cs +++ b/src/WireMock.Net/Matchers/CSharpCodeMatcher.cs @@ -1,20 +1,25 @@ using System; using System.Linq; using JetBrains.Annotations; +using Newtonsoft.Json.Linq; using WireMock.Exceptions; using WireMock.Validation; namespace WireMock.Matchers { - internal class CSharpCodeMatcher : IStringMatcher + internal class CSharpCodeMatcher : IObjectMatcher { - private const string Framework = "{0} public class CodeHelper {{ public bool IsMatch(string it) {{ {1} }} }}"; + private const string TemplateForIsMatchWithString = "{0} public class CodeHelper {{ public bool IsMatch(string it) {{ {1} }} }}"; + + private const string TemplateForIsMatchWithJObject = "{0} public class CodeHelper {{ public bool IsMatch(dynamic it) {{ {1} }} }}"; private readonly string[] _usings = { "System", "System.Linq", - "System.Collections.Generic" + "System.Collections.Generic", + "Microsoft.CSharp", + "Newtonsoft.Json.Linq" }; public MatchBehaviour MatchBehaviour { get; } @@ -43,6 +48,16 @@ public CSharpCodeMatcher(MatchBehaviour matchBehaviour, [NotNull] params string[ } public double IsMatch(string input) + { + return IsMatchInternal(input); + } + + public double IsMatch(object input) + { + return IsMatchInternal(input); + } + + public double IsMatchInternal(object input) { double match = MatchScores.Mismatch; @@ -54,9 +69,11 @@ public double IsMatch(string input) return MatchBehaviourHelper.Convert(MatchBehaviour, match); } - private bool IsMatch(string input, string pattern) + private bool IsMatch(dynamic input, string pattern) { - string source = GetSource(pattern); + bool isMatchWithString = input is string; + var inputValue = isMatchWithString ? input : JObject.FromObject(input); + string source = GetSourceForIsMatchWithString(pattern, isMatchWithString); #if NET451 || NET452 var compilerParams = new System.CodeDom.Compiler.CompilerParameters @@ -66,7 +83,9 @@ private bool IsMatch(string input, string pattern) ReferencedAssemblies = { "System.dll", - "System.Core.dll" + "System.Core.dll", + "Microsoft.CSharp.dll", + "Newtonsoft.Json.dll" } }; @@ -76,7 +95,7 @@ private bool IsMatch(string input, string pattern) if (compilerResults.Errors.Count != 0) { - var errors = from object er in compilerResults.Errors select er.ToString(); + var errors = from System.CodeDom.Compiler.CompilerError er in compilerResults.Errors select er.ToString(); throw new WireMockException(string.Join(", ", errors)); } @@ -91,14 +110,14 @@ private bool IsMatch(string input, string pattern) { throw new WireMockException("Unable to find method 'IsMatch' in CodeHelper"); } - - return (bool)methodInfo.Invoke(helper, new object[] { input }); + + return (bool)methodInfo.Invoke(helper, new[] { inputValue }); } #elif NET46 || NET461 try { dynamic script = CSScriptLibrary.CSScript.Evaluator.CompileCode(source).CreateObject("*"); - return (bool)script.IsMatch(input); + return (bool)script.IsMatch(inputValue); } catch { @@ -109,20 +128,21 @@ private bool IsMatch(string input, string pattern) { var assembly = CSScriptLib.CSScript.Evaluator.CompileCode(source); dynamic script = csscript.GenericExtensions.CreateObject(assembly, "*"); - return (bool)script.IsMatch(input); + return (bool)script.IsMatch(inputValue); } catch (Exception ex) { - throw new WireMockException("Unable to create compile and execute code from WireMock.CodeHelper", ex); + throw new WireMockException("Unable to create compiler and execute code for WireMock.CodeHelper", ex); } #else throw new NotSupportedException("The 'CSharpCodeMatcher' cannot be used in netstandard 1.3"); #endif } - private string GetSource(string pattern) + private string GetSourceForIsMatchWithString(string pattern, bool isMatchWithString) { - return string.Format(Framework, string.Join(Environment.NewLine, _usings.Select(u => $"using {u};")), pattern); + string template = isMatchWithString ? TemplateForIsMatchWithString : TemplateForIsMatchWithJObject; + return string.Format(template, string.Join(Environment.NewLine, _usings.Select(u => $"using {u};")), pattern); } /// diff --git a/src/WireMock.Net/Matchers/LinqMatcher.cs b/src/WireMock.Net/Matchers/LinqMatcher.cs index c7e90f6db..f27a96ad5 100644 --- a/src/WireMock.Net/Matchers/LinqMatcher.cs +++ b/src/WireMock.Net/Matchers/LinqMatcher.cs @@ -10,7 +10,7 @@ namespace WireMock.Matchers /// System.Linq.Dynamic.Core Expression Matcher /// /// - public class LinqMatcher : IStringMatcher + public class LinqMatcher : IObjectMatcher { private readonly string[] _patterns; diff --git a/test/WireMock.Net.Tests/Matchers/CSScriptMatcherTests.cs b/test/WireMock.Net.Tests/Matchers/CSScriptMatcherTests.cs deleted file mode 100644 index ec5534241..000000000 --- a/test/WireMock.Net.Tests/Matchers/CSScriptMatcherTests.cs +++ /dev/null @@ -1,112 +0,0 @@ -#if !NET452 -using NFluent; -using WireMock.Matchers; -using Xunit; - -namespace WireMock.Net.Tests.Matchers -{ - public class CSScriptMatcherTests - { - [Fact] - public void CSScriptMatcher_For_String_SinglePattern_IsMatch_Positive() - { - // Assign - string input = "x"; - - // Act - var matcher = new CSScriptMatcher("return it == \"x\";"); - - // Assert - Check.That(matcher.IsMatch(input)).IsEqualTo(1.0d); - } - - [Fact] - public void CSScriptMatcher_For_String_IsMatch_Negative() - { - // Assign - string input = "y"; - - // Act - var matcher = new CSScriptMatcher("return it == \"x\";"); - - // Assert - Check.That(matcher.IsMatch(input)).IsEqualTo(0.0d); - } - - [Fact] - public void CSScriptMatcher_For_String_IsMatch_RejectOnMatch() - { - // Assign - string input = "x"; - - // Act - var matcher = new CSScriptMatcher(MatchBehaviour.RejectOnMatch, "return it == \"x\";"); - - // Assert - Check.That(matcher.IsMatch(input)).IsEqualTo(0.0d); - } - - //[Fact] - //public void CSScriptMatcher_For_Object_IsMatch() - //{ - // // Assign - // var input = new - // { - // Id = 9, - // Name = "Test" - // }; - - // // Act - // var matcher = new CSScriptMatcher("Id > 1 AND Name == \"Test\""); - // double match = matcher.IsMatch(input); - - // // Assert - // Assert.Equal(1.0, match); - //} - - //[Fact] - //public void CSScriptMatcher_For_JObject_IsMatch() - //{ - // // Assign - // var input = new JObject - // { - // { "Id", new JValue(9) }, - // { "Name", new JValue("Test") } - // }; - - // // Act - // var matcher = new CSScriptMatcher("Id > 1 AND Name == \"Test\""); - // double match = matcher.IsMatch(input); - - // // Assert - // Assert.Equal(1.0, match); - //} - - [Fact] - public void CSScriptMatcher_GetName() - { - // Assign - var matcher = new CSScriptMatcher("x"); - - // Act - string name = matcher.Name; - - // Assert - Check.That(name).Equals("CSScriptMatcher"); - } - - [Fact] - public void CSScriptMatcher_GetPatterns() - { - // Assign - var matcher = new CSScriptMatcher("x"); - - // Act - string[] patterns = matcher.GetPatterns(); - - // Assert - Check.That(patterns).ContainsExactly("x"); - } - } -} -#endif \ No newline at end of file diff --git a/test/WireMock.Net.Tests/Matchers/CSharpCodeMatcherTests.cs b/test/WireMock.Net.Tests/Matchers/CSharpCodeMatcherTests.cs index f67787f61..1519736f9 100644 --- a/test/WireMock.Net.Tests/Matchers/CSharpCodeMatcherTests.cs +++ b/test/WireMock.Net.Tests/Matchers/CSharpCodeMatcherTests.cs @@ -1,5 +1,4 @@ -//#if NET452 -using NFluent; +using NFluent; using WireMock.Matchers; using Xunit; @@ -46,41 +45,23 @@ public void CSharpCodeMatcher_For_String_IsMatch_RejectOnMatch() Check.That(matcher.IsMatch(input)).IsEqualTo(0.0d); } - //[Fact] - //public void CSharpCodeMatcher_For_Object_IsMatch() - //{ - // // Assign - // var input = new - // { - // Id = 9, - // Name = "Test" - // }; - - // // Act - // var matcher = new CSharpCodeMatcher("Id > 1 AND Name == \"Test\""); - // double match = matcher.IsMatch(input); - - // // Assert - // Assert.Equal(1.0, match); - //} - - //[Fact] - //public void CSharpCodeMatcher_For_JObject_IsMatch() - //{ - // // Assign - // var input = new JObject - // { - // { "Id", new JValue(9) }, - // { "Name", new JValue("Test") } - // }; - - // // Act - // var matcher = new CSharpCodeMatcher("Id > 1 AND Name == \"Test\""); - // double match = matcher.IsMatch(input); - - // // Assert - // Assert.Equal(1.0, match); - //} + [Fact] + public void CSharpCodeMatcher_For_Object_IsMatch() + { + // Assign + var input = new + { + Id = 9, + Name = "Test" + }; + + // Act + var matcher = new CSharpCodeMatcher("return it.Id > 1 && it.Name == \"Test\";"); + double match = matcher.IsMatch(input); + + // Assert + Assert.Equal(1.0, match); + } [Fact] public void CSharpCodeMatcher_GetName() @@ -108,5 +89,4 @@ public void CSharpCodeMatcher_GetPatterns() Check.That(patterns).ContainsExactly("x"); } } -} -//#endif \ No newline at end of file +} \ No newline at end of file From 80f76e27f0b9c2bef3d7326b05bf87744b56fdf8 Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Sat, 17 Aug 2019 23:21:22 +0200 Subject: [PATCH 09/13] TemplateForIsMatchWithDynamic --- src/WireMock.Net/Matchers/CSharpCodeMatcher.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/WireMock.Net/Matchers/CSharpCodeMatcher.cs b/src/WireMock.Net/Matchers/CSharpCodeMatcher.cs index d890e476f..1d4d5bfc3 100644 --- a/src/WireMock.Net/Matchers/CSharpCodeMatcher.cs +++ b/src/WireMock.Net/Matchers/CSharpCodeMatcher.cs @@ -11,7 +11,7 @@ internal class CSharpCodeMatcher : IObjectMatcher { private const string TemplateForIsMatchWithString = "{0} public class CodeHelper {{ public bool IsMatch(string it) {{ {1} }} }}"; - private const string TemplateForIsMatchWithJObject = "{0} public class CodeHelper {{ public bool IsMatch(dynamic it) {{ {1} }} }}"; + private const string TemplateForIsMatchWithDynamic = "{0} public class CodeHelper {{ public bool IsMatch(dynamic it) {{ {1} }} }}"; private readonly string[] _usings = { @@ -141,7 +141,7 @@ private bool IsMatch(dynamic input, string pattern) private string GetSourceForIsMatchWithString(string pattern, bool isMatchWithString) { - string template = isMatchWithString ? TemplateForIsMatchWithString : TemplateForIsMatchWithJObject; + string template = isMatchWithString ? TemplateForIsMatchWithString : TemplateForIsMatchWithDynamic; return string.Format(template, string.Join(Environment.NewLine, _usings.Select(u => $"using {u};")), pattern); } From aed835dc5e1b70d618951aabfdc17951caf3c1a7 Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Sun, 18 Aug 2019 17:04:24 +0200 Subject: [PATCH 10/13] RequestMessageBodyMatcher_GetMatchingScore_BodyAsJson_CSharpCodeMatcher --- .../RequestMessageBodyMatcherTests.cs | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/test/WireMock.Net.Tests/RequestMatchers/RequestMessageBodyMatcherTests.cs b/test/WireMock.Net.Tests/RequestMatchers/RequestMessageBodyMatcherTests.cs index f21730240..d213b8f79 100644 --- a/test/WireMock.Net.Tests/RequestMatchers/RequestMessageBodyMatcherTests.cs +++ b/test/WireMock.Net.Tests/RequestMatchers/RequestMessageBodyMatcherTests.cs @@ -182,6 +182,28 @@ public void RequestMessageBodyMatcher_GetMatchingScore_BodyAsJson_IObjectMatcher objectMatcherMock.Verify(m => m.IsMatch(42), Times.Once); } + [Fact] + public void RequestMessageBodyMatcher_GetMatchingScore_BodyAsJson_CSharpCodeMatcher() + { + // Assign + var body = new BodyData + { + BodyAsJson = new { value = 42 }, + DetectedBodyType = BodyType.Json + }; + + var requestMessage = new RequestMessage(new UrlDetails("http://localhost"), "GET", "127.0.0.1", body); + + var matcher = new RequestMessageBodyMatcher(new CSharpCodeMatcher(MatchBehaviour.AcceptOnMatch, "return it.value == 42;")); + + // Act + var result = new RequestMatchResult(); + double score = matcher.GetMatchingScore(requestMessage, result); + + // Assert + Check.That(score).IsEqualTo(1.0d); + } + [Fact] public void RequestMessageBodyMatcher_GetMatchingScore_BodyAsBytes_IObjectMatcher() { From f8497ff322d6f660aecdf2a69ea78be8573da846 Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Mon, 19 Aug 2019 19:03:06 +0200 Subject: [PATCH 11/13] fix --- src/WireMock.Net/Matchers/CSharpCodeMatcher.cs | 9 +++++++-- src/WireMock.Net/Matchers/LinqMatcher.cs | 3 ++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/WireMock.Net/Matchers/CSharpCodeMatcher.cs b/src/WireMock.Net/Matchers/CSharpCodeMatcher.cs index 1d4d5bfc3..7b4b25bc5 100644 --- a/src/WireMock.Net/Matchers/CSharpCodeMatcher.cs +++ b/src/WireMock.Net/Matchers/CSharpCodeMatcher.cs @@ -7,7 +7,12 @@ namespace WireMock.Matchers { - internal class CSharpCodeMatcher : IObjectMatcher + /// + /// CSharpCode / CS-Script Matcher + /// + /// + /// + internal class CSharpCodeMatcher : IObjectMatcher, IStringMatcher { private const string TemplateForIsMatchWithString = "{0} public class CodeHelper {{ public bool IsMatch(string it) {{ {1} }} }}"; @@ -110,7 +115,7 @@ private bool IsMatch(dynamic input, string pattern) { throw new WireMockException("Unable to find method 'IsMatch' in CodeHelper"); } - + return (bool)methodInfo.Invoke(helper, new[] { inputValue }); } #elif NET46 || NET461 diff --git a/src/WireMock.Net/Matchers/LinqMatcher.cs b/src/WireMock.Net/Matchers/LinqMatcher.cs index f27a96ad5..be9e7ddda 100644 --- a/src/WireMock.Net/Matchers/LinqMatcher.cs +++ b/src/WireMock.Net/Matchers/LinqMatcher.cs @@ -9,8 +9,9 @@ namespace WireMock.Matchers /// /// System.Linq.Dynamic.Core Expression Matcher /// + /// /// - public class LinqMatcher : IObjectMatcher + public class LinqMatcher : IObjectMatcher, IStringMatcher { private readonly string[] _patterns; From a78093d9364a83f1bf9ce0e28f9a41454d866f68 Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Mon, 19 Aug 2019 19:55:09 +0200 Subject: [PATCH 12/13] } --- src/WireMock.Net/Matchers/LinqMatcher.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/WireMock.Net/Matchers/LinqMatcher.cs b/src/WireMock.Net/Matchers/LinqMatcher.cs index be9e7ddda..500cbe4e5 100644 --- a/src/WireMock.Net/Matchers/LinqMatcher.cs +++ b/src/WireMock.Net/Matchers/LinqMatcher.cs @@ -118,7 +118,6 @@ public double IsMatch(object input) } return MatchBehaviourHelper.Convert(MatchBehaviour, match); - } /// From 992d349ad2a0ca41f53e1cdae28adcdc831cd458 Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Thu, 29 Aug 2019 10:46:17 +0200 Subject: [PATCH 13/13] Better Exception Handling --- .../Matchers/CSharpCodeMatcher.cs | 53 +++++++++++++++---- 1 file changed, 44 insertions(+), 9 deletions(-) diff --git a/src/WireMock.Net/Matchers/CSharpCodeMatcher.cs b/src/WireMock.Net/Matchers/CSharpCodeMatcher.cs index 7b4b25bc5..30b5e2173 100644 --- a/src/WireMock.Net/Matchers/CSharpCodeMatcher.cs +++ b/src/WireMock.Net/Matchers/CSharpCodeMatcher.cs @@ -80,6 +80,8 @@ private bool IsMatch(dynamic input, string pattern) var inputValue = isMatchWithString ? input : JObject.FromObject(input); string source = GetSourceForIsMatchWithString(pattern, isMatchWithString); + object result = null; + #if NET451 || NET452 var compilerParams = new System.CodeDom.Compiler.CompilerParameters { @@ -107,41 +109,74 @@ private bool IsMatch(dynamic input, string pattern) object helper = compilerResults.CompiledAssembly.CreateInstance("CodeHelper"); if (helper == null) { - throw new WireMockException("Unable to create instance from CodeHelper"); + throw new WireMockException("CSharpCodeMatcher: Unable to create instance from WireMock.CodeHelper"); } var methodInfo = helper.GetType().GetMethod("IsMatch"); if (methodInfo == null) { - throw new WireMockException("Unable to find method 'IsMatch' in CodeHelper"); + throw new WireMockException("CSharpCodeMatcher: Unable to find method 'IsMatch' in WireMock.CodeHelper"); } - return (bool)methodInfo.Invoke(helper, new[] { inputValue }); + try + { + result = methodInfo.Invoke(helper, new[] { inputValue }); + } + catch + { + throw new WireMockException("CSharpCodeMatcher: Unable to call method 'IsMatch' in WireMock.CodeHelper"); + } } #elif NET46 || NET461 + dynamic script; + try + { + script = CSScriptLibrary.CSScript.Evaluator.CompileCode(source).CreateObject("*"); + } + catch + { + throw new WireMockException("CSharpCodeMatcher: Unable to create compiler for WireMock.CodeHelper"); + } + try { - dynamic script = CSScriptLibrary.CSScript.Evaluator.CompileCode(source).CreateObject("*"); - return (bool)script.IsMatch(inputValue); + result = script.IsMatch(inputValue); } catch { - throw new WireMockException("Unable to create compile and execute code from WireMock.CodeHelper"); + throw new WireMockException("CSharpCodeMatcher: Problem calling method 'IsMatch' in WireMock.CodeHelper"); } #elif NETSTANDARD2_0 + dynamic script; try { var assembly = CSScriptLib.CSScript.Evaluator.CompileCode(source); - dynamic script = csscript.GenericExtensions.CreateObject(assembly, "*"); - return (bool)script.IsMatch(inputValue); + script = csscript.GenericExtensions.CreateObject(assembly, "*"); + } + catch (Exception ex) + { + throw new WireMockException("CSharpCodeMatcher: Unable to compile code for WireMock.CodeHelper", ex); + } + + try + { + result = script.IsMatch(inputValue); } catch (Exception ex) { - throw new WireMockException("Unable to create compiler and execute code for WireMock.CodeHelper", ex); + throw new WireMockException("CSharpCodeMatcher: Problem calling method 'IsMatch' in WireMock.CodeHelper"); } #else throw new NotSupportedException("The 'CSharpCodeMatcher' cannot be used in netstandard 1.3"); #endif + try + { + return (bool)result; + } + catch + { + throw new WireMockException($"Unable to cast result '{result}' to bool"); + } } private string GetSourceForIsMatchWithString(string pattern, bool isMatchWithString)