Compare commits

...

30 Commits

Author SHA1 Message Date
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
71 changed files with 1626 additions and 486 deletions

View File

@@ -1,34 +1,61 @@
# 1.0.33.0 (12 October 2019)
- [#311](https://github.com/WireMock-Net/WireMock.Net/pull/311) - fix jsonpath matcher [bug] contributed by [StefH](https://github.com/StefH)
- [#324](https://github.com/WireMock-Net/WireMock.Net/pull/324) - Add CSharpCodeMatcher [feature] contributed by [StefH](https://github.com/StefH)
- [#335](https://github.com/WireMock-Net/WireMock.Net/pull/335) - Mark some classes and methods obsolete for version 1.1.0 [doc] contributed by [StefH](https://github.com/StefH)
- [#353](https://github.com/WireMock-Net/WireMock.Net/pull/353) - Fixed failing admin requests when content type includes a charset (based on idea from Paul Roub) [bug] contributed by [StefH](https://github.com/StefH)
- [#355](https://github.com/WireMock-Net/WireMock.Net/pull/355) - Add Try-Catch to the event LogEntriesChanged [feature] contributed by [StefH](https://github.com/StefH)
- [#357](https://github.com/WireMock-Net/WireMock.Net/pull/357) - Add Proxy Setting for: SaveMappingForStatusCodePattern to only save the mapping when the status code matches the pattern [feature] contributed by [StefH](https://github.com/StefH)
- [#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)
- [#252](https://github.com/WireMock-Net/WireMock.Net/issues/252) - Proxy with Transform
- [#287](https://github.com/WireMock-Net/WireMock.Net/issues/287) - Error with parameter that contains a &quot;=&quot; character [bug, question]
- [#308](https://github.com/WireMock-Net/WireMock.Net/issues/308) - __admin/requests - &quot;Collection was modified&quot; exception [bug]
- [#313](https://github.com/WireMock-Net/WireMock.Net/issues/313) - RequestLogExpirationDuration - bug [bug]
- [#315](https://github.com/WireMock-Net/WireMock.Net/issues/315) - Wiki - Proxying: AtPriority() example not set on right object [doc]
- [#318](https://github.com/WireMock-Net/WireMock.Net/issues/318) - Documentation: Add valid CLI parameters [doc, question]
- [#325](https://github.com/WireMock-Net/WireMock.Net/issues/325) - Admin API: PUT Mapping, FormatException because of wrong parsing of the Query [bug]
# 1.0.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)
- [#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]
# 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 +65,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 +87,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 +101,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 +116,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 +123,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 +157,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 +182,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 +200,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 +242,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 +275,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 +305,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 +317,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 +372,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 +379,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 +402,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 +411,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 +431,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.33</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 --version 1.0.33.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'

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

@@ -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,6 +41,7 @@ namespace WireMock.Net.ConsoleApplication
var server = FluentMockServer.Start(new FluentMockServerSettings
{
AllowCSharpCodeMatcher = true,
Urls = new[] { url1, url2, url3 },
StartAdminInterface = true,
ReadStaticMappings = true,
@@ -33,6 +54,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

@@ -17,7 +17,7 @@
<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>

View File

@@ -1,15 +1,17 @@
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
{
/// <summary>
/// The StandAloneApp
/// </summary>
[Obsolete("This class will be removed in version 1.1.0")]
public static class StandAloneApp
{
/// <summary>
@@ -17,6 +19,7 @@ namespace WireMock.Net.StandAlone
/// </summary>
/// <param name="settings">The FluentMockServerSettings</param>
[PublicAPI]
[Obsolete("Will be replaced by WireMockServer.Start(settings) in version 1.1.0")]
public static FluentMockServer Start([NotNull] IFluentMockServerSettings settings)
{
Check.NotNull(settings, nameof(settings));
@@ -34,6 +37,7 @@ namespace WireMock.Net.StandAlone
/// <param name="args">The commandline arguments</param>
/// <param name="logger">The logger</param>
[PublicAPI]
[Obsolete("Will be replaced by `var settings = WireMockServerSettingsParser.ParseArguments(args, logger); WireMockServer.Start(settings);` in version 1.1.0")]
public static FluentMockServer Start([NotNull] string[] args, [CanBeNull] IWireMockLogger logger = null)
{
Check.NotNull(args, nameof(args));
@@ -46,11 +50,12 @@ namespace WireMock.Net.StandAlone
StartAdminInterface = parser.GetBoolValue("StartAdminInterface", true),
ReadStaticMappings = parser.GetBoolValue("ReadStaticMappings"),
WatchStaticMappings = parser.GetBoolValue("WatchStaticMappings"),
AllowPartialMapping = parser.GetBoolValue("AllowPartialMapping", false),
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"),
};
if (logger != null)

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
@@ -26,7 +26,7 @@ namespace WireMock.Owin
ConcurrentDictionary<string, ScenarioState> Scenarios { get; }
ObservableCollection<LogEntry> LogEntries { get; }
ConcurrentObservableCollection<LogEntry> LogEntries { get; }
int? RequestLogExpirationDuration { get; set; }

View File

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

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

@@ -27,7 +27,7 @@ namespace WireMock.Owin
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; }

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

@@ -108,14 +108,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

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

@@ -40,8 +40,9 @@ namespace WireMock.Server
private const string AdminSettings = "/__admin/settings";
private const string AdminScenarios = "/__admin/scenarios";
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,11 +61,11 @@ 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
@@ -72,7 +73,7 @@ namespace WireMock.Server
// __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
@@ -266,7 +267,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[] { });
@@ -653,7 +655,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 +673,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 +690,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 +711,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 +719,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 +728,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());
}
}

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

@@ -25,6 +25,7 @@ namespace WireMock.Server
/// <summary>
/// The fluent mock server.
/// </summary>
[Obsolete("Will be replaced by WireMockServer in version 1.1.0")]
public partial class FluentMockServer : IDisposable
{
private const int ServerStartDelayInMs = 100;

View File

@@ -10,6 +10,7 @@ namespace WireMock.Settings
/// <summary>
/// FluentMockServerSettings
/// </summary>
[Obsolete("Will be replaced by WireMockServerSettings in version 1.1.0")]
public class FluentMockServerSettings : IFluentMockServerSettings
{
/// <inheritdoc cref="IFluentMockServerSettings.Port"/>
@@ -89,5 +90,9 @@ namespace WireMock.Settings
[PublicAPI]
[JsonIgnore]
public Action<IHandlebars, IFileSystemHandler> HandlebarsRegistrationCallback { get; set; }
/// <inheritdoc cref="IFluentMockServerSettings.AllowCSharpCodeMatcher"/>
[PublicAPI]
public bool? AllowCSharpCodeMatcher { 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;
@@ -9,6 +9,7 @@ namespace WireMock.Settings
/// <summary>
/// IFluentMockServerSettings
/// </summary>
[Obsolete("This interface will be removed and replaced by the class WireMockServerSettings in version 1.1.0")]
public interface IFluentMockServerSettings
{
/// <summary>
@@ -115,9 +116,14 @@ 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>
bool? AllowCSharpCodeMatcher { get; set; }
}
}

View File

@@ -1,8 +1,13 @@
namespace WireMock.Settings
using System;
using JetBrains.Annotations;
namespace WireMock.Settings
{
/// <summary>
/// IProxyAndRecordSettings
/// </summary>
[Obsolete("This interface will be removed and replaced by the class ProxyAndRecordSettings in version 1.1.0")]
public interface IProxyAndRecordSettings
{
/// <summary>
@@ -16,7 +21,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

@@ -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,133 @@ 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 method)
{
if (string.IsNullOrEmpty(method))
{
return false;
}
if (BodyAllowedForMethods.TryGetValue(method.ToUpper(), out var allowed))
}
if (BodyAllowedForMethods.TryGetValue(method.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>
@@ -31,6 +32,9 @@
<PublicSign Condition=" '$(OS)' != 'Windows_NT' ">true</PublicSign>
</PropertyGroup>
<!--https://github.com/aspnet/RoslynCodeDomProvider/issues/51-->
<Target Name="CheckIfShouldKillVBCSCompiler" />
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
<PathMap>$(MSBuildProjectDirectory)=/</PathMap>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
@@ -40,6 +44,10 @@
<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>
@@ -54,10 +62,10 @@
<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>
@@ -87,6 +95,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 +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)' == 'net46' ">
@@ -104,10 +114,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 +130,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

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

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

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

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

@@ -1,6 +1,6 @@
using NFluent;
using System;
using System;
using Moq;
using NFluent;
using WireMock.Admin.Mappings;
using WireMock.Matchers;
using WireMock.Serialization;
@@ -68,7 +68,7 @@ namespace WireMock.Net.Tests.Serialization
}
[Fact]
public void MatcherModelMapper_Map_ExactObjectMatcher_Pattern()
public void MatcherModelMapper_Map_ExactObjectMatcher_ValidBase64StringPattern()
{
// Assign
var model = new MatcherModel
@@ -84,6 +84,20 @@ namespace WireMock.Net.Tests.Serialization
Check.That(matcher.ValueAsBytes).ContainsExactly(new byte[] { 115, 116, 101, 102 });
}
[Fact]
public void MatcherModelMapper_Map_ExactObjectMatcher_InvalidBase64StringPattern()
{
// Assign
var model = new MatcherModel
{
Name = "ExactObjectMatcher",
Patterns = new object[] { "_" }
};
// Act & Assert
Check.ThatCode(() => _sut.Map(model)).Throws<ArgumentException>();
}
[Fact]
public void MatcherModelMapper_Map_RegexMatcher()
{

View File

@@ -52,12 +52,19 @@ namespace WireMock.Net.Tests
public static void SetPrivatePropertyValue<T>(this object obj, string propertyName, T value)
{
Type t = obj.GetType();
if (t.GetProperty(propertyName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) == null)
PropertyInfo propertyInfo = null;
while (propertyInfo == null && t != null)
{
throw new ArgumentOutOfRangeException(nameof(propertyName), $"Property {propertyName} was not found in Type {obj.GetType().FullName}");
propertyInfo = t.GetProperty(propertyName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
t = t.BaseType;
}
t.InvokeMember(propertyName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.SetProperty | BindingFlags.Instance, null, obj, new object[] { value });
if (propertyInfo == null)
{
throw new ArgumentOutOfRangeException(nameof(propertyName), $"Private property {propertyName} was not found in Type {obj.GetType().FullName}");
}
propertyInfo.SetValue(obj, value);
}
}
}

View File

@@ -50,6 +50,22 @@ namespace WireMock.Net.Tests.Util
Check.That(body.DetectedBodyTypeFromContentType).IsEqualTo(detectedBodyTypeFromContentType);
}
[Theory]
[InlineData(new byte[] {34, 97, 34}, BodyType.Json)]
[InlineData(new byte[] {97}, BodyType.String)]
[InlineData(new byte[] {0xFF, 0xD8, 0xFF, 0xE0}, BodyType.Bytes)]
public async Task BodyParser_Parse_DetectedBodyType(byte[] content, BodyType detectedBodyType)
{
// arrange
var memoryStream = new MemoryStream(content);
// act
var body = await BodyParser.Parse(memoryStream, null);
// assert
Check.That(body.DetectedBodyType).IsEqualTo(detectedBodyType);
}
[Fact]
public async Task BodyParser_Parse_WithUTF8EncodingAndContentTypeMultipart_DetectedBodyTypeEqualsString()
{

View File

@@ -0,0 +1,75 @@
using FluentAssertions;
using System;
using System.Net;
using WireMock.Util;
using Xunit;
namespace WireMock.Net.Tests.Util
{
/// <summary>
/// Based on https://raw.githubusercontent.com/tmenier/Flurl/129565361e135e639f1d44a35a78aea4302ac6ca/Test/Flurl.Test/Http/HttpStatusRangeParserTests.cs
/// </summary>
public class HttpStatusRangeParserTests
{
[Theory]
[InlineData("4**", 399, false)]
[InlineData("4**", 400, true)]
[InlineData("4**", 499, true)]
[InlineData("4**", 500, false)]
[InlineData("4xx", 399, false)]
[InlineData("4xx", 400, true)]
[InlineData("4xx", 499, true)]
[InlineData("4xx", 500, false)]
[InlineData("4XX", 399, false)]
[InlineData("4XX", 400, true)]
[InlineData("4XX", 499, true)]
[InlineData("4XX", 500, false)]
[InlineData("400-499", 399, false)]
[InlineData("400-499", 400, true)]
[InlineData("400-499", 499, true)]
[InlineData("400-499", 500, false)]
[InlineData("100,3xx,600", 100, true)]
[InlineData("100,3xx,600", 101, false)]
[InlineData("100,3xx,600", 300, true)]
[InlineData("100,3xx,600", 399, true)]
[InlineData("100,3xx,600", 400, false)]
[InlineData("100,3xx,600", 600, true)]
[InlineData("400-409,490-499", 399, false)]
[InlineData("400-409,490-499", 405, true)]
[InlineData("400-409,490-499", 450, false)]
[InlineData("400-409,490-499", 495, true)]
[InlineData("400-409,490-499", 500, false)]
[InlineData("*", 0, true)]
[InlineData(",,,*", 9999, true)]
[InlineData("", 0, false)]
[InlineData(",,,", 9999, false)]
[InlineData(null, 399, true)]
public void HttpStatusRangeParser_ValidPattern_IsMatch(string pattern, int value, bool expectedResult)
{
HttpStatusRangeParser.IsMatch(pattern, value).Should().Be(expectedResult);
}
[Fact]
public void HttpStatusRangeParser_ValidPattern_HttpStatusCode_IsMatch()
{
HttpStatusRangeParser.IsMatch("4xx", HttpStatusCode.BadRequest).Should().BeTrue();
}
[Theory]
[InlineData("-100")]
[InlineData("100-")]
[InlineData("1yy")]
public void HttpStatusRangeParser_InvalidPattern_ThrowsException(string pattern)
{
Assert.Throws<ArgumentException>(() => HttpStatusRangeParser.IsMatch(pattern, 100));
}
}
}

View File

@@ -23,6 +23,10 @@
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
</PropertyGroup>
<ItemGroup>
<Compile Remove="Matchers\CSScriptMatcherTests.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\WireMock.Net.StandAlone\WireMock.Net.StandAlone.csproj" />
<ProjectReference Include="..\..\src\WireMock.Net\WireMock.Net.csproj" />
@@ -39,7 +43,6 @@
<PackageReference Include="RestEase" Version="1.4.7" />
<PackageReference Include="RandomDataGenerator.Net" Version="1.0.8" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.9.0" />
<PackageReference Include="MimeKitLite" Version="2.0.7" />
<PackageReference Include="Moq" Version="4.10.1" />
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
<PackageReference Include="NFluent" Version="2.5.0" />