Compare commits

..

40 Commits

Author SHA1 Message Date
Stef Heyenrath
6784614814 1.0.1.5 2017-03-21 14:49:45 +01:00
Stef Heyenrath
e9ee4e91f9 HttpListener : Stop fix 2017-03-21 14:45:34 +01:00
Stef Heyenrath
9fcfb3109f fix MappingsSave 2017-03-21 10:52:32 +01:00
Stef Heyenrath
d0cdfe5dc3 add coveralls.io badge 2017-03-21 09:10:25 +01:00
Stef Heyenrath
f06b89fa06 FluentMockServer_ReadStaticMappings 2017-03-21 09:05:57 +01:00
Stef Heyenrath
e9e4ee7e9d fix AppVeyor tests 2017-03-21 08:59:49 +01:00
Stef Heyenrath
d62c81acd8 test 2017-03-21 08:14:56 +01:00
Stef Heyenrath
3bdc9c375b added sdk to global.json 2017-03-21 08:06:47 +01:00
Stef Heyenrath
780e14f214 DotNetCore.1.0.1-SDK.1.0.0.Preview2-003133-x64.exe 2017-03-20 23:12:51 +01:00
Stef Heyenrath
f50e87c9ba dotnet-1.1.1-sdk-win-x64.exe 2017-03-20 21:01:14 +01:00
Stef Heyenrath
5145ecb396 dotnet test 2017-03-20 20:55:24 +01:00
Stef Heyenrath
d731aefc82 test test 2017-03-20 18:55:37 +01:00
Stef Heyenrath
5ac375ef26 opencover dotnet 2017-03-20 18:25:44 +01:00
Stef Heyenrath
ffd5322587 OpenCover xunit 2017-03-20 18:21:04 +01:00
Stef Heyenrath
81211d1ce3 opencover xunit 2017-03-20 18:17:21 +01:00
Stef Heyenrath
59dd5fcc1e 1.0.1.3 2017-03-20 18:03:54 +01:00
Stef Heyenrath
65aaa9e87a AppVeyor : Debug 2017-03-20 18:02:30 +01:00
Stef Heyenrath
e1c1db6480 update tests (#21) 2017-03-20 17:56:34 +01:00
Stef Heyenrath
bd8e18b2c4 FluentMockServerSettings (#21) 2017-03-20 17:22:09 +01:00
Stef Heyenrath
7793330d1d add ReadStaticMappings & ReadStaticMapping 2017-03-17 16:57:00 +01:00
Stef Heyenrath
37de97ed5d Added title (linked to #21) 2017-03-15 12:36:08 +01:00
Stef Heyenrath
b09b882ad1 Added support for raw Output Headers 2017-03-06 14:29:01 +01:00
Stef Heyenrath
c8920c6356 COVERALLS_REPO_TOKEN 2017-02-27 12:55:09 +01:00
Stef Heyenrath
9d9e61d7ae COVERALLS_REPO_TOKEN 2017-02-27 12:42:31 +01:00
Stef Heyenrath
a960553d7d fix opencover - test folder 2017-02-27 12:33:33 +01:00
Stef Heyenrath
de3884097f global.json 2017-02-27 12:28:07 +01:00
Stef Heyenrath
a1a98819df change build-order 2017-02-27 12:23:04 +01:00
Stef Heyenrath
4fa295edb7 change tests and add OpenCover 2017-02-27 12:15:20 +01:00
Stef Heyenrath
ce39c7bad2 test_script 2017-02-27 11:24:32 +01:00
Stef Heyenrath
02803562c6 1.0.1.2 2017-02-27 11:16:34 +01:00
Stef Heyenrath
7c51b2e73c Merge pull request #24 from sbebrys/Body_Encoding
Body Encoding
2017-02-25 09:57:32 +01:00
Sebastian Bebrys
6513ac9de8 Body Encoding - admin interface 2017-02-23 14:45:43 +01:00
Sebastian Bebrys
c38373eb1f Body Encoding 2017-02-23 10:39:44 +01:00
Stef Heyenrath
3112054f59 /__admin/requests/find 2017-02-15 18:04:18 +01:00
Stef Heyenrath
571f434b9a Score 2017-02-14 21:38:44 +01:00
Stef Heyenrath
b25444d083 __admin/settings 2017-02-13 19:34:04 +01:00
Stef Heyenrath
2944b5392a update tests 2017-02-13 18:18:26 +01:00
Stef Heyenrath
7fa0fbf7da /reset 2017-02-12 10:10:46 +01:00
Stef Heyenrath
b65cf9b61b \__admin\mappings\save (issue #21) 2017-02-12 09:48:51 +01:00
Stef Heyenrath
bb35f55bbb add BASIC Auth (#22) 2017-02-10 20:04:06 +01:00
60 changed files with 2469 additions and 245 deletions

View File

@@ -2,7 +2,8 @@
A C# .NET version based on [mock4net](https://github.com/alexvictoor/mock4net) which mimics the functionality from the JAVA based http://WireMock.org
[![Build status](https://ci.appveyor.com/api/projects/status/b3n6q3ygbww4lyls?svg=true)](https://ci.appveyor.com/project/StefH/wiremock-net)
[![codecov](https://codecov.io/gh/StefH/WireMock.Net/branch/master/graph/badge.svg)](https://codecov.io/gh/StefH/WireMock.Net)
[![Coverage Status](https://coveralls.io/repos/github/StefH/WireMock.Net/badge.svg?branch=master)](https://coveralls.io/github/StefH/WireMock.Net?branch=master)
[![NuGet Badge](https://buildstats.info/nuget/WireMock.Net)](https://www.nuget.org/packages/WireMock.Net)
## Stubbing

View File

@@ -9,6 +9,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
ProjectSection(SolutionItems) = preProject
.runsettings = .runsettings
appveyor.yml = appveyor.yml
global.json = global.json
README.md = README.md
EndProjectSection
EndProject
@@ -23,16 +24,13 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WireMock.Net.ConsoleApplica
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
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "WireMock.Net.Tests", "test\WireMock.Net.Tests\WireMock.Net.Tests.xproj", "{31DC2EF8-C3FE-467D-84BE-FB5D956E612E}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -47,14 +45,14 @@ Global
{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
{31DC2EF8-C3FE-467D-84BE-FB5D956E612E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{31DC2EF8-C3FE-467D-84BE-FB5D956E612E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{31DC2EF8-C3FE-467D-84BE-FB5D956E612E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{31DC2EF8-C3FE-467D-84BE-FB5D956E612E}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -62,7 +60,7 @@ Global
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}
{31DC2EF8-C3FE-467D-84BE-FB5D956E612E} = {890A1DED-C229-4FA1-969E-AAC3BBFC05E5}
EndGlobalSection
EndGlobal

View File

@@ -1,10 +1,9 @@
os: Visual Studio 2015
version: 1.0.0.{build}
version: 1.0.1.{build}
configuration:
- Debug
- Release
platform: Any CPU
@@ -17,20 +16,44 @@ install:
environment:
PATH: $(PATH);$(PROGRAMFILES)\dotnet\
COVERALLS_REPO_TOKEN:
secure: Eq/3VV5DVAeQAlQhe6hvy21IYPo5uY4fWKxvC4pxdq3giJzcwFp1QxBvRpXJ8Wkw
before_build:
- appveyor-retry dotnet restore .\src\WireMock.Net -v Minimal
- 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
# build WireMock.Net
- dotnet build .\src\WireMock.Net\project.json -c %CONFIGURATION%
# restore and build WireMock.Net.Tests
- appveyor-retry dotnet restore .\test\WireMock.Net.Tests -v Minimal
- dotnet build .\test\WireMock.Net.Tests\project.json -c %CONFIGURATION%
# build WireMock.Net.ConsoleApplication
- 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
#test_script:
# test WireMock.Net.Tests
# - cd .\test\WireMock.Net.Tests
# - dotnet test -c %CONFIGURATION% --no-build
# - cd ..
# - cd ..
test_script:
- nuget.exe install OpenCover -ExcludeVersion
- nuget.exe install coveralls.net -ExcludeVersion
# - OpenCover\tools\OpenCover.Console.exe -register:user -target:nunit3-console.exe -targetargs:"\".\test\WireMock.Net.Tests\bin\%CONFIGURATION%\net452\win7-x64\WireMock.Net.Tests.dll\" --result=myresults.xml;format=AppVeyor" -returntargetcode -filter:"+[WireMock.Net]*" -excludebyattribute:*.ExcludeFromCodeCoverage* -hideskipped:All -output:coverage.xml
# - OpenCover\tools\OpenCover.Console.exe -register:user -target:xunit.console.clr4.exe "-targetargs:"\".\test\WireMock.Net.Tests\bin\%CONFIGURATION%\net452\win7-x64\WireMock.Net.Tests.dll\" --result=myresults.xml;format=AppVeyor" -filter:"+[WireMock.Net]*" -output:coverage.xml
- OpenCover\tools\OpenCover.Console.exe -register:user -target:"dotnet.exe" -searchdirs:".\test\WireMock.Net.Tests\bin\%CONFIGURATION%\net452\win7-x64" -oldstyle -targetargs:"test .\test\WireMock.Net.Tests" -returntargetcode -filter:"+[WireMock.Net]*" -output:coverage.xml
- "SET PATH=C:\\Python34;C:\\Python34\\Scripts;%PATH%"
- pip install codecov
- codecov -f "coverage.xml"
- coveralls.net\tools\csmacnz.Coveralls.exe --opencover -i .\coverage.xml
artifacts:
- path: artifacts\**\*.*

View File

@@ -16,9 +16,16 @@ namespace WireMock.Net.ConsoleApplication
string url2 = "http://localhost:9091/";
string url3 = "https://localhost:9443/";
var server = FluentMockServer.StartWithAdminInterface(url1, url2, url3);
var server = FluentMockServer.Start(new FluentMockServerSettings
{
Urls = new [] { url1, url2, url3 },
StartAdminInterface = true,
ReadStaticMappings = true
});
Console.WriteLine("FluentMockServer listening at {0}", string.Join(" and ", server.Urls));
server.SetBasicAuthentication("a", "b");
server.AllowPartialMapping();
server
@@ -77,7 +84,7 @@ namespace WireMock.Net.ConsoleApplication
// http://localhost:8080/any/any?start=1000&stop=1&stop=2
server
.Given(Request.Create().WithPath("/*").UsingGet())
.WithGuid(Guid.Parse("90356dba-b36c-469a-a17e-669cd84f1f05"))
.WithGuid("90356dba-b36c-469a-a17e-669cd84f1f05")
.AtPriority(server.Mappings.Count() + 1)
.RespondWith(Response.Create()
.WithStatusCode(200)
@@ -85,7 +92,6 @@ namespace WireMock.Net.ConsoleApplication
.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(1000)
.WithDelay(TimeSpan.FromMilliseconds(100))
);

6
global.json Normal file
View File

@@ -0,0 +1,6 @@
{
"projects": [ "src", "test" ],
"sdk": {
"version": "1.0.0-preview2-003133"
}
}

View File

@@ -16,6 +16,12 @@ namespace WireMock.Net.StandAlone
[SwitchArgument('p', "AllowPartialMapping", true, Description = "Allow Partial Mapping (default set to true).", Optional = true)]
public bool AllowPartialMapping { get; set; }
[SwitchArgument('s', "StartAdminInterface", true, Description = "Start the AdminInterface (default set to true).", Optional = true)]
public bool StartAdminInterface { get; set; }
[SwitchArgument('r', "ReadStaticMappings", true, Description = "Read StaticMappings from ./__admin/mappings (default set to true).", Optional = true)]
public bool ReadStaticMappings { get; set; }
}
static void Main(params string[] args)
@@ -31,7 +37,12 @@ namespace WireMock.Net.StandAlone
if (!options.Urls.Any())
options.Urls.Add("http://localhost:9090/");
var server = FluentMockServer.StartWithAdminInterface(options.Urls.ToArray());
var server = FluentMockServer.Start(new FluentMockServerSettings
{
Urls = options.Urls.ToArray(),
StartAdminInterface = options.StartAdminInterface,
ReadStaticMappings = options.ReadStaticMappings
});
if (options.AllowPartialMapping)
server.AllowPartialMapping();

View 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; }
}
}

View File

@@ -15,6 +15,14 @@ namespace WireMock.Admin.Mappings
/// </value>
public Guid? Guid { get; set; }
/// <summary>
/// Gets or sets the unique title.
/// </summary>
/// <value>
/// The unique title.
/// </value>
public string Title { get; set; }
/// <summary>
/// Gets or sets the priority.
/// </summary>

View File

@@ -39,6 +39,14 @@ namespace WireMock.Admin.Mappings
/// </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>
@@ -55,6 +63,14 @@ namespace WireMock.Admin.Mappings
/// </value>
public IDictionary<string, string> Headers { get; set; }
/// <summary>
/// Gets or sets the Headers (Raw).
/// </summary>
/// <value>
/// The Headers (Raw).
/// </value>
public string HeadersRaw { get; set; }
/// <summary>
/// Gets or sets the delay in milliseconds.
/// </summary>

View File

@@ -39,6 +39,14 @@ namespace WireMock.Admin.Requests
/// </value>
public Guid? MappingGuid { get; set; }
/// <summary>
/// Gets or sets the mapping unique title.
/// </summary>
/// <value>
/// The mapping unique title.
/// </value>
public string MappingTitle { get; set; }
/// <summary>
/// Gets or sets the request match result.
/// </summary>

View File

@@ -6,12 +6,12 @@
public class LogRequestMatchModel
{
/// <summary>
/// Gets or sets the number of matches.
/// Gets or sets the match-score.
/// </summary>
/// <value>
/// The number of matches.
/// The match-score.
/// </value>
public double MatchScore { get; set; }
public double TotalScore { get; set; }
/// <summary>
/// Gets or sets the total number of matches.
@@ -19,7 +19,7 @@
/// <value>
/// The total number of matches.
/// </value>
public int Total { get; set; }
public int TotalNumber { get; set; }
/// <summary>
/// Gets or sets a value indicating whether this instance is perfect match.
@@ -27,7 +27,7 @@
/// <value>
/// <c>true</c> if this instance is perfect match; otherwise, <c>false</c>.
/// </value>
public bool IsPerfectMatch => MatchScore == Total;
public bool IsPerfectMatch { get; set; }
/// <summary>
/// Gets the match percentage.
@@ -35,6 +35,6 @@
/// <value>
/// The match percentage.
/// </value>
public double MatchPercentage => Total == 0 ? 100 : 100.0 * MatchScore / Total;
public double AverageTotalScore { get; set; }
}
}

View File

@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using WireMock.Admin.Mappings;
using WireMock.Util;
namespace WireMock.Admin.Requests
@@ -66,5 +67,13 @@ namespace WireMock.Admin.Requests
/// 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; }
}
}

View File

@@ -1,4 +1,5 @@
using System.Collections.Generic;
using WireMock.Admin.Mappings;
namespace WireMock.Admin.Requests
{
@@ -26,5 +27,10 @@ namespace WireMock.Admin.Requests
/// Gets or sets the original body.
/// </summary>
public string BodyOriginal { get; set; }
/// <summary>
/// Gets or sets the body.
/// </summary>
public EncodingModel BodyEncoding { get; set; }
}
}

View 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; }
}
}

View File

@@ -13,11 +13,11 @@ namespace WireMock.Http
/// </summary>
public class TinyHttpServer
{
private readonly Action<HttpListenerContext> _httpHandler;
private readonly Action<HttpListenerContext, CancellationToken> _httpHandler;
private readonly HttpListener _listener;
private CancellationTokenSource _cts;
private readonly CancellationTokenSource _cts;
/// <summary>
/// Gets a value indicating whether this server is started.
@@ -33,6 +33,7 @@ namespace WireMock.Http
/// <value>
/// The urls.
/// </value>
[PublicAPI]
public List<Uri> Urls { get; } = new List<Uri>();
/// <summary>
@@ -41,6 +42,7 @@ namespace WireMock.Http
/// <value>
/// The ports.
/// </value>
[PublicAPI]
public List<int> Ports { get; } = new List<int>();
/// <summary>
@@ -48,11 +50,13 @@ namespace WireMock.Http
/// </summary>
/// <param name="uriPrefixes">The uriPrefixes.</param>
/// <param name="httpHandler">The http handler.</param>
public TinyHttpServer([NotNull] Action<HttpListenerContext> httpHandler, [NotNull] params string[] uriPrefixes)
public TinyHttpServer([NotNull] Action<HttpListenerContext, CancellationToken> httpHandler, [NotNull] params string[] uriPrefixes)
{
Check.NotNull(httpHandler, nameof(httpHandler));
Check.NotEmpty(uriPrefixes, nameof(uriPrefixes));
_cts = new CancellationTokenSource();
_httpHandler = httpHandler;
// Create a listener.
@@ -70,22 +74,26 @@ namespace WireMock.Http
/// <summary>
/// Start the server.
/// </summary>
[PublicAPI]
public void Start()
{
_listener.Start();
IsStarted = true;
_cts = new CancellationTokenSource();
Task.Run(
async () =>
{
using (_listener)
//using (_listener)
{
while (!_cts.Token.IsCancellationRequested)
{
HttpListenerContext context = await _listener.GetContextAsync();
_httpHandler(context);
_httpHandler(context, _cts.Token);
}
_listener.Stop();
IsStarted = false;
}
},
_cts.Token);
@@ -94,8 +102,11 @@ namespace WireMock.Http
/// <summary>
/// Stop the server.
/// </summary>
[PublicAPI]
public void Stop()
{
_listener?.Stop();
_cts.Cancel();
}
}

View File

@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
namespace WireMock
{
@@ -21,16 +22,16 @@ namespace WireMock
Uri url = listenerRequest.Url;
string verb = listenerRequest.HttpMethod;
byte[] body = GetRequestBody(listenerRequest);
string bodyAsString = body != null ? listenerRequest.ContentEncoding.GetString(body) : null;
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);
var message = new RequestMessage(url, verb, body, bodyAsString, headers, cookies) { DateTime = DateTime.Now };
return message;
return new RequestMessage(url, verb, body, bodyAsString, bodyEncoding, headers, cookies) { DateTime = DateTime.Now };
}
/// <summary>

View File

@@ -24,14 +24,16 @@ namespace WireMock
responseMessage.Headers.ToList().ForEach(pair => listenerResponse.AddHeader(pair.Key, pair.Value));
if (responseMessage.Body != null)
{
byte[] buffer = _utf8NoBom.GetBytes(responseMessage.Body);
listenerResponse.ContentEncoding = _utf8NoBom;
listenerResponse.ContentLength64 = buffer.Length;
listenerResponse.OutputStream.Write(buffer, 0, buffer.Length);
listenerResponse.OutputStream.Flush();
}
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();
}
}
}

View File

@@ -47,5 +47,13 @@ namespace WireMock.Logging
/// The mapping unique identifier.
/// </value>
public Guid? MappingGuid { get; set; }
/// <summary>
/// Gets or sets the mapping unique title.
/// </summary>
/// <value>
/// The mapping unique title.
/// </value>
public string MappingTitle { get; set; }
}
}

View File

@@ -1,5 +1,6 @@
using System;
using System.Threading.Tasks;
using JetBrains.Annotations;
using WireMock.Matchers.Request;
namespace WireMock
@@ -9,14 +10,6 @@ namespace WireMock
/// </summary>
public class Mapping
{
/// <summary>
/// Gets the priority.
/// </summary>
/// <value>
/// The priority.
/// </value>
public int Priority { get; }
/// <summary>
/// Gets the unique identifier.
/// </summary>
@@ -25,6 +18,22 @@ namespace WireMock
/// </value>
public Guid Guid { get; }
/// <summary>
/// Gets the unique title.
/// </summary>
/// <value>
/// The unique title.
/// </value>
public string Title { get; }
/// <summary>
/// Gets the priority.
/// </summary>
/// <value>
/// The priority.
/// </value>
public int Priority { get; }
/// <summary>
/// The Request matcher.
/// </summary>
@@ -38,14 +47,16 @@ namespace WireMock
/// <summary>
/// Initializes a new instance of the <see cref="Mapping"/> class.
/// </summary>
/// <param name="guid">The the unique identifier.</param>
/// <param name="guid">The unique identifier.</param>
/// <param name="title">The unique title (can be null_.</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)
public Mapping(Guid guid, [CanBeNull] string title, IRequestMatcher requestMatcher, IResponseProvider provider, int priority)
{
Priority = priority;
Guid = guid;
Title = title;
RequestMatcher = requestMatcher;
Provider = provider;
}
@@ -73,5 +84,13 @@ namespace WireMock
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;
}
}

