Compare commits

...

48 Commits

Author SHA1 Message Date
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
Stef Heyenrath
2e78a04f3d 1.0.35.0 2019-10-25 17:31:30 +02:00
Stef Heyenrath
f3d2452093 remove Obsolete (#368) 2019-10-25 08:40:40 +00:00
Stef Heyenrath
92e693818a <PackageLicenseExpression>MIT</PackageLicenseExpression> 2019-10-22 21:24:22 +02:00
Stef Heyenrath
9a1ae6a3f0 Do not build symbol NuGets anymore. (#367) 2019-10-22 19:19:53 +00:00
Stef Heyenrath
e701566a1f 1.0.34.0 2019-10-22 21:09:48 +02:00
Stef Heyenrath
84ad5a927e 2.x.x (#366) 2019-10-22 18:58:36 +00:00
Stef Heyenrath
3250604b5a #352 (#354) 2019-10-22 18:53:30 +00:00
dependabot[bot]
9d2963632e Bump Microsoft.AspNetCore.All in /examples/WireMock.Net.WebApplication (#365)
Bumps [Microsoft.AspNetCore.All](https://github.com/aspnet/Universe) from 2.0.8 to 2.0.9.
- [Release notes](https://github.com/aspnet/Universe/releases)
- [Changelog](https://github.com/aspnet/Universe/blob/master/docs/CrossRepoBreakingChanges.md)
- [Commits](https://github.com/aspnet/Universe/compare/2.0.8...2.0.9)

Signed-off-by: dependabot[bot] <support@github.com>
2019-10-22 09:32:37 +00:00
Stef Heyenrath
9e0536c54c 1.0.33.0 - changelog 2019-10-12 08:58:55 +02:00
Stef Heyenrath
676e973011 1.0.33 2019-10-12 08:56:29 +02:00
Stef Heyenrath
31f3d77b38 Mark some classes and methods obsolete for version 1.1.0 (#335)
* Obsolete

* StandAloneApp - obsolete

* obs

* make Interface obsolete
2019-10-12 08:54:01 +02:00
Stef Heyenrath
4a2d512f83 Add Proxy Setting for: SaveMappingForStatusCodePattern to only save the mapping when the status code matches the pattern (#357)
* proxy

* HttpStatusRangeParserTests

* test
2019-10-10 08:18:58 +02:00
Stef Heyenrath
87534c35f5 fix jsonpath matcher (#311) 2019-10-09 13:08:02 +02:00
Stef Heyenrath
7789f94737 Fix JsonMatcher (parsing DateTimeOffset) (#358)
* .

* JObject Parse

* JsonUtils.Parse

* fix code comments
2019-10-09 11:16:39 +02:00
Stef Heyenrath
b2167f85ae public event NotifyCollectionChangedEventHandler LogEntriesChanged (#355) 2019-10-08 09:50:59 +02:00
Stef Heyenrath
3cc361e216 Fixed failing admin requests when content type includes a charset (based on idea from Paul Roub) (#353)
* .

* #350

* fix

* .
2019-10-05 17:20:10 +02:00
Stef Heyenrath
0a9214ef47 Add CSharpCodeMatcher (#324)
* wip

* fix

* .

* windows-2019

* <Target Name="CheckIfShouldKillVBCSCompiler" />

* <!--https://github.com/aspnet/RoslynCodeDomProvider/issues/51-->

* AllowCSharpCodeMatcher

* CSharpCodeMatcher : IObjectMatcher

* TemplateForIsMatchWithDynamic

* RequestMessageBodyMatcher_GetMatchingScore_BodyAsJson_CSharpCodeMatcher

* fix

*  }

* Better Exception Handling
2019-09-28 17:55:07 +02:00
Stef Heyenrath
4afef3695b test utils 2019-09-28 10:55:24 +02:00
Stef Heyenrath
782b082949 Prerelease: '' 2019-09-21 11:34:00 +02:00
Stef Heyenrath
36325fe2c7 source 2019-09-21 11:27:49 +02:00
Stef Heyenrath
01171b9592 Fix Push to NuGet 2019-09-21 11:15:46 +02:00
Stef Heyenrath
d6f44b2202 Add NuGet push 2019-09-21 10:58:12 +02:00
Stef Heyenrath
a2b22d8b0c 1.0.32.0 2019-09-20 19:09:47 +02:00
Stef Heyenrath
666992ef24 When posting new mapping, use DateParseHandling.None (#348)
* .

* OfType + test

* remove line
2019-09-20 13:44:19 +02:00
Stef Heyenrath
9cd16f726f 1.0.31.0 2019-09-19 18:03:15 +02:00
Vitaliy Davydiak
feea64b328 Fix recorded requests skipped by request logger (#346)
* Fix recorded requests skipped request logger.
- When proxy is enabled the recorded requests are mistaken (IMO) for admin requests and skipped

* Add unit test

* Use different solution

* Introduce IsRecordedByProxy property on Mapping class

* Cleanup empty lines

* Refactored fix suggested way
2019-09-17 18:15:23 +02:00
Stef Heyenrath
e1798fbb8e . (#339) 2019-09-17 18:13:01 +02:00
Stef Heyenrath
5b8b588983 Fix CompareTo in RequestMatchResult (#345)
* Fix CompareTo in RequestMatchResult
#344

* fix test
2019-09-17 14:50:43 +02:00
andi0b
2f406029c9 Fix issues with Proxy mode and Binary Request Bodies (#334)
* Add Test for Proxy with binary request

* Fix binary parsing in BodyParser

* Fix binary Matching in RequestMessageBodyMatcher

* Improved Binary Matching in RequestMessageBodyMatcher

* BodyParser: Add test for Content Autodetection

* RequestMessageBodyMatcherTests: Make Code more pretty :)

* BodyParserChanges: Revert white space changes

* Fixed test for different behavior in request matching
2019-09-01 15:44:07 +00:00
Stef Heyenrath
8c9a51c46d release notes 2019-09-01 09:52:38 +02:00
Stef Heyenrath
ebe3275079 1.0.30.0 2019-08-31 21:13:38 +02:00
Stef Heyenrath
c0a43ed204 remove MimeKitLite and use MediaTypeHeaderValue (#338) 2019-08-31 19:01:44 +00:00
Stef Heyenrath
7941894543 . 2019-08-30 22:03:08 +02:00
Stef Heyenrath
ec0bfd7eed 1.0.29.0 2019-08-29 21:54:02 +02:00
Stef Heyenrath
d885de1276 HandlebarsRegistrationCallback example 2019-08-25 18:40:54 +02:00
Stef Heyenrath
ccb2c51a39 JsonMatcher support IgnoreCase (#333)
* JsonMatcher - IgnoreCase - #332

* also rename property name

* Remove example project
2019-08-23 19:49:15 +00:00
Stef Heyenrath
2e33bcc464 #327 (#328) 2019-08-23 11:38:29 +00:00
Simar
fc64c5f925 Convert collection into a new list before enumerating (#331) 2019-08-22 17:58:53 +00:00
105 changed files with 2259 additions and 697 deletions

View File

@@ -1,34 +1,76 @@
# 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, question]
# 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)
# 1.0.34.0 (22 October 2019)
- [#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]
# 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)
- [#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)
- [#358](https://github.com/WireMock-Net/WireMock.Net/pull/358) - Fix JsonMatcher (parsing DateTimeOffset) contributed by [StefH](https://github.com/StefH)
- [#306](https://github.com/WireMock-Net/WireMock.Net/issues/306) - Writing to the response body is invalid for responses with status code 204 [bug]
- [#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
# 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)
- [#347](https://github.com/WireMock-Net/WireMock.Net/issues/347) - Query string match on DateTimeOffset is not working [bug]
# 1.0.31.0 (19 September 2019)
- [#334](https://github.com/WireMock-Net/WireMock.Net/pull/334) - Fix issues with Proxy mode and Binary Request Bodies [bug] contributed by [andi0b](https://github.com/andi0b)
- [#338](https://github.com/WireMock-Net/WireMock.Net/pull/338) - Fix ContentType with parameters in Proxy Mode [bug] contributed by [StefH](https://github.com/StefH)
- [#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]
# 1.0.29.0 (29 August 2019)
- [#328](https://github.com/WireMock-Net/WireMock.Net/pull/328) - Fix LogRequest : Index Out Of Bounds [bug] contributed by [StefH](https://github.com/StefH)
- [#331](https://github.com/WireMock-Net/WireMock.Net/pull/331) - Fix: Collection was modified exception [bug] contributed by [theramis](https://github.com/theramis)
- [#333](https://github.com/WireMock-Net/WireMock.Net/pull/333) - JsonMatcher support IgnoreCase [feature] contributed by [StefH](https://github.com/StefH)
- [#332](https://github.com/WireMock-Net/WireMock.Net/issues/332) - Case sensitive true is ignored for JsonMatcher [feature]
# 1.0.28.0 (20 August 2019)
- [#309](https://github.com/WireMock-Net/WireMock.Net/pull/309) - Fix LogEntries: collection was modified exception [bug] contributed by [StefH](https://github.com/StefH)
- [#314](https://github.com/WireMock-Net/WireMock.Net/pull/314) - RequestLogExpirationDuration : use DateTime.UtcNow [bug] contributed by [StefH](https://github.com/StefH)
- [#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)
- [#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.27.0 (14 August 2019)
- [#316](https://github.com/WireMock-Net/WireMock.Net/pull/316) - Handles case where parameter value contains == [feature] contributed by [lobsteropteryx](https://github.com/lobsteropteryx)
- [#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]
- [#315](https://github.com/WireMock-Net/WireMock.Net/issues/315) - Wiki - Proxying: AtPriority() example not set on right object [doc]
# 1.0.26.0 (11 August 2019)
- [#309](https://github.com/WireMock-Net/WireMock.Net/pull/309) - Fix LogEntries: collection was modified exception [bug] contributed by [StefH](https://github.com/StefH)
- [#314](https://github.com/WireMock-Net/WireMock.Net/pull/314) - RequestLogExpirationDuration : use DateTime.UtcNow [bug] contributed by [StefH](https://github.com/StefH)
- [#252](https://github.com/WireMock-Net/WireMock.Net/issues/252) - Proxy with Transform [help wanted]
- [#305](https://github.com/WireMock-Net/WireMock.Net/issues/305) - Allow Custom SSL Certification? [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]
- [#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)
- [#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)
@@ -38,26 +80,21 @@
- [#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)
@@ -65,7 +102,6 @@
# 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)
@@ -80,7 +116,6 @@
# 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)
@@ -96,8 +131,6 @@
# 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)
@@ -105,26 +138,20 @@
# 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]
- [#243](https://github.com/WireMock-Net/WireMock.Net/issues/243) - Not able to read response from file [question]
# 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)
- [#249](https://github.com/WireMock-Net/WireMock.Net/pull/249) - Fixed &quot;Content-Type multipart/form-data&quot; [bug] contributed by [StefH](https://github.com/StefH)
- [#225](https://github.com/WireMock-Net/WireMock.Net/issues/225) - Feature: Improve logging in example for WireMock as Windows Service [feature]
- [#238](https://github.com/WireMock-Net/WireMock.Net/issues/238) - Localhost and free port problem [question]
- [#245](https://github.com/WireMock-Net/WireMock.Net/issues/245) - Not able to match XML request body using XPathMatcher pattern [question]
- [#248](https://github.com/WireMock-Net/WireMock.Net/issues/248) - Content-Type multipart/form-data is not seen as byte[] anymore
# 1.0.6 (15 December 2018)
- [#242](https://github.com/WireMock-Net/WireMock.Net/pull/242) - Post multiple Mappings [feature] contributed by [StefH](https://github.com/StefH)
- [#237](https://github.com/WireMock-Net/WireMock.Net/issues/237) - Port not released [question]
- [#239](https://github.com/WireMock-Net/WireMock.Net/issues/239) - Not able to hit the mock server if AllowPartialMapping is not set to true [question]
# 1.0.5 (07 December 2018)
- [#236](https://github.com/WireMock-Net/WireMock.Net/pull/236) - Add Random Regex (using Fare) [feature] contributed by [StefH](https://github.com/StefH)
@@ -145,7 +172,6 @@
- [#224](https://github.com/WireMock-Net/WireMock.Net/pull/224) - Fixed issue 223: Example for WireMock as Windows Service throws Exception because of WireMockConsoleLogger contributed by [paulssn](https://github.com/paulssn)
- [#228](https://github.com/WireMock-Net/WireMock.Net/pull/228) - Fixed logic for IsRestrictedResponseHeader [bug] contributed by [StefH](https://github.com/StefH)
- [#223](https://github.com/WireMock-Net/WireMock.Net/issues/223) - Bug: Example for WireMock as Windows Service throws Exception because of WireMockConsoleLogger [bug]
- [#227](https://github.com/WireMock-Net/WireMock.Net/issues/227) - Question: proxy passthrough when no match? [question]
# 1.0.4.19 (31 October 2018)
- [#220](https://github.com/WireMock-Net/WireMock.Net/pull/220) - Update SimpleCommandLineParser to handle arguments with key and value contributed by [StefH](https://github.com/StefH)
@@ -171,7 +197,6 @@
# 1.0.4.17 (22 September 2018)
- [#203](https://github.com/WireMock-Net/WireMock.Net/pull/203) - Set up CI with Azure Pipelines contributed by [azure-pipelines[bot]](https://github.com/apps/azure-pipelines)
- [#204](https://github.com/WireMock-Net/WireMock.Net/pull/204) - Lower priority from Proxy mappings in favor of Admin Mappings [feature] contributed by [StefH](https://github.com/StefH)
- [#115](https://github.com/WireMock-Net/WireMock.Net/issues/115) - Question : Do we have provision to read the Response data from a file? [question]
- [#200](https://github.com/WireMock-Net/WireMock.Net/issues/200) - Issue: Incorrect port matching
- [#205](https://github.com/WireMock-Net/WireMock.Net/issues/205) - Issue: DELETE method is proxied as lowercase [bug]
@@ -190,7 +215,6 @@
# 1.0.4.13 (31 August 2018)
- [#195](https://github.com/WireMock-Net/WireMock.Net/pull/195) - Add LinqMatcher contributed by [StefH](https://github.com/StefH)
- [#192](https://github.com/WireMock-Net/WireMock.Net/issues/192) - Cannot upgrade from 1.0.4.10 to 1.0.4.12 without upgrading to .net core 2.1 [bug]
- [#193](https://github.com/WireMock-Net/WireMock.Net/issues/193) - Question: WireMock in Azure [question]
# 1.0.4.12 (23 August 2018)
- [#190](https://github.com/WireMock-Net/WireMock.Net/pull/190) - Fix ResponseMessageTransformer (#188) contributed by [StefH](https://github.com/StefH)
@@ -233,10 +257,8 @@
- [#164](https://github.com/WireMock-Net/WireMock.Net/pull/164) - Support running WireMock.Net as a sub-app in IIS [feature] contributed by [StefH](https://github.com/StefH)
- [#165](https://github.com/WireMock-Net/WireMock.Net/pull/165) - Add SonarCloud contributed by [StefH](https://github.com/StefH)
- [#166](https://github.com/WireMock-Net/WireMock.Net/pull/166) - Fix Sonar issues contributed by [StefH](https://github.com/StefH)
- [#105](https://github.com/WireMock-Net/WireMock.Net/issues/105) - Question: URL binding issues [question]
- [#120](https://github.com/WireMock-Net/WireMock.Net/issues/120) - Question: JsonPathMatcher - not matching? Correct syntax?
- [#123](https://github.com/WireMock-Net/WireMock.Net/issues/123) - Fix for DateTime Header causing null value in ResponseBuilder
- [#158](https://github.com/WireMock-Net/WireMock.Net/issues/158) - Feature: Support running WireMock.Net as a sub-app in IIS [question]
# 1.0.4.4 (01 July 2018)
- [#156](https://github.com/WireMock-Net/WireMock.Net/issues/156) - Feature: when adding / updating a mapping : return more details
@@ -268,21 +290,17 @@
# 1.0.3.18 (25 May 2018)
- [#142](https://github.com/WireMock-Net/WireMock.Net/pull/142) - Allow all headers to be set as Response headers contributed by [StefH](https://github.com/StefH)
- [#97](https://github.com/WireMock-Net/WireMock.Net/issues/97) - Request matching logic is not practical [question]
- [#122](https://github.com/WireMock-Net/WireMock.Net/issues/122) - WireMock.Net not responding in unit tests - same works in console application
- [#126](https://github.com/WireMock-Net/WireMock.Net/issues/126) - Question: UsingHead always returns 0 for Content-Length header even when explicitly specified
- [#127](https://github.com/WireMock-Net/WireMock.Net/issues/127) - Question: Stub priority - Most recent stub is not always used [question]
- [#132](https://github.com/WireMock-Net/WireMock.Net/issues/132) - LogEntries not being recorded on subsequent tests
- [#136](https://github.com/WireMock-Net/WireMock.Net/issues/136) - Question: Does the WireMock send Content-Length response header
- [#137](https://github.com/WireMock-Net/WireMock.Net/issues/137) - Question: How to specify Transfer-Encoding response header?
- [#139](https://github.com/WireMock-Net/WireMock.Net/issues/139) - Wiki link https://github.com/StefH/WireMock.Net/wiki/Record-(via-proxy)-and-Save is dead
- [#140](https://github.com/WireMock-Net/WireMock.Net/issues/140) - Question: Why the Microsoft.Owin.Host.HttpListener is not referenced in the dll, which uses WireMock? [question]
# 1.0.3.17 (16 May 2018)
- [#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)
- [#103](https://github.com/WireMock-Net/WireMock.Net/issues/103) - Support for Faults [question]
- [#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]
@@ -302,9 +320,6 @@
- [#113](https://github.com/WireMock-Net/WireMock.Net/issues/113) - Feature: Add BodyAsJsonIndented for response message [feature]
- [#114](https://github.com/WireMock-Net/WireMock.Net/issues/114) - Feature: Add PathSegments in Transform [feature]
# 1.0.3.12 (24 March 2018)
- [#100](https://github.com/WireMock-Net/WireMock.Net/issues/100) - Issue: JsonPathMatcher - not working for rootless jsons? [question]
# 1.0.3.11 (20 March 2018)
- [#110](https://github.com/WireMock-Net/WireMock.Net/issues/110) - Fix: remove `Func[]` from MappingModel
@@ -317,9 +332,6 @@
# 1.0.3.8 (10 March 2018)
- [#106](https://github.com/WireMock-Net/WireMock.Net/issues/106) - Issue: Params does not work, when there are multiple values for a key
# 1.0.3.7 (09 March 2018)
- [#104](https://github.com/WireMock-Net/WireMock.Net/issues/104) - Issue: PlatformNotSupportedException [question]
# 1.0.3.4 (04 March 2018)
- [#95](https://github.com/WireMock-Net/WireMock.Net/pull/95) - Unittest fix contributed by [StefH](https://github.com/StefH)
- [#96](https://github.com/WireMock-Net/WireMock.Net/pull/96) - Replace log4net by custom logger (#94) contributed by [StefH](https://github.com/StefH)
@@ -375,7 +387,6 @@
- [#65](https://github.com/WireMock-Net/WireMock.Net/pull/65) - bug: Fix admin api client definition returning the wrong types contributed by [alastairtree](https://github.com/alastairtree)
- [#67](https://github.com/WireMock-Net/WireMock.Net/pull/67) - bug: fix supporting the Patch method and logging the body contributed by [alastairtree](https://github.com/alastairtree)
- [#64](https://github.com/WireMock-Net/WireMock.Net/issues/64) - Pull Requests do not trigger test + codecoverage ?
- [#68](https://github.com/WireMock-Net/WireMock.Net/issues/68) - Full path required in Stub [question]
# 1.0.2.7 (18 November 2017)
- [#62](https://github.com/WireMock-Net/WireMock.Net/pull/62) - Add the Host, Protocol, Port and Origin to the Request message so they can be used in templating contributed by [alastairtree](https://github.com/alastairtree)
@@ -383,7 +394,6 @@
- [#27](https://github.com/WireMock-Net/WireMock.Net/issues/27) - New feature: Record and Save
- [#42](https://github.com/WireMock-Net/WireMock.Net/issues/42) - Enhancement - Save/load request logs to/from disk [feature]
- [#53](https://github.com/WireMock-Net/WireMock.Net/issues/53) - New feature request: Access to Owin pipeline
- [#61](https://github.com/WireMock-Net/WireMock.Net/issues/61) - Partial mapping [question]
# 1.0.2.6 (30 October 2017)
- [#59](https://github.com/WireMock-Net/WireMock.Net/pull/59) - Add ability to provide multiple values for headers in response contributed by [Dreamescaper](https://github.com/Dreamescaper)
@@ -407,13 +417,8 @@
- [#45](https://github.com/WireMock-Net/WireMock.Net/pull/45) - Add RequestLogExpirationDuration and MaxRequestLogCount (#43) contributed by [StefH](https://github.com/StefH)
- [#51](https://github.com/WireMock-Net/WireMock.Net/pull/51) - Observable logs contributed by [deeptowncitizen](https://github.com/deeptowncitizen)
- [#15](https://github.com/WireMock-Net/WireMock.Net/issues/15) - New feature: Proxying [feature]
- [#19](https://github.com/WireMock-Net/WireMock.Net/issues/19) - Is this the same as Mock4Net? [question]
- [#20](https://github.com/WireMock-Net/WireMock.Net/issues/20) - Add client certificate authentication [question]
- [#31](https://github.com/WireMock-Net/WireMock.Net/issues/31) - Feature request: Nuget package for standalone version [feature]
- [#33](https://github.com/WireMock-Net/WireMock.Net/issues/33) - Issue with launching sample code (StandAlone server) [bug]
- [#34](https://github.com/WireMock-Net/WireMock.Net/issues/34) - Where is SearchLogsFor method? [question]
- [#36](https://github.com/WireMock-Net/WireMock.Net/issues/36) - How to implement a request body-dependent response? [question]
- [#37](https://github.com/WireMock-Net/WireMock.Net/issues/37) - Wrong Request Match result is returning [question]
- [#38](https://github.com/WireMock-Net/WireMock.Net/issues/38) - Bug: support also listening on *:{port}
- [#43](https://github.com/WireMock-Net/WireMock.Net/issues/43) - Feature: Add RequestLogExpirationDuration and MaxRequestLogCount
- [#46](https://github.com/WireMock-Net/WireMock.Net/issues/46) - Log the ip-address from the client/caller also in the RequestLog [feature]
@@ -421,8 +426,6 @@
- [#50](https://github.com/WireMock-Net/WireMock.Net/issues/50) - New Feature: Callbacks
# 1.0.2.1 (14 June 2017)
- [#28](https://github.com/WireMock-Net/WireMock.Net/issues/28) - Facing issue with WildcardMatcher and '?' [question]
- [#29](https://github.com/WireMock-Net/WireMock.Net/issues/29) - Support of .Net 4.0 [question]
- [#30](https://github.com/WireMock-Net/WireMock.Net/issues/30) - [Feature] Disable partial mappings by default in standalone version [bug, feature]
# 1.0.2.0 (05 May 2017)
@@ -443,7 +446,6 @@
- [#4](https://github.com/WireMock-Net/WireMock.Net/issues/4) - Handlebar support
- [#5](https://github.com/WireMock-Net/WireMock.Net/issues/5) - Xml(2)Path matching
- [#6](https://github.com/WireMock-Net/WireMock.Net/issues/6) - JsonPath support matching
- [#7](https://github.com/WireMock-Net/WireMock.Net/issues/7) - Add WithStatusCodeRange matching [invalid]
- [#9](https://github.com/WireMock-Net/WireMock.Net/issues/9) - Cookie matching
- [#10](https://github.com/WireMock-Net/WireMock.Net/issues/10) - Add usingDelete [feature]
- [#11](https://github.com/WireMock-Net/WireMock.Net/issues/11) - Add response body in binary format [feature]

View File

@@ -4,7 +4,7 @@
</PropertyGroup>
<PropertyGroup>
<VersionPrefix>1.0.28</VersionPrefix>
<VersionPrefix>1.0.37</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.28.0
GitHubReleaseNotes.exe --output CHANGELOG.md --skip-empty-releases --exclude-labels question invalid doc --version 1.0.37.0

View File

@@ -9,6 +9,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
ProjectSection(SolutionItems) = preProject
.runsettings = .runsettings
azure-pipelines-linux.yml = azure-pipelines-linux.yml
azure-pipelines-nuget.yml = azure-pipelines-nuget.yml
azure-pipelines.yml = azure-pipelines.yml
build-info.md = build-info.md
CHANGELOG.md = CHANGELOG.md
@@ -54,8 +55,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WireMock.Net.StandAlone.Net
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WireMock.Net.Service", "examples\Wiremock.Net.Service\WireMock.Net.Service.csproj", "{7F0B2446-0363-4720-AF46-F47F83B557DC}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WireMock.Net.Console.HeadersTest", "examples\WireMock.Net.Console.HeadersTest\WireMock.Net.Console.HeadersTest.csproj", "{B70278E7-A2C6-4A3B-BBA9-1C873CA6F03C}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WireMock.Net.Console.Net461.Classic", "examples\WireMock.Net.Console.Net461.Classic\WireMock.Net.Console.Net461.Classic.csproj", "{1261BB9B-A7D4-456C-8985-3CE560361B8E}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WireMock.Net.Console.Net452.Classic", "examples\WireMock.Net.Console.Net452.Classic\WireMock.Net.Console.Net452.Classic.csproj", "{668F689E-57B4-422E-8846-C0FF643CA268}"
@@ -120,10 +119,6 @@ Global
{7F0B2446-0363-4720-AF46-F47F83B557DC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7F0B2446-0363-4720-AF46-F47F83B557DC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7F0B2446-0363-4720-AF46-F47F83B557DC}.Release|Any CPU.Build.0 = Release|Any CPU
{B70278E7-A2C6-4A3B-BBA9-1C873CA6F03C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B70278E7-A2C6-4A3B-BBA9-1C873CA6F03C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B70278E7-A2C6-4A3B-BBA9-1C873CA6F03C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B70278E7-A2C6-4A3B-BBA9-1C873CA6F03C}.Release|Any CPU.Build.0 = Release|Any CPU
{1261BB9B-A7D4-456C-8985-3CE560361B8E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1261BB9B-A7D4-456C-8985-3CE560361B8E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1261BB9B-A7D4-456C-8985-3CE560361B8E}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -154,7 +149,6 @@ Global
{23A9AA3C-40FC-42AA-8A5E-05899795A1C6} = {F0C22C47-DF71-463C-9B04-B4E0F3B8708A}
{3C279524-DB73-4DE3-BEF1-F2B2958C9F65} = {F0C22C47-DF71-463C-9B04-B4E0F3B8708A}
{7F0B2446-0363-4720-AF46-F47F83B557DC} = {F0C22C47-DF71-463C-9B04-B4E0F3B8708A}
{B70278E7-A2C6-4A3B-BBA9-1C873CA6F03C} = {F0C22C47-DF71-463C-9B04-B4E0F3B8708A}
{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}

View File

@@ -1,4 +1,5 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=CS/@EntryIndexedValue">CS</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=ID/@EntryIndexedValue">ID</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=IP/@EntryIndexedValue">IP</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=MD/@EntryIndexedValue">MD5</s:String>

46
azure-pipelines-nuget.yml Normal file
View File

@@ -0,0 +1,46 @@
pool:
vmImage: 'vs2017-win2016'
variables:
Prerelease: ''
buildId: "1$(Build.BuildId)"
buildProjects: '**/src/**/*.csproj'
steps:
# Print buildId
- script: |
echo "BuildId = $(buildId)"
displayName: 'Print buildId'
# Based on https://whereslou.com/2018/09/versioning-and-publishing-nuget-packages-automatically-using-azure-devops-pipelines/
- task: DotNetCoreCLI@2
displayName: Build Release
inputs:
command: 'build'
arguments: /p:Configuration=Release # https://github.com/MicrosoftDocs/vsts-docs/issues/1976
projects: $(buildProjects)
- task: DotNetCoreCLI@2
displayName: Pack
condition: and(succeeded(), ne(variables['Build.Reason'], 'PullRequest')) # Do not run for PullRequests
inputs:
command: pack
configuration: 'Release'
packagesToPack: $(buildProjects)
nobuild: true
packDirectory: '$(Build.ArtifactStagingDirectory)/packages'
verbosityPack: 'normal'
- task: PublishBuildArtifacts@1
displayName: Publish Artifacts
condition: and(succeeded(), ne(variables['Build.Reason'], 'PullRequest')) # Do not run for PullRequests
inputs:
PathtoPublish: '$(Build.ArtifactStagingDirectory)'
- task: DotNetCoreCLI@2
displayName: Push to NuGet
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://api.nuget.org/v3/index.json --no-service-endpoint --api-key $(NuGetKey)

View File

@@ -1,5 +1,5 @@
pool:
vmImage: 'vs2017-win2016'
vmImage: 'windows-2019'
variables:
Prerelease: 'ci'
@@ -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: |
@@ -82,4 +85,4 @@ steps:
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

@@ -1,60 +0,0 @@
using System.IO;
using System.Reflection;
using log4net;
using log4net.Config;
using log4net.Repository;
using Newtonsoft.Json;
using WireMock.Logging;
using WireMock.RequestBuilders;
using WireMock.ResponseBuilders;
using WireMock.Server;
using WireMock.Settings;
namespace WireMock.Net.Console.NETCoreApp
{
static class Program
{
private static readonly ILoggerRepository LogRepository = LogManager.GetRepository(Assembly.GetEntryAssembly());
private static readonly ILog Log = LogManager.GetLogger(typeof(Program));
static void Main(params string[] args)
{
XmlConfigurator.Configure(LogRepository, new FileInfo("log4net.config"));
string url = "http://localhost:9999/";
var server = FluentMockServer.Start(new FluentMockServerSettings
{
Urls = new[] { url },
StartAdminInterface = true,
Logger = new WireMockConsoleLogger()
});
System.Console.WriteLine("FluentMockServer listening at {0}", string.Join(",", server.Urls));
server.SetBasicAuthentication("a", "b");
server.AllowPartialMapping();
server
.Given(Request.Create()
.UsingGet()
.WithHeader("Keep-Alive-Test", "stef")
)
.RespondWith(Response.Create()
.WithHeader("Keep-Alive", "timeout=1, max=1")
.WithBody("Keep-Alive OK")
);
System.Console.WriteLine("Press any key to stop the server");
System.Console.ReadKey();
server.Stop();
System.Console.WriteLine("Displaying all requests");
var allRequests = server.LogEntries;
System.Console.WriteLine(JsonConvert.SerializeObject(allRequests, Formatting.Indented));
System.Console.WriteLine("Press any key to quit");
System.Console.ReadKey();
}
}
}

View File

@@ -1,24 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp1.1</TargetFramework>
<ApplicationIcon>../../WireMock.Net-Logo.ico</ApplicationIcon>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\WireMock.Net\WireMock.Net.csproj" />
<PackageReference Include="log4net" Version="2.0.8" />
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
</ItemGroup>
<ItemGroup>
<None Update="log4net.config">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="nlog.config">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>

View File

@@ -1,20 +0,0 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="log4net" type="System.Configuration.IgnoreSectionHandler" />
</configSections>
<appSettings>
<add key="log4net.Internal.Debug" value="true"/>
</appSettings>
<log4net>
<appender name="ConsoleAppender" type="log4net.Appender.ConsoleAppender">
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date [%thread] %-5level %logger{1} - %message%newline" />
</layout>
</appender>
<root>
<level value="DEBUG" />
<appender-ref ref="ConsoleAppender" />
</root>
</log4net>
</configuration>

View File

@@ -1,18 +0,0 @@
<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
autoReload="true"
internalLogLevel="info"
internalLogFile="c:\temp\wiremock-internal-nlog.log">
<targets>
<!-- write logs to file -->
<target xsi:type="File" name="all" fileName="c:\temp\wiremock-${shortdate}.log"
layout="${longdate}|${event-properties:item=EventId.Id}|${uppercase:${level}}|${logger}|${message} ${exception}" />
</targets>
<!-- rules to map from logger name to target -->
<rules>
<logger name="*" minlevel="Debug" writeTo="all" />
</rules>
</nlog>

View File

@@ -19,6 +19,7 @@
<ItemGroup>
<ProjectReference Include="..\..\src\WireMock.Net\WireMock.Net.csproj" />
<PackageReference Include="Handlebars.Net" Version="1.9.5" />
<PackageReference Include="log4net" Version="2.0.8" />
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
</ItemGroup>

View File

@@ -24,6 +24,7 @@
<ItemGroup>
<ProjectReference Include="..\..\src\WireMock.Net\WireMock.Net.csproj" />
<PackageReference Include="Handlebars.Net" Version="1.9.5" />
<PackageReference Include="log4net" Version="2.0.8" />
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
</ItemGroup>

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

@@ -1,6 +1,9 @@
using Newtonsoft.Json;
using HandlebarsDotNet;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Net;
using WireMock.Logging;
using WireMock.Matchers;
@@ -11,6 +14,23 @@ using WireMock.Settings;
namespace WireMock.Net.ConsoleApplication
{
public interface IHandleBarTransformer
{
string Name { get; }
void Render(TextWriter textWriter, dynamic context, object[] arguments);
}
public class CustomNameTransformer : IHandleBarTransformer
{
public string Name => "CustomName";
public void Render(TextWriter writer, dynamic context, object[] parameters)
{
/* Handlebar logic to render */
}
}
public static class MainApp
{
public static void Run()
@@ -21,10 +41,12 @@ namespace WireMock.Net.ConsoleApplication
var server = FluentMockServer.Start(new FluentMockServerSettings
{
AllowCSharpCodeMatcher = true,
Urls = new[] { url1, url2, url3 },
StartAdminInterface = true,
ReadStaticMappings = true,
WatchStaticMappings = true,
WatchStaticMappingsInSubdirectories = true,
//ProxyAndRecordSettings = new ProxyAndRecordSettings
//{
// SaveMapping = true
@@ -33,6 +55,12 @@ namespace WireMock.Net.ConsoleApplication
PostWireMockMiddlewareInit = app => { System.Console.WriteLine($"PostWireMockMiddlewareInit : {app.GetType()}"); },
Logger = new WireMockConsoleLogger(),
HandlebarsRegistrationCallback = (handlebarsContext, fileSystemHandler) =>
{
var transformer = new CustomNameTransformer();
handlebarsContext.RegisterHelper(transformer.Name, transformer.Render);
}
// Uncomment below if you want to use the CustomFileSystemFileHandler
// FileSystemHandler = new CustomFileSystemFileHandler()
});

View File

@@ -36,6 +36,9 @@
<ApplicationIcon>..\..\WireMock.Net-Logo.ico</ApplicationIcon>
</PropertyGroup>
<ItemGroup>
<Reference Include="Handlebars, Version=1.9.5.0, Culture=neutral, PublicKeyToken=22225d0bf33cd661, processorArchitecture=MSIL">
<HintPath>..\..\packages\Handlebars.Net.1.9.5\lib\net452\Handlebars.dll</HintPath>
</Reference>
<Reference Include="log4net, Version=2.0.8.0, Culture=neutral, PublicKeyToken=669e0ddf0bb1aa2a, processorArchitecture=MSIL">
<HintPath>..\..\packages\log4net.2.0.8\lib\net45-full\log4net.dll</HintPath>
</Reference>

View File

@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Handlebars.Net" version="1.9.5" targetFramework="net452" />
<package id="log4net" version="2.0.8" targetFramework="net452" />
<package id="Microsoft.Owin.Host.HttpListener" version="3.1.0" targetFramework="net452" />
<package id="Newtonsoft.Json" version="11.0.2" targetFramework="net452" />

View File

@@ -35,6 +35,9 @@
<StartupObject>WireMock.Net.ConsoleApplication.Program</StartupObject>
</PropertyGroup>
<ItemGroup>
<Reference Include="Handlebars, Version=1.9.5.0, Culture=neutral, PublicKeyToken=22225d0bf33cd661, processorArchitecture=MSIL">
<HintPath>..\..\packages\Handlebars.Net.1.9.5\lib\net452\Handlebars.dll</HintPath>
</Reference>
<Reference Include="log4net, Version=2.0.8.0, Culture=neutral, PublicKeyToken=669e0ddf0bb1aa2a, processorArchitecture=MSIL">
<HintPath>..\..\packages\log4net.2.0.8\lib\net45-full\log4net.dll</HintPath>
</Reference>

View File

@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Handlebars.Net" version="1.9.5" targetFramework="net461" />
<package id="log4net" version="2.0.8" targetFramework="net461" />
<package id="Newtonsoft.Json" version="11.0.2" targetFramework="net461" />
<package id="SimMetrics.Net" version="1.0.5" targetFramework="net461" />

View File

@@ -11,13 +11,13 @@
<ItemGroup Condition=" '$(TargetFramework)' == 'netcoreapp2.0'">
<DotNetCliToolReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Tools" Version="2.0.2" />
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.8" />
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.9" />
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'netcoreapp2.1'">
<DotNetCliToolReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Tools" Version="2.0.4" />
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.1.5" />
<PackageReference Include="Microsoft.AspNetCore.All" />
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
</ItemGroup>

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,9 +1,10 @@
using System.Linq;
using System;
using System.Linq;
using JetBrains.Annotations;
using WireMock.Logging;
using WireMock.Server;
using WireMock.Settings;
using WireMock.Validation;
using JetBrains.Annotations;
using WireMock.Logging;
namespace WireMock.Net.StandAlone
{
@@ -46,11 +47,14 @@ namespace WireMock.Net.StandAlone
StartAdminInterface = parser.GetBoolValue("StartAdminInterface", true),
ReadStaticMappings = parser.GetBoolValue("ReadStaticMappings"),
WatchStaticMappings = parser.GetBoolValue("WatchStaticMappings"),
AllowPartialMapping = parser.GetBoolValue("AllowPartialMapping", false),
WatchStaticMappingsInSubdirectories = parser.GetBoolValue("WatchStaticMappingsInSubdirectories"),
AllowPartialMapping = parser.GetBoolValue("AllowPartialMapping"),
AdminUsername = parser.GetStringValue("AdminUsername"),
AdminPassword = parser.GetStringValue("AdminPassword"),
MaxRequestLogCount = parser.GetIntValue("MaxRequestLogCount"),
RequestLogExpirationDuration = parser.GetIntValue("RequestLogExpirationDuration")
RequestLogExpirationDuration = parser.GetIntValue("RequestLogExpirationDuration"),
AllowCSharpCodeMatcher = parser.GetBoolValue("AllowCSharpCodeMatcher"),
AllowBodyForAllHttpMethods = parser.GetBoolValue("AllowBodyForAllHttpMethods")
};
if (logger != null)
@@ -80,9 +84,10 @@ 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")
};
}

View File

@@ -11,14 +11,14 @@
<PackageReleaseNotes>See CHANGELOG.md</PackageReleaseNotes>
<PackageIconUrl>https://raw.githubusercontent.com/WireMock-Net/WireMock.Net/master/WireMock.Net-Logo.png</PackageIconUrl>
<PackageProjectUrl>https://github.com/WireMock-Net/WireMock.Net</PackageProjectUrl>
<PackageLicenseUrl>https://raw.githubusercontent.com/WireMock-Net/WireMock.Net/master/LICENSE</PackageLicenseUrl>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<RepositoryType>git</RepositoryType>
<RepositoryUrl>https://github.com/WireMock-Net/WireMock.Net</RepositoryUrl>
<ApplicationIcon>../../WireMock.Net-Logo.ico</ApplicationIcon>
<RootNamespace>WireMock.Net.StandAlone</RootNamespace>
<DebugType>full</DebugType>
<!--<DebugType>full</DebugType>
<IncludeSource>True</IncludeSource>
<IncludeSymbols>True</IncludeSymbols>
<IncludeSymbols>True</IncludeSymbols>-->
<ProjectGuid>{B6269AAC-170A-43D5-8B9A-579DED3D9A95}</ProjectGuid>
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<AllowedOutputExtensionsInPackageBuildOutputFolder>$(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb</AllowedOutputExtensionsInPackageBuildOutputFolder>

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

@@ -86,5 +86,10 @@ 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; }
}
}

View File

@@ -1,5 +1,6 @@
using System.Collections.Generic;
using WireMock.Admin.Mappings;
using WireMock.ResponseBuilders;
using WireMock.Util;
namespace WireMock.Admin.Requests
@@ -68,5 +69,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

@@ -24,5 +24,10 @@
/// Gets or sets the MaxRequestLog count.
/// </summary>
public int? MaxRequestLogCount { get; set; }
/// <summary>
/// Gets or sets wether to allow a body for all HTTP methods.
/// </summary>
public bool? AllowBodyForAllHttpMethods { 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

@@ -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

@@ -72,7 +72,7 @@ namespace WireMock.Http
var httpResponseMessage = await client.SendAsync(httpRequestMessage, HttpCompletionOption.ResponseContentRead);
// Create ResponseMessage
return await HttpResponseMessageHelper.Create(httpResponseMessage, requiredUri, originalUri);
return await HttpResponseMessageHelper.CreateAsync(httpResponseMessage, requiredUri, originalUri);
}
}
}

View File

@@ -2,8 +2,8 @@
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using JetBrains.Annotations;
using MimeKit;
using Newtonsoft.Json;
using WireMock.Util;
using WireMock.Validation;
@@ -19,11 +19,11 @@ namespace WireMock.Http
var httpRequestMessage = new HttpRequestMessage(new HttpMethod(requestMessage.Method), url);
ContentType contentType = null;
MediaTypeHeaderValue contentType = null;
if (requestMessage.Headers != null && requestMessage.Headers.ContainsKey(HttpKnownHeaderNames.ContentType))
{
var value = requestMessage.Headers[HttpKnownHeaderNames.ContentType].FirstOrDefault();
ContentType.TryParse(value, out contentType);
MediaTypeHeaderValue.TryParse(value, out contentType);
}
switch (requestMessage.BodyData?.DetectedBodyType)

View File

@@ -9,7 +9,7 @@ namespace WireMock.Http
{
internal static class HttpResponseMessageHelper
{
public static async Task<ResponseMessage> Create(HttpResponseMessage httpResponseMessage, Uri requiredUri, Uri originalUri)
public static async Task<ResponseMessage> CreateAsync(HttpResponseMessage httpResponseMessage, Uri requiredUri, Uri originalUri)
{
var responseMessage = new ResponseMessage { StatusCode = (int)httpResponseMessage.StatusCode };

View File

@@ -1,8 +1,6 @@
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using JetBrains.Annotations;
using MimeKit;
using WireMock.Validation;
namespace WireMock.Http
@@ -10,29 +8,18 @@ namespace WireMock.Http
internal static class StringContentHelper
{
/// <summary>
/// Creates a StringContent object. Note that the Encoding is only set when it's also set on the original header.
/// Creates a StringContent object.
/// </summary>
/// <param name="content">The string content (cannot be null)</param>
/// <param name="contentType">The ContentType (can be null)</param>
/// <returns>StringContent</returns>
internal static StringContent Create([NotNull] string content, [CanBeNull] ContentType contentType)
internal static StringContent Create([NotNull] string content, [CanBeNull] MediaTypeHeaderValue contentType)
{
Check.NotNull(content, nameof(content));
if (contentType == null)
{
return new StringContent(content);
}
if (contentType.Charset == null)
{
var stringContent = new StringContent(content);
stringContent.Headers.ContentType = new MediaTypeHeaderValue(contentType.MimeType);
return stringContent;
}
var encoding = Encoding.GetEncoding(contentType.Charset);
return new StringContent(content, encoding, contentType.MimeType);
var stringContent = new StringContent(content);
stringContent.Headers.ContentType = contentType;
return stringContent;
}
}
}

View File

@@ -78,6 +78,14 @@ namespace WireMock
/// <c>true</c> if this mapping is an Admin Interface; otherwise, <c>false</c>.
/// </value>
bool IsAdminInterface { get; }
/// <summary>
/// Gets a value indicating whether this mapping to be logged.
/// </summary>
/// <value>
/// <c>true</c> if this mapping to be logged; otherwise, <c>false</c>.
/// </value>
bool LogMapping { get; }
/// <summary>
/// ProvideResponseAsync

View File

@@ -48,6 +48,9 @@ namespace WireMock
/// <inheritdoc cref="IMapping.IsAdminInterface" />
public bool IsAdminInterface => Provider is DynamicResponseProvider || Provider is DynamicAsyncResponseProvider || Provider is ProxyAsyncResponseProvider;
/// <inheritdoc cref="IMapping.LogMapping" />
public bool LogMapping => !(Provider is DynamicResponseProvider || Provider is DynamicAsyncResponseProvider);
/// <summary>
/// Initializes a new instance of the <see cref="Mapping"/> class.
/// </summary>

View File

@@ -0,0 +1,197 @@
using System;
using System.Linq;
using JetBrains.Annotations;
using Newtonsoft.Json.Linq;
using WireMock.Exceptions;
using WireMock.Validation;
namespace WireMock.Matchers
{
/// <summary>
/// CSharpCode / CS-Script Matcher
/// </summary>
/// <inheritdoc cref="IObjectMatcher"/>
/// <inheritdoc cref="IStringMatcher"/>
internal class CSharpCodeMatcher : IObjectMatcher, IStringMatcher
{
private const string TemplateForIsMatchWithString = "{0} public class CodeHelper {{ public bool IsMatch(string it) {{ {1} }} }}";
private const string TemplateForIsMatchWithDynamic = "{0} public class CodeHelper {{ public bool IsMatch(dynamic it) {{ {1} }} }}";
private readonly string[] _usings =
{
"System",
"System.Linq",
"System.Collections.Generic",
"Microsoft.CSharp",
"Newtonsoft.Json.Linq"
};
public MatchBehaviour MatchBehaviour { get; }
private readonly string[] _patterns;
/// <summary>
/// Initializes a new instance of the <see cref="CSharpCodeMatcher"/> class.
/// </summary>
/// <param name="patterns">The patterns.</param>
public CSharpCodeMatcher([NotNull] params string[] patterns) : this(MatchBehaviour.AcceptOnMatch, patterns)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="CSharpCodeMatcher"/> class.
/// </summary>
/// <param name="matchBehaviour">The match behaviour.</param>
/// <param name="patterns">The patterns.</param>
public CSharpCodeMatcher(MatchBehaviour matchBehaviour, [NotNull] params string[] patterns)
{
Check.NotNull(patterns, nameof(patterns));
MatchBehaviour = matchBehaviour;
_patterns = patterns;
}
public double IsMatch(string input)
{
return IsMatchInternal(input);
}
public double IsMatch(object input)
{
return IsMatchInternal(input);
}
public double IsMatchInternal(object input)
{
double match = MatchScores.Mismatch;
if (input != null)
{
match = MatchScores.ToScore(_patterns.Select(pattern => IsMatch(input, pattern)));
}
return MatchBehaviourHelper.Convert(MatchBehaviour, match);
}
private bool IsMatch(dynamic input, string pattern)
{
bool isMatchWithString = input is string;
var inputValue = isMatchWithString ? input : JObject.FromObject(input);
string source = GetSourceForIsMatchWithString(pattern, isMatchWithString);
object result = null;
#if NET451 || NET452
var compilerParams = new System.CodeDom.Compiler.CompilerParameters
{
GenerateInMemory = true,
GenerateExecutable = false,
ReferencedAssemblies =
{
"System.dll",
"System.Core.dll",
"Microsoft.CSharp.dll",
"Newtonsoft.Json.dll"
}
};
using (var codeProvider = new Microsoft.CSharp.CSharpCodeProvider())
{
var compilerResults = codeProvider.CompileAssemblyFromSource(compilerParams, source);
if (compilerResults.Errors.Count != 0)
{
var errors = from System.CodeDom.Compiler.CompilerError er in compilerResults.Errors select er.ToString();
throw new WireMockException(string.Join(", ", errors));
}
object helper = compilerResults.CompiledAssembly.CreateInstance("CodeHelper");
if (helper == null)
{
throw new WireMockException("CSharpCodeMatcher: Unable to create instance from WireMock.CodeHelper");
}
var methodInfo = helper.GetType().GetMethod("IsMatch");
if (methodInfo == null)
{
throw new WireMockException("CSharpCodeMatcher: Unable to find method 'IsMatch' in WireMock.CodeHelper");
}
try
{
result = methodInfo.Invoke(helper, new[] { inputValue });
}
catch
{
throw new WireMockException("CSharpCodeMatcher: Unable to call method 'IsMatch' in WireMock.CodeHelper");
}
}
#elif NET46 || NET461
dynamic script;
try
{
script = CSScriptLibrary.CSScript.Evaluator.CompileCode(source).CreateObject("*");
}
catch
{
throw new WireMockException("CSharpCodeMatcher: Unable to create compiler for WireMock.CodeHelper");
}
try
{
result = script.IsMatch(inputValue);
}
catch
{
throw new WireMockException("CSharpCodeMatcher: Problem calling method 'IsMatch' in WireMock.CodeHelper");
}
#elif NETSTANDARD2_0
dynamic script;
try
{
var assembly = CSScriptLib.CSScript.Evaluator.CompileCode(source);
script = csscript.GenericExtensions.CreateObject(assembly, "*");
}
catch (Exception ex)
{
throw new WireMockException("CSharpCodeMatcher: Unable to compile code for WireMock.CodeHelper", ex);
}
try
{
result = script.IsMatch(inputValue);
}
catch (Exception ex)
{
throw new WireMockException("CSharpCodeMatcher: Problem calling method 'IsMatch' in WireMock.CodeHelper");
}
#else
throw new NotSupportedException("The 'CSharpCodeMatcher' cannot be used in netstandard 1.3");
#endif
try
{
return (bool)result;
}
catch
{
throw new WireMockException($"Unable to cast result '{result}' to bool");
}
}
private string GetSourceForIsMatchWithString(string pattern, bool isMatchWithString)
{
string template = isMatchWithString ? TemplateForIsMatchWithString : TemplateForIsMatchWithDynamic;
return string.Format(template, string.Join(Environment.NewLine, _usings.Select(u => $"using {u};")), pattern);
}
/// <inheritdoc cref="IStringMatcher.GetPatterns"/>
public string[] GetPatterns()
{
return _patterns;
}
/// <inheritdoc cref="IMatcher.Name"/>
public string Name => "CSharpCodeMatcher";
}
}

View File

@@ -0,0 +1,73 @@
using System.Net.Http.Headers;
using JetBrains.Annotations;
namespace WireMock.Matchers
{
/// <summary>
/// ContentTypeMatcher which accepts also all charsets
/// </summary>
/// <seealso cref="RegexMatcher" />
public class ContentTypeMatcher : WildcardMatcher
{
private readonly string[] _patterns;
/// <summary>
/// Initializes a new instance of the <see cref="ContentTypeMatcher"/> class.
/// </summary>
/// <param name="pattern">The pattern.</param>
/// <param name="ignoreCase">IgnoreCase (default false)</param>
public ContentTypeMatcher([NotNull] string pattern, bool ignoreCase = false) : this(new[] { pattern }, ignoreCase)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="ContentTypeMatcher"/> class.
/// </summary>
/// <param name="matchBehaviour">The match behaviour.</param>
/// <param name="pattern">The pattern.</param>
/// <param name="ignoreCase">IgnoreCase (default false)</param>
public ContentTypeMatcher(MatchBehaviour matchBehaviour, [NotNull] string pattern, bool ignoreCase = false) : this(matchBehaviour, new[] { pattern }, ignoreCase)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="ContentTypeMatcher"/> class.
/// </summary>
/// <param name="patterns">The patterns.</param>
/// <param name="ignoreCase">IgnoreCase (default false)</param>
public ContentTypeMatcher([NotNull] string[] patterns, bool ignoreCase = false) : this(MatchBehaviour.AcceptOnMatch, patterns, ignoreCase)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="ContentTypeMatcher"/> class.
/// </summary>
/// <param name="matchBehaviour">The match behaviour.</param>
/// <param name="patterns">The patterns.</param>
/// <param name="ignoreCase">IgnoreCase (default false)</param>
public ContentTypeMatcher(MatchBehaviour matchBehaviour, [NotNull] string[] patterns, bool ignoreCase = false) : base(matchBehaviour, patterns, ignoreCase)
{
_patterns = patterns;
}
/// <inheritdoc cref="RegexMatcher.IsMatch"/>
public override double IsMatch(string input)
{
if (string.IsNullOrEmpty(input) || !MediaTypeHeaderValue.TryParse(input, out MediaTypeHeaderValue contentType))
{
return MatchBehaviourHelper.Convert(MatchBehaviour, MatchScores.Mismatch);
}
return base.IsMatch(contentType.MediaType);
}
/// <inheritdoc cref="IStringMatcher.GetPatterns"/>
public override string[] GetPatterns()
{
return _patterns;
}
/// <inheritdoc cref="IMatcher.Name"/>
public override string Name => "ContentTypeMatcher";
}
}

View File

@@ -93,10 +93,7 @@ namespace WireMock.Matchers
private double IsMatch(JToken jtoken)
{
// Wrap in array if needed
JToken tokenOrArray = jtoken is JArray ? jtoken : new JArray(jtoken);
return MatchScores.ToScore(_patterns.Select(pattern => tokenOrArray.SelectToken(pattern) != null));
return MatchScores.ToScore(_patterns.Select(pattern => jtoken.SelectToken(pattern) != null));
}
}
}

View File

@@ -1,6 +1,8 @@
using JetBrains.Annotations;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.Linq;
using WireMock.Util;
using WireMock.Validation;
namespace WireMock.Matchers
@@ -8,7 +10,7 @@ namespace WireMock.Matchers
/// <summary>
/// JsonMatcher
/// </summary>
public class JsonMatcher : IValueMatcher
public class JsonMatcher : IValueMatcher, IIgnoreCaseMatcher
{
/// <inheritdoc cref="IValueMatcher.Value"/>
public object Value { get; }
@@ -19,11 +21,15 @@ namespace WireMock.Matchers
/// <inheritdoc cref="IMatcher.MatchBehaviour"/>
public MatchBehaviour MatchBehaviour { get; }
/// <inheritdoc cref="IIgnoreCaseMatcher.IgnoreCase"/>
public bool IgnoreCase { get; }
/// <summary>
/// Initializes a new instance of the <see cref="JsonMatcher"/> class.
/// </summary>
/// <param name="value">The string value to check for equality.</param>
public JsonMatcher([NotNull] string value) : this(MatchBehaviour.AcceptOnMatch, value)
/// <param name="ignoreCase">Ignore the case from the PropertyName and PropertyValue (string only).</param>
public JsonMatcher([NotNull] string value, bool ignoreCase = false) : this(MatchBehaviour.AcceptOnMatch, value, ignoreCase)
{
}
@@ -31,7 +37,8 @@ namespace WireMock.Matchers
/// Initializes a new instance of the <see cref="JsonMatcher"/> class.
/// </summary>
/// <param name="value">The object value to check for equality.</param>
public JsonMatcher([NotNull] object value) : this(MatchBehaviour.AcceptOnMatch, value)
/// <param name="ignoreCase">Ignore the case from the PropertyName and PropertyValue (string only).</param>
public JsonMatcher([NotNull] object value, bool ignoreCase = false) : this(MatchBehaviour.AcceptOnMatch, value, ignoreCase)
{
}
@@ -40,12 +47,14 @@ namespace WireMock.Matchers
/// </summary>
/// <param name="matchBehaviour">The match behaviour.</param>
/// <param name="value">The string value to check for equality.</param>
public JsonMatcher(MatchBehaviour matchBehaviour, [NotNull] string value)
/// <param name="ignoreCase">Ignore the case from the PropertyName and PropertyValue (string only).</param>
public JsonMatcher(MatchBehaviour matchBehaviour, [NotNull] string value, bool ignoreCase = false)
{
Check.NotNull(value, nameof(value));
MatchBehaviour = matchBehaviour;
Value = value;
IgnoreCase = ignoreCase;
}
/// <summary>
@@ -53,12 +62,14 @@ namespace WireMock.Matchers
/// </summary>
/// <param name="matchBehaviour">The match behaviour.</param>
/// <param name="value">The object value to check for equality.</param>
public JsonMatcher(MatchBehaviour matchBehaviour, [NotNull] object value)
/// <param name="ignoreCase">Ignore the case from the PropertyName and PropertyValue (string only).</param>
public JsonMatcher(MatchBehaviour matchBehaviour, [NotNull] object value, bool ignoreCase = false)
{
Check.NotNull(value, nameof(value));
MatchBehaviour = matchBehaviour;
Value = value;
IgnoreCase = ignoreCase;
}
/// <inheritdoc cref="IObjectMatcher.IsMatch"/>
@@ -83,7 +94,7 @@ namespace WireMock.Matchers
break;
case string stringValue:
jtokenValue = JToken.Parse(stringValue);
jtokenValue = JsonUtils.Parse(stringValue);
break;
default:
@@ -91,7 +102,7 @@ namespace WireMock.Matchers
break;
}
match = JToken.DeepEquals(jtokenValue, jtokenInput);
match = DeepEquals(jtokenValue, jtokenInput);
}
catch (JsonException)
{
@@ -101,5 +112,53 @@ namespace WireMock.Matchers
return MatchBehaviourHelper.Convert(MatchBehaviour, MatchScores.ToScore(match));
}
private bool DeepEquals(JToken value, JToken input)
{
if (!IgnoreCase)
{
return JToken.DeepEquals(value, input);
}
JToken renamedValue = Rename(value);
JToken renamedInput = Rename(input);
return JToken.DeepEquals(renamedValue, renamedInput);
}
private static string ToUpper(string input)
{
return input?.ToUpperInvariant();
}
// https://stackoverflow.com/questions/11679804/json-net-rename-properties
private static JToken Rename(JToken json)
{
if (json is JProperty property)
{
JToken propertyValue = property.Value;
if (propertyValue.Type == JTokenType.String)
{
string stringValue = propertyValue.Value<string>();
propertyValue = ToUpper(stringValue);
}
return new JProperty(ToUpper(property.Name), Rename(propertyValue));
}
if (json is JArray array)
{
var renamedValues = array.Select(Rename);
return new JArray(renamedValues);
}
if (json is JObject obj)
{
var renamedProperties = obj.Properties().Select(Rename);
return new JObject(renamedProperties);
}
return json;
}
}
}

View File

@@ -9,8 +9,9 @@ namespace WireMock.Matchers
/// <summary>
/// System.Linq.Dynamic.Core Expression Matcher
/// </summary>
/// <inheritdoc cref="IObjectMatcher"/>
/// <inheritdoc cref="IStringMatcher"/>
public class LinqMatcher : IStringMatcher
public class LinqMatcher : IObjectMatcher, IStringMatcher
{
private readonly string[] _patterns;
@@ -117,7 +118,6 @@ namespace WireMock.Matchers
}
return MatchBehaviourHelper.Convert(MatchBehaviour, match);
}
/// <inheritdoc cref="IStringMatcher.GetPatterns"/>

View File

@@ -71,7 +71,7 @@ namespace WireMock.Matchers
}
/// <inheritdoc cref="IStringMatcher.IsMatch"/>
public double IsMatch(string input)
public virtual double IsMatch(string input)
{
double match = MatchScores.Mismatch;
if (input != null)

View File

@@ -0,0 +1,11 @@
using System;
namespace WireMock.Matchers.Request
{
public class MatchDetail
{
public Type MatcherType { get; set; }
public double Score { get; set; }
}
}

View File

@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace WireMock.Matchers.Request
{
@@ -14,7 +15,7 @@ namespace WireMock.Matchers.Request
/// <value>
/// The match-score.
/// </value>
public double TotalScore { get; private set; }
public double TotalScore => MatchDetails.Sum(md => md.Score);
/// <summary>
/// Gets or sets the total number of matches.
@@ -22,7 +23,7 @@ namespace WireMock.Matchers.Request
/// <value>
/// The total number of matches.
/// </value>
public int TotalNumber { get; private set; }
public int TotalNumber => MatchDetails.Count;
/// <summary>
/// Gets or sets a value indicating whether this instance is perfect match.
@@ -43,12 +44,7 @@ namespace WireMock.Matchers.Request
/// <summary>
/// Gets the match details.
/// </summary>
public IList<KeyValuePair<Type, double>> MatchDetails { get; }
/// <summary>
/// Initializes a new instance of the <see cref="RequestMatchResult"/> class.
/// </summary>
public RequestMatchResult() => MatchDetails = new List<KeyValuePair<Type, double>>();
public IList<MatchDetail> MatchDetails { get; } = new List<MatchDetail>();
/// <summary>
/// Adds the score.
@@ -58,9 +54,7 @@ namespace WireMock.Matchers.Request
/// <returns>The score.</returns>
public double AddScore(Type matcherType, double score)
{
TotalScore += score;
TotalNumber++;
MatchDetails.Add(new KeyValuePair<Type, double>(matcherType, score));
MatchDetails.Add(new MatchDetail { MatcherType = matcherType, Score = score });
return score;
}
@@ -76,7 +70,10 @@ namespace WireMock.Matchers.Request
{
var compareObj = (RequestMatchResult)obj;
return compareObj.AverageTotalScore.CompareTo(AverageTotalScore);
int averageTotalScoreResult = compareObj.AverageTotalScore.CompareTo(AverageTotalScore);
// In case the score is equal, prefer the one with the most matchers.
return averageTotalScoreResult == 0 ? compareObj.TotalNumber.CompareTo(TotalNumber) : averageTotalScoreResult;
}
}
}

View File

@@ -145,17 +145,17 @@ namespace WireMock.Matchers.Request
if (Func != null)
{
return MatchScores.ToScore(requestMessage?.BodyData?.DetectedBodyType == BodyType.String && Func(requestMessage.BodyData.BodyAsString));
return MatchScores.ToScore(Func(requestMessage?.BodyData?.BodyAsString));
}
if (JsonFunc != null)
{
return MatchScores.ToScore(requestMessage?.BodyData?.DetectedBodyType == BodyType.Json && JsonFunc(requestMessage.BodyData.BodyAsJson));
return MatchScores.ToScore(JsonFunc(requestMessage?.BodyData?.BodyAsJson));
}
if (DataFunc != null)
{
return MatchScores.ToScore(requestMessage?.BodyData?.DetectedBodyType == BodyType.Bytes && DataFunc(requestMessage.BodyData.BodyAsBytes));
return MatchScores.ToScore(DataFunc(requestMessage?.BodyData?.BodyAsBytes));
}
return MatchScores.Mismatch;

View File

@@ -1,9 +1,9 @@
using System;
using System.Collections.Concurrent;
using System.Collections.ObjectModel;
using WireMock.Handlers;
using WireMock.Logging;
using WireMock.Matchers;
using WireMock.Util;
#if !USE_ASPNETCORE
using Owin;
#else
@@ -20,13 +20,13 @@ namespace WireMock.Owin
IStringMatcher AuthorizationMatcher { get; set; }
bool AllowPartialMapping { get; set; }
bool? AllowPartialMapping { get; set; }
ConcurrentDictionary<Guid, IMapping> Mappings { get; }
ConcurrentDictionary<string, ScenarioState> Scenarios { get; }
ObservableCollection<LogEntry> LogEntries { get; }
ConcurrentObservableCollection<LogEntry> LogEntries { get; }
int? RequestLogExpirationDuration { get; set; }
@@ -37,5 +37,7 @@ namespace WireMock.Owin
Action<IAppBuilder> PostWireMockMiddlewareInit { get; set; }
IFileSystemHandler FileSystemHandler { get; set; }
bool? AllowBodyForAllHttpMethods { get; set; }
}
}

View File

@@ -16,7 +16,8 @@ namespace WireMock.Owin.Mappers
/// MapAsync IRequest to RequestMessage
/// </summary>
/// <param name="request">The OwinRequest/HttpRequest</param>
/// <param name="options">The WireMockMiddlewareOptions</param>
/// <returns>RequestMessage</returns>
Task<RequestMessage> MapAsync(IRequest request);
Task<RequestMessage> MapAsync(IRequest request, IWireMockMiddlewareOptions options);
}
}

View File

@@ -20,7 +20,7 @@ namespace WireMock.Owin.Mappers
internal class OwinRequestMapper : IOwinRequestMapper
{
/// <inheritdoc cref="IOwinRequestMapper.MapAsync"/>
public async Task<RequestMessage> MapAsync(IRequest request)
public async Task<RequestMessage> MapAsync(IRequest request, IWireMockMiddlewareOptions options)
{
(UrlDetails urldetails, string clientIP) = ParseRequest(request);
@@ -47,7 +47,7 @@ namespace WireMock.Owin.Mappers
}
BodyData body = null;
if (request.Body != null && BodyParser.ShouldParseBody(method))
if (request.Body != null && BodyParser.ShouldParseBody(method, options.AllowBodyForAllHttpMethods == true))
{
body = await BodyParser.Parse(request.Body, request.ContentType);
}

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

@@ -37,7 +37,7 @@ namespace WireMock.Owin
}
}
if (_options.AllowPartialMapping)
if (_options.AllowPartialMapping == true)
{
var partialMappings = mappings
.Where(pm => (pm.Mapping.IsAdminInterface && pm.RequestMatchResult.IsPerfectMatch) || !pm.Mapping.IsAdminInterface)
@@ -49,8 +49,9 @@ namespace WireMock.Owin
}
return mappings
.OrderBy(m => m.Mapping.Priority)
.FirstOrDefault(m => m.RequestMatchResult.IsPerfectMatch);
.Where(m => m.RequestMatchResult.IsPerfectMatch)
.OrderBy(m => m.Mapping.Priority).ThenBy(m => m.RequestMatchResult)
.FirstOrDefault();
}
}
}

View File

@@ -69,7 +69,7 @@ namespace WireMock.Owin
private async Task InvokeInternal(IContext ctx)
{
var request = await _requestMapper.MapAsync(ctx.Request);
var request = await _requestMapper.MapAsync(ctx.Request, _options);
bool logRequest = false;
ResponseMessage response = null;
@@ -99,7 +99,7 @@ namespace WireMock.Owin
return;
}
logRequest = !targetMapping.IsAdminInterface;
logRequest = targetMapping.LogMapping;
if (targetMapping.IsAdminInterface && _options.AuthorizationMatcher != null)
{
@@ -162,10 +162,10 @@ namespace WireMock.Owin
if (_options.MaxRequestLogCount != null)
{
var amount = _options.LogEntries.Count - _options.MaxRequestLogCount.Value;
for (int i = 0; i < amount; i++)
var logEntries = _options.LogEntries.ToList();
foreach (var logEntry in logEntries.OrderBy(le => le.RequestMessage.DateTime).Take(logEntries.Count - _options.MaxRequestLogCount.Value))
{
_options.LogEntries.RemoveAt(0);
_options.LogEntries.Remove(logEntry);
}
}
@@ -173,13 +173,9 @@ namespace WireMock.Owin
{
var checkTime = DateTime.UtcNow.AddHours(-_options.RequestLogExpirationDuration.Value);
for (var i = _options.LogEntries.Count - 1; i >= 0; i--)
foreach (var logEntry in _options.LogEntries.ToList().Where(le => le.RequestMessage.DateTime < checkTime))
{
var le = _options.LogEntries[i];
if (le.RequestMessage.DateTime <= checkTime)
{
_options.LogEntries.RemoveAt(i);
}
_options.LogEntries.Remove(logEntry);
}
}
}

View File

@@ -21,13 +21,13 @@ namespace WireMock.Owin
public IStringMatcher AuthorizationMatcher { get; set; }
public bool AllowPartialMapping { get; set; }
public bool? AllowPartialMapping { get; set; }
public ConcurrentDictionary<Guid, IMapping> Mappings { get; } = new ConcurrentDictionary<Guid, IMapping>();
public ConcurrentDictionary<string, ScenarioState> Scenarios { get; } = new ConcurrentDictionary<string, ScenarioState>();
public ObservableCollection<LogEntry> LogEntries { get; } = new ConcurrentObservableCollection<LogEntry>();
public ConcurrentObservableCollection<LogEntry> LogEntries { get; } = new ConcurrentObservableCollection<LogEntry>();
public int? RequestLogExpirationDuration { get; set; }
@@ -39,5 +39,8 @@ namespace WireMock.Owin
/// <inheritdoc cref="IWireMockMiddlewareOptions.FileSystemHandler"/>
public IFileSystemHandler FileSystemHandler { get; set; }
/// <inheritdoc cref="IWireMockMiddlewareOptions.AllowBodyForAllHttpMethods"/>
public bool? AllowBodyForAllHttpMethods { get; set; }
}
}

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

@@ -40,7 +40,7 @@ namespace WireMock.RequestBuilders
/// <returns>A List{T}</returns>
public IList<T> GetRequestMessageMatchers<T>() where T : IRequestMatcher
{
return new ReadOnlyCollection<T>(_requestMatchers.Where(rm => rm is T).Cast<T>().ToList());
return new ReadOnlyCollection<T>(_requestMatchers.OfType<T>().ToList());
}
/// <summary>
@@ -50,7 +50,7 @@ namespace WireMock.RequestBuilders
/// <returns>A RequestMatcher</returns>
public T GetRequestMessageMatcher<T>() where T : IRequestMatcher
{
return _requestMatchers.Where(rm => rm is T).Cast<T>().FirstOrDefault();
return _requestMatchers.OfType<T>().FirstOrDefault();
}
/// <inheritdoc cref="IClientIPRequestBuilder.WithClientIP(IStringMatcher[])"/>

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.</param>
/// <returns>A <see cref="IResponseBuilder"/>.</returns>
IResponseBuilder WithFault(FaultType faultType, [CanBeNull] double? percentage = null);
}
}

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

@@ -19,7 +19,7 @@ namespace WireMock.ResponseBuilders
/// <summary>
/// The Response.
/// </summary>
public class Response : IResponseBuilder
public partial class Response : IResponseBuilder
{
private HttpClient _httpClientForProxy;
@@ -375,6 +375,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)
{

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;
@@ -108,14 +115,14 @@ namespace WireMock.Serialization
Response = logResponseModel,
RequestMatchResult = logEntry.RequestMatchResult != null ? new LogRequestMatchModel
{
IsPerfectMatch = logEntry.RequestMatchResult.IsPerfectMatch,
TotalScore = logEntry.RequestMatchResult.TotalScore,
TotalNumber = logEntry.RequestMatchResult.TotalNumber,
IsPerfectMatch = logEntry.RequestMatchResult.IsPerfectMatch,
AverageTotalScore = logEntry.RequestMatchResult.AverageTotalScore,
MatchDetails = logEntry.RequestMatchResult.MatchDetails.Select(x => new
MatchDetails = logEntry.RequestMatchResult.MatchDetails.Select(md => new
{
Name = x.Key.Name.Replace("RequestMessage", string.Empty),
Score = x.Value
Name = md.MatcherType.Name.Replace("RequestMessage", string.Empty),
Score = md.Score
} as object).ToList()
} : null
};

View File

@@ -114,6 +114,7 @@ namespace WireMock.Serialization
mappingModel.Response.UseTransformer = null;
mappingModel.Response.BodyEncoding = null;
mappingModel.Response.ProxyUrl = response.ProxyUrl;
mappingModel.Response.Fault = null;
}
else
{
@@ -161,6 +162,15 @@ 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;

View File

@@ -1,8 +1,8 @@
using JetBrains.Annotations;
using SimMetrics.Net;
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using JetBrains.Annotations;
using SimMetrics.Net;
using WireMock.Admin.Mappings;
using WireMock.Matchers;
using WireMock.Settings;
@@ -36,11 +36,19 @@ namespace WireMock.Serialization
string matcherName = parts[0];
string matcherType = parts.Length > 1 ? parts[1] : null;
string[] stringPatterns = matcher.Patterns != null ? matcher.Patterns.Cast<string>().ToArray() : new[] { matcher.Pattern as string };
string[] stringPatterns = matcher.Patterns != null ? matcher.Patterns.OfType<string>().ToArray() : new[] { matcher.Pattern as string };
MatchBehaviour matchBehaviour = matcher.RejectOnMatch == true ? MatchBehaviour.RejectOnMatch : MatchBehaviour.AcceptOnMatch;
switch (matcherName)
{
case "CSharpCodeMatcher":
if (_settings.AllowCSharpCodeMatcher == true)
{
return new CSharpCodeMatcher(matchBehaviour, stringPatterns);
}
throw new NotSupportedException("It's not allowed to use the 'CSharpCodeMatcher' because FluentMockServerSettings.AllowCSharpCodeMatcher is not set to 'true'.");
case "LinqMatcher":
return new LinqMatcher(matchBehaviour, stringPatterns);
@@ -48,14 +56,13 @@ namespace WireMock.Serialization
return new ExactMatcher(matchBehaviour, stringPatterns);
case "ExactObjectMatcher":
var bytePattern = Convert.FromBase64String(stringPatterns[0]);
return new ExactObjectMatcher(matchBehaviour, bytePattern);
return CreateExactObjectMatcher(matchBehaviour, stringPatterns[0]);
case "RegexMatcher":
return new RegexMatcher(matchBehaviour, stringPatterns, matcher.IgnoreCase == true);
case "JsonMatcher":
return new JsonMatcher(matchBehaviour, matcher.Pattern);
return new JsonMatcher(matchBehaviour, matcher.Pattern, matcher.IgnoreCase == true);
case "JsonPathMatcher":
return new JsonPathMatcher(matchBehaviour, stringPatterns);
@@ -64,11 +71,14 @@ namespace WireMock.Serialization
return new JmesPathMatcher(matchBehaviour, stringPatterns);
case "XPathMatcher":
return new XPathMatcher(matchBehaviour, (string)matcher.Pattern);
return new XPathMatcher(matchBehaviour, stringPatterns);
case "WildcardMatcher":
return new WildcardMatcher(matchBehaviour, stringPatterns, matcher.IgnoreCase == true);
case "ContentTypeMatcher":
return new ContentTypeMatcher(matchBehaviour, stringPatterns, matcher.IgnoreCase == true);
case "SimMetricsMatcher":
SimMetricType type = SimMetricType.Levenstein;
if (!string.IsNullOrEmpty(matcherType) && !Enum.TryParse(matcherType, out type))
@@ -76,13 +86,28 @@ namespace WireMock.Serialization
throw new NotSupportedException($"Matcher '{matcherName}' with Type '{matcherType}' is not supported.");
}
return new SimMetricsMatcher(matchBehaviour, (string)matcher.Pattern, type);
return new SimMetricsMatcher(matchBehaviour, stringPatterns, type);
default:
throw new NotSupportedException($"Matcher '{matcherName}' is not supported.");
}
}
private ExactObjectMatcher CreateExactObjectMatcher(MatchBehaviour matchBehaviour, string stringPattern)
{
byte[] bytePattern;
try
{
bytePattern = Convert.FromBase64String(stringPattern);
}
catch
{
throw new ArgumentException($"Matcher 'ExactObjectMatcher' has invalid pattern. The pattern value '{stringPattern}' is not a Base64String.", nameof(stringPattern));
}
return new ExactObjectMatcher(matchBehaviour, bytePattern);
}
public MatcherModel[] Map([CanBeNull] IEnumerable<IMatcher> matchers)
{
return matchers?.Select(Map).Where(m => m != null).ToArray();

View File

@@ -39,9 +39,11 @@ 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 _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 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})$");
private readonly RegexMatcher _adminRequestsGuidPathMatcher = new RegexMatcher(@"^\/__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 _jsonSerializerSettings = new JsonSerializerSettings
{
@@ -60,19 +62,19 @@ namespace WireMock.Server
{
// __admin/settings
Given(Request.Create().WithPath(AdminSettings).UsingGet()).AtPriority(AdminPriority).RespondWith(new DynamicResponseProvider(SettingsGet));
Given(Request.Create().WithPath(AdminSettings).UsingMethod("PUT", "POST").WithHeader(HttpKnownHeaderNames.ContentType, ContentTypeJson)).AtPriority(AdminPriority).RespondWith(new DynamicResponseProvider(SettingsUpdate));
Given(Request.Create().WithPath(AdminSettings).UsingMethod("PUT", "POST").WithHeader(HttpKnownHeaderNames.ContentType, _adminRequestContentTypeJson)).AtPriority(AdminPriority).RespondWith(new DynamicResponseProvider(SettingsUpdate));
// __admin/mappings
Given(Request.Create().WithPath(AdminMappings).UsingGet()).AtPriority(AdminPriority).RespondWith(new DynamicResponseProvider(MappingsGet));
Given(Request.Create().WithPath(AdminMappings).UsingPost().WithHeader(HttpKnownHeaderNames.ContentType, ContentTypeJson)).AtPriority(AdminPriority).RespondWith(new DynamicResponseProvider(MappingsPost));
Given(Request.Create().WithPath(AdminMappings).UsingPost().WithHeader(HttpKnownHeaderNames.ContentType, _adminRequestContentTypeJson)).AtPriority(AdminPriority).RespondWith(new DynamicResponseProvider(MappingsPost));
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));
Given(Request.Create().WithPath(_adminMappingsGuidPathMatcher).UsingPut().WithHeader(HttpKnownHeaderNames.ContentType, ContentTypeJson)).AtPriority(AdminPriority).RespondWith(new DynamicResponseProvider(MappingPut));
Given(Request.Create().WithPath(_adminMappingsGuidPathMatcher).UsingPut().WithHeader(HttpKnownHeaderNames.ContentType, _adminRequestContentTypeJson)).AtPriority(AdminPriority).RespondWith(new DynamicResponseProvider(MappingPut));
Given(Request.Create().WithPath(_adminMappingsGuidPathMatcher).UsingDelete()).AtPriority(AdminPriority).RespondWith(new DynamicResponseProvider(MappingDelete));
// __admin/mappings/save
@@ -140,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);
@@ -172,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);
@@ -266,7 +273,8 @@ namespace WireMock.Server
var responseMessage = await HttpClientHelper.SendAsync(_httpClientForProxy, requestMessage, proxyUriWithRequestPathAndQuery.AbsoluteUri);
if (settings.ProxyAndRecordSettings.SaveMapping || settings.ProxyAndRecordSettings.SaveMappingToFile)
if (HttpStatusRangeParser.IsMatch(settings.ProxyAndRecordSettings.SaveMappingForStatusCodePattern, responseMessage.StatusCode) &&
(settings.ProxyAndRecordSettings.SaveMapping || settings.ProxyAndRecordSettings.SaveMappingToFile))
{
var mapping = ToMapping(requestMessage, responseMessage, settings.ProxyAndRecordSettings.BlackListedHeaders ?? new string[] { }, settings.ProxyAndRecordSettings.BlackListedCookies ?? new string[] { });
@@ -337,7 +345,8 @@ namespace WireMock.Server
AllowPartialMapping = _options.AllowPartialMapping,
MaxRequestLogCount = _options.MaxRequestLogCount,
RequestLogExpirationDuration = _options.RequestLogExpirationDuration,
GlobalProcessingDelay = (int?)_options.RequestProcessingDelay?.TotalMilliseconds
GlobalProcessingDelay = (int?)_options.RequestProcessingDelay?.TotalMilliseconds,
AllowBodyForAllHttpMethods = _options.AllowBodyForAllHttpMethods
};
return ToJson(model);
@@ -359,6 +368,11 @@ namespace WireMock.Server
_options.RequestProcessingDelay = TimeSpan.FromMilliseconds(settings.GlobalProcessingDelay.Value);
}
if (settings.AllowBodyForAllHttpMethods != null)
{
_options.AllowBodyForAllHttpMethods = settings.AllowBodyForAllHttpMethods.Value;
}
return ResponseMessageBuilder.Create("Settings updated");
}
#endregion Settings
@@ -543,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}
@@ -653,7 +685,7 @@ namespace WireMock.Server
var clientIPModel = JsonUtils.ParseJTokenToObject<ClientIPModel>(requestModel.ClientIP);
if (clientIPModel?.Matchers != null)
{
requestBuilder = requestBuilder.WithPath(clientIPModel.Matchers.Select(_matcherMapper.Map).Cast<IStringMatcher>().ToArray());
requestBuilder = requestBuilder.WithPath(clientIPModel.Matchers.Select(_matcherMapper.Map).OfType<IStringMatcher>().ToArray());
}
}
}
@@ -671,7 +703,7 @@ namespace WireMock.Server
var pathModel = JsonUtils.ParseJTokenToObject<PathModel>(requestModel.Path);
if (pathModel?.Matchers != null)
{
requestBuilder = requestBuilder.WithPath(pathModel.Matchers.Select(_matcherMapper.Map).Cast<IStringMatcher>().ToArray());
requestBuilder = requestBuilder.WithPath(pathModel.Matchers.Select(_matcherMapper.Map).OfType<IStringMatcher>().ToArray());
pathOrUrlmatchersValid = true;
}
}
@@ -688,7 +720,7 @@ namespace WireMock.Server
var urlModel = JsonUtils.ParseJTokenToObject<UrlModel>(requestModel.Url);
if (urlModel?.Matchers != null)
{
requestBuilder = requestBuilder.WithUrl(urlModel.Matchers.Select(_matcherMapper.Map).Cast<IStringMatcher>().ToArray());
requestBuilder = requestBuilder.WithUrl(urlModel.Matchers.Select(_matcherMapper.Map).OfType<IStringMatcher>().ToArray());
pathOrUrlmatchersValid = true;
}
}
@@ -709,7 +741,7 @@ namespace WireMock.Server
{
foreach (var headerModel in requestModel.Headers.Where(h => h.Matchers != null))
{
requestBuilder = requestBuilder.WithHeader(headerModel.Name, headerModel.Matchers.Select(_matcherMapper.Map).Cast<IStringMatcher>().ToArray());
requestBuilder = requestBuilder.WithHeader(headerModel.Name, headerModel.Matchers.Select(_matcherMapper.Map).OfType<IStringMatcher>().ToArray());
}
}
@@ -717,7 +749,7 @@ namespace WireMock.Server
{
foreach (var cookieModel in requestModel.Cookies.Where(c => c.Matchers != null))
{
requestBuilder = requestBuilder.WithCookie(cookieModel.Name, cookieModel.Matchers.Select(_matcherMapper.Map).Cast<IStringMatcher>().ToArray());
requestBuilder = requestBuilder.WithCookie(cookieModel.Name, cookieModel.Matchers.Select(_matcherMapper.Map).OfType<IStringMatcher>().ToArray());
}
}
@@ -726,7 +758,7 @@ namespace WireMock.Server
foreach (var paramModel in requestModel.Params.Where(c => c.Matchers != null))
{
bool ignoreCase = paramModel?.IgnoreCase ?? false;
requestBuilder = requestBuilder.WithParam(paramModel.Name, ignoreCase, paramModel.Matchers.Select(_matcherMapper.Map).Cast<IStringMatcher>().ToArray());
requestBuilder = requestBuilder.WithParam(paramModel.Name, ignoreCase, paramModel.Matchers.Select(_matcherMapper.Map).OfType<IStringMatcher>().ToArray());
}
}
@@ -812,6 +844,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

@@ -3,11 +3,11 @@ using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Linq;
using JetBrains.Annotations;
using WireMock.Logging;
using WireMock.Matchers.Request;
using System.Linq;
using WireMock.Matchers;
using WireMock.Matchers.Request;
namespace WireMock.Server
{
@@ -19,7 +19,21 @@ namespace WireMock.Server
[PublicAPI]
public event NotifyCollectionChangedEventHandler LogEntriesChanged
{
add => _options.LogEntries.CollectionChanged += value;
add
{
_options.LogEntries.CollectionChanged += (sender, eventRecordArgs) =>
{
try
{
value(sender, eventRecordArgs);
}
catch (Exception exception)
{
_options.Logger.Error("Error calling the LogEntriesChanged event handler: {0}", exception.Message);
}
};
}
remove => _options.LogEntries.CollectionChanged -= value;
}
@@ -27,7 +41,7 @@ namespace WireMock.Server
/// Gets the request logs.
/// </summary>
[PublicAPI]
public IEnumerable<LogEntry> LogEntries => new ReadOnlyCollection<LogEntry>(_options.LogEntries.ToArray());
public IEnumerable<LogEntry> LogEntries => new ReadOnlyCollection<LogEntry>(_options.LogEntries.ToList());
/// <summary>
/// The search log-entries based on matchers.
@@ -39,7 +53,7 @@ namespace WireMock.Server
{
var results = new Dictionary<LogEntry, RequestMatchResult>();
foreach (var log in _options.LogEntries)
foreach (var log in _options.LogEntries.ToList())
{
var requestMatchResult = new RequestMatchResult();
foreach (var matcher in matchers)
@@ -73,7 +87,7 @@ namespace WireMock.Server
public bool DeleteLogEntry(Guid guid)
{
// Check a logentry exists with the same GUID, if so, remove it.
var existing = _options.LogEntries.FirstOrDefault(m => m.Guid == guid);
var existing = _options.LogEntries.ToList().FirstOrDefault(m => m.Guid == guid);
if (existing != null)
{
_options.LogEntries.Remove(existing);

View File

@@ -1,11 +1,11 @@
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 JetBrains.Annotations;
using Newtonsoft.Json;
using WireMock.Admin.Mappings;
using WireMock.Exceptions;
using WireMock.Handlers;
@@ -256,6 +256,12 @@ namespace WireMock.Server
}
}
if (settings.AllowBodyForAllHttpMethods == true)
{
_options.AllowBodyForAllHttpMethods = _settings.AllowBodyForAllHttpMethods;
_settings.Logger.Info("AllowBodyForAllHttpMethods is set to {0}", _settings.AllowBodyForAllHttpMethods == true);
}
if (settings.AllowPartialMapping == true)
{
AllowPartialMapping();

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; }
@@ -89,5 +93,13 @@ namespace WireMock.Settings
[PublicAPI]
[JsonIgnore]
public Action<IHandlebars, IFileSystemHandler> HandlebarsRegistrationCallback { get; set; }
/// <inheritdoc cref="IFluentMockServerSettings.AllowCSharpCodeMatcher"/>
[PublicAPI]
public bool? AllowCSharpCodeMatcher { get; set; }
/// <inheritdoc cref="IFluentMockServerSettings.AllowBodyForAllHttpMethods"/>
[PublicAPI]
public bool? AllowBodyForAllHttpMethods { get; set; }
}
}

View File

@@ -1,6 +1,6 @@
using HandlebarsDotNet;
using System;
using HandlebarsDotNet;
using JetBrains.Annotations;
using System;
using WireMock.Handlers;
using WireMock.Logging;
@@ -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>
@@ -115,9 +121,21 @@ namespace WireMock.Settings
IFileSystemHandler FileSystemHandler { get; set; }
/// <summary>
/// Action which can be used to add additional is Handlebar registrations. [Optional]
/// Action which can be used to add additional Handlebars registrations. [Optional]
/// </summary>
[PublicAPI]
Action<IHandlebars, IFileSystemHandler> HandlebarsRegistrationCallback { get; set; }
/// <summary>
/// Allow the usage of CSharpCodeMatcher (default is not allowed).
/// </summary>
[PublicAPI]
bool? AllowCSharpCodeMatcher { get; set; }
/// <summary>
/// Allow a Body for all HTTP Methods. (default set to false).
/// </summary>
[PublicAPI]
bool? AllowBodyForAllHttpMethods { get; set; }
}
}

View File

@@ -1,4 +1,6 @@
namespace WireMock.Settings
using JetBrains.Annotations;
namespace WireMock.Settings
{
/// <summary>
/// IProxyAndRecordSettings
@@ -16,7 +18,14 @@
bool SaveMapping { get; set; }
/// <summary>
/// Save the mapping for each request/response to also file. (Note that SaveMapping must also be set to true.)
/// Only save request/response to the internal Mappings if the status code is included in this pattern. (Note that SaveMapping must also be set to true.)
/// The pattern can contain a single value like "200", but also ranges like "2xx", "100,300,600" or "100-299,6xx" are supported.
/// </summary>
[CanBeNull]
string SaveMappingForStatusCodePattern { get; set; }
/// <summary>
/// Save the mapping for each request/response also to a file. (Note that SaveMapping must also be set to true.)
/// </summary>
bool SaveMappingToFile { get; set; }

View File

@@ -15,6 +15,10 @@ namespace WireMock.Settings
[PublicAPI]
public bool SaveMapping { get; set; } = true;
/// <inheritdoc cref="IProxyAndRecordSettings.SaveMappingForStatusCodePattern"/>
[PublicAPI]
public string SaveMappingForStatusCodePattern { get; set; } = "*";
/// <inheritdoc cref="IProxyAndRecordSettings.SaveMappingToFile"/>
[PublicAPI]
public bool SaveMappingToFile { get; set; } = true;

View File

@@ -1,8 +1,9 @@
using System;
using System.Linq;
using HandlebarsDotNet;
using HandlebarsDotNet;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Linq;
using WireMock.Util;
using WireMock.Validation;
namespace WireMock.Transformers
@@ -56,7 +57,7 @@ namespace WireMock.Transformers
switch (arguments[0])
{
case string jsonAsString:
valueToProcess = JObject.Parse(jsonAsString);
valueToProcess = JsonUtils.Parse(jsonAsString);
break;
case JObject jsonAsJObject:
@@ -67,7 +68,7 @@ namespace WireMock.Transformers
throw new NotSupportedException($"The value '{arguments[0]}' with type '{arguments[0]?.GetType()}' cannot be used in Handlebars JsonPath.");
}
return (valueToProcess, (string) arguments[1]);
return (valueToProcess, (string)arguments[1]);
}
}
}

View File

@@ -45,6 +45,9 @@ 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)

View File

@@ -1,33 +1,32 @@
using JetBrains.Annotations;
using MimeKit;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using WireMock.Matchers;
using WireMock.Validation;
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.
GET - No defined body semantics.
PUT - Body supported.
POST - Body supported.
DELETE - No defined body semantics.
TRACE - Body not supported.
OPTIONS - Body supported but no semantics on usage (maybe in the future).
CONNECT - No defined body semantics
PATCH - Body supported.
*/
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Newtonsoft.Json;
using WireMock.Matchers;
using WireMock.Validation;
namespace WireMock.Util
{
internal static class BodyParser
{
private static readonly Encoding DefaultEncoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true);
private static readonly Encoding[] SupportedBodyAsStringEncodingForMultipart = { DefaultEncoding, Encoding.ASCII };
/*
HEAD - No defined body semantics.
GET - No defined body semantics.
PUT - Body supported.
POST - Body supported.
DELETE - No defined body semantics.
TRACE - Body not supported.
OPTIONS - Body supported but no semantics on usage (maybe in the future).
CONNECT - No defined body semantics
PATCH - Body supported.
*/
private static readonly IDictionary<string, bool> BodyAllowedForMethods = new Dictionary<string, bool>
{
{ "HEAD", false },
@@ -39,134 +38,138 @@ namespace WireMock.Util
{ "OPTIONS", true },
{ "CONNECT", false },
{ "PATCH", true }
};
private static readonly IStringMatcher[] MultipartContentTypesMatchers = {
new WildcardMatcher("multipart/*", true)
};
private static readonly IStringMatcher[] JsonContentTypesMatchers = {
new WildcardMatcher("application/json", true),
new WildcardMatcher("application/vnd.*+json", true)
};
private static readonly IStringMatcher[] TextContentTypeMatchers =
{
new WildcardMatcher("text/*", true),
new RegexMatcher("^application\\/(java|type)script$", true),
new WildcardMatcher("application/*xml", true),
new WildcardMatcher("application/x-www-form-urlencoded", true)
};
public static bool ParseBodyAsIsValid([CanBeNull] string parseBodyAs)
{
return Enum.TryParse(parseBodyAs, out BodyType _);
}
public static bool ShouldParseBody([CanBeNull] string method)
{
if (String.IsNullOrEmpty(method))
};
private static readonly IStringMatcher[] MultipartContentTypesMatchers = {
new WildcardMatcher("multipart/*", true)
};
private static readonly IStringMatcher[] JsonContentTypesMatchers = {
new WildcardMatcher("application/json", true),
new WildcardMatcher("application/vnd.*+json", true)
};
private static readonly IStringMatcher[] TextContentTypeMatchers =
{
new WildcardMatcher("text/*", true),
new RegexMatcher("^application\\/(java|type)script$", true),
new WildcardMatcher("application/*xml", true),
new WildcardMatcher("application/x-www-form-urlencoded", true)
};
private static readonly JsonSerializerSettings JsonSerializerSettings = new JsonSerializerSettings { DateParseHandling = DateParseHandling.None };
public static bool ShouldParseBody([CanBeNull] string httpMethod, bool allowBodyForAllHttpMethods)
{
if (string.IsNullOrEmpty(httpMethod))
{
return false;
}
if (BodyAllowedForMethods.TryGetValue(method.ToUpper(), out var allowed))
}
if (allowBodyForAllHttpMethods)
{
return true;
}
if (BodyAllowedForMethods.TryGetValue(httpMethod.ToUpper(), out bool 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)
{
if (string.IsNullOrEmpty(contentTypeValue) || !ContentType.TryParse(contentTypeValue, out ContentType contentType))
{
return BodyType.Bytes;
}
if (TextContentTypeMatchers.Any(matcher => MatchScores.IsPerfect(matcher.IsMatch(contentType.MimeType))))
{
return BodyType.String;
}
if (JsonContentTypesMatchers.Any(matcher => MatchScores.IsPerfect(matcher.IsMatch(contentType.MimeType))))
{
return BodyType.Json;
}
if (MultipartContentTypesMatchers.Any(matcher => MatchScores.IsPerfect(matcher.IsMatch(contentType.MimeType))))
{
return BodyType.MultiPart;
}
return BodyType.Bytes;
}
public static async Task<BodyData> Parse([NotNull] Stream stream, [CanBeNull] string contentType)
{
Check.NotNull(stream, nameof(stream));
var data = new BodyData
{
BodyAsBytes = await ReadBytesAsync(stream),
DetectedBodyType = BodyType.Bytes,
DetectedBodyTypeFromContentType = DetectBodyTypeFromContentType(contentType)
};
// 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;
}
// Try to get the body as String
try
{
data.BodyAsString = DefaultEncoding.GetString(data.BodyAsBytes);
data.Encoding = DefaultEncoding;
data.DetectedBodyType = BodyType.String;
// If string is not null or empty, try to get as Json
if (!string.IsNullOrEmpty(data.BodyAsString))
{
try
{
data.BodyAsJson = JsonConvert.DeserializeObject(data.BodyAsString, new JsonSerializerSettings { Formatting = Formatting.Indented });
data.DetectedBodyType = BodyType.Json;
}
catch
{
// JsonConvert failed, just ignore.
}
}
}
catch
{
// Reading as string failed, just ignore
}
return data;
}
private static async Task<byte[]> ReadBytesAsync(Stream stream)
{
using (var memoryStream = new MemoryStream())
{
await stream.CopyToAsync(memoryStream);
return memoryStream.ToArray();
}
}
}
}
// 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)
{
if (string.IsNullOrEmpty(contentTypeValue) || !MediaTypeHeaderValue.TryParse(contentTypeValue, out MediaTypeHeaderValue contentType))
{
return BodyType.Bytes;
}
if (TextContentTypeMatchers.Any(matcher => MatchScores.IsPerfect(matcher.IsMatch(contentType.MediaType))))
{
return BodyType.String;
}
if (JsonContentTypesMatchers.Any(matcher => MatchScores.IsPerfect(matcher.IsMatch(contentType.MediaType))))
{
return BodyType.Json;
}
if (MultipartContentTypesMatchers.Any(matcher => MatchScores.IsPerfect(matcher.IsMatch(contentType.MediaType))))
{
return BodyType.MultiPart;
}
return BodyType.Bytes;
}
public static async Task<BodyData> Parse([NotNull] Stream stream, [CanBeNull] string contentType)
{
Check.NotNull(stream, nameof(stream));
var data = new BodyData
{
BodyAsBytes = await ReadBytesAsync(stream),
DetectedBodyType = BodyType.Bytes,
DetectedBodyTypeFromContentType = DetectBodyTypeFromContentType(contentType)
};
// 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;
}
// Try to get the body as String
try
{
data.BodyAsString = DefaultEncoding.GetString(data.BodyAsBytes);
data.Encoding = DefaultEncoding;
data.DetectedBodyType = BodyType.String;
// If string is not null or empty, try to deserialize the string to a JObject
if (!string.IsNullOrEmpty(data.BodyAsString))
{
try
{
data.BodyAsJson = JsonConvert.DeserializeObject(data.BodyAsString, JsonSerializerSettings);
data.DetectedBodyType = BodyType.Json;
}
catch
{
// JsonConvert failed, just ignore.
}
}
}
catch
{
// Reading as string failed, just ignore
}
return data;
}
private static async Task<byte[]> ReadBytesAsync(Stream stream)
{
using (var memoryStream = new MemoryStream())
{
await stream.CopyToAsync(memoryStream);
return memoryStream.ToArray();
}
}
}
}

View File

@@ -1,14 +1,15 @@
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
namespace WireMock.Util
{
/// <summary>
/// A special Collection that overrides methods of <see cref="ObservableCollection{T}"/> to make them thread safe
/// A special Collection that overrides methods of <see cref="ObservableCollection{T}"/> to make them thread safe.
/// </summary>
/// <typeparam name="T">The type of elements in the collection.</typeparam>
/// <inheritdoc cref="ObservableCollection{T}" />
public class ConcurrentObservableCollection<T> : ObservableCollection<T>
internal class ConcurrentObservableCollection<T> : ObservableCollection<T>
{
private readonly object _lockObject = new object();
@@ -73,5 +74,13 @@ namespace WireMock.Util
base.MoveItem(oldIndex, newIndex);
}
}
public List<T> ToList()
{
lock (_lockObject)
{
return Items.ToList();
}
}
}
}

View File

@@ -0,0 +1,71 @@
using System;
using System.Linq;
using System.Net;
using System.Text.RegularExpressions;
namespace WireMock.Util
{
/// <summary>
/// Based on https://github.com/tmenier/Flurl/blob/129565361e135e639f1d44a35a78aea4302ac6ca/src/Flurl.Http/HttpStatusRangeParser.cs
/// </summary>
internal static class HttpStatusRangeParser
{
/// <summary>
/// Determines whether the specified pattern is match.
/// </summary>
/// <param name="pattern">The pattern. (Can be null, in that case it's allowed.)</param>
/// <param name="httpStatusCode">The value.</param>
/// <exception cref="ArgumentException"><paramref name="pattern"/> is invalid.</exception>
public static bool IsMatch(string pattern, HttpStatusCode httpStatusCode)
{
return IsMatch(pattern, (int)httpStatusCode);
}
/// <summary>
/// Determines whether the specified pattern is match.
/// </summary>
/// <param name="pattern">The pattern. (Can be null, in that case it's allowed.)</param>
/// <param name="httpStatusCode">The value.</param>
/// <exception cref="ArgumentException"><paramref name="pattern"/> is invalid.</exception>
public static bool IsMatch(string pattern, int httpStatusCode)
{
if (pattern == null)
{
return true;
}
foreach (var range in pattern.Split(',').Select(p => p.Trim()))
{
switch (range)
{
case "":
continue;
case "*":
return true; // special case - allow everything
}
string[] bounds = range.Split('-');
int lower = 0;
int upper = 0;
bool valid =
bounds.Length <= 2 &&
int.TryParse(Regex.Replace(bounds.First().Trim(), "[*xX]", "0"), out lower) &&
int.TryParse(Regex.Replace(bounds.Last().Trim(), "[*xX]", "9"), out upper);
if (!valid)
{
throw new ArgumentException($"Invalid range pattern: \"{pattern}\". Examples of allowed patterns: \"400\", \"4xx\", \"300,400-403\", \"*\".");
}
if (httpStatusCode >= lower && httpStatusCode <= upper)
{
return true;
}
}
return false;
}
}
}

View File

@@ -1,4 +1,5 @@
using Newtonsoft.Json.Linq;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -8,6 +9,22 @@ namespace WireMock.Util
{
internal static class JsonUtils
{
private static readonly JsonSerializerSettings JsonSerializerSettings = new JsonSerializerSettings
{
DateParseHandling = DateParseHandling.None
};
/// <summary>
/// Load a Newtonsoft.Json.Linq.JObject from a string that contains JSON.
/// 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)
{
return JsonConvert.DeserializeObject<JObject>(json, JsonSerializerSettings);
}
public static T ParseJTokenToObject<T>(object value)
{
switch (value)

View File

@@ -3,6 +3,7 @@
<Description>Lightweight Http Mocking Server for .Net, inspired by WireMock from the Java landscape.</Description>
<AssemblyTitle>WireMock.Net</AssemblyTitle>
<Authors>Stef Heyenrath</Authors>
<!--<TargetFrameworks>net451;net452;net46;net461;netstandard1.3;netstandard2.0;netcoreapp2.1</TargetFrameworks>-->
<TargetFrameworks>net451;net452;net46;net461;netstandard1.3;netstandard2.0</TargetFrameworks>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<AssemblyName>WireMock.Net</AssemblyName>
@@ -11,14 +12,14 @@
<PackageReleaseNotes>See CHANGELOG.md</PackageReleaseNotes>
<PackageIconUrl>https://raw.githubusercontent.com/WireMock-Net/WireMock.Net/master/WireMock.Net-Logo.png</PackageIconUrl>
<PackageProjectUrl>https://github.com/WireMock-Net/WireMock.Net</PackageProjectUrl>
<PackageLicenseUrl>https://raw.githubusercontent.com/WireMock-Net/WireMock.Net/master/LICENSE</PackageLicenseUrl>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<RepositoryType>git</RepositoryType>
<RepositoryUrl>https://github.com/WireMock-Net/WireMock.Net</RepositoryUrl>
<ApplicationIcon>../../WireMock.Net-Logo.ico</ApplicationIcon>
<RootNamespace>WireMock</RootNamespace>
<DebugType>full</DebugType>
<!--<DebugType>full</DebugType>
<IncludeSource>True</IncludeSource>
<IncludeSymbols>True</IncludeSymbols>
<IncludeSymbols>True</IncludeSymbols>-->
<ProjectGuid>{D3804228-91F4-4502-9595-39584E5A01AD}</ProjectGuid>
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<AllowedOutputExtensionsInPackageBuildOutputFolder>$(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb</AllowedOutputExtensionsInPackageBuildOutputFolder>
@@ -31,6 +32,13 @@
<PublicSign Condition=" '$(OS)' != 'Windows_NT' ">true</PublicSign>
</PropertyGroup>
<!--<ItemGroup>
<None Include="WireMock.Net-Logo.png" Pack="true" PackagePath="../../"/>
</ItemGroup>-->
<!--https://github.com/aspnet/RoslynCodeDomProvider/issues/51-->
<Target Name="CheckIfShouldKillVBCSCompiler" />
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
<PathMap>$(MSBuildProjectDirectory)=/</PathMap>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
@@ -40,30 +48,39 @@
<DefineConstants>NETSTANDARD;USE_ASPNETCORE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(TargetFramework)' == 'netcoreapp2.1' or '$(TargetFramework)' == 'netcoreapp2.2'">
<DefineConstants>USE_ASPNETCORE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(TargetFramework)' == 'net461'">
<DefineConstants>USE_ASPNETCORE;NET46</DefineConstants>
</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>
</PackageReference>
<!--<PackageReference Include="Microsoft.CSharp" Version="4.5.0" />-->
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
<PackageReference Include="SimMetrics.Net" Version="1.0.5" />
<PackageReference Include="RestEase" Version="1.4.7" />
<PackageReference Include="MimeKitLite" Version="2.0.7" />
<PackageReference Include="SonarAnalyzer.CSharp" Version="7.8.0.7320">
<PrivateAssets>all</PrivateAssets>
<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" />-->
@@ -87,6 +104,7 @@
<PackageReference Include="Microsoft.AspNet.WebApi.OwinSelfHost" Version="5.2.6" />
<PackageReference Include="System.ValueTuple" Version="4.5.0" />
<PackageReference Include="Microsoft.CodeDom.Providers.DotNetCompilerPlatform" Version="2.0.1" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net452' ">
@@ -95,6 +113,7 @@
<PackageReference Include="Microsoft.AspNet.WebApi.OwinSelfHost" Version="5.2.6" />
<PackageReference Include="System.ValueTuple" Version="4.5.0" />
<PackageReference Include="Microsoft.CodeDom.Providers.DotNetCompilerPlatform" Version="2.0.1" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net46' ">
@@ -104,10 +123,12 @@
<PackageReference Include="Microsoft.Owin.Hosting" Version="4.0.0" />
<PackageReference Include="System.Net.Http" Version="4.3.3" />
<PackageReference Include="System.ValueTuple" Version="4.5.0" />
<PackageReference Include="CS-Script" Version="3.29.0" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net461' ">
<PackageReference Include="Microsoft.AspNetCore" Version="2.1.2" />
<PackageReference Include="CS-Script" Version="3.29.0" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard1.3' ">
@@ -118,7 +139,8 @@
<PackageReference Include="System.ValueTuple" Version="4.5.0" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard2.0' ">
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard2.0'">
<PackageReference Include="Microsoft.AspNetCore" Version="2.1.4" />
<PackageReference Include="CS-Script.Core" Version="1.1.1" />
</ItemGroup>
</Project>

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

@@ -6,6 +6,7 @@ using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
using FluentAssertions;
using WireMock.Matchers.Request;
using WireMock.RequestBuilders;
using WireMock.ResponseBuilders;
@@ -17,6 +18,35 @@ namespace WireMock.Net.Tests
{
public class FluentMockServerProxyTests
{
[Fact]
public async Task FluentMockServer_Proxy_Should_log_proxied_requests()
{
// Assign
var settings = new FluentMockServerSettings
{
ProxyAndRecordSettings = new ProxyAndRecordSettings
{
Url = "http://www.google.com",
SaveMapping = true,
SaveMappingToFile = false
}
};
var server = FluentMockServer.Start(settings);
// Act
var requestMessage = new HttpRequestMessage
{
Method = HttpMethod.Get,
RequestUri = new Uri(server.Urls[0])
};
var httpClientHandler = new HttpClientHandler { AllowAutoRedirect = false };
await new HttpClient(httpClientHandler).SendAsync(requestMessage);
// Assert
Check.That(server.Mappings).HasSize(2);
Check.That(server.LogEntries).HasSize(1);
}
[Fact]
public async Task FluentMockServer_Proxy_Should_proxy_responses()
{
@@ -39,6 +69,7 @@ namespace WireMock.Net.Tests
// Assert
Check.That(server.Mappings).HasSize(1);
Check.That(server.LogEntries).HasSize(1);
Check.That(content).Contains("google");
}
@@ -308,6 +339,43 @@ namespace WireMock.Net.Tests
Check.That(receivedRequest.Cookies).ContainsPair("name", "value");
}
/// <summary>
/// Send some binary content in a request through the proxy and check that the same content
/// arrived at the target. As example a JPEG/JIFF header is used, which is not representable
/// in UTF8 and breaks if it is not treated as binary content.
/// </summary>
[Fact]
public async Task FluentMockServer_Proxy_Should_preserve_binary_request_content()
{
// arrange
var jpegHeader = new byte[] {0xFF, 0xD8, 0xFF, 0xE0, 0x00, 0x10, 0x4A, 0x46, 0x49, 0x46, 0x00};
var brokenJpegHeader = new byte[]
{0xEF, 0xBF, 0xBD, 0xEF, 0xBF, 0xBD, 0xEF, 0xBF, 0xBD, 0xEF, 0xBF, 0xBD, 0x00, 0x10, 0x4A, 0x46, 0x49, 0x46, 0x00};
bool HasCorrectHeader(byte[] bytes) => bytes.SequenceEqual(jpegHeader);
bool HasBrokenHeader(byte[] bytes) => bytes.SequenceEqual(brokenJpegHeader);
var serverForProxyForwarding = FluentMockServer.Start();
serverForProxyForwarding
.Given(Request.Create().WithBody(HasCorrectHeader))
.RespondWith(Response.Create().WithSuccess());
serverForProxyForwarding
.Given(Request.Create().WithBody(HasBrokenHeader))
.RespondWith(Response.Create().WithStatusCode(HttpStatusCode.InternalServerError));
var server = FluentMockServer.Start();
server
.Given(Request.Create())
.RespondWith(Response.Create().WithProxy(serverForProxyForwarding.Urls[0]));
// act
var response = await new HttpClient().PostAsync(server.Urls[0], new ByteArrayContent(jpegHeader));
// assert
Check.That(response.StatusCode).IsEqualTo(HttpStatusCode.OK);
}
[Fact]
public async Task FluentMockServer_Proxy_Should_set_BodyAsJson_in_proxied_response()
{

View File

@@ -116,7 +116,25 @@ namespace WireMock.Net.Tests
// Assert
var options = server.GetPrivateFieldValue<IWireMockMiddlewareOptions>("_options");
Check.That(options.AllowPartialMapping).IsTrue();
Check.That(options.AllowPartialMapping).Equals(true);
// Verify
_loggerMock.Verify(l => l.Info(It.IsAny<string>(), It.IsAny<bool>()));
}
[Fact]
public void FluentMockServer_FluentMockServerSettings_AllowBodyForAllHttpMethods()
{
// Assign and Act
var server = FluentMockServer.Start(new FluentMockServerSettings
{
Logger = _loggerMock.Object,
AllowBodyForAllHttpMethods = true
});
// Assert
var options = server.GetPrivateFieldValue<IWireMockMiddlewareOptions>("_options");
Check.That(options.AllowBodyForAllHttpMethods).Equals(true);
// Verify
_loggerMock.Verify(l => l.Info(It.IsAny<string>(), It.IsAny<bool>()));

View File

@@ -1,9 +1,10 @@
using NFluent;
using System;
using System;
using System.Diagnostics;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using NFluent;
using WireMock.RequestBuilders;
using WireMock.ResponseBuilders;
using WireMock.Server;
@@ -187,7 +188,7 @@ namespace WireMock.Net.Tests
var server = FluentMockServer.Start();
server
.Given(Request.Create().WithBody(b => true))
.Given(Request.Create().WithBody((byte[] bodyBytes) => bodyBytes != null))
.AtPriority(0)
.RespondWith(Response.Create().WithStatusCode(400));
server
@@ -203,7 +204,7 @@ namespace WireMock.Net.Tests
// Assert
Check.That(response.StatusCode).Equals(HttpStatusCode.OK);
}
[Theory]
[InlineData("POST")]
[InlineData("PUT")]
@@ -233,5 +234,38 @@ namespace WireMock.Net.Tests
// Assert
Check.That(response.StatusCode).Equals(HttpStatusCode.OK);
}
[Theory]
[InlineData("application/json")]
[InlineData("application/json; charset=ascii")]
[InlineData("application/json; charset=utf-8")]
[InlineData("application/json; charset=UTF-8")]
public async Task WireMockServer_Should_AcceptPostMappingsWithContentTypeJsonAndAnyCharset(string contentType)
{
// Arrange
string message = @"{
""request"": {
""method"": ""GET"",
""url"": ""/some/thing""
},
""response"": {
""status"": 200,
""body"": ""Hello world!"",
""headers"": {
""Content-Type"": ""text/plain""
}
}
}";
var stringContent = new StringContent(message);
stringContent.Headers.ContentType = MediaTypeHeaderValue.Parse(contentType);
var server = FluentMockServer.StartWithAdminInterface();
// Act
var response = await new HttpClient().PostAsync($"{server.Urls[0]}/__admin/mappings", stringContent);
// Assert
Check.That(response.StatusCode).Equals(HttpStatusCode.Created);
Check.That(await response.Content.ReadAsStringAsync()).Contains("Mapping added");
}
}
}

View File

@@ -137,7 +137,7 @@ namespace WireMock.Net.Tests.Http
var message = HttpRequestMessageHelper.Create(request, "http://url");
// Assert
Check.That(message.Content.Headers.GetValues("Content-Type")).ContainsExactly("application/xml; charset=utf-8");
Check.That(message.Content.Headers.GetValues("Content-Type")).ContainsExactly("application/xml; charset=UTF-8");
}
[Fact]
@@ -156,7 +156,7 @@ namespace WireMock.Net.Tests.Http
var message = HttpRequestMessageHelper.Create(request, "http://url");
// Assert
Check.That(message.Content.Headers.GetValues("Content-Type")).ContainsExactly("application/xml; charset=us-ascii");
Check.That(message.Content.Headers.GetValues("Content-Type")).ContainsExactly("application/xml; charset=Ascii");
}
}
}

View File

@@ -0,0 +1,39 @@
using System.Net.Http.Headers;
using FluentAssertions;
using WireMock.Http;
using Xunit;
namespace WireMock.Net.Tests.Http
{
public class StringContentHelperTests
{
[Fact]
public void StringContentHelper_Create_WithNullContentType()
{
// Act
var result = StringContentHelper.Create("test", null);
// Assert
result.Headers.ContentType.Should().BeNull();
result.ReadAsStringAsync().Result.Should().Be("test");
}
[Theory]
[InlineData("application/json", "application/json")]
[InlineData("application/soap+xml", "application/soap+xml")]
[InlineData("application/soap+xml;charset=UTF-8", "application/soap+xml; charset=UTF-8")]
[InlineData("application/soap+xml;charset=UTF-8;action=\"http://myCompany.Customer.Contract/ICustomerService/GetSomeConfiguration\"", "application/soap+xml; charset=UTF-8; action=\"http://myCompany.Customer.Contract/ICustomerService/GetSomeConfiguration\"")]
public void StringContentHelper_Create(string test, string expected)
{
// Arrange
var contentType = MediaTypeHeaderValue.Parse(test);
// Act
var result = StringContentHelper.Create("test", contentType);
// Assert
result.Headers.ContentType.ToString().Should().Be(expected);
result.ReadAsStringAsync().Result.Should().Be("test");
}
}
}

View File

@@ -0,0 +1,92 @@
using NFluent;
using WireMock.Matchers;
using Xunit;
namespace WireMock.Net.Tests.Matchers
{
public class CSharpCodeMatcherTests
{
[Fact]
public void CSharpCodeMatcher_For_String_SinglePattern_IsMatch_Positive()
{
// Assign
string input = "x";
// Act
var matcher = new CSharpCodeMatcher("return it == \"x\";");
// Assert
Check.That(matcher.IsMatch(input)).IsEqualTo(1.0d);
}
[Fact]
public void CSharpCodeMatcher_For_String_IsMatch_Negative()
{
// Assign
string input = "y";
// Act
var matcher = new CSharpCodeMatcher("return it == \"x\";");
// Assert
Check.That(matcher.IsMatch(input)).IsEqualTo(0.0d);
}
[Fact]
public void CSharpCodeMatcher_For_String_IsMatch_RejectOnMatch()
{
// Assign
string input = "x";
// Act
var matcher = new CSharpCodeMatcher(MatchBehaviour.RejectOnMatch, "return it == \"x\";");
// Assert
Check.That(matcher.IsMatch(input)).IsEqualTo(0.0d);
}
[Fact]
public void CSharpCodeMatcher_For_Object_IsMatch()
{
// Assign
var input = new
{
Id = 9,
Name = "Test"
};
// Act
var matcher = new CSharpCodeMatcher("return it.Id > 1 && it.Name == \"Test\";");
double match = matcher.IsMatch(input);
// Assert
Assert.Equal(1.0, match);
}
[Fact]
public void CSharpCodeMatcher_GetName()
{
// Assign
var matcher = new CSharpCodeMatcher("x");
// Act
string name = matcher.Name;
// Assert
Check.That(name).Equals("CSharpCodeMatcher");
}
[Fact]
public void CSharpCodeMatcher_GetPatterns()
{
// Assign
var matcher = new CSharpCodeMatcher("x");
// Act
string[] patterns = matcher.GetPatterns();
// Assert
Check.That(patterns).ContainsExactly("x");
}
}
}

View File

@@ -0,0 +1,58 @@
using NFluent;
using WireMock.Matchers;
using Xunit;
namespace WireMock.Net.Tests.Matchers
{
public class ContentTypeMatcherTests
{
[Theory]
[InlineData("application/json")]
[InlineData("application/json; charset=ascii")]
[InlineData("application/json; charset=utf-8")]
[InlineData("application/json; charset=UTF-8")]
public void ContentTypeMatcher_IsMatchWithIgnoreCaseFalse_Positive(string contentType)
{
var matcher = new ContentTypeMatcher("application/json");
Check.That(matcher.IsMatch(contentType)).IsEqualTo(1.0d);
}
[Theory]
[InlineData("application/json")]
[InlineData("application/JSON")]
[InlineData("application/json; CharSet=ascii")]
[InlineData("application/json; charset=utf-8")]
[InlineData("application/json; charset=UTF-8")]
public void ContentTypeMatcher_IsMatchWithIgnoreCaseTrue_Positive(string contentType)
{
var matcher = new ContentTypeMatcher("application/json", true);
Check.That(matcher.IsMatch(contentType)).IsEqualTo(1.0d);
}
[Fact]
public void ContentTypeMatcher_GetName()
{
// Assign
var matcher = new ContentTypeMatcher("x");
// Act
string name = matcher.Name;
// Assert
Check.That(name).Equals("ContentTypeMatcher");
}
[Fact]
public void ContentTypeMatcher_GetPatterns()
{
// Assign
var matcher = new ContentTypeMatcher("x");
// Act
string[] patterns = matcher.GetPatterns();
// Assert
Check.That(patterns).ContainsExactly("x");
}
}
}

View File

@@ -76,7 +76,7 @@ namespace WireMock.Net.Tests.Matchers
}
[Fact]
public void JsonMatcher_IsMatch_JObject1()
public void JsonMatcher_IsMatch_JObject()
{
// Assign
var matcher = new JsonMatcher(new { Id = 1, Name = "Test" });
@@ -94,7 +94,25 @@ namespace WireMock.Net.Tests.Matchers
}
[Fact]
public void JsonMatcher_IsMatch_JObject2()
public void JsonMatcher_IsMatch_WithIgnoreCaseTrue_JObject()
{
// Assign
var matcher = new JsonMatcher(new { id = 1, Name = "test" }, true);
// Act
var jobject = new JObject
{
{ "Id", new JValue(1) },
{ "NaMe", new JValue("Test") }
};
double match = matcher.IsMatch(jobject);
// Assert
Assert.Equal(1.0, match);
}
[Fact]
public void JsonMatcher_IsMatch_JObjectParsed()
{
// Assign
var matcher = new JsonMatcher(new { Id = 1, Name = "Test" });
@@ -107,6 +125,20 @@ namespace WireMock.Net.Tests.Matchers
Assert.Equal(1.0, match);
}
[Fact]
public void JsonMatcher_IsMatch_WithIgnoreCaseTrue_JObjectParsed()
{
// Assign
var matcher = new JsonMatcher(new { Id = 1, Name = "TESt" }, true);
// Act
var jobject = JObject.Parse("{ \"Id\" : 1, \"Name\" : \"Test\" }");
double match = matcher.IsMatch(jobject);
// Assert
Assert.Equal(1.0, match);
}
[Fact]
public void JsonMatcher_IsMatch_JObjectAsString()
{
@@ -125,6 +157,24 @@ namespace WireMock.Net.Tests.Matchers
Assert.Equal(1.0, match);
}
[Fact]
public void JsonMatcher_IsMatch_WithIgnoreCaseTrue_JObjectAsString()
{
// Assign
var matcher = new JsonMatcher("{ \"Id\" : 1, \"Name\" : \"test\" }", true);
// Act
var jobject = new JObject
{
{ "Id", new JValue(1) },
{ "Name", new JValue("Test") }
};
double match = matcher.IsMatch(jobject);
// Assert
Assert.Equal(1.0, match);
}
[Fact]
public void JsonMatcher_IsMatch_JObjectAsString_RejectOnMatch()
{
@@ -142,5 +192,22 @@ namespace WireMock.Net.Tests.Matchers
// Assert
Assert.Equal(0.0, match);
}
[Fact]
public void JsonMatcher_IsMatch_JObjectWithDateTimeOffsetAsString()
{
// Assign
var matcher = new JsonMatcher("{ \"preferredAt\" : \"2019-11-21T10:32:53.2210009+00:00\" }");
// Act
var jobject = new JObject
{
{ "preferredAt", new JValue("2019-11-21T10:32:53.2210009+00:00") }
};
double match = matcher.IsMatch(jobject);
// Assert
Assert.Equal(1.0, match);
}
}
}

View File

@@ -0,0 +1,54 @@
using System.Linq;
using FluentAssertions;
using WireMock.Matchers;
using WireMock.Matchers.Request;
using Xunit;
namespace WireMock.Net.Tests.Matchers
{
public class RequestMatchResultTests
{
[Fact]
public void CompareTo_WithDifferentAverageScore_ReturnsBestMatch()
{
// Arrange
var result1 = new RequestMatchResult();
result1.AddScore(typeof(WildcardMatcher), 1);
result1.AddScore(typeof(WildcardMatcher), 0.9);
var result2 = new RequestMatchResult();
result2.AddScore(typeof(LinqMatcher), 1);
result2.AddScore(typeof(LinqMatcher), 1);
var results = new[] { result1, result2 };
// Act
var best = results.OrderBy(x => x).First();
// Assert
best.Should().Be(result2);
}
[Fact]
public void CompareTo_WithSameAverageScoreButMoreMatchers_ReturnsMatchWithMoreMatchers()
{
// Arrange
var result1 = new RequestMatchResult();
result1.AddScore(typeof(WildcardMatcher), 1);
result1.AddScore(typeof(WildcardMatcher), 1);
var result2 = new RequestMatchResult();
result2.AddScore(typeof(LinqMatcher), 1);
result2.AddScore(typeof(LinqMatcher), 1);
result2.AddScore(typeof(LinqMatcher), 1);
var results = new[] { result1, result2 };
// Act
var best = results.OrderBy(x => x).First();
// Assert
best.Should().Be(result2);
}
}
}

View File

@@ -5,16 +5,48 @@ using System.Net;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using Moq;
using NFluent;
using WireMock.Logging;
using WireMock.RequestBuilders;
using WireMock.ResponseBuilders;
using WireMock.Server;
using WireMock.Settings;
using Xunit;
namespace WireMock.Net.Tests
{
public class ObservableLogEntriesTest
{
[Fact]
public async void FluentMockServer_LogEntriesChanged_WithException_Should_LogError()
{
// Assign
string path = $"/log_{Guid.NewGuid()}";
var loggerMock = new Mock<IWireMockLogger>();
loggerMock.Setup(l => l.Error(It.IsAny<string>(), It.IsAny<object[]>()));
var settings = new FluentMockServerSettings
{
Logger = loggerMock.Object
};
var server = FluentMockServer.Start(settings);
server
.Given(Request.Create()
.WithPath(path)
.UsingGet())
.RespondWith(Response.Create()
.WithBody(@"{ msg: ""Hello world!""}"));
server.LogEntriesChanged += (sender, args) => throw new Exception();
// Act
await new HttpClient().GetAsync($"http://localhost:{server.Ports[0]}{path}");
// Assert
loggerMock.Verify(l => l.Error(It.IsAny<string>(), It.IsAny<object[]>()), Times.Once);
}
[Fact]
public async void FluentMockServer_LogEntriesChanged()
{

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

@@ -70,7 +70,10 @@ namespace WireMock.Net.Tests.Owin
public void MappingMatcher_FindBestMatch_WhenAllowPartialMappingIsFalse_ShouldReturnExactMatch()
{
// Assign
var mappings = InitMappings(new[] { (Guid.Parse("00000000-0000-0000-0000-000000000001"), 0.1), (Guid.Parse("00000000-0000-0000-0000-000000000002"), 1.0) });
var mappings = InitMappings(
(Guid.Parse("00000000-0000-0000-0000-000000000001"), new[] { 0.1 }),
(Guid.Parse("00000000-0000-0000-0000-000000000002"), new[] { 1.0 })
);
_optionsMock.Setup(o => o.Mappings).Returns(mappings);
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "GET", "::1");
@@ -88,7 +91,10 @@ namespace WireMock.Net.Tests.Owin
{
// Assign
_optionsMock.SetupGet(o => o.AllowPartialMapping).Returns(true);
var mappings = InitMappings(new[] { (Guid.Parse("00000000-0000-0000-0000-000000000001"), 0.1), (Guid.Parse("00000000-0000-0000-0000-000000000002"), 0.9) });
var mappings = InitMappings(
(Guid.Parse("00000000-0000-0000-0000-000000000001"), new[] { 0.1 }),
(Guid.Parse("00000000-0000-0000-0000-000000000002"), new[] { 0.9 })
);
_optionsMock.Setup(o => o.Mappings).Returns(mappings);
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "GET", "::1");
@@ -101,7 +107,27 @@ namespace WireMock.Net.Tests.Owin
Check.That(result.RequestMatchResult.AverageTotalScore).IsEqualTo(0.9);
}
private ConcurrentDictionary<Guid, IMapping> InitMappings(params (Guid guid, double match)[] matches)
[Fact]
public void MappingMatcher_FindBestMatch_WhenAllowPartialMappingIsFalse_And_WithSameAverageScoreButMoreMatchers_ReturnsMatchWithMoreMatchers()
{
// Assign
var mappings = InitMappings(
(Guid.Parse("00000000-0000-0000-0000-000000000001"), new[] { 1.0 }),
(Guid.Parse("00000000-0000-0000-0000-000000000002"), new[] { 1.0, 1.0 })
);
_optionsMock.Setup(o => o.Mappings).Returns(mappings);
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "GET", "::1");
// Act
var result = _sut.FindBestMatch(request);
// Assert and Verify
Check.That(result.Mapping.Guid).IsEqualTo(Guid.Parse("00000000-0000-0000-0000-000000000002"));
Check.That(result.RequestMatchResult.AverageTotalScore).IsEqualTo(1.0);
}
private ConcurrentDictionary<Guid, IMapping> InitMappings(params (Guid guid, double[] scores)[] matches)
{
var mappings = new ConcurrentDictionary<Guid, IMapping>();
@@ -110,9 +136,13 @@ namespace WireMock.Net.Tests.Owin
var mappingMock = new Mock<IMapping>();
mappingMock.SetupGet(m => m.Guid).Returns(match.guid);
var partialMatchResult = new RequestMatchResult();
partialMatchResult.AddScore(typeof(object), match.match);
mappingMock.Setup(m => m.GetRequestMatchResult(It.IsAny<RequestMessage>(), It.IsAny<string>())).Returns(partialMatchResult);
var requestMatchResult = new RequestMatchResult();
foreach (var score in match.scores)
{
requestMatchResult.AddScore(typeof(object), score);
}
mappingMock.Setup(m => m.GetRequestMatchResult(It.IsAny<RequestMessage>(), It.IsAny<string>())).Returns(requestMatchResult);
mappings.TryAdd(match.guid, mappingMock.Object);
}

View File

@@ -52,7 +52,7 @@ namespace WireMock.Net.Tests.Owin
_requestMapperMock = new Mock<IOwinRequestMapper>();
_requestMapperMock.SetupAllProperties();
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "GET", "::1");
_requestMapperMock.Setup(m => m.MapAsync(It.IsAny<IRequest>())).ReturnsAsync(request);
_requestMapperMock.Setup(m => m.MapAsync(It.IsAny<IRequest>(), It.IsAny<IWireMockMiddlewareOptions>())).ReturnsAsync(request);
_responseMapperMock = new Mock<IOwinResponseMapper>();
_responseMapperMock.SetupAllProperties();
@@ -87,7 +87,7 @@ namespace WireMock.Net.Tests.Owin
{
// Assign
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "GET", "::1", null, new Dictionary<string, string[]>());
_requestMapperMock.Setup(m => m.MapAsync(It.IsAny<IRequest>())).ReturnsAsync(request);
_requestMapperMock.Setup(m => m.MapAsync(It.IsAny<IRequest>(), It.IsAny<IWireMockMiddlewareOptions>())).ReturnsAsync(request);
_optionsMock.SetupGet(o => o.AuthorizationMatcher).Returns(new ExactMatcher());
_mappingMock.SetupGet(m => m.IsAdminInterface).Returns(true);
@@ -108,7 +108,7 @@ namespace WireMock.Net.Tests.Owin
{
// Assign
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "GET", "::1", null, new Dictionary<string, string[]> { { "h", new[] { "x" } } });
_requestMapperMock.Setup(m => m.MapAsync(It.IsAny<IRequest>())).ReturnsAsync(request);
_requestMapperMock.Setup(m => m.MapAsync(It.IsAny<IRequest>(), It.IsAny<IWireMockMiddlewareOptions>())).ReturnsAsync(request);
_optionsMock.SetupGet(o => o.AuthorizationMatcher).Returns(new ExactMatcher());
_mappingMock.SetupGet(m => m.IsAdminInterface).Returns(true);

View File

@@ -1,5 +1,10 @@
using System.Linq;
using System;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Moq;
using Newtonsoft.Json;
using NFluent;
using WireMock.Matchers;
using WireMock.Matchers.Request;
@@ -182,6 +187,28 @@ namespace WireMock.Net.Tests.RequestMatchers
objectMatcherMock.Verify(m => m.IsMatch(42), Times.Once);
}
[Fact]
public void RequestMessageBodyMatcher_GetMatchingScore_BodyAsJson_CSharpCodeMatcher()
{
// Assign
var body = new BodyData
{
BodyAsJson = new { value = 42 },
DetectedBodyType = BodyType.Json
};
var requestMessage = new RequestMessage(new UrlDetails("http://localhost"), "GET", "127.0.0.1", body);
var matcher = new RequestMessageBodyMatcher(new CSharpCodeMatcher(MatchBehaviour.AcceptOnMatch, "return it.value == 42;"));
// Act
var result = new RequestMatchResult();
double score = matcher.GetMatchingScore(requestMessage, result);
// Assert
Check.That(score).IsEqualTo(1.0d);
}
[Fact]
public void RequestMessageBodyMatcher_GetMatchingScore_BodyAsBytes_IObjectMatcher()
{
@@ -208,5 +235,74 @@ namespace WireMock.Net.Tests.RequestMatchers
// Verify
objectMatcherMock.Verify(m => m.IsMatch(It.IsAny<byte[]>()), Times.Once);
}
[Theory]
[MemberData(nameof(MatchingScoreData))]
public async Task RequestMessageBodyMatcher_GetMatchingScore_Funcs_Matching(object body, RequestMessageBodyMatcher matcher, bool shouldMatch)
{
// assign
BodyData bodyData;
if (body is byte[] b)
bodyData = await BodyParser.Parse(new MemoryStream(b), null);
else if (body is string s)
bodyData = await BodyParser.Parse(new MemoryStream(Encoding.UTF8.GetBytes(s)), null);
else
throw new Exception();
var requestMessage = new RequestMessage(new UrlDetails("http://localhost"), "GET", "127.0.0.1", bodyData);
// act
var result = new RequestMatchResult();
var score = matcher.GetMatchingScore(requestMessage, result);
// assert
Check.That(score).IsEqualTo(shouldMatch ? 1d : 0d);
}
public static TheoryData<object, RequestMessageBodyMatcher, bool> MatchingScoreData
{
get
{
var json = "{'a':'b'}";
var str = "HelloWorld";
var bytes = new byte[] {0xFF, 0xD8, 0xFF, 0xE0, 0x00, 0x10, 0x4A, 0x46, 0x49, 0x46, 0x00};
return new TheoryData<object, RequestMessageBodyMatcher, bool>
{
// JSON match +++
{json, new RequestMessageBodyMatcher((object o) => ((dynamic) o).a == "b"), true},
{json, new RequestMessageBodyMatcher((string s) => s == json), true},
{json, new RequestMessageBodyMatcher((byte[] b) => b.SequenceEqual(Encoding.UTF8.GetBytes(json))), true},
// JSON no match ---
{json, new RequestMessageBodyMatcher((object o) => false), false},
{json, new RequestMessageBodyMatcher((string s) => false), false},
{json, new RequestMessageBodyMatcher((byte[] b) => false), false},
{json, new RequestMessageBodyMatcher(), false },
// string match +++
{str, new RequestMessageBodyMatcher((object o) => o == null), true},
{str, new RequestMessageBodyMatcher((string s) => s == str), true},
{str, new RequestMessageBodyMatcher((byte[] b) => b.SequenceEqual(Encoding.UTF8.GetBytes(str))), true},
// string no match ---
{str, new RequestMessageBodyMatcher((object o) => false), false},
{str, new RequestMessageBodyMatcher((string s) => false), false},
{str, new RequestMessageBodyMatcher((byte[] b) => false), false},
{str, new RequestMessageBodyMatcher(), false },
// binary match +++
{bytes, new RequestMessageBodyMatcher((object o) => o == null), true},
{bytes, new RequestMessageBodyMatcher((string s) => s == null), true},
{bytes, new RequestMessageBodyMatcher((byte[] b) => b.SequenceEqual(bytes)), true},
// binary no match ---
{bytes, new RequestMessageBodyMatcher((object o) => false), false},
{bytes, new RequestMessageBodyMatcher((string s) => false), false},
{bytes, new RequestMessageBodyMatcher((byte[] b) => false), false},
{bytes, new RequestMessageBodyMatcher(), false },
};
}
}
}
}

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

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