Compare commits

..

13 Commits

Author SHA1 Message Date
Stef Heyenrath
1a184ebfdf ChangeLog 2018-08-14 18:58:06 +02:00
Stef Heyenrath
4b91c05fe7 Add IFileSystemHandler to support Azure for StaticMapping location (#180)
* wip

* CustomStaticMappingFileHandler

* Add unit-tests

* Tests

* IFileSystemHandler

* version
2018-08-14 18:54:53 +02:00
Stef Heyenrath
c92e733ef9 1.0.4.9 2018-08-08 11:42:24 +02:00
Stef Heyenrath
d9fde9329a Merge branch 'stef_fix_174_jsonpathmatcher' 2018-08-08 11:37:08 +02:00
Stef Heyenrath
9e7d3b6d2d #177 2018-08-08 11:05:27 +02:00
Stef Heyenrath
5ee25fb1e7 #175 2018-08-08 08:20:52 +02:00
Stef Heyenrath
36866d9fc3 #176 2018-08-07 20:34:35 +02:00
Stef Heyenrath
4f7259d27a Fix JsonMatcher and JsonPathMatcher 2018-08-07 19:27:10 +02:00
Stef Heyenrath
f13b829c00 Add unit-test 2018-08-02 16:11:14 +02:00
Stef Heyenrath
60d9487313 Update README.md 2018-07-27 12:54:45 +02:00
Stef Heyenrath
8cb15b2311 Add more details on Using and move HTTP SSL 2018-07-26 13:39:40 +02:00
Stef Heyenrath
3907e83138 1.0.4.8 2018-07-23 17:34:25 +02:00
Stef Heyenrath
1f226f7361 Support json path in the response (#170)
* jsonpath in response

* Fix tests

* Support also BodyAsJson

* Fix Sonar Issue

* Add example (zubinix2)

* Fix batch file

* Solve CodeFactor issues

* netcoreapp2.0;netcoreapp2.1

* 1.0.4.8
2018-07-23 17:28:32 +02:00
40 changed files with 1288 additions and 194 deletions

View File

@@ -1,3 +1,30 @@
# 1.0.4.10 (14 August 2018)
- [#180](https://github.com/WireMock-Net/WireMock.Net/pull/180) - Add IFileSystemHandler to support Azure for StaticMapping location contributed by Stef Heyenrath ([StefH](https://github.com/StefH))
- [#173](https://github.com/WireMock-Net/WireMock.Net/issues/173) - Feature: Mapping files lost when restarting an Azure app service +feature
Commits: 4b91c05fe7...4b91c05fe7
# 1.0.4.9 (08 August 2018)
- [#177](https://github.com/WireMock-Net/WireMock.Net/issues/177) - Feature: Skip invalid static mapping files +feature
- [#176](https://github.com/WireMock-Net/WireMock.Net/issues/176) - Question: Saving mapping with relative (not found) file fails
- [#175](https://github.com/WireMock-Net/WireMock.Net/issues/175) - Bug: Don't allow adding a mapping with no URL or PATH +fix
- [#174](https://github.com/WireMock-Net/WireMock.Net/issues/174) - Bug: JsonMatcher and JsonPathMatcher throws when posting byte[] +fix
- [#172](https://github.com/WireMock-Net/WireMock.Net/issues/172) - Question: Same/similar fluent interface for in process and admin client API
Commits: 8cb15b2311...d9fde9329a
# 1.0.4.8 (23 July 2018)
- [#170](https://github.com/WireMock-Net/WireMock.Net/pull/170) - Support json path in the response contributed by Stef Heyenrath ([StefH](https://github.com/StefH))
- [#167](https://github.com/WireMock-Net/WireMock.Net/issues/167) - Feature: Support for JsonPath in the response (with HandleBars) +feature
Commits: 1f226f7361...1f226f7361
# 1.0.4.7 (19 July 2018)
- [#169](https://github.com/WireMock-Net/WireMock.Net/pull/169) - Fix for Restricted Response headers contributed by Stef Heyenrath ([StefH](https://github.com/StefH)) +fix

View File

@@ -1,5 +1,5 @@
https://github.com/GitTools/GitReleaseNotes
GitReleaseNotes.exe . /OutputFile CHANGELOG.md /Version 1.0.4.7
GitReleaseNotes.exe . /OutputFile CHANGELOG.md /Version 1.0.4.10
GitReleaseNotes.exe . /OutputFile CHANGELOG.md /allTags

View File

@@ -1,69 +1,72 @@
# WireMock.Net
A C# .NET version based on [mock4net](https://github.com/alexvictoor/mock4net) which mimics the functionality from the JAVA based [WireMock.org](http://WireMock.org).
### Info
## Key Features
* HTTP response stubbing, matchable on URL/Path, headers, cookies and body content patterns
* Runs in unit tests, as a standalone process, as windows service, as Azure or IIS or as docker
* Configurable via a fluent DotNet API, JSON files and JSON over HTTP
* Record/playback of stubs
* Per-request conditional proxying
* Stateful behaviour simulation
* Configurable response delays
## Info
| | |
| --- | --- |
| **Chat** | [![Gitter](https://img.shields.io/gitter/room/wiremock_dotnet/Lobby.svg)](https://gitter.im/wiremock_dotnet/Lobby) |
| **Issues** | [![GitHub issues](https://img.shields.io/github/issues/WireMock-Net/WireMock.Net.svg)](https://github.com/WireMock-Net/WireMock.Net/issues) |
| **Build** | [![Build status](https://ci.appveyor.com/api/projects/status/b3n6q3ygbww4lyls?svg=true)](https://ci.appveyor.com/project/StefH/wiremock-net) |
| | |
| ***Sonar*** |   |
|  **Sonar Quality Gate** | [![Sonar Quality Gate](https://sonarcloud.io/api/project_badges/measure?project=wiremock&metric=alert_status)](https://sonarcloud.io/project/issues?id=wiremock) |
|  **Sonar Bugs** | [![Sonar Bugs](https://sonarcloud.io/api/project_badges/measure?project=wiremock&metric=bugs)](https://sonarcloud.io/project/issues?id=wiremock&resolved=false&types=BUG) |
|  **Sonar Code Smells** | [![Sonar Code Smells](https://sonarcloud.io/api/project_badges/measure?project=wiremock&metric=code_smells)](https://sonarcloud.io/project/issues?id=wiremock&resolved=false&types=CODE_SMELL) |
|  **Sonar Coverage** | [![Sonar Coverage](https://sonarcloud.io/api/project_badges/measure?project=wiremock&metric=coverage)](https://sonarcloud.io/component_measures?id=wiremock&metric=coverage) |
|  **Coveralls.io** | [![Coverage Status](https://coveralls.io/repos/github/WireMock-Net/WireMock.Net/badge.svg?branch=master)](https://coveralls.io/github/WireMock-Net/WireMock.Net?branch=master) |
| ***Project*** |   |
|   **Chat** | [![Gitter](https://img.shields.io/gitter/room/wiremock_dotnet/Lobby.svg)](https://gitter.im/wiremock_dotnet/Lobby) |
|   **Issues** | [![GitHub issues](https://img.shields.io/github/issues/WireMock-Net/WireMock.Net.svg)](https://github.com/WireMock-Net/WireMock.Net/issues) |
| | |
| ***Quality*** |   |
|   **Build** | [![Build status](https://ci.appveyor.com/api/projects/status/b3n6q3ygbww4lyls?svg=true)](https://ci.appveyor.com/project/StefH/wiremock-net) |
|   **CodeFactor** | [![CodeFactor](https://www.codefactor.io/repository/github/wiremock-net/wiremock.net/badge)](https://www.codefactor.io/repository/github/wiremock-net/wiremock.net)
|   **Sonar Quality Gate** | [![Sonar Quality Gate](https://sonarcloud.io/api/project_badges/measure?project=wiremock&metric=alert_status)](https://sonarcloud.io/project/issues?id=wiremock) |
|   **Sonar Bugs** | [![Sonar Bugs](https://sonarcloud.io/api/project_badges/measure?project=wiremock&metric=bugs)](https://sonarcloud.io/project/issues?id=wiremock&resolved=false&types=BUG) |
|   **Sonar Code Smells** | [![Sonar Code Smells](https://sonarcloud.io/api/project_badges/measure?project=wiremock&metric=code_smells)](https://sonarcloud.io/project/issues?id=wiremock&resolved=false&types=CODE_SMELL) |
|   **Sonar Coverage** | [![Sonar Coverage](https://sonarcloud.io/api/project_badges/measure?project=wiremock&metric=coverage)](https://sonarcloud.io/component_measures?id=wiremock&metric=coverage) |
|   **Coveralls** | [![Coverage Status](https://coveralls.io/repos/github/WireMock-Net/WireMock.Net/badge.svg?branch=master)](https://coveralls.io/github/WireMock-Net/WireMock.Net?branch=master) |
| |
| ***Nuget*** |   |
|  **WireMock.Net** | [![NuGet Badge WireMock.Net](https://buildstats.info/nuget/WireMock.Net)](https://www.nuget.org/packages/WireMock.Net) |
|  **WireMock.Net.StandAlone** | [![NuGet Badge WireMock.Net.StandAlone](https://buildstats.info/nuget/WireMock.Net.StandAlone)](https://www.nuget.org/packages/WireMock.Net.StandAlone) |
|   **WireMock.Net** | [![NuGet Badge WireMock.Net](https://buildstats.info/nuget/WireMock.Net)](https://www.nuget.org/packages/WireMock.Net) |
|   **WireMock.Net.StandAlone** | [![NuGet Badge WireMock.Net.StandAlone](https://buildstats.info/nuget/WireMock.Net.StandAlone)](https://www.nuget.org/packages/WireMock.Net.StandAlone) |
### Frameworks
The following frameworks are supported:
- net 4.5.2 and up
- net 4.6 and up
- netstandard 1.3
- netstandard 2.0
- net 4.5.2 and up & net 4.6 and up
- netstandard 1.3 & netstandard 2.0
## Build info
### Build info
To build you need:
- Microsoft .NET Framework 4.5.2 Developer Pack (https://www.microsoft.com/en-us/download/details.aspx?id=42637)
- Microsoft .NET Framework 4.6 Targeting Pack (https://www.microsoft.com/en-us/download/confirmation.aspx?id=48136)
- Microsoft .NET Framework 4.6.2 Developer Pack (https://www.microsoft.com/en-us/download/confirmation.aspx?id=53321)
- .NET Core 2.0 (https://www.microsoft.com/net/core)
## Stubbing
A core feature of WireMock.Net is the ability to return canned/predefined HTTP responses for requests matching criteria, see [Wiki : Stubbing & Request Matching](https://github.com/WireMock-Net/WireMock.Net/wiki/Stubbing-and-Request-Matching).
## Using WireMock.Net in UnitTest framework
You can use your favorite test framework and use WireMock within your tests, see
[Wiki : UnitTesting](https://github.com/StefH/WireMock.Net/wiki/Using-WireMock-in-UnitTests).
## Admin API Reference
The WireMock admin API provides functionality to define the mappings via a http interface, see [Wiki : Admin API Reference](https://github.com/StefH/WireMock.Net/wiki/Admin-API-Reference).
## WireMock.Net as a standalone process
This is quite straight forward to launch a mock server within a console application, see [Wiki : standalone](https://github.com/StefH/WireMock.Net/wiki/WireMock-as-a-standalone-process).
## Using
WireMock.Net can be used in several ways:
## WireMock.Net in a docker container
### UnitTesting
You can use your favorite test framework and use WireMock within your tests, see
[Wiki : UnitTesting](https://github.com/StefH/WireMock.Net/wiki/Using-WireMock-in-UnitTests).
### As standalone process / console application
This is quite straight forward to launch a mock server within a console application, see [Wiki : Standalone Process](https://github.com/StefH/WireMock.Net/wiki/WireMock-as-a-standalone-process).
### As a Windows Service
You can also run WireMock.Net as a Windows Service, follow this [WireMock-as-a-Windows-Service](https://github.com/WireMock-Net/WireMock.Net/wiki/WireMock-as-a-Windows-Service).
### As a Web Job in Azure or application in IIS
See this link [WireMock-as-a-(Azure)-Web-App](https://github.com/WireMock-Net/WireMock.Net/wiki/WireMock-as-a-(Azure)-Web-App)
### In a docker container
There is also a Linux and Windows-Nano container available at [hub.docker.com](https://hub.docker.com/r/sheyenrath).
For more details see also [WireMock.Net-docker](https://github.com/WireMock-Net/WireMock.Net-docker).
For more details see also [Docker](https://github.com/WireMock-Net/WireMock.Net-docker).
### SSL
You can start a standalone mock server listening for HTTPS requests. To do so, there is just a flag to set when creating the server:
```csharp
var server1 = FluentMockServer.Start(port: 8443, ssl: true);
// or like this
var server2 = FluentMockServer.Start(new FluentMockServerSettings
{
Urls = new[] { "http://localhost:9091", "https://localhost:9443" }
});
```
- In case when using **net 4.5.2** or **net 4.6**, you need a certificate registered on your box, properly associated with your application and the port number that will be used. This is not really specific to WireMock.Net, not very straightforward and hence the following stackoverflow thread might come handy: [Httplistener with https support](http://stackoverflow.com/questions/11403333/httplistener-with-https-support).
- When using **netstandard**, WireMock.Net uses a self signed certificate (which can be overriden if you like) to host https urls.
#### HTTPS / SSL
More details on using HTTPS (SSL) can be found here [Wiki : HTTPS](https://github.com/WireMock-Net/WireMock.Net/wiki/Using-HTTPS-(SSL))

View File

@@ -54,6 +54,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WireMock.Net.Service", "exa
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WireMock.Net.Console.HeadersTest", "examples\WireMock.Net.Console.HeadersTest\WireMock.Net.Console.HeadersTest.csproj", "{B70278E7-A2C6-4A3B-BBA9-1C873CA6F03C}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WireMock.Net.Console.NETCoreApp2", "examples\WireMock.Net.Console.NETCoreApp2\WireMock.Net.Console.NETCoreApp2.csproj", "{83645809-9E01-4E81-8733-BA9497554ABF}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -120,6 +122,10 @@ Global
{B70278E7-A2C6-4A3B-BBA9-1C873CA6F03C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B70278E7-A2C6-4A3B-BBA9-1C873CA6F03C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B70278E7-A2C6-4A3B-BBA9-1C873CA6F03C}.Release|Any CPU.Build.0 = Release|Any CPU
{83645809-9E01-4E81-8733-BA9497554ABF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{83645809-9E01-4E81-8733-BA9497554ABF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{83645809-9E01-4E81-8733-BA9497554ABF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{83645809-9E01-4E81-8733-BA9497554ABF}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -140,6 +146,7 @@ Global
{3C279524-DB73-4DE3-BEF1-F2B2958C9F65} = {F0C22C47-DF71-463C-9B04-B4E0F3B8708A}
{7F0B2446-0363-4720-AF46-F47F83B557DC} = {F0C22C47-DF71-463C-9B04-B4E0F3B8708A}
{B70278E7-A2C6-4A3B-BBA9-1C873CA6F03C} = {F0C22C47-DF71-463C-9B04-B4E0F3B8708A}
{83645809-9E01-4E81-8733-BA9497554ABF} = {F0C22C47-DF71-463C-9B04-B4E0F3B8708A}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {BF428BCC-C837-433B-87D2-15C7014B73E9}

View File

@@ -7,15 +7,12 @@
</PropertyGroup>
<ItemGroup>
<None Remove="__admin\mappings\826aff7c-6208-4a3c-923d-575248907db4.json" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\WireMock.Net.ConsoleApplication\CustomFileSystemFileHandler.cs" Link="CustomFileSystemFileHandler.cs" />
<Compile Include="..\WireMock.Net.ConsoleApplication\MainApp.cs" Link="MainApp.cs" />
</ItemGroup>
<ItemGroup>
<Content Include="__admin\mappings\11111110-a633-40e8-a244-5cb80bc0ab66.json">
<Content Include="__admin\mappings\*.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>

View File

@@ -3,14 +3,7 @@
"Title": "",
"Priority": 0,
"Request": {
"Path": {
"Matchers": [
{
"Name": "WildcardMatcher",
"Pattern": "/proxy-google-test-post"
}
]
},
"Path": "/proxy-google-test-post",
"Methods": [
"post"
],

View File

@@ -0,0 +1,19 @@
{
"Guid": "873d495f-940e-4b86-a1f4-4f0fc7be8b8b",
"Priority": 4,
"Request": {
"Path": {},
"Methods": [
"get"
]
},
"Response": {
"StatusCode": 200,
"BodyDestination": "SameAsSource",
"Body": "NO PATH OR URL",
"UseTransformer": false,
"Headers": {
"Content-Type": "application/json"
}
}
}

View File

@@ -0,0 +1,48 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.1</TargetFramework>
<ApplicationIcon>../../WireMock.Net-Logo.ico</ApplicationIcon>
<StartupObject>WireMock.Net.Console.NETCoreApp.Program</StartupObject>
</PropertyGroup>
<ItemGroup>
<Compile Include="..\WireMock.Net.ConsoleApplication\CustomFileSystemFileHandler.cs" Link="CustomFileSystemFileHandler.cs" />
<Compile Include="..\WireMock.Net.ConsoleApplication\MainApp.cs" Link="MainApp.cs" />
<Compile Include="..\WireMock.Net.Console.NETCoreApp\Program.cs" Link="Program.cs" />
</ItemGroup>
<ItemGroup>
<Content Include="__admin\mappings\*.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<None Include="..\WireMock.Net.Console.NETCoreApp\log4net.config" Link="log4net.config" />
<None Include="..\WireMock.Net.Console.NETCoreApp\nlog.config" Link="nlog.config" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\WireMock.Net\WireMock.Net.csproj" />
<PackageReference Include="log4net" Version="2.0.8" />
<PackageReference Include="Newtonsoft.Json" Version="10.0.3" />
</ItemGroup>
<ItemGroup>
<None Update="log4net.config">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="nlog.config">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="__admin\mappings\791a3f31-6946-4ce7-8e6f-0237c7443275.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="__admin\mappings\791a3f31-6946-4ce7-8e6f-0237c7443275.json">
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,22 @@
{
"Request": {
"Path": {
"Matchers": [
{
"Name": "WildcardMatcher",
"Pattern": "/static/mapping"
}
]
},
"Methods": [
"get"
]
},
"Response": {
"BodyAsJson": { "body": "static mapping" },
"Headers": {
"Content-Type": "application/json",
"Test-X": [ "test 1", "test 2" ]
}
}
}

View File

@@ -0,0 +1,29 @@
{
"Guid": "791a3f31-6946-4ce7-8e6f-0237c7443275",
"Title": "",
"Priority": 0,
"Request": {
"Path": "/proxy-google-test-post",
"Methods": [
"post"
],
"Body": {}
},
"Response": {
"StatusCode": 404,
"Body": "<!DOCTYPE html>\n<html lang=en>\n <meta charset=utf-8>\n <meta name=viewport content=\"initial-scale=1, minimum-scale=1, width=device-width\">\n <title>Error 404 (Not Found)!!1</title>\n <style>\n *{margin:0;padding:0}html,code{font:15px/22px arial,sans-serif}html{background:#fff;color:#222;padding:15px}body{margin:7% auto 0;max-width:390px;min-height:180px;padding:30px 0 15px}* > body{background:url(//www.google.com/images/errors/robot.png) 100% 5px no-repeat;padding-right:205px}p{margin:11px 0 22px;overflow:hidden}ins{color:#777;text-decoration:none}a img{border:0}@media screen and (max-width:772px){body{background:none;margin-top:0;max-width:none;padding-right:0}}#logo{background:url(//www.google.com/images/branding/googlelogo/1x/googlelogo_color_150x54dp.png) no-repeat;margin-left:-5px}@media only screen and (min-resolution:192dpi){#logo{background:url(//www.google.com/images/branding/googlelogo/2x/googlelogo_color_150x54dp.png) no-repeat 0% 0%/100% 100%;-moz-border-image:url(//www.google.com/images/branding/googlelogo/2x/googlelogo_color_150x54dp.png) 0}}@media only screen and (-webkit-min-device-pixel-ratio:2){#logo{background:url(//www.google.com/images/branding/googlelogo/2x/googlelogo_color_150x54dp.png) no-repeat;-webkit-background-size:100% 100%}}#logo{display:inline-block;height:54px;width:150px}\n </style>\n <a href=//www.google.com/><span id=logo aria-label=Google></span></a>\n <p><b>404.</b> <ins>Thats an error.</ins>\n <p>The requested URL <code>/proxy-google-test-post</code> was not found on this server. <ins>Thats all we know.</ins>\n",
"BodyAsBytes": "PCFET0NUWVBFIGh0bWw+CjxodG1sIGxhbmc9ZW4+CiAgPG1ldGEgY2hhcnNldD11dGYtOD4KICA8bWV0YSBuYW1lPXZpZXdwb3J0IGNvbnRlbnQ9ImluaXRpYWwtc2NhbGU9MSwgbWluaW11bS1zY2FsZT0xLCB3aWR0aD1kZXZpY2Utd2lkdGgiPgogIDx0aXRsZT5FcnJvciA0MDQgKE5vdCBGb3VuZCkhITE8L3RpdGxlPgogIDxzdHlsZT4KICAgICp7bWFyZ2luOjA7cGFkZGluZzowfWh0bWwsY29kZXtmb250OjE1cHgvMjJweCBhcmlhbCxzYW5zLXNlcmlmfWh0bWx7YmFja2dyb3VuZDojZmZmO2NvbG9yOiMyMjI7cGFkZGluZzoxNXB4fWJvZHl7bWFyZ2luOjclIGF1dG8gMDttYXgtd2lkdGg6MzkwcHg7bWluLWhlaWdodDoxODBweDtwYWRkaW5nOjMwcHggMCAxNXB4fSogPiBib2R5e2JhY2tncm91bmQ6dXJsKC8vd3d3Lmdvb2dsZS5jb20vaW1hZ2VzL2Vycm9ycy9yb2JvdC5wbmcpIDEwMCUgNXB4IG5vLXJlcGVhdDtwYWRkaW5nLXJpZ2h0OjIwNXB4fXB7bWFyZ2luOjExcHggMCAyMnB4O292ZXJmbG93OmhpZGRlbn1pbnN7Y29sb3I6Izc3Nzt0ZXh0LWRlY29yYXRpb246bm9uZX1hIGltZ3tib3JkZXI6MH1AbWVkaWEgc2NyZWVuIGFuZCAobWF4LXdpZHRoOjc3MnB4KXtib2R5e2JhY2tncm91bmQ6bm9uZTttYXJnaW4tdG9wOjA7bWF4LXdpZHRoOm5vbmU7cGFkZGluZy1yaWdodDowfX0jbG9nb3tiYWNrZ3JvdW5kOnVybCgvL3d3dy5nb29nbGUuY29tL2ltYWdlcy9icmFuZGluZy9nb29nbGVsb2dvLzF4L2dvb2dsZWxvZ29fY29sb3JfMTUweDU0ZHAucG5nKSBuby1yZXBlYXQ7bWFyZ2luLWxlZnQ6LTVweH1AbWVkaWEgb25seSBzY3JlZW4gYW5kIChtaW4tcmVzb2x1dGlvbjoxOTJkcGkpeyNsb2dve2JhY2tncm91bmQ6dXJsKC8vd3d3Lmdvb2dsZS5jb20vaW1hZ2VzL2JyYW5kaW5nL2dvb2dsZWxvZ28vMngvZ29vZ2xlbG9nb19jb2xvcl8xNTB4NTRkcC5wbmcpIG5vLXJlcGVhdCAwJSAwJS8xMDAlIDEwMCU7LW1vei1ib3JkZXItaW1hZ2U6dXJsKC8vd3d3Lmdvb2dsZS5jb20vaW1hZ2VzL2JyYW5kaW5nL2dvb2dsZWxvZ28vMngvZ29vZ2xlbG9nb19jb2xvcl8xNTB4NTRkcC5wbmcpIDB9fUBtZWRpYSBvbmx5IHNjcmVlbiBhbmQgKC13ZWJraXQtbWluLWRldmljZS1waXhlbC1yYXRpbzoyKXsjbG9nb3tiYWNrZ3JvdW5kOnVybCgvL3d3dy5nb29nbGUuY29tL2ltYWdlcy9icmFuZGluZy9nb29nbGVsb2dvLzJ4L2dvb2dsZWxvZ29fY29sb3JfMTUweDU0ZHAucG5nKSBuby1yZXBlYXQ7LXdlYmtpdC1iYWNrZ3JvdW5kLXNpemU6MTAwJSAxMDAlfX0jbG9nb3tkaXNwbGF5OmlubGluZS1ibG9jaztoZWlnaHQ6NTRweDt3aWR0aDoxNTBweH0KICA8L3N0eWxlPgogIDxhIGhyZWY9Ly93d3cuZ29vZ2xlLmNvbS8+PHNwYW4gaWQ9bG9nbyBhcmlhLWxhYmVsPUdvb2dsZT48L3NwYW4+PC9hPgogIDxwPjxiPjQwNC48L2I+IDxpbnM+VGhhdOKAmXMgYW4gZXJyb3IuPC9pbnM+CiAgPHA+VGhlIHJlcXVlc3RlZCBVUkwgPGNvZGU+L3Byb3h5LWdvb2dsZS10ZXN0LXBvc3Q8L2NvZGU+IHdhcyBub3QgZm91bmQgb24gdGhpcyBzZXJ2ZXIuICA8aW5zPlRoYXTigJlzIGFsbCB3ZSBrbm93LjwvaW5zPgo=",
"BodyEncoding": {
"CodePage": 65001,
"EncodingName": "Unicode (UTF-8)",
"WebName": "utf-8"
},
"UseTransformer": false,
"Headers": {
"Date": "Wed, 27 Oct 2017 18:57:40 GMT",
"Alt-Svc": "quic=\":443\"; ma=2592000; v=\"39,38,37,35\"",
"Referrer-Policy": "no-referrer",
"Connection": "close"
}
}
}

View File

@@ -0,0 +1,19 @@
{
"Guid": "873d495f-940e-4b86-a1f4-4f0fc7be8b8b",
"Priority": 4,
"Request": {
"Path": {},
"Methods": [
"get"
]
},
"Response": {
"StatusCode": 200,
"BodyDestination": "SameAsSource",
"Body": "NO PATH OR URL",
"UseTransformer": false,
"Headers": {
"Content-Type": "application/json"
}
}
}

View File

@@ -0,0 +1,47 @@
using System.Collections.Generic;
using System.IO;
using WireMock.Handlers;
namespace WireMock.Net.ConsoleApplication
{
internal class CustomFileSystemFileHandler : IFileSystemHandler
{
private static readonly string AdminMappingsFolder = Path.Combine("__admin", "mappings");
/// <inheritdoc cref="IFileSystemHandler.FolderExists"/>
public bool FolderExists(string path)
{
return Directory.Exists(path);
}
/// <inheritdoc cref="IFileSystemHandler.CreateFolder"/>
public void CreateFolder(string path)
{
Directory.CreateDirectory(path);
}
/// <inheritdoc cref="IFileSystemHandler.EnumerateFiles"/>
public IEnumerable<string> EnumerateFiles(string path)
{
return Directory.EnumerateFiles(path);
}
/// <inheritdoc cref="IFileSystemHandler.GetMappingFolder"/>
public string GetMappingFolder()
{
return Path.Combine(@"c:\temp-wiremock", AdminMappingsFolder);
}
/// <inheritdoc cref="IFileSystemHandler.ReadMappingFile"/>
public string ReadMappingFile(string path)
{
return File.ReadAllText(path);
}
/// <inheritdoc cref="IFileSystemHandler.WriteMappingFile"/>
public void WriteMappingFile(string path, string text)
{
File.WriteAllText(path, text);
}
}
}

View File

@@ -30,7 +30,9 @@ namespace WireMock.Net.ConsoleApplication
//},
PreWireMockMiddlewareInit = app => { System.Console.WriteLine($"PreWireMockMiddlewareInit : {app.GetType()}"); },
PostWireMockMiddlewareInit = app => { System.Console.WriteLine($"PostWireMockMiddlewareInit : {app.GetType()}"); },
Logger = new WireMockConsoleLogger()
Logger = new WireMockConsoleLogger(),
FileSystemHandler = new CustomFileSystemFileHandler()
});
System.Console.WriteLine("FluentMockServer listening at {0}", string.Join(",", server.Urls));
@@ -38,6 +40,14 @@ namespace WireMock.Net.ConsoleApplication
server.AllowPartialMapping();
server
.Given(Request.Create().WithPath(p => p.Contains("x")).UsingGet())
.AtPriority(4)
.RespondWith(Response.Create()
.WithStatusCode(200)
.WithHeader("Content-Type", "application/json")
.WithBody(@"{ ""result"": ""Contains x with FUNC 200""}"));
server
.Given(Request.Create()
.UsingGet()
@@ -53,7 +63,7 @@ namespace WireMock.Net.ConsoleApplication
.WithPath("/proxy-execute-keep-alive")
)
.RespondWith(Response.Create()
.WithProxy(new ProxyAndRecordSettings { Url = "http://localhost:9999", BlackListedHeaders = new [] { "Keep-Alive" } })
.WithProxy(new ProxyAndRecordSettings { Url = "http://localhost:9999", BlackListedHeaders = new[] { "Keep-Alive" } })
.WithHeader("Keep-Alive-Test", "stef")
);
@@ -144,8 +154,10 @@ namespace WireMock.Net.ConsoleApplication
server
.Given(Request.Create().WithPath("/file_rel").UsingGet())
.WithGuid("0000aaaa-fcf4-4256-a0d3-1c76e4862947")
.RespondWith(Response.Create()
.WithBodyFromFile("Program.cs", false)
.WithHeader("Content-Type", "application/xml")
.WithBodyFromFile("WireMock.Net.xml", false)
);
server
@@ -176,14 +188,6 @@ namespace WireMock.Net.ConsoleApplication
.WithStatusCode(200)
.WithBody("hi"));
server
.Given(Request.Create().WithPath(p => p.Contains("x")).UsingGet())
.AtPriority(4)
.RespondWith(Response.Create()
.WithStatusCode(200)
.WithHeader("Content-Type", "application/json")
.WithBody(@"{ ""result"": ""Contains x with FUNC 200""}"));
server
.Given(Request.Create().WithPath("/data").UsingPost().WithBody(b => b.Contains("e")))
.AtPriority(999)
@@ -256,6 +260,46 @@ namespace WireMock.Net.ConsoleApplication
.WithDelay(TimeSpan.FromMilliseconds(100))
);
server
.Given(Request.Create().WithPath("/jsonpathtestToken").UsingPost())
.RespondWith(Response.Create()
.WithHeader("Content-Type", "application/json")
.WithBody("{{JsonPath.SelectToken request.body \"$.Manufacturers[?(@.Name == 'Acme Co')]\"}}")
.WithTransformer()
);
server
.Given(Request.Create().WithPath("/zubinix").UsingPost())
.RespondWith(Response.Create()
.WithHeader("Content-Type", "application/json")
.WithBody("{ \"result\": \"{{JsonPath.SelectToken request.bodyAsJson \"username\"}}\" }")
.WithTransformer()
);
server
.Given(Request.Create().WithPath("/zubinix2").UsingPost())
.RespondWith(Response.Create()
.WithHeader("Content-Type", "application/json")
.WithBodyAsJson(new { path = "{{request.path}}", result = "{{JsonPath.SelectToken request.bodyAsJson \"username\"}}" })
.WithTransformer()
);
server
.Given(Request.Create().WithPath("/jsonpathtestTokenJson").UsingPost())
.RespondWith(Response.Create()
.WithHeader("Content-Type", "application/json")
.WithBodyAsJson(new { status = "OK", url = "{{request.url}}", transformed = "{{JsonPath.SelectToken request.body \"$.Manufacturers[?(@.Name == 'Acme Co')]\"}}" })
.WithTransformer()
);
server
.Given(Request.Create().WithPath("/jsonpathtestTokens").UsingPost())
.RespondWith(Response.Create()
.WithHeader("Content-Type", "application/json")
.WithBody("[{{#JsonPath.SelectTokens request.body \"$..Products[?(@.Price >= 50)].Name\"}} { \"idx\":{{id}}, \"value\":\"{{value}}\" }, {{/JsonPath.SelectTokens}} {} ]")
.WithTransformer()
);
server
.Given(Request.Create()
.WithPath("/state1")

View File

@@ -54,6 +54,7 @@
<Reference Include="System.XML" />
</ItemGroup>
<ItemGroup>
<Compile Include="CustomFileSystemFileHandler.cs" />
<Compile Include="MainApp.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
@@ -71,6 +72,9 @@
<Content Include="__admin\mappings\11111110-a633-40e8-a244-5cb80bc0ab66.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<None Include="__admin\mappings\873d495f-940e-4b86-a1f4-4f0fc7be8b8b.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\WireMock.Net.StandAlone\WireMock.Net.StandAlone.csproj">

View File

@@ -0,0 +1,19 @@
{
"Guid": "873d495f-940e-4b86-a1f4-4f0fc7be8b8b",
"Priority": 4,
"Request": {
"Path": {},
"Methods": [
"get"
]
},
"Response": {
"StatusCode": 200,
"BodyDestination": "SameAsSource",
"Body": "NO PATH OR URL",
"UseTransformer": false,
"Headers": {
"Content-Type": "application/json"
}
}
}

View File

@@ -1,5 +1,5 @@
@echo off
call uninstall.bat
call Service-Uninstall.bat
SET mypath=%~dp0
SET targetpath=C:\Services\WireMock.Net.Service\

View File

@@ -1,19 +1,25 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp2.0</TargetFramework>
<TargetFrameworks>netcoreapp2.0;netcoreapp2.1</TargetFrameworks>
<RuntimeIdentifiers>win10-x64</RuntimeIdentifiers>
<StartupObject>WireMock.Net.WebApplication.Program</StartupObject>
<AssemblyName>WireMock.Net.WebApplication</AssemblyName>
<RootNamespace>WireMock.Net.WebApplication</RootNamespace>
</PropertyGroup>
<ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'netcoreapp2.0'">
<DotNetCliToolReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Tools" Version="2.0.2" />
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.8" />
<PackageReference Include="Newtonsoft.Json" Version="10.0.3" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'netcoreapp2.1'">
<DotNetCliToolReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Tools" Version="2.0.4" />
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.1.2" />
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\WireMock.Net.StandAlone\WireMock.Net.StandAlone.csproj" />
<ProjectReference Include="..\..\src\WireMock.Net\WireMock.Net.csproj" />

View File

@@ -3,7 +3,7 @@
<PropertyGroup>
<Description>Lightweight StandAlone Http Mocking Server for .Net.</Description>
<AssemblyTitle>WireMock.Net.StandAlone</AssemblyTitle>
<Version>1.0.4.7</Version>
<Version>1.0.4.10</Version>
<Authors>Stef Heyenrath</Authors>
<TargetFrameworks>net452;net46;netstandard1.3;netstandard2.0</TargetFrameworks>
<GenerateDocumentationFile>true</GenerateDocumentationFile>

View File

@@ -0,0 +1,49 @@
using System.Collections.Generic;
namespace WireMock.Handlers
{
/// <summary>
/// Handler to interact with the file system to handle folders and read and write static mapping files.
/// </summary>
public interface IFileSystemHandler
{
/// <summary>
/// Gets the folder where the static mappings are located. For local file system, this would be `{CurrentFolder}/__admin/mappings`.
/// </summary>
/// <returns>The foldername.</returns>
string GetMappingFolder();
/// <summary>
/// Determines whether the given path refers to an existing directory on disk.
/// </summary>
/// <param name="path">The path.</param>
/// <returns>true if path refers to an existing directory; false if the directory does not exist or an error occurs when trying to determine if the specified directory exists.</returns>
bool FolderExists(string path);
/// <summary>
/// Creates all directories and subdirectories in the specified path unless they already exist.
/// </summary>
/// <param name="path">The path.</param>
void CreateFolder(string path);
/// <summary>
/// Returns an enumerable collection of file names in a specified path.
/// </summary>
/// <param name="path">The path.</param>
/// <returns>An enumerable collection of the full names (including paths) for the files in the directory specified by path.</returns>
IEnumerable<string> EnumerateFiles(string path);
/// <summary>
/// Read a static mapping file as text.
/// </summary>
/// <param name="path">The path (folder + filename with .json extension).</param>
string ReadMappingFile(string path);
/// <summary>
/// Write the static mapping.
/// </summary>
/// <param name="path">The path (folder + filename with .json extension).</param>
/// <param name="text">The text.</param>
void WriteMappingFile(string path, string text);
}
}

View File

@@ -0,0 +1,49 @@
using System.Collections.Generic;
using System.IO;
namespace WireMock.Handlers
{
/// <summary>
/// Default implementation for a handler to interact with the local file system to read and write static mapping files.
/// </summary>
public class LocalFileSystemHandler : IFileSystemHandler
{
private static readonly string AdminMappingsFolder = Path.Combine("__admin", "mappings");
/// <inheritdoc cref="IFileSystemHandler.FolderExists"/>
public bool FolderExists(string path)
{
return Directory.Exists(path);
}
/// <inheritdoc cref="IFileSystemHandler.CreateFolder"/>
public void CreateFolder(string path)
{
Directory.CreateDirectory(path);
}
/// <inheritdoc cref="IFileSystemHandler.EnumerateFiles"/>
public IEnumerable<string> EnumerateFiles(string path)
{
return Directory.EnumerateFiles(path);
}
/// <inheritdoc cref="IFileSystemHandler.GetMappingFolder"/>
public string GetMappingFolder()
{
return Path.Combine(Directory.GetCurrentDirectory(), AdminMappingsFolder);
}
/// <inheritdoc cref="IFileSystemHandler.ReadMappingFile"/>
public string ReadMappingFile(string path)
{
return File.ReadAllText(path);
}
/// <inheritdoc cref="IFileSystemHandler.WriteMappingFile"/>
public void WriteMappingFile(string path, string text)
{
File.WriteAllText(path, text);
}
}
}

View File

@@ -63,7 +63,9 @@ namespace WireMock.Matchers
public double IsMatch(object input)
{
double match = MatchScores.Mismatch;
if (input != null)
// When input is null or byte[], return Mismatch.
if (input != null && !(input is byte[]))
{
try
{

View File

@@ -65,7 +65,9 @@ namespace WireMock.Matchers
public double IsMatch(object input)
{
bool match = false;
if (input != null)
// When input is null or byte[], return Mismatch.
if (input != null && !(input is byte[]))
{
try
{
@@ -73,10 +75,21 @@ namespace WireMock.Matchers
JToken jtokenInput = input is JToken tokenInput ? tokenInput : JObject.FromObject(input);
// Check if JToken or string or object
JToken jtokenValue =
Value is JToken tokenValue ? tokenValue :
Value is string stringValue ? JToken.Parse(stringValue) :
JObject.FromObject(input);
JToken jtokenValue;
switch (Value)
{
case JToken tokenValue:
jtokenValue = tokenValue;
break;
case string stringValue:
jtokenValue = JToken.Parse(stringValue);
break;
default:
jtokenValue = JObject.FromObject(Value);
break;
}
match = JToken.DeepEquals(jtokenValue, jtokenInput);
}

View File

@@ -1,5 +1,4 @@
namespace WireMock.Matchers
namespace WireMock.Matchers
{
internal static class MatchBehaviourHelper
{

View File

@@ -76,6 +76,11 @@ namespace WireMock.Owin
#endif
)
{
if (responseMessage == null)
{
return;
}
response.StatusCode = responseMessage.StatusCode;
byte[] bytes = null;

View File

@@ -21,7 +21,7 @@ namespace WireMock.Serialization
string matcherName = parts[0];
string matcherType = parts.Length > 1 ? parts[1] : null;
string[] stringPatterns = matcher.Patterns != null ? matcher.Patterns.Cast<string>().ToArray() : new [] { matcher.Pattern as string };
string[] stringPatterns = matcher.Patterns != null ? matcher.Patterns.Cast<string>().ToArray() : new[] { matcher.Pattern as string };
MatchBehaviour matchBehaviour = matcher.RejectOnMatch == true ? MatchBehaviour.RejectOnMatch : MatchBehaviour.AcceptOnMatch;
switch (matcherName)
@@ -39,7 +39,7 @@ namespace WireMock.Serialization
return new JsonPathMatcher(matchBehaviour, stringPatterns);
case "XPathMatcher":
return new XPathMatcher(matchBehaviour, (string) matcher.Pattern);
return new XPathMatcher(matchBehaviour, (string)matcher.Pattern);
case "WildcardMatcher":
return new WildcardMatcher(matchBehaviour, stringPatterns, matcher.IgnoreCase == true);
@@ -51,7 +51,7 @@ namespace WireMock.Serialization
throw new NotSupportedException($"Matcher '{matcherName}' with Type '{matcherType}' is not supported.");
}
return new SimMetricsMatcher(matchBehaviour, (string) matcher.Pattern, type);
return new SimMetricsMatcher(matchBehaviour, (string)matcher.Pattern, type);
default:
throw new NotSupportedException($"Matcher '{matcherName}' is not supported.");

View File

@@ -30,12 +30,13 @@ namespace WireMock.Server
/// </summary>
public partial class FluentMockServer
{
private static readonly string AdminMappingsFolder = Path.Combine("__admin", "mappings");
private const string ContentTypeJson = "application/json";
private const string AdminMappings = "/__admin/mappings";
private const string AdminRequests = "/__admin/requests";
private const string AdminSettings = "/__admin/settings";
private const string AdminScenarios = "/__admin/scenarios";
private readonly RegexMatcher _adminMappingsGuidPathMatcher = new RegexMatcher(MatchBehaviour.AcceptOnMatch, @"^\/__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(MatchBehaviour.AcceptOnMatch, @"^\/__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})$");
@@ -100,44 +101,66 @@ namespace WireMock.Server
}
#endregion
#region StaticMappings
#region StaticMappings
/// <summary>
/// Saves the static mappings.
/// </summary>
/// <param name="folder">The optional folder. If not defined, use {CurrentFolder}/__admin/mappings</param>
[PublicAPI]
public void SaveStaticMappings([CanBeNull] string folder = null)
{
foreach (var mapping in Mappings.Where(m => !m.IsAdminInterface))
{
SaveMappingToFile(mapping, folder);
}
}
/// <summary>
/// Reads the static mappings from a folder.
/// </summary>
/// <param name="folder">The optional folder. If not defined, use \__admin\mappings\</param>
/// <param name="folder">The optional folder. If not defined, use {CurrentFolder}/__admin/mappings</param>
[PublicAPI]
public void ReadStaticMappings([CanBeNull] string folder = null)
{
if (folder == null)
{
folder = Path.Combine(Directory.GetCurrentDirectory(), AdminMappingsFolder);
folder = _fileSystemHandler.GetMappingFolder();
}
if (!Directory.Exists(folder))
if (!_fileSystemHandler.FolderExists(folder))
{
_logger.Info("The Static Mapping folder '{0}' does not exist, reading Static MappingFiles will be skipped.", folder);
return;
}
foreach (string filename in Directory.EnumerateFiles(folder).OrderBy(f => f))
foreach (string filename in _fileSystemHandler.EnumerateFiles(folder).OrderBy(f => f))
{
_logger.Info("Reading Static MappingFile : '{0}'", filename);
ReadStaticMappingAndAddOrUpdate(filename);
try
{
ReadStaticMappingAndAddOrUpdate(filename);
}
catch
{
_logger.Error("Static MappingFile : '{0}' could not be read. This file will be skipped.", filename);
}
}
}
/// <summary>
/// Watches the static mappings for changes.
/// </summary>
/// <param name="folder">The optional folder. If not defined, use \__admin\mappings\</param>
/// <param name="folder">The optional folder. If not defined, use {CurrentFolder}/__admin/mappings</param>
[PublicAPI]
public void WatchStaticMappings([CanBeNull] string folder = null)
{
if (folder == null)
{
folder = Path.Combine(Directory.GetCurrentDirectory(), AdminMappingsFolder);
folder = _fileSystemHandler.GetMappingFolder();
}
if (!Directory.Exists(folder))
if (!_fileSystemHandler.FolderExists(folder))
{
return;
}
@@ -184,7 +207,7 @@ namespace WireMock.Server
string filenameWithoutExtension = Path.GetFileNameWithoutExtension(path);
MappingModel mappingModel = JsonConvert.DeserializeObject<MappingModel>(FileHelper.ReadAllText(path));
MappingModel mappingModel = JsonConvert.DeserializeObject<MappingModel>(_fileSystemHandler.ReadMappingFile(path));
if (Guid.TryParse(filenameWithoutExtension, out Guid guidFromFilename))
{
DeserializeAndAddOrUpdateMapping(mappingModel, guidFromFilename, path);
@@ -316,9 +339,9 @@ namespace WireMock.Server
Guid guid = Guid.Parse(requestMessage.Path.TrimStart(AdminMappings.ToCharArray()));
var mappingModel = DeserializeObject<MappingModel>(requestMessage);
DeserializeAndAddOrUpdateMapping(mappingModel, guid);
Guid? guidFromPut = DeserializeAndAddOrUpdateMapping(mappingModel, guid);
return ResponseMessageBuilder.Create("Mapping added or updated", 200, guid);
return ResponseMessageBuilder.Create("Mapping added or updated", 200, guidFromPut);
}
private ResponseMessage MappingDelete(RequestMessage requestMessage)
@@ -337,29 +360,31 @@ namespace WireMock.Server
#region Mappings
private ResponseMessage MappingsSave(RequestMessage requestMessage)
{
foreach (var mapping in Mappings.Where(m => !m.IsAdminInterface))
{
SaveMappingToFile(mapping);
}
SaveStaticMappings();
return ResponseMessageBuilder.Create("Mappings saved to disk");
}
private void SaveMappingToFile(Mapping mapping)
private void SaveMappingToFile(Mapping mapping, string folder = null)
{
string folder = Path.Combine(Directory.GetCurrentDirectory(), AdminMappingsFolder);
if (!Directory.Exists(folder))
if (folder == null)
{
Directory.CreateDirectory(folder);
folder = _fileSystemHandler.GetMappingFolder();
}
if (!_fileSystemHandler.FolderExists(folder))
{
_fileSystemHandler.CreateFolder(folder);
}
var model = MappingConverter.ToMappingModel(mapping);
string filename = !string.IsNullOrEmpty(mapping.Title) ? SanitizeFileName(mapping.Title) : mapping.Guid.ToString();
string filename = (!string.IsNullOrEmpty(mapping.Title) ? SanitizeFileName(mapping.Title) : mapping.Guid.ToString()) + ".json";
string filePath = Path.Combine(folder, filename + ".json");
_logger.Info("Saving Mapping to file {0}", filePath);
string path = Path.Combine(folder, filename);
File.WriteAllText(filePath, JsonConvert.SerializeObject(model, _settings));
_logger.Info("Saving Mapping file {0}", filename);
_fileSystemHandler.WriteMappingFile(path, JsonConvert.SerializeObject(model, _settings));
}
private static string SanitizeFileName(string name, char replaceChar = '_')
@@ -401,13 +426,18 @@ namespace WireMock.Server
return ResponseMessageBuilder.Create("Mapping added", 201, guid);
}
private Guid DeserializeAndAddOrUpdateMapping(MappingModel mappingModel, Guid? guid = null, string path = null)
private Guid? DeserializeAndAddOrUpdateMapping(MappingModel mappingModel, Guid? guid = null, string path = null)
{
Check.NotNull(mappingModel, nameof(mappingModel));
Check.NotNull(mappingModel.Request, nameof(mappingModel.Request));
Check.NotNull(mappingModel.Response, nameof(mappingModel.Response));
var requestBuilder = InitRequestBuilder(mappingModel.Request);
var requestBuilder = InitRequestBuilder(mappingModel.Request, true);
if (requestBuilder == null)
{
return null;
}
var responseBuilder = InitResponseBuilder(mappingModel.Response);
var respondProvider = Given(requestBuilder);
@@ -511,7 +541,7 @@ namespace WireMock.Server
{
var requestModel = DeserializeObject<RequestModel>(requestMessage);
var request = (Request)InitRequestBuilder(requestModel);
var request = (Request)InitRequestBuilder(requestModel, false);
var dict = new Dictionary<LogEntry, RequestMatchResult>();
foreach (var logEntry in LogEntries.Where(le => !le.RequestMessage.Path.StartsWith("/__admin/")))
@@ -551,7 +581,7 @@ namespace WireMock.Server
}
#endregion
private IRequestBuilder InitRequestBuilder(RequestModel requestModel)
private IRequestBuilder InitRequestBuilder(RequestModel requestModel, bool pathOrUrlRequired)
{
IRequestBuilder requestBuilder = Request.Create();
@@ -571,11 +601,13 @@ namespace WireMock.Server
}
}
bool pathOrUrlmatchersValid = false;
if (requestModel.Path != null)
{
if (requestModel.Path is string path)
{
requestBuilder = requestBuilder.WithPath(path);
pathOrUrlmatchersValid = true;
}
else
{
@@ -583,15 +615,16 @@ namespace WireMock.Server
if (pathModel?.Matchers != null)
{
requestBuilder = requestBuilder.WithPath(pathModel.Matchers.Select(MatcherMapper.Map).Cast<IStringMatcher>().ToArray());
pathOrUrlmatchersValid = true;
}
}
}
if (requestModel.Url != null)
else if (requestModel.Url != null)
{
if (requestModel.Url is string url)
{
requestBuilder = requestBuilder.WithUrl(url);
pathOrUrlmatchersValid = true;
}
else
{
@@ -599,10 +632,17 @@ namespace WireMock.Server
if (urlModel?.Matchers != null)
{
requestBuilder = requestBuilder.WithUrl(urlModel.Matchers.Select(MatcherMapper.Map).Cast<IStringMatcher>().ToArray());
pathOrUrlmatchersValid = true;
}
}
}
if (pathOrUrlRequired && !pathOrUrlmatchersValid)
{
_logger.Error("Path or Url matcher is missing for this mapping, this mapping will not be added.");
return null;
}
if (requestModel.Methods != null)
{
requestBuilder = requestBuilder.UsingMethod(requestModel.Methods);
@@ -701,7 +741,6 @@ namespace WireMock.Server
{
responseBuilder = responseBuilder.WithBodyFromBase64(responseModel.BodyFromBase64, ToEncoding(responseModel.BodyEncoding));
}
else if (responseModel.BodyAsFile != null)
{
responseBuilder = responseBuilder.WithBodyFromFile(responseModel.BodyAsFile);

View File

@@ -1,11 +1,12 @@
using JetBrains.Annotations;
using Newtonsoft.Json;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using Newtonsoft.Json;
using WireMock.Handlers;
using WireMock.Http;
using WireMock.Logging;
using WireMock.Matchers;
@@ -24,6 +25,8 @@ namespace WireMock.Server
public partial class FluentMockServer : IDisposable
{
private readonly IWireMockLogger _logger;
private readonly IFileSystemHandler _fileSystemHandler;
private const int ServerStartDelay = 100;
private readonly IOwinSelfHost _httpServer;
private readonly WireMockMiddlewareOptions _options = new WireMockMiddlewareOptions();
@@ -182,7 +185,9 @@ namespace WireMock.Server
private FluentMockServer(IFluentMockServerSettings settings)
{
settings.Logger = settings.Logger ?? new WireMockConsoleLogger();
_logger = settings.Logger;
_fileSystemHandler = settings.FileSystemHandler ?? new LocalFileSystemHandler();
_logger.Info("WireMock.Net by Stef Heyenrath (https://github.com/WireMock-Net/WireMock.Net)");
_logger.Debug("WireMock.Net server settings {0}", JsonConvert.SerializeObject(settings, Formatting.Indented));
@@ -387,7 +392,6 @@ namespace WireMock.Server
public void SetMaxRequestLogCount([CanBeNull] int? maxRequestLogCount)
{
_options.MaxRequestLogCount = maxRequestLogCount;
}
/// <summary>

View File

@@ -1,6 +1,7 @@
using System;
using JetBrains.Annotations;
using Newtonsoft.Json;
using WireMock.Handlers;
using WireMock.Logging;
namespace WireMock.Settings
@@ -77,5 +78,10 @@ namespace WireMock.Settings
[PublicAPI]
[JsonIgnore]
public IWireMockLogger Logger { get; set; } = new WireMockNullLogger();
/// <inheritdoc cref="IFluentMockServerSettings.FileSystemHandler"/>
[PublicAPI]
[JsonIgnore]
public IFileSystemHandler FileSystemHandler { get; set; } = new LocalFileSystemHandler();
}
}

View File

@@ -1,5 +1,6 @@
using System;
using JetBrains.Annotations;
using WireMock.Handlers;
using WireMock.Logging;
namespace WireMock.Settings
@@ -105,5 +106,11 @@ namespace WireMock.Settings
/// </summary>
[PublicAPI]
IWireMockLogger Logger { get; set; }
/// <summary>
/// Handler to interact with the file system to read and write static mapping files.
/// </summary>
[PublicAPI]
IFileSystemHandler FileSystemHandler { get; set; }
}
}

View File

@@ -0,0 +1,89 @@
using System;
using System.Collections.Generic;
using HandlebarsDotNet;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using WireMock.Validation;
namespace WireMock.Transformers
{
internal static class HandlebarsHelpers
{
public static void Register()
{
Handlebars.RegisterHelper("JsonPath.SelectToken", (writer, context, arguments) =>
{
(JObject valueToProcess, string jsonpath) = Parse(arguments);
JToken result = null;
try
{
result = valueToProcess.SelectToken(jsonpath);
}
catch (JsonException)
{
// Ignore JsonException and return
return;
}
if (result != null)
{
writer.WriteSafeString(result);
}
});
Handlebars.RegisterHelper("JsonPath.SelectTokens", (writer, options, context, arguments) =>
{
(JObject valueToProcess, string jsonpath) = Parse(arguments);
IEnumerable<JToken> values = null;
try
{
values = valueToProcess.SelectTokens(jsonpath);
}
catch (JsonException)
{
// Ignore JsonException and return
return;
}
if (values == null)
{
return;
}
int id = 0;
foreach (JToken value in values)
{
options.Template(writer, new { id, value });
id++;
}
});
}
private static (JObject valueToProcess, string jsonpath) Parse(object[] arguments)
{
Check.Condition(arguments, args => args.Length == 2, nameof(arguments));
Check.NotNull(arguments[0], "arguments[0]");
Check.NotNullOrEmpty(arguments[1] as string, "arguments[1]");
JObject valueToProcess;
switch (arguments[0])
{
case string jsonAsString:
valueToProcess = JObject.Parse(jsonAsString);
break;
case JObject jsonAsJObject:
valueToProcess = jsonAsJObject;
break;
default:
throw new NotSupportedException($"The value '{arguments[0]}' with type {arguments[0].GetType()} cannot be used in Handlebars JsonPath.");
}
return (valueToProcess, arguments[1] as string);
}
}
}

View File

@@ -2,12 +2,18 @@
using System.Linq;
using HandlebarsDotNet;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using WireMock.Util;
namespace WireMock.Transformers
{
internal static class ResponseMessageTransformer
{
static ResponseMessageTransformer()
{
HandlebarsHelpers.Register();
}
public static ResponseMessage Transform(RequestMessage requestMessage, ResponseMessage original)
{
bool bodyIsJson = original.BodyAsJson != null;
@@ -20,21 +26,13 @@ namespace WireMock.Transformers
var template = new { request = requestMessage };
// Body
Formatting formatting = original.BodyAsJsonIndented == true ? Formatting.Indented : Formatting.None;
string body = bodyIsJson ? JsonConvert.SerializeObject(original.BodyAsJson, formatting) : original.Body;
if (body != null)
if (!bodyIsJson)
{
var templateBody = Handlebars.Compile(body);
if (!bodyIsJson)
{
responseMessage.Body = templateBody(template);
}
else
{
responseMessage.BodyAsJson = JsonConvert.DeserializeObject(templateBody(template));
}
TransformBodyAsString(template, original, responseMessage);
}
else
{
TransformBodyAsJson(template, original, responseMessage);
}
// Headers
@@ -54,5 +52,79 @@ namespace WireMock.Transformers
return responseMessage;
}
private static void TransformBodyAsJson(object template, ResponseMessage original, ResponseMessage responseMessage)
{
JObject jobject;
switch (original.BodyAsJson)
{
case JObject bodyAsJObject:
jobject = bodyAsJObject;
break;
default:
jobject = JObject.FromObject(original.BodyAsJson);
break;
}
WalkNode(jobject, template);
responseMessage.BodyAsJson = jobject;
}
private static void WalkNode(JToken node, object template)
{
if (node.Type == JTokenType.Object)
{
// In case of Object, loop all children.
foreach (JProperty child in node.Children<JProperty>())
{
WalkNode(child.Value, template);
}
}
else if (node.Type == JTokenType.Array)
{
// In case of Array, loop all items.
foreach (JToken child in node.Children())
{
WalkNode(child, template);
}
}
else if (node.Type == JTokenType.String)
{
// In case of string, try to transform the value.
string stringValue = node.Value<string>();
if (string.IsNullOrEmpty(stringValue))
{
return;
}
var templateForStringValue = Handlebars.Compile(stringValue);
string transformedString = templateForStringValue(template);
if (!string.Equals(stringValue, transformedString))
{
JToken value;
try
{
// Try to convert this string into a real JsonObject
value = JToken.Parse(transformedString);
}
catch (JsonException)
{
// Ignore JsonException and just convert to JToken
value = transformedString;
}
node.Replace(value);
}
}
}
private static void TransformBodyAsString(object template, ResponseMessage original, ResponseMessage responseMessage)
{
var templateBody = Handlebars.Compile(original.Body);
responseMessage.Body = templateBody(template);
}
}
}

View File

@@ -1,5 +1,8 @@
using System.IO;
using System.Threading;
using JetBrains.Annotations;
using WireMock.Handlers;
using WireMock.Validation;
namespace WireMock.Util
{
@@ -8,17 +11,19 @@ namespace WireMock.Util
private const int NumberOfRetries = 3;
private const int DelayOnRetry = 500;
public static string ReadAllText(string path)
public static string ReadAllTextWithRetryAndDelay([NotNull] IFileSystemHandler filehandler, [NotNull] string path)
{
Check.NotNull(filehandler, nameof(filehandler));
Check.NotNullOrEmpty(path, nameof(path));
for (int i = 1; i <= NumberOfRetries; ++i)
{
try
{
return File.ReadAllText(path);
return filehandler.ReadMappingFile(path);
}
catch
{
// You may check error code to filter some exceptions, not every error can be recovered.
Thread.Sleep(DelayOnRetry);
}
}

View File

@@ -3,7 +3,7 @@
<PropertyGroup>
<Description>Lightweight Http Mocking Server for .Net, inspired by WireMock from the Java landscape.</Description>
<AssemblyTitle>WireMock.Net</AssemblyTitle>
<Version>1.0.4.7</Version>
<Version>1.0.4.10</Version>
<Authors>Stef Heyenrath</Authors>
<TargetFrameworks>net452;net46;netstandard1.3;netstandard2.0</TargetFrameworks>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
@@ -41,7 +41,7 @@
<PackageReference Include="JetBrains.Annotations" Version="11.1.0">
<PrivateAssets>All</PrivateAssets>
</PackageReference>
<PackageReference Include="Handlebars.Net" Version="1.9.0" />
<PackageReference Include="Handlebars.Net" Version="1.9.5" />
<PackageReference Include="Newtonsoft.Json" Version="10.0.3" />
<PackageReference Include="SimMetrics.Net" Version="1.0.4" />
<PackageReference Include="System.Net.Http" Version="4.3.3" />
@@ -53,6 +53,7 @@
<PackageReference Include="Microsoft.AspNet.WebApi.OwinSelfHost" Version="5.2.6" />
<PackageReference Include="XPath2" Version="1.0.5.1" />
<Reference Include="System.Net.Http.WebRequest" />
<PackageReference Include="System.ValueTuple" Version="4.5.0" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net46' ">
@@ -62,6 +63,7 @@
<PackageReference Include="Microsoft.Owin.Hosting" Version="4.0.0" />
<PackageReference Include="System.Net.Http" Version="4.3.3" />
<PackageReference Include="XPath2" Version="1.0.5.1" />
<PackageReference Include="System.ValueTuple" Version="4.5.0" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard1.3' ">
@@ -69,6 +71,7 @@
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel.Https" Version="1.1.3" />
<PackageReference Include="System.Xml.XmlDocument" Version="4.3.0" />
<PackageReference Include="System.Xml.XPath.XmlDocument" Version="4.3.0" />
<PackageReference Include="System.ValueTuple" Version="4.5.0" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard2.0' ">

View File

@@ -7,6 +7,7 @@ using System.Net;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using Moq;
using NFluent;
using WireMock.Matchers;
using WireMock.RequestBuilders;
@@ -14,6 +15,10 @@ using WireMock.ResponseBuilders;
using WireMock.Server;
using Xunit;
using Newtonsoft.Json;
using WireMock.Handlers;
using WireMock.Logging;
using WireMock.Settings;
using WireMock.Admin.Mappings;
namespace WireMock.Net.Tests
{
@@ -42,6 +47,35 @@ namespace WireMock.Net.Tests
server2.Stop();
}
[Fact]
public void FluentMockServer_SaveStaticMappings()
{
// Assign
string guid = "791a3f31-6946-aaaa-8e6f-0237c7441111";
var _staticMappingHandlerMock = new Mock<IFileSystemHandler>();
_staticMappingHandlerMock.Setup(m => m.GetMappingFolder()).Returns("folder");
_staticMappingHandlerMock.Setup(m => m.FolderExists(It.IsAny<string>())).Returns(true);
_staticMappingHandlerMock.Setup(m => m.WriteMappingFile(It.IsAny<string>(), It.IsAny<string>()));
_server = FluentMockServer.Start(new FluentMockServerSettings
{
FileSystemHandler = _staticMappingHandlerMock.Object
});
_server
.Given(Request.Create().WithPath($"/foo_{Guid.NewGuid()}"))
.WithGuid(guid)
.RespondWith(Response.Create().WithBody("save test"));
// Act
_server.SaveStaticMappings();
// Assert and Verify
_staticMappingHandlerMock.Verify(m => m.GetMappingFolder(), Times.Once);
_staticMappingHandlerMock.Verify(m => m.FolderExists("folder"), Times.Once);
_staticMappingHandlerMock.Verify(m => m.WriteMappingFile(Path.Combine("folder", guid + ".json"), It.IsAny<string>()), Times.Once);
}
[Fact]
public void FluentMockServer_ReadStaticMapping_WithNonGuidFilename()
{
@@ -108,6 +142,49 @@ namespace WireMock.Net.Tests
Check.That(mappings.First().Title).IsNullOrEmpty();
}
[Fact]
public void FluentMockServer_ReadStaticMappings_FolderExistsIsTrue()
{
// Assign
var _staticMappingHandlerMock = new Mock<IFileSystemHandler>();
_staticMappingHandlerMock.Setup(m => m.GetMappingFolder()).Returns("folder");
_staticMappingHandlerMock.Setup(m => m.FolderExists(It.IsAny<string>())).Returns(true);
_staticMappingHandlerMock.Setup(m => m.EnumerateFiles(It.IsAny<string>())).Returns(new string[0]);
_server = FluentMockServer.Start(new FluentMockServerSettings
{
FileSystemHandler = _staticMappingHandlerMock.Object
});
// Act
_server.ReadStaticMappings();
// Assert and Verify
_staticMappingHandlerMock.Verify(m => m.GetMappingFolder(), Times.Once);
_staticMappingHandlerMock.Verify(m => m.FolderExists("folder"), Times.Once);
_staticMappingHandlerMock.Verify(m => m.EnumerateFiles("folder"), Times.Once);
}
[Fact]
public void FluentMockServer_ReadStaticMappingAndAddOrUpdate()
{
// Assign
string mapping = "{\"Request\": {\"Path\": {\"Matchers\": [{\"Name\": \"WildcardMatcher\",\"Pattern\": \"/static/mapping\"}]},\"Methods\": [\"get\"]},\"Response\": {\"BodyAsJson\": { \"body\": \"static mapping\" }}}";
var _staticMappingHandlerMock = new Mock<IFileSystemHandler>();
_staticMappingHandlerMock.Setup(m => m.ReadMappingFile(It.IsAny<string>())).Returns(mapping);
_server = FluentMockServer.Start(new FluentMockServerSettings
{
FileSystemHandler = _staticMappingHandlerMock.Object
});
// Act
_server.ReadStaticMappingAndAddOrUpdate(@"c:\test.json");
// Assert and Verify
_staticMappingHandlerMock.Verify(m => m.ReadMappingFile(@"c:\test.json"), Times.Once);
}
[Fact]
public void FluentMockServer_ReadStaticMappings()
{
@@ -142,7 +219,7 @@ namespace WireMock.Net.Tests
string guid = "90356dba-b36c-469a-a17e-669cd84f1f05";
_server = FluentMockServer.Start();
_server.Given(Request.Create().WithPath("/foo1").UsingGet()).WithGuid(guid)
_server.Given(Request.Create().WithPath("/foo100").UsingGet()).WithGuid(guid)
.RespondWith(Response.Create().WithStatusCode(201).WithBody("1"));
var mappings = _server.Mappings.ToArray();
@@ -219,13 +296,14 @@ namespace WireMock.Net.Tests
public async Task FluentMockServer_Should_respond_to_request_methodPatch()
{
// given
string path = $"/foo_{Guid.NewGuid()}";
_server = FluentMockServer.Start();
_server.Given(Request.Create().WithPath("/foo").UsingMethod("patch"))
_server.Given(Request.Create().WithPath(path).UsingMethod("patch"))
.RespondWith(Response.Create().WithBody("hello patch"));
// when
var msg = new HttpRequestMessage(new HttpMethod("patch"), new Uri("http://localhost:" + _server.Ports[0] + "/foo"))
var msg = new HttpRequestMessage(new HttpMethod("patch"), new Uri("http://localhost:" + _server.Ports[0] + path))
{
Content = new StringContent("{\"data\": {\"attr\":\"value\"}}")
};
@@ -338,13 +416,14 @@ namespace WireMock.Net.Tests
public async Task FluentMockServer_Should_respond_to_request_bodyAsBytes()
{
// given
string path = $"/foo_{Guid.NewGuid()}";
_server = FluentMockServer.Start();
_server.Given(Request.Create().WithPath("/foo").UsingGet()).RespondWith(Response.Create().WithBody(new byte[] { 48, 49 }));
_server.Given(Request.Create().WithPath(path).UsingGet()).RespondWith(Response.Create().WithBody(new byte[] { 48, 49 }));
// when
var responseAsString = await new HttpClient().GetStringAsync("http://localhost:" + _server.Ports[0] + "/foo");
var responseAsBytes = await new HttpClient().GetByteArrayAsync("http://localhost:" + _server.Ports[0] + "/foo");
var responseAsString = await new HttpClient().GetStringAsync("http://localhost:" + _server.Ports[0] + path);
var responseAsBytes = await new HttpClient().GetByteArrayAsync("http://localhost:" + _server.Ports[0] + path);
// then
Check.That(responseAsString).IsEqualTo("01");
@@ -371,13 +450,14 @@ namespace WireMock.Net.Tests
foreach (var item in validMatchersForHelloServerJsonMessage)
{
string path = $"/foo_{Guid.NewGuid()}";
_server
.Given(Request.Create().WithPath("/foo").WithBody((IMatcher)item[0]))
.Given(Request.Create().WithPath(path).WithBody((IMatcher)item[0]))
.RespondWith(Response.Create().WithBody("Hello client"));
// Act
var content = new StringContent(jsonRequestMessage, Encoding.UTF8, (string)item[1]);
var response = await new HttpClient().PostAsync("http://localhost:" + _server.Ports[0] + "/foo", content);
var response = await new HttpClient().PostAsync("http://localhost:" + _server.Ports[0] + path, content);
// Assert
var responseString = await response.Content.ReadAsStringAsync();
@@ -392,10 +472,11 @@ namespace WireMock.Net.Tests
public async Task FluentMockServer_Should_respond_404_for_unexpected_request()
{
// given
string path = $"/foo{Guid.NewGuid()}";
_server = FluentMockServer.Start();
// when
var response = await new HttpClient().GetAsync("http://localhost:" + _server.Ports[0] + "/foo");
var response = await new HttpClient().GetAsync("http://localhost:" + _server.Ports[0] + path);
// then
Check.That(response.StatusCode).IsEqualTo(HttpStatusCode.NotFound);
@@ -405,20 +486,21 @@ namespace WireMock.Net.Tests
[Fact]
public async Task FluentMockServer_Should_find_a_request_satisfying_a_request_spec()
{
// given
// Assign
string path = $"/bar_{Guid.NewGuid()}";
_server = FluentMockServer.Start();
// when
await new HttpClient().GetAsync("http://localhost:" + _server.Ports[0] + "/foo");
await new HttpClient().GetAsync("http://localhost:" + _server.Ports[0] + "/bar");
await new HttpClient().GetAsync("http://localhost:" + _server.Ports[0] + path);
// 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");
Check.That(requestLogged.RequestMessage.Path).IsEqualTo(path);
Check.That(requestLogged.RequestMessage.Url).IsEqualTo("http://localhost:" + _server.Ports[0] + path);
}
[Fact]
@@ -439,11 +521,12 @@ namespace WireMock.Net.Tests
public void FluentMockServer_Should_reset_mappings()
{
// given
string path = $"/foo_{Guid.NewGuid()}";
_server = FluentMockServer.Start();
_server
.Given(Request.Create()
.WithPath("/foo")
.WithPath(path)
.UsingGet())
.RespondWith(Response.Create()
.WithBody(@"{ msg: ""Hello world!""}"));
@@ -453,35 +536,38 @@ namespace WireMock.Net.Tests
// then
Check.That(_server.Mappings).IsEmpty();
Check.ThatAsyncCode(() => new HttpClient().GetStringAsync("http://localhost:" + _server.Ports[0] + "/foo"))
Check.ThatAsyncCode(() => new HttpClient().GetStringAsync("http://localhost:" + _server.Ports[0] + path))
.ThrowsAny();
}
[Fact]
public async Task FluentMockServer_Should_respond_a_redirect_without_body()
{
// given
// Assign
string path = $"/foo_{Guid.NewGuid()}";
string pathToRedirect = $"/bar_{Guid.NewGuid()}";
_server = FluentMockServer.Start();
_server
.Given(Request.Create()
.WithPath("/foo")
.WithPath(path)
.UsingGet())
.RespondWith(Response.Create()
.WithStatusCode(307)
.WithHeader("Location", "/bar"));
.WithHeader("Location", pathToRedirect));
_server
.Given(Request.Create()
.WithPath("/bar")
.WithPath(pathToRedirect)
.UsingGet())
.RespondWith(Response.Create()
.WithStatusCode(200)
.WithBody("REDIRECT SUCCESSFUL"));
// when
var response = await new HttpClient().GetStringAsync("http://localhost:" + _server.Ports[0] + "/foo");
// Act
var response = await new HttpClient().GetStringAsync($"http://localhost:{_server.Ports[0]}{path}");
// then
// Assert
Check.That(response).IsEqualTo("REDIRECT SUCCESSFUL");
}
@@ -585,7 +671,6 @@ namespace WireMock.Net.Tests
Check.That(response).IsEqualTo("/fooBar");
}
[Fact]
public async Task FluentMockServer_Should_exclude_restrictedResponseHeader_for_IOwinResponse()
{

View File

@@ -33,6 +33,20 @@ namespace WireMock.Net.Tests.Matchers
Check.That(value).Equals("{}");
}
[Fact]
public void JsonMatcher_IsMatch_ByteArray()
{
// Assign
var bytes = new byte[0];
var matcher = new JsonMatcher("");
// Act
double match = matcher.IsMatch(bytes);
// Assert
Check.That(match).IsEqualTo(0);
}
[Fact]
public void JsonMatcher_IsMatch_NullString()
{
@@ -65,7 +79,7 @@ namespace WireMock.Net.Tests.Matchers
public void JsonMatcher_IsMatch_JObject1()
{
// Assign
var matcher = new JsonMatcher(new { Id = 1, Name = "test" });
var matcher = new JsonMatcher(new { Id = 1, Name = "Test" });
// Act
var jobject = new JObject
@@ -83,7 +97,7 @@ namespace WireMock.Net.Tests.Matchers
public void JsonMatcher_IsMatch_JObject2()
{
// Assign
var matcher = new JsonMatcher(new { Id = 1, Name = "test" });
var matcher = new JsonMatcher(new { Id = 1, Name = "Test" });
// Act
var jobject = JObject.Parse("{ \"Id\" : 1, \"Name\" : \"Test\" }");

View File

@@ -33,6 +33,20 @@ namespace WireMock.Net.Tests.Matchers
Check.That(patterns).ContainsExactly("X");
}
[Fact]
public void JsonPathMatcher_IsMatch_ByteArray()
{
// Assign
var bytes = new byte[0];
var matcher = new JsonPathMatcher("");
// Act
double match = matcher.IsMatch(bytes);
// Assert
Check.That(match).IsEqualTo(0);
}
[Fact]
public void JsonPathMatcher_IsMatch_NullString()
{

View File

@@ -4,6 +4,7 @@ using System.Text;
using System.Threading.Tasks;
using Microsoft.Owin;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using NFluent;
using WireMock.Models;
using WireMock.ResponseBuilders;
@@ -19,7 +20,7 @@ namespace WireMock.Net.Tests.ResponseBuilderTests
[Fact]
public async Task Response_ProvideResponse_Handlebars_WithBodyAsJson()
{
// given
// Assign
string jsonString = "{ \"things\": [ { \"name\": \"RequiredThing\" }, { \"name\": \"Wiremock\" } ] }";
var bodyData = new BodyData
{
@@ -32,17 +33,17 @@ namespace WireMock.Net.Tests.ResponseBuilderTests
.WithBodyAsJson(new { x = "test {{request.url}}" })
.WithTransformer();
// act
// Act
var responseMessage = await response.ProvideResponseAsync(request);
// then
// Assert
Check.That(JsonConvert.SerializeObject(responseMessage.BodyAsJson)).Equals("{\"x\":\"test http://localhost/foo\"}");
}
[Fact]
public async Task Response_ProvideResponse_Handlebars_UrlPathVerb()
{
// given
// Assign
var body = new BodyData
{
BodyAsString = "whatever"
@@ -53,17 +54,17 @@ namespace WireMock.Net.Tests.ResponseBuilderTests
.WithBody("test {{request.url}} {{request.path}} {{request.method}}")
.WithTransformer();
// act
// Act
var responseMessage = await response.ProvideResponseAsync(request);
// then
// Assert
Check.That(responseMessage.Body).Equals("test http://localhost/foo /foo post");
}
[Fact]
public async Task Response_ProvideResponse_Handlebars_UrlPath()
{
// given
// Assign
var urlDetails = UrlUtils.Parse(new Uri("http://localhost/wiremock/a/b"), new PathString("/wiremock"));
var request = new RequestMessage(urlDetails, "POST", ClientIp);
@@ -71,17 +72,17 @@ namespace WireMock.Net.Tests.ResponseBuilderTests
.WithBody("{{request.url}} {{request.absoluteurl}} {{request.path}} {{request.absolutepath}}")
.WithTransformer();
// act
// Act
var responseMessage = await response.ProvideResponseAsync(request);
// then
// Assert
Check.That(responseMessage.Body).Equals("http://localhost/a/b http://localhost/wiremock/a/b /a/b /wiremock/a/b");
}
[Fact]
public async Task Response_ProvideResponse_Handlebars_PathSegments()
{
// given
// Assign
var urlDetails = UrlUtils.Parse(new Uri("http://localhost/wiremock/a/b"), new PathString("/wiremock"));
var request = new RequestMessage(urlDetails, "POST", ClientIp);
@@ -89,17 +90,17 @@ namespace WireMock.Net.Tests.ResponseBuilderTests
.WithBody("{{request.pathsegments.[0]}} {{request.absolutepathsegments.[0]}}")
.WithTransformer();
// act
// Act
var responseMessage = await response.ProvideResponseAsync(request);
// then
// Assert
Check.That(responseMessage.Body).Equals("a wiremock");
}
[Fact]
public async Task Response_ProvideResponse_Handlebars_Query()
{
// given
// Assign
var body = new BodyData
{
BodyAsString = "abc"
@@ -110,17 +111,17 @@ namespace WireMock.Net.Tests.ResponseBuilderTests
.WithBody("test keya={{request.query.a}} idx={{request.query.a.[0]}} idx={{request.query.a.[1]}} keyb={{request.query.b}}")
.WithTransformer();
// act
// Act
var responseMessage = await response.ProvideResponseAsync(request);
// then
// Assert
Check.That(responseMessage.Body).Equals("test keya=1 idx=1 idx=2 keyb=5");
}
[Fact]
public async Task Response_ProvideResponse_Handlebars_Header()
{
// given
// Assign
var body = new BodyData
{
BodyAsString = "abc"
@@ -129,10 +130,10 @@ namespace WireMock.Net.Tests.ResponseBuilderTests
var response = Response.Create().WithHeader("x", "{{request.headers.Content-Type}}").WithBody("test").WithTransformer();
// act
// Act
var responseMessage = await response.ProvideResponseAsync(request);
// then
// Assert
Check.That(responseMessage.Body).Equals("test");
Check.That(responseMessage.Headers).ContainsKey("x");
Check.That(responseMessage.Headers["x"]).ContainsExactly("text/plain");
@@ -141,7 +142,7 @@ namespace WireMock.Net.Tests.ResponseBuilderTests
[Fact]
public async Task Response_ProvideResponse_Handlebars_Headers()
{
// given
// Assign
var body = new BodyData
{
BodyAsString = "abc"
@@ -150,10 +151,10 @@ namespace WireMock.Net.Tests.ResponseBuilderTests
var response = Response.Create().WithHeader("x", "{{request.headers.Content-Type}}", "{{request.url}}").WithBody("test").WithTransformer();
// act
// Act
var responseMessage = await response.ProvideResponseAsync(request);
// then
// Assert
Check.That(responseMessage.Body).Equals("test");
Check.That(responseMessage.Headers).ContainsKey("x");
Check.That(responseMessage.Headers["x"]).Contains("text/plain");
@@ -163,7 +164,7 @@ namespace WireMock.Net.Tests.ResponseBuilderTests
[Fact]
public async Task Response_ProvideResponse_Handlebars_Origin_Port_Protocol_Host()
{
// given
// Assign
var body = new BodyData
{
BodyAsString = "abc"
@@ -174,11 +175,319 @@ namespace WireMock.Net.Tests.ResponseBuilderTests
.WithBody("test {{request.origin}} {{request.port}} {{request.protocol}} {{request.host}}")
.WithTransformer();
// act
// Act
var responseMessage = await response.ProvideResponseAsync(request);
// then
// Assert
Check.That(responseMessage.Body).Equals("test http://localhost:1234 1234 http localhost");
}
[Fact]
public async Task Response_ProvideResponse_Handlebars_JsonPath_SelectToken_Object_ResponseBodyAsJson()
{
// Assign
var body = new BodyData
{
BodyAsString = @"{
""Stores"": [
""Lambton Quay"",
""Willis Street""
],
""Manufacturers"": [
{
""Name"": ""Acme Co"",
""Products"": [
{
""Name"": ""Anvil"",
""Price"": 50
}
]
},
{
""Name"": ""Contoso"",
""Products"": [
{
""Name"": ""Elbow Grease"",
""Price"": 99.95
},
{
""Name"": ""Headlight Fluid"",
""Price"": 4
}
]
}
]
}"
};
var request = new RequestMessage(new UrlDetails("http://localhost:1234"), "POST", ClientIp, body);
var response = Response.Create()
.WithHeader("Content-Type", "application/json")
.WithBodyAsJson(new { x = "{{JsonPath.SelectToken request.body \"$.Manufacturers[?(@.Name == 'Acme Co')]\"}}" })
.WithTransformer();
// Act
var responseMessage = await response.ProvideResponseAsync(request);
// Assert
JObject j = JObject.FromObject(responseMessage.BodyAsJson);
Check.That(j["x"]).IsNotNull();
Check.That(j["x"]["Name"].ToString()).Equals("Acme Co");
}
[Fact]
public async Task Response_ProvideResponse_Handlebars_JsonPath_SelectToken_Number_ResponseBodyAsJson()
{
// Assign
var body = new BodyData { BodyAsString = "{ \"Price\": 99 }" };
var request = new RequestMessage(new UrlDetails("http://localhost:1234"), "POST", ClientIp, body);
var response = Response.Create()
.WithHeader("Content-Type", "application/json")
.WithBodyAsJson(new { x = "{{JsonPath.SelectToken request.body \"..Price\"}}" })
.WithTransformer();
// Act
var responseMessage = await response.ProvideResponseAsync(request);
// Assert
JObject j = JObject.FromObject(responseMessage.BodyAsJson);
Check.That(j["x"].Value<long>()).Equals(99);
}
[Fact]
public async Task Response_ProvideResponse_Handlebars_JsonPath_SelectToken_Request_BodyAsString()
{
// Assign
var body = new BodyData
{
BodyAsString = @"{
""Stores"": [
""Lambton Quay"",
""Willis Street""
],
""Manufacturers"": [
{
""Name"": ""Acme Co"",
""Products"": [
{
""Name"": ""Anvil"",
""Price"": 50
}
]
},
{
""Name"": ""Contoso"",
""Products"": [
{
""Name"": ""Elbow Grease"",
""Price"": 99.95
},
{
""Name"": ""Headlight Fluid"",
""Price"": 4
}
]
}
]
}"
};
var request = new RequestMessage(new UrlDetails("http://localhost:1234"), "POST", ClientIp, body);
var response = Response.Create()
.WithHeader("Content-Type", "application/json")
.WithBody("{{JsonPath.SelectToken request.body \"$.Manufacturers[?(@.Name == 'Acme Co')]\"}}")
.WithTransformer();
// Act
var responseMessage = await response.ProvideResponseAsync(request);
// Assert
Check.That(responseMessage.Body).Equals("{\r\n \"Name\": \"Acme Co\",\r\n \"Products\": [\r\n {\r\n \"Name\": \"Anvil\",\r\n \"Price\": 50\r\n }\r\n ]\r\n}");
}
[Fact]
public async Task Response_ProvideResponse_Handlebars_JsonPath_SelectToken_Request_BodyAsJObject()
{
// Assign
var body = new BodyData
{
BodyAsJson = JObject.Parse(@"{
'Stores': [
'Lambton Quay',
'Willis Street'
],
'Manufacturers': [
{
'Name': 'Acme Co',
'Products': [
{
'Name': 'Anvil',
'Price': 50
}
]
},
{
'Name': 'Contoso',
'Products': [
{
'Name': 'Elbow Grease',
'Price': 99.95
},
{
'Name': 'Headlight Fluid',
'Price': 4
}
]
}
]
}")
};
var request = new RequestMessage(new UrlDetails("http://localhost:1234"), "POST", ClientIp, body);
var response = Response.Create()
.WithHeader("Content-Type", "application/json")
.WithBody("{{JsonPath.SelectToken request.bodyAsJson \"$.Manufacturers[?(@.Name == 'Acme Co')]\"}}")
.WithTransformer();
// Act
var responseMessage = await response.ProvideResponseAsync(request);
// Assert
Check.That(responseMessage.Body).Equals("{\r\n \"Name\": \"Acme Co\",\r\n \"Products\": [\r\n {\r\n \"Name\": \"Anvil\",\r\n \"Price\": 50\r\n }\r\n ]\r\n}");
}
[Fact]
public async Task Response_ProvideResponse_Handlebars_JsonPath_SelectTokens_Request_BodyAsString()
{
// Assign
var body = new BodyData
{
BodyAsString = @"{
""Stores"": [
""Lambton Quay"",
""Willis Street""
],
""Manufacturers"": [
{
""Name"": ""Acme Co"",
""Products"": [
{
""Name"": ""Anvil"",
""Price"": 50
}
]
},
{
""Name"": ""Contoso"",
""Products"": [
{
""Name"": ""Elbow Grease"",
""Price"": 99.95
},
{
""Name"": ""Headlight Fluid"",
""Price"": 4
}
]
}
]
}"
};
var request = new RequestMessage(new UrlDetails("http://localhost:1234"), "POST", ClientIp, body);
var response = Response.Create()
.WithHeader("Content-Type", "application/json")
.WithBody("{{#JsonPath.SelectTokens request.body \"$..Products[?(@.Price >= 50)].Name\"}}{{id}} {{value}},{{/JsonPath.SelectTokens}}")
.WithTransformer();
// Act
var responseMessage = await response.ProvideResponseAsync(request);
// Assert
Check.That(responseMessage.Body).Equals("0 Anvil,1 Elbow Grease,");
}
[Fact]
public async Task Response_ProvideResponse_Handlebars_JsonPath_SelectTokens_Request_BodyAsJObject()
{
// Assign
var body = new BodyData
{
BodyAsJson = JObject.Parse(@"{
'Stores': [
'Lambton Quay',
'Willis Street'
],
'Manufacturers': [
{
'Name': 'Acme Co',
'Products': [
{
'Name': 'Anvil',
'Price': 50
}
]
},
{
'Name': 'Contoso',
'Products': [
{
'Name': 'Elbow Grease',
'Price': 99.95
},
{
'Name': 'Headlight Fluid',
'Price': 4
}
]
}
]
}")
};
var request = new RequestMessage(new UrlDetails("http://localhost:1234"), "POST", ClientIp, body);
var response = Response.Create()
.WithHeader("Content-Type", "application/json")
.WithBody("{{#JsonPath.SelectTokens request.bodyAsJson \"$..Products[?(@.Price >= 50)].Name\"}}{{id}} {{value}},{{/JsonPath.SelectTokens}}")
.WithTransformer();
// Act
var responseMessage = await response.ProvideResponseAsync(request);
// Assert
Check.That(responseMessage.Body).Equals("0 Anvil,1 Elbow Grease,");
}
[Fact]
public void Response_ProvideResponse_Handlebars_JsonPath_SelectTokens_Throws()
{
// Assign
var body = new BodyData
{
BodyAsJson = JObject.Parse(@"{
'Stores': [
'Lambton Quay',
'Willis Street'
]
}")
};
var request = new RequestMessage(new UrlDetails("http://localhost:1234"), "POST", ClientIp, body);
var response = Response.Create()
.WithHeader("Content-Type", "application/json")
.WithBody("{{#JsonPath.SelectTokens request.body \"$..Products[?(@.Price >= 50)].Name\"}}{{id}} {{value}},{{/JsonPath.SelectTokens}}")
.WithTransformer();
// Act
Check.ThatAsyncCode(() => response.ProvideResponseAsync(request)).Throws<ArgumentNullException>();
}
}
}

View File

@@ -1,5 +1,4 @@
using System;
using NFluent;
using NFluent;
using WireMock.Models;
using WireMock.ResponseBuilders;
using Xunit;

View File

@@ -1,4 +1,5 @@
using System.Linq;
using System;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
@@ -16,16 +17,17 @@ namespace WireMock.Net.Tests
public async Task Scenarios_Should_skip_non_relevant_states()
{
// given
string path = $"/foo_{Guid.NewGuid()}";
var server = FluentMockServer.Start();
server
.Given(Request.Create().WithPath("/foo").UsingGet())
.Given(Request.Create().WithPath(path).UsingGet())
.InScenario("s")
.WhenStateIs("Test state")
.RespondWith(Response.Create());
// when
var response = await new HttpClient().GetAsync("http://localhost:" + server.Ports[0] + "/foo");
var response = await new HttpClient().GetAsync("http://localhost:" + server.Ports[0] + path);
// then
Check.That(response.StatusCode).IsEqualTo(HttpStatusCode.NotFound);
@@ -37,23 +39,24 @@ namespace WireMock.Net.Tests
public async Task Scenarios_Should_process_request_if_equals_state_and_single_state_defined()
{
// given
string path = $"/foo_{Guid.NewGuid()}";
var server = FluentMockServer.Start();
server
.Given(Request.Create().WithPath("/foo").UsingGet())
.Given(Request.Create().WithPath(path).UsingGet())
.InScenario("s")
.WillSetStateTo("Test state")
.RespondWith(Response.Create().WithBody("No state msg"));
server
.Given(Request.Create().WithPath("/foo").UsingGet())
.Given(Request.Create().WithPath(path).UsingGet())
.InScenario("s")
.WhenStateIs("Test state")
.RespondWith(Response.Create().WithBody("Test state msg"));
// when
var responseNoState = await new HttpClient().GetStringAsync("http://localhost:" + server.Ports[0] + "/foo");
var responseWithState = await new HttpClient().GetStringAsync("http://localhost:" + server.Ports[0] + "/foo");
var responseNoState = await new HttpClient().GetStringAsync("http://localhost:" + server.Ports[0] + path);
var responseWithState = await new HttpClient().GetStringAsync("http://localhost:" + server.Ports[0] + path);
// then
Check.That(responseNoState).Equals("No state msg");

View File

@@ -0,0 +1,44 @@
using System;
using System.IO;
using Moq;
using NFluent;
using WireMock.Handlers;
using WireMock.Util;
using Xunit;
namespace WireMock.Net.Tests.Util
{
public class FileHelperTests
{
[Fact]
public void FileHelper_ReadAllTextWithRetryAndDelay()
{
// Assign
var _staticMappingHandlerMock = new Mock<IFileSystemHandler>();
_staticMappingHandlerMock.Setup(m => m.ReadMappingFile(It.IsAny<string>())).Returns("text");
// Act
string result = FileHelper.ReadAllTextWithRetryAndDelay(_staticMappingHandlerMock.Object, @"c:\temp");
// Assert
Check.That(result).Equals("text");
// Verify
_staticMappingHandlerMock.Verify(m => m.ReadMappingFile(@"c:\temp"), Times.Once);
}
[Fact]
public void FileHelper_ReadAllTextWithRetryAndDelay_Throws()
{
// Assign
var _staticMappingHandlerMock = new Mock<IFileSystemHandler>();
_staticMappingHandlerMock.Setup(m => m.ReadMappingFile(It.IsAny<string>())).Throws<NotSupportedException>();
// Act
Check.ThatCode(() => FileHelper.ReadAllTextWithRetryAndDelay(_staticMappingHandlerMock.Object, @"c:\temp")).Throws<IOException>();
// Verify
_staticMappingHandlerMock.Verify(m => m.ReadMappingFile(@"c:\temp"), Times.Exactly(3));
}
}
}