Compare commits

...

114 Commits

Author SHA1 Message Date
Stef Heyenrath
276f329eed mm 2026-01-05 21:38:14 +01:00
Stef Heyenrath
f5d53453e5 1.23.0 2026-01-05 21:34:11 +01:00
samlatham
0e60e3f3f9 Fix: Pass AllowedHandlebarsHelpers configuration to Handlebars.Net.Helpers (#1416)
Pass AllowedHandlebarsHelpers configuration to Handlebars.Net.Helpers so that optional handlebars helpers can be enabled.

Co-authored-by: Sam Latham <sam.latham@citrix.com>
2026-01-05 21:24:48 +01:00
Luca Ma
9cee6dde00 Pass the parameter matchOperator in Request.WithPath to its inner calls (#1414)
Co-authored-by: Luca Ma <lucama@microsoft.com>
2026-01-04 08:03:19 +01:00
Stef Heyenrath
c88e7378a7 1.22.0 2026-01-02 21:30:59 +01:00
Vadim Hatsura
b090296559 chore(testcontainers): bump up Testcontainers to version 4.10.0 (#1412) 2026-01-02 21:25:28 +01:00
Stef Heyenrath
e5afd69f7c 1.21.0 2025-12-25 15:00:54 +01:00
Stef Heyenrath
3b3f3604f7 Merge branch 'master' into version-2.x 2025-12-25 13:57:44 +01:00
Stef Heyenrath
f38133d7a4 Fix readyness-check for Testcontainers (#1408)
* Add XUnit Logging to TestcontainersTests

* .
2025-12-25 13:56:29 +01:00
Stef Heyenrath
d311813851 Merge branch 'master' into version-2.x 2025-12-24 17:00:27 +01:00
Stef Heyenrath
597c95000e vmImage: 'windows-2025' (#1407) 2025-12-24 16:59:02 +01:00
Stef Heyenrath
c8e1c66bf2 Merge branch 'master' into version-2.x 2025-12-24 15:30:29 +01:00
Stef Heyenrath
4617b99c30 [Collection("Grpc")] 2025-12-24 12:32:56 +01:00
Stef Heyenrath
76d6f92be2 mm 2025-12-24 12:18:53 +01:00
Stef Heyenrath
ffd4d89946 Re-enable TestcontainersTestsGrpc (#1406)
* Re-enable TestcontainersTestsGrpc

* //[Collection("Grpc")]
2025-12-24 12:16:56 +01:00
Stef Heyenrath
2d46c86f47 1.20.0 2025-12-24 10:11:53 +01:00
Stef Heyenrath
75f4fbe9d0 Fix Testcontainers AddProtoDefinition (#1405)
* Fix Testcontainers AddProtoDefinition

* .

* UntilHttpRequestIsSucceeded

* WireMockContainer.ContainerPort

* System.Net/System.Net.Http

* ...

* WithWaitStrategy

* MaxHealthCheckRetries

* for

* _adminApi

* static

* ...

* testOutputHelper.WriteLine("Dumping WireMock logs:");

* Console.WriteLine(

* testOutputHelper.WriteLine("Dumping WireMock.Net mappings:");

* fix WithWaitStrategy

* [Fact]

* <PackageReference Include="ProtoBufJsonConverter" Version="0.11.0" />

* [Collection("Grpc")] / [Fact(Skip = "TODO")]

* ...
2025-12-24 10:09:30 +01:00
Stef Heyenrath
3f349cca4c WireMock.Net.NUnit 2025-12-21 10:17:13 +01:00
Stef Heyenrath
19b85c7856 Merge branch 'master' into version-2.x 2025-12-21 09:50:31 +01:00
Stef Heyenrath
16e3872402 Run the Grpc TestcontainersTests sequential (#1402) 2025-12-21 09:40:16 +01:00
Stef Heyenrath
da912be37e mm 2025-12-20 13:46:11 +01:00
Stef Heyenrath
4c797c328f Add WireMock.Net.NUnit project (#1400)
* Add WireMock.Net.NUnit project

* <Version>0.0.1-preview-01</Version>

* --v
2025-12-20 13:43:54 +01:00
Stef Heyenrath
ca9fceab67 mm 2025-12-20 12:10:45 +01:00
Stef Heyenrath
a5e75a7278 Fix Grpc tests (#1401)
* Fix some Grpc tests

* await Task.Delay(1000);

* ports
2025-12-20 12:08:41 +01:00
Stef Heyenrath
56f65c19e2 Upgrade RamlToOpenApiConverter and YamlDotNet (#1399)
* Upgrade RamlToOpenApiConverter and YamlDotNet

* fix
2025-12-19 18:33:58 +01:00
Stef Heyenrath
c662bb7ad5 mm 2025-12-19 17:56:26 +01:00
Stef Heyenrath
6aef4816a5 WireMockServer_WithRequiredClientCertificates_Should_Work_Correct --> IgnoreOnContinuousIntegrationFact 2025-12-19 17:52:20 +01:00
Stef Heyenrath
197a211a52 TestcontainersTests 2025-12-13 11:48:48 +01:00
Stef Heyenrath
7d6fd35716 Merge branch 'master' into version-2.x 2025-12-12 11:19:09 +01:00
Stef Heyenrath
3cfeec6035 1.19.0 2025-12-12 11:16:38 +01:00
Stef Heyenrath
b57d5e7548 WireMockContainerBuilder: allow only docker images named wiremock (#1392) 2025-12-11 22:21:39 +01:00
Stef Heyenrath
36b89afce5 fix CI link in Readme 2025-12-11 11:25:28 +01:00
Stef Heyenrath
e2acac55a4 Update WireMockContainerBuilder (WithImage and WithCustomImage) (#1391)
* Update WireMockContainerBuilder (WithImage and WithCustomImage)

* .
2025-12-11 10:55:31 +01:00
Stef Heyenrath
ceabd27ce0 1.18.0 2025-12-09 18:28:28 +01:00
Stef Heyenrath
1b0b42d538 Merge branch 'master' into version-2.x 2025-12-08 19:16:51 +01:00
Stef Heyenrath
f8e2c7ee90 Add WithBodyAsType to RequestMatcher (#1388)
* Add WithBody<T>

* .

* t

* t2
2025-12-08 19:15:14 +01:00
Stef Heyenrath
c25d8f33d2 1.17.0 2025-12-07 10:55:07 +01:00
Stef Heyenrath
5f7b50a5b8 mm 2025-12-07 10:52:17 +01:00
Stef Heyenrath
6da190e596 Aspire: Add WithProtoDefinition to support proto definition at server level (#1383)
* Add property UseHttp2 to WireMockServerArguments

* .

* additionalUrls

* ok?

* WireMockServerArguments

* fx

* AddProtoDefinition

* ...

* FIX

* Always add the lifecycle hook to support dynamic mappings and proto definitions
2025-12-07 10:50:11 +01:00
Stef Heyenrath
44388ce80d Fix random delay in mapping json file (#1386) 2025-11-25 20:54:06 +01:00
Stef Heyenrath
5e25ca767d Fix BuildId (#1384) 2025-11-23 11:19:39 +01:00
Stef Heyenrath
0cc583a4a3 WireMock.Net.xUnit.v3 (netstandard2.0) 2025-11-18 18:52:07 +01:00
Stef Heyenrath
f9633adac1 1.16.0 2025-11-18 18:45:12 +01:00
Stef Heyenrath
37bad618a3 Add WireMock.Net.xUnit.v3 project (#1380)
* Add WireMock.Net.xUnit.v3 project

* .
2025-11-18 18:42:28 +01:00
Stef Heyenrath
2e37e822a8 Merge branch 'master' into version-2.x 2025-11-17 20:15:20 +01:00
Johannes Häggqvist
8e69f36f04 Add WireMockHealthCheck in WireMock.Net.Aspire (#1375)
* Add WireMockHealthCheck

For use with Aspire, to make WaitFor(wiremock) more useful.
Calls /__admin/health and checks the result, as well as checks if mappings using AdminApiMappingBuilder has been submitted to the server.

This created a catch-22 problem where the mappings were not submitted until the health check was healthy, but the health check was not healthy until the mappings were submitted.

To avoid this, the WireMockServerLifecycleHook class has been slightly re-arranged, and is now using the AfterEndpointsAllocatedAsync callback rather than the AfterResourcesCreatedAsync callback. Within which a separate Task is created that waits until the server is ready and submits the mappings.

* Move WireMockMappingState to its own file

* Dispose the cancellation tokens in WireMockServerLifecycleHook
2025-11-17 20:14:42 +01:00
Stef Heyenrath
21601889e0 Check if the path is valid when using WithPath(...) (#1377) 2025-11-08 09:02:00 +01:00
Stef Heyenrath
dfeabf228e WireMock.Net.OpenApiParser : support Examples (#1366) 2025-11-08 07:45:38 +01:00
Stef Heyenrath
1feb0ade70 Fix wiki links (#1373)
* Change all links from wiki to documention website

* .

* doc

* ws
2025-10-26 10:13:58 +01:00
Stef Heyenrath
1bc693512f HashSet 2025-10-22 12:26:39 +02:00
Stef Heyenrath
398dfe3eff mm 2025-10-22 11:01:42 +02:00
Stef Heyenrath
8b1bd1b21b 1.15.0 2025-10-22 10:58:44 +02:00
Michi
c1b23b615e Support Testcontainers 4.8.0 (#1370) 2025-10-22 10:18:16 +02:00
Stef Heyenrath
5885324dfb Fix WithProbability logic (#1367)
* Fix WithProbability logic

* .

* FIX

* Update src/WireMock.Net.Minimal/Owin/MappingMatcher.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-10-22 10:16:59 +02:00
Tom Akehurst
e3f3e0a8f2 Tweaked docs message in README 2025-10-13 20:29:29 +01:00
Tom Akehurst
685d28db0b Added link to new doc site from README.md 2025-10-13 20:23:18 +01:00
Stef Heyenrath
02228e5f8a mm 2025-10-06 17:08:28 +02:00
Stef Heyenrath
f6c5225fe0 1.14.0 2025-10-06 17:00:19 +02:00
Stef Heyenrath
27d1a4b494 mm 2025-10-06 09:18:20 +02:00
Stef Heyenrath
b9019a2f61 Update ProxyUrlReplaceSettingsModel with TransformTemplate property (#1362)
* Update ProxyUrlReplaceSettingsModel with TransformTemplate property + parse settings correctly

* oldValue nullable

* <Version>1.14.0-preview-01</Version>
2025-10-06 09:16:25 +02:00
Stef
3e170778c8 HttpClientBuilder / ssl3 2025-10-05 16:55:22 +02:00
Stef Heyenrath
b82dad2563 Add Tls13 (#1363)
* Add Tls13

* fix
2025-10-05 15:51:47 +02:00
Stef
9fa1f4081e mm 2025-10-05 15:19:18 +02:00
Stef Heyenrath
45d4e7077d 1.13.0 2025-09-28 12:44:14 +02:00
Stef Heyenrath
19e95325fa ProxyUrlTransformer (#1361)
* ProxyUrlTransformer

* tests

* Update src/WireMock.Net.Shared/Settings/ProxyUrlReplaceSettings.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-09-28 12:40:33 +02:00
Stef Heyenrath
37e140b342 #endif 2025-09-07 19:51:42 +02:00
Stef Heyenrath
4fd3ee1dfd . 2025-08-31 12:51:33 +02:00
Stef Heyenrath
5b43a4f341 NO ConfigureAwait(false) + cleanup 2025-08-31 12:39:51 +02:00
Stef Heyenrath
0960d7cebd openapi 2025-08-31 12:21:21 +02:00
Stef Heyenrath
b111b019bc cleanup + fix tests for net8 2025-08-31 11:39:03 +02:00
Stef Heyenrath
bb561c94d0 cleanup some #if for NETSTANDARD1_3 2025-08-31 11:07:25 +02:00
Stef Heyenrath
5c221ff4fa 12 2025-08-31 10:03:51 +02:00
Stef Heyenrath
3077e7bee1 Setup .NET 9 2025-08-31 09:43:48 +02:00
Stef Heyenrath
ec54599827 mm + ... 2025-08-31 09:21:08 +02:00
Stef Heyenrath
371bfdc160 TypeLoader: implement Try methods (#1358)
* TypeLoader: implement Try methods

* fix
2025-08-31 08:48:29 +02:00
Stef Heyenrath
5c5e104f2c 1.12.0 2025-08-30 11:49:20 +02:00
Stef Heyenrath
068fdf33e3 Upgrade Testcontainers to 4.7.0 (#1357)
* Upgrade Testcontainers to 4.7.0

* .
2025-08-30 11:46:52 +02:00
Stef Heyenrath
034766a2d6 Version 2.x 2025-08-30 10:24:07 +02:00
Stef Heyenrath
358590918e 1.11.2 2025-08-27 10:25:31 +02:00
Stef Heyenrath
32afea5d30 Revert JetBrains.Annotations and add System.Text.RegularExpressions to solve CVE (#1355) 2025-08-27 10:04:36 +02:00
Stef Heyenrath
50b3d58a01 1.11.1 2025-08-27 08:47:58 +02:00
Stef Heyenrath
35bf5e94a5 Add System.Net.Http again to solve CVE warning (#1354) 2025-08-27 08:45:24 +02:00
Stef Heyenrath
9fcc9ade10 Add additional try-catch to TypeLoader logic (#1352) 2025-08-27 08:33:01 +02:00
Stef Heyenrath
865bbf2432 WireMock.Net.ProtoBuf: fix version 2025-08-26 15:42:23 +02:00
Stef Heyenrath
560540099e 1.11.0 2025-08-26 07:58:04 +02:00
Stef Heyenrath
e5c4605020 Create WireMock.Net.ProtoBuf project (#1350)
* Create WireMock.Net.ProtoBuf project

* ok

* Update Directory.Build.props

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-08-26 07:55:02 +02:00
Stef Heyenrath
124d29c86a 1.10.1 2025-08-22 19:58:00 +02:00
Stef Heyenrath
08117e870b Add AtPath and AtAbsolutePath to Assertions projects (#1349)
* Add AtPath and AtAbsolutePath to Assertions projects

* tst
2025-08-22 19:40:59 +02:00
Stef Heyenrath
ddb181cc52 WireMock.Net.Extensions.Routing 2025-08-18 20:47:10 +02:00
Stef Heyenrath
10fdd24fca 1.10.0 2025-08-18 20:22:49 +02:00
Gennadii Saltyshchak
be2ea67b89 Add new package WireMock.Net.Extensions.Routing which provides minimal-API-style routing for WireMock.Net (#1344)
* Add new package WireMock.Net.Extensions.Routing

* Update documentation for WireMock.Net.Extensions.Routing

* Cleanup imports

* Add header to all source files inside WireMock.Net.Extensions.Routing

* Add header to all source files inside WireMock.Net.Extensions.Routing.Tests

* Revert unintended changes

* Remove redundant build configurations

* Remove incorrect links from documentation

* Update nuget package references

* Revert unintended changes

* Migrate to AwesomeAssertions

* Remove redundant project reference

* Adjust formatting

* Migrate to primary constructor

* Refactoring: rename delegate parameter

* Abstract over JSON converter

* Replace WireMock with WireMock.Net in comments

* Move local functions to the bottom of the methods
2025-08-18 19:52:42 +02:00
Stef Heyenrath
60eb519ae2 1.9.1 2025-08-17 10:11:48 +02:00
Stef Heyenrath
22ed94918a Fix generating source code for Scenario and State (#1347)
* Fix generating source code for Scenario and State

* Update src/WireMock.Net.Minimal/Serialization/MappingConverter.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update src/WireMock.Net.Minimal/Serialization/MappingConverter.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* .

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-08-17 10:06:39 +02:00
Stef Heyenrath
faffc56484 Add TimesInSameState to MappingModel (#1345)
* Add TimesInSameState to MappingModel

* fix tests
2025-08-11 08:46:18 +02:00
Stef Heyenrath
a5558777e2 1.9.0 2025-08-10 19:07:24 +02:00
Stef Heyenrath
6722ca40ba Update feature_request.md 2025-08-10 19:02:24 +02:00
Stef Heyenrath
0597a73e0e Create GraphQL project (#1334)
* Create new project for GraphQL

* ...

* .

* ok?

* Update src/WireMock.Net.Shared/Extensions/AnyOfExtensions.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* --

* ...

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-08-10 19:00:22 +02:00
Stef Heyenrath
0d510cdde8 1.8.18 2025-08-04 18:03:44 +02:00
Stef Heyenrath
52a396beef 1.8.18 2025-08-04 18:03:23 +02:00
Sam Fields
6ccfe68686 Fixes an issue with matching JSON bodies as bytes (#1339)
* Fixes an issue with matching JSON bodies as bytes

* Adding tests for exact object matching

* Simplify the check for byte data
2025-08-02 20:11:13 +02:00
Stef Heyenrath
e400e92452 1.8.17 2025-07-23 09:40:55 +02:00
Stef Heyenrath
7a187dfb78 Add more examples for WithGraphQLSchema (#1335) 2025-07-23 09:34:31 +02:00
Stef Heyenrath
e6ff8776fb Make CSharpCodeMatcher public (#1337) 2025-07-23 09:29:53 +02:00
Stef Heyenrath
c32e904f4d 1.8.16 2025-07-19 08:16:34 +02:00
Stef Heyenrath
e80d436dd6 Use corerct Handlebars.Net.Helpers.Xslt (2.5.2) (#1332) 2025-07-18 10:57:58 +02:00
Stef Heyenrath
fcc95ff06f 1.8.15 2025-07-18 08:45:42 +02:00
Stef Heyenrath
020cc15420 Correctly map the Pact Interaction Description property (#1331)
* Correctly map the Pact Interaction Description property

* Update src/WireMock.Net.Minimal/Serialization/PactMapper.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* post

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-07-18 08:41:04 +02:00
Stef Heyenrath
aeb15725e4 1.8.14 2025-07-13 08:53:53 +02:00
Stef Heyenrath
a06ee6b158 Fix HandlebarsContext.ParseAndEvaluate (#1329) 2025-07-12 11:05:02 +02:00
Stef Heyenrath
b0076b4e81 Implement IMimeMessageData (#1326)
* Implement IMimeMessageData

* 1

* Update src/WireMock.Net.MimePart/Util/MimeKitUtils.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* v1

* v2

* e

* ?

* fix

* if (Array.TrueForAll(_funcs, func => func(value).IsPerfect()))

* Update src/WireMock.Net.Shared/Util/IMimeKitUtils.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update src/WireMock.Net.Minimal/Matchers/Request/RequestMessageMultiPartMatcher.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update src/WireMock.Net.MimePart/Models/MimeEntityDataWrapper.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Models.Mime.IMimeMessageData? BodyAsMimeMessage { get; }

* Update src/WireMock.Net.MimePart/Util/MimeKitUtils.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update src/WireMock.Net.MimePart/Models/MimePartDataWrapper.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update src/WireMock.Net.MimePart/Models/MimeMessageDataWrapper.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update src/WireMock.Net.Shared/Util/IMimeKitUtils.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* .

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-07-12 09:54:18 +02:00
Stef Heyenrath
6c61f87ef3 Add method CreateHttpClientFactory (#1325)
* Add method CreateHttpClientFactory

* rev
2025-07-08 10:50:30 +02:00
Stef Heyenrath
35cd06b47b Update README.md 2025-06-24 09:31:22 +02:00
Stef Heyenrath
b925c537c7 1.8.13 2025-06-23 08:07:30 +02:00
Stef Heyenrath
f80925c1fb Add Scenario set State method (#1322)
* Add SetScenarioState

* add tests

* summary

* .

* 1.8.13-preview-01

* fix

* fix name
2025-06-23 08:03:11 +02:00
383 changed files with 9578 additions and 11242 deletions

View File

@@ -8,7 +8,7 @@ assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
A clear and concise description of what the problem is.
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
@@ -17,7 +17,7 @@ A clear and concise description of what you want to happen.
A clear and concise description of any alternative solutions or features you've considered.
**Is your feature request supported by [WireMock (java version)](https://www.wiremock.org)? Please provide details.**
Provide relevant information if requested feature is supported in [Handlebarsjs](https://handlebarsjs.com/) but is missing in our implementation.
Provide relevant information if requested feature is supported but is missing in this implementation.
**Additional context**
Add any other context or screenshots about the feature request here.

View File

@@ -19,6 +19,11 @@ jobs:
steps:
- uses: actions/checkout@v4
- name: Setup .NET 8
uses: actions/setup-dotnet@v4
with:
dotnet-version: '8.0.x'
- name: 'WireMock.Net.Tests'
run: dotnet test './test/WireMock.Net.Tests/WireMock.Net.Tests.csproj' -c Release --framework net8.0

View File

@@ -1,3 +1,122 @@
# 1.23.0 (05 January 2026)
- [#1414](https://github.com/wiremock/WireMock.Net/pull/1414) - Pass the parameter matchOperator in Request.WithPath to its inner calls [bug] contributed by [gbamqzkdyg](https://github.com/gbamqzkdyg)
- [#1416](https://github.com/wiremock/WireMock.Net/pull/1416) - Fix: Pass AllowedHandlebarsHelpers configuration to Handlebars.Net.Helpers library contributed by [samlatham](https://github.com/samlatham)
- [#1413](https://github.com/wiremock/WireMock.Net/issues/1413) - Parameter `matchOperator` is not respected in the method Request.WithPath [bug]
- [#1415](https://github.com/wiremock/WireMock.Net/issues/1415) - HandlebarsSettings AllowedHandlebarsHelpers Configuration Not Applied [bug]
# 1.22.0 (02 January 2026)
- [#1412](https://github.com/wiremock/WireMock.Net/pull/1412) - chore(testcontainers): bump up Testcontainers to version 4.10.0 [feature] contributed by [vhatsura](https://github.com/vhatsura)
- [#1411](https://github.com/wiremock/WireMock.Net/issues/1411) - WireMock.Net.Testcontainers isn't compatible with Testcontainers 4.10.0 [bug]
# 1.21.0 (25 December 2025)
- [#1408](https://github.com/wiremock/WireMock.Net/pull/1408) - Fix readyness-check for Testcontainers [bug] contributed by [StefH](https://github.com/StefH)
# 1.20.0 (24 December 2025)
- [#1399](https://github.com/wiremock/WireMock.Net/pull/1399) - Upgrade RamlToOpenApiConverter and YamlDotNet [feature] contributed by [StefH](https://github.com/StefH)
- [#1400](https://github.com/wiremock/WireMock.Net/pull/1400) - Add WireMock.Net.NUnit project [feature] contributed by [StefH](https://github.com/StefH)
- [#1405](https://github.com/wiremock/WireMock.Net/pull/1405) - Fix Testcontainers AddProtoDefinition [bug] contributed by [StefH](https://github.com/StefH)
- [#1398](https://github.com/wiremock/WireMock.Net/issues/1398) - Upgrade YamlDotNet dependency [feature]
- [#1404](https://github.com/wiremock/WireMock.Net/issues/1404) - An exception occurs when adding multiple proto definitions in the TestContainer. [bug]
# 1.19.0 (12 December 2025)
- [#1391](https://github.com/wiremock/WireMock.Net/pull/1391) - Update WireMockContainerBuilder (WithImage and WithCustomImage) [feature] contributed by [StefH](https://github.com/StefH)
- [#1392](https://github.com/wiremock/WireMock.Net/pull/1392) - WireMockContainerBuilder: allow all docker images named wiremock [feature] contributed by [StefH](https://github.com/StefH)
- [#1390](https://github.com/wiremock/WireMock.Net/issues/1390) - Unable to build WireMockContainerBuilder with custom image [feature]
# 1.18.0 (09 December 2025)
- [#1388](https://github.com/wiremock/WireMock.Net/pull/1388) - Add WithBodyAsType to RequestMatcher [feature] contributed by [StefH](https://github.com/StefH)
# 1.17.0 (07 December 2025)
- [#1383](https://github.com/wiremock/WireMock.Net/pull/1383) - Aspire: Add WithProtoDefinition to support proto definition at server level [feature] contributed by [StefH](https://github.com/StefH)
- [#1386](https://github.com/wiremock/WireMock.Net/pull/1386) - Fix random delay in mapping json file [bug] contributed by [StefH](https://github.com/StefH)
- [#1274](https://github.com/wiremock/WireMock.Net/issues/1274) - .WithMappings to mount volume is not working for GRPC [bug]
- [#1381](https://github.com/wiremock/WireMock.Net/issues/1381) - Downstream dependencies missing after 1.16.0 release [bug]
- [#1382](https://github.com/wiremock/WireMock.Net/issues/1382) - Does Aspire support enabling HTTP/2? [feature]
- [#1385](https://github.com/wiremock/WireMock.Net/issues/1385) - Do delays and probabilities show in saved static mappings? [bug]
- [#1387](https://github.com/wiremock/WireMock.Net/issues/1387) - Tests failing with TaskCanceledException on Windows Server 2025 Build 7171 [bug]
# 1.16.0 (18 November 2025)
- [#1366](https://github.com/wiremock/WireMock.Net/pull/1366) - WireMock.Net.OpenApiParser : support Examples [feature] contributed by [StefH](https://github.com/StefH)
- [#1375](https://github.com/wiremock/WireMock.Net/pull/1375) - Add WireMockHealthCheck in WireMock.Net.Aspire [feature] contributed by [Zguy](https://github.com/Zguy)
- [#1377](https://github.com/wiremock/WireMock.Net/pull/1377) - Check if the path is valid when using WithPath(...) [feature] contributed by [StefH](https://github.com/StefH)
- [#1380](https://github.com/wiremock/WireMock.Net/pull/1380) - Add WireMock.Net.xUnit.v3 project [feature] contributed by [StefH](https://github.com/StefH)
- [#1364](https://github.com/wiremock/WireMock.Net/issues/1364) - Choosing examples from open api specification for responses. [feature]
- [#1376](https://github.com/wiremock/WireMock.Net/issues/1376) - AdminApiMappingBuilder `WithPath` should add the starting `/` if missing [feature]
- [#1379](https://github.com/wiremock/WireMock.Net/issues/1379) - xUnit v3 [feature]
# 1.15.0 (22 October 2025)
- [#1367](https://github.com/wiremock/WireMock.Net/pull/1367) - Fix WithProbability logic [bug] contributed by [StefH](https://github.com/StefH)
- [#1370](https://github.com/wiremock/WireMock.Net/pull/1370) - Support Testcontainers 4.8.0 [bug] contributed by [MD-V](https://github.com/MD-V)
- [#1126](https://github.com/wiremock/WireMock.Net/issues/1126) - Request matching WithProbability strange behaviour [bug]
# 1.14.0 (06 October 2025)
- [#1362](https://github.com/wiremock/WireMock.Net/pull/1362) - Update ProxyUrlReplaceSettingsModel with TransformTemplate property [feature] contributed by [StefH](https://github.com/StefH)
- [#1363](https://github.com/wiremock/WireMock.Net/pull/1363) - Add Tls13 [bug] contributed by [StefH](https://github.com/StefH)
- [#1342](https://github.com/wiremock/WireMock.Net/issues/1342) - Facing SSL certificate validation error when using .NET 5 and above framework for some http services during recording [bug]
- [#1360](https://github.com/wiremock/WireMock.Net/issues/1360) - ProxyURL path configuration [feature]
# 1.13.0 (28 September 2025)
- [#1358](https://github.com/wiremock/WireMock.Net/pull/1358) - TypeLoader: implement Try methods [feature] contributed by [StefH](https://github.com/StefH)
- [#1361](https://github.com/wiremock/WireMock.Net/pull/1361) - ProxyUrlTransformer [feature] contributed by [StefH](https://github.com/StefH)
# 1.12.0 (30 August 2025)
- [#1357](https://github.com/wiremock/WireMock.Net/pull/1357) - Upgrade Testcontainers to 4.7.0 [feature] contributed by [StefH](https://github.com/StefH)
- [#1356](https://github.com/wiremock/WireMock.Net/issues/1356) - WireMock.Net 1.11.2 is not compatible with TestContainers 4.7.0 [bug]
# 1.11.2 (27 August 2025)
- [#1352](https://github.com/wiremock/WireMock.Net/pull/1352) - Add additional try-catch to TypeLoader logic [feature] contributed by [StefH](https://github.com/StefH)
- [#1354](https://github.com/wiremock/WireMock.Net/pull/1354) - Add System.Net.Http again to solve CVE warning [bug] contributed by [StefH](https://github.com/StefH)
- [#1355](https://github.com/wiremock/WireMock.Net/pull/1355) - Revert JetBrains.Annotations and add System.Text.RegularExpressions t&#8230; [bug] contributed by [StefH](https://github.com/StefH)
- [#1071](https://github.com/wiremock/WireMock.Net/issues/1071) - Split WireMock into multiple nuget packages depending on features [feature]
- [#1351](https://github.com/wiremock/WireMock.Net/issues/1351) - Version 1.11.0 seems to have a dependency to non-existing version of JetBrans.Annotations [bug]
- [#1353](https://github.com/wiremock/WireMock.Net/issues/1353) - 1.11.0: NU1903: Warning As Error: Package 'System.Net.Http' 4.3.0 has a known high severity vulnerability, https://github.com/advisories/GHSA-7jgj-8wvc-jh57 [bug]
# 1.11.0 (26 August 2025)
- [#1350](https://github.com/wiremock/WireMock.Net/pull/1350) - Create WireMock.Net.ProtoBuf project [feature] contributed by [StefH](https://github.com/StefH)
# 1.10.1 (22 August 2025)
- [#1349](https://github.com/wiremock/WireMock.Net/pull/1349) - Add AtPath and AtAbsolutePath to Assertions projects [feature] contributed by [StefH](https://github.com/StefH)
- [#1348](https://github.com/wiremock/WireMock.Net/issues/1348) - Support AtPath and AtAbsolutePath in WireMockAssertions [feature]
# 1.10.0 (18 August 2025)
- [#1344](https://github.com/wiremock/WireMock.Net/pull/1344) - Add new package WireMock.Net.Extensions.Routing which provides minimal-API-style routing for WireMock.Net [feature] contributed by [GennadyGS](https://github.com/GennadyGS)
- [#1340](https://github.com/wiremock/WireMock.Net/issues/1340) - Feature Request: Add minimal-API-style routing as Extension Package [feature]
# 1.9.1 (17 August 2025)
- [#1345](https://github.com/wiremock/WireMock.Net/pull/1345) - Add TimesInSameState to MappingModel [feature] contributed by [StefH](https://github.com/StefH)
- [#1347](https://github.com/wiremock/WireMock.Net/pull/1347) - Fix generating source code for Scenario and State [bug] contributed by [StefH](https://github.com/StefH)
- [#1343](https://github.com/wiremock/WireMock.Net/issues/1343) - MappingModel allows to configure the times for WillSetStateTo [feature]
- [#1346](https://github.com/wiremock/WireMock.Net/issues/1346) - Mapping: generated C# code is missing InScenario and WillSetStateTo [bug]
# 1.9.0 (10 August 2025)
- [#1334](https://github.com/wiremock/WireMock.Net/pull/1334) - Create GraphQL project [feature] contributed by [StefH](https://github.com/StefH)
# 1.8.18 (04 August 2025)
- [#1339](https://github.com/wiremock/WireMock.Net/pull/1339) - Fixes an issue with matching JSON bodies as bytes [bug] contributed by [smfields](https://github.com/smfields)
- [#1338](https://github.com/wiremock/WireMock.Net/issues/1338) - Specifying .WithBody(byte[]) fails to match for JSON bodies [bug]
# 1.8.17 (23 July 2025)
- [#1337](https://github.com/wiremock/WireMock.Net/pull/1337) - Make CSharpCodeMatcher public [bug] contributed by [StefH](https://github.com/StefH)
- [#1336](https://github.com/wiremock/WireMock.Net/issues/1336) - Is CSharpCodeMatcher actually usable? [bug]
# 1.8.16 (19 July 2025)
- [#1332](https://github.com/wiremock/WireMock.Net/pull/1332) - Use correct Handlebars.Net.Helpers.Xslt (2.5.2) [bug] contributed by [StefH](https://github.com/StefH)
- [#1328](https://github.com/wiremock/WireMock.Net/issues/1328) - ReflectionTypeLoadException occurs when running multiple unit tests (Nunit 3) [bug]
# 1.8.15 (18 July 2025)
- [#1331](https://github.com/wiremock/WireMock.Net/pull/1331) - Correctly map the Pact Interaction Description property [bug] contributed by [StefH](https://github.com/StefH)
- [#1330](https://github.com/wiremock/WireMock.Net/issues/1330) - Generated Pact-compatible consumer contract does not contain description [bug]
# 1.8.14 (13 July 2025)
- [#1325](https://github.com/wiremock/WireMock.Net/pull/1325) - Add method CreateHttpClientFactory [feature] contributed by [StefH](https://github.com/StefH)
- [#1326](https://github.com/wiremock/WireMock.Net/pull/1326) - Implement IMimeMessageData [feature] contributed by [StefH](https://github.com/StefH)
- [#1329](https://github.com/wiremock/WireMock.Net/pull/1329) - Fix HandlebarsContext.ParseAndEvaluate [bug] contributed by [StefH](https://github.com/StefH)
- [#1327](https://github.com/wiremock/WireMock.Net/issues/1327) - Response Body Does Not Evaluate Multiple Handlebars Templates [bug]
# 1.8.13 (23 June 2025)
- [#1322](https://github.com/wiremock/WireMock.Net/pull/1322) - Add Scenario set State method [feature] contributed by [StefH](https://github.com/StefH)
- [#1321](https://github.com/wiremock/WireMock.Net/issues/1321) - Feature: Setting individual scenario state via Admin API [feature]
# 1.8.12 (15 June 2025)
- [#1317](https://github.com/wiremock/WireMock.Net/pull/1317) - Set description when converting MappingModel to IRespondWithAProvider [feature] contributed by [BodrickLight](https://github.com/BodrickLight)
- [#1320](https://github.com/wiremock/WireMock.Net/pull/1320) - Fix TypeLoader [bug] contributed by [StefH](https://github.com/StefH)

View File

@@ -4,7 +4,7 @@
</PropertyGroup>
<PropertyGroup>
<VersionPrefix>1.8.12</VersionPrefix>
<VersionPrefix>1.23.0</VersionPrefix>
<PackageIcon>WireMock.Net-Logo.png</PackageIcon>
<PackageProjectUrl>https://github.com/wiremock/WireMock.Net</PackageProjectUrl>
<PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
@@ -13,7 +13,7 @@
<RepositoryUrl>https://github.com/wiremock/WireMock.Net</RepositoryUrl>
<ApplicationIcon>../../resources/WireMock.Net-Logo.ico</ApplicationIcon>
<PackageReadmeFile>PackageReadme.md</PackageReadmeFile>
<LangVersion>12.0</LangVersion>
<LangVersion>12</LangVersion>
<Nullable>enable</Nullable>
</PropertyGroup>
@@ -51,17 +51,17 @@
<ItemGroup>
<!-- CVE-2019-0820 -->
<PackageReference Include="System.Text.RegularExpressions" Version="4.3.1" />
<!--<PackageReference Include="System.Text.RegularExpressions" Version="4.3.1" />-->
<PackageReference Include="JetBrains.Annotations" Version="2024.3.0" PrivateAssets="All" />
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="8.0.0" PrivateAssets="All" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net8.0' ">
<PackageReference Include="SonarAnalyzer.CSharp" Version="9.32.0.97167">
<!--<PackageReference Include="SonarAnalyzer.CSharp" Version="10.15.0.120848">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</PackageReference>-->
<!-- <PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.556">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>

View File

@@ -1,6 +1,6 @@
rem https://github.com/StefH/GitHubReleaseNotes
SET version=1.8.12
SET version=1.23.0
GitHubReleaseNotes --output CHANGELOG.md --skip-empty-releases --exclude-labels wontfix test question invalid doc duplicate example environment --version %version% --token %GH_TOKEN%

View File

@@ -15,48 +15,48 @@ Lightweight Http Mocking Server for .NET, inspired by WireMock.org (from the Jav
### :star: Stubbing
A core feature of WireMock.Net is the ability to return predefined HTTP responses for requests matching criteria.
See [Wiki : Stubbing](https://github.com/wiremock/WireMock.Net/wiki/Stubbing).
See [Stubbing](https://wiremock.org/dotnet/stubbing).
### :star: Request Matching
WireMock.Net support advanced request-matching logic, see [Wiki : Request Matching](https://github.com/wiremock/WireMock.Net/wiki/Request-Matching).
WireMock.Net support advanced request-matching logic, see [Request Matching](https://wiremock.org/dotnet/request-matching).
### :star: Response Templating
The response which is returned WireMock.Net can be changed using templating. This is described here [Wiki : Response Templating](https://github.com/wiremock/WireMock.Net/wiki/Response-Templating).
The response which is returned WireMock.Net can be changed using templating. This is described here [Response Templating](https://wiremock.org/dotnet/response-templating).
### :star: Admin API Reference
The WireMock admin API provides functionality to define the mappings via a http interface see [Wiki : Admin API Reference](https://github.com/StefH/WireMock.Net/wiki/Admin-API-Reference).
The WireMock admin API provides functionality to define the mappings via a http interface see [Admin API Reference](https://wiremock.org/dotnet/admin-api-reference).
### :star: Using
WireMock.Net can be used in several ways:
#### UnitTesting
You can use your favorite test framework and use WireMock within your tests, see
[Wiki : UnitTesting](https://github.com/StefH/WireMock.Net/wiki/Using-WireMock-in-UnitTests).
[UnitTesting](https://wiremock.org/dotnet/using-wiremock-in-unittests).
### Unit/Integration Testing using Testcontainers.DotNet
See [Wiki : WireMock.Net.Testcontainers](https://github.com/wiremock/WireMock.Net/wiki/Using-WireMock.Net.Testcontainers) on how to build a WireMock.Net Docker container which can be used in Unit/Integration testing.
See [WireMock.Net.Testcontainers](https://wiremock.org/dotnet/using-wiremock-net-testcontainers/) on how to build a WireMock.Net Docker container which can be used in Unit/Integration testing.
### Unit/Integration Testing using an an Aspire Distributed Application
See [Wiki : WireMock.Net.Aspire](https://github.com/wiremock/WireMock.Net/wiki/Using-WireMock.Net.Aspire) on how to use WireMock.Net as an Aspire Hosted application to do Unit/Integration testing.
See [WireMock.Net.Aspire](https://wiremock.org/dotnet/using-wiremock-net-Aspire) on how to use WireMock.Net as an Aspire Hosted application to do Unit/Integration testing.
#### As a dotnet tool
It's simple to install WireMock.Net as (global) dotnet tool, see [Wiki : dotnet tool](https://github.com/StefH/WireMock.Net/wiki/WireMock-as-dotnet-tool).
It's simple to install WireMock.Net as (global) dotnet tool, see [dotnet tool](https://wiremock.org/dotnet/wiremock-as-dotnet-tool).
#### As standalone process / console application
This is quite straight forward to launch a mock server within a console application, see [Wiki : Standalone Process](https://github.com/StefH/WireMock.Net/wiki/WireMock-as-a-standalone-process).
This is quite straight forward to launch a mock server within a console application, see [Standalone Process](https://wiremock.org/dotnet/wiremock-as-a-standalone-process).
#### As a Windows Service
You can also run WireMock.Net as a Windows Service, follow this [WireMock-as-a-Windows-Service](https://github.com/wiremock/WireMock.Net/wiki/WireMock-as-a-Windows-Service).
You can also run WireMock.Net as a Windows Service, follow this [Windows Service](https://wiremock.org/dotnet/wiremock-as-a-windows-service).
#### As a Web Job in Azure or application in IIS
See this link [WireMock-as-a-(Azure)-Web-App](https://github.com/wiremock/WireMock.Net/wiki/WireMock-as-a-(Azure)-Web-App)
See this link [WireMock-as-a-(Azure)-Web-App](https://wiremock.org/dotnet/wiremock-as-a-azure-web-app/)
#### In a docker container
There is also a Linux and Windows-Nano container available at [hub.docker.com](https://hub.docker.com/r/sheyenrath).
For more details see also [Docker](https://github.com/wiremock/WireMock.Net-docker).
#### HTTPS / SSL
More details on using HTTPS (SSL) can be found here [Wiki : HTTPS](https://github.com/wiremock/WireMock.Net/wiki/Using-HTTPS-(SSL))
More details on using HTTPS (SSL) can be found here [HTTPS](https://wiremock.org/dotnet/using-https-ssl/)
## :books: Documentation
For more info, see also this WIKI page: [What is WireMock.Net](https://github.com/wiremock/WireMock.Net/wiki/What-Is-WireMock.Net).
For more info, see also this documentation page: [What is WireMock.Net](https://wiremock.org/dotnet/what-is-wiremock-net/).

View File

@@ -1,6 +1,7 @@
# 1.8.12 (15 June 2025)
- #1317 Set description when converting MappingModel to IRespondWithAProvider [feature]
- #1320 Fix TypeLoader [bug]
- #1319 Why is IRequestMessage.BodyAsMimeMessage null in new versions (&gt; 1.8.7) [bug]
# 1.23.0 (05 January 2026)
- #1414 Pass the parameter matchOperator in Request.WithPath to its inner calls [bug]
- #1416 Fix: Pass AllowedHandlebarsHelpers configuration to Handlebars.Net.Helpers library
- #1413 Parameter `matchOperator` is not respected in the method Request.WithPath [bug]
- #1415 HandlebarsSettings AllowedHandlebarsHelpers Configuration Not Applied [bug]
The full release notes can be found here: https://github.com/wiremock/WireMock.Net/blob/master/CHANGELOG.md

View File

@@ -1,7 +1,11 @@
# ![Project Icon](./resources/logo_32x32.png) WireMock.Net
A C# .NET version based on [mock4net](https://github.com/alexvictoor/mock4net) which mimics the functionality from the JAVA based [WireMock](http://wiremock.org).
A C# .NET version based on [mock4net](https://github.com/alexvictoor/mock4net) which mimics functionality from the original Java based WireMock.
For more info, see also this WIKI page: [What is WireMock.Net](https://github.com/wiremock/WireMock.Net/wiki/What-Is-WireMock.Net).
---
### :books: Full documentation can now be found at [wiremock.org](https://wiremock.org/dotnet)
---
## :star: Key Features
* HTTP response stubbing, matchable on URL/Path, headers, cookies and body content patterns
@@ -29,7 +33,7 @@ For more info, see also this WIKI page: [What is WireMock.Net](https://github.co
| &nbsp;&nbsp;**Issues** | [![GitHub issues](https://img.shields.io/github/issues/WireMock-Net/WireMock.Net.svg)](https://github.com/wiremock/WireMock.Net/issues) |
| | |
| ***Quality*** | &nbsp; |
| &nbsp;&nbsp;**Build Azure** | [![Build Status Azure](https://stef.visualstudio.com/WireMock.Net/_apis/build/status/WireMock.Net)](https://stef.visualstudio.com/WireMock.Net/_build/latest?definitionId=7) |
| &nbsp;&nbsp;**Build Azure** | [![Build Status Azure](https://stef.visualstudio.com/WireMock.Net/_apis/build/status/WireMock.Net)](https://stef.visualstudio.com/WireMock.Net/_build/latest?definitionId=61) |
| &nbsp;&nbsp;**Quality** | [![Sonar Quality Gate](https://sonarcloud.io/api/project_badges/measure?project=WireMock-Net_WireMock.Net&metric=alert_status)](https://sonarcloud.io/project/issues?id=WireMock-Net_WireMock.Net) [![CodeFactor](https://www.codefactor.io/repository/github/wiremock/wiremock.net/badge)](https://www.codefactor.io/repository/github/wiremock/wiremock.net) |
| &nbsp;&nbsp;**Sonar Bugs** | [![Sonar Bugs](https://sonarcloud.io/api/project_badges/measure?project=WireMock-Net_WireMock.Net&metric=bugs)](https://sonarcloud.io/project/issues?id=WireMock-Net_WireMock.Net&resolved=false&types=BUG) [![Sonar Code Smells](https://sonarcloud.io/api/project_badges/measure?project=WireMock-Net_WireMock.Net&metric=code_smells)](https://sonarcloud.io/project/issues?id=WireMock-Net_WireMock.Net&resolved=false&types=CODE_SMELL) |
| &nbsp;&nbsp;**Coverage** | [![Sonar Coverage](https://sonarcloud.io/api/project_badges/measure?project=WireMock-Net_WireMock.Net&metric=coverage)](https://sonarcloud.io/component_measures?id=WireMock-Net_WireMock.Net&metric=coverage) [![codecov](https://codecov.io/gh/wiremock/WireMock.Net/branch/master/graph/badge.svg)](https://codecov.io/gh/wiremock/WireMock.Net)|
@@ -37,7 +41,7 @@ For more info, see also this WIKI page: [What is WireMock.Net](https://github.co
### :package: NuGet packages
| | Official | Preview [:information_source:](https://github.com/wiremock/WireMock.Net/wiki/MyGet-preview-versions) |
| | Official | Preview [:information_source:](https://wiremock.org/dotnet/MyGet-preview-versions) |
| - | - | - |
| &nbsp;&nbsp;**WireMock.Net** | [![NuGet Badge WireMock.Net](https://img.shields.io/nuget/v/WireMock.Net)](https://www.nuget.org/packages/WireMock.Net) | [![MyGet Badge WireMock.Net](https://img.shields.io/myget/wiremock-net/vpre/WireMock.Net?includePreReleases=true&label=MyGet)](https://www.myget.org/feed/wiremock-net/package/nuget/WireMock.Net)
| &nbsp;&nbsp;**WireMock.Net.Minimal** 🔺| [![NuGet Badge WireMock.Net.Minimal](https://img.shields.io/nuget/v/WireMock.Net.Minimal)](https://www.nuget.org/packages/WireMock.Net.Minimal) | [![MyGet Badge WireMock.Net](https://img.shields.io/myget/wiremock-net/vpre/WireMock.Net.Minimal?includePreReleases=true&label=MyGet)](https://www.myget.org/feed/wiremock-net/package/nuget/WireMock.Net.Minimal)
@@ -49,18 +53,23 @@ For more info, see also this WIKI page: [What is WireMock.Net](https://github.co
| &nbsp;&nbsp;**WireMock.Net.AwesomeAssertions** | [![NuGet Badge WireMock.Net.AwesomeAssertions](https://img.shields.io/nuget/v/WireMock.Net.AwesomeAssertions)](https://www.nuget.org/packages/WireMock.Net.AwesomeAssertions) | [![MyGet Badge WireMock.Net.AwesomeAssertions](https://img.shields.io/myget/wiremock-net/vpre/WireMock.Net.AwesomeAssertions?includePreReleases=true&label=MyGet)](https://www.myget.org/feed/wiremock-net/package/nuget/WireMock.Net.AwesomeAssertions)
| &nbsp;&nbsp;**WireMock.Net.FluentAssertions** | [![NuGet Badge WireMock.Net.FluentAssertions](https://img.shields.io/nuget/v/WireMock.Net.FluentAssertions)](https://www.nuget.org/packages/WireMock.Net.FluentAssertions) | [![MyGet Badge WireMock.Net.FluentAssertions](https://img.shields.io/myget/wiremock-net/vpre/WireMock.Net.FluentAssertions?includePreReleases=true&label=MyGet)](https://www.myget.org/feed/wiremock-net/package/nuget/WireMock.Net.FluentAssertions)
| &nbsp;&nbsp;**WireMock.Net.xUnit** | [![NuGet Badge WireMock.Net.xUnit](https://img.shields.io/nuget/v/WireMock.Net.xUnit)](https://www.nuget.org/packages/WireMock.Net.xUnit) | [![MyGet Badge WireMock.Net.xUnit](https://img.shields.io/myget/wiremock-net/vpre/WireMock.Net.xUnit?includePreReleases=true&label=MyGet)](https://www.myget.org/feed/wiremock-net/package/nuget/WireMock.Net.xUnit)
| &nbsp;&nbsp;**WireMock.Net.xUnit.v3** | [![NuGet Badge WireMock.Net.xUnit](https://img.shields.io/nuget/v/WireMock.Net.xUnit.v3)](https://www.nuget.org/packages/WireMock.Net.xUnit.v3) | [![MyGet Badge WireMock.Net.xUnit](https://img.shields.io/myget/wiremock-net/vpre/WireMock.Net.xUnit.v3?includePreReleases=true&label=MyGet)](https://www.myget.org/feed/wiremock-net/package/nuget/WireMock.Net.xUnit.v3)
| &nbsp;&nbsp;**WireMock.Net.TUnit** | [![NuGet Badge WireMock.Net.TUnit](https://img.shields.io/nuget/v/WireMock.Net.TUnit)](https://www.nuget.org/packages/WireMock.Net.TUnit) | [![MyGet Badge WireMock.Net.TUnit](https://img.shields.io/myget/wiremock-net/vpre/WireMock.Net.TUnit?includePreReleases=true&label=MyGet)](https://www.myget.org/feed/wiremock-net/package/nuget/WireMock.Net.TUnit)
| &nbsp;&nbsp;**WireMock.Net.NUnit** | [![NuGet Badge WireMock.Net.NUnit](https://img.shields.io/nuget/v/WireMock.Net.NUnit)](https://www.nuget.org/packages/WireMock.Net.NUnit) | [![MyGet Badge WireMock.Net.TUnit](https://img.shields.io/myget/wiremock-net/vpre/WireMock.Net.NUnit?includePreReleases=true&label=MyGet)](https://www.myget.org/feed/wiremock-net/package/nuget/WireMock.Net.NUnit)
| | | |
| &nbsp;&nbsp;**WireMock.Net.Extensions.Routing** | [![NuGet Badge WireMock.Net.Extensions.Routing](https://img.shields.io/nuget/v/WireMock.Net.Extensions.Routing)](https://www.nuget.org/packages/WireMock.Net.Extensions.Routing) | [![MyGet Badge WireMock.Net.Extensions.Routing](https://img.shields.io/myget/wiremock-net/vpre/WireMock.Net.Extensions.Routing?includePreReleases=true&label=MyGet)](https://www.myget.org/feed/wiremock-net/package/nuget/WireMock.Net.Extensions.Routing)
| &nbsp;&nbsp;**WireMock.Net.Matchers.CSharpCode** | [![NuGet Badge WireMock.Net.Matchers.CSharpCode](https://img.shields.io/nuget/v/WireMock.Net.Matchers.CSharpCode)](https://www.nuget.org/packages/WireMock.Net.Matchers.CSharpCode) | [![MyGet Badge WireMock.Net.Matchers.CSharpCode](https://img.shields.io/myget/wiremock-net/vpre/WireMock.Net.Matchers.CSharpCode?includePreReleases=true&label=MyGet)](https://www.myget.org/feed/wiremock-net/package/nuget/WireMock.Net.Matchers.CSharpCode)
| &nbsp;&nbsp;**WireMock.Net.OpenApiParser** | [![NuGet Badge WireMock.Net.OpenApiParser](https://img.shields.io/nuget/v/WireMock.Net.OpenApiParser)](https://www.nuget.org/packages/WireMock.Net.OpenApiParser) | [![MyGet Badge WireMock.Net.OpenApiParser](https://img.shields.io/myget/wiremock-net/vpre/WireMock.Net.OpenApiParser?includePreReleases=true&label=MyGet)](https://www.myget.org/feed/wiremock-net/package/nuget/WireMock.Net.OpenApiParser)
| &nbsp;&nbsp;**WireMock.Net.MimePart** | [![NuGet Badge WireMock.Net.MimePart](https://img.shields.io/nuget/v/WireMock.Net.MimePart)](https://www.nuget.org/packages/WireMock.Net.MimePart) | [![MyGet Badge WireMock.Net.MimePart](https://img.shields.io/myget/wiremock-net/vpre/WireMock.Net.MimePart?includePreReleases=true&label=MyGet)](https://www.myget.org/feed/wiremock-net/package/nuget/WireMock.Net.MimePart)
| &nbsp;&nbsp;**WireMock.Net.GraphQL** | [![NuGet Badge WireMock.Net.GraphQL](https://img.shields.io/nuget/v/WireMock.Net.GraphQL)](https://www.nuget.org/packages/WireMock.Net.GraphQL) | [![MyGet Badge WireMock.Net.GraphQL](https://img.shields.io/myget/wiremock-net/vpre/WireMock.Net.GraphQL?includePreReleases=true&label=MyGet)](https://www.myget.org/feed/wiremock-net/package/nuget/WireMock.Net.GraphQL)
| &nbsp;&nbsp;**WireMock.Net.ProtoBuf** | [![NuGet Badge WireMock.Net.ProtoBuf](https://img.shields.io/nuget/v/WireMock.Net.ProtoBuf)](https://www.nuget.org/packages/WireMock.Net.ProtoBuf) | [![MyGet Badge WireMock.Net.ProtoBuf](https://img.shields.io/myget/wiremock-net/vpre/WireMock.Net.ProtoBuf?includePreReleases=true&label=MyGet)](https://www.myget.org/feed/wiremock-net/package/nuget/WireMock.Net.ProtoBuf)
| | | |
| &nbsp;&nbsp;**WireMock.Net.RestClient** | [![NuGet Badge WireMock.Net.RestClient](https://img.shields.io/nuget/v/WireMock.Net.RestClient)](https://www.nuget.org/packages/WireMock.Net.RestClient) | [![MyGet Badge WireMock.Net.RestClient](https://img.shields.io/myget/wiremock-net/vpre/WireMock.Net.RestClient?includePreReleases=true&label=MyGet)](https://www.myget.org/feed/wiremock-net/package/nuget/WireMock.Net.RestClient)
| &nbsp;&nbsp;**WireMock.Org.RestClient** | [![NuGet Badge WireMock.Org.RestClient](https://img.shields.io/nuget/v/WireMock.Org.RestClient)](https://www.nuget.org/packages/WireMock.Org.RestClient) | [![MyGet Badge WireMock.Org.RestClient](https://img.shields.io/myget/wiremock-net/vpre/WireMock.Org.RestClient?includePreReleases=true&label=MyGet)](https://www.myget.org/feed/wiremock-net/package/nuget/WireMock.Org.RestClient)
<br />
🔺 **WireMock.Net.Minimal** does not include: **WireMock.Net.MimePart**
🔺 **WireMock.Net.Minimal** does not include *WireMock.Net.MimePart*, *WireMock.Net.GraphQL* and *WireMock.Net.ProtoBuf*.
---
@@ -86,49 +95,57 @@ To still enable this feature, you need to add the `Environment` category to the
---
## :memo: Development
For the supported frameworks and build information, see [this](https://github.com/wiremock/WireMock.Net/wiki/Development-Information) page.
For the supported frameworks and build information, see [this](https://wiremock.org/dotnet/development-information) page.
## :star: Stubbing
A core feature of WireMock.Net is the ability to return predefined HTTP responses for requests matching criteria.
See [Wiki : Stubbing](https://github.com/wiremock/WireMock.Net/wiki/Stubbing).
See [Stubbing](https://wiremock.org/dotnet/stubbing).
## :star: Request Matching
WireMock.Net support advanced request-matching logic, see [Wiki : Request Matching](https://github.com/wiremock/WireMock.Net/wiki/Request-Matching).
WireMock.Net support advanced request-matching logic, see [Request Matching](https://wiremock.org/dotnet/request-matching).
## :star: Response Templating
The response which is returned WireMock.Net can be changed using templating. This is described here [Wiki : Response Templating](https://github.com/wiremock/WireMock.Net/wiki/Response-Templating).
The response which is returned WireMock.Net can be changed using templating. This is described here [Response Templating](https://wiremock.org/dotnet/response-templating).
## :star: Admin API Reference
The WireMock admin API provides functionality to define the mappings via a http interface see [Wiki : Admin API Reference](https://github.com/StefH/WireMock.Net/wiki/Admin-API-Reference).
The WireMock admin API provides functionality to define the mappings via a http interface see [Admin API Reference](https://wiremock.org/dotnet/admin-api-reference).
## :star: Using
WireMock.Net can be used in several ways:
### UnitTesting
You can use your favorite test framework and use WireMock within your tests, see
[Wiki : UnitTesting](https://github.com/StefH/WireMock.Net/wiki/Using-WireMock-in-UnitTests).
[UnitTesting](https://wiremock.org/dotnet/using-wiremock-in-unittests).
### Unit/Integration Testing using Testcontainers.DotNet
See [Wiki : WireMock.Net.Testcontainers](https://github.com/wiremock/WireMock.Net/wiki/Using-WireMock.Net.Testcontainers) on how to build a WireMock.Net Docker container which can be used in Unit/Integration testing.
See [WireMock.Net.Testcontainers](https://wiremock.org/dotnet/using-wiremock-net-testcontainers/) on how to build a WireMock.Net Docker container which can be used in Unit/Integration testing.
### Unit/Integration Testing using an an Aspire Distributed Application
See [Wiki : WireMock.Net.Aspire](https://github.com/wiremock/WireMock.Net/wiki/Using-WireMock.Net.Aspire) on how to use WireMock.Net as an Aspire Hosted application to do Unit/Integration testing.
See [WireMock.Net.Aspire](https://wiremock.org/dotnet/using-wiremock-net-Aspire) on how to use WireMock.Net as an Aspire Hosted application to do Unit/Integration testing.
### As a dotnet tool
It's simple to install WireMock.Net as (global) dotnet tool, see [Wiki : dotnet tool](https://github.com/StefH/WireMock.Net/wiki/WireMock-as-dotnet-tool).
It's simple to install WireMock.Net as (global) dotnet tool, see [dotnet tool](https://wiremock.org/dotnet/wiremock-as-dotnet-tool).
### As standalone process / console application
This is quite straight forward to launch a mock server within a console application, see [Wiki : Standalone Process](https://github.com/StefH/WireMock.Net/wiki/WireMock-as-a-standalone-process).
This is quite straight forward to launch a mock server within a console application, see [Standalone Process](https://wiremock.org/dotnet/wiremock-as-a-standalone-process).
### As a Windows Service
You can also run WireMock.Net as a Windows Service, follow this [WireMock-as-a-Windows-Service](https://github.com/wiremock/WireMock.Net/wiki/WireMock-as-a-Windows-Service).
You can also run WireMock.Net as a Windows Service, follow this [Windows Service](https://wiremock.org/dotnet/wiremock-as-a-windows-service).
### As a Web Job in Azure or application in IIS
See this link [WireMock-as-a-(Azure)-Web-App](https://github.com/wiremock/WireMock.Net/wiki/WireMock-as-a-(Azure)-Web-App)
See this link [WireMock-as-a-(Azure)-Web-App](https://wiremock.org/dotnet/wiremock-as-a-azure-web-app/)
### In a docker container
There is also a Linux and Windows-Nano container available at [hub.docker.com](https://hub.docker.com/r/sheyenrath).
For more details see also [Docker](https://github.com/wiremock/WireMock.Net-docker).
#### HTTPS / SSL
More details on using HTTPS (SSL) can be found here [Wiki : HTTPS](https://github.com/wiremock/WireMock.Net/wiki/Using-HTTPS-(SSL))
### HTTPS / SSL
More details on using HTTPS (SSL) can be found here [HTTPS](https://wiremock.org/dotnet/using-https-ssl/)
## :books: Documentation
For more info, see also this documentation page: [What is WireMock.Net](https://wiremock.org/dotnet/what-is-wiremock-net/).
---
## Powered by
[![JetBrains logo.](https://resources.jetbrains.com/storage/products/company/brand/logos/jetbrains.svg)](https://jb.gg/OpenSource)

View File

@@ -1,7 +1,7 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.0.31521.260
# Visual Studio Version 18
VisualStudioVersion = 18.0.11205.157
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{8F890C6F-9ACC-438D-928A-AD61CDA862F2}"
EndProject
@@ -68,14 +68,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WireMock.Org.RestClient", "
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WireMock.Org.Abstractions", "src\WireMock.Org.Abstractions\WireMock.Org.Abstractions.csproj", "{3BA5109E-5F30-4CC2-B699-02EC82560AA6}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WireMock.Net.WebApplication.NET6", "examples\WireMock.Net.WebApplication.NET6\WireMock.Net.WebApplication.NET6.csproj", "{3F7AA023-6833-4856-A08A-4B5717B592B8}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WireMock.Net.Console.Proxy.NETCoreApp", "examples\WireMock.Net.Console.Proxy.NETCoreApp\WireMock.Net.Console.Proxy.NETCoreApp.csproj", "{670C7562-C154-442E-A249-7D26849BCD13}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WireMock.Net.xUnit", "src\WireMock.Net.xUnit\WireMock.Net.xUnit.csproj", "{0DE0954F-8C00-4E8D-B94A-4361FC1CBE44}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WireMock.Net.Console.NET6.WithCertificate", "examples\WireMock.Net.Console.NET6.WithCertificate\WireMock.Net.Console.NET6.WithCertificate.csproj", "{7C2A9DE8-C89F-4841-9058-6B9BF81E5E34}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WireMockAzureQueueExample", "examples\WireMockAzureQueueExample\WireMockAzureQueueExample.csproj", "{BAA9EC2A-874B-45CE-8E51-A73622DC7F3D}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WireMock.Net.Testcontainers", "src\WireMock.Net.Testcontainers\WireMock.Net.Testcontainers.csproj", "{12B016A5-9D8B-4EFE-96C2-CA51BE43367D}"
@@ -136,6 +132,22 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WireMock.Net.ConsoleApp.Usi
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WireMock.Net.Tests.UsingNuGet", "test\WireMock.Net.Tests.UsingNuGet\WireMock.Net.Tests.UsingNuGet.csproj", "{BBA332C6-28A9-42E7-9C4D-A0816E52A198}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WireMock.Net.GraphQL", "src\WireMock.Net.GraphQL\WireMock.Net.GraphQL.csproj", "{B6269AAC-170A-4346-8B9A-444DED3D9A45}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WireMock.Net.Extensions.Routing.Tests", "test\WireMock.Net.Extensions.Routing.Tests\WireMock.Net.Extensions.Routing.Tests.csproj", "{3FCBCA9C-9DB0-4A96-B47E-30470764CC9C}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WireMock.Net.Extensions.Routing", "src\WireMock.Net.Extensions.Routing\WireMock.Net.Extensions.Routing.csproj", "{1E874C8F-08A2-493B-8421-619F9A6E9E77}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WireMock.Net.ProtoBuf", "src\WireMock.Net.ProtoBuf\WireMock.Net.ProtoBuf.csproj", "{B47413AA-55D3-49A7-896A-17ADBFF72407}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WireMock.Net.xUnit.v3", "src\WireMock.Net.xUnit.v3\WireMock.Net.xUnit.v3.csproj", "{4F46BD02-BEBC-4B2D-B857-4169AD1FB067}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WireMock.Net.NUnit", "src\WireMock.Net.NUnit\WireMock.Net.NUnit.csproj", "{2DBBD70D-8051-441F-92BB-FF9B8B4B4982}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WireMock.Net.Console.NET8.WithCertificate", "examples\WireMock.Net.Console.NET8.WithCertificate\WireMock.Net.Console.NET8.WithCertificate.csproj", "{2D86546D-8A24-0A55-C962-2071BD299E05}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WireMock.Net.WebApplication.IIS", "examples\WireMock.Net.WebApplication.IIS\WireMock.Net.WebApplication.IIS.csproj", "{5E6E9FA7-9135-7B82-2CCD-8CA87AC8043C}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -198,10 +210,6 @@ Global
{3BA5109E-5F30-4CC2-B699-02EC82560AA6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3BA5109E-5F30-4CC2-B699-02EC82560AA6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3BA5109E-5F30-4CC2-B699-02EC82560AA6}.Release|Any CPU.Build.0 = Release|Any CPU
{3F7AA023-6833-4856-A08A-4B5717B592B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3F7AA023-6833-4856-A08A-4B5717B592B8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3F7AA023-6833-4856-A08A-4B5717B592B8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3F7AA023-6833-4856-A08A-4B5717B592B8}.Release|Any CPU.Build.0 = Release|Any CPU
{670C7562-C154-442E-A249-7D26849BCD13}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{670C7562-C154-442E-A249-7D26849BCD13}.Debug|Any CPU.Build.0 = Debug|Any CPU
{670C7562-C154-442E-A249-7D26849BCD13}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -210,10 +218,6 @@ Global
{0DE0954F-8C00-4E8D-B94A-4361FC1CBE44}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0DE0954F-8C00-4E8D-B94A-4361FC1CBE44}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0DE0954F-8C00-4E8D-B94A-4361FC1CBE44}.Release|Any CPU.Build.0 = Release|Any CPU
{7C2A9DE8-C89F-4841-9058-6B9BF81E5E34}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7C2A9DE8-C89F-4841-9058-6B9BF81E5E34}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7C2A9DE8-C89F-4841-9058-6B9BF81E5E34}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7C2A9DE8-C89F-4841-9058-6B9BF81E5E34}.Release|Any CPU.Build.0 = Release|Any CPU
{BAA9EC2A-874B-45CE-8E51-A73622DC7F3D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{BAA9EC2A-874B-45CE-8E51-A73622DC7F3D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BAA9EC2A-874B-45CE-8E51-A73622DC7F3D}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -326,6 +330,38 @@ Global
{BBA332C6-28A9-42E7-9C4D-A0816E52A198}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BBA332C6-28A9-42E7-9C4D-A0816E52A198}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BBA332C6-28A9-42E7-9C4D-A0816E52A198}.Release|Any CPU.Build.0 = Release|Any CPU
{B6269AAC-170A-4346-8B9A-444DED3D9A45}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B6269AAC-170A-4346-8B9A-444DED3D9A45}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B6269AAC-170A-4346-8B9A-444DED3D9A45}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B6269AAC-170A-4346-8B9A-444DED3D9A45}.Release|Any CPU.Build.0 = Release|Any CPU
{3FCBCA9C-9DB0-4A96-B47E-30470764CC9C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3FCBCA9C-9DB0-4A96-B47E-30470764CC9C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3FCBCA9C-9DB0-4A96-B47E-30470764CC9C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3FCBCA9C-9DB0-4A96-B47E-30470764CC9C}.Release|Any CPU.Build.0 = Release|Any CPU
{1E874C8F-08A2-493B-8421-619F9A6E9E77}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1E874C8F-08A2-493B-8421-619F9A6E9E77}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1E874C8F-08A2-493B-8421-619F9A6E9E77}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1E874C8F-08A2-493B-8421-619F9A6E9E77}.Release|Any CPU.Build.0 = Release|Any CPU
{B47413AA-55D3-49A7-896A-17ADBFF72407}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B47413AA-55D3-49A7-896A-17ADBFF72407}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B47413AA-55D3-49A7-896A-17ADBFF72407}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B47413AA-55D3-49A7-896A-17ADBFF72407}.Release|Any CPU.Build.0 = Release|Any CPU
{2D86546D-8A24-0A55-C962-2071BD299E05}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2D86546D-8A24-0A55-C962-2071BD299E05}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2D86546D-8A24-0A55-C962-2071BD299E05}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2D86546D-8A24-0A55-C962-2071BD299E05}.Release|Any CPU.Build.0 = Release|Any CPU
{5E6E9FA7-9135-7B82-2CCD-8CA87AC8043C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5E6E9FA7-9135-7B82-2CCD-8CA87AC8043C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5E6E9FA7-9135-7B82-2CCD-8CA87AC8043C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5E6E9FA7-9135-7B82-2CCD-8CA87AC8043C}.Release|Any CPU.Build.0 = Release|Any CPU
{4F46BD02-BEBC-4B2D-B857-4169AD1FB067}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4F46BD02-BEBC-4B2D-B857-4169AD1FB067}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4F46BD02-BEBC-4B2D-B857-4169AD1FB067}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4F46BD02-BEBC-4B2D-B857-4169AD1FB067}.Release|Any CPU.Build.0 = Release|Any CPU
{2DBBD70D-8051-441F-92BB-FF9B8B4B4982}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2DBBD70D-8051-441F-92BB-FF9B8B4B4982}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2DBBD70D-8051-441F-92BB-FF9B8B4B4982}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2DBBD70D-8051-441F-92BB-FF9B8B4B4982}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -347,10 +383,8 @@ Global
{5B64F6CA-BF6B-4F67-BB2A-9C47E441703E} = {7EFB2C5B-1BB2-4AAF-BC9F-216ED80C594D}
{08B29DB1-FEFE-408A-AD0A-6BA6DDC8D70F} = {8F890C6F-9ACC-438D-928A-AD61CDA862F2}
{3BA5109E-5F30-4CC2-B699-02EC82560AA6} = {8F890C6F-9ACC-438D-928A-AD61CDA862F2}
{3F7AA023-6833-4856-A08A-4B5717B592B8} = {985E0ADB-D4B4-473A-AA40-567E279B7946}
{670C7562-C154-442E-A249-7D26849BCD13} = {985E0ADB-D4B4-473A-AA40-567E279B7946}
{0DE0954F-8C00-4E8D-B94A-4361FC1CBE44} = {8F890C6F-9ACC-438D-928A-AD61CDA862F2}
{7C2A9DE8-C89F-4841-9058-6B9BF81E5E34} = {985E0ADB-D4B4-473A-AA40-567E279B7946}
{BAA9EC2A-874B-45CE-8E51-A73622DC7F3D} = {985E0ADB-D4B4-473A-AA40-567E279B7946}
{12B016A5-9D8B-4EFE-96C2-CA51BE43367D} = {8F890C6F-9ACC-438D-928A-AD61CDA862F2}
{56A38798-C48B-4A4A-B805-071E05C02CE1} = {985E0ADB-D4B4-473A-AA40-567E279B7946}
@@ -379,6 +413,14 @@ Global
{BFEF8990-65B3-4274-310F-7355F0B84035} = {8F890C6F-9ACC-438D-928A-AD61CDA862F2}
{1F80A6E6-D146-4E40-9EA8-49DB8494239F} = {985E0ADB-D4B4-473A-AA40-567E279B7946}
{BBA332C6-28A9-42E7-9C4D-A0816E52A198} = {0BB8B634-407A-4610-A91F-11586990767A}
{B6269AAC-170A-4346-8B9A-444DED3D9A45} = {8F890C6F-9ACC-438D-928A-AD61CDA862F2}
{3FCBCA9C-9DB0-4A96-B47E-30470764CC9C} = {0BB8B634-407A-4610-A91F-11586990767A}
{1E874C8F-08A2-493B-8421-619F9A6E9E77} = {8F890C6F-9ACC-438D-928A-AD61CDA862F2}
{B47413AA-55D3-49A7-896A-17ADBFF72407} = {8F890C6F-9ACC-438D-928A-AD61CDA862F2}
{2D86546D-8A24-0A55-C962-2071BD299E05} = {985E0ADB-D4B4-473A-AA40-567E279B7946}
{5E6E9FA7-9135-7B82-2CCD-8CA87AC8043C} = {985E0ADB-D4B4-473A-AA40-567E279B7946}
{4F46BD02-BEBC-4B2D-B857-4169AD1FB067} = {8F890C6F-9ACC-438D-928A-AD61CDA862F2}
{2DBBD70D-8051-441F-92BB-FF9B8B4B4982} = {8F890C6F-9ACC-438D-928A-AD61CDA862F2}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {DC539027-9852-430C-B19F-FD035D018458}

View File

@@ -100,7 +100,7 @@ jobs:
- job: Windows_Build_Test
pool:
vmImage: 'windows-2022'
vmImage: 'windows-2025'
steps:
- task: UseDotNet@2
@@ -141,9 +141,13 @@ jobs:
dependsOn: Windows_Build_Test
pool:
vmImage: 'windows-2022'
vmImage: 'windows-2025'
steps:
- script: |
echo "BuildId = $(buildId)"
displayName: 'Print buildId'
- task: UseDotNet@2
displayName: Use .NET 8.0
inputs:

View File

@@ -1,5 +1,5 @@
pool:
vmImage: 'windows-2022'
vmImage: 'windows-2025'
variables:
Prerelease: ''

View File

@@ -21,4 +21,13 @@
<PackageReference Include="Aspire.Hosting.AppHost" Version="9.2.0" />
</ItemGroup>
</Project>
<ItemGroup>
<None Update="__admin\mappings\*.proto">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="__admin\mappings\*.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>

View File

@@ -4,12 +4,25 @@ var builder = DistributedApplication.CreateBuilder(args);
// IResourceBuilder<ProjectResource> apiService = builder.AddProject<Projects.AspireApp1_ApiService>("apiservice");
var mappingsPath = Path.Combine(Directory.GetCurrentDirectory(), "WireMockMappings");
var mappingsPath = Path.Combine(Directory.GetCurrentDirectory(), "__admin", "mappings");
IResourceBuilder<WireMockServerResource> apiService = builder
.AddWireMock("apiservice", WireMockServerArguments.DefaultPort)
//IResourceBuilder<WireMockServerResource> apiService1 = builder
// //.AddWireMock("apiservice", WireMockServerArguments.DefaultPort)
// .AddWireMock("apiservice1", "http://*:8081", "grpc://*:9091")
// .AsHttp2Service()
// .WithMappingsPath(mappingsPath)
// .WithReadStaticMappings()
// .WithWatchStaticMappings()
// .WithApiMappingBuilder(WeatherForecastApiMock.BuildAsync);
IResourceBuilder<WireMockServerResource> apiService2 = builder
.AddWireMock("apiservice", async args =>
{
args.WithAdditionalUrls("http://*:8081", "grpc://*:9093");
args.WithProtoDefinition("my-greeter", await File.ReadAllTextAsync(Path.Combine(mappingsPath, "greet.proto")));
})
.AsHttp2Service()
.WithMappingsPath(mappingsPath)
.WithReadStaticMappings()
.WithWatchStaticMappings()
.WithApiMappingBuilder(WeatherForecastApiMock.BuildAsync);
@@ -45,6 +58,7 @@ IResourceBuilder<WireMockServerResource> apiService = builder
builder.AddProject<Projects.AspireApp1_Web>("webfrontend")
.WithExternalHttpEndpoints()
.WithReference(apiService);
.WithReference(apiService2)
.WaitFor(apiService2);
builder.Build().Run();
await builder.Build().RunAsync();

View File

@@ -0,0 +1,21 @@
syntax = "proto3";
package greet;
service Greeter {
rpc SayHello (HelloRequest) returns (HelloReply);
}
message HelloRequest {
string name = 1;
}
message HelloReply {
string message = 1;
enum PhoneType {
none = 0;
mobile = 1;
home = 2;
}
PhoneType phoneType = 2;
}

View File

@@ -0,0 +1,40 @@
{
"Guid": "351f0240-bba0-4bcb-93c6-1feba0fe0004",
"Title": "ProtoBuf Mapping 4",
"Request": {
"Path": {
"Matchers": [
{
"Name": "WildcardMatcher",
"Pattern": "/greet.Greeter/SayHello",
"IgnoreCase": false
}
]
},
"Methods": [
"POST"
],
"Body": {
"Matcher": {
"Name": "ProtoBufMatcher",
"ProtoBufMessageType": "greet.HelloRequest"
}
}
},
"Response": {
"BodyAsJson": {
"message": "hello {{request.BodyAsJson.name}} {{request.method}}"
},
"UseTransformer": true,
"TransformerType": "Handlebars",
"TransformerReplaceNodeOptions": "EvaluateAndTryToConvert",
"Headers": {
"Content-Type": "application/grpc"
},
"TrailingHeaders": {
"grpc-status": "0"
},
"ProtoBufMessageType": "greet.HelloReply"
},
"ProtoDefinition": "my-greeter"
}

View File

@@ -7,7 +7,7 @@
<ItemGroup>
<ProjectReference Include="..\..\src\WireMock.Net\WireMock.Net.csproj" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="8.0.0" />
</ItemGroup>
<ItemGroup>

View File

@@ -132,7 +132,42 @@ namespace WireMock.Net.ConsoleApplication
}
""";
private const string TestSchema =
private const string TestSchemaQueryStudents =
"""
type Query {
students:[Student]
}
type Student {
id:ID!
firstName:String
lastName:String
fullName:String
}
""";
private const string TestSchemaQueryStudentById =
"""
type Query {
studentById(id:ID!):Student
}
type Student {
id:ID!
firstName:String
lastName:String
fullName:String
}
""";
private const string TestSchemaQueryGreeting =
"""
type Query {
greeting:String
}
""";
private const string TestSchemaMutationMessage =
"""
scalar DateTime
scalar MyCustomScalar
@@ -153,19 +188,6 @@ namespace WireMock.Net.ConsoleApplication
createAnotherMessage(x: MyCustomScalar, dt: DateTime): Message
updateMessage(id: ID!, input: MessageInput): Message
}
type Query {
greeting:String
students:[Student]
studentById(id:ID!):Student
}
type Student {
id:ID!
firstName:String
lastName:String
fullName:String
}
""";
private static void RunSse()
@@ -245,7 +267,7 @@ namespace WireMock.Net.ConsoleApplication
}
}
public static void Run()
public static async Task RunAsync()
{
//RunSse();
//RunOnLocal();
@@ -268,25 +290,56 @@ namespace WireMock.Net.ConsoleApplication
var server = WireMockServer.Start();
//server
// .Given(Request.Create()
// .WithPath("todos")
// .UsingGet()
// )
// .RespondWith(Response.Create()
// .WithBodyAsJson(todos.Values)
// );
//server
// .Given(Request.Create()
// .UsingGet()
// .WithPath("todos")
// .WithParam("id")
// )
// .RespondWith(Response.Create()
// .WithBodyAsJson(rm => todos[int.Parse(rm.Query!["id"].ToString())])
// );
var pX = 0.80;
server
.Given(Request.Create()
.WithPath("todos")
.UsingGet()
)
.RespondWith(Response.Create()
.WithBodyAsJson(todos.Values)
);
.Given(Request.Create().UsingGet().WithPath("/p"))
.WithProbability(pX)
.RespondWith(Response.Create().WithStatusCode(200).WithBody("X"));
server
.Given(Request.Create()
.UsingGet()
.WithPath("todos")
.WithParam("id")
)
.RespondWith(Response.Create()
.WithBodyAsJson(rm => todos[int.Parse(rm.Query!["id"].ToString())])
);
.Given(Request.Create().UsingGet().WithPath("/p"))
.RespondWith(Response.Create().WithStatusCode(200).WithBody("default"));
// Act
var requestUri = new Uri($"http://localhost:{server.Port}/p");
var c = server.CreateClient();
var xCount = 0;
var defaultCount = 0;
var tot = 1000;
for (var i = 0; i < tot; i++)
{
var response = await c.GetAsync(requestUri);
var value = await response.Content.ReadAsStringAsync();
if (value == "X")
{
xCount++;
}
else if (value == "default")
{
defaultCount++;
}
}
System.Console.WriteLine("X = {0} ; default = {1} ; pX = {2:0.00} ; valueX = {3:0.00}", xCount, defaultCount, pX, 1.0 * xCount / tot);
return;
using var httpAndHttpsWithPort = WireMockServer.Start(new WireMockServerSettings
{
HostingScheme = HostingScheme.HttpAndHttps,
@@ -323,9 +376,9 @@ namespace WireMock.Net.ConsoleApplication
PreWireMockMiddlewareInit = app => { System.Console.WriteLine($"PreWireMockMiddlewareInit : {app.GetType()}"); },
PostWireMockMiddlewareInit = app => { System.Console.WriteLine($"PostWireMockMiddlewareInit : {app.GetType()}"); },
#if USE_ASPNETCORE
//#if USE_ASPNETCORE
AdditionalServiceRegistration = services => { System.Console.WriteLine($"AdditionalServiceRegistration : {services.GetType()}"); },
#endif
//#endif
Logger = new WireMockConsoleLogger(),
HandlebarsRegistrationCallback = (handlebarsContext, fileSystemHandler) =>
@@ -346,7 +399,7 @@ namespace WireMock.Net.ConsoleApplication
//var response = await http.GetAsync($"{_wireMockServer.Url}/pricing");
//var value = await response.Content.ReadAsStringAsync();
#if PROTOBUF
//#if PROTOBUF
var protoBufJsonMatcher = new JsonPartialWildcardMatcher(new { name = "*" });
server
.Given(Request.Create()
@@ -425,22 +478,85 @@ namespace WireMock.Net.ConsoleApplication
.WithTrailingHeader("grpc-status", "0")
.WithTransformer()
);
#endif
//#endif
#if GRAPHQL
//#if GRAPHQL
var customScalars = new Dictionary<string, Type> { { "MyCustomScalar", typeof(int) } };
server
.Given(Request.Create()
.WithPath("/graphql")
.UsingPost()
.WithBodyAsGraphQL(TestSchema, customScalars)
.WithGraphQLSchema(TestSchemaQueryStudents)
)
.RespondWith(Response.Create()
.WithBody("GraphQL is ok")
.WithHeader("Content-Type", "application/json")
.WithBody(
"""
{
"data": {
"students": [
{
"id": "1",
"firstName": "Alice",
"lastName": "Johnson",
"fullName": "Alice Johnson"
},
{
"id": "2",
"firstName": "Bob",
"lastName": "Smith",
"fullName": "Bob Smith"
}
]
}
}
""")
);
#endif
#if MIMEKIT
server
.Given(Request.Create()
.WithPath("/graphql")
.UsingPost()
.WithGraphQLSchema(TestSchemaQueryStudentById)
.WithBody(new JsonPartialWildcardMatcher("{ \"variables\": { \"sid\": \"1\" } }"))
)
.WithTitle("Student found")
.RespondWith(Response.Create()
.WithHeader("Content-Type", "application/json")
.WithBody(
"""
{
"data": {
"studentById": {
"id": "123",
"firstName": "John",
"lastName": "Doe",
"fullName": "John Doe"
}
}
}
""")
);
server
.Given(Request.Create()
.WithPath("/graphql")
.UsingPost()
.WithGraphQLSchema(TestSchemaQueryStudentById)
)
.WithTitle("Student not found")
.RespondWith(Response.Create()
.WithHeader("Content-Type", "application/json")
.WithBody(
"""
{
"data": null
}
""")
);
//#endif
//#if MIMEKIT
var textPlainContentTypeMatcher = new ContentTypeMatcher("text/plain");
var textPlainContentMatcher = new ExactMatcher("This is some plain text");
var textPlainMatcher = new MimePartMatcher(MatchBehaviour.AcceptOnMatch, textPlainContentTypeMatcher, null, null, textPlainContentMatcher);
@@ -472,7 +588,7 @@ namespace WireMock.Net.ConsoleApplication
.RespondWith(Response.Create()
.WithBody("MultiPart is ok")
);
#endif
//#endif
// 400 ms
server
.Given(Request.Create()

View File

@@ -2,6 +2,7 @@
using System.IO;
using System.Reflection;
using System.Threading.Tasks;
using log4net;
using log4net.Config;
using log4net.Repository;
@@ -14,10 +15,10 @@ 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)
static async Task Main(params string[] args)
{
XmlConfigurator.Configure(LogRepository, new FileInfo("log4net.config"));
MainApp.Run();
await MainApp.RunAsync();
}
}

View File

@@ -1,34 +1,34 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<DefineConstants>$(DefineConstants);GRAPHQL;MIMEKIT;PROTOBUF</DefineConstants>
</PropertyGroup>
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<!--<DefineConstants>$(DefineConstants);GRAPHQL;MIMEKIT;PROTOBUF</DefineConstants>-->
</PropertyGroup>
<ItemGroup>
<Content Include="__admin\mappings\*.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<Content Include="__admin\mappings\*.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<Compile Remove="__admin\mappings\1.cs" />
</ItemGroup>
<ItemGroup>
<Compile Remove="__admin\mappings\1.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\WireMock.Net\WireMock.Net.csproj" />
<PackageReference Include="log4net" Version="2.0.15" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="8.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\WireMock.Net\WireMock.Net.csproj" />
<PackageReference Include="log4net" Version="2.0.15" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="8.0.0" />
</ItemGroup>
<ItemGroup>
<None Update="log4net.config">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="nlog.config">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
<ItemGroup>
<None Update="log4net.config">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="nlog.config">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>

View File

@@ -2,7 +2,7 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<ApplicationIcon>../../resources/WireMock.Net-Logo.ico</ApplicationIcon>
</PropertyGroup>

View File

@@ -7,12 +7,16 @@
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\WireMock.Net\WireMock.Net.csproj" />
</ItemGroup>
<!--<ItemGroup>
<PackageReference Include="WireMock.Net" Version="1.8.11" />
<ProjectReference Include="..\..\src\WireMock.Net\WireMock.Net.csproj" />
</ItemGroup>-->
<ItemGroup>
<PackageReference Update="SonarAnalyzer.CSharp" Version="10.12.0.118525" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="WireMock.Net" Version="1.12.0" />
</ItemGroup>
</Project>

View File

@@ -6,6 +6,8 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.OpenApi.YamlReader" Version="2.3.0" />
<ProjectReference Include="..\..\src\WireMock.Net.Abstractions\WireMock.Net.Abstractions.csproj" />
<ProjectReference Include="..\..\src\WireMock.Net.OpenApiParser\WireMock.Net.OpenApiParser.csproj" />
<ProjectReference Include="..\..\src\WireMock.Net\WireMock.Net.csproj" />

View File

@@ -115,10 +115,10 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="log4net">
<Version>3.0.3</Version>
<Version>3.2.0</Version>
</PackageReference>
<PackageReference Include="WireMock.Net">
<Version>1.8.11</Version>
<Version>1.12.0</Version>
</PackageReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />

View File

@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net6</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<!--<RuntimeIdentifiers>win10-x64</RuntimeIdentifiers>-->
<StartupObject>WireMock.Net.WebApplication.Program</StartupObject>
<AssemblyName>WireMock.Net.WebApplication</AssemblyName>
@@ -11,7 +11,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="8.0.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
</ItemGroup>

View File

@@ -1,25 +1,25 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<AzureFunctionsVersion>v4</AzureFunctionsVersion>
<UserSecretsId>bb7d8355-68c4-4f81-8c2d-6cdd80cd7602</UserSecretsId>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Azure.Storage.Blobs" Version="12.14.1" />
<PackageReference Include="Azure.Storage.Files.Shares" Version="12.12.1" />
<PackageReference Include="Azure.Storage.Queues" Version="12.12.0" />
<PackageReference Include="Microsoft.Azure.WebJobs.Extensions.Storage" Version="4.0.5" />
<PackageReference Include="Microsoft.Extensions.Azure" Version="1.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="6.0.1" />
<PackageReference Include="Microsoft.NET.Sdk.Functions" Version="4.1.3" />
</ItemGroup>
<ItemGroup>
<None Update="host.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="local.settings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<CopyToPublishDirectory>Never</CopyToPublishDirectory>
</None>
</ItemGroup>
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<AzureFunctionsVersion>v4</AzureFunctionsVersion>
<UserSecretsId>bb7d8355-68c4-4f81-8c2d-6cdd80cd7602</UserSecretsId>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Azure.Storage.Blobs" Version="12.14.1" />
<PackageReference Include="Azure.Storage.Files.Shares" Version="12.12.1" />
<PackageReference Include="Azure.Storage.Queues" Version="12.12.0" />
<PackageReference Include="Microsoft.Azure.WebJobs.Extensions.Storage" Version="4.0.5" />
<PackageReference Include="Microsoft.Extensions.Azure" Version="1.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="6.0.1" />
<PackageReference Include="Microsoft.NET.Sdk.Functions" Version="4.1.3" />
</ItemGroup>
<ItemGroup>
<None Update="host.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="local.settings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<CopyToPublishDirectory>Never</CopyToPublishDirectory>
</None>
</ItemGroup>
</Project>

View File

@@ -2,7 +2,7 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
</PropertyGroup>
<ItemGroup>

View File

@@ -56,6 +56,11 @@ public class MappingModel
/// In case the value is null state will not be changed.
/// </summary>
public string? SetStateTo { get; set; }
/// <summary>
/// The number of times this match should be matched before the state will be changed to the specified one.
/// </summary>
public int? TimesInSameState { get; set; }
/// <summary>
/// The request model.
@@ -86,7 +91,7 @@ public class MappingModel
/// Fire and forget for webhooks.
/// </summary>
public bool? UseWebhooksFireAndForget { get; set; }
/// <summary>
/// Data Object which can be used when WithTransformer is used.
/// e.g. lookup a path in this object using

View File

@@ -22,7 +22,7 @@ public class MatcherModel
public object? Pattern { get; set; }
/// <summary>
/// Gets or sets the patterns. Can be array of strings (default) or an array of objects.
/// Gets or sets the patterns. Can be an array of strings (default) or an array of objects.
/// </summary>
public object[]? Patterns { get; set; }

View File

@@ -24,4 +24,13 @@ public class StatusModel
/// The error message.
/// </summary>
public string? Error { get; set; }
/// <summary>
/// Returns a string that represents the current status model, including its unique identifier, status, and error information.
/// </summary>
/// <returns>A string containing the values of the Guid, Status, and Error properties formatted for display.</returns>
public override string ToString()
{
return $"StatusModel [Guid={Guid}, Status={Status}, Error={Error}]";
}
}

View File

@@ -47,5 +47,5 @@ public class LogRequestMatchModel
/// <value>
/// The match details.
/// </value>
public IList<object> MatchDetails { get; set; }
public IList<object> MatchDetails { get; set; } = [];
}

View File

@@ -0,0 +1,15 @@
// Copyright © WireMock.Net
namespace WireMock.Admin.Scenarios;
/// <summary>
/// ScenarioStateModel
/// </summary>
[FluentBuilder.AutoGenerateBuilder]
public class ScenarioStateUpdateModel
{
/// <summary>
/// Gets or sets the NextState.
/// </summary>
public string? State { get; set; }
}

View File

@@ -1,5 +1,7 @@
// Copyright © WireMock.Net
using WireMock.Types;
namespace WireMock.Admin.Settings;
/// <summary>
@@ -11,15 +13,25 @@ public class ProxyUrlReplaceSettingsModel
/// <summary>
/// The old path value to be replaced by the new path value
/// </summary>
public string OldValue { get; set; } = null!;
public string? OldValue { get; set; }
/// <summary>
/// The new path value to replace the old value with
/// </summary>
public string NewValue { get; set; } = null!;
public string? NewValue { get; set; }
/// <summary>
/// Defines if the case should be ignore when replacing.
/// Defines if the case should be ignored when replacing.
/// </summary>
public bool IgnoreCase { get; set; }
/// <summary>
/// Holds the transformation template.
/// </summary>
public string? TransformTemplate { get; set; }
/// <summary>
/// The transformer type.
/// </summary>
public TransformerType TransformerType { get; set; } = TransformerType.Handlebars;
}

View File

@@ -2,7 +2,6 @@
using System.Collections.Generic;
using System.Text.RegularExpressions;
using JetBrains.Annotations;
using WireMock.Handlers;
using WireMock.Types;
@@ -123,7 +122,6 @@ public class SettingsModel
/// </summary>
public Dictionary<string, string[]>? ProtoDefinitions { get; set; }
#if NETSTANDARD1_3_OR_GREATER || NET461
/// <summary>
/// Server client certificate mode
/// </summary>
@@ -133,5 +131,4 @@ public class SettingsModel
/// Whether to accept any client certificate
/// </summary>
public bool AcceptAnyClientCertificate { get; set; }
#endif
}

View File

@@ -1,6 +1,7 @@
// Copyright © WireMock.Net
using System;
using WireMock.Validators;
// ReSharper disable once CheckNamespace
namespace WireMock.Admin.Mappings;
@@ -94,9 +95,14 @@ public partial class RequestModelBuilder
}
/// <summary>
/// Set the Path.
/// Set the Path. Must start with a forward slash (/).
/// </summary>
public RequestModelBuilder WithPath(string value) => WithPath(() => value);
public RequestModelBuilder WithPath(string value)
{
PathValidator.ValidateAndThrow(value);
return WithPath(() => value);
}
/// <summary>
/// Set the Path.

View File

@@ -2,9 +2,7 @@
using System;
using System.Collections.Generic;
#if NETSTANDARD1_3_OR_GREATER || NET461
using System.Security.Cryptography.X509Certificates;
#endif
using WireMock.Types;
using WireMock.Util;
@@ -118,13 +116,11 @@ public interface IRequestMessage
/// </summary>
byte[]? BodyAsBytes { get; }
#if MIMEKIT
/// <summary>
/// The original body as MimeMessage.
/// Convenience getter for Handlebars and WireMockAssertions.
/// </summary>
object? BodyAsMimeMessage { get; }
#endif
Models.Mime.IMimeMessageData? BodyAsMimeMessage { get; }
/// <summary>
/// The detected body type. Convenience getter for Handlebars.
@@ -169,10 +165,8 @@ public interface IRequestMessage
/// <returns>The query parameter value as WireMockList or null when not found.</returns>
WireMockList<string>? GetParameter(string key, bool ignoreCase = false);
#if NETSTANDARD1_3_OR_GREATER || NET461
/// <summary>
/// Gets the connection's client certificate
/// </summary>
X509Certificate2? ClientCertificate { get; }
#endif
}

View File

@@ -15,12 +15,12 @@ public interface IResponseMessage
/// <summary>
/// The Body.
/// </summary>
IBodyData? BodyData { get; }
IBodyData? BodyData { get; set; }
/// <summary>
/// Gets the body destination (Null, SameAsSource, String or Bytes).
/// </summary>
string? BodyDestination { get; }
string? BodyDestination { get; set; }
/// <summary>
/// Gets or sets the body.
@@ -30,27 +30,27 @@ public interface IResponseMessage
/// <summary>
/// Gets the Fault percentage.
/// </summary>
double? FaultPercentage { get; }
double? FaultPercentage { get; set; }
/// <summary>
/// The FaultType.
/// </summary>
FaultType FaultType { get; }
FaultType FaultType { get; set; }
/// <summary>
/// Gets the headers.
/// </summary>
IDictionary<string, WireMockList<string>>? Headers { get; }
IDictionary<string, WireMockList<string>>? Headers { get; set; }
/// <summary>
/// Gets the trailing headers.
/// </summary>
IDictionary<string, WireMockList<string>>? TrailingHeaders { get; }
IDictionary<string, WireMockList<string>>? TrailingHeaders { get; set; }
/// <summary>
/// Gets or sets the status code.
/// </summary>
object? StatusCode { get; }
object? StatusCode { get; set; }
/// <summary>
/// Adds the header.

View File

@@ -0,0 +1,5 @@
// Copyright © WireMock.Net
namespace WireMock.Models.GraphQL;
public interface ISchemaData;

View File

@@ -0,0 +1,60 @@
// Copyright © WireMock.Net
using System;
using System.Collections.Generic;
namespace WireMock.Models.Mime;
/// <summary>
/// An interface exposing the public, readable properties of a ContentDisposition.
/// </summary>
public interface IContentDispositionData
{
/// <summary>
/// Get the disposition.
/// </summary>
/// <value>The disposition.</value>
string Disposition { get; }
/// <summary>
/// Get a value indicating whether the <see cref="IMimeEntityData"/> is an attachment.
/// </summary>
/// <value><see langword="true" /> if the <see cref="IMimeEntityData"/> is an attachment; otherwise, <see langword="false" />.</value>
bool IsAttachment { get; }
/// <summary>
/// Get the list of parameters on the ContentDisposition.
/// </summary>
/// <value>The parameters.</value>
public IList<string> Parameters { get; }
/// <summary>
/// Get the name of the file.
/// </summary>
/// <value>The name of the file.</value>
string FileName { get; }
/// <summary>
/// Get the creation-date parameter.
/// </summary>
/// <value>The creation date.</value>
DateTimeOffset? CreationDate { get; }
/// <summary>
/// Get the modification-date parameter.
/// </summary>
/// <value>The modification date.</value>
DateTimeOffset? ModificationDate { get; }
/// <summary>
/// Get the read-date parameter.
/// </summary>
/// <value>The read date.</value>
DateTimeOffset? ReadDate { get; }
/// <summary>
/// Get the size parameter.
/// </summary>
/// <value>The size.</value>
long? Size { get; }
}

View File

@@ -0,0 +1,67 @@
// Copyright © WireMock.Net
using System.Collections.Generic;
using System.Text;
namespace WireMock.Models.Mime;
/// <summary>
/// An interface exposing the public, readable properties of a ContentType
/// with complex types simplified to a generic object.
/// </summary>
public interface IContentTypeData
{
/// <summary>
/// Get the type of the media.
/// </summary>
/// <value>The type of the media.</value>
string MediaType { get; }
/// <summary>
/// Get the media subtype.
/// </summary>
/// <value>The media subtype.</value>
string MediaSubtype { get; }
/// <summary>
/// Get the list of parameters on the ContentType.
/// </summary>
/// <value>The parameters.</value>
IList<string> Parameters { get; }
/// <summary>
/// Get the boundary parameter.
/// </summary>
/// <value>The boundary.</value>
string Boundary { get; }
/// <summary>
/// Get the charset parameter.
/// </summary>
/// <value>The charset.</value>
string Charset { get; }
/// <summary>
/// Get the charset parameter as an Encoding.
/// </summary>
/// <value>The charset encoding.</value>
Encoding CharsetEncoding { get; }
/// <summary>
/// Get the format parameter.
/// </summary>
/// <value>The format.</value>
string Format { get; }
/// <summary>
/// Get the simple mime-type.
/// </summary>
/// <value>The mime-type.</value>
string MimeType { get; }
/// <summary>
/// Get the name parameter.
/// </summary>
/// <value>The name.</value>
string Name { get; }
}

View File

@@ -0,0 +1,54 @@
// Copyright © WireMock.Net
using System;
using System.Collections.Generic;
namespace WireMock.Models.Mime;
/// <summary>
/// A simplified interface exposing the public, readable properties of MimeEntity.
/// </summary>
public interface IMimeEntityData
{
/// <summary>
/// Get the list of headers.
/// </summary>
/// <value>The list of headers.</value>
IList<string> Headers { get; }
/// <summary>
/// Get the content disposition.
/// </summary>
/// <value>The content disposition.</value>
IContentDispositionData? ContentDisposition { get; }
/// <summary>
/// Get the type of the content.
/// </summary>
/// <value>The type of the content.</value>
IContentTypeData? ContentType { get; }
/// <summary>
/// Get the base content URI.
/// </summary>
/// <value>The base content URI or <see langword="null"/>.</value>
Uri ContentBase { get; }
/// <summary>
/// Get the content location.
/// </summary>
/// <value>The content location or <see langword="null"/>.</value>
Uri ContentLocation { get; }
/// <summary>
/// Get the Content-Id.
/// </summary>
/// <value>The content identifier.</value>
string ContentId { get; }
/// <summary>
/// Get a value indicating whether this <see cref="IMimeEntityData"/> is an attachment.
/// </summary>
/// <value><see langword="true" /> if this <see cref="IMimeEntityData"/> is an attachment; otherwise, <see langword="false" />.</value>
bool IsAttachment { get; }
}

View File

@@ -0,0 +1,186 @@
// Copyright © WireMock.Net
using System;
using System.Collections.Generic;
namespace WireMock.Models.Mime;
/// <summary>
/// A simplified interface exposing the public, readable properties of a MIME message.
/// </summary>
public interface IMimeMessageData
{
/// <summary>
/// Get the list of headers.
/// </summary>
/// <value>The list of headers.</value>
IList<string> Headers { get; }
/// <summary>
/// Get the value of the Importance header.
/// </summary>
/// <value>The importance, as an integer.</value>
int Importance { get; }
/// <summary>
/// Get the value of the Priority header.
/// </summary>
/// <value>The priority, as an integer.</value>
int Priority { get; }
/// <summary>
/// Get the value of the X-Priority header.
/// </summary>
/// <value>The X-priority, as an integer.</value>
int XPriority { get; }
/// <summary>
/// Get the address in the Sender header.
/// </summary>
/// <value>The address in the Sender header.</value>
string Sender { get; }
/// <summary>
/// Get the address in the Resent-Sender header.
/// </summary>
/// <value>The address in the Resent-Sender header.</value>
string ResentSender { get; }
/// <summary>
/// Get the list of addresses in the From header.
/// </summary>
/// <value>The list of addresses in the From header.</value>
IList<string> From { get; }
/// <summary>
/// Get the list of addresses in the Resent-From header.
/// </summary>
/// <value>The list of addresses in the Resent-From header.</value>
IList<string> ResentFrom { get; }
/// <summary>
/// Get the list of addresses in the Reply-To header.
/// </summary>
/// <value>The list of addresses in the Reply-To header.</value>
IList<string> ReplyTo { get; }
/// <summary>
/// Get the list of addresses in the Resent-Reply-To header.
/// </summary>
/// <value>The list of addresses in the Resent-Reply-To header.</value>
IList<string> ResentReplyTo { get; }
/// <summary>
/// Get the list of addresses in the To header.
/// </summary>
/// <value>The list of addresses in the To header.</value>
IList<string> To { get; }
/// <summary>
/// Get the list of addresses in the Resent-To header.
/// </summary>
/// <value>The list of addresses in the Resent-To header.</value>
IList<string> ResentTo { get; }
/// <summary>
/// Get the list of addresses in the Cc header.
/// </summary>
/// <value>The list of addresses in the Cc header.</value>
IList<string> Cc { get; }
/// <summary>
/// Get the list of addresses in the Resent-Cc header.
/// </summary>
/// <value>The list of addresses in the Resent-Cc header.</value>
IList<string> ResentCc { get; }
/// <summary>
/// Get the list of addresses in the Bcc header.
/// </summary>
/// <value>The list of addresses in the Bcc header.</value>
IList<string> Bcc { get; }
/// <summary>
/// Get the list of addresses in the Resent-Bcc header.
/// </summary>
/// <value>The list of addresses in the Resent-Bcc header.</value>
IList<string> ResentBcc { get; }
/// <summary>
/// Get the subject of the message.
/// </summary>
/// <value>The subject of the message.</value>
string Subject { get; }
/// <summary>
/// Get the date of the message.
/// </summary>
/// <value>The date of the message.</value>
DateTimeOffset Date { get; }
/// <summary>
/// Get the Resent-Date of the message.
/// </summary>
/// <value>The Resent-Date of the message.</value>
DateTimeOffset ResentDate { get; }
/// <summary>
/// Get the list of references to other messages.
/// </summary>
/// <value>The references.</value>
IList<string> References { get; }
/// <summary>
/// Get the Message-Id that this message is replying to.
/// </summary>
/// <value>The message id that this message is in reply to.</value>
string InReplyTo { get; }
/// <summary>
/// Get the message identifier.
/// </summary>
/// <value>The message identifier.</value>
string MessageId { get; }
/// <summary>
/// Get the Resent-Message-Id header.
/// </summary>
/// <value>The Resent-Message-Id.</value>
string ResentMessageId { get; }
/// <summary>
/// Get the MIME-Version.
/// </summary>
/// <value>The MIME version.</value>
Version MimeVersion { get; }
/// <summary>
/// Get the body of the message.
/// </summary>
/// <value>The body of the message.</value>
IMimeEntityData Body { get; }
/// <summary>
/// Get the text body of the message if it exists.
/// </summary>
/// <value>The text body if it exists; otherwise, <see langword="null"/>.</value>
string TextBody { get; }
/// <summary>
/// Get the html body of the message if it exists.
/// </summary>
/// <value>The html body if it exists; otherwise, <see langword="null"/>.</value>
string HtmlBody { get; }
/// <summary>
/// Get the body parts of the message.
/// </summary>
/// <value>The body parts.</value>
IList<IMimePartData> BodyParts { get; }
/// <summary>
/// Get the attachments.
/// </summary>
/// <value>The attachments.</value>
IList<IMimeEntityData> Attachments { get; }
}

View File

@@ -0,0 +1,57 @@
// Copyright © WireMock.Net
using System.Collections.Generic;
using System.IO;
namespace WireMock.Models.Mime;
/// <summary>
/// A simplified interface exposing the public, readable properties of MimePart.
/// </summary>
public interface IMimePartData : IMimeEntityData
{
/// <summary>
/// Get the description of the content if available.
/// </summary>
/// <value>The description of the content.</value>
string ContentDescription { get; }
/// <summary>
/// Get the duration of the content if available.
/// </summary>
/// <value>The duration of the content.</value>
int? ContentDuration { get; }
/// <summary>
/// Get the md5sum of the content.
/// </summary>
/// <value>The md5sum of the content.</value>
string ContentMd5 { get; }
/// <summary>
/// Get the content transfer encoding.
/// </summary>
/// <value>The content transfer encoding as a string.</value>
string ContentTransferEncoding { get; }
/// <summary>
/// Get the name of the file.
/// </summary>
/// <value>The name of the file.</value>
string FileName { get; }
/// <summary>
/// Get the MIME content.
/// </summary>
/// <value>The MIME content.</value>
IDictionary<string, object?> Content { get; }
/// <summary>
/// Open the decoded content stream.
/// </summary>
/// <remarks>
/// Provides a means of reading the decoded content without having to first write it to another stream.
/// </remarks>
/// <returns>The decoded content stream.</returns>
Stream Open();
}

View File

@@ -161,6 +161,11 @@ public interface IWireMockServer : IDisposable
/// </summary>
bool ResetScenario(string name);
/// <summary>
/// Sets a scenario to a state.
/// </summary>
bool SetScenarioState(string name, string? state);
/// <summary>
/// Resets the LogEntries.
/// </summary>

View File

@@ -2,7 +2,6 @@
namespace WireMock.Types;
#if NETSTANDARD1_3_OR_GREATER || NET461
/// <summary>
/// Describes the client certificate requirements for a HTTPS connection.
/// This enum is the same as https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.server.kestrel.https.clientcertificatemode
@@ -29,5 +28,4 @@ public enum ClientCertificateMode
/// It may be requested by the application later.
/// </summary>
DelayCertificate,
}
#endif
}

View File

@@ -0,0 +1,19 @@
// Copyright © WireMock.Net
using System;
namespace WireMock.Validators;
public static class PathValidator
{
/// <summary>
/// A valid path must start with a '/' and cannot be null, empty or whitespace.
/// </summary>
public static void ValidateAndThrow(string? path, string? paramName = null)
{
if (string.IsNullOrWhiteSpace(path) || path?.StartsWith("/") == false)
{
throw new ArgumentException("Path must start with a '/' and cannot be null, empty or whitespace.", paramName ?? nameof(path));
}
}
}

View File

@@ -4,7 +4,8 @@
<Description>Commonly used interfaces, models, enumerations and types.</Description>
<AssemblyTitle>WireMock.Net.Abstractions</AssemblyTitle>
<Authors>Stef Heyenrath</Authors>
<TargetFrameworks>net45;net451;net461;netstandard1.3;netstandard2.0;netstandard2.1</TargetFrameworks>
<!--<TargetFrameworks>net45;net451;net461;netstandard1.3;netstandard2.0;netstandard2.1</TargetFrameworks>-->
<TargetFrameworks>net48;net8.0</TargetFrameworks>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<NoWarn>$(NoWarn);1591;8603</NoWarn>
<AssemblyName>WireMock.Net.Abstractions</AssemblyName>
@@ -33,51 +34,22 @@
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
</PropertyGroup>
<PropertyGroup Condition=" '$(TargetFramework)' == 'netstandard2.0' or '$(TargetFramework)' == 'netstandard2.1'">
<DefineConstants>$(DefineConstants);GRAPHQL;MIMEKIT;PROTOBUF</DefineConstants>
</PropertyGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net48' ">
<!-- CVE-2018-8292 / https://github.com/advisories/GHSA-7jgj-8wvc-jh57 -->
<PackageReference Include="System.Net.Http " Version="4.3.4" />
</ItemGroup>
<ItemGroup>
<!-- CVE-2018-8292 -->
<PackageReference Include="System.Net.Http " Version="4.3.4" />
<!-- See also https://mstack.nl/blog/20210801-source-generators -->
<PackageReference Include="FluentBuilder" Version="0.10.0">
<PackageReference Include="FluentBuilder" Version="0.13.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="PolySharp" Version="1.15.0">
<!-- Keep at 6.14.0 -->
<PackageReference Include="Polyfill" Version="6.14.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<!--<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="Stef.Validation" Version="0.1.1" />
<PackageReference Include="AnyOf" Version="0.4.0" />-->
</ItemGroup>
<ItemGroup Condition="$(TargetFramework.StartsWith('netstandard')) and '$(TargetFramework)' != 'netstandard1.0'">
<PackageReference Include="System.Security.Cryptography.X509Certificates" Version="4.3.0" />
</ItemGroup>
<!--<ItemGroup>
<PackageReference Include="Nullable" Version="1.3.1">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>-->
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard1.3' or '$(TargetFramework)' == 'net45' or '$(TargetFramework)' == 'net451' or '$(TargetFramework)' == 'net461'">
<PackageReference Include="System.ValueTuple" Version="4.5.0" />
<!--<PackageReference Include="Nullable" Version="1.3.1">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>-->
<!--<PackageReference Include="PolySharp" Version="1.14.1">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>-->
</ItemGroup>
</Project>

View File

@@ -38,7 +38,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.2" />
</ItemGroup>
<ItemGroup>

View File

@@ -30,7 +30,10 @@
</ItemGroup>
<ItemGroup>
<Compile Include="..\WireMock.Net.Minimal\Util\EnhancedFileSystemWatcher.cs" Link="Utils\EnhancedFileSystemWatcher.cs" />
<Compile Include="..\WireMock.Net.Minimal\Util\EnhancedFileSystemWatcher.cs" Link="Util\EnhancedFileSystemWatcher.cs" />
<Compile Include="..\WireMock.Net.Minimal\Constants\WireMockConstants.cs" Link="Constants\WireMockConstants.cs" />
<Compile Include="..\WireMock.Net.Shared\Constants\RegexConstants.cs" Link="Constants\RegexConstants.cs" />
<Compile Include="..\WireMock.Net.Minimal\Util\PortUtils.cs" Link="Util\PortUtils.cs" />
</ItemGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Debug - Sonar'">

View File

@@ -0,0 +1,44 @@
// Copyright © WireMock.Net
using Aspire.Hosting.ApplicationModel;
using Microsoft.Extensions.Diagnostics.HealthChecks;
using WireMock.Client;
namespace WireMock.Net.Aspire;
/// <summary>
/// WireMockHealthCheck
/// </summary>
public class WireMockHealthCheck(WireMockServerResource resource) : IHealthCheck
{
private const string HealthStatusHealthy = "Healthy";
/// <inheritdoc />
public async Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default)
{
if (!await IsHealthyAsync(resource.AdminApi.Value, cancellationToken))
{
return HealthCheckResult.Unhealthy("WireMock.Net is not healthy");
}
if (resource.ApiMappingState == WireMockMappingState.NotSubmitted)
{
return HealthCheckResult.Unhealthy("WireMock.Net has not received mappings");
}
return HealthCheckResult.Healthy();
}
private static async Task<bool> IsHealthyAsync(IWireMockAdminApi adminApi, CancellationToken cancellationToken)
{
try
{
var status = await adminApi.GetHealthAsync(cancellationToken);
return string.Equals(status, HealthStatusHealthy, StringComparison.OrdinalIgnoreCase);
}
catch
{
return false;
}
}
}

View File

@@ -0,0 +1,12 @@
// Copyright © WireMock.Net
namespace WireMock.Net.Aspire;
internal enum WireMockMappingState
{
NoMappings = 0,
NotSubmitted = 1,
Submitted = 2
}

View File

@@ -1,7 +1,9 @@
// Copyright © WireMock.Net
using System.Diagnostics.CodeAnalysis;
using Stef.Validation;
using WireMock.Client.Builders;
using WireMock.Util;
// ReSharper disable once CheckNamespace
namespace Aspire.Hosting;
@@ -21,10 +23,15 @@ public class WireMockServerArguments
private const string DefaultLogger = "WireMockConsoleLogger";
/// <summary>
/// The HTTP port where WireMock.Net is listening.
/// The HTTP ports where WireMock.Net is listening on.
/// If not defined, .NET Aspire automatically assigns a random port.
/// </summary>
public int? HttpPort { get; set; }
public List<int> HttpPorts { get; set; } = [];
/// <summary>
/// Additional Urls on which WireMock listens.
/// </summary>
public List<string> AdditionalUrls { get; set; } = [];
/// <summary>
/// The admin username.
@@ -67,6 +74,42 @@ public class WireMockServerArguments
/// </summary>
public Func<AdminApiMappingBuilder, CancellationToken, Task>? ApiMappingBuilder { get; set; }
/// <summary>
/// Grpc ProtoDefinitions.
/// </summary>
public Dictionary<string, string[]> ProtoDefinitions { get; set; } = [];
/// <summary>
/// Add an additional Urls on which WireMock should listen.
/// </summary>
/// <param name="additionalUrls">The additional urls which the WireMock Server should listen on.</param>
public void WithAdditionalUrls(params string[] additionalUrls)
{
foreach (var url in additionalUrls)
{
if (!PortUtils.TryExtract(Guard.NotNullOrEmpty(url), out _, out _, out _, out _, out var port))
{
throw new ArgumentException($"The URL '{url}' is not valid.");
}
AdditionalUrls.Add(Guard.NotNullOrWhiteSpace(url));
HttpPorts.Add(port);
}
}
/// <summary>
/// Add a Grpc ProtoDefinition at server-level.
/// </summary>
/// <param name="id">Unique identifier for the ProtoDefinition.</param>
/// <param name="protoDefinitions">The ProtoDefinition as text.</param>
public void WithProtoDefinition(string id, params string[] protoDefinitions)
{
Guard.NotNullOrWhiteSpace(id);
Guard.NotNullOrEmpty(protoDefinitions);
ProtoDefinitions[id] = protoDefinitions;
}
/// <summary>
/// Converts the current instance's properties to an array of command-line arguments for starting the WireMock.Net server.
/// </summary>
@@ -95,6 +138,11 @@ public class WireMockServerArguments
Add(args, "--WatchStaticMappingsInSubdirectories", "true");
}
if (AdditionalUrls.Count > 0)
{
Add(args, "--Urls", $"http://*:{HttpContainerPort} {string.Join(' ', AdditionalUrls)}");
}
return args
.SelectMany(k => new[] { k.Key, k.Value })
.ToArray();

View File

@@ -3,10 +3,12 @@
using Aspire.Hosting.ApplicationModel;
using Aspire.Hosting.Lifecycle;
using Aspire.Hosting.WireMock;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Diagnostics.HealthChecks;
using Stef.Validation;
using WireMock.Client.Builders;
using WireMock.Net.Aspire;
using WireMock.Util;
// ReSharper disable once CheckNamespace
namespace Aspire.Hosting;
@@ -33,9 +35,31 @@ public static class WireMockServerBuilderExtensions
Guard.NotNullOrWhiteSpace(name);
Guard.Condition(port, p => p is null or > 0 and <= ushort.MaxValue);
return builder.AddWireMock(name, callback =>
return builder.AddWireMock(name, serverArguments =>
{
callback.HttpPort = port;
if (port != null)
{
serverArguments.HttpPorts = [port.Value];
}
});
}
/// <summary>
/// Adds a WireMock.Net Server resource to the application model.
/// </summary>
/// <param name="builder">The <see cref="IDistributedApplicationBuilder"/>.</param>
/// <param name="name">The name of the resource. This name will be used as the connection string name when referenced in a dependency.</param>
/// <param name="additionalUrls">The additional urls which the WireMock Server should listen on.</param>
/// <returns>A reference to the <see cref="IResourceBuilder{WireMockServerResource}"/>.</returns>
public static IResourceBuilder<WireMockServerResource> AddWireMock(this IDistributedApplicationBuilder builder, string name, params string[] additionalUrls)
{
Guard.NotNull(builder);
Guard.NotNullOrWhiteSpace(name);
Guard.NotNull(additionalUrls);
return builder.AddWireMock(name, serverArguments =>
{
serverArguments.WithAdditionalUrls(additionalUrls);
});
}
@@ -53,13 +77,50 @@ public static class WireMockServerBuilderExtensions
Guard.NotNull(arguments);
var wireMockContainerResource = new WireMockServerResource(name, arguments);
var healthCheckKey = $"{name}_check";
var healthCheckRegistration = new HealthCheckRegistration(
healthCheckKey,
_ => new WireMockHealthCheck(wireMockContainerResource),
failureStatus: null,
tags: null);
builder.Services.AddHealthChecks().Add(healthCheckRegistration);
var resourceBuilder = builder
.AddResource(wireMockContainerResource)
.WithImage(DefaultLinuxImage)
.WithEnvironment(ctx => ctx.EnvironmentVariables.Add("DOTNET_USE_POLLING_FILE_WATCHER", "1")) // https://khalidabuhakmeh.com/aspnet-docker-gotchas-and-workarounds#configuration-reloads-and-filesystemwatcher
.WithHttpEndpoint(port: arguments.HttpPort, targetPort: WireMockServerArguments.HttpContainerPort)
.WithHealthCheck(healthCheckKey)
.WithWireMockInspectorCommand();
if (arguments.HttpPorts.Count == 0)
{
resourceBuilder = resourceBuilder.WithHttpEndpoint(port: null, targetPort: WireMockServerArguments.HttpContainerPort);
}
else if (arguments.HttpPorts.Count == 1)
{
resourceBuilder = resourceBuilder.WithHttpEndpoint(port: arguments.HttpPorts[0], targetPort: WireMockServerArguments.HttpContainerPort);
}
else
{
// Required for the default admin endpoint and health checks
resourceBuilder = resourceBuilder.WithHttpEndpoint(port: null, targetPort: WireMockServerArguments.HttpContainerPort);
var anyIsHttp2 = false;
foreach (var url in arguments.AdditionalUrls)
{
PortUtils.TryExtract(url, out _, out var isHttp2, out var scheme, out _, out var httpPort);
anyIsHttp2 |= isHttp2;
resourceBuilder = resourceBuilder.WithEndpoint(port: httpPort, targetPort: httpPort, scheme: scheme, name: $"{scheme}-{httpPort}");
}
if (anyIsHttp2)
{
resourceBuilder = resourceBuilder.AsHttp2Service();
}
}
if (!string.IsNullOrEmpty(arguments.MappingsPath))
{
resourceBuilder = resourceBuilder.WithBindMount(arguments.MappingsPath, DefaultLinuxMappingsPath);
@@ -73,6 +134,9 @@ public static class WireMockServerBuilderExtensions
}
});
// Always add the lifecycle hook to support dynamic mappings and proto definitions
resourceBuilder.ApplicationBuilder.Services.TryAddLifecycleHook<WireMockServerLifecycleHook>();
return resourceBuilder;
}
@@ -83,7 +147,10 @@ public static class WireMockServerBuilderExtensions
/// <param name="name">The name of the resource. This name will be used as the connection string name when referenced in a dependency.</param>
/// <param name="callback">A callback that allows for setting the <see cref="WireMockServerArguments"/>.</param>
/// <returns>A reference to the <see cref="IResourceBuilder{WireMockServerResource}"/>.</returns>
public static IResourceBuilder<WireMockServerResource> AddWireMock(this IDistributedApplicationBuilder builder, string name, Action<WireMockServerArguments> callback)
public static IResourceBuilder<WireMockServerResource> AddWireMock(
this IDistributedApplicationBuilder builder,
string name,
Action<WireMockServerArguments> callback)
{
Guard.NotNull(builder);
Guard.NotNullOrWhiteSpace(name);
@@ -154,7 +221,7 @@ public static class WireMockServerBuilderExtensions
/// </summary>
/// <param name="wiremock">The <see cref="IResourceBuilder{WireMockServerResource}"/>.</param>
/// <param name="configure">Delegate that will be invoked to configure the WireMock.Net resource.</param>
/// <returns></returns>
/// <returns>A reference to the <see cref="IResourceBuilder{WireMockServerResource}"/>.</returns>
public static IResourceBuilder<WireMockServerResource> WithApiMappingBuilder(this IResourceBuilder<WireMockServerResource> wiremock, Func<AdminApiMappingBuilder, Task> configure)
{
return wiremock.WithApiMappingBuilder((adminApiMappingBuilder, _) => configure.Invoke(adminApiMappingBuilder));
@@ -165,13 +232,27 @@ public static class WireMockServerBuilderExtensions
/// </summary>
/// <param name="wiremock">The <see cref="IResourceBuilder{WireMockServerResource}"/>.</param>
/// <param name="configure">Delegate that will be invoked to configure the WireMock.Net resource.</param>
/// <returns></returns>
/// <returns>A reference to the <see cref="IResourceBuilder{WireMockServerResource}"/>.</returns>
public static IResourceBuilder<WireMockServerResource> WithApiMappingBuilder(this IResourceBuilder<WireMockServerResource> wiremock, Func<AdminApiMappingBuilder, CancellationToken, Task> configure)
{
Guard.NotNull(wiremock);
wiremock.ApplicationBuilder.Services.TryAddLifecycleHook<WireMockServerLifecycleHook>();
wiremock.Resource.Arguments.ApiMappingBuilder = configure;
wiremock.Resource.ApiMappingState = WireMockMappingState.NotSubmitted;
return wiremock;
}
/// <summary>
/// Add a Grpc ProtoDefinition at server-level.
/// </summary>
/// <param name="wiremock">The <see cref="IResourceBuilder{WireMockServerResource}"/>.</param>
/// <param name="id">Unique identifier for the ProtoDefinition.</param>
/// <param name="protoDefinitions">The ProtoDefinition as text.</param>
/// <returns>A reference to the <see cref="IResourceBuilder{WireMockServerResource}"/>.</returns>
public static IResourceBuilder<WireMockServerResource> WithProtoDefinition(this IResourceBuilder<WireMockServerResource> wiremock, string id, params string[] protoDefinitions)
{
Guard.NotNull(wiremock).Resource.Arguments.WithProtoDefinition(id, protoDefinitions);
return wiremock;
}
@@ -183,11 +264,11 @@ public static class WireMockServerBuilderExtensions
/// dotnet tool install WireMockInspector --global --no-cache --ignore-failed-sources
/// </code>
/// </summary>
/// <param name="builder">The <see cref="IResourceBuilder{WireMockNetResource}"/>.</param>
/// <returns></returns>
public static IResourceBuilder<WireMockServerResource> WithWireMockInspectorCommand(this IResourceBuilder<WireMockServerResource> builder)
/// <param name="wiremock">The <see cref="IResourceBuilder{WireMockNetResource}"/>.</param>
/// <returns>A reference to the <see cref="IResourceBuilder{WireMockServerResource}"/>.</returns>
public static IResourceBuilder<WireMockServerResource> WithWireMockInspectorCommand(this IResourceBuilder<WireMockServerResource> wiremock)
{
Guard.NotNull(builder);
Guard.NotNull(wiremock);
CommandOptions commandOptions = new()
{
@@ -197,13 +278,13 @@ public static class WireMockServerBuilderExtensions
IconVariant = IconVariant.Filled
};
builder.WithCommand(
wiremock.WithCommand(
name: "wiremock-inspector",
displayName: "WireMock Inspector",
executeCommand: _ => OnRunOpenInspectorCommandAsync(builder),
executeCommand: _ => OnRunOpenInspectorCommandAsync(wiremock),
commandOptions: commandOptions);
return builder;
return wiremock;
}
private static Task<ExecuteCommandResult> OnRunOpenInspectorCommandAsync(IResourceBuilder<WireMockServerResource> builder)

View File

@@ -1,5 +1,6 @@
// Copyright © WireMock.Net
using System.Diagnostics;
using Aspire.Hosting.ApplicationModel;
using Aspire.Hosting.Lifecycle;
using Microsoft.Extensions.Logging;
@@ -10,32 +11,49 @@ internal class WireMockServerLifecycleHook(ILoggerFactory loggerFactory) : IDist
{
private readonly CancellationTokenSource _shutdownCts = new();
public async Task AfterResourcesCreatedAsync(DistributedApplicationModel appModel, CancellationToken cancellationToken = default)
private CancellationTokenSource? _linkedCts;
private Task? _mappingTask;
public Task AfterEndpointsAllocatedAsync(DistributedApplicationModel appModel, CancellationToken cancellationToken = default)
{
var cts = CancellationTokenSource.CreateLinkedTokenSource(_shutdownCts.Token, cancellationToken);
_linkedCts = CancellationTokenSource.CreateLinkedTokenSource(_shutdownCts.Token, cancellationToken);
var wireMockServerResources = appModel.Resources
.OfType<WireMockServerResource>()
.ToArray();
foreach (var wireMockServerResource in wireMockServerResources)
_mappingTask = Task.Run(async () =>
{
wireMockServerResource.SetLogger(loggerFactory.CreateLogger<WireMockServerResource>());
var wireMockServerResources = appModel.Resources
.OfType<WireMockServerResource>()
.ToArray();
var endpoint = wireMockServerResource.GetEndpoint();
if (endpoint.IsAllocated)
foreach (var wireMockServerResource in wireMockServerResources)
{
await wireMockServerResource.WaitForHealthAsync(cts.Token);
wireMockServerResource.SetLogger(loggerFactory.CreateLogger<WireMockServerResource>());
await wireMockServerResource.CallApiMappingBuilderActionAsync(cts.Token);
var endpoint = wireMockServerResource.GetEndpoint();
Debug.Assert(endpoint.IsAllocated);
wireMockServerResource.StartWatchingStaticMappings(cts.Token);
await wireMockServerResource.WaitForHealthAsync(_linkedCts.Token);
await wireMockServerResource.CallAddProtoDefinitionsAsync(_linkedCts.Token);
await wireMockServerResource.CallApiMappingBuilderActionAsync(_linkedCts.Token);
wireMockServerResource.StartWatchingStaticMappings(_linkedCts.Token);
}
}
}, _linkedCts.Token);
return Task.CompletedTask;
}
public async ValueTask DisposeAsync()
{
await _shutdownCts.CancelAsync();
_linkedCts?.Dispose();
_shutdownCts.Dispose();
if (_mappingTask is not null)
{
await _mappingTask;
}
}
}

View File

@@ -5,6 +5,7 @@ using RestEase;
using Stef.Validation;
using WireMock.Client;
using WireMock.Client.Extensions;
using WireMock.Net.Aspire;
using WireMock.Util;
// ReSharper disable once CheckNamespace
@@ -19,6 +20,7 @@ public class WireMockServerResource : ContainerResource, IResourceWithServiceDis
internal WireMockServerArguments Arguments { get; }
internal Lazy<IWireMockAdminApi> AdminApi => new(CreateWireMockAdminApi);
internal WireMockMappingState ApiMappingState { get; set; } = WireMockMappingState.NoMappings;
private ILogger? _logger;
private EnhancedFileSystemWatcher? _enhancedFileSystemWatcher;
@@ -64,6 +66,36 @@ public class WireMockServerResource : ContainerResource, IResourceWithServiceDis
var mappingBuilder = AdminApi.Value.GetMappingBuilder();
await Arguments.ApiMappingBuilder.Invoke(mappingBuilder, cancellationToken);
ApiMappingState = WireMockMappingState.Submitted;
}
internal async Task CallAddProtoDefinitionsAsync(CancellationToken cancellationToken)
{
_logger?.LogInformation("Calling AdminApi to add GRPC ProtoDefinition at server level to WireMock.Net");
foreach (var (id, protoDefinitions) in Arguments.ProtoDefinitions)
{
_logger?.LogInformation("Adding ProtoDefinition {Id}", id);
foreach (var protoDefinition in protoDefinitions)
{
try
{
var status = await AdminApi.Value.AddProtoDefinitionAsync(id, protoDefinition, cancellationToken);
_logger?.LogInformation("ProtoDefinition '{Id}' added with status: {Status}.", id, status.Status);
}
catch (Exception ex)
{
_logger?.LogWarning(ex, "Error adding ProtoDefinition '{Id}'.", id);
}
}
}
// Force a reload of static mappings when ProtoDefinitions are added at server-level to fix #1382
if (Arguments.ProtoDefinitions.Count > 0)
{
await ReloadStaticMappingsAsync(default);
}
}
internal void StartWatchingStaticMappings(CancellationToken cancellationToken)
@@ -109,10 +141,17 @@ public class WireMockServerResource : ContainerResource, IResourceWithServiceDis
private async void FileCreatedChangedOrDeleted(object sender, FileSystemEventArgs args)
{
_logger?.LogInformation("MappingFile created, changed or deleted: '{0}'. Triggering ReloadStaticMappings.", args.FullPath);
_logger?.LogInformation("MappingFile created, changed or deleted: '{FullPath}'. Triggering ReloadStaticMappings.", args.FullPath);
await ReloadStaticMappingsAsync(default);
}
private async Task ReloadStaticMappingsAsync(CancellationToken cancellationToken)
{
try
{
await AdminApi.Value.ReloadStaticMappingsAsync();
var status = await AdminApi.Value.ReloadStaticMappingsAsync(cancellationToken);
_logger?.LogInformation("ReloadStaticMappings called with status: {Status}.", status);
}
catch (Exception ex)
{

View File

@@ -4,11 +4,11 @@ using Stef.Validation;
using WireMock.Server;
// ReSharper disable once CheckNamespace
namespace WireMock.FluentAssertions;
namespace WireMock.AwesomeAssertions;
/// <summary>
/// Provides assertion methods to verify the number of calls made to a WireMock server.
/// This class is used in the context of FluentAssertions.
/// This class is used in the context of AwesomeAssertions.
/// </summary>
public class WireMockANumberOfCallsAssertions
{

View File

@@ -0,0 +1,83 @@
// Copyright © WireMock.Net
using WireMock.Extensions;
using WireMock.Matchers;
// ReSharper disable once CheckNamespace
namespace WireMock.AwesomeAssertions;
#pragma warning disable CS1591
public partial class WireMockAssertions
{
[CustomAssertion]
public AndWhichConstraint<WireMockAssertions, string> AtAbsolutePath(string absolutePath, string because = "", params object[] becauseArgs)
{
_ = AtAbsolutePath(new ExactMatcher(true, absolutePath), because, becauseArgs);
return new AndWhichConstraint<WireMockAssertions, string>(this, absolutePath);
}
[CustomAssertion]
public AndWhichConstraint<WireMockAssertions, IStringMatcher> AtAbsolutePath(IStringMatcher absolutePathMatcher, string because = "", params object[] becauseArgs)
{
var (filter, condition) = BuildFilterAndCondition(request => absolutePathMatcher.IsPerfectMatch(request.AbsolutePath));
var absolutePath = absolutePathMatcher.GetPatterns().FirstOrDefault().GetPattern();
_chain
.BecauseOf(because, becauseArgs)
.Given(() => RequestMessages)
.ForCondition(requests => CallsCount == 0 || requests.Any())
.FailWith(
"Expected {context:wiremockserver} to have been called at address matching the absolute path {0}{reason}, but no calls were made.",
absolutePath
)
.Then
.ForCondition(condition)
.FailWith(
"Expected {context:wiremockserver} to have been called at address matching the absolute path {0}{reason}, but didn't find it among the calls to {1}.",
_ => absolutePath,
requests => requests.Select(request => request.AbsolutePath)
);
FilterRequestMessages(filter);
return new AndWhichConstraint<WireMockAssertions, IStringMatcher>(this, absolutePathMatcher);
}
[CustomAssertion]
public AndWhichConstraint<WireMockAssertions, string> AtPath(string path, string because = "", params object[] becauseArgs)
{
_ = AtPath(new ExactMatcher(true, path), because, becauseArgs);
return new AndWhichConstraint<WireMockAssertions, string>(this, path);
}
[CustomAssertion]
public AndWhichConstraint<WireMockAssertions, IStringMatcher> AtPath(IStringMatcher pathMatcher, string because = "", params object[] becauseArgs)
{
var (filter, condition) = BuildFilterAndCondition(request => pathMatcher.IsPerfectMatch(request.Path));
var path = pathMatcher.GetPatterns().FirstOrDefault().GetPattern();
_chain
.BecauseOf(because, becauseArgs)
.Given(() => RequestMessages)
.ForCondition(requests => CallsCount == 0 || requests.Any())
.FailWith(
"Expected {context:wiremockserver} to have been called at address matching the path {0}{reason}, but no calls were made.",
path
)
.Then
.ForCondition(condition)
.FailWith(
"Expected {context:wiremockserver} to have been called at address matching the path {0}{reason}, but didn't find it among the calls to {1}.",
_ => path,
requests => requests.Select(request => request.Path)
);
FilterRequestMessages(filter);
return new AndWhichConstraint<WireMockAssertions, IStringMatcher>(this, pathMatcher);
}
}

View File

@@ -4,7 +4,7 @@ using WireMock.Extensions;
using WireMock.Matchers;
// ReSharper disable once CheckNamespace
namespace WireMock.FluentAssertions;
namespace WireMock.AwesomeAssertions;
#pragma warning disable CS1591
public partial class WireMockAssertions

View File

@@ -4,7 +4,7 @@
using System;
// ReSharper disable once CheckNamespace
namespace WireMock.FluentAssertions;
namespace WireMock.AwesomeAssertions;
public partial class WireMockAssertions
{

View File

@@ -5,7 +5,7 @@ using System;
using WireMock.Constants;
// ReSharper disable once CheckNamespace
namespace WireMock.FluentAssertions;
namespace WireMock.AwesomeAssertions;
public partial class WireMockAssertions
{

View File

@@ -11,7 +11,7 @@ using WireMock.Matchers;
using WireMock.Models;
// ReSharper disable once CheckNamespace
namespace WireMock.FluentAssertions;
namespace WireMock.AwesomeAssertions;
public partial class WireMockAssertions
{

View File

@@ -3,7 +3,7 @@
#pragma warning disable CS1591
// ReSharper disable once CheckNamespace
namespace WireMock.FluentAssertions;
namespace WireMock.AwesomeAssertions;
public partial class WireMockAssertions
{

View File

@@ -4,7 +4,7 @@
using System;
// ReSharper disable once CheckNamespace
namespace WireMock.FluentAssertions;
namespace WireMock.AwesomeAssertions;
public partial class WireMockAssertions
{

View File

@@ -7,7 +7,7 @@ using WireMock.Matchers;
using WireMock.Server;
// ReSharper disable once CheckNamespace
namespace WireMock.FluentAssertions;
namespace WireMock.AwesomeAssertions;
public partial class WireMockAssertions
{

View File

@@ -4,7 +4,7 @@ using AwesomeAssertions.Primitives;
using WireMock.Server;
// ReSharper disable once CheckNamespace
namespace WireMock.FluentAssertions;
namespace WireMock.AwesomeAssertions;
/// <summary>
/// Contains a number of methods to assert that the <see cref="IWireMockServer"/> is in the expected state.

View File

@@ -3,7 +3,7 @@
using WireMock.Server;
// ReSharper disable once CheckNamespace
namespace WireMock.FluentAssertions
namespace WireMock.AwesomeAssertions
{
/// <summary>
/// Contains extension methods for custom assertions in unit tests.

View File

@@ -1,10 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Description>AwesomeAssertions extensions for WireMock.Net</Description>
<AssemblyTitle>WireMock.Net.AwesomeAssertions</AssemblyTitle>
<Authors>Francesco Venturoli;Mahmoud Ali;Stef Heyenrath</Authors>
<TargetFrameworks>net47;netstandard2.0;netstandard2.1</TargetFrameworks>
<!--<TargetFrameworks>net47;netstandard2.0;netstandard2.1</TargetFrameworks>-->
<TargetFrameworks>net48;net8.0</TargetFrameworks>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<AssemblyName>WireMock.Net.AwesomeAssertions</AssemblyName>
<PackageId>WireMock.Net.AwesomeAssertions</PackageId>
@@ -32,7 +33,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="AwesomeAssertions" Version="9.0.0" />
<PackageReference Include="AwesomeAssertions" Version="9.1.0" />
</ItemGroup>
<ItemGroup>

View File

@@ -0,0 +1,10 @@
// Copyright © WireMock.Net
namespace WireMock.Net.Extensions.Routing.Delegates;
/// <summary>
/// Represents a handler for processing WireMock.Net HTTP requests and returning a response asynchronously.
/// </summary>
/// <param name="requestMessage">The incoming request message.</param>
/// <returns>A task that resolves to a <see cref="ResponseMessage"/>.</returns>
public delegate Task<IResponseMessage> WireMockHttpRequestHandler(IRequestMessage requestMessage);

View File

@@ -0,0 +1,10 @@
// Copyright © WireMock.Net
namespace WireMock.Net.Extensions.Routing.Delegates;
/// <summary>
/// Represents a middleware component for WireMock.Net HTTP request handling.
/// </summary>
/// <param name="next">The next request handler in the middleware pipeline.</param>
/// <returns>A <see cref="WireMockHttpRequestHandler"/> that processes the request.</returns>
public delegate WireMockHttpRequestHandler WireMockMiddleware(WireMockHttpRequestHandler next);

View File

@@ -0,0 +1,19 @@
// Copyright © WireMock.Net
using System.Collections.Immutable;
namespace WireMock.Net.Extensions.Routing.Extensions;
internal static class DictionaryExtensions
{
public static IDictionary<TKey, TValue> AddIf<TKey, TValue>(
this IDictionary<TKey, TValue> source,
bool condition,
TKey key,
TValue value,
IEqualityComparer<TKey>? keyComparer = null)
where TKey : notnull =>
condition
? source.ToImmutableDictionary(keyComparer).Add(key, value)
: source;
}

View File

@@ -0,0 +1,34 @@
// Copyright © WireMock.Net
using Microsoft.AspNetCore.Http;
using WireMock.Types;
using WireMock.Util;
namespace WireMock.Net.Extensions.Routing.Extensions;
internal static class HttpResponseExtensions
{
public static async Task<ResponseMessage> ToResponseMessageAsync(
this HttpResponse response)
{
var headers = response.Headers.ToDictionary(
header => header.Key, header => new WireMockList<string?>(header.Value.ToArray()));
return new()
{
Headers = headers!,
BodyData = new BodyData
{
DetectedBodyType = BodyType.String,
BodyAsString = await response.ReadBodyAsStringAsync(),
},
StatusCode = response.StatusCode,
};
}
public static async Task<string> ReadBodyAsStringAsync(this HttpResponse response)
{
response.Body.Seek(0, SeekOrigin.Begin);
using var reader = new StreamReader(response.Body);
return await reader.ReadToEndAsync();
}
}

View File

@@ -0,0 +1,16 @@
// Copyright © WireMock.Net
using JsonConverter.Abstractions;
namespace WireMock.Net.Extensions.Routing.Extensions;
internal static class RequestMessageExtensions
{
public static T? GetBodyAsJson<T>(
this IRequestMessage requestMessage,
IJsonConverter jsonConverter,
JsonConverterOptions? jsonOptions = null) =>
requestMessage.Body is not null
? jsonConverter.Deserialize<T>(requestMessage.Body, jsonOptions)
: default;
}

View File

@@ -0,0 +1,9 @@
// Copyright © WireMock.Net
namespace WireMock.Net.Extensions.Routing.Extensions;
internal static class StringExtensions
{
public static string ToMatchFullStringRegex(this string regex) =>
$"^{regex}$";
}

View File

@@ -0,0 +1,39 @@
// Copyright © WireMock.Net
using System.Reflection;
namespace WireMock.Net.Extensions.Routing.Extensions;
internal static class TaskExtensions
{
[System.Diagnostics.CodeAnalysis.SuppressMessage(
"Usage",
"VSTHRD003:Avoid awaiting foreign Tasks",
Justification = "Await is required here to transform base task to generic one.")]
public static async Task<object?> ToGenericTaskAsync(this Task task)
{
await task;
var taskType = task.GetType();
if (!IsAssignableToGenericTaskType(taskType))
{
return null;
}
return task
.GetType()
.GetProperty("Result", BindingFlags.Instance | BindingFlags.Public)!
.GetValue(task);
}
private static bool IsAssignableToGenericTaskType(Type type)
{
if (type.IsGenericType &&
type.GetGenericTypeDefinition() == typeof(Task<>) &&
type.GetGenericArguments()[0] != Type.GetType("System.Threading.Tasks.VoidTaskResult"))
{
return true;
}
return type.BaseType is not null && IsAssignableToGenericTaskType(type.BaseType);
}
}

View File

@@ -0,0 +1,17 @@
// Copyright © WireMock.Net
using WireMock.Net.Extensions.Routing.Delegates;
namespace WireMock.Net.Extensions.Routing.Extensions;
internal static class WireMockHttpRequestHandlerExtensions
{
public static WireMockHttpRequestHandler UseMiddleware(
this WireMockHttpRequestHandler handler, WireMockMiddleware middleware) =>
middleware(handler);
public static WireMockHttpRequestHandler UseMiddlewareCollection(
this WireMockHttpRequestHandler handler,
IReadOnlyCollection<WireMockMiddleware> middlewareCollection) =>
middlewareCollection.Aggregate(handler, UseMiddleware);
}

View File

@@ -0,0 +1,224 @@
// Copyright © WireMock.Net
using JsonConverter.Abstractions;
using WireMock.Net.Extensions.Routing.Models;
namespace WireMock.Net.Extensions.Routing.Extensions;
/// <summary>
/// Provides extension methods for mapping HTTP routes to handlers in <see cref="WireMockRouter"/>.
/// </summary>
public static class WireMockRouterExtensions
{
/// <summary>
/// Maps a GET request to a synchronous request handler.
/// </summary>
/// <param name="source">The router to extend.</param>
/// <param name="pattern">The route pattern.</param>
/// <param name="requestHandler">The request handler function.</param>
/// <returns>The current <see cref="WireMockRouter"/> instance.</returns>
public static WireMockRouter MapGet(
this WireMockRouter source,
string pattern,
Func<WireMockRequestInfo, object?> requestHandler) =>
source.Map(HttpMethod.Get.Method, pattern, requestHandler);
/// <summary>
/// Maps a GET request to an asynchronous request handler.
/// </summary>
/// <param name="source">The router to extend.</param>
/// <param name="pattern">The route pattern.</param>
/// <param name="requestHandler">The asynchronous request handler function.</param>
/// <returns>The current <see cref="WireMockRouter"/> instance.</returns>
public static WireMockRouter MapGet(
this WireMockRouter source,
string pattern,
Func<WireMockRequestInfo, Task<object?>> requestHandler) =>
source.Map(HttpMethod.Get.Method, pattern, requestHandler);
/// <summary>
/// Maps a POST request to a synchronous request handler.
/// </summary>
/// <param name="source">The router to extend.</param>
/// <param name="pattern">The route pattern.</param>
/// <param name="requestHandler">The request handler function.</param>
/// <returns>The current <see cref="WireMockRouter"/> instance.</returns>
public static WireMockRouter MapPost(
this WireMockRouter source,
string pattern,
Func<WireMockRequestInfo, object?> requestHandler) =>
source.Map(HttpMethod.Post.Method, pattern, requestHandler);
/// <summary>
/// Maps a POST request to an asynchronous request handler.
/// </summary>
/// <param name="source">The router to extend.</param>
/// <param name="pattern">The route pattern.</param>
/// <param name="requestHandler">The asynchronous request handler function.</param>
/// <returns>The current <see cref="WireMockRouter"/> instance.</returns>
public static WireMockRouter MapPost(
this WireMockRouter source,
string pattern,
Func<WireMockRequestInfo, Task<object?>> requestHandler) =>
source.Map(HttpMethod.Post.Method, pattern, requestHandler);
/// <summary>
/// Maps a POST request to a synchronous request handler with a typed body.
/// </summary>
/// <typeparam name="TRequest">The type of the request body.</typeparam>
/// <param name="source">The router to extend.</param>
/// <param name="pattern">The route pattern.</param>
/// <param name="requestHandler">The request handler function.</param>
/// <param name="jsonConverter">The <see cref="IJsonConverter"/> [optional]. Default value is NewtonsoftJsonConverter.</param>
/// <param name="jsonOptions">The <see cref="JsonConverterOptions"/> [optional].</param>
/// <returns>The current <see cref="WireMockRouter"/> instance.</returns>
public static WireMockRouter MapPost<TRequest>(
this WireMockRouter source,
string pattern,
Func<WireMockRequestInfo<TRequest>, object?> requestHandler,
IJsonConverter? jsonConverter = null,
JsonConverterOptions? jsonOptions = null) =>
source.Map(HttpMethod.Post.Method, pattern, requestHandler, jsonConverter, jsonOptions);
/// <summary>
/// Maps a POST request to an asynchronous request handler with a typed body.
/// </summary>
/// <typeparam name="TRequest">The type of the request body.</typeparam>
/// <param name="source">The router to extend.</param>
/// <param name="pattern">The route pattern.</param>
/// <param name="requestHandler">The asynchronous request handler function.</param>
/// <param name="jsonConverter">The <see cref="IJsonConverter"/> [optional]. Default value is NewtonsoftJsonConverter.</param>
/// <param name="jsonOptions">The <see cref="JsonConverterOptions"/> [optional].</param>
/// <returns>The current <see cref="WireMockRouter"/> instance.</returns>
public static WireMockRouter MapPost<TRequest>(
this WireMockRouter source,
string pattern,
Func<WireMockRequestInfo<TRequest>, Task<object?>> requestHandler,
IJsonConverter? jsonConverter = null,
JsonConverterOptions? jsonOptions = null) =>
source.Map(HttpMethod.Post.Method, pattern, requestHandler, jsonConverter, jsonOptions);
/// <summary>
/// Maps a PUT request to a synchronous request handler.
/// </summary>
/// <param name="source">The router to extend.</param>
/// <param name="pattern">The route pattern.</param>
/// <param name="requestHandler">The request handler function.</param>
/// <returns>The current <see cref="WireMockRouter"/> instance.</returns>
public static WireMockRouter MapPut(
this WireMockRouter source,
string pattern,
Func<WireMockRequestInfo, object?> requestHandler) =>
source.Map(HttpMethod.Put.Method, pattern, requestHandler);
/// <summary>
/// Maps a PUT request to an asynchronous request handler.
/// </summary>
/// <param name="source">The router to extend.</param>
/// <param name="pattern">The route pattern.</param>
/// <param name="requestHandler">The asynchronous request handler function.</param>
/// <returns>The current <see cref="WireMockRouter"/> instance.</returns>
public static WireMockRouter MapPut(
this WireMockRouter source,
string pattern,
Func<WireMockRequestInfo, Task<object?>> requestHandler) =>
source.Map(HttpMethod.Put.Method, pattern, requestHandler);
/// <summary>
/// Maps a PUT request to a synchronous request handler with a typed body.
/// </summary>
/// <typeparam name="TRequest">The type of the request body.</typeparam>
/// <param name="source">The router to extend.</param>
/// <param name="pattern">The route pattern.</param>
/// <param name="requestHandler">The request handler function.</param>
/// <param name="jsonConverter">The <see cref="IJsonConverter"/> [optional]. Default value is NewtonsoftJsonConverter.</param>
/// <param name="jsonOptions">The <see cref="JsonConverterOptions"/> [optional].</param>
/// <returns>The current <see cref="WireMockRouter"/> instance.</returns>
public static WireMockRouter MapPut<TRequest>(
this WireMockRouter source,
string pattern,
Func<WireMockRequestInfo<TRequest>, object?> requestHandler,
IJsonConverter? jsonConverter = null,
JsonConverterOptions? jsonOptions = null) =>
source.Map(HttpMethod.Put.Method, pattern, requestHandler, jsonConverter, jsonOptions);
/// <summary>
/// Maps a PUT request to an asynchronous request handler with a typed body.
/// </summary>
/// <typeparam name="TRequest">The type of the request body.</typeparam>
/// <param name="source">The router to extend.</param>
/// <param name="pattern">The route pattern.</param>
/// <param name="requestHandler">The asynchronous request handler function.</param>
/// <param name="jsonConverter">The <see cref="IJsonConverter"/> [optional]. Default value is NewtonsoftJsonConverter.</param>
/// <param name="jsonOptions">The <see cref="JsonConverterOptions"/> [optional].</param>
/// <returns>The current <see cref="WireMockRouter"/> instance.</returns>
public static WireMockRouter MapPut<TRequest>(
this WireMockRouter source,
string pattern,
Func<WireMockRequestInfo<TRequest>, Task<object?>> requestHandler,
IJsonConverter? jsonConverter = null,
JsonConverterOptions? jsonOptions = null) =>
source.Map(HttpMethod.Put.Method, pattern, requestHandler, jsonConverter, jsonOptions);
/// <summary>
/// Maps a DELETE request to a synchronous request handler.
/// </summary>
/// <param name="source">The router to extend.</param>
/// <param name="pattern">The route pattern.</param>
/// <param name="requestHandler">The request handler function.</param>
/// <returns>The current <see cref="WireMockRouter"/> instance.</returns>
public static WireMockRouter MapDelete(
this WireMockRouter source,
string pattern,
Func<WireMockRequestInfo, object?> requestHandler) =>
source.Map(HttpMethod.Delete.Method, pattern, requestHandler);
/// <summary>
/// Maps a DELETE request to an asynchronous request handler.
/// </summary>
/// <param name="source">The router to extend.</param>
/// <param name="pattern">The route pattern.</param>
/// <param name="requestHandler">The asynchronous request handler function.</param>
/// <returns>The current <see cref="WireMockRouter"/> instance.</returns>
public static WireMockRouter MapDelete(
this WireMockRouter source,
string pattern,
Func<WireMockRequestInfo, Task<object?>> requestHandler) =>
source.Map(HttpMethod.Delete.Method, pattern, requestHandler);
/// <summary>
/// Maps a DELETE request to a synchronous request handler with a typed body.
/// </summary>
/// <typeparam name="TRequest">The type of the request body.</typeparam>
/// <param name="source">The router to extend.</param>
/// <param name="pattern">The route pattern.</param>
/// <param name="requestHandler">The request handler function.</param>
/// <param name="jsonConverter">The <see cref="IJsonConverter"/> [optional]. Default value is NewtonsoftJsonConverter.</param>
/// <param name="jsonOptions">The <see cref="JsonConverterOptions"/> [optional].</param>
/// <returns>The current <see cref="WireMockRouter"/> instance.</returns>
public static WireMockRouter MapDelete<TRequest>(
this WireMockRouter source,
string pattern,
Func<WireMockRequestInfo<TRequest>, object?> requestHandler,
IJsonConverter? jsonConverter = null,
JsonConverterOptions? jsonOptions = null) =>
source.Map(HttpMethod.Delete.Method, pattern, requestHandler, jsonConverter, jsonOptions);
/// <summary>
/// Maps a DELETE request to an asynchronous request handler with a typed body.
/// </summary>
/// <typeparam name="TRequest">The type of the request body.</typeparam>
/// <param name="source">The router to extend.</param>
/// <param name="pattern">The route pattern.</param>
/// <param name="requestHandler">The asynchronous request handler function.</param>
/// <param name="jsonConverter">The <see cref="IJsonConverter"/> [optional]. Default value is NewtonsoftJsonConverter.</param>
/// <param name="jsonOptions">The <see cref="JsonConverterOptions"/> [optional].</param>
/// <returns>The current <see cref="WireMockRouter"/> instance.</returns>
public static WireMockRouter MapDelete<TRequest>(
this WireMockRouter source,
string pattern,
Func<WireMockRequestInfo<TRequest>, Task<object?>> requestHandler,
IJsonConverter? jsonConverter = null,
JsonConverterOptions? jsonOptions = null) =>
source.Map(HttpMethod.Delete.Method, pattern, requestHandler, jsonConverter, jsonOptions);
}

View File

@@ -0,0 +1,35 @@
// Copyright © WireMock.Net
using WireMock.Matchers;
using WireMock.Net.Extensions.Routing.Delegates;
using WireMock.RequestBuilders;
using WireMock.ResponseBuilders;
using WireMock.Server;
namespace WireMock.Net.Extensions.Routing.Extensions;
/// <summary>
/// Provides extension methods for mapping HTTP requests to handlers in <see cref="WireMockServer"/>.
/// </summary>
public static class WireMockServerExtensions
{
/// <summary>
/// Maps a request to a WireMock.Net server using the specified method, path matcher, and request handler.
/// </summary>
/// <param name="source">The WireMock.Net server to extend.</param>
/// <param name="method">The HTTP method to match.</param>
/// <param name="pathMatcher">The matcher for the request path.</param>
/// <param name="httpRequestHandler">The handler to process the request.</param>
/// <returns>The current <see cref="WireMockServer"/> instance.</returns>
public static WireMockServer Map(
this WireMockServer source,
string method,
IStringMatcher pathMatcher,
WireMockHttpRequestHandler httpRequestHandler)
{
source
.Given(Request.Create().WithPath(pathMatcher).UsingMethod(method))
.RespondWith(Response.Create().WithCallback(req => httpRequestHandler(req)));
return source;
}
}

View File

@@ -0,0 +1,29 @@
// Copyright © WireMock.Net
namespace WireMock.Net.Extensions.Routing.Models;
/// <summary>
/// Represents request information for WireMock.Net routing, including the request message and route arguments.
/// </summary>
public class WireMockRequestInfo
{
/// <summary>
/// Initializes a new instance of the <see cref="WireMockRequestInfo"/> class.
/// </summary>
/// <param name="request">The incoming request message.</param>
public WireMockRequestInfo(IRequestMessage request)
{
Request = request;
}
/// <summary>
/// Gets the incoming request message.
/// </summary>
public IRequestMessage Request { get; }
/// <summary>
/// Gets or initializes the route arguments extracted from the request path.
/// </summary>
public IDictionary<string, object> RouteArgs { get; init; } =
new Dictionary<string, object>();
}

View File

@@ -0,0 +1,24 @@
// Copyright © WireMock.Net
namespace WireMock.Net.Extensions.Routing.Models;
/// <summary>
/// Represents request information with a strongly-typed deserialized body for WireMock.Net routing.
/// </summary>
/// <typeparam name="TBody">The type of the deserialized request body.</typeparam>
public sealed class WireMockRequestInfo<TBody> : WireMockRequestInfo
{
/// <summary>
/// Initializes a new instance of the <see cref="WireMockRequestInfo{TBody}"/> class.
/// </summary>
/// <param name="request">The incoming request message.</param>
public WireMockRequestInfo(IRequestMessage request)
: base(request)
{
}
/// <summary>
/// Gets or initializes the deserialized request body.
/// </summary>
public TBody? Body { get; init; }
}

View File

@@ -0,0 +1,138 @@
# WireMock.Net.Extensions.Routing
**WireMock.Net.Extensions.Routing** extends [WireMock.Net](https://github.com/wiremock/wiremock) with modern, minimal-API-style routing for .NET. It provides extension methods for expressive, maintainable, and testable HTTP routing, inspired by [ASP.NET Core Minimal APIs](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/minimal-apis?view=aspnetcore-9.0).
---
## Motivation
While [WireMock.Net](https://github.com/WireMock-Net/WireMock.Net) is a powerful tool for HTTP mocking in .NET, its native API for defining routes and request handlers can be verbose and require significant boilerplate. Setting up even simple endpoints often involves multiple chained method calls, manual parsing of request data, and repetitive configuration, which can make tests harder to read and maintain.
**WireMock.Net.Extensions.Routing** addresses these pain points by introducing a concise, fluent, and minimal-API-inspired approach to routing. This makes your test code:
- **More readable:** Route definitions are clear and expressive, closely resembling production minimal APIs.
- **Easier to maintain:** Less boilerplate means fewer places for errors and easier refactoring.
- **Faster to write:** Define routes and handlers in a single line, with strong typing and async support.
### Example: Native WireMock.Net vs. WireMock.Net.Extensions.Routing
#### Native WireMock.Net
```csharp
server.Given(
Request.Create().WithPath("/hello").UsingGet()
)
.RespondWith(
Response.Create().WithBody("Hello, world!")
);
server.Given(
Request.Create().WithPath("/user/*").UsingGet()
)
.RespondWith(
Response.Create().WithCallback(request =>
{
var id = request.PathSegments[1];
// ...fetch user by id...
return new ResponseMessage { Body = $"User: {id}" };
})
);
```
#### With WireMock.Net.Extensions.Routing
```csharp
router.MapGet("/hello", _ => "Hello, world!");
router.MapGet("/user/{id:int}", requestInfo =>
{
var id = requestInfo.RouteArgs["id"];
// ...fetch user by id...
return $"User: {id}";
});
```
With **WireMock.Net.Extensions.Routing**, you get:
- Minimal, one-line route definitions
- Typed route parameters (e.g., `{id:int}`)
- Direct access to parsed route arguments and request bodies
- Async handler support
This leads to more maintainable, scalable, and production-like test code.
---
## Features
- Minimal API-style route definitions for WireMock.Net
- Strongly-typed request handling
- Routing parameters with constraints (`int` and `string` are currently supported)
- Asynchronous handlers
- Fluent, composable routing extensions
- Easy integration with existing WireMock.Net servers
- .NET 8+ support
---
## Installation
Install from NuGet:
```shell
dotnet add package WireMock.Net.Extensions.Routing
```
---
## Quick Start
```csharp
using System.Net.Http.Json;
using WireMock.Net.Extensions.Routing;
using WireMock.Net.Extensions.Routing.Extensions;
using WireMock.Server;
var server = WireMockServer.Start();
var router = new WireMockRouter(server);
router.MapGet("/hello", _ => "Hello, world!");
using var client = server.CreateClient();
var result = await client.GetFromJsonAsync<string>("/hello");
// Hello, world!
```
---
## Usage
### Routing with route parameters
```csharp
router.MapGet("/user/{id:int}", async requestInfo => {
var userId = requestInfo.RouteArgs["id"];
// var user = await ...
return user;
});
```
### Strongly-Typed Request Info
```csharp
router.MapPost<Item>("/api/items", requestInfo => {
var item = requestInfo.Body!;
// process item
return Results.Json(new { success = true });
});
```
### Supported Methods
- `MapGet`, `MapPost`, `MapPut`, `MapDelete`
---
## Documentation
- [API Reference](./src/WireMock.Net.Extensions.Routing/)
- [WireMock.Net Documentation](https://github.com/WireMock-Net/WireMock.Net)

View File

@@ -0,0 +1,107 @@
// Copyright © WireMock.Net
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using System.Text.RegularExpressions;
using WireMock.Net.Extensions.Routing.Extensions;
namespace WireMock.Net.Extensions.Routing.Utils;
internal static class RoutePattern
{
private static readonly Regex ArgRegex =
new(@"{(?'name'\w+)(?::(?'type'\w+))?}", RegexOptions.Compiled);
public static IDictionary<string, object> GetArgs(string pattern, string route) =>
TryGetArgs(pattern, route, out var args)
? args
: throw new InvalidOperationException(
$"Url {route} does not match route pattern {pattern}");
public static bool TryGetArgs(
string pattern, string route, [NotNullWhen(true)] out IDictionary<string, object>? args)
{
var regex = new Regex(ToRegex(pattern), RegexOptions.IgnoreCase);
var match = regex.Match(route);
if (!match.Success)
{
args = null;
return false;
}
var routeArgTypeMap = GetArgTypeMap(pattern);
args = match.Groups
.Cast<Group>()
.Where(g => g.Index > 0)
.ToDictionary(g => g.Name, g => routeArgTypeMap[g.Name].Parse(g.Value));
return true;
}
public static string ToRegex(string pattern)
{
return ArgRegex
.Replace(pattern, m => $"(?'{m.Groups["name"].Value}'{GetArgMatchingRegex(m)})")
.ToMatchFullStringRegex();
static string GetArgMatchingRegex(Match match) =>
ArgType.GetByName(match.Groups["type"].Value).GetRegex();
}
private static IDictionary<string, ArgType> GetArgTypeMap(string pattern) =>
ArgRegex
.Matches(pattern)
.ToDictionary(
m => m.Groups["name"].Value, m => ArgType.GetByName(m.Groups["type"].Value));
private abstract record ArgType
{
private ArgType(string name)
{
Name = name;
}
public string Name { get; }
public static ArgType String { get; } = new StringArgType();
public static ArgType Int { get; } = new IntArgType();
private static IReadOnlyCollection<ArgType> All { get; } =
typeof(ArgType)
.GetProperties(BindingFlags.Public | BindingFlags.Static)
.Where(p => p.PropertyType.IsAssignableTo(typeof(ArgType)))
.Select(p => p.GetValue(null))
.Cast<ArgType>()
.ToList();
private static IReadOnlyDictionary<string, ArgType> MapByName { get; } =
All.ToDictionary(x => x.Name);
public static ArgType GetByName(string name) =>
GetByNameOrDefault(name)
?? throw new InvalidOperationException($"Route argument type {name} is not found");
public static ArgType? GetByNameOrDefault(string name) =>
!string.IsNullOrEmpty(name)
? MapByName.GetValueOrDefault(name)
: String;
public abstract object Parse(string input);
public abstract string GetRegex();
private sealed record StringArgType() : ArgType("string")
{
public override object Parse(string input) => input;
public override string GetRegex() => @".*";
}
private sealed record IntArgType() : ArgType("int")
{
public override object Parse(string input) => int.Parse(input);
public override string GetRegex() => @"-?\d+";
}
}
}

View File

@@ -0,0 +1,34 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Description>WireMock.Net.Routing extends WireMock.Net with modern, minimal-API-style routing for .NET</Description>
<Authors>Gennadii Saltyshchak</Authors>
<TargetFrameworks>net8.0</TargetFrameworks>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<PackageTags>tdd;mock;http;wiremock;test;server;unittest;routing;minimalapi</PackageTags>
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<AllowedOutputExtensionsInPackageBuildOutputFolder>$(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb</AllowedOutputExtensionsInPackageBuildOutputFolder>
<EmbedUntrackedSources>true</EmbedUntrackedSources>
<SignAssembly>true</SignAssembly>
<AssemblyOriginatorKeyFile>../WireMock.Net/WireMock.Net.snk</AssemblyOriginatorKeyFile>
<PublicSign Condition=" '$(OS)' != 'Windows_NT' ">true</PublicSign>
<PackageReadmeFile>README.md</PackageReadmeFile>
<ImplicitUsings>enable</ImplicitUsings>
<!--<TreatWarningsAsErrors>true</TreatWarningsAsErrors>-->
</PropertyGroup>
<ItemGroup>
<None Include="..\..\README.md" Link="README.md" Pack="true" PackagePath="" />
</ItemGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="JsonConverter.Newtonsoft.Json" Version="0.7.2" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\WireMock.Net.Minimal\WireMock.Net.Minimal.csproj" />
</ItemGroup>
</Project>

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