mirror of
https://github.com/wiremock/WireMock.Net.git
synced 2026-01-18 17:37:01 +01:00
Compare commits
53 Commits
1.14.0
...
MappingSer
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5f6f5663e1 | ||
|
|
803e983645 | ||
|
|
c88e7378a7 | ||
|
|
b090296559 | ||
|
|
8f81fb0d96 | ||
|
|
e5afd69f7c | ||
|
|
b4af082586 | ||
|
|
f38133d7a4 | ||
|
|
3011509c51 | ||
|
|
597c95000e | ||
|
|
ad150280fd | ||
|
|
4617b99c30 | ||
|
|
6bb506e1f8 | ||
|
|
ffd4d89946 | ||
|
|
2d46c86f47 | ||
|
|
75f4fbe9d0 | ||
|
|
b40fce694d | ||
|
|
16e3872402 | ||
|
|
79f758fd4d | ||
|
|
4c797c328f | ||
|
|
aafe2339e4 | ||
|
|
a5e75a7278 | ||
|
|
708af55051 | ||
|
|
e6e4138466 | ||
|
|
56f65c19e2 | ||
|
|
6cc8c04486 | ||
|
|
6aef4816a5 | ||
|
|
dd4770a78b | ||
|
|
de15e0d48e | ||
|
|
f6452d1dce | ||
|
|
197a211a52 | ||
|
|
3cfeec6035 | ||
|
|
b57d5e7548 | ||
|
|
36b89afce5 | ||
|
|
e2acac55a4 | ||
|
|
ceabd27ce0 | ||
|
|
f8e2c7ee90 | ||
|
|
c25d8f33d2 | ||
|
|
6da190e596 | ||
|
|
44388ce80d | ||
|
|
5e25ca767d | ||
|
|
0cc583a4a3 | ||
|
|
f9633adac1 | ||
|
|
37bad618a3 | ||
|
|
8e69f36f04 | ||
|
|
21601889e0 | ||
|
|
dfeabf228e | ||
|
|
1feb0ade70 | ||
|
|
8b1bd1b21b | ||
|
|
c1b23b615e | ||
|
|
5885324dfb | ||
|
|
e3f3e0a8f2 | ||
|
|
685d28db0b |
45
CHANGELOG.md
45
CHANGELOG.md
@@ -1,3 +1,48 @@
|
|||||||
|
# 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)
|
# 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)
|
- [#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)
|
- [#1363](https://github.com/wiremock/WireMock.Net/pull/1363) - Add Tls13 [bug] contributed by [StefH](https://github.com/StefH)
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<Version>1.14.0</Version>
|
<VersionPrefix>1.22.0</VersionPrefix>
|
||||||
<PackageIcon>WireMock.Net-Logo.png</PackageIcon>
|
<PackageIcon>WireMock.Net-Logo.png</PackageIcon>
|
||||||
<PackageProjectUrl>https://github.com/wiremock/WireMock.Net</PackageProjectUrl>
|
<PackageProjectUrl>https://github.com/wiremock/WireMock.Net</PackageProjectUrl>
|
||||||
<PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
|
<PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
rem https://github.com/StefH/GitHubReleaseNotes
|
rem https://github.com/StefH/GitHubReleaseNotes
|
||||||
|
|
||||||
SET version=1.14.0
|
SET version=1.22.0
|
||||||
|
|
||||||
GitHubReleaseNotes --output CHANGELOG.md --skip-empty-releases --exclude-labels wontfix test question invalid doc duplicate example environment --version %version% --token %GH_TOKEN%
|
GitHubReleaseNotes --output CHANGELOG.md --skip-empty-releases --exclude-labels wontfix test question invalid doc duplicate example environment --version %version% --token %GH_TOKEN%
|
||||||
|
|
||||||
|
|||||||
@@ -15,48 +15,48 @@ Lightweight Http Mocking Server for .NET, inspired by WireMock.org (from the Jav
|
|||||||
|
|
||||||
### :star: Stubbing
|
### :star: Stubbing
|
||||||
A core feature of WireMock.Net is the ability to return predefined HTTP responses for requests matching criteria.
|
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
|
### :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
|
### :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
|
### :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
|
### :star: Using
|
||||||
WireMock.Net can be used in several ways:
|
WireMock.Net can be used in several ways:
|
||||||
|
|
||||||
#### UnitTesting
|
#### UnitTesting
|
||||||
You can use your favorite test framework and use WireMock within your tests, see
|
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
|
### 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
|
### 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
|
#### 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
|
#### 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
|
#### 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
|
#### 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
|
#### In a docker container
|
||||||
There is also a Linux and Windows-Nano container available at [hub.docker.com](https://hub.docker.com/r/sheyenrath).
|
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).
|
For more details see also [Docker](https://github.com/wiremock/WireMock.Net-docker).
|
||||||
|
|
||||||
#### HTTPS / SSL
|
#### 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
|
## :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/).
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
# 1.14.0 (06 October 2025)
|
# 1.22.0 (02 January 2026)
|
||||||
- #1362 Update ProxyUrlReplaceSettingsModel with TransformTemplate property [feature]
|
- #1412 chore(testcontainers): bump up Testcontainers to version 4.10.0 [feature]
|
||||||
- #1363 Add Tls13 [bug]
|
- #1411 WireMock.Net.Testcontainers isn't compatible with Testcontainers 4.10.0 [bug]
|
||||||
- #1342 Facing SSL certificate validation error when using .NET 5 and above framework for some http services during recording [bug]
|
|
||||||
- #1360 ProxyURL path configuration [feature]
|
|
||||||
|
|
||||||
The full release notes can be found here: https://github.com/wiremock/WireMock.Net/blob/master/CHANGELOG.md
|
The full release notes can be found here: https://github.com/wiremock/WireMock.Net/blob/master/CHANGELOG.md
|
||||||
45
README.md
45
README.md
@@ -1,7 +1,11 @@
|
|||||||
#  WireMock.Net
|
#  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
|
## :star: Key Features
|
||||||
* HTTP response stubbing, matchable on URL/Path, headers, cookies and body content patterns
|
* 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
|
|||||||
| **Issues** | [](https://github.com/wiremock/WireMock.Net/issues) |
|
| **Issues** | [](https://github.com/wiremock/WireMock.Net/issues) |
|
||||||
| | |
|
| | |
|
||||||
| ***Quality*** | |
|
| ***Quality*** | |
|
||||||
| **Build Azure** | [](https://stef.visualstudio.com/WireMock.Net/_build/latest?definitionId=7) |
|
| **Build Azure** | [](https://stef.visualstudio.com/WireMock.Net/_build/latest?definitionId=61) |
|
||||||
| **Quality** | [](https://sonarcloud.io/project/issues?id=WireMock-Net_WireMock.Net) [](https://www.codefactor.io/repository/github/wiremock/wiremock.net) |
|
| **Quality** | [](https://sonarcloud.io/project/issues?id=WireMock-Net_WireMock.Net) [](https://www.codefactor.io/repository/github/wiremock/wiremock.net) |
|
||||||
| **Sonar Bugs** | [](https://sonarcloud.io/project/issues?id=WireMock-Net_WireMock.Net&resolved=false&types=BUG) [](https://sonarcloud.io/project/issues?id=WireMock-Net_WireMock.Net&resolved=false&types=CODE_SMELL) |
|
| **Sonar Bugs** | [](https://sonarcloud.io/project/issues?id=WireMock-Net_WireMock.Net&resolved=false&types=BUG) [](https://sonarcloud.io/project/issues?id=WireMock-Net_WireMock.Net&resolved=false&types=CODE_SMELL) |
|
||||||
| **Coverage** | [](https://sonarcloud.io/component_measures?id=WireMock-Net_WireMock.Net&metric=coverage) [](https://codecov.io/gh/wiremock/WireMock.Net)|
|
| **Coverage** | [](https://sonarcloud.io/component_measures?id=WireMock-Net_WireMock.Net&metric=coverage) [](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
|
### :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) |
|
||||||
| - | - | - |
|
| - | - | - |
|
||||||
| **WireMock.Net** | [](https://www.nuget.org/packages/WireMock.Net) | [](https://www.myget.org/feed/wiremock-net/package/nuget/WireMock.Net)
|
| **WireMock.Net** | [](https://www.nuget.org/packages/WireMock.Net) | [](https://www.myget.org/feed/wiremock-net/package/nuget/WireMock.Net)
|
||||||
| **WireMock.Net.Minimal** 🔺| [](https://www.nuget.org/packages/WireMock.Net.Minimal) | [](https://www.myget.org/feed/wiremock-net/package/nuget/WireMock.Net.Minimal)
|
| **WireMock.Net.Minimal** 🔺| [](https://www.nuget.org/packages/WireMock.Net.Minimal) | [](https://www.myget.org/feed/wiremock-net/package/nuget/WireMock.Net.Minimal)
|
||||||
@@ -49,7 +53,9 @@ For more info, see also this WIKI page: [What is WireMock.Net](https://github.co
|
|||||||
| **WireMock.Net.AwesomeAssertions** | [](https://www.nuget.org/packages/WireMock.Net.AwesomeAssertions) | [](https://www.myget.org/feed/wiremock-net/package/nuget/WireMock.Net.AwesomeAssertions)
|
| **WireMock.Net.AwesomeAssertions** | [](https://www.nuget.org/packages/WireMock.Net.AwesomeAssertions) | [](https://www.myget.org/feed/wiremock-net/package/nuget/WireMock.Net.AwesomeAssertions)
|
||||||
| **WireMock.Net.FluentAssertions** | [](https://www.nuget.org/packages/WireMock.Net.FluentAssertions) | [](https://www.myget.org/feed/wiremock-net/package/nuget/WireMock.Net.FluentAssertions)
|
| **WireMock.Net.FluentAssertions** | [](https://www.nuget.org/packages/WireMock.Net.FluentAssertions) | [](https://www.myget.org/feed/wiremock-net/package/nuget/WireMock.Net.FluentAssertions)
|
||||||
| **WireMock.Net.xUnit** | [](https://www.nuget.org/packages/WireMock.Net.xUnit) | [](https://www.myget.org/feed/wiremock-net/package/nuget/WireMock.Net.xUnit)
|
| **WireMock.Net.xUnit** | [](https://www.nuget.org/packages/WireMock.Net.xUnit) | [](https://www.myget.org/feed/wiremock-net/package/nuget/WireMock.Net.xUnit)
|
||||||
|
| **WireMock.Net.xUnit.v3** | [](https://www.nuget.org/packages/WireMock.Net.xUnit.v3) | [](https://www.myget.org/feed/wiremock-net/package/nuget/WireMock.Net.xUnit.v3)
|
||||||
| **WireMock.Net.TUnit** | [](https://www.nuget.org/packages/WireMock.Net.TUnit) | [](https://www.myget.org/feed/wiremock-net/package/nuget/WireMock.Net.TUnit)
|
| **WireMock.Net.TUnit** | [](https://www.nuget.org/packages/WireMock.Net.TUnit) | [](https://www.myget.org/feed/wiremock-net/package/nuget/WireMock.Net.TUnit)
|
||||||
|
| **WireMock.Net.NUnit** | [](https://www.nuget.org/packages/WireMock.Net.NUnit) | [](https://www.myget.org/feed/wiremock-net/package/nuget/WireMock.Net.NUnit)
|
||||||
| | | |
|
| | | |
|
||||||
| **WireMock.Net.Extensions.Routing** | [](https://www.nuget.org/packages/WireMock.Net.Extensions.Routing) | [](https://www.myget.org/feed/wiremock-net/package/nuget/WireMock.Net.Extensions.Routing)
|
| **WireMock.Net.Extensions.Routing** | [](https://www.nuget.org/packages/WireMock.Net.Extensions.Routing) | [](https://www.myget.org/feed/wiremock-net/package/nuget/WireMock.Net.Extensions.Routing)
|
||||||
| **WireMock.Net.Matchers.CSharpCode** | [](https://www.nuget.org/packages/WireMock.Net.Matchers.CSharpCode) | [](https://www.myget.org/feed/wiremock-net/package/nuget/WireMock.Net.Matchers.CSharpCode)
|
| **WireMock.Net.Matchers.CSharpCode** | [](https://www.nuget.org/packages/WireMock.Net.Matchers.CSharpCode) | [](https://www.myget.org/feed/wiremock-net/package/nuget/WireMock.Net.Matchers.CSharpCode)
|
||||||
@@ -89,52 +95,55 @@ To still enable this feature, you need to add the `Environment` category to the
|
|||||||
---
|
---
|
||||||
|
|
||||||
## :memo: Development
|
## :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
|
## :star: Stubbing
|
||||||
A core feature of WireMock.Net is the ability to return predefined HTTP responses for requests matching criteria.
|
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
|
## :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
|
## :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
|
## :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
|
## :star: Using
|
||||||
WireMock.Net can be used in several ways:
|
WireMock.Net can be used in several ways:
|
||||||
|
|
||||||
### UnitTesting
|
### UnitTesting
|
||||||
You can use your favorite test framework and use WireMock within your tests, see
|
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
|
### 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
|
### 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
|
### 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
|
### 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
|
### 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
|
### 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
|
### In a docker container
|
||||||
There is also a Linux and Windows-Nano container available at [hub.docker.com](https://hub.docker.com/r/sheyenrath).
|
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).
|
For more details see also [Docker](https://github.com/wiremock/WireMock.Net-docker).
|
||||||
|
|
||||||
#### HTTPS / SSL
|
### 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 documentation page: [What is WireMock.Net](https://wiremock.org/dotnet/what-is-wiremock-net/).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
|
|
||||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
# Visual Studio Version 17
|
# Visual Studio Version 18
|
||||||
VisualStudioVersion = 17.0.31521.260
|
VisualStudioVersion = 18.0.11205.157
|
||||||
MinimumVisualStudioVersion = 10.0.40219.1
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{8F890C6F-9ACC-438D-928A-AD61CDA862F2}"
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{8F890C6F-9ACC-438D-928A-AD61CDA862F2}"
|
||||||
EndProject
|
EndProject
|
||||||
@@ -144,6 +144,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WireMock.Net.Extensions.Rou
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WireMock.Net.ProtoBuf", "src\WireMock.Net.ProtoBuf\WireMock.Net.ProtoBuf.csproj", "{B47413AA-55D3-49A7-896A-17ADBFF72407}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WireMock.Net.ProtoBuf", "src\WireMock.Net.ProtoBuf\WireMock.Net.ProtoBuf.csproj", "{B47413AA-55D3-49A7-896A-17ADBFF72407}"
|
||||||
EndProject
|
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
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
@@ -350,6 +354,14 @@ Global
|
|||||||
{B47413AA-55D3-49A7-896A-17ADBFF72407}.Debug|Any CPU.Build.0 = 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.ActiveCfg = Release|Any CPU
|
||||||
{B47413AA-55D3-49A7-896A-17ADBFF72407}.Release|Any CPU.Build.0 = Release|Any CPU
|
{B47413AA-55D3-49A7-896A-17ADBFF72407}.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
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
@@ -407,6 +419,8 @@ Global
|
|||||||
{3FCBCA9C-9DB0-4A96-B47E-30470764CC9C} = {0BB8B634-407A-4610-A91F-11586990767A}
|
{3FCBCA9C-9DB0-4A96-B47E-30470764CC9C} = {0BB8B634-407A-4610-A91F-11586990767A}
|
||||||
{1E874C8F-08A2-493B-8421-619F9A6E9E77} = {8F890C6F-9ACC-438D-928A-AD61CDA862F2}
|
{1E874C8F-08A2-493B-8421-619F9A6E9E77} = {8F890C6F-9ACC-438D-928A-AD61CDA862F2}
|
||||||
{B47413AA-55D3-49A7-896A-17ADBFF72407} = {8F890C6F-9ACC-438D-928A-AD61CDA862F2}
|
{B47413AA-55D3-49A7-896A-17ADBFF72407} = {8F890C6F-9ACC-438D-928A-AD61CDA862F2}
|
||||||
|
{4F46BD02-BEBC-4B2D-B857-4169AD1FB067} = {8F890C6F-9ACC-438D-928A-AD61CDA862F2}
|
||||||
|
{2DBBD70D-8051-441F-92BB-FF9B8B4B4982} = {8F890C6F-9ACC-438D-928A-AD61CDA862F2}
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
SolutionGuid = {DC539027-9852-430C-B19F-FD035D018458}
|
SolutionGuid = {DC539027-9852-430C-B19F-FD035D018458}
|
||||||
|
|||||||
@@ -100,7 +100,7 @@ jobs:
|
|||||||
- job: Windows_Build_Test
|
- job: Windows_Build_Test
|
||||||
|
|
||||||
pool:
|
pool:
|
||||||
vmImage: 'windows-2022'
|
vmImage: 'windows-2025'
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- task: UseDotNet@2
|
- task: UseDotNet@2
|
||||||
@@ -141,9 +141,13 @@ jobs:
|
|||||||
dependsOn: Windows_Build_Test
|
dependsOn: Windows_Build_Test
|
||||||
|
|
||||||
pool:
|
pool:
|
||||||
vmImage: 'windows-2022'
|
vmImage: 'windows-2025'
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
|
- script: |
|
||||||
|
echo "BuildId = $(buildId)"
|
||||||
|
displayName: 'Print buildId'
|
||||||
|
|
||||||
- task: UseDotNet@2
|
- task: UseDotNet@2
|
||||||
displayName: Use .NET 8.0
|
displayName: Use .NET 8.0
|
||||||
inputs:
|
inputs:
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
pool:
|
pool:
|
||||||
vmImage: 'windows-2022'
|
vmImage: 'windows-2025'
|
||||||
|
|
||||||
variables:
|
variables:
|
||||||
Prerelease: ''
|
Prerelease: ''
|
||||||
|
|||||||
@@ -21,4 +21,13 @@
|
|||||||
<PackageReference Include="Aspire.Hosting.AppHost" Version="9.2.0" />
|
<PackageReference Include="Aspire.Hosting.AppHost" Version="9.2.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
<ItemGroup>
|
||||||
|
<None Update="__admin\mappings\*.proto">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
|
<None Update="__admin\mappings\*.json">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
@@ -4,12 +4,25 @@ var builder = DistributedApplication.CreateBuilder(args);
|
|||||||
|
|
||||||
// IResourceBuilder<ProjectResource> apiService = builder.AddProject<Projects.AspireApp1_ApiService>("apiservice");
|
// 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
|
//IResourceBuilder<WireMockServerResource> apiService1 = builder
|
||||||
.AddWireMock("apiservice", WireMockServerArguments.DefaultPort)
|
// //.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)
|
.WithMappingsPath(mappingsPath)
|
||||||
.WithReadStaticMappings()
|
|
||||||
.WithWatchStaticMappings()
|
.WithWatchStaticMappings()
|
||||||
.WithApiMappingBuilder(WeatherForecastApiMock.BuildAsync);
|
.WithApiMappingBuilder(WeatherForecastApiMock.BuildAsync);
|
||||||
|
|
||||||
@@ -45,6 +58,7 @@ IResourceBuilder<WireMockServerResource> apiService = builder
|
|||||||
|
|
||||||
builder.AddProject<Projects.AspireApp1_Web>("webfrontend")
|
builder.AddProject<Projects.AspireApp1_Web>("webfrontend")
|
||||||
.WithExternalHttpEndpoints()
|
.WithExternalHttpEndpoints()
|
||||||
.WithReference(apiService);
|
.WithReference(apiService2)
|
||||||
|
.WaitFor(apiService2);
|
||||||
|
|
||||||
builder.Build().Run();
|
await builder.Build().RunAsync();
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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"
|
||||||
|
}
|
||||||
@@ -267,7 +267,7 @@ namespace WireMock.Net.ConsoleApplication
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Run()
|
public static async Task RunAsync()
|
||||||
{
|
{
|
||||||
//RunSse();
|
//RunSse();
|
||||||
//RunOnLocal();
|
//RunOnLocal();
|
||||||
@@ -290,25 +290,56 @@ namespace WireMock.Net.ConsoleApplication
|
|||||||
|
|
||||||
var server = WireMockServer.Start();
|
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
|
server
|
||||||
.Given(Request.Create()
|
.Given(Request.Create().UsingGet().WithPath("/p"))
|
||||||
.WithPath("todos")
|
.WithProbability(pX)
|
||||||
.UsingGet()
|
.RespondWith(Response.Create().WithStatusCode(200).WithBody("X"));
|
||||||
)
|
|
||||||
.RespondWith(Response.Create()
|
|
||||||
.WithBodyAsJson(todos.Values)
|
|
||||||
);
|
|
||||||
|
|
||||||
server
|
server
|
||||||
.Given(Request.Create()
|
.Given(Request.Create().UsingGet().WithPath("/p"))
|
||||||
.UsingGet()
|
.RespondWith(Response.Create().WithStatusCode(200).WithBody("default"));
|
||||||
.WithPath("todos")
|
|
||||||
.WithParam("id")
|
|
||||||
)
|
|
||||||
.RespondWith(Response.Create()
|
|
||||||
.WithBodyAsJson(rm => todos[int.Parse(rm.Query!["id"].ToString())])
|
|
||||||
);
|
|
||||||
|
|
||||||
|
// 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
|
using var httpAndHttpsWithPort = WireMockServer.Start(new WireMockServerSettings
|
||||||
{
|
{
|
||||||
HostingScheme = HostingScheme.HttpAndHttps,
|
HostingScheme = HostingScheme.HttpAndHttps,
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using log4net;
|
using log4net;
|
||||||
using log4net.Config;
|
using log4net.Config;
|
||||||
using log4net.Repository;
|
using log4net.Repository;
|
||||||
@@ -14,10 +15,10 @@ static class Program
|
|||||||
private static readonly ILoggerRepository LogRepository = LogManager.GetRepository(Assembly.GetEntryAssembly());
|
private static readonly ILoggerRepository LogRepository = LogManager.GetRepository(Assembly.GetEntryAssembly());
|
||||||
private static readonly ILog Log = LogManager.GetLogger(typeof(Program));
|
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"));
|
XmlConfigurator.Configure(LogRepository, new FileInfo("log4net.config"));
|
||||||
|
|
||||||
MainApp.Run();
|
await MainApp.RunAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -22,7 +22,7 @@ public class MatcherModel
|
|||||||
public object? Pattern { get; set; }
|
public object? Pattern { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <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>
|
/// </summary>
|
||||||
public object[]? Patterns { get; set; }
|
public object[]? Patterns { get; set; }
|
||||||
|
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ public class ResponseModel
|
|||||||
public object? BodyAsJson { get; set; }
|
public object? BodyAsJson { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets a value indicating whether child objects to be indented according to the Newtonsoft.Json.JsonTextWriter.Indentation and Newtonsoft.Json.JsonTextWriter.IndentChar settings.
|
/// Gets or sets a value indicating whether the Json Body String needs to be indented.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool? BodyAsJsonIndented { get; set; }
|
public bool? BodyAsJsonIndented { get; set; }
|
||||||
|
|
||||||
|
|||||||
@@ -24,4 +24,13 @@ public class StatusModel
|
|||||||
/// The error message.
|
/// The error message.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string? Error { get; set; }
|
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}]";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
// Copyright © WireMock.Net
|
// Copyright © WireMock.Net
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using WireMock.Validators;
|
||||||
|
|
||||||
// ReSharper disable once CheckNamespace
|
// ReSharper disable once CheckNamespace
|
||||||
namespace WireMock.Admin.Mappings;
|
namespace WireMock.Admin.Mappings;
|
||||||
@@ -94,9 +95,14 @@ public partial class RequestModelBuilder
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Set the Path.
|
/// Set the Path. Must start with a forward slash (/).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public RequestModelBuilder WithPath(string value) => WithPath(() => value);
|
public RequestModelBuilder WithPath(string value)
|
||||||
|
{
|
||||||
|
PathValidator.ValidateAndThrow(value);
|
||||||
|
|
||||||
|
return WithPath(() => value);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Set the Path.
|
/// Set the Path.
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ public interface IBodyData
|
|||||||
object? BodyAsJson { get; set; }
|
object? BodyAsJson { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets a value indicating whether child objects to be indented according to the Newtonsoft.Json.JsonTextWriter.Indentation and Newtonsoft.Json.JsonTextWriter.IndentChar settings.
|
/// Gets or sets a value indicating whether the Json Body String needs to be indented.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
bool? BodyAsJsonIndented { get; set; }
|
bool? BodyAsJsonIndented { get; set; }
|
||||||
|
|
||||||
|
|||||||
19
src/WireMock.Net.Abstractions/Validators/PathValidator.cs
Normal file
19
src/WireMock.Net.Abstractions/Validators/PathValidator.cs
Normal 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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -30,7 +30,10 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<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>
|
</ItemGroup>
|
||||||
|
|
||||||
<PropertyGroup Condition="'$(Configuration)' == 'Debug - Sonar'">
|
<PropertyGroup Condition="'$(Configuration)' == 'Debug - Sonar'">
|
||||||
|
|||||||
44
src/WireMock.Net.Aspire/WireMockHealthCheck.cs
Normal file
44
src/WireMock.Net.Aspire/WireMockHealthCheck.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
12
src/WireMock.Net.Aspire/WireMockMappingState.cs
Normal file
12
src/WireMock.Net.Aspire/WireMockMappingState.cs
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
// Copyright © WireMock.Net
|
||||||
|
|
||||||
|
namespace WireMock.Net.Aspire;
|
||||||
|
|
||||||
|
internal enum WireMockMappingState
|
||||||
|
{
|
||||||
|
NoMappings = 0,
|
||||||
|
|
||||||
|
NotSubmitted = 1,
|
||||||
|
|
||||||
|
Submitted = 2
|
||||||
|
}
|
||||||
@@ -1,7 +1,9 @@
|
|||||||
// Copyright © WireMock.Net
|
// Copyright © WireMock.Net
|
||||||
|
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using Stef.Validation;
|
||||||
using WireMock.Client.Builders;
|
using WireMock.Client.Builders;
|
||||||
|
using WireMock.Util;
|
||||||
|
|
||||||
// ReSharper disable once CheckNamespace
|
// ReSharper disable once CheckNamespace
|
||||||
namespace Aspire.Hosting;
|
namespace Aspire.Hosting;
|
||||||
@@ -21,10 +23,15 @@ public class WireMockServerArguments
|
|||||||
private const string DefaultLogger = "WireMockConsoleLogger";
|
private const string DefaultLogger = "WireMockConsoleLogger";
|
||||||
|
|
||||||
/// <summary>
|
/// <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.
|
/// If not defined, .NET Aspire automatically assigns a random port.
|
||||||
/// </summary>
|
/// </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>
|
/// <summary>
|
||||||
/// The admin username.
|
/// The admin username.
|
||||||
@@ -67,6 +74,42 @@ public class WireMockServerArguments
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public Func<AdminApiMappingBuilder, CancellationToken, Task>? ApiMappingBuilder { get; set; }
|
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>
|
/// <summary>
|
||||||
/// Converts the current instance's properties to an array of command-line arguments for starting the WireMock.Net server.
|
/// Converts the current instance's properties to an array of command-line arguments for starting the WireMock.Net server.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -95,6 +138,11 @@ public class WireMockServerArguments
|
|||||||
Add(args, "--WatchStaticMappingsInSubdirectories", "true");
|
Add(args, "--WatchStaticMappingsInSubdirectories", "true");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (AdditionalUrls.Count > 0)
|
||||||
|
{
|
||||||
|
Add(args, "--Urls", $"http://*:{HttpContainerPort} {string.Join(' ', AdditionalUrls)}");
|
||||||
|
}
|
||||||
|
|
||||||
return args
|
return args
|
||||||
.SelectMany(k => new[] { k.Key, k.Value })
|
.SelectMany(k => new[] { k.Key, k.Value })
|
||||||
.ToArray();
|
.ToArray();
|
||||||
|
|||||||
@@ -3,10 +3,12 @@
|
|||||||
using Aspire.Hosting.ApplicationModel;
|
using Aspire.Hosting.ApplicationModel;
|
||||||
using Aspire.Hosting.Lifecycle;
|
using Aspire.Hosting.Lifecycle;
|
||||||
using Aspire.Hosting.WireMock;
|
using Aspire.Hosting.WireMock;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.Diagnostics.HealthChecks;
|
using Microsoft.Extensions.Diagnostics.HealthChecks;
|
||||||
using Stef.Validation;
|
using Stef.Validation;
|
||||||
using WireMock.Client.Builders;
|
using WireMock.Client.Builders;
|
||||||
using WireMock.Net.Aspire;
|
using WireMock.Net.Aspire;
|
||||||
|
using WireMock.Util;
|
||||||
|
|
||||||
// ReSharper disable once CheckNamespace
|
// ReSharper disable once CheckNamespace
|
||||||
namespace Aspire.Hosting;
|
namespace Aspire.Hosting;
|
||||||
@@ -33,9 +35,31 @@ public static class WireMockServerBuilderExtensions
|
|||||||
Guard.NotNullOrWhiteSpace(name);
|
Guard.NotNullOrWhiteSpace(name);
|
||||||
Guard.Condition(port, p => p is null or > 0 and <= ushort.MaxValue);
|
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);
|
Guard.NotNull(arguments);
|
||||||
|
|
||||||
var wireMockContainerResource = new WireMockServerResource(name, 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
|
var resourceBuilder = builder
|
||||||
.AddResource(wireMockContainerResource)
|
.AddResource(wireMockContainerResource)
|
||||||
.WithImage(DefaultLinuxImage)
|
.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
|
.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();
|
.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))
|
if (!string.IsNullOrEmpty(arguments.MappingsPath))
|
||||||
{
|
{
|
||||||
resourceBuilder = resourceBuilder.WithBindMount(arguments.MappingsPath, DefaultLinuxMappingsPath);
|
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;
|
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="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>
|
/// <param name="callback">A callback that allows for setting the <see cref="WireMockServerArguments"/>.</param>
|
||||||
/// <returns>A reference to the <see cref="IResourceBuilder{WireMockServerResource}"/>.</returns>
|
/// <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.NotNull(builder);
|
||||||
Guard.NotNullOrWhiteSpace(name);
|
Guard.NotNullOrWhiteSpace(name);
|
||||||
@@ -154,7 +221,7 @@ public static class WireMockServerBuilderExtensions
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="wiremock">The <see cref="IResourceBuilder{WireMockServerResource}"/>.</param>
|
/// <param name="wiremock">The <see cref="IResourceBuilder{WireMockServerResource}"/>.</param>
|
||||||
/// <param name="configure">Delegate that will be invoked to configure the WireMock.Net resource.</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)
|
public static IResourceBuilder<WireMockServerResource> WithApiMappingBuilder(this IResourceBuilder<WireMockServerResource> wiremock, Func<AdminApiMappingBuilder, Task> configure)
|
||||||
{
|
{
|
||||||
return wiremock.WithApiMappingBuilder((adminApiMappingBuilder, _) => configure.Invoke(adminApiMappingBuilder));
|
return wiremock.WithApiMappingBuilder((adminApiMappingBuilder, _) => configure.Invoke(adminApiMappingBuilder));
|
||||||
@@ -165,13 +232,27 @@ public static class WireMockServerBuilderExtensions
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="wiremock">The <see cref="IResourceBuilder{WireMockServerResource}"/>.</param>
|
/// <param name="wiremock">The <see cref="IResourceBuilder{WireMockServerResource}"/>.</param>
|
||||||
/// <param name="configure">Delegate that will be invoked to configure the WireMock.Net resource.</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)
|
public static IResourceBuilder<WireMockServerResource> WithApiMappingBuilder(this IResourceBuilder<WireMockServerResource> wiremock, Func<AdminApiMappingBuilder, CancellationToken, Task> configure)
|
||||||
{
|
{
|
||||||
Guard.NotNull(wiremock);
|
Guard.NotNull(wiremock);
|
||||||
|
|
||||||
wiremock.ApplicationBuilder.Services.TryAddLifecycleHook<WireMockServerLifecycleHook>();
|
|
||||||
wiremock.Resource.Arguments.ApiMappingBuilder = configure;
|
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;
|
return wiremock;
|
||||||
}
|
}
|
||||||
@@ -183,11 +264,11 @@ public static class WireMockServerBuilderExtensions
|
|||||||
/// dotnet tool install WireMockInspector --global --no-cache --ignore-failed-sources
|
/// dotnet tool install WireMockInspector --global --no-cache --ignore-failed-sources
|
||||||
/// </code>
|
/// </code>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="builder">The <see cref="IResourceBuilder{WireMockNetResource}"/>.</param>
|
/// <param name="wiremock">The <see cref="IResourceBuilder{WireMockNetResource}"/>.</param>
|
||||||
/// <returns></returns>
|
/// <returns>A reference to the <see cref="IResourceBuilder{WireMockServerResource}"/>.</returns>
|
||||||
public static IResourceBuilder<WireMockServerResource> WithWireMockInspectorCommand(this IResourceBuilder<WireMockServerResource> builder)
|
public static IResourceBuilder<WireMockServerResource> WithWireMockInspectorCommand(this IResourceBuilder<WireMockServerResource> wiremock)
|
||||||
{
|
{
|
||||||
Guard.NotNull(builder);
|
Guard.NotNull(wiremock);
|
||||||
|
|
||||||
CommandOptions commandOptions = new()
|
CommandOptions commandOptions = new()
|
||||||
{
|
{
|
||||||
@@ -197,13 +278,13 @@ public static class WireMockServerBuilderExtensions
|
|||||||
IconVariant = IconVariant.Filled
|
IconVariant = IconVariant.Filled
|
||||||
};
|
};
|
||||||
|
|
||||||
builder.WithCommand(
|
wiremock.WithCommand(
|
||||||
name: "wiremock-inspector",
|
name: "wiremock-inspector",
|
||||||
displayName: "WireMock Inspector",
|
displayName: "WireMock Inspector",
|
||||||
executeCommand: _ => OnRunOpenInspectorCommandAsync(builder),
|
executeCommand: _ => OnRunOpenInspectorCommandAsync(wiremock),
|
||||||
commandOptions: commandOptions);
|
commandOptions: commandOptions);
|
||||||
|
|
||||||
return builder;
|
return wiremock;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Task<ExecuteCommandResult> OnRunOpenInspectorCommandAsync(IResourceBuilder<WireMockServerResource> builder)
|
private static Task<ExecuteCommandResult> OnRunOpenInspectorCommandAsync(IResourceBuilder<WireMockServerResource> builder)
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
// Copyright © WireMock.Net
|
// Copyright © WireMock.Net
|
||||||
|
|
||||||
|
using System.Diagnostics;
|
||||||
using Aspire.Hosting.ApplicationModel;
|
using Aspire.Hosting.ApplicationModel;
|
||||||
using Aspire.Hosting.Lifecycle;
|
using Aspire.Hosting.Lifecycle;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
@@ -10,32 +11,49 @@ internal class WireMockServerLifecycleHook(ILoggerFactory loggerFactory) : IDist
|
|||||||
{
|
{
|
||||||
private readonly CancellationTokenSource _shutdownCts = new();
|
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
|
_mappingTask = Task.Run(async () =>
|
||||||
.OfType<WireMockServerResource>()
|
|
||||||
.ToArray();
|
|
||||||
|
|
||||||
foreach (var wireMockServerResource in wireMockServerResources)
|
|
||||||
{
|
{
|
||||||
wireMockServerResource.SetLogger(loggerFactory.CreateLogger<WireMockServerResource>());
|
var wireMockServerResources = appModel.Resources
|
||||||
|
.OfType<WireMockServerResource>()
|
||||||
|
.ToArray();
|
||||||
|
|
||||||
var endpoint = wireMockServerResource.GetEndpoint();
|
foreach (var wireMockServerResource in wireMockServerResources)
|
||||||
if (endpoint.IsAllocated)
|
|
||||||
{
|
{
|
||||||
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()
|
public async ValueTask DisposeAsync()
|
||||||
{
|
{
|
||||||
await _shutdownCts.CancelAsync();
|
await _shutdownCts.CancelAsync();
|
||||||
|
|
||||||
|
_linkedCts?.Dispose();
|
||||||
|
_shutdownCts.Dispose();
|
||||||
|
|
||||||
|
if (_mappingTask is not null)
|
||||||
|
{
|
||||||
|
await _mappingTask;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -5,6 +5,7 @@ using RestEase;
|
|||||||
using Stef.Validation;
|
using Stef.Validation;
|
||||||
using WireMock.Client;
|
using WireMock.Client;
|
||||||
using WireMock.Client.Extensions;
|
using WireMock.Client.Extensions;
|
||||||
|
using WireMock.Net.Aspire;
|
||||||
using WireMock.Util;
|
using WireMock.Util;
|
||||||
|
|
||||||
// ReSharper disable once CheckNamespace
|
// ReSharper disable once CheckNamespace
|
||||||
@@ -19,6 +20,7 @@ public class WireMockServerResource : ContainerResource, IResourceWithServiceDis
|
|||||||
|
|
||||||
internal WireMockServerArguments Arguments { get; }
|
internal WireMockServerArguments Arguments { get; }
|
||||||
internal Lazy<IWireMockAdminApi> AdminApi => new(CreateWireMockAdminApi);
|
internal Lazy<IWireMockAdminApi> AdminApi => new(CreateWireMockAdminApi);
|
||||||
|
internal WireMockMappingState ApiMappingState { get; set; } = WireMockMappingState.NoMappings;
|
||||||
|
|
||||||
private ILogger? _logger;
|
private ILogger? _logger;
|
||||||
private EnhancedFileSystemWatcher? _enhancedFileSystemWatcher;
|
private EnhancedFileSystemWatcher? _enhancedFileSystemWatcher;
|
||||||
@@ -64,6 +66,36 @@ public class WireMockServerResource : ContainerResource, IResourceWithServiceDis
|
|||||||
|
|
||||||
var mappingBuilder = AdminApi.Value.GetMappingBuilder();
|
var mappingBuilder = AdminApi.Value.GetMappingBuilder();
|
||||||
await Arguments.ApiMappingBuilder.Invoke(mappingBuilder, cancellationToken);
|
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)
|
internal void StartWatchingStaticMappings(CancellationToken cancellationToken)
|
||||||
@@ -109,10 +141,17 @@ public class WireMockServerResource : ContainerResource, IResourceWithServiceDis
|
|||||||
|
|
||||||
private async void FileCreatedChangedOrDeleted(object sender, FileSystemEventArgs args)
|
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
|
try
|
||||||
{
|
{
|
||||||
await AdminApi.Value.ReloadStaticMappingsAsync();
|
var status = await AdminApi.Value.ReloadStaticMappingsAsync(cancellationToken);
|
||||||
|
_logger?.LogInformation("ReloadStaticMappings called with status: {Status}.", status);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<Description>WireMock.Net.Routing extends WireMock.Net with modern, minimal-API-style routing for .NET</Description>
|
<Description>WireMock.Net.Routing extends WireMock.Net with modern, minimal-API-style routing for .NET</Description>
|
||||||
<Authors>Gennadii Saltyshchak</Authors>
|
<Authors>Gennadii Saltyshchak</Authors>
|
||||||
@@ -25,7 +25,7 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="JsonConverter.Newtonsoft.Json" Version="0.7.0" />
|
<PackageReference Include="JsonConverter.Abstractions" Version="0.8.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using Newtonsoft.Json;
|
|
||||||
using Stef.Validation;
|
using Stef.Validation;
|
||||||
using WireMock.Admin.Mappings;
|
using WireMock.Admin.Mappings;
|
||||||
using WireMock.Matchers.Request;
|
using WireMock.Matchers.Request;
|
||||||
@@ -164,8 +163,8 @@ public class MappingBuilder : IMappingBuilder
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string ToJson(object value)
|
private string ToJson(object value)
|
||||||
{
|
{
|
||||||
return JsonConvert.SerializeObject(value, JsonSerializationConstants.JsonSerializerSettingsDefault);
|
return _settings.DefaultJsonSerializer.Serialize(value, JsonSerializationConstants.JsonConverterOptionsDefault);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
// Copyright © WireMock.Net
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
using Stef.Validation;
|
||||||
|
|
||||||
|
namespace WireMock.Matchers.Request;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The request body matcher.
|
||||||
|
/// </summary>
|
||||||
|
public class RequestMessageBodyMatcher<T> : IRequestMatcher
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The body data function for type T
|
||||||
|
/// </summary>
|
||||||
|
public Func<T?, bool>? Func { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The <see cref="MatchOperator"/>
|
||||||
|
/// </summary>
|
||||||
|
public MatchOperator MatchOperator { get; } = MatchOperator.Or;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="RequestMessageBodyMatcher"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="func">The function.</param>
|
||||||
|
public RequestMessageBodyMatcher(Func<T?, bool> func)
|
||||||
|
{
|
||||||
|
Func = Guard.NotNull(func);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public double GetMatchingScore(IRequestMessage requestMessage, IRequestMatchResult requestMatchResult)
|
||||||
|
{
|
||||||
|
var (score, exception) = CalculateMatchScore(requestMessage).Expand();
|
||||||
|
return requestMatchResult.AddScore(GetType(), score, exception);
|
||||||
|
}
|
||||||
|
|
||||||
|
private MatchResult CalculateMatchScore(IRequestMessage requestMessage)
|
||||||
|
{
|
||||||
|
if (Func != null)
|
||||||
|
{
|
||||||
|
if (requestMessage.BodyData?.BodyAsJson is JObject jsonObject)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var bodyAsT = jsonObject.ToObject<T>();
|
||||||
|
return MatchScores.ToScore(Func(bodyAsT));
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return new MatchResult(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return default;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,16 +9,10 @@ using WireMock.Services;
|
|||||||
|
|
||||||
namespace WireMock.Owin;
|
namespace WireMock.Owin;
|
||||||
|
|
||||||
internal class MappingMatcher : IMappingMatcher
|
internal class MappingMatcher(IWireMockMiddlewareOptions options, IRandomizerDoubleBetween0And1 randomizerDoubleBetween0And1) : IMappingMatcher
|
||||||
{
|
{
|
||||||
private readonly IWireMockMiddlewareOptions _options;
|
private readonly IWireMockMiddlewareOptions _options = Guard.NotNull(options);
|
||||||
private readonly IRandomizerDoubleBetween0And1 _randomizerDoubleBetween0And1;
|
private readonly IRandomizerDoubleBetween0And1 _randomizerDoubleBetween0And1 = Guard.NotNull(randomizerDoubleBetween0And1);
|
||||||
|
|
||||||
public MappingMatcher(IWireMockMiddlewareOptions options, IRandomizerDoubleBetween0And1 randomizerDoubleBetween0And1)
|
|
||||||
{
|
|
||||||
_options = Guard.NotNull(options);
|
|
||||||
_randomizerDoubleBetween0And1 = Guard.NotNull(randomizerDoubleBetween0And1);
|
|
||||||
}
|
|
||||||
|
|
||||||
public (MappingMatcherResult? Match, MappingMatcherResult? Partial) FindBestMatch(RequestMessage request)
|
public (MappingMatcherResult? Match, MappingMatcherResult? Partial) FindBestMatch(RequestMessage request)
|
||||||
{
|
{
|
||||||
@@ -28,7 +22,7 @@ internal class MappingMatcher : IMappingMatcher
|
|||||||
|
|
||||||
var mappings = _options.Mappings.Values
|
var mappings = _options.Mappings.Values
|
||||||
.Where(m => m.TimeSettings.IsValid())
|
.Where(m => m.TimeSettings.IsValid())
|
||||||
.Where(m => m.Probability is null || m.Probability <= _randomizerDoubleBetween0And1.Generate())
|
.Where(m => m.Probability is null || _randomizerDoubleBetween0And1.Generate() <= m.Probability)
|
||||||
.ToArray();
|
.ToArray();
|
||||||
|
|
||||||
foreach (var mapping in mappings)
|
foreach (var mapping in mappings)
|
||||||
@@ -41,10 +35,10 @@ internal class MappingMatcher : IMappingMatcher
|
|||||||
|
|
||||||
var exceptions = mappingMatcherResult.RequestMatchResult.MatchDetails
|
var exceptions = mappingMatcherResult.RequestMatchResult.MatchDetails
|
||||||
.Where(md => md.Exception != null)
|
.Where(md => md.Exception != null)
|
||||||
.Select(md => md.Exception)
|
.Select(md => md.Exception!)
|
||||||
.ToArray();
|
.ToArray();
|
||||||
|
|
||||||
if (!exceptions.Any())
|
if (exceptions.Length == 0)
|
||||||
{
|
{
|
||||||
possibleMappings.Add(mappingMatcherResult);
|
possibleMappings.Add(mappingMatcherResult);
|
||||||
}
|
}
|
||||||
@@ -52,7 +46,7 @@ internal class MappingMatcher : IMappingMatcher
|
|||||||
{
|
{
|
||||||
foreach (var ex in exceptions)
|
foreach (var ex in exceptions)
|
||||||
{
|
{
|
||||||
LogException(mapping, ex!);
|
LogException(mapping, ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -62,14 +56,16 @@ internal class MappingMatcher : IMappingMatcher
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var partialMappings = possibleMappings
|
var partialMatches = possibleMappings
|
||||||
.Where(pm => (pm.Mapping.IsAdminInterface && pm.RequestMatchResult.IsPerfectMatch) || !pm.Mapping.IsAdminInterface)
|
.Where(pm => (pm.Mapping.IsAdminInterface && pm.RequestMatchResult.IsPerfectMatch) || !pm.Mapping.IsAdminInterface)
|
||||||
.OrderBy(m => m.RequestMatchResult)
|
.OrderBy(m => m.RequestMatchResult)
|
||||||
.ThenBy(m => m.RequestMatchResult.TotalNumber)
|
.ThenBy(m => m.RequestMatchResult.TotalNumber)
|
||||||
.ThenBy(m => m.Mapping.Priority)
|
.ThenBy(m => m.Mapping.Priority)
|
||||||
|
.ThenByDescending(m => m.Mapping.Probability)
|
||||||
.ThenByDescending(m => m.Mapping.UpdatedAt)
|
.ThenByDescending(m => m.Mapping.UpdatedAt)
|
||||||
.ToList();
|
.Where(pm => pm.RequestMatchResult.AverageTotalScore > 0.0)
|
||||||
var partialMatch = partialMappings.FirstOrDefault(pm => pm.RequestMatchResult.AverageTotalScore > 0.0);
|
.ToArray();
|
||||||
|
var partialMatch = partialMatches.FirstOrDefault();
|
||||||
|
|
||||||
if (_options.AllowPartialMapping == true)
|
if (_options.AllowPartialMapping == true)
|
||||||
{
|
{
|
||||||
@@ -78,7 +74,11 @@ internal class MappingMatcher : IMappingMatcher
|
|||||||
|
|
||||||
var match = possibleMappings
|
var match = possibleMappings
|
||||||
.Where(m => m.RequestMatchResult.IsPerfectMatch)
|
.Where(m => m.RequestMatchResult.IsPerfectMatch)
|
||||||
.OrderBy(m => m.Mapping.Priority).ThenBy(m => m.RequestMatchResult).ThenByDescending(m => m.Mapping.UpdatedAt)
|
.OrderBy(m => m.Mapping.Priority)
|
||||||
|
.ThenBy(m => m.RequestMatchResult)
|
||||||
|
.ThenBy(m => m.RequestMatchResult.TotalNumber)
|
||||||
|
.ThenByDescending(m => m.Mapping.Probability)
|
||||||
|
.ThenByDescending(m => m.Mapping.UpdatedAt)
|
||||||
.FirstOrDefault();
|
.FirstOrDefault();
|
||||||
|
|
||||||
return (match, partialMatch);
|
return (match, partialMatch);
|
||||||
|
|||||||
@@ -34,13 +34,6 @@ public partial class Request
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public IRequestBuilder WithBodyAsJson(object body, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch)
|
|
||||||
{
|
|
||||||
var matcher = body as IMatcher ?? new JsonMatcher(matchBehaviour, body);
|
|
||||||
return WithBody([matcher]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public IRequestBuilder WithBody(IMatcher matcher)
|
public IRequestBuilder WithBody(IMatcher matcher)
|
||||||
{
|
{
|
||||||
@@ -98,4 +91,20 @@ public partial class Request
|
|||||||
_requestMatchers.Add(new RequestMessageBodyMatcher(Guard.NotNull(func)));
|
_requestMatchers.Add(new RequestMessageBodyMatcher(Guard.NotNull(func)));
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public IRequestBuilder WithBodyAsJson(object body, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch)
|
||||||
|
{
|
||||||
|
var matcher = body as IMatcher ?? new JsonMatcher(matchBehaviour, body);
|
||||||
|
return WithBody([matcher]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public IRequestBuilder WithBodyAsType<T>(Func<T?, bool> func)
|
||||||
|
{
|
||||||
|
Guard.NotNull(func);
|
||||||
|
|
||||||
|
_requestMatchers.Add(new RequestMessageBodyMatcher<T>(func));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -4,6 +4,7 @@ using System;
|
|||||||
using Stef.Validation;
|
using Stef.Validation;
|
||||||
using WireMock.Matchers;
|
using WireMock.Matchers;
|
||||||
using WireMock.Matchers.Request;
|
using WireMock.Matchers.Request;
|
||||||
|
using WireMock.Validators;
|
||||||
|
|
||||||
namespace WireMock.RequestBuilders;
|
namespace WireMock.RequestBuilders;
|
||||||
|
|
||||||
@@ -34,6 +35,10 @@ public partial class Request
|
|||||||
public IRequestBuilder WithPath(MatchOperator matchOperator, params string[] paths)
|
public IRequestBuilder WithPath(MatchOperator matchOperator, params string[] paths)
|
||||||
{
|
{
|
||||||
Guard.NotNullOrEmpty(paths);
|
Guard.NotNullOrEmpty(paths);
|
||||||
|
foreach (var path in paths)
|
||||||
|
{
|
||||||
|
PathValidator.ValidateAndThrow(path, nameof(paths));
|
||||||
|
}
|
||||||
|
|
||||||
_requestMatchers.Add(new RequestMessagePathMatcher(MatchBehaviour.AcceptOnMatch, matchOperator, paths));
|
_requestMatchers.Add(new RequestMessagePathMatcher(MatchBehaviour.AcceptOnMatch, matchOperator, paths));
|
||||||
return this;
|
return this;
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ public partial class Request : RequestMessageCompositeMatcher, IRequestBuilder
|
|||||||
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
|
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
|
||||||
public static IRequestBuilder Create()
|
public static IRequestBuilder Create()
|
||||||
{
|
{
|
||||||
return new Request(new List<IRequestMatcher>());
|
return new Request([]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -226,14 +226,14 @@ internal class MappingConverter(MatcherMapper mapper)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (response.Delay is { })
|
if (response is { MinimumDelayMilliseconds: > 0, MaximumDelayMilliseconds: > 0 })
|
||||||
{
|
|
||||||
sb.AppendLine($" .WithDelay({response.Delay.Value.TotalMilliseconds})");
|
|
||||||
}
|
|
||||||
else if (response is { MinimumDelayMilliseconds: > 0, MaximumDelayMilliseconds: > 0 })
|
|
||||||
{
|
{
|
||||||
sb.AppendLine($" .WithRandomDelay({response.MinimumDelayMilliseconds}, {response.MaximumDelayMilliseconds})");
|
sb.AppendLine($" .WithRandomDelay({response.MinimumDelayMilliseconds}, {response.MaximumDelayMilliseconds})");
|
||||||
}
|
}
|
||||||
|
else if (response.Delay is { })
|
||||||
|
{
|
||||||
|
sb.AppendLine($" .WithDelay({response.Delay.Value.TotalMilliseconds})");
|
||||||
|
}
|
||||||
|
|
||||||
if (response.UseTransformer)
|
if (response.UseTransformer)
|
||||||
{
|
{
|
||||||
|
|||||||
49
src/WireMock.Net.Minimal/Serialization/MappingSerializer.cs
Normal file
49
src/WireMock.Net.Minimal/Serialization/MappingSerializer.cs
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
// Copyright © WireMock.Net
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using JsonConverter.Abstractions;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
#if NETSTANDARD2_0_OR_GREATER || NETCOREAPP3_1_OR_GREATER || NET6_0_OR_GREATER || NET461
|
||||||
|
using System.Text.Json;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace WireMock.Serialization;
|
||||||
|
|
||||||
|
internal class MappingSerializer(IJsonConverter jsonConverter)
|
||||||
|
{
|
||||||
|
internal T[] DeserializeJsonToArray<T>(string value)
|
||||||
|
{
|
||||||
|
return DeserializeObjectToArray<T>(jsonConverter.Deserialize<object>(value)!);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static T[] DeserializeObjectToArray<T>(object value)
|
||||||
|
{
|
||||||
|
if (value is JArray jArray)
|
||||||
|
{
|
||||||
|
return jArray.ToObject<T[]>()!;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value is JObject jObject)
|
||||||
|
{
|
||||||
|
var singleResult = jObject.ToObject<T>();
|
||||||
|
return [singleResult!];
|
||||||
|
}
|
||||||
|
|
||||||
|
#if NETSTANDARD2_0_OR_GREATER || NETCOREAPP3_1_OR_GREATER || NET6_0_OR_GREATER || NET461
|
||||||
|
if (value is JsonElement jElement)
|
||||||
|
{
|
||||||
|
if (jElement.ValueKind == JsonValueKind.Array)
|
||||||
|
{
|
||||||
|
return jElement.Deserialize<T[]>()!;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (jElement.ValueKind == JsonValueKind.Object)
|
||||||
|
{
|
||||||
|
var singleResult = jElement.Deserialize<T>();
|
||||||
|
return [singleResult!];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
throw new InvalidOperationException("Cannot deserialize the provided value to an array or object.");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,7 +2,8 @@
|
|||||||
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Newtonsoft.Json;
|
using JsonConverter.Abstractions;
|
||||||
|
using JsonConverter.Newtonsoft.Json;
|
||||||
using Stef.Validation;
|
using Stef.Validation;
|
||||||
using WireMock.Settings;
|
using WireMock.Settings;
|
||||||
|
|
||||||
@@ -12,12 +13,15 @@ internal class MappingToFileSaver
|
|||||||
{
|
{
|
||||||
private readonly WireMockServerSettings _settings;
|
private readonly WireMockServerSettings _settings;
|
||||||
private readonly MappingConverter _mappingConverter;
|
private readonly MappingConverter _mappingConverter;
|
||||||
|
private readonly IJsonConverter _jsonConverter;
|
||||||
private readonly MappingFileNameSanitizer _fileNameSanitizer;
|
private readonly MappingFileNameSanitizer _fileNameSanitizer;
|
||||||
|
|
||||||
public MappingToFileSaver(WireMockServerSettings settings, MappingConverter mappingConverter)
|
public MappingToFileSaver(WireMockServerSettings settings, MappingConverter mappingConverter)
|
||||||
{
|
{
|
||||||
_settings = Guard.NotNull(settings);
|
_settings = Guard.NotNull(settings);
|
||||||
_mappingConverter = Guard.NotNull(mappingConverter);
|
_mappingConverter = Guard.NotNull(mappingConverter);
|
||||||
|
|
||||||
|
_jsonConverter = settings.DefaultJsonSerializer ?? new NewtonsoftJsonConverter();
|
||||||
_fileNameSanitizer = new MappingFileNameSanitizer(settings);
|
_fileNameSanitizer = new MappingFileNameSanitizer(settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -56,6 +60,8 @@ internal class MappingToFileSaver
|
|||||||
{
|
{
|
||||||
_settings.Logger.Info("Saving Mapping file {0}", path);
|
_settings.Logger.Info("Saving Mapping file {0}", path);
|
||||||
|
|
||||||
_settings.FileSystemHandler.WriteMappingFile(path, JsonConvert.SerializeObject(value, JsonSerializationConstants.JsonSerializerSettingsDefault));
|
var json = _jsonConverter.Serialize(value, JsonSerializationConstants.JsonConverterOptionsDefault);
|
||||||
|
|
||||||
|
_settings.FileSystemHandler.WriteMappingFile(path, json);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -7,7 +7,7 @@ using System.Linq;
|
|||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
using Newtonsoft.Json;
|
using JsonConverter.Abstractions;
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
using Stef.Validation;
|
using Stef.Validation;
|
||||||
using WireMock.Admin.Mappings;
|
using WireMock.Admin.Mappings;
|
||||||
@@ -236,7 +236,7 @@ public partial class WireMockServer
|
|||||||
|
|
||||||
if (FileHelper.TryReadMappingFileWithRetryAndDelay(_settings.FileSystemHandler, path, out var value))
|
if (FileHelper.TryReadMappingFileWithRetryAndDelay(_settings.FileSystemHandler, path, out var value))
|
||||||
{
|
{
|
||||||
var mappingModels = DeserializeJsonToArray<MappingModel>(value);
|
var mappingModels = _mappingSerializer.DeserializeJsonToArray<MappingModel>(value);
|
||||||
if (mappingModels.Length == 1 && Guid.TryParse(filenameWithoutExtension, out var guidFromFilename))
|
if (mappingModels.Length == 1 && Guid.TryParse(filenameWithoutExtension, out var guidFromFilename))
|
||||||
{
|
{
|
||||||
ConvertMappingAndRegisterAsRespondProvider(mappingModels[0], guidFromFilename, path);
|
ConvertMappingAndRegisterAsRespondProvider(mappingModels[0], guidFromFilename, path);
|
||||||
@@ -826,25 +826,31 @@ public partial class WireMockServer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Encoding? ToEncoding(EncodingModel? encodingModel)
|
private ResponseMessage ToJson<T>(T result, bool keepNullValues = false, object? statusCode = null)
|
||||||
{
|
{
|
||||||
return encodingModel != null ? Encoding.GetEncoding(encodingModel.CodePage) : null;
|
var jsonOptions = new JsonConverterOptions
|
||||||
}
|
{
|
||||||
|
WriteIndented = true,
|
||||||
|
IgnoreNullValues = !keepNullValues
|
||||||
|
};
|
||||||
|
|
||||||
private static ResponseMessage ToJson<T>(T result, bool keepNullValues = false, object? statusCode = null)
|
|
||||||
{
|
|
||||||
return new ResponseMessage
|
return new ResponseMessage
|
||||||
{
|
{
|
||||||
BodyData = new BodyData
|
BodyData = new BodyData
|
||||||
{
|
{
|
||||||
DetectedBodyType = BodyType.String,
|
DetectedBodyType = BodyType.String,
|
||||||
BodyAsString = JsonConvert.SerializeObject(result, keepNullValues ? JsonSerializationConstants.JsonSerializerSettingsIncludeNullValues : JsonSerializationConstants.JsonSerializerSettingsDefault)
|
BodyAsString = _settings.DefaultJsonSerializer.Serialize(result!, jsonOptions)
|
||||||
},
|
},
|
||||||
StatusCode = statusCode ?? (int)HttpStatusCode.OK,
|
StatusCode = statusCode ?? (int)HttpStatusCode.OK,
|
||||||
Headers = new Dictionary<string, WireMockList<string>> { { HttpKnownHeaderNames.ContentType, new WireMockList<string>(WireMockConstants.ContentTypeJson) } }
|
Headers = new Dictionary<string, WireMockList<string>> { { HttpKnownHeaderNames.ContentType, new WireMockList<string>(WireMockConstants.ContentTypeJson) } }
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static Encoding? ToEncoding(EncodingModel? encodingModel)
|
||||||
|
{
|
||||||
|
return encodingModel != null ? Encoding.GetEncoding(encodingModel.CodePage) : null;
|
||||||
|
}
|
||||||
|
|
||||||
private static ResponseMessage ToResponseMessage(string text)
|
private static ResponseMessage ToResponseMessage(string text)
|
||||||
{
|
{
|
||||||
return new ResponseMessage
|
return new ResponseMessage
|
||||||
@@ -859,6 +865,18 @@ public partial class WireMockServer
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static T[] DeserializeRequestMessageToArray<T>(IRequestMessage requestMessage)
|
||||||
|
{
|
||||||
|
if (requestMessage.BodyData?.DetectedBodyType == BodyType.Json && requestMessage.BodyData.BodyAsJson != null)
|
||||||
|
{
|
||||||
|
var bodyAsJson = requestMessage.BodyData.BodyAsJson!;
|
||||||
|
|
||||||
|
return MappingSerializer.DeserializeObjectToArray<T>(bodyAsJson);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new NotSupportedException();
|
||||||
|
}
|
||||||
|
|
||||||
private static T DeserializeObject<T>(IRequestMessage requestMessage) where T : new()
|
private static T DeserializeObject<T>(IRequestMessage requestMessage) where T : new()
|
||||||
{
|
{
|
||||||
switch (requestMessage.BodyData?.DetectedBodyType)
|
switch (requestMessage.BodyData?.DetectedBodyType)
|
||||||
@@ -874,32 +892,4 @@ public partial class WireMockServer
|
|||||||
throw new NotSupportedException();
|
throw new NotSupportedException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static T[] DeserializeRequestMessageToArray<T>(IRequestMessage requestMessage)
|
|
||||||
{
|
|
||||||
if (requestMessage.BodyData?.DetectedBodyType == BodyType.Json && requestMessage.BodyData.BodyAsJson != null)
|
|
||||||
{
|
|
||||||
var bodyAsJson = requestMessage.BodyData.BodyAsJson;
|
|
||||||
|
|
||||||
return DeserializeObjectToArray<T>(bodyAsJson);
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new NotSupportedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static T[] DeserializeJsonToArray<T>(string value)
|
|
||||||
{
|
|
||||||
return DeserializeObjectToArray<T>(JsonUtils.DeserializeObject(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static T[] DeserializeObjectToArray<T>(object value)
|
|
||||||
{
|
|
||||||
if (value is JArray jArray)
|
|
||||||
{
|
|
||||||
return jArray.ToObject<T[]>()!;
|
|
||||||
}
|
|
||||||
|
|
||||||
var singleResult = ((JObject)value).ToObject<T>();
|
|
||||||
return new[] { singleResult! };
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -31,7 +31,7 @@ public partial class WireMockServer
|
|||||||
|
|
||||||
if (FileHelper.TryReadMappingFileWithRetryAndDelay(_settings.FileSystemHandler, path, out var value))
|
if (FileHelper.TryReadMappingFileWithRetryAndDelay(_settings.FileSystemHandler, path, out var value))
|
||||||
{
|
{
|
||||||
var mappings = DeserializeJsonToArray<OrgMapping>(value);
|
var mappings = _mappingSerializer.DeserializeJsonToArray<OrgMapping>(value);
|
||||||
foreach (var mapping in mappings)
|
foreach (var mapping in mappings)
|
||||||
{
|
{
|
||||||
if (mappings.Length == 1 && Guid.TryParse(filenameWithoutExtension, out var guidFromFilename))
|
if (mappings.Length == 1 && Guid.TryParse(filenameWithoutExtension, out var guidFromFilename))
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ using System.Net.Http;
|
|||||||
using System.Threading;
|
using System.Threading;
|
||||||
using AnyOfTypes;
|
using AnyOfTypes;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
|
using JsonConverter.Newtonsoft.Json;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using Stef.Validation;
|
using Stef.Validation;
|
||||||
using WireMock.Admin.Mappings;
|
using WireMock.Admin.Mappings;
|
||||||
@@ -47,6 +48,7 @@ public partial class WireMockServer : IWireMockServer
|
|||||||
private readonly MappingBuilder _mappingBuilder;
|
private readonly MappingBuilder _mappingBuilder;
|
||||||
private readonly IGuidUtils _guidUtils = new GuidUtils();
|
private readonly IGuidUtils _guidUtils = new GuidUtils();
|
||||||
private readonly IDateTimeUtils _dateTimeUtils = new DateTimeUtils();
|
private readonly IDateTimeUtils _dateTimeUtils = new DateTimeUtils();
|
||||||
|
private readonly MappingSerializer _mappingSerializer;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
[PublicAPI]
|
[PublicAPI]
|
||||||
@@ -357,6 +359,8 @@ public partial class WireMockServer : IWireMockServer
|
|||||||
{
|
{
|
||||||
_settings = Guard.NotNull(settings);
|
_settings = Guard.NotNull(settings);
|
||||||
|
|
||||||
|
_mappingSerializer = new MappingSerializer(settings.DefaultJsonSerializer ?? new NewtonsoftJsonConverter());
|
||||||
|
|
||||||
// Set default values if not provided
|
// Set default values if not provided
|
||||||
_settings.Logger = settings.Logger ?? new WireMockNullLogger();
|
_settings.Logger = settings.Logger ?? new WireMockNullLogger();
|
||||||
_settings.FileSystemHandler = settings.FileSystemHandler ?? new LocalFileSystemHandler();
|
_settings.FileSystemHandler = settings.FileSystemHandler ?? new LocalFileSystemHandler();
|
||||||
@@ -639,7 +643,7 @@ public partial class WireMockServer : IWireMockServer
|
|||||||
[PublicAPI]
|
[PublicAPI]
|
||||||
public IWireMockServer WithMapping(string mappings)
|
public IWireMockServer WithMapping(string mappings)
|
||||||
{
|
{
|
||||||
var mappingModels = DeserializeJsonToArray<MappingModel>(mappings);
|
var mappingModels = _mappingSerializer.DeserializeJsonToArray<MappingModel>(mappings);
|
||||||
foreach (var mappingModel in mappingModels)
|
foreach (var mappingModel in mappingModels)
|
||||||
{
|
{
|
||||||
ConvertMappingAndRegisterAsRespondProvider(mappingModel, mappingModel.Guid ?? Guid.NewGuid());
|
ConvertMappingAndRegisterAsRespondProvider(mappingModel, mappingModel.Guid ?? Guid.NewGuid());
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ internal class SimpleSettingsParser
|
|||||||
// Now also parse environment
|
// Now also parse environment
|
||||||
if (environment != null)
|
if (environment != null)
|
||||||
{
|
{
|
||||||
foreach (string key in environment.Keys)
|
foreach (var key in environment.Keys.OfType<string>())
|
||||||
{
|
{
|
||||||
if (key.StartsWith(Prefix, StringComparison.OrdinalIgnoreCase) && environment.TryGetStringValue(key, out var value))
|
if (key.StartsWith(Prefix, StringComparison.OrdinalIgnoreCase) && environment.TryGetStringValue(key, out var value))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ public static class WireMockServerSettingsParser
|
|||||||
|
|
||||||
if (parser.GetBoolSwitchValue("help"))
|
if (parser.GetBoolSwitchValue("help"))
|
||||||
{
|
{
|
||||||
(logger ?? new WireMockConsoleLogger()).Info("See https://github.com/wiremock/WireMock.Net/wiki/WireMock-commandline-parameters for details on all commandline options.");
|
(logger ?? new WireMockConsoleLogger()).Info("See https://wiremock.org/dotnet/wiremock-commandline-parameters/ for details on all commandline options.");
|
||||||
settings = null;
|
settings = null;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -153,7 +153,7 @@ public static class WireMockServerSettingsParser
|
|||||||
}
|
}
|
||||||
else if (settings.HostingScheme is null)
|
else if (settings.HostingScheme is null)
|
||||||
{
|
{
|
||||||
settings.Urls = parser.GetValues("Urls", ["http://*:9091/"]);
|
settings.Urls = parser.GetValues(nameof(WireMockServerSettings.Urls), defaultValue: ["http://*:9091/"]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -84,22 +84,22 @@ internal static class PortUtils
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Extract the isHttps, isHttp2, protocol, host and port from a URL.
|
/// Extract the isHttps, isHttp2, scheme, host and port from a URL.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static bool TryExtract(string url, out bool isHttps, out bool isHttp2, [NotNullWhen(true)] out string? protocol, [NotNullWhen(true)] out string? host, out int port)
|
public static bool TryExtract(string url, out bool isHttps, out bool isHttp2, [NotNullWhen(true)] out string? scheme, [NotNullWhen(true)] out string? host, out int port)
|
||||||
{
|
{
|
||||||
isHttps = false;
|
isHttps = false;
|
||||||
isHttp2 = false;
|
isHttp2 = false;
|
||||||
protocol = null;
|
scheme = null;
|
||||||
host = null;
|
host = null;
|
||||||
port = 0;
|
port = 0;
|
||||||
|
|
||||||
var match = UrlDetailsRegex.Match(url);
|
var match = UrlDetailsRegex.Match(url);
|
||||||
if (match.Success)
|
if (match.Success)
|
||||||
{
|
{
|
||||||
protocol = match.Groups["proto"].Value;
|
scheme = match.Groups["proto"].Value;
|
||||||
isHttps = protocol.StartsWith("https", StringComparison.OrdinalIgnoreCase) || protocol.StartsWith("grpcs", StringComparison.OrdinalIgnoreCase);
|
isHttps = scheme.StartsWith("https", StringComparison.OrdinalIgnoreCase) || scheme.StartsWith("grpcs", StringComparison.OrdinalIgnoreCase);
|
||||||
isHttp2 = protocol.StartsWith("grpc", StringComparison.OrdinalIgnoreCase);
|
isHttp2 = scheme.StartsWith("grpc", StringComparison.OrdinalIgnoreCase);
|
||||||
host = match.Groups["host"].Value;
|
host = match.Groups["host"].Value;
|
||||||
|
|
||||||
return int.TryParse(match.Groups["port"].Value, out port);
|
return int.TryParse(match.Groups["port"].Value, out port);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<Description>Minimal version from the lightweight Http Mocking Server for .NET</Description>
|
<Description>Minimal version from the lightweight Http Mocking Server for .NET</Description>
|
||||||
<AssemblyTitle>WireMock.Net.Minimal</AssemblyTitle>
|
<AssemblyTitle>WireMock.Net.Minimal</AssemblyTitle>
|
||||||
@@ -57,8 +57,8 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="JsonConverter.Abstractions" Version="0.7.0" />
|
<!--<PackageReference Include="JsonConverter.Abstractions" Version="0.8.0" />-->
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
<!--<PackageReference Include="JsonConverter.Newtonsoft.Json" Version="0.8.0" />-->
|
||||||
<PackageReference Include="NJsonSchema.Extensions" Version="0.1.0" />
|
<PackageReference Include="NJsonSchema.Extensions" Version="0.1.0" />
|
||||||
<PackageReference Include="NSwag.Core" Version="13.16.1" />
|
<PackageReference Include="NSwag.Core" Version="13.16.1" />
|
||||||
<PackageReference Include="SimMetrics.Net" Version="1.0.5" />
|
<PackageReference Include="SimMetrics.Net" Version="1.0.5" />
|
||||||
@@ -118,11 +118,13 @@
|
|||||||
|
|
||||||
<!-- https://github.com/wiremock/WireMock.Net/issues/507 -->
|
<!-- https://github.com/wiremock/WireMock.Net/issues/507 -->
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Server.IIS" Version="2.2.6" />
|
<PackageReference Include="Microsoft.AspNetCore.Server.IIS" Version="2.2.6" />
|
||||||
|
<PackageReference Include="JsonConverter.System.Text.Json" Version="0.8.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup Condition=" '$(TargetFramework)' == 'netcoreapp3.1' or '$(TargetFramework)' == 'net5.0' or '$(TargetFramework)' == 'net6.0' or '$(TargetFramework)' == 'net7.0' or '$(TargetFramework)' == 'net8.0'">
|
<ItemGroup Condition=" '$(TargetFramework)' == 'netcoreapp3.1' or '$(TargetFramework)' == 'net5.0' or '$(TargetFramework)' == 'net6.0' or '$(TargetFramework)' == 'net7.0' or '$(TargetFramework)' == 'net8.0'">
|
||||||
<FrameworkReference Include="Microsoft.AspNetCore.App" />
|
<FrameworkReference Include="Microsoft.AspNetCore.App" />
|
||||||
<PackageReference Include="Scriban.Signed" Version="5.5.0" />
|
<PackageReference Include="Scriban.Signed" Version="5.5.0" />
|
||||||
|
<PackageReference Include="JsonConverter.System.Text.Json" Version="0.8.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup Condition=" '$(TargetFramework)' == 'netcoreapp3.1' ">
|
<ItemGroup Condition=" '$(TargetFramework)' == 'netcoreapp3.1' ">
|
||||||
|
|||||||
71
src/WireMock.Net.NUnit/TestContextWireMockLogger.cs
Normal file
71
src/WireMock.Net.NUnit/TestContextWireMockLogger.cs
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
// Copyright © WireMock.Net
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using JsonConverter.Abstractions;
|
||||||
|
using JsonConverter.Newtonsoft.Json;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using WireMock.Admin.Requests;
|
||||||
|
using WireMock.Logging;
|
||||||
|
|
||||||
|
namespace WireMock.Net.NUnit;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// When using NUnit, this class enables to log the output from WireMock.Net using the <see cref="TestContext"/>.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class TestContextWireMockLogger(IJsonConverter? jsonConverter = null) : IWireMockLogger
|
||||||
|
{
|
||||||
|
private readonly JsonConverterOptions _jsonConverterOptions = new() { WriteIndented = true, IgnoreNullValues = true };
|
||||||
|
private readonly IJsonConverter _jsonConverter = jsonConverter ?? new NewtonsoftJsonConverter();
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void Debug(string formatString, params object[] args)
|
||||||
|
{
|
||||||
|
TestContext.WriteLine(Format("Debug", formatString, args));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void Info(string formatString, params object[] args)
|
||||||
|
{
|
||||||
|
TestContext.WriteLine(Format("Info", formatString, args));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void Warn(string formatString, params object[] args)
|
||||||
|
{
|
||||||
|
TestContext.WriteLine(Format("Warning", formatString, args));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void Error(string formatString, params object[] args)
|
||||||
|
{
|
||||||
|
TestContext.WriteLine(Format("Error", formatString, args));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void Error(string message, Exception exception)
|
||||||
|
{
|
||||||
|
TestContext.WriteLine(Format("Error", $"{message} {{0}}", exception));
|
||||||
|
|
||||||
|
if (exception is AggregateException ae)
|
||||||
|
{
|
||||||
|
ae.Handle(ex =>
|
||||||
|
{
|
||||||
|
TestContext.WriteLine(Format("Error", "Exception {0}", ex));
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void DebugRequestResponse(LogEntryModel logEntryModel, bool isAdminRequest)
|
||||||
|
{
|
||||||
|
var message = _jsonConverter.Serialize(logEntryModel, _jsonConverterOptions);
|
||||||
|
TestContext.WriteLine(Format("DebugRequestResponse", "Admin[{0}] {1}", isAdminRequest, message));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string Format(string level, string formatString, params object[] args)
|
||||||
|
{
|
||||||
|
var message = args.Length > 0 ? string.Format(formatString, args) : formatString;
|
||||||
|
return $"{DateTime.UtcNow} [{level}] : {message}";
|
||||||
|
}
|
||||||
|
}
|
||||||
36
src/WireMock.Net.NUnit/WireMock.Net.NUnit.csproj
Normal file
36
src/WireMock.Net.NUnit/WireMock.Net.NUnit.csproj
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
<PropertyGroup>
|
||||||
|
<Description>Some extensions for NUnit</Description>
|
||||||
|
<AssemblyTitle>WireMock.Net.NUnit</AssemblyTitle>
|
||||||
|
<Authors>Stef Heyenrath</Authors>
|
||||||
|
<TargetFrameworks>net462;net6.0;net8.0</TargetFrameworks>
|
||||||
|
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||||
|
<AssemblyName>WireMock.Net.NUnit</AssemblyName>
|
||||||
|
<RootNamespace>WireMock.Net.NUnit</RootNamespace>
|
||||||
|
<PackageId>WireMock.Net.NUnit</PackageId>
|
||||||
|
<PackageTags>tdd;wiremock;test;unittest;nunit</PackageTags>
|
||||||
|
<ProjectGuid>{2DBBD70D-8051-441F-92BB-3F9B8B4B4983}</ProjectGuid>
|
||||||
|
<PublishRepositoryUrl>true</PublishRepositoryUrl>
|
||||||
|
<AllowedOutputExtensionsInPackageBuildOutputFolder>$(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb</AllowedOutputExtensionsInPackageBuildOutputFolder>
|
||||||
|
<EmbedUntrackedSources>true</EmbedUntrackedSources>
|
||||||
|
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
|
||||||
|
<GenerateBindingRedirectsOutputType>true</GenerateBindingRedirectsOutputType>
|
||||||
|
<SignAssembly>true</SignAssembly>
|
||||||
|
<AssemblyOriginatorKeyFile>../WireMock.Net/WireMock.Net.snk</AssemblyOriginatorKeyFile>
|
||||||
|
<PublicSign Condition=" '$(OS)' != 'Windows_NT' ">true</PublicSign>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
|
||||||
|
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="JsonConverter.Newtonsoft.Json" Version="0.8.0" />
|
||||||
|
<PackageReference Include="NUnit" Version="4.4.0" />
|
||||||
|
<PackageReference Include="Stef.Validation" Version="0.1.1" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\WireMock.Net.Abstractions\WireMock.Net.Abstractions.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
||||||
@@ -5,9 +5,7 @@ using System.Linq;
|
|||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Text.Json.Nodes;
|
using System.Text.Json.Nodes;
|
||||||
using Microsoft.OpenApi.Interfaces;
|
using Microsoft.OpenApi;
|
||||||
using Microsoft.OpenApi.Models;
|
|
||||||
using Microsoft.OpenApi.Models.Interfaces;
|
|
||||||
using WireMock.Net.OpenApiParser.Types;
|
using WireMock.Net.OpenApiParser.Types;
|
||||||
|
|
||||||
namespace WireMock.Net.OpenApiParser.Extensions;
|
namespace WireMock.Net.OpenApiParser.Extensions;
|
||||||
|
|||||||
@@ -6,8 +6,7 @@ using System.Diagnostics.CodeAnalysis;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Text.Json.Nodes;
|
using System.Text.Json.Nodes;
|
||||||
using Microsoft.OpenApi.Models;
|
using Microsoft.OpenApi;
|
||||||
using Microsoft.OpenApi.Models.Interfaces;
|
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using Stef.Validation;
|
using Stef.Validation;
|
||||||
using WireMock.Admin.Mappings;
|
using WireMock.Admin.Mappings;
|
||||||
@@ -19,18 +18,12 @@ using SystemTextJsonSerializer = System.Text.Json.JsonSerializer;
|
|||||||
|
|
||||||
namespace WireMock.Net.OpenApiParser.Mappers;
|
namespace WireMock.Net.OpenApiParser.Mappers;
|
||||||
|
|
||||||
internal class OpenApiPathsMapper
|
internal class OpenApiPathsMapper(WireMockOpenApiParserSettings settings)
|
||||||
{
|
{
|
||||||
private const string HeaderContentType = "Content-Type";
|
private const string HeaderContentType = "Content-Type";
|
||||||
|
|
||||||
private readonly WireMockOpenApiParserSettings _settings;
|
private readonly WireMockOpenApiParserSettings _settings = Guard.NotNull(settings);
|
||||||
private readonly ExampleValueGenerator _exampleValueGenerator;
|
private readonly ExampleValueGenerator _exampleValueGenerator = new(settings);
|
||||||
|
|
||||||
public OpenApiPathsMapper(WireMockOpenApiParserSettings settings)
|
|
||||||
{
|
|
||||||
_settings = Guard.NotNull(settings);
|
|
||||||
_exampleValueGenerator = new ExampleValueGenerator(settings);
|
|
||||||
}
|
|
||||||
|
|
||||||
public IReadOnlyList<MappingModel> ToMappingModels(OpenApiPaths? paths, IList<OpenApiServer> servers)
|
public IReadOnlyList<MappingModel> ToMappingModels(OpenApiPaths? paths, IList<OpenApiServer> servers)
|
||||||
{
|
{
|
||||||
@@ -41,7 +34,7 @@ internal class OpenApiPathsMapper
|
|||||||
.ToArray() ?? [];
|
.ToArray() ?? [];
|
||||||
}
|
}
|
||||||
|
|
||||||
private IReadOnlyList<MappingModel> MapPath(string path, IOpenApiPathItem pathItem, IList<OpenApiServer> servers)
|
private MappingModel[] MapPath(string path, IOpenApiPathItem pathItem, IList<OpenApiServer> servers)
|
||||||
{
|
{
|
||||||
return pathItem.Operations?.Select(o => MapOperationToMappingModel(path, o.Key.ToString().ToUpperInvariant(), o.Value, servers)).ToArray() ?? [];
|
return pathItem.Operations?.Select(o => MapOperationToMappingModel(path, o.Key.ToString().ToUpperInvariant(), o.Value, servers)).ToArray() ?? [];
|
||||||
}
|
}
|
||||||
@@ -50,35 +43,7 @@ internal class OpenApiPathsMapper
|
|||||||
{
|
{
|
||||||
var queryParameters = operation.Parameters?.Where(p => p.In == ParameterLocation.Query) ?? [];
|
var queryParameters = operation.Parameters?.Where(p => p.In == ParameterLocation.Query) ?? [];
|
||||||
var pathParameters = operation.Parameters?.Where(p => p.In == ParameterLocation.Path) ?? [];
|
var pathParameters = operation.Parameters?.Where(p => p.In == ParameterLocation.Path) ?? [];
|
||||||
var headers = operation.Parameters?.Where(p => p.In == ParameterLocation.Header) ?? [];
|
var requestHeaders = operation.Parameters?.Where(p => p.In == ParameterLocation.Header) ?? [];
|
||||||
|
|
||||||
var response = operation.Responses?.FirstOrDefault() ?? new KeyValuePair<string, IOpenApiResponse>();
|
|
||||||
|
|
||||||
TryGetContent(response.Value?.Content, out OpenApiMediaType? responseContent, out var responseContentType);
|
|
||||||
var responseSchema = response.Value?.Content?.FirstOrDefault().Value?.Schema;
|
|
||||||
var responseExample = responseContent?.Example;
|
|
||||||
var responseSchemaExample = responseContent?.Schema?.Example;
|
|
||||||
|
|
||||||
var responseBody = responseExample ?? responseSchemaExample ?? MapSchemaToObject(responseSchema);
|
|
||||||
|
|
||||||
var requestBodyModel = new BodyModel();
|
|
||||||
if (operation.RequestBody != null && operation.RequestBody.Content != null && operation.RequestBody.Required)
|
|
||||||
{
|
|
||||||
var request = operation.RequestBody.Content;
|
|
||||||
TryGetContent(request, out var requestContent, out _);
|
|
||||||
|
|
||||||
var requestBodySchema = operation.RequestBody.Content.First().Value?.Schema;
|
|
||||||
var requestBodyExample = requestContent!.Example;
|
|
||||||
var requestBodySchemaExample = requestContent.Schema?.Example;
|
|
||||||
|
|
||||||
var requestBodyMapped = requestBodyExample ?? requestBodySchemaExample ?? MapSchemaToObject(requestBodySchema);
|
|
||||||
requestBodyModel = MapRequestBody(requestBodyMapped);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!int.TryParse(response.Key, out var httpStatusCode))
|
|
||||||
{
|
|
||||||
httpStatusCode = 200;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new MappingModel
|
return new MappingModel
|
||||||
{
|
{
|
||||||
@@ -88,15 +53,94 @@ internal class OpenApiPathsMapper
|
|||||||
Methods = [httpMethod],
|
Methods = [httpMethod],
|
||||||
Path = PathUtils.Combine(MapBasePath(servers), MapPathWithParameters(path, pathParameters)),
|
Path = PathUtils.Combine(MapBasePath(servers), MapPathWithParameters(path, pathParameters)),
|
||||||
Params = MapQueryParameters(queryParameters),
|
Params = MapQueryParameters(queryParameters),
|
||||||
Headers = MapRequestHeaders(headers),
|
Headers = MapRequestHeaders(requestHeaders),
|
||||||
Body = requestBodyModel
|
Body = GetRequestBodyModel(operation.RequestBody)
|
||||||
},
|
},
|
||||||
Response = new ResponseModel
|
Response = GetResponseModel(operation.Responses?.FirstOrDefault())
|
||||||
{
|
};
|
||||||
StatusCode = httpStatusCode,
|
}
|
||||||
Headers = MapHeaders(responseContentType, response.Value?.Headers),
|
|
||||||
BodyAsJson = responseBody != null ? JsonConvert.DeserializeObject(SystemTextJsonSerializer.Serialize(responseBody)) : null
|
private BodyModel GetRequestBodyModel(IOpenApiRequestBody? openApiRequestBody)
|
||||||
}
|
{
|
||||||
|
if (openApiRequestBody is not { Content: not null, Required: true })
|
||||||
|
{
|
||||||
|
return new BodyModel();
|
||||||
|
}
|
||||||
|
|
||||||
|
var content = openApiRequestBody.Content;
|
||||||
|
|
||||||
|
TryGetContent(content, out var requestContent, out _);
|
||||||
|
|
||||||
|
var requestExample = requestContent?.Example;
|
||||||
|
var requestExamples = requestContent?.Examples;
|
||||||
|
var requestSchemaExample = requestContent?.Schema?.Example;
|
||||||
|
var requestSchemaExamples = requestContent?.Schema?.Examples;
|
||||||
|
|
||||||
|
JsonNode? request;
|
||||||
|
if (requestExample != null)
|
||||||
|
{
|
||||||
|
request = requestExample;
|
||||||
|
}
|
||||||
|
else if (requestSchemaExample != null)
|
||||||
|
{
|
||||||
|
request = requestSchemaExample;
|
||||||
|
}
|
||||||
|
else if (requestExamples != null)
|
||||||
|
{
|
||||||
|
request = requestExamples.FirstOrDefault().Value.Value;
|
||||||
|
}
|
||||||
|
else if (requestSchemaExamples != null)
|
||||||
|
{
|
||||||
|
request = requestSchemaExamples.FirstOrDefault();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var requestSchema = content?.FirstOrDefault().Value.Schema;
|
||||||
|
request = MapSchemaToObject(requestSchema);
|
||||||
|
}
|
||||||
|
|
||||||
|
return MapRequestBody(request) ?? new BodyModel();
|
||||||
|
}
|
||||||
|
|
||||||
|
private ResponseModel GetResponseModel(KeyValuePair<string, IOpenApiResponse>? openApiResponse)
|
||||||
|
{
|
||||||
|
var content = openApiResponse?.Value.Content;
|
||||||
|
|
||||||
|
TryGetContent(content, out var responseContent, out var contentType);
|
||||||
|
|
||||||
|
var responseExample = responseContent?.Example;
|
||||||
|
var responseExamples = responseContent?.Examples;
|
||||||
|
var responseSchemaExample = responseContent?.Schema?.Example;
|
||||||
|
var responseSchemaExamples = responseContent?.Schema?.Examples;
|
||||||
|
|
||||||
|
JsonNode? response;
|
||||||
|
if (responseExample != null)
|
||||||
|
{
|
||||||
|
response = responseExample;
|
||||||
|
}
|
||||||
|
else if (responseSchemaExample != null)
|
||||||
|
{
|
||||||
|
response = responseSchemaExample;
|
||||||
|
}
|
||||||
|
else if (responseExamples != null)
|
||||||
|
{
|
||||||
|
response = responseExamples.FirstOrDefault().Value.Value;
|
||||||
|
}
|
||||||
|
else if (responseSchemaExamples != null)
|
||||||
|
{
|
||||||
|
response = responseSchemaExamples.FirstOrDefault();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var responseSchema = content?.FirstOrDefault().Value?.Schema;
|
||||||
|
response = MapSchemaToObject(responseSchema);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ResponseModel
|
||||||
|
{
|
||||||
|
StatusCode = int.TryParse(openApiResponse?.Key, out var httpStatusCode) ? httpStatusCode : 200,
|
||||||
|
Headers = MapHeaders(contentType, openApiResponse?.Value.Headers),
|
||||||
|
BodyAsJson = response != null ? JsonConvert.DeserializeObject(SystemTextJsonSerializer.Serialize(response)) : null
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
// Copyright © WireMock.Net
|
// Copyright © WireMock.Net
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using Microsoft.OpenApi.Models.Interfaces;
|
using Microsoft.OpenApi;
|
||||||
|
|
||||||
namespace WireMock.Net.OpenApiParser.Settings;
|
namespace WireMock.Net.OpenApiParser.Settings;
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
// Copyright © WireMock.Net
|
// Copyright © WireMock.Net
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using Microsoft.OpenApi.Models.Interfaces;
|
using Microsoft.OpenApi;
|
||||||
using RandomDataGenerator.FieldOptions;
|
using RandomDataGenerator.FieldOptions;
|
||||||
using RandomDataGenerator.Randomizers;
|
using RandomDataGenerator.Randomizers;
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
// Copyright © WireMock.Net
|
// Copyright © WireMock.Net
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using Microsoft.OpenApi.Models;
|
using Microsoft.OpenApi;
|
||||||
using Microsoft.OpenApi.Models.Interfaces;
|
|
||||||
|
|
||||||
namespace WireMock.Net.OpenApiParser.Settings;
|
namespace WireMock.Net.OpenApiParser.Settings;
|
||||||
|
|
||||||
|
|||||||
@@ -3,8 +3,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text.Json.Nodes;
|
using System.Text.Json.Nodes;
|
||||||
using Microsoft.OpenApi.Models;
|
using Microsoft.OpenApi;
|
||||||
using Microsoft.OpenApi.Models.Interfaces;
|
|
||||||
using Stef.Validation;
|
using Stef.Validation;
|
||||||
using WireMock.Net.OpenApiParser.Extensions;
|
using WireMock.Net.OpenApiParser.Extensions;
|
||||||
using WireMock.Net.OpenApiParser.Settings;
|
using WireMock.Net.OpenApiParser.Settings;
|
||||||
|
|||||||
@@ -27,22 +27,22 @@
|
|||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="RamlToOpenApiConverter.SourceOnly" Version="0.8.0" />
|
<PackageReference Include="RamlToOpenApiConverter.SourceOnly" Version="0.11.0" />
|
||||||
<PackageReference Include="YamlDotNet" Version="8.1.0" />
|
<PackageReference Include="YamlDotNet" Version="16.3.0" />
|
||||||
<PackageReference Include="RandomDataGenerator.Net" Version="1.0.19" />
|
<PackageReference Include="RandomDataGenerator.Net" Version="1.0.19" />
|
||||||
<PackageReference Include="Stef.Validation" Version="0.1.1" />
|
<PackageReference Include="Stef.Validation" Version="0.1.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup Condition="'$(Configuration)' == 'Debug'">
|
<ItemGroup Condition="'$(Configuration)' == 'Debug'">
|
||||||
<PackageReference Include="Microsoft.OpenApi.YamlReader" Version="2.0.0-preview.17" />
|
<PackageReference Include="Microsoft.OpenApi.YamlReader" Version="2.3.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup Condition="'$(Configuration)' == 'Release'">
|
<ItemGroup Condition="'$(Configuration)' == 'Release'">
|
||||||
<PackageReference Include="ILRepack.Lib.MSBuild.Task" Version="2.0.40" PrivateAssets="All" />
|
<PackageReference Include="ILRepack.Lib.MSBuild.Task" Version="2.0.40" PrivateAssets="All" />
|
||||||
<PackageReference Include="Microsoft.OpenApi" Version="2.0.0-preview.17" PrivateAssets="All" />
|
<PackageReference Include="Microsoft.OpenApi" Version="2.3.0" PrivateAssets="All" />
|
||||||
<PackageReference Include="Microsoft.OpenApi.YamlReader" Version="2.0.0-preview.17" PrivateAssets="All" />
|
<PackageReference Include="Microsoft.OpenApi.YamlReader" Version="2.3.0" PrivateAssets="All" />
|
||||||
<PackageReference Include="System.Text.Json" Version="8.0.5" />
|
<PackageReference Include="System.Text.Json" Version="8.0.5" />
|
||||||
<PackageReference Include="SharpYaml" Version="2.1.1" />
|
<PackageReference Include="SharpYaml" Version="2.1.3" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ using System.Collections.Generic;
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
using Microsoft.OpenApi.Models;
|
using Microsoft.OpenApi;
|
||||||
using Microsoft.OpenApi.Reader;
|
using Microsoft.OpenApi.Reader;
|
||||||
using Microsoft.OpenApi.YamlReader;
|
using Microsoft.OpenApi.YamlReader;
|
||||||
using RamlToOpenApiConverter;
|
using RamlToOpenApiConverter;
|
||||||
|
|||||||
@@ -96,14 +96,14 @@ public class ProtoBufMatcher : IProtoBufMatcher
|
|||||||
}
|
}
|
||||||
|
|
||||||
var protoDefinitions = ProtoDefinition().Texts;
|
var protoDefinitions = ProtoDefinition().Texts;
|
||||||
|
|
||||||
var resolver = new WireMockProtoFileResolver(protoDefinitions);
|
var resolver = new WireMockProtoFileResolver(protoDefinitions);
|
||||||
var request = new ConvertToObjectRequest(protoDefinitions[0], MessageType, input)
|
var request = new ConvertToObjectRequest(protoDefinitions[0], MessageType, input)
|
||||||
.WithProtoFileResolver(resolver);
|
.WithProtoFileResolver(resolver);
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
return await ProtoBufToJsonConverter.ConvertAsync(request, cancellationToken).ConfigureAwait(false);
|
return await ProtoBufToJsonConverter.ConvertAsync(request, cancellationToken);
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -26,7 +26,7 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="ProtoBufJsonConverter" Version="0.10.0" />
|
<PackageReference Include="ProtoBufJsonConverter" Version="0.11.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -33,7 +33,7 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="JsonConverter.Newtonsoft.Json" Version="0.7.0" />
|
<PackageReference Include="JsonConverter.Newtonsoft.Json" Version="0.8.0" />
|
||||||
<PackageReference Include="RestEase" Version="1.6.4" />
|
<PackageReference Include="RestEase" Version="1.6.4" />
|
||||||
<PackageReference Include="Stef.Validation" Version="0.1.1" />
|
<PackageReference Include="Stef.Validation" Version="0.1.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|||||||
@@ -80,6 +80,14 @@ public interface IBodyRequestBuilder : IMultiPartRequestBuilder
|
|||||||
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
|
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
|
||||||
IRequestBuilder WithBody(Func<object?, bool> func);
|
IRequestBuilder WithBody(Func<object?, bool> func);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// WithBody: func (type)
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The type.</typeparam>
|
||||||
|
/// <param name="func">The function.</param>
|
||||||
|
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
|
||||||
|
IRequestBuilder WithBodyAsType<T>(Func<T?, bool> func);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// WithBody: func (BodyData object)
|
/// WithBody: func (BodyData object)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
// Copyright © WireMock.Net
|
// Copyright © WireMock.Net
|
||||||
|
|
||||||
|
using JsonConverter.Abstractions;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using Newtonsoft.Json.Serialization;
|
using Newtonsoft.Json.Serialization;
|
||||||
|
|
||||||
@@ -7,24 +8,30 @@ namespace WireMock.Serialization;
|
|||||||
|
|
||||||
internal static class JsonSerializationConstants
|
internal static class JsonSerializationConstants
|
||||||
{
|
{
|
||||||
public static readonly JsonSerializerSettings JsonSerializerSettingsDefault = new()
|
internal static readonly JsonConverterOptions JsonConverterOptionsDefault = new()
|
||||||
{
|
{
|
||||||
Formatting = Formatting.Indented,
|
WriteIndented = true,
|
||||||
NullValueHandling = NullValueHandling.Ignore
|
IgnoreNullValues = true
|
||||||
};
|
};
|
||||||
|
|
||||||
public static readonly JsonSerializerSettings JsonSerializerSettingsIncludeNullValues = new()
|
//internal static readonly JsonSerializerSettings JsonSerializerSettingsDefault = new()
|
||||||
|
//{
|
||||||
|
// Formatting = Formatting.Indented,
|
||||||
|
// NullValueHandling = NullValueHandling.Ignore
|
||||||
|
//};
|
||||||
|
|
||||||
|
internal static readonly JsonSerializerSettings JsonSerializerSettingsIncludeNullValues = new()
|
||||||
{
|
{
|
||||||
Formatting = Formatting.Indented,
|
Formatting = Formatting.Indented,
|
||||||
NullValueHandling = NullValueHandling.Include
|
NullValueHandling = NullValueHandling.Include
|
||||||
};
|
};
|
||||||
|
|
||||||
public static readonly JsonSerializerSettings JsonDeserializerSettingsWithDateParsingNone = new()
|
internal static readonly JsonSerializerSettings JsonDeserializerSettingsWithDateParsingNone = new()
|
||||||
{
|
{
|
||||||
DateParseHandling = DateParseHandling.None
|
DateParseHandling = DateParseHandling.None
|
||||||
};
|
};
|
||||||
|
|
||||||
public static readonly JsonSerializerSettings JsonSerializerSettingsPact = new()
|
internal static readonly JsonSerializerSettings JsonSerializerSettingsPact = new()
|
||||||
{
|
{
|
||||||
Formatting = Formatting.Indented,
|
Formatting = Formatting.Indented,
|
||||||
NullValueHandling = NullValueHandling.Ignore,
|
NullValueHandling = NullValueHandling.Ignore,
|
||||||
|
|||||||
@@ -14,6 +14,8 @@ using WireMock.RegularExpressions;
|
|||||||
using WireMock.Types;
|
using WireMock.Types;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using WireMock.Models;
|
using WireMock.Models;
|
||||||
|
using JsonConverter.Abstractions;
|
||||||
|
using JsonConverter.Newtonsoft.Json;
|
||||||
|
|
||||||
#if USE_ASPNETCORE
|
#if USE_ASPNETCORE
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
@@ -338,4 +340,14 @@ public class WireMockServerSettings
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
[PublicAPI]
|
[PublicAPI]
|
||||||
public HandlebarsSettings? HandlebarsSettings { get; set; }
|
public HandlebarsSettings? HandlebarsSettings { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the default JSON converter used for serialization.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Set this property to customize how objects are serialized to and deserialized from JSON during mapping.
|
||||||
|
/// Default is <see cref="NewtonsoftJsonConverter"/>.
|
||||||
|
/// </remarks>
|
||||||
|
[PublicAPI]
|
||||||
|
public IJsonConverter DefaultJsonSerializer { get; set; } = new NewtonsoftJsonConverter();
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<Description>Shared interfaces, models, enumerations and types.</Description>
|
<Description>Shared interfaces, models, enumerations and types.</Description>
|
||||||
<Authors>Stef Heyenrath</Authors>
|
<Authors>Stef Heyenrath</Authors>
|
||||||
@@ -48,9 +48,10 @@
|
|||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Stef.Validation" Version="0.1.1" />
|
<PackageReference Include="Stef.Validation" Version="0.1.1" />
|
||||||
<PackageReference Include="AnyOf" Version="0.4.0" />
|
<PackageReference Include="AnyOf" Version="0.4.0" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
<!--<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />-->
|
||||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="1.1.0" />
|
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="1.1.0" />
|
||||||
<PackageReference Include="JsonConverter.Abstractions" Version="0.7.0" />
|
<!--<PackageReference Include="JsonConverter.Abstractions" Version="0.8.0" />-->
|
||||||
|
<PackageReference Include="JsonConverter.Newtonsoft.Json" Version="0.8.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
// Copyright © WireMock.Net
|
||||||
|
|
||||||
|
using WireMock.Net.Testcontainers;
|
||||||
|
|
||||||
|
namespace DotNet.Testcontainers.Configurations;
|
||||||
|
|
||||||
|
internal static class HttpWaitStrategyExtensions
|
||||||
|
{
|
||||||
|
internal static HttpWaitStrategy WithBasicAuthentication(this HttpWaitStrategy strategy, WireMockConfiguration configuration)
|
||||||
|
{
|
||||||
|
if (configuration.HasBasicAuthentication)
|
||||||
|
{
|
||||||
|
return strategy.WithBasicAuthentication(configuration.Username, configuration.Password);
|
||||||
|
}
|
||||||
|
|
||||||
|
return strategy;
|
||||||
|
}
|
||||||
|
}
|
||||||
20
src/WireMock.Net.Testcontainers/Utils/CombineUtils.cs
Normal file
20
src/WireMock.Net.Testcontainers/Utils/CombineUtils.cs
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
// Copyright © WireMock.Net
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace WireMock.Net.Testcontainers.Utils;
|
||||||
|
|
||||||
|
internal static class CombineUtils
|
||||||
|
{
|
||||||
|
internal static List<T> Combine<T>(List<T> oldValue, List<T> newValue)
|
||||||
|
{
|
||||||
|
return oldValue.Union(newValue).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static Dictionary<TKey, TValue> Combine<TKey, TValue>(Dictionary<TKey, TValue> oldValue, Dictionary<TKey, TValue> newValue)
|
||||||
|
where TKey : notnull
|
||||||
|
{
|
||||||
|
return oldValue.Union(newValue).ToDictionary(item => item.Key, item => item.Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -39,7 +39,7 @@
|
|||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Stef.Validation" Version="0.1.1" />
|
<PackageReference Include="Stef.Validation" Version="0.1.1" />
|
||||||
<PackageReference Include="Testcontainers" Version="4.7.0" />
|
<PackageReference Include="Testcontainers" Version="4.10.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
// Copyright © WireMock.Net
|
// Copyright © WireMock.Net
|
||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
|
||||||
using Docker.DotNet.Models;
|
using Docker.DotNet.Models;
|
||||||
using DotNet.Testcontainers.Builders;
|
using DotNet.Testcontainers.Builders;
|
||||||
using DotNet.Testcontainers.Configurations;
|
using DotNet.Testcontainers.Configurations;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
using Stef.Validation;
|
using Stef.Validation;
|
||||||
|
using WireMock.Net.Testcontainers.Utils;
|
||||||
|
|
||||||
namespace WireMock.Net.Testcontainers;
|
namespace WireMock.Net.Testcontainers;
|
||||||
|
|
||||||
@@ -77,8 +77,8 @@ public sealed class WireMockConfiguration : ContainerConfiguration
|
|||||||
StaticMappingsPath = BuildConfiguration.Combine(oldValue.StaticMappingsPath, newValue.StaticMappingsPath);
|
StaticMappingsPath = BuildConfiguration.Combine(oldValue.StaticMappingsPath, newValue.StaticMappingsPath);
|
||||||
WatchStaticMappings = BuildConfiguration.Combine(oldValue.WatchStaticMappings, newValue.WatchStaticMappings);
|
WatchStaticMappings = BuildConfiguration.Combine(oldValue.WatchStaticMappings, newValue.WatchStaticMappings);
|
||||||
WatchStaticMappingsInSubdirectories = BuildConfiguration.Combine(oldValue.WatchStaticMappingsInSubdirectories, newValue.WatchStaticMappingsInSubdirectories);
|
WatchStaticMappingsInSubdirectories = BuildConfiguration.Combine(oldValue.WatchStaticMappingsInSubdirectories, newValue.WatchStaticMappingsInSubdirectories);
|
||||||
AdditionalUrls = Combine(oldValue.AdditionalUrls, newValue.AdditionalUrls);
|
AdditionalUrls = CombineUtils.Combine(oldValue.AdditionalUrls, newValue.AdditionalUrls);
|
||||||
ProtoDefinitions = Combine(oldValue.ProtoDefinitions, newValue.ProtoDefinitions);
|
ProtoDefinitions = CombineUtils.Combine(oldValue.ProtoDefinitions, newValue.ProtoDefinitions);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -130,16 +130,4 @@ public sealed class WireMockConfiguration : ContainerConfiguration
|
|||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static List<T> Combine<T>(List<T> oldValue, List<T> newValue)
|
|
||||||
{
|
|
||||||
return oldValue.Concat(newValue).ToList();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Dictionary<TKey, TValue> Combine<TKey, TValue>(Dictionary<TKey, TValue> oldValue, Dictionary<TKey, TValue> newValue)
|
|
||||||
{
|
|
||||||
return newValue
|
|
||||||
.Concat(oldValue.Where(item => !newValue.Keys.Contains(item.Key)))
|
|
||||||
.ToDictionary(item => item.Key, item => item.Value);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -24,28 +24,21 @@ namespace WireMock.Net.Testcontainers;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// A container for running WireMock in a docker environment.
|
/// A container for running WireMock in a docker environment.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed class WireMockContainer : DockerContainer
|
/// <remarks>
|
||||||
|
/// Initializes a new instance of the <see cref="WireMockContainer" /> class.
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="configuration">The container configuration.</param>
|
||||||
|
public sealed class WireMockContainer(WireMockConfiguration configuration) : DockerContainer(configuration)
|
||||||
{
|
{
|
||||||
private const int EnhancedFileSystemWatcherTimeoutMs = 2000;
|
private const int EnhancedFileSystemWatcherTimeoutMs = 2000;
|
||||||
internal const int ContainerPort = 80;
|
internal const int ContainerPort = 80;
|
||||||
|
|
||||||
private readonly WireMockConfiguration _configuration;
|
private readonly WireMockConfiguration _configuration = Guard.NotNull(configuration);
|
||||||
|
|
||||||
private IWireMockAdminApi? _adminApi;
|
private IWireMockAdminApi? _adminApi;
|
||||||
private EnhancedFileSystemWatcher? _enhancedFileSystemWatcher;
|
private EnhancedFileSystemWatcher? _enhancedFileSystemWatcher;
|
||||||
private IDictionary<int, Uri>? _publicUris;
|
private IDictionary<int, Uri>? _publicUris;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the <see cref="WireMockContainer" /> class.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="configuration">The container configuration.</param>
|
|
||||||
public WireMockContainer(WireMockConfiguration configuration) : base(configuration)
|
|
||||||
{
|
|
||||||
_configuration = Guard.NotNull(configuration);
|
|
||||||
|
|
||||||
Started += async (sender, eventArgs) => await WireMockContainerStartedAsync(sender, eventArgs);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the public Url.
|
/// Gets the public Url.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -128,12 +121,14 @@ public sealed class WireMockContainer : DockerContainer
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="source">The source directory or file to be copied.</param>
|
/// <param name="source">The source directory or file to be copied.</param>
|
||||||
/// <param name="target">The target directory path to copy the files to.</param>
|
/// <param name="target">The target directory path to copy the files to.</param>
|
||||||
|
/// <param name="uid">The user ID to set for the copied file or directory. Defaults to 0 (root).</param>
|
||||||
|
/// <param name="gid">The group ID to set for the copied file or directory. Defaults to 0 (root).</param>
|
||||||
/// <param name="fileMode">The POSIX file mode permission.</param>
|
/// <param name="fileMode">The POSIX file mode permission.</param>
|
||||||
/// <param name="ct">Cancellation token.</param>
|
/// <param name="ct">Cancellation token.</param>
|
||||||
/// <returns>A task that completes when the directory or file has been copied.</returns>
|
/// <returns>A task that completes when the directory or file has been copied.</returns>
|
||||||
public new async Task CopyAsync(string source, string target, UnixFileModes fileMode = Unix.FileMode644, CancellationToken ct = default)
|
public new async Task CopyAsync(string source, string target, uint uid = 0, uint gid = 0, UnixFileModes fileMode = Unix.FileMode644, CancellationToken ct = default)
|
||||||
{
|
{
|
||||||
await base.CopyAsync(source, target, fileMode, ct);
|
await base.CopyAsync(source, target, uid, gid, fileMode, ct);
|
||||||
|
|
||||||
if (_configuration.WatchStaticMappings && await PathStartsWithContainerMappingsPath(target))
|
if (_configuration.WatchStaticMappings && await PathStartsWithContainerMappingsPath(target))
|
||||||
{
|
{
|
||||||
@@ -154,14 +149,29 @@ public sealed class WireMockContainer : DockerContainer
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await _adminApi.ReloadStaticMappingsAsync(cancellationToken);
|
var result = await _adminApi.ReloadStaticMappingsAsync(cancellationToken);
|
||||||
|
Logger.LogInformation("WireMock.Net -> ReloadStaticMappings result: {Result}", result);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Logger.LogWarning(ex, "Error calling /__admin/mappings/reloadStaticMappings");
|
Logger.LogWarning(ex, "WireMock.Net -> Error calling /__admin/mappings/reloadStaticMappings");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Performs additional actions after the container is ready.
|
||||||
|
/// </summary>
|
||||||
|
public Task CallAdditionalActionsAfterReadyAsync()
|
||||||
|
{
|
||||||
|
Logger.LogInformation("WireMock.Net -> Calling additional actions.");
|
||||||
|
|
||||||
|
_adminApi = CreateWireMockAdminClient();
|
||||||
|
|
||||||
|
RegisterEnhancedFileSystemWatcher();
|
||||||
|
|
||||||
|
return AddProtoDefinitionsAsync();
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
protected override ValueTask DisposeAsyncCore()
|
protected override ValueTask DisposeAsyncCore()
|
||||||
{
|
{
|
||||||
@@ -194,15 +204,6 @@ public sealed class WireMockContainer : DockerContainer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task WireMockContainerStartedAsync(object sender, EventArgs e)
|
|
||||||
{
|
|
||||||
_adminApi = CreateWireMockAdminClient();
|
|
||||||
|
|
||||||
RegisterEnhancedFileSystemWatcher();
|
|
||||||
|
|
||||||
await CallAdditionalActionsAfterStartedAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void RegisterEnhancedFileSystemWatcher()
|
private void RegisterEnhancedFileSystemWatcher()
|
||||||
{
|
{
|
||||||
if (!_configuration.WatchStaticMappings || string.IsNullOrEmpty(_configuration.StaticMappingsPath))
|
if (!_configuration.WatchStaticMappings || string.IsNullOrEmpty(_configuration.StaticMappingsPath))
|
||||||
@@ -220,23 +221,31 @@ public sealed class WireMockContainer : DockerContainer
|
|||||||
_enhancedFileSystemWatcher.EnableRaisingEvents = true;
|
_enhancedFileSystemWatcher.EnableRaisingEvents = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task CallAdditionalActionsAfterStartedAsync()
|
private async Task AddProtoDefinitionsAsync()
|
||||||
{
|
{
|
||||||
foreach (var kvp in _configuration.ProtoDefinitions)
|
foreach (var kvp in _configuration.ProtoDefinitions)
|
||||||
{
|
{
|
||||||
Logger.LogInformation("Adding ProtoDefinition {Id}", kvp.Key);
|
Logger.LogInformation("WireMock.Net -> Adding ProtoDefinition '{Id}'", kvp.Key);
|
||||||
|
|
||||||
foreach (var protoDefinition in kvp.Value)
|
foreach (var protoDefinition in kvp.Value)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await _adminApi!.AddProtoDefinitionAsync(kvp.Key, protoDefinition);
|
var result = await _adminApi!.AddProtoDefinitionAsync(kvp.Key, protoDefinition);
|
||||||
|
Logger.LogInformation("WireMock.Net -> AddProtoDefinition '{Id}' result: {Result}", kvp.Key, result);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Logger.LogWarning(ex, "Error adding ProtoDefinition '{Id}'.", kvp.Key);
|
Logger.LogWarning(ex, "WireMock.Net -> Error adding ProtoDefinition '{Id}'.", kvp.Key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Force a reload of static mappings when ProtoDefinitions are added at server-level to fix #1382
|
||||||
|
if (_configuration.ProtoDefinitions.Count > 0)
|
||||||
|
{
|
||||||
|
await ReloadStaticMappingsAsync();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void FileCreatedChangedOrDeleted(object sender, FileSystemEventArgs args)
|
private async void FileCreatedChangedOrDeleted(object sender, FileSystemEventArgs args)
|
||||||
@@ -244,16 +253,17 @@ public sealed class WireMockContainer : DockerContainer
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
await ReloadStaticMappingsAsync(args.FullPath);
|
await ReloadStaticMappingsAsync(args.FullPath);
|
||||||
|
Logger.LogInformation("WireMock.Net -> ReloadStaticMappings triggered from file change: '{FullPath}'.", args.FullPath);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Logger.LogWarning(ex, "Error reloading static mappings from '{FullPath}'.", args.FullPath);
|
Logger.LogWarning(ex, "WireMock.Net -> Error reloading static mappings from '{FullPath}'.", args.FullPath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task ReloadStaticMappingsAsync(string path, CancellationToken cancellationToken = default)
|
private async Task ReloadStaticMappingsAsync(string path, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
Logger.LogInformation("MappingFile created, changed or deleted: '{Path}'. Triggering ReloadStaticMappings.", path);
|
Logger.LogInformation("WireMock.Net -> MappingFile created, changed or deleted: '{Path}'. Triggering ReloadStaticMappings.", path);
|
||||||
await ReloadStaticMappingsAsync(cancellationToken);
|
await ReloadStaticMappingsAsync(cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Net;
|
||||||
|
using System.Net.Http;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using Docker.DotNet.Models;
|
using Docker.DotNet.Models;
|
||||||
using DotNet.Testcontainers.Builders;
|
using DotNet.Testcontainers.Builders;
|
||||||
@@ -62,6 +64,29 @@ public sealed class WireMockContainerBuilder : ContainerBuilder<WireMockContaine
|
|||||||
return WithImage(OSPlatform.Windows);
|
return WithImage(OSPlatform.Windows);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets a custom WireMock.Net image for which to create the container.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="image">The image name.</param>
|
||||||
|
/// <returns>A configured instance of <see cref="WireMockContainerBuilder"/></returns>
|
||||||
|
[PublicAPI]
|
||||||
|
public new WireMockContainerBuilder WithImage(string image)
|
||||||
|
{
|
||||||
|
return WithCustomImage(image);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets a custom WireMock.Net image for which to create the container.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="image">The image name.</param>
|
||||||
|
/// <returns>A configured instance of <see cref="WireMockContainerBuilder"/></returns>
|
||||||
|
[PublicAPI]
|
||||||
|
public WireMockContainerBuilder WithCustomImage(string image)
|
||||||
|
{
|
||||||
|
_imageOS ??= TestcontainersUtils.GetImageOSAsync.Value.GetAwaiter().GetResult();
|
||||||
|
return base.WithImage(image);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Set the admin username and password for the container (basic authentication).
|
/// Set the admin username and password for the container (basic authentication).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -112,6 +137,7 @@ public sealed class WireMockContainerBuilder : ContainerBuilder<WireMockContaine
|
|||||||
{
|
{
|
||||||
DockerResourceConfiguration.WithWatchStaticMappings(includeSubDirectories);
|
DockerResourceConfiguration.WithWatchStaticMappings(includeSubDirectories);
|
||||||
return
|
return
|
||||||
|
WithCommand("--ReadStaticMappings true").
|
||||||
WithCommand("--WatchStaticMappings true").
|
WithCommand("--WatchStaticMappings true").
|
||||||
WithCommand("--WatchStaticMappingsInSubdirectories", includeSubDirectories);
|
WithCommand("--WatchStaticMappingsInSubdirectories", includeSubDirectories);
|
||||||
}
|
}
|
||||||
@@ -129,9 +155,7 @@ public sealed class WireMockContainerBuilder : ContainerBuilder<WireMockContaine
|
|||||||
|
|
||||||
DockerResourceConfiguration.WithStaticMappingsPath(path);
|
DockerResourceConfiguration.WithStaticMappingsPath(path);
|
||||||
|
|
||||||
return
|
return WithWatchStaticMappings(includeSubDirectories);
|
||||||
WithReadStaticMappings().
|
|
||||||
WithCommand("--WatchStaticMappingsInSubdirectories", includeSubDirectories);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -208,9 +232,9 @@ public sealed class WireMockContainerBuilder : ContainerBuilder<WireMockContaine
|
|||||||
// In case the _imageOS is not set, determine it from the Image FullName.
|
// In case the _imageOS is not set, determine it from the Image FullName.
|
||||||
if (_imageOS == null)
|
if (_imageOS == null)
|
||||||
{
|
{
|
||||||
if (builder.DockerResourceConfiguration.Image.FullName.IndexOf("wiremock.net", StringComparison.OrdinalIgnoreCase) < 0)
|
if (builder.DockerResourceConfiguration.Image.FullName.IndexOf("wiremock", StringComparison.OrdinalIgnoreCase) < 0)
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException();
|
throw new InvalidOperationException("It's only possible to use a wiremock docker image.");
|
||||||
}
|
}
|
||||||
|
|
||||||
_imageOS = builder.DockerResourceConfiguration.Image.FullName.IndexOf("windows", StringComparison.OrdinalIgnoreCase) >= 0 ? OSPlatform.Windows : OSPlatform.Linux;
|
_imageOS = builder.DockerResourceConfiguration.Image.FullName.IndexOf("windows", StringComparison.OrdinalIgnoreCase) >= 0 ? OSPlatform.Windows : OSPlatform.Linux;
|
||||||
@@ -228,6 +252,25 @@ public sealed class WireMockContainerBuilder : ContainerBuilder<WireMockContaine
|
|||||||
|
|
||||||
builder.Validate();
|
builder.Validate();
|
||||||
|
|
||||||
|
var waitForContainerOS = _imageOS == OSPlatform.Windows ? Wait.ForWindowsContainer() : Wait.ForUnixContainer();
|
||||||
|
builder = builder
|
||||||
|
.WithWaitStrategy(waitForContainerOS
|
||||||
|
.UntilMessageIsLogged("WireMock.Net server running", waitStrategy => waitStrategy.WithTimeout(TimeSpan.FromSeconds(30)))
|
||||||
|
.UntilHttpRequestIsSucceeded(httpWaitStrategy => httpWaitStrategy
|
||||||
|
.ForPort(WireMockContainer.ContainerPort)
|
||||||
|
.WithMethod(HttpMethod.Get)
|
||||||
|
.WithBasicAuthentication(DockerResourceConfiguration)
|
||||||
|
.ForPath("/__admin/health")
|
||||||
|
.ForStatusCode(HttpStatusCode.OK)
|
||||||
|
.ForResponseMessageMatching(async httpResponseMessage =>
|
||||||
|
{
|
||||||
|
var content = await httpResponseMessage.Content.ReadAsStringAsync();
|
||||||
|
return content?.Contains("Healthy") == true;
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.AddCustomWaitStrategy(new WireMockWaitStrategy())
|
||||||
|
);
|
||||||
|
|
||||||
return new WireMockContainer(builder.DockerResourceConfiguration);
|
return new WireMockContainer(builder.DockerResourceConfiguration);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -236,11 +279,9 @@ public sealed class WireMockContainerBuilder : ContainerBuilder<WireMockContaine
|
|||||||
{
|
{
|
||||||
var builder = base.Init();
|
var builder = base.Init();
|
||||||
|
|
||||||
var waitForContainerOS = _imageOS == OSPlatform.Windows ? Wait.ForWindowsContainer() : Wait.ForUnixContainer();
|
|
||||||
return builder
|
return builder
|
||||||
.WithPortBinding(WireMockContainer.ContainerPort, true)
|
.WithPortBinding(WireMockContainer.ContainerPort, true)
|
||||||
.WithCommand($"--WireMockLogger {DefaultLogger}")
|
.WithCommand($"--WireMockLogger {DefaultLogger}");
|
||||||
.WithWaitStrategy(waitForContainerOS.UntilMessageIsLogged("WireMock.Net server running"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
|||||||
23
src/WireMock.Net.Testcontainers/WireMockWaitStrategy.cs
Normal file
23
src/WireMock.Net.Testcontainers/WireMockWaitStrategy.cs
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
// Copyright © WireMock.Net
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using DotNet.Testcontainers.Configurations;
|
||||||
|
using DotNet.Testcontainers.Containers;
|
||||||
|
|
||||||
|
namespace WireMock.Net.Testcontainers;
|
||||||
|
|
||||||
|
internal class WireMockWaitStrategy : IWaitUntil
|
||||||
|
{
|
||||||
|
public async Task<bool> UntilAsync(IContainer container)
|
||||||
|
{
|
||||||
|
if (container is not WireMockContainer wireMockContainer)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("The passed container is not a WireMockContainer.");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
await wireMockContainer.CallAdditionalActionsAfterReadyAsync();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
79
src/WireMock.Net.xUnit.v3/TestOutputHelperWireMockLogger.cs
Normal file
79
src/WireMock.Net.xUnit.v3/TestOutputHelperWireMockLogger.cs
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
// Copyright © WireMock.Net
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Stef.Validation;
|
||||||
|
using WireMock.Admin.Requests;
|
||||||
|
using WireMock.Logging;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace WireMock.Net.Xunit;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// When using xUnit, this class enables to log the output from WireMock.Net to the <see cref="ITestOutputHelper"/>.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class TestOutputHelperWireMockLogger : IWireMockLogger
|
||||||
|
{
|
||||||
|
private readonly ITestOutputHelper _testOutputHelper;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create a new instance on the <see cref="TestOutputHelperWireMockLogger"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="testOutputHelper">Represents a class which can be used to provide test output.</param>
|
||||||
|
public TestOutputHelperWireMockLogger(ITestOutputHelper testOutputHelper)
|
||||||
|
{
|
||||||
|
_testOutputHelper = Guard.NotNull(testOutputHelper);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void Debug(string formatString, params object[] args)
|
||||||
|
{
|
||||||
|
_testOutputHelper.WriteLine(Format("Debug", formatString, args));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void Info(string formatString, params object[] args)
|
||||||
|
{
|
||||||
|
_testOutputHelper.WriteLine(Format("Info", formatString, args));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void Warn(string formatString, params object[] args)
|
||||||
|
{
|
||||||
|
_testOutputHelper.WriteLine(Format("Warning", formatString, args));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void Error(string formatString, params object[] args)
|
||||||
|
{
|
||||||
|
_testOutputHelper.WriteLine(Format("Error", formatString, args));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void Error(string message, Exception exception)
|
||||||
|
{
|
||||||
|
_testOutputHelper.WriteLine(Format("Error", $"{message} {{0}}", exception));
|
||||||
|
|
||||||
|
if (exception is AggregateException ae)
|
||||||
|
{
|
||||||
|
ae.Handle(ex =>
|
||||||
|
{
|
||||||
|
_testOutputHelper.WriteLine(Format("Error", "Exception {0}", ex));
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void DebugRequestResponse(LogEntryModel logEntryModel, bool isAdminRequest)
|
||||||
|
{
|
||||||
|
var message = JsonConvert.SerializeObject(logEntryModel, Formatting.Indented);
|
||||||
|
_testOutputHelper.WriteLine(Format("DebugRequestResponse", "Admin[{0}] {1}", isAdminRequest, message));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string Format(string level, string formatString, params object[] args)
|
||||||
|
{
|
||||||
|
var message = args.Length > 0 ? string.Format(formatString, args) : formatString;
|
||||||
|
return $"{DateTime.UtcNow} [{level}] : {message}";
|
||||||
|
}
|
||||||
|
}
|
||||||
37
src/WireMock.Net.xUnit.v3/WireMock.Net.xUnit.v3.csproj
Normal file
37
src/WireMock.Net.xUnit.v3/WireMock.Net.xUnit.v3.csproj
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
<PropertyGroup>
|
||||||
|
<Description>Some extensions for xUnit (ITestOutputHelper)</Description>
|
||||||
|
<AssemblyTitle>WireMock.Net.xUnit.v3</AssemblyTitle>
|
||||||
|
<Authors>Stef Heyenrath</Authors>
|
||||||
|
<TargetFramework>netstandard2.0</TargetFramework>
|
||||||
|
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||||
|
<PackageTags>tdd;wiremock;test;unittest;xunit</PackageTags>
|
||||||
|
<ProjectGuid>{4F46BD02-BEBC-4B2D-B857-4169AD222267}</ProjectGuid>
|
||||||
|
<PublishRepositoryUrl>true</PublishRepositoryUrl>
|
||||||
|
<AllowedOutputExtensionsInPackageBuildOutputFolder>$(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb</AllowedOutputExtensionsInPackageBuildOutputFolder>
|
||||||
|
<EmbedUntrackedSources>true</EmbedUntrackedSources>
|
||||||
|
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
|
||||||
|
<GenerateBindingRedirectsOutputType>true</GenerateBindingRedirectsOutputType>
|
||||||
|
<SignAssembly>true</SignAssembly>
|
||||||
|
<AssemblyOriginatorKeyFile>../WireMock.Net/WireMock.Net.snk</AssemblyOriginatorKeyFile>
|
||||||
|
<PublicSign Condition=" '$(OS)' != 'Windows_NT' ">true</PublicSign>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
|
||||||
|
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.4" />
|
||||||
|
<PackageReference Include="Stef.Validation" Version="0.2.0" />
|
||||||
|
<PackageReference Include="xunit.v3.extensibility.core" Version="3.2.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\WireMock.Net.Abstractions\WireMock.Net.Abstractions.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Update="JetBrains.Annotations" Version="2025.2.2" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
||||||
@@ -19,6 +19,7 @@ public class IntegrationTests(ITestOutputHelper output)
|
|||||||
var appHostBuilder = await DistributedApplicationTestingBuilder.CreateAsync<WireMock_Net_Aspire_TestAppHost>();
|
var appHostBuilder = await DistributedApplicationTestingBuilder.CreateAsync<WireMock_Net_Aspire_TestAppHost>();
|
||||||
await using var app = await appHostBuilder.BuildAsync();
|
await using var app = await appHostBuilder.BuildAsync();
|
||||||
await app.StartAsync();
|
await app.StartAsync();
|
||||||
|
await app.ResourceNotifications.WaitForResourceHealthyAsync("wiremock-service");
|
||||||
|
|
||||||
using var httpClient = app.CreateHttpClient("wiremock-service");
|
using var httpClient = app.CreateHttpClient("wiremock-service");
|
||||||
|
|
||||||
@@ -46,6 +47,7 @@ public class IntegrationTests(ITestOutputHelper output)
|
|||||||
var appHostBuilder = await DistributedApplicationTestingBuilder.CreateAsync<WireMock_Net_Aspire_TestAppHost>();
|
var appHostBuilder = await DistributedApplicationTestingBuilder.CreateAsync<WireMock_Net_Aspire_TestAppHost>();
|
||||||
await using var app = await appHostBuilder.BuildAsync();
|
await using var app = await appHostBuilder.BuildAsync();
|
||||||
await app.StartAsync();
|
await app.StartAsync();
|
||||||
|
await app.ResourceNotifications.WaitForResourceHealthyAsync("wiremock-service");
|
||||||
|
|
||||||
var adminClient = app.CreateWireMockAdminClient("wiremock-service");
|
var adminClient = app.CreateWireMockAdminClient("wiremock-service");
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ public class WireMockServerArgumentsTests
|
|||||||
var args = new WireMockServerArguments();
|
var args = new WireMockServerArguments();
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
args.HttpPort.Should().BeNull();
|
args.HttpPorts.Should().BeEmpty();
|
||||||
args.AdminUsername.Should().BeNull();
|
args.AdminUsername.Should().BeNull();
|
||||||
args.AdminPassword.Should().BeNull();
|
args.AdminPassword.Should().BeNull();
|
||||||
args.ReadStaticMappings.Should().BeFalse();
|
args.ReadStaticMappings.Should().BeFalse();
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
using System.Net.Sockets;
|
using System.Net.Sockets;
|
||||||
using FluentAssertions;
|
using FluentAssertions;
|
||||||
using Moq;
|
using Moq;
|
||||||
|
using WireMock.Util;
|
||||||
|
|
||||||
namespace WireMock.Net.Aspire.Tests;
|
namespace WireMock.Net.Aspire.Tests;
|
||||||
|
|
||||||
@@ -40,7 +41,21 @@ public class WireMockServerBuilderExtensionsTests
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void AddWireMock()
|
public void AddWireMock_WithInvalidAdditionalUrls_ShouldThrowArgumentException()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
string[] invalidUrls = { "err" };
|
||||||
|
var builder = Mock.Of<IDistributedApplicationBuilder>();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
Action act = () => builder.AddWireMock("ValidName", invalidUrls);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
act.Should().Throw<ArgumentException>().WithMessage("The URL 'err' is not valid.");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void AddWireMockWithPort()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var name = $"apiservice{Guid.NewGuid()}";
|
var name = $"apiservice{Guid.NewGuid()}";
|
||||||
@@ -65,9 +80,9 @@ public class WireMockServerBuilderExtensionsTests
|
|||||||
ReadStaticMappings = true,
|
ReadStaticMappings = true,
|
||||||
WatchStaticMappings = false,
|
WatchStaticMappings = false,
|
||||||
MappingsPath = null,
|
MappingsPath = null,
|
||||||
HttpPort = port
|
HttpPorts = [port]
|
||||||
});
|
});
|
||||||
wiremock.Resource.Annotations.Should().HaveCount(5);
|
wiremock.Resource.Annotations.Should().HaveCount(6);
|
||||||
|
|
||||||
var containerImageAnnotation = wiremock.Resource.Annotations.OfType<ContainerImageAnnotation>().FirstOrDefault();
|
var containerImageAnnotation = wiremock.Resource.Annotations.OfType<ContainerImageAnnotation>().FirstOrDefault();
|
||||||
containerImageAnnotation.Should().BeEquivalentTo(new ContainerImageAnnotation
|
containerImageAnnotation.Should().BeEquivalentTo(new ContainerImageAnnotation
|
||||||
@@ -90,9 +105,90 @@ public class WireMockServerBuilderExtensionsTests
|
|||||||
));
|
));
|
||||||
|
|
||||||
wiremock.Resource.Annotations.OfType<EnvironmentCallbackAnnotation>().FirstOrDefault().Should().NotBeNull();
|
wiremock.Resource.Annotations.OfType<EnvironmentCallbackAnnotation>().FirstOrDefault().Should().NotBeNull();
|
||||||
|
|
||||||
wiremock.Resource.Annotations.OfType<CommandLineArgsCallbackAnnotation>().FirstOrDefault().Should().NotBeNull();
|
wiremock.Resource.Annotations.OfType<CommandLineArgsCallbackAnnotation>().FirstOrDefault().Should().NotBeNull();
|
||||||
|
wiremock.Resource.Annotations.OfType<ResourceCommandAnnotation>().FirstOrDefault().Should().NotBeNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void AddWireMockWithAdditionalUrls()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var name = $"apiservice{Guid.NewGuid()}";
|
||||||
|
var freePorts = PortUtils.FindFreeTcpPorts(2).ToList();
|
||||||
|
string[] additionalUrls = { $"http://*:{freePorts[0]}", $"grpc://*:{freePorts[1]}" };
|
||||||
|
const string username = "admin";
|
||||||
|
const string password = "test";
|
||||||
|
var builder = DistributedApplication.CreateBuilder();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var wiremock = builder
|
||||||
|
.AddWireMock(name, additionalUrls)
|
||||||
|
.WithAdminUserNameAndPassword(username, password)
|
||||||
|
.WithReadStaticMappings();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
wiremock.Resource.Should().NotBeNull();
|
||||||
|
wiremock.Resource.Name.Should().Be(name);
|
||||||
|
wiremock.Resource.Arguments.Should().BeEquivalentTo(new WireMockServerArguments
|
||||||
|
{
|
||||||
|
AdminPassword = password,
|
||||||
|
AdminUsername = username,
|
||||||
|
ReadStaticMappings = true,
|
||||||
|
WatchStaticMappings = false,
|
||||||
|
MappingsPath = null,
|
||||||
|
HttpPorts = freePorts,
|
||||||
|
AdditionalUrls = additionalUrls.ToList()
|
||||||
|
});
|
||||||
|
wiremock.Resource.Annotations.Should().HaveCount(9);
|
||||||
|
|
||||||
|
var containerImageAnnotation = wiremock.Resource.Annotations.OfType<ContainerImageAnnotation>().FirstOrDefault();
|
||||||
|
containerImageAnnotation.Should().BeEquivalentTo(new ContainerImageAnnotation
|
||||||
|
{
|
||||||
|
Image = "sheyenrath/wiremock.net-alpine",
|
||||||
|
Registry = null,
|
||||||
|
Tag = "latest"
|
||||||
|
});
|
||||||
|
|
||||||
|
var endpointAnnotations = wiremock.Resource.Annotations.OfType<EndpointAnnotation>().ToArray();
|
||||||
|
endpointAnnotations.Should().HaveCount(3);
|
||||||
|
|
||||||
|
var endpointAnnotationForHttp80 = endpointAnnotations[0];
|
||||||
|
endpointAnnotationForHttp80.Should().BeEquivalentTo(new EndpointAnnotation(
|
||||||
|
protocol: ProtocolType.Tcp,
|
||||||
|
uriScheme: "http",
|
||||||
|
transport: null,
|
||||||
|
name: null,
|
||||||
|
port: null,
|
||||||
|
targetPort: 80,
|
||||||
|
isExternal: null,
|
||||||
|
isProxied: true
|
||||||
|
));
|
||||||
|
var endpointAnnotationForHttpFreePort = endpointAnnotations[1];
|
||||||
|
endpointAnnotationForHttpFreePort.Should().BeEquivalentTo(new EndpointAnnotation(
|
||||||
|
protocol: ProtocolType.Tcp,
|
||||||
|
uriScheme: "http",
|
||||||
|
transport: null,
|
||||||
|
name: $"http-{freePorts[0]}",
|
||||||
|
port: freePorts[0],
|
||||||
|
targetPort: freePorts[0],
|
||||||
|
isExternal: null,
|
||||||
|
isProxied: true
|
||||||
|
));
|
||||||
|
|
||||||
|
var endpointAnnotationForGrpcFreePort = endpointAnnotations[2];
|
||||||
|
endpointAnnotationForGrpcFreePort.Should().BeEquivalentTo(new EndpointAnnotation(
|
||||||
|
protocol: ProtocolType.Tcp,
|
||||||
|
uriScheme: "grpc",
|
||||||
|
transport: null,
|
||||||
|
name: $"grpc-{freePorts[1]}",
|
||||||
|
port: freePorts[1],
|
||||||
|
targetPort: freePorts[1],
|
||||||
|
isExternal: null,
|
||||||
|
isProxied: true
|
||||||
|
));
|
||||||
|
|
||||||
|
wiremock.Resource.Annotations.OfType<EnvironmentCallbackAnnotation>().FirstOrDefault().Should().NotBeNull();
|
||||||
|
wiremock.Resource.Annotations.OfType<CommandLineArgsCallbackAnnotation>().FirstOrDefault().Should().NotBeNull();
|
||||||
wiremock.Resource.Annotations.OfType<ResourceCommandAnnotation>().FirstOrDefault().Should().NotBeNull();
|
wiremock.Resource.Annotations.OfType<ResourceCommandAnnotation>().FirstOrDefault().Should().NotBeNull();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -190,5 +190,92 @@
|
|||||||
BodyDestination: SameAsSource,
|
BodyDestination: SameAsSource,
|
||||||
Body: Buy milk
|
Body: Buy milk
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Guid: 98fae52e-76df-47d9-876f-2ee32e931006,
|
||||||
|
UpdatedAt: 2023-01-14 15:16:17,
|
||||||
|
Request: {
|
||||||
|
Path: {
|
||||||
|
Matchers: [
|
||||||
|
{
|
||||||
|
Name: WildcardMatcher,
|
||||||
|
Pattern: /delay,
|
||||||
|
IgnoreCase: false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
Methods: [
|
||||||
|
GET
|
||||||
|
]
|
||||||
|
},
|
||||||
|
Response: {
|
||||||
|
Delay: 1000
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Guid: 98fae52e-76df-47d9-876f-2ee32e931007,
|
||||||
|
UpdatedAt: 2023-01-14 15:16:17,
|
||||||
|
Request: {
|
||||||
|
Path: {
|
||||||
|
Matchers: [
|
||||||
|
{
|
||||||
|
Name: WildcardMatcher,
|
||||||
|
Pattern: /random-delay,
|
||||||
|
IgnoreCase: false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
Methods: [
|
||||||
|
GET
|
||||||
|
]
|
||||||
|
},
|
||||||
|
Response: {
|
||||||
|
MinimumRandomDelay: 1234,
|
||||||
|
MaximumRandomDelay: 60000
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Guid: 98fae52e-76df-47d9-876f-2ee32e931008,
|
||||||
|
UpdatedAt: 2023-01-14 15:16:17,
|
||||||
|
Request: {
|
||||||
|
Path: {
|
||||||
|
Matchers: [
|
||||||
|
{
|
||||||
|
Name: WildcardMatcher,
|
||||||
|
Pattern: /prob,
|
||||||
|
IgnoreCase: false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
Methods: [
|
||||||
|
GET
|
||||||
|
]
|
||||||
|
},
|
||||||
|
Response: {
|
||||||
|
StatusCode: 300
|
||||||
|
},
|
||||||
|
Probability: 0.1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Guid: 98fae52e-76df-47d9-876f-2ee32e931009,
|
||||||
|
UpdatedAt: 2023-01-14 15:16:17,
|
||||||
|
Request: {
|
||||||
|
Path: {
|
||||||
|
Matchers: [
|
||||||
|
{
|
||||||
|
Name: WildcardMatcher,
|
||||||
|
Pattern: /prob,
|
||||||
|
IgnoreCase: false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
Methods: [
|
||||||
|
GET
|
||||||
|
]
|
||||||
|
},
|
||||||
|
Response: {
|
||||||
|
StatusCode: 201
|
||||||
|
},
|
||||||
|
Probability: 0.9
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -78,3 +78,45 @@ builder
|
|||||||
.WithBody("Buy milk")
|
.WithBody("Buy milk")
|
||||||
);
|
);
|
||||||
|
|
||||||
|
builder
|
||||||
|
.Given(Request.Create()
|
||||||
|
.UsingMethod("GET")
|
||||||
|
.WithPath(new WildcardMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch, "/delay", false, WireMock.Matchers.MatchOperator.Or))
|
||||||
|
)
|
||||||
|
.WithGuid("98fae52e-76df-47d9-876f-2ee32e931006")
|
||||||
|
.RespondWith(Response.Create()
|
||||||
|
.WithDelay(1000)
|
||||||
|
);
|
||||||
|
|
||||||
|
builder
|
||||||
|
.Given(Request.Create()
|
||||||
|
.UsingMethod("GET")
|
||||||
|
.WithPath(new WildcardMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch, "/random-delay", false, WireMock.Matchers.MatchOperator.Or))
|
||||||
|
)
|
||||||
|
.WithGuid("98fae52e-76df-47d9-876f-2ee32e931007")
|
||||||
|
.RespondWith(Response.Create()
|
||||||
|
.WithRandomDelay(1234, 60000)
|
||||||
|
);
|
||||||
|
|
||||||
|
builder
|
||||||
|
.Given(Request.Create()
|
||||||
|
.UsingMethod("GET")
|
||||||
|
.WithPath(new WildcardMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch, "/prob", false, WireMock.Matchers.MatchOperator.Or))
|
||||||
|
)
|
||||||
|
.WithGuid("98fae52e-76df-47d9-876f-2ee32e931008")
|
||||||
|
.WithProbability(0.1)
|
||||||
|
.RespondWith(Response.Create()
|
||||||
|
.WithStatusCode(300)
|
||||||
|
);
|
||||||
|
|
||||||
|
builder
|
||||||
|
.Given(Request.Create()
|
||||||
|
.UsingMethod("GET")
|
||||||
|
.WithPath(new WildcardMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch, "/prob", false, WireMock.Matchers.MatchOperator.Or))
|
||||||
|
)
|
||||||
|
.WithGuid("98fae52e-76df-47d9-876f-2ee32e931009")
|
||||||
|
.WithProbability(0.9)
|
||||||
|
.RespondWith(Response.Create()
|
||||||
|
.WithStatusCode(201)
|
||||||
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -78,3 +78,45 @@ server
|
|||||||
.WithBody("Buy milk")
|
.WithBody("Buy milk")
|
||||||
);
|
);
|
||||||
|
|
||||||
|
server
|
||||||
|
.Given(Request.Create()
|
||||||
|
.UsingMethod("GET")
|
||||||
|
.WithPath(new WildcardMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch, "/delay", false, WireMock.Matchers.MatchOperator.Or))
|
||||||
|
)
|
||||||
|
.WithGuid("98fae52e-76df-47d9-876f-2ee32e931006")
|
||||||
|
.RespondWith(Response.Create()
|
||||||
|
.WithDelay(1000)
|
||||||
|
);
|
||||||
|
|
||||||
|
server
|
||||||
|
.Given(Request.Create()
|
||||||
|
.UsingMethod("GET")
|
||||||
|
.WithPath(new WildcardMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch, "/random-delay", false, WireMock.Matchers.MatchOperator.Or))
|
||||||
|
)
|
||||||
|
.WithGuid("98fae52e-76df-47d9-876f-2ee32e931007")
|
||||||
|
.RespondWith(Response.Create()
|
||||||
|
.WithRandomDelay(1234, 60000)
|
||||||
|
);
|
||||||
|
|
||||||
|
server
|
||||||
|
.Given(Request.Create()
|
||||||
|
.UsingMethod("GET")
|
||||||
|
.WithPath(new WildcardMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch, "/prob", false, WireMock.Matchers.MatchOperator.Or))
|
||||||
|
)
|
||||||
|
.WithGuid("98fae52e-76df-47d9-876f-2ee32e931008")
|
||||||
|
.WithProbability(0.1)
|
||||||
|
.RespondWith(Response.Create()
|
||||||
|
.WithStatusCode(300)
|
||||||
|
);
|
||||||
|
|
||||||
|
server
|
||||||
|
.Given(Request.Create()
|
||||||
|
.UsingMethod("GET")
|
||||||
|
.WithPath(new WildcardMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch, "/prob", false, WireMock.Matchers.MatchOperator.Or))
|
||||||
|
)
|
||||||
|
.WithGuid("98fae52e-76df-47d9-876f-2ee32e931009")
|
||||||
|
.WithProbability(0.9)
|
||||||
|
.RespondWith(Response.Create()
|
||||||
|
.WithStatusCode(201)
|
||||||
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -186,5 +186,92 @@
|
|||||||
BodyDestination: SameAsSource,
|
BodyDestination: SameAsSource,
|
||||||
Body: Buy milk
|
Body: Buy milk
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Guid: 98fae52e-76df-47d9-876f-2ee32e931006,
|
||||||
|
UpdatedAt: 2023-01-14T15:16:17,
|
||||||
|
Request: {
|
||||||
|
Path: {
|
||||||
|
Matchers: [
|
||||||
|
{
|
||||||
|
Name: WildcardMatcher,
|
||||||
|
Pattern: /delay,
|
||||||
|
IgnoreCase: false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
Methods: [
|
||||||
|
GET
|
||||||
|
]
|
||||||
|
},
|
||||||
|
Response: {
|
||||||
|
Delay: 1000
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Guid: 98fae52e-76df-47d9-876f-2ee32e931007,
|
||||||
|
UpdatedAt: 2023-01-14T15:16:17,
|
||||||
|
Request: {
|
||||||
|
Path: {
|
||||||
|
Matchers: [
|
||||||
|
{
|
||||||
|
Name: WildcardMatcher,
|
||||||
|
Pattern: /random-delay,
|
||||||
|
IgnoreCase: false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
Methods: [
|
||||||
|
GET
|
||||||
|
]
|
||||||
|
},
|
||||||
|
Response: {
|
||||||
|
MinimumRandomDelay: 1234,
|
||||||
|
MaximumRandomDelay: 60000
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Guid: 98fae52e-76df-47d9-876f-2ee32e931008,
|
||||||
|
UpdatedAt: 2023-01-14T15:16:17,
|
||||||
|
Request: {
|
||||||
|
Path: {
|
||||||
|
Matchers: [
|
||||||
|
{
|
||||||
|
Name: WildcardMatcher,
|
||||||
|
Pattern: /prob,
|
||||||
|
IgnoreCase: false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
Methods: [
|
||||||
|
GET
|
||||||
|
]
|
||||||
|
},
|
||||||
|
Response: {
|
||||||
|
StatusCode: 300
|
||||||
|
},
|
||||||
|
Probability: 0.1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Guid: 98fae52e-76df-47d9-876f-2ee32e931009,
|
||||||
|
UpdatedAt: 2023-01-14T15:16:17,
|
||||||
|
Request: {
|
||||||
|
Path: {
|
||||||
|
Matchers: [
|
||||||
|
{
|
||||||
|
Name: WildcardMatcher,
|
||||||
|
Pattern: /prob,
|
||||||
|
IgnoreCase: false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
Methods: [
|
||||||
|
GET
|
||||||
|
]
|
||||||
|
},
|
||||||
|
Response: {
|
||||||
|
StatusCode: 201
|
||||||
|
},
|
||||||
|
Probability: 0.9
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#if !(NET452 || NET461 || NETCOREAPP3_1)
|
#if !(NET452 || NET461 || NETCOREAPP3_1)
|
||||||
using System;
|
using System;
|
||||||
|
using System.Net;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Moq;
|
using Moq;
|
||||||
using VerifyTests;
|
using VerifyTests;
|
||||||
@@ -118,6 +119,35 @@ public class MappingBuilderTests
|
|||||||
.RespondWith(Response.Create()
|
.RespondWith(Response.Create()
|
||||||
.WithBody("Buy milk"));
|
.WithBody("Buy milk"));
|
||||||
|
|
||||||
|
_sut.Given(Request.Create()
|
||||||
|
.WithPath("/delay")
|
||||||
|
.UsingGet()
|
||||||
|
).RespondWith(Response.Create()
|
||||||
|
.WithDelay(1000)
|
||||||
|
);
|
||||||
|
|
||||||
|
_sut.Given(Request.Create()
|
||||||
|
.WithPath("/random-delay")
|
||||||
|
.UsingGet()
|
||||||
|
).RespondWith(Response.Create()
|
||||||
|
.WithRandomDelay(1234)
|
||||||
|
);
|
||||||
|
|
||||||
|
_sut.Given(Request.Create()
|
||||||
|
.WithPath("/prob")
|
||||||
|
.UsingGet()
|
||||||
|
).WithProbability(0.1)
|
||||||
|
.RespondWith(Response.Create()
|
||||||
|
.WithStatusCode(HttpStatusCode.Ambiguous)
|
||||||
|
);
|
||||||
|
_sut.Given(Request.Create()
|
||||||
|
.WithPath("/prob")
|
||||||
|
.UsingGet()
|
||||||
|
).WithProbability(0.9)
|
||||||
|
.RespondWith(Response.Create()
|
||||||
|
.WithStatusCode(HttpStatusCode.Created)
|
||||||
|
);
|
||||||
|
|
||||||
_numMappings = _sut.GetMappings().Length;
|
_numMappings = _sut.GetMappings().Length;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -9,7 +9,6 @@ using WireMock.Matchers.Request;
|
|||||||
using WireMock.Models;
|
using WireMock.Models;
|
||||||
using WireMock.Owin;
|
using WireMock.Owin;
|
||||||
using WireMock.Services;
|
using WireMock.Services;
|
||||||
using WireMock.Util;
|
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace WireMock.Net.Tests.Owin;
|
namespace WireMock.Net.Tests.Owin;
|
||||||
@@ -26,7 +25,7 @@ public class MappingMatcherTests
|
|||||||
_optionsMock = new Mock<IWireMockMiddlewareOptions>();
|
_optionsMock = new Mock<IWireMockMiddlewareOptions>();
|
||||||
_optionsMock.SetupAllProperties();
|
_optionsMock.SetupAllProperties();
|
||||||
_optionsMock.Setup(o => o.Mappings).Returns(new ConcurrentDictionary<Guid, IMapping>());
|
_optionsMock.Setup(o => o.Mappings).Returns(new ConcurrentDictionary<Guid, IMapping>());
|
||||||
_optionsMock.Setup(o => o.LogEntries).Returns(new ConcurrentObservableCollection<LogEntry>());
|
_optionsMock.Setup(o => o.LogEntries).Returns([]);
|
||||||
_optionsMock.Setup(o => o.Scenarios).Returns(new ConcurrentDictionary<string, ScenarioState>());
|
_optionsMock.Setup(o => o.Scenarios).Returns(new ConcurrentDictionary<string, ScenarioState>());
|
||||||
|
|
||||||
var loggerMock = new Mock<IWireMockLogger>();
|
var loggerMock = new Mock<IWireMockLogger>();
|
||||||
@@ -35,7 +34,7 @@ public class MappingMatcherTests
|
|||||||
_optionsMock.Setup(o => o.Logger).Returns(loggerMock.Object);
|
_optionsMock.Setup(o => o.Logger).Returns(loggerMock.Object);
|
||||||
|
|
||||||
_randomizerDoubleBetween0And1Mock = new Mock<IRandomizerDoubleBetween0And1>();
|
_randomizerDoubleBetween0And1Mock = new Mock<IRandomizerDoubleBetween0And1>();
|
||||||
_randomizerDoubleBetween0And1Mock.Setup(r => r.Generate()).Returns(0.0);
|
_randomizerDoubleBetween0And1Mock.Setup(r => r.Generate()).Returns(0.5);
|
||||||
|
|
||||||
_sut = new MappingMatcher(_optionsMock.Object, _randomizerDoubleBetween0And1Mock.Object);
|
_sut = new MappingMatcher(_optionsMock.Object, _randomizerDoubleBetween0And1Mock.Object);
|
||||||
}
|
}
|
||||||
@@ -84,8 +83,8 @@ public class MappingMatcherTests
|
|||||||
var guid2 = Guid.Parse("00000000-0000-0000-0000-000000000002");
|
var guid2 = Guid.Parse("00000000-0000-0000-0000-000000000002");
|
||||||
var mappings = InitMappings
|
var mappings = InitMappings
|
||||||
(
|
(
|
||||||
(guid1, new[] { 0.1 }, null),
|
(guid1, [0.1], null),
|
||||||
(guid2, new[] { 1.0 }, null)
|
(guid2, [1.0], null)
|
||||||
);
|
);
|
||||||
_optionsMock.Setup(o => o.Mappings).Returns(mappings);
|
_optionsMock.Setup(o => o.Mappings).Returns(mappings);
|
||||||
|
|
||||||
@@ -112,8 +111,8 @@ public class MappingMatcherTests
|
|||||||
var guid2 = Guid.Parse("00000000-0000-0000-0000-000000000002");
|
var guid2 = Guid.Parse("00000000-0000-0000-0000-000000000002");
|
||||||
var mappings = InitMappings
|
var mappings = InitMappings
|
||||||
(
|
(
|
||||||
(guid1, new[] { 0.1 }, null),
|
(guid1, [0.1], null),
|
||||||
(guid2, new[] { 0.9 }, null)
|
(guid2, [0.9], null)
|
||||||
);
|
);
|
||||||
_optionsMock.Setup(o => o.Mappings).Returns(mappings);
|
_optionsMock.Setup(o => o.Mappings).Returns(mappings);
|
||||||
|
|
||||||
@@ -139,8 +138,8 @@ public class MappingMatcherTests
|
|||||||
|
|
||||||
_optionsMock.SetupGet(o => o.AllowPartialMapping).Returns(true);
|
_optionsMock.SetupGet(o => o.AllowPartialMapping).Returns(true);
|
||||||
var mappings = InitMappings(
|
var mappings = InitMappings(
|
||||||
(guid1, new[] { 0.1 }, null),
|
(guid1, [0.1], null),
|
||||||
(guid2, new[] { 0.9 }, null)
|
(guid2, [0.9], null)
|
||||||
);
|
);
|
||||||
_optionsMock.Setup(o => o.Mappings).Returns(mappings);
|
_optionsMock.Setup(o => o.Mappings).Returns(mappings);
|
||||||
|
|
||||||
@@ -166,8 +165,8 @@ public class MappingMatcherTests
|
|||||||
var guid1 = Guid.Parse("00000000-0000-0000-0000-000000000001");
|
var guid1 = Guid.Parse("00000000-0000-0000-0000-000000000001");
|
||||||
var guid2 = Guid.Parse("00000000-0000-0000-0000-000000000002");
|
var guid2 = Guid.Parse("00000000-0000-0000-0000-000000000002");
|
||||||
var mappings = InitMappings(
|
var mappings = InitMappings(
|
||||||
(guid1, new[] { 1.0 }, null),
|
(guid1, [1.0], null),
|
||||||
(guid2, new[] { 1.0, 1.0 }, null)
|
(guid2, [1.0, 1.0], null)
|
||||||
);
|
);
|
||||||
_optionsMock.Setup(o => o.Mappings).Returns(mappings);
|
_optionsMock.Setup(o => o.Mappings).Returns(mappings);
|
||||||
|
|
||||||
@@ -187,15 +186,15 @@ public class MappingMatcherTests
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void MappingMatcher_FindBestMatch_WhenProbabilityFailsFirst_ShouldReturnSecondMatch()
|
public void MappingMatcher_FindBestMatch_WhenProbabilityDoesNotMatch_ShouldReturnNormalMatch()
|
||||||
{
|
{
|
||||||
// Assign
|
// Assign
|
||||||
var guid1 = Guid.Parse("00000000-0000-0000-0000-000000000001");
|
var withProbability = Guid.Parse("00000000-0000-0000-0000-000000000001");
|
||||||
var guid2 = Guid.Parse("00000000-0000-0000-0000-000000000002");
|
var noProbability = Guid.Parse("00000000-0000-0000-0000-000000000002");
|
||||||
var mappings = InitMappings
|
var mappings = InitMappings
|
||||||
(
|
(
|
||||||
(guid1, new[] { 1.0 }, 1.0),
|
(withProbability, [1.0], 0.4),
|
||||||
(guid2, new[] { 1.0 }, null)
|
(noProbability, [1.0], null)
|
||||||
);
|
);
|
||||||
_optionsMock.Setup(o => o.Mappings).Returns(mappings);
|
_optionsMock.Setup(o => o.Mappings).Returns(mappings);
|
||||||
|
|
||||||
@@ -206,8 +205,30 @@ public class MappingMatcherTests
|
|||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
result.Match.Should().NotBeNull();
|
result.Match.Should().NotBeNull();
|
||||||
result.Match!.Mapping.Guid.Should().Be(guid2);
|
result.Match!.Mapping.Guid.Should().Be(noProbability);
|
||||||
result.Match.RequestMatchResult.AverageTotalScore.Should().Be(1.0);
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void MappingMatcher_FindBestMatch_WhenProbabilityDoesMatch_ShouldReturnProbabilityMatch()
|
||||||
|
{
|
||||||
|
// Assign
|
||||||
|
var withProbability = Guid.Parse("00000000-0000-0000-0000-000000000001");
|
||||||
|
var noProbability = Guid.Parse("00000000-0000-0000-0000-000000000002");
|
||||||
|
var mappings = InitMappings
|
||||||
|
(
|
||||||
|
(withProbability, [1.0], 0.6),
|
||||||
|
(noProbability, [1.0], null)
|
||||||
|
);
|
||||||
|
_optionsMock.Setup(o => o.Mappings).Returns(mappings);
|
||||||
|
|
||||||
|
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "GET", "::1");
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = _sut.FindBestMatch(request);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Match.Should().NotBeNull();
|
||||||
|
result.Match!.Mapping.Guid.Should().Be(withProbability);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ConcurrentDictionary<Guid, IMapping> InitMappings(params (Guid guid, double[] scores, double? probability)[] matches)
|
private static ConcurrentDictionary<Guid, IMapping> InitMappings(params (Guid guid, double[] scores, double? probability)[] matches)
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
// Copyright © WireMock.Net
|
// Copyright © WireMock.Net
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using FluentAssertions;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using FluentAssertions;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
using NFluent;
|
using NFluent;
|
||||||
using WireMock.Matchers;
|
using WireMock.Matchers;
|
||||||
using WireMock.Matchers.Request;
|
using WireMock.Matchers.Request;
|
||||||
@@ -72,15 +73,17 @@ public class RequestBuilderWithBodyTests
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Request_WithBody_FuncJson()
|
public void Request_WithBody_FuncObject()
|
||||||
{
|
{
|
||||||
// Assign
|
// Assign
|
||||||
var requestBuilder = Request.Create().UsingAnyMethod().WithBody(b => b != null);
|
var requestBuilder = Request.Create()
|
||||||
|
.UsingAnyMethod()
|
||||||
|
.WithBody(b => b != null);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var body = new BodyData
|
var body = new BodyData
|
||||||
{
|
{
|
||||||
BodyAsJson = 123,
|
BodyAsJson = JObject.Parse("""{ "X": 123, "Y": "a" }"""),
|
||||||
DetectedBodyType = BodyType.Json
|
DetectedBodyType = BodyType.Json
|
||||||
};
|
};
|
||||||
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "POST", ClientIp, body);
|
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "POST", ClientIp, body);
|
||||||
@@ -90,6 +93,57 @@ public class RequestBuilderWithBodyTests
|
|||||||
Check.That(requestBuilder.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0);
|
Check.That(requestBuilder.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("""{ "X": 123, "Y": "a" }""", 1.0)]
|
||||||
|
[InlineData("""{ "X": 123, "Y": "b" }""", 0.0)]
|
||||||
|
public void Request_WithBodyAsType_Func(string json, double expected)
|
||||||
|
{
|
||||||
|
// Assign
|
||||||
|
var requestBuilder = Request.Create()
|
||||||
|
.UsingAnyMethod()
|
||||||
|
.WithBodyAsType<FuncType>(ft => ft != null && ft.X == 123 && ft.Y == "a");
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var body = new BodyData
|
||||||
|
{
|
||||||
|
BodyAsJson = JObject.Parse(json),
|
||||||
|
DetectedBodyType = BodyType.Json
|
||||||
|
};
|
||||||
|
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "POST", ClientIp, body);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var requestMatchResult = new RequestMatchResult();
|
||||||
|
Check.That(requestBuilder.GetMatchingScore(request, requestMatchResult)).IsEqualTo(expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Request_WithBodyAsType_Func_IncorrectType()
|
||||||
|
{
|
||||||
|
// Assign
|
||||||
|
var requestBuilder = Request.Create()
|
||||||
|
.UsingAnyMethod()
|
||||||
|
.WithBodyAsType<Version>(ft => ft != null);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var body = new BodyData
|
||||||
|
{
|
||||||
|
BodyAsJson = JObject.Parse("""{ "X": 123, "Y": "a" }"""),
|
||||||
|
DetectedBodyType = BodyType.Json
|
||||||
|
};
|
||||||
|
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "POST", ClientIp, body);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var requestMatchResult = new RequestMatchResult();
|
||||||
|
Check.That(requestBuilder.GetMatchingScore(request, requestMatchResult)).IsEqualTo(0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class FuncType
|
||||||
|
{
|
||||||
|
public int X { get; set; } = 42;
|
||||||
|
|
||||||
|
public string Y { get; set; } = string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Request_WithBody_FuncFormUrlEncoded()
|
public void Request_WithBody_FuncFormUrlEncoded()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ using System;
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using FluentAssertions;
|
using FluentAssertions;
|
||||||
|
using JsonConverter.Newtonsoft.Json;
|
||||||
using Moq;
|
using Moq;
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
using NFluent;
|
using NFluent;
|
||||||
@@ -316,12 +317,11 @@ public class ResponseWithBodyTests
|
|||||||
var response = await responseBuilder.ProvideResponseAsync(_mappingMock.Object, request1, _settings).ConfigureAwait(false);
|
var response = await responseBuilder.ProvideResponseAsync(_mappingMock.Object, request1, _settings).ConfigureAwait(false);
|
||||||
|
|
||||||
Check.That(response.Message.StatusCode).IsEqualTo(200);
|
Check.That(response.Message.StatusCode).IsEqualTo(200);
|
||||||
Check.That(response.Message.BodyData.BodyAsString).Contains("File deleted.");
|
Check.That(response.Message.BodyData?.BodyAsString).Contains("File deleted.");
|
||||||
}
|
}
|
||||||
|
|
||||||
#if !(NET451 || NET452)
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task Response_ProvideResponse_WithBody_IJsonConverter_SystemTextJson()
|
public async Task Response_ProvideResponse_WithBody_NewtonsoftJsonConverter()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var requestBody = new BodyData
|
var requestBody = new BodyData
|
||||||
@@ -331,13 +331,34 @@ public class ResponseWithBodyTests
|
|||||||
};
|
};
|
||||||
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "POST", ClientIp, requestBody);
|
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "POST", ClientIp, requestBody);
|
||||||
|
|
||||||
var responseBuilder = Response.Create().WithBody(new { foo = "bar", n = 42 }, new JsonConverter.System.Text.Json.SystemTextJsonConverter());
|
var responseBuilder = Response.Create().WithBody(new { foo = "< > & ' 😀 👍 ❤️", n = 42 }, new NewtonsoftJsonConverter());
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var response = await responseBuilder.ProvideResponseAsync(_mappingMock.Object, request, _settings).ConfigureAwait(false);
|
var response = await responseBuilder.ProvideResponseAsync(_mappingMock.Object, request, _settings).ConfigureAwait(false);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
response.Message.BodyData!.BodyAsString.Should().Be(@"{""foo"":""bar"",""n"":42}");
|
response.Message.BodyData!.BodyAsString.Should().Be("""{"foo":"< > & ' 😀 👍 ❤️","n":42}""");
|
||||||
|
}
|
||||||
|
|
||||||
|
#if !(NET451 || NET452 || NET461)
|
||||||
|
[Fact]
|
||||||
|
public async Task Response_ProvideResponse_WithBody_SystemTextJsonConverter()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var requestBody = new BodyData
|
||||||
|
{
|
||||||
|
DetectedBodyType = BodyType.String,
|
||||||
|
BodyAsString = "abc"
|
||||||
|
};
|
||||||
|
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "POST", ClientIp, requestBody);
|
||||||
|
|
||||||
|
var responseBuilder = Response.Create().WithBody(new { foo = "< > & ' 😀 👍 ❤️", n = 42 }, new JsonConverter.System.Text.Json.SystemTextJsonConverter());
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var response = await responseBuilder.ProvideResponseAsync(_mappingMock.Object, request, _settings).ConfigureAwait(false);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
response.Message.BodyData!.BodyAsString.Should().Be("""{"foo":"\u003C \u003E \u0026 \u0027 \uD83D\uDE00 \uD83D\uDC4D \u2764\uFE0F","n":42}""");
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@@ -109,7 +109,7 @@ public partial class MappingConverterTests
|
|||||||
var guid = new Guid("8e7b9ab7-e18e-4502-8bc9-11e6679811cc");
|
var guid = new Guid("8e7b9ab7-e18e-4502-8bc9-11e6679811cc");
|
||||||
var request = Request.Create()
|
var request = Request.Create()
|
||||||
.UsingGet()
|
.UsingGet()
|
||||||
.WithPath("test_path")
|
.WithPath("/test_path")
|
||||||
.WithParam("q", "42")
|
.WithParam("q", "42")
|
||||||
.WithClientIP("112.123.100.99")
|
.WithClientIP("112.123.100.99")
|
||||||
.WithHeader("h-key", "h-value")
|
.WithHeader("h-key", "h-value")
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
builder
|
builder
|
||||||
.Given(Request.Create()
|
.Given(Request.Create()
|
||||||
.UsingMethod("GET")
|
.UsingMethod("GET")
|
||||||
.WithPath(new WildcardMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch, "test_path", false, WireMock.Matchers.MatchOperator.Or))
|
.WithPath(new WildcardMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch, "/test_path", false, WireMock.Matchers.MatchOperator.Or))
|
||||||
.WithParam("q", new ExactMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch, false, WireMock.Matchers.MatchOperator.And, "42"))
|
.WithParam("q", new ExactMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch, false, WireMock.Matchers.MatchOperator.And, "42"))
|
||||||
.WithClientIP("112.123.100.99")
|
.WithClientIP("112.123.100.99")
|
||||||
.WithHeader("h-key", "h-value", true)
|
.WithHeader("h-key", "h-value", true)
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
builder
|
builder
|
||||||
.Given(Request.Create()
|
.Given(Request.Create()
|
||||||
.UsingMethod("GET")
|
.UsingMethod("GET")
|
||||||
.WithPath(new WildcardMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch, "test_path", false, WireMock.Matchers.MatchOperator.Or))
|
.WithPath(new WildcardMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch, "/test_path", false, WireMock.Matchers.MatchOperator.Or))
|
||||||
.WithParam("q", new ExactMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch, false, WireMock.Matchers.MatchOperator.And, "42"))
|
.WithParam("q", new ExactMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch, false, WireMock.Matchers.MatchOperator.And, "42"))
|
||||||
.WithClientIP("112.123.100.99")
|
.WithClientIP("112.123.100.99")
|
||||||
.WithHeader("h-key", "h-value", true)
|
.WithHeader("h-key", "h-value", true)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
server
|
server
|
||||||
.Given(Request.Create()
|
.Given(Request.Create()
|
||||||
.UsingMethod("GET")
|
.UsingMethod("GET")
|
||||||
.WithPath(new WildcardMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch, "test_path", false, WireMock.Matchers.MatchOperator.Or))
|
.WithPath(new WildcardMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch, "/test_path", false, WireMock.Matchers.MatchOperator.Or))
|
||||||
.WithParam("q", new ExactMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch, false, WireMock.Matchers.MatchOperator.And, "42"))
|
.WithParam("q", new ExactMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch, false, WireMock.Matchers.MatchOperator.And, "42"))
|
||||||
.WithClientIP("112.123.100.99")
|
.WithClientIP("112.123.100.99")
|
||||||
.WithHeader("h-key", "h-value", true)
|
.WithHeader("h-key", "h-value", true)
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
server
|
server
|
||||||
.Given(Request.Create()
|
.Given(Request.Create()
|
||||||
.UsingMethod("GET")
|
.UsingMethod("GET")
|
||||||
.WithPath(new WildcardMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch, "test_path", false, WireMock.Matchers.MatchOperator.Or))
|
.WithPath(new WildcardMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch, "/test_path", false, WireMock.Matchers.MatchOperator.Or))
|
||||||
.WithParam("q", new ExactMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch, false, WireMock.Matchers.MatchOperator.And, "42"))
|
.WithParam("q", new ExactMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch, false, WireMock.Matchers.MatchOperator.And, "42"))
|
||||||
.WithClientIP("112.123.100.99")
|
.WithClientIP("112.123.100.99")
|
||||||
.WithHeader("h-key", "h-value", true)
|
.WithHeader("h-key", "h-value", true)
|
||||||
|
|||||||
326
test/WireMock.Net.Tests/Serialization/MappingSerializerTests.cs
Normal file
326
test/WireMock.Net.Tests/Serialization/MappingSerializerTests.cs
Normal file
@@ -0,0 +1,326 @@
|
|||||||
|
// Copyright © WireMock.Net
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using FluentAssertions;
|
||||||
|
using JsonConverter.Newtonsoft.Json;
|
||||||
|
using WireMock.Admin.Mappings;
|
||||||
|
using WireMock.Serialization;
|
||||||
|
using Xunit;
|
||||||
|
#if NET8_0_OR_GREATER
|
||||||
|
using JsonConverter.System.Text.Json;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace WireMock.Net.Tests.Serialization;
|
||||||
|
|
||||||
|
public class MappingSerializerTests
|
||||||
|
{
|
||||||
|
private const string SingleMappingJson =
|
||||||
|
"""
|
||||||
|
{
|
||||||
|
"Guid": "12345678-1234-1234-1234-123456789012",
|
||||||
|
"Priority": 1,
|
||||||
|
"Request": {
|
||||||
|
"Path": "/test"
|
||||||
|
},
|
||||||
|
"Response": {
|
||||||
|
"StatusCode": 200
|
||||||
|
}
|
||||||
|
}
|
||||||
|
""";
|
||||||
|
|
||||||
|
private const string ArrayMappingJson =
|
||||||
|
"""
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"Guid": "12345678-1234-1234-1234-123456789012",
|
||||||
|
"Priority": 1,
|
||||||
|
"Request": {
|
||||||
|
"Path": "/test1"
|
||||||
|
},
|
||||||
|
"Response": {
|
||||||
|
"StatusCode": 200
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Guid": "87654321-4321-4321-4321-210987654321",
|
||||||
|
"Priority": 2,
|
||||||
|
"Request": {
|
||||||
|
"Path": "/test2"
|
||||||
|
},
|
||||||
|
"Response": {
|
||||||
|
"StatusCode": 404
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
""";
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void MappingSerializer_DeserializeJsonToArray_WithNewtonsoftJson_SingleObject_ShouldReturnArray()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var jsonConverter = new NewtonsoftJsonConverter();
|
||||||
|
var serializer = new MappingSerializer(jsonConverter);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = serializer.DeserializeJsonToArray<MappingModel>(SingleMappingJson);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Should().NotBeNull();
|
||||||
|
result.Should().HaveCount(1);
|
||||||
|
result[0].Guid.Should().Be(Guid.Parse("12345678-1234-1234-1234-123456789012"));
|
||||||
|
result[0].Priority.Should().Be(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void MappingSerializer_DeserializeJsonToArray_WithNewtonsoftJson_Array_ShouldReturnArray()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var jsonConverter = new NewtonsoftJsonConverter();
|
||||||
|
var serializer = new MappingSerializer(jsonConverter);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = serializer.DeserializeJsonToArray<MappingModel>(ArrayMappingJson);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Should().NotBeNull();
|
||||||
|
result.Should().HaveCount(2);
|
||||||
|
result[0].Guid.Should().Be(Guid.Parse("12345678-1234-1234-1234-123456789012"));
|
||||||
|
result[0].Priority.Should().Be(1);
|
||||||
|
result[1].Guid.Should().Be(Guid.Parse("87654321-4321-4321-4321-210987654321"));
|
||||||
|
result[1].Priority.Should().Be(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void MappingSerializer_DeserializeJsonToArray_WithNewtonsoftJson_EmptyArray_ShouldReturnEmptyArray()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var jsonConverter = new NewtonsoftJsonConverter();
|
||||||
|
var serializer = new MappingSerializer(jsonConverter);
|
||||||
|
var emptyArrayJson = "[]";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = serializer.DeserializeJsonToArray<MappingModel>(emptyArrayJson);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Should().NotBeNull();
|
||||||
|
result.Should().BeEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void MappingSerializer_DeserializeJsonToArray_WithNewtonsoftJson_InvalidJson_ShouldThrowException()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var jsonConverter = new NewtonsoftJsonConverter();
|
||||||
|
var serializer = new MappingSerializer(jsonConverter);
|
||||||
|
var invalidJson = "not valid json";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
Action act = () => serializer.DeserializeJsonToArray<MappingModel>(invalidJson);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
act.Should().Throw<Exception>();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void MappingSerializer_DeserializeJsonToArray_WithNewtonsoftJson_ComplexMapping_ShouldDeserializeCorrectly()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var jsonConverter = new NewtonsoftJsonConverter();
|
||||||
|
var serializer = new MappingSerializer(jsonConverter);
|
||||||
|
var complexJson =
|
||||||
|
"""
|
||||||
|
{
|
||||||
|
"Guid": "12345678-1234-1234-1234-123456789012",
|
||||||
|
"Title": "Test Mapping",
|
||||||
|
"Description": "A test mapping",
|
||||||
|
"Priority": 10,
|
||||||
|
"Request": {
|
||||||
|
"Path": "/api/test",
|
||||||
|
"Methods": ["GET", "POST"]
|
||||||
|
},
|
||||||
|
"Response": {
|
||||||
|
"StatusCode": 201,
|
||||||
|
"Body": "Test Response"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
""";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = serializer.DeserializeJsonToArray<MappingModel>(complexJson);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Should().NotBeNull();
|
||||||
|
result.Should().HaveCount(1);
|
||||||
|
result[0].Guid.Should().Be(Guid.Parse("12345678-1234-1234-1234-123456789012"));
|
||||||
|
result[0].Title.Should().Be("Test Mapping");
|
||||||
|
result[0].Description.Should().Be("A test mapping");
|
||||||
|
result[0].Priority.Should().Be(10);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void MappingSerializer_DeserializeJsonToArray_WithNewtonsoftJson_NullValue_ShouldThrowException()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var jsonConverter = new NewtonsoftJsonConverter();
|
||||||
|
var serializer = new MappingSerializer(jsonConverter);
|
||||||
|
var nullJson = "null";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
Action act = () => serializer.DeserializeJsonToArray<MappingModel>(nullJson);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
act.Should().Throw<Exception>();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void MappingSerializer_DeserializeJsonToArray_WithNewtonsoftJson_PrimitiveValue_ShouldThrowInvalidOperationException()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var jsonConverter = new NewtonsoftJsonConverter();
|
||||||
|
var serializer = new MappingSerializer(jsonConverter);
|
||||||
|
var primitiveJson = "\"string value\"";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
Action act = () => serializer.DeserializeJsonToArray<MappingModel>(primitiveJson);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
act.Should().Throw<InvalidOperationException>()
|
||||||
|
.WithMessage("Cannot deserialize the provided value to an array or object.");
|
||||||
|
}
|
||||||
|
|
||||||
|
#if NET8_0_OR_GREATER
|
||||||
|
[Fact]
|
||||||
|
public void MappingSerializer_DeserializeJsonToArray_WithSystemTextJson_SingleObject_ShouldReturnArray()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var jsonConverter = new SystemTextJsonConverter();
|
||||||
|
var serializer = new MappingSerializer(jsonConverter);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = serializer.DeserializeJsonToArray<MappingModel>(SingleMappingJson);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Should().NotBeNull();
|
||||||
|
result.Should().HaveCount(1);
|
||||||
|
result[0].Guid.Should().Be(Guid.Parse("12345678-1234-1234-1234-123456789012"));
|
||||||
|
result[0].Priority.Should().Be(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void MappingSerializer_DeserializeJsonToArray_WithSystemTextJson_Array_ShouldReturnArray()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var jsonConverter = new SystemTextJsonConverter();
|
||||||
|
var serializer = new MappingSerializer(jsonConverter);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = serializer.DeserializeJsonToArray<MappingModel>(ArrayMappingJson);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Should().NotBeNull();
|
||||||
|
result.Should().HaveCount(2);
|
||||||
|
result[0].Guid.Should().Be(Guid.Parse("12345678-1234-1234-1234-123456789012"));
|
||||||
|
result[0].Priority.Should().Be(1);
|
||||||
|
result[1].Guid.Should().Be(Guid.Parse("87654321-4321-4321-4321-210987654321"));
|
||||||
|
result[1].Priority.Should().Be(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void MappingSerializer_DeserializeJsonToArray_WithSystemTextJson_EmptyArray_ShouldReturnEmptyArray()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var jsonConverter = new SystemTextJsonConverter();
|
||||||
|
var serializer = new MappingSerializer(jsonConverter);
|
||||||
|
var emptyArrayJson = "[]";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = serializer.DeserializeJsonToArray<MappingModel>(emptyArrayJson);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Should().NotBeNull();
|
||||||
|
result.Should().BeEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void MappingSerializer_DeserializeJsonToArray_WithSystemTextJson_InvalidJson_ShouldThrowException()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var jsonConverter = new SystemTextJsonConverter();
|
||||||
|
var serializer = new MappingSerializer(jsonConverter);
|
||||||
|
var invalidJson = "not valid json";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
Action act = () => serializer.DeserializeJsonToArray<MappingModel>(invalidJson);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
act.Should().Throw<Exception>();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void MappingSerializer_DeserializeJsonToArray_WithSystemTextJson_ComplexMapping_ShouldDeserializeCorrectly()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var jsonConverter = new SystemTextJsonConverter();
|
||||||
|
var serializer = new MappingSerializer(jsonConverter);
|
||||||
|
var complexJson =
|
||||||
|
"""
|
||||||
|
{
|
||||||
|
"Guid": "12345678-1234-1234-1234-123456789012",
|
||||||
|
"Title": "Test Mapping",
|
||||||
|
"Description": "A test mapping",
|
||||||
|
"Priority": 10,
|
||||||
|
"Request": {
|
||||||
|
"Path": "/api/test",
|
||||||
|
"Methods": ["GET", "POST"]
|
||||||
|
},
|
||||||
|
"Response": {
|
||||||
|
"StatusCode": 201,
|
||||||
|
"Body": "Test Response"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
""";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = serializer.DeserializeJsonToArray<MappingModel>(complexJson);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Should().NotBeNull();
|
||||||
|
result.Should().HaveCount(1);
|
||||||
|
result[0].Guid.Should().Be(Guid.Parse("12345678-1234-1234-1234-123456789012"));
|
||||||
|
result[0].Title.Should().Be("Test Mapping");
|
||||||
|
result[0].Description.Should().Be("A test mapping");
|
||||||
|
result[0].Priority.Should().Be(10);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void MappingSerializer_DeserializeJsonToArray_WithSystemTextJson_NullValue_ShouldThrowException()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var jsonConverter = new SystemTextJsonConverter();
|
||||||
|
var serializer = new MappingSerializer(jsonConverter);
|
||||||
|
var nullJson = "null";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
Action act = () => serializer.DeserializeJsonToArray<MappingModel>(nullJson);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
act.Should().Throw<Exception>();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void MappingSerializer_DeserializeJsonToArray_WithSystemTextJson_PrimitiveValue_ShouldThrowInvalidOperationException()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var jsonConverter = new SystemTextJsonConverter();
|
||||||
|
var serializer = new MappingSerializer(jsonConverter);
|
||||||
|
var primitiveJson = "\"string value\"";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
Action act = () => serializer.DeserializeJsonToArray<MappingModel>(primitiveJson);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
act.Should().Throw<InvalidOperationException>()
|
||||||
|
.WithMessage("Cannot deserialize the provided value to an array or object.");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
@@ -9,7 +9,7 @@
|
|||||||
Matchers: [
|
Matchers: [
|
||||||
{
|
{
|
||||||
Name: WildcardMatcher,
|
Name: WildcardMatcher,
|
||||||
Pattern: x,
|
Pattern: /x,
|
||||||
IgnoreCase: false
|
IgnoreCase: false
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ public class ProxyMappingConverterTests
|
|||||||
|
|
||||||
var request = Request.Create()
|
var request = Request.Create()
|
||||||
.UsingPost()
|
.UsingPost()
|
||||||
.WithPath("x")
|
.WithPath("/x")
|
||||||
.WithParam("p1", "p1-v")
|
.WithParam("p1", "p1-v")
|
||||||
.WithParam("p2", "p2-v")
|
.WithParam("p2", "p2-v")
|
||||||
.WithHeader("Content-Type", new ContentTypeMatcher("text/plain"))
|
.WithHeader("Content-Type", new ContentTypeMatcher("text/plain"))
|
||||||
|
|||||||
161
test/WireMock.Net.Tests/Testcontainers/CombineUtilsTests.cs
Normal file
161
test/WireMock.Net.Tests/Testcontainers/CombineUtilsTests.cs
Normal file
@@ -0,0 +1,161 @@
|
|||||||
|
// Copyright © WireMock.Net
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using FluentAssertions;
|
||||||
|
using WireMock.Net.Testcontainers.Utils;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace WireMock.Net.Tests.Testcontainers;
|
||||||
|
|
||||||
|
public class CombineUtilsTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void Combine_Lists_WithBothEmpty_ReturnsEmptyList()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var oldValue = new List<string>();
|
||||||
|
var newValue = new List<string>();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = CombineUtils.Combine(oldValue, newValue);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Should().BeEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Combine_Lists_WithEmptyOldValue_ReturnsNewValue()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var oldValue = new List<string>();
|
||||||
|
var newValue = new List<string> { "item1", "item2" };
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = CombineUtils.Combine(oldValue, newValue);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Should().Equal("item1", "item2");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Combine_Lists_WithEmptyNewValue_ReturnsOldValue()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var oldValue = new List<string> { "item1", "item2" };
|
||||||
|
var newValue = new List<string>();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = CombineUtils.Combine(oldValue, newValue);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Should().Equal("item1", "item2");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Combine_Lists_WithBothPopulated_ReturnsConcatenatedList()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var oldValue = new List<int> { 1, 2, 3 };
|
||||||
|
var newValue = new List<int> { 4, 5, 6 };
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = CombineUtils.Combine(oldValue, newValue);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Should().Equal(1, 2, 3, 4, 5, 6);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Combine_Lists_WithDuplicates_RemovesDuplicates()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var oldValue = new List<string> { "a", "b", "c" };
|
||||||
|
var newValue = new List<string> { "b", "c", "d" };
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = CombineUtils.Combine(oldValue, newValue);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Should().Equal("a", "b", "c", "d");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Combine_Dictionaries_WithBothEmpty_ReturnsEmptyDictionary()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var oldValue = new Dictionary<string, int>();
|
||||||
|
var newValue = new Dictionary<string, int>();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = CombineUtils.Combine(oldValue, newValue);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Should().BeEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Combine_Dictionaries_WithEmptyOldValue_ReturnsNewValue()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var oldValue = new Dictionary<string, int>();
|
||||||
|
var newValue = new Dictionary<string, int>
|
||||||
|
{
|
||||||
|
{ "key1", 1 },
|
||||||
|
{ "key2", 2 }
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = CombineUtils.Combine(oldValue, newValue);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Should().HaveCount(2);
|
||||||
|
result["key1"].Should().Be(1);
|
||||||
|
result["key2"].Should().Be(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Combine_Dictionaries_WithEmptyNewValue_ReturnsOldValue()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var oldValue = new Dictionary<string, int>
|
||||||
|
{
|
||||||
|
{ "key1", 1 },
|
||||||
|
{ "key2", 2 }
|
||||||
|
};
|
||||||
|
var newValue = new Dictionary<string, int>();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = CombineUtils.Combine(oldValue, newValue);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Should().HaveCount(2);
|
||||||
|
result["key1"].Should().Be(1);
|
||||||
|
result["key2"].Should().Be(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Combine_Dictionaries_WithNoOverlappingKeys_ReturnsMergedDictionary()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var oldValue = new Dictionary<string, string>
|
||||||
|
{
|
||||||
|
{ "key1", "value1" },
|
||||||
|
{ "key2", "value2" }
|
||||||
|
};
|
||||||
|
var newValue = new Dictionary<string, string>
|
||||||
|
{
|
||||||
|
{ "key3", "value3" },
|
||||||
|
{ "key4", "value4" }
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = CombineUtils.Combine(oldValue, newValue);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Should().HaveCount(4);
|
||||||
|
result["key1"].Should().Be("value1");
|
||||||
|
result["key2"].Should().Be("value2");
|
||||||
|
result["key3"].Should().Be("value3");
|
||||||
|
result["key4"].Should().Be("value4");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,6 +7,8 @@ using System.Threading.Tasks;
|
|||||||
using DotNet.Testcontainers.Builders;
|
using DotNet.Testcontainers.Builders;
|
||||||
using FluentAssertions;
|
using FluentAssertions;
|
||||||
using FluentAssertions.Execution;
|
using FluentAssertions.Execution;
|
||||||
|
using Meziantou.Extensions.Logging.Xunit;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
using WireMock.Net.Testcontainers;
|
using WireMock.Net.Testcontainers;
|
||||||
using WireMock.Net.Testcontainers.Utils;
|
using WireMock.Net.Testcontainers.Utils;
|
||||||
using WireMock.Net.Tests.Facts;
|
using WireMock.Net.Tests.Facts;
|
||||||
@@ -15,8 +17,14 @@ using Xunit.Abstractions;
|
|||||||
|
|
||||||
namespace WireMock.Net.Tests.Testcontainers;
|
namespace WireMock.Net.Tests.Testcontainers;
|
||||||
|
|
||||||
public partial class TestcontainersTests(ITestOutputHelper testOutputHelper)
|
public class TestcontainersTests(ITestOutputHelper testOutputHelper)
|
||||||
{
|
{
|
||||||
|
private readonly ILogger _logger = new XUnitLogger(testOutputHelper, new LoggerExternalScopeProvider(), nameof(TestcontainersTests), new XUnitLoggerOptions
|
||||||
|
{
|
||||||
|
IncludeCategory = true,
|
||||||
|
TimestampFormat = "yyy-MM-dd HH:mm:ss.fff"
|
||||||
|
});
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task WireMockContainer_Build_And_StartAsync_and_StopAsync()
|
public async Task WireMockContainer_Build_And_StartAsync_and_StopAsync()
|
||||||
{
|
{
|
||||||
@@ -24,6 +32,7 @@ public partial class TestcontainersTests(ITestOutputHelper testOutputHelper)
|
|||||||
var adminUsername = $"username_{Guid.NewGuid()}";
|
var adminUsername = $"username_{Guid.NewGuid()}";
|
||||||
var adminPassword = $"password_{Guid.NewGuid()}";
|
var adminPassword = $"password_{Guid.NewGuid()}";
|
||||||
var wireMockContainer = new WireMockContainerBuilder()
|
var wireMockContainer = new WireMockContainerBuilder()
|
||||||
|
.WithLogger(_logger)
|
||||||
.WithAdminUserNameAndPassword(adminUsername, adminPassword)
|
.WithAdminUserNameAndPassword(adminUsername, adminPassword)
|
||||||
.WithAutoRemove(true)
|
.WithAutoRemove(true)
|
||||||
.WithCleanUp(true)
|
.WithCleanUp(true)
|
||||||
@@ -43,6 +52,7 @@ public partial class TestcontainersTests(ITestOutputHelper testOutputHelper)
|
|||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
var wireMockContainer = new WireMockContainerBuilder()
|
var wireMockContainer = new WireMockContainerBuilder()
|
||||||
|
.WithLogger(_logger)
|
||||||
.WithNetwork(dummyNetwork)
|
.WithNetwork(dummyNetwork)
|
||||||
.WithWatchStaticMappings(true)
|
.WithWatchStaticMappings(true)
|
||||||
.Build();
|
.Build();
|
||||||
@@ -58,6 +68,7 @@ public partial class TestcontainersTests(ITestOutputHelper testOutputHelper)
|
|||||||
var adminUsername = $"username_{Guid.NewGuid()}";
|
var adminUsername = $"username_{Guid.NewGuid()}";
|
||||||
var adminPassword = $"password_{Guid.NewGuid()}";
|
var adminPassword = $"password_{Guid.NewGuid()}";
|
||||||
var wireMockContainerBuilder = new WireMockContainerBuilder()
|
var wireMockContainerBuilder = new WireMockContainerBuilder()
|
||||||
|
.WithLogger(_logger)
|
||||||
.WithAdminUserNameAndPassword(adminUsername, adminPassword);
|
.WithAdminUserNameAndPassword(adminUsername, adminPassword);
|
||||||
|
|
||||||
var imageOS = await TestcontainersUtils.GetImageOSAsync.Value;
|
var imageOS = await TestcontainersUtils.GetImageOSAsync.Value;
|
||||||
@@ -83,6 +94,7 @@ public partial class TestcontainersTests(ITestOutputHelper testOutputHelper)
|
|||||||
var adminUsername = $"username_{Guid.NewGuid()}";
|
var adminUsername = $"username_{Guid.NewGuid()}";
|
||||||
var adminPassword = $"password_{Guid.NewGuid()}";
|
var adminPassword = $"password_{Guid.NewGuid()}";
|
||||||
var wireMockContainerBuilder = new WireMockContainerBuilder()
|
var wireMockContainerBuilder = new WireMockContainerBuilder()
|
||||||
|
.WithLogger(_logger)
|
||||||
.WithAdminUserNameAndPassword(adminUsername, adminPassword);
|
.WithAdminUserNameAndPassword(adminUsername, adminPassword);
|
||||||
|
|
||||||
var imageOS = await TestcontainersUtils.GetImageOSAsync.Value;
|
var imageOS = await TestcontainersUtils.GetImageOSAsync.Value;
|
||||||
@@ -92,7 +104,7 @@ public partial class TestcontainersTests(ITestOutputHelper testOutputHelper)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
wireMockContainerBuilder = wireMockContainerBuilder.WithImage("sheyenrath/wiremock.net");
|
wireMockContainerBuilder = wireMockContainerBuilder.WithImage("sheyenrath/wiremock.net-alpine");
|
||||||
}
|
}
|
||||||
|
|
||||||
var wireMockContainer = wireMockContainerBuilder.Build();
|
var wireMockContainer = wireMockContainerBuilder.Build();
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
#if NET6_0_OR_GREATER
|
#if NET6_0_OR_GREATER
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
@@ -10,30 +11,45 @@ using FluentAssertions;
|
|||||||
using FluentAssertions.Execution;
|
using FluentAssertions.Execution;
|
||||||
using Greet;
|
using Greet;
|
||||||
using Grpc.Net.Client;
|
using Grpc.Net.Client;
|
||||||
|
using Meziantou.Extensions.Logging.Xunit;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
using WireMock.Constants;
|
using WireMock.Constants;
|
||||||
using WireMock.Net.Testcontainers;
|
using WireMock.Net.Testcontainers;
|
||||||
|
using WireMock.Util;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
using Xunit.Abstractions;
|
||||||
|
|
||||||
namespace WireMock.Net.Tests.Testcontainers;
|
namespace WireMock.Net.Tests.Testcontainers;
|
||||||
|
|
||||||
public partial class TestcontainersTests
|
[Collection("Grpc")]
|
||||||
|
public class TestcontainersTestsGrpc(ITestOutputHelper testOutputHelper)
|
||||||
{
|
{
|
||||||
|
private readonly ILogger _logger = new XUnitLogger(testOutputHelper, new LoggerExternalScopeProvider(), nameof(TestcontainersTestsGrpc), new XUnitLoggerOptions
|
||||||
|
{
|
||||||
|
IncludeCategory = true,
|
||||||
|
TimestampFormat = "yyy-MM-dd HH:mm:ss.fff"
|
||||||
|
});
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task WireMockContainer_Build_Grpc_TestPortsAndUrls1()
|
public async Task WireMockContainer_Build_Grpc_TestPortsAndUrls1()
|
||||||
{
|
{
|
||||||
// Act
|
// Arrange
|
||||||
var adminUsername = $"username_{Guid.NewGuid()}";
|
var adminUsername = $"username_{Guid.NewGuid()}";
|
||||||
var adminPassword = $"password_{Guid.NewGuid()}";
|
var adminPassword = $"password_{Guid.NewGuid()}";
|
||||||
|
var port = PortUtils.FindFreeTcpPort();
|
||||||
|
|
||||||
|
// Act
|
||||||
var wireMockContainer = new WireMockContainerBuilder()
|
var wireMockContainer = new WireMockContainerBuilder()
|
||||||
|
.WithLogger(_logger)
|
||||||
.WithAdminUserNameAndPassword(adminUsername, adminPassword)
|
.WithAdminUserNameAndPassword(adminUsername, adminPassword)
|
||||||
.WithCommand("--UseHttp2")
|
.WithCommand("--UseHttp2")
|
||||||
.WithCommand("--Urls", "http://*:80 grpc://*:9090")
|
.WithCommand("--Urls", $"http://*:80 grpc://*:{port}")
|
||||||
.WithPortBinding(9090, true)
|
.WithPortBinding(port, true)
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await wireMockContainer.StartAsync().ConfigureAwait(false);
|
await wireMockContainer.StartAsync();
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
using (new AssertionScope())
|
using (new AssertionScope())
|
||||||
@@ -53,10 +69,10 @@ public partial class TestcontainersTests
|
|||||||
var httpUrl = wireMockContainer.GetMappedPublicUrl(80);
|
var httpUrl = wireMockContainer.GetMappedPublicUrl(80);
|
||||||
httpUrl.Should().StartWith("http://");
|
httpUrl.Should().StartWith("http://");
|
||||||
|
|
||||||
var grpcPort = wireMockContainer.GetMappedPublicPort(9090);
|
var grpcPort = wireMockContainer.GetMappedPublicPort(port);
|
||||||
grpcPort.Should().BeGreaterThan(0);
|
grpcPort.Should().BeGreaterThan(0);
|
||||||
|
|
||||||
var grpcUrl = wireMockContainer.GetMappedPublicUrl(80);
|
var grpcUrl = wireMockContainer.GetMappedPublicUrl(port);
|
||||||
grpcUrl.Should().StartWith("http://");
|
grpcUrl.Should().StartWith("http://");
|
||||||
|
|
||||||
var adminClient = wireMockContainer.CreateWireMockAdminClient();
|
var adminClient = wireMockContainer.CreateWireMockAdminClient();
|
||||||
@@ -74,19 +90,23 @@ public partial class TestcontainersTests
|
|||||||
[Fact]
|
[Fact]
|
||||||
public async Task WireMockContainer_Build_Grpc_TestPortsAndUrls2()
|
public async Task WireMockContainer_Build_Grpc_TestPortsAndUrls2()
|
||||||
{
|
{
|
||||||
// Act
|
// Arrange
|
||||||
var adminUsername = $"username_{Guid.NewGuid()}";
|
var adminUsername = $"username_{Guid.NewGuid()}";
|
||||||
var adminPassword = $"password_{Guid.NewGuid()}";
|
var adminPassword = $"password_{Guid.NewGuid()}";
|
||||||
|
var ports = PortUtils.FindFreeTcpPorts(3);
|
||||||
|
|
||||||
|
// Act
|
||||||
var wireMockContainer = new WireMockContainerBuilder()
|
var wireMockContainer = new WireMockContainerBuilder()
|
||||||
|
.WithLogger(_logger)
|
||||||
.WithAdminUserNameAndPassword(adminUsername, adminPassword)
|
.WithAdminUserNameAndPassword(adminUsername, adminPassword)
|
||||||
.AddUrl("http://*:8080")
|
.AddUrl($"http://*:{ports[0]}")
|
||||||
.AddUrl("grpc://*:9090")
|
.AddUrl($"grpc://*:{ports[1]}")
|
||||||
.AddUrl("grpc://*:9091")
|
.AddUrl($"grpc://*:{ports[2]}")
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await wireMockContainer.StartAsync().ConfigureAwait(false);
|
await wireMockContainer.StartAsync();
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
using (new AssertionScope())
|
using (new AssertionScope())
|
||||||
@@ -100,7 +120,7 @@ public partial class TestcontainersTests
|
|||||||
var urls = wireMockContainer.GetPublicUrls();
|
var urls = wireMockContainer.GetPublicUrls();
|
||||||
urls.Should().HaveCount(4);
|
urls.Should().HaveCount(4);
|
||||||
|
|
||||||
foreach (var internalPort in new[] { 80, 8080, 9090, 9091 })
|
foreach (var internalPort in new[] { ports[0], ports[1], ports[2], 80 })
|
||||||
{
|
{
|
||||||
var publicPort = wireMockContainer.GetMappedPublicPort(internalPort);
|
var publicPort = wireMockContainer.GetMappedPublicPort(internalPort);
|
||||||
publicPort.Should().BeGreaterThan(0);
|
publicPort.Should().BeGreaterThan(0);
|
||||||
@@ -149,10 +169,75 @@ public partial class TestcontainersTests
|
|||||||
await StopAsync(wireMockContainer);
|
await StopAsync(wireMockContainer);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task<WireMockContainer> Given_WireMockContainerIsStartedForHttpAndGrpcAsync()
|
[Fact]
|
||||||
|
public async Task WireMockContainer_Build_Grpc_ProtoDefinitionAtServerLevel_UsingGrpcGeneratedClient_AndWithWatchStaticMappings()
|
||||||
{
|
{
|
||||||
|
var wireMockContainer = await Given_WireMockContainerWithProtoDefinitionAtServerLevelWithWatchStaticMappingsIsStartedForHttpAndGrpcAsync();
|
||||||
|
|
||||||
|
var reply = await When_GrpcClient_Calls_SayHelloAsync(wireMockContainer);
|
||||||
|
|
||||||
|
Then_ReplyMessage_Should_BeCorrect(reply);
|
||||||
|
|
||||||
|
await StopAsync(wireMockContainer);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<HelloReply> When_GrpcClient_Calls_SayHelloAsync(WireMockContainer wireMockContainer)
|
||||||
|
{
|
||||||
|
var address = wireMockContainer.GetPublicUrls().First(x => x.Key != 80).Value;
|
||||||
|
var channel = GrpcChannel.ForAddress(address);
|
||||||
|
|
||||||
|
var client = new Greeter.GreeterClient(channel);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return await client.SayHelloAsync(new HelloRequest { Name = "stef" });
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
testOutputHelper.WriteLine("Exception during GrpcClient Call to {0}. Exception = {1}.", address, ex);
|
||||||
|
|
||||||
|
testOutputHelper.WriteLine("Dumping WireMock.Net logs:");
|
||||||
|
var (stdOut, stdError) = await wireMockContainer.GetLogsAsync(DateTime.MinValue);
|
||||||
|
testOutputHelper.WriteLine("Out :\r\n{0}", stdOut);
|
||||||
|
testOutputHelper.WriteLine("Error:\r\n{0}", stdError);
|
||||||
|
|
||||||
|
testOutputHelper.WriteLine("Dumping WireMock.Net mappings:");
|
||||||
|
using var httpClient = wireMockContainer.CreateClient();
|
||||||
|
using var response = await httpClient.GetAsync("/__admin/mappings");
|
||||||
|
var mappings = await response.Content.ReadAsStringAsync();
|
||||||
|
testOutputHelper.WriteLine("Mappings:\r\n{0}", mappings);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task StopAsync(WireMockContainer wireMockContainer)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await wireMockContainer.StopAsync();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
// Sometimes we get this exception, so for now ignore it.
|
||||||
|
/*
|
||||||
|
Failed WireMock.Net.Tests.Testcontainers.TestcontainersTests.WireMockContainer_Build_WithImageAsText_And_StartAsync_and_StopAsync [9 s]
|
||||||
|
Error Message:
|
||||||
|
System.NullReferenceException : Object reference not set to an instance of an object.
|
||||||
|
Stack Trace:
|
||||||
|
at DotNet.Testcontainers.Containers.DockerContainer.UnsafeStopAsync(CancellationToken ct) in /_/src/Testcontainers/Containers/DockerContainer.cs:line 567
|
||||||
|
at DotNet.Testcontainers.Containers.DockerContainer.StopAsync(CancellationToken ct) in /_/src/Testcontainers/Containers/DockerContainer.cs:line 319
|
||||||
|
*/
|
||||||
|
|
||||||
|
testOutputHelper.WriteLine($"Exception during StopAsync: {ex}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<WireMockContainer> Given_WireMockContainerIsStartedForHttpAndGrpcAsync()
|
||||||
|
{
|
||||||
|
var port = PortUtils.FindFreeTcpPort();
|
||||||
var wireMockContainer = new WireMockContainerBuilder()
|
var wireMockContainer = new WireMockContainerBuilder()
|
||||||
.AddUrl("grpc://*:9090")
|
.WithLogger(_logger)
|
||||||
|
.AddUrl($"grpc://*:{port}")
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
await wireMockContainer.StartAsync();
|
await wireMockContainer.StartAsync();
|
||||||
@@ -160,10 +245,12 @@ public partial class TestcontainersTests
|
|||||||
return wireMockContainer;
|
return wireMockContainer;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task<WireMockContainer> Given_WireMockContainerWithProtoDefinitionAtServerLevelIsStartedForHttpAndGrpcAsync()
|
private async Task<WireMockContainer> Given_WireMockContainerWithProtoDefinitionAtServerLevelIsStartedForHttpAndGrpcAsync()
|
||||||
{
|
{
|
||||||
|
var port = PortUtils.FindFreeTcpPort();
|
||||||
var wireMockContainer = new WireMockContainerBuilder()
|
var wireMockContainer = new WireMockContainerBuilder()
|
||||||
.AddUrl("grpc://*:9090")
|
.WithLogger(_logger)
|
||||||
|
.AddUrl($"grpc://*:{port}")
|
||||||
.AddProtoDefinition("my-greeter", ReadFile("greet.proto"))
|
.AddProtoDefinition("my-greeter", ReadFile("greet.proto"))
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
@@ -172,6 +259,21 @@ public partial class TestcontainersTests
|
|||||||
return wireMockContainer;
|
return wireMockContainer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task<WireMockContainer> Given_WireMockContainerWithProtoDefinitionAtServerLevelWithWatchStaticMappingsIsStartedForHttpAndGrpcAsync()
|
||||||
|
{
|
||||||
|
var port = PortUtils.FindFreeTcpPort();
|
||||||
|
var wireMockContainer = new WireMockContainerBuilder()
|
||||||
|
.WithLogger(_logger)
|
||||||
|
.AddUrl($"grpc://*:{port}")
|
||||||
|
.AddProtoDefinition("my-greeter", ReadFile("greet.proto"))
|
||||||
|
.WithMappings(Path.Combine(Directory.GetCurrentDirectory(), "__admin", "mappings"))
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
await wireMockContainer.StartAsync();
|
||||||
|
|
||||||
|
return wireMockContainer;
|
||||||
|
}
|
||||||
|
|
||||||
private static async Task Given_ProtoBufMappingIsAddedViaAdminInterfaceAsync(WireMockContainer wireMockContainer, string filename)
|
private static async Task Given_ProtoBufMappingIsAddedViaAdminInterfaceAsync(WireMockContainer wireMockContainer, string filename)
|
||||||
{
|
{
|
||||||
var mappingsJson = ReadFile(filename);
|
var mappingsJson = ReadFile(filename);
|
||||||
@@ -182,16 +284,6 @@ public partial class TestcontainersTests
|
|||||||
result.EnsureSuccessStatusCode();
|
result.EnsureSuccessStatusCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task<HelloReply> When_GrpcClient_Calls_SayHelloAsync(WireMockContainer wireMockContainer)
|
|
||||||
{
|
|
||||||
var address = wireMockContainer.GetPublicUrls()[9090];
|
|
||||||
var channel = GrpcChannel.ForAddress(address);
|
|
||||||
|
|
||||||
var client = new Greeter.GreeterClient(channel);
|
|
||||||
|
|
||||||
return await client.SayHelloAsync(new HelloRequest { Name = "stef" });
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void Then_ReplyMessage_Should_BeCorrect(HelloReply reply)
|
private static void Then_ReplyMessage_Should_BeCorrect(HelloReply reply)
|
||||||
{
|
{
|
||||||
reply.Message.Should().Be("hello stef POST");
|
reply.Message.Should().Be("hello stef POST");
|
||||||
@@ -15,13 +15,13 @@ public class PortUtilsTests
|
|||||||
var url = "test";
|
var url = "test";
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = PortUtils.TryExtract(url, out var isHttps, out var isGrpc, out var proto, out var host, out var port);
|
var result = PortUtils.TryExtract(url, out var isHttps, out var isGrpc, out var scheme, out var host, out var port);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
result.Should().BeFalse();
|
result.Should().BeFalse();
|
||||||
isHttps.Should().BeFalse();
|
isHttps.Should().BeFalse();
|
||||||
isGrpc.Should().BeFalse();
|
isGrpc.Should().BeFalse();
|
||||||
proto.Should().BeNull();
|
scheme.Should().BeNull();
|
||||||
host.Should().BeNull();
|
host.Should().BeNull();
|
||||||
port.Should().Be(default(int));
|
port.Should().Be(default(int));
|
||||||
}
|
}
|
||||||
@@ -33,13 +33,13 @@ public class PortUtilsTests
|
|||||||
var url = "http://0.0.0.0";
|
var url = "http://0.0.0.0";
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = PortUtils.TryExtract(url, out var isHttps, out var isGrpc, out var proto, out var host, out var port);
|
var result = PortUtils.TryExtract(url, out var isHttps, out var isGrpc, out var scheme, out var host, out var port);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
result.Should().BeFalse();
|
result.Should().BeFalse();
|
||||||
isHttps.Should().BeFalse();
|
isHttps.Should().BeFalse();
|
||||||
isGrpc.Should().BeFalse();
|
isGrpc.Should().BeFalse();
|
||||||
proto.Should().BeNull();
|
scheme.Should().BeNull();
|
||||||
host.Should().BeNull();
|
host.Should().BeNull();
|
||||||
port.Should().Be(default(int));
|
port.Should().Be(default(int));
|
||||||
}
|
}
|
||||||
@@ -51,13 +51,13 @@ public class PortUtilsTests
|
|||||||
var url = "http://wiremock.net:1234";
|
var url = "http://wiremock.net:1234";
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = PortUtils.TryExtract(url, out var isHttps, out var isGrpc, out var proto, out var host, out var port);
|
var result = PortUtils.TryExtract(url, out var isHttps, out var isGrpc, out var scheme, out var host, out var port);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
result.Should().BeTrue();
|
result.Should().BeTrue();
|
||||||
isHttps.Should().BeFalse();
|
isHttps.Should().BeFalse();
|
||||||
isGrpc.Should().BeFalse();
|
isGrpc.Should().BeFalse();
|
||||||
proto.Should().Be("http");
|
scheme.Should().Be("http");
|
||||||
host.Should().Be("wiremock.net");
|
host.Should().Be("wiremock.net");
|
||||||
port.Should().Be(1234);
|
port.Should().Be(1234);
|
||||||
}
|
}
|
||||||
@@ -69,13 +69,13 @@ public class PortUtilsTests
|
|||||||
var url = "https://wiremock.net:5000";
|
var url = "https://wiremock.net:5000";
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = PortUtils.TryExtract(url, out var isHttps, out var isGrpc, out var proto, out var host, out var port);
|
var result = PortUtils.TryExtract(url, out var isHttps, out var isGrpc, out var scheme, out var host, out var port);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
result.Should().BeTrue();
|
result.Should().BeTrue();
|
||||||
isHttps.Should().BeTrue();
|
isHttps.Should().BeTrue();
|
||||||
isGrpc.Should().BeFalse();
|
isGrpc.Should().BeFalse();
|
||||||
proto.Should().Be("https");
|
scheme.Should().Be("https");
|
||||||
host.Should().Be("wiremock.net");
|
host.Should().Be("wiremock.net");
|
||||||
port.Should().Be(5000);
|
port.Should().Be(5000);
|
||||||
}
|
}
|
||||||
@@ -87,13 +87,13 @@ public class PortUtilsTests
|
|||||||
var url = "grpc://wiremock.net:1234";
|
var url = "grpc://wiremock.net:1234";
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = PortUtils.TryExtract(url, out var isHttps, out var isGrpc, out var proto, out var host, out var port);
|
var result = PortUtils.TryExtract(url, out var isHttps, out var isGrpc, out var scheme, out var host, out var port);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
result.Should().BeTrue();
|
result.Should().BeTrue();
|
||||||
isHttps.Should().BeFalse();
|
isHttps.Should().BeFalse();
|
||||||
isGrpc.Should().BeTrue();
|
isGrpc.Should().BeTrue();
|
||||||
proto.Should().Be("grpc");
|
scheme.Should().Be("grpc");
|
||||||
host.Should().Be("wiremock.net");
|
host.Should().Be("wiremock.net");
|
||||||
port.Should().Be(1234);
|
port.Should().Be(1234);
|
||||||
}
|
}
|
||||||
@@ -105,13 +105,13 @@ public class PortUtilsTests
|
|||||||
var url = "https://0.0.0.0:5000";
|
var url = "https://0.0.0.0:5000";
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = PortUtils.TryExtract(url, out var isHttps, out var isGrpc, out var proto, out var host, out var port);
|
var result = PortUtils.TryExtract(url, out var isHttps, out var isGrpc, out var scheme, out var host, out var port);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
result.Should().BeTrue();
|
result.Should().BeTrue();
|
||||||
isHttps.Should().BeTrue();
|
isHttps.Should().BeTrue();
|
||||||
isGrpc.Should().BeFalse();
|
isGrpc.Should().BeFalse();
|
||||||
proto.Should().Be("https");
|
scheme.Should().Be("https");
|
||||||
host.Should().Be("0.0.0.0");
|
host.Should().Be("0.0.0.0");
|
||||||
port.Should().Be(5000);
|
port.Should().Be(5000);
|
||||||
}
|
}
|
||||||
|
|||||||
42
test/WireMock.Net.Tests/Validators/PathValidatorTests.cs
Normal file
42
test/WireMock.Net.Tests/Validators/PathValidatorTests.cs
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
// Copyright © WireMock.Net
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using FluentAssertions;
|
||||||
|
using WireMock.Validators;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace WireMock.Net.Tests.Validators;
|
||||||
|
|
||||||
|
[ExcludeFromCodeCoverage]
|
||||||
|
public class PathValidatorTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void ValidateAndThrow_ValidPath_DoesNotThrow()
|
||||||
|
{
|
||||||
|
Action act = () => PathValidator.ValidateAndThrow("/valid/path");
|
||||||
|
act.Should().NotThrow();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData(null)]
|
||||||
|
[InlineData("")]
|
||||||
|
[InlineData("\r")]
|
||||||
|
[InlineData("\n")]
|
||||||
|
[InlineData("\t")]
|
||||||
|
public void ValidateAndThrow_InvalidPath_ThrowsArgumentException_WithDefaultParamName(string? path)
|
||||||
|
{
|
||||||
|
Action act = () => PathValidator.ValidateAndThrow(path);
|
||||||
|
var ex = act.Should().Throw<ArgumentException>().Which;
|
||||||
|
ex.Message.Should().StartWith("Path must start with a '/' and cannot be null, empty or whitespace.");
|
||||||
|
ex.ParamName.Should().Be("path");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ValidateAndThrow_NoLeadingSlash_ThrowsArgumentException_WithProvidedParamName()
|
||||||
|
{
|
||||||
|
Action act = () => PathValidator.ValidateAndThrow("noSlash", "myParam");
|
||||||
|
var ex = act.Should().Throw<ArgumentException>().Which;
|
||||||
|
ex.ParamName.Should().Be("myParam");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<Authors>Stef Heyenrath</Authors>
|
<Authors>Stef Heyenrath</Authors>
|
||||||
@@ -38,6 +38,10 @@
|
|||||||
<Compile Remove="Util\JsonUtilsTests.cs" />
|
<Compile Remove="Util\JsonUtilsTests.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Include="..\..\src\WireMock.Net.Testcontainers\Utils\CombineUtils.cs" Link="Testcontainers\CombineUtils.cs" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<!-- https://stackoverflow.com/questions/59406201/filenesting-not-working-for-class-or-shared-library-projects -->
|
<!-- https://stackoverflow.com/questions/59406201/filenesting-not-working-for-class-or-shared-library-projects -->
|
||||||
<ProjectCapability Include="ConfigurableFileNesting" />
|
<ProjectCapability Include="ConfigurableFileNesting" />
|
||||||
@@ -103,7 +107,7 @@
|
|||||||
|
|
||||||
<ItemGroup Condition="'$(TargetFramework)' != 'net452'">
|
<ItemGroup Condition="'$(TargetFramework)' != 'net452'">
|
||||||
<PackageReference Include="System.Net.Http.Json" Version="8.0.1" />
|
<PackageReference Include="System.Net.Http.Json" Version="8.0.1" />
|
||||||
<PackageReference Include="JsonConverter.System.Text.Json" Version="0.7.0" />
|
<!--<PackageReference Include="JsonConverter.System.Text.Json" Version="0.8.0" />-->
|
||||||
<PackageReference Include="Google.Protobuf" Version="3.25.1" />
|
<PackageReference Include="Google.Protobuf" Version="3.25.1" />
|
||||||
<PackageReference Include="Grpc.Net.Client" Version="2.60.0" />
|
<PackageReference Include="Grpc.Net.Client" Version="2.60.0" />
|
||||||
<PackageReference Include="Grpc.Tools" Version="2.60.0">
|
<PackageReference Include="Grpc.Tools" Version="2.60.0">
|
||||||
@@ -117,6 +121,7 @@
|
|||||||
|
|
||||||
<ItemGroup Condition="'$(TargetFramework)' == 'net6.0' or '$(TargetFramework)' == 'net7.0' or '$(TargetFramework)' == 'net8.0'">
|
<ItemGroup Condition="'$(TargetFramework)' == 'net6.0' or '$(TargetFramework)' == 'net7.0' or '$(TargetFramework)' == 'net8.0'">
|
||||||
<ProjectReference Include="..\..\src\WireMock.Net.Testcontainers\WireMock.Net.Testcontainers.csproj" />
|
<ProjectReference Include="..\..\src\WireMock.Net.Testcontainers\WireMock.Net.Testcontainers.csproj" />
|
||||||
|
<PackageReference Include="Meziantou.Extensions.Logging.Xunit" Version="1.0.21" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -4,21 +4,21 @@
|
|||||||
|
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
|
using System.Security.Cryptography.X509Certificates;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using FluentAssertions;
|
using FluentAssertions;
|
||||||
using System.Security.Cryptography.X509Certificates;
|
using WireMock.Net.Tests.Facts;
|
||||||
using WireMock.RequestBuilders;
|
using WireMock.RequestBuilders;
|
||||||
using WireMock.ResponseBuilders;
|
using WireMock.ResponseBuilders;
|
||||||
using WireMock.Server;
|
using WireMock.Server;
|
||||||
using WireMock.Settings;
|
using WireMock.Settings;
|
||||||
using WireMock.Types;
|
using WireMock.Types;
|
||||||
using Xunit;
|
|
||||||
|
|
||||||
namespace WireMock.Net.Tests;
|
namespace WireMock.Net.Tests;
|
||||||
|
|
||||||
public partial class WireMockServerTests
|
public partial class WireMockServerTests
|
||||||
{
|
{
|
||||||
[Fact]
|
[IgnoreOnContinuousIntegrationFact]
|
||||||
public async Task WireMockServer_WithRequiredClientCertificates_Should_Work_Correct()
|
public async Task WireMockServer_WithRequiredClientCertificates_Should_Work_Correct()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
@@ -57,5 +57,4 @@ public partial class WireMockServerTests
|
|||||||
response.StatusCode.Should().Be(HttpStatusCode.OK);
|
response.StatusCode.Should().Be(HttpStatusCode.OK);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -6,7 +6,6 @@ using System.Collections.Generic;
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Net.Http.Headers;
|
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using FluentAssertions;
|
using FluentAssertions;
|
||||||
@@ -34,11 +33,10 @@ public partial class WireMockServerTests
|
|||||||
Request.Create()
|
Request.Create()
|
||||||
.WithPath("/a")
|
.WithPath("/a")
|
||||||
.WithBody(
|
.WithBody(
|
||||||
new IMatcher[]
|
[
|
||||||
{
|
|
||||||
new JmesPathMatcher("requestId == '1'"),
|
new JmesPathMatcher("requestId == '1'"),
|
||||||
new JmesPathMatcher("value == 'A'")
|
new JmesPathMatcher("value == 'A'")
|
||||||
},
|
],
|
||||||
MatchOperator.And
|
MatchOperator.And
|
||||||
)
|
)
|
||||||
.UsingPost()
|
.UsingPost()
|
||||||
@@ -49,11 +47,10 @@ public partial class WireMockServerTests
|
|||||||
Request.Create()
|
Request.Create()
|
||||||
.WithPath("/a")
|
.WithPath("/a")
|
||||||
.WithBody(
|
.WithBody(
|
||||||
new IMatcher[]
|
[
|
||||||
{
|
|
||||||
new JmesPathMatcher("requestId == '2'"),
|
new JmesPathMatcher("requestId == '2'"),
|
||||||
new JmesPathMatcher("value == 'A'")
|
new JmesPathMatcher("value == 'A'")
|
||||||
},
|
],
|
||||||
MatchOperator.And
|
MatchOperator.And
|
||||||
)
|
)
|
||||||
.UsingPost()
|
.UsingPost()
|
||||||
@@ -81,12 +78,11 @@ public partial class WireMockServerTests
|
|||||||
Request.Create()
|
Request.Create()
|
||||||
.WithPath("/a")
|
.WithPath("/a")
|
||||||
.WithBody(
|
.WithBody(
|
||||||
new IMatcher[]
|
[
|
||||||
{
|
|
||||||
new JmesPathMatcher("extra == 'X'"),
|
new JmesPathMatcher("extra == 'X'"),
|
||||||
new JmesPathMatcher("requestId == '1'"),
|
new JmesPathMatcher("requestId == '1'"),
|
||||||
new JmesPathMatcher("value == 'A'")
|
new JmesPathMatcher("value == 'A'")
|
||||||
},
|
],
|
||||||
MatchOperator.And
|
MatchOperator.And
|
||||||
)
|
)
|
||||||
.UsingPost()
|
.UsingPost()
|
||||||
@@ -98,11 +94,10 @@ public partial class WireMockServerTests
|
|||||||
Request.Create()
|
Request.Create()
|
||||||
.WithPath("/a")
|
.WithPath("/a")
|
||||||
.WithBody(
|
.WithBody(
|
||||||
new IMatcher[]
|
[
|
||||||
{
|
|
||||||
new JmesPathMatcher("requestId == '1'"),
|
new JmesPathMatcher("requestId == '1'"),
|
||||||
new JmesPathMatcher("value == 'A'")
|
new JmesPathMatcher("value == 'A'")
|
||||||
},
|
],
|
||||||
MatchOperator.And
|
MatchOperator.And
|
||||||
)
|
)
|
||||||
.UsingPost()
|
.UsingPost()
|
||||||
@@ -188,6 +183,7 @@ public partial class WireMockServerTests
|
|||||||
{"jsonrpc":"2.0","id":"{{request.bodyAsJson.id}}","result":{"protocolVersion":"2024-11-05","capabilities":{"logging":{},"prompts":{"listChanged":true},"resources":{"subscribe":true,"listChanged":true},"tools":{"listChanged":true}},"serverInfo":{"name":"ExampleServer","version":"1.0.0"}}}
|
{"jsonrpc":"2.0","id":"{{request.bodyAsJson.id}}","result":{"protocolVersion":"2024-11-05","capabilities":{"logging":{},"prompts":{"listChanged":true},"resources":{"subscribe":true,"listChanged":true},"tools":{"listChanged":true}},"serverInfo":{"name":"ExampleServer","version":"1.0.0"}}}
|
||||||
""")
|
""")
|
||||||
.WithStatusCode(200)
|
.WithStatusCode(200)
|
||||||
|
.WithTransformer(true)
|
||||||
);
|
);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
@@ -199,15 +195,47 @@ public partial class WireMockServerTests
|
|||||||
// Assert
|
// Assert
|
||||||
response.StatusCode.Should().Be(HttpStatusCode.OK);
|
response.StatusCode.Should().Be(HttpStatusCode.OK);
|
||||||
|
|
||||||
var responseText = await response.RequestMessage!.Content!.ReadAsStringAsync();
|
var responseText = await response.Content.ReadAsStringAsync();
|
||||||
responseText.Should().Contain("ec475f56d4694b48bc737500ba575b35-1");
|
responseText.Should().Contain("ec475f56d4694b48bc737500ba575b35-1");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if NET6_0_OR_GREATER
|
||||||
|
[Fact]
|
||||||
|
public async Task WireMockServer_WithBodyAsJson_Using_PostAsync_And_JsonPartialWildcardMatcher_And_SystemTextJson_ShouldMatch()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
using var server = WireMockServer.Start(x => x.DefaultJsonSerializer = new JsonConverter.System.Text.Json.SystemTextJsonConverter() );
|
||||||
|
|
||||||
|
var matcher = new JsonPartialWildcardMatcher(new { id = "^[a-f0-9]{32}-[0-9]$" }, ignoreCase: true, regex: true);
|
||||||
|
server.Given(Request.Create()
|
||||||
|
.WithHeader("Content-Type", "application/json*")
|
||||||
|
.UsingPost()
|
||||||
|
.WithPath("/system-text-json")
|
||||||
|
.WithBody(matcher)
|
||||||
|
)
|
||||||
|
.RespondWith(Response.Create()
|
||||||
|
.WithBody("OK")
|
||||||
|
);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var content = """{"id":"ec475f56d4694b48bc737500ba575b35-1"}""";
|
||||||
|
var response = await new HttpClient()
|
||||||
|
.PostAsync($"{server.Url}/system-text-json", new StringContent(content, Encoding.UTF8, "application/json"))
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
response.StatusCode.Should().Be(HttpStatusCode.OK);
|
||||||
|
|
||||||
|
var responseText = await response.Content.ReadAsStringAsync();
|
||||||
|
responseText.Should().Contain("OK");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task WireMockServer_WithBodyAsFormUrlEncoded_Using_PostAsync_And_WithFunc()
|
public async Task WireMockServer_WithBodyAsFormUrlEncoded_Using_PostAsync_And_WithFunc()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var server = WireMockServer.Start();
|
using var server = WireMockServer.Start();
|
||||||
server.Given(
|
server.Given(
|
||||||
Request.Create()
|
Request.Create()
|
||||||
.UsingPost()
|
.UsingPost()
|
||||||
@@ -219,7 +247,7 @@ public partial class WireMockServerTests
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var content = new FormUrlEncodedContent(new[] { new KeyValuePair<string, string>("key1", "value1") });
|
var content = new FormUrlEncodedContent([new KeyValuePair<string, string>("key1", "value1")]);
|
||||||
var response = await new HttpClient()
|
var response = await new HttpClient()
|
||||||
.PostAsync($"{server.Url}/foo", content)
|
.PostAsync($"{server.Url}/foo", content)
|
||||||
.ConfigureAwait(false);
|
.ConfigureAwait(false);
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user