diff --git a/README.md b/README.md
index 05e27efc..8d04b786 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,2 @@
# WireMock.Net
-A C# .NET version based on http://wiremock.org
+A C# .NET version based on https://github.com/alexvictoor/mock4net which tries to mimic the functionality from http://WireMock.org
diff --git a/WireMock.Net Solution.sln b/WireMock.Net Solution.sln
new file mode 100644
index 00000000..56694f12
--- /dev/null
+++ b/WireMock.Net Solution.sln
@@ -0,0 +1,56 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 14
+VisualStudioVersion = 14.0.25420.1
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{EF242EDF-7133-4277-9A0C-18744DE08707}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{197A0EE3-94E5-4807-BBCF-2F1BCA28A6AE}"
+ ProjectSection(SolutionItems) = preProject
+ README.md = README.md
+ EndProjectSection
+EndProject
+Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "WireMock", "src\WireMock\WireMock.xproj", "{D3804228-91F4-4502-9595-39584E5A01AD}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "examples", "examples", "{F0C22C47-DF71-463C-9B04-B4E0F3B8708A}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WireMock.Net.ConsoleApplication", "examples\WireMock.Net.ConsoleApplication\WireMock.Net.ConsoleApplication.csproj", "{668F689E-57B4-422E-8846-C0FF643CA268}"
+ ProjectSection(ProjectDependencies) = postProject
+ {D3804228-91F4-4502-9595-39584E5A01AD} = {D3804228-91F4-4502-9595-39584E5A01AD}
+ EndProjectSection
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{890A1DED-C229-4FA1-969E-AAC3BBFC05E5}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WireMock.Net.Tests", "test\WireMock.Net.Tests\WireMock.Net.Tests.csproj", "{D8B56D28-33CE-4BEF-97D4-7DD546E37F25}"
+ ProjectSection(ProjectDependencies) = postProject
+ {D3804228-91F4-4502-9595-39584E5A01AD} = {D3804228-91F4-4502-9595-39584E5A01AD}
+ EndProjectSection
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {D3804228-91F4-4502-9595-39584E5A01AD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D3804228-91F4-4502-9595-39584E5A01AD}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D3804228-91F4-4502-9595-39584E5A01AD}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D3804228-91F4-4502-9595-39584E5A01AD}.Release|Any CPU.Build.0 = Release|Any CPU
+ {668F689E-57B4-422E-8846-C0FF643CA268}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {668F689E-57B4-422E-8846-C0FF643CA268}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {668F689E-57B4-422E-8846-C0FF643CA268}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {668F689E-57B4-422E-8846-C0FF643CA268}.Release|Any CPU.Build.0 = Release|Any CPU
+ {D8B56D28-33CE-4BEF-97D4-7DD546E37F25}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D8B56D28-33CE-4BEF-97D4-7DD546E37F25}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D8B56D28-33CE-4BEF-97D4-7DD546E37F25}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D8B56D28-33CE-4BEF-97D4-7DD546E37F25}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(NestedProjects) = preSolution
+ {D3804228-91F4-4502-9595-39584E5A01AD} = {EF242EDF-7133-4277-9A0C-18744DE08707}
+ {668F689E-57B4-422E-8846-C0FF643CA268} = {F0C22C47-DF71-463C-9B04-B4E0F3B8708A}
+ {D8B56D28-33CE-4BEF-97D4-7DD546E37F25} = {890A1DED-C229-4FA1-969E-AAC3BBFC05E5}
+ EndGlobalSection
+EndGlobal
diff --git a/examples/WireMock.Net.ConsoleApp.netcore/Program.cs b/examples/WireMock.Net.ConsoleApp.netcore/Program.cs
new file mode 100644
index 00000000..8f65a78c
--- /dev/null
+++ b/examples/WireMock.Net.ConsoleApp.netcore/Program.cs
@@ -0,0 +1,44 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace WireMock.Net.ConsoleApp
+{
+ public class Program
+ {
+ public static void Main(string[] args)
+ {
+ int port;
+ if (args.Length == 0 || !int.TryParse(args[0], out port))
+ port = 8080;
+
+ var server = FluentMockServer.Start(port);
+ Console.WriteLine("FluentMockServer running at {0}", server.Port);
+
+ server
+ .Given(
+ Requests
+ .WithUrl("/*")
+ .UsingGet()
+ )
+ .RespondWith(
+ Responses
+ .WithStatusCode(200)
+ .WithHeader("Content-Type", "application/json")
+ .WithBody(@"{ ""msg"": ""Hello world!""}")
+ );
+
+ Console.WriteLine("Press any key to stop the server");
+ Console.ReadKey();
+
+
+ Console.WriteLine("Displaying all requests");
+ var allRequests = server.RequestLogs;
+ Console.WriteLine(JsonConvert.SerializeObject(allRequests, Formatting.Indented));
+
+ Console.WriteLine("Press any key to quit");
+ Console.ReadKey();
+ }
+ }
+}
diff --git a/examples/WireMock.Net.ConsoleApp.netcore/Properties/AssemblyInfo.cs b/examples/WireMock.Net.ConsoleApp.netcore/Properties/AssemblyInfo.cs
new file mode 100644
index 00000000..c860343e
--- /dev/null
+++ b/examples/WireMock.Net.ConsoleApp.netcore/Properties/AssemblyInfo.cs
@@ -0,0 +1,19 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("WireMock.Net.ConsoleApp")]
+[assembly: AssemblyTrademark("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("bb7ad978-5c03-4d76-a903-3865802b45eb")]
diff --git a/examples/WireMock.Net.ConsoleApp.netcore/WireMock.Net.ConsoleApp.xproj b/examples/WireMock.Net.ConsoleApp.netcore/WireMock.Net.ConsoleApp.xproj
new file mode 100644
index 00000000..cb9dfd3b
--- /dev/null
+++ b/examples/WireMock.Net.ConsoleApp.netcore/WireMock.Net.ConsoleApp.xproj
@@ -0,0 +1,21 @@
+
+
+
+ 14.0
+ $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)
+
+
+
+
+ bb7ad978-5c03-4d76-a903-3865802b45eb
+ WireMock.Net.ConsoleApp
+ .\obj
+ .\bin\
+ v4.5.2
+
+
+
+ 2.0
+
+
+
diff --git a/examples/WireMock.Net.ConsoleApp.netcore/project.json b/examples/WireMock.Net.ConsoleApp.netcore/project.json
new file mode 100644
index 00000000..cd924dee
--- /dev/null
+++ b/examples/WireMock.Net.ConsoleApp.netcore/project.json
@@ -0,0 +1,19 @@
+{
+ "version": "1.0.0-*",
+ "buildOptions": {
+ "emitEntryPoint": true
+ },
+
+ "dependencies": {
+ "Microsoft.NETCore.App": {
+ "type": "platform",
+ "version": "1.1.0"
+ }
+ },
+
+ "frameworks": {
+ "netcoreapp1.0": {
+ "imports": "dnxcore50"
+ }
+ }
+}
diff --git a/examples/WireMock.Net.ConsoleApplication/App.config b/examples/WireMock.Net.ConsoleApplication/App.config
new file mode 100644
index 00000000..88fa4027
--- /dev/null
+++ b/examples/WireMock.Net.ConsoleApplication/App.config
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/WireMock.Net.ConsoleApplication/Program.cs b/examples/WireMock.Net.ConsoleApplication/Program.cs
new file mode 100644
index 00000000..969eee4a
--- /dev/null
+++ b/examples/WireMock.Net.ConsoleApplication/Program.cs
@@ -0,0 +1,42 @@
+using System;
+using Newtonsoft.Json;
+
+namespace WireMock.Net.ConsoleApplication
+{
+ static class Program
+ {
+ static void Main(string[] args)
+ {
+ int port;
+ if (args.Length == 0 || !int.TryParse(args[0], out port))
+ port = 8080;
+
+ var server = FluentMockServer.Start(port);
+ Console.WriteLine("FluentMockServer running at {0}", server.Port);
+
+ server
+ .Given(
+ Requests
+ .WithUrl("/*")
+ .UsingGet()
+ )
+ .RespondWith(
+ Responses
+ .WithStatusCode(200)
+ .WithHeader("Content-Type", "application/json")
+ .WithBody(@"{ ""msg"": ""Hello world!""}")
+ );
+
+ Console.WriteLine("Press any key to stop the server");
+ Console.ReadKey();
+
+
+ Console.WriteLine("Displaying all requests");
+ var allRequests = server.RequestLogs;
+ Console.WriteLine(JsonConvert.SerializeObject(allRequests, Formatting.Indented));
+
+ Console.WriteLine("Press any key to quit");
+ Console.ReadKey();
+ }
+ }
+}
\ No newline at end of file
diff --git a/examples/WireMock.Net.ConsoleApplication/Properties/AssemblyInfo.cs b/examples/WireMock.Net.ConsoleApplication/Properties/AssemblyInfo.cs
new file mode 100644
index 00000000..5b156822
--- /dev/null
+++ b/examples/WireMock.Net.ConsoleApplication/Properties/AssemblyInfo.cs
@@ -0,0 +1,35 @@
+using System.Reflection;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("Mock4Net.Core.ConsoleApplication")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("Mock4Net.Core.ConsoleApplication")]
+[assembly: AssemblyCopyright("Copyright © Stef Heyenrath 2017")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("668f689e-57b4-422e-8846-c0ff643ca268")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/examples/WireMock.Net.ConsoleApplication/WireMock.Net.ConsoleApplication.csproj b/examples/WireMock.Net.ConsoleApplication/WireMock.Net.ConsoleApplication.csproj
new file mode 100644
index 00000000..0b2e0676
--- /dev/null
+++ b/examples/WireMock.Net.ConsoleApplication/WireMock.Net.ConsoleApplication.csproj
@@ -0,0 +1,62 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {668F689E-57B4-422E-8846-C0FF643CA268}
+ Exe
+ Properties
+ WireMock.Net.ConsoleApplication
+ WireMock.Net.ConsoleApplication
+ v4.5.2
+ 512
+ true
+
+
+ AnyCPU
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ AnyCPU
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+
+ ..\..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll
+ True
+
+
+
+
+ ..\..\src\WireMock\bin\$(Configuration)\net45\WireMock.dll
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/WireMock.Net.ConsoleApplication/packages.config b/examples/WireMock.Net.ConsoleApplication/packages.config
new file mode 100644
index 00000000..9d64bf36
--- /dev/null
+++ b/examples/WireMock.Net.ConsoleApplication/packages.config
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/src/WireMock/CompositeRequestSpec.cs b/src/WireMock/CompositeRequestSpec.cs
new file mode 100644
index 00000000..18e278f2
--- /dev/null
+++ b/src/WireMock/CompositeRequestSpec.cs
@@ -0,0 +1,57 @@
+using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
+using System.Linq;
+
+[module:
+ SuppressMessage("StyleCop.CSharp.ReadabilityRules",
+ "SA1101:PrefixLocalCallsWithThis",
+ Justification = "Reviewed. Suppression is OK here, as it conflicts with internal naming rules.")]
+[module:
+ SuppressMessage("StyleCop.CSharp.NamingRules",
+ "SA1309:FieldNamesMustNotBeginWithUnderscore",
+ Justification = "Reviewed. Suppression is OK here, as it conflicts with internal naming rules.")]
+[module:
+ SuppressMessage("StyleCop.CSharp.DocumentationRules",
+ "SA1633:FileMustHaveHeader",
+ Justification = "Reviewed. Suppression is OK here, as unknown copyright and company.")]
+// ReSharper disable ArrangeThisQualifier
+// ReSharper disable InconsistentNaming
+namespace WireMock
+{
+ ///
+ /// The composite request spec.
+ ///
+ public class CompositeRequestSpec : ISpecifyRequests
+ {
+ ///
+ /// The _request specs.
+ ///
+ private readonly IEnumerable _requestSpecs;
+
+ ///
+ /// Initializes a new instance of the class.
+ /// The constructor.
+ ///
+ ///
+ /// The request specs.
+ ///
+ public CompositeRequestSpec(IEnumerable requestSpecs)
+ {
+ _requestSpecs = requestSpecs;
+ }
+
+ ///
+ /// The is satisfied by.
+ ///
+ ///
+ /// The request.
+ ///
+ ///
+ /// The .
+ ///
+ public bool IsSatisfiedBy(Request request)
+ {
+ return _requestSpecs.All(spec => spec.IsSatisfiedBy(request));
+ }
+ }
+}
diff --git a/src/WireMock/FluentMockServer.cs b/src/WireMock/FluentMockServer.cs
new file mode 100644
index 00000000..8c30bbe4
--- /dev/null
+++ b/src/WireMock/FluentMockServer.cs
@@ -0,0 +1,311 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Diagnostics.CodeAnalysis;
+using System.Linq;
+using System.Net;
+using System.Text;
+using System.Threading.Tasks;
+using WireMock.Http;
+
+[module:
+ SuppressMessage("StyleCop.CSharp.ReadabilityRules",
+ "SA1101:PrefixLocalCallsWithThis",
+ Justification = "Reviewed. Suppression is OK here, as it conflicts with internal naming rules.")]
+[module:
+ SuppressMessage("StyleCop.CSharp.NamingRules",
+ "SA1309:FieldNamesMustNotBeginWithUnderscore",
+ Justification = "Reviewed. Suppression is OK here, as it conflicts with internal naming rules.")]
+[module:
+ SuppressMessage("StyleCop.CSharp.DocumentationRules",
+ "SA1633:FileMustHaveHeader",
+ Justification = "Reviewed. Suppression is OK here, as unknown copyright and company.")]
+// ReSharper disable ArrangeThisQualifier
+// ReSharper disable InconsistentNaming
+namespace WireMock
+{
+ ///
+ /// The fluent mock server.
+ ///
+ public class FluentMockServer
+ {
+ ///
+ /// The _http server.
+ ///
+ private readonly TinyHttpServer _httpServer;
+
+ ///
+ /// The _routes.
+ ///
+ private readonly IList _routes = new List();
+
+ ///
+ /// The _request logs.
+ ///
+ private readonly IList _requestLogs = new List();
+
+ ///
+ /// The _request mapper.
+ ///
+ private readonly HttpListenerRequestMapper _requestMapper = new HttpListenerRequestMapper();
+
+ ///
+ /// The _response mapper.
+ ///
+ private readonly HttpListenerResponseMapper _responseMapper = new HttpListenerResponseMapper();
+
+ ///
+ /// The _sync root.
+ ///
+ private readonly object _syncRoot = new object();
+
+ ///
+ /// The _request processing delay.
+ ///
+ private TimeSpan _requestProcessingDelay = TimeSpan.Zero;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// The port.
+ ///
+ ///
+ /// The SSL support.
+ ///
+ private FluentMockServer(int port, bool ssl)
+ {
+ string protocol = ssl ? "https" : "http";
+ _httpServer = new TinyHttpServer(protocol + "://localhost:" + port + "/", HandleRequest);
+ Port = port;
+ _httpServer.Start();
+ }
+
+ ///
+ /// The RespondWithAProvider interface.
+ ///
+ public interface IRespondWithAProvider
+ {
+ ///
+ /// The respond with.
+ ///
+ ///
+ /// The provider.
+ ///
+ void RespondWith(IProvideResponses provider);
+ }
+
+ ///
+ /// Gets the port.
+ ///
+ public int Port { get; }
+
+ ///
+ /// Gets the request logs.
+ ///
+ public IEnumerable RequestLogs
+ {
+ get
+ {
+ lock (((ICollection)_requestLogs).SyncRoot)
+ {
+ return new ReadOnlyCollection(_requestLogs);
+ }
+ }
+ }
+
+ ///
+ /// The start.
+ ///
+ ///
+ /// The port.
+ ///
+ ///
+ /// The SSL support.
+ ///
+ ///
+ /// The .
+ ///
+ public static FluentMockServer Start(int port = 0, bool ssl = false)
+ {
+ if (port == 0)
+ {
+ port = Ports.FindFreeTcpPort();
+ }
+
+ return new FluentMockServer(port, ssl);
+ }
+
+ ///
+ /// The reset.
+ ///
+ public void Reset()
+ {
+ lock (((ICollection)_requestLogs).SyncRoot)
+ {
+ _requestLogs.Clear();
+ }
+
+ lock (((ICollection)_routes).SyncRoot)
+ {
+ _routes.Clear();
+ }
+ }
+
+ ///
+ /// The search logs for.
+ ///
+ ///
+ /// The spec.
+ ///
+ ///
+ /// The .
+ ///
+ public IEnumerable SearchLogsFor(ISpecifyRequests spec)
+ {
+ lock (((ICollection)_requestLogs).SyncRoot)
+ {
+ return _requestLogs.Where(spec.IsSatisfiedBy);
+ }
+ }
+
+ ///
+ /// The add request processing delay.
+ ///
+ ///
+ /// The delay.
+ ///
+ public void AddRequestProcessingDelay(TimeSpan delay)
+ {
+ lock (_syncRoot)
+ {
+ _requestProcessingDelay = delay;
+ }
+ }
+
+ ///
+ /// The stop.
+ ///
+ public void Stop()
+ {
+ _httpServer.Stop();
+ }
+
+ ///
+ /// The given.
+ ///
+ ///
+ /// The request spec.
+ ///
+ ///
+ /// The .
+ ///
+ public IRespondWithAProvider Given(ISpecifyRequests requestSpec)
+ {
+ return new RespondWithAProvider(RegisterRoute, requestSpec);
+ }
+
+ ///
+ /// The register route.
+ ///
+ ///
+ /// The route.
+ ///
+ private void RegisterRoute(Route route)
+ {
+ lock (((ICollection)_routes).SyncRoot)
+ {
+ _routes.Add(route);
+ }
+ }
+
+ ///
+ /// The log request.
+ ///
+ ///
+ /// The request.
+ ///
+ private void LogRequest(Request request)
+ {
+ lock (((ICollection)_requestLogs).SyncRoot)
+ {
+ _requestLogs.Add(request);
+ }
+ }
+
+ ///
+ /// The handle request.
+ ///
+ ///
+ /// The context.
+ ///
+ private async void HandleRequest(HttpListenerContext ctx)
+ {
+ lock (_syncRoot)
+ {
+ Task.Delay(_requestProcessingDelay).Wait();
+ }
+
+ var request = _requestMapper.Map(ctx.Request);
+ LogRequest(request);
+ var targetRoute = _routes.FirstOrDefault(route => route.IsRequestHandled(request));
+ if (targetRoute == null)
+ {
+ ctx.Response.StatusCode = 404;
+ var content = Encoding.UTF8.GetBytes("Mock Server: page not found");
+ ctx.Response.OutputStream.Write(content, 0, content.Length);
+ }
+ else
+ {
+ var response = await targetRoute.ResponseTo(request);
+
+ _responseMapper.Map(response, ctx.Response);
+ }
+
+ ctx.Response.Close();
+ }
+
+ ///
+ /// The respond with a provider.
+ ///
+ private class RespondWithAProvider : IRespondWithAProvider
+ {
+ ///
+ /// The _registration callback.
+ ///
+ private readonly RegistrationCallback _registrationCallback;
+
+ ///
+ /// The _request spec.
+ ///
+ private readonly ISpecifyRequests _requestSpec;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// The registration callback.
+ ///
+ ///
+ /// The request spec.
+ ///
+ public RespondWithAProvider(RegistrationCallback registrationCallback, ISpecifyRequests requestSpec)
+ {
+ _registrationCallback = registrationCallback;
+ _requestSpec = requestSpec;
+ }
+
+ ///
+ /// The respond with.
+ ///
+ ///
+ /// The provider.
+ ///
+ public void RespondWith(IProvideResponses provider)
+ {
+ _registrationCallback(new Route(_requestSpec, provider));
+ }
+ }
+ }
+}
diff --git a/src/WireMock/Http/Ports.cs b/src/WireMock/Http/Ports.cs
new file mode 100644
index 00000000..4d5c2e1e
--- /dev/null
+++ b/src/WireMock/Http/Ports.cs
@@ -0,0 +1,38 @@
+using System.Diagnostics.CodeAnalysis;
+using System.Net;
+using System.Net.Sockets;
+
+[module:
+ SuppressMessage("StyleCop.CSharp.DocumentationRules",
+ "SA1633:FileMustHaveHeader",
+ Justification = "Reviewed. Suppression is OK here, as unknown copyright and company.")]
+[module:
+ SuppressMessage("StyleCop.CSharp.DocumentationRules",
+ "SA1650:ElementDocumentationMustBeSpelledCorrectly",
+ Justification = "Reviewed. Suppression is OK here.")]
+
+namespace WireMock.Http
+{
+ ///
+ /// The ports.
+ ///
+ public static class Ports
+ {
+ ///
+ /// The find free TCP port.
+ ///
+ ///
+ /// The .
+ ///
+ /// see http://stackoverflow.com/questions/138043/find-the-next-tcp-port-in-net.
+ // ReSharper disable once StyleCop.SA1650
+ public static int FindFreeTcpPort()
+ {
+ TcpListener l = new TcpListener(IPAddress.Loopback, 0);
+ l.Start();
+ int port = ((IPEndPoint)l.LocalEndpoint).Port;
+ l.Stop();
+ return port;
+ }
+ }
+}
diff --git a/src/WireMock/Http/TinyHttpServer.cs b/src/WireMock/Http/TinyHttpServer.cs
new file mode 100644
index 00000000..23f3bcbe
--- /dev/null
+++ b/src/WireMock/Http/TinyHttpServer.cs
@@ -0,0 +1,98 @@
+using System;
+using System.Diagnostics.CodeAnalysis;
+using System.Net;
+using System.Threading;
+using System.Threading.Tasks;
+
+[module:
+ SuppressMessage("StyleCop.CSharp.ReadabilityRules",
+ "SA1101:PrefixLocalCallsWithThis",
+ Justification = "Reviewed. Suppression is OK here, as it conflicts with internal naming rules.")]
+[module:
+ SuppressMessage("StyleCop.CSharp.NamingRules",
+ "SA1309:FieldNamesMustNotBeginWithUnderscore",
+ Justification = "Reviewed. Suppression is OK here, as it conflicts with internal naming rules.")]
+[module:
+ SuppressMessage("StyleCop.CSharp.DocumentationRules",
+ "SA1633:FileMustHaveHeader",
+ Justification = "Reviewed. Suppression is OK here, as unknown copyright and company.")]
+// ReSharper disable ArrangeThisQualifier
+// ReSharper disable InconsistentNaming
+namespace WireMock.Http
+{
+ ///
+ /// The tiny http server.
+ ///
+ public class TinyHttpServer
+ {
+ ///
+ /// The _http handler.
+ ///
+ private readonly Action _httpHandler;
+
+ ///
+ /// The _listener.
+ ///
+ private readonly HttpListener _listener;
+
+ ///
+ /// The cancellation token source.
+ ///
+ private CancellationTokenSource _cts;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// The url prefix.
+ ///
+ ///
+ /// The http handler.
+ ///
+ public TinyHttpServer(string urlPrefix, Action httpHandler)
+ {
+ _httpHandler = httpHandler;
+
+ // .Net Framework is not supportted on XP or Server 2003, so no need for the check
+ /*if (!HttpListener.IsSupported)
+ {
+ Console.WriteLine("Windows XP SP2 or Server 2003 is required to use the HttpListener class.");
+ return;
+ }*/
+
+ // Create a listener.
+ _listener = new HttpListener();
+ _listener.Prefixes.Add(urlPrefix);
+ }
+
+ ///
+ /// The start.
+ ///
+ public void Start()
+ {
+ _listener.Start();
+ _cts = new CancellationTokenSource();
+ Task.Run(
+ async () =>
+ {
+ using (_listener)
+ {
+ while (!_cts.Token.IsCancellationRequested)
+ {
+ HttpListenerContext context = await _listener.GetContextAsync();
+ _httpHandler(context);
+ }
+ }
+ },
+ _cts.Token);
+ }
+
+ ///
+ /// The stop.
+ ///
+ public void Stop()
+ {
+ _cts.Cancel();
+ }
+ }
+}
diff --git a/src/WireMock/HttpListenerRequestMapper.cs b/src/WireMock/HttpListenerRequestMapper.cs
new file mode 100644
index 00000000..d31588ec
--- /dev/null
+++ b/src/WireMock/HttpListenerRequestMapper.cs
@@ -0,0 +1,68 @@
+using System.Diagnostics.CodeAnalysis;
+using System.IO;
+using System.Linq;
+using System.Net;
+
+[module:
+ SuppressMessage("StyleCop.CSharp.ReadabilityRules",
+ "SA1101:PrefixLocalCallsWithThis",
+ Justification = "Reviewed. Suppression is OK here, as it conflicts with internal naming rules.")]
+[module:
+ SuppressMessage("StyleCop.CSharp.DocumentationRules",
+ "SA1633:FileMustHaveHeader",
+ Justification = "Reviewed. Suppression is OK here, as unknown copyright and company.")]
+// ReSharper disable ArrangeThisQualifier
+namespace WireMock
+{
+ ///
+ /// The http listener request mapper.
+ ///
+ public class HttpListenerRequestMapper
+ {
+ ///
+ /// The map.
+ ///
+ ///
+ /// The listener request.
+ ///
+ ///
+ /// The .
+ ///
+ public Request Map(HttpListenerRequest listenerRequest)
+ {
+ var path = listenerRequest.Url.AbsolutePath;
+ var query = listenerRequest.Url.Query;
+ var verb = listenerRequest.HttpMethod;
+ var body = GetRequestBody(listenerRequest);
+ var listenerHeaders = listenerRequest.Headers;
+ var headers = listenerHeaders.AllKeys.ToDictionary(k => k, k => listenerHeaders[k]);
+
+ return new Request(path, query, verb, body, headers);
+ }
+
+ ///
+ /// The get request body.
+ ///
+ ///
+ /// The request.
+ ///
+ ///
+ /// The .
+ ///
+ private string GetRequestBody(HttpListenerRequest request)
+ {
+ if (!request.HasEntityBody)
+ {
+ return null;
+ }
+
+ using (var body = request.InputStream)
+ {
+ using (var reader = new StreamReader(body, request.ContentEncoding))
+ {
+ return reader.ReadToEnd();
+ }
+ }
+ }
+ }
+}
diff --git a/src/WireMock/HttpListenerResponseMapper.cs b/src/WireMock/HttpListenerResponseMapper.cs
new file mode 100644
index 00000000..ad4ccfd1
--- /dev/null
+++ b/src/WireMock/HttpListenerResponseMapper.cs
@@ -0,0 +1,38 @@
+using System.Diagnostics.CodeAnalysis;
+using System.Linq;
+using System.Net;
+using System.Text;
+
+[module:
+ SuppressMessage("StyleCop.CSharp.DocumentationRules",
+ "SA1633:FileMustHaveHeader",
+ Justification = "Reviewed. Suppression is OK here, as unknown copyright and company.")]
+
+namespace WireMock
+{
+ ///
+ /// The http listener response mapper.
+ ///
+ public class HttpListenerResponseMapper
+ {
+ ///
+ /// The map.
+ ///
+ ///
+ /// The response.
+ ///
+ ///
+ /// The result.
+ ///
+ public void Map(Response response, HttpListenerResponse result)
+ {
+ result.StatusCode = response.StatusCode;
+ response.Headers.ToList().ForEach(pair => result.AddHeader(pair.Key, pair.Value));
+ if (response.Body != null)
+ {
+ var content = Encoding.UTF8.GetBytes(response.Body);
+ result.OutputStream.Write(content, 0, content.Length);
+ }
+ }
+ }
+}
diff --git a/src/WireMock/IProvideResponses.cs b/src/WireMock/IProvideResponses.cs
new file mode 100644
index 00000000..f5565c8c
--- /dev/null
+++ b/src/WireMock/IProvideResponses.cs
@@ -0,0 +1,27 @@
+using System.Diagnostics.CodeAnalysis;
+using System.Threading.Tasks;
+
+[module:
+ SuppressMessage("StyleCop.CSharp.DocumentationRules",
+ "SA1633:FileMustHaveHeader",
+ Justification = "Reviewed. Suppression is OK here, as unknown copyright and company.")]
+
+namespace WireMock
+{
+ ///
+ /// The ProvideResponses interface.
+ ///
+ public interface IProvideResponses
+ {
+ ///
+ /// The provide response.
+ ///
+ ///
+ /// The request.
+ ///
+ ///
+ /// The .
+ ///
+ Task ProvideResponse(Request request);
+ }
+}
diff --git a/src/WireMock/ISpecifyRequests.cs b/src/WireMock/ISpecifyRequests.cs
new file mode 100644
index 00000000..d3515cba
--- /dev/null
+++ b/src/WireMock/ISpecifyRequests.cs
@@ -0,0 +1,26 @@
+using System.Diagnostics.CodeAnalysis;
+
+[module:
+ SuppressMessage("StyleCop.CSharp.DocumentationRules",
+ "SA1633:FileMustHaveHeader",
+ Justification = "Reviewed. Suppression is OK here, as unknown copyright and company.")]
+
+namespace WireMock
+{
+ ///
+ /// The SpecifyRequests interface.
+ ///
+ public interface ISpecifyRequests
+ {
+ ///
+ /// The is satisfied by.
+ ///
+ ///
+ /// The request.
+ ///
+ ///
+ /// The .
+ ///
+ bool IsSatisfiedBy(Request request);
+ }
+}
diff --git a/src/WireMock/Request.cs b/src/WireMock/Request.cs
new file mode 100644
index 00000000..f7bc0c79
--- /dev/null
+++ b/src/WireMock/Request.cs
@@ -0,0 +1,138 @@
+using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
+using System.Linq;
+
+[module:
+ SuppressMessage("StyleCop.CSharp.ReadabilityRules",
+ "SA1101:PrefixLocalCallsWithThis",
+ Justification = "Reviewed. Suppression is OK here, as it conflicts with internal naming rules.")]
+[module:
+ SuppressMessage("StyleCop.CSharp.NamingRules",
+ "SA1309:FieldNamesMustNotBeginWithUnderscore",
+ Justification = "Reviewed. Suppression is OK here, as it conflicts with internal naming rules.")]
+[module:
+ SuppressMessage("StyleCop.CSharp.DocumentationRules",
+ "SA1633:FileMustHaveHeader",
+ Justification = "Reviewed. Suppression is OK here, as unknown copyright and company.")]
+[module:
+ SuppressMessage("StyleCop.CSharp.DocumentationRules",
+ "SA1650:ElementDocumentationMustBeSpelledCorrectly",
+ Justification = "Reviewed. Suppression is OK here.")]
+// ReSharper disable ArrangeThisQualifier
+// ReSharper disable InconsistentNaming
+namespace WireMock
+{
+ ///
+ /// The request.
+ ///
+ public class Request
+ {
+ ///
+ /// The _params.
+ ///
+ private readonly Dictionary> _params = new Dictionary>();
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// The path.
+ ///
+ ///
+ /// The query.
+ ///
+ ///
+ /// The verb.
+ ///
+ ///
+ /// The body.
+ ///
+ ///
+ /// The headers.
+ ///
+ public Request(string path, string query, string verb, string body, IDictionary headers)
+ {
+ if (!string.IsNullOrEmpty(query))
+ {
+ if (query.StartsWith("?"))
+ {
+ query = query.Substring(1);
+ }
+
+ _params = query.Split('&').Aggregate(
+ new Dictionary>(),
+ (dict, term) =>
+ {
+ var key = term.Split('=')[0];
+ if (!dict.ContainsKey(key))
+ {
+ dict.Add(key, new List());
+ }
+
+ dict[key].Add(term.Split('=')[1]);
+ return dict;
+ });
+ }
+
+ Path = path;
+ Headers = headers.ToDictionary(kv => kv.Key.ToLower(), kv => kv.Value.ToLower());
+ Verb = verb.ToLower();
+ Body = body?.Trim() ?? string.Empty;
+ }
+
+ ///
+ /// Gets the url.
+ ///
+ public string Url
+ {
+ get
+ {
+ if (!_params.Any())
+ {
+ return Path;
+ }
+
+ return Path + "?" + string.Join("&", _params.SelectMany(kv => kv.Value.Select(value => kv.Key + "=" + value)));
+ }
+ }
+
+ ///
+ /// Gets the path.
+ ///
+ public string Path { get; }
+
+ ///
+ /// Gets the verb.
+ ///
+ public string Verb { get; }
+
+ ///
+ /// Gets the headers.
+ ///
+ public IDictionary Headers { get; }
+
+ ///
+ /// Gets the body.
+ ///
+ public string Body { get; }
+
+ ///
+ /// The get parameter.
+ ///
+ ///
+ /// The key.
+ ///
+ ///
+ /// The parameter.
+ ///
+ public List GetParameter(string key)
+ {
+ if (_params.ContainsKey(key))
+ {
+ return _params[key];
+ }
+
+ return new List();
+ }
+ }
+}
diff --git a/src/WireMock/RequestBodySpec.cs b/src/WireMock/RequestBodySpec.cs
new file mode 100644
index 00000000..f5388f5d
--- /dev/null
+++ b/src/WireMock/RequestBodySpec.cs
@@ -0,0 +1,54 @@
+using System.Diagnostics.CodeAnalysis;
+
+[module:
+ SuppressMessage("StyleCop.CSharp.ReadabilityRules",
+ "SA1101:PrefixLocalCallsWithThis",
+ Justification = "Reviewed. Suppression is OK here, as it conflicts with internal naming rules.")]
+[module:
+ SuppressMessage("StyleCop.CSharp.NamingRules",
+ "SA1309:FieldNamesMustNotBeginWithUnderscore",
+ Justification = "Reviewed. Suppression is OK here, as it conflicts with internal naming rules.")]
+[module:
+ SuppressMessage("StyleCop.CSharp.DocumentationRules",
+ "SA1633:FileMustHaveHeader",
+ Justification = "Reviewed. Suppression is OK here, as unknown copyright and company.")]
+// ReSharper disable ArrangeThisQualifier
+// ReSharper disable InconsistentNaming
+namespace WireMock
+{
+ ///
+ /// The request body spec.
+ ///
+ public class RequestBodySpec : ISpecifyRequests
+ {
+ ///
+ /// The _body.
+ ///
+ private readonly string _body;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// The body.
+ ///
+ public RequestBodySpec(string body)
+ {
+ _body = body.Trim();
+ }
+
+ ///
+ /// The is satisfied by.
+ ///
+ ///
+ /// The request.
+ ///
+ ///
+ /// The .
+ ///
+ public bool IsSatisfiedBy(Request request)
+ {
+ return WildcardPatternMatcher.MatchWildcardString(_body, request.Body.Trim());
+ }
+ }
+}
diff --git a/src/WireMock/RequestHeaderSpec.cs b/src/WireMock/RequestHeaderSpec.cs
new file mode 100644
index 00000000..91961c01
--- /dev/null
+++ b/src/WireMock/RequestHeaderSpec.cs
@@ -0,0 +1,64 @@
+using System.Diagnostics.CodeAnalysis;
+
+[module:
+ SuppressMessage("StyleCop.CSharp.ReadabilityRules",
+ "SA1101:PrefixLocalCallsWithThis",
+ Justification = "Reviewed. Suppression is OK here, as it conflicts with internal naming rules.")]
+[module:
+ SuppressMessage("StyleCop.CSharp.NamingRules",
+ "SA1309:FieldNamesMustNotBeginWithUnderscore",
+ Justification = "Reviewed. Suppression is OK here, as it conflicts with internal naming rules.")]
+[module:
+ SuppressMessage("StyleCop.CSharp.DocumentationRules",
+ "SA1633:FileMustHaveHeader",
+ Justification = "Reviewed. Suppression is OK here, as unknown copyright and company.")]
+// ReSharper disable ArrangeThisQualifier
+// ReSharper disable InconsistentNaming
+namespace WireMock
+{
+ ///
+ /// The request header spec.
+ ///
+ public class RequestHeaderSpec : ISpecifyRequests
+ {
+ ///
+ /// The _name.
+ ///
+ private readonly string _name;
+
+ ///
+ /// The _pattern.
+ ///
+ private readonly string _pattern;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// The name.
+ ///
+ ///
+ /// The pattern.
+ ///
+ public RequestHeaderSpec(string name, string pattern)
+ {
+ _name = name.ToLower();
+ _pattern = pattern.ToLower();
+ }
+
+ ///
+ /// The is satisfied by.
+ ///
+ ///
+ /// The request.
+ ///
+ ///
+ /// The .
+ ///
+ public bool IsSatisfiedBy(Request request)
+ {
+ string headerValue = request.Headers[_name];
+ return WildcardPatternMatcher.MatchWildcardString(_pattern, headerValue);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/WireMock/RequestParamSpec.cs b/src/WireMock/RequestParamSpec.cs
new file mode 100644
index 00000000..19de981b
--- /dev/null
+++ b/src/WireMock/RequestParamSpec.cs
@@ -0,0 +1,65 @@
+using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
+using System.Linq;
+
+[module:
+ SuppressMessage("StyleCop.CSharp.ReadabilityRules",
+ "SA1101:PrefixLocalCallsWithThis",
+ Justification = "Reviewed. Suppression is OK here, as it conflicts with internal naming rules.")]
+[module:
+ SuppressMessage("StyleCop.CSharp.NamingRules",
+ "SA1309:FieldNamesMustNotBeginWithUnderscore",
+ Justification = "Reviewed. Suppression is OK here, as it conflicts with internal naming rules.")]
+[module:
+ SuppressMessage("StyleCop.CSharp.DocumentationRules",
+ "SA1633:FileMustHaveHeader",
+ Justification = "Reviewed. Suppression is OK here, as unknown copyright and company.")]
+// ReSharper disable ArrangeThisQualifier
+// ReSharper disable InconsistentNaming
+namespace WireMock
+{
+ ///
+ /// The request parameters spec.
+ ///
+ public class RequestParamSpec : ISpecifyRequests
+ {
+ ///
+ /// The _key.
+ ///
+ private readonly string _key;
+
+ ///
+ /// The _values.
+ ///
+ private readonly List _values;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// The key.
+ ///
+ ///
+ /// The values.
+ ///
+ public RequestParamSpec(string key, List values)
+ {
+ _key = key;
+ _values = values;
+ }
+
+ ///
+ /// The is satisfied by.
+ ///
+ ///
+ /// The request.
+ ///
+ ///
+ /// The .
+ ///
+ public bool IsSatisfiedBy(Request request)
+ {
+ return request.GetParameter(_key).Intersect(_values).Count() == _values.Count;
+ }
+ }
+}
diff --git a/src/WireMock/RequestPathSpec.cs b/src/WireMock/RequestPathSpec.cs
new file mode 100644
index 00000000..a5b45bea
--- /dev/null
+++ b/src/WireMock/RequestPathSpec.cs
@@ -0,0 +1,54 @@
+using System.Diagnostics.CodeAnalysis;
+
+[module:
+ SuppressMessage("StyleCop.CSharp.ReadabilityRules",
+ "SA1101:PrefixLocalCallsWithThis",
+ Justification = "Reviewed. Suppression is OK here, as it conflicts with internal naming rules.")]
+[module:
+ SuppressMessage("StyleCop.CSharp.NamingRules",
+ "SA1309:FieldNamesMustNotBeginWithUnderscore",
+ Justification = "Reviewed. Suppression is OK here, as it conflicts with internal naming rules.")]
+[module:
+ SuppressMessage("StyleCop.CSharp.DocumentationRules",
+ "SA1633:FileMustHaveHeader",
+ Justification = "Reviewed. Suppression is OK here, as unknown copyright and company.")]
+// ReSharper disable ArrangeThisQualifier
+// ReSharper disable InconsistentNaming
+namespace WireMock
+{
+ ///
+ /// The request path spec.
+ ///
+ public class RequestPathSpec : ISpecifyRequests
+ {
+ ///
+ /// The _path.
+ ///
+ private readonly string _path;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// The path.
+ ///
+ public RequestPathSpec(string path)
+ {
+ _path = path;
+ }
+
+ ///
+ /// The is satisfied by.
+ ///
+ ///
+ /// The request.
+ ///
+ ///
+ /// The .
+ ///
+ public bool IsSatisfiedBy(Request request)
+ {
+ return WildcardPatternMatcher.MatchWildcardString(_path, request.Path);
+ }
+ }
+}
diff --git a/src/WireMock/RequestSpecBuilder.cs b/src/WireMock/RequestSpecBuilder.cs
new file mode 100644
index 00000000..f61eaa53
--- /dev/null
+++ b/src/WireMock/RequestSpecBuilder.cs
@@ -0,0 +1,123 @@
+using System.Diagnostics.CodeAnalysis;
+
+[module:
+ SuppressMessage("StyleCop.CSharp.DocumentationRules",
+ "SA1633:FileMustHaveHeader",
+ Justification = "Reviewed. Suppression is OK here, as unknown copyright and company.")]
+
+namespace WireMock
+{
+ ///
+ /// The VerbRequestBuilder interface.
+ ///
+ public interface IVerbRequestBuilder : ISpecifyRequests, IHeadersRequestBuilder
+ {
+ ///
+ /// The using get.
+ ///
+ ///
+ /// The .
+ ///
+ IHeadersRequestBuilder UsingGet();
+
+ ///
+ /// The using post.
+ ///
+ ///
+ /// The .
+ ///
+ IHeadersRequestBuilder UsingPost();
+
+ ///
+ /// The using put.
+ ///
+ ///
+ /// The .
+ ///
+ IHeadersRequestBuilder UsingPut();
+
+ ///
+ /// The using head.
+ ///
+ ///
+ /// The .
+ ///
+ IHeadersRequestBuilder UsingHead();
+
+ ///
+ /// The using any verb.
+ ///
+ ///
+ /// The .
+ ///
+ IHeadersRequestBuilder UsingAnyVerb();
+
+ ///
+ /// The using verb.
+ ///
+ ///
+ /// The verb.
+ ///
+ ///
+ /// The .
+ ///
+ IHeadersRequestBuilder UsingVerb(string verb);
+ }
+
+ ///
+ /// The HeadersRequestBuilder interface.
+ ///
+ public interface IHeadersRequestBuilder : IBodyRequestBuilder, ISpecifyRequests, IParamsRequestBuilder
+ {
+ ///
+ /// The with header.
+ ///
+ ///
+ /// The name.
+ ///
+ ///
+ /// The value.
+ ///
+ ///
+ /// The .
+ ///
+ IHeadersRequestBuilder WithHeader(string name, string value);
+ }
+
+ ///
+ /// The BodyRequestBuilder interface.
+ ///
+ public interface IBodyRequestBuilder
+ {
+ ///
+ /// The with body.
+ ///
+ ///
+ /// The body.
+ ///
+ ///
+ /// The .
+ ///
+ ISpecifyRequests WithBody(string body);
+ }
+
+ ///
+ /// The ParametersRequestBuilder interface.
+ ///
+ public interface IParamsRequestBuilder
+ {
+ ///
+ /// The with parameters.
+ ///
+ ///
+ /// The key.
+ ///
+ ///
+ /// The values.
+ ///
+ ///
+ /// The .
+ ///
+ ISpecifyRequests WithParam(string key, params string[] values);
+ }
+}
diff --git a/src/WireMock/RequestUrlSpec.cs b/src/WireMock/RequestUrlSpec.cs
new file mode 100644
index 00000000..b3ea9f78
--- /dev/null
+++ b/src/WireMock/RequestUrlSpec.cs
@@ -0,0 +1,54 @@
+using System.Diagnostics.CodeAnalysis;
+
+[module:
+ SuppressMessage("StyleCop.CSharp.ReadabilityRules",
+ "SA1101:PrefixLocalCallsWithThis",
+ Justification = "Reviewed. Suppression is OK here, as it conflicts with internal naming rules.")]
+[module:
+ SuppressMessage("StyleCop.CSharp.NamingRules",
+ "SA1309:FieldNamesMustNotBeginWithUnderscore",
+ Justification = "Reviewed. Suppression is OK here, as it conflicts with internal naming rules.")]
+[module:
+ SuppressMessage("StyleCop.CSharp.DocumentationRules",
+ "SA1633:FileMustHaveHeader",
+ Justification = "Reviewed. Suppression is OK here, as unknown copyright and company.")]
+// ReSharper disable ArrangeThisQualifier
+// ReSharper disable InconsistentNaming
+namespace WireMock
+{
+ ///
+ /// The request url spec.
+ ///
+ public class RequestUrlSpec : ISpecifyRequests
+ {
+ ///
+ /// The _url.
+ ///
+ private readonly string _url;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// The url.
+ ///
+ public RequestUrlSpec(string url)
+ {
+ _url = url;
+ }
+
+ ///
+ /// The is satisfied by.
+ ///
+ ///
+ /// The request.
+ ///
+ ///
+ /// The .
+ ///
+ public bool IsSatisfiedBy(Request request)
+ {
+ return WildcardPatternMatcher.MatchWildcardString(_url, request.Url);
+ }
+ }
+}
diff --git a/src/WireMock/RequestVerbSpec.cs b/src/WireMock/RequestVerbSpec.cs
new file mode 100644
index 00000000..cb362cf9
--- /dev/null
+++ b/src/WireMock/RequestVerbSpec.cs
@@ -0,0 +1,54 @@
+using System.Diagnostics.CodeAnalysis;
+
+[module:
+ SuppressMessage("StyleCop.CSharp.ReadabilityRules",
+ "SA1101:PrefixLocalCallsWithThis",
+ Justification = "Reviewed. Suppression is OK here, as it conflicts with internal naming rules.")]
+[module:
+ SuppressMessage("StyleCop.CSharp.NamingRules",
+ "SA1309:FieldNamesMustNotBeginWithUnderscore",
+ Justification = "Reviewed. Suppression is OK here, as it conflicts with internal naming rules.")]
+[module:
+ SuppressMessage("StyleCop.CSharp.DocumentationRules",
+ "SA1633:FileMustHaveHeader",
+ Justification = "Reviewed. Suppression is OK here, as unknown copyright and company.")]
+// ReSharper disable ArrangeThisQualifier
+// ReSharper disable InconsistentNaming
+namespace WireMock
+{
+ ///
+ /// The request verb spec.
+ ///
+ internal class RequestVerbSpec : ISpecifyRequests
+ {
+ ///
+ /// The _verb.
+ ///
+ private readonly string _verb;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// The verb.
+ ///
+ public RequestVerbSpec(string verb)
+ {
+ _verb = verb.ToLower();
+ }
+
+ ///
+ /// The is satisfied by.
+ ///
+ ///
+ /// The request.
+ ///
+ ///
+ /// The .
+ ///
+ public bool IsSatisfiedBy(Request request)
+ {
+ return request.Verb == _verb;
+ }
+ }
+}
diff --git a/src/WireMock/Requests.cs b/src/WireMock/Requests.cs
new file mode 100644
index 00000000..bd5b2fe4
--- /dev/null
+++ b/src/WireMock/Requests.cs
@@ -0,0 +1,205 @@
+using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
+using System.Linq;
+
+[module:
+ SuppressMessage("StyleCop.CSharp.ReadabilityRules",
+ "SA1101:PrefixLocalCallsWithThis",
+ Justification = "Reviewed. Suppression is OK here, as it conflicts with internal naming rules.")]
+[module:
+ SuppressMessage("StyleCop.CSharp.ReadabilityRules",
+ "SA1126:PrefixCallsCorrectly",
+ Justification = "Reviewed. Suppression is OK here, as it conflicts with internal naming rules.")]
+[module:
+ SuppressMessage("StyleCop.CSharp.NamingRules",
+ "SA1309:FieldNamesMustNotBeginWithUnderscore",
+ Justification = "Reviewed. Suppression is OK here, as it conflicts with internal naming rules.")]
+[module:
+ SuppressMessage("StyleCop.CSharp.DocumentationRules",
+ "SA1633:FileMustHaveHeader",
+ Justification = "Reviewed. Suppression is OK here, as unknown copyright and company.")]
+// ReSharper disable ArrangeThisQualifier
+// ReSharper disable InconsistentNaming
+namespace WireMock
+{
+ ///
+ /// The requests.
+ ///
+ public class Requests : CompositeRequestSpec, IVerbRequestBuilder, IHeadersRequestBuilder, IParamsRequestBuilder
+ {
+ ///
+ /// The _request specs.
+ ///
+ private readonly IList _requestSpecs;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// The request specs.
+ ///
+ private Requests(IList requestSpecs) : base(requestSpecs)
+ {
+ _requestSpecs = requestSpecs;
+ }
+
+ ///
+ /// The with url.
+ ///
+ ///
+ /// The url.
+ ///
+ ///
+ /// The .
+ ///
+ public static IVerbRequestBuilder WithUrl(string url)
+ {
+ var specs = new List();
+ var requests = new Requests(specs);
+ specs.Add(new RequestUrlSpec(url));
+ return requests;
+ }
+
+ ///
+ /// The with path.
+ ///
+ ///
+ /// The path.
+ ///
+ ///
+ /// The .
+ ///
+ public static IVerbRequestBuilder WithPath(string path)
+ {
+ var specs = new List();
+ var requests = new Requests(specs);
+ specs.Add(new RequestPathSpec(path));
+ return requests;
+ }
+
+ ///
+ /// The using get.
+ ///
+ ///
+ /// The .
+ ///
+ public IHeadersRequestBuilder UsingGet()
+ {
+ _requestSpecs.Add(new RequestVerbSpec("get"));
+ return this;
+ }
+
+ ///
+ /// The using post.
+ ///
+ ///
+ /// The .
+ ///
+ public IHeadersRequestBuilder UsingPost()
+ {
+ _requestSpecs.Add(new RequestVerbSpec("post"));
+ return this;
+ }
+
+ ///
+ /// The using put.
+ ///
+ ///
+ /// The .
+ ///
+ public IHeadersRequestBuilder UsingPut()
+ {
+ _requestSpecs.Add(new RequestVerbSpec("put"));
+ return this;
+ }
+
+ ///
+ /// The using head.
+ ///
+ ///
+ /// The .
+ ///
+ public IHeadersRequestBuilder UsingHead()
+ {
+ _requestSpecs.Add(new RequestVerbSpec("head"));
+ return this;
+ }
+
+ ///
+ /// The using any verb.
+ ///
+ ///
+ /// The .
+ ///
+ public IHeadersRequestBuilder UsingAnyVerb()
+ {
+ return this;
+ }
+
+ ///
+ /// The using verb.
+ ///
+ ///
+ /// The verb.
+ ///
+ ///
+ /// The .
+ ///
+ public IHeadersRequestBuilder UsingVerb(string verb)
+ {
+ _requestSpecs.Add(new RequestVerbSpec(verb));
+ return this;
+ }
+
+ ///
+ /// The with body.
+ ///
+ ///
+ /// The body.
+ ///
+ ///
+ /// The .
+ ///
+ public ISpecifyRequests WithBody(string body)
+ {
+ _requestSpecs.Add(new RequestBodySpec(body));
+ return this;
+ }
+
+ ///
+ /// The with parameters.
+ ///
+ ///
+ /// The key.
+ ///
+ ///
+ /// The values.
+ ///
+ ///
+ /// The .
+ ///
+ public ISpecifyRequests WithParam(string key, params string[] values)
+ {
+ _requestSpecs.Add(new RequestParamSpec(key, values.ToList()));
+ return this;
+ }
+
+ ///
+ /// The with header.
+ ///
+ ///
+ /// The name.
+ ///
+ ///
+ /// The value.
+ ///
+ ///
+ /// The .
+ ///
+ public IHeadersRequestBuilder WithHeader(string name, string value)
+ {
+ _requestSpecs.Add(new RequestHeaderSpec(name, value));
+ return this;
+ }
+ }
+}
diff --git a/src/WireMock/Response.cs b/src/WireMock/Response.cs
new file mode 100644
index 00000000..ef134c3a
--- /dev/null
+++ b/src/WireMock/Response.cs
@@ -0,0 +1,92 @@
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
+
+[module:
+ SuppressMessage("StyleCop.CSharp.ReadabilityRules",
+ "SA1101:PrefixLocalCallsWithThis",
+ Justification = "Reviewed. Suppression is OK here, as it conflicts with internal naming rules.")]
+[module:
+ SuppressMessage("StyleCop.CSharp.NamingRules",
+ "SA1309:FieldNamesMustNotBeginWithUnderscore",
+ Justification = "Reviewed. Suppression is OK here, as it conflicts with internal naming rules.")]
+[module:
+ SuppressMessage("StyleCop.CSharp.DocumentationRules",
+ "SA1633:FileMustHaveHeader",
+ Justification = "Reviewed. Suppression is OK here, as unknown copyright and company.")]
+// ReSharper disable ArrangeThisQualifier
+// ReSharper disable InconsistentNaming
+namespace WireMock
+{
+ ///
+ /// The response.
+ ///
+ public class Response
+ {
+ ///
+ /// The _headers.
+ ///
+ private readonly IDictionary _headers = new ConcurrentDictionary();
+
+ ///
+ /// The status code.
+ ///
+ private volatile int statusCode = 200;
+
+ ///
+ /// The body.
+ ///
+ private volatile string body;
+
+ ///
+ /// Gets the headers.
+ ///
+ public IDictionary Headers => _headers;
+
+ ///
+ /// Gets or sets the status code.
+ ///
+ public int StatusCode
+ {
+ get
+ {
+ return statusCode;
+ }
+
+ set
+ {
+ statusCode = value;
+ }
+ }
+
+ ///
+ /// Gets or sets the body.
+ ///
+ public string Body
+ {
+ get
+ {
+ return body;
+ }
+
+ set
+ {
+ body = value;
+ }
+ }
+
+ ///
+ /// The add header.
+ ///
+ ///
+ /// The name.
+ ///
+ ///
+ /// The value.
+ ///
+ public void AddHeader(string name, string value)
+ {
+ _headers.Add(name, value);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/WireMock/ResponseBuilder.cs b/src/WireMock/ResponseBuilder.cs
new file mode 100644
index 00000000..9c23a1d5
--- /dev/null
+++ b/src/WireMock/ResponseBuilder.cs
@@ -0,0 +1,64 @@
+using System;
+using System.Diagnostics.CodeAnalysis;
+
+[module:
+ SuppressMessage("StyleCop.CSharp.DocumentationRules",
+ "SA1633:FileMustHaveHeader",
+ Justification = "Reviewed. Suppression is OK here, as unknown copyright and company.")]
+
+namespace WireMock
+{
+ ///
+ /// The HeadersResponseBuilder interface.
+ ///
+ public interface IHeadersResponseBuilder : IBodyResponseBuilder
+ {
+ ///
+ /// The with header.
+ ///
+ ///
+ /// The name.
+ ///
+ ///
+ /// The value.
+ ///
+ ///
+ /// The .
+ ///
+ IHeadersResponseBuilder WithHeader(string name, string value);
+ }
+
+ ///
+ /// The BodyResponseBuilder interface.
+ ///
+ public interface IBodyResponseBuilder : IDelayResponseBuilder
+ {
+ ///
+ /// The with body.
+ ///
+ ///
+ /// The body.
+ ///
+ ///
+ /// The .
+ ///
+ IDelayResponseBuilder WithBody(string body);
+ }
+
+ ///
+ /// The DelayResponseBuilder interface.
+ ///
+ public interface IDelayResponseBuilder : IProvideResponses
+ {
+ ///
+ /// The after delay.
+ ///
+ ///
+ /// The delay.
+ ///
+ ///
+ /// The .
+ ///
+ IProvideResponses AfterDelay(TimeSpan delay);
+ }
+}
diff --git a/src/WireMock/Responses.cs b/src/WireMock/Responses.cs
new file mode 100644
index 00000000..f74ba8c8
--- /dev/null
+++ b/src/WireMock/Responses.cs
@@ -0,0 +1,143 @@
+using System;
+using System.Diagnostics.CodeAnalysis;
+using System.Threading.Tasks;
+
+[module:
+ SuppressMessage("StyleCop.CSharp.ReadabilityRules",
+ "SA1101:PrefixLocalCallsWithThis",
+ Justification = "Reviewed. Suppression is OK here, as it conflicts with internal naming rules.")]
+[module:
+ SuppressMessage("StyleCop.CSharp.NamingRules",
+ "SA1309:FieldNamesMustNotBeginWithUnderscore",
+ Justification = "Reviewed. Suppression is OK here, as it conflicts with internal naming rules.")]
+[module:
+ SuppressMessage("StyleCop.CSharp.DocumentationRules",
+ "SA1633:FileMustHaveHeader",
+ Justification = "Reviewed. Suppression is OK here, as unknown copyright and company.")]
+// ReSharper disable ArrangeThisQualifier
+// ReSharper disable InconsistentNaming
+namespace WireMock
+{
+ ///
+ /// The responses.
+ ///
+ public class Responses : IHeadersResponseBuilder
+ {
+ ///
+ /// The _response.
+ ///
+ private readonly Response _response;
+
+ ///
+ /// The _delay.
+ ///
+ private TimeSpan _delay = TimeSpan.Zero;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// The response.
+ ///
+ public Responses(Response response)
+ {
+ _response = response;
+ }
+
+ ///
+ /// The with Success status code.
+ ///
+ /// The .
+ public static IHeadersResponseBuilder WithSuccess()
+ {
+ return WithStatusCode(200);
+ }
+
+ ///
+ /// The with NotFound status code.
+ ///
+ /// The .
+ public static IHeadersResponseBuilder WithNotFound()
+ {
+ return WithStatusCode(404);
+ }
+
+ ///
+ /// The with status code.
+ ///
+ ///
+ /// The code.
+ ///
+ ///
+ /// The .
+ ///
+ public static IHeadersResponseBuilder WithStatusCode(int code)
+ {
+ var response = new Response { StatusCode = code };
+ return new Responses(response);
+ }
+
+ ///
+ /// The provide response.
+ ///
+ ///
+ /// The request.
+ ///
+ ///
+ /// The .
+ ///
+ public async Task ProvideResponse(Request request)
+ {
+ await Task.Delay(_delay);
+ return _response;
+ }
+
+ ///
+ /// The with header.
+ ///
+ ///
+ /// The name.
+ ///
+ ///
+ /// The value.
+ ///
+ ///
+ /// The .
+ ///
+ public IHeadersResponseBuilder WithHeader(string name, string value)
+ {
+ _response.AddHeader(name, value);
+ return this;
+ }
+
+ ///
+ /// The with body.
+ ///
+ ///
+ /// The body.
+ ///
+ ///
+ /// The .
+ ///
+ public IDelayResponseBuilder WithBody(string body)
+ {
+ _response.Body = body;
+ return this;
+ }
+
+ ///
+ /// The after delay.
+ ///
+ ///
+ /// The delay.
+ ///
+ ///
+ /// The .
+ ///
+ public IProvideResponses AfterDelay(TimeSpan delay)
+ {
+ _delay = delay;
+ return this;
+ }
+ }
+}
diff --git a/src/WireMock/Route.cs b/src/WireMock/Route.cs
new file mode 100644
index 00000000..4f1d3f9f
--- /dev/null
+++ b/src/WireMock/Route.cs
@@ -0,0 +1,78 @@
+using System.Diagnostics.CodeAnalysis;
+using System.Threading.Tasks;
+
+[module:
+ SuppressMessage("StyleCop.CSharp.ReadabilityRules",
+ "SA1101:PrefixLocalCallsWithThis",
+ Justification = "Reviewed. Suppression is OK here, as it conflicts with internal naming rules.")]
+[module:
+ SuppressMessage("StyleCop.CSharp.NamingRules",
+ "SA1309:FieldNamesMustNotBeginWithUnderscore",
+ Justification = "Reviewed. Suppression is OK here, as it conflicts with internal naming rules.")]
+[module:
+ SuppressMessage("StyleCop.CSharp.DocumentationRules",
+ "SA1633:FileMustHaveHeader",
+ Justification = "Reviewed. Suppression is OK here, as unknown copyright and company.")]
+// ReSharper disable ArrangeThisQualifier
+// ReSharper disable InconsistentNaming
+namespace WireMock
+{
+ ///
+ /// The route.
+ ///
+ public class Route
+ {
+ ///
+ /// The _request spec.
+ ///
+ private readonly ISpecifyRequests _requestSpec;
+
+ ///
+ /// The _provider.
+ ///
+ private readonly IProvideResponses _provider;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// The request spec.
+ ///
+ ///
+ /// The provider.
+ ///
+ public Route(ISpecifyRequests requestSpec, IProvideResponses provider)
+ {
+ _requestSpec = requestSpec;
+ _provider = provider;
+ }
+
+ ///
+ /// The response to.
+ ///
+ ///
+ /// The request.
+ ///
+ ///
+ /// The .
+ ///
+ public Task ResponseTo(Request request)
+ {
+ return _provider.ProvideResponse(request);
+ }
+
+ ///
+ /// The is request handled.
+ ///
+ ///
+ /// The request.
+ ///
+ ///
+ /// The .
+ ///
+ public bool IsRequestHandled(Request request)
+ {
+ return _requestSpec.IsSatisfiedBy(request);
+ }
+ }
+}
diff --git a/src/WireMock/RouteRegistrationCallback.cs b/src/WireMock/RouteRegistrationCallback.cs
new file mode 100644
index 00000000..738ea206
--- /dev/null
+++ b/src/WireMock/RouteRegistrationCallback.cs
@@ -0,0 +1,17 @@
+using System.Diagnostics.CodeAnalysis;
+
+[module:
+ SuppressMessage("StyleCop.CSharp.DocumentationRules",
+ "SA1633:FileMustHaveHeader",
+ Justification = "Reviewed. Suppression is OK here, as unknown copyright and company.")]
+
+namespace WireMock
+{
+ ///
+ /// The registration callback.
+ ///
+ ///
+ /// The route.
+ ///
+ public delegate void RegistrationCallback(Route route);
+}
diff --git a/src/WireMock/WildcardPatternMatcher.cs b/src/WireMock/WildcardPatternMatcher.cs
new file mode 100644
index 00000000..5c54eaae
--- /dev/null
+++ b/src/WireMock/WildcardPatternMatcher.cs
@@ -0,0 +1,74 @@
+using System.Diagnostics.CodeAnalysis;
+
+[module:
+ SuppressMessage("StyleCop.CSharp.DocumentationRules",
+ "SA1633:FileMustHaveHeader",
+ Justification = "Reviewed. Suppression is OK here, as unknown copyright and company.")]
+[module:
+ SuppressMessage("StyleCop.CSharp.DocumentationRules",
+ "SA1650:ElementDocumentationMustBeSpelledCorrectly",
+ Justification = "Reviewed. Suppression is OK here.")]
+
+namespace WireMock
+{
+ ///
+ /// The wildcard pattern matcher.
+ ///
+ public static class WildcardPatternMatcher
+ {
+ ///
+ /// The match wildcard string.
+ ///
+ ///
+ /// The pattern.
+ ///
+ ///
+ /// The input.
+ ///
+ ///
+ /// The .
+ ///
+ ///
+ /// Copy/paste from http://www.codeproject.com/Tips/57304/Use-wildcard-characters-and-to-compare-strings
+ ///
+ public static bool MatchWildcardString(string pattern, string input)
+ {
+ if (string.CompareOrdinal(pattern, input) == 0)
+ {
+ return true;
+ }
+
+ if (string.IsNullOrEmpty(input))
+ {
+ return string.IsNullOrEmpty(pattern.Trim('*'));
+ }
+
+ if (pattern.Length == 0)
+ {
+ return false;
+ }
+
+ if (pattern[0] == '?')
+ {
+ return MatchWildcardString(pattern.Substring(1), input.Substring(1));
+ }
+
+ if (pattern[pattern.Length - 1] == '?')
+ {
+ return MatchWildcardString(pattern.Substring(0, pattern.Length - 1), input.Substring(0, input.Length - 1));
+ }
+
+ if (pattern[0] == '*')
+ {
+ return MatchWildcardString(pattern.Substring(1), input) || MatchWildcardString(pattern, input.Substring(1));
+ }
+
+ if (pattern[pattern.Length - 1] == '*')
+ {
+ return MatchWildcardString(pattern.Substring(0, pattern.Length - 1), input) || MatchWildcardString(pattern, input.Substring(0, input.Length - 1));
+ }
+
+ return pattern[0] == input[0] && MatchWildcardString(pattern.Substring(1), input.Substring(1));
+ }
+ }
+}
diff --git a/src/WireMock/WireMock.xproj b/src/WireMock/WireMock.xproj
new file mode 100644
index 00000000..6349f5bb
--- /dev/null
+++ b/src/WireMock/WireMock.xproj
@@ -0,0 +1,19 @@
+
+
+
+ 14.0.25420
+ $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)
+
+
+
+ d3804228-91f4-4502-9595-39584e5a01ad
+ WireMock
+ .\obj
+ .\bin\
+
+
+
+ 2.0
+
+
+
\ No newline at end of file
diff --git a/src/WireMock/netstandard1.3.txt b/src/WireMock/netstandard1.3.txt
new file mode 100644
index 00000000..77ffd0c5
--- /dev/null
+++ b/src/WireMock/netstandard1.3.txt
@@ -0,0 +1,15 @@
+,
+ "netstandard1.3": {
+ "buildOptions": { "define": [ "NETSTANDARD" ] },
+ "imports": [
+ "dotnet5.4"
+ ],
+ "dependencies": {
+ "System.Collections.Concurrent": "4.3.0",
+ "System.Diagnostics.Tools": "4.3.0",
+ "System.Linq": "4.3.0",
+ "System.Net.Http": "4.3.0",
+ "System.Net.Sockets": "4.3.0",
+ "System.Threading.Tasks": "4.3.0"
+ }
+ }
\ No newline at end of file
diff --git a/src/WireMock/project.json b/src/WireMock/project.json
new file mode 100644
index 00000000..834f983c
--- /dev/null
+++ b/src/WireMock/project.json
@@ -0,0 +1,37 @@
+{
+ "version": "1.0.0.9",
+ "title": "WireMock.Net",
+ "description": "xxxx",
+ "authors": [ "Alexandre Victoor", "Stef Heyenrath" ],
+
+ "packOptions": {
+ "summary": "This is a .NET Core port of the the Microsoft assembly for the .Net 4.0 Dynamic language functionality.",
+ "tags": [ "system", "linq", "dynamic", "core" ],
+ "owners": [ "Stef Heyenrath" ],
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/StefH/WireMock.Net"
+ },
+ "projectUrl": "https://github.com/StefH/WireMock.Net",
+ "licenseUrl": "https://github.com/StefH/WireMock.Net/blob/master/licence.txt",
+ "releaseNotes": ""
+ },
+
+ "buildOptions": {
+ "xmlDoc": true
+ },
+
+ "dependencies": {
+ "JetBrains.Annotations": {
+ "version": "10.2.1",
+ "type": "build"
+ }
+ },
+
+ "frameworks": {
+ "net45": {
+ "frameworkAssemblies": {
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/test/WireMock.Net.Tests/FluentMockServerTests.cs b/test/WireMock.Net.Tests/FluentMockServerTests.cs
new file mode 100644
index 00000000..0093c816
--- /dev/null
+++ b/test/WireMock.Net.Tests/FluentMockServerTests.cs
@@ -0,0 +1,225 @@
+using System;
+using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
+using System.Linq;
+using System.Net;
+using System.Net.Http;
+using System.Threading.Tasks;
+using NFluent;
+using NUnit.Framework;
+
+[module:
+ SuppressMessage("StyleCop.CSharp.ReadabilityRules",
+ "SA1101:PrefixLocalCallsWithThis",
+ Justification = "Reviewed. Suppression is OK here, as it conflicts with internal naming rules.")]
+[module:
+ SuppressMessage("StyleCop.CSharp.NamingRules",
+ "SA1309:FieldNamesMustNotBeginWithUnderscore",
+ Justification = "Reviewed. Suppression is OK here, as it conflicts with internal naming rules.")]
+[module:
+ SuppressMessage("StyleCop.CSharp.DocumentationRules",
+ "SA1600:ElementsMustBeDocumented",
+ Justification = "Reviewed. Suppression is OK here, as it's a tests class.")]
+[module:
+ SuppressMessage("StyleCop.CSharp.DocumentationRules",
+ "SA1633:FileMustHaveHeader",
+ Justification = "Reviewed. Suppression is OK here, as unknown copyright and company.")]
+// ReSharper disable ArrangeThisQualifier
+// ReSharper disable InconsistentNaming
+namespace WireMock.Net.Tests
+{
+ [TestFixture]
+ [Timeout(5000)]
+ public class FluentMockServerTests
+ {
+ private FluentMockServer _server;
+
+ [Test]
+ public async Task Should_respond_to_request()
+ {
+ // given
+ _server = FluentMockServer.Start();
+
+ _server
+ .Given(Requests
+ .WithUrl("/foo")
+ .UsingGet())
+ .RespondWith(Responses
+ .WithStatusCode(200)
+ .WithBody(@"{ msg: ""Hello world!""}"));
+
+ // when
+ var response
+ = await new HttpClient().GetStringAsync("http://localhost:" + _server.Port + "/foo");
+
+ // then
+ Check.That(response).IsEqualTo(@"{ msg: ""Hello world!""}");
+ }
+
+ [Test]
+ public async Task Should_respond_404_for_unexpected_request()
+ {
+ // given
+ _server = FluentMockServer.Start();
+
+ // when
+ var response
+ = await new HttpClient().GetAsync("http://localhost:" + _server.Port + "/foo");
+
+ // then
+ Check.That(response.StatusCode).IsEqualTo(HttpStatusCode.NotFound);
+ Check.That((int)response.StatusCode).IsEqualTo(404);
+ }
+
+ [Test]
+ public async Task Should_record_requests_in_the_requestlogs()
+ {
+ // given
+ _server = FluentMockServer.Start();
+
+ // when
+ await new HttpClient().GetAsync("http://localhost:" + _server.Port + "/foo");
+
+ // then
+ Check.That(_server.RequestLogs).HasSize(1);
+ var requestLogged = _server.RequestLogs.First();
+ Check.That(requestLogged.Verb).IsEqualTo("get");
+ Check.That(requestLogged.Body).IsEmpty();
+ }
+
+ [Test]
+ public async Task Should_find_a_request_satisfying_a_request_spec()
+ {
+ // given
+ _server = FluentMockServer.Start();
+
+ // when
+ await new HttpClient().GetAsync("http://localhost:" + _server.Port + "/foo");
+ await new HttpClient().GetAsync("http://localhost:" + _server.Port + "/bar");
+
+ // then
+ var result = _server.SearchLogsFor(Requests.WithUrl("/b*"));
+ Check.That(result).HasSize(1);
+ var requestLogged = result.First();
+ Check.That(requestLogged.Url).IsEqualTo("/bar");
+ }
+
+ [Test]
+ public async Task Should_reset_requestlogs()
+ {
+ // given
+ _server = FluentMockServer.Start();
+
+ // when
+ await new HttpClient().GetAsync("http://localhost:" + _server.Port + "/foo");
+ _server.Reset();
+
+ // then
+ Check.That(_server.RequestLogs).IsEmpty();
+ }
+
+ [Test]
+ public void Should_reset_routes()
+ {
+ // given
+ _server = FluentMockServer.Start();
+
+ _server
+ .Given(Requests
+ .WithUrl("/foo")
+ .UsingGet())
+ .RespondWith(Responses
+ .WithStatusCode(200)
+ .WithBody(@"{ msg: ""Hello world!""}"));
+
+ // when
+ _server.Reset();
+
+ // then
+ Check.ThatAsyncCode(() => new HttpClient().GetStringAsync("http://localhost:" + _server.Port + "/foo"))
+ .ThrowsAny();
+ }
+
+ [Test]
+ public async Task Should_respond_a_redirect_without_body()
+ {
+ // given
+ _server = FluentMockServer.Start();
+
+ _server
+ .Given(Requests
+ .WithUrl("/foo")
+ .UsingGet())
+ .RespondWith(Responses
+ .WithStatusCode(307)
+ .WithHeader("Location", "/bar"));
+ _server
+ .Given(Requests
+ .WithUrl("/bar")
+ .UsingGet())
+ .RespondWith(Responses
+ .WithStatusCode(200)
+ .WithBody("REDIRECT SUCCESSFUL"));
+
+ // when
+ var response
+ = await new HttpClient().GetStringAsync("http://localhost:" + _server.Port + "/foo");
+
+ // then
+ Check.That(response).IsEqualTo("REDIRECT SUCCESSFUL");
+ }
+
+ [Test]
+ public async Task Should_delay_responses_for_a_given_route()
+ {
+ // given
+ _server = FluentMockServer.Start();
+
+ _server
+ .Given(Requests
+ .WithUrl("/*"))
+ .RespondWith(Responses
+ .WithStatusCode(200)
+ .WithBody(@"{ msg: ""Hello world!""}")
+ .AfterDelay(TimeSpan.FromMilliseconds(2000)));
+
+ // when
+ var watch = new Stopwatch();
+ watch.Start();
+ await new HttpClient().GetStringAsync("http://localhost:" + _server.Port + "/foo");
+ watch.Stop();
+
+ // then
+ Check.That(watch.ElapsedMilliseconds).IsGreaterThan(2000);
+ }
+
+ [Test]
+ public async Task Should_delay_responses()
+ {
+ // given
+ _server = FluentMockServer.Start();
+ _server.AddRequestProcessingDelay(TimeSpan.FromMilliseconds(2000));
+ _server
+ .Given(Requests
+ .WithUrl("/*"))
+ .RespondWith(Responses
+ .WithStatusCode(200)
+ .WithBody(@"{ msg: ""Hello world!""}"));
+
+ // when
+ var watch = new Stopwatch();
+ watch.Start();
+ await new HttpClient().GetStringAsync("http://localhost:" + _server.Port + "/foo");
+ watch.Stop();
+
+ // then
+ Check.That(watch.ElapsedMilliseconds).IsGreaterThan(2000);
+ }
+
+ [TearDown]
+ public void ShutdownServer()
+ {
+ _server.Stop();
+ }
+ }
+}
diff --git a/test/WireMock.Net.Tests/Http/TinyHttpServerTests.cs b/test/WireMock.Net.Tests/Http/TinyHttpServerTests.cs
new file mode 100644
index 00000000..0286c67b
--- /dev/null
+++ b/test/WireMock.Net.Tests/Http/TinyHttpServerTests.cs
@@ -0,0 +1,39 @@
+using System.Diagnostics.CodeAnalysis;
+using System.Net.Http;
+using NFluent;
+using NUnit.Framework;
+using WireMock.Http;
+
+[module:
+ SuppressMessage("StyleCop.CSharp.DocumentationRules",
+ "SA1600:ElementsMustBeDocumented",
+ Justification = "Reviewed. Suppression is OK here, as it's a tests class.")]
+[module:
+ SuppressMessage("StyleCop.CSharp.DocumentationRules",
+ "SA1633:FileMustHaveHeader",
+ Justification = "Reviewed. Suppression is OK here, as unknown copyright and company.")]
+
+namespace WireMock.Net.Tests.Http
+{
+ [TestFixture]
+ public class TinyHttpServerTests
+ {
+ [Test]
+ public void Should_Call_Handler_on_Request()
+ {
+ // given
+ var port = Ports.FindFreeTcpPort();
+ bool called = false;
+ var urlPrefix = "http://localhost:" + port + "/";
+ var server = new TinyHttpServer(urlPrefix, ctx => called = true);
+ server.Start();
+
+ // when
+ var httpClient = new HttpClient();
+ httpClient.GetAsync(urlPrefix).Wait(3000);
+
+ // then
+ Check.That(called).IsTrue();
+ }
+ }
+}
diff --git a/test/WireMock.Net.Tests/HttpListenerRequestMapperTests.cs b/test/WireMock.Net.Tests/HttpListenerRequestMapperTests.cs
new file mode 100644
index 00000000..564bd62d
--- /dev/null
+++ b/test/WireMock.Net.Tests/HttpListenerRequestMapperTests.cs
@@ -0,0 +1,166 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
+using System.Net;
+using System.Net.Http;
+using System.Threading.Tasks;
+using NFluent;
+using NUnit.Framework;
+using WireMock.Http;
+
+[module:
+ SuppressMessage("StyleCop.CSharp.ReadabilityRules",
+ "SA1101:PrefixLocalCallsWithThis",
+ Justification = "Reviewed. Suppression is OK here, as it conflicts with internal naming rules.")]
+[module:
+ SuppressMessage("StyleCop.CSharp.NamingRules",
+ "SA1309:FieldNamesMustNotBeginWithUnderscore",
+ Justification = "Reviewed. Suppression is OK here, as it conflicts with internal naming rules.")]
+[module:
+ SuppressMessage("StyleCop.CSharp.DocumentationRules",
+ "SA1600:ElementsMustBeDocumented",
+ Justification = "Reviewed. Suppression is OK here, as it's a tests class.")]
+[module:
+ SuppressMessage("StyleCop.CSharp.DocumentationRules",
+ "SA1633:FileMustHaveHeader",
+ Justification = "Reviewed. Suppression is OK here, as unknown copyright and company.")]
+// ReSharper disable ArrangeThisQualifier
+// ReSharper disable InconsistentNaming
+namespace WireMock.Net.Tests
+{
+ [TestFixture]
+ public class HttpListenerRequestMapperTests
+ {
+ private MapperServer _server;
+
+ [SetUp]
+ public void StartListenerServer()
+ {
+ _server = MapperServer.Start();
+ }
+
+ [Test]
+ public async Task Should_map_uri_from_listener_request()
+ {
+ // given
+ var client = new HttpClient();
+
+ // when
+ await client.GetAsync(MapperServer.UrlPrefix + "toto");
+
+ // then
+ Check.That(MapperServer.LastRequest).IsNotNull();
+ Check.That(MapperServer.LastRequest.Url).IsEqualTo("/toto");
+ }
+
+ [Test]
+ public async Task Should_map_verb_from_listener_request()
+ {
+ // given
+ var client = new HttpClient();
+
+ // when
+ await client.PutAsync(MapperServer.UrlPrefix, new StringContent("Hello!"));
+
+ // then
+ Check.That(MapperServer.LastRequest).IsNotNull();
+ Check.That(MapperServer.LastRequest.Verb).IsEqualTo("put");
+ }
+
+ [Test]
+ public async Task Should_map_body_from_listener_request()
+ {
+ // given
+ var client = new HttpClient();
+
+ // when
+ await client.PutAsync(MapperServer.UrlPrefix, new StringContent("Hello!"));
+
+ // then
+ Check.That(MapperServer.LastRequest).IsNotNull();
+ Check.That(MapperServer.LastRequest.Body).IsEqualTo("Hello!");
+ }
+
+ [Test]
+ public async Task Should_map_headers_from_listener_request()
+ {
+ // given
+ var client = new HttpClient();
+ client.DefaultRequestHeaders.Add("X-Alex", "1706");
+
+ // when
+ await client.GetAsync(MapperServer.UrlPrefix);
+
+ // then
+ Check.That(MapperServer.LastRequest).IsNotNull();
+ Check.That(MapperServer.LastRequest.Headers).Not.IsNullOrEmpty();
+ Check.That(MapperServer.LastRequest.Headers.Contains(new KeyValuePair("x-alex", "1706"))).IsTrue();
+ }
+
+ [Test]
+ public async Task Should_map_params_from_listener_request()
+ {
+ // given
+ var client = new HttpClient();
+
+ // when
+ await client.GetAsync(MapperServer.UrlPrefix + "index.html?id=toto");
+
+ // then
+ Check.That(MapperServer.LastRequest).IsNotNull();
+ Check.That(MapperServer.LastRequest.Path).EndsWith("/index.html");
+ Check.That(MapperServer.LastRequest.GetParameter("id")).HasSize(1);
+ }
+
+ [TearDown]
+ public void StopListenerServer()
+ {
+ _server.Stop();
+ }
+
+ private class MapperServer : TinyHttpServer
+ {
+ private static volatile Request _lastRequest;
+
+ private MapperServer(string urlPrefix, Action httpHandler) : base(urlPrefix, httpHandler)
+ {
+ }
+
+ public static Request LastRequest
+ {
+ get
+ {
+ return _lastRequest;
+ }
+
+ private set
+ {
+ _lastRequest = value;
+ }
+ }
+
+ public static string UrlPrefix { get; private set; }
+
+ public static new MapperServer Start()
+ {
+ var port = Ports.FindFreeTcpPort();
+ UrlPrefix = "http://localhost:" + port + "/";
+ var server = new MapperServer(
+ UrlPrefix,
+ context =>
+ {
+ LastRequest = new HttpListenerRequestMapper().Map(context.Request);
+ context.Response.Close();
+ });
+ ((TinyHttpServer)server).Start();
+ return server;
+ }
+
+ public new void Stop()
+ {
+ base.Stop();
+ LastRequest = null;
+ }
+ }
+ }
+}
diff --git a/test/WireMock.Net.Tests/HttpListenerResponseMapperTests.cs b/test/WireMock.Net.Tests/HttpListenerResponseMapperTests.cs
new file mode 100644
index 00000000..72d4358b
--- /dev/null
+++ b/test/WireMock.Net.Tests/HttpListenerResponseMapperTests.cs
@@ -0,0 +1,126 @@
+using System.Diagnostics.CodeAnalysis;
+using System.Net;
+using System.Net.Http;
+using System.Threading;
+using System.Threading.Tasks;
+using NFluent;
+using NUnit.Framework;
+using WireMock.Http;
+
+[module:
+ SuppressMessage("StyleCop.CSharp.ReadabilityRules",
+ "SA1101:PrefixLocalCallsWithThis",
+ Justification = "Reviewed. Suppression is OK here, as it conflicts with internal naming rules.")]
+[module:
+ SuppressMessage("StyleCop.CSharp.NamingRules",
+ "SA1309:FieldNamesMustNotBeginWithUnderscore",
+ Justification = "Reviewed. Suppression is OK here, as it conflicts with internal naming rules.")]
+[module:
+ SuppressMessage("StyleCop.CSharp.DocumentationRules",
+ "SA1600:ElementsMustBeDocumented",
+ Justification = "Reviewed. Suppression is OK here, as it's a tests class.")]
+[module:
+ SuppressMessage("StyleCop.CSharp.DocumentationRules",
+ "SA1633:FileMustHaveHeader",
+ Justification = "Reviewed. Suppression is OK here, as unknown copyright and company.")]
+// ReSharper disable ArrangeThisQualifier
+// ReSharper disable InconsistentNaming
+namespace WireMock.Net.Tests
+{
+ [TestFixture]
+ public class HttpListenerResponseMapperTests
+ {
+ private TinyHttpServer _server;
+ private Task _responseMsgTask;
+
+ [Test]
+ public void Should_map_status_code_from_original_response()
+ {
+ // given
+ var response = new Response { StatusCode = 404 };
+ var httpListenerResponse = CreateHttpListenerResponse();
+
+ // when
+ new HttpListenerResponseMapper().Map(response, httpListenerResponse);
+
+ // then
+ Check.That(httpListenerResponse.StatusCode).IsEqualTo(404);
+ }
+
+ [Test]
+ public void Should_map_headers_from_original_response()
+ {
+ // given
+ var response = new Response();
+ response.AddHeader("cache-control", "no-cache");
+ var httpListenerResponse = CreateHttpListenerResponse();
+
+ // when
+ new HttpListenerResponseMapper().Map(response, httpListenerResponse);
+
+ // then
+ Check.That(httpListenerResponse.Headers).HasSize(1);
+ Check.That(httpListenerResponse.Headers.Keys).Contains("cache-control");
+ Check.That(httpListenerResponse.Headers.Get("cache-control")).Contains("no-cache");
+ }
+
+ [Test]
+ public void Should_map_body_from_original_response()
+ {
+ // given
+ var response = new Response();
+ response.Body = "Hello !!!";
+ var httpListenerResponse = CreateHttpListenerResponse();
+
+ // when
+ new HttpListenerResponseMapper().Map(response, httpListenerResponse);
+
+ // then
+ var responseMessage = ToResponseMessage(httpListenerResponse);
+ Check.That(responseMessage).IsNotNull();
+ var contentTask = responseMessage.Content.ReadAsStringAsync();
+ Check.That(contentTask.Result).IsEqualTo("Hello !!!");
+ }
+
+ [TearDown]
+ public void StopServer()
+ {
+ if (_server != null)
+ {
+ _server.Stop();
+ }
+ }
+
+ ///
+ /// Dirty HACK to get HttpListenerResponse instances
+ ///
+ ///
+ /// The .
+ ///
+ public HttpListenerResponse CreateHttpListenerResponse()
+ {
+ var port = Ports.FindFreeTcpPort();
+ var urlPrefix = "http://localhost:" + port + "/";
+ var responseReady = new AutoResetEvent(false);
+ HttpListenerResponse response = null;
+ _server = new TinyHttpServer(
+ urlPrefix,
+ context =>
+ {
+ response = context.Response;
+ responseReady.Set();
+ });
+ _server.Start();
+ _responseMsgTask = new HttpClient().GetAsync(urlPrefix);
+ responseReady.WaitOne();
+ return response;
+ }
+
+ public HttpResponseMessage ToResponseMessage(HttpListenerResponse listenerResponse)
+ {
+ listenerResponse.Close();
+ _responseMsgTask.Wait();
+ return _responseMsgTask.Result;
+ }
+ }
+}
diff --git a/test/WireMock.Net.Tests/Properties/AssemblyInfo.cs b/test/WireMock.Net.Tests/Properties/AssemblyInfo.cs
new file mode 100644
index 00000000..420b1ae8
--- /dev/null
+++ b/test/WireMock.Net.Tests/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("WireMock.Net.Tests")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("WireMock.Net.Tests")]
+[assembly: AssemblyCopyright("Copyright © 2017")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("d8b56d28-33ce-4bef-97d4-7dd546e37f25")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/test/WireMock.Net.Tests/RequestTests.cs b/test/WireMock.Net.Tests/RequestTests.cs
new file mode 100644
index 00000000..5777f1c1
--- /dev/null
+++ b/test/WireMock.Net.Tests/RequestTests.cs
@@ -0,0 +1,42 @@
+using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
+using NFluent;
+using NUnit.Framework;
+
+[module:
+ SuppressMessage("StyleCop.CSharp.DocumentationRules",
+ "SA1600:ElementsMustBeDocumented",
+ Justification = "Reviewed. Suppression is OK here, as it's a tests class.")]
+[module:
+ SuppressMessage("StyleCop.CSharp.DocumentationRules",
+ "SA1633:FileMustHaveHeader",
+ Justification = "Reviewed. Suppression is OK here, as unknown copyright and company.")]
+// ReSharper disable InconsistentNaming
+namespace WireMock.Net.Tests
+{
+ [TestFixture]
+ public class RequestTests
+ {
+ [Test]
+ public void Should_handle_empty_query()
+ {
+ // given
+ var request = new Request("/foo", string.Empty, "blabla", "whatever", new Dictionary());
+
+ // then
+ Check.That(request.GetParameter("foo")).IsEmpty();
+ }
+
+ [Test]
+ public void Should_parse_query_params()
+ {
+ // given
+ var request = new Request("/foo", "foo=bar&multi=1&multi=2", "blabla", "whatever", new Dictionary());
+
+ // then
+ Check.That(request.GetParameter("foo")).Contains("bar");
+ Check.That(request.GetParameter("multi")).Contains("1");
+ Check.That(request.GetParameter("multi")).Contains("2");
+ }
+ }
+}
diff --git a/test/WireMock.Net.Tests/RequestsTests.cs b/test/WireMock.Net.Tests/RequestsTests.cs
new file mode 100644
index 00000000..d9606928
--- /dev/null
+++ b/test/WireMock.Net.Tests/RequestsTests.cs
@@ -0,0 +1,215 @@
+using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
+using NFluent;
+using NUnit.Framework;
+
+[module:
+ SuppressMessage("StyleCop.CSharp.DocumentationRules",
+ "SA1600:ElementsMustBeDocumented",
+ Justification = "Reviewed. Suppression is OK here, as it's a tests class.")]
+[module:
+ SuppressMessage("StyleCop.CSharp.DocumentationRules",
+ "SA1633:FileMustHaveHeader",
+ Justification = "Reviewed. Suppression is OK here, as unknown copyright and company.")]
+// ReSharper disable InconsistentNaming
+namespace WireMock.Net.Tests
+{
+ [TestFixture]
+ public class RequestsTests
+ {
+ [Test]
+ public void Should_specify_requests_matching_given_url()
+ {
+ // given
+ var spec = Requests.WithUrl("/foo");
+
+ // when
+ var request = new Request("/foo", string.Empty, "blabla", "whatever", new Dictionary());
+
+ // then
+ Check.That(spec.IsSatisfiedBy(request)).IsTrue();
+ }
+
+ [Test]
+ public void Should_specify_requests_matching_given_url_prefix()
+ {
+ // given
+ var spec = Requests.WithUrl("/foo*");
+
+ // when
+ var request = new Request("/foo/bar", string.Empty, "blabla", "whatever", new Dictionary());
+
+ // then
+ Check.That(spec.IsSatisfiedBy(request)).IsTrue();
+ }
+
+ [Test]
+ public void Should_exclude_requests_not_matching_given_url()
+ {
+ // given
+ var spec = Requests.WithUrl("/foo");
+
+ // when
+ var request = new Request("/bar", string.Empty, "blabla", "whatever", new Dictionary());
+
+ // then
+ Check.That(spec.IsSatisfiedBy(request)).IsFalse();
+ }
+
+ [Test]
+ public void Should_specify_requests_matching_given_path()
+ {
+ // given
+ var spec = Requests.WithPath("/foo");
+
+ // when
+ var request = new Request("/foo", "?param=1", "blabla", "whatever", new Dictionary());
+
+ // then
+ Check.That(spec.IsSatisfiedBy(request)).IsTrue();
+ }
+
+ [Test]
+ public void Should_specify_requests_matching_given_url_and_method()
+ {
+ // given
+ var spec = Requests.WithUrl("/foo").UsingPut();
+
+ // when
+ var request = new Request("/foo", string.Empty, "PUT", "whatever", new Dictionary());
+
+ // then
+ Check.That(spec.IsSatisfiedBy(request)).IsTrue();
+ }
+
+ [Test]
+ public void Should_exclude_requests_matching_given_url_but_not_http_method()
+ {
+ // given
+ var spec = Requests.WithUrl("/foo").UsingPut();
+
+ // when
+ var request = new Request("/foo", string.Empty, "POST", "whatever", new Dictionary());
+
+ // then
+ Check.That(spec.IsSatisfiedBy(request)).IsFalse();
+ }
+
+ [Test]
+ public void Should_exclude_requests_matching_given_http_method_but_not_url()
+ {
+ // given
+ var spec = Requests.WithUrl("/bar").UsingPut();
+
+ // when
+ var request = new Request("/foo", string.Empty, "PUT", "whatever", new Dictionary());
+
+ // then
+ Check.That(spec.IsSatisfiedBy(request)).IsFalse();
+ }
+
+ [Test]
+ public void Should_specify_requests_matching_given_url_and_headers()
+ {
+ // given
+ var spec = Requests.WithUrl("/foo").UsingAnyVerb().WithHeader("X-toto", "tata");
+
+ // when
+ var request = new Request("/foo", string.Empty, "PUT", "whatever", new Dictionary { { "X-toto", "tata" } });
+
+ // then
+ Check.That(spec.IsSatisfiedBy(request)).IsTrue();
+ }
+
+ [Test]
+ public void Should_exclude_requests_not_matching_given_headers()
+ {
+ // given
+ var spec = Requests.WithUrl("/foo").UsingAnyVerb().WithHeader("X-toto", "tatata");
+
+ // when
+ var request = new Request("/foo", string.Empty, "PUT", "whatever", new Dictionary { { "X-toto", "tata" } });
+
+ // then
+ Check.That(spec.IsSatisfiedBy(request)).IsFalse();
+ }
+
+ [Test]
+ public void Should_specify_requests_matching_given_header_prefix()
+ {
+ // given
+ var spec = Requests.WithUrl("/foo").UsingAnyVerb().WithHeader("X-toto", "tata*");
+
+ // when
+ var request = new Request("/foo", string.Empty, "PUT", "whatever", new Dictionary { { "X-toto", "tatata" } });
+
+ // then
+ Check.That(spec.IsSatisfiedBy(request)).IsTrue();
+ }
+
+ [Test]
+ public void Should_specify_requests_matching_given_body()
+ {
+ // given
+ var spec = Requests.WithUrl("/foo").UsingAnyVerb().WithBody(" Hello world! ");
+
+ // when
+ var request = new Request("/foo", string.Empty, "PUT", "Hello world!", new Dictionary { { "X-toto", "tatata" } });
+
+ // then
+ Check.That(spec.IsSatisfiedBy(request)).IsTrue();
+ }
+
+ [Test]
+ public void Should_specify_requests_matching_given_body_as_wildcard()
+ {
+ // given
+ var spec = Requests.WithUrl("/foo").UsingAnyVerb().WithBody("H*o wor?d!");
+
+ // when
+ var request = new Request("/foo", string.Empty, "PUT", "Hello world!", new Dictionary { { "X-toto", "tatata" } });
+
+ // then
+ Check.That(spec.IsSatisfiedBy(request)).IsTrue();
+ }
+
+ [Test]
+ public void Should_exclude_requests_not_matching_given_body()
+ {
+ // given
+ var spec = Requests.WithUrl("/foo").UsingAnyVerb().WithBody(" Hello world! ");
+
+ // when
+ var request = new Request("/foo", string.Empty, "PUT", "XXXXXXXXXXX", new Dictionary { { "X-toto", "tatata" } });
+
+ // then
+ Check.That(spec.IsSatisfiedBy(request)).IsFalse();
+ }
+
+ [Test]
+ public void Should_specify_requests_matching_given_params()
+ {
+ // given
+ var spec = Requests.WithPath("/foo").WithParam("bar", "1", "2");
+
+ // when
+ var request = new Request("/foo", "bar=1&bar=2", "Get", "Hello world!", new Dictionary());
+
+ // then
+ Check.That(spec.IsSatisfiedBy(request)).IsTrue();
+ }
+
+ [Test]
+ public void Should_exclude_requests_not_matching_given_params()
+ {
+ // given
+ var spec = Requests.WithPath("/foo").WithParam("bar", "1");
+
+ // when
+ var request = new Request("/foo", string.Empty, "PUT", "XXXXXXXXXXX", new Dictionary());
+
+ // then
+ Check.That(spec.IsSatisfiedBy(request)).IsFalse();
+ }
+ }
+}
diff --git a/test/WireMock.Net.Tests/WildcardPatternMatcherTests.cs b/test/WireMock.Net.Tests/WildcardPatternMatcherTests.cs
new file mode 100644
index 00000000..91486bf4
--- /dev/null
+++ b/test/WireMock.Net.Tests/WildcardPatternMatcherTests.cs
@@ -0,0 +1,48 @@
+using System.Diagnostics.CodeAnalysis;
+using NFluent;
+using NUnit.Framework;
+
+[module:
+ SuppressMessage("StyleCop.CSharp.DocumentationRules",
+ "SA1600:ElementsMustBeDocumented",
+ Justification = "Reviewed. Suppression is OK here, as it's a tests class.")]
+[module:
+ SuppressMessage("StyleCop.CSharp.DocumentationRules",
+ "SA1633:FileMustHaveHeader",
+ Justification = "Reviewed. Suppression is OK here, as unknown copyright and company.")]
+// ReSharper disable InconsistentNaming
+namespace WireMock.Net.Tests
+{
+ [TestFixture]
+ public class WildcardPatternMatcherTests
+ {
+ [Test]
+ public void Should_evaluate_patterns()
+ {
+ // Positive Tests
+ Check.That(WildcardPatternMatcher.MatchWildcardString("*", string.Empty)).IsTrue();
+ Check.That(WildcardPatternMatcher.MatchWildcardString("?", " ")).IsTrue();
+ Check.That(WildcardPatternMatcher.MatchWildcardString("*", "a")).IsTrue();
+ Check.That(WildcardPatternMatcher.MatchWildcardString("*", "ab")).IsTrue();
+ Check.That(WildcardPatternMatcher.MatchWildcardString("?", "a")).IsTrue();
+ Check.That(WildcardPatternMatcher.MatchWildcardString("*?", "abc")).IsTrue();
+ Check.That(WildcardPatternMatcher.MatchWildcardString("?*", "abc")).IsTrue();
+ Check.That(WildcardPatternMatcher.MatchWildcardString("*abc", "abc")).IsTrue();
+ Check.That(WildcardPatternMatcher.MatchWildcardString("*abc*", "abc")).IsTrue();
+ Check.That(WildcardPatternMatcher.MatchWildcardString("*a*bc*", "aXXXbc")).IsTrue();
+
+ // Negative Tests
+ Check.That(WildcardPatternMatcher.MatchWildcardString("*a", string.Empty)).IsFalse();
+ Check.That(WildcardPatternMatcher.MatchWildcardString("a*", string.Empty)).IsFalse();
+ Check.That(WildcardPatternMatcher.MatchWildcardString("?", string.Empty)).IsFalse();
+ Check.That(WildcardPatternMatcher.MatchWildcardString("*b*", "a")).IsFalse();
+ Check.That(WildcardPatternMatcher.MatchWildcardString("b*a", "ab")).IsFalse();
+ Check.That(WildcardPatternMatcher.MatchWildcardString("??", "a")).IsFalse();
+ Check.That(WildcardPatternMatcher.MatchWildcardString("*?", string.Empty)).IsFalse();
+ Check.That(WildcardPatternMatcher.MatchWildcardString("??*", "a")).IsFalse();
+ Check.That(WildcardPatternMatcher.MatchWildcardString("*abc", "abX")).IsFalse();
+ Check.That(WildcardPatternMatcher.MatchWildcardString("*abc*", "Xbc")).IsFalse();
+ Check.That(WildcardPatternMatcher.MatchWildcardString("*a*bc*", "ac")).IsFalse();
+ }
+ }
+}
diff --git a/test/WireMock.Net.Tests/WireMock.Net.Tests.csproj b/test/WireMock.Net.Tests/WireMock.Net.Tests.csproj
new file mode 100644
index 00000000..abf5ae38
--- /dev/null
+++ b/test/WireMock.Net.Tests/WireMock.Net.Tests.csproj
@@ -0,0 +1,85 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {D8B56D28-33CE-4BEF-97D4-7DD546E37F25}
+ Library
+ Properties
+ WireMock.Net.Tests
+ WireMock.Net.Tests
+ v4.5.2
+ 512
+
+
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+
+ ..\..\packages\Castle.Core.3.3.3\lib\net45\Castle.Core.dll
+ True
+
+
+ ..\..\packages\Moq.4.5.30\lib\net45\Moq.dll
+ True
+
+
+ ..\..\packages\NFluent.1.3.1.0\lib\net40\NFluent.dll
+ True
+
+
+ ..\..\packages\NUnit.3.6.0\lib\net45\nunit.framework.dll
+ True
+
+
+
+
+
+
+
+
+
+
+ ..\..\src\WireMock\bin\$(Configuration)\net45\WireMock.dll
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/test/WireMock.Net.Tests/packages.config b/test/WireMock.Net.Tests/packages.config
new file mode 100644
index 00000000..48aed189
--- /dev/null
+++ b/test/WireMock.Net.Tests/packages.config
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file