mirror of
https://github.com/wiremock/WireMock.Net.git
synced 2026-02-14 06:17:40 +01:00
Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f9633adac1 | ||
|
|
37bad618a3 | ||
|
|
8e69f36f04 | ||
|
|
21601889e0 | ||
|
|
dfeabf228e | ||
|
|
1feb0ade70 | ||
|
|
8b1bd1b21b | ||
|
|
c1b23b615e | ||
|
|
5885324dfb | ||
|
|
e3f3e0a8f2 | ||
|
|
685d28db0b | ||
|
|
f6c5225fe0 | ||
|
|
b9019a2f61 | ||
|
|
b82dad2563 |
21
CHANGELOG.md
21
CHANGELOG.md
@@ -1,7 +1,26 @@
|
||||
# 1.16.0 (18 November 2025)
|
||||
- [#1366](https://github.com/wiremock/WireMock.Net/pull/1366) - WireMock.Net.OpenApiParser : support Examples [feature] contributed by [StefH](https://github.com/StefH)
|
||||
- [#1375](https://github.com/wiremock/WireMock.Net/pull/1375) - Add WireMockHealthCheck in WireMock.Net.Aspire [feature] contributed by [Zguy](https://github.com/Zguy)
|
||||
- [#1377](https://github.com/wiremock/WireMock.Net/pull/1377) - Check if the path is valid when using WithPath(...) [feature] contributed by [StefH](https://github.com/StefH)
|
||||
- [#1380](https://github.com/wiremock/WireMock.Net/pull/1380) - Add WireMock.Net.xUnit.v3 project [feature] contributed by [StefH](https://github.com/StefH)
|
||||
- [#1364](https://github.com/wiremock/WireMock.Net/issues/1364) - Choosing examples from open api specification for responses. [feature]
|
||||
- [#1376](https://github.com/wiremock/WireMock.Net/issues/1376) - AdminApiMappingBuilder `WithPath` should add the starting `/` if missing [feature]
|
||||
- [#1379](https://github.com/wiremock/WireMock.Net/issues/1379) - xUnit v3 [feature]
|
||||
|
||||
# 1.15.0 (22 October 2025)
|
||||
- [#1367](https://github.com/wiremock/WireMock.Net/pull/1367) - Fix WithProbability logic [bug] contributed by [StefH](https://github.com/StefH)
|
||||
- [#1370](https://github.com/wiremock/WireMock.Net/pull/1370) - Support Testcontainers 4.8.0 [bug] contributed by [MD-V](https://github.com/MD-V)
|
||||
- [#1126](https://github.com/wiremock/WireMock.Net/issues/1126) - Request matching WithProbability strange behaviour [bug]
|
||||
|
||||
# 1.14.0 (06 October 2025)
|
||||
- [#1362](https://github.com/wiremock/WireMock.Net/pull/1362) - Update ProxyUrlReplaceSettingsModel with TransformTemplate property [feature] contributed by [StefH](https://github.com/StefH)
|
||||
- [#1363](https://github.com/wiremock/WireMock.Net/pull/1363) - Add Tls13 [bug] contributed by [StefH](https://github.com/StefH)
|
||||
- [#1342](https://github.com/wiremock/WireMock.Net/issues/1342) - Facing SSL certificate validation error when using .NET 5 and above framework for some http services during recording [bug]
|
||||
- [#1360](https://github.com/wiremock/WireMock.Net/issues/1360) - ProxyURL path configuration [feature]
|
||||
|
||||
# 1.13.0 (28 September 2025)
|
||||
- [#1358](https://github.com/wiremock/WireMock.Net/pull/1358) - TypeLoader: implement Try methods [feature] contributed by [StefH](https://github.com/StefH)
|
||||
- [#1361](https://github.com/wiremock/WireMock.Net/pull/1361) - ProxyUrlTransformer [feature] contributed by [StefH](https://github.com/StefH)
|
||||
- [#1360](https://github.com/wiremock/WireMock.Net/issues/1360) - ProxyURL path configuration [feature]
|
||||
|
||||
# 1.12.0 (30 August 2025)
|
||||
- [#1357](https://github.com/wiremock/WireMock.Net/pull/1357) - Upgrade Testcontainers to 4.7.0 [feature] contributed by [StefH](https://github.com/StefH)
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<Version>1.13.0</Version>
|
||||
<Version>1.16.0</Version>
|
||||
<PackageIcon>WireMock.Net-Logo.png</PackageIcon>
|
||||
<PackageProjectUrl>https://github.com/wiremock/WireMock.Net</PackageProjectUrl>
|
||||
<PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
rem https://github.com/StefH/GitHubReleaseNotes
|
||||
|
||||
SET version=1.13.0
|
||||
SET version=1.16.0
|
||||
|
||||
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
|
||||
A core feature of WireMock.Net is the ability to return predefined HTTP responses for requests matching criteria.
|
||||
See [Wiki : Stubbing](https://github.com/wiremock/WireMock.Net/wiki/Stubbing).
|
||||
See [Stubbing](https://wiremock.org/dotnet/stubbing).
|
||||
|
||||
### :star: Request Matching
|
||||
WireMock.Net support advanced request-matching logic, see [Wiki : Request Matching](https://github.com/wiremock/WireMock.Net/wiki/Request-Matching).
|
||||
WireMock.Net support advanced request-matching logic, see [Request Matching](https://wiremock.org/dotnet/request-matching).
|
||||
|
||||
### :star: Response Templating
|
||||
The response which is returned WireMock.Net can be changed using templating. This is described here [Wiki : Response Templating](https://github.com/wiremock/WireMock.Net/wiki/Response-Templating).
|
||||
The response which is returned WireMock.Net can be changed using templating. This is described here [Response Templating](https://wiremock.org/dotnet/response-templating).
|
||||
|
||||
### :star: Admin API Reference
|
||||
The WireMock admin API provides functionality to define the mappings via a http interface see [Wiki : Admin API Reference](https://github.com/StefH/WireMock.Net/wiki/Admin-API-Reference).
|
||||
The WireMock admin API provides functionality to define the mappings via a http interface see [Admin API Reference](https://wiremock.org/dotnet/admin-api-reference).
|
||||
|
||||
### :star: Using
|
||||
WireMock.Net can be used in several ways:
|
||||
|
||||
#### UnitTesting
|
||||
You can use your favorite test framework and use WireMock within your tests, see
|
||||
[Wiki : UnitTesting](https://github.com/StefH/WireMock.Net/wiki/Using-WireMock-in-UnitTests).
|
||||
[UnitTesting](https://wiremock.org/dotnet/using-wiremock-in-unittests).
|
||||
|
||||
### Unit/Integration Testing using Testcontainers.DotNet
|
||||
See [Wiki : WireMock.Net.Testcontainers](https://github.com/wiremock/WireMock.Net/wiki/Using-WireMock.Net.Testcontainers) on how to build a WireMock.Net Docker container which can be used in Unit/Integration testing.
|
||||
See [WireMock.Net.Testcontainers](https://wiremock.org/dotnet/using-wiremock-net-testcontainers/) on how to build a WireMock.Net Docker container which can be used in Unit/Integration testing.
|
||||
|
||||
### Unit/Integration Testing using an an Aspire Distributed Application
|
||||
See [Wiki : WireMock.Net.Aspire](https://github.com/wiremock/WireMock.Net/wiki/Using-WireMock.Net.Aspire) on how to use WireMock.Net as an Aspire Hosted application to do Unit/Integration testing.
|
||||
See [WireMock.Net.Aspire](https://wiremock.org/dotnet/using-wiremock-net-Aspire) on how to use WireMock.Net as an Aspire Hosted application to do Unit/Integration testing.
|
||||
|
||||
#### As a dotnet tool
|
||||
It's simple to install WireMock.Net as (global) dotnet tool, see [Wiki : dotnet tool](https://github.com/StefH/WireMock.Net/wiki/WireMock-as-dotnet-tool).
|
||||
It's simple to install WireMock.Net as (global) dotnet tool, see [dotnet tool](https://wiremock.org/dotnet/wiremock-as-dotnet-tool).
|
||||
|
||||
#### As standalone process / console application
|
||||
This is quite straight forward to launch a mock server within a console application, see [Wiki : Standalone Process](https://github.com/StefH/WireMock.Net/wiki/WireMock-as-a-standalone-process).
|
||||
This is quite straight forward to launch a mock server within a console application, see [Standalone Process](https://wiremock.org/dotnet/wiremock-as-a-standalone-process).
|
||||
|
||||
#### As a Windows Service
|
||||
You can also run WireMock.Net as a Windows Service, follow this [WireMock-as-a-Windows-Service](https://github.com/wiremock/WireMock.Net/wiki/WireMock-as-a-Windows-Service).
|
||||
You can also run WireMock.Net as a Windows Service, follow this [Windows Service](https://wiremock.org/dotnet/wiremock-as-a-windows-service).
|
||||
|
||||
#### As a Web Job in Azure or application in IIS
|
||||
See this link [WireMock-as-a-(Azure)-Web-App](https://github.com/wiremock/WireMock.Net/wiki/WireMock-as-a-(Azure)-Web-App)
|
||||
See this link [WireMock-as-a-(Azure)-Web-App](https://wiremock.org/dotnet/wiremock-as-a-azure-web-app/)
|
||||
|
||||
#### In a docker container
|
||||
There is also a Linux and Windows-Nano container available at [hub.docker.com](https://hub.docker.com/r/sheyenrath).
|
||||
For more details see also [Docker](https://github.com/wiremock/WireMock.Net-docker).
|
||||
|
||||
#### HTTPS / SSL
|
||||
More details on using HTTPS (SSL) can be found here [Wiki : HTTPS](https://github.com/wiremock/WireMock.Net/wiki/Using-HTTPS-(SSL))
|
||||
More details on using HTTPS (SSL) can be found here [HTTPS](https://wiremock.org/dotnet/using-https-ssl/)
|
||||
|
||||
## :books: Documentation
|
||||
For more info, see also this WIKI page: [What is WireMock.Net](https://github.com/wiremock/WireMock.Net/wiki/What-Is-WireMock.Net).
|
||||
For more info, see also this documentation page: [What is WireMock.Net](https://wiremock.org/dotnet/what-is-wiremock-net/).
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
# 1.13.0 (28 September 2025)
|
||||
- #1358 TypeLoader: implement Try methods [feature]
|
||||
- #1361 ProxyUrlTransformer [feature]
|
||||
- #1360 ProxyURL path configuration [feature]
|
||||
# 1.16.0 (18 November 2025)
|
||||
- #1366 WireMock.Net.OpenApiParser : support Examples [feature]
|
||||
- #1375 Add WireMockHealthCheck in WireMock.Net.Aspire [feature]
|
||||
- #1377 Check if the path is valid when using WithPath(...) [feature]
|
||||
- #1380 Add WireMock.Net.xUnit.v3 project [feature]
|
||||
- #1364 Choosing examples from open api specification for responses. [feature]
|
||||
- #1376 AdminApiMappingBuilder `WithPath` should add the starting `/` if missing [feature]
|
||||
- #1379 xUnit v3 [feature]
|
||||
|
||||
The full release notes can be found here: https://github.com/wiremock/WireMock.Net/blob/master/CHANGELOG.md
|
||||
42
README.md
42
README.md
@@ -1,7 +1,11 @@
|
||||
#  WireMock.Net
|
||||
A C# .NET version based on [mock4net](https://github.com/alexvictoor/mock4net) which mimics the functionality from the JAVA based [WireMock](http://wiremock.org).
|
||||
A C# .NET version based on [mock4net](https://github.com/alexvictoor/mock4net) which mimics functionality from the original Java based WireMock.
|
||||
|
||||
For more info, see also this WIKI page: [What is WireMock.Net](https://github.com/wiremock/WireMock.Net/wiki/What-Is-WireMock.Net).
|
||||
---
|
||||
|
||||
### :books: Full documentation can now be found at [wiremock.org](https://wiremock.org/dotnet)
|
||||
|
||||
---
|
||||
|
||||
## :star: Key Features
|
||||
* HTTP response stubbing, matchable on URL/Path, headers, cookies and body content patterns
|
||||
@@ -37,7 +41,7 @@ For more info, see also this WIKI page: [What is WireMock.Net](https://github.co
|
||||
|
||||
### :package: NuGet packages
|
||||
|
||||
| | Official | Preview [:information_source:](https://github.com/wiremock/WireMock.Net/wiki/MyGet-preview-versions) |
|
||||
| | Official | Preview [:information_source:](https://wiremock.org/dotnet/MyGet-preview-versions) |
|
||||
| - | - | - |
|
||||
| **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)
|
||||
@@ -49,6 +53,7 @@ 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.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.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.Extensions.Routing** | [](https://www.nuget.org/packages/WireMock.Net.Extensions.Routing) | [](https://www.myget.org/feed/wiremock-net/package/nuget/WireMock.Net.Extensions.Routing)
|
||||
@@ -89,52 +94,55 @@ To still enable this feature, you need to add the `Environment` category to the
|
||||
---
|
||||
|
||||
## :memo: Development
|
||||
For the supported frameworks and build information, see [this](https://github.com/wiremock/WireMock.Net/wiki/Development-Information) page.
|
||||
For the supported frameworks and build information, see [this](https://wiremock.org/dotnet/development-information) page.
|
||||
|
||||
## :star: Stubbing
|
||||
A core feature of WireMock.Net is the ability to return predefined HTTP responses for requests matching criteria.
|
||||
See [Wiki : Stubbing](https://github.com/wiremock/WireMock.Net/wiki/Stubbing).
|
||||
See [Stubbing](https://wiremock.org/dotnet/stubbing).
|
||||
|
||||
## :star: Request Matching
|
||||
WireMock.Net support advanced request-matching logic, see [Wiki : Request Matching](https://github.com/wiremock/WireMock.Net/wiki/Request-Matching).
|
||||
WireMock.Net support advanced request-matching logic, see [Request Matching](https://wiremock.org/dotnet/request-matching).
|
||||
|
||||
## :star: Response Templating
|
||||
The response which is returned WireMock.Net can be changed using templating. This is described here [Wiki : Response Templating](https://github.com/wiremock/WireMock.Net/wiki/Response-Templating).
|
||||
The response which is returned WireMock.Net can be changed using templating. This is described here [Response Templating](https://wiremock.org/dotnet/response-templating).
|
||||
|
||||
## :star: Admin API Reference
|
||||
The WireMock admin API provides functionality to define the mappings via a http interface see [Wiki : Admin API Reference](https://github.com/StefH/WireMock.Net/wiki/Admin-API-Reference).
|
||||
The WireMock admin API provides functionality to define the mappings via a http interface see [Admin API Reference](https://wiremock.org/dotnet/admin-api-reference).
|
||||
|
||||
## :star: Using
|
||||
WireMock.Net can be used in several ways:
|
||||
|
||||
### UnitTesting
|
||||
You can use your favorite test framework and use WireMock within your tests, see
|
||||
[Wiki : UnitTesting](https://github.com/StefH/WireMock.Net/wiki/Using-WireMock-in-UnitTests).
|
||||
[UnitTesting](https://wiremock.org/dotnet/using-wiremock-in-unittests).
|
||||
|
||||
### Unit/Integration Testing using Testcontainers.DotNet
|
||||
See [Wiki : WireMock.Net.Testcontainers](https://github.com/wiremock/WireMock.Net/wiki/Using-WireMock.Net.Testcontainers) on how to build a WireMock.Net Docker container which can be used in Unit/Integration testing.
|
||||
See [WireMock.Net.Testcontainers](https://wiremock.org/dotnet/using-wiremock-net-testcontainers/) on how to build a WireMock.Net Docker container which can be used in Unit/Integration testing.
|
||||
|
||||
### Unit/Integration Testing using an an Aspire Distributed Application
|
||||
See [Wiki : WireMock.Net.Aspire](https://github.com/wiremock/WireMock.Net/wiki/Using-WireMock.Net.Aspire) on how to use WireMock.Net as an Aspire Hosted application to do Unit/Integration testing.
|
||||
See [WireMock.Net.Aspire](https://wiremock.org/dotnet/using-wiremock-net-Aspire) on how to use WireMock.Net as an Aspire Hosted application to do Unit/Integration testing.
|
||||
|
||||
### As a dotnet tool
|
||||
It's simple to install WireMock.Net as (global) dotnet tool, see [Wiki : dotnet tool](https://github.com/StefH/WireMock.Net/wiki/WireMock-as-dotnet-tool).
|
||||
It's simple to install WireMock.Net as (global) dotnet tool, see [dotnet tool](https://wiremock.org/dotnet/wiremock-as-dotnet-tool).
|
||||
|
||||
### As standalone process / console application
|
||||
This is quite straight forward to launch a mock server within a console application, see [Wiki : Standalone Process](https://github.com/StefH/WireMock.Net/wiki/WireMock-as-a-standalone-process).
|
||||
This is quite straight forward to launch a mock server within a console application, see [Standalone Process](https://wiremock.org/dotnet/wiremock-as-a-standalone-process).
|
||||
|
||||
### As a Windows Service
|
||||
You can also run WireMock.Net as a Windows Service, follow this [WireMock-as-a-Windows-Service](https://github.com/wiremock/WireMock.Net/wiki/WireMock-as-a-Windows-Service).
|
||||
You can also run WireMock.Net as a Windows Service, follow this [Windows Service](https://wiremock.org/dotnet/wiremock-as-a-windows-service).
|
||||
|
||||
### As a Web Job in Azure or application in IIS
|
||||
See this link [WireMock-as-a-(Azure)-Web-App](https://github.com/wiremock/WireMock.Net/wiki/WireMock-as-a-(Azure)-Web-App)
|
||||
See this link [WireMock-as-a-(Azure)-Web-App](https://wiremock.org/dotnet/wiremock-as-a-azure-web-app/)
|
||||
|
||||
### In a docker container
|
||||
There is also a Linux and Windows-Nano container available at [hub.docker.com](https://hub.docker.com/r/sheyenrath).
|
||||
For more details see also [Docker](https://github.com/wiremock/WireMock.Net-docker).
|
||||
|
||||
#### HTTPS / SSL
|
||||
More details on using HTTPS (SSL) can be found here [Wiki : HTTPS](https://github.com/wiremock/WireMock.Net/wiki/Using-HTTPS-(SSL))
|
||||
### HTTPS / SSL
|
||||
More details on using HTTPS (SSL) can be found here [HTTPS](https://wiremock.org/dotnet/using-https-ssl/)
|
||||
|
||||
## :books: Documentation
|
||||
For more info, see also this documentation page: [What is WireMock.Net](https://wiremock.org/dotnet/what-is-wiremock-net/).
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.0.31521.260
|
||||
# Visual Studio Version 18
|
||||
VisualStudioVersion = 18.0.11205.157 d18.0
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{8F890C6F-9ACC-438D-928A-AD61CDA862F2}"
|
||||
EndProject
|
||||
@@ -144,6 +144,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WireMock.Net.Extensions.Rou
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WireMock.Net.ProtoBuf", "src\WireMock.Net.ProtoBuf\WireMock.Net.ProtoBuf.csproj", "{B47413AA-55D3-49A7-896A-17ADBFF72407}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WireMock.Net.xUnit.v3", "src\WireMock.Net.xUnit.v3\WireMock.Net.xUnit.v3.csproj", "{4F46BD02-BEBC-4B2D-B857-4169AD1FB067}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
@@ -350,6 +352,10 @@ Global
|
||||
{B47413AA-55D3-49A7-896A-17ADBFF72407}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{B47413AA-55D3-49A7-896A-17ADBFF72407}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{B47413AA-55D3-49A7-896A-17ADBFF72407}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{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
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
@@ -407,6 +413,7 @@ Global
|
||||
{3FCBCA9C-9DB0-4A96-B47E-30470764CC9C} = {0BB8B634-407A-4610-A91F-11586990767A}
|
||||
{1E874C8F-08A2-493B-8421-619F9A6E9E77} = {8F890C6F-9ACC-438D-928A-AD61CDA862F2}
|
||||
{B47413AA-55D3-49A7-896A-17ADBFF72407} = {8F890C6F-9ACC-438D-928A-AD61CDA862F2}
|
||||
{4F46BD02-BEBC-4B2D-B857-4169AD1FB067} = {8F890C6F-9ACC-438D-928A-AD61CDA862F2}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {DC539027-9852-430C-B19F-FD035D018458}
|
||||
|
||||
@@ -45,6 +45,7 @@ IResourceBuilder<WireMockServerResource> apiService = builder
|
||||
|
||||
builder.AddProject<Projects.AspireApp1_Web>("webfrontend")
|
||||
.WithExternalHttpEndpoints()
|
||||
.WithReference(apiService);
|
||||
.WithReference(apiService)
|
||||
.WaitFor(apiService);
|
||||
|
||||
builder.Build().Run();
|
||||
@@ -267,7 +267,7 @@ namespace WireMock.Net.ConsoleApplication
|
||||
}
|
||||
}
|
||||
|
||||
public static void Run()
|
||||
public static async Task RunAsync()
|
||||
{
|
||||
//RunSse();
|
||||
//RunOnLocal();
|
||||
@@ -290,25 +290,56 @@ namespace WireMock.Net.ConsoleApplication
|
||||
|
||||
var server = WireMockServer.Start();
|
||||
|
||||
//server
|
||||
// .Given(Request.Create()
|
||||
// .WithPath("todos")
|
||||
// .UsingGet()
|
||||
// )
|
||||
// .RespondWith(Response.Create()
|
||||
// .WithBodyAsJson(todos.Values)
|
||||
// );
|
||||
|
||||
//server
|
||||
// .Given(Request.Create()
|
||||
// .UsingGet()
|
||||
// .WithPath("todos")
|
||||
// .WithParam("id")
|
||||
// )
|
||||
// .RespondWith(Response.Create()
|
||||
// .WithBodyAsJson(rm => todos[int.Parse(rm.Query!["id"].ToString())])
|
||||
// );
|
||||
|
||||
var pX = 0.80;
|
||||
server
|
||||
.Given(Request.Create()
|
||||
.WithPath("todos")
|
||||
.UsingGet()
|
||||
)
|
||||
.RespondWith(Response.Create()
|
||||
.WithBodyAsJson(todos.Values)
|
||||
);
|
||||
.Given(Request.Create().UsingGet().WithPath("/p"))
|
||||
.WithProbability(pX)
|
||||
.RespondWith(Response.Create().WithStatusCode(200).WithBody("X"));
|
||||
|
||||
server
|
||||
.Given(Request.Create()
|
||||
.UsingGet()
|
||||
.WithPath("todos")
|
||||
.WithParam("id")
|
||||
)
|
||||
.RespondWith(Response.Create()
|
||||
.WithBodyAsJson(rm => todos[int.Parse(rm.Query!["id"].ToString())])
|
||||
);
|
||||
.Given(Request.Create().UsingGet().WithPath("/p"))
|
||||
.RespondWith(Response.Create().WithStatusCode(200).WithBody("default"));
|
||||
|
||||
// Act
|
||||
var requestUri = new Uri($"http://localhost:{server.Port}/p");
|
||||
var c = server.CreateClient();
|
||||
var xCount = 0;
|
||||
var defaultCount = 0;
|
||||
var tot = 1000;
|
||||
for (var i = 0; i < tot; i++)
|
||||
{
|
||||
var response = await c.GetAsync(requestUri);
|
||||
var value = await response.Content.ReadAsStringAsync();
|
||||
if (value == "X")
|
||||
{
|
||||
xCount++;
|
||||
}
|
||||
else if (value == "default")
|
||||
{
|
||||
defaultCount++;
|
||||
}
|
||||
}
|
||||
System.Console.WriteLine("X = {0} ; default = {1} ; pX = {2:0.00} ; valueX = {3:0.00}", xCount, defaultCount, pX, 1.0 * xCount / tot);
|
||||
return;
|
||||
using var httpAndHttpsWithPort = WireMockServer.Start(new WireMockServerSettings
|
||||
{
|
||||
HostingScheme = HostingScheme.HttpAndHttps,
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using log4net;
|
||||
using log4net.Config;
|
||||
using log4net.Repository;
|
||||
@@ -14,10 +15,10 @@ static class Program
|
||||
private static readonly ILoggerRepository LogRepository = LogManager.GetRepository(Assembly.GetEntryAssembly());
|
||||
private static readonly ILog Log = LogManager.GetLogger(typeof(Program));
|
||||
|
||||
static void Main(params string[] args)
|
||||
static async Task Main(params string[] args)
|
||||
{
|
||||
XmlConfigurator.Configure(LogRepository, new FileInfo("log4net.config"));
|
||||
|
||||
MainApp.Run();
|
||||
await MainApp.RunAsync();
|
||||
}
|
||||
}
|
||||
@@ -22,7 +22,7 @@ public class MatcherModel
|
||||
public object? Pattern { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the patterns. Can be array of strings (default) or an array of objects.
|
||||
/// Gets or sets the patterns. Can be an array of strings (default) or an array of objects.
|
||||
/// </summary>
|
||||
public object[]? Patterns { get; set; }
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using WireMock.Types;
|
||||
|
||||
namespace WireMock.Admin.Settings;
|
||||
|
||||
/// <summary>
|
||||
@@ -11,15 +13,25 @@ public class ProxyUrlReplaceSettingsModel
|
||||
/// <summary>
|
||||
/// The old path value to be replaced by the new path value
|
||||
/// </summary>
|
||||
public string OldValue { get; set; } = null!;
|
||||
public string? OldValue { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The new path value to replace the old value with
|
||||
/// </summary>
|
||||
public string NewValue { get; set; } = null!;
|
||||
public string? NewValue { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Defines if the case should be ignore when replacing.
|
||||
/// Defines if the case should be ignored when replacing.
|
||||
/// </summary>
|
||||
public bool IgnoreCase { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Holds the transformation template.
|
||||
/// </summary>
|
||||
public string? TransformTemplate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The transformer type.
|
||||
/// </summary>
|
||||
public TransformerType TransformerType { get; set; } = TransformerType.Handlebars;
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System;
|
||||
using WireMock.Validators;
|
||||
|
||||
// ReSharper disable once CheckNamespace
|
||||
namespace WireMock.Admin.Mappings;
|
||||
@@ -94,9 +95,14 @@ public partial class RequestModelBuilder
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the Path.
|
||||
/// Set the Path. Must start with a forward slash (/).
|
||||
/// </summary>
|
||||
public RequestModelBuilder WithPath(string value) => WithPath(() => value);
|
||||
public RequestModelBuilder WithPath(string value)
|
||||
{
|
||||
PathValidator.ValidateAndThrow(value);
|
||||
|
||||
return WithPath(() => value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the Path.
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
10
src/WireMock.Net.Aspire/WireMockMappingState.cs
Normal file
10
src/WireMock.Net.Aspire/WireMockMappingState.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
namespace WireMock.Net.Aspire;
|
||||
|
||||
internal enum WireMockMappingState
|
||||
{
|
||||
NoMappings,
|
||||
NotSubmitted,
|
||||
Submitted,
|
||||
}
|
||||
@@ -3,6 +3,7 @@
|
||||
using Aspire.Hosting.ApplicationModel;
|
||||
using Aspire.Hosting.Lifecycle;
|
||||
using Aspire.Hosting.WireMock;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Diagnostics.HealthChecks;
|
||||
using Stef.Validation;
|
||||
using WireMock.Client.Builders;
|
||||
@@ -53,11 +54,21 @@ public static class WireMockServerBuilderExtensions
|
||||
Guard.NotNull(arguments);
|
||||
|
||||
var wireMockContainerResource = new WireMockServerResource(name, arguments);
|
||||
|
||||
var healthCheckKey = $"{name}_check";
|
||||
var healthCheckRegistration = new HealthCheckRegistration(
|
||||
healthCheckKey,
|
||||
_ => new WireMockHealthCheck(wireMockContainerResource),
|
||||
failureStatus: null,
|
||||
tags: null);
|
||||
builder.Services.AddHealthChecks().Add(healthCheckRegistration);
|
||||
|
||||
var resourceBuilder = builder
|
||||
.AddResource(wireMockContainerResource)
|
||||
.WithImage(DefaultLinuxImage)
|
||||
.WithEnvironment(ctx => ctx.EnvironmentVariables.Add("DOTNET_USE_POLLING_FILE_WATCHER", "1")) // https://khalidabuhakmeh.com/aspnet-docker-gotchas-and-workarounds#configuration-reloads-and-filesystemwatcher
|
||||
.WithHttpEndpoint(port: arguments.HttpPort, targetPort: WireMockServerArguments.HttpContainerPort)
|
||||
.WithHealthCheck(healthCheckKey)
|
||||
.WithWireMockInspectorCommand();
|
||||
|
||||
if (!string.IsNullOrEmpty(arguments.MappingsPath))
|
||||
@@ -172,6 +183,7 @@ public static class WireMockServerBuilderExtensions
|
||||
|
||||
wiremock.ApplicationBuilder.Services.TryAddLifecycleHook<WireMockServerLifecycleHook>();
|
||||
wiremock.Resource.Arguments.ApiMappingBuilder = configure;
|
||||
wiremock.Resource.ApiMappingState = WireMockMappingState.NotSubmitted;
|
||||
|
||||
return wiremock;
|
||||
}
|
||||
|
||||
@@ -10,32 +10,47 @@ internal class WireMockServerLifecycleHook(ILoggerFactory loggerFactory) : IDist
|
||||
{
|
||||
private readonly CancellationTokenSource _shutdownCts = new();
|
||||
|
||||
public async Task AfterResourcesCreatedAsync(DistributedApplicationModel appModel, CancellationToken cancellationToken = default)
|
||||
private CancellationTokenSource? _linkedCts;
|
||||
private Task? _mappingTask;
|
||||
|
||||
public Task AfterEndpointsAllocatedAsync(DistributedApplicationModel appModel, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var cts = CancellationTokenSource.CreateLinkedTokenSource(_shutdownCts.Token, cancellationToken);
|
||||
_linkedCts = CancellationTokenSource.CreateLinkedTokenSource(_shutdownCts.Token, cancellationToken);
|
||||
|
||||
var wireMockServerResources = appModel.Resources
|
||||
.OfType<WireMockServerResource>()
|
||||
.ToArray();
|
||||
|
||||
foreach (var wireMockServerResource in wireMockServerResources)
|
||||
_mappingTask = Task.Run(async () =>
|
||||
{
|
||||
wireMockServerResource.SetLogger(loggerFactory.CreateLogger<WireMockServerResource>());
|
||||
var wireMockServerResources = appModel.Resources
|
||||
.OfType<WireMockServerResource>()
|
||||
.ToArray();
|
||||
|
||||
var endpoint = wireMockServerResource.GetEndpoint();
|
||||
if (endpoint.IsAllocated)
|
||||
foreach (var wireMockServerResource in wireMockServerResources)
|
||||
{
|
||||
await wireMockServerResource.WaitForHealthAsync(cts.Token);
|
||||
wireMockServerResource.SetLogger(loggerFactory.CreateLogger<WireMockServerResource>());
|
||||
|
||||
await wireMockServerResource.CallApiMappingBuilderActionAsync(cts.Token);
|
||||
var endpoint = wireMockServerResource.GetEndpoint();
|
||||
System.Diagnostics.Debug.Assert(endpoint.IsAllocated);
|
||||
|
||||
wireMockServerResource.StartWatchingStaticMappings(cts.Token);
|
||||
await wireMockServerResource.WaitForHealthAsync(_linkedCts.Token);
|
||||
|
||||
await wireMockServerResource.CallApiMappingBuilderActionAsync(_linkedCts.Token);
|
||||
|
||||
wireMockServerResource.StartWatchingStaticMappings(_linkedCts.Token);
|
||||
}
|
||||
}
|
||||
}, _linkedCts.Token);
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public async ValueTask DisposeAsync()
|
||||
{
|
||||
await _shutdownCts.CancelAsync();
|
||||
|
||||
_linkedCts?.Dispose();
|
||||
_shutdownCts.Dispose();
|
||||
|
||||
if (_mappingTask is not null)
|
||||
{
|
||||
await _mappingTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ using RestEase;
|
||||
using Stef.Validation;
|
||||
using WireMock.Client;
|
||||
using WireMock.Client.Extensions;
|
||||
using WireMock.Net.Aspire;
|
||||
using WireMock.Util;
|
||||
|
||||
// ReSharper disable once CheckNamespace
|
||||
@@ -19,6 +20,7 @@ public class WireMockServerResource : ContainerResource, IResourceWithServiceDis
|
||||
|
||||
internal WireMockServerArguments Arguments { get; }
|
||||
internal Lazy<IWireMockAdminApi> AdminApi => new(CreateWireMockAdminApi);
|
||||
internal WireMockMappingState ApiMappingState { get; set; } = WireMockMappingState.NoMappings;
|
||||
|
||||
private ILogger? _logger;
|
||||
private EnhancedFileSystemWatcher? _enhancedFileSystemWatcher;
|
||||
@@ -64,6 +66,8 @@ public class WireMockServerResource : ContainerResource, IResourceWithServiceDis
|
||||
|
||||
var mappingBuilder = AdminApi.Value.GetMappingBuilder();
|
||||
await Arguments.ApiMappingBuilder.Invoke(mappingBuilder, cancellationToken);
|
||||
|
||||
ApiMappingState = WireMockMappingState.Submitted;
|
||||
}
|
||||
|
||||
internal void StartWatchingStaticMappings(CancellationToken cancellationToken)
|
||||
|
||||
@@ -15,8 +15,13 @@ internal static class HttpClientBuilder
|
||||
var handler = new HttpClientHandler
|
||||
{
|
||||
CheckCertificateRevocationList = false,
|
||||
#if NET5_0_OR_GREATER
|
||||
SslProtocols = System.Security.Authentication.SslProtocols.Tls13 | System.Security.Authentication.SslProtocols.Tls12 | System.Security.Authentication.SslProtocols.Tls11 | System.Security.Authentication.SslProtocols.Tls,
|
||||
#else
|
||||
SslProtocols = System.Security.Authentication.SslProtocols.Tls12 | System.Security.Authentication.SslProtocols.Tls11 | System.Security.Authentication.SslProtocols.Tls,
|
||||
#endif
|
||||
ServerCertificateCustomValidationCallback = (message, cert, chain, errors) => true,
|
||||
|
||||
AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate
|
||||
};
|
||||
#elif NET46
|
||||
@@ -62,7 +67,10 @@ internal static class HttpClientBuilder
|
||||
}
|
||||
}
|
||||
|
||||
#if !NETSTANDARD1_3
|
||||
#if NET5_0_OR_GREATER
|
||||
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls13 | SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls;
|
||||
ServicePointManager.ServerCertificateValidationCallback = (message, cert, chain, errors) => true;
|
||||
#elif !NETSTANDARD1_3
|
||||
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls;
|
||||
ServicePointManager.ServerCertificateValidationCallback = (message, cert, chain, errors) => true;
|
||||
#endif
|
||||
|
||||
@@ -9,16 +9,10 @@ using WireMock.Services;
|
||||
|
||||
namespace WireMock.Owin;
|
||||
|
||||
internal class MappingMatcher : IMappingMatcher
|
||||
internal class MappingMatcher(IWireMockMiddlewareOptions options, IRandomizerDoubleBetween0And1 randomizerDoubleBetween0And1) : IMappingMatcher
|
||||
{
|
||||
private readonly IWireMockMiddlewareOptions _options;
|
||||
private readonly IRandomizerDoubleBetween0And1 _randomizerDoubleBetween0And1;
|
||||
|
||||
public MappingMatcher(IWireMockMiddlewareOptions options, IRandomizerDoubleBetween0And1 randomizerDoubleBetween0And1)
|
||||
{
|
||||
_options = Guard.NotNull(options);
|
||||
_randomizerDoubleBetween0And1 = Guard.NotNull(randomizerDoubleBetween0And1);
|
||||
}
|
||||
private readonly IWireMockMiddlewareOptions _options = Guard.NotNull(options);
|
||||
private readonly IRandomizerDoubleBetween0And1 _randomizerDoubleBetween0And1 = Guard.NotNull(randomizerDoubleBetween0And1);
|
||||
|
||||
public (MappingMatcherResult? Match, MappingMatcherResult? Partial) FindBestMatch(RequestMessage request)
|
||||
{
|
||||
@@ -28,7 +22,7 @@ internal class MappingMatcher : IMappingMatcher
|
||||
|
||||
var mappings = _options.Mappings.Values
|
||||
.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();
|
||||
|
||||
foreach (var mapping in mappings)
|
||||
@@ -41,10 +35,10 @@ internal class MappingMatcher : IMappingMatcher
|
||||
|
||||
var exceptions = mappingMatcherResult.RequestMatchResult.MatchDetails
|
||||
.Where(md => md.Exception != null)
|
||||
.Select(md => md.Exception)
|
||||
.Select(md => md.Exception!)
|
||||
.ToArray();
|
||||
|
||||
if (!exceptions.Any())
|
||||
if (exceptions.Length == 0)
|
||||
{
|
||||
possibleMappings.Add(mappingMatcherResult);
|
||||
}
|
||||
@@ -52,7 +46,7 @@ internal class MappingMatcher : IMappingMatcher
|
||||
{
|
||||
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)
|
||||
.OrderBy(m => m.RequestMatchResult)
|
||||
.ThenBy(m => m.RequestMatchResult.TotalNumber)
|
||||
.ThenBy(m => m.Mapping.Priority)
|
||||
.ThenByDescending(m => m.Mapping.Probability)
|
||||
.ThenByDescending(m => m.Mapping.UpdatedAt)
|
||||
.ToList();
|
||||
var partialMatch = partialMappings.FirstOrDefault(pm => pm.RequestMatchResult.AverageTotalScore > 0.0);
|
||||
.Where(pm => pm.RequestMatchResult.AverageTotalScore > 0.0)
|
||||
.ToArray();
|
||||
var partialMatch = partialMatches.FirstOrDefault();
|
||||
|
||||
if (_options.AllowPartialMapping == true)
|
||||
{
|
||||
@@ -78,7 +74,11 @@ internal class MappingMatcher : IMappingMatcher
|
||||
|
||||
var match = possibleMappings
|
||||
.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();
|
||||
|
||||
return (match, partialMatch);
|
||||
|
||||
@@ -4,6 +4,7 @@ using System;
|
||||
using Stef.Validation;
|
||||
using WireMock.Matchers;
|
||||
using WireMock.Matchers.Request;
|
||||
using WireMock.Validators;
|
||||
|
||||
namespace WireMock.RequestBuilders;
|
||||
|
||||
@@ -34,6 +35,10 @@ public partial class Request
|
||||
public IRequestBuilder WithPath(MatchOperator matchOperator, params string[] paths)
|
||||
{
|
||||
Guard.NotNullOrEmpty(paths);
|
||||
foreach (var path in paths)
|
||||
{
|
||||
PathValidator.ValidateAndThrow(path, nameof(paths));
|
||||
}
|
||||
|
||||
_requestMatchers.Add(new RequestMessagePathMatcher(MatchBehaviour.AcceptOnMatch, matchOperator, paths));
|
||||
return this;
|
||||
|
||||
@@ -36,7 +36,7 @@ public static class WireMockServerSettingsParser
|
||||
|
||||
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;
|
||||
return false;
|
||||
}
|
||||
@@ -202,14 +202,27 @@ public static class WireMockServerSettingsParser
|
||||
|
||||
private static void ParseProxyUrlReplaceSettings(ProxyAndRecordSettings settings, SimpleSettingsParser parser)
|
||||
{
|
||||
var proxyUrlReplaceOldValue = parser.GetStringValue("ProxyUrlReplaceOldValue");
|
||||
var proxyUrlReplaceNewValue = parser.GetStringValue("ProxyUrlReplaceNewValue");
|
||||
const string prefix = "ProxyUrlReplace";
|
||||
var proxyUrlReplaceOldValue = parser.GetStringValue($"{prefix}OldValue");
|
||||
var proxyUrlReplaceNewValue = parser.GetStringValue($"{prefix}NewValue");
|
||||
if (!string.IsNullOrEmpty(proxyUrlReplaceOldValue) && proxyUrlReplaceNewValue != null)
|
||||
{
|
||||
settings.ReplaceSettings = new ProxyUrlReplaceSettings
|
||||
{
|
||||
OldValue = proxyUrlReplaceOldValue!,
|
||||
NewValue = proxyUrlReplaceNewValue
|
||||
OldValue = proxyUrlReplaceOldValue,
|
||||
NewValue = proxyUrlReplaceNewValue,
|
||||
IgnoreCase = parser.GetBoolValue($"{prefix}IgnoreCase")
|
||||
};
|
||||
return;
|
||||
}
|
||||
|
||||
var transformTemplate = parser.GetStringValue($"{prefix}TransformTemplate");
|
||||
if (!string.IsNullOrEmpty(transformTemplate))
|
||||
{
|
||||
settings.ReplaceSettings = new ProxyUrlReplaceSettings
|
||||
{
|
||||
TransformTemplate = transformTemplate,
|
||||
TransformerType = parser.GetEnumValue($"{prefix}TransformerType", TransformerType.Handlebars)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,18 +19,12 @@ using SystemTextJsonSerializer = System.Text.Json.JsonSerializer;
|
||||
|
||||
namespace WireMock.Net.OpenApiParser.Mappers;
|
||||
|
||||
internal class OpenApiPathsMapper
|
||||
internal class OpenApiPathsMapper(WireMockOpenApiParserSettings settings)
|
||||
{
|
||||
private const string HeaderContentType = "Content-Type";
|
||||
|
||||
private readonly WireMockOpenApiParserSettings _settings;
|
||||
private readonly ExampleValueGenerator _exampleValueGenerator;
|
||||
|
||||
public OpenApiPathsMapper(WireMockOpenApiParserSettings settings)
|
||||
{
|
||||
_settings = Guard.NotNull(settings);
|
||||
_exampleValueGenerator = new ExampleValueGenerator(settings);
|
||||
}
|
||||
private readonly WireMockOpenApiParserSettings _settings = Guard.NotNull(settings);
|
||||
private readonly ExampleValueGenerator _exampleValueGenerator = new(settings);
|
||||
|
||||
public IReadOnlyList<MappingModel> ToMappingModels(OpenApiPaths? paths, IList<OpenApiServer> servers)
|
||||
{
|
||||
@@ -41,7 +35,7 @@ internal class OpenApiPathsMapper
|
||||
.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() ?? [];
|
||||
}
|
||||
@@ -50,35 +44,7 @@ internal class OpenApiPathsMapper
|
||||
{
|
||||
var queryParameters = operation.Parameters?.Where(p => p.In == ParameterLocation.Query) ?? [];
|
||||
var pathParameters = operation.Parameters?.Where(p => p.In == ParameterLocation.Path) ?? [];
|
||||
var headers = 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;
|
||||
}
|
||||
var requestHeaders = operation.Parameters?.Where(p => p.In == ParameterLocation.Header) ?? [];
|
||||
|
||||
return new MappingModel
|
||||
{
|
||||
@@ -88,15 +54,94 @@ internal class OpenApiPathsMapper
|
||||
Methods = [httpMethod],
|
||||
Path = PathUtils.Combine(MapBasePath(servers), MapPathWithParameters(path, pathParameters)),
|
||||
Params = MapQueryParameters(queryParameters),
|
||||
Headers = MapRequestHeaders(headers),
|
||||
Body = requestBodyModel
|
||||
Headers = MapRequestHeaders(requestHeaders),
|
||||
Body = GetRequestBodyModel(operation.RequestBody)
|
||||
},
|
||||
Response = new ResponseModel
|
||||
{
|
||||
StatusCode = httpStatusCode,
|
||||
Headers = MapHeaders(responseContentType, response.Value?.Headers),
|
||||
BodyAsJson = responseBody != null ? JsonConvert.DeserializeObject(SystemTextJsonSerializer.Serialize(responseBody)) : null
|
||||
}
|
||||
Response = GetResponseModel(operation.Responses?.FirstOrDefault())
|
||||
};
|
||||
}
|
||||
|
||||
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
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Stef.Validation" Version="0.1.1" />
|
||||
<PackageReference Include="Testcontainers" Version="4.7.0" />
|
||||
<PackageReference Include="Testcontainers" Version="4.8.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -128,12 +128,14 @@ public sealed class WireMockContainer : DockerContainer
|
||||
/// </summary>
|
||||
/// <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="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="ct">Cancellation token.</param>
|
||||
/// <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))
|
||||
{
|
||||
|
||||
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>
|
||||
<TargetFrameworks>net472;net8.0</TargetFrameworks>
|
||||
<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>();
|
||||
await using var app = await appHostBuilder.BuildAsync();
|
||||
await app.StartAsync();
|
||||
await app.ResourceNotifications.WaitForResourceHealthyAsync("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>();
|
||||
await using var app = await appHostBuilder.BuildAsync();
|
||||
await app.StartAsync();
|
||||
await app.ResourceNotifications.WaitForResourceHealthyAsync("wiremock-service");
|
||||
|
||||
var adminClient = app.CreateWireMockAdminClient("wiremock-service");
|
||||
|
||||
|
||||
@@ -67,7 +67,7 @@ public class WireMockServerBuilderExtensionsTests
|
||||
MappingsPath = null,
|
||||
HttpPort = port
|
||||
});
|
||||
wiremock.Resource.Annotations.Should().HaveCount(5);
|
||||
wiremock.Resource.Annotations.Should().HaveCount(6);
|
||||
|
||||
var containerImageAnnotation = wiremock.Resource.Annotations.OfType<ContainerImageAnnotation>().FirstOrDefault();
|
||||
containerImageAnnotation.Should().BeEquivalentTo(new ContainerImageAnnotation
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
{
|
||||
Guid: 90356dba-b36c-469a-a17e-669cd84f1f06,
|
||||
UpdatedAt: DateTime_1,
|
||||
Request: {
|
||||
Path: {
|
||||
Matchers: [
|
||||
{
|
||||
Name: WildcardMatcher,
|
||||
Pattern: /1,
|
||||
IgnoreCase: false
|
||||
}
|
||||
]
|
||||
},
|
||||
Body: {
|
||||
Matcher: {
|
||||
Name: RegexMatcher,
|
||||
Pattern: hello,
|
||||
IgnoreCase: true
|
||||
}
|
||||
}
|
||||
},
|
||||
Response: {
|
||||
ProxyUrl: https://my-proxy.com,
|
||||
ProxyUrlReplaceSettings: {
|
||||
IgnoreCase: false,
|
||||
TransformTemplate: x{{this}}y
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -450,6 +450,55 @@ public partial class WireMockAdminApiTests
|
||||
server.Stop();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task IWireMockAdminApi_GetMappingAsync_WithProxy_And_ProxyUrlReplaceSettings_And_TransformTemplate()
|
||||
{
|
||||
// Arrange
|
||||
var guid = Guid.Parse("90356dba-b36c-469a-a17e-669cd84f1f06");
|
||||
var server = WireMockServer.StartWithAdminInterface();
|
||||
var api = RestClient.For<IWireMockAdminApi>(server.Url);
|
||||
|
||||
// Act
|
||||
var model = new MappingModel
|
||||
{
|
||||
Guid = guid,
|
||||
Request = new RequestModel
|
||||
{
|
||||
Path = "/1",
|
||||
Body = new BodyModel
|
||||
{
|
||||
Matcher = new MatcherModel
|
||||
{
|
||||
Name = "RegexMatcher",
|
||||
Pattern = "hello",
|
||||
IgnoreCase = true
|
||||
}
|
||||
}
|
||||
},
|
||||
Response = new ResponseModel
|
||||
{
|
||||
ProxyUrl = "https://my-proxy.com",
|
||||
ProxyUrlReplaceSettings = new ProxyUrlReplaceSettingsModel
|
||||
{
|
||||
TransformTemplate = "x{{this}}y"
|
||||
}
|
||||
}
|
||||
};
|
||||
var postMappingResult = await api.PostMappingAsync(model).ConfigureAwait(false);
|
||||
|
||||
// Assert
|
||||
postMappingResult.Should().NotBeNull();
|
||||
|
||||
var mapping = server.Mappings.FirstOrDefault(m => m.Guid == guid);
|
||||
mapping.Should().NotBeNull();
|
||||
|
||||
var getMappingResult = await api.GetMappingAsync(guid).ConfigureAwait(false);
|
||||
|
||||
await Verifier.Verify(getMappingResult, VerifySettings).DontScrubGuids();
|
||||
|
||||
server.Stop();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task IWireMockAdminApi_GetRequestsAsync_Json()
|
||||
{
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -9,7 +9,6 @@ using WireMock.Matchers.Request;
|
||||
using WireMock.Models;
|
||||
using WireMock.Owin;
|
||||
using WireMock.Services;
|
||||
using WireMock.Util;
|
||||
using Xunit;
|
||||
|
||||
namespace WireMock.Net.Tests.Owin;
|
||||
@@ -26,7 +25,7 @@ public class MappingMatcherTests
|
||||
_optionsMock = new Mock<IWireMockMiddlewareOptions>();
|
||||
_optionsMock.SetupAllProperties();
|
||||
_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>());
|
||||
|
||||
var loggerMock = new Mock<IWireMockLogger>();
|
||||
@@ -35,7 +34,7 @@ public class MappingMatcherTests
|
||||
_optionsMock.Setup(o => o.Logger).Returns(loggerMock.Object);
|
||||
|
||||
_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);
|
||||
}
|
||||
@@ -84,8 +83,8 @@ public class MappingMatcherTests
|
||||
var guid2 = Guid.Parse("00000000-0000-0000-0000-000000000002");
|
||||
var mappings = InitMappings
|
||||
(
|
||||
(guid1, new[] { 0.1 }, null),
|
||||
(guid2, new[] { 1.0 }, null)
|
||||
(guid1, [0.1], null),
|
||||
(guid2, [1.0], null)
|
||||
);
|
||||
_optionsMock.Setup(o => o.Mappings).Returns(mappings);
|
||||
|
||||
@@ -112,8 +111,8 @@ public class MappingMatcherTests
|
||||
var guid2 = Guid.Parse("00000000-0000-0000-0000-000000000002");
|
||||
var mappings = InitMappings
|
||||
(
|
||||
(guid1, new[] { 0.1 }, null),
|
||||
(guid2, new[] { 0.9 }, null)
|
||||
(guid1, [0.1], null),
|
||||
(guid2, [0.9], null)
|
||||
);
|
||||
_optionsMock.Setup(o => o.Mappings).Returns(mappings);
|
||||
|
||||
@@ -139,8 +138,8 @@ public class MappingMatcherTests
|
||||
|
||||
_optionsMock.SetupGet(o => o.AllowPartialMapping).Returns(true);
|
||||
var mappings = InitMappings(
|
||||
(guid1, new[] { 0.1 }, null),
|
||||
(guid2, new[] { 0.9 }, null)
|
||||
(guid1, [0.1], null),
|
||||
(guid2, [0.9], null)
|
||||
);
|
||||
_optionsMock.Setup(o => o.Mappings).Returns(mappings);
|
||||
|
||||
@@ -166,8 +165,8 @@ public class MappingMatcherTests
|
||||
var guid1 = Guid.Parse("00000000-0000-0000-0000-000000000001");
|
||||
var guid2 = Guid.Parse("00000000-0000-0000-0000-000000000002");
|
||||
var mappings = InitMappings(
|
||||
(guid1, new[] { 1.0 }, null),
|
||||
(guid2, new[] { 1.0, 1.0 }, null)
|
||||
(guid1, [1.0], null),
|
||||
(guid2, [1.0, 1.0], null)
|
||||
);
|
||||
_optionsMock.Setup(o => o.Mappings).Returns(mappings);
|
||||
|
||||
@@ -187,15 +186,15 @@ public class MappingMatcherTests
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MappingMatcher_FindBestMatch_WhenProbabilityFailsFirst_ShouldReturnSecondMatch()
|
||||
public void MappingMatcher_FindBestMatch_WhenProbabilityDoesNotMatch_ShouldReturnNormalMatch()
|
||||
{
|
||||
// Assign
|
||||
var guid1 = Guid.Parse("00000000-0000-0000-0000-000000000001");
|
||||
var guid2 = Guid.Parse("00000000-0000-0000-0000-000000000002");
|
||||
var withProbability = Guid.Parse("00000000-0000-0000-0000-000000000001");
|
||||
var noProbability = Guid.Parse("00000000-0000-0000-0000-000000000002");
|
||||
var mappings = InitMappings
|
||||
(
|
||||
(guid1, new[] { 1.0 }, 1.0),
|
||||
(guid2, new[] { 1.0 }, null)
|
||||
(withProbability, [1.0], 0.4),
|
||||
(noProbability, [1.0], null)
|
||||
);
|
||||
_optionsMock.Setup(o => o.Mappings).Returns(mappings);
|
||||
|
||||
@@ -206,8 +205,30 @@ public class MappingMatcherTests
|
||||
|
||||
// Assert
|
||||
result.Match.Should().NotBeNull();
|
||||
result.Match!.Mapping.Guid.Should().Be(guid2);
|
||||
result.Match.RequestMatchResult.AverageTotalScore.Should().Be(1.0);
|
||||
result.Match!.Mapping.Guid.Should().Be(noProbability);
|
||||
}
|
||||
|
||||
[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)
|
||||
|
||||
@@ -109,7 +109,7 @@ public partial class MappingConverterTests
|
||||
var guid = new Guid("8e7b9ab7-e18e-4502-8bc9-11e6679811cc");
|
||||
var request = Request.Create()
|
||||
.UsingGet()
|
||||
.WithPath("test_path")
|
||||
.WithPath("/test_path")
|
||||
.WithParam("q", "42")
|
||||
.WithClientIP("112.123.100.99")
|
||||
.WithHeader("h-key", "h-value")
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
builder
|
||||
.Given(Request.Create()
|
||||
.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"))
|
||||
.WithClientIP("112.123.100.99")
|
||||
.WithHeader("h-key", "h-value", true)
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
builder
|
||||
.Given(Request.Create()
|
||||
.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"))
|
||||
.WithClientIP("112.123.100.99")
|
||||
.WithHeader("h-key", "h-value", true)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
server
|
||||
.Given(Request.Create()
|
||||
.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"))
|
||||
.WithClientIP("112.123.100.99")
|
||||
.WithHeader("h-key", "h-value", true)
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
server
|
||||
.Given(Request.Create()
|
||||
.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"))
|
||||
.WithClientIP("112.123.100.99")
|
||||
.WithHeader("h-key", "h-value", true)
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
Matchers: [
|
||||
{
|
||||
Name: WildcardMatcher,
|
||||
Pattern: x,
|
||||
Pattern: /x,
|
||||
IgnoreCase: false
|
||||
}
|
||||
]
|
||||
|
||||
@@ -56,7 +56,7 @@ public class ProxyMappingConverterTests
|
||||
|
||||
var request = Request.Create()
|
||||
.UsingPost()
|
||||
.WithPath("x")
|
||||
.WithPath("/x")
|
||||
.WithParam("p1", "p1-v")
|
||||
.WithParam("p2", "p2-v")
|
||||
.WithHeader("Content-Type", new ContentTypeMatcher("text/plain"))
|
||||
|
||||
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");
|
||||
}
|
||||
}
|
||||
@@ -32,7 +32,7 @@ public partial class WireMockServerTests
|
||||
var response = await server.CreateClient().GetAsync(requestUri).ConfigureAwait(false);
|
||||
|
||||
// Assert
|
||||
Assert.True(new[] { HttpStatusCode.OK, HttpStatusCode.InternalServerError }.Contains(response.StatusCode));
|
||||
Assert.Contains(response.StatusCode, [HttpStatusCode.OK, HttpStatusCode.InternalServerError]);
|
||||
|
||||
server.Stop();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user