View File

@@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
using System.Collections.Generic;
using System.Linq;
namespace WireMock.Matchers

View File

@@ -8,12 +8,12 @@ namespace WireMock.Matchers.Request
public class RequestMatchResult : IComparable
{
/// <summary>
/// Gets or sets the matches score.
/// Gets or sets the match-score.
/// </summary>
/// <value>
/// The number of matches.
/// The match-score.
/// </value>
public double MatchScore { get; set; }
public double TotalScore { get; set; }
/// <summary>
/// Gets or sets the total number of matches.
@@ -21,7 +21,7 @@ namespace WireMock.Matchers.Request
/// <value>
/// The total number of matches.
/// </value>
public int Total { get; set; }
public int TotalNumber { get; set; }
/// <summary>
/// Gets or sets a value indicating whether this instance is perfect match.
@@ -29,7 +29,7 @@ namespace WireMock.Matchers.Request
/// <value>
/// <c>true</c> if this instance is perfect match; otherwise, <c>false</c>.
/// </value>
public bool IsPerfectMatch => Math.Abs(MatchScore - Total) < MatchScores.Tolerance;
public bool IsPerfectMatch => Math.Abs(TotalScore - TotalNumber) < MatchScores.Tolerance;
/// <summary>
/// Gets the match percentage.
@@ -37,7 +37,7 @@ namespace WireMock.Matchers.Request
/// <value>
/// The match percentage.
/// </value>
public double MatchPercentage => Total == 0 ? 1.0 : MatchScore / Total;
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.
@@ -51,7 +51,7 @@ namespace WireMock.Matchers.Request
{
var compareObj = (RequestMatchResult)obj;
return compareObj.MatchPercentage.CompareTo(MatchPercentage);
return compareObj.AverageTotalScore.CompareTo(AverageTotalScore);
}
}
}

View File

@@ -98,9 +98,9 @@ namespace WireMock.Matchers.Request
public double GetMatchingScore(RequestMessage requestMessage, RequestMatchResult requestMatchResult)
{
double score = IsMatch(requestMessage);
requestMatchResult.MatchScore += score;
requestMatchResult.TotalScore += score;
requestMatchResult.Total++;
requestMatchResult.TotalNumber++;
return score;
}

View File

@@ -77,9 +77,9 @@ namespace WireMock.Matchers.Request
public double GetMatchingScore(RequestMessage requestMessage, RequestMatchResult requestMatchResult)
{
double score = IsMatch(requestMessage);
requestMatchResult.MatchScore += score;
requestMatchResult.TotalScore += score;
requestMatchResult.Total++;
requestMatchResult.TotalNumber++;
return score;
}

View File

@@ -77,9 +77,9 @@ namespace WireMock.Matchers.Request
public double GetMatchingScore(RequestMessage requestMessage, RequestMatchResult requestMatchResult)
{
double score = IsMatch(requestMessage);
requestMatchResult.MatchScore += score;
requestMatchResult.TotalScore += score;
requestMatchResult.Total++;
requestMatchResult.TotalNumber++;
return score;
}

View File

@@ -37,9 +37,9 @@ namespace WireMock.Matchers.Request
public double GetMatchingScore(RequestMessage requestMessage, RequestMatchResult requestMatchResult)
{
double score = IsMatch(requestMessage);
requestMatchResult.MatchScore += score;
requestMatchResult.TotalScore += score;
requestMatchResult.Total++;
requestMatchResult.TotalNumber++;
return score;
}

View File

@@ -65,9 +65,9 @@ namespace WireMock.Matchers.Request
public double GetMatchingScore(RequestMessage requestMessage, RequestMatchResult requestMatchResult)
{
double score = IsMatch(requestMessage);
requestMatchResult.MatchScore += score;
requestMatchResult.TotalScore += score;
requestMatchResult.Total++;
requestMatchResult.TotalNumber++;
return score;
}

View File

@@ -60,9 +60,9 @@ namespace WireMock.Matchers.Request
public double GetMatchingScore(RequestMessage requestMessage, RequestMatchResult requestMatchResult)
{
double score = IsMatch(requestMessage);
requestMatchResult.MatchScore += score;
requestMatchResult.TotalScore += score;
requestMatchResult.Total++;
requestMatchResult.TotalNumber++;
return score;
}

View File

@@ -60,9 +60,9 @@ namespace WireMock.Matchers.Request
public double GetMatchingScore(RequestMessage requestMessage, RequestMatchResult requestMatchResult)
{
double score = IsMatch(requestMessage);
requestMatchResult.MatchScore += score;
requestMatchResult.TotalScore += score;
requestMatchResult.Total++;
requestMatchResult.TotalNumber++;
return score;
}

View File

@@ -4,6 +4,7 @@ using System.Linq;
using JetBrains.Annotations;
using WireMock.Util;
using WireMock.Validation;
using System.Text;
namespace WireMock
{
@@ -57,6 +58,11 @@ namespace WireMock
/// </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>
@@ -64,9 +70,10 @@ namespace WireMock
/// <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] IDictionary<string, string> headers = null, [CanBeNull] IDictionary<string, string> cookies = null)
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));
@@ -76,6 +83,7 @@ namespace WireMock
Method = verb.ToLower();
BodyAsBytes = bodyAsBytes;
Body = body;
BodyEncoding = bodyEncoding;
Headers = headers;
Cookies = cookies;

