Compare commits

...

24 Commits

Author SHA1 Message Date
Stef Heyenrath
7678e8fb70 1.0.41.0 2019-12-14 12:19:55 +01:00
Stef Heyenrath
8ae0abb023 . (#392) 2019-12-13 13:21:50 +01:00
Stef Heyenrath
b3c2af0c22 1.0.40.0 (changelog) 2019-12-09 17:32:47 +01:00
Stef Heyenrath
2dd30b4f14 1.0.40 2019-12-09 17:31:50 +01:00
Stef Heyenrath
45d8c0cc27 Fix QueryStringParser (#389)
* Fix QueryStringParser

* add extra test
2019-12-09 17:20:17 +01:00
Stef Heyenrath
178f2cf02f 1.0.39.0 2019-12-07 08:58:24 +01:00
Stef Heyenrath
1b326a54d6 Add WebProxySettings (use when proxying requests) (#370)
* webproxy part 1

* fixed

* Push to MyGet

* WebProxy standalone

* -n true

* nuget --- "-n true"

* AllowAutoRedirect

* .
2019-12-07 08:52:04 +01:00
Stef Heyenrath
8bafd6a1ba Transform body as file (#388)
* .

* fix
2019-12-06 06:52:08 +01:00
Stef Heyenrath
a47750c058 1.0.38 2019-11-30 08:54:10 +01:00
Stef Heyenrath
5bb10c3350 Use dotnet default development certificate for .NET Core 2.x (#381) 2019-11-29 14:51:03 +01:00
Stef Heyenrath
3a221f51b1 set handlebars dependency for .net 4.5.1 to fixed value (#378) 2019-11-25 18:56:01 +01:00
Noah Lerner
0ff23a3d15 Support int values for states and scenario naming (#376)
* Support int values for states and scenario naming
* fix remove added dependency
2019-11-21 12:01:20 +01:00
Stef Heyenrath
9b323ab388 example 2019-11-19 18:54:28 +01:00
Stef Heyenrath
9b3c750754 -n true 2019-11-08 17:39:46 +01:00
Stef Heyenrath
a5d6061c2d 1.0.37.0 2019-11-08 17:26:08 +01:00
Stef Heyenrath
395be3c583 WatchStaticMappingsInSubdirectories (#374)
* WatchStaticMappingsInSubdirectories

* 37

* IEnumerable<string> EnumerateFiles([NotNull] string path, bool includeSubdirectories);

* reloadStaticMappings
2019-11-07 15:31:43 +01:00
Stef Heyenrath
ea6a8d3b73 Push to MyGet 2019-10-31 20:43:12 +01:00
Stef Heyenrath
4886ac6196 Push to MyGet 2019-10-31 20:34:23 +01:00
Stef Heyenrath
0ca63eef66 RUN_SONAR (#373) 2019-10-31 07:15:23 +00:00
Stef Heyenrath
c72487a748 Add images 2019-10-31 07:23:41 +01:00
Stef Heyenrath
79db955611 SaveMappingForStatusCodePattern 2019-10-29 19:02:59 +01:00
Stef Heyenrath
395f48a2bf 1.0.36.0 2019-10-26 08:04:40 +02:00
Stef Heyenrath
596177d4e5 Add support for Faults (#360)
* FaultType - wip

* .

* copy fault

* tests

* code-factor

* more tests

* fix tests

* fixed

* remove RANDOM_DATA_THEN_CLOSE
2019-10-26 06:01:25 +00:00
Stef Heyenrath
fc024678fa Doesn't push symbols (even if present). 2019-10-25 17:56:21 +02:00
71 changed files with 1104 additions and 167 deletions

View File

@@ -1,3 +1,33 @@
# 1.0.41.0 (14 December 2019)
- [#392](https://github.com/WireMock-Net/WireMock.Net/pull/392) - Fix array in JsonMatcher [bug] contributed by [StefH](https://github.com/StefH)
- [#390](https://github.com/WireMock-Net/WireMock.Net/issues/390) - JsonMatcher does not match a body containing an array of strings [bug]
# 1.0.40.0 (09 December 2019)
- [#389](https://github.com/WireMock-Net/WireMock.Net/pull/389) - Fix QueryStringParser [bug] contributed by [StefH](https://github.com/StefH)
- [#387](https://github.com/WireMock-Net/WireMock.Net/issues/387) - Query string parameter value which contains %26 does not work with ExactMatcher [bug]
# 1.0.39.0 (07 December 2019)
- [#370](https://github.com/WireMock-Net/WireMock.Net/pull/370) - Add WebProxySettings (use when proxying requests) [feature] contributed by [StefH](https://github.com/StefH)
- [#388](https://github.com/WireMock-Net/WireMock.Net/pull/388) - Transform body as file [bug] contributed by [StefH](https://github.com/StefH)
- [#369](https://github.com/WireMock-Net/WireMock.Net/issues/369) - Question: Is there a way to provide a corporate proxy configuration? [feature]
- [#375](https://github.com/WireMock-Net/WireMock.Net/issues/375) - Proxying does not follow redirects : make this configurable [feature]
- [#386](https://github.com/WireMock-Net/WireMock.Net/issues/386) - Is transforming contents of XML file supported.? [bug]
# 1.0.38.0 (30 November 2019)
- [#376](https://github.com/WireMock-Net/WireMock.Net/pull/376) - Support int values for states and scenario naming [feature] contributed by [thewholuver94](https://github.com/thewholuver94)
- [#378](https://github.com/WireMock-Net/WireMock.Net/pull/378) - Set handlebars dependency for .net 4.5.1 to fixed value [bug] contributed by [StefH](https://github.com/StefH)
- [#381](https://github.com/WireMock-Net/WireMock.Net/pull/381) - Use dotnet default development certificate for .NET Core 2.x [feature] contributed by [StefH](https://github.com/StefH)
- [#377](https://github.com/WireMock-Net/WireMock.Net/issues/377) - Unable to build against .NET 4.5.1 because of Handlebars [bug]
# 1.0.37.0 (08 November 2019)
- [#373](https://github.com/WireMock-Net/WireMock.Net/pull/373) - Make Sonar and WhiteSource optional in the Azure pipelines build [feature] contributed by [StefH](https://github.com/StefH)
- [#374](https://github.com/WireMock-Net/WireMock.Net/pull/374) - WatchStaticMappingsInSubdirectories [feature] contributed by [StefH](https://github.com/StefH)
- [#372](https://github.com/WireMock-Net/WireMock.Net/issues/372) - Reset in WireMock admin API not working fine. [feature]
# 1.0.36.0 (26 October 2019)
- [#360](https://github.com/WireMock-Net/WireMock.Net/pull/360) - Add support for Faults [feature] contributed by [StefH](https://github.com/StefH)
- [#343](https://github.com/WireMock-Net/WireMock.Net/issues/343) - Feature: Please provide support for Bad responses. [feature]
# 1.0.35.0 (25 October 2019)
- [#367](https://github.com/WireMock-Net/WireMock.Net/pull/367) - No symbol NuGets [feature] contributed by [StefH](https://github.com/StefH)
- [#368](https://github.com/WireMock-Net/WireMock.Net/pull/368) - Remove Obsolete annotations [feature] contributed by [StefH](https://github.com/StefH)
@@ -6,12 +36,11 @@
- [#354](https://github.com/WireMock-Net/WireMock.Net/pull/354) - AllowBodyForAllHttpMethods [bug, feature] contributed by [StefH](https://github.com/StefH)
- [#365](https://github.com/WireMock-Net/WireMock.Net/pull/365) - Bump Microsoft.AspNetCore.All from 2.0.8 to 2.0.9 in /examples/WireMock.Net.WebApplication [dependencies] contributed by [dependabot[bot]](https://github.com/apps/dependabot)
- [#366](https://github.com/WireMock-Net/WireMock.Net/pull/366) - Update ObsoleteAnnotations [feature] contributed by [StefH](https://github.com/StefH)
- [#352](https://github.com/WireMock-Net/WireMock.Net/issues/352) - DELETE request drops the body [feature, question]
- [#352](https://github.com/WireMock-Net/WireMock.Net/issues/352) - DELETE request drops the body [feature]
# 1.0.33.0 (12 October 2019)
- [#311](https://github.com/WireMock-Net/WireMock.Net/pull/311) - fix jsonpath matcher [bug] contributed by [StefH](https://github.com/StefH)
- [#324](https://github.com/WireMock-Net/WireMock.Net/pull/324) - Add CSharpCodeMatcher [feature] contributed by [StefH](https://github.com/StefH)
- [#335](https://github.com/WireMock-Net/WireMock.Net/pull/335) - Mark some classes and methods obsolete for version 1.1.0 [doc] contributed by [StefH](https://github.com/StefH)
- [#353](https://github.com/WireMock-Net/WireMock.Net/pull/353) - Fixed failing admin requests when content type includes a charset (based on idea from Paul Roub) [bug] contributed by [StefH](https://github.com/StefH)
- [#355](https://github.com/WireMock-Net/WireMock.Net/pull/355) - Add Try-Catch to the event LogEntriesChanged [feature] contributed by [StefH](https://github.com/StefH)
- [#357](https://github.com/WireMock-Net/WireMock.Net/pull/357) - Add Proxy Setting for: SaveMappingForStatusCodePattern to only save the mapping when the status code matches the pattern [feature] contributed by [StefH](https://github.com/StefH)
@@ -20,7 +49,7 @@
- [#307](https://github.com/WireMock-Net/WireMock.Net/issues/307) - JsonPathMatcher always convert to JArray before matching [bug]
- [#329](https://github.com/WireMock-Net/WireMock.Net/issues/329) - Feature: Add support for CSharpCodeMatcher [feature]
- [#350](https://github.com/WireMock-Net/WireMock.Net/issues/350) - Admin requests fail when content type includes a charset [bug]
- [#356](https://github.com/WireMock-Net/WireMock.Net/issues/356) - JsonMatcher not working when JSON contains a DateTimeOffset
- [#356](https://github.com/WireMock-Net/WireMock.Net/issues/356) - JsonMatcher not working when JSON contains a DateTimeOffset [bug]
# 1.0.32.0 (20 September 2019)
- [#348](https://github.com/WireMock-Net/WireMock.Net/pull/348) - When posting new mapping, use DateParseHandling.None [bug] contributed by [StefH](https://github.com/StefH)
@@ -32,7 +61,6 @@
- [#339](https://github.com/WireMock-Net/WireMock.Net/pull/339) - Fix ConcurrentObservableCollection [bug] contributed by [StefH](https://github.com/StefH)
- [#345](https://github.com/WireMock-Net/WireMock.Net/pull/345) - Fix CompareTo in RequestMatchResult [bug] contributed by [StefH](https://github.com/StefH)
- [#346](https://github.com/WireMock-Net/WireMock.Net/pull/346) - Fix recorded requests skipped by request logger contributed by [vitaliydavydiak](https://github.com/vitaliydavydiak)
- [#327](https://github.com/WireMock-Net/WireMock.Net/issues/327) - Index must be within the bounds of the List - Bug [bug]
- [#337](https://github.com/WireMock-Net/WireMock.Net/issues/337) - Proxy Missing header Content-Type - tried with Recording [bug]
- [#344](https://github.com/WireMock-Net/WireMock.Net/issues/344) - Mapping adding order matters for multiple mappings? [bug]
@@ -48,16 +76,12 @@
- [#316](https://github.com/WireMock-Net/WireMock.Net/pull/316) - Handles case where parameter value contains == [feature] contributed by [lobsteropteryx](https://github.com/lobsteropteryx)
- [#317](https://github.com/WireMock-Net/WireMock.Net/pull/317) - Make SaveMapping and SaveMappingToFile settings independent. [feature] contributed by [vitaliydavydiak](https://github.com/vitaliydavydiak)
- [#319](https://github.com/WireMock-Net/WireMock.Net/pull/319) - Add blacklist for Request Cookies. contributed by [vitaliydavydiak](https://github.com/vitaliydavydiak)
- [#320](https://github.com/WireMock-Net/WireMock.Net/pull/320) - Remove coverlet folder from source control [doc] contributed by [StefH](https://github.com/StefH)
- [#322](https://github.com/WireMock-Net/WireMock.Net/pull/322) - Fix MappingMatcher in case of an exception in LinqMatcher. [bug] contributed by [StefH](https://github.com/StefH)
- [#323](https://github.com/WireMock-Net/WireMock.Net/pull/323) - Refactor MappingConverter &amp; MatcherMapper [refactor] contributed by [StefH](https://github.com/StefH)
- [#326](https://github.com/WireMock-Net/WireMock.Net/pull/326) - Fix Parsing Guid in PUT Mapping [bug] contributed by [StefH](https://github.com/StefH)
- [#252](https://github.com/WireMock-Net/WireMock.Net/issues/252) - Proxy with Transform
- [#287](https://github.com/WireMock-Net/WireMock.Net/issues/287) - Error with parameter that contains a &quot;=&quot; character [bug, question]
- [#308](https://github.com/WireMock-Net/WireMock.Net/issues/308) - __admin/requests - &quot;Collection was modified&quot; exception [bug]
- [#313](https://github.com/WireMock-Net/WireMock.Net/issues/313) - RequestLogExpirationDuration - bug [bug]
- [#315](https://github.com/WireMock-Net/WireMock.Net/issues/315) - Wiki - Proxying: AtPriority() example not set on right object [doc]
- [#318](https://github.com/WireMock-Net/WireMock.Net/issues/318) - Documentation: Add valid CLI parameters [doc, question]
- [#325](https://github.com/WireMock-Net/WireMock.Net/issues/325) - Admin API: PUT Mapping, FormatException because of wrong parsing of the Query [bug]
# 1.0.25.0 (23 July 2019)
@@ -65,7 +89,6 @@
# 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]
- [#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)
@@ -73,15 +96,12 @@
# 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]
# 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)
- [#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]
- [#292](https://github.com/WireMock-Net/WireMock.Net/issues/292) - Can't start server in Xamarin Android [bug]
@@ -107,7 +127,6 @@
- [#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)
@@ -138,6 +157,7 @@
# 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]
- [#243](https://github.com/WireMock-Net/WireMock.Net/issues/243) - Not able to read response from file [bug]
# 1.0.6.1 (10 January 2019)
- [#247](https://github.com/WireMock-Net/WireMock.Net/pull/247) - Issue 225 improve logging in example for wire mock as windows service contributed by [paulssn](https://github.com/paulssn)
@@ -296,9 +316,7 @@
- [#134](https://github.com/WireMock-Net/WireMock.Net/pull/134) - Stef negate matcher contributed by [alastairtree](https://github.com/alastairtree)
- [#135](https://github.com/WireMock-Net/WireMock.Net/pull/135) - Merge into the stef_negate_matcher branch (solves issue #133) contributed by [StefH](https://github.com/StefH)
- [#138](https://github.com/WireMock-Net/WireMock.Net/pull/138) - Added Negate matcher logic contributed by [StefH](https://github.com/StefH)
- [#128](https://github.com/WireMock-Net/WireMock.Net/issues/128) - Feature: Negate a matcher [feature, question]
- [#130](https://github.com/WireMock-Net/WireMock.Net/issues/130) - ...
- [#133](https://github.com/WireMock-Net/WireMock.Net/issues/133) - Issue: Wildcard matching a json body does not work? [bug, question]
# 1.0.3.16 (17 April 2018)
- [#121](https://github.com/WireMock-Net/WireMock.Net/pull/121) - Fix for issue #118 [bug] contributed by [raghavendrabankapur](https://github.com/raghavendrabankapur)

View File

@@ -4,7 +4,7 @@
</PropertyGroup>
<PropertyGroup>
<VersionPrefix>1.0.35</VersionPrefix>
<VersionPrefix>1.0.41</VersionPrefix>
</PropertyGroup>
<Choose>

View File

@@ -1,3 +1,3 @@
https://github.com/StefH/GitHubReleaseNotes
GitHubReleaseNotes.exe --output CHANGELOG.md --skip-empty-releases --exclude-labels question invalid --version 1.0.35.0
GitHubReleaseNotes.exe --output CHANGELOG.md --skip-empty-releases --exclude-labels question invalid doc --version 1.0.41.0

View File

@@ -1,7 +1,7 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.27130.2036
# Visual Studio Version 16
VisualStudioVersion = 16.0.29512.175
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{EF242EDF-7133-4277-9A0C-18744DE08707}"
EndProject
@@ -61,6 +61,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WireMock.Net.Console.Net452
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WireMock.Net.Console.NETCoreApp2", "examples\WireMock.Net.Console.NETCoreApp2\WireMock.Net.Console.NETCoreApp2.csproj", "{83645809-9E01-4E81-8733-BA9497554ABF}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WireMock.Net.Console.RequestLogTest", "examples\WireMock.Net.Console.RequestLogTest\WireMock.Net.Console.RequestLogTest.csproj", "{A9D039B9-7509-4CF1-9EFD-87EB82998575}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -131,6 +133,10 @@ Global
{83645809-9E01-4E81-8733-BA9497554ABF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{83645809-9E01-4E81-8733-BA9497554ABF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{83645809-9E01-4E81-8733-BA9497554ABF}.Release|Any CPU.Build.0 = Release|Any CPU
{A9D039B9-7509-4CF1-9EFD-87EB82998575}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A9D039B9-7509-4CF1-9EFD-87EB82998575}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A9D039B9-7509-4CF1-9EFD-87EB82998575}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A9D039B9-7509-4CF1-9EFD-87EB82998575}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -152,6 +158,7 @@ Global
{1261BB9B-A7D4-456C-8985-3CE560361B8E} = {F0C22C47-DF71-463C-9B04-B4E0F3B8708A}
{668F689E-57B4-422E-8846-C0FF643CA268} = {F0C22C47-DF71-463C-9B04-B4E0F3B8708A}
{83645809-9E01-4E81-8733-BA9497554ABF} = {F0C22C47-DF71-463C-9B04-B4E0F3B8708A}
{A9D039B9-7509-4CF1-9EFD-87EB82998575} = {F0C22C47-DF71-463C-9B04-B4E0F3B8708A}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {BF428BCC-C837-433B-87D2-15C7014B73E9}

View File

@@ -43,4 +43,4 @@ steps:
inputs:
command: custom
custom: nuget
arguments: push $(Build.ArtifactStagingDirectory)\packages\*.nupkg --source https://api.nuget.org/v3/index.json --no-service-endpoint --api-key $(NuGetKey)
arguments: push $(Build.ArtifactStagingDirectory)\packages\*.nupkg -n true -s https://api.nuget.org/v3/index.json -k $(NuGetKey)

View File

@@ -26,6 +26,7 @@ steps:
- script: |
%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
condition: and(succeeded(), eq(variables['RUN_SONAR'], 'yes'))
# Build source, tests and run tests for net452 and netcoreapp2.1 (with coverage)
- script: |
@@ -37,9 +38,11 @@ steps:
- script: |
%USERPROFILE%\.dotnet\tools\dotnet-sonarscanner end /d:sonar.login="$(SONAR_TOKEN)"
displayName: End SonarScanner
condition: and(succeeded(), eq(variables['RUN_SONAR'], 'yes'))
- task: whitesource.ws-bolt.bolt.wss.WhiteSource Bolt@19
displayName: 'WhiteSource Bolt'
condition: and(succeeded(), eq(variables['RUN_WHITESOURCE'], 'yes'))
# Upload coverage to codecov.io
- script: |
@@ -76,10 +79,11 @@ steps:
inputs:
PathtoPublish: '$(Build.ArtifactStagingDirectory)'
# https://github.com/NuGet/Home/issues/8148
- task: DotNetCoreCLI@2
displayName: Push to MyGet
condition: and(succeeded(), ne(variables['Build.Reason'], 'PullRequest')) # Do not run for PullRequests
inputs:
command: custom
custom: nuget
arguments: push $(Build.ArtifactStagingDirectory)\packages\*.nupkg --source https://www.myget.org/F/wiremock-net/api/v3/index.json --no-service-endpoint --api-key $(MyGetKey)
arguments: push $(Build.ArtifactStagingDirectory)\packages\*.nupkg -n true -s https://www.myget.org/F/wiremock-net/api/v3/index.json -k $(MyGetKey)

View File

@@ -3,13 +3,14 @@ using RestEase;
using System;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
using WireMock.Client;
namespace WireMock.Net.Client
{
class Program
{
static void Main(string[] args)
static async Task Main(string[] args)
{
// Create an implementation of the IFluentMockServerAdmin and pass in the base URL for the API.
var api = RestClient.For<IFluentMockServerAdmin>("http://localhost:9091");
@@ -18,29 +19,29 @@ namespace WireMock.Net.Client
var value = Convert.ToBase64String(Encoding.ASCII.GetBytes("a:b"));
api.Authorization = new AuthenticationHeaderValue("Basic", value);
var settings1 = api.GetSettingsAsync().Result;
var settings1 = await api.GetSettingsAsync();
Console.WriteLine($"settings1 = {JsonConvert.SerializeObject(settings1)}");
settings1.GlobalProcessingDelay = 1077;
api.PostSettingsAsync(settings1).Wait();
var settings2 = api.GetSettingsAsync().Result;
var settings2 = await api.GetSettingsAsync();
Console.WriteLine($"settings2 = {JsonConvert.SerializeObject(settings2)}");
var mappings = api.GetMappingsAsync().Result;
var mappings = await api.GetMappingsAsync();
Console.WriteLine($"mappings = {JsonConvert.SerializeObject(mappings)}");
try
{
var guid = Guid.Parse("11111110-a633-40e8-a244-5cb80bc0ab66");
var mapping = api.GetMappingAsync(guid).Result;
var mapping = await api.GetMappingAsync(guid);
Console.WriteLine($"mapping = {JsonConvert.SerializeObject(mapping)}");
}
catch (Exception e)
{
}
var request = api.GetRequestsAsync().Result;
var request = await api.GetRequestsAsync();
Console.WriteLine($"request = {JsonConvert.SerializeObject(request)}");
//var deleteRequestsAsync = api.DeleteRequestsAsync().Result;
@@ -49,15 +50,21 @@ namespace WireMock.Net.Client
//var resetRequestsAsync = api.ResetRequestsAsync().Result;
//Console.WriteLine($"ResetRequestsAsync = {resetRequestsAsync.Status}");
var scenarioStates = api.GetScenariosAsync().Result;
var scenarioStates = await api.GetScenariosAsync();
Console.WriteLine($"GetScenariosAsync = {JsonConvert.SerializeObject(scenarioStates)}");
var postFileResult = api.PostFileAsync("1.cs", "C# Hello").GetAwaiter().GetResult();
var postFileResult = await api.PostFileAsync("1.cs", "C# Hello");
Console.WriteLine($"postFileResult = {JsonConvert.SerializeObject(postFileResult)}");
var getFileResult = api.GetFileAsync("1.cs").GetAwaiter().GetResult();
var getFileResult = await api.GetFileAsync("1.cs");
Console.WriteLine($"getFileResult = {getFileResult}");
var resetMappingsAsync = await api.ResetMappingsAsync();
Console.WriteLine($"resetMappingsAsync = {resetMappingsAsync.Status}");
var resetMappingsAndReloadStaticMappingsAsync = await api.ResetMappingsAsync(true);
Console.WriteLine($"resetMappingsAndReloadStaticMappingsAsync = {resetMappingsAndReloadStaticMappingsAsync.Status}");
Console.WriteLine("Press any key to quit");
Console.ReadKey();
}

View File

@@ -2,8 +2,7 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.0</TargetFramework>
<!-- <RuntimeFrameworkVersion>1.0.1</RuntimeFrameworkVersion> -->
<TargetFramework>netcoreapp2.1</TargetFramework>
<ApplicationIcon>../../WireMock.Net-Logo.ico</ApplicationIcon>
</PropertyGroup>

View File

@@ -0,0 +1 @@
// C# Hello

View File

@@ -21,9 +21,9 @@ namespace WireMock.Net.ConsoleApplication
}
/// <inheritdoc cref="IFileSystemHandler.EnumerateFiles"/>
public IEnumerable<string> EnumerateFiles(string path)
public IEnumerable<string> EnumerateFiles(string path, bool includeSubdirectories)
{
return Directory.EnumerateFiles(path);
return includeSubdirectories ? Directory.EnumerateFiles(path, "*", SearchOption.AllDirectories) : Directory.EnumerateFiles(path);
}
/// <inheritdoc cref="IFileSystemHandler.GetMappingFolder"/>

View File

@@ -46,6 +46,7 @@ namespace WireMock.Net.ConsoleApplication
StartAdminInterface = true,
ReadStaticMappings = true,
WatchStaticMappings = true,
WatchStaticMappingsInSubdirectories = true,
//ProxyAndRecordSettings = new ProxyAndRecordSettings
//{
// SaveMapping = true
@@ -104,6 +105,24 @@ namespace WireMock.Net.ConsoleApplication
.WithProxy(new ProxyAndRecordSettings { Url = "http://postman-echo.com/get" })
);
server
.Given(Request.Create()
.UsingGet()
.WithHeader("postmanecho", "get2")
)
.RespondWith(Response.Create()
.WithProxy(new ProxyAndRecordSettings
{
Url = "http://postman-echo.com/get",
WebProxySettings = new WebProxySettings
{
Address = "http://company",
UserName = "test",
Password = "pwd"
}
})
);
server
.Given(Request.Create()
.UsingGet()

View File

@@ -0,0 +1,63 @@
using System;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using WireMock.Logging;
using WireMock.Net.StandAlone;
using WireMock.RequestBuilders;
using WireMock.ResponseBuilders;
using WireMock.Settings;
namespace WireMock.Net.Console.RequestLogTest
{
class Program
{
static async Task Main(string[] args)
{
var server = StandAloneApp.Start(new FluentMockServerSettings
{
Port = 19019,
StartAdminInterface = true,
StartTimeout = 1000,
MaxRequestLogCount = 100,
RequestLogExpirationDuration = 6,
Logger = new WireMockConsoleLogger()
});
server
.Given(Request
.Create()
.UsingGet())
.RespondWith(Response.Create()
.WithHeader("Content-Type", "application/json")
.WithBodyAsJson(new { result = "x" }));
await Task.Delay(2000);
var client = new HttpClient();
client.BaseAddress = new Uri(server.Urls[0]);
var options = new ParallelOptions()
{
MaxDegreeOfParallelism = 2
};
var list = Enumerable.Range(1, 1000);
Parallel.ForEach(list, options, async i =>
{
string result = await client.GetStringAsync("/x");
System.Console.WriteLine(result);
});
//for (int i = 0; i < 1000; i++)
//{
// string result = await client.GetStringAsync("/x");
// System.Console.WriteLine(result);
//}
System.Console.WriteLine("Press any key to stop the server");
System.Console.ReadKey();
server.Stop();
}
}
}

View File

@@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.2</TargetFramework>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\WireMock.Net.StandAlone\WireMock.Net.StandAlone.csproj" />
<ProjectReference Include="..\..\src\WireMock.Net\WireMock.Net.csproj" />
</ItemGroup>
</Project>

BIN
resources/MyGet-Config.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

BIN
resources/MyGet-Use.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

View File

@@ -1,6 +1,5 @@
using System;
using JetBrains.Annotations;
using System.Linq;
using JetBrains.Annotations;
using WireMock.Logging;
using WireMock.Server;
using WireMock.Settings;
@@ -47,6 +46,7 @@ namespace WireMock.Net.StandAlone
StartAdminInterface = parser.GetBoolValue("StartAdminInterface", true),
ReadStaticMappings = parser.GetBoolValue("ReadStaticMappings"),
WatchStaticMappings = parser.GetBoolValue("WatchStaticMappings"),
WatchStaticMappingsInSubdirectories = parser.GetBoolValue("WatchStaticMappingsInSubdirectories"),
AllowPartialMapping = parser.GetBoolValue("AllowPartialMapping"),
AdminUsername = parser.GetStringValue("AdminUsername"),
AdminPassword = parser.GetStringValue("AdminPassword"),
@@ -83,10 +83,23 @@ namespace WireMock.Net.StandAlone
Url = proxyURL,
SaveMapping = parser.GetBoolValue("SaveMapping"),
SaveMappingToFile = parser.GetBoolValue("SaveMappingToFile"),
SaveMappingForStatusCodePattern = parser.GetStringValue("SaveMappingForStatusCodePattern"),
ClientX509Certificate2ThumbprintOrSubjectName = parser.GetStringValue("ClientX509Certificate2ThumbprintOrSubjectName"),
BlackListedHeaders = parser.GetValues("BlackListedHeaders"),
BlackListedCookies = parser.GetValues("BlackListedCookies")
BlackListedCookies = parser.GetValues("BlackListedCookies"),
AllowAutoRedirect = parser.GetBoolValue("AllowAutoRedirect")
};
string proxyAddress = parser.GetStringValue("WebProxyAddress");
if (!string.IsNullOrEmpty(proxyAddress))
{
settings.ProxyAndRecordSettings.WebProxySettings = new WebProxySettings
{
Address = proxyAddress,
UserName = parser.GetStringValue("WebProxyUserName"),
Password = parser.GetStringValue("WebProxyPassword")
};
}
}
settings.Logger.Debug("WireMock.Net server arguments [{0}]", string.Join(", ", args.Select(a => $"'{a}'")));

View File

@@ -0,0 +1,18 @@
namespace WireMock.Admin.Mappings
{
/// <summary>
/// Fault Model
/// </summary>
public class FaultModel
{
/// <summary>
/// Gets or sets the fault. Can be null, "", NONE, EMPTY_RESPONSE, MALFORMED_RESPONSE_CHUNK or RANDOM_DATA_THEN_CLOSE.
/// </summary>
public string Type { get; set; }
/// <summary>
/// Gets or sets the fault percentage.
/// </summary>
public double? Percentage { get; set; }
}
}

View File

@@ -62,6 +62,11 @@ namespace WireMock.Admin.Mappings
/// </summary>
public bool? UseTransformer { get; set; }
/// <summary>
/// Use the Handlerbars transformer for the content from the referenced BodyAsFile.
/// </summary>
public bool? UseTransformerForBodyAsFile { get; set; }
/// <summary>
/// Gets or sets the headers.
/// </summary>
@@ -86,5 +91,15 @@ namespace WireMock.Admin.Mappings
/// The client X509Certificate2 Thumbprint or SubjectName to use.
/// </summary>
public string X509Certificate2ThumbprintOrSubjectName { get; set; }
/// <summary>
/// Gets or sets the fault.
/// </summary>
public FaultModel Fault { get; set; }
/// <summary>
/// Gets or sets the WebProxy settings.
/// </summary>
public WebProxyModel WebProxy { get; set; }
}
}

View File

@@ -0,0 +1,23 @@
namespace WireMock.Admin.Mappings
{
/// <summary>
/// WebProxy settings
/// </summary>
public class WebProxyModel
{
/// <summary>
/// A string instance that contains the address of the proxy server.
/// </summary>
public string Address { get; set; }
/// <summary>
/// The user name associated with the credentials.
/// </summary>
public string UserName { get; set; }
/// <summary>
/// The password for the user name associated with the credentials.
/// </summary>
public string Password { get; set; }
}
}

View File

@@ -68,5 +68,15 @@ namespace WireMock.Admin.Requests
/// The detected body type (detection based on Content-Type).
/// </summary>
public BodyType DetectedBodyTypeFromContentType { get; set; }
/// <summary>
/// The FaultType.
/// </summary>
public string FaultType { get; set; }
/// <summary>
/// Gets or sets the Fault percentage.
/// </summary>
public double? FaultPercentage { get; set; }
}
}

View File

@@ -75,8 +75,9 @@ namespace WireMock.Client
/// <summary>
/// Delete (reset) all mappings.
/// </summary>
/// <param name="reloadStaticMappings">A value indicating whether to reload the static mappings after the reset.</param>
[Post("__admin/mappings/reset")]
Task<StatusModel> ResetMappingsAsync();
Task<StatusModel> ResetMappingsAsync(bool? reloadStaticMappings = false);
/// <summary>
/// Get a mapping based on the guid

View File

@@ -0,0 +1,28 @@
#if NETSTANDARD1_3
using System;
using System.Net;
namespace System.Net
{
internal class WebProxy : IWebProxy
{
private readonly string _proxy;
public ICredentials Credentials { get; set; }
public WebProxy(string proxy)
{
_proxy = proxy;
}
public Uri GetProxy(Uri destination)
{
return new Uri(_proxy);
}
public bool IsBypassed(Uri host)
{
return true;
}
}
}
#endif

View File

@@ -31,8 +31,9 @@ namespace WireMock.Handlers
/// 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([NotNull] string path);
/// <param name="includeSubdirectories">A value indicating whether subdirectories should also included when enumerating files.</param>
/// <returns>An enumerable collection of the full names (including paths) for the files in the directory (and optionally subdirectories) specified by path.</returns>
IEnumerable<string> EnumerateFiles([NotNull] string path, bool includeSubdirectories);
/// <summary>
/// Read a static mapping file as text.

View File

@@ -46,11 +46,11 @@ namespace WireMock.Handlers
}
/// <inheritdoc cref="IFileSystemHandler.EnumerateFiles"/>
public IEnumerable<string> EnumerateFiles(string path)
public IEnumerable<string> EnumerateFiles(string path, bool includeSubdirectories)
{
Check.NotNullOrEmpty(path, nameof(path));
return Directory.EnumerateFiles(path);
return includeSubdirectories ? Directory.EnumerateFiles(path, "*", SearchOption.AllDirectories) : Directory.EnumerateFiles(path);
}
/// <inheritdoc cref="IFileSystemHandler.GetMappingFolder"/>

View File

@@ -1,16 +1,17 @@
using System;
using JetBrains.Annotations;
using System;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using JetBrains.Annotations;
using WireMock.HttpsCertificate;
using WireMock.Settings;
using WireMock.Validation;
namespace WireMock.Http
{
internal static class HttpClientHelper
{
public static HttpClient CreateHttpClient(string clientX509Certificate2ThumbprintOrSubjectName = null)
public static HttpClient CreateHttpClient(IProxyAndRecordSettings settings)
{
#if NETSTANDARD
var handler = new HttpClientHandler
@@ -36,20 +37,30 @@ namespace WireMock.Http
ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11;
#endif
if (!string.IsNullOrEmpty(clientX509Certificate2ThumbprintOrSubjectName))
if (!string.IsNullOrEmpty(settings.ClientX509Certificate2ThumbprintOrSubjectName))
{
handler.ClientCertificateOptions = ClientCertificateOption.Manual;
var x509Certificate2 = ClientCertificateHelper.GetCertificate(clientX509Certificate2ThumbprintOrSubjectName);
var x509Certificate2 = ClientCertificateHelper.GetCertificate(settings.ClientX509Certificate2ThumbprintOrSubjectName);
handler.ClientCertificates.Add(x509Certificate2);
}
// For proxy we shouldn't follow auto redirects
handler.AllowAutoRedirect = false;
handler.AllowAutoRedirect = settings.AllowAutoRedirect == true;
// If UseCookies enabled, httpClient ignores Cookie header
handler.UseCookies = false;
if (settings.WebProxySettings != null)
{
handler.UseProxy = true;
handler.Proxy = new WebProxy(settings.WebProxySettings.Address);
if (settings.WebProxySettings.UserName != null && settings.WebProxySettings.Password != null)
{
handler.Proxy.Credentials = new NetworkCredential(settings.WebProxySettings.UserName, settings.WebProxySettings.Password);
}
}
var client = new HttpClient(handler);
#if NET452 || NET46
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls;

View File

@@ -3,6 +3,9 @@ using System.Security.Cryptography.X509Certificates;
namespace WireMock.HttpsCertificate
{
/// <summary>
/// Only used for NetStandard 1.3
/// </summary>
internal static class PublicCertificateHelper
{
// 1] Generate using https://www.pluralsight.com/blog/software-development/selfcert-create-a-self-signed-certificate-interactively-gui-or-programmatically-in-net

View File

@@ -1,6 +1,7 @@
using JetBrains.Annotations;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.Collections;
using System.Linq;
using WireMock.Util;
using WireMock.Validation;
@@ -85,7 +86,7 @@ namespace WireMock.Matchers
// Check if JToken or object
JToken jtokenInput = input is JToken tokenInput ? tokenInput : JObject.FromObject(input);
// Check if JToken or string or object
// Check if JToken, string, IEnumerable or object
JToken jtokenValue;
switch (Value)
{
@@ -97,6 +98,10 @@ namespace WireMock.Matchers
jtokenValue = JsonUtils.Parse(stringValue);
break;
case IEnumerable enumerableValue:
jtokenValue = JArray.FromObject(enumerableValue);
break;
default:
jtokenValue = JObject.FromObject(Value);
break;

View File

@@ -106,7 +106,7 @@ namespace WireMock.Owin
PortUtils.TryExtract(url, out string protocol, out string host, out int port);
options.Listen(System.Net.IPAddress.Any, port, listenOptions =>
{
listenOptions.UseHttps(PublicCertificateHelper.GetX509Certificate2());
listenOptions.UseHttps(); // PublicCertificateHelper.GetX509Certificate2()
});
}
#endif

View File

@@ -4,8 +4,11 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json;
using RandomDataGenerator.FieldOptions;
using RandomDataGenerator.Randomizers;
using WireMock.Handlers;
using WireMock.Http;
using WireMock.ResponseBuilders;
using WireMock.Util;
using WireMock.Validation;
#if !USE_ASPNETCORE
@@ -22,6 +25,8 @@ namespace WireMock.Owin.Mappers
/// </summary>
public class OwinResponseMapper : IOwinResponseMapper
{
private readonly IRandomizerNumber<double> _randomizerDouble = RandomizerFactory.GetRandomizer(new FieldOptionsDouble { Min = 0, Max = 1 });
private readonly IRandomizerBytes _randomizerBytes = RandomizerFactory.GetRandomizer(new FieldOptionsBytes { Min = 100, Max = 200 });
private readonly IFileSystemHandler _fileSystemHandler;
private readonly Encoding _utf8NoBom = new UTF8Encoding(false);
@@ -53,8 +58,43 @@ namespace WireMock.Owin.Mappers
return;
}
response.StatusCode = responseMessage.StatusCode;
byte[] bytes;
switch (responseMessage.FaultType)
{
case FaultType.EMPTY_RESPONSE:
bytes = IsFault(responseMessage) ? new byte[0] : GetNormalBody(responseMessage);
break;
case FaultType.MALFORMED_RESPONSE_CHUNK:
bytes = GetNormalBody(responseMessage) ?? new byte[0];
if (IsFault(responseMessage))
{
bytes = bytes.Take(bytes.Length / 2).Union(_randomizerBytes.Generate()).ToArray();
}
break;
default:
bytes = GetNormalBody(responseMessage);
break;
}
response.StatusCode = responseMessage.StatusCode;
SetResponseHeaders(responseMessage, response);
if (bytes != null)
{
await response.Body.WriteAsync(bytes, 0, bytes.Length);
}
}
private bool IsFault(ResponseMessage responseMessage)
{
return responseMessage.FaultPercentage == null || _randomizerDouble.Generate() <= responseMessage.FaultPercentage;
}
private byte[] GetNormalBody(ResponseMessage responseMessage)
{
byte[] bytes = null;
switch (responseMessage.BodyData?.DetectedBodyType)
{
@@ -63,7 +103,9 @@ namespace WireMock.Owin.Mappers
break;
case BodyType.Json:
Formatting formatting = responseMessage.BodyData.BodyAsJsonIndented == true ? Formatting.Indented : Formatting.None;
Formatting formatting = responseMessage.BodyData.BodyAsJsonIndented == true
? Formatting.Indented
: Formatting.None;
string jsonBody = JsonConvert.SerializeObject(responseMessage.BodyData.BodyAsJson, new JsonSerializerSettings { Formatting = formatting, NullValueHandling = NullValueHandling.Ignore });
bytes = (responseMessage.BodyData.Encoding ?? _utf8NoBom).GetBytes(jsonBody);
break;
@@ -77,12 +119,7 @@ namespace WireMock.Owin.Mappers
break;
}
SetResponseHeaders(responseMessage, response);
if (bytes != null)
{
await response.Body.WriteAsync(bytes, 0, bytes.Length);
}
return bytes;
}
private void SetResponseHeaders(ResponseMessage responseMessage, IResponse response)

View File

@@ -1,13 +1,14 @@
using JetBrains.Annotations;
using System;
using WireMock.Matchers;
using WireMock.Matchers.Request;
namespace WireMock.RequestBuilders
{
/// <summary>
/// The BodyRequestBuilder interface.
/// </summary>
public interface IBodyRequestBuilder
public interface IBodyRequestBuilder : IRequestMatcher
{
/// <summary>
/// WithBody: IMatcher

View File

@@ -1,15 +1,14 @@
using System;
using JetBrains.Annotations;
using System;
using System.Collections.Generic;
using JetBrains.Annotations;
using WireMock.Matchers;
using WireMock.Matchers.Request;
namespace WireMock.RequestBuilders
{
/// <summary>
/// The HeadersAndCookieRequestBuilder interface.
/// </summary>
public interface IHeadersAndCookiesRequestBuilder : IBodyRequestBuilder, IRequestMatcher, IParamsRequestBuilder
public interface IHeadersAndCookiesRequestBuilder : IParamsRequestBuilder
{
/// <summary>
/// WithHeader: matching based on name, pattern and matchBehaviour.

View File

@@ -9,7 +9,7 @@ namespace WireMock.RequestBuilders
/// <summary>
/// The ParamsRequestBuilder interface.
/// </summary>
public interface IParamsRequestBuilder
public interface IParamsRequestBuilder : IBodyRequestBuilder
{
/// <summary>
/// WithParam: matching on key only.

View File

@@ -170,7 +170,7 @@ namespace WireMock
Headers = headers?.ToDictionary(header => header.Key, header => new WireMockList<string>(header.Value));
Cookies = cookies;
RawQuery = WebUtility.UrlDecode(urlDetails.Url.Query);
RawQuery = urlDetails.Url.Query;
Query = QueryStringParser.Parse(RawQuery);
}

View File

@@ -0,0 +1,23 @@
namespace WireMock.ResponseBuilders
{
/// <summary>
/// The FaultType enumeration
/// </summary>
public enum FaultType
{
/// <summary>
/// No Fault
/// </summary>
NONE,
/// <summary>
/// Return a completely empty response.
/// </summary>
EMPTY_RESPONSE,
/// <summary>
/// Send a defined status header, then garbage, then close the connection.
/// </summary>
MALFORMED_RESPONSE_CHUNK
}
}

View File

@@ -7,7 +7,7 @@ namespace WireMock.ResponseBuilders
/// <summary>
/// The BodyResponseBuilder interface.
/// </summary>
public interface IBodyResponseBuilder : ITransformResponseBuilder
public interface IBodyResponseBuilder : IFaultResponseBuilder
{
/// <summary>
/// WithBody : Create a ... response based on a string.

View File

@@ -0,0 +1,18 @@
using JetBrains.Annotations;
namespace WireMock.ResponseBuilders
{
/// <summary>
/// The FaultRequestBuilder interface.
/// </summary>
public interface IFaultResponseBuilder : ITransformResponseBuilder
{
/// <summary>
/// WithBody : Create a fault response.
/// </summary>
/// <param name="faultType">The FaultType.</param>
/// <param name="percentage">The percentage when this fault should occur. When null, it's always a fault.</param>
/// <returns>A <see cref="IResponseBuilder"/>.</returns>
IResponseBuilder WithFault(FaultType faultType, [CanBeNull] double? percentage = null);
}
}

View File

@@ -11,6 +11,6 @@
/// <returns>
/// The <see cref="IResponseBuilder"/>.
/// </returns>
IResponseBuilder WithTransformer();
IResponseBuilder WithTransformer(bool transformContentFromBodyAsFile = false);
}
}

View File

@@ -0,0 +1,14 @@
namespace WireMock.ResponseBuilders
{
public partial class Response
{
/// <inheritdoc cref="IFaultResponseBuilder.WithFault(FaultType, double?)"/>
public IResponseBuilder WithFault(FaultType faultType, double? percentage = null)
{
ResponseMessage.FaultType = faultType;
ResponseMessage.FaultPercentage = percentage;
return this;
}
}
}

View File

@@ -0,0 +1,48 @@
using System.Net.Http;
using WireMock.Http;
using WireMock.Settings;
using WireMock.Validation;
namespace WireMock.ResponseBuilders
{
public partial class Response
{
private HttpClient _httpClientForProxy;
/// <summary>
/// The Proxy URL to use.
/// </summary>
public string ProxyUrl { get; private set; }
/// <summary>
/// The WebProxy settings.
/// </summary>
public IWebProxySettings WebProxySettings { get; private set; }
/// <inheritdoc cref="IProxyResponseBuilder.WithProxy(string, string)"/>
public IResponseBuilder WithProxy(string proxyUrl, string clientX509Certificate2ThumbprintOrSubjectName = null)
{
Check.NotNullOrEmpty(proxyUrl, nameof(proxyUrl));
var settings = new ProxyAndRecordSettings
{
Url = proxyUrl,
ClientX509Certificate2ThumbprintOrSubjectName = clientX509Certificate2ThumbprintOrSubjectName
};
return WithProxy(settings);
}
/// <inheritdoc cref="IProxyResponseBuilder.WithProxy(IProxyAndRecordSettings)"/>
public IResponseBuilder WithProxy(IProxyAndRecordSettings settings)
{
Check.NotNull(settings, nameof(settings));
ProxyUrl = settings.Url;
WebProxySettings = settings.WebProxySettings;
_httpClientForProxy = HttpClientHelper.CreateHttpClient(settings);
return this;
}
}
}

View File

@@ -19,10 +19,8 @@ namespace WireMock.ResponseBuilders
/// <summary>
/// The Response.
/// </summary>
public class Response : IResponseBuilder
public partial class Response : IResponseBuilder
{
private HttpClient _httpClientForProxy;
/// <summary>
/// The delay
/// </summary>
@@ -34,14 +32,9 @@ namespace WireMock.ResponseBuilders
public bool UseTransformer { get; private set; }
/// <summary>
/// The Proxy URL to use.
/// Gets a value indicating whether to use the Handlerbars transformer for the content from the referenced BodyAsFile.
/// </summary>
public string ProxyUrl { get; private set; }
/// <summary>
/// The client X509Certificate2 Thumbprint or SubjectName to use.
/// </summary>
public string ClientX509Certificate2ThumbprintOrSubjectName { get; private set; }
public bool UseTransformerForBodyAsFile { get; private set; }
/// <summary>
/// Gets the response message.
@@ -311,10 +304,11 @@ namespace WireMock.ResponseBuilders
return this;
}
/// <inheritdoc cref="ITransformResponseBuilder.WithTransformer"/>
public IResponseBuilder WithTransformer()
/// <inheritdoc cref="ITransformResponseBuilder.WithTransformer(bool)"/>
public IResponseBuilder WithTransformer(bool transformContentFromBodyAsFile = false)
{
UseTransformer = true;
UseTransformerForBodyAsFile = transformContentFromBodyAsFile;
return this;
}
@@ -333,25 +327,6 @@ namespace WireMock.ResponseBuilders
return WithDelay(TimeSpan.FromMilliseconds(milliseconds));
}
/// <inheritdoc cref="IProxyResponseBuilder.WithProxy(string, string)"/>
public IResponseBuilder WithProxy(string proxyUrl, string clientX509Certificate2ThumbprintOrSubjectName = null)
{
Check.NotNullOrEmpty(proxyUrl, nameof(proxyUrl));
ProxyUrl = proxyUrl;
ClientX509Certificate2ThumbprintOrSubjectName = clientX509Certificate2ThumbprintOrSubjectName;
_httpClientForProxy = HttpClientHelper.CreateHttpClient(clientX509Certificate2ThumbprintOrSubjectName);
return this;
}
/// <inheritdoc cref="IProxyResponseBuilder.WithProxy(IProxyAndRecordSettings)"/>
public IResponseBuilder WithProxy(IProxyAndRecordSettings settings)
{
Check.NotNull(settings, nameof(settings));
return WithProxy(settings.Url, settings.ClientX509Certificate2ThumbprintOrSubjectName);
}
/// <inheritdoc cref="ICallbackResponseBuilder.WithCallback"/>
public IResponseBuilder WithCallback(Func<RequestMessage, ResponseMessage> callbackHandler)
{
@@ -375,6 +350,7 @@ namespace WireMock.ResponseBuilders
public async Task<ResponseMessage> ProvideResponseAsync(RequestMessage requestMessage, IFluentMockServerSettings settings)
{
Check.NotNull(requestMessage, nameof(requestMessage));
Check.NotNull(settings, nameof(settings));
if (Delay != null)
{
@@ -413,7 +389,7 @@ namespace WireMock.ResponseBuilders
{
var factory = new HandlebarsContextFactory(settings.FileSystemHandler, settings.HandlebarsRegistrationCallback);
var responseMessageTransformer = new ResponseMessageTransformer(factory);
return responseMessageTransformer.Transform(requestMessage, ResponseMessage);
return responseMessageTransformer.Transform(requestMessage, ResponseMessage, UseTransformerForBodyAsFile);
}
if (!UseTransformer && ResponseMessage.BodyData?.BodyAsFileIsCached == true)

View File

@@ -1,5 +1,6 @@
using System.Collections.Generic;
using System.Linq;
using WireMock.ResponseBuilders;
using WireMock.Util;
using WireMock.Validation;
@@ -35,6 +36,16 @@ namespace WireMock
/// </summary>
public BodyData BodyData { get; set; }
/// <summary>
/// The FaultType.
/// </summary>
public FaultType FaultType { get; set; }
/// <summary>
/// Gets or sets the Fault percentage.
/// </summary>
public double? FaultPercentage { get; set; }
/// <summary>
/// Adds the header.
/// </summary>

View File

@@ -2,6 +2,7 @@
using WireMock.Admin.Mappings;
using WireMock.Admin.Requests;
using WireMock.Logging;
using WireMock.ResponseBuilders;
using WireMock.Util;
namespace WireMock.Serialization
@@ -61,6 +62,12 @@ namespace WireMock.Serialization
Headers = logEntry.ResponseMessage.Headers
};
if (logEntry.ResponseMessage.FaultType != FaultType.NONE)
{
logResponseModel.FaultType = logEntry.ResponseMessage.FaultType.ToString();
logResponseModel.FaultPercentage = logEntry.ResponseMessage.FaultPercentage;
}
if (logEntry.ResponseMessage.BodyData != null)
{
logResponseModel.BodyOriginal = logEntry.ResponseMessage.BodyOriginal;

View File

@@ -4,6 +4,7 @@ using WireMock.Admin.Mappings;
using WireMock.Matchers.Request;
using WireMock.RequestBuilders;
using WireMock.ResponseBuilders;
using WireMock.Settings;
using WireMock.Util;
using WireMock.Validation;
@@ -112,18 +113,26 @@ namespace WireMock.Serialization
mappingModel.Response.BodyAsFile = null;
mappingModel.Response.BodyAsFileIsCached = null;
mappingModel.Response.UseTransformer = null;
mappingModel.Response.UseTransformerForBodyAsFile = null;
mappingModel.Response.BodyEncoding = null;
mappingModel.Response.ProxyUrl = response.ProxyUrl;
mappingModel.Response.Fault = null;
mappingModel.Response.WebProxy = MapWebProxy(response.WebProxySettings);
}
else
{
mappingModel.Response.WebProxy = null;
mappingModel.Response.BodyDestination = response.ResponseMessage.BodyDestination;
mappingModel.Response.StatusCode = response.ResponseMessage.StatusCode;
mappingModel.Response.Headers = Map(response.ResponseMessage.Headers);
mappingModel.Response.Headers = MapHeaders(response.ResponseMessage.Headers);
if (response.UseTransformer)
{
mappingModel.Response.UseTransformer = response.UseTransformer;
}
if (response.UseTransformerForBodyAsFile)
{
mappingModel.Response.UseTransformerForBodyAsFile = response.UseTransformerForBodyAsFile;
}
if (response.ResponseMessage.BodyData != null)
{
@@ -161,19 +170,39 @@ namespace WireMock.Serialization
};
}
}
if (response.ResponseMessage.FaultType != FaultType.NONE)
{
mappingModel.Response.Fault = new FaultModel
{
Type = response.ResponseMessage.FaultType.ToString(),
Percentage = response.ResponseMessage.FaultPercentage
};
}
}
return mappingModel;
}
private static IDictionary<string, object> Map(IDictionary<string, WireMockList<string>> dictionary)
private static WebProxyModel MapWebProxy(IWebProxySettings settings)
{
return settings != null ? new WebProxyModel
{
Address = settings.Address,
UserName = settings.UserName,
Password = settings.Password
} : null;
}
private static IDictionary<string, object> MapHeaders(IDictionary<string, WireMockList<string>> dictionary)
{
var newDictionary = new Dictionary<string, object>();
if (dictionary == null || dictionary.Count == 0)
{
return null;
return newDictionary;
}
var newDictionary = new Dictionary<string, object>();
foreach (var entry in dictionary)
{
object value = entry.Value.Count == 1 ? (object)entry.Value.ToString() : entry.Value;

View File

@@ -39,6 +39,7 @@ namespace WireMock.Server
private const string AdminRequests = "/__admin/requests";
private const string AdminSettings = "/__admin/settings";
private const string AdminScenarios = "/__admin/scenarios";
private const string QueryParamReloadStaticMappings = "reloadStaticMappings";
private readonly RegexMatcher _adminRequestContentTypeJson = new ContentTypeMatcher(ContentTypeJson, true);
private readonly RegexMatcher _adminMappingsGuidPathMatcher = new RegexMatcher(@"^\/__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})$");
@@ -69,7 +70,7 @@ namespace WireMock.Server
Given(Request.Create().WithPath(AdminMappings).UsingDelete()).AtPriority(AdminPriority).RespondWith(new DynamicResponseProvider(MappingsDelete));
// __admin/mappings/reset
Given(Request.Create().WithPath(AdminMappings + "/reset").UsingPost()).AtPriority(AdminPriority).RespondWith(new DynamicResponseProvider(MappingsDelete));
Given(Request.Create().WithPath(AdminMappings + "/reset").UsingPost()).AtPriority(AdminPriority).RespondWith(new DynamicResponseProvider(MappingsReset));
// __admin/mappings/{guid}
Given(Request.Create().WithPath(_adminMappingsGuidPathMatcher).UsingGet()).AtPriority(AdminPriority).RespondWith(new DynamicResponseProvider(MappingGet));
@@ -141,7 +142,7 @@ namespace WireMock.Server
return;
}
foreach (string filename in _settings.FileSystemHandler.EnumerateFiles(folder).OrderBy(f => f))
foreach (string filename in _settings.FileSystemHandler.EnumerateFiles(folder, _settings.WatchStaticMappingsInSubdirectories == true).OrderBy(f => f))
{
_settings.Logger.Info("Reading Static MappingFile : '{0}'", filename);
@@ -173,9 +174,14 @@ namespace WireMock.Server
return;
}
_settings.Logger.Info("Watching folder '{0}' for new, updated and deleted MappingFiles.", folder);
bool includeSubdirectories = _settings.WatchStaticMappingsInSubdirectories == true;
string includeSubdirectoriesText = includeSubdirectories ? " and Subdirectories" : string.Empty;
_settings.Logger.Info($"Watching folder '{folder}'{includeSubdirectoriesText} for new, updated and deleted MappingFiles.");
var watcher = new EnhancedFileSystemWatcher(folder, "*.json", EnhancedFileSystemWatcherTimeoutMs);
watcher.IncludeSubdirectories = includeSubdirectories;
watcher.Created += (sender, args) =>
{
_settings.Logger.Info("MappingFile created : '{0}', reading file.", args.FullPath);
@@ -248,7 +254,7 @@ namespace WireMock.Server
private void InitProxyAndRecord(IFluentMockServerSettings settings)
{
_httpClientForProxy = HttpClientHelper.CreateHttpClient(settings.ProxyAndRecordSettings.ClientX509Certificate2ThumbprintOrSubjectName);
_httpClientForProxy = HttpClientHelper.CreateHttpClient(settings.ProxyAndRecordSettings);
var respondProvider = Given(Request.Create().WithPath("/*").UsingAnyMethod());
if (settings.StartAdminInterface == true)
@@ -551,6 +557,24 @@ namespace WireMock.Server
return ResponseMessageBuilder.Create("Mappings deleted");
}
private ResponseMessage MappingsReset(RequestMessage requestMessage)
{
ResetMappings();
ResetScenarios();
string message = "Mappings reset";
if (requestMessage.Query.ContainsKey(QueryParamReloadStaticMappings) &&
bool.TryParse(requestMessage.Query[QueryParamReloadStaticMappings].ToString(), out bool reloadStaticMappings)
&& reloadStaticMappings)
{
ReadStaticMappings();
message = $"{message} and static mappings reloaded";
}
return ResponseMessageBuilder.Create(message);
}
#endregion Mappings
#region Request/{guid}
@@ -761,17 +785,24 @@ namespace WireMock.Server
if (responseModel.UseTransformer == true)
{
responseBuilder = responseBuilder.WithTransformer();
responseBuilder = responseBuilder.WithTransformer(responseModel.UseTransformerForBodyAsFile == true);
}
if (!string.IsNullOrEmpty(responseModel.ProxyUrl))
{
if (string.IsNullOrEmpty(responseModel.X509Certificate2ThumbprintOrSubjectName))
var proxyAndRecordSettings = new ProxyAndRecordSettings
{
return responseBuilder.WithProxy(responseModel.ProxyUrl);
}
Url = responseModel.ProxyUrl,
ClientX509Certificate2ThumbprintOrSubjectName = responseModel.X509Certificate2ThumbprintOrSubjectName,
WebProxySettings = responseModel.WebProxy != null ? new WebProxySettings
{
Address = responseModel.WebProxy.Address,
UserName = responseModel.WebProxy.UserName,
Password = responseModel.WebProxy.Password
} : null
};
return responseBuilder.WithProxy(responseModel.ProxyUrl, responseModel.X509Certificate2ThumbprintOrSubjectName);
return responseBuilder.WithProxy(proxyAndRecordSettings);
}
if (responseModel.StatusCode.HasValue)
@@ -820,6 +851,11 @@ namespace WireMock.Server
responseBuilder = responseBuilder.WithBodyFromFile(responseModel.BodyAsFile);
}
if (responseModel.Fault != null && Enum.TryParse(responseModel.Fault.Type, out FaultType faultType))
{
responseBuilder.WithFault(faultType, responseModel.Fault.Percentage);
}
return responseBuilder;
}

View File

@@ -61,6 +61,13 @@ namespace WireMock.Server
/// <returns>The <see cref="IRespondWithAProvider"/>.</returns>
IRespondWithAProvider InScenario(string scenario);
/// <summary>
/// Sets the the scenario with an integer value.
/// </summary>
/// <param name="scenario">The scenario.</param>
/// <returns>The <see cref="IRespondWithAProvider"/>.</returns>
IRespondWithAProvider InScenario(int scenario);
/// <summary>
/// Execute this respond only in case the current state is equal to specified one.
/// </summary>
@@ -68,11 +75,25 @@ namespace WireMock.Server
/// <returns>The <see cref="IRespondWithAProvider"/>.</returns>
IRespondWithAProvider WhenStateIs(string state);
/// <summary>
/// Execute this respond only in case the current state is equal to specified one.
/// </summary>
/// <param name="state">Any object which identifies the current state</param>
/// <returns>The <see cref="IRespondWithAProvider"/>.</returns>
IRespondWithAProvider WhenStateIs(int state);
/// <summary>
/// Once this mapping is executed the state will be changed to specified one.
/// </summary>
/// <param name="state">Any object which identifies the new state</param>
/// <returns>The <see cref="IRespondWithAProvider"/>.</returns>
IRespondWithAProvider WillSetStateTo(string state);
/// <summary>
/// Once this mapping is executed the state will be changed to specified one.
/// </summary>
/// <param name="state">Any object which identifies the new state</param>
/// <returns>The <see cref="IRespondWithAProvider"/>.</returns>
IRespondWithAProvider WillSetStateTo(int state);
}
}

View File

@@ -93,7 +93,13 @@ namespace WireMock.Server
return this;
}
/// <see cref="IRespondWithAProvider.WhenStateIs"/>
/// <see cref="IRespondWithAProvider.InScenario(int)"/>
public IRespondWithAProvider InScenario(int scenario)
{
return InScenario(scenario.ToString());
}
/// <see cref="IRespondWithAProvider.WhenStateIs(string)"/>
public IRespondWithAProvider WhenStateIs(string state)
{
if (string.IsNullOrEmpty(_scenario))
@@ -106,7 +112,13 @@ namespace WireMock.Server
return this;
}
/// <see cref="IRespondWithAProvider.WillSetStateTo"/>
/// <see cref="IRespondWithAProvider.WhenStateIs(int)"/>
public IRespondWithAProvider WhenStateIs(int state)
{
return WhenStateIs(state.ToString());
}
/// <see cref="IRespondWithAProvider.WillSetStateTo(string)"/>
public IRespondWithAProvider WillSetStateTo(string state)
{
if (string.IsNullOrEmpty(_scenario))
@@ -118,5 +130,11 @@ namespace WireMock.Server
return this;
}
/// <see cref="IRespondWithAProvider.WillSetStateTo(int)"/>
public IRespondWithAProvider WillSetStateTo(int state)
{
return WillSetStateTo(state.ToString());
}
}
}

View File

@@ -33,6 +33,10 @@ namespace WireMock.Settings
[PublicAPI]
public bool? WatchStaticMappings { get; set; }
/// <inheritdoc cref="IFluentMockServerSettings.WatchStaticMappingsInSubdirectories"/>
[PublicAPI]
public bool? WatchStaticMappingsInSubdirectories { get; set; }
/// <inheritdoc cref="IFluentMockServerSettings.ProxyAndRecordSettings"/>
[PublicAPI]
public IProxyAndRecordSettings ProxyAndRecordSettings { get; set; }

View File

@@ -42,6 +42,12 @@ namespace WireMock.Settings
[PublicAPI]
bool? WatchStaticMappings { get; set; }
/// <summary>
/// A value indicating whether subdirectories within the static mappings path should be monitored.
/// </summary>
[PublicAPI]
bool? WatchStaticMappingsInSubdirectories { get; set; }
/// <summary>
/// Gets or sets if the proxy and record settings.
/// </summary>

View File

@@ -44,5 +44,15 @@ namespace WireMock.Settings
/// Defines a list of cookies which will excluded from the saved mappings.
/// </summary>
string[] BlackListedCookies { get; set; }
/// <summary>
/// Defines the WebProxySettings.
/// </summary>
IWebProxySettings WebProxySettings { get; set; }
/// <summary>
/// Proxy requests should follow redirection (30x).
/// </summary>
bool? AllowAutoRedirect { get; set; }
}
}
}

View File

@@ -0,0 +1,20 @@
namespace WireMock.Settings
{
public interface IWebProxySettings
{
/// <summary>
/// A string instance that contains the address of the proxy server.
/// </summary>
string Address { get; set; }
/// <summary>
/// The user name associated with the credentials.
/// </summary>
string UserName { get; set; }
/// <summary>
/// The password for the user name associated with the credentials.
/// </summary>
string Password { get; set; }
}
}

View File

@@ -34,5 +34,13 @@ namespace WireMock.Settings
/// <inheritdoc cref="IProxyAndRecordSettings.BlackListedCookies"/>
[PublicAPI]
public string[] BlackListedCookies { get; set; }
/// <inheritdoc cref="IProxyAndRecordSettings.WebProxySettings"/>
[PublicAPI]
public IWebProxySettings WebProxySettings { get; set; }
/// <inheritdoc cref="IProxyAndRecordSettings.AllowAutoRedirect"/>
[PublicAPI]
public bool? AllowAutoRedirect { get; set; }
}
}

View File

@@ -0,0 +1,19 @@
using JetBrains.Annotations;
namespace WireMock.Settings
{
public class WebProxySettings : IWebProxySettings
{
/// <inheritdoc cref="IWebProxySettings.Address"/>
[PublicAPI]
public string Address { get; set; }
/// <inheritdoc cref="IWebProxySettings.UserName"/>
[PublicAPI]
public string UserName { get; set; }
/// <inheritdoc cref="IWebProxySettings.Password"/>
[PublicAPI]
public string Password { get; set; }
}
}

View File

@@ -2,6 +2,7 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections;
using System.Linq;
using WireMock.Util;
using WireMock.Validation;
@@ -14,7 +15,7 @@ namespace WireMock.Transformers
{
handlebarsContext.RegisterHelper("JsonPath.SelectToken", (writer, context, arguments) =>
{
(JObject valueToProcess, string jsonPath) = ParseArguments(arguments);
(JToken valueToProcess, string jsonPath) = ParseArguments(arguments);
try
{
@@ -29,7 +30,7 @@ namespace WireMock.Transformers
handlebarsContext.RegisterHelper("JsonPath.SelectTokens", (writer, options, context, arguments) =>
{
(JObject valueToProcess, string jsonPath) = ParseArguments(arguments);
(JToken valueToProcess, string jsonPath) = ParseArguments(arguments);
try
{
@@ -46,22 +47,26 @@ namespace WireMock.Transformers
});
}
private static (JObject valueToProcess, string jsonpath) ParseArguments(object[] arguments)
private static (JToken valueToProcess, string jsonpath) ParseArguments(object[] arguments)
{
Check.Condition(arguments, args => args.Length == 2, nameof(arguments));
Check.NotNull(arguments[0], "arguments[0]");
Check.NotNullOrEmpty(arguments[1] as string, "arguments[1]");
JObject valueToProcess;
JToken valueToProcess;
switch (arguments[0])
{
case string jsonAsString:
valueToProcess = JsonUtils.Parse(jsonAsString);
case JToken tokenValue:
valueToProcess = tokenValue;
break;
case JObject jsonAsJObject:
valueToProcess = jsonAsJObject;
case string stringValue:
valueToProcess = JsonUtils.Parse(stringValue);
break;
case IEnumerable enumerableValue:
valueToProcess = JArray.FromObject(enumerableValue);
break;
default:

View File

@@ -0,0 +1,11 @@
using HandlebarsDotNet;
using WireMock.Handlers;
namespace WireMock.Transformers
{
internal class HandlebarsContext : IHandlebarsContext
{
public IHandlebars Handlebars { get; set; }
public IFileSystemHandler FileSystemHandler { get; set; }
}
}

View File

@@ -20,15 +20,19 @@ namespace WireMock.Transformers
_action = action;
}
public IHandlebars Create()
public IHandlebarsContext Create()
{
var handlebarsContext = Handlebars.Create(HandlebarsConfiguration);
var handlebars = Handlebars.Create(HandlebarsConfiguration);
HandlebarsHelpers.Register(handlebarsContext, _fileSystemHandler);
HandlebarsHelpers.Register(handlebars, _fileSystemHandler);
_action?.Invoke(handlebarsContext, _fileSystemHandler);
_action?.Invoke(handlebars, _fileSystemHandler);
return handlebarsContext;
return new HandlebarsContext
{
Handlebars = handlebars,
FileSystemHandler = _fileSystemHandler
};
}
}
}
}

View File

@@ -0,0 +1,12 @@
using HandlebarsDotNet;
using WireMock.Handlers;
namespace WireMock.Transformers
{
interface IHandlebarsContext
{
IHandlebars Handlebars { get; set; }
IFileSystemHandler FileSystemHandler { get; set; }
}
}

View File

@@ -4,6 +4,6 @@ namespace WireMock.Transformers
{
interface IHandlebarsContextFactory
{
IHandlebars Create();
IHandlebarsContext Create();
}
}

View File

@@ -1,5 +1,4 @@
using HandlebarsDotNet;
using JetBrains.Annotations;
using JetBrains.Annotations;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
@@ -21,7 +20,7 @@ namespace WireMock.Transformers
_factory = factory;
}
public ResponseMessage Transform(RequestMessage requestMessage, ResponseMessage original)
public ResponseMessage Transform(RequestMessage requestMessage, ResponseMessage original, bool useTransformerForBodyAsFile)
{
var handlebarsContext = _factory.Create();
@@ -36,7 +35,7 @@ namespace WireMock.Transformers
break;
case BodyType.File:
TransformBodyAsFile(handlebarsContext, template, original, responseMessage);
TransformBodyAsFile(handlebarsContext, template, original, responseMessage, useTransformerForBodyAsFile);
break;
case BodyType.String:
@@ -45,13 +44,16 @@ namespace WireMock.Transformers
break;
}
responseMessage.FaultType = original.FaultType;
responseMessage.FaultPercentage = original.FaultPercentage;
// Headers
var newHeaders = new Dictionary<string, WireMockList<string>>();
foreach (var header in original.Headers)
{
var templateHeaderKey = handlebarsContext.Compile(header.Key);
var templateHeaderKey = handlebarsContext.Handlebars.Compile(header.Key);
var templateHeaderValues = header.Value
.Select(handlebarsContext.Compile)
.Select(handlebarsContext.Handlebars.Compile)
.Select(func => func(template))
.ToArray();
@@ -63,7 +65,7 @@ namespace WireMock.Transformers
return responseMessage;
}
private static void TransformBodyAsJson(IHandlebars handlebarsContext, object template, ResponseMessage original, ResponseMessage responseMessage)
private static void TransformBodyAsJson(IHandlebarsContext handlebarsContext, object template, ResponseMessage original, ResponseMessage responseMessage)
{
JToken jToken;
switch (original.BodyData.BodyAsJson)
@@ -91,7 +93,7 @@ namespace WireMock.Transformers
};
}
private static void WalkNode(IHandlebars handlebarsContext, JToken node, object context)
private static void WalkNode(IHandlebarsContext handlebarsContext, JToken node, object context)
{
if (node.Type == JTokenType.Object)
{
@@ -118,7 +120,7 @@ namespace WireMock.Transformers
return;
}
var templateForStringValue = handlebarsContext.Compile(stringValue);
var templateForStringValue = handlebarsContext.Handlebars.Compile(stringValue);
string transformedString = templateForStringValue(context);
if (!string.Equals(stringValue, transformedString))
{
@@ -150,9 +152,9 @@ namespace WireMock.Transformers
node.Replace(value);
}
private static void TransformBodyAsString(IHandlebars handlebarsContext, object template, ResponseMessage original, ResponseMessage responseMessage)
private static void TransformBodyAsString(IHandlebarsContext handlebarsContext, object template, ResponseMessage original, ResponseMessage responseMessage)
{
var templateBodyAsString = handlebarsContext.Compile(original.BodyData.BodyAsString);
var templateBodyAsString = handlebarsContext.Handlebars.Compile(original.BodyData.BodyAsString);
responseMessage.BodyData = new BodyData
{
@@ -162,16 +164,33 @@ namespace WireMock.Transformers
};
}
private static void TransformBodyAsFile(IHandlebars handlebarsContext, object template, ResponseMessage original, ResponseMessage responseMessage)
private void TransformBodyAsFile(IHandlebarsContext handlebarsContext, object template, ResponseMessage original, ResponseMessage responseMessage, bool useTransformerForBodyAsFile)
{
var templateBodyAsFile = handlebarsContext.Compile(original.BodyData.BodyAsFile);
var templateBodyAsFile = handlebarsContext.Handlebars.Compile(original.BodyData.BodyAsFile);
string transformedBodyAsFilename = templateBodyAsFile(template);
responseMessage.BodyData = new BodyData
if (!useTransformerForBodyAsFile)
{
DetectedBodyType = original.BodyData.DetectedBodyType,
DetectedBodyTypeFromContentType = original.BodyData.DetectedBodyTypeFromContentType,
BodyAsFile = templateBodyAsFile(template)
};
responseMessage.BodyData = new BodyData
{
DetectedBodyType = original.BodyData.DetectedBodyType,
DetectedBodyTypeFromContentType = original.BodyData.DetectedBodyTypeFromContentType,
BodyAsFile = transformedBodyAsFilename
};
}
else
{
string text = handlebarsContext.FileSystemHandler.ReadResponseBodyAsString(transformedBodyAsFilename);
var templateBodyAsString = handlebarsContext.Handlebars.Compile(text);
responseMessage.BodyData = new BodyData
{
DetectedBodyType = BodyType.String,
DetectedBodyTypeFromContentType = original.BodyData.DetectedBodyTypeFromContentType,
BodyAsString = templateBodyAsString(template),
BodyAsFile = transformedBodyAsFilename
};
}
}
}
}
}

View File

@@ -19,10 +19,10 @@ namespace WireMock.Util
/// Using : DateParseHandling = DateParseHandling.None
/// </summary>
/// <param name="json">A System.String that contains JSON.</param>
/// <returns>A Newtonsoft.Json.Linq.JObject populated from the string that contains JSON.</returns>
public static JObject Parse(string json)
/// <returns>A Newtonsoft.Json.Linq.JToken populated from the string that contains JSON.</returns>
public static JToken Parse(string json)
{
return JsonConvert.DeserializeObject<JObject>(json, JsonSerializerSettings);
return JsonConvert.DeserializeObject<JToken>(json, JsonSerializerSettings);
}
public static T ParseJTokenToObject<T>(object value)

View File

@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
namespace WireMock.Util
{
@@ -30,7 +31,7 @@ namespace WireMock.Util
.Split(new[] { '&', ';' }, StringSplitOptions.RemoveEmptyEntries) // Support "?key=value;key=anotherValue" and "?key=value&key=anotherValue"
.Select(parameter => parameter.Split(new[] { '=' }, 2, StringSplitOptions.RemoveEmptyEntries))
.GroupBy(parts => parts[0], JoinParts)
.ToDictionary(grouping => grouping.Key, grouping => new WireMockList<string>(grouping.SelectMany(x => x)));
.ToDictionary(grouping => grouping.Key, grouping => new WireMockList<string>(grouping.SelectMany(x => x).Select(WebUtility.UrlDecode)));
}
}
}

View File

@@ -57,11 +57,16 @@
</PropertyGroup>
<ItemGroup>
<Compile Remove="RequestBuilders\Request.WithFault.cs" />
<Compile Remove="Util\CloneUtils.cs" />
<Compile Remove="Util\IndexableDictionary.cs" />
<Compile Remove="Util\NamedReaderWriterLocker.cs" />
</ItemGroup>
<ItemGroup>
<None Include="ResponseBuilders\Response.WithFault.cs" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="JetBrains.Annotations" Version="2018.2.1">
<PrivateAssets>All</PrivateAssets>
@@ -75,14 +80,14 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
<PackageReference Include="System.Linq.Dynamic.Core" Version="1.0.12" />
<PackageReference Include="RandomDataGenerator.Net" Version="1.0.8" />
<PackageReference Include="RandomDataGenerator.Net" Version="1.0.10" />
<PackageReference Include="JmesPath.Net" Version="1.0.125" />
<!--<PackageReference Include="CloneExtensionsEx" Version="1.0.2" />-->
<!--<PackageReference Include="FastDeepCloner" Version="1.2.5" />-->
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net451' ">
<PackageReference Include="Handlebars.Net" Version="1.9.0" />
<PackageReference Include="Handlebars.Net" Version="[1.9.0]" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' != 'net451' ">

View File

@@ -171,7 +171,7 @@ namespace WireMock.Net.Tests
var staticMappingHandlerMock = new Mock<IFileSystemHandler>();
staticMappingHandlerMock.Setup(m => m.GetMappingFolder()).Returns("folder");
staticMappingHandlerMock.Setup(m => m.FolderExists(It.IsAny<string>())).Returns(true);
staticMappingHandlerMock.Setup(m => m.EnumerateFiles(It.IsAny<string>())).Returns(new string[0]);
staticMappingHandlerMock.Setup(m => m.EnumerateFiles(It.IsAny<string>(), It.IsAny<bool>())).Returns(new string[0]);
var server = FluentMockServer.Start(new FluentMockServerSettings
{
@@ -184,7 +184,7 @@ namespace WireMock.Net.Tests
// Assert and Verify
staticMappingHandlerMock.Verify(m => m.GetMappingFolder(), Times.Once);
staticMappingHandlerMock.Verify(m => m.FolderExists("folder"), Times.Once);
staticMappingHandlerMock.Verify(m => m.EnumerateFiles("folder"), Times.Once);
staticMappingHandlerMock.Verify(m => m.EnumerateFiles("folder", false), Times.Once);
}
[Fact]

View File

@@ -75,6 +75,24 @@ namespace WireMock.Net.Tests.Matchers
Check.That(match).IsEqualTo(0);
}
[Fact]
public void JsonMatcher_IsMatch_JArray()
{
// Assign
var matcher = new JsonMatcher(new[] { "x", "y" });
// Act
var jArray = new JArray
{
"x",
"y"
};
double match = matcher.IsMatch(jArray);
// Assert
Assert.Equal(1.0, match);
}
[Fact]
public void JsonMatcher_IsMatch_JObject()
{
@@ -139,6 +157,24 @@ namespace WireMock.Net.Tests.Matchers
Assert.Equal(1.0, match);
}
[Fact]
public void JsonMatcher_IsMatch_JArrayAsString()
{
// Assign
var matcher = new JsonMatcher("[ \"x\", \"y\" ]");
// Act
var jArray = new JArray
{
"x",
"y"
};
double match = matcher.IsMatch(jArray);
// Assert
Assert.Equal(1.0, match);
}
[Fact]
public void JsonMatcher_IsMatch_JObjectAsString()
{

View File

@@ -6,6 +6,7 @@ using System.Threading.Tasks;
using System.Threading;
using WireMock.Handlers;
using WireMock.Owin.Mappers;
using WireMock.ResponseBuilders;
using WireMock.Util;
#if NET452
using Microsoft.Owin;
@@ -97,7 +98,7 @@ namespace WireMock.Net.Tests.Owin.Mappers
public async Task OwinResponseMapper_MapAsync_Body()
{
// Arrange
string body = "abc";
string body = "abcd";
var responseMessage = new ResponseMessage
{
Headers = new Dictionary<string, WireMockList<string>>(),
@@ -108,7 +109,7 @@ namespace WireMock.Net.Tests.Owin.Mappers
await _sut.MapAsync(responseMessage, _responseMock.Object);
// Assert
_stream.Verify(s => s.WriteAsync(new byte[] { 97, 98, 99 }, 0, 3, It.IsAny<CancellationToken>()), Times.Once);
_stream.Verify(s => s.WriteAsync(new byte[] { 97, 98, 99, 100 }, 0, 4, It.IsAny<CancellationToken>()), Times.Once);
}
[Fact]
@@ -167,5 +168,47 @@ namespace WireMock.Net.Tests.Owin.Mappers
_headers.Verify(h => h.TryGetValue("h", out v), Times.Once);
#endif
}
[Fact]
public async Task OwinResponseMapper_MapAsync_WithFault_EMPTY_RESPONSE()
{
// Arrange
string body = "abc";
var responseMessage = new ResponseMessage
{
Headers = new Dictionary<string, WireMockList<string>>(),
BodyData = new BodyData { DetectedBodyType = BodyType.String, BodyAsString = body },
FaultType = FaultType.EMPTY_RESPONSE
};
// Act
await _sut.MapAsync(responseMessage, _responseMock.Object);
// Assert
_stream.Verify(s => s.WriteAsync(new byte[0], 0, 0, It.IsAny<CancellationToken>()), Times.Once);
}
[Theory]
[InlineData("abcd", BodyType.String)]
[InlineData("", BodyType.String)]
[InlineData(null, BodyType.None)]
public async Task OwinResponseMapper_MapAsync_WithFault_MALFORMED_RESPONSE_CHUNK(string body, BodyType detected)
{
// Arrange
var responseMessage = new ResponseMessage
{
Headers = new Dictionary<string, WireMockList<string>>(),
BodyData = new BodyData { DetectedBodyType = detected, BodyAsString = body },
StatusCode = 100,
FaultType = FaultType.MALFORMED_RESPONSE_CHUNK
};
// Act
await _sut.MapAsync(responseMessage, _responseMock.Object);
// Assert
_responseMock.VerifySet(r => r.StatusCode = 100, Times.Once);
_stream.Verify(s => s.WriteAsync(It.IsAny<byte[]>(), 0, It.Is<int>(count => count >= 0), It.IsAny<CancellationToken>()), Times.Once);
}
}
}

View File

@@ -5,6 +5,7 @@ using System.Threading.Tasks;
using Moq;
using Newtonsoft.Json;
using NFluent;
using WireMock.Handlers;
using WireMock.Models;
using WireMock.ResponseBuilders;
using WireMock.Settings;
@@ -226,7 +227,7 @@ namespace WireMock.Net.Tests.ResponseBuilders
var response = Response.Create()
.WithTransformer()
.WithBodyFromFile(@"c:\\{{request.query.MyUniqueNumber}}\test.xml"); // why use a \\ here ?
.WithBodyFromFile(@"c:\\{{request.query.MyUniqueNumber}}\\test.xml");
// Act
var responseMessage = await response.ProvideResponseAsync(request, _settingsMock.Object);
@@ -235,6 +236,30 @@ namespace WireMock.Net.Tests.ResponseBuilders
Check.That(responseMessage.BodyData.BodyAsFile).Equals(@"c:\1\test.xml");
}
[Fact]
public async Task Response_ProvideResponse_Handlebars_WithBodyAsFile_And_TransformContentFromBodyAsFile()
{
// Assign
var filesystemHandlerMock = new Mock<IFileSystemHandler>(MockBehavior.Strict);
filesystemHandlerMock.Setup(fs => fs.ReadResponseBodyAsString(It.IsAny<string>())).Returns("<xml MyUniqueNumber=\"{{request.query.MyUniqueNumber}}\"></xml>");
_settingsMock.SetupGet(s => s.FileSystemHandler).Returns(filesystemHandlerMock.Object);
var request = new RequestMessage(new UrlDetails("http://localhost/foo?MyUniqueNumber=1"), "GET", ClientIp);
var response = Response.Create()
.WithTransformer(true)
.WithBodyFromFile(@"c:\\{{request.query.MyUniqueNumber}}\\test.xml");
// Act
var responseMessage = await response.ProvideResponseAsync(request, _settingsMock.Object);
// Assert
Check.That(responseMessage.BodyData.BodyAsFile).Equals(@"c:\1\test.xml");
Check.That(responseMessage.BodyData.DetectedBodyType).Equals(BodyType.String);
Check.That(responseMessage.BodyData.BodyAsString).Equals("<xml MyUniqueNumber=\"1\"></xml>");
}
[Fact]
public async Task Response_ProvideResponse_Handlebars_WithBodyAsFile_JsonPath()
{

View File

@@ -2,6 +2,7 @@
using NFluent;
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Threading.Tasks;
using WireMock.Models;
using WireMock.RequestBuilders;
@@ -45,6 +46,28 @@ namespace WireMock.Net.Tests.ResponseBuilders
Check.That(responseMessage.Headers["Content-Type"].ToString()).IsEqualTo("application/json");
}
[Fact]
public void Response_WithProxy_WebProxySettings()
{
// Assign
var settings = new ProxyAndRecordSettings
{
Url = "http://test.nl",
WebProxySettings = new WebProxySettings
{
Address = "http://company",
UserName = "x",
Password = "y"
}
};
var response = Response.Create().WithProxy(settings);
// Act
var request = new RequestMessage(new UrlDetails($"{_server.Urls[0]}/{_guid}"), "GET", "::1");
Check.ThatAsyncCode(() => response.ProvideResponseAsync(request, _settingsMock.Object)).Throws<HttpRequestException>();
}
public void Dispose()
{
_server?.Dispose();

View File

@@ -0,0 +1,49 @@
using FluentAssertions;
using Moq;
using System.Threading.Tasks;
using WireMock.Models;
using WireMock.ResponseBuilders;
using WireMock.Settings;
using Xunit;
namespace WireMock.Net.Tests.ResponseBuilders
{
public class ResponseWithWithFaultTests
{
private readonly Mock<IFluentMockServerSettings> _settingsMock = new Mock<IFluentMockServerSettings>();
private const string ClientIp = "::1";
[Theory]
[InlineData(FaultType.EMPTY_RESPONSE)]
[InlineData(FaultType.MALFORMED_RESPONSE_CHUNK)]
public async Task Response_ProvideResponse_WithFault(FaultType faultType)
{
// Arrange
var request = new RequestMessage(new UrlDetails("http://localhost/fault"), "GET", ClientIp);
// Act
var response = Response.Create().WithFault(faultType);
var responseMessage = await response.ProvideResponseAsync(request, _settingsMock.Object);
// Assert
responseMessage.FaultType.Should().Be(faultType);
responseMessage.FaultPercentage.Should().BeNull();
}
[Theory]
[InlineData(FaultType.EMPTY_RESPONSE, 0.5)]
public async Task Response_ProvideResponse_WithFault_IncludingPercentage(FaultType faultType, double percentage)
{
// Arrange
var request = new RequestMessage(new UrlDetails("http://localhost/fault"), "GET", ClientIp);
// Act
var response = Response.Create().WithFault(faultType, percentage);
var responseMessage = await response.ProvideResponseAsync(request, _settingsMock.Object);
// Assert
responseMessage.FaultType.Should().Be(faultType);
responseMessage.FaultPercentage.Should().Be(percentage);
}
}
}

View File

@@ -1,6 +1,8 @@
using NFluent;
using FluentAssertions;
using NFluent;
using WireMock.Logging;
using WireMock.Models;
using WireMock.ResponseBuilders;
using WireMock.Serialization;
using WireMock.Util;
using Xunit;
@@ -59,8 +61,7 @@ namespace WireMock.Net.Tests.Serialization
// Assign
var logEntry = new LogEntry
{
RequestMessage = new RequestMessage(new UrlDetails("http://localhost"), "get", "::1"
),
RequestMessage = new RequestMessage(new UrlDetails("http://localhost"), "get", "::1"),
ResponseMessage = new ResponseMessage
{
BodyData = new BodyData
@@ -88,5 +89,32 @@ namespace WireMock.Net.Tests.Serialization
Check.That(result.Response.BodyAsJson).IsNull();
Check.That(result.Response.BodyAsFile).IsEqualTo("test");
}
[Fact]
public void LogEntryMapper_Map_LogEntry_WithFault()
{
// Assign
var logEntry = new LogEntry
{
RequestMessage = new RequestMessage(new UrlDetails("http://localhost"), "get", "::1"),
ResponseMessage = new ResponseMessage
{
BodyData = new BodyData
{
DetectedBodyType = BodyType.File,
BodyAsFile = "test"
},
FaultType = FaultType.EMPTY_RESPONSE,
FaultPercentage = 0.5
}
};
// Act
var result = LogEntryMapper.Map(logEntry);
// Assert
result.Response.FaultType.Should().Be("EMPTY_RESPONSE");
result.Response.FaultPercentage.Should().Be(0.5);
}
}
}

View File

@@ -61,6 +61,90 @@ namespace WireMock.Net.Tests
Check.That(responseWithState).Equals("Test state msg");
}
[Fact]
public async Task Scenarios_Should_Respect_Int_Valued_Scenarios_and_States()
{
// given
string path = $"/foo_{Guid.NewGuid()}";
var server = FluentMockServer.Start();
server
.Given(Request.Create().WithPath(path).UsingGet())
.InScenario(1)
.WillSetStateTo(2)
.RespondWith(Response.Create().WithBody("Scenario 1, Setting State 2"));
server
.Given(Request.Create().WithPath(path).UsingGet())
.InScenario(1)
.WhenStateIs(2)
.RespondWith(Response.Create().WithBody("Scenario 1, State 2"));
// when
var responseIntScenario = await new HttpClient().GetStringAsync("http://localhost:" + server.Ports[0] + path);
var responseWithIntState = await new HttpClient().GetStringAsync("http://localhost:" + server.Ports[0] + path);
// then
Check.That(responseIntScenario).Equals("Scenario 1, Setting State 2");
Check.That(responseWithIntState).Equals("Scenario 1, State 2");
}
[Fact]
public async Task Scenarios_Should_Respect_Mixed_String_Scenario_and_Int_State()
{
// given
string path = $"/foo_{Guid.NewGuid()}";
var server = FluentMockServer.Start();
server
.Given(Request.Create().WithPath(path).UsingGet())
.InScenario("state string")
.WillSetStateTo(1)
.RespondWith(Response.Create().WithBody("string state, Setting State 2"));
server
.Given(Request.Create().WithPath(path).UsingGet())
.InScenario("state string")
.WhenStateIs(1)
.RespondWith(Response.Create().WithBody("string state, State 2"));
// when
var responseIntScenario = await new HttpClient().GetStringAsync("http://localhost:" + server.Ports[0] + path);
var responseWithIntState = await new HttpClient().GetStringAsync("http://localhost:" + server.Ports[0] + path);
// then
Check.That(responseIntScenario).Equals("string state, Setting State 2");
Check.That(responseWithIntState).Equals("string state, State 2");
}
[Fact]
public async Task Scenarios_Should_Respect_Mixed_Int_Scenario_and_String_Scenario_and_String_State()
{
// given
string path = $"/foo_{Guid.NewGuid()}";
var server = FluentMockServer.Start();
server
.Given(Request.Create().WithPath(path).UsingGet())
.InScenario(1)
.WillSetStateTo("Next State")
.RespondWith(Response.Create().WithBody("int state, Setting State 2"));
server
.Given(Request.Create().WithPath(path).UsingGet())
.InScenario("1")
.WhenStateIs("Next State")
.RespondWith(Response.Create().WithBody("string state, State 2"));
// when
var responseIntScenario = await new HttpClient().GetStringAsync("http://localhost:" + server.Ports[0] + path);
var responseWithIntState = await new HttpClient().GetStringAsync("http://localhost:" + server.Ports[0] + path);
// then
Check.That(responseIntScenario).Equals("int state, Setting State 2");
Check.That(responseWithIntState).Equals("string state, State 2");
}
[Fact]
public async Task Scenarios_TodoList_Example()
{

View File

@@ -188,6 +188,34 @@ namespace WireMock.Net.Tests.Util
result["key"].Should().Equal(new WireMockList<string>(new[] { "1", "2", "3" }));
}
[Fact]
public void Parse_With1ParamContainingEscapedAnd()
{
// Assign
string query = "?winkel=C%26A";
// Act
var result = QueryStringParser.Parse(query);
// Assert
result.Count.Should().Be(1);
result["winkel"].Should().Equal(new WireMockList<string>(new[] { "C&A" }));
}
[Fact]
public void Parse_With1ParamContainingParentheses()
{
// Assign
string query = "?Transaction=(123)";
// Act
var result = QueryStringParser.Parse(query);
// Assert
result.Count.Should().Be(1);
result["Transaction"].Should().Equal(new WireMockList<string>(new[] { "(123)" }));
}
[Fact]
public void Parse_WithMultipleParamWithSameKey()
{
@@ -227,12 +255,12 @@ namespace WireMock.Net.Tests.Util
// Assert
result.Count.Should().Be(6);
result["q"].Should().Equal(new WireMockList<string>("energy+edge"));
result["q"].Should().Equal(new WireMockList<string>("energy edge"));
result["rls"].Should().Equal(new WireMockList<string>("com.microsoft:en-au"));
result["ie"].Should().Equal(new WireMockList<string>("UTF-8"));
result["oe"].Should().Equal(new WireMockList<string>("UTF-8"));
result["startIndex"].Should().Equal(new WireMockList<string>());
result["startPage"].Should().Equal(new WireMockList<string>("1%22"));
result["startPage"].Should().Equal(new WireMockList<string>("1\""));
}
}
}

View File

@@ -41,7 +41,7 @@
<PackageReference Include="FluentAssertions" Version="5.7.0" />
<PackageReference Include="System.Threading" Version="4.3.0" />
<PackageReference Include="RestEase" Version="1.4.7" />
<PackageReference Include="RandomDataGenerator.Net" Version="1.0.8" />
<PackageReference Include="RandomDataGenerator.Net" Version="1.0.10" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.9.0" />
<PackageReference Include="Moq" Version="4.10.1" />
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" />