Compare commits

...

41 Commits
ai ... 1.0.25.0

Author SHA1 Message Date
Stef Heyenrath
19dd9e113e 1.0.25.0 2019-07-23 18:13:13 +02:00
Stef Heyenrath
cb09d65f10 Support WithBody with multiple matchers (#304) 2019-07-23 18:02:46 +02:00
Stef Heyenrath
1402e14621 1.0.24.0 2019-07-22 18:39:17 +02:00
Ronald Wildenberg
7201479439 Fixed bug 301 by not setting BodyAsFile to null after first use (#302)
* Fixed bug 301 by not setting BodyAsFile to null after first use

* Added extra unit test

* 1.0.24.0
2019-07-22 18:33:40 +02:00
Stef Heyenrath
af46a490a5 MappingModels (#298)
* MappingModels

* WhiteSource Bolt

* 23
2019-07-16 22:05:36 +02:00
Stef Heyenrath
c02b7ea5dc 1.0.22.0 2019-07-15 17:07:37 +02:00
Stef Heyenrath
1db2bc7c89 FixNullRef (#295) (#297) 2019-07-15 17:00:52 +02:00
Stef Heyenrath
4bcc27ff01 1.0.21.0 2019-07-03 19:27:38 +02:00
Stef Heyenrath
93682c9bbf Handlebars Extension (#286)
* wip

* HandlebarsRegistrationCallback

* HandlebarsContextFactoryTests

* 1.0.21.0

* fix sonar

* LocalFileSystemHandler

* readme

* Fix System.IO.IOException
2019-07-03 18:37:22 +02:00
Stef Heyenrath
653e03fcd9 MainApp 2019-07-03 18:32:52 +02:00
Eli Bishop
06576ab317 don't strip request body if we don't recognize the request method (#294)
* don't strip request body if we don't recognize the request method

* use IsNullOrEmpty
2019-07-03 18:15:17 +02:00
Eli Bishop
55a0a6ee71 workaround for AppContext.BaseDirectory being null on some platforms (#293) 2019-07-03 18:14:22 +02:00
Stef Heyenrath
0ce26ab1a0 1.0.20.0 2019-06-17 20:37:10 +02:00
Stef Heyenrath
7a4814e335 Add SaveToFile in the mapping (#284)
* savetofile

* fix !
2019-06-17 20:34:16 +02:00
Stef Heyenrath
561bb75f9f 1.0.19.0 2019-06-15 10:36:52 +02:00
Stef Heyenrath
f764881622 QueryStringParserTests 2019-06-15 10:26:39 +02:00
Stef Heyenrath
fdc433f0ce QueryStringParser 2019-06-15 10:22:28 +02:00
Stef Heyenrath
c76bb94b4c 1.0.18.0 2019-06-10 17:52:05 +02:00
Stef Heyenrath
d7b6e03cb2 --WireMockLogger WireMockConsoleLogger (#282) 2019-06-10 17:46:04 +02:00
Stef Heyenrath
9031541b91 1.0.17.0 2019-06-05 16:05:26 +02:00
Stef Heyenrath
bd030594d5 Add support for HandleBars File (to read a file) (#278)
* HandleBarsFileFragment

* 1.0.17

* {{File}}
2019-06-05 16:00:25 +02:00
Stef Heyenrath
eed73ee8b3 1.0.16.0 2019-05-16 14:30:53 +02:00
Stef Heyenrath
8476e3c47f Sign Assembly (#274)
* sign

* Fix

* sign also test

* DynamicProxyGenAssembly2

* PublicSign Condition=

* remove delaysign
2019-05-16 13:09:00 +02:00
Stef Heyenrath
a7fbc051c9 1.0.15.0 2019-05-04 10:41:42 +02:00
Stef Heyenrath
6e45255c9e Dynamic response handlebars templating (2) (#273)
* Dynamic response files using Handlebars templating
(#270)

* * Response templating / transformation using Handlebars and extensions

* Add unit test for JsonPath and BodyAsFile mapping (#272)

* fix merge issue
2019-05-04 10:20:36 +02:00
Stef Heyenrath
96e68ae2a0 Support Dynamic response files using Handlebars templating (#271)
* Dynamic response files using Handlebars templating
(#270)

* * Response templating / transformation using Handlebars and extensions
2019-05-04 10:02:09 +02:00
Stef Heyenrath
b151a581cc 1.0.14.0 2019-04-20 10:49:37 +02:00
Stef Heyenrath
fdb58b757b Add JmesPath matcher (#269)
* JmesPathMatcher

* netstandard1.3

* update System.Linq.Dynamic.Core

* simplyfy `double IsMatch(object input)`
2019-04-20 10:44:13 +02:00
Stef Heyenrath
dd115a69b7 1.0.13.0 2019-04-11 12:30:00 +02:00
JackCreativeCrew
12444cc11e [265] Add file upload to allow mocking of file operations (#266)
* [265] Add file upload to allow mocking of file operations

* [265] Fix failing test

* Update code + add tests

* LocalFileSystemHandlerTests

* 1.0.13

* Fixed the file post to create the mapping folder if none exists to begin with, otherwise the file upload fails with 404 (can't find the folder to upload to).

* fix tests

* add more tests for LocalFileSystemHandler

* Added the head method for files to check if a file exists without returning it as a body.

* Add a test and fix the response message (head requires no body).

* Fix newline

* Fix newline.

* Fix the number of mapping tests

* Update tests and update client-interface-api

* Cleanup "MappingConverter.cs"
2019-04-11 08:46:14 +02:00
Stef Heyenrath
6c32b9c31a 1.0.12.0 2019-04-05 15:15:22 +02:00
Stef Heyenrath
3d845d5be5 Proxy : also save multipart as string in mapping file (#264)
* ExactObjectMatcher

* BytesEncodingUtils

* BodyParser

* Encoding.ASCII
2019-04-05 14:51:29 +02:00
Stef Heyenrath
409d55350f 1.0.11.0 2019-03-30 09:47:28 +01:00
Alex Kursov
e7ac620721 Add ProvideResponse_WithJsonBodyAndTransform test (#262) 2019-03-30 09:34:13 +01:00
Alex Kursov
ceb6596823 Fix ResponseMessageTransformer to not replace BodyAsJson in an original message (#261)
Fix ResponseMessageTransformer to not replace BodyAsJson in an original message with transformed results
2019-03-30 09:33:49 +01:00
Stef Heyenrath
47e599214f 1.0.10.0 2019-03-27 08:34:30 +01:00
Stef Heyenrath
b99e300acf Fix Response.Delay property serialization (#260)
* Fix Response.Delay property serialization issue

* Fix Response.Delay
2019-03-27 08:26:55 +01:00
Stef Heyenrath
482b05fc4a 1.0.9.0 2019-03-25 18:26:07 +01:00
Stef Heyenrath
9e123fbbea Fixed Multi Param Match logic (#256)
* fix?

* 1.0.9

* <PackageReference Include="System.Linq.Dynamic.Core" Version="1.0.11" />

* fix tests ?

* fix

* RequestMessageParamMatcher
2019-03-25 18:00:22 +01:00
Stef Heyenrath
658f74ac61 1.0.8.0 2019-03-12 15:18:42 +01:00
Stef Heyenrath
1392119f9d RequestMessageParamMatcher supports Ignore Case for the key (#254)
* RequestMessageParamMatcher

* /o:

* 1.0.8
2019-03-12 14:42:52 +01:00
105 changed files with 3476 additions and 596 deletions

View File

@@ -1,3 +1,90 @@
# 1.0.25.0 (23 July 2019)
- [#304](https://github.com/WireMock-Net/WireMock.Net/pull/304) - Support WithBody with multiple matchers [feature] contributed by [StefH](https://github.com/StefH)
- [#303](https://github.com/WireMock-Net/WireMock.Net/issues/303) - Question: WithBody can't be used with multiple matching rules? [question]
# 1.0.24.0 (22 July 2019)
- [#302](https://github.com/WireMock-Net/WireMock.Net/pull/302) - Fixed bug 301 by not setting BodyAsFile to null after first use [bug] contributed by [rwwilden](https://github.com/rwwilden)
- [#299](https://github.com/WireMock-Net/WireMock.Net/issues/299) - Get Mappings models instead of just IMapping interface [feature, question]
- [#300](https://github.com/WireMock-Net/WireMock.Net/issues/300) - Integration with Azure Function [question]
- [#301](https://github.com/WireMock-Net/WireMock.Net/issues/301) - Error thrown when calling mocked endpoint second time when using file-based response body [bug]
# 1.0.23.0 (16 July 2019)
- [#298](https://github.com/WireMock-Net/WireMock.Net/pull/298) - MappingModels [feature] contributed by [StefH](https://github.com/StefH)
# 1.0.22.0 (15 July 2019)
- [#297](https://github.com/WireMock-Net/WireMock.Net/pull/297) - FixNullRef (#295) contributed by [StefH](https://github.com/StefH)
- [#178](https://github.com/WireMock-Net/WireMock.Net/issues/178) - Bug: Path matching fails when the URL contains encoded parts [bug, question]
- [#295](https://github.com/WireMock-Net/WireMock.Net/issues/295) - NullRef in 1.0.21 [bug]
- [#296](https://github.com/WireMock-Net/WireMock.Net/issues/296) - url param - how to accept either of multiple values [question]
# 1.0.21.0 (03 July 2019)
- [#286](https://github.com/WireMock-Net/WireMock.Net/pull/286) - Handlebars Extension [feature] contributed by [StefH](https://github.com/StefH)
- [#293](https://github.com/WireMock-Net/WireMock.Net/pull/293) - workaround for AppContext.BaseDirectory being null on some platforms contributed by [eli-darkly](https://github.com/eli-darkly)
- [#294](https://github.com/WireMock-Net/WireMock.Net/pull/294) - don't strip request body if we don't recognize the request method contributed by [eli-darkly](https://github.com/eli-darkly)
- [#275](https://github.com/WireMock-Net/WireMock.Net/issues/275) - Server not starting up with 1.0.15 code [invalid, question]
- [#280](https://github.com/WireMock-Net/WireMock.Net/issues/280) - Static Mapping Not work [question]
- [#285](https://github.com/WireMock-Net/WireMock.Net/issues/285) - Need ability to add custom ResponseMessageTransformers [feature, question]
- [#288](https://github.com/WireMock-Net/WireMock.Net/issues/288) - Allow setting root folder in LocalFileSystemHandler [feature, question]
- [#289](https://github.com/WireMock-Net/WireMock.Net/issues/289) - Bug: When WatchStaticMappings=true throws exceptions on updating the mapping files [bug]
- [#290](https://github.com/WireMock-Net/WireMock.Net/issues/290) - Request body is dropped if verb is REPORT [bug]
- [#291](https://github.com/WireMock-Net/WireMock.Net/issues/291) - Define multiple scenarios for the same url path and switch to it at run-time? [question]
- [#292](https://github.com/WireMock-Net/WireMock.Net/issues/292) - Can't start server in Xamarin Android [bug]
# 1.0.20.0 (17 June 2019)
- [#284](https://github.com/WireMock-Net/WireMock.Net/pull/284) - Add SaveToFile in the mapping [feature] contributed by [StefH](https://github.com/StefH)
# 1.0.19.0 (15 June 2019)
- [#279](https://github.com/WireMock-Net/WireMock.Net/issues/279) - How to simulate disconnect? [question]
- [#283](https://github.com/WireMock-Net/WireMock.Net/issues/283) - Support equal-sign in query [bug]
# 1.0.18.0 (10 June 2019)
- [#282](https://github.com/WireMock-Net/WireMock.Net/pull/282) - WireMock.Net.Standalone : Add --WireMockLogger commandline argument [feature] contributed by [StefH](https://github.com/StefH)
# 1.0.17.0 (05 June 2019)
- [#278](https://github.com/WireMock-Net/WireMock.Net/pull/278) - Add support for HandleBars File (to read a file) [feature] contributed by [StefH](https://github.com/StefH)
- [#276](https://github.com/WireMock-Net/WireMock.Net/issues/276) - No server response in Postman and Receive Failure in Fiddler [invalid]
# 1.0.16.0 (16 May 2019)
- [#274](https://github.com/WireMock-Net/WireMock.Net/pull/274) - Sign Assembly [feature] contributed by [StefH](https://github.com/StefH)
- [#160](https://github.com/WireMock-Net/WireMock.Net/issues/160) - Feature: Sign 'WireMock.Net' [feature]
- [#267](https://github.com/WireMock-Net/WireMock.Net/issues/267) - Assembly does not have strong name
# 1.0.15.0 (04 May 2019)
- [#271](https://github.com/WireMock-Net/WireMock.Net/pull/271) - Support Dynamic response files using Handlebars templating [bug, feature] contributed by [StefH](https://github.com/StefH)
- [#272](https://github.com/WireMock-Net/WireMock.Net/pull/272) - Add unit test for JsonPath and BodyAsFile mapping contributed by [denstorti](https://github.com/denstorti)
- [#273](https://github.com/WireMock-Net/WireMock.Net/pull/273) - Dynamic response handlebars templating (2) [bug, feature] contributed by [StefH](https://github.com/StefH)
- [#270](https://github.com/WireMock-Net/WireMock.Net/issues/270) - Dynamic response files using Handlebars templating [bug, question]
# 1.0.14.0 (20 April 2019)
- [#269](https://github.com/WireMock-Net/WireMock.Net/pull/269) - Add JmesPath matcher [feature] contributed by [StefH](https://github.com/StefH)
- [#268](https://github.com/WireMock-Net/WireMock.Net/issues/268) - Swagger UI for admin api [question]
# 1.0.13.0 (11 April 2019)
- [#266](https://github.com/WireMock-Net/WireMock.Net/pull/266) - [265] Add file upload to allow mocking of file operations contributed by [JackCreativeCrew](https://github.com/JackCreativeCrew)
- [#265](https://github.com/WireMock-Net/WireMock.Net/issues/265) - File Upload [feature]
# 1.0.12.0 (05 April 2019)
- [#264](https://github.com/WireMock-Net/WireMock.Net/pull/264) - Proxy : also save multipart as string in mapping file contributed by [StefH](https://github.com/StefH)
- [#263](https://github.com/WireMock-Net/WireMock.Net/issues/263) - Content-Type multipart/form-data is not serialized in proxy and recording mode [bug]
# 1.0.11.0 (30 March 2019)
- [#261](https://github.com/WireMock-Net/WireMock.Net/pull/261) - Fix BodyAsJson transform bug in ResponseMessageTransformer contributed by [psypilat](https://github.com/psypilat)
- [#262](https://github.com/WireMock-Net/WireMock.Net/pull/262) - Add ProvideResponse_WithJsonBodyAndTransform test contributed by [psypilat](https://github.com/psypilat)
# 1.0.10.0 (27 March 2019)
- [#260](https://github.com/WireMock-Net/WireMock.Net/pull/260) - Fix Response.Delay property serialization [bug] contributed by [StefH](https://github.com/StefH)
- [#257](https://github.com/WireMock-Net/WireMock.Net/issues/257) - Doc: Update outdated [question]
- [#258](https://github.com/WireMock-Net/WireMock.Net/issues/258) - InvalidProgramException when following running as standalone process example with dotnetcore [invalid]
# 1.0.9.0 (25 March 2019)
- [#256](https://github.com/WireMock-Net/WireMock.Net/pull/256) - Fixed Multi Param Match logic contributed by [StefH](https://github.com/StefH)
- [#255](https://github.com/WireMock-Net/WireMock.Net/issues/255) - ExactMatcher with array pattern not working? [bug]
# 1.0.8.0 (12 March 2019)
- [#254](https://github.com/WireMock-Net/WireMock.Net/pull/254) - RequestMessageParamMatcher supports Ignore Case for the key [feature] contributed by [StefH](https://github.com/StefH)
- [#251](https://github.com/WireMock-Net/WireMock.Net/issues/251) - Problem with Request Match WithBody [question]
- [#253](https://github.com/WireMock-Net/WireMock.Net/issues/253) - Request Path and query parameter keys are case-sensitive
# 1.0.7.0 (19 January 2019)
- [#244](https://github.com/WireMock-Net/WireMock.Net/pull/244) - Fix BodyAsFile to also allow relative paths [feature] contributed by [StefH](https://github.com/StefH)
- [#240](https://github.com/WireMock-Net/WireMock.Net/issues/240) - How to submit mappings for multiple request, responses [feature]

View File

@@ -4,7 +4,7 @@
</PropertyGroup>
<PropertyGroup>
<VersionPrefix>1.0.7</VersionPrefix>
<VersionPrefix>1.0.25</VersionPrefix>
</PropertyGroup>
<Choose>

View File

@@ -1,3 +1,3 @@
https://github.com/StefH/GitHubReleaseNotes
GitHubReleaseNotes.exe --output CHANGELOG.md --skip-empty-releases --version 1.0.7.0
GitHubReleaseNotes.exe --output CHANGELOG.md --skip-empty-releases --version 1.0.25.0

View File

@@ -3,12 +3,14 @@ A C# .NET version based on [mock4net](https://github.com/alexvictoor/mock4net) w
## 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
* Library can be used in unit tests and integration tests
* Runs as a standalone process, as windows service, as Azure/IIS or as docker
* Configurable via a fluent DotNet API, JSON files and JSON over HTTP
* Record/playback of stubs (proxying)
* Per-request conditional proxying
* Stateful behaviour simulation
* Response transformation
* Response templating / transformation using Handlebars and extensions
* Can be used locally or in CI/CD scenarios.
## Info
| | |

View File

@@ -10,6 +10,7 @@
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=XUA/@EntryIndexedValue">XUA</s:String>
<s:Boolean x:Key="/Default/UserDictionary/Words/=funcs/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Heyenrath/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Jmes/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=randomizer/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Stef/@EntryIndexedValue">True</s:Boolean>
</wpf:ResourceDictionary>

View File

@@ -24,7 +24,7 @@ steps:
# - https://github.com/Microsoft/vsts-tasks/issues/8291
#
- script: |
%USERPROFILE%\.dotnet\tools\dotnet-sonarscanner begin /k:"wiremock" /d:sonar.organization="stefh-github" /d:sonar.host.url="https://sonarcloud.io" /d:sonar.login="$(SONAR_TOKEN)" /v:"$(buildId)" /d:sonar.cs.opencover.reportsPaths="**\coverage.opencover.xml"
%USERPROFILE%\.dotnet\tools\dotnet-sonarscanner begin /k:"wiremock" /o:"stefh-github" /d:sonar.host.url="https://sonarcloud.io" /d:sonar.login="$(SONAR_TOKEN)" /v:"$(buildId)" /d:sonar.cs.opencover.reportsPaths="**\coverage.opencover.xml"
displayName: Begin SonarScanner
# Build source, tests and run tests for net452 and netcoreapp2.1 (with coverage)
@@ -38,6 +38,9 @@ steps:
%USERPROFILE%\.dotnet\tools\dotnet-sonarscanner end /d:sonar.login="$(SONAR_TOKEN)"
displayName: End SonarScanner
- task: whitesource.ws-bolt.bolt.wss.WhiteSource Bolt@19
displayName: 'WhiteSource Bolt'
# Upload coverage to codecov.io
- script: |
%USERPROFILE%\.nuget\packages\codecov\1.1.0\tools\codecov.exe -f "./test/WireMock.Net.Tests/coverage.opencover.xml" -t $(CODECOV_TOKEN)

View File

@@ -52,6 +52,12 @@ namespace WireMock.Net.Client
var scenarioStates = api.GetScenariosAsync().Result;
Console.WriteLine($"GetScenariosAsync = {JsonConvert.SerializeObject(scenarioStates)}");
var postFileResult = api.PostFileAsync("1.cs", "C# Hello").GetAwaiter().GetResult();
Console.WriteLine($"postFileResult = {JsonConvert.SerializeObject(postFileResult)}");
var getFileResult = api.GetFileAsync("1.cs").GetAwaiter().GetResult();
Console.WriteLine($"getFileResult = {getFileResult}");
Console.WriteLine("Press any key to quit");
Console.ReadKey();
}

View File

@@ -49,5 +49,45 @@ namespace WireMock.Net.ConsoleApplication
{
return File.ReadAllBytes(Path.GetFileName(path) == path ? Path.Combine(GetMappingFolder(), path) : path);
}
/// <inheritdoc cref="IFileSystemHandler.ReadResponseBodyAsFile"/>
public string ReadResponseBodyAsString(string path)
{
return File.ReadAllText(Path.GetFileName(path) == path ? Path.Combine(GetMappingFolder(), path) : path);
}
/// <inheritdoc cref="IFileSystemHandler.FileExists"/>
public bool FileExists(string path)
{
return File.Exists(AdjustPath(path));
}
/// <inheritdoc cref="IFileSystemHandler.WriteFile(string, byte[])"/>
public void WriteFile(string path, byte[] bytes)
{
File.WriteAllBytes(AdjustPath(path), bytes);
}
/// <inheritdoc cref="IFileSystemHandler.DeleteFile"/>
public void DeleteFile(string path)
{
File.Delete(AdjustPath(path));
}
/// <inheritdoc cref="IFileSystemHandler.ReadFile"/>
public byte[] ReadFile(string path)
{
return File.ReadAllBytes(AdjustPath(path));
}
/// <summary>
/// Adjusts the path to the MappingFolder.
/// </summary>
/// <param name="path">The path.</param>
/// <returns>Adjusted path</returns>
private string AdjustPath(string path)
{
return Path.Combine(GetMappingFolder(), path);
}
}
}

View File

@@ -33,7 +33,8 @@ namespace WireMock.Net.ConsoleApplication
PostWireMockMiddlewareInit = app => { System.Console.WriteLine($"PostWireMockMiddlewareInit : {app.GetType()}"); },
Logger = new WireMockConsoleLogger(),
FileSystemHandler = new CustomFileSystemFileHandler()
// Uncomment below if you want to use the CustomFileSystemFileHandler
// FileSystemHandler = new CustomFileSystemFileHandler()
});
System.Console.WriteLine("FluentMockServer listening at {0}", string.Join(",", server.Urls));
@@ -93,6 +94,13 @@ namespace WireMock.Net.ConsoleApplication
)
.RespondWith(Response.Create().WithBody("XPathMatcher!"));
server
.Given(Request.Create()
.WithPath("/xpaths").UsingPost()
.WithBody(new[] { new XPathMatcher("/todo-list[count(todo-item) = 3]"), new XPathMatcher("/todo-list[count(todo-item) = 4]") })
)
.RespondWith(Response.Create().WithBody("xpaths!"));
server
.Given(Request
.Create()
@@ -438,6 +446,17 @@ namespace WireMock.Net.ConsoleApplication
.WithBody("<xml>ok</xml>")
);
server.Given(Request.Create()
.WithPath("/services/query/")
.WithParam("q", "SELECT Id from User where username='user@gmail.com'")
.UsingGet())
.RespondWith(Response.Create()
.WithStatusCode(200)
.WithHeader("Content-Type", "application/json")
.WithBodyAsJson(new { Id = "5bdf076c-5654-4b3e-842c-7caf1fabf8c9" }));
System.Console.WriteLine(JsonConvert.SerializeObject(server.MappingModels, Formatting.Indented));
System.Console.WriteLine("Press any key to stop the server");
System.Console.ReadKey();
server.Stop();

View File

@@ -1,4 +1,6 @@
using Newtonsoft.Json;
using System;
using System.Net.Http;
using Newtonsoft.Json;
using WireMock.Server;
using WireMock.Settings;
@@ -8,14 +10,15 @@ namespace WireMock.Net.Console.Proxy.Net452
{
static void Main(string[] args)
{
string[] urls = { "http://localhost:9091/", "https://localhost:9443/" };
var server = FluentMockServer.Start(new FluentMockServerSettings
{
Urls = new[] { "http://localhost:9091/", "https://localhost:9443/" },
Urls = urls,
StartAdminInterface = true,
ReadStaticMappings = false,
ProxyAndRecordSettings = new ProxyAndRecordSettings
{
Url = "https://www.google.com",
Url = "http://postman-echo.com/post",
//ClientX509Certificate2ThumbprintOrSubjectName = "www.yourclientcertname.com OR yourcertificatethumbprint (only if the service you're proxying to requires it)",
SaveMapping = true,
SaveMappingToFile = false,
@@ -28,6 +31,13 @@ namespace WireMock.Net.Console.Proxy.Net452
System.Console.WriteLine(JsonConvert.SerializeObject(eventRecordArgs.NewItems, Formatting.Indented));
};
var uri = new Uri(urls[0]);
var form = new MultipartFormDataContent
{
{ new StringContent("data"), "test", "test.txt" }
};
new HttpClient().PostAsync(uri, form).GetAwaiter().GetResult();
System.Console.WriteLine("Press any key to stop the server");
System.Console.ReadKey();
server.Stop();

View File

@@ -2,7 +2,7 @@
"profiles": {
"WireMock.Net.StandAlone.NETCoreApp": {
"commandName": "Project",
"commandLineArgs": "--Urls http://*:9091"
"commandLineArgs": "--Urls http://*:9091 --WireMockLogger WireMockConsoleLogger"
}
}
}

View File

@@ -44,7 +44,7 @@
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Linq.Dynamic.Core" publicKeyToken="0f07ec44de6ac832" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-1.0.9.0" newVersion="1.0.9.0" />
<bindingRedirect oldVersion="0.0.0.0-1.0.12.0" newVersion="1.0.12.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />

View File

@@ -225,8 +225,8 @@
<Reference Include="System.IO.Pipelines, Version=4.0.0.1, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\..\packages\System.IO.Pipelines.4.5.2\lib\netstandard2.0\System.IO.Pipelines.dll</HintPath>
</Reference>
<Reference Include="System.Linq.Dynamic.Core, Version=1.0.9.0, Culture=neutral, PublicKeyToken=0f07ec44de6ac832, processorArchitecture=MSIL">
<HintPath>..\..\packages\System.Linq.Dynamic.Core.1.0.9\lib\net46\System.Linq.Dynamic.Core.dll</HintPath>
<Reference Include="System.Linq.Dynamic.Core, Version=1.0.12.0, Culture=neutral, PublicKeyToken=0f07ec44de6ac832, processorArchitecture=MSIL">
<HintPath>..\..\packages\System.Linq.Dynamic.Core.1.0.12\lib\net46\System.Linq.Dynamic.Core.dll</HintPath>
</Reference>
<Reference Include="System.Memory, Version=4.0.1.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\..\packages\System.Memory.4.5.1\lib\netstandard2.0\System.Memory.dll</HintPath>

View File

@@ -65,7 +65,7 @@
<package id="System.Collections.Immutable" version="1.5.0" targetFramework="net461" />
<package id="System.Diagnostics.DiagnosticSource" version="4.5.0" targetFramework="net461" />
<package id="System.IO.Pipelines" version="4.5.2" targetFramework="net461" />
<package id="System.Linq.Dynamic.Core" version="1.0.9" targetFramework="net461" />
<package id="System.Linq.Dynamic.Core" version="1.0.12" targetFramework="net461" />
<package id="System.Memory" version="4.5.1" targetFramework="net461" />
<package id="System.Numerics.Vectors" version="4.5.0" targetFramework="net461" />
<package id="System.Reflection.Metadata" version="1.6.0" targetFramework="net461" />

View File

@@ -1,6 +1,6 @@
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("WireMock.Net.Tests")]
[assembly: InternalsVisibleTo("WireMock.Net.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100e138ec44d93acac565953052636eb8d5e7e9f27ddb030590055cd1a0ab2069a5623f1f77ca907d78e0b37066ca0f6d63da7eecc3fcb65b76aa8ebeccf7ebe1d11264b8404cd9b1cbbf2c83f566e033b3e54129f6ef28daffff776ba7aebbc53c0d635ebad8f45f78eb3f7e0459023c218f003416e080f96a1a3c5ffeb56bee9e")]
// Needed for Moq in the UnitTest project
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")]
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")]

View File

@@ -50,7 +50,7 @@ namespace WireMock.Net.StandAlone
AdminUsername = parser.GetStringValue("AdminUsername"),
AdminPassword = parser.GetStringValue("AdminPassword"),
MaxRequestLogCount = parser.GetIntValue("MaxRequestLogCount"),
RequestLogExpirationDuration = parser.GetIntValue("RequestLogExpirationDuration"),
RequestLogExpirationDuration = parser.GetIntValue("RequestLogExpirationDuration")
};
if (logger != null)
@@ -58,6 +58,11 @@ namespace WireMock.Net.StandAlone
settings.Logger = logger;
}
if (parser.GetStringValue("WireMockLogger") == "WireMockConsoleLogger")
{
settings.Logger = new WireMockConsoleLogger();
}
if (parser.Contains("Port"))
{
settings.Port = parser.GetIntValue("Port");
@@ -82,9 +87,7 @@ namespace WireMock.Net.StandAlone
settings.Logger.Debug("WireMock.Net server arguments [{0}]", string.Join(", ", args.Select(a => $"'{a}'")));
FluentMockServer server = Start(settings);
return server;
return Start(settings);
}
}
}

View File

@@ -25,6 +25,10 @@
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<GenerateBindingRedirectsOutputType>true</GenerateBindingRedirectsOutputType>
<CodeAnalysisRuleSet>WireMock.Net.StandAlone.ruleset</CodeAnalysisRuleSet>
<SignAssembly>true</SignAssembly>
<AssemblyOriginatorKeyFile>../WireMock.Net/WireMock.Net.snk</AssemblyOriginatorKeyFile>
<!--<DelaySign>true</DelaySign>-->
<PublicSign Condition=" '$(OS)' != 'Windows_NT' ">true</PublicSign>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">

View File

@@ -9,5 +9,10 @@
/// Gets or sets the matcher.
/// </summary>
public MatcherModel Matcher { get; set; }
/// <summary>
/// Gets or sets the matchers.
/// </summary>
public MatcherModel[] Matchers { get; set; }
}
}

View File

@@ -39,13 +39,18 @@ namespace WireMock.Admin.Mappings
public string SetStateTo { get; set; }
/// <summary>
/// The request.
/// The request model.
/// </summary>
public RequestModel Request { get; set; }
/// <summary>
/// The response.
/// The response model.
/// </summary>
public ResponseModel Response { get; set; }
/// <summary>
/// Saves this mapping as a static mapping file.
/// </summary>
public bool? SaveToFile { get; set; }
}
}

View File

@@ -10,6 +10,11 @@
/// </summary>
public string Name { get; set; }
/// <summary>
/// Defines if the key should be matched using case-ignore.
/// </summary>
public bool? IgnoreCase { get; set; }
/// <summary>
/// Gets or sets the matchers.
/// </summary>

View File

@@ -58,9 +58,9 @@ namespace WireMock.Admin.Mappings
public EncodingModel BodyEncoding { get; set; }
/// <summary>
/// Gets or sets a value indicating whether [use transformer].
/// Use Handlebars transformer.
/// </summary>
public bool UseTransformer { get; set; }
public bool? UseTransformer { get; set; }
/// <summary>
/// Gets or sets the headers.

View File

@@ -167,5 +167,42 @@ namespace WireMock.Client
/// </summary>
[Post("__admin/scenarios")]
Task<StatusModel> ResetScenariosAsync();
/// <summary>
/// Create a new File
/// </summary>
/// <param name="filename">The filename</param>
/// <param name="body">The body</param>
[Post("__admin/files/{filename}")]
Task<StatusModel> PostFileAsync([Path] string filename, [Body] string body);
/// <summary>
/// Update an existing File
/// </summary>
/// <param name="filename">The filename</param>
/// <param name="body">The body</param>
[Put("__admin/files/{filename}")]
Task<StatusModel> PutFileAsync([Path] string filename, [Body] string body);
/// <summary>
/// Get the content of an existing File
/// </summary>
/// <param name="filename">The filename</param>
[Get("__admin/files/{filename}")]
Task<string> GetFileAsync([Path] string filename);
/// <summary>
/// Delete an existing File
/// </summary>
/// <param name="filename">The filename</param>
[Delete("__admin/files/{filename}")]
Task<StatusModel> DeleteFileAsync([Path] string filename);
/// <summary>
/// Check if a file exists
/// </summary>
/// <param name="filename">The filename</param>
[Head("__admin/files/{filename}")]
Task FileExistsAsync([Path] string filename);
}
}

View File

@@ -1,4 +1,5 @@
using System.Collections.Generic;
using JetBrains.Annotations;
using System.Collections.Generic;
namespace WireMock.Handlers
{
@@ -18,40 +19,74 @@ namespace WireMock.Handlers
/// </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);
bool FolderExists([NotNull] 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);
void CreateFolder([NotNull] 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);
IEnumerable<string> EnumerateFiles([NotNull] string path);
/// <summary>
/// Read a static mapping file as text.
/// </summary>
/// <param name="path">The path (folder + filename with .json extension).</param>
/// <returns>The file content as text.</returns>
string ReadMappingFile(string path);
string ReadMappingFile([NotNull] string path);
/// <summary>
/// Write the static mapping.
/// Write the static mapping file.
/// </summary>
/// <param name="path">The path (folder + filename with .json extension).</param>
/// <param name="text">The text.</param>
void WriteMappingFile(string path, string text);
void WriteMappingFile([NotNull] string path, [NotNull] string text);
/// <summary>
/// Read a response body file as byte[].
/// </summary>
/// <param name="path">The path or filename from the file to read.</param>
/// <returns>The file content as bytes.</returns>
byte[] ReadResponseBodyAsFile([NotNull] string path);
/// <summary>
/// Read a response body file as text.
/// </summary>
/// <param name="path">The path or filename from the file to read.</param>
/// <returns>The file content as text.</returns>
string ReadResponseBodyAsString([NotNull] string path);
/// <summary>
/// Delete a file.
/// </summary>
/// <param name="filename">The filename.</param>
void DeleteFile([NotNull] string filename);
/// <summary>
/// Determines whether the given path refers to an existing file on disk.
/// </summary>
/// <param name="filename">The filename.</param>
/// <returns>true if path refers to an existing file; false if the file does not exist.</returns>
bool FileExists([NotNull] string filename);
/// <summary>
/// Write a file.
/// </summary>
/// <param name="filename">The filename.</param>
/// <param name="bytes">The bytes.</param>
void WriteFile([NotNull] string filename, [NotNull] byte[] bytes);
/// <summary>
/// Read a file as bytes.
/// </summary>
/// <param name="filename">The filename.</param>
/// <returns>The file content as bytes.</returns>
byte[] ReadResponseBodyAsFile(string path);
byte[] ReadFile([NotNull] string filename);
}
}

View File

@@ -1,5 +1,4 @@
using JetBrains.Annotations;
using System.Collections.Generic;
using System.Collections.Generic;
using System.IO;
using WireMock.Validation;
@@ -12,8 +11,26 @@ namespace WireMock.Handlers
{
private static readonly string AdminMappingsFolder = Path.Combine("__admin", "mappings");
private readonly string _rootFolder;
/// <summary>
/// Initializes a new instance of the <see cref="LocalFileSystemHandler"/> class.
/// </summary>
public LocalFileSystemHandler() : this(Directory.GetCurrentDirectory())
{
}
/// <summary>
/// Initializes a new instance of the <see cref="LocalFileSystemHandler"/> class.
/// </summary>
/// <param name="rootFolder">The root folder.</param>
public LocalFileSystemHandler(string rootFolder)
{
_rootFolder = rootFolder;
}
/// <inheritdoc cref="IFileSystemHandler.FolderExists"/>
public bool FolderExists([NotNull] string path)
public bool FolderExists(string path)
{
Check.NotNullOrEmpty(path, nameof(path));
@@ -21,7 +38,7 @@ namespace WireMock.Handlers
}
/// <inheritdoc cref="IFileSystemHandler.CreateFolder"/>
public void CreateFolder([NotNull] string path)
public void CreateFolder(string path)
{
Check.NotNullOrEmpty(path, nameof(path));
@@ -29,7 +46,7 @@ namespace WireMock.Handlers
}
/// <inheritdoc cref="IFileSystemHandler.EnumerateFiles"/>
public IEnumerable<string> EnumerateFiles([NotNull] string path)
public IEnumerable<string> EnumerateFiles(string path)
{
Check.NotNullOrEmpty(path, nameof(path));
@@ -39,19 +56,19 @@ namespace WireMock.Handlers
/// <inheritdoc cref="IFileSystemHandler.GetMappingFolder"/>
public string GetMappingFolder()
{
return Path.Combine(Directory.GetCurrentDirectory(), AdminMappingsFolder);
return Path.Combine(_rootFolder, AdminMappingsFolder);
}
/// <inheritdoc cref="IFileSystemHandler.ReadMappingFile"/>
public string ReadMappingFile([NotNull] string path)
public string ReadMappingFile(string path)
{
Check.NotNullOrEmpty(path, nameof(path));
return File.ReadAllText(path);
}
/// <inheritdoc cref="IFileSystemHandler.WriteMappingFile"/>
public void WriteMappingFile([NotNull] string path, [NotNull] string text)
/// <inheritdoc cref="IFileSystemHandler.WriteMappingFile(string, string)"/>
public void WriteMappingFile(string path, string text)
{
Check.NotNullOrEmpty(path, nameof(path));
Check.NotNull(text, nameof(text));
@@ -68,5 +85,58 @@ namespace WireMock.Handlers
// Else the path will just be as-is.
return File.ReadAllBytes(Path.GetFileName(path) == path ? Path.Combine(GetMappingFolder(), path) : path);
}
/// <inheritdoc cref="IFileSystemHandler.ReadResponseBodyAsString"/>
public string ReadResponseBodyAsString(string path)
{
Check.NotNullOrEmpty(path, nameof(path));
// In case the path is a filename, the path will be adjusted to the MappingFolder.
// Else the path will just be as-is.
return File.ReadAllText(Path.GetFileName(path) == path ? Path.Combine(GetMappingFolder(), path) : path);
}
/// <inheritdoc cref="IFileSystemHandler.FileExists"/>
public bool FileExists(string filename)
{
Check.NotNullOrEmpty(filename, nameof(filename));
return File.Exists(AdjustPath(filename));
}
/// <inheritdoc cref="IFileSystemHandler.WriteFile(string, byte[])"/>
public void WriteFile(string filename, byte[] bytes)
{
Check.NotNullOrEmpty(filename, nameof(filename));
Check.NotNull(bytes, nameof(bytes));
File.WriteAllBytes(AdjustPath(filename), bytes);
}
/// <inheritdoc cref="IFileSystemHandler.DeleteFile"/>
public void DeleteFile(string filename)
{
Check.NotNullOrEmpty(filename, nameof(filename));
File.Delete(AdjustPath(filename));
}
/// <inheritdoc cref="IFileSystemHandler.ReadFile"/>
public byte[] ReadFile(string filename)
{
Check.NotNullOrEmpty(filename, nameof(filename));
return File.ReadAllBytes(AdjustPath(filename));
}
/// <summary>
/// Adjusts the path to the MappingFolder.
/// </summary>
/// <param name="filename">The path.</param>
/// <returns>Adjusted path</returns>
private string AdjustPath(string filename)
{
return Path.Combine(GetMappingFolder(), filename);
}
}
}

View File

@@ -1,8 +1,9 @@
using System;
using JetBrains.Annotations;
using System;
using System.Threading.Tasks;
using JetBrains.Annotations;
using WireMock.Matchers.Request;
using WireMock.ResponseProviders;
using WireMock.Settings;
namespace WireMock
{
@@ -60,6 +61,11 @@ namespace WireMock
/// </summary>
IResponseProvider Provider { get; }
/// <summary>
/// The FluentMockServerSettings.
/// </summary>
IFluentMockServerSettings Settings { get; }
/// <summary>
/// Is State started ?
/// </summary>

View File

@@ -1,8 +1,9 @@
using System;
using JetBrains.Annotations;
using System;
using System.Threading.Tasks;
using JetBrains.Annotations;
using WireMock.Matchers.Request;
using WireMock.ResponseProviders;
using WireMock.Settings;
namespace WireMock
{
@@ -38,6 +39,9 @@ namespace WireMock
/// <inheritdoc cref="IMapping.Provider" />
public IResponseProvider Provider { get; }
/// <inheritdoc cref="IMapping.Settings" />
public IFluentMockServerSettings Settings { get; }
/// <inheritdoc cref="IMapping.IsStartState" />
public bool IsStartState => Scenario == null || Scenario != null && NextState != null && ExecutionConditionState == null;
@@ -50,17 +54,21 @@ namespace WireMock
/// <param name="guid">The unique identifier.</param>
/// <param name="title">The unique title (can be null).</param>
/// <param name="path">The full file path from this mapping title (can be null).</param>
/// <param name="settings">The FluentMockServerSettings.</param>
/// <param name="requestMatcher">The request matcher.</param>
/// <param name="provider">The provider.</param>
/// <param name="priority">The priority for this mapping.</param>
/// <param name="scenario">The scenario. [Optional]</param>
/// <param name="executionConditionState">State in which the current mapping can occur. [Optional]</param>
/// <param name="nextState">The next state which will occur after the current mapping execution. [Optional]</param>
public Mapping(Guid guid, [CanBeNull] string title, [CanBeNull] string path, IRequestMatcher requestMatcher, IResponseProvider provider, int priority, [CanBeNull] string scenario, [CanBeNull] string executionConditionState, [CanBeNull] string nextState)
public Mapping(Guid guid, [CanBeNull] string title, [CanBeNull] string path,
[NotNull] IFluentMockServerSettings settings, [NotNull] IRequestMatcher requestMatcher, [NotNull] IResponseProvider provider,
int priority, [CanBeNull] string scenario, [CanBeNull] string executionConditionState, [CanBeNull] string nextState)
{
Guid = guid;
Title = title;
Path = path;
Settings = settings;
RequestMatcher = requestMatcher;
Provider = provider;
Priority = priority;
@@ -72,7 +80,7 @@ namespace WireMock
/// <inheritdoc cref="IMapping.ResponseToAsync" />
public async Task<ResponseMessage> ResponseToAsync(RequestMessage requestMessage)
{
return await Provider.ProvideResponseAsync(requestMessage);
return await Provider.ProvideResponseAsync(requestMessage, Settings);
}
/// <inheritdoc cref="IMapping.GetRequestMatchResult" />

View File

@@ -4,5 +4,6 @@
/// The registration callback.
/// </summary>
/// <param name="mapping">The mapping.</param>
public delegate void RegistrationCallback(IMapping mapping);
/// <param name="saveToFile">Optional boolean to indicate if this mapping should be saved as static mapping file.</param>
public delegate void RegistrationCallback(IMapping mapping, bool saveToFile = false);
}

View File

@@ -39,7 +39,12 @@ namespace WireMock.Matchers
/// <inheritdoc cref="IStringMatcher.IsMatch"/>
public double IsMatch(string input)
{
return MatchBehaviourHelper.Convert(MatchBehaviour, MatchScores.ToScore(_values.Select(value => value.Equals(input))));
if (_values.Length == 1)
{
return MatchBehaviourHelper.Convert(MatchBehaviour, MatchScores.ToScore(_values[0] == input));
}
return MatchBehaviourHelper.Convert(MatchBehaviour, MatchScores.ToScore(_values.Contains(input)));
}
/// <inheritdoc cref="IStringMatcher.GetPatterns"/>

View File

@@ -1,5 +1,5 @@
using System.Linq;
using JetBrains.Annotations;
using JetBrains.Annotations;
using System.Linq;
using WireMock.Validation;
namespace WireMock.Matchers
@@ -10,8 +10,15 @@ namespace WireMock.Matchers
/// <seealso cref="IObjectMatcher" />
public class ExactObjectMatcher : IObjectMatcher
{
private readonly object _object;
private readonly byte[] _bytes;
/// <summary>
/// Gets the value as object.
/// </summary>
public object ValueAsObject { get; }
/// <summary>
/// Gets the value as byte[].
/// </summary>
public byte[] ValueAsBytes { get; }
/// <inheritdoc cref="IMatcher.MatchBehaviour"/>
public MatchBehaviour MatchBehaviour { get; }
@@ -33,7 +40,7 @@ namespace WireMock.Matchers
{
Check.NotNull(value, nameof(value));
_object = value;
ValueAsObject = value;
MatchBehaviour = matchBehaviour;
}
@@ -54,14 +61,14 @@ namespace WireMock.Matchers
{
Check.NotNull(value, nameof(value));
_bytes = value;
ValueAsBytes = value;
MatchBehaviour = matchBehaviour;
}
/// <inheritdoc cref="IObjectMatcher.IsMatch"/>
public double IsMatch(object input)
{
bool equals = _object != null ? Equals(_object, input) : _bytes.SequenceEqual((byte[])input);
bool equals = ValueAsObject != null ? Equals(ValueAsObject, input) : ValueAsBytes.SequenceEqual((byte[])input);
return MatchBehaviourHelper.Convert(MatchBehaviour, MatchScores.ToScore(equals));
}

View File

@@ -0,0 +1,83 @@
using DevLab.JmesPath;
using JetBrains.Annotations;
using Newtonsoft.Json;
using System.Linq;
using WireMock.Validation;
namespace WireMock.Matchers
{
/// <summary>
/// http://jmespath.org/
/// </summary>
public class JmesPathMatcher : IStringMatcher, IObjectMatcher
{
private readonly string[] _patterns;
/// <inheritdoc cref="IMatcher.MatchBehaviour"/>
public MatchBehaviour MatchBehaviour { get; }
/// <summary>
/// Initializes a new instance of the <see cref="JmesPathMatcher"/> class.
/// </summary>
/// <param name="patterns">The patterns.</param>
public JmesPathMatcher([NotNull] params string[] patterns) : this(MatchBehaviour.AcceptOnMatch, patterns)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="JmesPathMatcher"/> class.
/// </summary>
/// <param name="matchBehaviour">The match behaviour.</param>
/// <param name="patterns">The patterns.</param>
public JmesPathMatcher(MatchBehaviour matchBehaviour, [NotNull] params string[] patterns)
{
Check.NotNull(patterns, nameof(patterns));
MatchBehaviour = matchBehaviour;
_patterns = patterns;
}
/// <inheritdoc cref="IStringMatcher.IsMatch"/>
public double IsMatch(string input)
{
double match = MatchScores.Mismatch;
if (input != null)
{
try
{
match = MatchScores.ToScore(_patterns.Select(pattern => bool.Parse(new JmesPath().Transform(input, pattern))));
}
catch (JsonException)
{
// just ignore JsonException
}
}
return MatchBehaviourHelper.Convert(MatchBehaviour, match);
}
/// <inheritdoc cref="IObjectMatcher.IsMatch"/>
public double IsMatch(object input)
{
double match = MatchScores.Mismatch;
// When input is null or byte[], return Mismatch.
if (input != null && !(input is byte[]))
{
string inputAsString = JsonConvert.SerializeObject(input);
return IsMatch(inputAsString);
}
return MatchBehaviourHelper.Convert(MatchBehaviour, match);
}
/// <inheritdoc cref="IStringMatcher.GetPatterns"/>
public string[] GetPatterns()
{
return _patterns;
}
/// <inheritdoc cref="IMatcher.Name"/>
public string Name => "JmesPathMatcher";
}
}

View File

@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
namespace WireMock.Matchers
@@ -54,6 +55,7 @@ namespace WireMock.Matchers
/// </summary>
/// <param name="values">The values.</param>
/// <returns>average score</returns>
[SuppressMessage("ReSharper", "PossibleMultipleEnumeration")]
public static double ToScore(IEnumerable<bool> values)
{
return values.Any() ? values.Select(ToScore).Average() : Mismatch;
@@ -64,6 +66,7 @@ namespace WireMock.Matchers
/// </summary>
/// <param name="values">The values.</param>
/// <returns>average score</returns>
[SuppressMessage("ReSharper", "PossibleMultipleEnumeration")]
public static double ToScore(IEnumerable<double> values)
{
return values.Any() ? values.Average() : Mismatch;

View File

@@ -1,5 +1,6 @@
using System;
using JetBrains.Annotations;
using System;
using System.Linq;
using WireMock.Util;
using WireMock.Validation;
@@ -26,16 +27,16 @@ namespace WireMock.Matchers.Request
public Func<object, bool> JsonFunc { get; }
/// <summary>
/// The matcher.
/// The matchers.
/// </summary>
public IMatcher Matcher { get; }
public IMatcher[] Matchers { get; }
/// <summary>
/// Initializes a new instance of the <see cref="RequestMessageBodyMatcher"/> class.
/// </summary>
/// <param name="matchBehaviour">The match behaviour.</param>
/// <param name="body">The body.</param>
public RequestMessageBodyMatcher(MatchBehaviour matchBehaviour, [NotNull] string body) : this(new WildcardMatcher(matchBehaviour, body))
public RequestMessageBodyMatcher(MatchBehaviour matchBehaviour, [NotNull] string body) : this(new[] { new WildcardMatcher(matchBehaviour, body) }.Cast<IMatcher>().ToArray())
{
}
@@ -44,7 +45,7 @@ namespace WireMock.Matchers.Request
/// </summary>
/// <param name="matchBehaviour">The match behaviour.</param>
/// <param name="body">The body.</param>
public RequestMessageBodyMatcher(MatchBehaviour matchBehaviour, [NotNull] byte[] body) : this(new ExactObjectMatcher(matchBehaviour, body))
public RequestMessageBodyMatcher(MatchBehaviour matchBehaviour, [NotNull] byte[] body) : this(new[] { new ExactObjectMatcher(matchBehaviour, body) }.Cast<IMatcher>().ToArray())
{
}
@@ -53,7 +54,7 @@ namespace WireMock.Matchers.Request
/// </summary>
/// <param name="matchBehaviour">The match behaviour.</param>
/// <param name="body">The body.</param>
public RequestMessageBodyMatcher(MatchBehaviour matchBehaviour, [NotNull] object body) : this(new ExactObjectMatcher(matchBehaviour, body))
public RequestMessageBodyMatcher(MatchBehaviour matchBehaviour, [NotNull] object body) : this(new[] { new ExactObjectMatcher(matchBehaviour, body) }.Cast<IMatcher>().ToArray())
{
}
@@ -90,25 +91,24 @@ namespace WireMock.Matchers.Request
/// <summary>
/// Initializes a new instance of the <see cref="RequestMessageBodyMatcher"/> class.
/// </summary>
/// <param name="matcher">The matcher.</param>
public RequestMessageBodyMatcher([NotNull] IMatcher matcher)
/// <param name="matchers">The matchers.</param>
public RequestMessageBodyMatcher([NotNull] params IMatcher[] matchers)
{
Check.NotNull(matcher, nameof(matcher));
Matcher = matcher;
Check.NotNull(matchers, nameof(matchers));
Matchers = matchers;
}
/// <see cref="IRequestMatcher.GetMatchingScore"/>
public double GetMatchingScore(RequestMessage requestMessage, RequestMatchResult requestMatchResult)
{
double score = IsMatch(requestMessage);
double score = CalculateMatchScore(requestMessage);
return requestMatchResult.AddScore(GetType(), score);
}
private double IsMatch(RequestMessage requestMessage)
private double CalculateMatchScore(RequestMessage requestMessage, IMatcher matcher)
{
// Check if the matcher is a IObjectMatcher
if (Matcher is IObjectMatcher objectMatcher)
if (matcher is IObjectMatcher objectMatcher)
{
// If the body is a JSON object, try to match.
if (requestMessage?.BodyData?.DetectedBodyType == BodyType.Json)
@@ -124,7 +124,7 @@ namespace WireMock.Matchers.Request
}
// Check if the matcher is a IStringMatcher
if (Matcher is IStringMatcher stringMatcher)
if (matcher is IStringMatcher stringMatcher)
{
// If the body is a Json or a String, use the BodyAsString to match on.
if (requestMessage?.BodyData?.DetectedBodyType == BodyType.Json || requestMessage?.BodyData?.DetectedBodyType == BodyType.String)
@@ -133,6 +133,16 @@ namespace WireMock.Matchers.Request
}
}
return MatchScores.Mismatch;
}
private double CalculateMatchScore(RequestMessage requestMessage)
{
if (Matchers != null && Matchers.Any())
{
return Matchers.Max(matcher => CalculateMatchScore(requestMessage, matcher));
}
if (Func != null)
{
return MatchScores.ToScore(requestMessage?.BodyData?.DetectedBodyType == BodyType.String && Func(requestMessage.BodyData.BodyAsString));

View File

@@ -1,7 +1,7 @@
using System;
using JetBrains.Annotations;
using System;
using System.Collections.Generic;
using System.Linq;
using JetBrains.Annotations;
using WireMock.Util;
using WireMock.Validation;
@@ -24,6 +24,11 @@ namespace WireMock.Matchers.Request
/// </summary>
public string Key { get; }
/// <summary>
/// Defines if the key should be matched using case-ignore.
/// </summary>
public bool? IgnoreCase { get; private set; }
/// <summary>
/// The matchers.
/// </summary>
@@ -34,7 +39,8 @@ namespace WireMock.Matchers.Request
/// </summary>
/// <param name="matchBehaviour">The match behaviour.</param>
/// <param name="key">The key.</param>
public RequestMessageParamMatcher(MatchBehaviour matchBehaviour, [NotNull] string key) : this(matchBehaviour, key, (IStringMatcher[])null)
/// <param name="ignoreCase">Defines if the key should be matched using case-ignore.</param>
public RequestMessageParamMatcher(MatchBehaviour matchBehaviour, [NotNull] string key, bool ignoreCase) : this(matchBehaviour, key, ignoreCase, (IStringMatcher[])null)
{
}
@@ -43,8 +49,9 @@ namespace WireMock.Matchers.Request
/// </summary>
/// <param name="matchBehaviour">The match behaviour.</param>
/// <param name="key">The key.</param>
/// <param name="ignoreCase">Defines if the key should be matched using case-ignore.</param>
/// <param name="values">The values.</param>
public RequestMessageParamMatcher(MatchBehaviour matchBehaviour, [NotNull] string key, [CanBeNull] string[] values) : this(matchBehaviour, key, values?.Select(value => new ExactMatcher(matchBehaviour, value)).Cast<IStringMatcher>().ToArray())
public RequestMessageParamMatcher(MatchBehaviour matchBehaviour, [NotNull] string key, bool ignoreCase, [CanBeNull] string[] values) : this(matchBehaviour, key, ignoreCase, values?.Select(value => new ExactMatcher(matchBehaviour, value)).Cast<IStringMatcher>().ToArray())
{
}
@@ -53,13 +60,15 @@ namespace WireMock.Matchers.Request
/// </summary>
/// <param name="matchBehaviour">The match behaviour.</param>
/// <param name="key">The key.</param>
/// <param name="ignoreCase">Defines if the key should be matched using case-ignore.</param>
/// <param name="matchers">The matchers.</param>
public RequestMessageParamMatcher(MatchBehaviour matchBehaviour, [NotNull] string key, [CanBeNull] IStringMatcher[] matchers)
public RequestMessageParamMatcher(MatchBehaviour matchBehaviour, [NotNull] string key, bool ignoreCase, [CanBeNull] IStringMatcher[] matchers)
{
Check.NotNull(key, nameof(key));
_matchBehaviour = matchBehaviour;
Key = key;
IgnoreCase = ignoreCase;
Matchers = matchers;
}
@@ -88,7 +97,7 @@ namespace WireMock.Matchers.Request
return MatchScores.ToScore(requestMessage.Query != null && Funcs.Any(f => f(requestMessage.Query)));
}
WireMockList<string> valuesPresentInRequestMessage = requestMessage.GetParameter(Key);
WireMockList<string> valuesPresentInRequestMessage = requestMessage.GetParameter(Key, IgnoreCase ?? false);
if (valuesPresentInRequestMessage == null)
{
// Key is not present at all, just return Mismatch
@@ -97,15 +106,8 @@ namespace WireMock.Matchers.Request
if (Matchers != null && Matchers.Any())
{
// Matchers are defined, just use the matchers to calculate the match score.
var scores = new List<double>();
foreach (string valuePresentInRequestMessage in valuesPresentInRequestMessage)
{
double score = Matchers.Max(m => m.IsMatch(valuePresentInRequestMessage));
scores.Add(score);
}
return scores.Any() ? scores.Average() : MatchScores.Mismatch;
// Return the score based on Matchers and valuesPresentInRequestMessage
return CalculateScore(valuesPresentInRequestMessage);
}
if (Matchers == null || !Matchers.Any())
@@ -116,5 +118,35 @@ namespace WireMock.Matchers.Request
return MatchScores.Mismatch;
}
private double CalculateScore(WireMockList<string> valuesPresentInRequestMessage)
{
var total = new List<double>();
// If the total patterns in all matchers > values in message, use the matcher as base
if (Matchers.Sum(m => m.GetPatterns().Length) > valuesPresentInRequestMessage.Count)
{
foreach (var matcher in Matchers)
{
double score = 0d;
foreach (string valuePresentInRequestMessage in valuesPresentInRequestMessage)
{
score += matcher.IsMatch(valuePresentInRequestMessage) / matcher.GetPatterns().Length;
}
total.Add(score);
}
}
else
{
foreach (string valuePresentInRequestMessage in valuesPresentInRequestMessage)
{
double score = Matchers.Max(m => m.IsMatch(valuePresentInRequestMessage));
total.Add(score);
}
}
return total.Any() ? MatchScores.ToScore(total) : MatchScores.Mismatch;
}
}
}

View File

@@ -56,7 +56,18 @@ namespace WireMock.Owin
public Task StartAsync()
{
_host = new WebHostBuilder()
var builder = new WebHostBuilder();
// Workaround for https://github.com/WireMock-Net/WireMock.Net/issues/292
// On some platforms, AppContext.BaseDirectory is null, which causes WebHostBuilder to fail if ContentRoot is not
// specified (even though we don't actually use that base path mechanism, since we have our own way of configuring
// a filesystem handler).
if (string.IsNullOrEmpty(AppContext.BaseDirectory))
{
builder.UseContentRoot(System.IO.Directory.GetCurrentDirectory());
}
_host = builder
.ConfigureServices(services =>
{
services.AddSingleton(_options.FileSystemHandler);

View File

@@ -1,7 +1,7 @@
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("WireMock.Net.StandAlone")]
[assembly: InternalsVisibleTo("WireMock.Net.Tests")]
[assembly: InternalsVisibleTo("WireMock.Net.StandAlone, PublicKey=0024000004800000940000000602000000240000525341310004000001000100e138ec44d93acac565953052636eb8d5e7e9f27ddb030590055cd1a0ab2069a5623f1f77ca907d78e0b37066ca0f6d63da7eecc3fcb65b76aa8ebeccf7ebe1d11264b8404cd9b1cbbf2c83f566e033b3e54129f6ef28daffff776ba7aebbc53c0d635ebad8f45f78eb3f7e0459023c218f003416e080f96a1a3c5ffeb56bee9e")]
[assembly: InternalsVisibleTo("WireMock.Net.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100e138ec44d93acac565953052636eb8d5e7e9f27ddb030590055cd1a0ab2069a5623f1f77ca907d78e0b37066ca0f6d63da7eecc3fcb65b76aa8ebeccf7ebe1d11264b8404cd9b1cbbf2c83f566e033b3e54129f6ef28daffff776ba7aebbc53c0d635ebad8f45f78eb3f7e0459023c218f003416e080f96a1a3c5ffeb56bee9e")]
// Needed for Moq in the UnitTest project
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")]
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")]

View File

@@ -1,5 +1,5 @@
using System;
using JetBrains.Annotations;
using JetBrains.Annotations;
using System;
using WireMock.Matchers;
namespace WireMock.RequestBuilders
@@ -16,6 +16,13 @@ namespace WireMock.RequestBuilders
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
IRequestBuilder WithBody([NotNull] IMatcher matcher);
/// <summary>
/// WithBody: IMatcher[]
/// </summary>
/// <param name="matchers">The matchers.</param>
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
IRequestBuilder WithBody([NotNull] IMatcher[] matchers);
/// <summary>
/// WithBody: Body as string
/// </summary>

View File

@@ -1,6 +1,6 @@
using System;
using JetBrains.Annotations;
using System;
using System.Collections.Generic;
using JetBrains.Annotations;
using WireMock.Matchers;
using WireMock.Util;
@@ -19,6 +19,15 @@ namespace WireMock.RequestBuilders
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
IRequestBuilder WithParam([NotNull] string key, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch);
/// <summary>
/// WithParam: matching on key only.
/// </summary>
/// <param name="key">The key.</param>
/// <param name="ignoreCase">Defines if the key should be matched using case-ignore.</param>
/// <param name="matchBehaviour">The match behaviour (optional).</param>
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
IRequestBuilder WithParam([NotNull] string key, bool ignoreCase, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch);
/// <summary>
/// WithParam: matching on key and values.
/// </summary>
@@ -27,6 +36,15 @@ namespace WireMock.RequestBuilders
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
IRequestBuilder WithParam([NotNull] string key, [CanBeNull] params string[] values);
/// <summary>
/// WithParam: matching on key and values.
/// </summary>
/// <param name="key">The key.</param>
/// <param name="ignoreCase">Defines if the key should be matched using case-ignore.</param>
/// <param name="values">The values.</param>
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
IRequestBuilder WithParam([NotNull] string key, bool ignoreCase, [CanBeNull] params string[] values);
/// <summary>
/// WithParam: matching on key and matchers.
/// </summary>
@@ -35,6 +53,15 @@ namespace WireMock.RequestBuilders
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
IRequestBuilder WithParam([NotNull] string key, [CanBeNull] params IStringMatcher[] matchers);
/// <summary>
/// WithParam: matching on key and matchers.
/// </summary>
/// <param name="key">The key.</param>
/// <param name="ignoreCase">Defines if the key should be matched using case-ignore.</param>
/// <param name="matchers">The matchers.</param>
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
IRequestBuilder WithParam([NotNull] string key, bool ignoreCase, [CanBeNull] params IStringMatcher[] matchers);
/// <summary>
/// WithParam: matching on key, values and matchBehaviour.
/// </summary>
@@ -44,6 +71,16 @@ namespace WireMock.RequestBuilders
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
IRequestBuilder WithParam([NotNull] string key, MatchBehaviour matchBehaviour, [CanBeNull] params string[] values);
/// <summary>
/// WithParam: matching on key, values and matchBehaviour.
/// </summary>
/// <param name="key">The key.</param>
/// <param name="ignoreCase">Defines if the key should be matched using case-ignore.</param>
/// <param name="values">The values.</param>
/// <param name="matchBehaviour">The match behaviour.</param>
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
IRequestBuilder WithParam([NotNull] string key, MatchBehaviour matchBehaviour, bool ignoreCase = false, [CanBeNull] params string[] values);
/// <summary>
/// WithParam: matching on key, matchers and matchBehaviour.
/// </summary>
@@ -53,6 +90,16 @@ namespace WireMock.RequestBuilders
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
IRequestBuilder WithParam([NotNull] string key, MatchBehaviour matchBehaviour, [CanBeNull] params IStringMatcher[] matchers);
/// <summary>
/// WithParam: matching on key, matchers and matchBehaviour.
/// </summary>
/// <param name="key">The key.</param>
/// <param name="ignoreCase">Defines if the key should be matched using case-ignore.</param>
/// <param name="matchers">The matchers.</param>
/// <param name="matchBehaviour">The match behaviour.</param>
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
IRequestBuilder WithParam([NotNull] string key, MatchBehaviour matchBehaviour, bool ignoreCase = false, [CanBeNull] params IStringMatcher[] matchers);
/// <summary>
/// WithParam: matching on functions.
/// </summary>

View File

@@ -0,0 +1,73 @@
using System;
using WireMock.Matchers;
using WireMock.Matchers.Request;
using WireMock.Validation;
namespace WireMock.RequestBuilders
{
public partial class Request
{
/// <inheritdoc cref="IBodyRequestBuilder.WithBody(string, MatchBehaviour)"/>
public IRequestBuilder WithBody(string body, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch)
{
_requestMatchers.Add(new RequestMessageBodyMatcher(matchBehaviour, body));
return this;
}
/// <inheritdoc cref="IBodyRequestBuilder.WithBody(byte[], MatchBehaviour)"/>
public IRequestBuilder WithBody(byte[] body, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch)
{
_requestMatchers.Add(new RequestMessageBodyMatcher(matchBehaviour, body));
return this;
}
/// <inheritdoc cref="IBodyRequestBuilder.WithBody(object, MatchBehaviour)"/>
public IRequestBuilder WithBody(object body, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch)
{
_requestMatchers.Add(new RequestMessageBodyMatcher(matchBehaviour, body));
return this;
}
/// <inheritdoc cref="IBodyRequestBuilder.WithBody(IMatcher[])"/>
public IRequestBuilder WithBody(IMatcher matcher)
{
return WithBody(new[] { matcher });
}
/// <inheritdoc cref="IBodyRequestBuilder.WithBody(IMatcher[])"/>
public IRequestBuilder WithBody(IMatcher[] matchers)
{
Check.NotNull(matchers, nameof(matchers));
_requestMatchers.Add(new RequestMessageBodyMatcher(matchers));
return this;
}
/// <inheritdoc cref="IBodyRequestBuilder.WithBody(Func{string, bool})"/>
public IRequestBuilder WithBody(Func<string, bool> func)
{
Check.NotNull(func, nameof(func));
_requestMatchers.Add(new RequestMessageBodyMatcher(func));
return this;
}
/// <inheritdoc cref="IBodyRequestBuilder.WithBody(Func{byte[], bool})"/>
public IRequestBuilder WithBody(Func<byte[], bool> func)
{
Check.NotNull(func, nameof(func));
_requestMatchers.Add(new RequestMessageBodyMatcher(func));
return this;
}
/// <inheritdoc cref="IBodyRequestBuilder.WithBody(Func{object, bool})"/>
public IRequestBuilder WithBody(Func<object, bool> func)
{
Check.NotNull(func, nameof(func));
_requestMatchers.Add(new RequestMessageBodyMatcher(func));
return this;
}
}
}

View File

@@ -0,0 +1,90 @@
using System;
using System.Collections.Generic;
using WireMock.Matchers;
using WireMock.Matchers.Request;
using WireMock.Util;
using WireMock.Validation;
namespace WireMock.RequestBuilders
{
public partial class Request
{
/// <inheritdoc cref="IParamsRequestBuilder.WithParam(string, MatchBehaviour)"/>
public IRequestBuilder WithParam(string key, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch)
{
return WithParam(key, false, matchBehaviour);
}
/// <inheritdoc cref="IParamsRequestBuilder.WithParam(string, bool, MatchBehaviour)"/>
public IRequestBuilder WithParam(string key, bool ignoreCase, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch)
{
Check.NotNull(key, nameof(key));
_requestMatchers.Add(new RequestMessageParamMatcher(matchBehaviour, key, ignoreCase));
return this;
}
/// <inheritdoc cref="IParamsRequestBuilder.WithParam(string, string[])"/>
public IRequestBuilder WithParam(string key, params string[] values)
{
return WithParam(key, MatchBehaviour.AcceptOnMatch, false, values);
}
/// <inheritdoc cref="IParamsRequestBuilder.WithParam(string, bool, string[])"/>
public IRequestBuilder WithParam(string key, bool ignoreCase, params string[] values)
{
return WithParam(key, MatchBehaviour.AcceptOnMatch, ignoreCase, values);
}
/// <inheritdoc cref="IParamsRequestBuilder.WithParam(string, IStringMatcher[])"/>
public IRequestBuilder WithParam(string key, params IStringMatcher[] matchers)
{
return WithParam(key, MatchBehaviour.AcceptOnMatch, false, matchers);
}
/// <inheritdoc cref="IParamsRequestBuilder.WithParam(string, bool, IStringMatcher[])"/>
public IRequestBuilder WithParam(string key, bool ignoreCase, params IStringMatcher[] matchers)
{
return WithParam(key, MatchBehaviour.AcceptOnMatch, ignoreCase, matchers);
}
/// <inheritdoc cref="IParamsRequestBuilder.WithParam(string, MatchBehaviour, string[])"/>
public IRequestBuilder WithParam(string key, MatchBehaviour matchBehaviour, params string[] values)
{
return WithParam(key, matchBehaviour, false, values);
}
/// <inheritdoc cref="IParamsRequestBuilder.WithParam(string, MatchBehaviour, bool, string[])"/>
public IRequestBuilder WithParam(string key, MatchBehaviour matchBehaviour, bool ignoreCase = false, params string[] values)
{
Check.NotNull(key, nameof(key));
_requestMatchers.Add(new RequestMessageParamMatcher(matchBehaviour, key, ignoreCase, values));
return this;
}
/// <inheritdoc cref="IParamsRequestBuilder.WithParam(string, MatchBehaviour, IStringMatcher[])"/>
public IRequestBuilder WithParam(string key, MatchBehaviour matchBehaviour, params IStringMatcher[] matchers)
{
return WithParam(key, matchBehaviour, false, matchers);
}
/// <inheritdoc cref="IParamsRequestBuilder.WithParam(string, MatchBehaviour, bool, IStringMatcher[])"/>
public IRequestBuilder WithParam(string key, MatchBehaviour matchBehaviour, bool ignoreCase, params IStringMatcher[] matchers)
{
Check.NotNull(key, nameof(key));
_requestMatchers.Add(new RequestMessageParamMatcher(matchBehaviour, key, ignoreCase, matchers));
return this;
}
/// <inheritdoc cref="IParamsRequestBuilder.WithParam(Func{IDictionary{string, WireMockList{string}}, bool}[])"/>
public IRequestBuilder WithParam(params Func<IDictionary<string, WireMockList<string>>, bool>[] funcs)
{
Check.NotNullOrEmpty(funcs, nameof(funcs));
_requestMatchers.Add(new RequestMessageParamMatcher(funcs));
return this;
}
}
}

View File

@@ -4,7 +4,6 @@ using System.Collections.ObjectModel;
using System.Linq;
using WireMock.Matchers;
using WireMock.Matchers.Request;
using WireMock.Util;
using WireMock.Validation;
namespace WireMock.RequestBuilders
@@ -12,7 +11,7 @@ namespace WireMock.RequestBuilders
/// <summary>
/// The requests.
/// </summary>
public class Request : RequestMessageCompositeMatcher, IRequestBuilder
public partial class Request : RequestMessageCompositeMatcher, IRequestBuilder
{
private readonly IList<IRequestMatcher> _requestMatchers;
@@ -234,111 +233,6 @@ namespace WireMock.RequestBuilders
return this;
}
/// <inheritdoc cref="IBodyRequestBuilder.WithBody(string, MatchBehaviour)"/>
public IRequestBuilder WithBody(string body, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch)
{
_requestMatchers.Add(new RequestMessageBodyMatcher(matchBehaviour, body));
return this;
}
/// <inheritdoc cref="IBodyRequestBuilder.WithBody(byte[], MatchBehaviour)"/>
public IRequestBuilder WithBody(byte[] body, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch)
{
_requestMatchers.Add(new RequestMessageBodyMatcher(matchBehaviour, body));
return this;
}
/// <inheritdoc cref="IBodyRequestBuilder.WithBody(object, MatchBehaviour)"/>
public IRequestBuilder WithBody(object body, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch)
{
_requestMatchers.Add(new RequestMessageBodyMatcher(matchBehaviour, body));
return this;
}
/// <inheritdoc cref="IBodyRequestBuilder.WithBody(IMatcher)"/>
public IRequestBuilder WithBody(IMatcher matcher)
{
Check.NotNull(matcher, nameof(matcher));
_requestMatchers.Add(new RequestMessageBodyMatcher(matcher));
return this;
}
/// <inheritdoc cref="IBodyRequestBuilder.WithBody(Func{string, bool})"/>
public IRequestBuilder WithBody(Func<string, bool> func)
{
Check.NotNull(func, nameof(func));
_requestMatchers.Add(new RequestMessageBodyMatcher(func));
return this;
}
/// <inheritdoc cref="IBodyRequestBuilder.WithBody(Func{byte[], bool})"/>
public IRequestBuilder WithBody(Func<byte[], bool> func)
{
Check.NotNull(func, nameof(func));
_requestMatchers.Add(new RequestMessageBodyMatcher(func));
return this;
}
/// <inheritdoc cref="IBodyRequestBuilder.WithBody(Func{object, bool})"/>
public IRequestBuilder WithBody(Func<object, bool> func)
{
Check.NotNull(func, nameof(func));
_requestMatchers.Add(new RequestMessageBodyMatcher(func));
return this;
}
/// <inheritdoc cref="IParamsRequestBuilder.WithParam(string, MatchBehaviour)"/>
public IRequestBuilder WithParam(string key, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch)
{
Check.NotNull(key, nameof(key));
_requestMatchers.Add(new RequestMessageParamMatcher(matchBehaviour, key));
return this;
}
/// <inheritdoc cref="IParamsRequestBuilder.WithParam(string, string[])"/>
public IRequestBuilder WithParam(string key, params string[] values)
{
return WithParam(key, MatchBehaviour.AcceptOnMatch, values);
}
/// <inheritdoc cref="IParamsRequestBuilder.WithParam(string, IStringMatcher[])"/>
public IRequestBuilder WithParam(string key, params IStringMatcher[] matchers)
{
return WithParam(key, MatchBehaviour.AcceptOnMatch, matchers);
}
/// <inheritdoc cref="IParamsRequestBuilder.WithParam(string, MatchBehaviour, string[])"/>
public IRequestBuilder WithParam(string key, MatchBehaviour matchBehaviour, params string[] values)
{
Check.NotNull(key, nameof(key));
_requestMatchers.Add(new RequestMessageParamMatcher(matchBehaviour, key, values));
return this;
}
/// <inheritdoc cref="IParamsRequestBuilder.WithParam(string, MatchBehaviour, IStringMatcher[])"/>
public IRequestBuilder WithParam(string key, MatchBehaviour matchBehaviour, params IStringMatcher[] matchers)
{
Check.NotNull(key, nameof(key));
_requestMatchers.Add(new RequestMessageParamMatcher(matchBehaviour, key, matchers));
return this;
}
/// <inheritdoc cref="IParamsRequestBuilder.WithParam(Func{IDictionary{string, WireMockList{string}}, bool}[])"/>
public IRequestBuilder WithParam(params Func<IDictionary<string, WireMockList<string>>, bool>[] funcs)
{
Check.NotNullOrEmpty(funcs, nameof(funcs));
_requestMatchers.Add(new RequestMessageParamMatcher(funcs));
return this;
}
/// <inheritdoc cref="IHeadersAndCookiesRequestBuilder.WithHeader(string, string, MatchBehaviour)"/>
public IRequestBuilder WithHeader(string name, string pattern, MatchBehaviour matchBehaviour)
{

View File

@@ -1,8 +1,8 @@
using System;
using JetBrains.Annotations;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using JetBrains.Annotations;
using WireMock.Models;
using WireMock.Util;
using WireMock.Validation;
@@ -171,55 +171,25 @@ namespace WireMock
Headers = headers?.ToDictionary(header => header.Key, header => new WireMockList<string>(header.Value));
Cookies = cookies;
RawQuery = WebUtility.UrlDecode(urlDetails.Url.Query);
Query = ParseQuery(RawQuery);
}
private static IDictionary<string, WireMockList<string>> ParseQuery(string queryString)
{
if (string.IsNullOrEmpty(queryString))
{
return null;
}
if (queryString.StartsWith("?"))
{
queryString = queryString.Substring(1);
}
return queryString.Split(new[] { '&' }, StringSplitOptions.RemoveEmptyEntries)
.Aggregate(new Dictionary<string, WireMockList<string>>(),
(dict, term) =>
{
string[] parts = term.Split(new[] { '=' }, StringSplitOptions.RemoveEmptyEntries);
string key = parts[0];
if (!dict.ContainsKey(key))
{
dict.Add(key, new WireMockList<string>());
}
if (parts.Length == 2)
{
string[] values = parts[1].Split(new[] { "," }, StringSplitOptions.RemoveEmptyEntries);
dict[key].AddRange(values);
}
return dict;
});
Query = QueryStringParser.Parse(RawQuery);
}
/// <summary>
/// Get a query parameter.
/// </summary>
/// <param name="key">The key.</param>
/// <param name="ignoreCase">Defines if the key should be matched using case-ignore.</param>
/// <returns>The query parameter.</returns>
public WireMockList<string> GetParameter(string key)
public WireMockList<string> GetParameter(string key, bool ignoreCase = false)
{
if (Query == null)
{
return null;
}
return Query.ContainsKey(key) ? Query[key] : null;
var query = !ignoreCase ? Query : new Dictionary<string, WireMockList<string>>(Query, StringComparer.OrdinalIgnoreCase);
return query.ContainsKey(key) ? query[key] : null;
}
}
}

View File

@@ -7,8 +7,8 @@ using System.Net;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using WireMock.Handlers;
using WireMock.Http;
using WireMock.ResponseProviders;
using WireMock.Settings;
using WireMock.Transformers;
using WireMock.Util;
@@ -21,7 +21,6 @@ namespace WireMock.ResponseBuilders
/// </summary>
public class Response : IResponseBuilder
{
private readonly IFileSystemHandler _fileSystemHandler = new LocalFileSystemHandler();
private HttpClient _httpClientForProxy;
/// <summary>
@@ -221,18 +220,17 @@ namespace WireMock.ResponseBuilders
ResponseMessage.BodyData = new BodyData
{
BodyAsFileIsCached = cache
BodyAsFileIsCached = cache,
BodyAsFile = filename
};
if (cache)
if (cache && !UseTransformer)
{
ResponseMessage.BodyData.DetectedBodyType = BodyType.Bytes;
ResponseMessage.BodyData.BodyAsBytes = _fileSystemHandler.ReadResponseBodyAsFile(filename);
}
else
{
ResponseMessage.BodyData.DetectedBodyType = BodyType.File;
ResponseMessage.BodyData.BodyAsFile = filename;
}
return this;
@@ -373,12 +371,8 @@ namespace WireMock.ResponseBuilders
return this;
}
/// <summary>
/// The provide response.
/// </summary>
/// <param name="requestMessage">The request.</param>
/// <returns>The <see cref="ResponseMessage"/>.</returns>
public async Task<ResponseMessage> ProvideResponseAsync(RequestMessage requestMessage)
/// <inheritdoc cref="IResponseProvider.ProvideResponseAsync(RequestMessage, IFluentMockServerSettings)"/>
public async Task<ResponseMessage> ProvideResponseAsync(RequestMessage requestMessage, IFluentMockServerSettings settings)
{
Check.NotNull(requestMessage, nameof(requestMessage));
@@ -417,10 +411,16 @@ namespace WireMock.ResponseBuilders
if (UseTransformer)
{
return ResponseMessageTransformer.Transform(requestMessage, ResponseMessage);
var factory = new HandlebarsContextFactory(settings.FileSystemHandler, settings.HandlebarsRegistrationCallback);
var responseMessageTransformer = new ResponseMessageTransformer(factory);
return responseMessageTransformer.Transform(requestMessage, ResponseMessage);
}
if (!UseTransformer && ResponseMessage.BodyData?.BodyAsFileIsCached == true)
{
ResponseMessage.BodyData.BodyAsBytes = settings.FileSystemHandler.ReadResponseBodyAsFile(ResponseMessage.BodyData.BodyAsFile);
}
// Just return normal defined ResponseMessage
return ResponseMessage;
}
}

View File

@@ -9,7 +9,10 @@ namespace WireMock
internal static class ResponseMessageBuilder
{
private static string ContentTypeJson = "application/json";
private static readonly IDictionary<string, WireMockList<string>> ContentTypeJsonHeaders = new Dictionary<string, WireMockList<string>> { { HttpKnownHeaderNames.ContentType, new WireMockList<string> { ContentTypeJson } } };
private static readonly IDictionary<string, WireMockList<string>> ContentTypeJsonHeaders = new Dictionary<string, WireMockList<string>>
{
{ HttpKnownHeaderNames.ContentType, new WireMockList<string> { ContentTypeJson } }
};
internal static ResponseMessage Create(string message, int statusCode = 200, Guid? guid = null)
{
@@ -24,11 +27,23 @@ namespace WireMock
response.BodyData = new BodyData
{
DetectedBodyType = BodyType.Json,
BodyAsJson = new StatusModel { Status = message, Guid = guid }
BodyAsJson = new StatusModel
{
Guid = guid,
Status = message
}
};
}
return response;
}
internal static ResponseMessage Create(int statusCode)
{
return new ResponseMessage
{
StatusCode = statusCode
};
}
}
}

View File

@@ -1,7 +1,6 @@
using System;
using System.Threading.Tasks;
using JetBrains.Annotations;
using WireMock.Validation;
using WireMock.Settings;
namespace WireMock.ResponseProviders
{
@@ -9,14 +8,12 @@ namespace WireMock.ResponseProviders
{
private readonly Func<RequestMessage, Task<ResponseMessage>> _responseMessageFunc;
public DynamicAsyncResponseProvider([NotNull] Func<RequestMessage, Task<ResponseMessage>> responseMessageFunc)
public DynamicAsyncResponseProvider(Func<RequestMessage, Task<ResponseMessage>> responseMessageFunc)
{
Check.NotNull(responseMessageFunc, nameof(responseMessageFunc));
_responseMessageFunc = responseMessageFunc;
}
public Task<ResponseMessage> ProvideResponseAsync(RequestMessage requestMessage)
public Task<ResponseMessage> ProvideResponseAsync(RequestMessage requestMessage, IFluentMockServerSettings settings)
{
return _responseMessageFunc(requestMessage);
}

View File

@@ -1,7 +1,6 @@
using System;
using System.Threading.Tasks;
using JetBrains.Annotations;
using WireMock.Validation;
using WireMock.Settings;
namespace WireMock.ResponseProviders
{
@@ -9,14 +8,12 @@ namespace WireMock.ResponseProviders
{
private readonly Func<RequestMessage, ResponseMessage> _responseMessageFunc;
public DynamicResponseProvider([NotNull] Func<RequestMessage, ResponseMessage> responseMessageFunc)
public DynamicResponseProvider(Func<RequestMessage, ResponseMessage> responseMessageFunc)
{
Check.NotNull(responseMessageFunc, nameof(responseMessageFunc));
_responseMessageFunc = responseMessageFunc;
}
public Task<ResponseMessage> ProvideResponseAsync(RequestMessage requestMessage)
public Task<ResponseMessage> ProvideResponseAsync(RequestMessage requestMessage, IFluentMockServerSettings settings)
{
return Task.FromResult(_responseMessageFunc(requestMessage));
}

View File

@@ -1,5 +1,6 @@
using System.Threading.Tasks;
using JetBrains.Annotations;
using JetBrains.Annotations;
using System.Threading.Tasks;
using WireMock.Settings;
namespace WireMock.ResponseProviders
{
@@ -12,7 +13,8 @@ namespace WireMock.ResponseProviders
/// The provide response.
/// </summary>
/// <param name="requestMessage">The request.</param>
/// <param name="settings">The FluentMockServerSettings.</param>
/// <returns>The <see cref="ResponseMessage"/>.</returns>
Task<ResponseMessage> ProvideResponseAsync([NotNull] RequestMessage requestMessage);
Task<ResponseMessage> ProvideResponseAsync([NotNull] RequestMessage requestMessage, [NotNull] IFluentMockServerSettings settings);
}
}

View File

@@ -1,26 +1,21 @@
using System;
using System.Threading.Tasks;
using JetBrains.Annotations;
using WireMock.Settings;
using WireMock.Validation;
namespace WireMock.ResponseProviders
{
internal class ProxyAsyncResponseProvider : IResponseProvider
{
private readonly Func<RequestMessage, IProxyAndRecordSettings, Task<ResponseMessage>> _responseMessageFunc;
private readonly IProxyAndRecordSettings _settings;
private readonly Func<RequestMessage, IFluentMockServerSettings, Task<ResponseMessage>> _responseMessageFunc;
private readonly IFluentMockServerSettings _settings;
public ProxyAsyncResponseProvider([NotNull] Func<RequestMessage, IProxyAndRecordSettings, Task<ResponseMessage>> responseMessageFunc, [NotNull] IProxyAndRecordSettings settings)
public ProxyAsyncResponseProvider(Func<RequestMessage, IFluentMockServerSettings, Task<ResponseMessage>> responseMessageFunc, IFluentMockServerSettings settings)
{
Check.NotNull(responseMessageFunc, nameof(responseMessageFunc));
Check.NotNull(settings, nameof(settings));
_responseMessageFunc = responseMessageFunc;
_settings = settings;
}
public Task<ResponseMessage> ProvideResponseAsync(RequestMessage requestMessage)
public Task<ResponseMessage> ProvideResponseAsync(RequestMessage requestMessage, IFluentMockServerSettings settings)
{
return _responseMessageFunc(requestMessage, _settings);
}

View File

@@ -21,14 +21,14 @@ namespace WireMock.Serialization
var headerMatchers = request.GetRequestMessageMatchers<RequestMessageHeaderMatcher>();
var cookieMatchers = request.GetRequestMessageMatchers<RequestMessageCookieMatcher>();
var paramsMatchers = request.GetRequestMessageMatchers<RequestMessageParamMatcher>();
var bodyMatcher = request.GetRequestMessageMatcher<RequestMessageBodyMatcher>();
var methodMatcher = request.GetRequestMessageMatcher<RequestMessageMethodMatcher>();
var bodyMatcher = request.GetRequestMessageMatcher<RequestMessageBodyMatcher>();
var mappingModel = new MappingModel
{
Guid = mapping.Guid,
Title = mapping.Title,
Priority = mapping.Priority,
Priority = mapping.Priority != 0 ? mapping.Priority : (int?)null,
Scenario = mapping.Scenario,
WhenStateIs = mapping.ExecutionConditionState,
SetStateTo = mapping.NextState,
@@ -66,20 +66,30 @@ namespace WireMock.Serialization
Params = paramsMatchers != null && paramsMatchers.Any() ? paramsMatchers.Select(pm => new ParamModel
{
Name = pm.Key,
IgnoreCase = pm.IgnoreCase == true ? true : (bool?)null,
Matchers = MatcherMapper.Map(pm.Matchers)
}).ToList() : null,
Body = methodMatcher?.Methods != null && methodMatcher.Methods.Any(m => m == "get") ? null : new BodyModel
{
Matcher = bodyMatcher != null ? MatcherMapper.Map(bodyMatcher.Matcher) : null
}
}).ToList() : null
},
Response = new ResponseModel
{
Delay = response.Delay?.Milliseconds
Delay = (int?)response.Delay?.TotalMilliseconds
}
};
if (methodMatcher?.Methods != null && methodMatcher.Methods.All(m => m != "get") && bodyMatcher?.Matchers != null)
{
mappingModel.Request.Body = new BodyModel();
if (bodyMatcher.Matchers.Length == 1)
{
mappingModel.Request.Body.Matcher = MatcherMapper.Map(bodyMatcher.Matchers[0]);
}
else if (bodyMatcher.Matchers.Length > 1)
{
mappingModel.Request.Body.Matchers = MatcherMapper.Map(bodyMatcher.Matchers);
}
}
if (!string.IsNullOrEmpty(response.ProxyUrl))
{
mappingModel.Response.StatusCode = null;
@@ -91,7 +101,7 @@ namespace WireMock.Serialization
mappingModel.Response.BodyAsBytes = null;
mappingModel.Response.BodyAsFile = null;
mappingModel.Response.BodyAsFileIsCached = null;
mappingModel.Response.UseTransformer = false;
mappingModel.Response.UseTransformer = null;
mappingModel.Response.BodyEncoding = null;
mappingModel.Response.ProxyUrl = response.ProxyUrl;
}
@@ -100,7 +110,10 @@ namespace WireMock.Serialization
mappingModel.Response.BodyDestination = response.ResponseMessage.BodyDestination;
mappingModel.Response.StatusCode = response.ResponseMessage.StatusCode;
mappingModel.Response.Headers = Map(response.ResponseMessage.Headers);
mappingModel.Response.UseTransformer = response.UseTransformer;
if (response.UseTransformer)
{
mappingModel.Response.UseTransformer = response.UseTransformer;
}
if (response.ResponseMessage.BodyData != null)
{
@@ -112,7 +125,10 @@ namespace WireMock.Serialization
case BodyType.Json:
mappingModel.Response.BodyAsJson = response.ResponseMessage.BodyData.BodyAsJson;
mappingModel.Response.BodyAsJsonIndented = response.ResponseMessage.BodyData.BodyAsJsonIndented;
if (response.ResponseMessage.BodyData.BodyAsJsonIndented == true)
{
mappingModel.Response.BodyAsJsonIndented = response.ResponseMessage.BodyData.BodyAsJsonIndented;
}
break;
case BodyType.Bytes:

View File

@@ -1,8 +1,8 @@
using System;
using JetBrains.Annotations;
using SimMetrics.Net;
using System;
using System.Collections.Generic;
using System.Linq;
using JetBrains.Annotations;
using SimMetrics.Net;
using WireMock.Admin.Mappings;
using WireMock.Matchers;
@@ -10,6 +10,11 @@ namespace WireMock.Serialization
{
internal static class MatcherMapper
{
public static IMatcher[] Map([CanBeNull] IEnumerable<MatcherModel> matchers)
{
return matchers?.Select(Map).Where(m => m != null).ToArray();
}
public static IMatcher Map([CanBeNull] MatcherModel matcher)
{
if (matcher == null)
@@ -32,6 +37,10 @@ namespace WireMock.Serialization
case "ExactMatcher":
return new ExactMatcher(matchBehaviour, stringPatterns);
case "ExactObjectMatcher":
var bytePattern = Convert.FromBase64String(stringPatterns[0]);
return new ExactObjectMatcher(matchBehaviour, bytePattern);
case "RegexMatcher":
return new RegexMatcher(matchBehaviour, stringPatterns, matcher.IgnoreCase == true);
@@ -41,6 +50,9 @@ namespace WireMock.Serialization
case "JsonPathMatcher":
return new JsonPathMatcher(matchBehaviour, stringPatterns);
case "JmesPathMatcher":
return new JmesPathMatcher(matchBehaviour, stringPatterns);
case "XPathMatcher":
return new XPathMatcher(matchBehaviour, (string)matcher.Pattern);
@@ -63,7 +75,7 @@ namespace WireMock.Serialization
public static MatcherModel[] Map([CanBeNull] IEnumerable<IMatcher> matchers)
{
return matchers?.Select(Map).Where(x => x != null).ToArray();
return matchers?.Select(Map).Where(m => m != null).ToArray();
}
public static MatcherModel Map([CanBeNull] IMatcher matcher)
@@ -73,20 +85,33 @@ namespace WireMock.Serialization
return null;
}
// If the matcher is a IStringMatcher, get the patterns.
// If the matcher is a IValueMatcher, get the value (can be string or object).
// Else empty array
object[] patterns = matcher is IStringMatcher stringMatcher ?
stringMatcher.GetPatterns().Cast<object>().ToArray() :
matcher is IValueMatcher valueMatcher ? new[] { valueMatcher.Value } :
new object[0];
bool? ignorecase = matcher is IIgnoreCaseMatcher ignoreCaseMatcher ? ignoreCaseMatcher.IgnoreCase : (bool?)null;
object[] patterns = new object[0]; // Default empty array
switch (matcher)
{
// If the matcher is a IStringMatcher, get the patterns.
case IStringMatcher stringMatcher:
patterns = stringMatcher.GetPatterns().Cast<object>().ToArray();
break;
// If the matcher is a IValueMatcher, get the value (can be string or object).
case IValueMatcher valueMatcher:
patterns = new[] { valueMatcher.Value };
break;
// If the matcher is a ExactObjectMatcher, get the ValueAsObject or ValueAsBytes.
case ExactObjectMatcher exactObjectMatcher:
patterns = new[] { exactObjectMatcher.ValueAsObject ?? exactObjectMatcher.ValueAsBytes };
break;
}
bool? ignoreCase = matcher is IIgnoreCaseMatcher ignoreCaseMatcher ? ignoreCaseMatcher.IgnoreCase : (bool?)null;
bool? rejectOnMatch = matcher.MatchBehaviour == MatchBehaviour.RejectOnMatch ? true : (bool?)null;
return new MatcherModel
{
RejectOnMatch = rejectOnMatch,
IgnoreCase = ignorecase,
IgnoreCase = ignoreCase,
Name = matcher.Name,
Pattern = patterns.Length == 1 ? patterns.First() : null,
Patterns = patterns.Length > 1 ? patterns : null

View File

@@ -34,15 +34,16 @@ namespace WireMock.Server
private const int AdminPriority = int.MinValue;
private const int ProxyPriority = 1000;
private const string ContentTypeJson = "application/json";
private const string AdminFiles = "/__admin/files";
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})$");
private readonly RegexMatcher _adminMappingsGuidPathMatcher = new RegexMatcher(MatchBehaviour.AcceptOnMatch, @"^\/__admin\/mappings\/([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})$");
private readonly RegexMatcher _adminRequestsGuidPathMatcher = new RegexMatcher(MatchBehaviour.AcceptOnMatch, @"^\/__admin\/requests\/([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})$");
private readonly JsonSerializerSettings _settings = new JsonSerializerSettings
private readonly JsonSerializerSettings _jsonSerializerSettings = new JsonSerializerSettings
{
Formatting = Formatting.Indented,
NullValueHandling = NullValueHandling.Ignore
@@ -97,10 +98,17 @@ namespace WireMock.Server
// __admin/scenarios/reset
Given(Request.Create().WithPath(AdminScenarios + "/reset").UsingPost()).AtPriority(AdminPriority).RespondWith(new DynamicResponseProvider(ScenariosReset));
// __admin/files/{filename}
Given(Request.Create().WithPath(_adminFilesFilenamePathMatcher).UsingPost()).AtPriority(AdminPriority).RespondWith(new DynamicResponseProvider(FilePost));
Given(Request.Create().WithPath(_adminFilesFilenamePathMatcher).UsingPut()).AtPriority(AdminPriority).RespondWith(new DynamicResponseProvider(FilePut));
Given(Request.Create().WithPath(_adminFilesFilenamePathMatcher).UsingGet()).AtPriority(AdminPriority).RespondWith(new DynamicResponseProvider(FileGet));
Given(Request.Create().WithPath(_adminFilesFilenamePathMatcher).UsingHead()).AtPriority(AdminPriority).RespondWith(new DynamicResponseProvider(FileHead));
Given(Request.Create().WithPath(_adminFilesFilenamePathMatcher).UsingDelete()).AtPriority(AdminPriority).RespondWith(new DynamicResponseProvider(FileDelete));
}
#endregion
#region StaticMappings
#region StaticMappings
/// <summary>
/// Saves the static mappings.
/// </summary>
@@ -123,18 +131,18 @@ namespace WireMock.Server
{
if (folder == null)
{
folder = _fileSystemHandler.GetMappingFolder();
folder = _settings.FileSystemHandler.GetMappingFolder();
}
if (!_fileSystemHandler.FolderExists(folder))
if (!_settings.FileSystemHandler.FolderExists(folder))
{
_logger.Info("The Static Mapping folder '{0}' does not exist, reading Static MappingFiles will be skipped.", folder);
_settings.Logger.Info("The Static Mapping folder '{0}' does not exist, reading Static MappingFiles will be skipped.", folder);
return;
}
foreach (string filename in _fileSystemHandler.EnumerateFiles(folder).OrderBy(f => f))
foreach (string filename in _settings.FileSystemHandler.EnumerateFiles(folder).OrderBy(f => f))
{
_logger.Info("Reading Static MappingFile : '{0}'", filename);
_settings.Logger.Info("Reading Static MappingFile : '{0}'", filename);
try
{
@@ -142,7 +150,7 @@ namespace WireMock.Server
}
catch
{
_logger.Error("Static MappingFile : '{0}' could not be read. This file will be skipped.", filename);
_settings.Logger.Error("Static MappingFile : '{0}' could not be read. This file will be skipped.", filename);
}
}
}
@@ -156,30 +164,36 @@ namespace WireMock.Server
{
if (folder == null)
{
folder = _fileSystemHandler.GetMappingFolder();
folder = _settings.FileSystemHandler.GetMappingFolder();
}
if (!_fileSystemHandler.FolderExists(folder))
if (!_settings.FileSystemHandler.FolderExists(folder))
{
return;
}
_logger.Info("Watching folder '{0}' for new, updated and deleted MappingFiles.", folder);
_settings.Logger.Info("Watching folder '{0}' for new, updated and deleted MappingFiles.", folder);
var watcher = new EnhancedFileSystemWatcher(folder, "*.json", EnhancedFileSystemWatcherTimeoutMs);
watcher.Created += (sender, args) =>
{
_logger.Info("New MappingFile created : '{0}'", args.FullPath);
ReadStaticMappingAndAddOrUpdate(args.FullPath);
_settings.Logger.Info("MappingFile created : '{0}', reading file.", args.FullPath);
if (!ReadStaticMappingAndAddOrUpdate(args.FullPath))
{
_settings.Logger.Error("Unable to read MappingFile '{0}'.", args.FullPath);
}
};
watcher.Changed += (sender, args) =>
{
_logger.Info("New MappingFile updated : '{0}'", args.FullPath);
ReadStaticMappingAndAddOrUpdate(args.FullPath);
_settings.Logger.Info("MappingFile updated : '{0}', reading file.", args.FullPath);
if (!ReadStaticMappingAndAddOrUpdate(args.FullPath))
{
_settings.Logger.Error("Unable to read MappingFile '{0}'.", args.FullPath);
}
};
watcher.Deleted += (sender, args) =>
{
_logger.Info("New MappingFile deleted : '{0}'", args.FullPath);
_settings.Logger.Info("MappingFile deleted : '{0}'", args.FullPath);
string filenameWithoutExtension = Path.GetFileNameWithoutExtension(args.FullPath);
if (Guid.TryParse(filenameWithoutExtension, out Guid guidFromFilename))
@@ -200,24 +214,31 @@ namespace WireMock.Server
/// </summary>
/// <param name="path">The path.</param>
[PublicAPI]
public void ReadStaticMappingAndAddOrUpdate([NotNull] string path)
public bool ReadStaticMappingAndAddOrUpdate([NotNull] string path)
{
Check.NotNull(path, nameof(path));
string filenameWithoutExtension = Path.GetFileNameWithoutExtension(path);
var mappingModels = DeserializeObjectToArray<MappingModel>(JsonConvert.DeserializeObject(_fileSystemHandler.ReadMappingFile(path)));
foreach (var mappingModel in mappingModels)
if (FileHelper.TryReadMappingFileWithRetryAndDelay(_settings.FileSystemHandler, path, out string value))
{
if (mappingModels.Length == 1 && Guid.TryParse(filenameWithoutExtension, out Guid guidFromFilename))
var mappingModels = DeserializeObjectToArray<MappingModel>(JsonConvert.DeserializeObject(value));
foreach (var mappingModel in mappingModels)
{
DeserializeAndAddOrUpdateMapping(mappingModel, guidFromFilename, path);
}
else
{
DeserializeAndAddOrUpdateMapping(mappingModel, null, path);
if (mappingModels.Length == 1 && Guid.TryParse(filenameWithoutExtension, out Guid guidFromFilename))
{
DeserializeAndAddOrUpdateMapping(mappingModel, guidFromFilename, path);
}
else
{
DeserializeAndAddOrUpdateMapping(mappingModel, null, path);
}
}
return true;
}
return false;
}
#endregion
@@ -234,23 +255,23 @@ namespace WireMock.Server
respondProvider.AtPriority(ProxyPriority);
}
respondProvider.RespondWith(new ProxyAsyncResponseProvider(ProxyAndRecordAsync, settings.ProxyAndRecordSettings));
respondProvider.RespondWith(new ProxyAsyncResponseProvider(ProxyAndRecordAsync, settings));
}
private async Task<ResponseMessage> ProxyAndRecordAsync(RequestMessage requestMessage, IProxyAndRecordSettings settings)
private async Task<ResponseMessage> ProxyAndRecordAsync(RequestMessage requestMessage, IFluentMockServerSettings settings)
{
var requestUri = new Uri(requestMessage.Url);
var proxyUri = new Uri(settings.Url);
var proxyUri = new Uri(settings.ProxyAndRecordSettings.Url);
var proxyUriWithRequestPathAndQuery = new Uri(proxyUri, requestUri.PathAndQuery);
var responseMessage = await HttpClientHelper.SendAsync(_httpClientForProxy, requestMessage, proxyUriWithRequestPathAndQuery.AbsoluteUri);
if (settings.SaveMapping)
if (settings.ProxyAndRecordSettings.SaveMapping)
{
var mapping = ToMapping(requestMessage, responseMessage, settings.BlackListedHeaders ?? new string[] { });
var mapping = ToMapping(requestMessage, responseMessage, settings.ProxyAndRecordSettings.BlackListedHeaders ?? new string[] { });
_options.Mappings.TryAdd(mapping.Guid, mapping);
if (settings.SaveMappingToFile)
if (settings.ProxyAndRecordSettings.SaveMappingToFile)
{
SaveMappingToFile(mapping);
}
@@ -265,7 +286,7 @@ namespace WireMock.Server
request.WithPath(requestMessage.Path);
request.UsingMethod(requestMessage.Method);
requestMessage.Query.Loop((key, value) => request.WithParam(key, value.ToArray()));
requestMessage.Query.Loop((key, value) => request.WithParam(key, false, value.ToArray()));
requestMessage.Cookies.Loop((key, value) => request.WithCookie(key, value));
var allBlackListedHeaders = new List<string>(blacklistedHeaders) { "Cookie" };
@@ -277,18 +298,24 @@ namespace WireMock.Server
}
});
if (requestMessage.BodyData?.DetectedBodyType == BodyType.Json)
switch (requestMessage.BodyData?.DetectedBodyType)
{
request.WithBody(new JsonMatcher(MatchBehaviour.AcceptOnMatch, requestMessage.BodyData.BodyAsJson));
}
else if (requestMessage.BodyData?.DetectedBodyType == BodyType.String)
{
request.WithBody(new ExactMatcher(MatchBehaviour.AcceptOnMatch, requestMessage.BodyData.BodyAsString));
case BodyType.Json:
request.WithBody(new JsonMatcher(MatchBehaviour.AcceptOnMatch, requestMessage.BodyData.BodyAsJson));
break;
case BodyType.String:
request.WithBody(new ExactMatcher(MatchBehaviour.AcceptOnMatch, requestMessage.BodyData.BodyAsString));
break;
case BodyType.Bytes:
request.WithBody(new ExactObjectMatcher(MatchBehaviour.AcceptOnMatch, requestMessage.BodyData.BodyAsBytes));
break;
}
var response = Response.Create(responseMessage);
return new Mapping(Guid.NewGuid(), string.Empty, null, request, response, 0, null, null, null);
return new Mapping(Guid.NewGuid(), string.Empty, null, _settings, request, response, 0, null, null, null);
}
#endregion
@@ -334,7 +361,7 @@ namespace WireMock.Server
if (mapping == null)
{
_logger.Warn("HttpStatusCode set to 404 : Mapping not found");
_settings.Logger.Warn("HttpStatusCode set to 404 : Mapping not found");
return ResponseMessageBuilder.Create("Mapping not found", 404);
}
@@ -378,12 +405,12 @@ namespace WireMock.Server
{
if (folder == null)
{
folder = _fileSystemHandler.GetMappingFolder();
folder = _settings.FileSystemHandler.GetMappingFolder();
}
if (!_fileSystemHandler.FolderExists(folder))
if (!_settings.FileSystemHandler.FolderExists(folder))
{
_fileSystemHandler.CreateFolder(folder);
_settings.FileSystemHandler.CreateFolder(folder);
}
var model = MappingConverter.ToMappingModel(mapping);
@@ -391,26 +418,23 @@ namespace WireMock.Server
string path = Path.Combine(folder, filename);
_logger.Info("Saving Mapping file {0}", filename);
_settings.Logger.Info("Saving Mapping file {0}", filename);
_fileSystemHandler.WriteMappingFile(path, JsonConvert.SerializeObject(model, _settings));
_settings.FileSystemHandler.WriteMappingFile(path, JsonConvert.SerializeObject(model, _jsonSerializerSettings));
}
private static string SanitizeFileName(string name, char replaceChar = '_')
{
return Path.GetInvalidFileNameChars().Aggregate(name, (current, c) => current.Replace(c, replaceChar));
}
private IEnumerable<MappingModel> ToMappingModels()
{
return Mappings.Where(m => !m.IsAdminInterface).Select(MappingConverter.ToMappingModel);
}
private ResponseMessage MappingsGet(RequestMessage requestMessage)
{
var result = new List<MappingModel>();
foreach (var mapping in Mappings.Where(m => !m.IsAdminInterface))
{
var model = MappingConverter.ToMappingModel(mapping);
result.Add(model);
}
return ToJson(result);
return ToJson(ToMappingModels());
}
private ResponseMessage MappingsPost(RequestMessage requestMessage)
@@ -433,12 +457,12 @@ namespace WireMock.Server
}
catch (ArgumentException a)
{
_logger.Error("HttpStatusCode set to 400 {0}", a);
_settings.Logger.Error("HttpStatusCode set to 400 {0}", a);
return ResponseMessageBuilder.Create(a.Message, 400);
}
catch (Exception e)
{
_logger.Error("HttpStatusCode set to 500 {0}", e);
_settings.Logger.Error("HttpStatusCode set to 500 {0}", e);
return ResponseMessageBuilder.Create(e.ToString(), 500);
}
}
@@ -457,7 +481,7 @@ namespace WireMock.Server
var responseBuilder = InitResponseBuilder(mappingModel.Response);
var respondProvider = Given(requestBuilder);
var respondProvider = Given(requestBuilder, mappingModel.SaveToFile == true);
if (guid != null)
{
@@ -513,7 +537,7 @@ namespace WireMock.Server
if (entry == null)
{
_logger.Warn("HttpStatusCode set to 404 : Request not found");
_settings.Logger.Warn("HttpStatusCode set to 404 : Request not found");
return ResponseMessageBuilder.Create("Request not found", 404);
}
@@ -656,7 +680,7 @@ namespace WireMock.Server
if (pathOrUrlRequired && !pathOrUrlmatchersValid)
{
_logger.Error("Path or Url matcher is missing for this mapping, this mapping will not be added.");
_settings.Logger.Error("Path or Url matcher is missing for this mapping, this mapping will not be added.");
return null;
}
@@ -685,14 +709,18 @@ namespace WireMock.Server
{
foreach (var paramModel in requestModel.Params.Where(c => c.Matchers != null))
{
requestBuilder = requestBuilder.WithParam(paramModel.Name, paramModel.Matchers.Select(MatcherMapper.Map).Cast<IStringMatcher>().ToArray());
bool ignoreCase = paramModel?.IgnoreCase ?? false;
requestBuilder = requestBuilder.WithParam(paramModel.Name, ignoreCase, paramModel.Matchers.Select(MatcherMapper.Map).Cast<IStringMatcher>().ToArray());
}
}
if (requestModel.Body?.Matcher != null)
{
var bodyMatcher = MatcherMapper.Map(requestModel.Body.Matcher);
requestBuilder = requestBuilder.WithBody(bodyMatcher);
requestBuilder = requestBuilder.WithBody(MatcherMapper.Map(requestModel.Body.Matcher));
}
else if (requestModel.Body?.Matchers != null)
{
requestBuilder = requestBuilder.WithBody(MatcherMapper.Map(requestModel.Body.Matchers));
}
return requestBuilder;
@@ -707,6 +735,11 @@ namespace WireMock.Server
responseBuilder = responseBuilder.WithDelay(responseModel.Delay.Value);
}
if (responseModel.UseTransformer == true)
{
responseBuilder = responseBuilder.WithTransformer();
}
if (!string.IsNullOrEmpty(responseModel.ProxyUrl))
{
if (string.IsNullOrEmpty(responseModel.X509Certificate2ThumbprintOrSubjectName))
@@ -763,11 +796,6 @@ namespace WireMock.Server
responseBuilder = responseBuilder.WithBodyFromFile(responseModel.BodyAsFile);
}
if (responseModel.UseTransformer)
{
responseBuilder = responseBuilder.WithTransformer();
}
return responseBuilder;
}
@@ -778,7 +806,7 @@ namespace WireMock.Server
BodyData = new BodyData
{
DetectedBodyType = BodyType.String,
BodyAsString = JsonConvert.SerializeObject(result, keepNullValues ? _settingsIncludeNullValues : _settings)
BodyAsString = JsonConvert.SerializeObject(result, keepNullValues ? _settingsIncludeNullValues : _jsonSerializerSettings)
},
StatusCode = 200,
Headers = new Dictionary<string, WireMockList<string>> { { HttpKnownHeaderNames.ContentType, new WireMockList<string>(ContentTypeJson) } }

View File

@@ -0,0 +1,114 @@
using System.IO;
using System.Linq;
using System.Text;
using WireMock.Matchers;
using WireMock.Util;
namespace WireMock.Server
{
public partial class FluentMockServer
{
private readonly RegexMatcher _adminFilesFilenamePathMatcher = new RegexMatcher(MatchBehaviour.AcceptOnMatch, @"^\/__admin\/files\/.*$");
private static readonly Encoding[] FileBodyIsString = { Encoding.UTF8, Encoding.ASCII };
#region Files/{filename}
private ResponseMessage FilePost(RequestMessage requestMessage)
{
string filename = GetFileNameFromRequestMessage(requestMessage);
string mappingFolder = _settings.FileSystemHandler.GetMappingFolder();
if (!_settings.FileSystemHandler.FolderExists(mappingFolder))
{
_settings.FileSystemHandler.CreateFolder(mappingFolder);
}
_settings.FileSystemHandler.WriteFile(filename, requestMessage.BodyAsBytes);
return ResponseMessageBuilder.Create("File created");
}
private ResponseMessage FilePut(RequestMessage requestMessage)
{
string filename = GetFileNameFromRequestMessage(requestMessage);
if (!_settings.FileSystemHandler.FileExists(filename))
{
_settings.Logger.Info("The file '{0}' does not exist, updating file will be skipped.", filename);
return ResponseMessageBuilder.Create("File is not found", 404);
}
_settings.FileSystemHandler.WriteFile(filename, requestMessage.BodyAsBytes);
return ResponseMessageBuilder.Create("File updated");
}
private ResponseMessage FileGet(RequestMessage requestMessage)
{
string filename = GetFileNameFromRequestMessage(requestMessage);
if (!_settings.FileSystemHandler.FileExists(filename))
{
_settings.Logger.Info("The file '{0}' does not exist.", filename);
return ResponseMessageBuilder.Create("File is not found", 404);
}
byte[] bytes = _settings.FileSystemHandler.ReadFile(filename);
var response = new ResponseMessage
{
StatusCode = 200,
BodyData = new BodyData
{
BodyAsBytes = bytes,
DetectedBodyType = BodyType.Bytes,
DetectedBodyTypeFromContentType = BodyType.None
}
};
if (BytesEncodingUtils.TryGetEncoding(bytes, out Encoding encoding) && FileBodyIsString.Select(x => x.Equals(encoding)).Any())
{
response.BodyData.DetectedBodyType = BodyType.String;
response.BodyData.BodyAsString = encoding.GetString(bytes);
}
return response;
}
/// <summary>
/// Checks if file exists.
/// Note: Response is returned with no body as a head request doesn't accept a body, only the status code.
/// </summary>
/// <param name="requestMessage">The request message.</param>
private ResponseMessage FileHead(RequestMessage requestMessage)
{
string filename = GetFileNameFromRequestMessage(requestMessage);
if (!_settings.FileSystemHandler.FileExists(filename))
{
_settings.Logger.Info("The file '{0}' does not exist.", filename);
return ResponseMessageBuilder.Create(404);
}
return ResponseMessageBuilder.Create(204);
}
private ResponseMessage FileDelete(RequestMessage requestMessage)
{
string filename = GetFileNameFromRequestMessage(requestMessage);
if (!_settings.FileSystemHandler.FileExists(filename))
{
_settings.Logger.Info("The file '{0}' does not exist.", filename);
return ResponseMessageBuilder.Create("File is not deleted", 404);
}
_settings.FileSystemHandler.DeleteFile(filename);
return ResponseMessageBuilder.Create("File deleted.");
}
private string GetFileNameFromRequestMessage(RequestMessage requestMessage)
{
return Path.GetFileName(requestMessage.Path.Substring(AdminFiles.Length + 1));
}
#endregion
}
}

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.Admin.Mappings;
using WireMock.Exceptions;
using WireMock.Handlers;
using WireMock.Logging;
@@ -27,8 +28,7 @@ namespace WireMock.Server
{
private const int ServerStartDelayInMs = 100;
private readonly IWireMockLogger _logger;
private readonly IFileSystemHandler _fileSystemHandler;
private readonly IFluentMockServerSettings _settings;
private readonly IOwinSelfHost _httpServer;
private readonly IWireMockMiddlewareOptions _options = new WireMockMiddlewareOptions();
@@ -56,6 +56,12 @@ namespace WireMock.Server
[PublicAPI]
public IEnumerable<IMapping> Mappings => _options.Mappings.Values.ToArray();
/// <summary>
/// Gets the mappings as MappingModels.
/// </summary>
[PublicAPI]
public IEnumerable<MappingModel> MappingModels => ToMappingModels();
/// <summary>
/// Gets the scenarios.
/// </summary>
@@ -92,7 +98,7 @@ namespace WireMock.Server
/// <param name="settings">The FluentMockServerSettings.</param>
/// <returns>The <see cref="FluentMockServer"/>.</returns>
[PublicAPI]
public static FluentMockServer Start(IFluentMockServerSettings settings)
public static FluentMockServer Start([NotNull] IFluentMockServerSettings settings)
{
Check.NotNull(settings, nameof(settings));
@@ -185,13 +191,14 @@ namespace WireMock.Server
private FluentMockServer(IFluentMockServerSettings settings)
{
settings.Logger = settings.Logger ?? new WireMockConsoleLogger();
_settings = settings;
_logger = settings.Logger;
_fileSystemHandler = settings.FileSystemHandler ?? new LocalFileSystemHandler();
// Set default values if not provided
_settings.Logger = settings.Logger ?? new WireMockNullLogger();
_settings.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));
_settings.Logger.Info("WireMock.Net by Stef Heyenrath (https://github.com/WireMock-Net/WireMock.Net)");
_settings.Logger.Debug("WireMock.Net server settings {0}", JsonConvert.SerializeObject(settings, Formatting.Indented));
if (settings.Urls != null)
{
@@ -203,10 +210,10 @@ namespace WireMock.Server
Urls = new[] { $"{(settings.UseSSL == true ? "https" : "http")}://localhost:{port}" };
}
_options.FileSystemHandler = settings.FileSystemHandler;
_options.FileSystemHandler = _settings.FileSystemHandler;
_options.PreWireMockMiddlewareInit = settings.PreWireMockMiddlewareInit;
_options.PostWireMockMiddlewareInit = settings.PostWireMockMiddlewareInit;
_options.Logger = _logger;
_options.Logger = _settings.Logger;
#if USE_ASPNETCORE
_httpServer = new AspNetCoreSelfHost(_options, Urls);
@@ -369,7 +376,7 @@ namespace WireMock.Server
[PublicAPI]
public void AllowPartialMapping(bool allow = true)
{
_logger.Info("AllowPartialMapping is set to {0}", allow);
_settings.Logger.Info("AllowPartialMapping is set to {0}", allow);
_options.AllowPartialMapping = allow;
}
@@ -430,14 +437,15 @@ namespace WireMock.Server
/// The given.
/// </summary>
/// <param name="requestMatcher">The request matcher.</param>
/// <param name="saveToFile">Optional boolean to indicate if this mapping should be saved as static mapping file.</param>
/// <returns>The <see cref="IRespondWithAProvider"/>.</returns>
[PublicAPI]
public IRespondWithAProvider Given(IRequestMatcher requestMatcher)
public IRespondWithAProvider Given(IRequestMatcher requestMatcher, bool saveToFile = false)
{
return new RespondWithAProvider(RegisterMapping, requestMatcher);
return new RespondWithAProvider(RegisterMapping, requestMatcher, _settings, saveToFile);
}
private void RegisterMapping(IMapping mapping)
private void RegisterMapping(IMapping mapping, bool saveToFile)
{
// Check a mapping exists with the same Guid, if so, replace it.
if (_options.Mappings.ContainsKey(mapping.Guid))
@@ -448,6 +456,11 @@ namespace WireMock.Server
{
_options.Mappings.TryAdd(mapping.Guid, mapping);
}
if (saveToFile)
{
SaveMappingToFile(mapping);
}
}
}
}

View File

@@ -1,6 +1,7 @@
using System;
using WireMock.Matchers.Request;
using WireMock.ResponseProviders;
using WireMock.Settings;
namespace WireMock.Server
{
@@ -17,6 +18,8 @@ namespace WireMock.Server
private string _scenario;
private readonly RegistrationCallback _registrationCallback;
private readonly IRequestMatcher _requestMatcher;
private readonly IFluentMockServerSettings _settings;
private readonly bool _saveToFile;
public Guid Guid { get; private set; } = Guid.NewGuid();
@@ -25,10 +28,14 @@ namespace WireMock.Server
/// </summary>
/// <param name="registrationCallback">The registration callback.</param>
/// <param name="requestMatcher">The request matcher.</param>
public RespondWithAProvider(RegistrationCallback registrationCallback, IRequestMatcher requestMatcher)
/// <param name="settings">The FluentMockServerSettings.</param>
/// <param name="saveToFile">Optional boolean to indicate if this mapping should be saved as static mapping file.</param>
public RespondWithAProvider(RegistrationCallback registrationCallback, IRequestMatcher requestMatcher, IFluentMockServerSettings settings, bool saveToFile = false)
{
_registrationCallback = registrationCallback;
_requestMatcher = requestMatcher;
_settings = settings;
_saveToFile = saveToFile;
}
/// <summary>
@@ -37,7 +44,7 @@ namespace WireMock.Server
/// <param name="provider">The provider.</param>
public void RespondWith(IResponseProvider provider)
{
_registrationCallback(new Mapping(Guid, _title, _path, _requestMatcher, provider, _priority, _scenario, _executionConditionState, _nextState));
_registrationCallback(new Mapping(Guid, _title, _path, _settings, _requestMatcher, provider, _priority, _scenario, _executionConditionState, _nextState), _saveToFile);
}
/// <see cref="IRespondWithAProvider.WithGuid(string)"/>

View File

@@ -1,6 +1,7 @@
using System;
using HandlebarsDotNet;
using JetBrains.Annotations;
using Newtonsoft.Json;
using System;
using WireMock.Handlers;
using WireMock.Logging;
@@ -82,6 +83,11 @@ namespace WireMock.Settings
/// <inheritdoc cref="IFluentMockServerSettings.FileSystemHandler"/>
[PublicAPI]
[JsonIgnore]
public IFileSystemHandler FileSystemHandler { get; set; } = new LocalFileSystemHandler();
public IFileSystemHandler FileSystemHandler { get; set; }
/// <inheritdoc cref="IFluentMockServerSettings.HandlebarsRegistrationCallback"/>
[PublicAPI]
[JsonIgnore]
public Action<IHandlebars, IFileSystemHandler> HandlebarsRegistrationCallback { get; set; }
}
}

View File

@@ -1,5 +1,6 @@
using System;
using HandlebarsDotNet;
using JetBrains.Annotations;
using System;
using WireMock.Handlers;
using WireMock.Logging;
@@ -24,7 +25,7 @@ namespace WireMock.Settings
bool? UseSSL { get; set; }
/// <summary>
/// Gets or sets wether to start admin interface.
/// Gets or sets whether to start admin interface.
/// </summary>
[PublicAPI]
bool? StartAdminInterface { get; set; }
@@ -112,5 +113,11 @@ namespace WireMock.Settings
/// </summary>
[PublicAPI]
IFileSystemHandler FileSystemHandler { get; set; }
/// <summary>
/// Action which can be used to add additional is Handlebar registrations. [Optional]
/// </summary>
[PublicAPI]
Action<IHandlebars, IFileSystemHandler> HandlebarsRegistrationCallback { get; set; }
}
}

View File

@@ -0,0 +1,41 @@
using HandlebarsDotNet;
using System;
using WireMock.Handlers;
using WireMock.Validation;
namespace WireMock.Transformers
{
internal static class HandleBarsFile
{
public static void Register(IHandlebars handlebarsContext, IFileSystemHandler fileSystemHandler)
{
handlebarsContext.RegisterHelper("File", (writer, context, arguments) =>
{
string value = ParseArgumentAndReadFileFragment(handlebarsContext, context, fileSystemHandler, arguments);
writer.Write(value);
});
handlebarsContext.RegisterHelper("File", (writer, options, context, arguments) =>
{
string value = ParseArgumentAndReadFileFragment(handlebarsContext, context, fileSystemHandler, arguments);
options.Template(writer, value);
});
}
private static string ParseArgumentAndReadFileFragment(IHandlebars handlebarsContext, dynamic context, IFileSystemHandler fileSystemHandler, object[] arguments)
{
Check.Condition(arguments, args => args.Length == 1, nameof(arguments));
Check.NotNull(arguments[0], "arguments[0]");
switch (arguments[0])
{
case string path:
var templateFunc = handlebarsContext.Compile(path);
string transformed = templateFunc(context);
return fileSystemHandler.ReadResponseBodyAsString(transformed);
}
throw new NotSupportedException($"The value '{arguments[0]}' with type '{arguments[0]?.GetType()}' cannot be used in Handlebars File.");
}
}
}

View File

@@ -1,10 +1,11 @@
using HandlebarsDotNet;
using WireMock.Handlers;
namespace WireMock.Transformers
{
internal static class HandlebarsHelpers
{
public static void Register(IHandlebars handlebarsContext)
public static void Register(IHandlebars handlebarsContext, IFileSystemHandler fileSystemHandler)
{
HandleBarsRegex.Register(handlebarsContext);
@@ -15,6 +16,8 @@ namespace WireMock.Transformers
HandleBarsRandom.Register(handlebarsContext);
HandleBarsXeger.Register(handlebarsContext);
HandleBarsFile.Register(handlebarsContext, fileSystemHandler);
}
}
}

View File

@@ -0,0 +1,34 @@
using HandlebarsDotNet;
using System;
using WireMock.Handlers;
namespace WireMock.Transformers
{
internal class HandlebarsContextFactory : IHandlebarsContextFactory
{
private static readonly HandlebarsConfiguration HandlebarsConfiguration = new HandlebarsConfiguration
{
UnresolvedBindingFormatter = "{0}"
};
private readonly IFileSystemHandler _fileSystemHandler;
private readonly Action<IHandlebars, IFileSystemHandler> _action;
public HandlebarsContextFactory(IFileSystemHandler fileSystemHandler, Action<IHandlebars, IFileSystemHandler> action)
{
_fileSystemHandler = fileSystemHandler;
_action = action;
}
public IHandlebars Create()
{
var handlebarsContext = Handlebars.Create(HandlebarsConfiguration);
HandlebarsHelpers.Register(handlebarsContext, _fileSystemHandler);
_action?.Invoke(handlebarsContext, _fileSystemHandler);
return handlebarsContext;
}
}
}

View File

@@ -0,0 +1,9 @@
using HandlebarsDotNet;
namespace WireMock.Transformers
{
interface IHandlebarsContextFactory
{
IHandlebars Create();
}
}

View File

@@ -1,55 +1,57 @@
using System;
using System.Collections.Generic;
using System.Linq;
using HandlebarsDotNet;
using HandlebarsDotNet;
using JetBrains.Annotations;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Linq;
using WireMock.Util;
using WireMock.Validation;
namespace WireMock.Transformers
{
internal static class ResponseMessageTransformer
internal class ResponseMessageTransformer
{
private static readonly HandlebarsConfiguration HandlebarsConfiguration = new HandlebarsConfiguration
{
UnresolvedBindingFormatter = "{0}"
};
private readonly IHandlebarsContextFactory _factory;
private static readonly IHandlebars HandlebarsContext = Handlebars.Create(HandlebarsConfiguration);
static ResponseMessageTransformer()
public ResponseMessageTransformer([NotNull] IHandlebarsContextFactory factory)
{
HandlebarsHelpers.Register(HandlebarsContext);
Check.NotNull(factory, nameof(factory));
_factory = factory;
}
public static ResponseMessage Transform(RequestMessage requestMessage, ResponseMessage original)
public ResponseMessage Transform(RequestMessage requestMessage, ResponseMessage original)
{
bool bodyIsJson = original.BodyData.DetectedBodyType == BodyType.Json;
var responseMessage = new ResponseMessage { StatusCode = original.StatusCode };
var handlebarsContext = _factory.Create();
if (!bodyIsJson)
{
responseMessage.BodyOriginal = original.BodyData.BodyAsString;
}
var responseMessage = new ResponseMessage { StatusCode = original.StatusCode };
var template = new { request = requestMessage };
if (!bodyIsJson)
switch (original.BodyData.DetectedBodyType)
{
TransformBodyAsString(template, original, responseMessage);
}
else
{
TransformBodyAsJson(template, original, responseMessage);
case BodyType.Json:
TransformBodyAsJson(handlebarsContext, template, original, responseMessage);
break;
case BodyType.File:
TransformBodyAsFile(handlebarsContext, template, original, responseMessage);
break;
case BodyType.String:
responseMessage.BodyOriginal = original.BodyData.BodyAsString;
TransformBodyAsString(handlebarsContext, template, original, responseMessage);
break;
}
// Headers
var newHeaders = new Dictionary<string, WireMockList<string>>();
foreach (var header in original.Headers)
{
var templateHeaderKey = HandlebarsContext.Compile(header.Key);
var templateHeaderKey = handlebarsContext.Compile(header.Key);
var templateHeaderValues = header.Value
.Select(HandlebarsContext.Compile)
.Select(handlebarsContext.Compile)
.Select(func => func(template))
.ToArray();
@@ -61,13 +63,13 @@ namespace WireMock.Transformers
return responseMessage;
}
private static void TransformBodyAsJson(object template, ResponseMessage original, ResponseMessage responseMessage)
private static void TransformBodyAsJson(IHandlebars handlebarsContext, object template, ResponseMessage original, ResponseMessage responseMessage)
{
JToken jToken;
switch (original.BodyData.BodyAsJson)
{
case JObject bodyAsJObject:
jToken = bodyAsJObject;
jToken = bodyAsJObject.DeepClone();
break;
case Array bodyAsArray:
@@ -79,7 +81,7 @@ namespace WireMock.Transformers
break;
}
WalkNode(jToken, template);
WalkNode(handlebarsContext, jToken, template);
responseMessage.BodyData = new BodyData
{
@@ -89,14 +91,14 @@ namespace WireMock.Transformers
};
}
private static void WalkNode(JToken node, object template)
private static void WalkNode(IHandlebars handlebarsContext, JToken node, object context)
{
if (node.Type == JTokenType.Object)
{
// In case of Object, loop all children. Do a ToArray() to avoid `Collection was modified` exceptions.
foreach (JProperty child in node.Children<JProperty>().ToArray())
{
WalkNode(child.Value, template);
WalkNode(handlebarsContext, child.Value, context);
}
}
else if (node.Type == JTokenType.Array)
@@ -104,7 +106,7 @@ namespace WireMock.Transformers
// In case of Array, loop all items. Do a ToArray() to avoid `Collection was modified` exceptions.
foreach (JToken child in node.Children().ToArray())
{
WalkNode(child, template);
WalkNode(handlebarsContext, child, context);
}
}
else if (node.Type == JTokenType.String)
@@ -116,8 +118,8 @@ namespace WireMock.Transformers
return;
}
var templateForStringValue = HandlebarsContext.Compile(stringValue);
string transformedString = templateForStringValue(template);
var templateForStringValue = handlebarsContext.Compile(stringValue);
string transformedString = templateForStringValue(context);
if (!string.Equals(stringValue, transformedString))
{
ReplaceNodeValue(node, transformedString);
@@ -148,16 +150,28 @@ namespace WireMock.Transformers
node.Replace(value);
}
private static void TransformBodyAsString(object template, ResponseMessage original, ResponseMessage responseMessage)
private static void TransformBodyAsString(IHandlebars handlebarsContext, object template, ResponseMessage original, ResponseMessage responseMessage)
{
var templateBody = HandlebarsContext.Compile(original.BodyData.BodyAsString);
var templateBodyAsString = handlebarsContext.Compile(original.BodyData.BodyAsString);
responseMessage.BodyData = new BodyData
{
DetectedBodyType = original.BodyData.DetectedBodyType,
DetectedBodyTypeFromContentType = original.BodyData.DetectedBodyTypeFromContentType,
BodyAsString = templateBody(template)
BodyAsString = templateBodyAsString(template)
};
}
private static void TransformBodyAsFile(IHandlebars handlebarsContext, object template, ResponseMessage original, ResponseMessage responseMessage)
{
var templateBodyAsFile = handlebarsContext.Compile(original.BodyData.BodyAsFile);
responseMessage.BodyData = new BodyData
{
DetectedBodyType = original.BodyData.DetectedBodyType,
DetectedBodyTypeFromContentType = original.BodyData.DetectedBodyTypeFromContentType,
BodyAsFile = templateBodyAsFile(template)
};
}
}
}
}

View File

@@ -2,6 +2,7 @@
using MimeKit;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
@@ -14,6 +15,7 @@ namespace WireMock.Util
internal static class BodyParser
{
private static readonly Encoding DefaultEncoding = Encoding.UTF8;
private static readonly Encoding[] SupportedBodyAsStringEncodingForMultipart = { Encoding.UTF8, Encoding.ASCII };
/*
HEAD - No defined body semantics.
@@ -26,7 +28,18 @@ namespace WireMock.Util
CONNECT - No defined body semantics
PATCH - Body supported.
*/
private static readonly string[] AllowedBodyParseMethods = { "PUT", "POST", "OPTIONS", "PATCH" };
private static readonly IDictionary<string, bool> BodyAllowedForMethods = new Dictionary<string, bool>
{
{ "HEAD", false },
{ "GET", false },
{ "PUT", true },
{ "POST", true },
{ "DELETE", false },
{ "TRACE", false },
{ "OPTIONS", true },
{ "CONNECT", false },
{ "PATCH", true }
};
private static readonly IStringMatcher[] MultipartContentTypesMatchers = {
new WildcardMatcher("multipart/*", true)
@@ -52,7 +65,19 @@ namespace WireMock.Util
public static bool ShouldParseBody([CanBeNull] string method)
{
return AllowedBodyParseMethods.Contains(method, StringComparer.OrdinalIgnoreCase);
if (String.IsNullOrEmpty(method))
{
return false;
}
if (BodyAllowedForMethods.TryGetValue(method.ToUpper(), out var allowed))
{
return allowed;
}
// If we don't have any knowledge of this method, we should assume that a body *may*
// be present, so we should parse it if it is. Therefore, if a new method is added to
// the HTTP Method Registry, we only really need to add it to BodyAllowedForMethods if
// we want to make it clear that a body is *not* allowed.
return true;
}
public static BodyType DetectBodyTypeFromContentType([CanBeNull] string contentTypeValue)
@@ -91,9 +116,19 @@ namespace WireMock.Util
DetectedBodyTypeFromContentType = DetectBodyTypeFromContentType(contentType)
};
// In case of MultiPart: never try to read as String but keep as-is
// In case of MultiPart: check if the BodyAsBytes is a valid UTF8 or ASCII string, in that case read as String else keep as-is
if (data.DetectedBodyTypeFromContentType == BodyType.MultiPart)
{
if (BytesEncodingUtils.TryGetEncoding(data.BodyAsBytes, out Encoding encoding) &&
SupportedBodyAsStringEncodingForMultipart.Select(x => x.Equals(encoding)).Any())
{
data.BodyAsString = encoding.GetString(data.BodyAsBytes);
data.Encoding = encoding;
data.DetectedBodyType = BodyType.String;
return data;
}
return data;
}

View File

@@ -0,0 +1,230 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace WireMock.Util
{
/// <summary>
/// Based on:
/// http://utf8checker.codeplex.com
/// https://github.com/0x53A/Mvvm/blob/master/src/Mvvm/src/Utf8Checker.cs
///
/// References:
/// http://anubis.dkuug.dk/JTC1/SC2/WG2/docs/n1335
/// http://www.cl.cam.ac.uk/~mgk25/ucs/ISO-10646-UTF-8.html
/// http://www.unicode.org/versions/corrigendum1.html
/// http://www.ietf.org/rfc/rfc2279.txt
/// </summary>
public static class BytesEncodingUtils
{
/// <summary>
/// Tries the get the Encoding from an array of bytes.
/// </summary>
/// <param name="bytes">The bytes.</param>
/// <param name="encoding">The output encoding.</param>
public static bool TryGetEncoding(byte[] bytes, out Encoding encoding)
{
encoding = null;
if (bytes.All(b => b < 80))
{
encoding = Encoding.ASCII;
return true;
}
if (StartsWith(bytes, new byte[] { 0xff, 0xfe, 0x00, 0x00 }))
{
encoding = Encoding.UTF32;
return true;
}
if (StartsWith(bytes, new byte[] { 0xfe, 0xff }))
{
encoding = Encoding.BigEndianUnicode;
return true;
}
if (StartsWith(bytes, new byte[] { 0xff, 0xfe }))
{
encoding = Encoding.Unicode;
return true;
}
if (StartsWith(bytes, new byte[] { 0xef, 0xbb, 0xbf }))
{
encoding = Encoding.UTF8;
return true;
}
if (IsUtf8(bytes, bytes.Length))
{
encoding = new UTF8Encoding(false);
return true;
}
return false;
}
private static bool StartsWith(IEnumerable<byte> data, IReadOnlyCollection<byte> other)
{
byte[] arraySelf = data.Take(other.Count).ToArray();
return other.SequenceEqual(arraySelf);
}
private static bool IsUtf8(IReadOnlyList<byte> buffer, int length)
{
int position = 0;
int bytes = 0;
while (position < length)
{
if (!IsValid(buffer, position, length, ref bytes))
{
return false;
}
position += bytes;
}
return true;
}
#pragma warning disable S3776 // Cognitive Complexity of methods should not be too high
private static bool IsValid(IReadOnlyList<byte> buffer, int position, int length, ref int bytes)
{
if (length > buffer.Count)
{
throw new ArgumentException("Invalid length");
}
if (position > length - 1)
{
bytes = 0;
return true;
}
byte ch = buffer[position];
if (ch <= 0x7F)
{
bytes = 1;
return true;
}
if (ch >= 0xc2 && ch <= 0xdf)
{
if (position >= length - 2)
{
bytes = 0;
return false;
}
if (buffer[position + 1] < 0x80 || buffer[position + 1] > 0xbf)
{
bytes = 0;
return false;
}
bytes = 2;
return true;
}
if (ch == 0xe0)
{
if (position >= length - 3)
{
bytes = 0;
return false;
}
if (buffer[position + 1] < 0xa0 || buffer[position + 1] > 0xbf ||
buffer[position + 2] < 0x80 || buffer[position + 2] > 0xbf)
{
bytes = 0;
return false;
}
bytes = 3;
return true;
}
if (ch >= 0xe1 && ch <= 0xef)
{
if (position >= length - 3)
{
bytes = 0;
return false;
}
if (buffer[position + 1] < 0x80 || buffer[position + 1] > 0xbf ||
buffer[position + 2] < 0x80 || buffer[position + 2] > 0xbf)
{
bytes = 0;
return false;
}
bytes = 3;
return true;
}
if (ch == 0xf0)
{
if (position >= length - 4)
{
bytes = 0;
return false;
}
if (buffer[position + 1] < 0x90 || buffer[position + 1] > 0xbf ||
buffer[position + 2] < 0x80 || buffer[position + 2] > 0xbf ||
buffer[position + 3] < 0x80 || buffer[position + 3] > 0xbf)
{
bytes = 0;
return false;
}
bytes = 4;
return true;
}
if (ch == 0xf4)
{
if (position >= length - 4)
{
bytes = 0;
return false;
}
if (buffer[position + 1] < 0x80 || buffer[position + 1] > 0x8f ||
buffer[position + 2] < 0x80 || buffer[position + 2] > 0xbf ||
buffer[position + 3] < 0x80 || buffer[position + 3] > 0xbf)
{
bytes = 0;
return false;
}
bytes = 4;
return true;
}
if (ch >= 0xf1 && ch <= 0xf3)
{
if (position >= length - 4)
{
bytes = 0;
return false;
}
if (buffer[position + 1] < 0x80 || buffer[position + 1] > 0xbf ||
buffer[position + 2] < 0x80 || buffer[position + 2] > 0xbf ||
buffer[position + 3] < 0x80 || buffer[position + 3] > 0xbf)
{
bytes = 0;
return false;
}
bytes = 4;
return true;
}
return false;
}
}
#pragma warning restore S3776 // Cognitive Complexity of methods should not be too high
}

View File

@@ -1,6 +1,5 @@
using System.IO;
using JetBrains.Annotations;
using System.Threading;
using JetBrains.Annotations;
using WireMock.Handlers;
using WireMock.Validation;
@@ -11,16 +10,19 @@ namespace WireMock.Util
private const int NumberOfRetries = 3;
private const int DelayOnRetry = 500;
public static string ReadAllTextWithRetryAndDelay([NotNull] IFileSystemHandler handler, [NotNull] string path)
public static bool TryReadMappingFileWithRetryAndDelay([NotNull] IFileSystemHandler handler, [NotNull] string path, out string value)
{
Check.NotNull(handler, nameof(handler));
Check.NotNullOrEmpty(path, nameof(path));
value = null;
for (int i = 1; i <= NumberOfRetries; ++i)
{
try
{
return handler.ReadMappingFile(path);
value = handler.ReadMappingFile(path);
return true;
}
catch
{
@@ -28,7 +30,7 @@ namespace WireMock.Util
}
}
throw new IOException();
return false;
}
}
}

View File

@@ -0,0 +1,41 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace WireMock.Util
{
/// <summary>
/// Based on https://stackoverflow.com/questions/659887/get-url-parameters-from-a-string-in-net
/// </summary>
internal static class QueryStringParser
{
public static IDictionary<string, WireMockList<string>> Parse(string queryString)
{
if (string.IsNullOrEmpty(queryString))
{
return new Dictionary<string, WireMockList<string>>();
}
string[] JoinParts(string[] parts)
{
if (parts.Length > 2)
{
return new[] { string.Join("=", parts, 1, parts.Length - 1) };
}
if (parts.Length > 1)
{
return parts[1].Split(new[] { "," }, StringSplitOptions.RemoveEmptyEntries); // support "?key=1,2"
}
return new string[0];
}
return queryString.TrimStart('?')
.Split(new[] { '&', ';' }, StringSplitOptions.RemoveEmptyEntries) // Support "?key=value;key=anotherValue" and "?key=value&key=anotherValue"
.Select(parameter => parameter.Split(new[] { '=' }, StringSplitOptions.RemoveEmptyEntries))
.GroupBy(parts => parts[0], JoinParts)
.ToDictionary(grouping => grouping.Key, grouping => new WireMockList<string>(grouping.SelectMany(x => x)));
}
}
}

View File

@@ -25,6 +25,10 @@
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<GenerateBindingRedirectsOutputType>true</GenerateBindingRedirectsOutputType>
<CodeAnalysisRuleSet>WireMock.Net.ruleset</CodeAnalysisRuleSet>
<SignAssembly>true</SignAssembly>
<AssemblyOriginatorKeyFile>WireMock.Net.snk</AssemblyOriginatorKeyFile>
<!--<DelaySign>true</DelaySign>-->
<PublicSign Condition=" '$(OS)' != 'Windows_NT' ">true</PublicSign>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
@@ -57,8 +61,9 @@
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
<PackageReference Include="System.Linq.Dynamic.Core" Version="1.0.9" />
<PackageReference Include="RandomDataGenerator.Net" Version="1.0.7" />
<PackageReference Include="System.Linq.Dynamic.Core" Version="1.0.12" />
<PackageReference Include="RandomDataGenerator.Net" Version="1.0.8" />
<PackageReference Include="JmesPath.Net" Version="1.0.125" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net451' ">

Binary file not shown.

View File

@@ -0,0 +1,215 @@
using Moq;
using NFluent;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
using WireMock.Handlers;
using WireMock.Server;
using WireMock.Settings;
using Xunit;
namespace WireMock.Net.Tests
{
public class FluentMockServerAdminFilesTests
{
private readonly HttpClient _client = new HttpClient();
[Fact]
public async Task FluentMockServer_Admin_Files_Post_Ascii()
{
// Arrange
var filesystemHandlerMock = new Mock<IFileSystemHandler>(MockBehavior.Strict);
filesystemHandlerMock.Setup(fs => fs.GetMappingFolder()).Returns("__admin/mappings");
filesystemHandlerMock.Setup(fs => fs.FolderExists(It.IsAny<string>())).Returns(true);
filesystemHandlerMock.Setup(fs => fs.WriteFile(It.IsAny<string>(), It.IsAny<byte[]>()));
var server = FluentMockServer.Start(new FluentMockServerSettings
{
UseSSL = false,
StartAdminInterface = true,
FileSystemHandler = filesystemHandlerMock.Object
});
var multipartFormDataContent = new MultipartFormDataContent();
multipartFormDataContent.Headers.ContentType = MediaTypeHeaderValue.Parse("multipart/form-data");
multipartFormDataContent.Add(new StreamContent(new MemoryStream(Encoding.ASCII.GetBytes("Here's a string."))));
// Act
var httpResponseMessage = await _client.PostAsync("http://localhost:" + server.Ports[0] + "/__admin/files/filename.txt", multipartFormDataContent);
// Assert
Check.That(httpResponseMessage.StatusCode).Equals(HttpStatusCode.OK);
Check.That(server.LogEntries.Count().Equals(1));
// Verify
filesystemHandlerMock.Verify(fs => fs.GetMappingFolder(), Times.Once);
filesystemHandlerMock.Verify(fs => fs.FolderExists(It.IsAny<string>()), Times.Once);
filesystemHandlerMock.Verify(fs => fs.WriteFile(It.Is<string>(p => p == "filename.txt"), It.IsAny<byte[]>()), Times.Once);
filesystemHandlerMock.VerifyNoOtherCalls();
}
[Fact]
public async Task FluentMockServer_Admin_Files_Post_MappingFolderDoesNotExistsButWillBeCreated()
{
// Arrange
var filesystemHandlerMock = new Mock<IFileSystemHandler>(MockBehavior.Strict);
filesystemHandlerMock.Setup(fs => fs.GetMappingFolder()).Returns("x");
filesystemHandlerMock.Setup(fs => fs.CreateFolder(It.IsAny<string>()));
filesystemHandlerMock.Setup(fs => fs.FolderExists(It.IsAny<string>())).Returns(false);
filesystemHandlerMock.Setup(fs => fs.WriteFile(It.IsAny<string>(), It.IsAny<byte[]>()));
var server = FluentMockServer.Start(new FluentMockServerSettings
{
UseSSL = false,
StartAdminInterface = true,
FileSystemHandler = filesystemHandlerMock.Object
});
var multipartFormDataContent = new MultipartFormDataContent();
multipartFormDataContent.Headers.ContentType = MediaTypeHeaderValue.Parse("multipart/form-data");
multipartFormDataContent.Add(new StreamContent(new MemoryStream(Encoding.ASCII.GetBytes("Here's a string."))));
// Act
var httpResponseMessage = await _client.PostAsync("http://localhost:" + server.Ports[0] + "/__admin/files/filename.txt", multipartFormDataContent);
// Assert
Check.That(httpResponseMessage.StatusCode).Equals(HttpStatusCode.OK);
// Verify
filesystemHandlerMock.Verify(fs => fs.GetMappingFolder(), Times.Once);
filesystemHandlerMock.Verify(fs => fs.FolderExists(It.IsAny<string>()), Times.Once);
filesystemHandlerMock.Verify(fs => fs.CreateFolder(It.Is<string>(p => p == "x")), Times.Once);
filesystemHandlerMock.Verify(fs => fs.WriteFile(It.Is<string>(p => p == "filename.txt"), It.IsAny<byte[]>()), Times.Once);
filesystemHandlerMock.VerifyNoOtherCalls();
}
[Fact]
public async Task FluentMockServer_Admin_Files_GetAscii()
{
// Arrange
var filesystemHandlerMock = new Mock<IFileSystemHandler>(MockBehavior.Strict);
filesystemHandlerMock.Setup(fs => fs.FileExists(It.IsAny<string>())).Returns(true);
filesystemHandlerMock.Setup(fs => fs.ReadFile(It.IsAny<string>())).Returns(Encoding.ASCII.GetBytes("Here's a string."));
var server = FluentMockServer.Start(new FluentMockServerSettings
{
UseSSL = false,
StartAdminInterface = true,
FileSystemHandler = filesystemHandlerMock.Object
});
var multipartFormDataContent = new MultipartFormDataContent();
multipartFormDataContent.Headers.ContentType = MediaTypeHeaderValue.Parse("multipart/form-data");
multipartFormDataContent.Add(new StreamContent(new MemoryStream()));
// Act
var httpResponseMessageGet = await _client.GetAsync("http://localhost:" + server.Ports[0] + "/__admin/files/filename.txt");
// Assert
Check.That(httpResponseMessageGet.StatusCode).Equals(HttpStatusCode.OK);
Check.That(httpResponseMessageGet.Content.ReadAsStringAsync().Result).Equals("Here's a string.");
Check.That(server.LogEntries.Count().Equals(2));
// Verify
filesystemHandlerMock.Verify(fs => fs.ReadFile(It.Is<string>(p => p == "filename.txt")), Times.Once);
filesystemHandlerMock.Verify(fs => fs.FileExists(It.Is<string>(p => p == "filename.txt")), Times.Once);
filesystemHandlerMock.VerifyNoOtherCalls();
}
[Fact]
public async Task FluentMockServer_Admin_Files_GetUTF16()
{
// Arrange
byte[] symbol = Encoding.UTF32.GetBytes(char.ConvertFromUtf32(0x1D161));
var filesystemHandlerMock = new Mock<IFileSystemHandler>(MockBehavior.Strict);
filesystemHandlerMock.Setup(fs => fs.FileExists(It.IsAny<string>())).Returns(true);
filesystemHandlerMock.Setup(fs => fs.ReadFile(It.IsAny<string>())).Returns(symbol);
var server = FluentMockServer.Start(new FluentMockServerSettings
{
UseSSL = false,
StartAdminInterface = true,
FileSystemHandler = filesystemHandlerMock.Object
});
var multipartFormDataContent = new MultipartFormDataContent();
multipartFormDataContent.Headers.ContentType = MediaTypeHeaderValue.Parse("multipart/form-data");
multipartFormDataContent.Add(new StreamContent(new MemoryStream()));
// Act
var httpResponseMessageGet = await _client.GetAsync("http://localhost:" + server.Ports[0] + "/__admin/files/filename.bin");
// Assert
Check.That(httpResponseMessageGet.StatusCode).Equals(HttpStatusCode.OK);
Check.That(httpResponseMessageGet.Content.ReadAsByteArrayAsync().Result).Equals(symbol);
Check.That(server.LogEntries.Count().Equals(2));
// Verify
filesystemHandlerMock.Verify(fs => fs.ReadFile(It.Is<string>(p => p == "filename.bin")), Times.Once);
filesystemHandlerMock.Verify(fs => fs.FileExists(It.Is<string>(p => p == "filename.bin")), Times.Once);
filesystemHandlerMock.VerifyNoOtherCalls();
}
[Fact]
public async Task FluentMockServer_Admin_Files_Head()
{
// Arrange
var filesystemHandlerMock = new Mock<IFileSystemHandler>(MockBehavior.Strict);
filesystemHandlerMock.Setup(fs => fs.FileExists(It.IsAny<string>())).Returns(true);
var server = FluentMockServer.Start(new FluentMockServerSettings
{
UseSSL = false,
StartAdminInterface = true,
FileSystemHandler = filesystemHandlerMock.Object
});
// Act
var requestUri = "http://localhost:" + server.Ports[0] + "/__admin/files/filename.txt";
var httpRequestMessage = new HttpRequestMessage(HttpMethod.Head, requestUri);
var httpResponseMessage = await _client.SendAsync(httpRequestMessage);
// Assert
Check.That(httpResponseMessage.StatusCode).Equals(HttpStatusCode.NoContent);
Check.That(server.LogEntries.Count().Equals(1));
// Verify
filesystemHandlerMock.Verify(fs => fs.FileExists(It.IsAny<string>()), Times.Once);
filesystemHandlerMock.VerifyNoOtherCalls();
}
[Fact]
public async Task FluentMockServer_Admin_Files_Head_FileDoesNotExistsReturns404()
{
// Arrange
var filesystemHandlerMock = new Mock<IFileSystemHandler>(MockBehavior.Strict);
filesystemHandlerMock.Setup(fs => fs.FileExists(It.IsAny<string>())).Returns(false);
var server = FluentMockServer.Start(new FluentMockServerSettings
{
UseSSL = false,
StartAdminInterface = true,
FileSystemHandler = filesystemHandlerMock.Object
});
// Act
var requestUri = "http://localhost:" + server.Ports[0] + "/__admin/files/filename.txt";
var httpRequestMessage = new HttpRequestMessage(HttpMethod.Head, requestUri);
var httpResponseMessage = await _client.SendAsync(httpRequestMessage);
// Assert
Check.That(httpResponseMessage.StatusCode).Equals(HttpStatusCode.NotFound);
Check.That(server.LogEntries.Count().Equals(1));
// Verify
filesystemHandlerMock.Verify(fs => fs.FileExists(It.IsAny<string>()), Times.Once);
filesystemHandlerMock.VerifyNoOtherCalls();
}
}
}

View File

@@ -1,12 +1,15 @@
using NFluent;
using Moq;
using NFluent;
using RestEase;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
using WireMock.Admin.Mappings;
using WireMock.Admin.Settings;
using WireMock.Client;
using WireMock.Handlers;
using WireMock.Logging;
using WireMock.Server;
using WireMock.Settings;
@@ -232,6 +235,237 @@ namespace WireMock.Net.Tests
Check.That(requestLogged.Request.Body).IsNotNull();
Check.That(requestLogged.Request.Body).Contains("T000001");
}
}
[Fact]
public async Task IFluentMockServerAdmin_PostFileAsync_Ascii()
{
// Arrange
var filesystemHandlerMock = new Mock<IFileSystemHandler>(MockBehavior.Strict);
filesystemHandlerMock.Setup(fs => fs.GetMappingFolder()).Returns("__admin/mappings");
filesystemHandlerMock.Setup(fs => fs.FolderExists(It.IsAny<string>())).Returns(true);
filesystemHandlerMock.Setup(fs => fs.WriteFile(It.IsAny<string>(), It.IsAny<byte[]>()));
var server = FluentMockServer.Start(new FluentMockServerSettings
{
UseSSL = false,
StartAdminInterface = true,
FileSystemHandler = filesystemHandlerMock.Object
});
var api = RestClient.For<IFluentMockServerAdmin>(server.Urls[0]);
// Act
var request = await api.PostFileAsync("filename.txt", "abc");
// Assert
Check.That(request.Guid).IsNull();
Check.That(request.Status).Contains("File");
// Verify
filesystemHandlerMock.Verify(fs => fs.GetMappingFolder(), Times.Once);
filesystemHandlerMock.Verify(fs => fs.FolderExists(It.IsAny<string>()), Times.Once);
filesystemHandlerMock.Verify(fs => fs.WriteFile(It.Is<string>(p => p == "filename.txt"), It.IsAny<byte[]>()), Times.Once);
filesystemHandlerMock.VerifyNoOtherCalls();
server.Stop();
}
[Fact]
public async Task IFluentMockServerAdmin_PutFileAsync_Ascii()
{
// Arrange
var filesystemHandlerMock = new Mock<IFileSystemHandler>(MockBehavior.Strict);
filesystemHandlerMock.Setup(fs => fs.FileExists(It.IsAny<string>())).Returns(true);
filesystemHandlerMock.Setup(fs => fs.WriteFile(It.IsAny<string>(), It.IsAny<byte[]>()));
var server = FluentMockServer.Start(new FluentMockServerSettings
{
UseSSL = false,
StartAdminInterface = true,
FileSystemHandler = filesystemHandlerMock.Object
});
var api = RestClient.For<IFluentMockServerAdmin>(server.Urls[0]);
// Act
var request = await api.PutFileAsync("filename.txt", "abc-abc");
// Assert
Check.That(request.Guid).IsNull();
Check.That(request.Status).Contains("File");
// Verify
filesystemHandlerMock.Verify(fs => fs.WriteFile(It.Is<string>(p => p == "filename.txt"), It.IsAny<byte[]>()), Times.Once);
filesystemHandlerMock.Verify(fs => fs.FileExists(It.Is<string>(p => p == "filename.txt")), Times.Once);
filesystemHandlerMock.VerifyNoOtherCalls();
server.Stop();
}
[Fact]
public void IFluentMockServerAdmin_PutFileAsync_NotFound()
{
// Arrange
var filesystemHandlerMock = new Mock<IFileSystemHandler>(MockBehavior.Strict);
filesystemHandlerMock.Setup(fs => fs.FileExists(It.IsAny<string>())).Returns(false);
var server = FluentMockServer.Start(new FluentMockServerSettings
{
UseSSL = false,
StartAdminInterface = true,
FileSystemHandler = filesystemHandlerMock.Object
});
var api = RestClient.For<IFluentMockServerAdmin>(server.Urls[0]);
// Act and Assert
Check.ThatAsyncCode(() => api.PutFileAsync("filename.txt", "xxx")).Throws<ApiException>();
// Verify
filesystemHandlerMock.Verify(fs => fs.FileExists(It.Is<string>(p => p == "filename.txt")), Times.Once);
filesystemHandlerMock.VerifyNoOtherCalls();
server.Stop();
}
[Fact]
public void IFluentMockServerAdmin_GetFileAsync_NotFound()
{
// Arrange
var filesystemHandlerMock = new Mock<IFileSystemHandler>(MockBehavior.Strict);
filesystemHandlerMock.Setup(fs => fs.FileExists(It.IsAny<string>())).Returns(false);
filesystemHandlerMock.Setup(fs => fs.ReadFile(It.IsAny<string>())).Returns(Encoding.ASCII.GetBytes("Here's a string."));
var server = FluentMockServer.Start(new FluentMockServerSettings
{
UseSSL = false,
StartAdminInterface = true,
FileSystemHandler = filesystemHandlerMock.Object
});
var api = RestClient.For<IFluentMockServerAdmin>(server.Urls[0]);
// Act and Assert
Check.ThatAsyncCode(() => api.GetFileAsync("filename.txt")).Throws<ApiException>();
// Verify
filesystemHandlerMock.Verify(fs => fs.FileExists(It.Is<string>(p => p == "filename.txt")), Times.Once);
filesystemHandlerMock.VerifyNoOtherCalls();
server.Stop();
}
[Fact]
public async Task IFluentMockServerAdmin_GetFileAsync_Found()
{
// Arrange
string data = "Here's a string.";
var filesystemHandlerMock = new Mock<IFileSystemHandler>(MockBehavior.Strict);
filesystemHandlerMock.Setup(fs => fs.FileExists(It.IsAny<string>())).Returns(true);
filesystemHandlerMock.Setup(fs => fs.ReadFile(It.IsAny<string>())).Returns(Encoding.ASCII.GetBytes(data));
var server = FluentMockServer.Start(new FluentMockServerSettings
{
UseSSL = false,
StartAdminInterface = true,
FileSystemHandler = filesystemHandlerMock.Object
});
var api = RestClient.For<IFluentMockServerAdmin>(server.Urls[0]);
// Act
string file = await api.GetFileAsync("filename.txt");
// Assert
Check.That(file).Equals(data);
// Verify
filesystemHandlerMock.Verify(fs => fs.FileExists(It.Is<string>(p => p == "filename.txt")), Times.Once);
filesystemHandlerMock.Verify(fs => fs.ReadFile(It.Is<string>(p => p == "filename.txt")), Times.Once);
filesystemHandlerMock.VerifyNoOtherCalls();
server.Stop();
}
[Fact]
public async Task IFluentMockServerAdmin_DeleteFileAsync_Ok()
{
// Arrange
var filesystemHandlerMock = new Mock<IFileSystemHandler>(MockBehavior.Strict);
filesystemHandlerMock.Setup(fs => fs.FileExists(It.IsAny<string>())).Returns(true);
filesystemHandlerMock.Setup(fs => fs.DeleteFile(It.IsAny<string>()));
var server = FluentMockServer.Start(new FluentMockServerSettings
{
UseSSL = false,
StartAdminInterface = true,
FileSystemHandler = filesystemHandlerMock.Object
});
var api = RestClient.For<IFluentMockServerAdmin>(server.Urls[0]);
// Act
await api.DeleteFileAsync("filename.txt");
// Verify
filesystemHandlerMock.Verify(fs => fs.FileExists(It.Is<string>(p => p == "filename.txt")), Times.Once);
filesystemHandlerMock.Verify(fs => fs.DeleteFile(It.Is<string>(p => p == "filename.txt")), Times.Once);
filesystemHandlerMock.VerifyNoOtherCalls();
server.Stop();
}
[Fact]
public void IFluentMockServerAdmin_DeleteFileAsync_NotFound()
{
// Arrange
var filesystemHandlerMock = new Mock<IFileSystemHandler>(MockBehavior.Strict);
filesystemHandlerMock.Setup(fs => fs.FileExists(It.IsAny<string>())).Returns(false);
filesystemHandlerMock.Setup(fs => fs.DeleteFile(It.IsAny<string>()));
var server = FluentMockServer.Start(new FluentMockServerSettings
{
UseSSL = false,
StartAdminInterface = true,
FileSystemHandler = filesystemHandlerMock.Object
});
var api = RestClient.For<IFluentMockServerAdmin>(server.Urls[0]);
// Act and Assert
Check.ThatAsyncCode(() => api.DeleteFileAsync("filename.txt")).Throws<ApiException>();
// Verify
filesystemHandlerMock.Verify(fs => fs.FileExists(It.Is<string>(p => p == "filename.txt")), Times.Once);
filesystemHandlerMock.VerifyNoOtherCalls();
server.Stop();
}
[Fact]
public void IFluentMockServerAdmin_FileExistsAsync_NotFound()
{
// Arrange
var filesystemHandlerMock = new Mock<IFileSystemHandler>(MockBehavior.Strict);
filesystemHandlerMock.Setup(fs => fs.FileExists(It.IsAny<string>())).Returns(false);
var server = FluentMockServer.Start(new FluentMockServerSettings
{
UseSSL = false,
StartAdminInterface = true,
FileSystemHandler = filesystemHandlerMock.Object
});
var api = RestClient.For<IFluentMockServerAdmin>(server.Urls[0]);
// Act and Assert
Check.ThatAsyncCode(() => api.FileExistsAsync("filename.txt")).Throws<ApiException>();
// Verify
filesystemHandlerMock.Verify(fs => fs.FileExists(It.Is<string>(p => p == "filename.txt")), Times.Once);
filesystemHandlerMock.VerifyNoOtherCalls();
server.Stop();
}
}
}

View File

@@ -1,11 +1,11 @@
using System;
using Moq;
using Newtonsoft.Json;
using NFluent;
using System;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using Moq;
using Newtonsoft.Json;
using NFluent;
using WireMock.Handlers;
using WireMock.Logging;
using WireMock.RequestBuilders;
@@ -47,12 +47,14 @@ namespace WireMock.Net.Tests
server.ReadStaticMappings(folder);
Check.That(server.Mappings).HasSize(5);
Check.That(server.MappingModels).HasSize(5);
// Act
server.ResetMappings();
// Assert
Check.That(server.Mappings).HasSize(0);
Check.That(server.MappingModels).HasSize(0);
}
[Fact]
@@ -375,5 +377,32 @@ namespace WireMock.Net.Tests
fileMock.Verify(f => f.GetMappingFolder(), Times.Once);
fileMock.Verify(f => f.FolderExists(It.IsAny<string>()), Times.Once);
}
[Fact]
public void FluentMockServer_Admin_AddMappingsAndSaveToFile()
{
// Assign
string guid = "791a3f31-6946-aaaa-8e6f-0237c7442222";
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>()));
var server = FluentMockServer.Start(new FluentMockServerSettings
{
FileSystemHandler = staticMappingHandlerMock.Object
});
// Act
server
.Given(Request.Create().WithPath($"/foo_{Guid.NewGuid()}"), true)
.WithGuid(guid)
.RespondWith(Response.Create().WithBody("post and save test"));
// 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);
}
}
}

View File

@@ -290,6 +290,36 @@ namespace WireMock.Net.Tests
Check.That(response.Content.Headers.GetValues("Content-Type")).ContainsExactly("application/json; charset=utf-8");
}
[Fact]
public async Task FluentMockServer_Proxy_Should_set_Body_in_multipart_proxied_response()
{
// Assign
string path = $"/prx_{Guid.NewGuid().ToString()}";
var serverForProxyForwarding = FluentMockServer.Start();
serverForProxyForwarding
.Given(Request.Create().WithPath(path))
.RespondWith(Response.Create()
.WithBodyAsJson(new { i = 42 })
);
var server = FluentMockServer.Start();
server
.Given(Request.Create().WithPath(path))
.RespondWith(Response.Create().WithProxy(serverForProxyForwarding.Urls[0]));
// Act
var uri = new Uri($"{server.Urls[0]}{path}");
var form = new MultipartFormDataContent
{
{ new StringContent("data"), "test", "test.txt" }
};
var response = await new HttpClient().PostAsync(uri, form);
// Assert
string content = await response.Content.ReadAsStringAsync();
Check.That(content).IsEqualTo("{\"i\":42}");
}
[Fact]
public async Task FluentMockServer_Proxy_Should_Not_overrule_AdminMappings()
{

View File

@@ -62,7 +62,7 @@ namespace WireMock.Net.Tests
// Assert
var mappings = server.Mappings;
Check.That(mappings.Count()).IsEqualTo(19);
Check.That(mappings.Count()).IsEqualTo(24);
Check.That(mappings.All(m => m.Priority == int.MinValue)).IsTrue();
}
@@ -81,8 +81,8 @@ namespace WireMock.Net.Tests
// Assert
var mappings = server.Mappings;
Check.That(mappings.Count()).IsEqualTo(20);
Check.That(mappings.Count(m => m.Priority == int.MinValue)).IsEqualTo(19);
Check.That(mappings.Count()).IsEqualTo(25);
Check.That(mappings.Count(m => m.Priority == int.MinValue)).IsEqualTo(24);
Check.That(mappings.Count(m => m.Priority == 1000)).IsEqualTo(1);
}

View File

@@ -1,6 +1,7 @@
using NFluent;
using System;
using System.Diagnostics;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using WireMock.RequestBuilders;
@@ -172,5 +173,65 @@ namespace WireMock.Net.Tests
Check.That(response.Headers.Contains("test")).IsTrue();
Check.That(response.Headers.Contains("Transfer-Encoding")).IsFalse();
}
[Theory]
[InlineData("DELETE")]
#if !NET452
[InlineData("TRACE")]
[InlineData("GET")]
#endif
public async Task FluentMockServer_Should_exclude_body_for_methods_where_body_is_definitely_disallowed(string method)
{
// Assign
string content = "hello";
var server = FluentMockServer.Start();
server
.Given(Request.Create().WithBody(b => true))
.AtPriority(0)
.RespondWith(Response.Create().WithStatusCode(400));
server
.Given(Request.Create())
.AtPriority(1)
.RespondWith(Response.Create().WithStatusCode(200));
// Act
var request = new HttpRequestMessage(new HttpMethod(method), "http://localhost:" + server.Ports[0] + "/");
request.Content = new StringContent(content);
var response = await new HttpClient().SendAsync(request);
// Assert
Check.That(response.StatusCode).Equals(HttpStatusCode.OK);
}
[Theory]
[InlineData("POST")]
[InlineData("PUT")]
[InlineData("OPTIONS")]
[InlineData("REPORT")]
[InlineData("SOME-UNKNOWN-METHOD")] // default behavior for unknown methods is to allow a body (see BodyParser.ShouldParseBody)
public async Task FluentMockServer_Should_not_exclude_body_for_supported_methods(string method)
{
// Assign
string content = "hello";
var server = FluentMockServer.Start();
server
.Given(Request.Create().WithBody(content))
.AtPriority(0)
.RespondWith(Response.Create().WithStatusCode(200));
server
.Given(Request.Create())
.AtPriority(1)
.RespondWith(Response.Create().WithStatusCode(400));
// Act
var request = new HttpRequestMessage(new HttpMethod(method), "http://localhost:" + server.Ports[0] + "/");
request.Content = new StringContent(content);
var response = await new HttpClient().SendAsync(request);
// Assert
Check.That(response.StatusCode).Equals(HttpStatusCode.OK);
}
}
}

View File

@@ -1,6 +1,6 @@
using System;
using NFluent;
using System;
using System.IO;
using NFluent;
using WireMock.Handlers;
using Xunit;
@@ -21,24 +21,62 @@ namespace WireMock.Net.Tests.Handlers
}
[Fact]
public void LocalFileSystemHandler_CreateFolder_Throws()
public void LocalFileSystemHandler_CreateFolder_ThrowsArgumentNullException()
{
// Act
Check.ThatCode(() => _sut.CreateFolder(null)).Throws<ArgumentNullException>();
}
[Fact]
public void LocalFileSystemHandler_WriteMappingFile_Throws()
public void LocalFileSystemHandler_WriteMappingFile_ThrowsArgumentNullException()
{
// Act
Check.ThatCode(() => _sut.WriteMappingFile(null, null)).Throws<ArgumentNullException>();
}
[Fact]
public void LocalFileSystemHandler_ReadResponseBodyAsFile_Throws()
public void LocalFileSystemHandler_ReadResponseBodyAsFile_ThrowsArgumentNullException()
{
// Act
Check.ThatCode(() => _sut.ReadResponseBodyAsFile(null)).Throws<ArgumentNullException>();
}
[Fact]
public void LocalFileSystemHandler_FileExists_ReturnsFalse()
{
// Act
var result = _sut.FileExists("x.x");
// Assert
Check.That(result).IsFalse();
}
[Fact]
public void LocalFileSystemHandler_FileExists_ThrowsArgumentNullException()
{
// Act
Check.ThatCode(() => _sut.FileExists(null)).Throws<ArgumentNullException>();
}
[Fact]
public void LocalFileSystemHandler_ReadFile_ThrowsArgumentNullException()
{
// Act
Check.ThatCode(() => _sut.ReadFile(null)).Throws<ArgumentNullException>();
}
[Fact]
public void LocalFileSystemHandler_WriteFile_ThrowsArgumentNullException()
{
// Act
Check.ThatCode(() => _sut.WriteFile(null, null)).Throws<ArgumentNullException>();
}
[Fact]
public void LocalFileSystemHandler_DeleteFile_ThrowsArgumentNullException()
{
// Act
Check.ThatCode(() => _sut.DeleteFile(null)).Throws<ArgumentNullException>();
}
}
}

View File

@@ -33,7 +33,33 @@ namespace WireMock.Net.Tests.Matchers
}
[Fact]
public void ExactMatcher_IsMatch_MultiplePatterns()
public void ExactMatcher_IsMatch_WithSinglePattern_ReturnsMatch1_0()
{
// Assign
var matcher = new ExactMatcher("x");
// Act
double result = matcher.IsMatch("x");
// Assert
Check.That(result).IsEqualTo(1.0);
}
[Fact]
public void ExactMatcher_IsMatch_WithSinglePattern_ReturnsMatch0_0()
{
// Assign
var matcher = new ExactMatcher("x");
// Act
double result = matcher.IsMatch("y");
// Assert
Check.That(result).IsEqualTo(0.0);
}
[Fact]
public void ExactMatcher_IsMatch_WithMultiplePatterns_ReturnsMatch0_5()
{
// Assign
var matcher = new ExactMatcher("x", "y");
@@ -42,7 +68,7 @@ namespace WireMock.Net.Tests.Matchers
double result = matcher.IsMatch("x");
// Assert
Check.That(result).IsEqualTo(0.5d);
Check.That(result).IsEqualTo(1.0);
}
[Fact]

View File

@@ -0,0 +1,166 @@
using Newtonsoft.Json.Linq;
using NFluent;
using WireMock.Matchers;
using Xunit;
namespace WireMock.Net.Tests.Matchers
{
public class JmesPathMatcherTests
{
[Fact]
public void JmesPathMatcher_GetName()
{
// Assign
var matcher = new JmesPathMatcher("X");
// Act
string name = matcher.Name;
// Assert
Check.That(name).Equals("JmesPathMatcher");
}
[Fact]
public void JmesPathMatcher_GetPatterns()
{
// Assign
var matcher = new JmesPathMatcher("X");
// Act
string[] patterns = matcher.GetPatterns();
// Assert
Check.That(patterns).ContainsExactly("X");
}
[Fact]
public void JmesPathMatcher_IsMatch_ByteArray()
{
// Assign
var bytes = new byte[0];
var matcher = new JmesPathMatcher("");
// Act
double match = matcher.IsMatch(bytes);
// Assert
Check.That(match).IsEqualTo(0);
}
[Fact]
public void JmesPathMatcher_IsMatch_NullString()
{
// Assign
string s = null;
var matcher = new JmesPathMatcher("");
// Act
double match = matcher.IsMatch(s);
// Assert
Check.That(match).IsEqualTo(0);
}
[Fact]
public void JmesPathMatcher_IsMatch_NullObject()
{
// Assign
object o = null;
var matcher = new JmesPathMatcher("");
// Act
double match = matcher.IsMatch(o);
// Assert
Check.That(match).IsEqualTo(0);
}
[Fact]
public void JmesPathMatcher_IsMatch_String_Exception_Mismatch()
{
// Assign
var matcher = new JmesPathMatcher("xxx");
// Act
double match = matcher.IsMatch("");
// Assert
Check.That(match).IsEqualTo(0);
}
[Fact]
public void JmesPathMatcher_IsMatch_Object_Exception_Mismatch()
{
// Assign
var matcher = new JmesPathMatcher("");
// Act
double match = matcher.IsMatch("x");
// Assert
Check.That(match).IsEqualTo(0);
}
[Fact]
public void JmesPathMatcher_IsMatch_AnonymousObject()
{
// Assign
var matcher = new JmesPathMatcher("things.name == 'RequiredThing'");
// Act
double match = matcher.IsMatch(new { things = new { name = "RequiredThing" } });
// Assert
Check.That(match).IsEqualTo(1);
}
[Fact]
public void JmesPathMatcher_IsMatch_JObject()
{
// Assign
string[] patterns = { "things.x == 'RequiredThing'" };
var matcher = new JmesPathMatcher(patterns);
// Act
var sub = new JObject
{
{ "x", new JValue("RequiredThing") }
};
var jobject = new JObject
{
{ "Id", new JValue(1) },
{ "things", sub }
};
double match = matcher.IsMatch(jobject);
// Assert
Check.That(match).IsEqualTo(1);
}
[Fact]
public void JmesPathMatcher_IsMatch_JObject_Parsed()
{
// Assign
var matcher = new JmesPathMatcher("things.x == 'RequiredThing'");
// Act
double match = matcher.IsMatch(JObject.Parse("{ \"things\": { \"x\": \"RequiredThing\" } }"));
// Assert
Check.That(match).IsEqualTo(1);
}
[Fact]
public void JmesPathMatcher_IsMatch_RejectOnMatch()
{
// Assign
var matcher = new JmesPathMatcher(MatchBehaviour.RejectOnMatch, "things.x == 'RequiredThing'");
// Act
double match = matcher.IsMatch(JObject.Parse("{ \"things\": { \"x\": \"RequiredThing\" } }"));
// Assert
Check.That(match).IsEqualTo(0.0);
}
}
}

View File

@@ -16,7 +16,7 @@ namespace WireMock.Net.Tests.RequestBuilders
// Assert 1
var matchers = requestBuilder.GetPrivateFieldValue<IList<IRequestMatcher>>("_requestMatchers");
Check.That(matchers.Count()).IsEqualTo(1);
Check.That(matchers.Count).IsEqualTo(1);
Check.That((matchers[0] as RequestMessageMethodMatcher).Methods).ContainsExactly("PATCH");
}
@@ -28,7 +28,7 @@ namespace WireMock.Net.Tests.RequestBuilders
// Assert 1
var matchers = requestBuilder.GetPrivateFieldValue<IList<IRequestMatcher>>("_requestMatchers");
Check.That(matchers.Count()).IsEqualTo(1);
Check.That(matchers.Count).IsEqualTo(1);
Check.That(matchers[0]).IsInstanceOfType(typeof(RequestMessageMethodMatcher));
// Act
@@ -36,7 +36,7 @@ namespace WireMock.Net.Tests.RequestBuilders
// Assert 2
matchers = requestBuilder.GetPrivateFieldValue<IList<IRequestMatcher>>("_requestMatchers");
Check.That(matchers.Count()).IsEqualTo(0);
Check.That(matchers.Count).IsEqualTo(0);
}
}
}

View File

@@ -1,5 +1,6 @@
using System.Collections.Generic;
using NFluent;
using FluentAssertions;
using System.Collections.Generic;
using System.Linq;
using WireMock.Matchers;
using WireMock.Matchers.Request;
using WireMock.RequestBuilders;
@@ -20,8 +21,24 @@ namespace WireMock.Net.Tests.RequestBuilders
// Assert
var matchers = requestBuilder.GetPrivateFieldValue<IList<IRequestMatcher>>("_requestMatchers");
Check.That(matchers.Count()).IsEqualTo(1);
Check.That(((RequestMessageBodyMatcher) matchers[0]).Matcher).IsEqualTo(matcher);
matchers.Should().HaveCount(1);
((RequestMessageBodyMatcher)matchers[0]).Matchers.Should().Contain(matcher);
}
[Fact]
public void RequestBuilder_WithBody_IMatchers()
{
// Assign
var matcher1 = new WildcardMatcher("x");
var matcher2 = new WildcardMatcher("y");
// Act
var requestBuilder = (Request)Request.Create().WithBody(new[] { matcher1, matcher2 }.Cast<IMatcher>().ToArray());
// Assert
var matchers = requestBuilder.GetPrivateFieldValue<IList<IRequestMatcher>>("_requestMatchers");
matchers.Should().HaveCount(1);
((RequestMessageBodyMatcher)matchers[0]).Matchers.Should().Contain(new[] { matcher1, matcher2 });
}
}
}

View File

@@ -17,7 +17,7 @@ namespace WireMock.Net.Tests.RequestBuilders
// Assert
var matchers = requestBuilder.GetPrivateFieldValue<IList<IRequestMatcher>>("_requestMatchers");
Check.That(matchers.Count()).IsEqualTo(1);
Check.That(matchers.Count).IsEqualTo(1);
Check.That(matchers[0]).IsInstanceOfType(typeof(RequestMessageCookieMatcher));
}
@@ -29,7 +29,7 @@ namespace WireMock.Net.Tests.RequestBuilders
// Assert
var matchers = requestBuilder.GetPrivateFieldValue<IList<IRequestMatcher>>("_requestMatchers");
Check.That(matchers.Count()).IsEqualTo(1);
Check.That(matchers.Count).IsEqualTo(1);
Check.That(matchers[0]).IsInstanceOfType(typeof(RequestMessageCookieMatcher));
}
@@ -41,7 +41,7 @@ namespace WireMock.Net.Tests.RequestBuilders
// Assert
var matchers = requestBuilder.GetPrivateFieldValue<IList<IRequestMatcher>>("_requestMatchers");
Check.That(matchers.Count()).IsEqualTo(1);
Check.That(matchers.Count).IsEqualTo(1);
Check.That(matchers[0]).IsInstanceOfType(typeof(RequestMessageCookieMatcher));
}
}

View File

@@ -17,7 +17,7 @@ namespace WireMock.Net.Tests.RequestBuilders
// Assert
var matchers = requestBuilder.GetPrivateFieldValue<IList<IRequestMatcher>>("_requestMatchers");
Check.That(matchers.Count()).IsEqualTo(1);
Check.That(matchers.Count).IsEqualTo(1);
Check.That(matchers[0]).IsInstanceOfType(typeof(RequestMessageHeaderMatcher));
}
@@ -29,7 +29,7 @@ namespace WireMock.Net.Tests.RequestBuilders
// Assert
var matchers = requestBuilder.GetPrivateFieldValue<IList<IRequestMatcher>>("_requestMatchers");
Check.That(matchers.Count()).IsEqualTo(1);
Check.That(matchers.Count).IsEqualTo(1);
Check.That(matchers[0]).IsInstanceOfType(typeof(RequestMessageHeaderMatcher));
}
@@ -41,7 +41,7 @@ namespace WireMock.Net.Tests.RequestBuilders
// Assert
var matchers = requestBuilder.GetPrivateFieldValue<IList<IRequestMatcher>>("_requestMatchers");
Check.That(matchers.Count()).IsEqualTo(1);
Check.That(matchers.Count).IsEqualTo(1);
Check.That(matchers[0]).IsInstanceOfType(typeof(RequestMessageHeaderMatcher));
}
@@ -53,7 +53,7 @@ namespace WireMock.Net.Tests.RequestBuilders
// Assert
var matchers = requestBuilder.GetPrivateFieldValue<IList<IRequestMatcher>>("_requestMatchers");
Check.That(matchers.Count()).IsEqualTo(1);
Check.That(matchers.Count).IsEqualTo(1);
Check.That(matchers[0]).IsInstanceOfType(typeof(RequestMessageHeaderMatcher));
}
@@ -65,7 +65,7 @@ namespace WireMock.Net.Tests.RequestBuilders
// Assert
var matchers = requestBuilder.GetPrivateFieldValue<IList<IRequestMatcher>>("_requestMatchers");
Check.That(matchers.Count()).IsEqualTo(1);
Check.That(matchers.Count).IsEqualTo(1);
Check.That(matchers[0]).IsInstanceOfType(typeof(RequestMessageHeaderMatcher));
}
@@ -77,7 +77,7 @@ namespace WireMock.Net.Tests.RequestBuilders
// Assert
var matchers = requestBuilder.GetPrivateFieldValue<IList<IRequestMatcher>>("_requestMatchers");
Check.That(matchers.Count()).IsEqualTo(1);
Check.That(matchers.Count).IsEqualTo(1);
Check.That(matchers[0]).IsInstanceOfType(typeof(RequestMessageHeaderMatcher));
}
}

View File

@@ -17,7 +17,7 @@ namespace WireMock.Net.Tests.RequestBuilders
// Assert
var matchers = requestBuilder.GetPrivateFieldValue<IList<IRequestMatcher>>("_requestMatchers");
Check.That(matchers.Count()).IsEqualTo(1);
Check.That(matchers.Count).IsEqualTo(1);
Check.That(matchers[0]).IsInstanceOfType(typeof(RequestMessageParamMatcher));
}
@@ -29,7 +29,7 @@ namespace WireMock.Net.Tests.RequestBuilders
// Assert
var matchers = requestBuilder.GetPrivateFieldValue<IList<IRequestMatcher>>("_requestMatchers");
Check.That(matchers.Count()).IsEqualTo(1);
Check.That(matchers.Count).IsEqualTo(1);
Check.That(matchers[0]).IsInstanceOfType(typeof(RequestMessageParamMatcher));
}
@@ -41,7 +41,7 @@ namespace WireMock.Net.Tests.RequestBuilders
// Assert
var matchers = requestBuilder.GetPrivateFieldValue<IList<IRequestMatcher>>("_requestMatchers");
Check.That(matchers.Count()).IsEqualTo(1);
Check.That(matchers.Count).IsEqualTo(1);
Check.That(matchers[0]).IsInstanceOfType(typeof(RequestMessageParamMatcher));
}
@@ -53,7 +53,7 @@ namespace WireMock.Net.Tests.RequestBuilders
// Assert
var matchers = requestBuilder.GetPrivateFieldValue<IList<IRequestMatcher>>("_requestMatchers");
Check.That(matchers.Count()).IsEqualTo(1);
Check.That(matchers.Count).IsEqualTo(1);
Check.That(matchers[0]).IsInstanceOfType(typeof(RequestMessageParamMatcher));
}
}

View File

@@ -17,7 +17,7 @@ namespace WireMock.Net.Tests.RequestBuilders
// Assert
var matchers = requestBuilder.GetPrivateFieldValue<IList<IRequestMatcher>>("_requestMatchers");
Check.That(matchers.Count()).IsEqualTo(1);
Check.That(matchers.Count).IsEqualTo(1);
Check.That(matchers[0]).IsInstanceOfType(typeof(RequestMessageUrlMatcher));
}
@@ -29,7 +29,7 @@ namespace WireMock.Net.Tests.RequestBuilders
// Assert
var matchers = requestBuilder.GetPrivateFieldValue<IList<IRequestMatcher>>("_requestMatchers");
Check.That(matchers.Count()).IsEqualTo(1);
Check.That(matchers.Count).IsEqualTo(1);
Check.That(matchers[0]).IsInstanceOfType(typeof(RequestMessageUrlMatcher));
}
@@ -41,7 +41,7 @@ namespace WireMock.Net.Tests.RequestBuilders
// Assert
var matchers = requestBuilder.GetPrivateFieldValue<IList<IRequestMatcher>>("_requestMatchers");
Check.That(matchers.Count()).IsEqualTo(1);
Check.That(matchers.Count).IsEqualTo(1);
Check.That(matchers[0]).IsInstanceOfType(typeof(RequestMessageUrlMatcher));
}
@@ -53,7 +53,7 @@ namespace WireMock.Net.Tests.RequestBuilders
// Assert
var matchers = requestBuilder.GetPrivateFieldValue<IList<IRequestMatcher>>("_requestMatchers");
Check.That(matchers.Count()).IsEqualTo(1);
Check.That(matchers.Count).IsEqualTo(1);
Check.That(matchers[0]).IsInstanceOfType(typeof(RequestMessageUrlMatcher));
}
}

View File

@@ -1,4 +1,5 @@
using Moq;
using System.Linq;
using Moq;
using NFluent;
using WireMock.Matchers;
using WireMock.Matchers.Request;
@@ -38,6 +39,39 @@ namespace WireMock.Net.Tests.RequestMatchers
stringMatcherMock.Verify(m => m.IsMatch("b"), Times.Once);
}
[Fact]
public void RequestMessageBodyMatcher_GetMatchingScore_BodyAsString_IStringMatchers()
{
// Assign
var body = new BodyData
{
BodyAsString = "b",
DetectedBodyType = BodyType.String
};
var stringMatcherMock1 = new Mock<IStringMatcher>();
stringMatcherMock1.Setup(m => m.IsMatch(It.IsAny<string>())).Returns(0.2d);
var stringMatcherMock2 = new Mock<IStringMatcher>();
stringMatcherMock2.Setup(m => m.IsMatch(It.IsAny<string>())).Returns(0.8d);
var matchers = new[] { stringMatcherMock1.Object, stringMatcherMock2.Object };
var requestMessage = new RequestMessage(new UrlDetails("http://localhost"), "GET", "127.0.0.1", body);
var matcher = new RequestMessageBodyMatcher(matchers.Cast<IMatcher>().ToArray());
// Act
var result = new RequestMatchResult();
double score = matcher.GetMatchingScore(requestMessage, result);
// Assert
Check.That(score).IsEqualTo(0.8d);
// Verify
stringMatcherMock1.Verify(m => m.GetPatterns(), Times.Never);
stringMatcherMock1.Verify(m => m.IsMatch("b"), Times.Once);
stringMatcherMock2.Verify(m => m.GetPatterns(), Times.Never);
stringMatcherMock2.Verify(m => m.IsMatch("b"), Times.Once);
}
[Fact]
public void RequestMessageBodyMatcher_GetMatchingScore_BodyAsBytes_IStringMatcher()
{

View File

@@ -9,11 +9,11 @@ namespace WireMock.Net.Tests.RequestMatchers
public class RequestMessageParamMatcherTests
{
[Fact]
public void RequestMessageParamMatcher_GetMatchingScore_KeyWithValuesPresentInUrl_MatchExactOnStringValues()
public void RequestMessageParamMatcher_GetMatchingScore_IgnoreCaseKeyWithValuesPresentInUrl_And_With1StringValues_Returns1_0()
{
// Assign
var requestMessage = new RequestMessage(new UrlDetails("http://localhost?key=test1,test2"), "GET", "127.0.0.1");
var matcher = new RequestMessageParamMatcher(MatchBehaviour.AcceptOnMatch, "key", new[] { "test1", "test2" });
var requestMessage = new RequestMessage(new UrlDetails("http://localhost?key=test1"), "GET", "127.0.0.1");
var matcher = new RequestMessageParamMatcher(MatchBehaviour.AcceptOnMatch, "KeY", true, new[] { "test1" });
// Act
var result = new RequestMatchResult();
@@ -24,11 +24,71 @@ namespace WireMock.Net.Tests.RequestMatchers
}
[Fact]
public void RequestMessageParamMatcher_GetMatchingScore_KeyWithValuesPresentInUrl_MatchExactOnExactMatchers()
public void RequestMessageParamMatcher_GetMatchingScore_KeyWith1ValuePresentInUrl_And_With2Strings_Returns0_5()
{
// Assign
var requestMessage = new RequestMessage(new UrlDetails("http://localhost?key=test1"), "GET", "127.0.0.1");
var matcher = new RequestMessageParamMatcher(MatchBehaviour.AcceptOnMatch, "key", false, new[] { "test1", "test2" });
// Act
var result = new RequestMatchResult();
double score = matcher.GetMatchingScore(requestMessage, result);
// Assert
Check.That(score).IsEqualTo(0.5d);
}
[Fact]
public void RequestMessageParamMatcher_GetMatchingScore_KeyWith3ValuesPresentInUrl_And_With1ExactStringWith2Patterns_Returns0_66()
{
// Assign
var requestMessage = new RequestMessage(new UrlDetails("http://localhost?key=test1,test2,test3"), "GET", "127.0.0.1");
var matcher = new RequestMessageParamMatcher(MatchBehaviour.AcceptOnMatch, "key", false, new IStringMatcher[] { new ExactMatcher("test1", "test2") });
// Act
var result = new RequestMatchResult();
double score = matcher.GetMatchingScore(requestMessage, result);
// Assert
Check.That(score).IsCloseTo(0.66d, 0.1d);
}
[Fact]
public void RequestMessageParamMatcher_GetMatchingScore_KeyWith2ValuesPresentInUrl_And_With1ExactStringWith3Patterns_Returns0_66()
{
// Assign
var requestMessage = new RequestMessage(new UrlDetails("http://localhost?key=test1,test2"), "GET", "127.0.0.1");
var matcher = new RequestMessageParamMatcher(MatchBehaviour.AcceptOnMatch, "key", new IStringMatcher[] { new ExactMatcher("test1"), new ExactMatcher("test2") });
var matcher = new RequestMessageParamMatcher(MatchBehaviour.AcceptOnMatch, "key", false, new IStringMatcher[] { new ExactMatcher("test1", "test2", "test3") });
// Act
var result = new RequestMatchResult();
double score = matcher.GetMatchingScore(requestMessage, result);
// Assert
Check.That(score).IsCloseTo(0.66d, 0.1d);
}
[Fact]
public void RequestMessageParamMatcher_GetMatchingScore_KeyWith2ValuesPresentInUrl_And_With2Strings_Returns1_0()
{
// Assign
var requestMessage = new RequestMessage(new UrlDetails("http://localhost?key=test1,test2"), "GET", "127.0.0.1");
var matcher = new RequestMessageParamMatcher(MatchBehaviour.AcceptOnMatch, "key", false, new[] { "test1", "test2" });
// Act
var result = new RequestMatchResult();
double score = matcher.GetMatchingScore(requestMessage, result);
// Assert
Check.That(score).IsEqualTo(1.0d);
}
[Fact]
public void RequestMessageParamMatcher_GetMatchingScore_KeyWith2ValuesPresentInUrl_And_With2ExactStringMatchers_Returns1_0()
{
// Assign
var requestMessage = new RequestMessage(new UrlDetails("http://localhost?key=test1,test2"), "GET", "127.0.0.1");
var matcher = new RequestMessageParamMatcher(MatchBehaviour.AcceptOnMatch, "key", false, new IStringMatcher[] { new ExactMatcher("test1"), new ExactMatcher("test2") });
// Act
var result = new RequestMatchResult();
@@ -43,7 +103,7 @@ namespace WireMock.Net.Tests.RequestMatchers
{
// Assign
var requestMessage = new RequestMessage(new UrlDetails("http://localhost?key=test0,test2"), "GET", "127.0.0.1");
var matcher = new RequestMessageParamMatcher(MatchBehaviour.AcceptOnMatch, "key", new[] { "test1", "test2" });
var matcher = new RequestMessageParamMatcher(MatchBehaviour.AcceptOnMatch, "key", false, new[] { "test1", "test2" });
// Act
var result = new RequestMatchResult();
@@ -58,7 +118,7 @@ namespace WireMock.Net.Tests.RequestMatchers
{
// Assign
var requestMessage = new RequestMessage(new UrlDetails("http://localhost?key"), "GET", "127.0.0.1");
var matcher = new RequestMessageParamMatcher(MatchBehaviour.AcceptOnMatch, "key", new[] { "test1", "test2" });
var matcher = new RequestMessageParamMatcher(MatchBehaviour.AcceptOnMatch, "key", false, new[] { "test1", "test2" });
// Act
var result = new RequestMatchResult();
@@ -73,7 +133,7 @@ namespace WireMock.Net.Tests.RequestMatchers
{
// Assign
var requestMessage = new RequestMessage(new UrlDetails("http://localhost?key"), "GET", "127.0.0.1");
var matcher = new RequestMessageParamMatcher(MatchBehaviour.AcceptOnMatch, "key");
var matcher = new RequestMessageParamMatcher(MatchBehaviour.AcceptOnMatch, "key", false);
// Act
var result = new RequestMatchResult();
@@ -88,7 +148,7 @@ namespace WireMock.Net.Tests.RequestMatchers
{
// Assign
var requestMessage = new RequestMessage(new UrlDetails("http://localhost?key"), "GET", "127.0.0.1");
var matcher = new RequestMessageParamMatcher(MatchBehaviour.AcceptOnMatch, "key", new string[] { });
var matcher = new RequestMessageParamMatcher(MatchBehaviour.AcceptOnMatch, "key", false, new string[] { });
// Act
var result = new RequestMatchResult();
@@ -103,7 +163,7 @@ namespace WireMock.Net.Tests.RequestMatchers
{
// Assign
var requestMessage = new RequestMessage(new UrlDetails("http://localhost?key=frank@contoso.com"), "GET", "127.0.0.1");
var matcher = new RequestMessageParamMatcher(MatchBehaviour.AcceptOnMatch, "key");
var matcher = new RequestMessageParamMatcher(MatchBehaviour.AcceptOnMatch, "key", false);
// Act
var result = new RequestMatchResult();

View File

@@ -39,6 +39,16 @@ namespace WireMock.Net.Tests
Check.That(request.GetParameter("foo")).ContainsExactly("bar");
}
[Fact]
public void RequestMessage_ParseQuery_SingleKey_SingleValue_WithIgnoreCase()
{
// Assign
var request = new RequestMessage(new UrlDetails("http://localhost?foo=bar"), "POST", ClientIp);
// Assert
Check.That(request.GetParameter("FoO", true)).ContainsExactly("bar");
}
[Fact]
public void RequestMessage_ParseQuery_MultipleKeys_MultipleValues()
{
@@ -50,6 +60,18 @@ namespace WireMock.Net.Tests
Check.That(request.GetParameter("key")).Contains("2");
}
[Fact]
public void RequestMessage_ParseQuery_SingleKey_MultipleValuesCommaSeparated()
{
// Assign
var request = new RequestMessage(new UrlDetails("http://localhost?key=1,2,3"), "POST", ClientIp);
// Assert
Check.That(request.GetParameter("key")).Contains("1");
Check.That(request.GetParameter("key")).Contains("2");
Check.That(request.GetParameter("key")).Contains("3");
}
[Fact]
public void RequestMessage_ParseQuery_SingleKey_MultipleValues()
{

View File

@@ -1,13 +1,17 @@
using System.Threading.Tasks;
using Moq;
using NFluent;
using System.Threading.Tasks;
using WireMock.Models;
using WireMock.ResponseBuilders;
using WireMock.Settings;
using Xunit;
namespace WireMock.Net.Tests.ResponseBuilders
{
public class ResponseCreateTests
{
private readonly Mock<IFluentMockServerSettings> _settingsMock = new Mock<IFluentMockServerSettings>();
[Fact]
public async Task Response_Create_Func()
{
@@ -18,7 +22,7 @@ namespace WireMock.Net.Tests.ResponseBuilders
var response = Response.Create(() => responseMessage);
// Act
var providedResponse = await response.ProvideResponseAsync(request);
var providedResponse = await response.ProvideResponseAsync(request, _settingsMock.Object);
// Assert
Check.That(providedResponse).Equals(responseMessage);

View File

@@ -0,0 +1,49 @@
using FluentAssertions;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using WireMock.RequestBuilders;
using WireMock.ResponseBuilders;
using WireMock.Server;
using Xunit;
namespace WireMock.Net.Tests.ResponseBuilders
{
public class ResponseWithBodyFromFileTests
{
[Fact]
public async Task Response_ProvideResponse_WithBodyFromFile()
{
// Arrange
var server = FluentMockServer.Start();
string path = Path.Combine(Directory.GetCurrentDirectory(), "__admin", "mappings", "MyXmlResponse.xml");
server
.Given(
Request
.Create()
.UsingGet()
.WithPath("/v1/content")
)
.RespondWith(
Response
.Create()
.WithStatusCode(HttpStatusCode.OK)
.WithHeader("Content-Type", "application/xml")
.WithBodyFromFile(path)
);
// Act
var response1 = await new HttpClient().GetStringAsync("http://localhost:" + server.Ports[0] + "/v1/content");
var response2 = await new HttpClient().GetStringAsync("http://localhost:" + server.Ports[0] + "/v1/content");
var response3 = await new HttpClient().GetStringAsync("http://localhost:" + server.Ports[0] + "/v1/content");
// Assert
response1.Should().Contain("<hello>world</hello>");
response2.Should().Contain("<hello>world</hello>");
response3.Should().Contain("<hello>world</hello>");
}
}
}

View File

@@ -1,8 +1,12 @@
using System.Text;
using System.Threading.Tasks;
using Moq;
using Newtonsoft.Json.Linq;
using NFluent;
using System;
using System.Text;
using System.Threading.Tasks;
using WireMock.Models;
using WireMock.ResponseBuilders;
using WireMock.Settings;
using WireMock.Util;
using Xunit;
@@ -10,6 +14,7 @@ namespace WireMock.Net.Tests.ResponseBuilders
{
public class ResponseWithBodyTests
{
private readonly Mock<IFluentMockServerSettings> _settingsMock = new Mock<IFluentMockServerSettings>();
private const string ClientIp = "::1";
[Fact]
@@ -26,7 +31,7 @@ namespace WireMock.Net.Tests.ResponseBuilders
var response = Response.Create().WithBody(new byte[] { 48, 49 }, BodyDestinationFormat.String, Encoding.ASCII);
// act
var responseMessage = await response.ProvideResponseAsync(request);
var responseMessage = await response.ProvideResponseAsync(request, _settingsMock.Object);
// then
Check.That(responseMessage.BodyData.BodyAsString).Equals("01");
@@ -48,7 +53,7 @@ namespace WireMock.Net.Tests.ResponseBuilders
var response = Response.Create().WithBody(new byte[] { 48, 49 }, BodyDestinationFormat.SameAsSource, Encoding.ASCII);
// act
var responseMessage = await response.ProvideResponseAsync(request);
var responseMessage = await response.ProvideResponseAsync(request, _settingsMock.Object);
// then
Check.That(responseMessage.BodyData.BodyAsBytes).ContainsExactly(new byte[] { 48, 49 });
@@ -70,7 +75,7 @@ namespace WireMock.Net.Tests.ResponseBuilders
var response = Response.Create().WithBody("test", null, Encoding.ASCII);
// act
var responseMessage = await response.ProvideResponseAsync(request);
var responseMessage = await response.ProvideResponseAsync(request, _settingsMock.Object);
// then
Check.That(responseMessage.BodyData.BodyAsString).Equals("test");
@@ -92,7 +97,7 @@ namespace WireMock.Net.Tests.ResponseBuilders
var response = Response.Create().WithBodyAsJson(x, Encoding.ASCII);
// act
var responseMessage = await response.ProvideResponseAsync(request);
var responseMessage = await response.ProvideResponseAsync(request, _settingsMock.Object);
// then
Check.That(responseMessage.BodyData.BodyAsJson).Equals(x);
@@ -114,7 +119,7 @@ namespace WireMock.Net.Tests.ResponseBuilders
var response = Response.Create().WithBodyAsJson(x, true);
// act
var responseMessage = await response.ProvideResponseAsync(request);
var responseMessage = await response.ProvideResponseAsync(request, _settingsMock.Object);
// then
Check.That(responseMessage.BodyData.BodyAsJson).Equals(x);
@@ -130,7 +135,7 @@ namespace WireMock.Net.Tests.ResponseBuilders
var response = Response.Create().WithBody("r", BodyDestinationFormat.SameAsSource, Encoding.ASCII);
// Act
var responseMessage = await response.ProvideResponseAsync(request);
var responseMessage = await response.ProvideResponseAsync(request, _settingsMock.Object);
// Assert
Check.That(responseMessage.BodyData.BodyAsBytes).IsNull();
@@ -148,7 +153,7 @@ namespace WireMock.Net.Tests.ResponseBuilders
var response = Response.Create().WithBody("r", BodyDestinationFormat.Bytes, Encoding.ASCII);
// Act
var responseMessage = await response.ProvideResponseAsync(request);
var responseMessage = await response.ProvideResponseAsync(request, _settingsMock.Object);
// Assert
Check.That(responseMessage.BodyData.BodyAsString).IsNull();
@@ -166,7 +171,7 @@ namespace WireMock.Net.Tests.ResponseBuilders
var response = Response.Create().WithBody("{ \"value\": 42 }", BodyDestinationFormat.Json, Encoding.ASCII);
// Act
var responseMessage = await response.ProvideResponseAsync(request);
var responseMessage = await response.ProvideResponseAsync(request, _settingsMock.Object);
// Assert
Check.That(responseMessage.BodyData.BodyAsString).IsNull();
@@ -188,7 +193,7 @@ namespace WireMock.Net.Tests.ResponseBuilders
.WithBody(req => $"path: {req.Path}");
// Act
var responseMessage = await response.ProvideResponseAsync(request);
var responseMessage = await response.ProvideResponseAsync(request, _settingsMock.Object);
// Assert
Check.That(responseMessage.BodyData.BodyAsString).IsEqualTo("path: /test");
@@ -199,5 +204,84 @@ namespace WireMock.Net.Tests.ResponseBuilders
Check.That(responseMessage.Headers["H1"].ToString()).IsEqualTo("X1");
Check.That(responseMessage.Headers["H2"].ToString()).IsEqualTo("X2");
}
[Fact]
public async Task Response_ProvideResponse_WithJsonBodyAndTransform_Func()
{
// Assign
const int request1Id = 1;
const int request2Id = 2;
var request1 = new RequestMessage(new UrlDetails($"http://localhost/test?id={request1Id}"), "GET", ClientIp);
var request2 = new RequestMessage(new UrlDetails($"http://localhost/test?id={request2Id}"), "GET", ClientIp);
var response = Response.Create()
.WithStatusCode(200)
.WithBodyAsJson(JObject.Parse("{ \"id\": \"{{request.query.id}}\" }"))
.WithTransformer();
// Act
var response1Message = await response.ProvideResponseAsync(request1, _settingsMock.Object);
var response2Message = await response.ProvideResponseAsync(request2, _settingsMock.Object);
// Assert
Check.That(((JToken)response1Message.BodyData.BodyAsJson).SelectToken("id")?.Value<int>()).IsEqualTo(request1Id);
Check.That(response1Message.BodyData.BodyAsBytes).IsNull();
Check.That(response1Message.BodyData.BodyAsString).IsNull();
Check.That(response1Message.StatusCode).IsEqualTo(200);
Check.That(((JToken)response2Message.BodyData.BodyAsJson).SelectToken("id")?.Value<int>()).IsEqualTo(request2Id);
Check.That(response2Message.BodyData.BodyAsBytes).IsNull();
Check.That(response2Message.BodyData.BodyAsString).IsNull();
Check.That(response2Message.StatusCode).IsEqualTo(200);
}
[Fact]
public async Task Response_ProvideResponse_WithBodyAsFile()
{
var fileContents = "testFileContents" + Guid.NewGuid();
var bodyDataAsFile = new BodyData { BodyAsFile = fileContents };
var request1 = new RequestMessage(new UrlDetails("http://localhost/__admin/files/filename.txt"), "PUT", ClientIp, bodyDataAsFile);
var response = Response.Create().WithStatusCode(200).WithBody(fileContents);
var provideResponseAsync = await response.ProvideResponseAsync(request1, _settingsMock.Object);
Check.That(provideResponseAsync.StatusCode).IsEqualTo(200);
Check.That(provideResponseAsync.BodyData.BodyAsString).Contains(fileContents);
}
[Fact]
public async Task Response_ProvideResponse_WithResponseAsFile()
{
var fileContents = "testFileContents" + Guid.NewGuid();
var bodyDataAsFile = new BodyData { BodyAsFile = fileContents };
var request1 = new RequestMessage(new UrlDetails("http://localhost/__admin/files/filename.txt"), "GET", ClientIp, bodyDataAsFile);
var response = Response.Create().WithStatusCode(200).WithBody(fileContents);
var provideResponseAsync = await response.ProvideResponseAsync(request1, _settingsMock.Object);
Check.That(provideResponseAsync.StatusCode).IsEqualTo(200);
Check.That(provideResponseAsync.BodyData.BodyAsString).Contains(fileContents);
}
[Fact]
public async Task Response_ProvideResponse_WithResponseDeleted()
{
var fileContents = "testFileContents" + Guid.NewGuid();
var bodyDataAsFile = new BodyData { BodyAsFile = fileContents };
var request1 = new RequestMessage(new UrlDetails("http://localhost/__admin/files/filename.txt"), "DELETE", ClientIp, bodyDataAsFile);
var response = Response.Create().WithStatusCode(200).WithBody("File deleted.");
var provideResponseAsync = await response.ProvideResponseAsync(request1, _settingsMock.Object);
Check.That(provideResponseAsync.StatusCode).IsEqualTo(200);
Check.That(provideResponseAsync.BodyData.BodyAsString).Contains("File deleted.");
}
}
}
}

View File

@@ -1,7 +1,9 @@
using System.Threading.Tasks;
using Moq;
using NFluent;
using System.Threading.Tasks;
using WireMock.Models;
using WireMock.ResponseBuilders;
using WireMock.Settings;
using WireMock.Util;
using Xunit;
@@ -9,6 +11,8 @@ namespace WireMock.Net.Tests.ResponseBuilders
{
public class ResponseWithCallbackTests
{
private readonly Mock<IFluentMockServerSettings> _settingsMock = new Mock<IFluentMockServerSettings>();
[Fact]
public async Task Response_WithCallback()
{
@@ -17,7 +21,7 @@ namespace WireMock.Net.Tests.ResponseBuilders
var response = Response.Create().WithCallback(req => new ResponseMessage { BodyData = new BodyData { DetectedBodyType = BodyType.String, BodyAsString = req.Path + "Bar" }, StatusCode = 302 });
// Act
var responseMessage = await response.ProvideResponseAsync(request);
var responseMessage = await response.ProvideResponseAsync(request, _settingsMock.Object);
// Assert
Check.That(responseMessage.BodyData.BodyAsString).IsEqualTo("/fooBar");

View File

@@ -0,0 +1,100 @@
using Moq;
using Newtonsoft.Json.Linq;
using NFluent;
using System;
using System.Threading.Tasks;
using WireMock.Handlers;
using WireMock.Models;
using WireMock.ResponseBuilders;
using WireMock.Settings;
using Xunit;
namespace WireMock.Net.Tests.ResponseBuilders
{
public class ResponseWithHandlebarsFileTests
{
private readonly Mock<IFileSystemHandler> _filesystemHandlerMock;
private readonly Mock<IFluentMockServerSettings> _settingsMock;
private const string ClientIp = "::1";
public ResponseWithHandlebarsFileTests()
{
_filesystemHandlerMock = new Mock<IFileSystemHandler>(MockBehavior.Strict);
_filesystemHandlerMock.Setup(fs => fs.ReadResponseBodyAsString(It.IsAny<string>())).Returns("abc");
_settingsMock = new Mock<IFluentMockServerSettings>();
_settingsMock.SetupGet(s => s.FileSystemHandler).Returns(_filesystemHandlerMock.Object);
}
[Fact]
public async Task Response_ProvideResponseAsync_Handlebars_File()
{
// Assign
var request = new RequestMessage(new UrlDetails("http://localhost:1234"), "GET", ClientIp);
var response = Response.Create()
.WithBodyAsJson(new
{
Data = "{{File \"x.json\"}}"
})
.WithTransformer();
// Act
var responseMessage = await response.ProvideResponseAsync(request, _settingsMock.Object);
// Assert
JObject j = JObject.FromObject(responseMessage.BodyData.BodyAsJson);
Check.That(j["Data"].Value<string>()).Equals("abc");
// Verify
_filesystemHandlerMock.Verify(fs => fs.ReadResponseBodyAsString("x.json"), Times.Once);
_filesystemHandlerMock.VerifyNoOtherCalls();
}
[Fact]
public async Task Response_ProvideResponseAsync_Handlebars_File_Replace()
{
// Assign
var request = new RequestMessage(new UrlDetails("http://localhost:1234?id=x"), "GET", ClientIp);
var response = Response.Create()
.WithBodyAsJson(new
{
Data = "{{File \"{{request.query.id}}.json\"}}"
})
.WithTransformer();
// Act
var responseMessage = await response.ProvideResponseAsync(request, _settingsMock.Object);
// Assert
JObject j = JObject.FromObject(responseMessage.BodyData.BodyAsJson);
Check.That(j["Data"].Value<string>()).Equals("abc");
// Verify
_filesystemHandlerMock.Verify(fs => fs.ReadResponseBodyAsString("x.json"), Times.Once);
_filesystemHandlerMock.VerifyNoOtherCalls();
}
[Fact]
public void Response_ProvideResponseAsync_Handlebars_File_WithMissingArgument_ThrowsArgumentOutOfRangeException()
{
// Assign
var request = new RequestMessage(new UrlDetails("http://localhost:1234"), "GET", ClientIp);
var response = Response.Create()
.WithBodyAsJson(new
{
Data = "{{File}}"
})
.WithTransformer();
// Act
Check.ThatAsyncCode(() => response.ProvideResponseAsync(request, _settingsMock.Object)).Throws<ArgumentOutOfRangeException>();
// Verify
_filesystemHandlerMock.Verify(fs => fs.ReadResponseBodyAsString(It.IsAny<string>()), Times.Never);
_filesystemHandlerMock.VerifyNoOtherCalls();
}
}
}

View File

@@ -1,9 +1,11 @@
using System;
using System.Threading.Tasks;
using Moq;
using Newtonsoft.Json.Linq;
using NFluent;
using System;
using System.Threading.Tasks;
using WireMock.Models;
using WireMock.ResponseBuilders;
using WireMock.Settings;
using WireMock.Util;
using Xunit;
@@ -11,6 +13,7 @@ namespace WireMock.Net.Tests.ResponseBuilders
{
public class ResponseWithHandlebarsJsonPathTests
{
private readonly Mock<IFluentMockServerSettings> _settingsMock = new Mock<IFluentMockServerSettings>();
private const string ClientIp = "::1";
[Fact]
@@ -60,7 +63,7 @@ namespace WireMock.Net.Tests.ResponseBuilders
.WithTransformer();
// Act
var responseMessage = await response.ProvideResponseAsync(request);
var responseMessage = await response.ProvideResponseAsync(request, _settingsMock.Object);
// Assert
JObject j = JObject.FromObject(responseMessage.BodyData.BodyAsJson);
@@ -86,7 +89,7 @@ namespace WireMock.Net.Tests.ResponseBuilders
.WithTransformer();
// Act
var responseMessage = await response.ProvideResponseAsync(request);
var responseMessage = await response.ProvideResponseAsync(request, _settingsMock.Object);
// Assert
JObject j = JObject.FromObject(responseMessage.BodyData.BodyAsJson);
@@ -140,7 +143,7 @@ namespace WireMock.Net.Tests.ResponseBuilders
.WithTransformer();
// Act
var responseMessage = await response.ProvideResponseAsync(request);
var responseMessage = await response.ProvideResponseAsync(request, _settingsMock.Object);
// Assert
Check.That(responseMessage.BodyData.BodyAsString).Equals($"{{{Environment.NewLine} \"Name\": \"Acme Co\",{Environment.NewLine} \"Products\": [{Environment.NewLine} {{{Environment.NewLine} \"Name\": \"Anvil\",{Environment.NewLine} \"Price\": 50{Environment.NewLine} }}{Environment.NewLine} ]{Environment.NewLine}}}");
@@ -193,7 +196,7 @@ namespace WireMock.Net.Tests.ResponseBuilders
.WithTransformer();
// Act
var responseMessage = await response.ProvideResponseAsync(request);
var responseMessage = await response.ProvideResponseAsync(request, _settingsMock.Object);
// Assert
Check.That(responseMessage.BodyData.BodyAsString).Equals($"{{{Environment.NewLine} \"Name\": \"Acme Co\",{Environment.NewLine} \"Products\": [{Environment.NewLine} {{{Environment.NewLine} \"Name\": \"Anvil\",{Environment.NewLine} \"Price\": 50{Environment.NewLine} }}{Environment.NewLine} ]{Environment.NewLine}}}");
@@ -246,7 +249,7 @@ namespace WireMock.Net.Tests.ResponseBuilders
.WithTransformer();
// Act
var responseMessage = await response.ProvideResponseAsync(request);
var responseMessage = await response.ProvideResponseAsync(request, _settingsMock.Object);
// Assert
Check.That(responseMessage.BodyData.BodyAsString).Equals("%0:Anvil%%1:Elbow Grease%");
@@ -299,7 +302,7 @@ namespace WireMock.Net.Tests.ResponseBuilders
.WithTransformer();
// Act
var responseMessage = await response.ProvideResponseAsync(request);
var responseMessage = await response.ProvideResponseAsync(request, _settingsMock.Object);
// Assert
Check.That(responseMessage.BodyData.BodyAsString).Equals("%0:Anvil%%1:Elbow Grease%");
@@ -328,7 +331,7 @@ namespace WireMock.Net.Tests.ResponseBuilders
.WithTransformer();
// Act
Check.ThatAsyncCode(() => response.ProvideResponseAsync(request)).Throws<ArgumentNullException>();
Check.ThatAsyncCode(() => response.ProvideResponseAsync(request, _settingsMock.Object)).Throws<ArgumentNullException>();
}
}
}

View File

@@ -1,9 +1,11 @@
using System;
using System.Threading.Tasks;
using Moq;
using Newtonsoft.Json.Linq;
using NFluent;
using System;
using System.Threading.Tasks;
using WireMock.Models;
using WireMock.ResponseBuilders;
using WireMock.Settings;
using WireMock.Util;
using Xunit;
@@ -11,11 +13,13 @@ namespace WireMock.Net.Tests.ResponseBuilders
{
public class ResponseWithHandlebarsLinqTests
{
private readonly Mock<IFluentMockServerSettings> _settingsMock = new Mock<IFluentMockServerSettings>();
[Fact]
public async Task Response_ProvideResponse_Handlebars_Linq1_String0()
{
// Assign
var body = new BodyData { };
var body = new BodyData();
var request = new RequestMessage(new UrlDetails("http://localhost:1234/pathtest"), "POST", "::1", body);
@@ -25,7 +29,7 @@ namespace WireMock.Net.Tests.ResponseBuilders
.WithTransformer();
// Act
var responseMessage = await response.ProvideResponseAsync(request);
var responseMessage = await response.ProvideResponseAsync(request, _settingsMock.Object);
// Assert
JObject j = JObject.FromObject(responseMessage.BodyData.BodyAsJson);
@@ -55,7 +59,7 @@ namespace WireMock.Net.Tests.ResponseBuilders
.WithTransformer();
// Act
var responseMessage = await response.ProvideResponseAsync(request);
var responseMessage = await response.ProvideResponseAsync(request, _settingsMock.Object);
// Assert
JObject j = JObject.FromObject(responseMessage.BodyData.BodyAsJson);
@@ -85,7 +89,7 @@ namespace WireMock.Net.Tests.ResponseBuilders
.WithTransformer();
// Act
var responseMessage = await response.ProvideResponseAsync(request);
var responseMessage = await response.ProvideResponseAsync(request, _settingsMock.Object);
// Assert
JObject j = JObject.FromObject(responseMessage.BodyData.BodyAsJson);
@@ -115,7 +119,7 @@ namespace WireMock.Net.Tests.ResponseBuilders
.WithTransformer();
// Act
var responseMessage = await response.ProvideResponseAsync(request);
var responseMessage = await response.ProvideResponseAsync(request, _settingsMock.Object);
// Assert
JObject j = JObject.FromObject(responseMessage.BodyData.BodyAsJson);
@@ -140,14 +144,14 @@ namespace WireMock.Net.Tests.ResponseBuilders
.WithTransformer();
// Act
Check.ThatAsyncCode(() => response.ProvideResponseAsync(request)).Throws<NotSupportedException>();
Check.ThatAsyncCode(() => response.ProvideResponseAsync(request, _settingsMock.Object)).Throws<NotSupportedException>();
}
[Fact]
public void Response_ProvideResponse_Handlebars_Linq1_Throws_ArgumentNullException()
{
// Assign
var body = new BodyData { };
var body = new BodyData();
var request = new RequestMessage(new UrlDetails("http://localhost:1234"), "POST", "::1", body);
@@ -156,14 +160,14 @@ namespace WireMock.Net.Tests.ResponseBuilders
.WithTransformer();
// Act
Check.ThatAsyncCode(() => response.ProvideResponseAsync(request)).Throws<ArgumentNullException>();
Check.ThatAsyncCode(() => response.ProvideResponseAsync(request, _settingsMock.Object)).Throws<ArgumentNullException>();
}
[Fact]
public void Response_ProvideResponse_Handlebars_Linq1_Throws_ArgumentException()
{
// Assign
var body = new BodyData { };
var body = new BodyData();
var request = new RequestMessage(new UrlDetails("http://localhost:1234"), "POST", "::1", body);
@@ -172,7 +176,7 @@ namespace WireMock.Net.Tests.ResponseBuilders
.WithTransformer();
// Act
Check.ThatAsyncCode(() => response.ProvideResponseAsync(request)).Throws<ArgumentException>();
Check.ThatAsyncCode(() => response.ProvideResponseAsync(request, _settingsMock.Object)).Throws<ArgumentException>();
}
[Fact]
@@ -196,7 +200,7 @@ namespace WireMock.Net.Tests.ResponseBuilders
.WithTransformer();
// Act
var responseMessage = await response.ProvideResponseAsync(request);
var responseMessage = await response.ProvideResponseAsync(request, _settingsMock.Object);
// Assert
JObject j = JObject.FromObject(responseMessage.BodyData.BodyAsJson);
@@ -224,7 +228,7 @@ namespace WireMock.Net.Tests.ResponseBuilders
.WithTransformer();
// Act
var responseMessage = await response.ProvideResponseAsync(request);
var responseMessage = await response.ProvideResponseAsync(request, _settingsMock.Object);
// Assert
JObject j = JObject.FromObject(responseMessage.BodyData.BodyAsJson);

View File

@@ -1,16 +1,18 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using Moq;
using Newtonsoft.Json.Linq;
using NFluent;
using System.Linq;
using System.Threading.Tasks;
using WireMock.Models;
using WireMock.ResponseBuilders;
using WireMock.Settings;
using Xunit;
namespace WireMock.Net.Tests.ResponseBuilders
{
public class ResponseWithHandlebarsRandomTests
{
private readonly Mock<IFluentMockServerSettings> _settingsMock = new Mock<IFluentMockServerSettings>();
private const string ClientIp = "::1";
[Fact]
@@ -30,7 +32,7 @@ namespace WireMock.Net.Tests.ResponseBuilders
.WithTransformer();
// Act
var responseMessage = await response.ProvideResponseAsync(request);
var responseMessage = await response.ProvideResponseAsync(request, _settingsMock.Object);
// Assert
JObject j = JObject.FromObject(responseMessage.BodyData.BodyAsJson);
@@ -53,7 +55,7 @@ namespace WireMock.Net.Tests.ResponseBuilders
.WithTransformer();
// Act
var responseMessage = await response.ProvideResponseAsync(request);
var responseMessage = await response.ProvideResponseAsync(request, _settingsMock.Object);
// Assert
JObject j = JObject.FromObject(responseMessage.BodyData.BodyAsJson);
@@ -75,7 +77,7 @@ namespace WireMock.Net.Tests.ResponseBuilders
.WithTransformer();
// Act
var responseMessage = await response.ProvideResponseAsync(request);
var responseMessage = await response.ProvideResponseAsync(request, _settingsMock.Object);
// Assert
JObject j = JObject.FromObject(responseMessage.BodyData.BodyAsJson);
@@ -99,7 +101,7 @@ namespace WireMock.Net.Tests.ResponseBuilders
.WithTransformer();
// Act
var responseMessage = await response.ProvideResponseAsync(request);
var responseMessage = await response.ProvideResponseAsync(request, _settingsMock.Object);
// Assert
JObject j = JObject.FromObject(responseMessage.BodyData.BodyAsJson);
@@ -121,7 +123,7 @@ namespace WireMock.Net.Tests.ResponseBuilders
.WithTransformer();
// Act
var responseMessage = await response.ProvideResponseAsync(request);
var responseMessage = await response.ProvideResponseAsync(request, _settingsMock.Object);
// Assert
JObject j = JObject.FromObject(responseMessage.BodyData.BodyAsJson);

View File

@@ -1,8 +1,10 @@
using System;
using System.Threading.Tasks;
using Moq;
using NFluent;
using System;
using System.Threading.Tasks;
using WireMock.Models;
using WireMock.ResponseBuilders;
using WireMock.Settings;
using WireMock.Util;
using Xunit;
@@ -10,6 +12,7 @@ namespace WireMock.Net.Tests.ResponseBuilders
{
public class ResponseWithHandlebarsRegexTests
{
private readonly Mock<IFluentMockServerSettings> _settingsMock = new Mock<IFluentMockServerSettings>();
private const string ClientIp = "::1";
[Fact]
@@ -25,7 +28,7 @@ namespace WireMock.Net.Tests.ResponseBuilders
.WithTransformer();
// Act
var responseMessage = await response.ProvideResponseAsync(request);
var responseMessage = await response.ProvideResponseAsync(request, _settingsMock.Object);
// assert
Check.That(responseMessage.BodyData.BodyAsString).Equals("abc");
@@ -44,7 +47,7 @@ namespace WireMock.Net.Tests.ResponseBuilders
.WithTransformer();
// Act
var responseMessage = await response.ProvideResponseAsync(request);
var responseMessage = await response.ProvideResponseAsync(request, _settingsMock.Object);
// assert
Check.That(responseMessage.BodyData.BodyAsString).Equals("");
@@ -63,7 +66,7 @@ namespace WireMock.Net.Tests.ResponseBuilders
.WithTransformer();
// Act
var responseMessage = await response.ProvideResponseAsync(request);
var responseMessage = await response.ProvideResponseAsync(request, _settingsMock.Object);
// assert
Check.That(responseMessage.BodyData.BodyAsString).Equals("d");
@@ -82,7 +85,7 @@ namespace WireMock.Net.Tests.ResponseBuilders
.WithTransformer();
// Act
var responseMessage = await response.ProvideResponseAsync(request);
var responseMessage = await response.ProvideResponseAsync(request, _settingsMock.Object);
// assert
Check.That(responseMessage.BodyData.BodyAsString).Equals("5000-https");
@@ -101,7 +104,7 @@ namespace WireMock.Net.Tests.ResponseBuilders
.WithTransformer();
// Act
var responseMessage = await response.ProvideResponseAsync(request);
var responseMessage = await response.ProvideResponseAsync(request, _settingsMock.Object);
// assert
Check.That(responseMessage.BodyData.BodyAsString).Equals("");
@@ -120,7 +123,7 @@ namespace WireMock.Net.Tests.ResponseBuilders
.WithTransformer();
// Act
var responseMessage = await response.ProvideResponseAsync(request);
var responseMessage = await response.ProvideResponseAsync(request, _settingsMock.Object);
// assert
Check.That(responseMessage.BodyData.BodyAsString).Equals("x");
@@ -139,7 +142,7 @@ namespace WireMock.Net.Tests.ResponseBuilders
.WithTransformer();
// Act and Assert
Check.ThatAsyncCode(() => response.ProvideResponseAsync(request)).Throws<NotSupportedException>();
Check.ThatAsyncCode(() => response.ProvideResponseAsync(request, _settingsMock.Object)).Throws<NotSupportedException>();
}
}
}

View File

@@ -2,10 +2,12 @@
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using Moq;
using Newtonsoft.Json;
using NFluent;
using WireMock.Models;
using WireMock.ResponseBuilders;
using WireMock.Settings;
using WireMock.Util;
using Xunit;
#if NET452
@@ -18,6 +20,7 @@ namespace WireMock.Net.Tests.ResponseBuilders
{
public class ResponseWithHandlebarsTests
{
private readonly Mock<IFluentMockServerSettings> _settingsMock = new Mock<IFluentMockServerSettings>();
private const string ClientIp = "::1";
[Fact]
@@ -38,7 +41,7 @@ namespace WireMock.Net.Tests.ResponseBuilders
.WithTransformer();
// Act
var responseMessage = await response.ProvideResponseAsync(request);
var responseMessage = await response.ProvideResponseAsync(request, _settingsMock.Object);
// Assert
Check.That(JsonConvert.SerializeObject(responseMessage.BodyData.BodyAsJson)).Equals("{\"x\":\"test /foo_object\"}");
@@ -60,7 +63,7 @@ namespace WireMock.Net.Tests.ResponseBuilders
.WithTransformer();
// Act
var responseMessage = await response.ProvideResponseAsync(request);
var responseMessage = await response.ProvideResponseAsync(request, _settingsMock.Object);
// Assert
Check.That(responseMessage.BodyData.BodyAsString).Equals("test http://localhost/foo /foo POSt");
@@ -78,7 +81,7 @@ namespace WireMock.Net.Tests.ResponseBuilders
.WithTransformer();
// Act
var responseMessage = await response.ProvideResponseAsync(request);
var responseMessage = await response.ProvideResponseAsync(request, _settingsMock.Object);
// Assert
Check.That(responseMessage.BodyData.BodyAsString).Equals("http://localhost/a/b http://localhost/wiremock/a/b /a/b /wiremock/a/b");
@@ -96,7 +99,7 @@ namespace WireMock.Net.Tests.ResponseBuilders
.WithTransformer();
// Act
var responseMessage = await response.ProvideResponseAsync(request);
var responseMessage = await response.ProvideResponseAsync(request, _settingsMock.Object);
// Assert
Check.That(responseMessage.BodyData.BodyAsString).Equals("a wiremock");
@@ -118,7 +121,7 @@ namespace WireMock.Net.Tests.ResponseBuilders
.WithTransformer();
// Act
var responseMessage = await response.ProvideResponseAsync(request);
var responseMessage = await response.ProvideResponseAsync(request, _settingsMock.Object);
// Assert
Check.That(responseMessage.BodyData.BodyAsString).Equals("test keya=1 idx=1 idx=2 keyb=5");
@@ -138,7 +141,7 @@ namespace WireMock.Net.Tests.ResponseBuilders
var response = Response.Create().WithHeader("x", "{{request.headers.Content-Type}}").WithBody("test").WithTransformer();
// Act
var responseMessage = await response.ProvideResponseAsync(request);
var responseMessage = await response.ProvideResponseAsync(request, _settingsMock.Object);
// Assert
Check.That(responseMessage.BodyData.BodyAsString).Equals("test");
@@ -160,7 +163,7 @@ namespace WireMock.Net.Tests.ResponseBuilders
var response = Response.Create().WithHeader("x", "{{request.headers.Content-Type}}", "{{request.url}}").WithBody("test").WithTransformer();
// Act
var responseMessage = await response.ProvideResponseAsync(request);
var responseMessage = await response.ProvideResponseAsync(request, _settingsMock.Object);
// Assert
Check.That(responseMessage.BodyData.BodyAsString).Equals("test");
@@ -185,7 +188,7 @@ namespace WireMock.Net.Tests.ResponseBuilders
.WithTransformer();
// Act
var responseMessage = await response.ProvideResponseAsync(request);
var responseMessage = await response.ProvideResponseAsync(request, _settingsMock.Object);
// Assert
Check.That(responseMessage.BodyData.BodyAsString).Equals("test http://localhost:1234 1234 http localhost");
@@ -209,10 +212,54 @@ namespace WireMock.Net.Tests.ResponseBuilders
.WithTransformer();
// Act
var responseMessage = await response.ProvideResponseAsync(request);
var responseMessage = await response.ProvideResponseAsync(request, _settingsMock.Object);
// Assert
Check.That(JsonConvert.SerializeObject(responseMessage.BodyData.BodyAsJson)).Equals("[\"first\",\"/foo_array\",\"test 1\",\"test 2\",\"last\"]");
}
[Fact]
public async Task Response_ProvideResponse_Handlebars_WithBodyAsFile()
{
// Assign
var request = new RequestMessage(new UrlDetails("http://localhost/foo?MyUniqueNumber=1"), "GET", ClientIp);
var response = Response.Create()
.WithTransformer()
.WithBodyFromFile(@"c:\\{{request.query.MyUniqueNumber}}\test.xml"); // why use a \\ here ?
// Act
var responseMessage = await response.ProvideResponseAsync(request, _settingsMock.Object);
// Assert
Check.That(responseMessage.BodyData.BodyAsFile).Equals(@"c:\1\test.xml");
}
[Fact]
public async Task Response_ProvideResponse_Handlebars_WithBodyAsFile_JsonPath()
{
// Assign
string jsonString = "{ \"MyUniqueNumber\": \"1\" }";
var bodyData = new BodyData
{
BodyAsString = jsonString,
BodyAsJson = JsonConvert.DeserializeObject(jsonString),
DetectedBodyType = BodyType.Json,
DetectedBodyTypeFromContentType = BodyType.Json,
Encoding = Encoding.UTF8
};
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "POST", ClientIp, bodyData);
string jsonPath = "\"$.MyUniqueNumber\"";
var response = Response.Create()
.WithTransformer()
.WithBodyFromFile(@"c:\\{{JsonPath.SelectToken request.body " + jsonPath + "}}\\test.json"); // why use a \\ here ?
// Act
var responseMessage = await response.ProvideResponseAsync(request, _settingsMock.Object);
// Assert
Check.That(responseMessage.BodyData.BodyAsFile).Equals(@"c:\1\test.json");
}
}
}

View File

@@ -1,14 +1,17 @@
using System.Threading.Tasks;
using Moq;
using Newtonsoft.Json.Linq;
using NFluent;
using System.Threading.Tasks;
using WireMock.Models;
using WireMock.ResponseBuilders;
using WireMock.Settings;
using Xunit;
namespace WireMock.Net.Tests.ResponseBuilders
{
public class ResponseWithHandlebarsXegerTests
{
private readonly Mock<IFluentMockServerSettings> _settingsMock = new Mock<IFluentMockServerSettings>();
private const string ClientIp = "::1";
[Fact]
@@ -26,7 +29,7 @@ namespace WireMock.Net.Tests.ResponseBuilders
.WithTransformer();
// Act
var responseMessage = await response.ProvideResponseAsync(request);
var responseMessage = await response.ProvideResponseAsync(request, _settingsMock.Object);
// Assert
JObject j = JObject.FromObject(responseMessage.BodyData.BodyAsJson);
@@ -49,7 +52,7 @@ namespace WireMock.Net.Tests.ResponseBuilders
.WithTransformer();
// Act
var responseMessage = await response.ProvideResponseAsync(request);
var responseMessage = await response.ProvideResponseAsync(request, _settingsMock.Object);
// Assert
JObject j = JObject.FromObject(responseMessage.BodyData.BodyAsJson);

View File

@@ -1,8 +1,10 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using Moq;
using NFluent;
using System.Collections.Generic;
using System.Threading.Tasks;
using WireMock.Models;
using WireMock.ResponseBuilders;
using WireMock.Settings;
using WireMock.Util;
using Xunit;
@@ -10,6 +12,7 @@ namespace WireMock.Net.Tests.ResponseBuilders
{
public class ResponseWithHeadersTests
{
private readonly Mock<IFluentMockServerSettings> _settingsMock = new Mock<IFluentMockServerSettings>();
private const string ClientIp = "::1";
[Theory]
@@ -23,7 +26,7 @@ namespace WireMock.Net.Tests.ResponseBuilders
IResponseBuilder builder = Response.Create().WithHeader(headerName, headerValue);
// Act
var response = await builder.ProvideResponseAsync(requestMock);
var response = await builder.ProvideResponseAsync(requestMock, _settingsMock.Object);
// Assert
Check.That(response.Headers[headerName].ToString()).Equals(headerValue);
@@ -39,7 +42,7 @@ namespace WireMock.Net.Tests.ResponseBuilders
IResponseBuilder builder = Response.Create().WithHeader(headerName, headerValues);
// Act
var response = await builder.ProvideResponseAsync(requestMock);
var response = await builder.ProvideResponseAsync(requestMock, _settingsMock.Object);
// Assert
Check.That(response.Headers[headerName].ToArray()).Equals(headerValues);
@@ -54,7 +57,7 @@ namespace WireMock.Net.Tests.ResponseBuilders
var response = Response.Create().WithHeaders(headers);
// Act
var responseMessage = await response.ProvideResponseAsync(request);
var responseMessage = await response.ProvideResponseAsync(request, _settingsMock.Object);
// Assert
Check.That(responseMessage.Headers["h"]).ContainsExactly("x");
@@ -69,7 +72,7 @@ namespace WireMock.Net.Tests.ResponseBuilders
var response = Response.Create().WithHeaders(headers);
// Act
var responseMessage = await response.ProvideResponseAsync(request);
var responseMessage = await response.ProvideResponseAsync(request, _settingsMock.Object);
// Assert
Check.That(responseMessage.Headers["h"]).ContainsExactly("x");
@@ -84,7 +87,7 @@ namespace WireMock.Net.Tests.ResponseBuilders
var response = Response.Create().WithHeaders(headers);
// Act
var responseMessage = await response.ProvideResponseAsync(request);
var responseMessage = await response.ProvideResponseAsync(request, _settingsMock.Object);
// Assert
Check.That(responseMessage.Headers["h"]).ContainsExactly("x");

View File

@@ -1,11 +1,13 @@
using System;
using Moq;
using NFluent;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using NFluent;
using WireMock.Models;
using WireMock.RequestBuilders;
using WireMock.ResponseBuilders;
using WireMock.Server;
using WireMock.Settings;
using WireMock.Util;
using Xunit;
@@ -13,6 +15,7 @@ namespace WireMock.Net.Tests.ResponseBuilders
{
public class ResponseWithProxyTests : IDisposable
{
private readonly Mock<IFluentMockServerSettings> _settingsMock = new Mock<IFluentMockServerSettings>();
private readonly FluentMockServer _server;
private readonly Guid _guid;
@@ -30,11 +33,11 @@ namespace WireMock.Net.Tests.ResponseBuilders
{
// Assign
var headers = new Dictionary<string, string[]> { { "Content-Type", new[] { "application/xml" } } };
var request = new RequestMessage(new UrlDetails($"{_server.Urls[0]}/{_guid}"), "POST", "::1", new BodyData { DetectedBodyType = BodyType.Json, BodyAsJson = new { a = 1 } }, headers);
var request = new RequestMessage(new UrlDetails($"{_server.Urls[0]}/{_guid}"), "POST", "::1", new BodyData { DetectedBodyType = BodyType.Json, BodyAsJson = new { a = 1 } }, headers);
var response = Response.Create().WithProxy(_server.Urls[0]);
// Act
var responseMessage = await response.ProvideResponseAsync(request);
var responseMessage = await response.ProvideResponseAsync(request, _settingsMock.Object);
// Assert
Check.That(responseMessage.BodyData.BodyAsString).IsEqualTo("{\"p\":42}");

View File

@@ -1,27 +1,51 @@
using System;
using NFluent;
using FluentAssertions;
using Moq;
using System;
using WireMock.RequestBuilders;
using WireMock.ResponseBuilders;
using WireMock.Serialization;
using WireMock.Settings;
using Xunit;
namespace WireMock.Net.Tests.Serialization
{
public class MappingConverterTests
{
private readonly Mock<IFluentMockServerSettings> _settingsMock = new Mock<IFluentMockServerSettings>();
[Fact]
public void MappingConverter_ToMappingModel()
public void ToMappingModel()
{
// Assign
var request = Request.Create();
var response = Response.Create();
var mapping = new Mapping(Guid.NewGuid(), "", null, request, response, 0, null, null, null);
var mapping = new Mapping(Guid.NewGuid(), "", null, _settingsMock.Object, request, response, 0, null, null, null);
// Act
var model = MappingConverter.ToMappingModel(mapping);
// Assert
Check.That(model).IsNotNull();
model.Should().NotBeNull();
model.Priority.Should().BeNull();
model.Response.BodyAsJsonIndented.Should().BeNull();
model.Response.UseTransformer.Should().BeNull();
}
[Fact]
public void ToMappingModel_WithPriority_ReturnsPriority()
{
// Assign
var request = Request.Create();
var response = Response.Create().WithBodyAsJson(new { x = "x" }).WithTransformer();
var mapping = new Mapping(Guid.NewGuid(), "", null, _settingsMock.Object, request, response, 42, null, null, null);
// Act
var model = MappingConverter.ToMappingModel(mapping);
// Assert
model.Should().NotBeNull();
model.Priority.Should().Be(42);
model.Response.UseTransformer.Should().BeTrue();
}
}
}

View File

@@ -1,5 +1,5 @@
using System;
using NFluent;
using NFluent;
using System;
using WireMock.Admin.Mappings;
using WireMock.Matchers;
using WireMock.Serialization;
@@ -53,6 +53,23 @@ namespace WireMock.Net.Tests.Serialization
Check.That(matcher.GetPatterns()).ContainsExactly("x", "y");
}
[Fact]
public void MatcherModelMapper_Map_ExactObjectMatcher_Pattern()
{
// Assign
var model = new MatcherModel
{
Name = "ExactObjectMatcher",
Patterns = new object[] { "c3RlZg==" }
};
// Act
var matcher = (ExactObjectMatcher)MatcherMapper.Map(model);
// Assert
Check.That(matcher.ValueAsBytes).ContainsExactly(new byte[] { 115, 116, 101, 102 });
}
[Fact]
public void MatcherModelMapper_Map_RegexMatcher()
{

View File

@@ -1,4 +1,5 @@
using System.Reflection;
using System;
using System.Reflection;
namespace WireMock.Net.Tests
{
@@ -10,5 +11,53 @@ namespace WireMock.Net.Tests
return (T)field.GetValue(obj);
}
/// <summary>
/// Set a _private_ Field Value on a given Object
/// </summary>
/// <typeparam name="T">Type of the Property</typeparam>
/// <param name="obj">Object from where the Property Value is returned</param>
/// <param name="propertyName">Property name as string.</param>
/// <param name="value">the value to set</param>
public static void SetPrivateFieldValue<T>(this object obj, string propertyName, T value)
{
if (obj == null)
{
throw new ArgumentNullException(nameof(obj));
}
Type t = obj.GetType();
FieldInfo fi = null;
while (fi == null && t != null)
{
fi = t.GetField(propertyName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
t = t.BaseType;
}
if (fi == null)
{
throw new ArgumentOutOfRangeException(nameof(propertyName), $"Field {propertyName} was not found in Type {obj.GetType().FullName}");
}
fi.SetValue(obj, value);
}
/// <summary>
/// Sets a _private_ Property Value from a given Object.
/// </summary>
/// <typeparam name="T">Type of the Property</typeparam>
/// <param name="obj">Object from where the Property Value is set</param>
/// <param name="propertyName">Property name as string.</param>
/// <param name="value">Value to set.</param>
public static void SetPrivatePropertyValue<T>(this object obj, string propertyName, T value)
{
Type t = obj.GetType();
if (t.GetProperty(propertyName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) == null)
{
throw new ArgumentOutOfRangeException(nameof(propertyName), $"Property {propertyName} was not found in Type {obj.GetType().FullName}");
}
t.InvokeMember(propertyName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.SetProperty | BindingFlags.Instance, null, obj, new object[] { value });
}
}
}

Some files were not shown because too many files have changed in this diff Show More