View File

@@ -12,15 +12,17 @@ namespace WireMock.ResponseBuilders
/// 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);
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);
IResponseBuilder WithBodyAsJson([NotNull] object body, [CanBeNull] Encoding encoding = null);
/// <summary>
/// The with body as base64.

View File

@@ -143,12 +143,15 @@ namespace WireMock.ResponseBuilders
/// 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)
public IResponseBuilder WithBody(string body, Encoding encoding = null)
{
Check.NotNull(body, nameof(body));
ResponseMessage.Body = body;
ResponseMessage.BodyEncoding = encoding ?? Encoding.UTF8;
return this;
}
@@ -156,12 +159,22 @@ namespace WireMock.ResponseBuilders
/// 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)
public IResponseBuilder WithBodyAsJson(object body, Encoding encoding = null)
{
Check.NotNull(body, nameof(body));
ResponseMessage.Body = JsonConvert.SerializeObject(body, new JsonSerializerSettings { Formatting = Formatting.None, NullValueHandling = NullValueHandling.Ignore });
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;
}
@@ -175,7 +188,11 @@ namespace WireMock.ResponseBuilders
{
Check.NotNull(bodyAsbase64, nameof(bodyAsbase64));
ResponseMessage.Body = (encoding ?? Encoding.UTF8).GetString(Convert.FromBase64String(bodyAsbase64));
encoding = encoding ?? Encoding.UTF8;
ResponseMessage.Body = encoding.GetString(Convert.FromBase64String(bodyAsbase64));
ResponseMessage.BodyEncoding = encoding;
return this;
}

View File

@@ -1,5 +1,7 @@
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Text;
namespace WireMock
{
/// <summary>
@@ -27,6 +29,11 @@ namespace WireMock
/// </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>

View File

@@ -2,11 +2,13 @@ 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;
@@ -22,57 +24,130 @@ namespace WireMock.Server
/// </summary>
public partial class FluentMockServer
{
private const string AdminMappingsFolder = @"\__admin\mappings";
private static readonly string AdminMappingsFolder = Path.Combine("__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.None,
NullValueHandling = NullValueHandling.Ignore
Formatting = Formatting.Indented,
NullValueHandling = NullValueHandling.Ignore,
};
private void ReadStaticMappings()
/// <summary>
/// Reads the static mappings from a folder.
/// </summary>
/// <param name="folder">The optional folder. If not defined, use \__admin\mappings\</param>
[PublicAPI]
public void ReadStaticMappings([CanBeNull] string folder = null)
{
if (!Directory.Exists(Directory.GetCurrentDirectory() + AdminMappingsFolder))
if (folder == null)
folder = Path.Combine(Directory.GetCurrentDirectory(), AdminMappingsFolder);
if (!Directory.Exists(folder))
return;
foreach (string filename in Directory.EnumerateFiles(Directory.GetCurrentDirectory() + AdminMappingsFolder))
foreach (string filename in Directory.EnumerateFiles(folder).OrderBy(f => f))
{
var json = File.OpenText(filename).ReadToEnd();
DeserializeAndAddMapping(json, Guid.Parse(Path.GetFileNameWithoutExtension(filename)));
ReadStaticMapping(filename);
}
}
/// <summary>
/// Reads the static mapping.
/// </summary>
/// <param name="filename">The filename.</param>
[PublicAPI]
public void ReadStaticMapping([NotNull] string filename)
{
Check.NotNull(filename, nameof(filename));
string filenameWithoutExtension = Path.GetFileNameWithoutExtension(filename);
Guid guidFromFilename;
if (Guid.TryParse(filenameWithoutExtension, out guidFromFilename))
{
DeserializeAndAddMapping(File.ReadAllText(filename), guidFromFilename);
}
else
{
DeserializeAndAddMapping(File.ReadAllText(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.Provider is DynamicResponseProvider) && m.Guid == guid);
var mapping = Mappings.FirstOrDefault(m => !m.IsAdminInterface && m.Guid == guid);
if (mapping == null)
return new ResponseMessage { StatusCode = 404, Body = "Mapping not found" };
@@ -93,12 +168,15 @@ namespace WireMock.Server
if (mappingModel.Response == null)
return new ResponseMessage { StatusCode = 400, Body = "Response missing" };
var requestBuilder = InitRequestBuilder(mappingModel);
var responseBuilder = InitResponseBuilder(mappingModel);
var requestBuilder = InitRequestBuilder(mappingModel.Request);
var responseBuilder = InitResponseBuilder(mappingModel.Response);
Given(requestBuilder)
.WithGuid(guid)
.RespondWith(responseBuilder);
IRespondWithAProvider respondProvider = Given(requestBuilder).WithGuid(guid);
if (!string.IsNullOrEmpty(mappingModel.Title))
respondProvider = respondProvider.WithTitle(mappingModel.Title);
respondProvider.RespondWith(responseBuilder);
return new ResponseMessage { Body = "Mapping added or updated" };
}
@@ -115,10 +193,33 @@ namespace WireMock.Server
#endregion Mapping/{guid}
#region Mappings
private ResponseMessage MappingsSave(RequestMessage requestMessage)
{
string folder = Path.Combine(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);
string filename = !string.IsNullOrEmpty(mapping.Title) ? SanitizeFileName(mapping.Title) : mapping.Guid.ToString();
File.WriteAllText(Path.Combine(folder, filename + ".json"), json);
}
return new ResponseMessage { Body = "Mappings saved to disk" };
}
private static string SanitizeFileName(string name, char replaceChar = '_')
{
return Path.GetInvalidFileNameChars().Aggregate(name, (current, c) => current.Replace(c, replaceChar));
}
private ResponseMessage MappingsGet(RequestMessage requestMessage)
{
var result = new List<MappingModel>();
foreach (var mapping in Mappings.Where(m => !(m.Provider is DynamicResponseProvider)))
foreach (var mapping in Mappings.Where(m => !m.IsAdminInterface))
{
var model = ToMappingModel(mapping);
result.Add(model);
@@ -142,7 +243,7 @@ namespace WireMock.Server
return new ResponseMessage { StatusCode = 500, Body = e.ToString() };
}
return new ResponseMessage { Body = "Mapping added" };
return new ResponseMessage { StatusCode = 201, Body = "Mapping added" };
}
private void DeserializeAndAddMapping(string json, Guid? guid = null)
@@ -153,8 +254,8 @@ namespace WireMock.Server
Check.NotNull(mappingModel.Request, nameof(mappingModel.Request));
Check.NotNull(mappingModel.Response, nameof(mappingModel.Response));
var requestBuilder = InitRequestBuilder(mappingModel);
var responseBuilder = InitResponseBuilder(mappingModel);
var requestBuilder = InitRequestBuilder(mappingModel.Request);
var responseBuilder = InitResponseBuilder(mappingModel.Response);
IRespondWithAProvider respondProvider = Given(requestBuilder);
@@ -167,6 +268,9 @@ namespace WireMock.Server
respondProvider = respondProvider.WithGuid(mappingModel.Guid.Value);
}
if (!string.IsNullOrEmpty(mappingModel.Title))
respondProvider = respondProvider.WithTitle(mappingModel.Title);
if (mappingModel.Priority != null)
respondProvider = respondProvider.AtPriority(mappingModel.Priority.Value);
@@ -230,20 +334,35 @@ namespace WireMock.Server
Method = logEntry.RequestMessage.Method,
Body = logEntry.RequestMessage.Body,
Headers = logEntry.RequestMessage.Headers,
Cookies = logEntry.RequestMessage.Cookies
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
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,
MappingTitle = logEntry.MappingTitle,
RequestMatchResult = logEntry.RequestMatchResult != null ? new LogRequestMatchModel
{
MatchScore = logEntry.RequestMatchResult.MatchScore,
Total = logEntry.RequestMatchResult.Total
TotalScore = logEntry.RequestMatchResult.TotalScore,
TotalNumber = logEntry.RequestMatchResult.TotalNumber,
IsPerfectMatch = logEntry.RequestMatchResult.IsPerfectMatch,
AverageTotalScore = logEntry.RequestMatchResult.AverageTotalScore
} : null
};
}
@@ -256,96 +375,125 @@ namespace WireMock.Server
}
#endregion Requests
private IRequestBuilder InitRequestBuilder(MappingModel mappingModel)
#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 (mappingModel.Request.Path != null)
if (requestModel.Path != null)
{
string path = mappingModel.Request.Path as string;
string path = requestModel.Path as string;
if (path != null)
requestBuilder = requestBuilder.WithPath(path);
else
{
var pathModel = JsonUtils.ParseJTokenToObject<PathModel>(mappingModel.Request.Path);
var pathModel = JsonUtils.ParseJTokenToObject<PathModel>(requestModel.Path);
if (pathModel?.Matchers != null)
requestBuilder = requestBuilder.WithPath(pathModel.Matchers.Select(Map).ToArray());
}
}
if (mappingModel.Request.Url != null)
if (requestModel.Url != null)
{
string url = mappingModel.Request.Url as string;
string url = requestModel.Url as string;
if (url != null)
requestBuilder = requestBuilder.WithUrl(url);
else
{
var urlModel = JsonUtils.ParseJTokenToObject<UrlModel>(mappingModel.Request.Url);
var urlModel = JsonUtils.ParseJTokenToObject<UrlModel>(requestModel.Url);
if (urlModel?.Matchers != null)
requestBuilder = requestBuilder.WithUrl(urlModel.Matchers.Select(Map).ToArray());
}
}
if (mappingModel.Request.Methods != null)
requestBuilder = requestBuilder.UsingVerb(mappingModel.Request.Methods);
else
requestBuilder = requestBuilder.UsingAnyVerb();
if (requestModel.Methods != null)
requestBuilder = requestBuilder.UsingVerb(requestModel.Methods);
if (mappingModel.Request.Headers != null)
if (requestModel.Headers != null)
{
foreach (var headerModel in mappingModel.Request.Headers.Where(h => h.Matchers != null))
foreach (var headerModel in requestModel.Headers.Where(h => h.Matchers != null))
{
requestBuilder = requestBuilder.WithHeader(headerModel.Name, headerModel.Matchers.Select(Map).ToArray());
}
}
if (mappingModel.Request.Cookies != null)
if (requestModel.Cookies != null)
{
foreach (var cookieModel in mappingModel.Request.Cookies.Where(c => c.Matchers != null))
foreach (var cookieModel in requestModel.Cookies.Where(c => c.Matchers != null))
{
requestBuilder = requestBuilder.WithCookie(cookieModel.Name, cookieModel.Matchers.Select(Map).ToArray());
}
}
if (mappingModel.Request.Params != null)
if (requestModel.Params != null)
{
foreach (var paramModel in mappingModel.Request.Params.Where(p => p.Values != null))
foreach (var paramModel in requestModel.Params.Where(p => p.Values != null))
{
requestBuilder = requestBuilder.WithParam(paramModel.Name, paramModel.Values.ToArray());
}
}
if (mappingModel.Request.Body?.Matcher != null)
if (requestModel.Body?.Matcher != null)
{
var bodyMatcher = Map(mappingModel.Request.Body.Matcher);
var bodyMatcher = Map(requestModel.Body.Matcher);
requestBuilder = requestBuilder.WithBody(bodyMatcher);
}
return requestBuilder;
}
private IResponseBuilder InitResponseBuilder(MappingModel mappingModel)
private IResponseBuilder InitResponseBuilder(ResponseModel responseModel)
{
IResponseBuilder responseBuilder = Response.Create();
if (mappingModel.Response.StatusCode.HasValue)
responseBuilder = responseBuilder.WithStatusCode(mappingModel.Response.StatusCode.Value);
if (responseModel.StatusCode.HasValue)
responseBuilder = responseBuilder.WithStatusCode(responseModel.StatusCode.Value);
if (mappingModel.Response.Headers != null)
responseBuilder = responseBuilder.WithHeaders(mappingModel.Response.Headers);
if (responseModel.Headers != null)
responseBuilder = responseBuilder.WithHeaders(responseModel.Headers);
else if (responseModel.HeadersRaw != null)
{
foreach (string headerLine in responseModel.HeadersRaw.Split(new[] { "\n", "\r\n" }, StringSplitOptions.RemoveEmptyEntries))
{
int indexColon = headerLine.IndexOf(":", StringComparison.Ordinal);
string key = headerLine.Substring(0, indexColon).TrimStart(' ', '\t');
string value = headerLine.Substring(indexColon + 1).TrimStart(' ', '\t');
responseBuilder = responseBuilder.WithHeader(key, value);
}
}
if (mappingModel.Response.Body != null)
responseBuilder = responseBuilder.WithBody(mappingModel.Response.Body);
else if (mappingModel.Response.BodyAsJson != null)
responseBuilder = responseBuilder.WithBodyAsJson(mappingModel.Response.BodyAsJson);
else if (mappingModel.Response.BodyAsBase64 != null)
responseBuilder = responseBuilder.WithBodyAsBase64(mappingModel.Response.BodyAsBase64);
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 (mappingModel.Response.UseTransformer)
if (responseModel.UseTransformer)
responseBuilder = responseBuilder.WithTransformer();
if (mappingModel.Response.Delay != null)
responseBuilder = responseBuilder.WithDelay(mappingModel.Response.Delay.Value);
if (responseModel.Delay > 0)
responseBuilder = responseBuilder.WithDelay(responseModel.Delay.Value);
return responseBuilder;
}
@@ -366,6 +514,7 @@ namespace WireMock.Server
return new MappingModel
{
Guid = mapping.Guid,
Title = mapping.Title,
Priority = mapping.Priority,
Request = new RequestModel
{
@@ -383,21 +532,21 @@ namespace WireMock.Server
Methods = methodMatcher?.Methods,
Headers = headerMatchers != null && headerMatchers.Any() ? headerMatchers?.Select(hm => new HeaderModel
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
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
Params = paramsMatchers != null && paramsMatchers.Any() ? paramsMatchers.Select(pm => new ParamModel
{
Name = pm.Key,
Values = pm.Values?.ToList(),
@@ -417,7 +566,14 @@ namespace WireMock.Server
Headers = response.ResponseMessage.Headers,
Body = response.ResponseMessage.Body,
UseTransformer = response.UseTransformer,
Delay = response.Delay?.Milliseconds
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
}
};
}
@@ -507,5 +663,10 @@ namespace WireMock.Server
Headers = new Dictionary<string, string> { { "Content-Type", "application/json" } }
};
}
private Encoding ToEncoding(EncodingModel encodingModel)
{
return encodingModel != null ? Encoding.GetEncoding(encodingModel.CodePage) : null;
}
}
}

