mirror of
https://github.com/wiremock/WireMock.Net.git
synced 2026-01-14 06:13:35 +01:00
Compare commits
87 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
02803562c6 | ||
|
|
7c51b2e73c | ||
|
|
6513ac9de8 | ||
|
|
c38373eb1f | ||
|
|
3112054f59 | ||
|
|
571f434b9a | ||
|
|
b25444d083 | ||
|
|
2944b5392a | ||
|
|
7fa0fbf7da | ||
|
|
b65cf9b61b | ||
|
|
bb35f55bbb | ||
|
|
ba86d81a17 | ||
|
|
4d2205dbd5 | ||
|
|
e6c951bb37 | ||
|
|
91a76f50a2 | ||
|
|
8567d28ac3 | ||
|
|
abde3cd83f | ||
|
|
4919e32264 | ||
|
|
a9a46057be | ||
|
|
ee77a5edac | ||
|
|
3ba29e580c | ||
|
|
8ecf7e414a | ||
|
|
f7628fc51e | ||
|
|
eb463cd6a9 | ||
|
|
63536c2ed0 | ||
|
|
51cc74ad20 | ||
|
|
c987a59ca8 | ||
|
|
1adc7340d1 | ||
|
|
8e1ceeb6b2 | ||
|
|
4f000f8880 | ||
|
|
0d83a4d8a1 | ||
|
|
2bac30b442 | ||
|
|
e1b557f875 | ||
|
|
8231b25805 | ||
|
|
44f00cb9fa | ||
|
|
ec2d105db2 | ||
|
|
9b99a7f26b | ||
|
|
841da80291 | ||
|
|
d1aa517f99 | ||
|
|
84901ab1e4 | ||
|
|
8a4e5b5790 | ||
|
|
de914ef24d | ||
|
|
517304b999 | ||
|
|
9a413a0940 | ||
|
|
7f790a5861 | ||
|
|
0a2cd88b5c | ||
|
|
f6b8986dd6 | ||
|
|
dee7c1bd18 | ||
|
|
5db727549c | ||
|
|
edb1354986 | ||
|
|
fddc85f48d | ||
|
|
7fe0f41a9d | ||
|
|
4fd05b0fd4 | ||
|
|
1f33e6a671 | ||
|
|
a334974bef | ||
|
|
1bf543a91e | ||
|
|
f56251a483 | ||
|
|
b036de5442 | ||
|
|
5761cf560e | ||
|
|
3cb1a6d2e1 | ||
|
|
4809fed513 | ||
|
|
3f84ba8b20 | ||
|
|
45aa83ee91 | ||
|
|
363d96e615 | ||
|
|
b4d5eb18d4 | ||
|
|
32f9171d01 | ||
|
|
f4ce2dbeb3 | ||
|
|
6c16d45256 | ||
|
|
1b2d20fd69 | ||
|
|
0d046daac5 | ||
|
|
e2552f03b9 | ||
|
|
847745c256 | ||
|
|
68f3a2ff76 | ||
|
|
95b93e80ce | ||
|
|
72335d48d6 | ||
|
|
1b2e5368a9 | ||
|
|
8dfcf7288f | ||
|
|
1d2c7fcf79 | ||
|
|
07488cd66b | ||
|
|
7884ee1eda | ||
|
|
8f4216eb3f | ||
|
|
984f2a8862 | ||
|
|
65c17ff519 | ||
|
|
9d1fd8fd51 | ||
|
|
8f02e99a00 | ||
|
|
c0872995b0 | ||
|
|
eeaeeb2c61 |
29
.runsettings
Normal file
29
.runsettings
Normal file
@@ -0,0 +1,29 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RunSettings>
|
||||
<DataCollectionRunSettings>
|
||||
<DataCollectors>
|
||||
<DataCollector friendlyName="Code Coverage"
|
||||
uri="datacollector://Microsoft/CodeCoverage/2.0"
|
||||
assemblyQualifiedName="Microsoft.VisualStudio.Coverage.DynamicCoverageDataCollector, Microsoft.VisualStudio.TraceCollector, Version=11.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<Configuration>
|
||||
<CodeCoverage>
|
||||
<ModulePaths>
|
||||
<Include>
|
||||
<ModulePath>.*\.dll$</ModulePath>
|
||||
</Include>
|
||||
<Exclude>
|
||||
<ModulePath>.*Validation.*</ModulePath>
|
||||
<ModulePath>.*\.tests.dll$</ModulePath>
|
||||
<ModulePath>.*simmetrics.*</ModulePath>
|
||||
</Exclude>
|
||||
</ModulePaths>
|
||||
<UseVerifiableInstrumentation>True</UseVerifiableInstrumentation>
|
||||
<AllowLowIntegrityProcesses>True</AllowLowIntegrityProcesses>
|
||||
<CollectFromChildProcesses>True</CollectFromChildProcesses>
|
||||
<CollectAspDotNet>False</CollectAspDotNet>
|
||||
</CodeCoverage>
|
||||
</Configuration>
|
||||
</DataCollector>
|
||||
</DataCollectors>
|
||||
</DataCollectionRunSettings>
|
||||
</RunSettings>
|
||||
29
README.md
29
README.md
@@ -1,2 +1,29 @@
|
||||
# WireMock.Net
|
||||
A C# .NET version based on http://wiremock.org
|
||||
A C# .NET version based on [mock4net](https://github.com/alexvictoor/mock4net) which mimics the functionality from the JAVA based http://WireMock.org
|
||||
|
||||
[](https://ci.appveyor.com/project/StefH/wiremock-net)
|
||||
|
||||
[](https://www.nuget.org/packages/WireMock.Net)
|
||||
|
||||
## Stubbing
|
||||
A core feature of WireMock.Net is the ability to return canned/predefined HTTP responses for requests matching criteria, see [Wiki : Stubbing](https://github.com/StefH/WireMock.Net/wiki/Stubbing).
|
||||
|
||||
## Using WireMock in UnitTest framework
|
||||
You can use your favorite test framework and use WireMock within your tests, see
|
||||
[Wiki : UnitTesting](https://github.com/StefH/WireMock.Net/wiki/Using-WireMock-in-UnitTests).
|
||||
|
||||
## Admin API Reference
|
||||
The WireMock admin API provides functionality to define the mappings via a http interface, see [Wiki : Admin API Reference](https://github.com/StefH/WireMock.Net/wiki/Admin-API-Reference).
|
||||
|
||||
## WireMock as a standalone process
|
||||
This is quite straight forward to launch a mock server within a console application, see [Wiki : standalone](https://github.com/StefH/WireMock.Net/wiki/WireMock-as-a-standalone-process).
|
||||
|
||||
### SSL
|
||||
You can start a standalone mock server listening for HTTPS requests. To do so, there is just a flag to set when creating the server:
|
||||
```csharp
|
||||
var server = FluentMockServer.Start(port: 8443, ssl: true);
|
||||
```
|
||||
Obviously you need a certificate registered on your box, properly associated with your application and the port number that will be used. This is not really specific to WireMock, not very straightforward and hence the following stackoverflow thread might come handy: [Httplistener with https support](http://stackoverflow.com/questions/11403333/httplistener-with-https-support)
|
||||
|
||||
## Simulating faults
|
||||
Currently not done - need to get rid of HttpListener and use lower level TcpListener in order to be able to implement this properly
|
||||
|
||||
29
ReSharper_WireMock.DotSettings
Normal file
29
ReSharper_WireMock.DotSettings
Normal file
@@ -0,0 +1,29 @@
|
||||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||
<s:Boolean x:Key="/Default/PatternsAndTemplates/StructuralSearch/Pattern/=99FF42DE363B4B439A0BF7D1DA299892/@KeyIndexDefined">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/PatternsAndTemplates/StructuralSearch/Pattern/=99FF42DE363B4B439A0BF7D1DA299892/CustomPatternPlaceholder/=args/@KeyIndexDefined">True</s:Boolean>
|
||||
<s:String x:Key="/Default/PatternsAndTemplates/StructuralSearch/Pattern/=99FF42DE363B4B439A0BF7D1DA299892/CustomPatternPlaceholder/=args/Properties/=Maximal/@EntryIndexedValue">-1</s:String>
|
||||
<s:String x:Key="/Default/PatternsAndTemplates/StructuralSearch/Pattern/=99FF42DE363B4B439A0BF7D1DA299892/CustomPatternPlaceholder/=args/Properties/=Minimal/@EntryIndexedValue">-1</s:String>
|
||||
<s:String x:Key="/Default/PatternsAndTemplates/StructuralSearch/Pattern/=99FF42DE363B4B439A0BF7D1DA299892/CustomPatternPlaceholder/=args/Type/@EntryValue">ArgumentPlaceholder</s:String>
|
||||
<s:Boolean x:Key="/Default/PatternsAndTemplates/StructuralSearch/Pattern/=99FF42DE363B4B439A0BF7D1DA299892/CustomPatternPlaceholder/=ArgumentNullException/@KeyIndexDefined">True</s:Boolean>
|
||||
<s:String x:Key="/Default/PatternsAndTemplates/StructuralSearch/Pattern/=99FF42DE363B4B439A0BF7D1DA299892/CustomPatternPlaceholder/=ArgumentNullException/Properties/=ExactType/@EntryIndexedValue">False</s:String>
|
||||
<s:String x:Key="/Default/PatternsAndTemplates/StructuralSearch/Pattern/=99FF42DE363B4B439A0BF7D1DA299892/CustomPatternPlaceholder/=ArgumentNullException/Properties/=Type/@EntryIndexedValue"></s:String>
|
||||
<s:String x:Key="/Default/PatternsAndTemplates/StructuralSearch/Pattern/=99FF42DE363B4B439A0BF7D1DA299892/CustomPatternPlaceholder/=ArgumentNullException/Type/@EntryValue">TypePlaceholder</s:String>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<s:Boolean x:Key="/Default/PatternsAndTemplates/StructuralSearch/Pattern/=99FF42DE363B4B439A0BF7D1DA299892/CustomPatternPlaceholder/=paramName/@KeyIndexDefined">True</s:Boolean>
|
||||
<s:String x:Key="/Default/PatternsAndTemplates/StructuralSearch/Pattern/=99FF42DE363B4B439A0BF7D1DA299892/CustomPatternPlaceholder/=paramName/Properties/=ExactType/@EntryIndexedValue">False</s:String>
|
||||
<s:String x:Key="/Default/PatternsAndTemplates/StructuralSearch/Pattern/=99FF42DE363B4B439A0BF7D1DA299892/CustomPatternPlaceholder/=paramName/Properties/=ExpressionType/@EntryIndexedValue"></s:String>
|
||||
|
||||
|
||||
<s:String x:Key="/Default/PatternsAndTemplates/StructuralSearch/Pattern/=99FF42DE363B4B439A0BF7D1DA299892/CustomPatternPlaceholder/=paramName/Type/@EntryValue">ExpressionPlaceholder</s:String>
|
||||
<s:Boolean x:Key="/Default/PatternsAndTemplates/StructuralSearch/Pattern/=99FF42DE363B4B439A0BF7D1DA299892/IsReplacePattern/@EntryValue">True</s:Boolean>
|
||||
<s:String x:Key="/Default/PatternsAndTemplates/StructuralSearch/Pattern/=99FF42DE363B4B439A0BF7D1DA299892/LanguageName/@EntryValue">CSHARP</s:String>
|
||||
<s:String x:Key="/Default/PatternsAndTemplates/StructuralSearch/Pattern/=99FF42DE363B4B439A0BF7D1DA299892/ReplacePattern/@EntryValue">Check.NotNull($paramName$, nameof($paramName$));</s:String>
|
||||
<s:String x:Key="/Default/PatternsAndTemplates/StructuralSearch/Pattern/=99FF42DE363B4B439A0BF7D1DA299892/SearchPattern/@EntryValue">if ($paramName$ == null) throw new $ArgumentNullException$($args$);</s:String>
|
||||
<s:String x:Key="/Default/PatternsAndTemplates/StructuralSearch/Pattern/=99FF42DE363B4B439A0BF7D1DA299892/Severity/@EntryValue">SUGGESTION</s:String></wpf:ResourceDictionary>
|
||||
68
WireMock.Net Solution.sln
Normal file
68
WireMock.Net Solution.sln
Normal file
@@ -0,0 +1,68 @@
|
||||
|
||||
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
|
||||
.runsettings = .runsettings
|
||||
appveyor.yml = appveyor.yml
|
||||
README.md = README.md
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "WireMock.Net", "src\WireMock.Net\WireMock.Net.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
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WireMock.Net.StandAlone", "src\WireMock.Net.StandAlone\WireMock.Net.StandAlone.csproj", "{668F689E-57B4-422E-8846-C0FF643CA999}"
|
||||
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
|
||||
{668F689E-57B4-422E-8846-C0FF643CA999}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{668F689E-57B4-422E-8846-C0FF643CA999}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{668F689E-57B4-422E-8846-C0FF643CA999}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{668F689E-57B4-422E-8846-C0FF643CA999}.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}
|
||||
{668F689E-57B4-422E-8846-C0FF643CA999} = {EF242EDF-7133-4277-9A0C-18744DE08707}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
6
WireMock.Net Solution.sln.DotSettings
Normal file
6
WireMock.Net Solution.sln.DotSettings
Normal file
@@ -0,0 +1,6 @@
|
||||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||
<s:String x:Key="/Default/Environment/InjectedLayers/FileInjectedLayer/=FBE2016FCEACD34DB66E1CC40B87F9F2/AbsolutePath/@EntryValue">C:\Users\azureuser\Documents\Github\WireMock.Net\ReSharper_WireMock.DotSettings</s:String>
|
||||
<s:String x:Key="/Default/Environment/InjectedLayers/FileInjectedLayer/=FBE2016FCEACD34DB66E1CC40B87F9F2/RelativePath/@EntryValue">..\ReSharper_WireMock.DotSettings</s:String>
|
||||
<s:Boolean x:Key="/Default/Environment/InjectedLayers/FileInjectedLayer/=FBE2016FCEACD34DB66E1CC40B87F9F2/@KeyIndexDefined">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/Environment/InjectedLayers/InjectedLayerCustomization/=FileFBE2016FCEACD34DB66E1CC40B87F9F2/@KeyIndexDefined">True</s:Boolean>
|
||||
<s:Double x:Key="/Default/Environment/InjectedLayers/InjectedLayerCustomization/=FileFBE2016FCEACD34DB66E1CC40B87F9F2/RelativePriority/@EntryValue">1</s:Double></wpf:ResourceDictionary>
|
||||
BIN
WireMock.Net-Logo.ico
Normal file
BIN
WireMock.Net-Logo.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 24 KiB |
BIN
WireMock.Net-Logo.png
Normal file
BIN
WireMock.Net-Logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 13 KiB |
38
appveyor.yml
Normal file
38
appveyor.yml
Normal file
@@ -0,0 +1,38 @@
|
||||
os: Visual Studio 2015
|
||||
|
||||
version: 1.0.0.{build}
|
||||
|
||||
configuration:
|
||||
- Debug
|
||||
- Release
|
||||
|
||||
platform: Any CPU
|
||||
|
||||
init:
|
||||
- ps: $Env:LABEL = "CI" + $Env:APPVEYOR_BUILD_NUMBER.PadLeft(5, "0")
|
||||
|
||||
install:
|
||||
- ps: Start-FileDownload 'https://download.microsoft.com/download/0/A/3/0A372822-205D-4A86-BFA7-084D2CBE9EDF/DotNetCore.1.0.1-SDK.1.0.0.Preview2-003133-x64.exe'
|
||||
- cmd: DotNetCore.1.0.1-SDK.1.0.0.Preview2-003133-x64 /quiet
|
||||
|
||||
environment:
|
||||
PATH: $(PATH);$(PROGRAMFILES)\dotnet\
|
||||
|
||||
before_build:
|
||||
- nuget restore .\examples\WireMock.Net.ConsoleApplication\WireMock.Net.ConsoleApplication.csproj -PackagesDirectory packages
|
||||
- nuget restore .\test\WireMock.Net.Tests\WireMock.Net.Tests.csproj -PackagesDirectory packages
|
||||
|
||||
build_script:
|
||||
- appveyor-retry dotnet restore .\src\WireMock.Net -v Minimal
|
||||
|
||||
- dotnet build .\src\WireMock.Net\project.json -c %CONFIGURATION%
|
||||
- cmd: msbuild .\examples\WireMock.Net.ConsoleApplication\WireMock.Net.ConsoleApplication.csproj /p:Configuration=%CONFIGURATION% /p:Platform=AnyCPU
|
||||
- cmd: msbuild .\test\WireMock.Net.Tests\WireMock.Net.Tests.csproj /p:Configuration=%CONFIGURATION% /p:Platform=AnyCPU
|
||||
|
||||
- dotnet pack -c %CONFIGURATION% --no-build --version-suffix %LABEL% -o .\artifacts .\src\WireMock.Net\project.json
|
||||
|
||||
artifacts:
|
||||
- path: artifacts\**\*.*
|
||||
|
||||
cache:
|
||||
- packages
|
||||
44
examples/WireMock.Net.ConsoleApp.netcore/Program.cs
Normal file
44
examples/WireMock.Net.ConsoleApp.netcore/Program.cs
Normal file
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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")]
|
||||
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
|
||||
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
|
||||
</PropertyGroup>
|
||||
|
||||
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.Props" Condition="'$(VSToolsPath)' != ''" />
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>bb7ad978-5c03-4d76-a903-3865802b45eb</ProjectGuid>
|
||||
<RootNamespace>WireMock.Net.ConsoleApp</RootNamespace>
|
||||
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">.\obj</BaseIntermediateOutputPath>
|
||||
<OutputPath Condition="'$(OutputPath)'=='' ">.\bin\</OutputPath>
|
||||
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.targets" Condition="'$(VSToolsPath)' != ''" />
|
||||
</Project>
|
||||
19
examples/WireMock.Net.ConsoleApp.netcore/project.json
Normal file
19
examples/WireMock.Net.ConsoleApp.netcore/project.json
Normal file
@@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
6
examples/WireMock.Net.ConsoleApplication/App.config
Normal file
6
examples/WireMock.Net.ConsoleApplication/App.config
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<startup>
|
||||
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" />
|
||||
</startup>
|
||||
</configuration>
|
||||
104
examples/WireMock.Net.ConsoleApplication/Program.cs
Normal file
104
examples/WireMock.Net.ConsoleApplication/Program.cs
Normal file
@@ -0,0 +1,104 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Newtonsoft.Json;
|
||||
using WireMock.Matchers;
|
||||
using WireMock.RequestBuilders;
|
||||
using WireMock.ResponseBuilders;
|
||||
using WireMock.Server;
|
||||
|
||||
namespace WireMock.Net.ConsoleApplication
|
||||
{
|
||||
static class Program
|
||||
{
|
||||
static void Main(params string[] args)
|
||||
{
|
||||
string url1 = "http://localhost:9090/";
|
||||
string url2 = "http://localhost:9091/";
|
||||
string url3 = "https://localhost:9443/";
|
||||
|
||||
var server = FluentMockServer.StartWithAdminInterface(url1, url2, url3);
|
||||
Console.WriteLine("FluentMockServer listening at {0}", string.Join(" and ", server.Urls));
|
||||
|
||||
server.SetBasicAuthentication("a", "b");
|
||||
|
||||
server.AllowPartialMapping();
|
||||
|
||||
server
|
||||
.Given(Request.Create().WithPath(p => p.Contains("x")).UsingGet())
|
||||
.AtPriority(4)
|
||||
.RespondWith(Response.Create()
|
||||
.WithStatusCode(200)
|
||||
.WithHeader("Content-Type", "application/json")
|
||||
.WithBody(@"{ ""result"": ""Contains x with FUNC 200""}"));
|
||||
|
||||
server
|
||||
.Given(Request.Create().WithPath("/data").UsingPost().WithBody(b => b.Contains("e")))
|
||||
.AtPriority(999)
|
||||
.RespondWith(Response.Create()
|
||||
.WithStatusCode(201)
|
||||
.WithHeader("Content-Type", "application/json")
|
||||
.WithBody(@"{ ""result"": ""data posted with FUNC 201""}"));
|
||||
|
||||
server
|
||||
.Given(Request.Create().WithPath("/data", "/ax").UsingPost().WithHeader("Content-Type", "application/json*"))
|
||||
.RespondWith(Response.Create()
|
||||
.WithStatusCode(201)
|
||||
.WithHeader("Content-Type", "application/json")
|
||||
.WithBody(@"{ ""result"": ""data posted with 201""}"));
|
||||
|
||||
server
|
||||
.Given(Request.Create().WithPath("/json").UsingPost().WithBody(new JsonPathMatcher("$.things[?(@.name == 'RequiredThing')]")))
|
||||
.RespondWith(Response.Create()
|
||||
.WithStatusCode(201)
|
||||
.WithHeader("Content-Type", "application/json")
|
||||
.WithBody(@"{ ""result"": ""json posted with 201""}"));
|
||||
|
||||
server
|
||||
.Given(Request.Create().WithPath("/json2").UsingPost().WithBody("x"))
|
||||
.RespondWith(Response.Create()
|
||||
.WithStatusCode(201)
|
||||
.WithHeader("Content-Type", "application/json")
|
||||
.WithBody(@"{ ""result"": ""json posted with x - 201""}"));
|
||||
|
||||
server
|
||||
.Given(Request.Create().WithPath("/data").UsingDelete())
|
||||
.RespondWith(Response.Create()
|
||||
.WithStatusCode(200)
|
||||
.WithHeader("Content-Type", "application/json")
|
||||
.WithBody(@"{ ""result"": ""data deleted with 200""}"));
|
||||
|
||||
server
|
||||
.Given(Request.Create().WithPath("/nobody").UsingGet())
|
||||
.RespondWith(Response.Create().WithDelay(TimeSpan.FromSeconds(1))
|
||||
.WithStatusCode(200));
|
||||
|
||||
server
|
||||
.Given(Request.Create().WithPath("/partial").UsingPost().WithBody(new SimMetricsMatcher(new [] { "cat", "dog" })))
|
||||
.RespondWith(Response.Create().WithStatusCode(200).WithBody("partial = 200"));
|
||||
|
||||
// http://localhost:8080/any/any?start=1000&stop=1&stop=2
|
||||
server
|
||||
.Given(Request.Create().WithPath("/*").UsingGet())
|
||||
.WithGuid("90356dba-b36c-469a-a17e-669cd84f1f05")
|
||||
.AtPriority(server.Mappings.Count() + 1)
|
||||
.RespondWith(Response.Create()
|
||||
.WithStatusCode(200)
|
||||
.WithHeader("Content-Type", "application/json")
|
||||
.WithHeader("Transformed-Postman-Token", "token is {{request.headers.Postman-Token}}")
|
||||
.WithBody(@"{""msg"": ""Hello world CATCH-ALL on /*, {{request.path}}, bykey={{request.query.start}}, bykey={{request.query.stop}}, byidx0={{request.query.stop.[0]}}, byidx1={{request.query.stop.[1]}}"" }")
|
||||
.WithTransformer()
|
||||
.WithDelay(TimeSpan.FromMilliseconds(100))
|
||||
);
|
||||
|
||||
Console.WriteLine("Press any key to stop the server");
|
||||
Console.ReadKey();
|
||||
|
||||
Console.WriteLine("Displaying all requests");
|
||||
var allRequests = server.LogEntries;
|
||||
Console.WriteLine(JsonConvert.SerializeObject(allRequests, Formatting.Indented));
|
||||
|
||||
Console.WriteLine("Press any key to quit");
|
||||
Console.ReadKey();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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("WireMock.Net.ConsoleApplication")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("WireMock.Net.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")]
|
||||
@@ -0,0 +1,74 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{668F689E-57B4-422E-8846-C0FF643CA268}</ProjectGuid>
|
||||
<OutputType>Exe</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>WireMock.Net.ConsoleApplication</RootNamespace>
|
||||
<AssemblyName>WireMock.Net.ConsoleApplication</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<ApplicationIcon>..\..\WireMock.Net-Logo.ico</ApplicationIcon>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Newtonsoft.Json, Version=6.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Newtonsoft.Json.6.0.8\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="SimMetrics.Net, Version=1.0.1.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\SimMetrics.Net.1.0.1.0\lib\net45\SimMetrics.Net.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="WireMock.Net">
|
||||
<HintPath>..\..\src\WireMock.Net\bin\$(Configuration)\net45\WireMock.Net.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Program.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="App.config" />
|
||||
<None Include="packages.config">
|
||||
<SubType>Designer</SubType>
|
||||
</None>
|
||||
<Content Include="__admin\mappings\11111110-a633-40e8-a244-5cb80bc0ab66.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
||||
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"Request": {
|
||||
"Path": {
|
||||
"Matchers": [
|
||||
{
|
||||
"Name": "WildcardMatcher",
|
||||
"Pattern": "/static/mapping"
|
||||
}
|
||||
]
|
||||
},
|
||||
"Methods": [
|
||||
"get"
|
||||
]
|
||||
},
|
||||
"Response": {
|
||||
"BodyAsJson": { "body": "static mapping" },
|
||||
"Headers": {
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
}
|
||||
}
|
||||
5
examples/WireMock.Net.ConsoleApplication/packages.config
Normal file
5
examples/WireMock.Net.ConsoleApplication/packages.config
Normal file
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Newtonsoft.Json" version="6.0.8" targetFramework="net452" />
|
||||
<package id="SimMetrics.Net" version="1.0.1.0" targetFramework="net452" />
|
||||
</packages>
|
||||
51
src/WireMock.Net.StandAlone/Program.cs
Normal file
51
src/WireMock.Net.StandAlone/Program.cs
Normal file
@@ -0,0 +1,51 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using CommandLineParser.Arguments;
|
||||
using CommandLineParser.Exceptions;
|
||||
using WireMock.Server;
|
||||
|
||||
namespace WireMock.Net.StandAlone
|
||||
{
|
||||
public class Program
|
||||
{
|
||||
private class Options
|
||||
{
|
||||
[ValueArgument(typeof(string), 'u', "Urls", Description = "URL(s) to listen on.", Optional = true, AllowMultiple = true)]
|
||||
public List<string> Urls { get; set; }
|
||||
|
||||
[SwitchArgument('p', "AllowPartialMapping", true, Description = "Allow Partial Mapping (default set to true).", Optional = true)]
|
||||
public bool AllowPartialMapping { get; set; }
|
||||
}
|
||||
|
||||
static void Main(params string[] args)
|
||||
{
|
||||
var options = new Options();
|
||||
var parser = new CommandLineParser.CommandLineParser();
|
||||
parser.ExtractArgumentAttributes(options);
|
||||
|
||||
try
|
||||
{
|
||||
parser.ParseCommandLine(args);
|
||||
|
||||
if (!options.Urls.Any())
|
||||
options.Urls.Add("http://localhost:9090/");
|
||||
|
||||
var server = FluentMockServer.StartWithAdminInterface(options.Urls.ToArray());
|
||||
|
||||
if (options.AllowPartialMapping)
|
||||
server.AllowPartialMapping();
|
||||
|
||||
Console.WriteLine("WireMock.Net server listening at {0}", string.Join(" and ", server.Urls));
|
||||
}
|
||||
catch (CommandLineException e)
|
||||
{
|
||||
Console.WriteLine(e.Message);
|
||||
parser.ShowUsage();
|
||||
}
|
||||
|
||||
Console.WriteLine("Press any key to stop the server");
|
||||
Console.ReadKey();
|
||||
}
|
||||
}
|
||||
}
|
||||
35
src/WireMock.Net.StandAlone/Properties/AssemblyInfo.cs
Normal file
35
src/WireMock.Net.StandAlone/Properties/AssemblyInfo.cs
Normal file
@@ -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("WireMock.Net.StandAlone")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("WireMock.Net.StandAlone")]
|
||||
[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-c0ff643ca999")]
|
||||
|
||||
// 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")]
|
||||
65
src/WireMock.Net.StandAlone/WireMock.Net.StandAlone.csproj
Normal file
65
src/WireMock.Net.StandAlone/WireMock.Net.StandAlone.csproj
Normal file
@@ -0,0 +1,65 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{668F689E-57B4-422E-8846-C0FF643CA999}</ProjectGuid>
|
||||
<OutputType>Exe</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>WireMock.Net.StandAlone</RootNamespace>
|
||||
<AssemblyName>WireMock.Net.StandAlone</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<ApplicationIcon>..\..\WireMock.Net-Logo.ico</ApplicationIcon>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="CommandLineArgumentsParser, Version=3.0.5.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\CommandLineArgumentsParser.3.0.5\lib\net45\CommandLineArgumentsParser.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="WireMock.Net">
|
||||
<HintPath>..\..\src\WireMock.Net\bin\$(Configuration)\net45\WireMock.Net.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Program.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="app.config" />
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
||||
3
src/WireMock.Net.StandAlone/app.config
Normal file
3
src/WireMock.Net.StandAlone/app.config
Normal file
@@ -0,0 +1,3 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
</configuration>
|
||||
4
src/WireMock.Net.StandAlone/packages.config
Normal file
4
src/WireMock.Net.StandAlone/packages.config
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="CommandLineArgumentsParser" version="3.0.5" targetFramework="net452" />
|
||||
</packages>
|
||||
32
src/WireMock.Net/Admin/Mappings/BodyModel.cs
Normal file
32
src/WireMock.Net/Admin/Mappings/BodyModel.cs
Normal file
@@ -0,0 +1,32 @@
|
||||
namespace WireMock.Admin.Mappings
|
||||
{
|
||||
/// <summary>
|
||||
/// Body Model
|
||||
/// </summary>
|
||||
public class BodyModel
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the matcher.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The matcher.
|
||||
/// </value>
|
||||
public MatcherModel Matcher { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the function.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The function.
|
||||
/// </value>
|
||||
public string Func { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the data function.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The data function.
|
||||
/// </value>
|
||||
public string DataFunc { get; set; }
|
||||
}
|
||||
}
|
||||
34
src/WireMock.Net/Admin/Mappings/CookieModel.cs
Normal file
34
src/WireMock.Net/Admin/Mappings/CookieModel.cs
Normal file
@@ -0,0 +1,34 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace WireMock.Admin.Mappings
|
||||
{
|
||||
/// <summary>
|
||||
/// Cookie Model
|
||||
/// </summary>
|
||||
public class CookieModel
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the name.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The name.
|
||||
/// </value>
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the matchers.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The matchers.
|
||||
/// </value>
|
||||
public IList<MatcherModel> Matchers { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the functions.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The functions.
|
||||
/// </value>
|
||||
public string[] Funcs { get; set; }
|
||||
}
|
||||
}
|
||||
23
src/WireMock.Net/Admin/Mappings/EncodingModel.cs
Normal file
23
src/WireMock.Net/Admin/Mappings/EncodingModel.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
namespace WireMock.Admin.Mappings
|
||||
{
|
||||
/// <summary>
|
||||
/// EncodingModel
|
||||
/// </summary>
|
||||
public class EncodingModel
|
||||
{
|
||||
/// <summary>
|
||||
/// Encoding CodePage
|
||||
/// </summary>
|
||||
public int CodePage { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Encoding EncodingName
|
||||
/// </summary>
|
||||
public string EncodingName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Encoding WebName
|
||||
/// </summary>
|
||||
public string WebName { get; set; }
|
||||
}
|
||||
}
|
||||
34
src/WireMock.Net/Admin/Mappings/HeaderModel.cs
Normal file
34
src/WireMock.Net/Admin/Mappings/HeaderModel.cs
Normal file
@@ -0,0 +1,34 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace WireMock.Admin.Mappings
|
||||
{
|
||||
/// <summary>
|
||||
/// Header Model
|
||||
/// </summary>
|
||||
public class HeaderModel
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the name.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The name.
|
||||
/// </value>
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the matchers.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The matchers.
|
||||
/// </value>
|
||||
public IList<MatcherModel> Matchers { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the functions.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The functions.
|
||||
/// </value>
|
||||
public string[] Funcs { get; set; }
|
||||
}
|
||||
}
|
||||
42
src/WireMock.Net/Admin/Mappings/MappingModel.cs
Normal file
42
src/WireMock.Net/Admin/Mappings/MappingModel.cs
Normal file
@@ -0,0 +1,42 @@
|
||||
using System;
|
||||
|
||||
namespace WireMock.Admin.Mappings
|
||||
{
|
||||
/// <summary>
|
||||
/// MappingModel
|
||||
/// </summary>
|
||||
public class MappingModel
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the unique identifier.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The unique identifier.
|
||||
/// </value>
|
||||
public Guid? Guid { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the priority.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The priority.
|
||||
/// </value>
|
||||
public int? Priority { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the request.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The request.
|
||||
/// </value>
|
||||
public RequestModel Request { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the response.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The response.
|
||||
/// </value>
|
||||
public ResponseModel Response { get; set; }
|
||||
}
|
||||
}
|
||||
40
src/WireMock.Net/Admin/Mappings/MatcherModel.cs
Normal file
40
src/WireMock.Net/Admin/Mappings/MatcherModel.cs
Normal file
@@ -0,0 +1,40 @@
|
||||
namespace WireMock.Admin.Mappings
|
||||
{
|
||||
/// <summary>
|
||||
/// MatcherModel
|
||||
/// </summary>
|
||||
public class MatcherModel
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the name.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The name.
|
||||
/// </value>
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the pattern.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The pattern.
|
||||
/// </value>
|
||||
public string Pattern { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the patterns.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The patterns.
|
||||
/// </value>
|
||||
public string[] Patterns { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the ignore case.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The ignore case.
|
||||
/// </value>
|
||||
public bool? IgnoreCase { get; set; }
|
||||
}
|
||||
}
|
||||
34
src/WireMock.Net/Admin/Mappings/ParamModel.cs
Normal file
34
src/WireMock.Net/Admin/Mappings/ParamModel.cs
Normal file
@@ -0,0 +1,34 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace WireMock.Admin.Mappings
|
||||
{
|
||||
/// <summary>
|
||||
/// Param Model
|
||||
/// </summary>
|
||||
public class ParamModel
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the name.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The name.
|
||||
/// </value>
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the values.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The values.
|
||||
/// </value>
|
||||
public IList<string> Values { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the functions.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The functions.
|
||||
/// </value>
|
||||
public string[] Funcs { get; set; }
|
||||
}
|
||||
}
|
||||
24
src/WireMock.Net/Admin/Mappings/PathModel.cs
Normal file
24
src/WireMock.Net/Admin/Mappings/PathModel.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
namespace WireMock.Admin.Mappings
|
||||
{
|
||||
/// <summary>
|
||||
/// PathModel
|
||||
/// </summary>
|
||||
public class PathModel
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the matchers.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The matchers.
|
||||
/// </value>
|
||||
public MatcherModel[] Matchers { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the functions.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The functions.
|
||||
/// </value>
|
||||
public string[] Funcs { get; set; }
|
||||
}
|
||||
}
|
||||
63
src/WireMock.Net/Admin/Mappings/RequestModel.cs
Normal file
63
src/WireMock.Net/Admin/Mappings/RequestModel.cs
Normal file
@@ -0,0 +1,63 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace WireMock.Admin.Mappings
|
||||
{
|
||||
/// <summary>
|
||||
/// RequestModel
|
||||
/// </summary>
|
||||
public class RequestModel
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the Path. (Can be a string or a PathModel)
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The URL.
|
||||
/// </value>
|
||||
public object Path { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Url. (Can be a string or a UrlModel)
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The URL.
|
||||
/// </value>
|
||||
public object Url { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The methods
|
||||
/// </summary>
|
||||
public string[] Methods { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Headers.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The Headers.
|
||||
/// </value>
|
||||
public IList<HeaderModel> Headers { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Cookies.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The Cookies.
|
||||
/// </value>
|
||||
public IList<CookieModel> Cookies { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Params.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The Headers.
|
||||
/// </value>
|
||||
public IList<ParamModel> Params { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the body.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The body.
|
||||
/// </value>
|
||||
public BodyModel Body { get; set; }
|
||||
}
|
||||
}
|
||||
74
src/WireMock.Net/Admin/Mappings/ResponseModel.cs
Normal file
74
src/WireMock.Net/Admin/Mappings/ResponseModel.cs
Normal file
@@ -0,0 +1,74 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace WireMock.Admin.Mappings
|
||||
{
|
||||
/// <summary>
|
||||
/// ResponseModel
|
||||
/// </summary>
|
||||
public class ResponseModel
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the HTTP status.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The HTTP status.
|
||||
/// </value>
|
||||
public int? StatusCode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the body.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The body.
|
||||
/// </value>
|
||||
public string Body { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the body.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The body.
|
||||
/// </value>
|
||||
public string BodyAsBase64 { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the body (as JSON object).
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The body.
|
||||
/// </value>
|
||||
public object BodyAsJson { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the body encoding.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The body encoding.
|
||||
/// </value>
|
||||
public EncodingModel BodyEncoding { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether [use transformer].
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <c>true</c> if [use transformer]; otherwise, <c>false</c>.
|
||||
/// </value>
|
||||
public bool UseTransformer { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the headers.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The headers.
|
||||
/// </value>
|
||||
public IDictionary<string, string> Headers { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the delay in milliseconds.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The delay in milliseconds.
|
||||
/// </value>
|
||||
public int? Delay { get; set; }
|
||||
}
|
||||
}
|
||||
24
src/WireMock.Net/Admin/Mappings/UrlModel.cs
Normal file
24
src/WireMock.Net/Admin/Mappings/UrlModel.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
namespace WireMock.Admin.Mappings
|
||||
{
|
||||
/// <summary>
|
||||
/// UrlModel
|
||||
/// </summary>
|
||||
public class UrlModel
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the matchers.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The matchers.
|
||||
/// </value>
|
||||
public MatcherModel[] Matchers { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the functions.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The functions.
|
||||
/// </value>
|
||||
public string[] Funcs { get; set; }
|
||||
}
|
||||
}
|
||||
50
src/WireMock.Net/Admin/Requests/LogEntryModel.cs
Normal file
50
src/WireMock.Net/Admin/Requests/LogEntryModel.cs
Normal file
@@ -0,0 +1,50 @@
|
||||
using System;
|
||||
|
||||
namespace WireMock.Admin.Requests
|
||||
{
|
||||
/// <summary>
|
||||
/// Request Log Model
|
||||
/// </summary>
|
||||
public class LogEntryModel
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the unique identifier.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The unique identifier.
|
||||
/// </value>
|
||||
public Guid Guid { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the request.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The request.
|
||||
/// </value>
|
||||
public LogRequestModel Request { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the response.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The response.
|
||||
/// </value>
|
||||
public LogResponseModel Response { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the mapping unique identifier.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The mapping unique identifier.
|
||||
/// </value>
|
||||
public Guid? MappingGuid { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the request match result.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The request match result.
|
||||
/// </value>
|
||||
public LogRequestMatchModel RequestMatchResult { get; set; }
|
||||
}
|
||||
}
|
||||
40
src/WireMock.Net/Admin/Requests/LogRequestMatchModel.cs
Normal file
40
src/WireMock.Net/Admin/Requests/LogRequestMatchModel.cs
Normal file
@@ -0,0 +1,40 @@
|
||||
namespace WireMock.Admin.Requests
|
||||
{
|
||||
/// <summary>
|
||||
/// LogRequestMatchModel
|
||||
/// </summary>
|
||||
public class LogRequestMatchModel
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the match-score.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The match-score.
|
||||
/// </value>
|
||||
public double TotalScore { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the total number of matches.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The total number of matches.
|
||||
/// </value>
|
||||
public int TotalNumber { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether this instance is perfect match.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <c>true</c> if this instance is perfect match; otherwise, <c>false</c>.
|
||||
/// </value>
|
||||
public bool IsPerfectMatch { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the match percentage.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The match percentage.
|
||||
/// </value>
|
||||
public double AverageTotalScore { get; set; }
|
||||
}
|
||||
}
|
||||
79
src/WireMock.Net/Admin/Requests/LogRequestModel.cs
Normal file
79
src/WireMock.Net/Admin/Requests/LogRequestModel.cs
Normal file
@@ -0,0 +1,79 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using WireMock.Admin.Mappings;
|
||||
using WireMock.Util;
|
||||
|
||||
namespace WireMock.Admin.Requests
|
||||
{
|
||||
/// <summary>
|
||||
/// RequestMessage Model
|
||||
/// </summary>
|
||||
public class LogRequestModel
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the DateTime.
|
||||
/// </summary>
|
||||
public DateTime DateTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Path.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The Path.
|
||||
/// </value>
|
||||
public string Path { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the absolete URL.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The absolete URL.
|
||||
/// </value>
|
||||
public string AbsoleteUrl { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the query.
|
||||
/// </summary>
|
||||
public IDictionary<string, WireMockList<string>> Query { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the method.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The method.
|
||||
/// </value>
|
||||
public string Method { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Headers.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The Headers.
|
||||
/// </value>
|
||||
public IDictionary<string, string> Headers { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Cookies.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The Cookies.
|
||||
/// </value>
|
||||
public IDictionary<string, string> Cookies { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the body.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The body.
|
||||
/// </value>
|
||||
public string Body { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the body encoding.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The body encoding.
|
||||
/// </value>
|
||||
public EncodingModel BodyEncoding { get; set; }
|
||||
}
|
||||
}
|
||||
36
src/WireMock.Net/Admin/Requests/LogResponseModel.cs
Normal file
36
src/WireMock.Net/Admin/Requests/LogResponseModel.cs
Normal file
@@ -0,0 +1,36 @@
|
||||
using System.Collections.Generic;
|
||||
using WireMock.Admin.Mappings;
|
||||
|
||||
namespace WireMock.Admin.Requests
|
||||
{
|
||||
/// <summary>
|
||||
/// Response MessageModel
|
||||
/// </summary>
|
||||
public class LogResponseModel
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the status code.
|
||||
/// </summary>
|
||||
public int StatusCode { get; set; } = 200;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the headers.
|
||||
/// </summary>
|
||||
public IDictionary<string, string> Headers { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the body.
|
||||
/// </summary>
|
||||
public string Body { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the original body.
|
||||
/// </summary>
|
||||
public string BodyOriginal { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the body.
|
||||
/// </summary>
|
||||
public EncodingModel BodyEncoding { get; set; }
|
||||
}
|
||||
}
|
||||
18
src/WireMock.Net/Admin/Settings/SettingsModel.cs
Normal file
18
src/WireMock.Net/Admin/Settings/SettingsModel.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
namespace WireMock.Admin.Settings
|
||||
{
|
||||
/// <summary>
|
||||
/// Settings
|
||||
/// </summary>
|
||||
public class SettingsModel
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the global delay in milliseconds.
|
||||
/// </summary>
|
||||
public int? GlobalProcessingDelay { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets if partial mapping is allowed.
|
||||
/// </summary>
|
||||
public bool? AllowPartialMapping { get; set; }
|
||||
}
|
||||
}
|
||||
24
src/WireMock.Net/DynamicResponseProvider.cs
Normal file
24
src/WireMock.Net/DynamicResponseProvider.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using JetBrains.Annotations;
|
||||
using WireMock.Validation;
|
||||
|
||||
namespace WireMock
|
||||
{
|
||||
internal class DynamicResponseProvider : IResponseProvider
|
||||
{
|
||||
private readonly Func<RequestMessage, ResponseMessage> _responseMessageFunc;
|
||||
|
||||
public DynamicResponseProvider([NotNull] Func<RequestMessage, ResponseMessage> responseMessageFunc)
|
||||
{
|
||||
Check.NotNull(responseMessageFunc, nameof(responseMessageFunc));
|
||||
|
||||
_responseMessageFunc = responseMessageFunc;
|
||||
}
|
||||
|
||||
public Task<ResponseMessage> ProvideResponse(RequestMessage requestMessage)
|
||||
{
|
||||
return Task.FromResult(_responseMessageFunc(requestMessage));
|
||||
}
|
||||
}
|
||||
}
|
||||
63
src/WireMock.Net/Extensions/DictionaryExtensions.cs
Normal file
63
src/WireMock.Net/Extensions/DictionaryExtensions.cs
Normal file
@@ -0,0 +1,63 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Dynamic;
|
||||
|
||||
namespace WireMock.Extensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Dictionary Extensions
|
||||
/// </summary>
|
||||
public static class DictionaryExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Converts IDictionary to an ExpandObject.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="dictionary">The dictionary.</param>
|
||||
/// <returns></returns>
|
||||
public static dynamic ToExpandoObject<T>(this IDictionary<string, T> dictionary)
|
||||
{
|
||||
dynamic expando = new ExpandoObject();
|
||||
var expandoDic = (IDictionary<string, object>)expando;
|
||||
|
||||
// go through the items in the dictionary and copy over the key value pairs)
|
||||
foreach (var kvp in dictionary)
|
||||
{
|
||||
// if the value can also be turned into an ExpandoObject, then do it!
|
||||
var value = kvp.Value as IDictionary<string, object>;
|
||||
if (value != null)
|
||||
{
|
||||
var expandoValue = value.ToExpandoObject();
|
||||
expandoDic.Add(kvp.Key, expandoValue);
|
||||
}
|
||||
else if (kvp.Value is ICollection)
|
||||
{
|
||||
// iterate through the collection and convert any strin-object dictionaries
|
||||
// along the way into expando objects
|
||||
var itemList = new List<object>();
|
||||
foreach (var item in (ICollection)kvp.Value)
|
||||
{
|
||||
var objects = item as IDictionary<string, object>;
|
||||
if (objects != null)
|
||||
{
|
||||
var expandoItem = objects.ToExpandoObject();
|
||||
itemList.Add(expandoItem);
|
||||
}
|
||||
else
|
||||
{
|
||||
itemList.Add(item);
|
||||
}
|
||||
}
|
||||
|
||||
expandoDic.Add(kvp.Key, itemList);
|
||||
}
|
||||
else
|
||||
{
|
||||
expandoDic.Add(kvp.Key, kvp.Value);
|
||||
}
|
||||
}
|
||||
|
||||
return expando;
|
||||
}
|
||||
}
|
||||
}
|
||||
34
src/WireMock.Net/Http/PortUtil.cs
Normal file
34
src/WireMock.Net/Http/PortUtil.cs
Normal file
@@ -0,0 +1,34 @@
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
|
||||
namespace WireMock.Http
|
||||
{
|
||||
/// <summary>
|
||||
/// The ports.
|
||||
/// </summary>
|
||||
public static class PortUtil
|
||||
{
|
||||
/// <summary>
|
||||
/// The find free TCP port.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// The <see cref="int"/>.
|
||||
/// </returns>
|
||||
/// <remarks>see http://stackoverflow.com/questions/138043/find-the-next-tcp-port-in-net.</remarks>
|
||||
public static int FindFreeTcpPort()
|
||||
{
|
||||
TcpListener tcpListener = null;
|
||||
try
|
||||
{
|
||||
tcpListener = new TcpListener(IPAddress.Loopback, 0);
|
||||
tcpListener.Start();
|
||||
|
||||
return ((IPEndPoint)tcpListener.LocalEndpoint).Port;
|
||||
}
|
||||
finally
|
||||
{
|
||||
tcpListener?.Stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
102
src/WireMock.Net/Http/TinyHttpServer.cs
Normal file
102
src/WireMock.Net/Http/TinyHttpServer.cs
Normal file
@@ -0,0 +1,102 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using JetBrains.Annotations;
|
||||
using WireMock.Validation;
|
||||
|
||||
namespace WireMock.Http
|
||||
{
|
||||
/// <summary>
|
||||
/// The tiny http server.
|
||||
/// </summary>
|
||||
public class TinyHttpServer
|
||||
{
|
||||
private readonly Action<HttpListenerContext> _httpHandler;
|
||||
|
||||
private readonly HttpListener _listener;
|
||||
|
||||
private CancellationTokenSource _cts;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this server is started.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <c>true</c> if this server is started; otherwise, <c>false</c>.
|
||||
/// </value>
|
||||
public bool IsStarted { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the url.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The urls.
|
||||
/// </value>
|
||||
public List<Uri> Urls { get; } = new List<Uri>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the ports.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The ports.
|
||||
/// </value>
|
||||
public List<int> Ports { get; } = new List<int>();
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TinyHttpServer"/> class.
|
||||
/// </summary>
|
||||
/// <param name="uriPrefixes">The uriPrefixes.</param>
|
||||
/// <param name="httpHandler">The http handler.</param>
|
||||
public TinyHttpServer([NotNull] Action<HttpListenerContext> httpHandler, [NotNull] params string[] uriPrefixes)
|
||||
{
|
||||
Check.NotNull(httpHandler, nameof(httpHandler));
|
||||
Check.NotEmpty(uriPrefixes, nameof(uriPrefixes));
|
||||
|
||||
_httpHandler = httpHandler;
|
||||
|
||||
// Create a listener.
|
||||
_listener = new HttpListener();
|
||||
foreach (string uriPrefix in uriPrefixes)
|
||||
{
|
||||
var uri = new Uri(uriPrefix);
|
||||
Urls.Add(uri);
|
||||
Ports.Add(uri.Port);
|
||||
|
||||
_listener.Prefixes.Add(uriPrefix);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Start the server.
|
||||
/// </summary>
|
||||
public void Start()
|
||||
{
|
||||
_listener.Start();
|
||||
IsStarted = true;
|
||||
|
||||
_cts = new CancellationTokenSource();
|
||||
Task.Run(
|
||||
async () =>
|
||||
{
|
||||
using (_listener)
|
||||
{
|
||||
while (!_cts.Token.IsCancellationRequested)
|
||||
{
|
||||
HttpListenerContext context = await _listener.GetContextAsync();
|
||||
_httpHandler(context);
|
||||
}
|
||||
}
|
||||
},
|
||||
_cts.Token);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stop the server.
|
||||
/// </summary>
|
||||
public void Stop()
|
||||
{
|
||||
_cts.Cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
60
src/WireMock.Net/HttpListenerRequestMapper.cs
Normal file
60
src/WireMock.Net/HttpListenerRequestMapper.cs
Normal file
@@ -0,0 +1,60 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
|
||||
namespace WireMock
|
||||
{
|
||||
/// <summary>
|
||||
/// The http listener request mapper.
|
||||
/// </summary>
|
||||
public class HttpListenerRequestMapper
|
||||
{
|
||||
/// <summary>
|
||||
/// The map.
|
||||
/// </summary>
|
||||
/// <param name="listenerRequest">The listener request.</param>
|
||||
/// <returns>The <see cref="RequestMessage"/>.</returns>
|
||||
public RequestMessage Map(HttpListenerRequest listenerRequest)
|
||||
{
|
||||
Uri url = listenerRequest.Url;
|
||||
string verb = listenerRequest.HttpMethod;
|
||||
byte[] body = GetRequestBody(listenerRequest);
|
||||
Encoding bodyEncoding = body != null ? listenerRequest.ContentEncoding : null;
|
||||
string bodyAsString = bodyEncoding?.GetString(body);
|
||||
var listenerHeaders = listenerRequest.Headers;
|
||||
var headers = listenerHeaders.AllKeys.ToDictionary(k => k, k => listenerHeaders[k]);
|
||||
var cookies = new Dictionary<string, string>();
|
||||
|
||||
foreach (Cookie cookie in listenerRequest.Cookies)
|
||||
cookies.Add(cookie.Name, cookie.Value);
|
||||
|
||||
return new RequestMessage(url, verb, body, bodyAsString, bodyEncoding, headers, cookies) { DateTime = DateTime.Now };
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The get request body.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
/// <returns>The <see cref="string"/>.</returns>
|
||||
private byte[] GetRequestBody(HttpListenerRequest request)
|
||||
{
|
||||
if (!request.HasEntityBody)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
using (var bodyStream = request.InputStream)
|
||||
{
|
||||
using (var memoryStream = new MemoryStream())
|
||||
{
|
||||
bodyStream.CopyTo(memoryStream);
|
||||
|
||||
return memoryStream.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
39
src/WireMock.Net/HttpListenerResponseMapper.cs
Normal file
39
src/WireMock.Net/HttpListenerResponseMapper.cs
Normal file
@@ -0,0 +1,39 @@
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
|
||||
namespace WireMock
|
||||
{
|
||||
/// <summary>
|
||||
/// The http listener response mapper.
|
||||
/// </summary>
|
||||
public class HttpListenerResponseMapper
|
||||
{
|
||||
private readonly Encoding _utf8NoBom = new UTF8Encoding(false);
|
||||
|
||||
/// <summary>
|
||||
/// The map.
|
||||
/// </summary>
|
||||
/// <param name="responseMessage">
|
||||
/// The response.
|
||||
/// </param>
|
||||
/// <param name="listenerResponse">The listenerResponse.</param>
|
||||
public void Map(ResponseMessage responseMessage, HttpListenerResponse listenerResponse)
|
||||
{
|
||||
listenerResponse.StatusCode = responseMessage.StatusCode;
|
||||
|
||||
responseMessage.Headers.ToList().ForEach(pair => listenerResponse.AddHeader(pair.Key, pair.Value));
|
||||
|
||||
if (responseMessage.Body == null)
|
||||
return;
|
||||
|
||||
var encoding = responseMessage.BodyEncoding ?? _utf8NoBom;
|
||||
byte[] buffer = encoding.GetBytes(responseMessage.Body);
|
||||
|
||||
listenerResponse.ContentEncoding = encoding;
|
||||
listenerResponse.ContentLength64 = buffer.Length;
|
||||
listenerResponse.OutputStream.Write(buffer, 0, buffer.Length);
|
||||
listenerResponse.OutputStream.Flush();
|
||||
}
|
||||
}
|
||||
}
|
||||
21
src/WireMock.Net/IResponseProvider.cs
Normal file
21
src/WireMock.Net/IResponseProvider.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace WireMock
|
||||
{
|
||||
/// <summary>
|
||||
/// The Response Provider interface.
|
||||
/// </summary>
|
||||
public interface IResponseProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// The provide response.
|
||||
/// </summary>
|
||||
/// <param name="requestMessage">
|
||||
/// The request.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// The <see cref="Task"/>.
|
||||
/// </returns>
|
||||
Task<ResponseMessage> ProvideResponse(RequestMessage requestMessage);
|
||||
}
|
||||
}
|
||||
51
src/WireMock.Net/Logging/LogEntry.cs
Normal file
51
src/WireMock.Net/Logging/LogEntry.cs
Normal file
@@ -0,0 +1,51 @@
|
||||
using System;
|
||||
using WireMock.Matchers.Request;
|
||||
|
||||
namespace WireMock.Logging
|
||||
{
|
||||
/// <summary>
|
||||
/// LogEntry
|
||||
/// </summary>
|
||||
public class LogEntry
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the unique identifier.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The unique identifier.
|
||||
/// </value>
|
||||
public Guid Guid { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the request message.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The request message.
|
||||
/// </value>
|
||||
public RequestMessage RequestMessage { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the response message.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The response message.
|
||||
/// </value>
|
||||
public ResponseMessage ResponseMessage { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the request match result.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The request match result.
|
||||
/// </value>
|
||||
public RequestMatchResult RequestMatchResult { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the mapping unique identifier.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The mapping unique identifier.
|
||||
/// </value>
|
||||
public Guid? MappingGuid { get; set; }
|
||||
}
|
||||
}
|
||||
85
src/WireMock.Net/Mapping.cs
Normal file
85
src/WireMock.Net/Mapping.cs
Normal file
@@ -0,0 +1,85 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using WireMock.Matchers.Request;
|
||||
|
||||
namespace WireMock
|
||||
{
|
||||
/// <summary>
|
||||
/// The Mapping.
|
||||
/// </summary>
|
||||
public class Mapping
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the priority.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The priority.
|
||||
/// </value>
|
||||
public int Priority { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the unique identifier.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The unique identifier.
|
||||
/// </value>
|
||||
public Guid Guid { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The Request matcher.
|
||||
/// </summary>
|
||||
public IRequestMatcher RequestMatcher { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The Provider.
|
||||
/// </summary>
|
||||
public IResponseProvider Provider { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Mapping"/> class.
|
||||
/// </summary>
|
||||
/// <param name="guid">The the unique identifier.</param>
|
||||
/// <param name="requestMatcher">The request matcher.</param>
|
||||
/// <param name="provider">The provider.</param>
|
||||
/// <param name="priority">The priority for this mapping.</param>
|
||||
public Mapping(Guid guid, IRequestMatcher requestMatcher, IResponseProvider provider, int priority)
|
||||
{
|
||||
Priority = priority;
|
||||
Guid = guid;
|
||||
RequestMatcher = requestMatcher;
|
||||
Provider = provider;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The response to.
|
||||
/// </summary>
|
||||
/// <param name="requestMessage">The request message.</param>
|
||||
/// <returns>The <see cref="Task"/>.</returns>
|
||||
public async Task<ResponseMessage> ResponseTo(RequestMessage requestMessage)
|
||||
{
|
||||
return await Provider.ProvideResponse(requestMessage);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the RequestMessage is handled.
|
||||
/// </summary>
|
||||
/// <param name="requestMessage">The request message.</param>
|
||||
/// <returns>The <see cref="RequestMatchResult"/>.</returns>
|
||||
public RequestMatchResult IsRequestHandled(RequestMessage requestMessage)
|
||||
{
|
||||
var result = new RequestMatchResult();
|
||||
|
||||
RequestMatcher.GetMatchingScore(requestMessage, result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this mapping is an Admin Interface.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <c>true</c> if this mapping is an Admin Interface; otherwise, <c>false</c>.
|
||||
/// </value>
|
||||
public bool IsAdminInterface => Provider is DynamicResponseProvider;
|
||||
}
|
||||
}
|
||||
10
src/WireMock.Net/MappingRegistrationCallback.cs
Normal file
10
src/WireMock.Net/MappingRegistrationCallback.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
namespace WireMock
|
||||
{
|
||||
/// <summary>
|
||||
/// The registration callback.
|
||||
/// </summary>
|
||||
/// <param name="mapping">
|
||||
/// The route.
|
||||
/// </param>
|
||||
public delegate void RegistrationCallback(Mapping mapping);
|
||||
}
|
||||
54
src/WireMock.Net/Matchers/ExactMatcher.cs
Normal file
54
src/WireMock.Net/Matchers/ExactMatcher.cs
Normal file
@@ -0,0 +1,54 @@
|
||||
using System.Linq;
|
||||
using JetBrains.Annotations;
|
||||
using WireMock.Validation;
|
||||
|
||||
namespace WireMock.Matchers
|
||||
{
|
||||
/// <summary>
|
||||
/// ExactMatcher
|
||||
/// </summary>
|
||||
/// <seealso cref="IMatcher" />
|
||||
public class ExactMatcher : IMatcher
|
||||
{
|
||||
private readonly string[] _values;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ExactMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="values">The values.</param>
|
||||
public ExactMatcher([NotNull] params string[] values)
|
||||
{
|
||||
Check.NotNull(values, nameof(values));
|
||||
|
||||
_values = values;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified input is match.
|
||||
/// </summary>
|
||||
/// <param name="input">The input.</param>
|
||||
/// <returns>A value between 0.0 - 1.0 of the similarity.</returns>
|
||||
public double IsMatch(string input)
|
||||
{
|
||||
return MatchScores.ToScore(_values.Select(value => value.Equals(input)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value.
|
||||
/// </summary>
|
||||
/// <returns>Patterns</returns>
|
||||
public string[] GetPatterns()
|
||||
{
|
||||
return _values;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name.
|
||||
/// </summary>
|
||||
/// <returns>Name</returns>
|
||||
public string GetName()
|
||||
{
|
||||
return "ExactMatcher";
|
||||
}
|
||||
}
|
||||
}
|
||||
27
src/WireMock.Net/Matchers/IMatcher.cs
Normal file
27
src/WireMock.Net/Matchers/IMatcher.cs
Normal file
@@ -0,0 +1,27 @@
|
||||
namespace WireMock.Matchers
|
||||
{
|
||||
/// <summary>
|
||||
/// IMatcher
|
||||
/// </summary>
|
||||
public interface IMatcher
|
||||
{
|
||||
/// <summary>
|
||||
/// Determines whether the specified input is match.
|
||||
/// </summary>
|
||||
/// <param name="input">The input.</param>
|
||||
/// <returns>A value between 0.0 - 1.0 of the similarity.</returns>
|
||||
double IsMatch(string input);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the patterns.
|
||||
/// </summary>
|
||||
/// <returns>Patterns</returns>
|
||||
string[] GetPatterns();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name.
|
||||
/// </summary>
|
||||
/// <returns>Name</returns>
|
||||
string GetName();
|
||||
}
|
||||
}
|
||||
68
src/WireMock.Net/Matchers/JSONPathMatcher.cs
Normal file
68
src/WireMock.Net/Matchers/JSONPathMatcher.cs
Normal file
@@ -0,0 +1,68 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using JetBrains.Annotations;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using WireMock.Validation;
|
||||
|
||||
namespace WireMock.Matchers
|
||||
{
|
||||
/// <summary>
|
||||
/// JSONPathMatcher
|
||||
/// </summary>
|
||||
/// <seealso cref="IMatcher" />
|
||||
public class JsonPathMatcher : IMatcher
|
||||
{
|
||||
private readonly string[] _patterns;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="JsonPathMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="patterns">The patterns.</param>
|
||||
public JsonPathMatcher([NotNull] params string[] patterns)
|
||||
{
|
||||
Check.NotNull(patterns, nameof(patterns));
|
||||
|
||||
_patterns = patterns;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified input is match.
|
||||
/// </summary>
|
||||
/// <param name="input">The input string</param>
|
||||
/// <returns>A value between 0.0 - 1.0 of the similarity.</returns>
|
||||
public double IsMatch(string input)
|
||||
{
|
||||
if (input == null)
|
||||
return MatchScores.Mismatch;
|
||||
|
||||
try
|
||||
{
|
||||
JObject o = JObject.Parse(input);
|
||||
|
||||
return MatchScores.ToScore(_patterns.Select(p => o.SelectToken(p) != null));
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return MatchScores.Mismatch;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the patterns.
|
||||
/// </summary>
|
||||
/// <returns>Pattern</returns>
|
||||
public string[] GetPatterns()
|
||||
{
|
||||
return _patterns;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name.
|
||||
/// </summary>
|
||||
/// <returns>Name</returns>
|
||||
public string GetName()
|
||||
{
|
||||
return "JsonPathMatcher";
|
||||
}
|
||||
}
|
||||
}
|
||||
58
src/WireMock.Net/Matchers/MatchScores.cs
Normal file
58
src/WireMock.Net/Matchers/MatchScores.cs
Normal file
@@ -0,0 +1,58 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace WireMock.Matchers
|
||||
{
|
||||
/// <summary>
|
||||
/// MatchScores
|
||||
/// </summary>
|
||||
public static class MatchScores
|
||||
{
|
||||
/// <summary>
|
||||
/// The tolerance
|
||||
/// </summary>
|
||||
public const double Tolerance = 0.0001;
|
||||
|
||||
/// <summary>
|
||||
/// The default mismatch score
|
||||
/// </summary>
|
||||
public const double Mismatch = 0.0;
|
||||
|
||||
/// <summary>
|
||||
/// The default perfect match score
|
||||
/// </summary>
|
||||
public const double Perfect = 1.0;
|
||||
|
||||
/// <summary>
|
||||
/// Convert a bool to the score.
|
||||
/// </summary>
|
||||
/// <param name="value">if set to <c>true</c> [value].</param>
|
||||
/// <returns>score</returns>
|
||||
public static double ToScore(bool value)
|
||||
{
|
||||
return value ? Perfect : Mismatch;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the score from multiple funcs.
|
||||
/// </summary>
|
||||
/// <param name="values">The values.</param>
|
||||
/// <returns>score</returns>
|
||||
public static double ToScore(IEnumerable<bool> values)
|
||||
{
|
||||
var list = values.Select(ToScore).ToList();
|
||||
return list.Sum() / list.Count;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the score from multiple funcs.
|
||||
/// </summary>
|
||||
/// <param name="values">The values.</param>
|
||||
/// <returns>score</returns>
|
||||
public static double ToScore(IEnumerable<double> values)
|
||||
{
|
||||
var list = values.ToList();
|
||||
return list.Sum() / list.Count;
|
||||
}
|
||||
}
|
||||
}
|
||||
85
src/WireMock.Net/Matchers/RegexMatcher.cs
Normal file
85
src/WireMock.Net/Matchers/RegexMatcher.cs
Normal file
@@ -0,0 +1,85 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using JetBrains.Annotations;
|
||||
using WireMock.Validation;
|
||||
|
||||
namespace WireMock.Matchers
|
||||
{
|
||||
/// <summary>
|
||||
/// Regular Expression Matcher
|
||||
/// </summary>
|
||||
/// <seealso cref="IMatcher" />
|
||||
public class RegexMatcher : IMatcher
|
||||
{
|
||||
private readonly string[] _patterns;
|
||||
private readonly Regex[] _expressions;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RegexMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="pattern">The pattern.</param>
|
||||
/// <param name="ignoreCase">IgnoreCase</param>
|
||||
public RegexMatcher([NotNull, RegexPattern] string pattern, bool ignoreCase = false) : this(new [] { pattern }, ignoreCase )
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RegexMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="patterns">The patterns.</param>
|
||||
/// <param name="ignoreCase">IgnoreCase</param>
|
||||
public RegexMatcher([NotNull, RegexPattern] string[] patterns, bool ignoreCase = false)
|
||||
{
|
||||
Check.NotNull(patterns, nameof(patterns));
|
||||
|
||||
_patterns = patterns;
|
||||
|
||||
RegexOptions options = RegexOptions.Compiled;
|
||||
if (ignoreCase)
|
||||
options |= RegexOptions.IgnoreCase;
|
||||
|
||||
_expressions = patterns.Select(p => new Regex(p, options)).ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified input is match.
|
||||
/// </summary>
|
||||
/// <param name="input">The input string</param>
|
||||
/// <returns>A value between 0.0 - 1.0 of the similarity.</returns>
|
||||
public double IsMatch(string input)
|
||||
{
|
||||
if (input == null)
|
||||
return MatchScores.Mismatch;
|
||||
|
||||
try
|
||||
{
|
||||
return MatchScores.ToScore(_expressions.Select(e => e.IsMatch(input)));
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return MatchScores.Mismatch;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the patterns.
|
||||
/// </summary>
|
||||
/// <returns>Pattern</returns>
|
||||
public virtual string[] GetPatterns()
|
||||
{
|
||||
return _patterns;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// Name
|
||||
/// </returns>
|
||||
public virtual string GetName()
|
||||
{
|
||||
return "RegexMatcher";
|
||||
}
|
||||
}
|
||||
}
|
||||
18
src/WireMock.Net/Matchers/Request/CompositeMatcherType.cs
Normal file
18
src/WireMock.Net/Matchers/Request/CompositeMatcherType.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
namespace WireMock.Matchers.Request
|
||||
{
|
||||
/// <summary>
|
||||
/// CompositeMatcherType
|
||||
/// </summary>
|
||||
public enum CompositeMatcherType
|
||||
{
|
||||
/// <summary>
|
||||
/// And
|
||||
/// </summary>
|
||||
And = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Or
|
||||
/// </summary>
|
||||
Or = 1
|
||||
}
|
||||
}
|
||||
20
src/WireMock.Net/Matchers/Request/IRequestMatcher.cs
Normal file
20
src/WireMock.Net/Matchers/Request/IRequestMatcher.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
using JetBrains.Annotations;
|
||||
|
||||
namespace WireMock.Matchers.Request
|
||||
{
|
||||
/// <summary>
|
||||
/// The RequestMatcher interface.
|
||||
/// </summary>
|
||||
public interface IRequestMatcher
|
||||
{
|
||||
/// <summary>
|
||||
/// Determines whether the specified RequestMessage is match.
|
||||
/// </summary>
|
||||
/// <param name="requestMessage">The RequestMessage.</param>
|
||||
/// <param name="requestMatchResult">The RequestMatchResult.</param>
|
||||
/// <returns>
|
||||
/// A value between 0.0 - 1.0 of the similarity.
|
||||
/// </returns>
|
||||
double GetMatchingScore([NotNull] RequestMessage requestMessage, [NotNull] RequestMatchResult requestMatchResult);
|
||||
}
|
||||
}
|
||||
57
src/WireMock.Net/Matchers/Request/RequestMatchResult.cs
Normal file
57
src/WireMock.Net/Matchers/Request/RequestMatchResult.cs
Normal file
@@ -0,0 +1,57 @@
|
||||
using System;
|
||||
|
||||
namespace WireMock.Matchers.Request
|
||||
{
|
||||
/// <summary>
|
||||
/// RequestMatchResult
|
||||
/// </summary>
|
||||
public class RequestMatchResult : IComparable
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the match-score.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The match-score.
|
||||
/// </value>
|
||||
public double TotalScore { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the total number of matches.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The total number of matches.
|
||||
/// </value>
|
||||
public int TotalNumber { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether this instance is perfect match.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <c>true</c> if this instance is perfect match; otherwise, <c>false</c>.
|
||||
/// </value>
|
||||
public bool IsPerfectMatch => Math.Abs(TotalScore - TotalNumber) < MatchScores.Tolerance;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the match percentage.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The match percentage.
|
||||
/// </value>
|
||||
public double AverageTotalScore => TotalNumber == 0 ? 0.0 : TotalScore / TotalNumber;
|
||||
|
||||
/// <summary>
|
||||
/// Compares the current instance with another object of the same type and returns an integer that indicates whether the current instance precedes, follows, or occurs in the same position in the sort order as the other object.
|
||||
/// </summary>
|
||||
/// <param name="obj">An object to compare with this instance.</param>
|
||||
/// <returns>
|
||||
/// A value that indicates the relative order of the objects being compared. The return value has these meanings: Value Meaning Less than zero This instance precedes <paramref name="obj" /> in the sort order. Zero This instance occurs in the same position in the sort order as <paramref name="obj" />. Greater than zero This instance follows <paramref name="obj" /> in the sort order.
|
||||
/// </returns>
|
||||
/// <exception cref="System.NotImplementedException"></exception>
|
||||
public int CompareTo(object obj)
|
||||
{
|
||||
var compareObj = (RequestMatchResult)obj;
|
||||
|
||||
return compareObj.AverageTotalScore.CompareTo(AverageTotalScore);
|
||||
}
|
||||
}
|
||||
}
|
||||
125
src/WireMock.Net/Matchers/Request/RequestMessageBodyMatcher.cs
Normal file
125
src/WireMock.Net/Matchers/Request/RequestMessageBodyMatcher.cs
Normal file
@@ -0,0 +1,125 @@
|
||||
using System;
|
||||
using JetBrains.Annotations;
|
||||
using WireMock.Validation;
|
||||
|
||||
namespace WireMock.Matchers.Request
|
||||
{
|
||||
/// <summary>
|
||||
/// The request body matcher.
|
||||
/// </summary>
|
||||
public class RequestMessageBodyMatcher : IRequestMatcher
|
||||
{
|
||||
/// <summary>
|
||||
/// The body as byte[].
|
||||
/// </summary>
|
||||
private readonly byte[] _bodyData;
|
||||
|
||||
/// <summary>
|
||||
/// The body function
|
||||
/// </summary>
|
||||
public Func<string, bool> Func { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The body data function
|
||||
/// </summary>
|
||||
public Func<byte[], bool> DataFunc { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The matcher.
|
||||
/// </summary>
|
||||
public IMatcher Matcher { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RequestMessageBodyMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="body">
|
||||
/// The body Regex pattern.
|
||||
/// </param>
|
||||
public RequestMessageBodyMatcher([NotNull] string body) : this(new SimMetricsMatcher(body))
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RequestMessageBodyMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="body">
|
||||
/// The body Regex pattern.
|
||||
/// </param>
|
||||
public RequestMessageBodyMatcher([NotNull] byte[] body)
|
||||
{
|
||||
Check.NotNull(body, nameof(body));
|
||||
_bodyData = body;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RequestMessageBodyMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="func">
|
||||
/// The body func.
|
||||
/// </param>
|
||||
public RequestMessageBodyMatcher([NotNull] Func<string, bool> func)
|
||||
{
|
||||
Check.NotNull(func, nameof(func));
|
||||
Func = func;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RequestMessageBodyMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="func">
|
||||
/// The body func.
|
||||
/// </param>
|
||||
public RequestMessageBodyMatcher([NotNull] Func<byte[], bool> func)
|
||||
{
|
||||
Check.NotNull(func, nameof(func));
|
||||
DataFunc = func;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RequestMessageBodyMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="matcher">
|
||||
/// The body matcher.
|
||||
/// </param>
|
||||
public RequestMessageBodyMatcher([NotNull] IMatcher matcher)
|
||||
{
|
||||
Check.NotNull(matcher, nameof(matcher));
|
||||
Matcher = matcher;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified RequestMessage is match.
|
||||
/// </summary>
|
||||
/// <param name="requestMessage">The RequestMessage.</param>
|
||||
/// <param name="requestMatchResult">The RequestMatchResult.</param>
|
||||
/// <returns>
|
||||
/// A value between 0.0 - 1.0 of the similarity.
|
||||
/// </returns>
|
||||
public double GetMatchingScore(RequestMessage requestMessage, RequestMatchResult requestMatchResult)
|
||||
{
|
||||
double score = IsMatch(requestMessage);
|
||||
requestMatchResult.TotalScore += score;
|
||||
|
||||
requestMatchResult.TotalNumber++;
|
||||
|
||||
return score;
|
||||
}
|
||||
|
||||
private double IsMatch(RequestMessage requestMessage)
|
||||
{
|
||||
if (Matcher != null)
|
||||
return Matcher.IsMatch(requestMessage.Body);
|
||||
|
||||
if (_bodyData != null)
|
||||
return MatchScores.ToScore(requestMessage.BodyAsBytes == _bodyData);
|
||||
|
||||
if (Func != null)
|
||||
return MatchScores.ToScore(requestMessage.Body != null && Func(requestMessage.Body));
|
||||
|
||||
if (DataFunc != null && requestMessage.BodyAsBytes != null)
|
||||
return MatchScores.ToScore(requestMessage.BodyAsBytes != null && DataFunc(requestMessage.BodyAsBytes));
|
||||
|
||||
return MatchScores.Mismatch;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using JetBrains.Annotations;
|
||||
using WireMock.Validation;
|
||||
|
||||
namespace WireMock.Matchers.Request
|
||||
{
|
||||
/// <summary>
|
||||
/// The composite request matcher.
|
||||
/// </summary>
|
||||
public abstract class RequestMessageCompositeMatcher : IRequestMatcher
|
||||
{
|
||||
private readonly CompositeMatcherType _type;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the request matchers.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The request matchers.
|
||||
/// </value>
|
||||
private IEnumerable<IRequestMatcher> RequestMatchers { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RequestMessageCompositeMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="requestMatchers">The request matchers.</param>
|
||||
/// <param name="type">The CompositeMatcherType type (Defaults to 'And')</param>
|
||||
protected RequestMessageCompositeMatcher([NotNull] IEnumerable<IRequestMatcher> requestMatchers, CompositeMatcherType type = CompositeMatcherType.And)
|
||||
{
|
||||
Check.NotNull(requestMatchers, nameof(requestMatchers));
|
||||
|
||||
_type = type;
|
||||
RequestMatchers = requestMatchers;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified RequestMessage is match.
|
||||
/// </summary>
|
||||
/// <param name="requestMessage">The RequestMessage.</param>
|
||||
/// <param name="requestMatchResult">The RequestMatchResult.</param>
|
||||
/// <returns>
|
||||
/// A value between 0.0 - 1.0 of the similarity.
|
||||
/// </returns>
|
||||
public double GetMatchingScore(RequestMessage requestMessage, RequestMatchResult requestMatchResult)
|
||||
{
|
||||
var list = new List<double>();
|
||||
if (_type == CompositeMatcherType.And)
|
||||
{
|
||||
foreach (var requestMatcher in RequestMatchers)
|
||||
{
|
||||
double score = requestMatcher.GetMatchingScore(requestMessage, requestMatchResult);
|
||||
list.Add(score);
|
||||
}
|
||||
|
||||
return list.Sum() / list.Count;
|
||||
}
|
||||
|
||||
foreach (var requestMatcher in RequestMatchers)
|
||||
{
|
||||
double score = requestMatcher.GetMatchingScore(requestMessage, requestMatchResult);
|
||||
list.Add(score);
|
||||
}
|
||||
|
||||
return list.Max();
|
||||
}
|
||||
}
|
||||
}
|
||||
105
src/WireMock.Net/Matchers/Request/RequestMessageCookieMatcher.cs
Normal file
105
src/WireMock.Net/Matchers/Request/RequestMessageCookieMatcher.cs
Normal file
@@ -0,0 +1,105 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using JetBrains.Annotations;
|
||||
using WireMock.Validation;
|
||||
|
||||
namespace WireMock.Matchers.Request
|
||||
{
|
||||
/// <summary>
|
||||
/// The request cookie matcher.
|
||||
/// </summary>
|
||||
public class RequestMessageCookieMatcher : IRequestMatcher
|
||||
{
|
||||
/// <value>
|
||||
/// The funcs.
|
||||
/// </value>
|
||||
public Func<IDictionary<string, string>, bool>[] Funcs { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The name
|
||||
/// </summary>
|
||||
public string Name { get; }
|
||||
|
||||
/// <value>
|
||||
/// The matchers.
|
||||
/// </value>
|
||||
public IMatcher[] Matchers { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RequestMessageCookieMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="name">The name.</param>
|
||||
/// <param name="pattern">The pattern.</param>
|
||||
/// <param name="ignoreCase">The ignoreCase.</param>
|
||||
public RequestMessageCookieMatcher([NotNull] string name, [NotNull] string pattern, bool ignoreCase = true)
|
||||
{
|
||||
Check.NotNull(name, nameof(name));
|
||||
Check.NotNull(pattern, nameof(pattern));
|
||||
|
||||
Name = name;
|
||||
Matchers = new IMatcher[] { new WildcardMatcher(pattern, ignoreCase) };
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RequestMessageCookieMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="name">The name.</param>
|
||||
/// <param name="matchers">The matchers.</param>
|
||||
public RequestMessageCookieMatcher([NotNull] string name, [NotNull] params IMatcher[] matchers)
|
||||
{
|
||||
Check.NotNull(name, nameof(name));
|
||||
Check.NotNull(matchers, nameof(matchers));
|
||||
|
||||
Name = name;
|
||||
Matchers = matchers;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RequestMessageCookieMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="funcs">The funcs.</param>
|
||||
public RequestMessageCookieMatcher([NotNull] params Func<IDictionary<string, string>, bool>[] funcs)
|
||||
{
|
||||
Check.NotNull(funcs, nameof(funcs));
|
||||
|
||||
Funcs = funcs;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified RequestMessage is match.
|
||||
/// </summary>
|
||||
/// <param name="requestMessage">The RequestMessage.</param>
|
||||
/// <param name="requestMatchResult">The RequestMatchResult.</param>
|
||||
/// <returns>
|
||||
/// A value between 0.0 - 1.0 of the similarity.
|
||||
/// </returns>
|
||||
public double GetMatchingScore(RequestMessage requestMessage, RequestMatchResult requestMatchResult)
|
||||
{
|
||||
double score = IsMatch(requestMessage);
|
||||
requestMatchResult.TotalScore += score;
|
||||
|
||||
requestMatchResult.TotalNumber++;
|
||||
|
||||
return score;
|
||||
}
|
||||
|
||||
private double IsMatch(RequestMessage requestMessage)
|
||||
{
|
||||
if (requestMessage.Cookies == null)
|
||||
return MatchScores.Mismatch;
|
||||
|
||||
if (Funcs != null)
|
||||
return MatchScores.ToScore(Funcs.Any(f => f(requestMessage.Cookies)));
|
||||
|
||||
if (Matchers == null)
|
||||
return MatchScores.Mismatch;
|
||||
|
||||
if (!requestMessage.Cookies.ContainsKey(Name))
|
||||
return MatchScores.Mismatch;
|
||||
|
||||
string value = requestMessage.Cookies[Name];
|
||||
return Matchers.Max(m => m.IsMatch(value));
|
||||
}
|
||||
}
|
||||
}
|
||||
105
src/WireMock.Net/Matchers/Request/RequestMessageHeaderMatcher.cs
Normal file
105
src/WireMock.Net/Matchers/Request/RequestMessageHeaderMatcher.cs
Normal file
@@ -0,0 +1,105 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using JetBrains.Annotations;
|
||||
using WireMock.Validation;
|
||||
|
||||
namespace WireMock.Matchers.Request
|
||||
{
|
||||
/// <summary>
|
||||
/// The request header matcher.
|
||||
/// </summary>
|
||||
public class RequestMessageHeaderMatcher : IRequestMatcher
|
||||
{
|
||||
/// <summary>
|
||||
/// The functions
|
||||
/// </summary>
|
||||
public Func<IDictionary<string, string>, bool>[] Funcs { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The name
|
||||
/// </summary>
|
||||
public string Name { get; }
|
||||
|
||||
/// <value>
|
||||
/// The matchers.
|
||||
/// </value>
|
||||
public IMatcher[] Matchers { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RequestMessageHeaderMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="name">The name.</param>
|
||||
/// <param name="pattern">The pattern.</param>
|
||||
/// <param name="ignoreCase">The ignoreCase.</param>
|
||||
public RequestMessageHeaderMatcher([NotNull] string name, [NotNull] string pattern, bool ignoreCase = true)
|
||||
{
|
||||
Check.NotNull(name, nameof(name));
|
||||
Check.NotNull(pattern, nameof(pattern));
|
||||
|
||||
Name = name;
|
||||
Matchers = new IMatcher[] { new WildcardMatcher(pattern, ignoreCase) };
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RequestMessageHeaderMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="name">The name.</param>
|
||||
/// <param name="matchers">The matchers.</param>
|
||||
public RequestMessageHeaderMatcher([NotNull] string name, [NotNull] params IMatcher[] matchers)
|
||||
{
|
||||
Check.NotNull(name, nameof(name));
|
||||
Check.NotNull(matchers, nameof(matchers));
|
||||
|
||||
Name = name;
|
||||
Matchers = matchers;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RequestMessageHeaderMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="funcs">The funcs.</param>
|
||||
public RequestMessageHeaderMatcher([NotNull] params Func<IDictionary<string, string>, bool>[] funcs)
|
||||
{
|
||||
Check.NotNull(funcs, nameof(funcs));
|
||||
|
||||
Funcs = funcs;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified RequestMessage is match.
|
||||
/// </summary>
|
||||
/// <param name="requestMessage">The RequestMessage.</param>
|
||||
/// <param name="requestMatchResult">The RequestMatchResult.</param>
|
||||
/// <returns>
|
||||
/// A value between 0.0 - 1.0 of the similarity.
|
||||
/// </returns>
|
||||
public double GetMatchingScore(RequestMessage requestMessage, RequestMatchResult requestMatchResult)
|
||||
{
|
||||
double score = IsMatch(requestMessage);
|
||||
requestMatchResult.TotalScore += score;
|
||||
|
||||
requestMatchResult.TotalNumber++;
|
||||
|
||||
return score;
|
||||
}
|
||||
|
||||
private double IsMatch(RequestMessage requestMessage)
|
||||
{
|
||||
if (requestMessage.Headers == null)
|
||||
return MatchScores.Mismatch;
|
||||
|
||||
if (Funcs != null)
|
||||
return MatchScores.ToScore(Funcs.Any(f => f(requestMessage.Headers)));
|
||||
|
||||
if (Matchers == null)
|
||||
return MatchScores.Mismatch;
|
||||
|
||||
if (!requestMessage.Headers.ContainsKey(Name))
|
||||
return MatchScores.Mismatch;
|
||||
|
||||
string value = requestMessage.Headers[Name];
|
||||
return Matchers.Max(m => m.IsMatch(value));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
using System.Linq;
|
||||
using JetBrains.Annotations;
|
||||
using WireMock.Validation;
|
||||
|
||||
namespace WireMock.Matchers.Request
|
||||
{
|
||||
/// <summary>
|
||||
/// The request verb matcher.
|
||||
/// </summary>
|
||||
internal class RequestMessageMethodMatcher : IRequestMatcher
|
||||
{
|
||||
/// <summary>
|
||||
/// The methods
|
||||
/// </summary>
|
||||
public string[] Methods { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RequestMessageMethodMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="methods">
|
||||
/// The verb.
|
||||
/// </param>
|
||||
public RequestMessageMethodMatcher([NotNull] params string[] methods)
|
||||
{
|
||||
Check.NotNull(methods, nameof(methods));
|
||||
Methods = methods.Select(v => v.ToLower()).ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified RequestMessage is match.
|
||||
/// </summary>
|
||||
/// <param name="requestMessage">The RequestMessage.</param>
|
||||
/// <param name="requestMatchResult">The RequestMatchResult.</param>
|
||||
/// <returns>
|
||||
/// A value between 0.0 - 1.0 of the similarity.
|
||||
/// </returns>
|
||||
public double GetMatchingScore(RequestMessage requestMessage, RequestMatchResult requestMatchResult)
|
||||
{
|
||||
double score = IsMatch(requestMessage);
|
||||
requestMatchResult.TotalScore += score;
|
||||
|
||||
requestMatchResult.TotalNumber++;
|
||||
|
||||
return score;
|
||||
}
|
||||
|
||||
private double IsMatch(RequestMessage requestMessage)
|
||||
{
|
||||
return MatchScores.ToScore(Methods.Contains(requestMessage.Method));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using JetBrains.Annotations;
|
||||
using WireMock.Util;
|
||||
using WireMock.Validation;
|
||||
|
||||
namespace WireMock.Matchers.Request
|
||||
{
|
||||
/// <summary>
|
||||
/// The request parameters matcher.
|
||||
/// </summary>
|
||||
public class RequestMessageParamMatcher : IRequestMatcher
|
||||
{
|
||||
/// <summary>
|
||||
/// The funcs
|
||||
/// </summary>
|
||||
public Func<IDictionary<string, WireMockList<string>>, bool>[] Funcs { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The key
|
||||
/// </summary>
|
||||
public string Key { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The values
|
||||
/// </summary>
|
||||
public IEnumerable<string> Values { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RequestMessageParamMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="key">
|
||||
/// The key.
|
||||
/// </param>
|
||||
/// <param name="values">
|
||||
/// The values.
|
||||
/// </param>
|
||||
public RequestMessageParamMatcher([NotNull] string key, [CanBeNull] IEnumerable<string> values)
|
||||
{
|
||||
Check.NotNull(key, nameof(key));
|
||||
|
||||
Key = key;
|
||||
Values = values;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RequestMessageParamMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="funcs">The funcs.</param>
|
||||
public RequestMessageParamMatcher([NotNull] params Func<IDictionary<string, WireMockList<string>>, bool>[] funcs)
|
||||
{
|
||||
Check.NotNull(funcs, nameof(funcs));
|
||||
Funcs = funcs;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified RequestMessage is match.
|
||||
/// </summary>
|
||||
/// <param name="requestMessage">The RequestMessage.</param>
|
||||
/// <param name="requestMatchResult">The RequestMatchResult.</param>
|
||||
/// <returns>
|
||||
/// A value between 0.0 - 1.0 of the similarity.
|
||||
/// </returns>
|
||||
public double GetMatchingScore(RequestMessage requestMessage, RequestMatchResult requestMatchResult)
|
||||
{
|
||||
double score = IsMatch(requestMessage);
|
||||
requestMatchResult.TotalScore += score;
|
||||
|
||||
requestMatchResult.TotalNumber++;
|
||||
|
||||
return score;
|
||||
}
|
||||
|
||||
private double IsMatch(RequestMessage requestMessage)
|
||||
{
|
||||
if (Funcs != null)
|
||||
return MatchScores.ToScore(requestMessage.Query != null && Funcs.Any(f => f(requestMessage.Query)));
|
||||
|
||||
List<string> values = requestMessage.GetParameter(Key);
|
||||
|
||||
return MatchScores.ToScore(values?.Intersect(Values).Count() == Values.Count());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using JetBrains.Annotations;
|
||||
using WireMock.Validation;
|
||||
|
||||
namespace WireMock.Matchers.Request
|
||||
{
|
||||
/// <summary>
|
||||
/// The request path matcher.
|
||||
/// </summary>
|
||||
public class RequestMessagePathMatcher : IRequestMatcher
|
||||
{
|
||||
/// <summary>
|
||||
/// The matcher.
|
||||
/// </summary>
|
||||
public IReadOnlyList<IMatcher> Matchers { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The path functions
|
||||
/// </summary>
|
||||
public Func<string, bool>[] Funcs { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RequestMessagePathMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="paths">The paths.</param>
|
||||
public RequestMessagePathMatcher([NotNull] params string[] paths) : this(paths.Select(path => new WildcardMatcher(path)).ToArray())
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RequestMessagePathMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="matchers">The matchers.</param>
|
||||
public RequestMessagePathMatcher([NotNull] params IMatcher[] matchers)
|
||||
{
|
||||
Check.NotNull(matchers, nameof(matchers));
|
||||
Matchers = matchers;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RequestMessagePathMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="funcs">The path functions.</param>
|
||||
public RequestMessagePathMatcher([NotNull] params Func<string, bool>[] funcs)
|
||||
{
|
||||
Check.NotNull(funcs, nameof(funcs));
|
||||
Funcs = funcs;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified RequestMessage is match.
|
||||
/// </summary>
|
||||
/// <param name="requestMessage">The RequestMessage.</param>
|
||||
/// <param name="requestMatchResult">The RequestMatchResult.</param>
|
||||
/// <returns>
|
||||
/// A value between 0.0 - 1.0 of the similarity.
|
||||
/// </returns>
|
||||
public double GetMatchingScore(RequestMessage requestMessage, RequestMatchResult requestMatchResult)
|
||||
{
|
||||
double score = IsMatch(requestMessage);
|
||||
requestMatchResult.TotalScore += score;
|
||||
|
||||
requestMatchResult.TotalNumber++;
|
||||
|
||||
return score;
|
||||
}
|
||||
|
||||
private double IsMatch(RequestMessage requestMessage)
|
||||
{
|
||||
if (Matchers != null)
|
||||
return Matchers.Max(m => m.IsMatch(requestMessage.Path));
|
||||
|
||||
if (Funcs != null)
|
||||
return MatchScores.ToScore(requestMessage.Path != null && Funcs.Any(func => func(requestMessage.Path)));
|
||||
|
||||
return MatchScores.Mismatch;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using JetBrains.Annotations;
|
||||
using WireMock.Validation;
|
||||
|
||||
namespace WireMock.Matchers.Request
|
||||
{
|
||||
/// <summary>
|
||||
/// The request url matcher.
|
||||
/// </summary>
|
||||
public class RequestMessageUrlMatcher : IRequestMatcher
|
||||
{
|
||||
/// <summary>
|
||||
/// The matcher.
|
||||
/// </summary>
|
||||
public IReadOnlyList<IMatcher> Matchers { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The url functions
|
||||
/// </summary>
|
||||
public Func<string, bool>[] Funcs { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RequestMessageUrlMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="urls">The urls.</param>
|
||||
public RequestMessageUrlMatcher([NotNull] params string[] urls) : this(urls.Select(url => new WildcardMatcher(url)).ToArray())
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RequestMessageUrlMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="matchers">The matchers.</param>
|
||||
public RequestMessageUrlMatcher([NotNull] params IMatcher[] matchers)
|
||||
{
|
||||
Check.NotNull(matchers, nameof(matchers));
|
||||
Matchers = matchers;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RequestMessageUrlMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="funcs">The url functions.</param>
|
||||
public RequestMessageUrlMatcher([NotNull] params Func<string, bool>[] funcs)
|
||||
{
|
||||
Check.NotNull(funcs, nameof(funcs));
|
||||
Funcs = funcs;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified RequestMessage is match.
|
||||
/// </summary>
|
||||
/// <param name="requestMessage">The RequestMessage.</param>
|
||||
/// <param name="requestMatchResult">The RequestMatchResult.</param>
|
||||
/// <returns>
|
||||
/// A value between 0.0 - 1.0 of the similarity.
|
||||
/// </returns>
|
||||
public double GetMatchingScore(RequestMessage requestMessage, RequestMatchResult requestMatchResult)
|
||||
{
|
||||
double score = IsMatch(requestMessage);
|
||||
requestMatchResult.TotalScore += score;
|
||||
|
||||
requestMatchResult.TotalNumber++;
|
||||
|
||||
return score;
|
||||
}
|
||||
|
||||
private double IsMatch(RequestMessage requestMessage)
|
||||
{
|
||||
if (Matchers != null)
|
||||
return Matchers.Max(matcher => matcher.IsMatch(requestMessage.Url));
|
||||
|
||||
if (Funcs != null)
|
||||
return MatchScores.ToScore(requestMessage.Url != null && Funcs.Any(func => func(requestMessage.Url)));
|
||||
|
||||
return MatchScores.Mismatch;
|
||||
}
|
||||
}
|
||||
}
|
||||
114
src/WireMock.Net/Matchers/SimMetricsMatcher.cs
Normal file
114
src/WireMock.Net/Matchers/SimMetricsMatcher.cs
Normal file
@@ -0,0 +1,114 @@
|
||||
using System.Linq;
|
||||
using JetBrains.Annotations;
|
||||
using SimMetrics.Net;
|
||||
using SimMetrics.Net.API;
|
||||
using SimMetrics.Net.Metric;
|
||||
using WireMock.Validation;
|
||||
|
||||
namespace WireMock.Matchers
|
||||
{
|
||||
/// <summary>
|
||||
/// SimMetricsMatcher
|
||||
/// </summary>
|
||||
/// <seealso cref="IMatcher" />
|
||||
public class SimMetricsMatcher : IMatcher
|
||||
{
|
||||
private readonly string[] _patterns;
|
||||
private readonly SimMetricType _simMetricType;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="SimMetricsMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="pattern">The pattern.</param>
|
||||
/// <param name="simMetricType">The SimMetric Type</param>
|
||||
public SimMetricsMatcher([NotNull] string pattern, SimMetricType simMetricType = SimMetricType.Levenstein) : this(new [] { pattern }, simMetricType)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="SimMetricsMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="patterns">The patterns.</param>
|
||||
/// <param name="simMetricType">The SimMetric Type</param>
|
||||
public SimMetricsMatcher([NotNull] string[] patterns, SimMetricType simMetricType = SimMetricType.Levenstein)
|
||||
{
|
||||
Check.NotEmpty(patterns, nameof(patterns));
|
||||
|
||||
_patterns = patterns;
|
||||
_simMetricType = simMetricType;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified input is match.
|
||||
/// </summary>
|
||||
/// <param name="input">The input string</param>
|
||||
/// <returns>A value between 0.0 - 1.0 of the similarity.</returns>
|
||||
public double IsMatch(string input)
|
||||
{
|
||||
IStringMetric m = GetStringMetricType();
|
||||
|
||||
return MatchScores.ToScore(_patterns.Select(p => m.GetSimilarity(p, input)));
|
||||
}
|
||||
|
||||
private IStringMetric GetStringMetricType()
|
||||
{
|
||||
switch (_simMetricType)
|
||||
{
|
||||
case SimMetricType.BlockDistance:
|
||||
return new BlockDistance();
|
||||
case SimMetricType.ChapmanLengthDeviation:
|
||||
return new ChapmanLengthDeviation();
|
||||
case SimMetricType.CosineSimilarity:
|
||||
return new CosineSimilarity();
|
||||
case SimMetricType.DiceSimilarity:
|
||||
return new DiceSimilarity();
|
||||
case SimMetricType.EuclideanDistance:
|
||||
return new EuclideanDistance();
|
||||
case SimMetricType.JaccardSimilarity:
|
||||
return new JaccardSimilarity();
|
||||
case SimMetricType.Jaro:
|
||||
return new Jaro();
|
||||
case SimMetricType.JaroWinkler:
|
||||
return new JaroWinkler();
|
||||
case SimMetricType.MatchingCoefficient:
|
||||
return new MatchingCoefficient();
|
||||
case SimMetricType.MongeElkan:
|
||||
return new MongeElkan();
|
||||
case SimMetricType.NeedlemanWunch:
|
||||
return new NeedlemanWunch();
|
||||
case SimMetricType.OverlapCoefficient:
|
||||
return new OverlapCoefficient();
|
||||
case SimMetricType.QGramsDistance:
|
||||
return new QGramsDistance();
|
||||
case SimMetricType.SmithWaterman:
|
||||
return new SmithWaterman();
|
||||
case SimMetricType.SmithWatermanGotoh:
|
||||
return new SmithWatermanGotoh();
|
||||
case SimMetricType.SmithWatermanGotohWindowedAffine:
|
||||
return new SmithWatermanGotohWindowedAffine();
|
||||
case SimMetricType.ChapmanMeanLength:
|
||||
return new ChapmanMeanLength();
|
||||
default:
|
||||
return new Levenstein();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the pattern.
|
||||
/// </summary>
|
||||
/// <returns>Pattern</returns>
|
||||
public string[] GetPatterns()
|
||||
{
|
||||
return _patterns;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name.
|
||||
/// </summary>
|
||||
/// <returns>Name</returns>
|
||||
public string GetName()
|
||||
{
|
||||
return $"SimMetricsMatcher.{_simMetricType}";
|
||||
}
|
||||
}
|
||||
}
|
||||
54
src/WireMock.Net/Matchers/WildcardMatcher.cs
Normal file
54
src/WireMock.Net/Matchers/WildcardMatcher.cs
Normal file
@@ -0,0 +1,54 @@
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using JetBrains.Annotations;
|
||||
|
||||
namespace WireMock.Matchers
|
||||
{
|
||||
/// <summary>
|
||||
/// WildcardMatcher
|
||||
/// </summary>
|
||||
/// <seealso cref="IMatcher" />
|
||||
public class WildcardMatcher : RegexMatcher
|
||||
{
|
||||
private readonly string[] _patterns;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="WildcardMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="pattern">The pattern.</param>
|
||||
/// <param name="ignoreCase">IgnoreCase</param>
|
||||
public WildcardMatcher([NotNull] string pattern, bool ignoreCase = false) : this(new [] { pattern }, ignoreCase)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="WildcardMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="patterns">The patterns.</param>
|
||||
/// <param name="ignoreCase">IgnoreCase</param>
|
||||
public WildcardMatcher([NotNull] string[] patterns, bool ignoreCase = false) : base(patterns.Select(pattern => "^" + Regex.Escape(pattern).Replace(@"\*", ".*").Replace(@"\?", ".") + "$").ToArray(), ignoreCase)
|
||||
{
|
||||
_patterns = patterns;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the pattern.
|
||||
/// </summary>
|
||||
/// <returns>Pattern</returns>
|
||||
public override string[] GetPatterns()
|
||||
{
|
||||
return _patterns;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// Name
|
||||
/// </returns>
|
||||
public override string GetName()
|
||||
{
|
||||
return "WildcardMatcher";
|
||||
}
|
||||
}
|
||||
}
|
||||
71
src/WireMock.Net/Matchers/XPathMatcher.cs
Normal file
71
src/WireMock.Net/Matchers/XPathMatcher.cs
Normal file
@@ -0,0 +1,71 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Xml;
|
||||
using JetBrains.Annotations;
|
||||
using WireMock.Validation;
|
||||
using Wmhelp.XPath2;
|
||||
|
||||
namespace WireMock.Matchers
|
||||
{
|
||||
/// <summary>
|
||||
/// XPath2Matcher
|
||||
/// </summary>
|
||||
/// <seealso cref="WireMock.Matchers.IMatcher" />
|
||||
public class XPathMatcher : IMatcher
|
||||
{
|
||||
private readonly string[] _patterns;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="XPathMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="patterns">The patterns.</param>
|
||||
public XPathMatcher([NotNull] params string[] patterns)
|
||||
{
|
||||
Check.NotNull(patterns, nameof(patterns));
|
||||
|
||||
_patterns = patterns;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified input is match.
|
||||
/// </summary>
|
||||
/// <param name="input">The input string</param>
|
||||
/// <returns>A value between 0.0 - 1.0 of the similarity.</returns>
|
||||
public double IsMatch(string input)
|
||||
{
|
||||
if (input == null)
|
||||
return MatchScores.Mismatch;
|
||||
|
||||
try
|
||||
{
|
||||
var nav = new XmlDocument { InnerXml = input }.CreateNavigator();
|
||||
|
||||
return MatchScores.ToScore(_patterns.Select(p => true.Equals(nav.XPath2Evaluate($"boolean({p})"))));
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return MatchScores.Mismatch;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the patterns.
|
||||
/// </summary>
|
||||
/// <returns>Patterns</returns>
|
||||
public string[] GetPatterns()
|
||||
{
|
||||
return _patterns;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// Name
|
||||
/// </returns>
|
||||
public string GetName()
|
||||
{
|
||||
return "XPathMatcher";
|
||||
}
|
||||
}
|
||||
}
|
||||
47
src/WireMock.Net/RequestBuilders/IBodyRequestBuilder.cs
Normal file
47
src/WireMock.Net/RequestBuilders/IBodyRequestBuilder.cs
Normal file
@@ -0,0 +1,47 @@
|
||||
using System;
|
||||
using JetBrains.Annotations;
|
||||
using WireMock.Matchers;
|
||||
|
||||
namespace WireMock.RequestBuilders
|
||||
{
|
||||
/// <summary>
|
||||
/// The BodyRequestBuilder interface.
|
||||
/// </summary>
|
||||
public interface IBodyRequestBuilder
|
||||
{
|
||||
/// <summary>
|
||||
/// The with body.
|
||||
/// </summary>
|
||||
/// <param name="matcher">The matcher.</param>
|
||||
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
|
||||
IRequestBuilder WithBody([NotNull] IMatcher matcher);
|
||||
|
||||
/// <summary>
|
||||
/// The with body.
|
||||
/// </summary>
|
||||
/// <param name="body">The body.</param>
|
||||
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
|
||||
IRequestBuilder WithBody(string body);
|
||||
|
||||
/// <summary>
|
||||
/// The with body byte[].
|
||||
/// </summary>
|
||||
/// <param name="body">The body as byte[].</param>
|
||||
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
|
||||
IRequestBuilder WithBody(byte[] body);
|
||||
|
||||
/// <summary>
|
||||
/// The with body string func.
|
||||
/// </summary>
|
||||
/// <param name="body">The body string function.</param>
|
||||
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
|
||||
IRequestBuilder WithBody([NotNull] Func<string, bool> body);
|
||||
|
||||
/// <summary>
|
||||
/// The with body byte[] func.
|
||||
/// </summary>
|
||||
/// <param name="body">The body byte[] function.</param>
|
||||
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
|
||||
IRequestBuilder WithBody([NotNull] Func<byte[], bool> body);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using JetBrains.Annotations;
|
||||
using WireMock.Matchers;
|
||||
using WireMock.Matchers.Request;
|
||||
|
||||
namespace WireMock.RequestBuilders
|
||||
{
|
||||
/// <summary>
|
||||
/// The HeadersAndCookieRequestBuilder interface.
|
||||
/// </summary>
|
||||
public interface IHeadersAndCookiesRequestBuilder : IBodyRequestBuilder, IRequestMatcher, IParamsRequestBuilder
|
||||
{
|
||||
/// <summary>
|
||||
/// The with header.
|
||||
/// </summary>
|
||||
/// <param name="name">The name.</param>
|
||||
/// <param name="pattern">The pattern.</param>
|
||||
/// <param name="ignoreCase">ignore Case</param>
|
||||
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
|
||||
IRequestBuilder WithHeader([NotNull] string name, string pattern, bool ignoreCase = true);
|
||||
|
||||
/// <summary>
|
||||
/// The with header.
|
||||
/// </summary>
|
||||
/// <param name="name">The name.</param>
|
||||
/// <param name="matchers">The matchers.</param>
|
||||
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
|
||||
IRequestBuilder WithHeader([NotNull] string name, [NotNull] params IMatcher[] matchers);
|
||||
|
||||
/// <summary>
|
||||
/// The with header.
|
||||
/// </summary>
|
||||
/// <param name="funcs">The headers funcs.</param>
|
||||
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
|
||||
IRequestBuilder WithHeader([NotNull] params Func<IDictionary<string, string>, bool>[] funcs);
|
||||
|
||||
/// <summary>
|
||||
/// The with cookie.
|
||||
/// </summary>
|
||||
/// <param name="name">The name.</param>
|
||||
/// <param name="pattern">The pattern.</param>
|
||||
/// <param name="ignoreCase">ignore Case</param>
|
||||
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
|
||||
IRequestBuilder WithCookie([NotNull] string name, string pattern, bool ignoreCase = true);
|
||||
|
||||
/// <summary>
|
||||
/// The with cookie.
|
||||
/// </summary>
|
||||
/// <param name="name">The name.</param>
|
||||
/// <param name="matchers">The matchers.</param>
|
||||
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
|
||||
IRequestBuilder WithCookie([NotNull] string name, [NotNull] params IMatcher[] matchers);
|
||||
|
||||
/// <summary>
|
||||
/// The with cookie.
|
||||
/// </summary>
|
||||
/// <param name="cookieFuncs">The funcs.</param>
|
||||
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
|
||||
IRequestBuilder WithCookie([NotNull] params Func<IDictionary<string, string>, bool>[] cookieFuncs);
|
||||
}
|
||||
}
|
||||
65
src/WireMock.Net/RequestBuilders/IMethodRequestBuilder.cs
Normal file
65
src/WireMock.Net/RequestBuilders/IMethodRequestBuilder.cs
Normal file
@@ -0,0 +1,65 @@
|
||||
using JetBrains.Annotations;
|
||||
|
||||
namespace WireMock.RequestBuilders
|
||||
{
|
||||
/// <summary>
|
||||
/// The MethodRequestBuilder interface.
|
||||
/// </summary>
|
||||
public interface IMethodRequestBuilder : IHeadersAndCookiesRequestBuilder
|
||||
{
|
||||
/// <summary>
|
||||
/// The using get.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// The <see cref="IRequestBuilder"/>.
|
||||
/// </returns>
|
||||
IRequestBuilder UsingGet();
|
||||
|
||||
/// <summary>
|
||||
/// The using post.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// The <see cref="IRequestBuilder"/>.
|
||||
/// </returns>
|
||||
IRequestBuilder UsingPost();
|
||||
|
||||
/// <summary>
|
||||
/// The using delete.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// The <see cref="IRequestBuilder"/>.
|
||||
/// </returns>
|
||||
IRequestBuilder UsingDelete();
|
||||
|
||||
/// <summary>
|
||||
/// The using put.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// The <see cref="IRequestBuilder"/>.
|
||||
/// </returns>
|
||||
IRequestBuilder UsingPut();
|
||||
|
||||
/// <summary>
|
||||
/// The using head.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// The <see cref="IRequestBuilder"/>.
|
||||
/// </returns>
|
||||
IRequestBuilder UsingHead();
|
||||
|
||||
/// <summary>
|
||||
/// The using any verb.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// The <see cref="IRequestBuilder"/>.
|
||||
/// </returns>
|
||||
IRequestBuilder UsingAnyVerb();
|
||||
|
||||
/// <summary>
|
||||
/// The using verb.
|
||||
/// </summary>
|
||||
/// <param name="verbs">The verb.</param>
|
||||
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
|
||||
IRequestBuilder UsingVerb([NotNull] params string[] verbs);
|
||||
}
|
||||
}
|
||||
28
src/WireMock.Net/RequestBuilders/IParamsRequestBuilder.cs
Normal file
28
src/WireMock.Net/RequestBuilders/IParamsRequestBuilder.cs
Normal file
@@ -0,0 +1,28 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using JetBrains.Annotations;
|
||||
using WireMock.Util;
|
||||
|
||||
namespace WireMock.RequestBuilders
|
||||
{
|
||||
/// <summary>
|
||||
/// The ParametersRequestBuilder interface.
|
||||
/// </summary>
|
||||
public interface IParamsRequestBuilder
|
||||
{
|
||||
/// <summary>
|
||||
/// The with parameters.
|
||||
/// </summary>
|
||||
/// <param name="key">The key.</param>
|
||||
/// <param name="values">The values.</param>
|
||||
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
|
||||
IRequestBuilder WithParam([NotNull] string key, [CanBeNull] params string[] values);
|
||||
|
||||
/// <summary>
|
||||
/// The with parameters.
|
||||
/// </summary>
|
||||
/// <param name="funcs">The funcs.</param>
|
||||
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
|
||||
IRequestBuilder WithParam([NotNull] params Func<IDictionary<string, WireMockList<string>>, bool>[] funcs);
|
||||
}
|
||||
}
|
||||
9
src/WireMock.Net/RequestBuilders/IRequestBuilder.cs
Normal file
9
src/WireMock.Net/RequestBuilders/IRequestBuilder.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
namespace WireMock.RequestBuilders
|
||||
{
|
||||
/// <summary>
|
||||
/// IRequestBuilder
|
||||
/// </summary>
|
||||
public interface IRequestBuilder : IUrlAndPathRequestBuilder
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
using System;
|
||||
using JetBrains.Annotations;
|
||||
using WireMock.Matchers;
|
||||
|
||||
namespace WireMock.RequestBuilders
|
||||
{
|
||||
/// <summary>
|
||||
/// IUrlAndPathRequestBuilder
|
||||
/// </summary>
|
||||
public interface IUrlAndPathRequestBuilder : IMethodRequestBuilder
|
||||
{
|
||||
/// <summary>
|
||||
/// The with path.
|
||||
/// </summary>
|
||||
/// <param name="matchers">The matchers.</param>
|
||||
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
|
||||
IRequestBuilder WithPath([NotNull] params IMatcher[] matchers);
|
||||
|
||||
/// <summary>
|
||||
/// The with path.
|
||||
/// </summary>
|
||||
/// <param name="paths">The paths.</param>
|
||||
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
|
||||
IRequestBuilder WithPath([NotNull] params string[] paths);
|
||||
|
||||
/// <summary>
|
||||
/// The with path.
|
||||
/// </summary>
|
||||
/// <param name="funcs">The path funcs.</param>
|
||||
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
|
||||
IRequestBuilder WithPath([NotNull] params Func<string, bool>[] funcs);
|
||||
|
||||
/// <summary>
|
||||
/// The with url.
|
||||
/// </summary>
|
||||
/// <param name="matchers">The matchers.</param>
|
||||
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
|
||||
IRequestBuilder WithUrl([NotNull] params IMatcher[] matchers);
|
||||
|
||||
/// <summary>
|
||||
/// The with url.
|
||||
/// </summary>
|
||||
/// <param name="urls">The urls.</param>
|
||||
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
|
||||
IRequestBuilder WithUrl([NotNull] params string[] urls);
|
||||
|
||||
/// <summary>
|
||||
/// The with path.
|
||||
/// </summary>
|
||||
/// <param name="func">The path func.</param>
|
||||
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
|
||||
IRequestBuilder WithUrl([NotNull] params Func<string, bool>[] func);
|
||||
}
|
||||
}
|
||||
407
src/WireMock.Net/RequestBuilders/Request.cs
Normal file
407
src/WireMock.Net/RequestBuilders/Request.cs
Normal file
@@ -0,0 +1,407 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using WireMock.Matchers;
|
||||
using WireMock.Matchers.Request;
|
||||
using WireMock.Util;
|
||||
using WireMock.Validation;
|
||||
|
||||
namespace WireMock.RequestBuilders
|
||||
{
|
||||
/// <summary>
|
||||
/// The requests.
|
||||
/// </summary>
|
||||
public class Request : RequestMessageCompositeMatcher, IRequestBuilder
|
||||
{
|
||||
private readonly IList<IRequestMatcher> _requestMatchers;
|
||||
|
||||
/// <summary>
|
||||
/// Creates this instance.
|
||||
/// </summary>
|
||||
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
|
||||
public static IRequestBuilder Create()
|
||||
{
|
||||
return new Request(new List<IRequestMatcher>());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Request"/> class.
|
||||
/// </summary>
|
||||
/// <param name="requestMatchers">The request matchers.</param>
|
||||
private Request(IList<IRequestMatcher> requestMatchers) : base(requestMatchers)
|
||||
{
|
||||
_requestMatchers = requestMatchers;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the request message matchers.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of IRequestMatcher</typeparam>
|
||||
/// <returns>A List{T}</returns>
|
||||
public IList<T> GetRequestMessageMatchers<T>() where T : IRequestMatcher
|
||||
{
|
||||
return new ReadOnlyCollection<T>(_requestMatchers.Where(rm => rm is T).Cast<T>().ToList());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the request message matcher.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of IRequestMatcher</typeparam>
|
||||
/// <returns>A RequestMatcher</returns>
|
||||
public T GetRequestMessageMatcher<T>() where T : IRequestMatcher
|
||||
{
|
||||
return _requestMatchers.Where(rm => rm is T).Cast<T>().FirstOrDefault();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The with path.
|
||||
/// </summary>
|
||||
/// <param name="matchers">The matchers.</param>
|
||||
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
|
||||
public IRequestBuilder WithPath(params IMatcher[] matchers)
|
||||
{
|
||||
Check.NotEmpty(matchers, nameof(matchers));
|
||||
|
||||
_requestMatchers.Add(new RequestMessagePathMatcher(matchers));
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The with path.
|
||||
/// </summary>
|
||||
/// <param name="paths">The paths.</param>
|
||||
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
|
||||
public IRequestBuilder WithPath(params string[] paths)
|
||||
{
|
||||
Check.NotEmpty(paths, nameof(paths));
|
||||
|
||||
_requestMatchers.Add(new RequestMessagePathMatcher(paths));
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The with path.
|
||||
/// </summary>
|
||||
/// <param name="funcs">The path func.</param>
|
||||
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
|
||||
public IRequestBuilder WithPath(params Func<string, bool>[] funcs)
|
||||
{
|
||||
Check.NotEmpty(funcs, nameof(funcs));
|
||||
|
||||
_requestMatchers.Add(new RequestMessagePathMatcher(funcs));
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The with url.
|
||||
/// </summary>
|
||||
/// <param name="matchers">The matchers.</param>
|
||||
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
|
||||
public IRequestBuilder WithUrl(params IMatcher[] matchers)
|
||||
{
|
||||
Check.NotEmpty(matchers, nameof(matchers));
|
||||
|
||||
_requestMatchers.Add(new RequestMessageUrlMatcher(matchers));
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The with url.
|
||||
/// </summary>
|
||||
/// <param name="urls">The urls.</param>
|
||||
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
|
||||
public IRequestBuilder WithUrl(params string[] urls)
|
||||
{
|
||||
Check.NotEmpty(urls, nameof(urls));
|
||||
|
||||
_requestMatchers.Add(new RequestMessageUrlMatcher(urls));
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The with url.
|
||||
/// </summary>
|
||||
/// <param name="funcs">The url func.</param>
|
||||
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
|
||||
public IRequestBuilder WithUrl(params Func<string, bool>[] funcs)
|
||||
{
|
||||
Check.NotEmpty(funcs, nameof(funcs));
|
||||
|
||||
_requestMatchers.Add(new RequestMessageUrlMatcher(funcs));
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The using get.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// The <see cref="IRequestBuilder"/>.
|
||||
/// </returns>
|
||||
public IRequestBuilder UsingGet()
|
||||
{
|
||||
_requestMatchers.Add(new RequestMessageMethodMatcher("get"));
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The using post.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// The <see cref="IRequestBuilder"/>.
|
||||
/// </returns>
|
||||
public IRequestBuilder UsingPost()
|
||||
{
|
||||
_requestMatchers.Add(new RequestMessageMethodMatcher("post"));
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The using put.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// The <see cref="IRequestBuilder"/>.
|
||||
/// </returns>
|
||||
public IRequestBuilder UsingPut()
|
||||
{
|
||||
_requestMatchers.Add(new RequestMessageMethodMatcher("put"));
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The using delete.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// The <see cref="IRequestBuilder"/>.
|
||||
/// </returns>
|
||||
public IRequestBuilder UsingDelete()
|
||||
{
|
||||
_requestMatchers.Add(new RequestMessageMethodMatcher("delete"));
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The using head.
|
||||
/// </summary>
|
||||
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
|
||||
public IRequestBuilder UsingHead()
|
||||
{
|
||||
_requestMatchers.Add(new RequestMessageMethodMatcher("head"));
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The using any verb.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// The <see cref="IRequestBuilder"/>.
|
||||
/// </returns>
|
||||
public IRequestBuilder UsingAnyVerb()
|
||||
{
|
||||
var matchers = _requestMatchers.Where(m => m is RequestMessageMethodMatcher).ToList();
|
||||
foreach (var matcher in matchers)
|
||||
{
|
||||
_requestMatchers.Remove(matcher);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The using verb.
|
||||
/// </summary>
|
||||
/// <param name="verbs">The verbs.</param>
|
||||
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
|
||||
public IRequestBuilder UsingVerb(params string[] verbs)
|
||||
{
|
||||
Check.NotEmpty(verbs, nameof(verbs));
|
||||
|
||||
_requestMatchers.Add(new RequestMessageMethodMatcher(verbs));
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The with body.
|
||||
/// </summary>
|
||||
/// <param name="body">
|
||||
/// The body.
|
||||
/// </param>
|
||||
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
|
||||
public IRequestBuilder WithBody(string body)
|
||||
{
|
||||
_requestMatchers.Add(new RequestMessageBodyMatcher(body));
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The with body byte[].
|
||||
/// </summary>
|
||||
/// <param name="body">
|
||||
/// The body as byte[].
|
||||
/// </param>
|
||||
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
|
||||
public IRequestBuilder WithBody(byte[] body)
|
||||
{
|
||||
_requestMatchers.Add(new RequestMessageBodyMatcher(body));
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The with body.
|
||||
/// </summary>
|
||||
/// <param name="func">
|
||||
/// The body function.
|
||||
/// </param>
|
||||
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
|
||||
public IRequestBuilder WithBody(Func<string, bool> func)
|
||||
{
|
||||
Check.NotNull(func, nameof(func));
|
||||
|
||||
_requestMatchers.Add(new RequestMessageBodyMatcher(func));
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The with body.
|
||||
/// </summary>
|
||||
/// <param name="func">
|
||||
/// The body function.
|
||||
/// </param>
|
||||
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
|
||||
public IRequestBuilder WithBody(Func<byte[], bool> func)
|
||||
{
|
||||
Check.NotNull(func, nameof(func));
|
||||
|
||||
_requestMatchers.Add(new RequestMessageBodyMatcher(func));
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The with body.
|
||||
/// </summary>
|
||||
/// <param name="matcher">The matcher.</param>
|
||||
/// <returns>The <see cref="IRequestBuilder" />.</returns>
|
||||
public IRequestBuilder WithBody(IMatcher matcher)
|
||||
{
|
||||
Check.NotNull(matcher, nameof(matcher));
|
||||
|
||||
_requestMatchers.Add(new RequestMessageBodyMatcher(matcher));
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The with parameters.
|
||||
/// </summary>
|
||||
/// <param name="key">
|
||||
/// The key.
|
||||
/// </param>
|
||||
/// <param name="values">
|
||||
/// The values.
|
||||
/// </param>
|
||||
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
|
||||
public IRequestBuilder WithParam(string key, params string[] values)
|
||||
{
|
||||
Check.NotNull(key, nameof(key));
|
||||
|
||||
_requestMatchers.Add(new RequestMessageParamMatcher(key, values));
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The with parameters.
|
||||
/// </summary>
|
||||
/// <param name="funcs">The funcs.</param>
|
||||
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
|
||||
public IRequestBuilder WithParam(params Func<IDictionary<string, WireMockList<string>>, bool>[] funcs)
|
||||
{
|
||||
Check.NotEmpty(funcs, nameof(funcs));
|
||||
|
||||
_requestMatchers.Add(new RequestMessageParamMatcher(funcs));
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// With header.
|
||||
/// </summary>
|
||||
/// <param name="name">The name.</param>
|
||||
/// <param name="pattern">The pattern.</param>
|
||||
/// <param name="ignoreCase">if set to <c>true</c> [ignore case].</param>
|
||||
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
|
||||
public IRequestBuilder WithHeader(string name, string pattern, bool ignoreCase = true)
|
||||
{
|
||||
Check.NotNull(name, nameof(name));
|
||||
Check.NotNull(pattern, nameof(pattern));
|
||||
|
||||
_requestMatchers.Add(new RequestMessageHeaderMatcher(name, pattern, ignoreCase));
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// With header.
|
||||
/// </summary>
|
||||
/// <param name="name">The name.</param>
|
||||
/// <param name="matchers">The matchers.</param>
|
||||
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
|
||||
public IRequestBuilder WithHeader(string name, params IMatcher[] matchers)
|
||||
{
|
||||
Check.NotNull(name, nameof(name));
|
||||
Check.NotEmpty(matchers, nameof(matchers));
|
||||
|
||||
_requestMatchers.Add(new RequestMessageHeaderMatcher(name, matchers));
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// With header.
|
||||
/// </summary>
|
||||
/// <param name="funcs">The funcs.</param>
|
||||
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
|
||||
public IRequestBuilder WithHeader(params Func<IDictionary<string, string>, bool>[] funcs)
|
||||
{
|
||||
Check.NotEmpty(funcs, nameof(funcs));
|
||||
|
||||
_requestMatchers.Add(new RequestMessageHeaderMatcher(funcs));
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// With cookie.
|
||||
/// </summary>
|
||||
/// <param name="name">The name.</param>
|
||||
/// <param name="pattern">The pattern.</param>
|
||||
/// <param name="ignoreCase">if set to <c>true</c> [ignore case].</param>
|
||||
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
|
||||
public IRequestBuilder WithCookie(string name, string pattern, bool ignoreCase = true)
|
||||
{
|
||||
_requestMatchers.Add(new RequestMessageCookieMatcher(name, pattern, ignoreCase));
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// With cookie.
|
||||
/// </summary>
|
||||
/// <param name="name">The name.</param>
|
||||
/// <param name="matchers">The matchers.</param>
|
||||
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
|
||||
public IRequestBuilder WithCookie(string name, params IMatcher[] matchers)
|
||||
{
|
||||
Check.NotEmpty(matchers, nameof(matchers));
|
||||
|
||||
_requestMatchers.Add(new RequestMessageCookieMatcher(name, matchers));
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// With header.
|
||||
/// </summary>
|
||||
/// <param name="funcs">The funcs.</param>
|
||||
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
|
||||
public IRequestBuilder WithCookie(params Func<IDictionary<string, string>, bool>[] funcs)
|
||||
{
|
||||
Check.NotEmpty(funcs, nameof(funcs));
|
||||
|
||||
_requestMatchers.Add(new RequestMessageCookieMatcher(funcs));
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
127
src/WireMock.Net/RequestMessage.cs
Normal file
127
src/WireMock.Net/RequestMessage.cs
Normal file
@@ -0,0 +1,127 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using JetBrains.Annotations;
|
||||
using WireMock.Util;
|
||||
using WireMock.Validation;
|
||||
using System.Text;
|
||||
|
||||
namespace WireMock
|
||||
{
|
||||
/// <summary>
|
||||
/// The request.
|
||||
/// </summary>
|
||||
public class RequestMessage
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the url.
|
||||
/// </summary>
|
||||
public string Url { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the DateTime.
|
||||
/// </summary>
|
||||
public DateTime DateTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the path.
|
||||
/// </summary>
|
||||
public string Path { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the method.
|
||||
/// </summary>
|
||||
public string Method { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the headers.
|
||||
/// </summary>
|
||||
public IDictionary<string, string> Headers { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the cookies.
|
||||
/// </summary>
|
||||
public IDictionary<string, string> Cookies { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the query.
|
||||
/// </summary>
|
||||
public IDictionary<string, WireMockList<string>> Query { get; } = new Dictionary<string, WireMockList<string>>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the bodyAsBytes.
|
||||
/// </summary>
|
||||
public byte[] BodyAsBytes { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the body.
|
||||
/// </summary>
|
||||
public string Body { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the body encoding.
|
||||
/// </summary>
|
||||
public Encoding BodyEncoding { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RequestMessage"/> class.
|
||||
/// </summary>
|
||||
/// <param name="url">The original url.</param>
|
||||
/// <param name="verb">The verb.</param>
|
||||
/// <param name="bodyAsBytes">The bodyAsBytes byte[].</param>
|
||||
/// <param name="body">The body string.</param>
|
||||
/// <param name="bodyEncoding">The body encoding</param>
|
||||
/// <param name="headers">The headers.</param>
|
||||
/// <param name="cookies">The cookies.</param>
|
||||
public RequestMessage([NotNull] Uri url, [NotNull] string verb, [CanBeNull] byte[] bodyAsBytes = null, [CanBeNull] string body = null, [CanBeNull] Encoding bodyEncoding = null, [CanBeNull] IDictionary<string, string> headers = null, [CanBeNull] IDictionary<string, string> cookies = null)
|
||||
{
|
||||
Check.NotNull(url, nameof(url));
|
||||
Check.NotNull(verb, nameof(verb));
|
||||
|
||||
Url = url.ToString();
|
||||
Path = url.AbsolutePath;
|
||||
Method = verb.ToLower();
|
||||
BodyAsBytes = bodyAsBytes;
|
||||
Body = body;
|
||||
BodyEncoding = bodyEncoding;
|
||||
Headers = headers;
|
||||
Cookies = cookies;
|
||||
|
||||
string query = url.Query;
|
||||
if (!string.IsNullOrEmpty(query))
|
||||
{
|
||||
if (query.StartsWith("?"))
|
||||
{
|
||||
query = query.Substring(1);
|
||||
}
|
||||
|
||||
Query = query.Split('&').Aggregate(
|
||||
new Dictionary<string, WireMockList<string>>(),
|
||||
(dict, term) =>
|
||||
{
|
||||
var parts = term.Split('=');
|
||||
var key = parts[0];
|
||||
if (!dict.ContainsKey(key))
|
||||
{
|
||||
dict.Add(key, new WireMockList<string>());
|
||||
}
|
||||
|
||||
if (parts.Length == 2)
|
||||
dict[key].Add(parts[1]);
|
||||
|
||||
return dict;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The get a query parameter.
|
||||
/// </summary>
|
||||
/// <param name="key">The key.</param>
|
||||
/// <returns>The query parameter.</returns>
|
||||
public List<string> GetParameter(string key)
|
||||
{
|
||||
return Query.ContainsKey(key) ? Query[key] : null;
|
||||
}
|
||||
}
|
||||
}
|
||||
35
src/WireMock.Net/ResponseBuilders/IBodyResponseBuilder.cs
Normal file
35
src/WireMock.Net/ResponseBuilders/IBodyResponseBuilder.cs
Normal file
@@ -0,0 +1,35 @@
|
||||
using System.Text;
|
||||
using JetBrains.Annotations;
|
||||
|
||||
namespace WireMock.ResponseBuilders
|
||||
{
|
||||
/// <summary>
|
||||
/// The BodyResponseBuilder interface.
|
||||
/// </summary>
|
||||
public interface IBodyResponseBuilder : ITransformResponseBuilder
|
||||
{
|
||||
/// <summary>
|
||||
/// The with body.
|
||||
/// </summary>
|
||||
/// <param name="body">The body.</param>
|
||||
/// <param name="encoding">The body encoding.</param>
|
||||
/// <returns>A <see cref="IResponseBuilder"/>.</returns>
|
||||
IResponseBuilder WithBody([NotNull] string body, [CanBeNull] Encoding encoding = null);
|
||||
|
||||
/// <summary>
|
||||
/// The with body.
|
||||
/// </summary>
|
||||
/// <param name="body">The body.</param>
|
||||
/// <param name="encoding">The body encoding.</param>
|
||||
/// <returns>A <see cref="IResponseBuilder"/>.</returns>
|
||||
IResponseBuilder WithBodyAsJson([NotNull] object body, [CanBeNull] Encoding encoding = null);
|
||||
|
||||
/// <summary>
|
||||
/// The with body as base64.
|
||||
/// </summary>
|
||||
/// <param name="bodyAsbase64">The body asbase64.</param>
|
||||
/// <param name="encoding">The Encoding.</param>
|
||||
/// <returns>A <see cref="IResponseBuilder"/>.</returns>
|
||||
IResponseBuilder WithBodyAsBase64([NotNull] string bodyAsbase64, [CanBeNull] Encoding encoding = null);
|
||||
}
|
||||
}
|
||||
24
src/WireMock.Net/ResponseBuilders/IDelayResponseBuilder.cs
Normal file
24
src/WireMock.Net/ResponseBuilders/IDelayResponseBuilder.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
using System;
|
||||
|
||||
namespace WireMock.ResponseBuilders
|
||||
{
|
||||
/// <summary>
|
||||
/// The DelayResponseBuilder interface.
|
||||
/// </summary>
|
||||
public interface IDelayResponseBuilder : IResponseProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// The with delay.
|
||||
/// </summary>
|
||||
/// <param name="delay">The TimeSpan to delay.</param>
|
||||
/// <returns>The <see cref="IResponseBuilder"/>.</returns>
|
||||
IResponseBuilder WithDelay(TimeSpan delay);
|
||||
|
||||
/// <summary>
|
||||
/// The with delay.
|
||||
/// </summary>
|
||||
/// <param name="milliseconds">The milliseconds to delay.</param>
|
||||
/// <returns>The <see cref="IResponseBuilder"/>.</returns>
|
||||
IResponseBuilder WithDelay(int milliseconds);
|
||||
}
|
||||
}
|
||||
26
src/WireMock.Net/ResponseBuilders/IHeadersResponseBuilder.cs
Normal file
26
src/WireMock.Net/ResponseBuilders/IHeadersResponseBuilder.cs
Normal file
@@ -0,0 +1,26 @@
|
||||
using System.Collections.Generic;
|
||||
using JetBrains.Annotations;
|
||||
|
||||
namespace WireMock.ResponseBuilders
|
||||
{
|
||||
/// <summary>
|
||||
/// The HeadersResponseBuilder interface.
|
||||
/// </summary>
|
||||
public interface IHeadersResponseBuilder : IBodyResponseBuilder
|
||||
{
|
||||
/// <summary>
|
||||
/// The with header.
|
||||
/// </summary>
|
||||
/// <param name="name">The name.</param>
|
||||
/// <param name="value">The value.</param>
|
||||
/// <returns>The <see cref="IResponseBuilder"/>.</returns>
|
||||
IResponseBuilder WithHeader([NotNull] string name, string value);
|
||||
|
||||
/// <summary>
|
||||
/// The with headers.
|
||||
/// </summary>
|
||||
/// <param name="headers">The headers.</param>
|
||||
/// <returns>The <see cref="IResponseBuilder"/>.</returns>
|
||||
IResponseBuilder WithHeaders([NotNull] IDictionary<string,string> headers);
|
||||
}
|
||||
}
|
||||
9
src/WireMock.Net/ResponseBuilders/IResponseBuilder.cs
Normal file
9
src/WireMock.Net/ResponseBuilders/IResponseBuilder.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
namespace WireMock.ResponseBuilders
|
||||
{
|
||||
/// <summary>
|
||||
/// The ResponseBuilder interface.
|
||||
/// </summary>
|
||||
public interface IResponseBuilder : IStatusCodeResponseBuilder
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
using System.Net;
|
||||
|
||||
namespace WireMock.ResponseBuilders
|
||||
{
|
||||
/// <summary>
|
||||
/// The StatusCodeResponseBuilder interface.
|
||||
/// </summary>
|
||||
public interface IStatusCodeResponseBuilder : IHeadersResponseBuilder
|
||||
{
|
||||
/// <summary>
|
||||
/// The with status code.
|
||||
/// </summary>
|
||||
/// <param name="code">
|
||||
/// The code.
|
||||
/// </param>
|
||||
/// <returns>The <see cref="IResponseBuilder"/>.</returns>
|
||||
IResponseBuilder WithStatusCode(int code);
|
||||
|
||||
/// <summary>
|
||||
/// The with status code.
|
||||
/// </summary>
|
||||
/// <param name="code">
|
||||
/// The code.
|
||||
/// </param>
|
||||
/// <returns>The <see cref="IResponseBuilder"/>.</returns>
|
||||
IResponseBuilder WithStatusCode(HttpStatusCode code);
|
||||
|
||||
/// <summary>
|
||||
/// The with Success status code (200).
|
||||
/// </summary>
|
||||
/// <returns>The <see cref="IResponseBuilder"/>.</returns>
|
||||
IResponseBuilder WithSuccess();
|
||||
|
||||
/// <summary>
|
||||
/// The with NotFound status code (404).
|
||||
/// </summary>
|
||||
/// <returns>The <see cref="IResponseBuilder"/>.</returns>
|
||||
IResponseBuilder WithNotFound();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
namespace WireMock.ResponseBuilders
|
||||
{
|
||||
/// <summary>
|
||||
/// The BodyResponseBuilder interface.
|
||||
/// </summary>
|
||||
public interface ITransformResponseBuilder : IDelayResponseBuilder
|
||||
{
|
||||
/// <summary>
|
||||
/// The with transformer.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// The <see cref="IResponseBuilder"/>.
|
||||
/// </returns>
|
||||
IResponseBuilder WithTransformer();
|
||||
}
|
||||
}
|
||||
277
src/WireMock.Net/ResponseBuilders/Response.cs
Normal file
277
src/WireMock.Net/ResponseBuilders/Response.cs
Normal file
@@ -0,0 +1,277 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using HandlebarsDotNet;
|
||||
using JetBrains.Annotations;
|
||||
using Newtonsoft.Json;
|
||||
using WireMock.Validation;
|
||||
|
||||
namespace WireMock.ResponseBuilders
|
||||
{
|
||||
/// <summary>
|
||||
/// The Response.
|
||||
/// </summary>
|
||||
public class Response : IResponseBuilder
|
||||
{
|
||||
/// <summary>
|
||||
/// The delay
|
||||
/// </summary>
|
||||
public TimeSpan? Delay { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether [use transformer].
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <c>true</c> if [use transformer]; otherwise, <c>false</c>.
|
||||
/// </value>
|
||||
public bool UseTransformer { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the response message.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The response message.
|
||||
/// </value>
|
||||
public ResponseMessage ResponseMessage { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates this instance.
|
||||
/// </summary>
|
||||
/// <param name="responseMessage">ResponseMessage</param>
|
||||
/// <returns>A <see cref="IResponseBuilder"/>.</returns>
|
||||
[PublicAPI]
|
||||
public static IResponseBuilder Create([CanBeNull] ResponseMessage responseMessage = null)
|
||||
{
|
||||
var message = responseMessage ?? new ResponseMessage { StatusCode = (int)HttpStatusCode.OK };
|
||||
return new Response(message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates this instance.
|
||||
/// </summary>
|
||||
/// <returns>A <see cref="IResponseBuilder"/>.</returns>
|
||||
[PublicAPI]
|
||||
public static IResponseBuilder Create([NotNull] Func<ResponseMessage> func)
|
||||
{
|
||||
Check.NotNull(func, nameof(func));
|
||||
|
||||
return new Response(func());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Response"/> class.
|
||||
/// </summary>
|
||||
/// <param name="responseMessage">
|
||||
/// The response.
|
||||
/// </param>
|
||||
private Response(ResponseMessage responseMessage)
|
||||
{
|
||||
ResponseMessage = responseMessage;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The with status code.
|
||||
/// </summary>
|
||||
/// <param name="code">The code.</param>
|
||||
/// <returns>A <see cref="IResponseBuilder"/>.</returns>\
|
||||
[PublicAPI]
|
||||
public IResponseBuilder WithStatusCode(int code)
|
||||
{
|
||||
ResponseMessage.StatusCode = code;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The with status code.
|
||||
/// </summary>
|
||||
/// <param name="code">The code.</param>
|
||||
/// <returns>A <see cref="IResponseBuilder"/>.</returns>
|
||||
[PublicAPI]
|
||||
public IResponseBuilder WithStatusCode(HttpStatusCode code)
|
||||
{
|
||||
return WithStatusCode((int)code);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The with Success status code (200).
|
||||
/// </summary>
|
||||
/// <returns>A <see cref="IResponseBuilder"/>.</returns>
|
||||
[PublicAPI]
|
||||
public IResponseBuilder WithSuccess()
|
||||
{
|
||||
return WithStatusCode((int)HttpStatusCode.OK);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The with NotFound status code (404).
|
||||
/// </summary>
|
||||
/// <returns>The <see cref="IResponseBuilder"/>.</returns>
|
||||
[PublicAPI]
|
||||
public IResponseBuilder WithNotFound()
|
||||
{
|
||||
return WithStatusCode((int)HttpStatusCode.NotFound);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The with header.
|
||||
/// </summary>
|
||||
/// <param name="name">The name.</param>
|
||||
/// <param name="value">The value.</param>
|
||||
/// <returns>The <see cref="IResponseBuilder"/>.</returns>
|
||||
public IResponseBuilder WithHeader(string name, string value)
|
||||
{
|
||||
Check.NotNull(name, nameof(name));
|
||||
|
||||
ResponseMessage.AddHeader(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The with headers.
|
||||
/// </summary>
|
||||
/// <param name="headers">The headers.</param>
|
||||
/// <returns></returns>
|
||||
public IResponseBuilder WithHeaders(IDictionary<string, string> headers)
|
||||
{
|
||||
ResponseMessage.Headers = headers;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The with body.
|
||||
/// </summary>
|
||||
/// <param name="body">The body.</param>
|
||||
/// <param name="encoding">The body encoding.</param>
|
||||
/// <returns>A <see cref="IResponseBuilder"/>.</returns>
|
||||
public IResponseBuilder WithBody(string body, Encoding encoding = null)
|
||||
{
|
||||
Check.NotNull(body, nameof(body));
|
||||
|
||||
ResponseMessage.Body = body;
|
||||
ResponseMessage.BodyEncoding = encoding ?? Encoding.UTF8;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The with body (AsJson object).
|
||||
/// </summary>
|
||||
/// <param name="body">The body.</param>
|
||||
/// <param name="encoding">The body encoding.</param>
|
||||
/// <returns>A <see cref="IResponseBuilder"/>.</returns>
|
||||
public IResponseBuilder WithBodyAsJson(object body, Encoding encoding = null)
|
||||
{
|
||||
Check.NotNull(body, nameof(body));
|
||||
|
||||
string jsonBody = JsonConvert.SerializeObject(body, new JsonSerializerSettings { Formatting = Formatting.None, NullValueHandling = NullValueHandling.Ignore });
|
||||
|
||||
if (encoding != null && !encoding.Equals(Encoding.UTF8))
|
||||
{
|
||||
jsonBody = encoding.GetString(Encoding.UTF8.GetBytes(jsonBody));
|
||||
ResponseMessage.BodyEncoding = encoding;
|
||||
}
|
||||
|
||||
ResponseMessage.Body = jsonBody;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The with body as base64.
|
||||
/// </summary>
|
||||
/// <param name="bodyAsbase64">The body asbase64.</param>
|
||||
/// <param name="encoding">The Encoding.</param>
|
||||
/// <returns>A <see cref="IResponseBuilder"/>.</returns>
|
||||
public IResponseBuilder WithBodyAsBase64(string bodyAsbase64, Encoding encoding = null)
|
||||
{
|
||||
Check.NotNull(bodyAsbase64, nameof(bodyAsbase64));
|
||||
|
||||
encoding = encoding ?? Encoding.UTF8;
|
||||
|
||||
ResponseMessage.Body = encoding.GetString(Convert.FromBase64String(bodyAsbase64));
|
||||
ResponseMessage.BodyEncoding = encoding;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The with transformer.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// The <see cref="IResponseBuilder"/>.
|
||||
/// </returns>
|
||||
public IResponseBuilder WithTransformer()
|
||||
{
|
||||
UseTransformer = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The with delay.
|
||||
/// </summary>
|
||||
/// <param name="delay">The TimeSpan to delay.</param>
|
||||
/// <returns>The <see cref="IResponseBuilder"/>.</returns>
|
||||
public IResponseBuilder WithDelay(TimeSpan delay)
|
||||
{
|
||||
Check.Condition(delay, d => d > TimeSpan.Zero, nameof(delay));
|
||||
Delay = delay;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The with delay.
|
||||
/// </summary>
|
||||
/// <param name="milliseconds">The milliseconds to delay.</param>
|
||||
/// <returns>The <see cref="IResponseBuilder"/>.</returns>
|
||||
public IResponseBuilder WithDelay(int milliseconds)
|
||||
{
|
||||
return WithDelay(TimeSpan.FromMilliseconds(milliseconds));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The provide response.
|
||||
/// </summary>
|
||||
/// <param name="requestMessage">
|
||||
/// The request.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// The <see cref="Task"/>.
|
||||
/// </returns>
|
||||
public async Task<ResponseMessage> ProvideResponse(RequestMessage requestMessage)
|
||||
{
|
||||
ResponseMessage responseMessage;
|
||||
if (UseTransformer)
|
||||
{
|
||||
responseMessage = new ResponseMessage { StatusCode = ResponseMessage.StatusCode, BodyOriginal = ResponseMessage.Body };
|
||||
|
||||
var template = new { request = requestMessage };
|
||||
|
||||
// Body
|
||||
var templateBody = Handlebars.Compile(ResponseMessage.Body);
|
||||
responseMessage.Body = templateBody(template);
|
||||
|
||||
// Headers
|
||||
var newHeaders = new Dictionary<string, string>();
|
||||
foreach (var header in ResponseMessage.Headers)
|
||||
{
|
||||
var templateHeaderKey = Handlebars.Compile(header.Key);
|
||||
var templateHeaderValue = Handlebars.Compile(header.Value);
|
||||
|
||||
newHeaders.Add(templateHeaderKey(template), templateHeaderValue(template));
|
||||
}
|
||||
responseMessage.Headers = newHeaders;
|
||||
}
|
||||
else
|
||||
{
|
||||
responseMessage = ResponseMessage;
|
||||
}
|
||||
|
||||
if (Delay != null)
|
||||
await Task.Delay(Delay.Value);
|
||||
|
||||
return responseMessage;
|
||||
}
|
||||
}
|
||||
}
|
||||
51
src/WireMock.Net/ResponseMessage.cs
Normal file
51
src/WireMock.Net/ResponseMessage.cs
Normal file
@@ -0,0 +1,51 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace WireMock
|
||||
{
|
||||
/// <summary>
|
||||
/// The response.
|
||||
/// </summary>
|
||||
public class ResponseMessage
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the headers.
|
||||
/// </summary>
|
||||
public IDictionary<string, string> Headers { get; set; } = new ConcurrentDictionary<string, string>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the status code.
|
||||
/// </summary>
|
||||
public int StatusCode { get; set; } = 200;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the body.
|
||||
/// </summary>
|
||||
public string BodyOriginal { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the body.
|
||||
/// </summary>
|
||||
public string Body { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the body encoding.
|
||||
/// </summary>
|
||||
public Encoding BodyEncoding { get; set; } = new UTF8Encoding(false);
|
||||
|
||||
/// <summary>
|
||||
/// The add header.
|
||||
/// </summary>
|
||||
/// <param name="name">
|
||||
/// The name.
|
||||
/// </param>
|
||||
/// <param name="value">
|
||||
/// The value.
|
||||
/// </param>
|
||||
public void AddHeader(string name, string value)
|
||||
{
|
||||
Headers.Add(name, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
619
src/WireMock.Net/Server/FluentMockServer.Admin.cs
Normal file
619
src/WireMock.Net/Server/FluentMockServer.Admin.cs
Normal file
@@ -0,0 +1,619 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using JetBrains.Annotations;
|
||||
using Newtonsoft.Json;
|
||||
using SimMetrics.Net;
|
||||
using WireMock.Admin.Mappings;
|
||||
using WireMock.Admin.Requests;
|
||||
using WireMock.Admin.Settings;
|
||||
using WireMock.Logging;
|
||||
using WireMock.Matchers;
|
||||
using WireMock.Matchers.Request;
|
||||
using WireMock.RequestBuilders;
|
||||
using WireMock.ResponseBuilders;
|
||||
using WireMock.Util;
|
||||
using WireMock.Validation;
|
||||
|
||||
namespace WireMock.Server
|
||||
{
|
||||
/// <summary>
|
||||
/// The fluent mock server.
|
||||
/// </summary>
|
||||
public partial class FluentMockServer
|
||||
{
|
||||
private const string AdminMappingsFolder = @"\__admin\mappings\";
|
||||
private const string AdminMappings = "/__admin/mappings";
|
||||
private const string AdminRequests = "/__admin/requests";
|
||||
private const string AdminSettings = "/__admin/settings";
|
||||
private readonly RegexMatcher _adminMappingsGuidPathMatcher = new RegexMatcher(@"^\/__admin\/mappings\/(\{{0,1}([0-9a-fA-F]){8}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){12}\}{0,1})$");
|
||||
private readonly RegexMatcher _adminRequestsGuidPathMatcher = new RegexMatcher(@"^\/__admin\/requests\/(\{{0,1}([0-9a-fA-F]){8}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){12}\}{0,1})$");
|
||||
|
||||
private readonly JsonSerializerSettings _settings = new JsonSerializerSettings
|
||||
{
|
||||
Formatting = Formatting.Indented,
|
||||
NullValueHandling = NullValueHandling.Ignore,
|
||||
};
|
||||
|
||||
private void ReadStaticMappings()
|
||||
{
|
||||
if (!Directory.Exists(Directory.GetCurrentDirectory() + AdminMappingsFolder))
|
||||
return;
|
||||
|
||||
foreach (string filename in Directory.EnumerateFiles(Directory.GetCurrentDirectory() + AdminMappingsFolder))
|
||||
{
|
||||
var json = File.ReadAllText(filename);
|
||||
DeserializeAndAddMapping(json, Guid.Parse(Path.GetFileNameWithoutExtension(filename)));
|
||||
}
|
||||
}
|
||||
|
||||
private void InitAdmin()
|
||||
{
|
||||
// __admin/settings
|
||||
Given(Request.Create().WithPath(AdminSettings).UsingGet()).RespondWith(new DynamicResponseProvider(SettingsGet));
|
||||
Given(Request.Create().WithPath(AdminSettings).UsingVerb("PUT", "POST")).RespondWith(new DynamicResponseProvider(SettingsUpdate));
|
||||
|
||||
|
||||
// __admin/mappings
|
||||
Given(Request.Create().WithPath(AdminMappings).UsingGet()).RespondWith(new DynamicResponseProvider(MappingsGet));
|
||||
Given(Request.Create().WithPath(AdminMappings).UsingPost()).RespondWith(new DynamicResponseProvider(MappingsPost));
|
||||
Given(Request.Create().WithPath(AdminMappings).UsingDelete()).RespondWith(new DynamicResponseProvider(MappingsDelete));
|
||||
|
||||
// __admin/mappings/reset
|
||||
Given(Request.Create().WithPath(AdminMappings + "/reset").UsingPost()).RespondWith(new DynamicResponseProvider(MappingsDelete));
|
||||
|
||||
// __admin/mappings/{guid}
|
||||
Given(Request.Create().WithPath(_adminMappingsGuidPathMatcher).UsingGet()).RespondWith(new DynamicResponseProvider(MappingGet));
|
||||
Given(Request.Create().WithPath(_adminMappingsGuidPathMatcher).UsingPut().WithHeader("Content-Type", "application/json")).RespondWith(new DynamicResponseProvider(MappingPut));
|
||||
Given(Request.Create().WithPath(_adminMappingsGuidPathMatcher).UsingDelete()).RespondWith(new DynamicResponseProvider(MappingDelete));
|
||||
|
||||
// __admin/mappings/save
|
||||
Given(Request.Create().WithPath(AdminMappings + "/save").UsingPost()).RespondWith(new DynamicResponseProvider(MappingsSave));
|
||||
|
||||
|
||||
// __admin/requests
|
||||
Given(Request.Create().WithPath(AdminRequests).UsingGet()).RespondWith(new DynamicResponseProvider(RequestsGet));
|
||||
Given(Request.Create().WithPath(AdminRequests).UsingDelete()).RespondWith(new DynamicResponseProvider(RequestsDelete));
|
||||
|
||||
// __admin/requests/reset
|
||||
Given(Request.Create().WithPath(AdminRequests + "/reset").UsingPost()).RespondWith(new DynamicResponseProvider(RequestsDelete));
|
||||
|
||||
// __admin/request/{guid}
|
||||
Given(Request.Create().WithPath(_adminRequestsGuidPathMatcher).UsingGet()).RespondWith(new DynamicResponseProvider(RequestGet));
|
||||
Given(Request.Create().WithPath(_adminRequestsGuidPathMatcher).UsingDelete()).RespondWith(new DynamicResponseProvider(RequestDelete));
|
||||
|
||||
// __admin/requests/find
|
||||
Given(Request.Create().WithPath(AdminRequests + "/find").UsingPost()).RespondWith(new DynamicResponseProvider(RequestsFind));
|
||||
}
|
||||
|
||||
#region Settings
|
||||
private ResponseMessage SettingsGet(RequestMessage requestMessage)
|
||||
{
|
||||
var model = new SettingsModel
|
||||
{
|
||||
AllowPartialMapping = _allowPartialMapping,
|
||||
GlobalProcessingDelay = _requestProcessingDelay?.Milliseconds
|
||||
};
|
||||
|
||||
return ToJson(model);
|
||||
}
|
||||
|
||||
private ResponseMessage SettingsUpdate(RequestMessage requestMessage)
|
||||
{
|
||||
var settings = JsonConvert.DeserializeObject<SettingsModel>(requestMessage.Body);
|
||||
|
||||
if (settings.AllowPartialMapping != null)
|
||||
_allowPartialMapping = settings.AllowPartialMapping.Value;
|
||||
|
||||
if (settings.GlobalProcessingDelay != null)
|
||||
_requestProcessingDelay = TimeSpan.FromMilliseconds(settings.GlobalProcessingDelay.Value);
|
||||
|
||||
return new ResponseMessage { Body = "Settings updated" };
|
||||
}
|
||||
#endregion Settings
|
||||
|
||||
#region Mapping/{guid}
|
||||
private ResponseMessage MappingGet(RequestMessage requestMessage)
|
||||
{
|
||||
Guid guid = Guid.Parse(requestMessage.Path.Substring(AdminMappings.Length + 1));
|
||||
var mapping = Mappings.FirstOrDefault(m => !m.IsAdminInterface && m.Guid == guid);
|
||||
|
||||
if (mapping == null)
|
||||
return new ResponseMessage { StatusCode = 404, Body = "Mapping not found" };
|
||||
|
||||
var model = ToMappingModel(mapping);
|
||||
|
||||
return ToJson(model);
|
||||
}
|
||||
|
||||
private ResponseMessage MappingPut(RequestMessage requestMessage)
|
||||
{
|
||||
Guid guid = Guid.Parse(requestMessage.Path.TrimStart(AdminMappings.ToCharArray()));
|
||||
var mappingModel = JsonConvert.DeserializeObject<MappingModel>(requestMessage.Body);
|
||||
|
||||
if (mappingModel.Request == null)
|
||||
return new ResponseMessage { StatusCode = 400, Body = "Request missing" };
|
||||
|
||||
if (mappingModel.Response == null)
|
||||
return new ResponseMessage { StatusCode = 400, Body = "Response missing" };
|
||||
|
||||
var requestBuilder = InitRequestBuilder(mappingModel.Request);
|
||||
var responseBuilder = InitResponseBuilder(mappingModel.Response);
|
||||
|
||||
Given(requestBuilder)
|
||||
.WithGuid(guid)
|
||||
.RespondWith(responseBuilder);
|
||||
|
||||
return new ResponseMessage { Body = "Mapping added or updated" };
|
||||
}
|
||||
|
||||
private ResponseMessage MappingDelete(RequestMessage requestMessage)
|
||||
{
|
||||
Guid guid = Guid.Parse(requestMessage.Path.Substring(AdminMappings.Length + 1));
|
||||
|
||||
if (DeleteMapping(guid))
|
||||
return new ResponseMessage { Body = "Mapping removed" };
|
||||
|
||||
return new ResponseMessage { Body = "Mapping not found" };
|
||||
}
|
||||
#endregion Mapping/{guid}
|
||||
|
||||
#region Mappings
|
||||
private ResponseMessage MappingsSave(RequestMessage requestMessage)
|
||||
{
|
||||
string folder = Directory.GetCurrentDirectory() + AdminMappingsFolder;
|
||||
if (!Directory.Exists(folder))
|
||||
Directory.CreateDirectory(folder);
|
||||
|
||||
foreach (var mapping in Mappings.Where(m => !m.IsAdminInterface))
|
||||
{
|
||||
var model = ToMappingModel(mapping);
|
||||
string json = JsonConvert.SerializeObject(model, _settings);
|
||||
|
||||
File.WriteAllText(Path.Combine(folder, mapping.Guid + ".json"), json);
|
||||
}
|
||||
|
||||
return new ResponseMessage { Body = "Mappings saved to disk" };
|
||||
}
|
||||
|
||||
private ResponseMessage MappingsGet(RequestMessage requestMessage)
|
||||
{
|
||||
var result = new List<MappingModel>();
|
||||
foreach (var mapping in Mappings.Where(m => !m.IsAdminInterface))
|
||||
{
|
||||
var model = ToMappingModel(mapping);
|
||||
result.Add(model);
|
||||
}
|
||||
|
||||
return ToJson(result);
|
||||
}
|
||||
|
||||
private ResponseMessage MappingsPost(RequestMessage requestMessage)
|
||||
{
|
||||
try
|
||||
{
|
||||
DeserializeAndAddMapping(requestMessage.Body);
|
||||
}
|
||||
catch (ArgumentException a)
|
||||
{
|
||||
return new ResponseMessage { StatusCode = 400, Body = a.Message };
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return new ResponseMessage { StatusCode = 500, Body = e.ToString() };
|
||||
}
|
||||
|
||||
return new ResponseMessage { StatusCode = 201, Body = "Mapping added" };
|
||||
}
|
||||
|
||||
private void DeserializeAndAddMapping(string json, Guid? guid = null)
|
||||
{
|
||||
var mappingModel = JsonConvert.DeserializeObject<MappingModel>(json);
|
||||
|
||||
Check.NotNull(mappingModel, nameof(mappingModel));
|
||||
Check.NotNull(mappingModel.Request, nameof(mappingModel.Request));
|
||||
Check.NotNull(mappingModel.Response, nameof(mappingModel.Response));
|
||||
|
||||
var requestBuilder = InitRequestBuilder(mappingModel.Request);
|
||||
var responseBuilder = InitResponseBuilder(mappingModel.Response);
|
||||
|
||||
IRespondWithAProvider respondProvider = Given(requestBuilder);
|
||||
|
||||
if (guid != null)
|
||||
{
|
||||
respondProvider = respondProvider.WithGuid(guid.Value);
|
||||
}
|
||||
else if (mappingModel.Guid != null && mappingModel.Guid != Guid.Empty)
|
||||
{
|
||||
respondProvider = respondProvider.WithGuid(mappingModel.Guid.Value);
|
||||
}
|
||||
|
||||
if (mappingModel.Priority != null)
|
||||
respondProvider = respondProvider.AtPriority(mappingModel.Priority.Value);
|
||||
|
||||
respondProvider.RespondWith(responseBuilder);
|
||||
}
|
||||
|
||||
private ResponseMessage MappingsDelete(RequestMessage requestMessage)
|
||||
{
|
||||
ResetMappings();
|
||||
|
||||
return new ResponseMessage { Body = "Mappings deleted" };
|
||||
}
|
||||
#endregion Mappings
|
||||
|
||||
#region Request/{guid}
|
||||
private ResponseMessage RequestGet(RequestMessage requestMessage)
|
||||
{
|
||||
Guid guid = Guid.Parse(requestMessage.Path.Substring(AdminRequests.Length + 1));
|
||||
var entry = LogEntries.FirstOrDefault(r => !r.RequestMessage.Path.StartsWith("/__admin/") && r.Guid == guid);
|
||||
|
||||
if (entry == null)
|
||||
return new ResponseMessage { StatusCode = 404, Body = "Request not found" };
|
||||
|
||||
var model = ToLogEntryModel(entry);
|
||||
|
||||
return ToJson(model);
|
||||
}
|
||||
|
||||
private ResponseMessage RequestDelete(RequestMessage requestMessage)
|
||||
{
|
||||
Guid guid = Guid.Parse(requestMessage.Path.Substring(AdminRequests.Length + 1));
|
||||
|
||||
if (DeleteLogEntry(guid))
|
||||
return new ResponseMessage { Body = "Request removed" };
|
||||
|
||||
return new ResponseMessage { Body = "Request not found" };
|
||||
}
|
||||
#endregion Request/{guid}
|
||||
|
||||
#region Requests
|
||||
private ResponseMessage RequestsGet(RequestMessage requestMessage)
|
||||
{
|
||||
var result = LogEntries
|
||||
.Where(r => !r.RequestMessage.Path.StartsWith("/__admin/"))
|
||||
.Select(ToLogEntryModel);
|
||||
|
||||
return ToJson(result);
|
||||
}
|
||||
|
||||
private LogEntryModel ToLogEntryModel(LogEntry logEntry)
|
||||
{
|
||||
return new LogEntryModel
|
||||
{
|
||||
Guid = logEntry.Guid,
|
||||
Request = new LogRequestModel
|
||||
{
|
||||
DateTime = logEntry.RequestMessage.DateTime,
|
||||
Path = logEntry.RequestMessage.Path,
|
||||
AbsoleteUrl = logEntry.RequestMessage.Url,
|
||||
Query = logEntry.RequestMessage.Query,
|
||||
Method = logEntry.RequestMessage.Method,
|
||||
Body = logEntry.RequestMessage.Body,
|
||||
Headers = logEntry.RequestMessage.Headers,
|
||||
Cookies = logEntry.RequestMessage.Cookies,
|
||||
BodyEncoding = logEntry.RequestMessage.BodyEncoding != null ? new EncodingModel
|
||||
{
|
||||
EncodingName = logEntry.RequestMessage.BodyEncoding.EncodingName,
|
||||
CodePage = logEntry.RequestMessage.BodyEncoding.CodePage,
|
||||
WebName = logEntry.RequestMessage.BodyEncoding.WebName
|
||||
} : null
|
||||
},
|
||||
Response = new LogResponseModel
|
||||
{
|
||||
StatusCode = logEntry.ResponseMessage.StatusCode,
|
||||
Body = logEntry.ResponseMessage.Body,
|
||||
BodyOriginal = logEntry.ResponseMessage.BodyOriginal,
|
||||
Headers = logEntry.ResponseMessage.Headers,
|
||||
BodyEncoding = logEntry.ResponseMessage.BodyEncoding != null ? new EncodingModel
|
||||
{
|
||||
EncodingName = logEntry.ResponseMessage.BodyEncoding.EncodingName,
|
||||
CodePage = logEntry.ResponseMessage.BodyEncoding.CodePage,
|
||||
WebName = logEntry.ResponseMessage.BodyEncoding.WebName
|
||||
} : null
|
||||
},
|
||||
MappingGuid = logEntry.MappingGuid,
|
||||
RequestMatchResult = logEntry.RequestMatchResult != null ? new LogRequestMatchModel
|
||||
{
|
||||
TotalScore = logEntry.RequestMatchResult.TotalScore,
|
||||
TotalNumber = logEntry.RequestMatchResult.TotalNumber,
|
||||
IsPerfectMatch = logEntry.RequestMatchResult.IsPerfectMatch,
|
||||
AverageTotalScore = logEntry.RequestMatchResult.AverageTotalScore
|
||||
} : null
|
||||
};
|
||||
}
|
||||
|
||||
private ResponseMessage RequestsDelete(RequestMessage requestMessage)
|
||||
{
|
||||
ResetLogEntries();
|
||||
|
||||
return new ResponseMessage { Body = "Requests deleted" };
|
||||
}
|
||||
#endregion Requests
|
||||
|
||||
#region Requests/find
|
||||
private ResponseMessage RequestsFind(RequestMessage requestMessage)
|
||||
{
|
||||
var requestModel = JsonConvert.DeserializeObject<RequestModel>(requestMessage.Body);
|
||||
|
||||
var request = (Request)InitRequestBuilder(requestModel);
|
||||
|
||||
var dict = new Dictionary<LogEntry, RequestMatchResult>();
|
||||
foreach (var logEntry in LogEntries.Where(le => !le.RequestMessage.Path.StartsWith("/__admin/")))
|
||||
{
|
||||
var requestMatchResult = new RequestMatchResult();
|
||||
if (request.GetMatchingScore(logEntry.RequestMessage, requestMatchResult) > 0.99)
|
||||
dict.Add(logEntry, requestMatchResult);
|
||||
}
|
||||
|
||||
var result = dict.OrderBy(x => x.Value.AverageTotalScore).Select(x => x.Key);
|
||||
|
||||
return ToJson(result);
|
||||
}
|
||||
#endregion Requests/find
|
||||
|
||||
private IRequestBuilder InitRequestBuilder(RequestModel requestModel)
|
||||
{
|
||||
IRequestBuilder requestBuilder = Request.Create();
|
||||
|
||||
if (requestModel.Path != null)
|
||||
{
|
||||
string path = requestModel.Path as string;
|
||||
if (path != null)
|
||||
requestBuilder = requestBuilder.WithPath(path);
|
||||
else
|
||||
{
|
||||
var pathModel = JsonUtils.ParseJTokenToObject<PathModel>(requestModel.Path);
|
||||
if (pathModel?.Matchers != null)
|
||||
requestBuilder = requestBuilder.WithPath(pathModel.Matchers.Select(Map).ToArray());
|
||||
}
|
||||
}
|
||||
|
||||
if (requestModel.Url != null)
|
||||
{
|
||||
string url = requestModel.Url as string;
|
||||
if (url != null)
|
||||
requestBuilder = requestBuilder.WithUrl(url);
|
||||
else
|
||||
{
|
||||
var urlModel = JsonUtils.ParseJTokenToObject<UrlModel>(requestModel.Url);
|
||||
if (urlModel?.Matchers != null)
|
||||
requestBuilder = requestBuilder.WithUrl(urlModel.Matchers.Select(Map).ToArray());
|
||||
}
|
||||
}
|
||||
|
||||
if (requestModel.Methods != null)
|
||||
requestBuilder = requestBuilder.UsingVerb(requestModel.Methods);
|
||||
|
||||
if (requestModel.Headers != null)
|
||||
{
|
||||
foreach (var headerModel in requestModel.Headers.Where(h => h.Matchers != null))
|
||||
{
|
||||
requestBuilder = requestBuilder.WithHeader(headerModel.Name, headerModel.Matchers.Select(Map).ToArray());
|
||||
}
|
||||
}
|
||||
|
||||
if (requestModel.Cookies != null)
|
||||
{
|
||||
foreach (var cookieModel in requestModel.Cookies.Where(c => c.Matchers != null))
|
||||
{
|
||||
requestBuilder = requestBuilder.WithCookie(cookieModel.Name, cookieModel.Matchers.Select(Map).ToArray());
|
||||
}
|
||||
}
|
||||
|
||||
if (requestModel.Params != null)
|
||||
{
|
||||
foreach (var paramModel in requestModel.Params.Where(p => p.Values != null))
|
||||
{
|
||||
requestBuilder = requestBuilder.WithParam(paramModel.Name, paramModel.Values.ToArray());
|
||||
}
|
||||
}
|
||||
|
||||
if (requestModel.Body?.Matcher != null)
|
||||
{
|
||||
var bodyMatcher = Map(requestModel.Body.Matcher);
|
||||
requestBuilder = requestBuilder.WithBody(bodyMatcher);
|
||||
}
|
||||
|
||||
return requestBuilder;
|
||||
}
|
||||
|
||||
private IResponseBuilder InitResponseBuilder(ResponseModel responseModel)
|
||||
{
|
||||
IResponseBuilder responseBuilder = Response.Create();
|
||||
|
||||
if (responseModel.StatusCode.HasValue)
|
||||
responseBuilder = responseBuilder.WithStatusCode(responseModel.StatusCode.Value);
|
||||
|
||||
if (responseModel.Headers != null)
|
||||
responseBuilder = responseBuilder.WithHeaders(responseModel.Headers);
|
||||
|
||||
if (responseModel.Body != null)
|
||||
responseBuilder = responseBuilder.WithBody(responseModel.Body, ToEncoding(responseModel.BodyEncoding));
|
||||
else if (responseModel.BodyAsJson != null)
|
||||
responseBuilder = responseBuilder.WithBodyAsJson(responseModel.BodyAsJson, ToEncoding(responseModel.BodyEncoding));
|
||||
else if (responseModel.BodyAsBase64 != null)
|
||||
responseBuilder = responseBuilder.WithBodyAsBase64(responseModel.BodyAsBase64, ToEncoding(responseModel.BodyEncoding));
|
||||
|
||||
if (responseModel.UseTransformer)
|
||||
responseBuilder = responseBuilder.WithTransformer();
|
||||
|
||||
if (responseModel.Delay > 0)
|
||||
responseBuilder = responseBuilder.WithDelay(responseModel.Delay.Value);
|
||||
|
||||
return responseBuilder;
|
||||
}
|
||||
|
||||
private MappingModel ToMappingModel(Mapping mapping)
|
||||
{
|
||||
var request = (Request)mapping.RequestMatcher;
|
||||
var response = (Response)mapping.Provider;
|
||||
|
||||
var pathMatchers = request.GetRequestMessageMatchers<RequestMessagePathMatcher>();
|
||||
var urlMatchers = request.GetRequestMessageMatchers<RequestMessageUrlMatcher>();
|
||||
var headerMatchers = request.GetRequestMessageMatchers<RequestMessageHeaderMatcher>();
|
||||
var cookieMatchers = request.GetRequestMessageMatchers<RequestMessageCookieMatcher>();
|
||||
var paramsMatchers = request.GetRequestMessageMatchers<RequestMessageParamMatcher>();
|
||||
var bodyMatcher = request.GetRequestMessageMatcher<RequestMessageBodyMatcher>();
|
||||
var methodMatcher = request.GetRequestMessageMatcher<RequestMessageMethodMatcher>();
|
||||
|
||||
return new MappingModel
|
||||
{
|
||||
Guid = mapping.Guid,
|
||||
Priority = mapping.Priority,
|
||||
Request = new RequestModel
|
||||
{
|
||||
Path = pathMatchers != null && pathMatchers.Any() ? new PathModel
|
||||
{
|
||||
Matchers = Map(pathMatchers.Where(m => m.Matchers != null).SelectMany(m => m.Matchers)),
|
||||
Funcs = Map(pathMatchers.Where(m => m.Funcs != null).SelectMany(m => m.Funcs))
|
||||
} : null,
|
||||
|
||||
Url = urlMatchers != null && urlMatchers.Any() ? new UrlModel
|
||||
{
|
||||
Matchers = Map(urlMatchers.Where(m => m.Matchers != null).SelectMany(m => m.Matchers)),
|
||||
Funcs = Map(urlMatchers.Where(m => m.Funcs != null).SelectMany(m => m.Funcs))
|
||||
} : null,
|
||||
|
||||
Methods = methodMatcher?.Methods,
|
||||
|
||||
Headers = headerMatchers != null && headerMatchers.Any() ? headerMatchers.Select(hm => new HeaderModel
|
||||
{
|
||||
Name = hm.Name,
|
||||
Matchers = Map(hm.Matchers),
|
||||
Funcs = Map(hm.Funcs)
|
||||
}).ToList() : null,
|
||||
|
||||
Cookies = cookieMatchers != null && cookieMatchers.Any() ? cookieMatchers.Select(cm => new CookieModel
|
||||
{
|
||||
Name = cm.Name,
|
||||
Matchers = Map(cm.Matchers),
|
||||
Funcs = Map(cm.Funcs)
|
||||
}).ToList() : null,
|
||||
|
||||
Params = paramsMatchers != null && paramsMatchers.Any() ? paramsMatchers?.Select(pm => new ParamModel
|
||||
{
|
||||
Name = pm.Key,
|
||||
Values = pm.Values?.ToList(),
|
||||
Funcs = Map(pm.Funcs)
|
||||
}).ToList() : null,
|
||||
|
||||
Body = new BodyModel
|
||||
{
|
||||
Matcher = bodyMatcher != null ? Map(bodyMatcher.Matcher) : null,
|
||||
Func = bodyMatcher != null ? Map(bodyMatcher.Func) : null,
|
||||
DataFunc = bodyMatcher != null ? Map(bodyMatcher.DataFunc) : null
|
||||
}
|
||||
},
|
||||
Response = new ResponseModel
|
||||
{
|
||||
StatusCode = response.ResponseMessage.StatusCode,
|
||||
Headers = response.ResponseMessage.Headers,
|
||||
Body = response.ResponseMessage.Body,
|
||||
UseTransformer = response.UseTransformer,
|
||||
Delay = response.Delay?.Milliseconds,
|
||||
|
||||
BodyEncoding = response.ResponseMessage.BodyEncoding != null ? new EncodingModel
|
||||
{
|
||||
EncodingName = response.ResponseMessage.BodyEncoding.EncodingName,
|
||||
CodePage = response.ResponseMessage.BodyEncoding.CodePage,
|
||||
WebName = response.ResponseMessage.BodyEncoding.WebName
|
||||
} : null
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private MatcherModel[] Map([CanBeNull] IEnumerable<IMatcher> matchers)
|
||||
{
|
||||
if (matchers == null || !matchers.Any())
|
||||
return null;
|
||||
|
||||
return matchers.Select(Map).Where(x => x != null).ToArray();
|
||||
}
|
||||
|
||||
private MatcherModel Map([CanBeNull] IMatcher matcher)
|
||||
{
|
||||
if (matcher == null)
|
||||
return null;
|
||||
|
||||
var patterns = matcher.GetPatterns();
|
||||
|
||||
return new MatcherModel
|
||||
{
|
||||
Name = matcher.GetName(),
|
||||
Pattern = patterns.Length == 1 ? patterns.First() : null,
|
||||
Patterns = patterns.Length > 1 ? patterns : null
|
||||
};
|
||||
}
|
||||
|
||||
private string[] Map<T>([CanBeNull] IEnumerable<Func<T, bool>> funcs)
|
||||
{
|
||||
if (funcs == null || !funcs.Any())
|
||||
return null;
|
||||
|
||||
return funcs.Select(Map).Where(x => x != null).ToArray();
|
||||
}
|
||||
|
||||
private string Map<T>([CanBeNull] Func<T, bool> func)
|
||||
{
|
||||
return func?.ToString();
|
||||
}
|
||||
|
||||
private IMatcher Map([CanBeNull] MatcherModel matcher)
|
||||
{
|
||||
if (matcher == null)
|
||||
return null;
|
||||
|
||||
var parts = matcher.Name.Split('.');
|
||||
string matcherName = parts[0];
|
||||
string matcherType = parts.Length > 1 ? parts[1] : null;
|
||||
|
||||
string[] patterns = matcher.Patterns ?? new[] { matcher.Pattern };
|
||||
|
||||
switch (matcherName)
|
||||
{
|
||||
case "ExactMatcher":
|
||||
return new ExactMatcher(patterns);
|
||||
|
||||
case "RegexMatcher":
|
||||
return new RegexMatcher(patterns);
|
||||
|
||||
case "JsonPathMatcher":
|
||||
return new JsonPathMatcher(patterns);
|
||||
|
||||
case "XPathMatcher":
|
||||
return new XPathMatcher(matcher.Pattern);
|
||||
|
||||
case "WildcardMatcher":
|
||||
return new WildcardMatcher(patterns, matcher.IgnoreCase == true);
|
||||
|
||||
case "SimMetricsMatcher":
|
||||
SimMetricType type = SimMetricType.Levenstein;
|
||||
if (!string.IsNullOrEmpty(matcherType) && !Enum.TryParse(matcherType, out type))
|
||||
throw new NotSupportedException($"Matcher '{matcherName}' with Type '{matcherType}' is not supported.");
|
||||
|
||||
return new SimMetricsMatcher(matcher.Pattern, type);
|
||||
|
||||
default:
|
||||
throw new NotSupportedException($"Matcher '{matcherName}' is not supported.");
|
||||
}
|
||||
}
|
||||
|
||||
private ResponseMessage ToJson<T>(T result)
|
||||
{
|
||||
return new ResponseMessage
|
||||
{
|
||||
Body = JsonConvert.SerializeObject(result, _settings),
|
||||
StatusCode = 200,
|
||||
Headers = new Dictionary<string, string> { { "Content-Type", "application/json" } }
|
||||
};
|
||||
}
|
||||
|
||||
private Encoding ToEncoding(EncodingModel encodingModel)
|
||||
{
|
||||
return encodingModel != null ? Encoding.GetEncoding(encodingModel.CodePage) : null;
|
||||
}
|
||||
}
|
||||
}
|
||||
465
src/WireMock.Net/Server/FluentMockServer.cs
Normal file
465
src/WireMock.Net/Server/FluentMockServer.cs
Normal file
@@ -0,0 +1,465 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using JetBrains.Annotations;
|
||||
using WireMock.Http;
|
||||
using WireMock.Logging;
|
||||
using WireMock.Matchers;
|
||||
using WireMock.Matchers.Request;
|
||||
using WireMock.RequestBuilders;
|
||||
using WireMock.Validation;
|
||||
|
||||
namespace WireMock.Server
|
||||
{
|
||||
/// <summary>
|
||||
/// The fluent mock server.
|
||||
/// </summary>
|
||||
public partial class FluentMockServer
|
||||
{
|
||||
private readonly TinyHttpServer _httpServer;
|
||||
|
||||
private IList<Mapping> _mappings = new List<Mapping>();
|
||||
|
||||
private readonly IList<LogEntry> _logEntries = new List<LogEntry>();
|
||||
|
||||
private readonly HttpListenerRequestMapper _requestMapper = new HttpListenerRequestMapper();
|
||||
|
||||
private readonly HttpListenerResponseMapper _responseMapper = new HttpListenerResponseMapper();
|
||||
|
||||
private readonly object _syncRoot = new object();
|
||||
|
||||
private TimeSpan? _requestProcessingDelay;
|
||||
|
||||
private bool _allowPartialMapping;
|
||||
|
||||
private IMatcher _authorizationMatcher;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the ports.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The ports.
|
||||
/// </value>
|
||||
[PublicAPI]
|
||||
public List<int> Ports { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the urls.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public string[] Urls { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the request logs.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public IEnumerable<LogEntry> LogEntries
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (((ICollection)_logEntries).SyncRoot)
|
||||
{
|
||||
return new ReadOnlyCollection<LogEntry>(_logEntries);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The search log-entries based on matchers.
|
||||
/// </summary>
|
||||
/// <param name="matchers">The matchers.</param>
|
||||
/// <returns>The <see cref="IEnumerable"/>.</returns>
|
||||
[PublicAPI]
|
||||
public IEnumerable<LogEntry> FindLogEntries([NotNull] params IRequestMatcher[] matchers)
|
||||
{
|
||||
lock (((ICollection)_logEntries).SyncRoot)
|
||||
{
|
||||
var results = new Dictionary<LogEntry, RequestMatchResult>();
|
||||
|
||||
foreach (var log in _logEntries)
|
||||
{
|
||||
var requestMatchResult = new RequestMatchResult();
|
||||
foreach (var matcher in matchers)
|
||||
{
|
||||
matcher.GetMatchingScore(log.RequestMessage, requestMatchResult);
|
||||
}
|
||||
|
||||
if (requestMatchResult.AverageTotalScore > 0.99)
|
||||
results.Add(log, requestMatchResult);
|
||||
}
|
||||
|
||||
return new ReadOnlyCollection<LogEntry>(results.OrderBy(x => x.Value).Select(x => x.Key).ToList());
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the mappings.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public IEnumerable<Mapping> Mappings
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (((ICollection)_mappings).SyncRoot)
|
||||
{
|
||||
return new ReadOnlyCollection<Mapping>(_mappings);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Start this FluentMockServer.
|
||||
/// </summary>
|
||||
/// <param name="port">The port.</param>
|
||||
/// <param name="ssl">The SSL support.</param>
|
||||
/// <returns>The <see cref="FluentMockServer"/>.</returns>
|
||||
[PublicAPI]
|
||||
public static FluentMockServer Start(int port = 0, bool ssl = false)
|
||||
{
|
||||
Check.Condition(port, p => p >= 0, nameof(port));
|
||||
|
||||
if (port == 0)
|
||||
port = PortUtil.FindFreeTcpPort();
|
||||
|
||||
return new FluentMockServer(false, port, ssl);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Start this FluentMockServer.
|
||||
/// </summary>
|
||||
/// <param name="urls">The urls to listen on.</param>
|
||||
/// <returns>The <see cref="FluentMockServer"/>.</returns>
|
||||
[PublicAPI]
|
||||
public static FluentMockServer Start(params string[] urls)
|
||||
{
|
||||
Check.NotEmpty(urls, nameof(urls));
|
||||
|
||||
return new FluentMockServer(false, urls);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Start this FluentMockServer with the admin interface.
|
||||
/// </summary>
|
||||
/// <param name="port">The port.</param>
|
||||
/// <param name="ssl">The SSL support.</param>
|
||||
/// <returns>The <see cref="FluentMockServer"/>.</returns>
|
||||
[PublicAPI]
|
||||
public static FluentMockServer StartWithAdminInterface(int port = 0, bool ssl = false)
|
||||
{
|
||||
Check.Condition(port, p => p >= 0, nameof(port));
|
||||
|
||||
if (port == 0)
|
||||
port = PortUtil.FindFreeTcpPort();
|
||||
|
||||
return new FluentMockServer(true, port, ssl);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Start this FluentMockServer with the admin interface.
|
||||
/// </summary>
|
||||
/// <param name="urls">The urls.</param>
|
||||
/// <returns>The <see cref="FluentMockServer"/>.</returns>
|
||||
[PublicAPI]
|
||||
public static FluentMockServer StartWithAdminInterface(params string[] urls)
|
||||
{
|
||||
Check.NotEmpty(urls, nameof(urls));
|
||||
|
||||
return new FluentMockServer(true, urls);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the catch all mapping.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public void AddCatchAllMapping()
|
||||
{
|
||||
Given(Request.Create().WithPath("/*").UsingAnyVerb())
|
||||
.WithGuid(Guid.Parse("90008000-0000-4444-a17e-669cd84f1f05"))
|
||||
.AtPriority(1000)
|
||||
.RespondWith(new DynamicResponseProvider(request => new ResponseMessage { StatusCode = 404, Body = "No matching mapping found" }));
|
||||
}
|
||||
|
||||
private FluentMockServer(bool startAdminInterface, int port, bool ssl) : this(startAdminInterface, (ssl ? "https" : "http") + "://localhost:" + port + "/")
|
||||
{
|
||||
}
|
||||
|
||||
private FluentMockServer(bool startAdminInterface, params string[] urls)
|
||||
{
|
||||
Urls = urls;
|
||||
|
||||
_httpServer = new TinyHttpServer(HandleRequestAsync, urls);
|
||||
Ports = _httpServer.Ports;
|
||||
|
||||
_httpServer.Start();
|
||||
|
||||
if (startAdminInterface)
|
||||
{
|
||||
InitAdmin();
|
||||
}
|
||||
|
||||
ReadStaticMappings();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stop this server.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public void Stop()
|
||||
{
|
||||
_httpServer.Stop();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets LogEntries and Mappings.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public void Reset()
|
||||
{
|
||||
ResetLogEntries();
|
||||
|
||||
ResetMappings();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets the LogEntries.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public void ResetLogEntries()
|
||||
{
|
||||
lock (((ICollection)_logEntries).SyncRoot)
|
||||
{
|
||||
_logEntries.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes the mapping.
|
||||
/// </summary>
|
||||
/// <param name="guid">The unique identifier.</param>
|
||||
[PublicAPI]
|
||||
public bool DeleteLogEntry(Guid guid)
|
||||
{
|
||||
lock (((ICollection)_logEntries).SyncRoot)
|
||||
{
|
||||
// Check a logentry exists with the same GUID, if so, remove it.
|
||||
var existing = _logEntries.FirstOrDefault(m => m.Guid == guid);
|
||||
if (existing != null)
|
||||
{
|
||||
_logEntries.Remove(existing);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets the Mappings.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public void ResetMappings()
|
||||
{
|
||||
lock (((ICollection)_mappings).SyncRoot)
|
||||
{
|
||||
_mappings = _mappings.Where(m => m.Provider is DynamicResponseProvider).ToList();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes the mapping.
|
||||
/// </summary>
|
||||
/// <param name="guid">The unique identifier.</param>
|
||||
[PublicAPI]
|
||||
public bool DeleteMapping(Guid guid)
|
||||
{
|
||||
lock (((ICollection)_mappings).SyncRoot)
|
||||
{
|
||||
// Check a mapping exists with the same GUID, if so, remove it.
|
||||
var existingMapping = _mappings.FirstOrDefault(m => m.Guid == guid);
|
||||
if (existingMapping != null)
|
||||
{
|
||||
_mappings.Remove(existingMapping);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The add request processing delay.
|
||||
/// </summary>
|
||||
/// <param name="delay">
|
||||
/// The delay.
|
||||
/// </param>
|
||||
[PublicAPI]
|
||||
public void AddGlobalProcessingDelay(TimeSpan delay)
|
||||
{
|
||||
lock (_syncRoot)
|
||||
{
|
||||
_requestProcessingDelay = delay;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Allows the partial mapping.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public void AllowPartialMapping()
|
||||
{
|
||||
lock (_syncRoot)
|
||||
{
|
||||
_allowPartialMapping = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the basic authentication.
|
||||
/// </summary>
|
||||
/// <param name="username">The username.</param>
|
||||
/// <param name="password">The password.</param>
|
||||
[PublicAPI]
|
||||
public void SetBasicAuthentication([NotNull] string username, [NotNull] string password)
|
||||
{
|
||||
Check.NotNull(username, nameof(username));
|
||||
Check.NotNull(password, nameof(password));
|
||||
|
||||
string authorization = Convert.ToBase64String(Encoding.GetEncoding("ISO-8859-1").GetBytes(username + ":" + password));
|
||||
_authorizationMatcher = new RegexMatcher("^(?i)BASIC " + authorization + "$");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The given.
|
||||
/// </summary>
|
||||
/// <param name="requestMatcher">The request matcher.</param>
|
||||
/// <returns>The <see cref="IRespondWithAProvider"/>.</returns>
|
||||
[PublicAPI]
|
||||
public IRespondWithAProvider Given(IRequestMatcher requestMatcher)
|
||||
{
|
||||
return new RespondWithAProvider(RegisterMapping, requestMatcher);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The register mapping.
|
||||
/// </summary>
|
||||
/// <param name="mapping">
|
||||
/// The mapping.
|
||||
/// </param>
|
||||
private void RegisterMapping(Mapping mapping)
|
||||
{
|
||||
lock (((ICollection)_mappings).SyncRoot)
|
||||
{
|
||||
// Check a mapping exists with the same GUID, if so, remove it first.
|
||||
DeleteMapping(mapping.Guid);
|
||||
|
||||
_mappings.Add(mapping);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The log request.
|
||||
/// </summary>
|
||||
/// <param name="entry">The request.</param>
|
||||
private void LogRequest(LogEntry entry)
|
||||
{
|
||||
lock (((ICollection)_logEntries).SyncRoot)
|
||||
{
|
||||
_logEntries.Add(entry);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The handle request.
|
||||
/// </summary>
|
||||
/// <param name="ctx">The HttpListenerContext.</param>
|
||||
private async void HandleRequestAsync(HttpListenerContext ctx)
|
||||
{
|
||||
if (_requestProcessingDelay > TimeSpan.Zero)
|
||||
{
|
||||
lock (_syncRoot)
|
||||
{
|
||||
Task.Delay(_requestProcessingDelay.Value).Wait();
|
||||
}
|
||||
}
|
||||
|
||||
var request = _requestMapper.Map(ctx.Request);
|
||||
|
||||
ResponseMessage response = null;
|
||||
Mapping targetMapping = null;
|
||||
RequestMatchResult requestMatchResult = null;
|
||||
try
|
||||
{
|
||||
var mappings = _mappings
|
||||
.Select(m => new { Mapping = m, MatchResult = m.IsRequestHandled(request) })
|
||||
.ToList();
|
||||
|
||||
if (_allowPartialMapping)
|
||||
{
|
||||
var partialMappings = mappings
|
||||
.Where(pm => pm.Mapping.IsAdminInterface && pm.MatchResult.IsPerfectMatch || !pm.Mapping.IsAdminInterface)
|
||||
.OrderBy(m => m.MatchResult)
|
||||
.ThenBy(m => m.Mapping.Priority)
|
||||
.ToList();
|
||||
|
||||
var bestPartialMatch = partialMappings.FirstOrDefault(pm => pm.MatchResult.AverageTotalScore > 0.0);
|
||||
|
||||
targetMapping = bestPartialMatch?.Mapping;
|
||||
requestMatchResult = bestPartialMatch?.MatchResult;
|
||||
}
|
||||
else
|
||||
{
|
||||
var perfectMatch = mappings
|
||||
.OrderBy(m => m.Mapping.Priority)
|
||||
.FirstOrDefault(m => m.MatchResult.IsPerfectMatch);
|
||||
|
||||
targetMapping = perfectMatch?.Mapping;
|
||||
requestMatchResult = perfectMatch?.MatchResult;
|
||||
}
|
||||
|
||||
if (targetMapping == null)
|
||||
{
|
||||
response = new ResponseMessage { StatusCode = 404, Body = "No matching mapping found" };
|
||||
return;
|
||||
}
|
||||
|
||||
if (targetMapping.IsAdminInterface && _authorizationMatcher != null)
|
||||
{
|
||||
string authorization;
|
||||
bool present = request.Headers.TryGetValue("Authorization", out authorization);
|
||||
if (!present || _authorizationMatcher.IsMatch(authorization) < 1.0)
|
||||
{
|
||||
response = new ResponseMessage { StatusCode = 401 };
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
response = await targetMapping.ResponseTo(request);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
response = new ResponseMessage { StatusCode = 500, Body = ex.ToString() };
|
||||
}
|
||||
finally
|
||||
{
|
||||
var log = new LogEntry
|
||||
{
|
||||
Guid = Guid.NewGuid(),
|
||||
RequestMessage = request,
|
||||
ResponseMessage = response,
|
||||
MappingGuid = targetMapping?.Guid,
|
||||
RequestMatchResult = requestMatchResult
|
||||
};
|
||||
|
||||
LogRequest(log);
|
||||
|
||||
_responseMapper.Map(response, ctx.Response);
|
||||
ctx.Response.Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
39
src/WireMock.Net/Server/IRespondWithAProvider.cs
Normal file
39
src/WireMock.Net/Server/IRespondWithAProvider.cs
Normal file
@@ -0,0 +1,39 @@
|
||||
using System;
|
||||
|
||||
namespace WireMock.Server
|
||||
{
|
||||
/// <summary>
|
||||
/// IRespondWithAProvider
|
||||
/// </summary>
|
||||
public interface IRespondWithAProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Define a unique identifier for this mapping.
|
||||
/// </summary>
|
||||
/// <param name="guid">The unique identifier.</param>
|
||||
/// <returns>The <see cref="IRespondWithAProvider"/>.</returns>
|
||||
IRespondWithAProvider WithGuid(Guid guid);
|
||||
|
||||
/// <summary>
|
||||
/// Define a unique identifier for this mapping.
|
||||
/// </summary>
|
||||
/// <param name="guid">The unique identifier.</param>
|
||||
/// <returns>The <see cref="IRespondWithAProvider"/>.</returns>
|
||||
IRespondWithAProvider WithGuid(string guid);
|
||||
|
||||
/// <summary>
|
||||
/// Define the priority for this mapping.
|
||||
/// </summary>
|
||||
/// <param name="priority">The priority.</param>
|
||||
/// <returns>The <see cref="IRespondWithAProvider"/>.</returns>
|
||||
IRespondWithAProvider AtPriority(int priority);
|
||||
|
||||
/// <summary>
|
||||
/// The respond with.
|
||||
/// </summary>
|
||||
/// <param name="provider">
|
||||
/// The provider.
|
||||
/// </param>
|
||||
void RespondWith(IResponseProvider provider);
|
||||
}
|
||||
}
|
||||
81
src/WireMock.Net/Server/RespondWithAProvider.cs
Normal file
81
src/WireMock.Net/Server/RespondWithAProvider.cs
Normal file
@@ -0,0 +1,81 @@
|
||||
using System;
|
||||
using WireMock.Matchers.Request;
|
||||
|
||||
namespace WireMock.Server
|
||||
{
|
||||
/// <summary>
|
||||
/// The respond with a provider.
|
||||
/// </summary>
|
||||
internal class RespondWithAProvider : IRespondWithAProvider
|
||||
{
|
||||
private int _priority;
|
||||
private Guid? _guid;
|
||||
|
||||
/// <summary>
|
||||
/// The _registration callback.
|
||||
/// </summary>
|
||||
private readonly RegistrationCallback _registrationCallback;
|
||||
|
||||
/// <summary>
|
||||
/// The _request matcher.
|
||||
/// </summary>
|
||||
private readonly IRequestMatcher _requestMatcher;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RespondWithAProvider"/> class.
|
||||
/// </summary>
|
||||
/// <param name="registrationCallback">The registration callback.</param>
|
||||
/// <param name="requestMatcher">The request matcher.</param>
|
||||
public RespondWithAProvider(RegistrationCallback registrationCallback, IRequestMatcher requestMatcher)
|
||||
{
|
||||
_registrationCallback = registrationCallback;
|
||||
_requestMatcher = requestMatcher;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The respond with.
|
||||
/// </summary>
|
||||
/// <param name="provider">
|
||||
/// The provider.
|
||||
/// </param>
|
||||
public void RespondWith(IResponseProvider provider)
|
||||
{
|
||||
var mappingGuid = _guid ?? Guid.NewGuid();
|
||||
_registrationCallback(new Mapping(mappingGuid, _requestMatcher, provider, _priority));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Define a unique identifier for this mapping.
|
||||
/// </summary>
|
||||
/// <param name="guid">The unique identifier.</param>
|
||||
/// <returns>The <see cref="IRespondWithAProvider"/>.</returns>
|
||||
public IRespondWithAProvider WithGuid(string guid)
|
||||
{
|
||||
return WithGuid(Guid.Parse(guid));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Define a unique identifier for this mapping.
|
||||
/// </summary>
|
||||
/// <param name="guid">The unique identifier.</param>
|
||||
/// <returns>The <see cref="IRespondWithAProvider"/>.</returns>
|
||||
public IRespondWithAProvider WithGuid(Guid guid)
|
||||
{
|
||||
_guid = guid;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Define the priority for this mapping.
|
||||
/// </summary>
|
||||
/// <param name="priority">The priority.</param>
|
||||
/// <returns>The <see cref="IRespondWithAProvider"/>.</returns>
|
||||
public IRespondWithAProvider AtPriority(int priority)
|
||||
{
|
||||
_priority = priority;
|
||||
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
19
src/WireMock.Net/Util/JsonUtils.cs
Normal file
19
src/WireMock.Net/Util/JsonUtils.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace WireMock.Util
|
||||
{
|
||||
internal static class JsonUtils
|
||||
{
|
||||
public static T ParseJTokenToObject<T>(object value)
|
||||
{
|
||||
if (value == null)
|
||||
return default(T);
|
||||
|
||||
JToken token = value as JToken;
|
||||
if (token == null)
|
||||
return default(T);
|
||||
|
||||
return token.ToObject<T>();
|
||||
}
|
||||
}
|
||||
}
|
||||
50
src/WireMock.Net/Util/WireMockList.cs
Normal file
50
src/WireMock.Net/Util/WireMockList.cs
Normal file
@@ -0,0 +1,50 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace WireMock.Util
|
||||
{
|
||||
/// <summary>
|
||||
/// A special List which overrides the ToString() to return first value.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The generic type</typeparam>
|
||||
/// <seealso cref="List{T}" />
|
||||
public class WireMockList<T> : List<T>
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="WireMockList{T}"/> class.
|
||||
/// </summary>
|
||||
public WireMockList()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="WireMockList{T}"/> class.
|
||||
/// </summary>
|
||||
/// <param name="collection">The collection whose elements are copied to the new list.</param>
|
||||
public WireMockList(params T[] collection) : base(collection)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="WireMockList{T}"/> class.
|
||||
/// </summary>
|
||||
/// <param name="collection">The collection whose elements are copied to the new list.</param>
|
||||
public WireMockList(IEnumerable<T> collection) : base(collection)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a <see cref="string" /> that represents this instance.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A <see cref="string" /> that represents this instance.
|
||||
/// </returns>
|
||||
public override string ToString()
|
||||
{
|
||||
if (this != null && this.Any())
|
||||
return this.First().ToString();
|
||||
|
||||
return base.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
143
src/WireMock.Net/Validation/Check.cs
Normal file
143
src/WireMock.Net/Validation/Check.cs
Normal file
@@ -0,0 +1,143 @@
|
||||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using JetBrains.Annotations;
|
||||
|
||||
// Copied from https://github.com/aspnet/EntityFramework/blob/dev/src/Shared/Check.cs
|
||||
namespace WireMock.Validation
|
||||
{
|
||||
[ExcludeFromCodeCoverage]
|
||||
[DebuggerStepThrough]
|
||||
internal static class Check
|
||||
{
|
||||
[ContractAnnotation("value:null => halt")]
|
||||
public static T Condition<T>([NoEnumeration] T value, [NotNull] Predicate<T> condition, [InvokerParameterName] [NotNull] string parameterName)
|
||||
{
|
||||
NotNull(condition, nameof(condition));
|
||||
NotNull(value, nameof(value));
|
||||
|
||||
if (!condition(value))
|
||||
{
|
||||
NotEmpty(parameterName, nameof(parameterName));
|
||||
|
||||
throw new ArgumentOutOfRangeException(parameterName);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
[ContractAnnotation("value:null => halt")]
|
||||
public static T NotNull<T>([NoEnumeration] T value, [InvokerParameterName] [NotNull] string parameterName)
|
||||
{
|
||||
if (ReferenceEquals(value, null))
|
||||
{
|
||||
NotEmpty(parameterName, nameof(parameterName));
|
||||
|
||||
throw new ArgumentNullException(parameterName);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
[ContractAnnotation("value:null => halt")]
|
||||
public static T NotNull<T>(
|
||||
[NoEnumeration] T value,
|
||||
[InvokerParameterName] [NotNull] string parameterName,
|
||||
[NotNull] string propertyName)
|
||||
{
|
||||
if (ReferenceEquals(value, null))
|
||||
{
|
||||
NotEmpty(parameterName, nameof(parameterName));
|
||||
NotEmpty(propertyName, nameof(propertyName));
|
||||
|
||||
throw new ArgumentException(CoreStrings.ArgumentPropertyNull(propertyName, parameterName));
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
[ContractAnnotation("value:null => halt")]
|
||||
public static IList<T> NotEmpty<T>(IList<T> value, [InvokerParameterName] [NotNull] string parameterName)
|
||||
{
|
||||
NotNull(value, parameterName);
|
||||
|
||||
if (value.Count == 0)
|
||||
{
|
||||
NotEmpty(parameterName, nameof(parameterName));
|
||||
|
||||
throw new ArgumentException(CoreStrings.CollectionArgumentIsEmpty(parameterName));
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
[ContractAnnotation("value:null => halt")]
|
||||
public static string NotEmpty(string value, [InvokerParameterName] [NotNull] string parameterName)
|
||||
{
|
||||
Exception e = null;
|
||||
if (ReferenceEquals(value, null))
|
||||
{
|
||||
e = new ArgumentNullException(parameterName);
|
||||
}
|
||||
else if (value.Trim().Length == 0)
|
||||
{
|
||||
e = new ArgumentException(CoreStrings.ArgumentIsEmpty(parameterName));
|
||||
}
|
||||
|
||||
if (e != null)
|
||||
{
|
||||
NotEmpty(parameterName, nameof(parameterName));
|
||||
|
||||
throw e;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
public static string NullButNotEmpty(string value, [InvokerParameterName] [NotNull] string parameterName)
|
||||
{
|
||||
if (!ReferenceEquals(value, null)
|
||||
&& (value.Length == 0))
|
||||
{
|
||||
NotEmpty(parameterName, nameof(parameterName));
|
||||
|
||||
throw new ArgumentException(CoreStrings.ArgumentIsEmpty(parameterName));
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
public static IList<T> HasNoNulls<T>(IList<T> value, [InvokerParameterName] [NotNull] string parameterName)
|
||||
where T : class
|
||||
{
|
||||
NotNull(value, parameterName);
|
||||
|
||||
if (value.Any(e => e == null))
|
||||
{
|
||||
NotEmpty(parameterName, nameof(parameterName));
|
||||
|
||||
throw new ArgumentException(parameterName);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
public static Type ValidEntityType(Type value, [InvokerParameterName] [NotNull] string parameterName)
|
||||
{
|
||||
if (!value.GetTypeInfo().IsClass)
|
||||
{
|
||||
NotEmpty(parameterName, nameof(parameterName));
|
||||
|
||||
throw new ArgumentException(CoreStrings.InvalidEntityType(value, parameterName));
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
44
src/WireMock.Net/Validation/CoreStrings.cs
Normal file
44
src/WireMock.Net/Validation/CoreStrings.cs
Normal file
@@ -0,0 +1,44 @@
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Globalization;
|
||||
using JetBrains.Annotations;
|
||||
|
||||
// copied from https://github.com/aspnet/EntityFramework/blob/dev/src/Microsoft.EntityFrameworkCore/Properties/CoreStrings.resx
|
||||
namespace WireMock.Validation
|
||||
{
|
||||
[ExcludeFromCodeCoverage]
|
||||
internal static class CoreStrings
|
||||
{
|
||||
/// <summary>
|
||||
/// The property '{property}' of the argument '{argument}' cannot be null.
|
||||
/// </summary>
|
||||
public static string ArgumentPropertyNull([CanBeNull] string property, [CanBeNull] string argument)
|
||||
{
|
||||
return string.Format(CultureInfo.CurrentCulture, $"The property '{property}' of the argument '{argument}' cannot be null.", property, argument);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The string argument '{argumentName}' cannot be empty.
|
||||
/// </summary>
|
||||
public static string ArgumentIsEmpty([CanBeNull] string argumentName)
|
||||
{
|
||||
return string.Format(CultureInfo.CurrentCulture, $"The string argument '{argumentName}' cannot be empty.", argumentName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The entity type '{type}' provided for the argument '{argumentName}' must be a reference type.
|
||||
/// </summary>
|
||||
public static string InvalidEntityType([CanBeNull] Type type, [CanBeNull] string argumentName)
|
||||
{
|
||||
return string.Format(CultureInfo.CurrentCulture, $"The entity type '{type}' provided for the argument '{argumentName}' must be a reference type.", type, argumentName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The collection argument '{argumentName}' must contain at least one element.
|
||||
/// </summary>
|
||||
public static string CollectionArgumentIsEmpty([CanBeNull] string argumentName)
|
||||
{
|
||||
return string.Format(CultureInfo.CurrentCulture, $"The collection argument '{argumentName}' must contain at least one element.", argumentName);
|
||||
}
|
||||
}
|
||||
}
|
||||
19
src/WireMock.Net/WireMock.Net.xproj
Normal file
19
src/WireMock.Net/WireMock.Net.xproj
Normal file
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="14.0.25420" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0.25420</VisualStudioVersion>
|
||||
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.Props" Condition="'$(VSToolsPath)' != ''" />
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>d3804228-91f4-4502-9595-39584e5a01ad</ProjectGuid>
|
||||
<RootNamespace>WireMock</RootNamespace>
|
||||
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">.\obj</BaseIntermediateOutputPath>
|
||||
<OutputPath Condition="'$(OutputPath)'=='' ">.\bin\</OutputPath>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.targets" Condition="'$(VSToolsPath)' != ''" />
|
||||
</Project>
|
||||
15
src/WireMock.Net/netstandard1.3.txt
Normal file
15
src/WireMock.Net/netstandard1.3.txt
Normal file
@@ -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"
|
||||
}
|
||||
}
|
||||
44
src/WireMock.Net/project.json
Normal file
44
src/WireMock.Net/project.json
Normal file
@@ -0,0 +1,44 @@
|
||||
{
|
||||
"version": "1.0.1.2",
|
||||
"title": "WireMock.Net",
|
||||
"description": "Lightweight Http Mocking Server for .Net, inspired by WireMock from the Java landscape.",
|
||||
"authors": [ "Alexandre Victoor", "Stef Heyenrath" ],
|
||||
|
||||
"packOptions": {
|
||||
"summary": "Lightweight Http Mocking Server for .Net, inspired by WireMock from the Java landscape.",
|
||||
"tags": [ "tdd", "mock", "http", "wiremock", "test", "server", "unittest" ],
|
||||
"owners": [ "Stef Heyenrath" ],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/StefH/WireMock.Net"
|
||||
},
|
||||
"projectUrl": "https://github.com/StefH/WireMock.Net",
|
||||
"iconUrl": "https://raw.githubusercontent.com/StefH/WireMock.Net/master/WireMock.Net-Logo.png",
|
||||
"licenseUrl": "https://raw.githubusercontent.com/StefH/WireMock.Net/master/LICENSE",
|
||||
"releaseNotes": "Added more Admin-Interface commands (save/find/reset/settings) and added support for body encoding."
|
||||
},
|
||||
|
||||
"buildOptions": {
|
||||
"xmlDoc": true
|
||||
},
|
||||
|
||||
"dependencies": {
|
||||
"JetBrains.Annotations": {
|
||||
"version": "10.2.1",
|
||||
"type": "build"
|
||||
},
|
||||
"Handlebars.Net": "1.8.0",
|
||||
"Newtonsoft.Json": "6.0.8",
|
||||
"SimMetrics.Net": "1.0.1",
|
||||
"XPath2": "1.0.3.1"
|
||||
},
|
||||
|
||||
"frameworks": {
|
||||
"net45": {
|
||||
"dependencies": {
|
||||
},
|
||||
"frameworkAssemblies": {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
3
src/_build nuget.cmd
Normal file
3
src/_build nuget.cmd
Normal file
@@ -0,0 +1,3 @@
|
||||
dotnet restore
|
||||
dotnet pack -c Release WireMock.Net\project.json
|
||||
pause
|
||||
291
test/WireMock.Net.Tests/FluentMockServerTests.cs
Normal file
291
test/WireMock.Net.Tests/FluentMockServerTests.cs
Normal file
@@ -0,0 +1,291 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using NFluent;
|
||||
using NUnit.Framework;
|
||||
using WireMock.Matchers;
|
||||
using WireMock.RequestBuilders;
|
||||
using WireMock.ResponseBuilders;
|
||||
using WireMock.Server;
|
||||
|
||||
namespace WireMock.Net.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
[Timeout(5000)]
|
||||
public class FluentMockServerTests
|
||||
{
|
||||
private FluentMockServer _server;
|
||||
|
||||
[Test]
|
||||
public void FluentMockServer_Admin_Mappings_Get()
|
||||
{
|
||||
var guid = Guid.Parse("90356dba-b36c-469a-a17e-669cd84f1f05");
|
||||
_server = FluentMockServer.Start();
|
||||
|
||||
_server.Given(Request.Create().WithPath("/foo1").UsingGet())
|
||||
.WithGuid(guid)
|
||||
.RespondWith(Response.Create().WithStatusCode(201).WithBody("1"));
|
||||
|
||||
_server.Given(Request.Create().WithPath("/foo2").UsingGet())
|
||||
.RespondWith(Response.Create().WithStatusCode(202).WithBody("2"));
|
||||
|
||||
var mappings = _server.Mappings.ToArray();
|
||||
Check.That(mappings).HasSize(2);
|
||||
|
||||
Check.That(mappings.First().RequestMatcher).IsNotNull();
|
||||
Check.That(mappings.First().Provider).IsNotNull();
|
||||
Check.That(mappings.First().Guid).Equals(guid);
|
||||
|
||||
Check.That(mappings[1].Guid).Not.Equals(guid);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void FluentMockServer_Admin_Mappings_Add_SameGuid()
|
||||
{
|
||||
var guid = Guid.Parse("90356dba-b36c-469a-a17e-669cd84f1f05");
|
||||
_server = FluentMockServer.Start();
|
||||
|
||||
_server.Given(Request.Create().WithPath("/1").UsingGet())
|
||||
.WithGuid(guid)
|
||||
.RespondWith(Response.Create().WithStatusCode(500));
|
||||
|
||||
var mappings = _server.Mappings.ToArray();
|
||||
Check.That(mappings).HasSize(1);
|
||||
Check.That(mappings.First().Guid).Equals(guid);
|
||||
|
||||
_server.Given(Request.Create().WithPath("/2").UsingGet())
|
||||
.WithGuid(guid)
|
||||
.RespondWith(Response.Create().WithStatusCode(500));
|
||||
|
||||
Check.That(mappings).HasSize(1);
|
||||
Check.That(mappings.First().Guid).Equals(guid);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task FluentMockServer_Admin_Mappings_AtPriority()
|
||||
{
|
||||
_server = FluentMockServer.Start();
|
||||
|
||||
// given
|
||||
_server.Given(Request.Create().WithPath("/1").UsingGet())
|
||||
.AtPriority(2)
|
||||
.RespondWith(Response.Create().WithStatusCode(200));
|
||||
|
||||
_server.Given(Request.Create().WithPath("/1").UsingGet())
|
||||
.AtPriority(1)
|
||||
.RespondWith(Response.Create().WithStatusCode(400));
|
||||
|
||||
var mappings = _server.Mappings.ToArray();
|
||||
Check.That(mappings).HasSize(2);
|
||||
Check.That(mappings[0].Priority).Equals(2);
|
||||
Check.That(mappings[1].Priority).Equals(1);
|
||||
|
||||
// when
|
||||
var response = await new HttpClient().GetAsync("http://localhost:" + _server.Ports[0] + "/1");
|
||||
|
||||
// then
|
||||
Check.That((int)response.StatusCode).IsEqualTo(400);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task FluentMockServer_Admin_Requests_Get()
|
||||
{
|
||||
// given
|
||||
_server = FluentMockServer.Start();
|
||||
|
||||
// when
|
||||
await new HttpClient().GetAsync("http://localhost:" + _server.Ports[0] + "/foo");
|
||||
|
||||
// then
|
||||
Check.That(_server.LogEntries).HasSize(1);
|
||||
var requestLogged = _server.LogEntries.First();
|
||||
Check.That(requestLogged.RequestMessage.Method).IsEqualTo("get");
|
||||
Check.That(requestLogged.RequestMessage.BodyAsBytes).IsNull();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Should_respond_to_request()
|
||||
{
|
||||
// given
|
||||
_server = FluentMockServer.Start();
|
||||
|
||||
_server
|
||||
.Given(Request.Create()
|
||||
.WithPath("/foo")
|
||||
.UsingGet())
|
||||
.RespondWith(Response.Create()
|
||||
.WithStatusCode(200)
|
||||
.WithBody(@"{ msg: ""Hello world!""}"));
|
||||
|
||||
// when
|
||||
var response = await new HttpClient().GetStringAsync("http://localhost:" + _server.Ports[0] + "/foo");
|
||||
|
||||
// then
|
||||
Check.That(response).IsEqualTo(@"{ msg: ""Hello world!""}");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Should_respond_to_request_bodyAsBase64()
|
||||
{
|
||||
// given
|
||||
_server = FluentMockServer.Start();
|
||||
|
||||
_server.Given(Request.Create().WithPath("/foo").UsingGet()).RespondWith(Response.Create().WithBodyAsBase64("SGVsbG8gV29ybGQ/"));
|
||||
|
||||
// when
|
||||
var response = await new HttpClient().GetStringAsync("http://localhost:" + _server.Ports[0] + "/foo");
|
||||
|
||||
// then
|
||||
Check.That(response).IsEqualTo("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.Ports[0] + "/foo");
|
||||
|
||||
// then
|
||||
Check.That(response.StatusCode).IsEqualTo(HttpStatusCode.NotFound);
|
||||
Check.That((int)response.StatusCode).IsEqualTo(404);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Should_find_a_request_satisfying_a_request_spec()
|
||||
{
|
||||
// given
|
||||
_server = FluentMockServer.Start();
|
||||
|
||||
// when
|
||||
await new HttpClient().GetAsync("http://localhost:" + _server.Ports[0] + "/foo");
|
||||
await new HttpClient().GetAsync("http://localhost:" + _server.Ports[0] + "/bar");
|
||||
|
||||
// then
|
||||
var result = _server.FindLogEntries(Request.Create().WithPath(new RegexMatcher("^/b.*"))).ToList();
|
||||
Check.That(result).HasSize(1);
|
||||
|
||||
var requestLogged = result.First();
|
||||
Check.That(requestLogged.RequestMessage.Path).IsEqualTo("/bar");
|
||||
Check.That(requestLogged.RequestMessage.Url).IsEqualTo("http://localhost:" + _server.Ports[0] + "/bar");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Should_reset_requestlogs()
|
||||
{
|
||||
// given
|
||||
_server = FluentMockServer.Start();
|
||||
|
||||
// when
|
||||
await new HttpClient().GetAsync("http://localhost:" + _server.Ports[0] + "/foo");
|
||||
_server.ResetLogEntries();
|
||||
|
||||
// then
|
||||
Check.That(_server.LogEntries).IsEmpty();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Should_reset_mappings()
|
||||
{
|
||||
// given
|
||||
_server = FluentMockServer.Start();
|
||||
|
||||
_server
|
||||
.Given(Request.Create()
|
||||
.WithPath("/foo")
|
||||
.UsingGet())
|
||||
.RespondWith(Response.Create()
|
||||
.WithBody(@"{ msg: ""Hello world!""}"));
|
||||
|
||||
// when
|
||||
_server.ResetMappings();
|
||||
|
||||
// then
|
||||
Check.That(_server.Mappings).IsEmpty();
|
||||
Check.ThatAsyncCode(() => new HttpClient().GetStringAsync("http://localhost:" + _server.Ports[0] + "/foo"))
|
||||
.ThrowsAny();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Should_respond_a_redirect_without_body()
|
||||
{
|
||||
// given
|
||||
_server = FluentMockServer.Start();
|
||||
|
||||
_server
|
||||
.Given(Request.Create()
|
||||
.WithPath("/foo")
|
||||
.UsingGet())
|
||||
.RespondWith(Response.Create()
|
||||
.WithStatusCode(307)
|
||||
.WithHeader("Location", "/bar"));
|
||||
_server
|
||||
.Given(Request.Create()
|
||||
.WithPath("/bar")
|
||||
.UsingGet())
|
||||
.RespondWith(Response.Create()
|
||||
.WithStatusCode(200)
|
||||
.WithBody("REDIRECT SUCCESSFUL"));
|
||||
|
||||
// when
|
||||
var response = await new HttpClient().GetStringAsync("http://localhost:" + _server.Ports[0] + "/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(Request.Create()
|
||||
.WithPath("/*"))
|
||||
.RespondWith(Response.Create()
|
||||
.WithBody(@"{ msg: ""Hello world!""}")
|
||||
.WithDelay(TimeSpan.FromMilliseconds(200)));
|
||||
|
||||
// when
|
||||
var watch = new Stopwatch();
|
||||
watch.Start();
|
||||
await new HttpClient().GetStringAsync("http://localhost:" + _server.Ports[0] + "/foo");
|
||||
watch.Stop();
|
||||
|
||||
// then
|
||||
Check.That(watch.ElapsedMilliseconds).IsGreaterThan(200);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Should_delay_responses()
|
||||
{
|
||||
// given
|
||||
_server = FluentMockServer.Start();
|
||||
_server.AddGlobalProcessingDelay(TimeSpan.FromMilliseconds(200));
|
||||
_server
|
||||
.Given(Request.Create().WithPath("/*"))
|
||||
.RespondWith(Response.Create().WithBody(@"{ msg: ""Hello world!""}"));
|
||||
|
||||
// when
|
||||
var watch = new Stopwatch();
|
||||
watch.Start();
|
||||
await new HttpClient().GetStringAsync("http://localhost:" + _server.Ports[0] + "/foo");
|
||||
watch.Stop();
|
||||
|
||||
// then
|
||||
Check.That(watch.ElapsedMilliseconds).IsGreaterThan(200);
|
||||
}
|
||||
|
||||
[TearDown]
|
||||
public void ShutdownServer()
|
||||
{
|
||||
_server.Stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
39
test/WireMock.Net.Tests/Http/TinyHttpServerTests.cs
Normal file
39
test/WireMock.Net.Tests/Http/TinyHttpServerTests.cs
Normal file
@@ -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 = PortUtil.FindFreeTcpPort();
|
||||
bool called = false;
|
||||
var urlPrefix = "http://localhost:" + port + "/";
|
||||
var server = new TinyHttpServer(ctx => called = true, urlPrefix);
|
||||
server.Start();
|
||||
|
||||
// when
|
||||
var httpClient = new HttpClient();
|
||||
httpClient.GetAsync(urlPrefix).Wait(3000);
|
||||
|
||||
// then
|
||||
Check.That(called).IsTrue();
|
||||
}
|
||||
}
|
||||
}
|
||||
147
test/WireMock.Net.Tests/HttpListenerRequestMapperTests.cs
Normal file
147
test/WireMock.Net.Tests/HttpListenerRequestMapperTests.cs
Normal file
@@ -0,0 +1,147 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using NFluent;
|
||||
using NUnit.Framework;
|
||||
using WireMock.Http;
|
||||
|
||||
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.LastRequestMessage).IsNotNull();
|
||||
Check.That(MapperServer.LastRequestMessage.Path).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.LastRequestMessage).IsNotNull();
|
||||
Check.That(MapperServer.LastRequestMessage.Method).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.LastRequestMessage).IsNotNull();
|
||||
Check.That(MapperServer.LastRequestMessage.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.LastRequestMessage).IsNotNull();
|
||||
Check.That(MapperServer.LastRequestMessage.Headers).Not.IsNullOrEmpty();
|
||||
Check.That(MapperServer.LastRequestMessage.Headers.Contains(new KeyValuePair<string, string>("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.LastRequestMessage).IsNotNull();
|
||||
Check.That(MapperServer.LastRequestMessage.Path).EndsWith("/index.html");
|
||||
Check.That(MapperServer.LastRequestMessage.GetParameter("id")).HasSize(1);
|
||||
}
|
||||
|
||||
[TearDown]
|
||||
public void StopListenerServer()
|
||||
{
|
||||
_server.Stop();
|
||||
}
|
||||
|
||||
private class MapperServer : TinyHttpServer
|
||||
{
|
||||
private static volatile RequestMessage _lastRequestMessage;
|
||||
|
||||
private MapperServer(Action<HttpListenerContext> httpHandler, string urlPrefix) : base(httpHandler, urlPrefix)
|
||||
{
|
||||
}
|
||||
|
||||
public static RequestMessage LastRequestMessage
|
||||
{
|
||||
get
|
||||
{
|
||||
return _lastRequestMessage;
|
||||
}
|
||||
|
||||
private set
|
||||
{
|
||||
_lastRequestMessage = value;
|
||||
}
|
||||
}
|
||||
|
||||
public static string UrlPrefix { get; private set; }
|
||||
|
||||
public new static MapperServer Start()
|
||||
{
|
||||
int port = PortUtil.FindFreeTcpPort();
|
||||
UrlPrefix = "http://localhost:" + port + "/";
|
||||
var server = new MapperServer(
|
||||
context =>
|
||||
{
|
||||
LastRequestMessage = new HttpListenerRequestMapper().Map(context.Request);
|
||||
context.Response.Close();
|
||||
}, UrlPrefix);
|
||||
((TinyHttpServer)server).Start();
|
||||
|
||||
return server;
|
||||
}
|
||||
|
||||
public new void Stop()
|
||||
{
|
||||
base.Stop();
|
||||
LastRequestMessage = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
133
test/WireMock.Net.Tests/HttpListenerResponseMapperTests.cs
Normal file
133
test/WireMock.Net.Tests/HttpListenerResponseMapperTests.cs
Normal file
@@ -0,0 +1,133 @@
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using NFluent;
|
||||
using NUnit.Framework;
|
||||
using WireMock.Http;
|
||||
|
||||
namespace WireMock.Net.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
public class HttpListenerResponseMapperTests
|
||||
{
|
||||
private TinyHttpServer _server;
|
||||
private Task<HttpResponseMessage> _responseMsgTask;
|
||||
|
||||
[Test]
|
||||
public void Should_map_status_code_from_original_response()
|
||||
{
|
||||
// given
|
||||
var response = new ResponseMessage { 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 ResponseMessage();
|
||||
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 ResponseMessage
|
||||
{
|
||||
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 !!!");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Should_map_encoded_body_from_original_response()
|
||||
{
|
||||
// given
|
||||
var response = new ResponseMessage
|
||||
{
|
||||
Body = "Hello !!!",
|
||||
BodyEncoding = Encoding.ASCII
|
||||
};
|
||||
|
||||
var httpListenerResponse = CreateHttpListenerResponse();
|
||||
|
||||
// when
|
||||
new HttpListenerResponseMapper().Map(response, httpListenerResponse);
|
||||
|
||||
// then
|
||||
Check.That(httpListenerResponse.ContentEncoding).Equals(Encoding.ASCII);
|
||||
|
||||
var responseMessage = ToResponseMessage(httpListenerResponse);
|
||||
Check.That(responseMessage).IsNotNull();
|
||||
|
||||
var contentTask = responseMessage.Content.ReadAsStringAsync();
|
||||
Check.That(contentTask.Result).IsEqualTo("Hello !!!");
|
||||
}
|
||||
|
||||
[TearDown]
|
||||
public void StopServer()
|
||||
{
|
||||
_server?.Stop();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dirty HACK to get HttpListenerResponse instances
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// The <see cref="HttpListenerResponse"/>.
|
||||
/// </returns>
|
||||
public HttpListenerResponse CreateHttpListenerResponse()
|
||||
{
|
||||
var port = PortUtil.FindFreeTcpPort();
|
||||
var urlPrefix = "http://localhost:" + port + "/";
|
||||
var responseReady = new AutoResetEvent(false);
|
||||
HttpListenerResponse response = null;
|
||||
_server = new TinyHttpServer(
|
||||
context =>
|
||||
{
|
||||
response = context.Response;
|
||||
responseReady.Set();
|
||||
}, urlPrefix);
|
||||
_server.Start();
|
||||
_responseMsgTask = new HttpClient().GetAsync(urlPrefix);
|
||||
responseReady.WaitOne();
|
||||
return response;
|
||||
}
|
||||
|
||||
public HttpResponseMessage ToResponseMessage(HttpListenerResponse listenerResponse)
|
||||
{
|
||||
listenerResponse.Close();
|
||||
_responseMsgTask.Wait();
|
||||
return _responseMsgTask.Result;
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user