View File

@@ -4,19 +4,23 @@ 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;
using System.Threading;
namespace WireMock.Server
{
/// <summary>
/// The fluent mock server.
/// </summary>
public partial class FluentMockServer
public partial class FluentMockServer : IDisposable
{
private readonly TinyHttpServer _httpServer;
@@ -30,26 +34,31 @@ namespace WireMock.Server
private readonly object _syncRoot = new object();
private TimeSpan _requestProcessingDelay = TimeSpan.Zero;
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
@@ -61,9 +70,38 @@ namespace WireMock.Server
}
}
/// <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
@@ -75,6 +113,19 @@ namespace WireMock.Server
}
}
/// <summary>
/// Starts the specified settings.
/// </summary>
/// <param name="settings">The FluentMockServerSettings.</param>
/// <returns>The <see cref="FluentMockServer"/>.</returns>
[PublicAPI]
public static FluentMockServer Start(FluentMockServerSettings settings)
{
Check.NotNull(settings, nameof(settings));
return new FluentMockServer(settings);
}
/// <summary>
/// Start this FluentMockServer.
/// </summary>
@@ -82,14 +133,13 @@ namespace WireMock.Server
/// <param name="ssl">The SSL support.</param>
/// <returns>The <see cref="FluentMockServer"/>.</returns>
[PublicAPI]
public static FluentMockServer Start(int port = 0, bool ssl = false)
public static FluentMockServer Start([CanBeNull] 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);
return new FluentMockServer(new FluentMockServerSettings
{
Port = port,
UseSSL = ssl
});
}
/// <summary>
@@ -102,7 +152,10 @@ namespace WireMock.Server
{
Check.NotEmpty(urls, nameof(urls));
return new FluentMockServer(false, urls);
return new FluentMockServer(new FluentMockServerSettings
{
Urls = urls
});
}
/// <summary>
@@ -112,14 +165,14 @@ namespace WireMock.Server
/// <param name="ssl">The SSL support.</param>
/// <returns>The <see cref="FluentMockServer"/>.</returns>
[PublicAPI]
public static FluentMockServer StartWithAdminInterface(int port = 0, bool ssl = false)
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);
return new FluentMockServer(new FluentMockServerSettings
{
Port = port,
UseSSL = ssl,
StartAdminInterface = true
});
}
/// <summary>
@@ -132,41 +185,95 @@ namespace WireMock.Server
{
Check.NotEmpty(urls, nameof(urls));
return new FluentMockServer(true, urls);
return new FluentMockServer(new FluentMockServerSettings
{
Urls = urls,
StartAdminInterface = true
});
}
private FluentMockServer(bool startAdminInterface, int port, bool ssl) : this(startAdminInterface, (ssl ? "https" : "http") + "://localhost:" + port + "/")
/// <summary>
/// Start this FluentMockServer with the admin interface and read static mappings.
/// </summary>
/// <param name="urls">The urls.</param>
/// <returns>The <see cref="FluentMockServer"/>.</returns>
[PublicAPI]
public static FluentMockServer StartWithAdminInterfaceAndReadStaticMappings(params string[] urls)
{
Check.NotEmpty(urls, nameof(urls));
return new FluentMockServer(new FluentMockServerSettings
{
Urls = urls,
StartAdminInterface = true,
ReadStaticMappings = true
});
}
private FluentMockServer(bool startAdminInterface, params string[] urls)
private FluentMockServer(FluentMockServerSettings settings)
{
Urls = urls;
if (settings.Urls != null)
{
Urls = settings.Urls;
}
else
{
int port = settings.Port > 0 ? settings.Port.Value : PortUtil.FindFreeTcpPort();
Urls = new[] { (settings.UseSSL == true ? "https" : "http") + "://localhost:" + port + "/" };
}
_httpServer = new TinyHttpServer(HandleRequestAsync, urls);
_httpServer = new TinyHttpServer(HandleRequestAsync, Urls);
Ports = _httpServer.Ports;
_httpServer.Start();
if (startAdminInterface)
if (settings.StartAdminInterface == true)
{
InitAdmin();
}
ReadStaticMappings();
if (settings.ReadStaticMappings == true)
{
ReadStaticMappings();
}
}
/// <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" }));
}
/// <summary>
/// Stop this server.
/// </summary>
[PublicAPI]
public void Stop()
{
_httpServer.Stop();
_httpServer?.Stop();
}
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
public void Dispose()
{
if (_httpServer != null && _httpServer.IsStarted)
{
_httpServer.Stop();
}
}
/// <summary>
/// Resets LogEntries and Mappings.
/// </summary>
[PublicAPI]
public void Reset()
{
ResetLogEntries();
@@ -177,6 +284,7 @@ namespace WireMock.Server
/// <summary>
/// Resets the LogEntries.
/// </summary>
[PublicAPI]
public void ResetLogEntries()
{
lock (((ICollection)_logEntries).SyncRoot)
@@ -209,6 +317,7 @@ namespace WireMock.Server
/// <summary>
/// Resets the Mappings.
/// </summary>
[PublicAPI]
public void ResetMappings()
{
lock (((ICollection)_mappings).SyncRoot)
@@ -238,27 +347,14 @@ namespace WireMock.Server
}
}
/// <summary>
/// The search logs for.
/// </summary>
/// <param name="matcher">The matcher.</param>
/// <returns>The <see cref="IEnumerable"/>.</returns>
public IEnumerable<LogEntry> SearchLogsFor(IRequestMatcher matcher)
{
lock (((ICollection)_logEntries).SyncRoot)
{
var requestMatchResult = new RequestMatchResult();
return _logEntries.Where(log => matcher.GetMatchingScore(log.RequestMessage, requestMatchResult) > 0.99);
}
}
/// <summary>
/// The add request processing delay.
/// </summary>
/// <param name="delay">
/// The delay.
/// </param>
public void AddRequestProcessingDelay(TimeSpan delay)
[PublicAPI]
public void AddGlobalProcessingDelay(TimeSpan delay)
{
lock (_syncRoot)
{
@@ -269,6 +365,7 @@ namespace WireMock.Server
/// <summary>
/// Allows the partial mapping.
/// </summary>
[PublicAPI]
public void AllowPartialMapping()
{
lock (_syncRoot)
@@ -277,11 +374,27 @@ namespace WireMock.Server
}
}
/// <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);
@@ -320,13 +433,17 @@ namespace WireMock.Server
/// The handle request.
/// </summary>
/// <param name="ctx">The HttpListenerContext.</param>
private async void HandleRequestAsync(HttpListenerContext ctx)
/// <param name="cancel">The CancellationToken.</param>
private async void HandleRequestAsync(HttpListenerContext ctx, CancellationToken cancel)
{
if (cancel.IsCancellationRequested)
return;
if (_requestProcessingDelay > TimeSpan.Zero)
{
lock (_syncRoot)
{
Task.Delay(_requestProcessingDelay).Wait();
Task.Delay(_requestProcessingDelay.Value, cancel).Wait(cancel);
}
}
@@ -344,15 +461,12 @@ namespace WireMock.Server
if (_allowPartialMapping)
{
var partialMappings = mappings
.Where(pm =>
(pm.Mapping.Provider is DynamicResponseProvider && pm.MatchResult.IsPerfectMatch) ||
!(pm.Mapping.Provider is DynamicResponseProvider)
)
.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.MatchPercentage > 0.0);
var bestPartialMatch = partialMappings.FirstOrDefault(pm => pm.MatchResult.AverageTotalScore > 0.0);
targetMapping = bestPartialMatch?.Mapping;
requestMatchResult = bestPartialMatch?.MatchResult;
@@ -369,24 +483,26 @@ namespace WireMock.Server
if (targetMapping == null)
{
response = new ResponseMessage
{
StatusCode = 404,
Body = "No mapping found"
};
response = new ResponseMessage { StatusCode = 404, Body = "No matching mapping found" };
return;
}
else
if (targetMapping.IsAdminInterface && _authorizationMatcher != null)
{
response = await targetMapping.ResponseTo(request);
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()
};
response = new ResponseMessage { StatusCode = 500, Body = ex.ToString() };
}
finally
{
@@ -396,6 +512,7 @@ namespace WireMock.Server
RequestMessage = request,
ResponseMessage = response,
MappingGuid = targetMapping?.Guid,
MappingTitle = targetMapping?.Title,
RequestMatchResult = requestMatchResult
};
@@ -406,4 +523,4 @@ namespace WireMock.Server
}
}
}
}
}

View File

@@ -0,0 +1,48 @@
namespace WireMock.Server
{
/// <summary>
/// FluentMockServerSettings
/// </summary>
public class FluentMockServerSettings
{
/// <summary>
/// Gets or sets the port.
/// </summary>
/// <value>
/// The port.
/// </value>
public int? Port { get; set; }
/// <summary>
/// Gets or sets the use SSL.
/// </summary>
/// <value>
/// The use SSL.
/// </value>
public bool? UseSSL { get; set; }
/// <summary>
/// Gets or sets the start admin interface.
/// </summary>
/// <value>
/// The start admin interface.
/// </value>
public bool? StartAdminInterface { get; set; }
/// <summary>
/// Gets or sets the read static mappings.
/// </summary>
/// <value>
/// The read static mappings.
/// </value>
public bool? ReadStaticMappings { get; set; }
/// <summary>
/// Gets or sets the urls.
/// </summary>
/// <value>
/// The urls.
/// </value>
public string[] Urls { get; set; }
}
}

View File

@@ -14,6 +14,20 @@ namespace WireMock.Server
/// <returns>The <see cref="IRespondWithAProvider"/>.</returns>
IRespondWithAProvider WithGuid(Guid guid);
/// <summary>
/// Define a unique title for this mapping.
/// </summary>
/// <param name="title">The unique title.</param>
/// <returns>The <see cref="IRespondWithAProvider"/>.</returns>
IRespondWithAProvider WithTitle(string title);
/// <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>

View File

@@ -10,6 +10,7 @@ namespace WireMock.Server
{
private int _priority;
private Guid? _guid;
private string _title;
/// <summary>
/// The _registration callback.
@@ -41,7 +42,17 @@ namespace WireMock.Server
public void RespondWith(IResponseProvider provider)
{
var mappingGuid = _guid ?? Guid.NewGuid();
_registrationCallback(new Mapping(mappingGuid, _requestMatcher, provider, _priority));
_registrationCallback(new Mapping(mappingGuid, _title, _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>
@@ -56,6 +67,18 @@ namespace WireMock.Server
return this;
}
/// <summary>
/// Define a unique identifier for this mapping.
/// </summary>
/// <param name="title">The unique identifier.</param>
/// <returns>The <see cref="IRespondWithAProvider"/>.</returns>
public IRespondWithAProvider WithTitle(string title)
{
_title = title;
return this;
}
/// <summary>
/// Define the priority for this mapping.
/// </summary>

View File

@@ -1,5 +1,5 @@
{
"version": "1.0.1.1",
"version": "1.0.1.5",
"title": "WireMock.Net",
"description": "Lightweight Http Mocking Server for .Net, inspired by WireMock from the Java landscape.",
"authors": [ "Alexandre Victoor", "Stef Heyenrath" ],
@@ -15,7 +15,7 @@
"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 Admin-Interface with static mapping.json file support"
"releaseNotes": "Fix StartAndStop from listener."
},
"buildOptions": {
@@ -24,7 +24,7 @@
"dependencies": {
"JetBrains.Annotations": {
"version": "10.2.1",
"version": "10.4.0",
"type": "build"
},
"Handlebars.Net": "1.8.0",

View 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();
}
}
}

View 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();
}
}
}

View 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;
}
}
}
}

View 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;
}
}
}

View File

@@ -1,5 +1,4 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following

View File

@@ -0,0 +1,35 @@
using System;
using System.Text;
using NFluent;
using NUnit.Framework;
namespace WireMock.Net.Tests
{
[TestFixture]
public class RequestMessageTests
{
[Test]
public void Should_handle_empty_query()
{
// given
var request = new RequestMessage(new Uri("http://localhost/foo"), "POST");
// then
Check.That(request.GetParameter("not_there")).IsNull();
}
[Test]
public void Should_parse_query_params()
{
// given
string bodyAsString = "whatever";
byte[] body = Encoding.UTF8.GetBytes(bodyAsString);
var request = new RequestMessage(new Uri("http://localhost?foo=bar&multi=1&multi=2"), "POST", body, bodyAsString, Encoding.UTF8);
// then
Check.That(request.GetParameter("foo")).Contains("bar");
Check.That(request.GetParameter("multi")).Contains("1");
Check.That(request.GetParameter("multi")).Contains("2");
}
}
}

View File

@@ -0,0 +1,550 @@
using System;
using System.Collections.Generic;
using System.Text;
using NFluent;
using NUnit.Framework;
using WireMock.RequestBuilders;
using WireMock.Matchers;
using WireMock.Matchers.Request;
namespace WireMock.Net.Tests
{
[TestFixture]
public class RequestTests
{
[Test]
public void Should_specify_requests_matching_given_path()
{
// given
var spec = Request.Create().WithPath("/foo");
// when
var request = new RequestMessage(new Uri("http://localhost/foo"), "blabla");
// then
var requestMatchResult = new RequestMatchResult();
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0);
}
[Test]
public void Should_specify_requests_matching_given_paths()
{
var requestBuilder = Request.Create().WithPath("/x1", "/x2");
var request1 = new RequestMessage(new Uri("http://localhost/x1"), "blabla");
var request2 = new RequestMessage(new Uri("http://localhost/x2"), "blabla");
var requestMatchResult = new RequestMatchResult();
Check.That(requestBuilder.GetMatchingScore(request1, requestMatchResult)).IsEqualTo(1.0);
Check.That(requestBuilder.GetMatchingScore(request2, requestMatchResult)).IsEqualTo(1.0);
}
[Test]
public void Should_specify_requests_matching_given_pathFuncs()
{
// given
var spec = Request.Create().WithPath(url => url.EndsWith("/foo"));
// when
var request = new RequestMessage(new Uri("http://localhost/foo"), "blabla");
// then
var requestMatchResult = new RequestMatchResult();
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0);
}
[Test]
public void Should_specify_requests_matching_given_path_prefix()
{
// given
var spec = Request.Create().WithPath(new RegexMatcher("^/foo"));
// when
var request = new RequestMessage(new Uri("http://localhost/foo/bar"), "blabla");
// then
var requestMatchResult = new RequestMatchResult();
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0);
}
[Test]
public void Should_exclude_requests_not_matching_given_path()
{
// given
var spec = Request.Create().WithPath("/foo");
// when
var request = new RequestMessage(new Uri("http://localhost/bar"), "blabla");
// then
var requestMatchResult = new RequestMatchResult();
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsNotEqualTo(1.0);
}
[Test]
public void Should_specify_requests_matching_given_url()
{
// given
var spec = Request.Create().WithUrl("*/foo");
// when
var request = new RequestMessage(new Uri("http://localhost/foo"), "blabla");
// then
var requestMatchResult = new RequestMatchResult();
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0);
}
[Test]
public void Should_specify_requests_matching_given_path_and_method_put()
{
// given
var spec = Request.Create().WithPath("/foo").UsingPut();
// when
var request = new RequestMessage(new Uri("http://localhost/foo"), "PUT");
// then
var requestMatchResult = new RequestMatchResult();
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0);
}
[Test]
public void Should_specify_requests_matching_given_path_and_method_post()
{
// given
var spec = Request.Create().WithPath("/foo").UsingPost();
// when
var request = new RequestMessage(new Uri("http://localhost/foo"), "POST");
// then
var requestMatchResult = new RequestMatchResult();
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0);
}
[Test]
public void Should_specify_requests_matching_given_path_and_method_get()
{
// given
var spec = Request.Create().WithPath("/foo").UsingGet();
// when
var request = new RequestMessage(new Uri("http://localhost/foo"), "GET");
// then
var requestMatchResult = new RequestMatchResult();
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0);
}
[Test]
public void Should_specify_requests_matching_given_path_and_method_delete()
{
// given
var spec = Request.Create().WithPath("/foo").UsingDelete();
// when
string bodyAsString = "whatever";
byte[] body = Encoding.UTF8.GetBytes(bodyAsString);
var request = new RequestMessage(new Uri("http://localhost/foo"), "Delete", body, bodyAsString, Encoding.UTF8);
// then
var requestMatchResult = new RequestMatchResult();
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0);
}
[Test]
public void Should_specify_requests_matching_given_path_and_method_head()
{
// given
var spec = Request.Create().WithPath("/foo").UsingHead();
// when
var request = new RequestMessage(new Uri("http://localhost/foo"), "HEAD");
// then
var requestMatchResult = new RequestMatchResult();
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0);
}
[Test]
public void Should_exclude_requests_matching_given_path_but_not_http_method()
{
// given
var spec = Request.Create().WithPath("/foo").UsingPut();
// when
var request = new RequestMessage(new Uri("http://localhost/foo"), "HEAD");
// then
var requestMatchResult = new RequestMatchResult();
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsNotEqualTo(1.0);
}
[Test]
public void Should_exclude_requests_matching_given_http_method_but_not_url()
{
// given
var spec = Request.Create().WithPath("/bar").UsingPut();
// when
var request = new RequestMessage(new Uri("http://localhost/foo"), "PUT");
// then
var requestMatchResult = new RequestMatchResult();
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsNotEqualTo(1.0);
}
[Test]
public void Should_specify_requests_matching_given_path_and_headers()
{
// given
var spec = Request.Create().WithPath("/foo").UsingAnyVerb().WithHeader("X-toto", "tata");
// when
string bodyAsString = "whatever";
byte[] body = Encoding.UTF8.GetBytes(bodyAsString);
var request = new RequestMessage(new Uri("http://localhost/foo"), "PUT", body, bodyAsString, Encoding.UTF8, new Dictionary <string, string> { { "X-toto", "tata" } });
// then
var requestMatchResult = new RequestMatchResult();
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0);
}
[Test]
public void Should_exclude_requests_not_matching_given_headers()
{
// given
var spec = Request.Create().UsingAnyVerb().WithHeader("X-toto", "tatata");
// when
string bodyAsString = "whatever";
byte[] body = Encoding.UTF8.GetBytes(bodyAsString);
var request = new RequestMessage(new Uri("http://localhost/foo"), "PUT", body, bodyAsString, Encoding.UTF8, new Dictionary <string, string> { { "X-toto", "tata" } });
// then
var requestMatchResult = new RequestMatchResult();
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsNotEqualTo(1.0);
}
[Test]
public void Should_exclude_requests_not_matching_given_headers_ignorecase()
{
// given
var spec = Request.Create().UsingAnyVerb().WithHeader("X-toto", "abc", false);
// when
string bodyAsString = "whatever";
byte[] body = Encoding.UTF8.GetBytes(bodyAsString);
var request = new RequestMessage(new Uri("http://localhost/foo"), "PUT", body, bodyAsString, Encoding.UTF8, new Dictionary <string, string> { { "X-toto", "ABC" } });
// then
var requestMatchResult = new RequestMatchResult();
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsNotEqualTo(1.0);
}
[Test]
public void Should_specify_requests_matching_given_header_prefix()
{
// given
var spec = Request.Create().UsingAnyVerb().WithHeader("X-toto", "tata*");
// when
string bodyAsString = "whatever";
byte[] body = Encoding.UTF8.GetBytes(bodyAsString);
var request = new RequestMessage(new Uri("http://localhost/foo"), "PUT", body, bodyAsString, Encoding.UTF8, new Dictionary<string, string> { { "X-toto", "TaTa" } });
// then
var requestMatchResult = new RequestMatchResult();
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0);
}
[Test]
public void Should_specify_requests_matching_given_cookies()
{
// given
var spec = Request.Create().UsingAnyVerb().WithCookie("session", "a*");
// when
var request = new RequestMessage(new Uri("http://localhost/foo"), "PUT", null, null, null, null, new Dictionary<string, string> { { "session", "abc" } });
// then
var requestMatchResult = new RequestMatchResult();
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0);
}
[Test]
public void Should_specify_requests_matching_given_body()
{
// given
var spec = Request.Create().UsingAnyVerb().WithBody("Hello world!");
// when
string bodyAsString = "Hello world!";
byte[] body = Encoding.UTF8.GetBytes(bodyAsString);
var request = new RequestMessage(new Uri("http://localhost/foo"), "PUT", body, bodyAsString, Encoding.UTF8);
// then
var requestMatchResult = new RequestMatchResult();
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0);
}
[Test]
public void Should_specify_requests_matching_given_body_using_ExactMatcher_true()
{
// given
var requestBuilder = Request.Create().UsingAnyVerb().WithBody(new ExactMatcher("cat"));
// when
string bodyAsString = "cat";
byte[] body = Encoding.UTF8.GetBytes(bodyAsString);
var request = new RequestMessage(new Uri("http://localhost/foo"), "POST", body, bodyAsString, Encoding.UTF8);
// then
var requestMatchResult = new RequestMatchResult();
Check.That(requestBuilder.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0);
}
[Test]
public void Should_specify_requests_matching_given_body_using_ExactMatcher_multiplePatterns()
{
// given
var requestBuilder = Request.Create().UsingAnyVerb().WithBody(new ExactMatcher("cat", "dog"));
// when
string bodyAsString = "cat";
byte[] body = Encoding.UTF8.GetBytes(bodyAsString);
var request = new RequestMessage(new Uri("http://localhost/foo"), "POST", body, bodyAsString, Encoding.UTF8);
// then
var requestMatchResult = new RequestMatchResult();
Check.That(requestBuilder.GetMatchingScore(request, requestMatchResult)).IsEqualTo(0.5);
}
[Test]
public void Should_specify_requests_matching_given_body_using_ExactMatcher_false()
{
// given
var requestBuilder = Request.Create().UsingAnyVerb().WithBody(new ExactMatcher("cat"));
// when
string bodyAsString = "caR";
byte[] body = Encoding.UTF8.GetBytes(bodyAsString);
var request = new RequestMessage(new Uri("http://localhost/foo"), "POST", body, bodyAsString, Encoding.UTF8);
// then
var requestMatchResult = new RequestMatchResult();
Check.That(requestBuilder.GetMatchingScore(request, requestMatchResult)).IsLessThan(1.0);
}
[Test]
public void Should_specify_requests_matching_given_body_using_SimMetricsMatcher1()
{
// given
var requestBuilder = Request.Create().UsingAnyVerb().WithBody(new SimMetricsMatcher("The cat walks in the street."));
// when
string bodyAsString = "The car drives in the street.";
byte[] body = Encoding.UTF8.GetBytes(bodyAsString);
var request = new RequestMessage(new Uri("http://localhost/foo"), "POST", body, bodyAsString, Encoding.UTF8);
// then
var requestMatchResult = new RequestMatchResult();
Check.That(requestBuilder.GetMatchingScore(request, requestMatchResult)).IsLessThan(1.0).And.IsGreaterThan(0.5);
}
[Test]
public void Should_specify_requests_matching_given_body_using_SimMetricsMatcher2()
{
// given
var requestBuilder = Request.Create().UsingAnyVerb().WithBody(new SimMetricsMatcher("The cat walks in the street."));
// when
string bodyAsString = "Hello";
byte[] body = Encoding.UTF8.GetBytes(bodyAsString);
var request = new RequestMessage(new Uri("http://localhost/foo"), "POST", body, bodyAsString, Encoding.UTF8);
// then
var requestMatchResult = new RequestMatchResult();
Check.That(requestBuilder.GetMatchingScore(request, requestMatchResult)).IsLessThan(0.1).And.IsGreaterThan(0.05);
}
[Test]
public void Should_specify_requests_matching_given_body_using_WildcardMatcher()
{
// given
var spec = Request.Create().WithPath("/foo").UsingAnyVerb().WithBody(new WildcardMatcher("H*o*"));
// when
string bodyAsString = "Hello world!";
byte[] body = Encoding.UTF8.GetBytes(bodyAsString);
var request = new RequestMessage(new Uri("http://localhost/foo"), "PUT", body, bodyAsString, Encoding.UTF8, new Dictionary<string, string> { { "X-toto", "tatata" } });
// then
var requestMatchResult = new RequestMatchResult();
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0);
}
[Test]
public void Should_specify_requests_matching_given_body_using_RegexMatcher()
{
// given
var spec = Request.Create().UsingAnyVerb().WithBody(new RegexMatcher("H.*o"));
// when
string bodyAsString = "Hello world!";
byte[] body = Encoding.UTF8.GetBytes(bodyAsString);
var request = new RequestMessage(new Uri("http://localhost/foo"), "PUT", body, bodyAsString, Encoding.UTF8);
// then
var requestMatchResult = new RequestMatchResult();
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0);
}
[Test]
public void Should_specify_requests_matching_given_body_using_XPathMatcher_true()
{
// given
var spec = Request.Create().UsingAnyVerb().WithBody(new XPathMatcher("/todo-list[count(todo-item) = 3]"));
// when
string xmlBodyAsString = @"
<todo-list>
<todo-item id='a1'>abc</todo-item>
<todo-item id='a2'>def</todo-item>
<todo-item id='a3'>xyz</todo-item>
</todo-list>";
byte[] body = Encoding.UTF8.GetBytes(xmlBodyAsString);
var request = new RequestMessage(new Uri("http://localhost/foo"), "PUT", body, xmlBodyAsString, Encoding.UTF8);
// then
var requestMatchResult = new RequestMatchResult();
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0);
}
[Test]
public void Should_specify_requests_matching_given_body_using_XPathMatcher_false()
{
// given
var spec = Request.Create().UsingAnyVerb().WithBody(new XPathMatcher("/todo-list[count(todo-item) = 99]"));
// when
string xmlBodyAsString = @"
<todo-list>
<todo-item id='a1'>abc</todo-item>
<todo-item id='a2'>def</todo-item>
<todo-item id='a3'>xyz</todo-item>
</todo-list>";
byte[] body = Encoding.UTF8.GetBytes(xmlBodyAsString);
var request = new RequestMessage(new Uri("http://localhost/foo"), "PUT", body, xmlBodyAsString, Encoding.UTF8);
// then
var requestMatchResult = new RequestMatchResult();
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsNotEqualTo(1.0);
}
[Test]
public void Should_specify_requests_matching_given_body_using_JsonPathMatcher_true()
{
// given
var spec = Request.Create().UsingAnyVerb().WithBody(new JsonPathMatcher("$.things[?(@.name == 'RequiredThing')]"));
// when
string bodyAsString = "{ \"things\": [ { \"name\": \"RequiredThing\" }, { \"name\": \"Wiremock\" } ] }";
byte[] body = Encoding.UTF8.GetBytes(bodyAsString);
var request = new RequestMessage(new Uri("http://localhost/foo"), "PUT", body, bodyAsString, Encoding.UTF8);
// then
var requestMatchResult = new RequestMatchResult();
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0);
}
[Test]
public void Should_specify_requests_matching_given_body_using_JsonPathMatcher_false()
{
// given
var spec = Request.Create().UsingAnyVerb().WithBody(new JsonPathMatcher("$.things[?(@.name == 'RequiredThing')]"));
// when
string bodyAsString = "{ \"things\": { \"name\": \"Wiremock\" } }";
byte[] body = Encoding.UTF8.GetBytes(bodyAsString);
var request = new RequestMessage(new Uri("http://localhost/foo"), "PUT", body, bodyAsString, Encoding.UTF8);
// then
var requestMatchResult = new RequestMatchResult();
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsNotEqualTo(1.0);
}
[Test]
public void Should_exclude_requests_not_matching_given_body()
{
// given
var spec = Request.Create().UsingAnyVerb().WithBody(" Hello world! ");
// when
string bodyAsString = "xxx";
byte[] body = Encoding.UTF8.GetBytes(bodyAsString);
var request = new RequestMessage(new Uri("http://localhost/foo"), "PUT", body, bodyAsString, Encoding.UTF8, new Dictionary<string, string> { { "X-toto", "tatata" } });
// then
var requestMatchResult = new RequestMatchResult();
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsNotEqualTo(1.0);
}
[Test]
public void Should_specify_requests_matching_given_param()
{
// given
var spec = Request.Create().WithParam("bar", "1", "2");
// when
var request = new RequestMessage(new Uri("http://localhost/foo?bar=1&bar=2"), "PUT");
// then
var requestMatchResult = new RequestMatchResult();
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0);
}
[Test]
public void Should_specify_requests_matching_given_paramNoValue()
{
// given
var spec = Request.Create().WithParam("bar");
// when
var request = new RequestMessage(new Uri("http://localhost/foo?bar"), "PUT");
// then
var requestMatchResult = new RequestMatchResult();
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0);
}
[Test]
public void Should_specify_requests_matching_given_param_func()
{
// given
var spec = Request.Create().UsingAnyVerb().WithParam(p => p.ContainsKey("bar"));
// when
var request = new RequestMessage(new Uri("http://localhost/foo?bar=1&bar=2"), "PUT");
// then
var requestMatchResult = new RequestMatchResult();
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0);
}
[Test]
public void Should_exclude_requests_not_matching_given_params()
{
// given
var spec = Request.Create().WithParam("bar", "1");
// when
var request = new RequestMessage(new Uri("http://localhost/test=7"), "PUT");
// then
var requestMatchResult = new RequestMatchResult();
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsNotEqualTo(1.0);
}
}
}

View File

@@ -0,0 +1,106 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using NFluent;
using NUnit.Framework;
using WireMock.ResponseBuilders;
namespace WireMock.Net.Tests
{
[TestFixture]
public class ResponseTests
{
[Test]
public async Task Response_ProvideResponse_Handlebars_UrlPathVerb()
{
// given
string bodyAsString = "abc";
byte[] body = Encoding.UTF8.GetBytes(bodyAsString);
var request = new RequestMessage(new Uri("http://localhost/foo"), "POST", body, bodyAsString, Encoding.UTF8);
var response = Response.Create()
.WithBody("test {{request.url}} {{request.path}} {{request.method}}")
.WithTransformer();
// act
var responseMessage = await response.ProvideResponse(request);
// then
Check.That(responseMessage.Body).Equals("test http://localhost/foo /foo post");
}
[Test]
public async Task Response_ProvideResponse_Handlebars_Query()
{
// given
string bodyAsString = "abc";
byte[] body = Encoding.UTF8.GetBytes(bodyAsString);
var request = new RequestMessage(new Uri("http://localhost/foo?a=1&a=2&b=5"), "POST", body, bodyAsString, Encoding.UTF8);
var response = Response.Create()
.WithBody("test keya={{request.query.a}} idx={{request.query.a.[0]}} idx={{request.query.a.[1]}} keyb={{request.query.b}}")
.WithTransformer();
// act
var responseMessage = await response.ProvideResponse(request);
// then
Check.That(responseMessage.Body).Equals("test keya=1 idx=1 idx=2 keyb=5");
}
[Test]
public async Task Response_ProvideResponse_Handlebars_Headers()
{
// given
string bodyAsString = "abc";
byte[] body = Encoding.UTF8.GetBytes(bodyAsString);
var request = new RequestMessage(new Uri("http://localhost/foo"), "POST", body, bodyAsString, Encoding.UTF8, new Dictionary<string, string> { { "Content-Type", "text/plain" } });
var response = Response.Create().WithHeader("x", "{{request.headers.Content-Type}}").WithBody("test").WithTransformer();
// act
var responseMessage = await response.ProvideResponse(request);
// then
Check.That(responseMessage.Body).Equals("test");
Check.That(responseMessage.Headers).Contains(new KeyValuePair<string,string>("x", "text/plain"));
}
[Test]
public async Task Response_ProvideResponse_Encoding_Body()
{
// given
string bodyAsString = "abc";
byte[] body = Encoding.UTF8.GetBytes(bodyAsString);
var request = new RequestMessage(new Uri("http://localhost/foo"), "POST", body, bodyAsString, Encoding.UTF8);
var response = Response.Create().WithBody("test", Encoding.ASCII);
// act
var responseMessage = await response.ProvideResponse(request);
// then
Check.That(responseMessage.Body).Equals("test");
Check.That(responseMessage.BodyEncoding).Equals(Encoding.ASCII);
}
[Test]
public async Task Response_ProvideResponse_Encoding_JsonBody()
{
// given
string bodyAsString = "abc";
byte[] body = Encoding.UTF8.GetBytes(bodyAsString);
var request = new RequestMessage(new Uri("http://localhost/foo"), "POST", body, bodyAsString, Encoding.UTF8);
var response = Response.Create().WithBodyAsJson(new { value = "test" }, Encoding.ASCII);
// act
var responseMessage = await response.ProvideResponse(request);
// then
Check.That(responseMessage.Body).Equals("{\"value\":\"test\"}");
Check.That(responseMessage.BodyEncoding).Equals(Encoding.ASCII);
}
}
}

View File

@@ -0,0 +1,59 @@
using NUnit.Framework;
using WireMock.Matchers;
namespace WireMock.Net.Tests
{
[TestFixture]
public class WildcardMatcherTest
{
[Test]
public void WildcardMatcher_patterns_positive()
{
var tests = new[]
{
new { p = "*", i = "" },
new { p = "?", i = " " },
new { p = "*", i = "a" },
new { p = "*", i = "ab" },
new { p = "?", i = "a" },
new { p = "*?", i = "abc" },
new { p = "?*", i = "abc" },
new { p = "abc", i = "abc" },
new { p = "abc*", i = "abc" },
new { p = "abc*", i = "abcd" },
new { p = "*abc*", i = "abc" },
new { p = "*a*bc*", i = "abc" },
new { p = "*a*b?", i = "aXXXbc" }
};
foreach (var test in tests)
{
var matcher = new WildcardMatcher(test.p);
Assert.AreEqual(1.0, matcher.IsMatch(test.i), "p = " + test.p + ", i = " + test.i);
}
}
[Test]
public void WildcardMatcher_patterns_negative()
{
var tests = new[]
{
new { p = "*a", i = ""},
new { p = "a*", i = ""},
new { p = "?", i = ""},
new { p = "*b*", i = "a"},
new { p = "b*a", i = "ab"},
new { p = "??", i = "a"},
new { p = "*?", i = ""},
new { p = "??*", i = "a"},
new { p = "*abc", i = "abX"},
new { p = "*abc*", i = "Xbc"},
new { p = "*a*bc*", i = "ac"}
};
foreach (var test in tests)
{
var matcher = new WildcardMatcher(test.p);
Assert.AreEqual(0.0, matcher.IsMatch(test.i), "p = " + test.p + ", i = " + test.i);
}
}
}
}

View File

@@ -46,6 +46,10 @@
<HintPath>..\..\packages\NUnit.3.6.0\lib\net45\nunit.framework.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="System.Xml.Linq" />

View File

@@ -4,4 +4,5 @@
<package id="Moq" version="4.5.30" targetFramework="net452" />
<package id="NFluent" version="1.3.1.0" targetFramework="net452" />
<package id="NUnit" version="3.6.0" targetFramework="net452" />
<package id="SimMetrics.Net" version="1.0.1.0" targetFramework="net452" />
</packages>

View File

@@ -1,5 +1,6 @@
using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
@@ -19,6 +20,76 @@ namespace WireMock.Net.Tests
{
private FluentMockServer _server;
// For for AppVeyor + OpenCover
private string GetCurrentFolder()
{
string current = Directory.GetCurrentDirectory();
if (!current.EndsWith("WireMock.Net.Tests"))
return Path.Combine(current, "test", "WireMock.Net.Tests");
return current;
}
[Test]
public void FluentMockServer_StartStop()
{
var server1 = FluentMockServer.Start("http://localhost:9090/");
server1.Stop();
var server2 = FluentMockServer.Start("http://localhost:9090/");
server2.Stop();
}
[Test]
public void FluentMockServer_ReadStaticMapping_WithNonGuidFilename()
{
var guid = Guid.Parse("04ee4872-9efd-4770-90d3-88d445265d0d");
string title = "documentdb_root_title";
_server = FluentMockServer.Start();
string folder = Path.Combine(GetCurrentFolder(), "__admin", "mappings", "documentdb_root.json");
_server.ReadStaticMapping(folder);
var mappings = _server.Mappings.ToArray();
Check.That(mappings).HasSize(1);
Check.That(mappings.First().RequestMatcher).IsNotNull();
Check.That(mappings.First().Provider).IsNotNull();
Check.That(mappings.First().Guid).Equals(guid);
Check.That(mappings.First().Title).Equals(title);
}
[Test]
public void FluentMockServer_ReadStaticMapping_WithGuidFilename()
{
string guid = "00000002-ee28-4f29-ae63-1ac9b0802d86";
_server = FluentMockServer.Start();
string folder = Path.Combine(GetCurrentFolder(), "__admin", "mappings", guid + ".json");
_server.ReadStaticMapping(folder);
var mappings = _server.Mappings.ToArray();
Check.That(mappings).HasSize(1);
Check.That(mappings.First().RequestMatcher).IsNotNull();
Check.That(mappings.First().Provider).IsNotNull();
Check.That(mappings.First().Guid).Equals(Guid.Parse(guid));
Check.That(mappings.First().Title).IsNullOrEmpty();
}
[Test]
public void FluentMockServer_ReadStaticMappings()
{
_server = FluentMockServer.Start();
string folder = Path.Combine(GetCurrentFolder(), "__admin", "mappings");
_server.ReadStaticMappings(folder);
var mappings = _server.Mappings.ToArray();
Check.That(mappings).HasSize(2);
}
[Test]
public void FluentMockServer_Admin_Mappings_Get()
{
@@ -167,7 +238,7 @@ namespace WireMock.Net.Tests
await new HttpClient().GetAsync("http://localhost:" + _server.Ports[0] + "/bar");
// then
var result = _server.SearchLogsFor(Request.Create().WithPath(new RegexMatcher("^/b.*"))).ToList();
var result = _server.FindLogEntries(Request.Create().WithPath(new RegexMatcher("^/b.*"))).ToList();
Check.That(result).HasSize(1);
var requestLogged = result.First();
@@ -259,7 +330,7 @@ namespace WireMock.Net.Tests
watch.Stop();
// then
Check.That(watch.ElapsedMilliseconds).IsGreaterThan(200);
Check.That(watch.ElapsedMilliseconds).IsStrictlyGreaterThan(200);
}
[Test]
@@ -267,7 +338,7 @@ namespace WireMock.Net.Tests
{
// given
_server = FluentMockServer.Start();
_server.AddRequestProcessingDelay(TimeSpan.FromMilliseconds(200));
_server.AddGlobalProcessingDelay(TimeSpan.FromMilliseconds(200));
_server
.Given(Request.Create().WithPath("/*"))
.RespondWith(Response.Create().WithBody(@"{ msg: ""Hello world!""}"));
@@ -279,13 +350,13 @@ namespace WireMock.Net.Tests
watch.Stop();
// then
Check.That(watch.ElapsedMilliseconds).IsGreaterThan(200);
Check.That(watch.ElapsedMilliseconds).IsStrictlyGreaterThan(200);
}
[TearDown]
public void ShutdownServer()
{
_server.Stop();
_server?.Stop();
}
}
}

View File

@@ -25,7 +25,7 @@ namespace WireMock.Net.Tests.Http
var port = PortUtil.FindFreeTcpPort();
bool called = false;
var urlPrefix = "http://localhost:" + port + "/";
var server = new TinyHttpServer(ctx => called = true, urlPrefix);
var server = new TinyHttpServer((ctx, token) => called = true, urlPrefix);
server.Start();
// when

View File

@@ -1,8 +1,8 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Net;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using NFluent;
using NUnit.Framework;
@@ -104,7 +104,7 @@ namespace WireMock.Net.Tests
{
private static volatile RequestMessage _lastRequestMessage;
private MapperServer(Action<HttpListenerContext> httpHandler, string urlPrefix) : base(httpHandler, urlPrefix)
private MapperServer(Action<HttpListenerContext, CancellationToken> httpHandler, string urlPrefix) : base(httpHandler, urlPrefix)
{
}
@@ -128,7 +128,7 @@ namespace WireMock.Net.Tests
int port = PortUtil.FindFreeTcpPort();
UrlPrefix = "http://localhost:" + port + "/";
var server = new MapperServer(
context =>
(context, token) =>
{
LastRequestMessage = new HttpListenerRequestMapper().Map(context.Request);
context.Response.Close();

View File

@@ -1,5 +1,6 @@
using System.Net;
using System.Net.Http;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using NFluent;
@@ -67,6 +68,31 @@ namespace WireMock.Net.Tests
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()
{
@@ -86,7 +112,7 @@ namespace WireMock.Net.Tests
var responseReady = new AutoResetEvent(false);
HttpListenerResponse response = null;
_server = new TinyHttpServer(
context =>
(context, token) =>
{
response = context.Response;
responseReady.Set();

View File

@@ -24,7 +24,7 @@ namespace WireMock.Net.Tests
// given
string bodyAsString = "whatever";
byte[] body = Encoding.UTF8.GetBytes(bodyAsString);
var request = new RequestMessage(new Uri("http://localhost?foo=bar&multi=1&multi=2"), "POST", body, bodyAsString);
var request = new RequestMessage(new Uri("http://localhost?foo=bar&multi=1&multi=2"), "POST", body, bodyAsString, Encoding.UTF8);
// then
Check.That(request.GetParameter("foo")).Contains("bar");

View File

@@ -146,7 +146,7 @@ namespace WireMock.Net.Tests
// when
string bodyAsString = "whatever";
byte[] body = Encoding.UTF8.GetBytes(bodyAsString);
var request = new RequestMessage(new Uri("http://localhost/foo"), "Delete", body, bodyAsString);
var request = new RequestMessage(new Uri("http://localhost/foo"), "Delete", body, bodyAsString, Encoding.UTF8);
// then
var requestMatchResult = new RequestMatchResult();
@@ -204,7 +204,7 @@ namespace WireMock.Net.Tests
// when
string bodyAsString = "whatever";
byte[] body = Encoding.UTF8.GetBytes(bodyAsString);
var request = new RequestMessage(new Uri("http://localhost/foo"), "PUT", body, bodyAsString, new Dictionary<string, string> { { "X-toto", "tata" } });
var request = new RequestMessage(new Uri("http://localhost/foo"), "PUT", body, bodyAsString, Encoding.UTF8, new Dictionary <string, string> { { "X-toto", "tata" } });
// then
var requestMatchResult = new RequestMatchResult();
@@ -220,7 +220,7 @@ namespace WireMock.Net.Tests
// when
string bodyAsString = "whatever";
byte[] body = Encoding.UTF8.GetBytes(bodyAsString);
var request = new RequestMessage(new Uri("http://localhost/foo"), "PUT", body, bodyAsString, new Dictionary<string, string> { { "X-toto", "tata" } });
var request = new RequestMessage(new Uri("http://localhost/foo"), "PUT", body, bodyAsString, Encoding.UTF8, new Dictionary <string, string> { { "X-toto", "tata" } });
// then
var requestMatchResult = new RequestMatchResult();
@@ -236,7 +236,7 @@ namespace WireMock.Net.Tests
// when
string bodyAsString = "whatever";
byte[] body = Encoding.UTF8.GetBytes(bodyAsString);
var request = new RequestMessage(new Uri("http://localhost/foo"), "PUT", body, bodyAsString, new Dictionary<string, string> { { "X-toto", "ABC" } });
var request = new RequestMessage(new Uri("http://localhost/foo"), "PUT", body, bodyAsString, Encoding.UTF8, new Dictionary <string, string> { { "X-toto", "ABC" } });
// then
var requestMatchResult = new RequestMatchResult();
@@ -252,7 +252,7 @@ namespace WireMock.Net.Tests
// when
string bodyAsString = "whatever";
byte[] body = Encoding.UTF8.GetBytes(bodyAsString);
var request = new RequestMessage(new Uri("http://localhost/foo"), "PUT", body, bodyAsString, new Dictionary<string, string> { { "X-toto", "TaTa" } });
var request = new RequestMessage(new Uri("http://localhost/foo"), "PUT", body, bodyAsString, Encoding.UTF8, new Dictionary<string, string> { { "X-toto", "TaTa" } });
// then
var requestMatchResult = new RequestMatchResult();
@@ -266,7 +266,7 @@ namespace WireMock.Net.Tests
var spec = Request.Create().UsingAnyVerb().WithCookie("session", "a*");
// when
var request = new RequestMessage(new Uri("http://localhost/foo"), "PUT", null, null, null, new Dictionary<string, string> { { "session", "abc" } });
var request = new RequestMessage(new Uri("http://localhost/foo"), "PUT", null, null, null, null, new Dictionary<string, string> { { "session", "abc" } });
// then
var requestMatchResult = new RequestMatchResult();
@@ -282,7 +282,7 @@ namespace WireMock.Net.Tests
// when
string bodyAsString = "Hello world!";
byte[] body = Encoding.UTF8.GetBytes(bodyAsString);
var request = new RequestMessage(new Uri("http://localhost/foo"), "PUT", body, bodyAsString);
var request = new RequestMessage(new Uri("http://localhost/foo"), "PUT", body, bodyAsString, Encoding.UTF8);
// then
var requestMatchResult = new RequestMatchResult();
@@ -298,7 +298,7 @@ namespace WireMock.Net.Tests
// when
string bodyAsString = "cat";
byte[] body = Encoding.UTF8.GetBytes(bodyAsString);
var request = new RequestMessage(new Uri("http://localhost/foo"), "POST", body, bodyAsString);
var request = new RequestMessage(new Uri("http://localhost/foo"), "POST", body, bodyAsString, Encoding.UTF8);
// then
var requestMatchResult = new RequestMatchResult();
@@ -314,7 +314,7 @@ namespace WireMock.Net.Tests
// when
string bodyAsString = "cat";
byte[] body = Encoding.UTF8.GetBytes(bodyAsString);
var request = new RequestMessage(new Uri("http://localhost/foo"), "POST", body, bodyAsString);
var request = new RequestMessage(new Uri("http://localhost/foo"), "POST", body, bodyAsString, Encoding.UTF8);
// then
var requestMatchResult = new RequestMatchResult();
@@ -330,27 +330,43 @@ namespace WireMock.Net.Tests
// when
string bodyAsString = "caR";
byte[] body = Encoding.UTF8.GetBytes(bodyAsString);
var request = new RequestMessage(new Uri("http://localhost/foo"), "POST", body, bodyAsString);
var request = new RequestMessage(new Uri("http://localhost/foo"), "POST", body, bodyAsString, Encoding.UTF8);
// then
var requestMatchResult = new RequestMatchResult();
Check.That(requestBuilder.GetMatchingScore(request, requestMatchResult)).IsLessThan(1.0);
Check.That(requestBuilder.GetMatchingScore(request, requestMatchResult)).IsStrictlyLessThan(1.0);
}
[Test]
public void Should_specify_requests_matching_given_body_using_SimMetricsMatcher()
public void Should_specify_requests_matching_given_body_using_SimMetricsMatcher1()
{
// given
var requestBuilder = Request.Create().UsingAnyVerb().WithBody("The cat walks in the street.");
var requestBuilder = Request.Create().UsingAnyVerb().WithBody(new SimMetricsMatcher("The cat walks in the street."));
// when
string bodyAsString = "The car drives in the street.";
byte[] body = Encoding.UTF8.GetBytes(bodyAsString);
var request = new RequestMessage(new Uri("http://localhost/foo"), "POST", body, bodyAsString);
var request = new RequestMessage(new Uri("http://localhost/foo"), "POST", body, bodyAsString, Encoding.UTF8);
// then
var requestMatchResult = new RequestMatchResult();
Check.That(requestBuilder.GetMatchingScore(request, requestMatchResult)).IsLessThan(1.0).And.IsGreaterThan(0.5);
Check.That(requestBuilder.GetMatchingScore(request, requestMatchResult)).IsStrictlyLessThan(1.0).And.IsStrictlyGreaterThan(0.5);
}
[Test]
public void Should_specify_requests_matching_given_body_using_SimMetricsMatcher2()
{
// given
var requestBuilder = Request.Create().UsingAnyVerb().WithBody(new SimMetricsMatcher("The cat walks in the street."));
// when
string bodyAsString = "Hello";
byte[] body = Encoding.UTF8.GetBytes(bodyAsString);
var request = new RequestMessage(new Uri("http://localhost/foo"), "POST", body, bodyAsString, Encoding.UTF8);
// then
var requestMatchResult = new RequestMatchResult();
Check.That(requestBuilder.GetMatchingScore(request, requestMatchResult)).IsStrictlyLessThan(0.1).And.IsStrictlyGreaterThan(0.05);
}
[Test]
@@ -362,7 +378,7 @@ namespace WireMock.Net.Tests
// when
string bodyAsString = "Hello world!";
byte[] body = Encoding.UTF8.GetBytes(bodyAsString);
var request = new RequestMessage(new Uri("http://localhost/foo"), "PUT", body, bodyAsString, new Dictionary<string, string> { { "X-toto", "tatata" } });
var request = new RequestMessage(new Uri("http://localhost/foo"), "PUT", body, bodyAsString, Encoding.UTF8, new Dictionary<string, string> { { "X-toto", "tatata" } });
// then
var requestMatchResult = new RequestMatchResult();
@@ -378,7 +394,7 @@ namespace WireMock.Net.Tests
// when
string bodyAsString = "Hello world!";
byte[] body = Encoding.UTF8.GetBytes(bodyAsString);
var request = new RequestMessage(new Uri("http://localhost/foo"), "PUT", body, bodyAsString);
var request = new RequestMessage(new Uri("http://localhost/foo"), "PUT", body, bodyAsString, Encoding.UTF8);
// then
var requestMatchResult = new RequestMatchResult();
@@ -399,7 +415,7 @@ namespace WireMock.Net.Tests
<todo-item id='a3'>xyz</todo-item>
</todo-list>";
byte[] body = Encoding.UTF8.GetBytes(xmlBodyAsString);
var request = new RequestMessage(new Uri("http://localhost/foo"), "PUT", body, xmlBodyAsString);
var request = new RequestMessage(new Uri("http://localhost/foo"), "PUT", body, xmlBodyAsString, Encoding.UTF8);
// then
var requestMatchResult = new RequestMatchResult();
@@ -420,7 +436,7 @@ namespace WireMock.Net.Tests
<todo-item id='a3'>xyz</todo-item>
</todo-list>";
byte[] body = Encoding.UTF8.GetBytes(xmlBodyAsString);
var request = new RequestMessage(new Uri("http://localhost/foo"), "PUT", body, xmlBodyAsString);
var request = new RequestMessage(new Uri("http://localhost/foo"), "PUT", body, xmlBodyAsString, Encoding.UTF8);
// then
var requestMatchResult = new RequestMatchResult();
@@ -436,7 +452,7 @@ namespace WireMock.Net.Tests
// when
string bodyAsString = "{ \"things\": [ { \"name\": \"RequiredThing\" }, { \"name\": \"Wiremock\" } ] }";
byte[] body = Encoding.UTF8.GetBytes(bodyAsString);
var request = new RequestMessage(new Uri("http://localhost/foo"), "PUT", body, bodyAsString);
var request = new RequestMessage(new Uri("http://localhost/foo"), "PUT", body, bodyAsString, Encoding.UTF8);
// then
var requestMatchResult = new RequestMatchResult();
@@ -452,7 +468,7 @@ namespace WireMock.Net.Tests
// when
string bodyAsString = "{ \"things\": { \"name\": \"Wiremock\" } }";
byte[] body = Encoding.UTF8.GetBytes(bodyAsString);
var request = new RequestMessage(new Uri("http://localhost/foo"), "PUT", body, bodyAsString);
var request = new RequestMessage(new Uri("http://localhost/foo"), "PUT", body, bodyAsString, Encoding.UTF8);
// then
var requestMatchResult = new RequestMatchResult();
@@ -468,7 +484,7 @@ namespace WireMock.Net.Tests
// when
string bodyAsString = "xxx";
byte[] body = Encoding.UTF8.GetBytes(bodyAsString);
var request = new RequestMessage(new Uri("http://localhost/foo"), "PUT", body, bodyAsString, new Dictionary<string, string> { { "X-toto", "tatata" } });
var request = new RequestMessage(new Uri("http://localhost/foo"), "PUT", body, bodyAsString, Encoding.UTF8, new Dictionary<string, string> { { "X-toto", "tatata" } });
// then
var requestMatchResult = new RequestMatchResult();

View File

@@ -17,7 +17,7 @@ namespace WireMock.Net.Tests
// given
string bodyAsString = "abc";
byte[] body = Encoding.UTF8.GetBytes(bodyAsString);
var request = new RequestMessage(new Uri("http://localhost/foo"), "POST", body, bodyAsString);
var request = new RequestMessage(new Uri("http://localhost/foo"), "POST", body, bodyAsString, Encoding.UTF8);
var response = Response.Create()
.WithBody("test {{request.url}} {{request.path}} {{request.method}}")
@@ -36,7 +36,7 @@ namespace WireMock.Net.Tests
// given
string bodyAsString = "abc";
byte[] body = Encoding.UTF8.GetBytes(bodyAsString);
var request = new RequestMessage(new Uri("http://localhost/foo?a=1&a=2&b=5"), "POST", body, bodyAsString);
var request = new RequestMessage(new Uri("http://localhost/foo?a=1&a=2&b=5"), "POST", body, bodyAsString, Encoding.UTF8);
var response = Response.Create()
.WithBody("test keya={{request.query.a}} idx={{request.query.a.[0]}} idx={{request.query.a.[1]}} keyb={{request.query.b}}")
@@ -55,7 +55,7 @@ namespace WireMock.Net.Tests
// given
string bodyAsString = "abc";
byte[] body = Encoding.UTF8.GetBytes(bodyAsString);
var request = new RequestMessage(new Uri("http://localhost/foo"), "POST", body, bodyAsString, new Dictionary<string, string> { { "Content-Type", "text/plain" } });
var request = new RequestMessage(new Uri("http://localhost/foo"), "POST", body, bodyAsString, Encoding.UTF8, new Dictionary<string, string> { { "Content-Type", "text/plain" } });
var response = Response.Create().WithHeader("x", "{{request.headers.Content-Type}}").WithBody("test").WithTransformer();
@@ -66,5 +66,41 @@ namespace WireMock.Net.Tests
Check.That(responseMessage.Body).Equals("test");
Check.That(responseMessage.Headers).Contains(new KeyValuePair<string,string>("x", "text/plain"));
}
[Test]
public async Task Response_ProvideResponse_Encoding_Body()
{
// given
string bodyAsString = "abc";
byte[] body = Encoding.UTF8.GetBytes(bodyAsString);
var request = new RequestMessage(new Uri("http://localhost/foo"), "POST", body, bodyAsString, Encoding.UTF8);
var response = Response.Create().WithBody("test", Encoding.ASCII);
// act
var responseMessage = await response.ProvideResponse(request);
// then
Check.That(responseMessage.Body).Equals("test");
Check.That(responseMessage.BodyEncoding).Equals(Encoding.ASCII);
}
[Test]
public async Task Response_ProvideResponse_Encoding_JsonBody()
{
// given
string bodyAsString = "abc";
byte[] body = Encoding.UTF8.GetBytes(bodyAsString);
var request = new RequestMessage(new Uri("http://localhost/foo"), "POST", body, bodyAsString, Encoding.UTF8);
var response = Response.Create().WithBodyAsJson(new { value = "test" }, Encoding.ASCII);
// act
var responseMessage = await response.ProvideResponse(request);
// then
Check.That(responseMessage.Body).Equals("{\"value\":\"test\"}");
Check.That(responseMessage.BodyEncoding).Equals(Encoding.ASCII);
}
}
}

View File

@@ -0,0 +1,21 @@
<?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>31dc2ef8-c3fe-467d-84be-fb5d956e612e</ProjectGuid>
<RootNamespace>WireMock.Net.Tests</RootNamespace>
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">.\obj</BaseIntermediateOutputPath>
<OutputPath Condition="'$(OutputPath)'=='' ">.\bin\</OutputPath>
</PropertyGroup>
<PropertyGroup>
<SchemaVersion>2.0</SchemaVersion>
</PropertyGroup>
<ItemGroup>
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
</ItemGroup>
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>

View File

@@ -0,0 +1,50 @@
{
"Guid": "00000002-ee28-4f29-ae63-1ac9b0802d86",
"Priority": 0,
"Request": {
"Path": {
"Matchers": [
{
"Name": "ExactMatcher",
"Pattern": "/dbs"
}
]
},
"Methods": [
"post"
],
"Body": {
"Matcher": {
"Name": "WildcardMatcher",
"Pattern": "*db-abc*"
}
}
},
"Response": {
"StatusCode": 200,
"Body": "{\"_rid\":\"\",\"Databases\":[{\"id\":\"db-abc\",\"_rid\":\"hBYWAA==\",\"_self\":\"dbs/hBYWAA==/\",\"_etag\":\"\\\"00008e00-0000-0000-0000-58b94f910000\\\"\",\"_colls\":\"colls/\",\"_users\":\"users/\",\"_ts\":1488539514}],\"_count\":1}",
"BodyEncoding": {
"CodePage": 65001,
"EncodingName": "Unicode (UTF-8)",
"WebName": "utf-8"
},
"UseTransformer": false,
"Headers": {
"x-ms-schemaversion": "1.3",
"Date": "Mon, 06 Mar 2017 10:56:43 GMT",
"x-ms-activity-id": "5e39429d-7fcc-4b75-93d6-519b7c582772",
"x-ms-item-count": "1",
"x-ms-resource-usage": "databases=1;",
"x-ms-gatewayversion": "version=1.11.164.3",
"x-ms-xp-role": "2",
"x-ms-session-token": "0:25908",
"x-ms-serviceversion": "version=1.11.150.2",
"Strict-Transport-Security": "max-age=31536000",
"Server": "Microsoft-HTTPAPI/2.0",
"x-ms-last-state-change-utc": "Fri, 24 Feb 2017 11:35:16.053 GMT",
"Content-Type": "application/json",
"x-ms-request-charge": "4.68",
"x-ms-resource-quota": "databases=100;"
}
}
}

View File

@@ -0,0 +1,42 @@
{
"Guid": "04ee4872-9efd-4770-90d3-88d445265d0d",
"Title": "documentdb_root_title",
"Priority": 0,
"Request": {
"Path": {
"Matchers": [
{
"Name": "ExactMatcher",
"Pattern": "/"
}
]
},
"Methods": [
"get"
],
"Body": {}
},
"Response": {
"StatusCode": 200,
"Body": "{\"_self\":\"\",\"id\":\"abc\",\"_rid\":\"abc.documents.azure.com\",\"media\":\"//media/\",\"addresses\":\"//addresses/\",\"_dbs\":\"//dbs/\",\"writableLocations\":[{\"name\":\"West Europe\",\"databaseAccountEndpoint\":\"http://localhost:9090/\"}],\"readableLocations\":[{\"name\":\"West Europe\",\"databaseAccountEndpoint\":\"http://localhost:9090/\"}],\"userReplicationPolicy\":{\"asyncReplication\":false,\"minReplicaSetSize\":3,\"maxReplicasetSize\":4},\"userConsistencyPolicy\":{\"defaultConsistencyLevel\":\"Session\"},\"systemReplicationPolicy\":{\"minReplicaSetSize\":3,\"maxReplicasetSize\":4},\"readPolicy\":{\"primaryReadCoefficient\":1,\"secondaryReadCoefficient\":1},\"queryEngineConfiguration\":\"{\\\"maxSqlQueryInputLength\\\":30720,\\\"maxJoinsPerSqlQuery\\\":5,\\\"maxLogicalAndPerSqlQuery\\\":500,\\\"maxLogicalOrPerSqlQuery\\\":500,\\\"maxUdfRefPerSqlQuery\\\":2,\\\"maxInExpressionItemsCount\\\":8000,\\\"queryMaxInMemorySortDocumentCount\\\":500,\\\"maxQueryRequestTimeoutFraction\\\":0.9,\\\"sqlAllowNonFiniteNumbers\\\":false,\\\"sqlAllowAggregateFunctions\\\":true,\\\"sqlAllowSubQuery\\\":false,\\\"allowNewKeywords\\\":true,\\\"sqlAllowLike\\\":false,\\\"maxSpatialQueryCells\\\":12,\\\"spatialMaxGeometryPointCount\\\":256,\\\"sqlAllowTop\\\":true,\\\"enableSpatialIndexing\\\":true}\"}",
"BodyEncoding": {
"CodePage": 65001,
"EncodingName": "Unicode (UTF-8)",
"WebName": "utf-8"
},
"UseTransformer": false,
"Headers": {
"x-ms-databaseaccount-reserved-mb": "0",
"x-ms-databaseaccount-consumed-mb": "0",
"Strict-Transport-Security": "max-age=31536000",
"x-ms-max-media-storage-usage-mb": "2048",
"x-ms-gatewayversion": "version=1.11.164.3",
"x-ms-media-storage-usage-mb": "0",
"x-ms-databaseaccount-provisioned-mb": "0",
"Content-Location": "http://localhost:9090/",
"Date": "Mon, 06 Mar 2017 10:56:40 GMT",
"Content-Type": "application/json",
"Server": "Microsoft-HTTPAPI/2.0"
}
}
}

View File

@@ -0,0 +1,28 @@
{
"version": "1.0.0-*",
"authors": [ "Stef Heyenrath" ],
"buildOptions": { "debugType": "portable" },
"dependencies": {
"Microsoft.Extensions.Testing.Abstractions": "1.0.0-preview2-003121",
"Moq": "4.7.1",
"NUnit": "3.6.1",
"dotnet-test-nunit": "3.4.0-beta-3",
"NFluent": "2.0.0-alpha",
"SimMetrics.Net": "1.0.1.0",
"WireMock.Net": { "target": "project" }
},
"frameworks": {
"net452": {
"frameworkAssemblies": {
"System.Net.Http": { "type": "build" }
},
"dependencies": {
}
}
},
"testRunner": "nunit"
}