mirror of
https://github.com/wiremock/WireMock.Net.git
synced 2026-04-14 04:29:36 +02:00
Compare commits
31 Commits
1.5.22
...
stef-Ignor
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dabe3a2a10 | ||
|
|
1f1bc05f00 | ||
|
|
c107e38e3b | ||
|
|
b609191095 | ||
|
|
b1ae9aaf46 | ||
|
|
a77c4fe1ac | ||
|
|
ad3ef83c5e | ||
|
|
35ffbbc7d9 | ||
|
|
c1e71707c5 | ||
|
|
69499afe43 | ||
|
|
aadac78577 | ||
|
|
71393204cc | ||
|
|
e5cc6f570c | ||
|
|
7c3a0c815d | ||
|
|
e61f08fe48 | ||
|
|
11f4c47851 | ||
|
|
3956cd703b | ||
|
|
27682d0ce4 | ||
|
|
8444c8c506 | ||
|
|
6ef116a295 | ||
|
|
59195eaed8 | ||
|
|
7d9e450814 | ||
|
|
7019a5a78c | ||
|
|
d29f3e81f3 | ||
|
|
ccd8026884 | ||
|
|
1214ba5108 | ||
|
|
427715a38a | ||
|
|
d949dfb64c | ||
|
|
0a2763c06e | ||
|
|
9ef8bd0b7b | ||
|
|
090e0eb437 |
28
CHANGELOG.md
28
CHANGELOG.md
@@ -1,3 +1,31 @@
|
|||||||
|
# 1.5.27 (03 June 2023)
|
||||||
|
- [#946](https://github.com/WireMock-Net/WireMock.Net/pull/946) - Add warning logging when sending a request to a Webhook does not return status 200 [feature] contributed by [StefH](https://github.com/StefH)
|
||||||
|
- [#949](https://github.com/WireMock-Net/WireMock.Net/pull/949) - Add ".NET Framework 4.7" to WireMock.Net.FluentAssertions [feature] contributed by [StefH](https://github.com/StefH)
|
||||||
|
- [#928](https://github.com/WireMock-Net/WireMock.Net/issues/928) - TypeLoadException when using WithHeader method. [bug]
|
||||||
|
- [#945](https://github.com/WireMock-Net/WireMock.Net/issues/945) - Webhook logging [feature]
|
||||||
|
|
||||||
|
# 1.5.26 (25 May 2023)
|
||||||
|
- [#938](https://github.com/WireMock-Net/WireMock.Net/pull/938) - Add more unitests for CSharpFormatter utils [test] contributed by [StefH](https://github.com/StefH)
|
||||||
|
- [#939](https://github.com/WireMock-Net/WireMock.Net/pull/939) - WireMockMiddleware should use HandleRequestsSynchronously correctly [bug] contributed by [StefH](https://github.com/StefH)
|
||||||
|
- [#940](https://github.com/WireMock-Net/WireMock.Net/pull/940) - Code generator improvements contributed by [cezarypiatek](https://github.com/cezarypiatek)
|
||||||
|
- [#942](https://github.com/WireMock-Net/WireMock.Net/pull/942) - Add GetParameter method to IRequestMessage [feature] contributed by [StefH](https://github.com/StefH)
|
||||||
|
- [#941](https://github.com/WireMock-Net/WireMock.Net/issues/941) - RequestMessage.GetParameter method missing from IRequestMessage interface [feature]
|
||||||
|
|
||||||
|
# 1.5.25 (13 May 2023)
|
||||||
|
- [#934](https://github.com/WireMock-Net/WireMock.Net/pull/934) - Code generator improvements [feature] contributed by [cezarypiatek](https://github.com/cezarypiatek)
|
||||||
|
|
||||||
|
# 1.5.24 (07 May 2023)
|
||||||
|
- [#926](https://github.com/WireMock-Net/WireMock.Net/pull/926) - Fix C# mapping code generator for header names [bug] contributed by [cezarypiatek](https://github.com/cezarypiatek)
|
||||||
|
- [#927](https://github.com/WireMock-Net/WireMock.Net/pull/927) - Enrich generated code with status code [feature] contributed by [cezarypiatek](https://github.com/cezarypiatek)
|
||||||
|
- [#930](https://github.com/WireMock-Net/WireMock.Net/pull/930) - Update C# mapping code generator for WithStatusCode [feature] contributed by [StefH](https://github.com/StefH)
|
||||||
|
- [#931](https://github.com/WireMock-Net/WireMock.Net/pull/931) - Add property 'IsStartedWithAdminInterface' to 'IWireMockServer' [feature] contributed by [StefH](https://github.com/StefH)
|
||||||
|
- [#933](https://github.com/WireMock-Net/WireMock.Net/pull/933) - C# code generator improvements [feature] contributed by [cezarypiatek](https://github.com/cezarypiatek)
|
||||||
|
|
||||||
|
# 1.5.23 (23 April 2023)
|
||||||
|
- [#922](https://github.com/WireMock-Net/WireMock.Net/pull/922) - Add WithProbability [feature] contributed by [StefH](https://github.com/StefH)
|
||||||
|
- [#924](https://github.com/WireMock-Net/WireMock.Net/pull/924) - Allow removal of prefix when proxying to another server (#630) [feature] contributed by [nudejustin](https://github.com/nudejustin)
|
||||||
|
- [#925](https://github.com/WireMock-Net/WireMock.Net/pull/925) - Add IgnoreCase option to ProxyUrlReplaceSettings [feature] contributed by [StefH](https://github.com/StefH)
|
||||||
|
|
||||||
# 1.5.22 (08 April 2023)
|
# 1.5.22 (08 April 2023)
|
||||||
- [#914](https://github.com/WireMock-Net/WireMock.Net/pull/914) - #912 add excluded params to proxy mapping [feature] contributed by [walidhaidarii](https://github.com/walidhaidarii)
|
- [#914](https://github.com/WireMock-Net/WireMock.Net/pull/914) - #912 add excluded params to proxy mapping [feature] contributed by [walidhaidarii](https://github.com/walidhaidarii)
|
||||||
- [#916](https://github.com/WireMock-Net/WireMock.Net/pull/916) - Include WireMockOpenApiParser project [feature] contributed by [StefH](https://github.com/StefH)
|
- [#916](https://github.com/WireMock-Net/WireMock.Net/pull/916) - Include WireMockOpenApiParser project [feature] contributed by [StefH](https://github.com/StefH)
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<VersionPrefix>1.5.22</VersionPrefix>
|
<VersionPrefix>1.5.27</VersionPrefix>
|
||||||
<PackageIcon>WireMock.Net-Logo.png</PackageIcon>
|
<PackageIcon>WireMock.Net-Logo.png</PackageIcon>
|
||||||
<PackageProjectUrl>https://github.com/WireMock-Net/WireMock.Net</PackageProjectUrl>
|
<PackageProjectUrl>https://github.com/WireMock-Net/WireMock.Net</PackageProjectUrl>
|
||||||
<PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
|
<PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
|
||||||
@@ -12,6 +12,7 @@
|
|||||||
<RepositoryType>git</RepositoryType>
|
<RepositoryType>git</RepositoryType>
|
||||||
<RepositoryUrl>https://github.com/WireMock-Net/WireMock.Net</RepositoryUrl>
|
<RepositoryUrl>https://github.com/WireMock-Net/WireMock.Net</RepositoryUrl>
|
||||||
<ApplicationIcon>../../resources/WireMock.Net-Logo.ico</ApplicationIcon>
|
<ApplicationIcon>../../resources/WireMock.Net-Logo.ico</ApplicationIcon>
|
||||||
|
<PackageReadmeFile>PackageReadme.md</PackageReadmeFile>
|
||||||
<LangVersion>Latest</LangVersion>
|
<LangVersion>Latest</LangVersion>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
@@ -29,7 +30,7 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="../../resources/WireMock.Net-Logo.png" Pack="true" PackagePath="" />
|
<None Include="../../resources/WireMock.Net-Logo.png" Pack="true" PackagePath="" />
|
||||||
<!--<None Include="../../PackageReadme.md" Pack="true" PackagePath=""/>-->
|
<None Include="../../PackageReadme.md" Pack="true" PackagePath=""/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<Choose>
|
<Choose>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
rem https://github.com/StefH/GitHubReleaseNotes
|
rem https://github.com/StefH/GitHubReleaseNotes
|
||||||
|
|
||||||
SET version=1.5.22
|
SET version=1.5.27
|
||||||
|
|
||||||
GitHubReleaseNotes --output CHANGELOG.md --skip-empty-releases --exclude-labels question invalid doc duplicate --version %version% --token %GH_TOKEN%
|
GitHubReleaseNotes --output CHANGELOG.md --skip-empty-releases --exclude-labels question invalid doc duplicate --version %version% --token %GH_TOKEN%
|
||||||
|
|
||||||
|
|||||||
55
PackageReadme.md
Normal file
55
PackageReadme.md
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
## WireMock.Net
|
||||||
|
Lightweight Http Mocking Server for .NET, inspired by [WireMock(http://WireMock.org) from the Java landscape.
|
||||||
|
|
||||||
|
### :star: Key Features
|
||||||
|
* HTTP response stubbing, matchable on URL/Path, headers, cookies and body content patterns
|
||||||
|
* Library can be used in unit tests and integration tests
|
||||||
|
* Runs as a standalone process, as windows service, as Azure/IIS or as docker
|
||||||
|
* Configurable via a fluent C# .NET API, JSON files and JSON over HTTP
|
||||||
|
* Record/playback of stubs (proxying)
|
||||||
|
* Per-request conditional proxying
|
||||||
|
* Stateful behaviour simulation
|
||||||
|
* Response templating / transformation using Handlebars and extensions
|
||||||
|
* Can be used locally or in CI/CD scenarios
|
||||||
|
|
||||||
|
### :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-Net/WireMock.Net/wiki/Stubbing).
|
||||||
|
|
||||||
|
### :star: Request Matching
|
||||||
|
WireMock.Net support advanced request-matching logic, see [Wiki : Request Matching](https://github.com/WireMock-Net/WireMock.Net/wiki/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-Net/WireMock.Net/wiki/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).
|
||||||
|
|
||||||
|
### :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).
|
||||||
|
|
||||||
|
#### 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).
|
||||||
|
|
||||||
|
#### 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).
|
||||||
|
|
||||||
|
#### 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-Net/WireMock.Net/wiki/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-Net/WireMock.Net/wiki/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-Net/WireMock.Net-docker).
|
||||||
|
|
||||||
|
#### HTTPS / SSL
|
||||||
|
More details on using HTTPS (SSL) can be found here [Wiki : HTTPS](https://github.com/WireMock-Net/WireMock.Net/wiki/Using-HTTPS-(SSL))
|
||||||
|
|
||||||
|
## :books: Documentation
|
||||||
|
For more info, see also this WIKI page: [What is WireMock.Net](https://github.com/WireMock-Net/WireMock.Net/wiki/What-Is-WireMock.Net).
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
# 1.5.22 (08 April 2023)
|
# 1.5.27 (03 June 2023)
|
||||||
- #914 #912 add excluded params to proxy mapping [feature]
|
- #946 Add warning logging when sending a request to a Webhook does not return status 200 [feature]
|
||||||
- #916 Include WireMockOpenApiParser project [feature]
|
- #949 Add ".NET Framework 4.7" to WireMock.Net.FluentAssertions [feature]
|
||||||
- #912 Feature: adding excluded params to proxy and records settings [feature]
|
- #928 TypeLoadException when using WithHeader method. [bug]
|
||||||
|
- #945 Webhook logging [feature]
|
||||||
|
|
||||||
The full release notes can be found here: https://github.com/WireMock-Net/WireMock.Net/blob/master/CHANGELOG.md
|
The full release notes can be found here: https://github.com/WireMock-Net/WireMock.Net/blob/master/CHANGELOG.md
|
||||||
26
README.md
26
README.md
@@ -1,28 +1,28 @@
|
|||||||
# 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.org](http://WireMock.org).
|
A C# .NET version based on [mock4net](https://github.com/alexvictoor/mock4net) which mimics the functionality from the JAVA based [WireMock](http://WireMock.org).
|
||||||
|
|
||||||
For more info, see also this WIKI page: [What is WireMock.Net](https://github.com/WireMock-Net/WireMock.Net/wiki/What-Is-WireMock.Net).
|
For more info, see also this WIKI page: [What is WireMock.Net](https://github.com/WireMock-Net/WireMock.Net/wiki/What-Is-WireMock.Net).
|
||||||
|
|
||||||
## 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
|
||||||
* Library can be used in unit tests and integration tests
|
* Library can be used in unit tests and integration tests
|
||||||
* Runs as a standalone process, as windows service, as Azure/IIS or as docker
|
* Runs as a standalone process, as windows service, as Azure/IIS or as docker
|
||||||
* Configurable via a fluent DotNet API, JSON files and JSON over HTTP
|
* Configurable via a fluent C# .NET API, JSON files and JSON over HTTP
|
||||||
* Record/playback of stubs (proxying)
|
* Record/playback of stubs (proxying)
|
||||||
* Per-request conditional proxying
|
* Per-request conditional proxying
|
||||||
* Stateful behaviour simulation
|
* Stateful behaviour simulation
|
||||||
* Response templating / transformation using Handlebars and extensions
|
* Response templating / transformation using Handlebars and extensions
|
||||||
* Can be used locally or in CI/CD scenarios
|
* Can be used locally or in CI/CD scenarios
|
||||||
|
|
||||||
## Blogs
|
## :memo: Blogs
|
||||||
- [mStack.nl : Generate C# Code from Mapping(s)](https://mstack.nl/blog/20230201-wiremock.net-tocode/)
|
- [mStack.nl : Generate C# Code from Mapping(s)](https://mstack.nl/blog/20230201-wiremock.net-tocode/)
|
||||||
|
|
||||||
|
|
||||||
## Project Info
|
## :computer: Project Info
|
||||||
| | |
|
| | |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| ***Project*** | |
|
| ***Project*** | |
|
||||||
| **Chat** | [](https://gitter.im/wiremock_dotnet/Lobby) |
|
| **Chat** | [](https://slack.wiremock.org/) [](https://gitter.im/wiremock_dotnet/Lobby) |
|
||||||
| **Issues** | [](https://github.com/WireMock-Net/WireMock.Net/issues) |
|
| **Issues** | [](https://github.com/WireMock-Net/WireMock.Net/issues) |
|
||||||
| | |
|
| | |
|
||||||
| ***Quality*** | |
|
| ***Quality*** | |
|
||||||
@@ -31,7 +31,7 @@ For more info, see also this WIKI page: [What is WireMock.Net](https://github.co
|
|||||||
| **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-Net/WireMock.Net)|
|
| **Coverage** | [](https://sonarcloud.io/component_measures?id=WireMock-Net_WireMock.Net&metric=coverage) [](https://codecov.io/gh/WireMock-Net/WireMock.Net)|
|
||||||
|
|
||||||
### NuGet packages
|
### :package: NuGet packages
|
||||||
|
|
||||||
| | Official | Preview [:information_source:](https://github.com/WireMock-Net/WireMock.Net/wiki/MyGet-preview-versions) |
|
| | Official | Preview [:information_source:](https://github.com/WireMock-Net/WireMock.Net/wiki/MyGet-preview-versions) |
|
||||||
| - | - | - |
|
| - | - | - |
|
||||||
@@ -45,23 +45,23 @@ For more info, see also this WIKI page: [What is WireMock.Net](https://github.co
|
|||||||
| **WireMock.Org.RestClient** | [](https://www.nuget.org/packages/WireMock.Org.RestClient) | [](https://www.myget.org/feed/wiremock-net/package/nuget/WireMock.Org.RestClient)
|
| **WireMock.Org.RestClient** | [](https://www.nuget.org/packages/WireMock.Org.RestClient) | [](https://www.myget.org/feed/wiremock-net/package/nuget/WireMock.Org.RestClient)
|
||||||
|
|
||||||
|
|
||||||
## Development
|
## :memo: Development
|
||||||
For the supported frameworks and build information, see [this](https://github.com/WireMock-Net/WireMock.Net/wiki/Development-Information) page.
|
For the supported frameworks and build information, see [this](https://github.com/WireMock-Net/WireMock.Net/wiki/Development-Information) page.
|
||||||
|
|
||||||
## 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-Net/WireMock.Net/wiki/Stubbing).
|
See [Wiki : Stubbing](https://github.com/WireMock-Net/WireMock.Net/wiki/Stubbing).
|
||||||
|
|
||||||
## Request Matching
|
## :star: Request Matching
|
||||||
WireMock.Net support advanced request-matching logic, see [Wiki : Request Matching](https://github.com/WireMock-Net/WireMock.Net/wiki/Request-Matching).
|
WireMock.Net support advanced request-matching logic, see [Wiki : Request Matching](https://github.com/WireMock-Net/WireMock.Net/wiki/Request-Matching).
|
||||||
|
|
||||||
## 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-Net/WireMock.Net/wiki/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-Net/WireMock.Net/wiki/Response-Templating).
|
||||||
|
|
||||||
## 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 [Wiki : Admin API Reference](https://github.com/StefH/WireMock.Net/wiki/Admin-API-Reference).
|
||||||
|
|
||||||
## Using
|
## :star: Using
|
||||||
WireMock.Net can be used in several ways:
|
WireMock.Net can be used in several ways:
|
||||||
|
|
||||||
### UnitTesting
|
### UnitTesting
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
|
|||||||
.github\FUNDING.yml = .github\FUNDING.yml
|
.github\FUNDING.yml = .github\FUNDING.yml
|
||||||
Generate-ReleaseNotes.cmd = Generate-ReleaseNotes.cmd
|
Generate-ReleaseNotes.cmd = Generate-ReleaseNotes.cmd
|
||||||
nuget.config = nuget.config
|
nuget.config = nuget.config
|
||||||
|
PackageReadme.md = PackageReadme.md
|
||||||
PackageReleaseNotes.template = PackageReleaseNotes.template
|
PackageReleaseNotes.template = PackageReleaseNotes.template
|
||||||
PackageReleaseNotes.txt = PackageReleaseNotes.txt
|
PackageReleaseNotes.txt = PackageReleaseNotes.txt
|
||||||
README.md = README.md
|
README.md = README.md
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using WireMock.Net.OpenApiParser.Types;
|
||||||
using WireMock.RequestBuilders;
|
using WireMock.RequestBuilders;
|
||||||
using WireMock.ResponseBuilders;
|
using WireMock.ResponseBuilders;
|
||||||
|
|
||||||
@@ -18,7 +19,7 @@ class Program
|
|||||||
private static void RunMockServerWithDynamicExampleGeneration()
|
private static void RunMockServerWithDynamicExampleGeneration()
|
||||||
{
|
{
|
||||||
// Run your mocking framework specifying your Example Values generator class.
|
// Run your mocking framework specifying your Example Values generator class.
|
||||||
var serverCustomer_V2_json = Run.RunServer(Path.Combine(Folder, "Swagger_Customer_V2.0.json"), "http://localhost:8090/", true, new DynamicDataGeneration(), Types.ExampleValueType.Value, Types.ExampleValueType.Value);
|
var serverCustomer_V2_json = Run.RunServer(Path.Combine(Folder, "Swagger_Customer_V2.0.json"), "http://localhost:8090/", true, new DynamicDataGeneration(), ExampleValueType.Value, ExampleValueType.Value);
|
||||||
Console.WriteLine("Press any key to stop the servers");
|
Console.WriteLine("Press any key to stop the servers");
|
||||||
|
|
||||||
Console.ReadKey();
|
Console.ReadKey();
|
||||||
|
|||||||
@@ -1,19 +1,18 @@
|
|||||||
namespace WireMock.Admin.Mappings
|
namespace WireMock.Admin.Mappings;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Fault Model
|
||||||
|
/// </summary>
|
||||||
|
[FluentBuilder.AutoGenerateBuilder]
|
||||||
|
public class FaultModel
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Fault Model
|
/// Gets or sets the fault. Can be null, "", NONE, EMPTY_RESPONSE or MALFORMED_RESPONSE_CHUNK.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[FluentBuilder.AutoGenerateBuilder]
|
public string? Type { get; set; }
|
||||||
public class FaultModel
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the fault. Can be null, "", NONE, EMPTY_RESPONSE, MALFORMED_RESPONSE_CHUNK or RANDOM_DATA_THEN_CLOSE.
|
|
||||||
/// </summary>
|
|
||||||
public string Type { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the fault percentage.
|
/// Gets or sets the fault percentage.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public double? Percentage { get; set; }
|
public double? Percentage { get; set; }
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using WireMock.Models;
|
using WireMock.Models;
|
||||||
|
|
||||||
namespace WireMock.Admin.Mappings;
|
namespace WireMock.Admin.Mappings;
|
||||||
@@ -94,4 +93,9 @@ public class MappingModel
|
|||||||
/// </example>
|
/// </example>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public object? Data { get; set; }
|
public object? Data { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The probability when this request should be matched. Value is between 0 and 1. [Optional]
|
||||||
|
/// </summary>
|
||||||
|
public double? Probability { get; set; }
|
||||||
}
|
}
|
||||||
@@ -1,71 +1,76 @@
|
|||||||
namespace WireMock.Admin.Settings;
|
namespace WireMock.Admin.Settings;
|
||||||
|
|
||||||
[FluentBuilder.AutoGenerateBuilder]
|
[FluentBuilder.AutoGenerateBuilder]
|
||||||
public class ProxyAndRecordSettingsModel
|
public class ProxyAndRecordSettingsModel
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The clientCertificate thumbprint or subject name fragment to use.
|
/// The clientCertificate thumbprint or subject name fragment to use.
|
||||||
/// Example thumbprint : "D2DBF135A8D06ACCD0E1FAD9BFB28678DF7A9818". Example subject name: "www.google.com""
|
/// Example thumbprint : "D2DBF135A8D06ACCD0E1FAD9BFB28678DF7A9818". Example subject name: "www.google.com""
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string ClientX509Certificate2ThumbprintOrSubjectName { get; set; }
|
public string ClientX509Certificate2ThumbprintOrSubjectName { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Defines the WebProxySettings.
|
/// Defines the WebProxySettings.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public WebProxySettingsModel WebProxySettings { get; set; }
|
public WebProxySettingsModel WebProxySettings { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Proxy requests should follow redirection (30x).
|
/// Proxy requests should follow redirection (30x).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool? AllowAutoRedirect { get; set; }
|
public bool? AllowAutoRedirect { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The URL to proxy.
|
/// The URL to proxy.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string Url { get; set; }
|
public string Url { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Save the mapping for each request/response to the internal Mappings.
|
/// Save the mapping for each request/response to the internal Mappings.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool SaveMapping { get; set; }
|
public bool SaveMapping { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Save the mapping for each request/response also to a file. (Note that SaveMapping must also be set to true.)
|
/// Save the mapping for each request/response also to a file. (Note that SaveMapping must also be set to true.)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool SaveMappingToFile { get; set; }
|
public bool SaveMappingToFile { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Only save request/response to the internal Mappings if the status code is included in this pattern. (Note that SaveMapping must also be set to true.)
|
/// Only save request/response to the internal Mappings if the status code is included in this pattern. (Note that SaveMapping must also be set to true.)
|
||||||
/// The pattern can contain a single value like "200", but also ranges like "2xx", "100,300,600" or "100-299,6xx" are supported.
|
/// The pattern can contain a single value like "200", but also ranges like "2xx", "100,300,600" or "100-299,6xx" are supported.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string SaveMappingForStatusCodePattern { get; set; } = "*";
|
public string SaveMappingForStatusCodePattern { get; set; } = "*";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Defines a list from headers which will be excluded from the saved mappings.
|
/// Defines a list from headers which will be excluded from the saved mappings.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string[] ExcludedHeaders { get; set; }
|
public string[] ExcludedHeaders { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Defines a list of cookies which will be excluded from the saved mappings.
|
/// Defines a list of cookies which will be excluded from the saved mappings.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string[] ExcludedCookies { get; set; }
|
public string[] ExcludedCookies { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Prefer the Proxy Mapping over the saved Mapping (in case SaveMapping is set to <c>true</c>).
|
/// Prefer the Proxy Mapping over the saved Mapping (in case SaveMapping is set to <c>true</c>).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
// public bool PreferProxyMapping { get; set; }
|
// public bool PreferProxyMapping { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// When SaveMapping is set to <c>true</c>, this setting can be used to control the behavior of the generated request matchers for the new mapping.
|
/// When SaveMapping is set to <c>true</c>, this setting can be used to control the behavior of the generated request matchers for the new mapping.
|
||||||
/// - <c>false</c>, the default matchers will be used.
|
/// - <c>false</c>, the default matchers will be used.
|
||||||
/// - <c>true</c>, the defined mappings in the request wil be used for the new mapping.
|
/// - <c>true</c>, the defined mappings in the request wil be used for the new mapping.
|
||||||
///
|
///
|
||||||
/// Default value is false.
|
/// Default value is false.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool UseDefinedRequestMatchers { get; set; }
|
public bool UseDefinedRequestMatchers { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Append an unique GUID to the filename from the saved mapping file.
|
/// Append an unique GUID to the filename from the saved mapping file.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool AppendGuidToSavedMappingFile { get; set; }
|
public bool AppendGuidToSavedMappingFile { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Defines the Replace Settings
|
||||||
|
/// </summary>
|
||||||
|
public ProxyUrlReplaceSettingsModel? ReplaceSettings { get; set; }
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
namespace WireMock.Admin.Settings;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Defines an old path param and a new path param to be replaced when proxying.
|
||||||
|
/// </summary>
|
||||||
|
[FluentBuilder.AutoGenerateBuilder]
|
||||||
|
public class ProxyUrlReplaceSettingsModel
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The old path value to be replaced by the new path value
|
||||||
|
/// </summary>
|
||||||
|
public string OldValue { get; set; } = null!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The new path value to replace the old value with
|
||||||
|
/// </summary>
|
||||||
|
public string NewValue { get; set; } = null!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Defines if the case should be ignore when replacing.
|
||||||
|
/// </summary>
|
||||||
|
public bool IgnoreCase { get; set; }
|
||||||
|
}
|
||||||
@@ -143,6 +143,14 @@ public interface IRequestMessage
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
string Origin { get; }
|
string Origin { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get a query parameter.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key">The key.</param>
|
||||||
|
/// <param name="ignoreCase">Defines if the key should be matched using case-ignore.</param>
|
||||||
|
/// <returns>The query parameter value as WireMockList or null when not found.</returns>
|
||||||
|
WireMockList<string>? GetParameter(string key, bool ignoreCase = false);
|
||||||
|
|
||||||
#if NETSTANDARD1_3_OR_GREATER || NET461
|
#if NETSTANDARD1_3_OR_GREATER || NET461
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the connection's client certificate
|
/// Gets the connection's client certificate
|
||||||
|
|||||||
@@ -1,23 +1,23 @@
|
|||||||
namespace WireMock.ResponseBuilders
|
// ReSharper disable InconsistentNaming
|
||||||
|
namespace WireMock.ResponseBuilders;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The FaultType enumeration
|
||||||
|
/// </summary>
|
||||||
|
public enum FaultType
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The FaultType enumeration
|
/// No Fault
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public enum FaultType
|
NONE,
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// No Fault
|
|
||||||
/// </summary>
|
|
||||||
NONE,
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Return a completely empty response.
|
/// Return a completely empty response.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
EMPTY_RESPONSE,
|
EMPTY_RESPONSE,
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Send a defined status header, then garbage, then close the connection.
|
/// Send a defined status header, then garbage, then close the connection.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
MALFORMED_RESPONSE_CHUNK
|
MALFORMED_RESPONSE_CHUNK
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -17,6 +17,11 @@ public interface IWireMockServer : IDisposable
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
bool IsStarted { get; }
|
bool IsStarted { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a value indicating whether this server is started with the admin interface enabled.
|
||||||
|
/// </summary>
|
||||||
|
bool IsStartedWithAdminInterface { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the request logs.
|
/// Gets the request logs.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -1,49 +1,49 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<Description>FluentAssertions extensions for WireMock.Net</Description>
|
<Description>FluentAssertions extensions for WireMock.Net</Description>
|
||||||
<AssemblyTitle>WireMock.Net.FluentAssertions</AssemblyTitle>
|
<AssemblyTitle>WireMock.Net.FluentAssertions</AssemblyTitle>
|
||||||
<Authors>Mahmoud Ali;Stef Heyenrath</Authors>
|
<Authors>Mahmoud Ali;Stef Heyenrath</Authors>
|
||||||
<TargetFrameworks>net451;netstandard1.3;netstandard2.0;netstandard2.1</TargetFrameworks>
|
<TargetFrameworks>net451;net47;netstandard1.3;netstandard2.0;netstandard2.1</TargetFrameworks>
|
||||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||||
<AssemblyName>WireMock.Net.FluentAssertions</AssemblyName>
|
<AssemblyName>WireMock.Net.FluentAssertions</AssemblyName>
|
||||||
<PackageId>WireMock.Net.FluentAssertions</PackageId>
|
<PackageId>WireMock.Net.FluentAssertions</PackageId>
|
||||||
<PackageTags>wiremock;FluentAssertions;UnitTest;Assert;Assertions</PackageTags>
|
<PackageTags>wiremock;FluentAssertions;UnitTest;Assert;Assertions</PackageTags>
|
||||||
<RootNamespace>WireMock.FluentAssertions</RootNamespace>
|
<RootNamespace>WireMock.FluentAssertions</RootNamespace>
|
||||||
<ProjectGuid>{B6269AAC-170A-4346-8B9A-579DED3D9A95}</ProjectGuid>
|
<ProjectGuid>{B6269AAC-170A-4346-8B9A-579DED3D9A95}</ProjectGuid>
|
||||||
<PublishRepositoryUrl>true</PublishRepositoryUrl>
|
<PublishRepositoryUrl>true</PublishRepositoryUrl>
|
||||||
<AllowedOutputExtensionsInPackageBuildOutputFolder>$(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb</AllowedOutputExtensionsInPackageBuildOutputFolder>
|
<AllowedOutputExtensionsInPackageBuildOutputFolder>$(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb</AllowedOutputExtensionsInPackageBuildOutputFolder>
|
||||||
<EmbedUntrackedSources>true</EmbedUntrackedSources>
|
<EmbedUntrackedSources>true</EmbedUntrackedSources>
|
||||||
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
|
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
|
||||||
<GenerateBindingRedirectsOutputType>true</GenerateBindingRedirectsOutputType>
|
<GenerateBindingRedirectsOutputType>true</GenerateBindingRedirectsOutputType>
|
||||||
<CodeAnalysisRuleSet>../WireMock.Net/WireMock.Net.ruleset</CodeAnalysisRuleSet>
|
<CodeAnalysisRuleSet>../WireMock.Net/WireMock.Net.ruleset</CodeAnalysisRuleSet>
|
||||||
<SignAssembly>true</SignAssembly>
|
<SignAssembly>true</SignAssembly>
|
||||||
<AssemblyOriginatorKeyFile>../WireMock.Net/WireMock.Net.snk</AssemblyOriginatorKeyFile>
|
<AssemblyOriginatorKeyFile>../WireMock.Net/WireMock.Net.snk</AssemblyOriginatorKeyFile>
|
||||||
<!--<DelaySign>true</DelaySign>-->
|
<!--<DelaySign>true</DelaySign>-->
|
||||||
<PublicSign Condition=" '$(OS)' != 'Windows_NT' ">true</PublicSign>
|
<PublicSign Condition=" '$(OS)' != 'Windows_NT' ">true</PublicSign>
|
||||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||||
<LangVersion>10</LangVersion>
|
<LangVersion>10</LangVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
|
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
|
||||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="JetBrains.Annotations" Version="2022.3.1" PrivateAssets="All" />
|
<PackageReference Include="JetBrains.Annotations" Version="2022.3.1" PrivateAssets="All" />
|
||||||
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1" PrivateAssets="All" />
|
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1" PrivateAssets="All" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup Condition="'$(TargetFramework)' == 'net451' or '$(TargetFramework)' == 'netstandard1.3'">
|
<ItemGroup Condition="'$(TargetFramework)' == 'net451' or '$(TargetFramework)' == 'netstandard1.3'">
|
||||||
<PackageReference Include="FluentAssertions" Version="5.10.3" />
|
<PackageReference Include="FluentAssertions" Version="5.10.3" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup Condition="'$(TargetFramework)' != 'net451' and '$(TargetFramework)' != 'netstandard1.3'">
|
<ItemGroup Condition="'$(TargetFramework)' != 'net451' and '$(TargetFramework)' != 'netstandard1.3'">
|
||||||
<PackageReference Include="FluentAssertions" Version="6.5.1" />
|
<PackageReference Include="FluentAssertions" Version="6.5.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\WireMock.Net.Abstractions\WireMock.Net.Abstractions.csproj" />
|
<ProjectReference Include="..\WireMock.Net.Abstractions\WireMock.Net.Abstractions.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
@@ -23,12 +23,14 @@ internal class OpenApiPathsMapper
|
|||||||
private const string HeaderContentType = "Content-Type";
|
private const string HeaderContentType = "Content-Type";
|
||||||
|
|
||||||
private readonly WireMockOpenApiParserSettings _settings;
|
private readonly WireMockOpenApiParserSettings _settings;
|
||||||
private readonly ExampleValueGenerator _exampleValueGenerator;
|
private readonly IExampleValueGenerator _exampleValueGenerator;
|
||||||
|
private readonly IExampleValueGenerator _regexExampleValueGenerator;
|
||||||
|
|
||||||
public OpenApiPathsMapper(WireMockOpenApiParserSettings settings)
|
public OpenApiPathsMapper(WireMockOpenApiParserSettings settings)
|
||||||
{
|
{
|
||||||
_settings = Guard.NotNull(settings);
|
_settings = Guard.NotNull(settings);
|
||||||
_exampleValueGenerator = new ExampleValueGenerator(settings);
|
_exampleValueGenerator = new ExampleValueGenerator(settings);
|
||||||
|
_regexExampleValueGenerator = new RegexExampleValueGenerator(settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
public IReadOnlyList<MappingModel> ToMappingModels(OpenApiPaths? paths, IList<OpenApiServer> servers)
|
public IReadOnlyList<MappingModel> ToMappingModels(OpenApiPaths? paths, IList<OpenApiServer> servers)
|
||||||
@@ -37,18 +39,7 @@ internal class OpenApiPathsMapper
|
|||||||
.OrderBy(p => p.Key)
|
.OrderBy(p => p.Key)
|
||||||
.Select(p => MapPath(p.Key, p.Value, servers))
|
.Select(p => MapPath(p.Key, p.Value, servers))
|
||||||
.SelectMany(x => x)
|
.SelectMany(x => x)
|
||||||
.ToArray() ??
|
.ToArray() ?? Array.Empty<MappingModel>();
|
||||||
Array.Empty<MappingModel>();
|
|
||||||
}
|
|
||||||
|
|
||||||
private IReadOnlyList<MappingModel> MapPaths(OpenApiPaths? paths, IList<OpenApiServer> servers)
|
|
||||||
{
|
|
||||||
return paths?
|
|
||||||
.OrderBy(p => p.Key)
|
|
||||||
.Select(p => MapPath(p.Key, p.Value, servers))
|
|
||||||
.SelectMany(x => x)
|
|
||||||
.ToArray() ??
|
|
||||||
Array.Empty<MappingModel>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private IReadOnlyList<MappingModel> MapPath(string path, OpenApiPathItem pathItem, IList<OpenApiServer> servers)
|
private IReadOnlyList<MappingModel> MapPath(string path, OpenApiPathItem pathItem, IList<OpenApiServer> servers)
|
||||||
@@ -280,41 +271,6 @@ internal class OpenApiPathsMapper
|
|||||||
return newPath;
|
return newPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
private string MapBasePath(IList<OpenApiServer>? servers)
|
|
||||||
{
|
|
||||||
if (servers == null || servers.Count == 0)
|
|
||||||
{
|
|
||||||
return string.Empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
OpenApiServer server = servers.First();
|
|
||||||
if (Uri.TryCreate(server.Url, UriKind.RelativeOrAbsolute, out Uri uriResult))
|
|
||||||
{
|
|
||||||
return uriResult.IsAbsoluteUri ? uriResult.AbsolutePath : uriResult.ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
return string.Empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
private JToken? MapOpenApiAnyToJToken(IOpenApiAny? any)
|
|
||||||
{
|
|
||||||
if (any == null)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
using var outputString = new StringWriter();
|
|
||||||
var writer = new OpenApiJsonWriter(outputString);
|
|
||||||
any.Write(writer, OpenApiSpecVersion.OpenApi3_0);
|
|
||||||
|
|
||||||
if (any.AnyType == AnyType.Array)
|
|
||||||
{
|
|
||||||
return JArray.Parse(outputString.ToString());
|
|
||||||
}
|
|
||||||
|
|
||||||
return JObject.Parse(outputString.ToString());
|
|
||||||
}
|
|
||||||
|
|
||||||
private IDictionary<string, object>? MapHeaders(string? responseContentType, IDictionary<string, OpenApiHeader>? headers)
|
private IDictionary<string, object>? MapHeaders(string? responseContentType, IDictionary<string, OpenApiHeader>? headers)
|
||||||
{
|
{
|
||||||
var mappedHeaders = headers?.ToDictionary(
|
var mappedHeaders = headers?.ToDictionary(
|
||||||
@@ -366,28 +322,38 @@ internal class OpenApiPathsMapper
|
|||||||
return list.Any() ? list : null;
|
return list.Any() ? list : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private MatcherModel GetExampleMatcherModel(OpenApiSchema? schema, ExampleValueType type)
|
private MatcherModel GetExampleMatcherModel(OpenApiSchema? schema, ExampleValueType exampleValueType)
|
||||||
{
|
{
|
||||||
return type switch
|
return exampleValueType switch
|
||||||
{
|
{
|
||||||
ExampleValueType.Value => new MatcherModel
|
ExampleValueType.Value => new MatcherModel
|
||||||
{
|
{
|
||||||
Name = "ExactMatcher",
|
Name = "ExactMatcher",
|
||||||
Pattern = GetExampleValueAsStringForSchemaType(schema),
|
Pattern = GetExampleValueAsStringForSchemaType(schema, exampleValueType),
|
||||||
|
IgnoreCase = _settings.IgnoreCaseExampleValues
|
||||||
|
},
|
||||||
|
|
||||||
|
ExampleValueType.Regex => new MatcherModel
|
||||||
|
{
|
||||||
|
Name = "RegexMatcher",
|
||||||
|
Pattern = GetExampleValueAsStringForSchemaType(schema, exampleValueType),
|
||||||
IgnoreCase = _settings.IgnoreCaseExampleValues
|
IgnoreCase = _settings.IgnoreCaseExampleValues
|
||||||
},
|
},
|
||||||
|
|
||||||
_ => new MatcherModel
|
_ => new MatcherModel
|
||||||
{
|
{
|
||||||
Name = "WildcardMatcher",
|
Name = "WildcardMatcher",
|
||||||
Pattern = "*"
|
Pattern = "*",
|
||||||
|
IgnoreCase = _settings.IgnoreCaseExampleValues
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private string GetExampleValueAsStringForSchemaType(OpenApiSchema? schema)
|
private string GetExampleValueAsStringForSchemaType(OpenApiSchema? schema, ExampleValueType exampleValueType)
|
||||||
{
|
{
|
||||||
var value = _exampleValueGenerator.GetExampleValue(schema);
|
var value = exampleValueType == ExampleValueType.Regex ?
|
||||||
|
_regexExampleValueGenerator.GetExampleValue(schema) :
|
||||||
|
_exampleValueGenerator.GetExampleValue(schema);
|
||||||
|
|
||||||
return value switch
|
return value switch
|
||||||
{
|
{
|
||||||
@@ -396,4 +362,39 @@ internal class OpenApiPathsMapper
|
|||||||
_ => value.ToString(),
|
_ => value.ToString(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static string MapBasePath(IList<OpenApiServer>? servers)
|
||||||
|
{
|
||||||
|
if (servers == null || servers.Count == 0)
|
||||||
|
{
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
var server = servers.First();
|
||||||
|
if (Uri.TryCreate(server.Url, UriKind.RelativeOrAbsolute, out Uri uriResult))
|
||||||
|
{
|
||||||
|
return uriResult.IsAbsoluteUri ? uriResult.AbsolutePath : uriResult.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static JToken? MapOpenApiAnyToJToken(IOpenApiAny? any)
|
||||||
|
{
|
||||||
|
if (any == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
using var outputString = new StringWriter();
|
||||||
|
var writer = new OpenApiJsonWriter(outputString);
|
||||||
|
any.Write(writer, OpenApiSpecVersion.OpenApi3_0);
|
||||||
|
|
||||||
|
if (any.AnyType == AnyType.Array)
|
||||||
|
{
|
||||||
|
return JArray.Parse(outputString.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
return JObject.Parse(outputString.ToString());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
namespace WireMock.Net.OpenApiParser.Types;
|
namespace WireMock.Net.OpenApiParser.Types;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The example value to use
|
/// The (example) value pattern to use.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public enum ExampleValueType
|
public enum ExampleValueType
|
||||||
{
|
{
|
||||||
@@ -12,6 +12,11 @@ public enum ExampleValueType
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
Value,
|
Value,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Build a Regex based on the SchemaType.
|
||||||
|
/// </summary>
|
||||||
|
Regex,
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Just use a Wildcard (*) character.
|
/// Just use a Wildcard (*) character.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ using WireMock.Net.OpenApiParser.Types;
|
|||||||
|
|
||||||
namespace WireMock.Net.OpenApiParser.Utils;
|
namespace WireMock.Net.OpenApiParser.Utils;
|
||||||
|
|
||||||
internal class ExampleValueGenerator
|
internal class ExampleValueGenerator : IExampleValueGenerator
|
||||||
{
|
{
|
||||||
private readonly IWireMockOpenApiParserExampleValues _exampleValues;
|
private readonly IWireMockOpenApiParserExampleValues _exampleValues;
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
using Microsoft.OpenApi.Models;
|
||||||
|
|
||||||
|
namespace WireMock.Net.OpenApiParser.Utils;
|
||||||
|
|
||||||
|
internal interface IExampleValueGenerator
|
||||||
|
{
|
||||||
|
object GetExampleValue(OpenApiSchema? schema);
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
using Microsoft.OpenApi.Models;
|
||||||
|
using Stef.Validation;
|
||||||
|
using WireMock.Net.OpenApiParser.Extensions;
|
||||||
|
using WireMock.Net.OpenApiParser.Settings;
|
||||||
|
using WireMock.Net.OpenApiParser.Types;
|
||||||
|
|
||||||
|
namespace WireMock.Net.OpenApiParser.Utils;
|
||||||
|
|
||||||
|
internal class RegexExampleValueGenerator : IExampleValueGenerator
|
||||||
|
{
|
||||||
|
public RegexExampleValueGenerator(WireMockOpenApiParserSettings settings)
|
||||||
|
{
|
||||||
|
Guard.NotNull(settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
public object GetExampleValue(OpenApiSchema? schema)
|
||||||
|
{
|
||||||
|
switch (schema?.GetSchemaType())
|
||||||
|
{
|
||||||
|
case SchemaType.Boolean:
|
||||||
|
return @"(true|false)";
|
||||||
|
|
||||||
|
case SchemaType.Integer:
|
||||||
|
return @"-?\d+";
|
||||||
|
|
||||||
|
case SchemaType.Number:
|
||||||
|
return @"[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?";
|
||||||
|
|
||||||
|
default:
|
||||||
|
return schema?.GetSchemaFormat() switch
|
||||||
|
{
|
||||||
|
SchemaFormat.Date => @"(\d{4})-([01]\d)-([0-3]\d)",
|
||||||
|
SchemaFormat.DateTime => @"(\d{4})-([01]\d)-([0-3]\d)T([0-2]\d):([0-5]\d):([0-5]\d)(\.\d+)?(Z|[+-][0-2]\d:[0-5]\d)",
|
||||||
|
SchemaFormat.Byte => @"(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)",
|
||||||
|
SchemaFormat.Binary => @"[a-zA-Z0-9\+/]*={0,3}",
|
||||||
|
_ => ".*"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,6 +9,7 @@ using RamlToOpenApiConverter;
|
|||||||
using WireMock.Admin.Mappings;
|
using WireMock.Admin.Mappings;
|
||||||
using WireMock.Net.OpenApiParser.Mappers;
|
using WireMock.Net.OpenApiParser.Mappers;
|
||||||
using WireMock.Net.OpenApiParser.Settings;
|
using WireMock.Net.OpenApiParser.Settings;
|
||||||
|
using WireMock.Net.OpenApiParser.Types;
|
||||||
|
|
||||||
namespace WireMock.Net.OpenApiParser;
|
namespace WireMock.Net.OpenApiParser;
|
||||||
|
|
||||||
@@ -17,13 +18,20 @@ namespace WireMock.Net.OpenApiParser;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class WireMockOpenApiParser : IWireMockOpenApiParser
|
public class WireMockOpenApiParser : IWireMockOpenApiParser
|
||||||
{
|
{
|
||||||
|
private readonly WireMockOpenApiParserSettings _wireMockOpenApiParserSettings = new WireMockOpenApiParserSettings
|
||||||
|
{
|
||||||
|
HeaderPatternToUse = ExampleValueType.Regex,
|
||||||
|
QueryParameterPatternToUse = ExampleValueType.Regex,
|
||||||
|
PathPatternToUse = ExampleValueType.Regex
|
||||||
|
};
|
||||||
|
|
||||||
private readonly OpenApiStreamReader _reader = new();
|
private readonly OpenApiStreamReader _reader = new();
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
[PublicAPI]
|
[PublicAPI]
|
||||||
public IReadOnlyList<MappingModel> FromFile(string path, out OpenApiDiagnostic diagnostic)
|
public IReadOnlyList<MappingModel> FromFile(string path, out OpenApiDiagnostic diagnostic)
|
||||||
{
|
{
|
||||||
return FromFile(path, new WireMockOpenApiParserSettings(), out diagnostic);
|
return FromFile(path, _wireMockOpenApiParserSettings, out diagnostic);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@@ -49,7 +57,7 @@ public class WireMockOpenApiParser : IWireMockOpenApiParser
|
|||||||
[PublicAPI]
|
[PublicAPI]
|
||||||
public IReadOnlyList<MappingModel> FromDocument(OpenApiDocument openApiDocument, WireMockOpenApiParserSettings? settings = null)
|
public IReadOnlyList<MappingModel> FromDocument(OpenApiDocument openApiDocument, WireMockOpenApiParserSettings? settings = null)
|
||||||
{
|
{
|
||||||
return new OpenApiPathsMapper(settings ?? new WireMockOpenApiParserSettings()).ToMappingModels(openApiDocument.Paths, openApiDocument.Servers);
|
return new OpenApiPathsMapper(settings ?? _wireMockOpenApiParserSettings).ToMappingModels(openApiDocument.Paths, openApiDocument.Servers);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
|||||||
17
src/WireMock.Net/.filenesting.json
Normal file
17
src/WireMock.Net/.filenesting.json
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"help": "https://go.microsoft.com/fwlink/?linkid=866610",
|
||||||
|
"root": true,
|
||||||
|
|
||||||
|
"dependentFileProviders": {
|
||||||
|
"add": {
|
||||||
|
"addedExtension": {},
|
||||||
|
"pathSegment": {
|
||||||
|
"add": {
|
||||||
|
".*": [
|
||||||
|
".cs"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
15
src/WireMock.Net/Compatibility/StringExtensions.cs
Normal file
15
src/WireMock.Net/Compatibility/StringExtensions.cs
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
#if NET451 || NET452 || NET46 || NET451 || NET461 || NETSTANDARD1_3 || NETSTANDARD2_0
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
|
// ReSharper disable once CheckNamespace
|
||||||
|
namespace System;
|
||||||
|
|
||||||
|
internal static class StringExtensions
|
||||||
|
{
|
||||||
|
public static string Replace(this string text, string oldValue, string newValue, StringComparison stringComparison)
|
||||||
|
{
|
||||||
|
var options = stringComparison == StringComparison.OrdinalIgnoreCase ? RegexOptions.IgnoreCase : RegexOptions.None;
|
||||||
|
return Regex.Replace(text, oldValue, newValue, options);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using WireMock.Matchers.Request;
|
using WireMock.Matchers.Request;
|
||||||
using WireMock.Models;
|
using WireMock.Models;
|
||||||
@@ -132,6 +131,11 @@ public interface IMapping
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
object? Data { get; }
|
object? Data { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The probability when this request should be matched. Value is between 0 and 1. [Optional]
|
||||||
|
/// </summary>
|
||||||
|
double? Probability { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// ProvideResponseAsync
|
/// ProvideResponseAsync
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ using System.Linq.Dynamic.Core;
|
|||||||
|
|
||||||
namespace WireMock.Json;
|
namespace WireMock.Json;
|
||||||
|
|
||||||
public class DynamicPropertyWithValue : DynamicProperty
|
internal class DynamicPropertyWithValue : DynamicProperty
|
||||||
{
|
{
|
||||||
public object? Value { get; }
|
public object? Value { get; }
|
||||||
|
|
||||||
|
|||||||
@@ -75,6 +75,9 @@ public class Mapping : IMapping
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public object? Data { get; }
|
public object? Data { get; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public double? Probability { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="Mapping"/> class.
|
/// Initializes a new instance of the <see cref="Mapping"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -95,6 +98,7 @@ public class Mapping : IMapping
|
|||||||
/// <param name="useWebhooksFireAndForget">Use Fire and Forget for the defined webhook(s). [Optional]</param>
|
/// <param name="useWebhooksFireAndForget">Use Fire and Forget for the defined webhook(s). [Optional]</param>
|
||||||
/// <param name="timeSettings">The TimeSettings. [Optional]</param>
|
/// <param name="timeSettings">The TimeSettings. [Optional]</param>
|
||||||
/// <param name="data">The data object. [Optional]</param>
|
/// <param name="data">The data object. [Optional]</param>
|
||||||
|
/// <param name="probability">Define the probability when this request should be matched. [Optional]</param>
|
||||||
public Mapping(
|
public Mapping(
|
||||||
Guid guid,
|
Guid guid,
|
||||||
DateTime updatedAt,
|
DateTime updatedAt,
|
||||||
@@ -112,7 +116,8 @@ public class Mapping : IMapping
|
|||||||
IWebhook[]? webhooks,
|
IWebhook[]? webhooks,
|
||||||
bool? useWebhooksFireAndForget,
|
bool? useWebhooksFireAndForget,
|
||||||
ITimeSettings? timeSettings,
|
ITimeSettings? timeSettings,
|
||||||
object? data)
|
object? data,
|
||||||
|
double? probability)
|
||||||
{
|
{
|
||||||
Guid = guid;
|
Guid = guid;
|
||||||
UpdatedAt = updatedAt;
|
UpdatedAt = updatedAt;
|
||||||
@@ -131,6 +136,7 @@ public class Mapping : IMapping
|
|||||||
UseWebhooksFireAndForget = useWebhooksFireAndForget;
|
UseWebhooksFireAndForget = useWebhooksFireAndForget;
|
||||||
TimeSettings = timeSettings;
|
TimeSettings = timeSettings;
|
||||||
Data = data;
|
Data = data;
|
||||||
|
Probability = probability;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc cref="IMapping.ProvideResponseAsync" />
|
/// <inheritdoc cref="IMapping.ProvideResponseAsync" />
|
||||||
|
|||||||
@@ -69,12 +69,7 @@ public class MappingBuilder : IMappingBuilder
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public MappingModel[] GetMappings()
|
public MappingModel[] GetMappings()
|
||||||
{
|
{
|
||||||
return GetMappingsInternal().Select(_mappingConverter.ToMappingModel).ToArray();
|
return GetNonAdminMappings().Select(_mappingConverter.ToMappingModel).ToArray();
|
||||||
}
|
|
||||||
|
|
||||||
internal IMapping[] GetMappingsInternal()
|
|
||||||
{
|
|
||||||
return _options.Mappings.Values.ToArray().Where(m => !m.IsAdminInterface).ToArray();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@@ -86,7 +81,7 @@ public class MappingBuilder : IMappingBuilder
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public string? ToCSharpCode(Guid guid, MappingConverterType converterType)
|
public string? ToCSharpCode(Guid guid, MappingConverterType converterType)
|
||||||
{
|
{
|
||||||
var mapping = GetMappingsInternal().FirstOrDefault(m => m.Guid == guid);
|
var mapping = GetNonAdminMappings().FirstOrDefault(m => m.Guid == guid);
|
||||||
if (mapping is null)
|
if (mapping is null)
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
@@ -101,7 +96,7 @@ public class MappingBuilder : IMappingBuilder
|
|||||||
{
|
{
|
||||||
var sb = new StringBuilder();
|
var sb = new StringBuilder();
|
||||||
bool addStart = true;
|
bool addStart = true;
|
||||||
foreach (var mapping in GetMappingsInternal())
|
foreach (var mapping in GetNonAdminMappings())
|
||||||
{
|
{
|
||||||
sb.AppendLine(_mappingConverter.ToCSharpCode(mapping, new MappingConverterSettings { AddStart = addStart, ConverterType = converterType }));
|
sb.AppendLine(_mappingConverter.ToCSharpCode(mapping, new MappingConverterSettings { AddStart = addStart, ConverterType = converterType }));
|
||||||
|
|
||||||
@@ -123,7 +118,7 @@ public class MappingBuilder : IMappingBuilder
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void SaveMappingsToFolder(string? folder)
|
public void SaveMappingsToFolder(string? folder)
|
||||||
{
|
{
|
||||||
foreach (var mapping in GetNonAdminMappings().Where(m => !m.IsAdminInterface))
|
foreach (var mapping in GetNonAdminMappings())
|
||||||
{
|
{
|
||||||
_mappingToFileSaver.SaveMappingToFile(mapping, folder);
|
_mappingToFileSaver.SaveMappingToFile(mapping, folder);
|
||||||
}
|
}
|
||||||
@@ -131,7 +126,7 @@ public class MappingBuilder : IMappingBuilder
|
|||||||
|
|
||||||
private IMapping[] GetNonAdminMappings()
|
private IMapping[] GetNonAdminMappings()
|
||||||
{
|
{
|
||||||
return _options.Mappings.Values.ToArray();
|
return _options.Mappings.Values.Where(m => !m.IsAdminInterface).OrderBy(m => m.UpdatedAt).ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RegisterMapping(IMapping mapping, bool saveToFile)
|
private void RegisterMapping(IMapping mapping, bool saveToFile)
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using JetBrains.Annotations;
|
|
||||||
using Stef.Validation;
|
using Stef.Validation;
|
||||||
|
|
||||||
namespace WireMock.Matchers.Request;
|
namespace WireMock.Matchers.Request;
|
||||||
@@ -25,9 +24,9 @@ public abstract class RequestMessageCompositeMatcher : IRequestMatcher
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="requestMatchers">The request matchers.</param>
|
/// <param name="requestMatchers">The request matchers.</param>
|
||||||
/// <param name="type">The CompositeMatcherType type (Defaults to 'And')</param>
|
/// <param name="type">The CompositeMatcherType type (Defaults to 'And')</param>
|
||||||
protected RequestMessageCompositeMatcher([NotNull] IEnumerable<IRequestMatcher> requestMatchers, CompositeMatcherType type = CompositeMatcherType.And)
|
protected RequestMessageCompositeMatcher(IEnumerable<IRequestMatcher> requestMatchers, CompositeMatcherType type = CompositeMatcherType.And)
|
||||||
{
|
{
|
||||||
Guard.NotNull(requestMatchers, nameof(requestMatchers));
|
Guard.NotNull(requestMatchers);
|
||||||
|
|
||||||
_type = type;
|
_type = type;
|
||||||
RequestMatchers = requestMatchers;
|
RequestMatchers = requestMatchers;
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ using Microsoft.Extensions.DependencyInjection;
|
|||||||
using Stef.Validation;
|
using Stef.Validation;
|
||||||
using WireMock.Logging;
|
using WireMock.Logging;
|
||||||
using WireMock.Owin.Mappers;
|
using WireMock.Owin.Mappers;
|
||||||
|
using WireMock.Services;
|
||||||
using WireMock.Util;
|
using WireMock.Util;
|
||||||
|
|
||||||
namespace WireMock.Owin
|
namespace WireMock.Owin
|
||||||
@@ -66,6 +67,7 @@ namespace WireMock.Owin
|
|||||||
{
|
{
|
||||||
services.AddSingleton(_wireMockMiddlewareOptions);
|
services.AddSingleton(_wireMockMiddlewareOptions);
|
||||||
services.AddSingleton<IMappingMatcher, MappingMatcher>();
|
services.AddSingleton<IMappingMatcher, MappingMatcher>();
|
||||||
|
services.AddSingleton<IRandomizerDoubleBetween0And1, RandomizerDoubleBetween0And1>();
|
||||||
services.AddSingleton<IOwinRequestMapper, OwinRequestMapper>();
|
services.AddSingleton<IOwinRequestMapper, OwinRequestMapper>();
|
||||||
services.AddSingleton<IOwinResponseMapper, OwinResponseMapper>();
|
services.AddSingleton<IOwinResponseMapper, OwinResponseMapper>();
|
||||||
|
|
||||||
|
|||||||
@@ -62,11 +62,11 @@ namespace WireMock.Owin.Mappers
|
|||||||
switch (responseMessage.FaultType)
|
switch (responseMessage.FaultType)
|
||||||
{
|
{
|
||||||
case FaultType.EMPTY_RESPONSE:
|
case FaultType.EMPTY_RESPONSE:
|
||||||
bytes = IsFault(responseMessage) ? new byte[0] : GetNormalBody(responseMessage);
|
bytes = IsFault(responseMessage) ? EmptyArray<byte>.Value : GetNormalBody(responseMessage);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case FaultType.MALFORMED_RESPONSE_CHUNK:
|
case FaultType.MALFORMED_RESPONSE_CHUNK:
|
||||||
bytes = GetNormalBody(responseMessage) ?? new byte[0];
|
bytes = GetNormalBody(responseMessage) ?? EmptyArray<byte>.Value;
|
||||||
if (IsFault(responseMessage))
|
if (IsFault(responseMessage))
|
||||||
{
|
{
|
||||||
bytes = bytes.Take(bytes.Length / 2).Union(_randomizerBytes.Generate()).ToArray();
|
bytes = bytes.Take(bytes.Length / 2).Union(_randomizerBytes.Generate()).ToArray();
|
||||||
@@ -87,7 +87,7 @@ namespace WireMock.Owin.Mappers
|
|||||||
|
|
||||||
case { } typeAsString when typeAsString == typeof(string):
|
case { } typeAsString when typeAsString == typeof(string):
|
||||||
// Note: this case will also match on null
|
// Note: this case will also match on null
|
||||||
int.TryParse(responseMessage.StatusCode as string, out int result);
|
int.TryParse(responseMessage.StatusCode as string, out var result);
|
||||||
response.StatusCode = MapStatusCode(result);
|
response.StatusCode = MapStatusCode(result);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|||||||
@@ -1,18 +1,21 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using WireMock.Extensions;
|
|
||||||
using Stef.Validation;
|
using Stef.Validation;
|
||||||
|
using WireMock.Extensions;
|
||||||
|
using WireMock.Services;
|
||||||
|
|
||||||
namespace WireMock.Owin;
|
namespace WireMock.Owin;
|
||||||
|
|
||||||
internal class MappingMatcher : IMappingMatcher
|
internal class MappingMatcher : IMappingMatcher
|
||||||
{
|
{
|
||||||
private readonly IWireMockMiddlewareOptions _options;
|
private readonly IWireMockMiddlewareOptions _options;
|
||||||
|
private readonly IRandomizerDoubleBetween0And1 _randomizerDoubleBetween0And1;
|
||||||
|
|
||||||
public MappingMatcher(IWireMockMiddlewareOptions options)
|
public MappingMatcher(IWireMockMiddlewareOptions options, IRandomizerDoubleBetween0And1 randomizerDoubleBetween0And1)
|
||||||
{
|
{
|
||||||
_options = Guard.NotNull(options);
|
_options = Guard.NotNull(options);
|
||||||
|
_randomizerDoubleBetween0And1 = Guard.NotNull(randomizerDoubleBetween0And1);
|
||||||
}
|
}
|
||||||
|
|
||||||
public (MappingMatcherResult? Match, MappingMatcherResult? Partial) FindBestMatch(RequestMessage request)
|
public (MappingMatcherResult? Match, MappingMatcherResult? Partial) FindBestMatch(RequestMessage request)
|
||||||
@@ -21,7 +24,12 @@ internal class MappingMatcher : IMappingMatcher
|
|||||||
|
|
||||||
var possibleMappings = new List<MappingMatcherResult>();
|
var possibleMappings = new List<MappingMatcherResult>();
|
||||||
|
|
||||||
foreach (var mapping in _options.Mappings.Values.Where(m => m.TimeSettings.IsValid()))
|
var mappings = _options.Mappings.Values
|
||||||
|
.Where(m => m.TimeSettings.IsValid())
|
||||||
|
.Where(m => m.Probability is null || m.Probability <= _randomizerDoubleBetween0And1.Generate())
|
||||||
|
.ToArray();
|
||||||
|
|
||||||
|
foreach (var mapping in mappings)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -9,6 +9,9 @@ using JetBrains.Annotations;
|
|||||||
using WireMock.Logging;
|
using WireMock.Logging;
|
||||||
using WireMock.Owin.Mappers;
|
using WireMock.Owin.Mappers;
|
||||||
using Stef.Validation;
|
using Stef.Validation;
|
||||||
|
using RandomDataGenerator.FieldOptions;
|
||||||
|
using RandomDataGenerator.Randomizers;
|
||||||
|
using WireMock.Services;
|
||||||
|
|
||||||
namespace WireMock.Owin;
|
namespace WireMock.Owin;
|
||||||
|
|
||||||
@@ -70,7 +73,7 @@ internal class OwinSelfHost : IOwinSelfHost
|
|||||||
{
|
{
|
||||||
var requestMapper = new OwinRequestMapper();
|
var requestMapper = new OwinRequestMapper();
|
||||||
var responseMapper = new OwinResponseMapper(_options);
|
var responseMapper = new OwinResponseMapper(_options);
|
||||||
var matcher = new MappingMatcher(_options);
|
var matcher = new MappingMatcher(_options, new RandomizerDoubleBetween0And1());
|
||||||
|
|
||||||
Action<IAppBuilder> startup = app =>
|
Action<IAppBuilder> startup = app =>
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ namespace WireMock.Owin
|
|||||||
public Task Invoke(IContext ctx)
|
public Task Invoke(IContext ctx)
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
if (_options.HandleRequestsSynchronously.GetValueOrDefault(true))
|
if (_options.HandleRequestsSynchronously.GetValueOrDefault(false))
|
||||||
{
|
{
|
||||||
lock (_lock)
|
lock (_lock)
|
||||||
{
|
{
|
||||||
@@ -216,7 +216,12 @@ namespace WireMock.Owin
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await webhookSender.SendAsync(httpClientForWebhook, mapping, webhookRequest, request, response).ConfigureAwait(false);
|
var result = await webhookSender.SendAsync(httpClientForWebhook, mapping, webhookRequest, request, response).ConfigureAwait(false);
|
||||||
|
if (!result.IsSuccessStatusCode)
|
||||||
|
{
|
||||||
|
var content = await result.Content.ReadAsStringAsync().ConfigureAwait(false);
|
||||||
|
_options.Logger.Warn($"Sending message to Webhook [{webHookIndex}] from Mapping '{mapping.Guid}' failed. HttpStatusCode: {result.StatusCode} Content: {content}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -37,7 +37,19 @@ internal class ProxyHelper
|
|||||||
var requiredUri = new Uri(url);
|
var requiredUri = new Uri(url);
|
||||||
|
|
||||||
// Create HttpRequestMessage
|
// Create HttpRequestMessage
|
||||||
var httpRequestMessage = HttpRequestMessageHelper.Create(requestMessage, url);
|
var replaceSettings = proxyAndRecordSettings.ReplaceSettings;
|
||||||
|
string proxyUrl;
|
||||||
|
if (replaceSettings is not null)
|
||||||
|
{
|
||||||
|
var stringComparison = replaceSettings.IgnoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal;
|
||||||
|
proxyUrl = url.Replace(replaceSettings.OldValue, replaceSettings.NewValue, stringComparison);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
proxyUrl = url;
|
||||||
|
}
|
||||||
|
|
||||||
|
var httpRequestMessage = HttpRequestMessageHelper.Create(requestMessage, proxyUrl);
|
||||||
|
|
||||||
// Call the URL
|
// Call the URL
|
||||||
var httpResponseMessage = await client.SendAsync(httpRequestMessage, HttpCompletionOption.ResponseContentRead).ConfigureAwait(false);
|
var httpResponseMessage = await client.SendAsync(httpRequestMessage, HttpCompletionOption.ResponseContentRead).ConfigureAwait(false);
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
namespace WireMock.RequestBuilders
|
namespace WireMock.RequestBuilders;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// IRequestBuilder
|
||||||
|
/// </summary>
|
||||||
|
public interface IRequestBuilder : IClientIPRequestBuilder
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// IRequestBuilder
|
|
||||||
/// </summary>
|
|
||||||
public interface IRequestBuilder : IClientIPRequestBuilder
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,82 +1,81 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using WireMock.Matchers;
|
using WireMock.Matchers;
|
||||||
using WireMock.Matchers.Request;
|
using WireMock.Matchers.Request;
|
||||||
using Stef.Validation;
|
using Stef.Validation;
|
||||||
|
|
||||||
namespace WireMock.RequestBuilders
|
namespace WireMock.RequestBuilders;
|
||||||
|
|
||||||
|
public partial class Request
|
||||||
{
|
{
|
||||||
public partial class Request
|
/// <inheritdoc cref="ICookiesRequestBuilder.WithCookie(string, string, MatchBehaviour)"/>
|
||||||
|
public IRequestBuilder WithCookie(string name, string pattern, MatchBehaviour matchBehaviour)
|
||||||
{
|
{
|
||||||
/// <inheritdoc cref="ICookiesRequestBuilder.WithCookie(string, string, MatchBehaviour)"/>
|
return WithCookie(name, pattern, true, matchBehaviour);
|
||||||
public IRequestBuilder WithCookie(string name, string pattern, MatchBehaviour matchBehaviour)
|
}
|
||||||
{
|
|
||||||
return WithCookie(name, pattern, true, matchBehaviour);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc cref="ICookiesRequestBuilder.WithCookie(string, string, bool, MatchBehaviour)"/>
|
/// <inheritdoc cref="ICookiesRequestBuilder.WithCookie(string, string, bool, MatchBehaviour)"/>
|
||||||
public IRequestBuilder WithCookie(string name, string pattern, bool ignoreCase = true, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch)
|
public IRequestBuilder WithCookie(string name, string pattern, bool ignoreCase = true, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch)
|
||||||
{
|
{
|
||||||
Guard.NotNull(name, nameof(name));
|
Guard.NotNull(name, nameof(name));
|
||||||
Guard.NotNull(pattern, nameof(pattern));
|
Guard.NotNull(pattern, nameof(pattern));
|
||||||
|
|
||||||
_requestMatchers.Add(new RequestMessageCookieMatcher(matchBehaviour, name, pattern, ignoreCase));
|
_requestMatchers.Add(new RequestMessageCookieMatcher(matchBehaviour, name, pattern, ignoreCase));
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc cref="ICookiesRequestBuilder.WithCookie(string, string[], MatchBehaviour)"/>
|
/// <inheritdoc cref="ICookiesRequestBuilder.WithCookie(string, string[], MatchBehaviour)"/>
|
||||||
public IRequestBuilder WithCookie(string name, string[] patterns, MatchBehaviour matchBehaviour)
|
public IRequestBuilder WithCookie(string name, string[] patterns, MatchBehaviour matchBehaviour)
|
||||||
{
|
{
|
||||||
return WithCookie(name, patterns, true, matchBehaviour);
|
return WithCookie(name, patterns, true, matchBehaviour);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc cref="ICookiesRequestBuilder.WithCookie(string, string[], bool, MatchBehaviour)"/>
|
/// <inheritdoc cref="ICookiesRequestBuilder.WithCookie(string, string[], bool, MatchBehaviour)"/>
|
||||||
public IRequestBuilder WithCookie(string name, string[] patterns, bool ignoreCase = true, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch)
|
public IRequestBuilder WithCookie(string name, string[] patterns, bool ignoreCase = true, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch)
|
||||||
{
|
{
|
||||||
Guard.NotNull(name, nameof(name));
|
Guard.NotNull(name, nameof(name));
|
||||||
Guard.NotNull(patterns, nameof(patterns));
|
Guard.NotNull(patterns, nameof(patterns));
|
||||||
|
|
||||||
_requestMatchers.Add(new RequestMessageCookieMatcher(matchBehaviour, name, ignoreCase, patterns));
|
_requestMatchers.Add(new RequestMessageCookieMatcher(matchBehaviour, name, ignoreCase, patterns));
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc cref="ICookiesRequestBuilder.WithCookie(string, IStringMatcher[])"/>
|
/// <inheritdoc cref="ICookiesRequestBuilder.WithCookie(string, IStringMatcher[])"/>
|
||||||
public IRequestBuilder WithCookie(string name, params IStringMatcher[] matchers)
|
public IRequestBuilder WithCookie(string name, params IStringMatcher[] matchers)
|
||||||
{
|
{
|
||||||
Guard.NotNull(name, nameof(name));
|
Guard.NotNull(name, nameof(name));
|
||||||
Guard.NotNullOrEmpty(matchers, nameof(matchers));
|
Guard.NotNullOrEmpty(matchers, nameof(matchers));
|
||||||
|
|
||||||
_requestMatchers.Add(new RequestMessageCookieMatcher(MatchBehaviour.AcceptOnMatch, name, false, matchers));
|
_requestMatchers.Add(new RequestMessageCookieMatcher(MatchBehaviour.AcceptOnMatch, name, false, matchers));
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc cref="ICookiesRequestBuilder.WithCookie(string, bool, IStringMatcher[])"/>
|
/// <inheritdoc cref="ICookiesRequestBuilder.WithCookie(string, bool, IStringMatcher[])"/>
|
||||||
public IRequestBuilder WithCookie(string name, bool ignoreCase, params IStringMatcher[] matchers)
|
public IRequestBuilder WithCookie(string name, bool ignoreCase, params IStringMatcher[] matchers)
|
||||||
{
|
{
|
||||||
Guard.NotNull(name, nameof(name));
|
Guard.NotNull(name, nameof(name));
|
||||||
Guard.NotNullOrEmpty(matchers, nameof(matchers));
|
Guard.NotNullOrEmpty(matchers, nameof(matchers));
|
||||||
|
|
||||||
_requestMatchers.Add(new RequestMessageCookieMatcher(MatchBehaviour.AcceptOnMatch, name, ignoreCase, matchers));
|
_requestMatchers.Add(new RequestMessageCookieMatcher(MatchBehaviour.AcceptOnMatch, name, ignoreCase, matchers));
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc cref="ICookiesRequestBuilder.WithCookie(string, IStringMatcher[])"/>
|
/// <inheritdoc cref="ICookiesRequestBuilder.WithCookie(string, IStringMatcher[])"/>
|
||||||
public IRequestBuilder WithCookie(string name, bool ignoreCase, MatchBehaviour matchBehaviour, params IStringMatcher[] matchers)
|
public IRequestBuilder WithCookie(string name, bool ignoreCase, MatchBehaviour matchBehaviour, params IStringMatcher[] matchers)
|
||||||
{
|
{
|
||||||
Guard.NotNull(name, nameof(name));
|
Guard.NotNull(name, nameof(name));
|
||||||
Guard.NotNullOrEmpty(matchers, nameof(matchers));
|
Guard.NotNullOrEmpty(matchers, nameof(matchers));
|
||||||
|
|
||||||
_requestMatchers.Add(new RequestMessageCookieMatcher(matchBehaviour, name, ignoreCase, matchers));
|
_requestMatchers.Add(new RequestMessageCookieMatcher(matchBehaviour, name, ignoreCase, matchers));
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc cref="ICookiesRequestBuilder.WithCookie(Func{IDictionary{string, string}, bool}[])"/>
|
/// <inheritdoc cref="ICookiesRequestBuilder.WithCookie(Func{IDictionary{string, string}, bool}[])"/>
|
||||||
public IRequestBuilder WithCookie(params Func<IDictionary<string, string>, bool>[] funcs)
|
public IRequestBuilder WithCookie(params Func<IDictionary<string, string>, bool>[] funcs)
|
||||||
{
|
{
|
||||||
Guard.NotNullOrEmpty(funcs, nameof(funcs));
|
Guard.NotNullOrEmpty(funcs, nameof(funcs));
|
||||||
|
|
||||||
_requestMatchers.Add(new RequestMessageCookieMatcher(funcs));
|
_requestMatchers.Add(new RequestMessageCookieMatcher(funcs));
|
||||||
return this;
|
return this;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -7,86 +7,85 @@ using WireMock.Matchers.Request;
|
|||||||
using WireMock.Types;
|
using WireMock.Types;
|
||||||
using Stef.Validation;
|
using Stef.Validation;
|
||||||
|
|
||||||
namespace WireMock.RequestBuilders
|
namespace WireMock.RequestBuilders;
|
||||||
|
|
||||||
|
public partial class Request
|
||||||
{
|
{
|
||||||
public partial class Request
|
/// <inheritdoc cref="IParamsRequestBuilder.WithParam(string, MatchBehaviour)"/>
|
||||||
|
public IRequestBuilder WithParam(string key, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch)
|
||||||
{
|
{
|
||||||
/// <inheritdoc cref="IParamsRequestBuilder.WithParam(string, MatchBehaviour)"/>
|
return WithParam(key, false, matchBehaviour);
|
||||||
public IRequestBuilder WithParam(string key, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch)
|
}
|
||||||
{
|
|
||||||
return WithParam(key, false, matchBehaviour);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc cref="IParamsRequestBuilder.WithParam(string, bool, MatchBehaviour)"/>
|
/// <inheritdoc cref="IParamsRequestBuilder.WithParam(string, bool, MatchBehaviour)"/>
|
||||||
public IRequestBuilder WithParam(string key, bool ignoreCase, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch)
|
public IRequestBuilder WithParam(string key, bool ignoreCase, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch)
|
||||||
{
|
{
|
||||||
Guard.NotNull(key);
|
Guard.NotNull(key);
|
||||||
|
|
||||||
_requestMatchers.Add(new RequestMessageParamMatcher(matchBehaviour, key, ignoreCase));
|
_requestMatchers.Add(new RequestMessageParamMatcher(matchBehaviour, key, ignoreCase));
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc cref="IParamsRequestBuilder.WithParam(string, string[])"/>
|
/// <inheritdoc cref="IParamsRequestBuilder.WithParam(string, string[])"/>
|
||||||
public IRequestBuilder WithParam(string key, params string[] values)
|
public IRequestBuilder WithParam(string key, params string[] values)
|
||||||
{
|
{
|
||||||
return WithParam(key, MatchBehaviour.AcceptOnMatch, false, values);
|
return WithParam(key, MatchBehaviour.AcceptOnMatch, false, values);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc cref="IParamsRequestBuilder.WithParam(string, bool, string[])"/>
|
/// <inheritdoc cref="IParamsRequestBuilder.WithParam(string, bool, string[])"/>
|
||||||
public IRequestBuilder WithParam(string key, bool ignoreCase, params string[] values)
|
public IRequestBuilder WithParam(string key, bool ignoreCase, params string[] values)
|
||||||
{
|
{
|
||||||
return WithParam(key, MatchBehaviour.AcceptOnMatch, ignoreCase, values);
|
return WithParam(key, MatchBehaviour.AcceptOnMatch, ignoreCase, values);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc cref="IParamsRequestBuilder.WithParam(string, IStringMatcher[])"/>
|
/// <inheritdoc cref="IParamsRequestBuilder.WithParam(string, IStringMatcher[])"/>
|
||||||
public IRequestBuilder WithParam(string key, params IStringMatcher[] matchers)
|
public IRequestBuilder WithParam(string key, params IStringMatcher[] matchers)
|
||||||
{
|
{
|
||||||
return WithParam(key, MatchBehaviour.AcceptOnMatch, false, matchers);
|
return WithParam(key, MatchBehaviour.AcceptOnMatch, false, matchers);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc cref="IParamsRequestBuilder.WithParam(string, bool, IStringMatcher[])"/>
|
/// <inheritdoc cref="IParamsRequestBuilder.WithParam(string, bool, IStringMatcher[])"/>
|
||||||
public IRequestBuilder WithParam(string key, bool ignoreCase, params IStringMatcher[] matchers)
|
public IRequestBuilder WithParam(string key, bool ignoreCase, params IStringMatcher[] matchers)
|
||||||
{
|
{
|
||||||
return WithParam(key, MatchBehaviour.AcceptOnMatch, ignoreCase, matchers);
|
return WithParam(key, MatchBehaviour.AcceptOnMatch, ignoreCase, matchers);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc cref="IParamsRequestBuilder.WithParam(string, MatchBehaviour, string[])"/>
|
/// <inheritdoc cref="IParamsRequestBuilder.WithParam(string, MatchBehaviour, string[])"/>
|
||||||
public IRequestBuilder WithParam(string key, MatchBehaviour matchBehaviour, params string[] values)
|
public IRequestBuilder WithParam(string key, MatchBehaviour matchBehaviour, params string[] values)
|
||||||
{
|
{
|
||||||
return WithParam(key, matchBehaviour, false, values);
|
return WithParam(key, matchBehaviour, false, values);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc cref="IParamsRequestBuilder.WithParam(string, MatchBehaviour, bool, string[])"/>
|
/// <inheritdoc cref="IParamsRequestBuilder.WithParam(string, MatchBehaviour, bool, string[])"/>
|
||||||
public IRequestBuilder WithParam(string key, MatchBehaviour matchBehaviour, bool ignoreCase = false, params string[] values)
|
public IRequestBuilder WithParam(string key, MatchBehaviour matchBehaviour, bool ignoreCase = false, params string[] values)
|
||||||
{
|
{
|
||||||
Guard.NotNull(key);
|
Guard.NotNull(key);
|
||||||
|
|
||||||
_requestMatchers.Add(new RequestMessageParamMatcher(matchBehaviour, key, ignoreCase, values));
|
_requestMatchers.Add(new RequestMessageParamMatcher(matchBehaviour, key, ignoreCase, values));
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc cref="IParamsRequestBuilder.WithParam(string, MatchBehaviour, IStringMatcher[])"/>
|
/// <inheritdoc cref="IParamsRequestBuilder.WithParam(string, MatchBehaviour, IStringMatcher[])"/>
|
||||||
public IRequestBuilder WithParam(string key, MatchBehaviour matchBehaviour, params IStringMatcher[] matchers)
|
public IRequestBuilder WithParam(string key, MatchBehaviour matchBehaviour, params IStringMatcher[] matchers)
|
||||||
{
|
{
|
||||||
return WithParam(key, matchBehaviour, false, matchers);
|
return WithParam(key, matchBehaviour, false, matchers);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc cref="IParamsRequestBuilder.WithParam(string, MatchBehaviour, bool, IStringMatcher[])"/>
|
/// <inheritdoc cref="IParamsRequestBuilder.WithParam(string, MatchBehaviour, bool, IStringMatcher[])"/>
|
||||||
public IRequestBuilder WithParam(string key, MatchBehaviour matchBehaviour, bool ignoreCase, params IStringMatcher[] matchers)
|
public IRequestBuilder WithParam(string key, MatchBehaviour matchBehaviour, bool ignoreCase, params IStringMatcher[] matchers)
|
||||||
{
|
{
|
||||||
Guard.NotNull(key);
|
Guard.NotNull(key);
|
||||||
|
|
||||||
_requestMatchers.Add(new RequestMessageParamMatcher(matchBehaviour, key, ignoreCase, matchers));
|
_requestMatchers.Add(new RequestMessageParamMatcher(matchBehaviour, key, ignoreCase, matchers));
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc cref="IParamsRequestBuilder.WithParam(Func{IDictionary{string, WireMockList{string}}, bool}[])"/>
|
/// <inheritdoc cref="IParamsRequestBuilder.WithParam(Func{IDictionary{string, WireMockList{string}}, bool}[])"/>
|
||||||
public IRequestBuilder WithParam(params Func<IDictionary<string, WireMockList<string>>, bool>[] funcs)
|
public IRequestBuilder WithParam(params Func<IDictionary<string, WireMockList<string>>, bool>[] funcs)
|
||||||
{
|
{
|
||||||
Guard.NotNullOrEmpty(funcs, nameof(funcs));
|
Guard.NotNullOrEmpty(funcs, nameof(funcs));
|
||||||
|
|
||||||
_requestMatchers.Add(new RequestMessageParamMatcher(funcs));
|
_requestMatchers.Add(new RequestMessageParamMatcher(funcs));
|
||||||
return this;
|
return this;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3,47 +3,46 @@ using Stef.Validation;
|
|||||||
using WireMock.Matchers;
|
using WireMock.Matchers;
|
||||||
using WireMock.Matchers.Request;
|
using WireMock.Matchers.Request;
|
||||||
|
|
||||||
namespace WireMock.RequestBuilders
|
namespace WireMock.RequestBuilders;
|
||||||
|
|
||||||
|
public partial class Request
|
||||||
{
|
{
|
||||||
public partial class Request
|
/// <inheritdoc />
|
||||||
|
public IRequestBuilder WithUrl(params IStringMatcher[] matchers)
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
return WithUrl(MatchOperator.Or, matchers);
|
||||||
public IRequestBuilder WithUrl(params IStringMatcher[] matchers)
|
|
||||||
{
|
|
||||||
return WithUrl(MatchOperator.Or, matchers);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public IRequestBuilder WithUrl(MatchOperator matchOperator, params IStringMatcher[] matchers)
|
|
||||||
{
|
|
||||||
Guard.NotNullOrEmpty(matchers);
|
|
||||||
|
|
||||||
_requestMatchers.Add(new RequestMessageUrlMatcher(MatchBehaviour.AcceptOnMatch, matchOperator, matchers));
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public IRequestBuilder WithUrl(params string[] urls)
|
|
||||||
{
|
|
||||||
return WithUrl(MatchOperator.Or, urls);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public IRequestBuilder WithUrl(MatchOperator matchOperator, params string[] urls)
|
|
||||||
{
|
|
||||||
Guard.NotNullOrEmpty(urls);
|
|
||||||
|
|
||||||
_requestMatchers.Add(new RequestMessageUrlMatcher(MatchBehaviour.AcceptOnMatch, matchOperator, urls));
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc cref="IUrlAndPathRequestBuilder.WithUrl(Func{string, bool}[])"/>
|
|
||||||
public IRequestBuilder WithUrl(params Func<string, bool>[] funcs)
|
|
||||||
{
|
|
||||||
Guard.NotNullOrEmpty(funcs);
|
|
||||||
|
|
||||||
_requestMatchers.Add(new RequestMessageUrlMatcher(funcs));
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public IRequestBuilder WithUrl(MatchOperator matchOperator, params IStringMatcher[] matchers)
|
||||||
|
{
|
||||||
|
Guard.NotNullOrEmpty(matchers);
|
||||||
|
|
||||||
|
_requestMatchers.Add(new RequestMessageUrlMatcher(MatchBehaviour.AcceptOnMatch, matchOperator, matchers));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public IRequestBuilder WithUrl(params string[] urls)
|
||||||
|
{
|
||||||
|
return WithUrl(MatchOperator.Or, urls);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public IRequestBuilder WithUrl(MatchOperator matchOperator, params string[] urls)
|
||||||
|
{
|
||||||
|
Guard.NotNullOrEmpty(urls);
|
||||||
|
|
||||||
|
_requestMatchers.Add(new RequestMessageUrlMatcher(MatchBehaviour.AcceptOnMatch, matchOperator, urls));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc cref="IUrlAndPathRequestBuilder.WithUrl(Func{string, bool}[])"/>
|
||||||
|
public IRequestBuilder WithUrl(params Func<string, bool>[] funcs)
|
||||||
|
{
|
||||||
|
Guard.NotNullOrEmpty(funcs);
|
||||||
|
|
||||||
|
_requestMatchers.Add(new RequestMessageUrlMatcher(funcs));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -63,5 +63,4 @@ public partial class Request : RequestMessageCompositeMatcher, IRequestBuilder
|
|||||||
{
|
{
|
||||||
return _requestMatchers.OfType<T>().FirstOrDefault(func);
|
return _requestMatchers.OfType<T>().FirstOrDefault(func);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -180,12 +180,7 @@ public class RequestMessage : IRequestMessage
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <inheritdoc />
|
||||||
/// Get a query parameter.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="key">The key.</param>
|
|
||||||
/// <param name="ignoreCase">Defines if the key should be matched using case-ignore.</param>
|
|
||||||
/// <returns>The query parameter.</returns>
|
|
||||||
public WireMockList<string>? GetParameter(string key, bool ignoreCase = false)
|
public WireMockList<string>? GetParameter(string key, bool ignoreCase = false)
|
||||||
{
|
{
|
||||||
if (Query == null)
|
if (Query == null)
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Net;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
using Newtonsoft.Json;
|
||||||
using Stef.Validation;
|
using Stef.Validation;
|
||||||
using WireMock.Admin.Mappings;
|
using WireMock.Admin.Mappings;
|
||||||
using WireMock.Constants;
|
using WireMock.Constants;
|
||||||
@@ -14,7 +16,9 @@ using WireMock.RequestBuilders;
|
|||||||
using WireMock.ResponseBuilders;
|
using WireMock.ResponseBuilders;
|
||||||
using WireMock.Settings;
|
using WireMock.Settings;
|
||||||
using WireMock.Types;
|
using WireMock.Types;
|
||||||
|
using WireMock.Util;
|
||||||
|
|
||||||
|
using static WireMock.Util.CSharpFormatter;
|
||||||
namespace WireMock.Serialization;
|
namespace WireMock.Serialization;
|
||||||
|
|
||||||
internal class MappingConverter
|
internal class MappingConverter
|
||||||
@@ -33,7 +37,7 @@ internal class MappingConverter
|
|||||||
settings ??= new MappingConverterSettings();
|
settings ??= new MappingConverterSettings();
|
||||||
|
|
||||||
var request = (Request)mapping.RequestMatcher;
|
var request = (Request)mapping.RequestMatcher;
|
||||||
var response = (Response) mapping.Provider;
|
var response = (Response)mapping.Provider;
|
||||||
|
|
||||||
var clientIPMatcher = request.GetRequestMessageMatcher<RequestMessageClientIPMatcher>();
|
var clientIPMatcher = request.GetRequestMessageMatcher<RequestMessageClientIPMatcher>();
|
||||||
var pathMatcher = request.GetRequestMessageMatcher<RequestMessagePathMatcher>();
|
var pathMatcher = request.GetRequestMessageMatcher<RequestMessagePathMatcher>();
|
||||||
@@ -103,11 +107,28 @@ internal class MappingConverter
|
|||||||
|
|
||||||
if (bodyMatcher is { Matchers: { } })
|
if (bodyMatcher is { Matchers: { } })
|
||||||
{
|
{
|
||||||
var wildcardMatcher = bodyMatcher.Matchers.OfType<WildcardMatcher>().FirstOrDefault();
|
if (bodyMatcher.Matchers.OfType<WildcardMatcher>().FirstOrDefault() is { } wildcardMatcher && wildcardMatcher.GetPatterns().Any())
|
||||||
if (wildcardMatcher is { } && wildcardMatcher.GetPatterns().Any())
|
|
||||||
{
|
{
|
||||||
sb.AppendLine($" .WithBody({GetString(wildcardMatcher)})");
|
sb.AppendLine($" .WithBody({GetString(wildcardMatcher)})");
|
||||||
}
|
}
|
||||||
|
else if (bodyMatcher.Matchers.OfType<JsonPartialMatcher>().FirstOrDefault() is { Value: { } } jsonPartialMatcher)
|
||||||
|
{
|
||||||
|
sb.AppendLine(@$" .WithBody(new JsonPartialMatcher(
|
||||||
|
value: {ToCSharpStringLiteral(jsonPartialMatcher.Value.ToString())},
|
||||||
|
ignoreCase: {ToCSharpBooleanLiteral(jsonPartialMatcher.IgnoreCase)},
|
||||||
|
throwException: {ToCSharpBooleanLiteral(jsonPartialMatcher.ThrowException)},
|
||||||
|
regex: {ToCSharpBooleanLiteral(jsonPartialMatcher.Regex)}
|
||||||
|
))");
|
||||||
|
}
|
||||||
|
else if (bodyMatcher.Matchers.OfType<JsonPartialWildcardMatcher>().FirstOrDefault() is { Value: { } } jsonPartialWildcardMatcher)
|
||||||
|
{
|
||||||
|
sb.AppendLine(@$" .WithBody(new JsonPartialWildcardMatcher(
|
||||||
|
value: {ToCSharpStringLiteral(jsonPartialWildcardMatcher.Value.ToString())},
|
||||||
|
ignoreCase: {ToCSharpBooleanLiteral(jsonPartialWildcardMatcher.IgnoreCase)},
|
||||||
|
throwException: {ToCSharpBooleanLiteral(jsonPartialWildcardMatcher.ThrowException)},
|
||||||
|
regex: {ToCSharpBooleanLiteral(jsonPartialWildcardMatcher.Regex)}
|
||||||
|
))");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sb.AppendLine(@" )");
|
sb.AppendLine(@" )");
|
||||||
@@ -115,24 +136,50 @@ internal class MappingConverter
|
|||||||
// Guid
|
// Guid
|
||||||
sb.AppendLine($" .WithGuid(\"{mapping.Guid}\")");
|
sb.AppendLine($" .WithGuid(\"{mapping.Guid}\")");
|
||||||
|
|
||||||
|
if (mapping.Probability != null)
|
||||||
|
{
|
||||||
|
sb.AppendLine($" .WithProbability({mapping.Probability.Value.ToString(CultureInfoUtils.CultureInfoEnUS)})");
|
||||||
|
}
|
||||||
|
|
||||||
// Response
|
// Response
|
||||||
sb.AppendLine(" .RespondWith(Response.Create()");
|
sb.AppendLine(" .RespondWith(Response.Create()");
|
||||||
|
|
||||||
|
if (response.ResponseMessage.StatusCode is int or string)
|
||||||
|
{
|
||||||
|
sb.AppendLine($" .WithStatusCode({JsonConvert.SerializeObject(response.ResponseMessage.StatusCode)})");
|
||||||
|
}
|
||||||
|
else if (response.ResponseMessage.StatusCode is HttpStatusCode httpStatusCode)
|
||||||
|
{
|
||||||
|
sb.AppendLine($" .WithStatusCode({(int)httpStatusCode})");
|
||||||
|
}
|
||||||
|
|
||||||
if (response.ResponseMessage.Headers is { })
|
if (response.ResponseMessage.Headers is { })
|
||||||
{
|
{
|
||||||
foreach (var header in response.ResponseMessage.Headers)
|
foreach (var header in response.ResponseMessage.Headers)
|
||||||
{
|
{
|
||||||
sb.AppendLine($" .WithHeader(\"{header.Key})\", {ToValueArguments(header.Value.ToArray())})");
|
sb.AppendLine($" .WithHeader(\"{header.Key}\", {ToValueArguments(header.Value.ToArray())})");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (response.ResponseMessage.BodyData is { })
|
if (response.ResponseMessage.BodyData is { } bodyData)
|
||||||
{
|
{
|
||||||
switch (response.ResponseMessage.BodyData.DetectedBodyType)
|
switch (response.ResponseMessage.BodyData.DetectedBodyType)
|
||||||
{
|
{
|
||||||
case BodyType.String:
|
case BodyType.String:
|
||||||
case BodyType.FormUrlEncoded:
|
case BodyType.FormUrlEncoded:
|
||||||
sb.AppendLine($" .WithBody(\"{response.ResponseMessage.BodyData.BodyAsString}\")");
|
sb.AppendLine($" .WithBody({ToCSharpStringLiteral(bodyData.BodyAsString)})");
|
||||||
|
break;
|
||||||
|
case BodyType.Json:
|
||||||
|
if (bodyData.BodyAsJson is string bodyStringValue)
|
||||||
|
{
|
||||||
|
sb.AppendLine($" .WithBody({ToCSharpStringLiteral(bodyStringValue)})");
|
||||||
|
}
|
||||||
|
else if(bodyData.BodyAsJson is {} jsonBody)
|
||||||
|
{
|
||||||
|
var anonymousObjectDefinition = ConvertToAnonymousObjectDefinition(jsonBody);
|
||||||
|
sb.AppendLine($" .WithBodyAsJson({anonymousObjectDefinition})");
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -183,6 +230,7 @@ internal class MappingConverter
|
|||||||
WhenStateIs = mapping.ExecutionConditionState,
|
WhenStateIs = mapping.ExecutionConditionState,
|
||||||
SetStateTo = mapping.NextState,
|
SetStateTo = mapping.NextState,
|
||||||
Data = mapping.Data,
|
Data = mapping.Data,
|
||||||
|
Probability = mapping.Probability,
|
||||||
Request = new RequestModel
|
Request = new RequestModel
|
||||||
{
|
{
|
||||||
Headers = headerMatchers.Any() ? headerMatchers.Select(hm => new HeaderModel
|
Headers = headerMatchers.Any() ? headerMatchers.Select(hm => new HeaderModel
|
||||||
@@ -374,7 +422,7 @@ internal class MappingConverter
|
|||||||
|
|
||||||
private static string GetString(IStringMatcher stringMatcher)
|
private static string GetString(IStringMatcher stringMatcher)
|
||||||
{
|
{
|
||||||
return stringMatcher.GetPatterns().Select(p => $"\"{p.GetPattern()}\"").First();
|
return stringMatcher.GetPatterns().Select(p => ToCSharpStringLiteral(p.GetPattern())).First();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string[] GetStringArray(IReadOnlyList<IStringMatcher> stringMatchers)
|
private static string[] GetStringArray(IReadOnlyList<IStringMatcher> stringMatchers)
|
||||||
@@ -427,7 +475,7 @@ internal class MappingConverter
|
|||||||
|
|
||||||
private static string ToValueArguments(string[]? values, string defaultValue = "")
|
private static string ToValueArguments(string[]? values, string defaultValue = "")
|
||||||
{
|
{
|
||||||
return values is { } ? string.Join(", ", values.Select(v => $"\"{v}\"")) : $"\"{defaultValue}\"";
|
return values is { } ? string.Join(", ", values.Select(ToCSharpStringLiteral)) : ToCSharpStringLiteral(defaultValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static WebProxyModel? MapWebProxy(WebProxySettings? settings)
|
private static WebProxyModel? MapWebProxy(WebProxySettings? settings)
|
||||||
@@ -458,4 +506,6 @@ internal class MappingConverter
|
|||||||
|
|
||||||
return newDictionary;
|
return newDictionary;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -189,7 +189,8 @@ internal class ProxyMappingConverter
|
|||||||
webhooks: null,
|
webhooks: null,
|
||||||
useWebhooksFireAndForget: null,
|
useWebhooksFireAndForget: null,
|
||||||
timeSettings: null,
|
timeSettings: null,
|
||||||
data: mapping?.Data
|
data: mapping?.Data,
|
||||||
|
probability: null
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -175,5 +175,13 @@ public interface IRespondWithAProvider
|
|||||||
/// lookup data "1"
|
/// lookup data "1"
|
||||||
/// </example>
|
/// </example>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <returns>The <see cref="IRespondWithAProvider"/>.</returns>
|
||||||
IRespondWithAProvider WithData(object data);
|
IRespondWithAProvider WithData(object data);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Define the probability when this request should be matched. Value is between 0 and 1.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="probability">The probability when this request should be matched. Value is between 0 and 1.</param>
|
||||||
|
/// <returns>The <see cref="IRespondWithAProvider"/>.</returns>
|
||||||
|
IRespondWithAProvider WithProbability(double probability);
|
||||||
}
|
}
|
||||||
@@ -18,6 +18,12 @@ namespace WireMock.Server;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
internal class RespondWithAProvider : IRespondWithAProvider
|
internal class RespondWithAProvider : IRespondWithAProvider
|
||||||
{
|
{
|
||||||
|
private readonly RegistrationCallback _registrationCallback;
|
||||||
|
private readonly IRequestMatcher _requestMatcher;
|
||||||
|
private readonly WireMockServerSettings _settings;
|
||||||
|
private readonly IDateTimeUtils _dateTimeUtils;
|
||||||
|
private readonly bool _saveToFile;
|
||||||
|
|
||||||
private int _priority;
|
private int _priority;
|
||||||
private string? _title;
|
private string? _title;
|
||||||
private string? _description;
|
private string? _description;
|
||||||
@@ -26,13 +32,8 @@ internal class RespondWithAProvider : IRespondWithAProvider
|
|||||||
private string? _nextState;
|
private string? _nextState;
|
||||||
private string? _scenario;
|
private string? _scenario;
|
||||||
private int _timesInSameState = 1;
|
private int _timesInSameState = 1;
|
||||||
private readonly RegistrationCallback _registrationCallback;
|
|
||||||
private readonly IRequestMatcher _requestMatcher;
|
|
||||||
private readonly WireMockServerSettings _settings;
|
|
||||||
private readonly IDateTimeUtils _dateTimeUtils;
|
|
||||||
private readonly bool _saveToFile;
|
|
||||||
|
|
||||||
private bool? _useWebhookFireAndForget;
|
private bool? _useWebhookFireAndForget;
|
||||||
|
private double? _probability;
|
||||||
|
|
||||||
public Guid Guid { get; private set; }
|
public Guid Guid { get; private set; }
|
||||||
|
|
||||||
@@ -92,7 +93,8 @@ internal class RespondWithAProvider : IRespondWithAProvider
|
|||||||
Webhooks,
|
Webhooks,
|
||||||
_useWebhookFireAndForget,
|
_useWebhookFireAndForget,
|
||||||
TimeSettings,
|
TimeSettings,
|
||||||
Data);
|
Data,
|
||||||
|
_probability);
|
||||||
|
|
||||||
_registrationCallback(mapping, _saveToFile);
|
_registrationCallback(mapping, _saveToFile);
|
||||||
}
|
}
|
||||||
@@ -285,6 +287,13 @@ internal class RespondWithAProvider : IRespondWithAProvider
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public IRespondWithAProvider WithProbability(double probability)
|
||||||
|
{
|
||||||
|
_probability = Guard.Condition(probability, p => p is >= 0 and <= 1.0);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
private static IWebhook InitWebhook(
|
private static IWebhook InitWebhook(
|
||||||
string url,
|
string url,
|
||||||
string method,
|
string method,
|
||||||
|
|||||||
@@ -113,6 +113,11 @@ public partial class WireMockServer
|
|||||||
respondProvider.WithWebhookFireAndForget(mappingModel.UseWebhooksFireAndForget.Value);
|
respondProvider.WithWebhookFireAndForget(mappingModel.UseWebhooksFireAndForget.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mappingModel.Probability != null)
|
||||||
|
{
|
||||||
|
respondProvider.WithProbability(mappingModel.Probability.Value);
|
||||||
|
}
|
||||||
|
|
||||||
var responseBuilder = InitResponseBuilder(mappingModel.Response);
|
var responseBuilder = InitResponseBuilder(mappingModel.Response);
|
||||||
respondProvider.RespondWith(responseBuilder);
|
respondProvider.RespondWith(responseBuilder);
|
||||||
|
|
||||||
|
|||||||
@@ -2,20 +2,39 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
|
using Newtonsoft.Json;
|
||||||
using WireMock.Net.OpenApiParser;
|
using WireMock.Net.OpenApiParser;
|
||||||
|
using WireMock.Net.OpenApiParser.Settings;
|
||||||
|
using WireMock.Net.OpenApiParser.Types;
|
||||||
|
using WireMock.Serialization;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace WireMock.Server;
|
namespace WireMock.Server;
|
||||||
|
|
||||||
public partial class WireMockServer
|
public partial class WireMockServer
|
||||||
{
|
{
|
||||||
|
#if OPENAPIPARSER
|
||||||
|
private readonly WireMockOpenApiParserSettings _openApiParserSettings = new WireMockOpenApiParserSettings
|
||||||
|
{
|
||||||
|
PathPatternToUse = ExampleValueType.Regex,
|
||||||
|
HeaderPatternToUse = ExampleValueType.Regex,
|
||||||
|
QueryParameterPatternToUse = ExampleValueType.Regex
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
private IResponseMessage OpenApiConvertToMappings(IRequestMessage requestMessage)
|
private IResponseMessage OpenApiConvertToMappings(IRequestMessage requestMessage)
|
||||||
{
|
{
|
||||||
#if OPENAPIPARSER
|
#if OPENAPIPARSER
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var mappingModels = new WireMockOpenApiParser().FromText(requestMessage.Body, out var diagnostic);
|
var mappingModels = new WireMockOpenApiParser().FromText(requestMessage.Body, out var diagnostic);
|
||||||
return diagnostic.Errors.Any() ? ToJson(diagnostic, false, HttpStatusCode.BadRequest) : ToJson(mappingModels);
|
if (diagnostic.Errors.Any())
|
||||||
|
{
|
||||||
|
var diagnosticAsJson = JsonConvert.SerializeObject(diagnostic, JsonSerializationConstants.JsonSerializerSettingsDefault);
|
||||||
|
_settings.Logger.Warn("OpenApiError(s) while converting OpenAPI specification to MappingModel(s) : {0}", diagnosticAsJson);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ToJson(mappingModels);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
@@ -35,7 +54,8 @@ public partial class WireMockServer
|
|||||||
var mappingModels = new WireMockOpenApiParser().FromText(requestMessage.Body, out var diagnostic);
|
var mappingModels = new WireMockOpenApiParser().FromText(requestMessage.Body, out var diagnostic);
|
||||||
if (diagnostic.Errors.Any())
|
if (diagnostic.Errors.Any())
|
||||||
{
|
{
|
||||||
return ToJson(diagnostic, false, HttpStatusCode.BadRequest);
|
var diagnosticAsJson = JsonConvert.SerializeObject(diagnostic, JsonSerializationConstants.JsonSerializerSettingsDefault);
|
||||||
|
_settings.Logger.Warn("OpenApiError(s) while converting OpenAPI specification to MappingModel(s) : {0}", diagnosticAsJson);
|
||||||
}
|
}
|
||||||
|
|
||||||
ConvertMappingsAndRegisterAsRespondProvider(mappingModels);
|
ConvertMappingsAndRegisterAsRespondProvider(mappingModels);
|
||||||
|
|||||||
@@ -43,10 +43,14 @@ public partial class WireMockServer : IWireMockServer
|
|||||||
private readonly IGuidUtils _guidUtils = new GuidUtils();
|
private readonly IGuidUtils _guidUtils = new GuidUtils();
|
||||||
private readonly IDateTimeUtils _dateTimeUtils = new DateTimeUtils();
|
private readonly IDateTimeUtils _dateTimeUtils = new DateTimeUtils();
|
||||||
|
|
||||||
/// <inheritdoc cref="IWireMockServer.IsStarted" />
|
/// <inheritdoc />
|
||||||
[PublicAPI]
|
[PublicAPI]
|
||||||
public bool IsStarted => _httpServer is { IsStarted: true };
|
public bool IsStarted => _httpServer is { IsStarted: true };
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
[PublicAPI]
|
||||||
|
public bool IsStartedWithAdminInterface => IsStarted && _settings.StartAdminInterface.GetValueOrDefault();
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
[PublicAPI]
|
[PublicAPI]
|
||||||
public List<int> Ports { get; }
|
public List<int> Ports { get; }
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
namespace WireMock.Services;
|
||||||
|
|
||||||
|
internal interface IRandomizerDoubleBetween0And1
|
||||||
|
{
|
||||||
|
double Generate();
|
||||||
|
}
|
||||||
14
src/WireMock.Net/Services/RandomizerDoubleBetween0And1.cs
Normal file
14
src/WireMock.Net/Services/RandomizerDoubleBetween0And1.cs
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
using RandomDataGenerator.FieldOptions;
|
||||||
|
using RandomDataGenerator.Randomizers;
|
||||||
|
|
||||||
|
namespace WireMock.Services;
|
||||||
|
|
||||||
|
internal class RandomizerDoubleBetween0And1 : IRandomizerDoubleBetween0And1
|
||||||
|
{
|
||||||
|
private readonly IRandomizerNumber<double> _randomizerDoubleBetween0And1 = RandomizerFactory.GetRandomizer(new FieldOptionsDouble { Min = 0, Max = 1 });
|
||||||
|
|
||||||
|
public double Generate()
|
||||||
|
{
|
||||||
|
return _randomizerDoubleBetween0And1.Generate() ?? 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,91 +1,97 @@
|
|||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
|
|
||||||
namespace WireMock.Settings;
|
namespace WireMock.Settings;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// ProxyAndRecordSettings
|
/// ProxyAndRecordSettings
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class ProxyAndRecordSettings : HttpClientSettings
|
public class ProxyAndRecordSettings : HttpClientSettings
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The URL to proxy.
|
/// The URL to proxy.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[PublicAPI]
|
[PublicAPI]
|
||||||
public string Url { get; set; } = null!;
|
public string Url { get; set; } = null!;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Save the mapping for each request/response to the internal Mappings.
|
/// Save the mapping for each request/response to the internal Mappings.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[PublicAPI]
|
[PublicAPI]
|
||||||
public bool SaveMapping { get; set; }
|
public bool SaveMapping { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Save the mapping for each request/response also to a file. (Note that SaveMapping must also be set to true.)
|
/// Save the mapping for each request/response also to a file. (Note that SaveMapping must also be set to true.)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[PublicAPI]
|
[PublicAPI]
|
||||||
public bool SaveMappingToFile { get; set; }
|
public bool SaveMappingToFile { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Only save request/response to the internal Mappings if the status code is included in this pattern. (Note that SaveMapping must also be set to true.)
|
/// Only save request/response to the internal Mappings if the status code is included in this pattern. (Note that SaveMapping must also be set to true.)
|
||||||
/// The pattern can contain a single value like "200", but also ranges like "2xx", "100,300,600" or "100-299,6xx" are supported.
|
/// The pattern can contain a single value like "200", but also ranges like "2xx", "100,300,600" or "100-299,6xx" are supported.
|
||||||
///
|
///
|
||||||
/// Deprecated : use SaveMappingSettings.
|
/// Deprecated : use SaveMappingSettings.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[PublicAPI]
|
[PublicAPI]
|
||||||
public string SaveMappingForStatusCodePattern
|
public string SaveMappingForStatusCodePattern
|
||||||
{
|
{
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (SaveMappingSettings is null)
|
if (SaveMappingSettings is null)
|
||||||
{
|
{
|
||||||
SaveMappingSettings = new ProxySaveMappingSettings();
|
SaveMappingSettings = new ProxySaveMappingSettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
SaveMappingSettings.StatusCodePattern = value;
|
SaveMappingSettings.StatusCodePattern = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Additional SaveMappingSettings.
|
/// Additional SaveMappingSettings.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[PublicAPI]
|
[PublicAPI]
|
||||||
public ProxySaveMappingSettings? SaveMappingSettings { get; set; }
|
public ProxySaveMappingSettings? SaveMappingSettings { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Defines a list from headers which will be excluded from the saved mappings.
|
/// Defines a list from headers which will be excluded from the saved mappings.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[PublicAPI]
|
[PublicAPI]
|
||||||
public string[]? ExcludedHeaders { get; set; }
|
public string[]? ExcludedHeaders { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Defines a list of params which will be excluded from the saved mappings.
|
/// Defines a list of params which will be excluded from the saved mappings.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[PublicAPI]
|
[PublicAPI]
|
||||||
public string[]? ExcludedParams { get; set; }
|
public string[]? ExcludedParams { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Defines a list of cookies which will be excluded from the saved mappings.
|
/// Defines a list of cookies which will be excluded from the saved mappings.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[PublicAPI]
|
[PublicAPI]
|
||||||
public string[]? ExcludedCookies { get; set; }
|
public string[]? ExcludedCookies { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Prefer the Proxy Mapping over the saved Mapping (in case SaveMapping is set to <c>true</c>).
|
/// Replace Settings
|
||||||
/// </summary>
|
/// </summary>
|
||||||
//[PublicAPI]
|
[PublicAPI]
|
||||||
//public bool PreferProxyMapping { get; set; }
|
public ProxyUrlReplaceSettings? ReplaceSettings { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// When SaveMapping is set to <c>true</c>, this setting can be used to control the behavior of the generated request matchers for the new mapping.
|
/// Prefer the Proxy Mapping over the saved Mapping (in case SaveMapping is set to <c>true</c>).
|
||||||
/// - <c>false</c>, the default matchers will be used.
|
/// </summary>
|
||||||
/// - <c>true</c>, the defined mappings in the request wil be used for the new mapping.
|
//[PublicAPI]
|
||||||
///
|
//public bool PreferProxyMapping { get; set; }
|
||||||
/// Default value is false.
|
|
||||||
/// </summary>
|
/// <summary>
|
||||||
public bool UseDefinedRequestMatchers { get; set; }
|
/// When SaveMapping is set to <c>true</c>, this setting can be used to control the behavior of the generated request matchers for the new mapping.
|
||||||
|
/// - <c>false</c>, the default matchers will be used.
|
||||||
/// <summary>
|
/// - <c>true</c>, the defined mappings in the request wil be used for the new mapping.
|
||||||
/// Append an unique GUID to the filename from the saved mapping file.
|
///
|
||||||
/// </summary>
|
/// Default value is false.
|
||||||
public bool AppendGuidToSavedMappingFile { get; set; }
|
/// </summary>
|
||||||
|
public bool UseDefinedRequestMatchers { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Append an unique GUID to the filename from the saved mapping file.
|
||||||
|
/// </summary>
|
||||||
|
public bool AppendGuidToSavedMappingFile { get; set; }
|
||||||
}
|
}
|
||||||
22
src/WireMock.Net/Settings/ProxyUrlReplaceSettings.cs
Normal file
22
src/WireMock.Net/Settings/ProxyUrlReplaceSettings.cs
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
namespace WireMock.Settings;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Defines an old path param and a new path param to be replaced when proxying.
|
||||||
|
/// </summary>
|
||||||
|
public class ProxyUrlReplaceSettings
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The old path value to be replaced by the new path value
|
||||||
|
/// </summary>
|
||||||
|
public string OldValue { get; set; } = null!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The new path value to replace the old value with
|
||||||
|
/// </summary>
|
||||||
|
public string NewValue { get; set; } = null!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Defines if the case should be ignore when replacing.
|
||||||
|
/// </summary>
|
||||||
|
public bool IgnoreCase { get; set; }
|
||||||
|
}
|
||||||
@@ -21,7 +21,8 @@ public static class WireMockServerSettingsParser
|
|||||||
/// <param name="logger">The logger (optional, can be null)</param>
|
/// <param name="logger">The logger (optional, can be null)</param>
|
||||||
/// <param name="settings">The parsed settings</param>
|
/// <param name="settings">The parsed settings</param>
|
||||||
[PublicAPI]
|
[PublicAPI]
|
||||||
public static bool TryParseArguments(string[] args, [NotNullWhen(true)] out WireMockServerSettings? settings, IWireMockLogger? logger = null)
|
public static bool TryParseArguments(string[] args, [NotNullWhen(true)] out WireMockServerSettings? settings,
|
||||||
|
IWireMockLogger? logger = null)
|
||||||
{
|
{
|
||||||
Guard.HasNoNulls(args);
|
Guard.HasNoNulls(args);
|
||||||
|
|
||||||
@@ -70,6 +71,17 @@ public static class WireMockServerSettingsParser
|
|||||||
settings.AcceptAnyClientCertificate = parser.GetBoolValue(nameof(WireMockServerSettings.AcceptAnyClientCertificate));
|
settings.AcceptAnyClientCertificate = parser.GetBoolValue(nameof(WireMockServerSettings.AcceptAnyClientCertificate));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
ParseLoggerSettings(settings, logger, parser);
|
||||||
|
ParsePortSettings(settings, parser);
|
||||||
|
ParseProxyAndRecordSettings(settings, parser);
|
||||||
|
ParseCertificateSettings(settings, parser);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ParseLoggerSettings(WireMockServerSettings settings, IWireMockLogger? logger,
|
||||||
|
SimpleCommandLineParser parser)
|
||||||
|
{
|
||||||
var loggerType = parser.GetStringValue("WireMockLogger");
|
var loggerType = parser.GetStringValue("WireMockLogger");
|
||||||
switch (loggerType)
|
switch (loggerType)
|
||||||
{
|
{
|
||||||
@@ -86,22 +98,17 @@ public static class WireMockServerSettingsParser
|
|||||||
{
|
{
|
||||||
settings.Logger = logger;
|
settings.Logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (parser.Contains(nameof(WireMockServerSettings.Port)))
|
private static void ParseProxyAndRecordSettings(WireMockServerSettings settings, SimpleCommandLineParser parser)
|
||||||
{
|
{
|
||||||
settings.Port = parser.GetIntValue(nameof(WireMockServerSettings.Port));
|
|
||||||
}
|
|
||||||
else if (settings.HostingScheme is null)
|
|
||||||
{
|
|
||||||
settings.Urls = parser.GetValues("Urls", new[] { "http://*:9091/" });
|
|
||||||
}
|
|
||||||
|
|
||||||
var proxyUrl = parser.GetStringValue("ProxyURL") ?? parser.GetStringValue("ProxyUrl");
|
var proxyUrl = parser.GetStringValue("ProxyURL") ?? parser.GetStringValue("ProxyUrl");
|
||||||
if (!string.IsNullOrEmpty(proxyUrl))
|
if (!string.IsNullOrEmpty(proxyUrl))
|
||||||
{
|
{
|
||||||
settings.ProxyAndRecordSettings = new ProxyAndRecordSettings
|
var proxyAndRecordSettings = new ProxyAndRecordSettings
|
||||||
{
|
{
|
||||||
AllowAutoRedirect = parser.GetBoolValue("AllowAutoRedirect"),
|
AllowAutoRedirect = parser.GetBoolValue("AllowAutoRedirect"),
|
||||||
ClientX509Certificate2ThumbprintOrSubjectName = parser.GetStringValue("ClientX509Certificate2ThumbprintOrSubjectName"),
|
ClientX509Certificate2ThumbprintOrSubjectName = parser.GetStringValue("ClientX509Certificate2ThumbprintOrSubjectName"),
|
||||||
@@ -121,18 +128,27 @@ public static class WireMockServerSettingsParser
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
string? proxyAddress = parser.GetStringValue("WebProxyAddress");
|
ParseWebProxyAddressSettings(proxyAndRecordSettings, parser);
|
||||||
if (!string.IsNullOrEmpty(proxyAddress))
|
ParseProxyUrlReplaceSettings(proxyAndRecordSettings, parser);
|
||||||
{
|
|
||||||
settings.ProxyAndRecordSettings.WebProxySettings = new WebProxySettings
|
|
||||||
{
|
|
||||||
Address = proxyAddress!,
|
|
||||||
UserName = parser.GetStringValue("WebProxyUserName"),
|
|
||||||
Password = parser.GetStringValue("WebProxyPassword")
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
settings.ProxyAndRecordSettings = proxyAndRecordSettings;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ParsePortSettings(WireMockServerSettings settings, SimpleCommandLineParser parser)
|
||||||
|
{
|
||||||
|
if (parser.Contains(nameof(WireMockServerSettings.Port)))
|
||||||
|
{
|
||||||
|
settings.Port = parser.GetIntValue(nameof(WireMockServerSettings.Port));
|
||||||
|
}
|
||||||
|
else if (settings.HostingScheme is null)
|
||||||
|
{
|
||||||
|
settings.Urls = parser.GetValues("Urls", new[] { "http://*:9091/" });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ParseCertificateSettings(WireMockServerSettings settings, SimpleCommandLineParser parser)
|
||||||
|
{
|
||||||
var certificateSettings = new WireMockCertificateSettings
|
var certificateSettings = new WireMockCertificateSettings
|
||||||
{
|
{
|
||||||
X509StoreName = parser.GetStringValue("X509StoreName"),
|
X509StoreName = parser.GetStringValue("X509StoreName"),
|
||||||
@@ -145,7 +161,33 @@ public static class WireMockServerSettingsParser
|
|||||||
{
|
{
|
||||||
settings.CertificateSettings = certificateSettings;
|
settings.CertificateSettings = certificateSettings;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
private static void ParseWebProxyAddressSettings(ProxyAndRecordSettings settings, SimpleCommandLineParser parser)
|
||||||
|
{
|
||||||
|
string? proxyAddress = parser.GetStringValue("WebProxyAddress");
|
||||||
|
if (!string.IsNullOrEmpty(proxyAddress))
|
||||||
|
{
|
||||||
|
settings.WebProxySettings = new WebProxySettings
|
||||||
|
{
|
||||||
|
Address = proxyAddress!,
|
||||||
|
UserName = parser.GetStringValue("WebProxyUserName"),
|
||||||
|
Password = parser.GetStringValue("WebProxyPassword")
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ParseProxyUrlReplaceSettings(ProxyAndRecordSettings settings, SimpleCommandLineParser parser)
|
||||||
|
{
|
||||||
|
var proxyUrlReplaceOldValue = parser.GetStringValue("ProxyUrlReplaceOldValue");
|
||||||
|
var proxyUrlReplaceNewValue = parser.GetStringValue("ProxyUrlReplaceNewValue");
|
||||||
|
if (!string.IsNullOrEmpty(proxyUrlReplaceOldValue) && proxyUrlReplaceNewValue != null)
|
||||||
|
{
|
||||||
|
settings.ReplaceSettings = new ProxyUrlReplaceSettings
|
||||||
|
{
|
||||||
|
OldValue = proxyUrlReplaceOldValue!,
|
||||||
|
NewValue = proxyUrlReplaceNewValue!
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
180
src/WireMock.Net/Util/CSharpFormatter.cs
Normal file
180
src/WireMock.Net/Util/CSharpFormatter.cs
Normal file
@@ -0,0 +1,180 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
||||||
|
namespace WireMock.Util;
|
||||||
|
|
||||||
|
internal static class CSharpFormatter
|
||||||
|
{
|
||||||
|
#region Reserved Keywords
|
||||||
|
private static readonly HashSet<string> CSharpReservedKeywords = new(new[]
|
||||||
|
{
|
||||||
|
"abstract",
|
||||||
|
"as",
|
||||||
|
"base",
|
||||||
|
"bool",
|
||||||
|
"break",
|
||||||
|
"byte",
|
||||||
|
"case",
|
||||||
|
"catch",
|
||||||
|
"char",
|
||||||
|
"checked",
|
||||||
|
"class",
|
||||||
|
"const",
|
||||||
|
"continue",
|
||||||
|
"decimal",
|
||||||
|
"default",
|
||||||
|
"delegate",
|
||||||
|
"do",
|
||||||
|
"double",
|
||||||
|
"else",
|
||||||
|
"enum",
|
||||||
|
"event",
|
||||||
|
"explicit",
|
||||||
|
"extern",
|
||||||
|
"false",
|
||||||
|
"finally",
|
||||||
|
"fixed",
|
||||||
|
"float",
|
||||||
|
"for",
|
||||||
|
"foreach",
|
||||||
|
"goto",
|
||||||
|
"if",
|
||||||
|
"implicit",
|
||||||
|
"in",
|
||||||
|
"int",
|
||||||
|
"interface",
|
||||||
|
"internal",
|
||||||
|
"is",
|
||||||
|
"lock",
|
||||||
|
"long",
|
||||||
|
"namespace",
|
||||||
|
"new",
|
||||||
|
"null",
|
||||||
|
"object",
|
||||||
|
"operator",
|
||||||
|
"out",
|
||||||
|
"override",
|
||||||
|
"params",
|
||||||
|
"private",
|
||||||
|
"protected",
|
||||||
|
"public",
|
||||||
|
"readonly",
|
||||||
|
"ref",
|
||||||
|
"return",
|
||||||
|
"sbyte",
|
||||||
|
"sealed",
|
||||||
|
"short",
|
||||||
|
"sizeof",
|
||||||
|
"stackalloc",
|
||||||
|
"static",
|
||||||
|
"string",
|
||||||
|
"struct",
|
||||||
|
"switch",
|
||||||
|
"this",
|
||||||
|
"throw",
|
||||||
|
"true",
|
||||||
|
"try",
|
||||||
|
"typeof",
|
||||||
|
"uint",
|
||||||
|
"ulong",
|
||||||
|
"unchecked",
|
||||||
|
"unsafe",
|
||||||
|
"ushort",
|
||||||
|
"using",
|
||||||
|
"virtual",
|
||||||
|
"void",
|
||||||
|
"volatile",
|
||||||
|
"while"
|
||||||
|
});
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
private const string Null = "null";
|
||||||
|
|
||||||
|
public static object ConvertToAnonymousObjectDefinition(object jsonBody)
|
||||||
|
{
|
||||||
|
var serializedBody = JsonConvert.SerializeObject(jsonBody);
|
||||||
|
using var jsonReader = new JsonTextReader(new StringReader(serializedBody));
|
||||||
|
jsonReader.DateParseHandling = DateParseHandling.None;
|
||||||
|
var deserializedBody = JObject.Load(jsonReader);
|
||||||
|
|
||||||
|
return ConvertJsonToAnonymousObjectDefinition(deserializedBody, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string ConvertJsonToAnonymousObjectDefinition(JToken token, int ind = 0)
|
||||||
|
{
|
||||||
|
return token switch
|
||||||
|
{
|
||||||
|
JArray jArray => FormatArray(jArray, ind),
|
||||||
|
JObject jObject => FormatObject(jObject, ind),
|
||||||
|
JProperty jProperty => $"{FormatPropertyName(jProperty.Name)} = {ConvertJsonToAnonymousObjectDefinition(jProperty.Value, ind)}",
|
||||||
|
JValue jValue => jValue.Type switch
|
||||||
|
{
|
||||||
|
JTokenType.None => Null,
|
||||||
|
JTokenType.Integer => jValue.Value != null ? string.Format(CultureInfo.InvariantCulture, "{0}", jValue.Value) : Null,
|
||||||
|
JTokenType.Float => jValue.Value != null ? string.Format(CultureInfo.InvariantCulture, "{0}", jValue.Value) : Null,
|
||||||
|
JTokenType.String => ToCSharpStringLiteral(jValue.Value?.ToString()),
|
||||||
|
JTokenType.Boolean => jValue.Value != null ? string.Format(CultureInfo.InvariantCulture, "{0}", jValue.Value).ToLower() : Null,
|
||||||
|
JTokenType.Null => Null,
|
||||||
|
JTokenType.Undefined => Null,
|
||||||
|
JTokenType.Date when jValue.Value is DateTime dateValue =>
|
||||||
|
$"DateTime.Parse({ToCSharpStringLiteral(dateValue.ToString("s"))})",
|
||||||
|
_ => $"UNHANDLED_CASE: {jValue.Type}"
|
||||||
|
},
|
||||||
|
_ => $"UNHANDLED_CASE: {token}"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string ToCSharpBooleanLiteral(bool value) => value ? "true" : "false";
|
||||||
|
|
||||||
|
public static string ToCSharpStringLiteral(string? value)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(value))
|
||||||
|
{
|
||||||
|
return "\"\"";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value.Contains("\n"))
|
||||||
|
{
|
||||||
|
var escapedValue = value?.Replace("\"", "\"\"") ?? string.Empty;
|
||||||
|
return $"@\"{escapedValue}\"";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var escapedValue = value?.Replace("\"", "\\\"") ?? string.Empty;
|
||||||
|
return $"\"{escapedValue}\"";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string FormatPropertyName(string propertyName)
|
||||||
|
{
|
||||||
|
return CSharpReservedKeywords.Contains(propertyName) ? "@" + propertyName : propertyName;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string FormatObject(JObject jObject, int ind)
|
||||||
|
{
|
||||||
|
var indStr = new string(' ', 4 * ind);
|
||||||
|
var indStrSub = new string(' ', 4 * (ind + 1));
|
||||||
|
var items = jObject.Properties().Select(x => ConvertJsonToAnonymousObjectDefinition(x, ind + 1));
|
||||||
|
|
||||||
|
return $"new\r\n{indStr}{{\r\n{indStrSub}{string.Join($",\r\n{indStrSub}", items)}\r\n{indStr}}}";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string FormatArray(JArray jArray, int ind)
|
||||||
|
{
|
||||||
|
var hasComplexItems = jArray.FirstOrDefault() is JObject or JArray;
|
||||||
|
var items = jArray.Select(x => ConvertJsonToAnonymousObjectDefinition(x, hasComplexItems ? ind + 1 : ind));
|
||||||
|
if (hasComplexItems)
|
||||||
|
{
|
||||||
|
var indStr = new string(' ', 4 * ind);
|
||||||
|
var indStrSub = new string(' ', 4 * (ind + 1));
|
||||||
|
return $"new []\r\n{indStr}{{\r\n{indStrSub}{string.Join($",\r\n{indStrSub}", items)}\r\n{indStr}}}";
|
||||||
|
}
|
||||||
|
|
||||||
|
return $"new [] {{ {string.Join(", ", items)} }}";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,6 +5,8 @@ namespace WireMock.Util;
|
|||||||
|
|
||||||
internal static class CultureInfoUtils
|
internal static class CultureInfoUtils
|
||||||
{
|
{
|
||||||
|
public static readonly CultureInfo CultureInfoEnUS = new("en-US");
|
||||||
|
|
||||||
public static CultureInfo Parse(string? value)
|
public static CultureInfo Parse(string? value)
|
||||||
{
|
{
|
||||||
if (value is null)
|
if (value is null)
|
||||||
|
|||||||
@@ -1,29 +1,31 @@
|
|||||||
using Nelibur.ObjectMapper;
|
using Nelibur.ObjectMapper;
|
||||||
using WireMock.Admin.Settings;
|
using WireMock.Admin.Settings;
|
||||||
using WireMock.Settings;
|
using WireMock.Settings;
|
||||||
|
|
||||||
namespace WireMock.Util;
|
namespace WireMock.Util;
|
||||||
|
|
||||||
internal sealed class TinyMapperUtils
|
internal sealed class TinyMapperUtils
|
||||||
{
|
{
|
||||||
public static TinyMapperUtils Instance { get; } = new();
|
public static TinyMapperUtils Instance { get; } = new();
|
||||||
|
|
||||||
private TinyMapperUtils()
|
private TinyMapperUtils()
|
||||||
{
|
{
|
||||||
TinyMapper.Bind<ProxyAndRecordSettings, ProxyAndRecordSettingsModel>();
|
TinyMapper.Bind<ProxyAndRecordSettings, ProxyAndRecordSettingsModel>();
|
||||||
TinyMapper.Bind<WebProxySettings, WebProxySettingsModel>();
|
TinyMapper.Bind<WebProxySettings, WebProxySettingsModel>();
|
||||||
|
TinyMapper.Bind<ProxyUrlReplaceSettings, ProxyUrlReplaceSettingsModel>();
|
||||||
TinyMapper.Bind<ProxyAndRecordSettingsModel, ProxyAndRecordSettings>();
|
|
||||||
TinyMapper.Bind<WebProxySettingsModel, WebProxySettings>();
|
TinyMapper.Bind<ProxyAndRecordSettingsModel, ProxyAndRecordSettings>();
|
||||||
}
|
TinyMapper.Bind<WebProxySettingsModel, WebProxySettings>();
|
||||||
|
TinyMapper.Bind<ProxyUrlReplaceSettingsModel, ProxyUrlReplaceSettings>();
|
||||||
public ProxyAndRecordSettingsModel? Map(ProxyAndRecordSettings? instance)
|
}
|
||||||
{
|
|
||||||
return instance == null ? null : TinyMapper.Map<ProxyAndRecordSettingsModel>(instance);
|
public ProxyAndRecordSettingsModel? Map(ProxyAndRecordSettings? instance)
|
||||||
}
|
{
|
||||||
|
return instance == null ? null : TinyMapper.Map<ProxyAndRecordSettingsModel>(instance);
|
||||||
public ProxyAndRecordSettings? Map(ProxyAndRecordSettingsModel? model)
|
}
|
||||||
{
|
|
||||||
return model == null ? null : TinyMapper.Map<ProxyAndRecordSettings>(model);
|
public ProxyAndRecordSettings? Map(ProxyAndRecordSettingsModel? model)
|
||||||
}
|
{
|
||||||
|
return model == null ? null : TinyMapper.Map<ProxyAndRecordSettings>(model);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,16 +1,11 @@
|
|||||||
namespace WireMock.Org.Abstractions
|
namespace WireMock.Org.Abstractions;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The fault to apply (instead of a full, valid response).
|
||||||
|
/// </summary>
|
||||||
|
public static class MappingsResponseFaultConstants
|
||||||
{
|
{
|
||||||
/// <summary>
|
public const string EMPTYRESPONSE = "EMPTY_RESPONSE";
|
||||||
/// The fault to apply (instead of a full, valid response).
|
|
||||||
/// </summary>
|
|
||||||
public static class MappingsResponseFaultConstants
|
|
||||||
{
|
|
||||||
public const string CONNECTIONRESETBYPEER = "CONNECTION_RESET_BY_PEER";
|
|
||||||
|
|
||||||
public const string EMPTYRESPONSE = "EMPTY_RESPONSE";
|
public const string MALFORMEDRESPONSECHUNK = "MALFORMED_RESPONSE_CHUNK";
|
||||||
|
}
|
||||||
public const string MALFORMEDRESPONSECHUNK = "MALFORMED_RESPONSE_CHUNK";
|
|
||||||
|
|
||||||
public const string RANDOMDATATHENCLOSE = "RANDOM_DATA_THEN_CLOSE";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -6,6 +6,7 @@ using WireMock.Logging;
|
|||||||
using WireMock.Matchers.Request;
|
using WireMock.Matchers.Request;
|
||||||
using WireMock.Models;
|
using WireMock.Models;
|
||||||
using WireMock.Owin;
|
using WireMock.Owin;
|
||||||
|
using WireMock.Services;
|
||||||
using WireMock.Util;
|
using WireMock.Util;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
@@ -14,6 +15,8 @@ namespace WireMock.Net.Tests.Owin;
|
|||||||
public class MappingMatcherTests
|
public class MappingMatcherTests
|
||||||
{
|
{
|
||||||
private readonly Mock<IWireMockMiddlewareOptions> _optionsMock;
|
private readonly Mock<IWireMockMiddlewareOptions> _optionsMock;
|
||||||
|
private readonly Mock<IRandomizerDoubleBetween0And1> _randomizerDoubleBetween0And1Mock;
|
||||||
|
|
||||||
private readonly MappingMatcher _sut;
|
private readonly MappingMatcher _sut;
|
||||||
|
|
||||||
public MappingMatcherTests()
|
public MappingMatcherTests()
|
||||||
@@ -29,7 +32,10 @@ public class MappingMatcherTests
|
|||||||
loggerMock.Setup(l => l.Error(It.IsAny<string>()));
|
loggerMock.Setup(l => l.Error(It.IsAny<string>()));
|
||||||
_optionsMock.Setup(o => o.Logger).Returns(loggerMock.Object);
|
_optionsMock.Setup(o => o.Logger).Returns(loggerMock.Object);
|
||||||
|
|
||||||
_sut = new MappingMatcher(_optionsMock.Object);
|
_randomizerDoubleBetween0And1Mock = new Mock<IRandomizerDoubleBetween0And1>();
|
||||||
|
_randomizerDoubleBetween0And1Mock.Setup(r => r.Generate()).Returns(0.0);
|
||||||
|
|
||||||
|
_sut = new MappingMatcher(_optionsMock.Object, _randomizerDoubleBetween0And1Mock.Object);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
@@ -76,8 +82,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 }),
|
(guid1, new[] { 0.1 }, null),
|
||||||
(guid2, new[] { 1.0 })
|
(guid2, new[] { 1.0 }, null)
|
||||||
);
|
);
|
||||||
_optionsMock.Setup(o => o.Mappings).Returns(mappings);
|
_optionsMock.Setup(o => o.Mappings).Returns(mappings);
|
||||||
|
|
||||||
@@ -104,8 +110,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 }),
|
(guid1, new[] { 0.1 }, null),
|
||||||
(guid2, new[] { 0.9 })
|
(guid2, new[] { 0.9 }, null)
|
||||||
);
|
);
|
||||||
_optionsMock.Setup(o => o.Mappings).Returns(mappings);
|
_optionsMock.Setup(o => o.Mappings).Returns(mappings);
|
||||||
|
|
||||||
@@ -131,8 +137,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 }),
|
(guid1, new[] { 0.1 }, null),
|
||||||
(guid2, new[] { 0.9 })
|
(guid2, new[] { 0.9 }, null)
|
||||||
);
|
);
|
||||||
_optionsMock.Setup(o => o.Mappings).Returns(mappings);
|
_optionsMock.Setup(o => o.Mappings).Returns(mappings);
|
||||||
|
|
||||||
@@ -158,8 +164,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 }),
|
(guid1, new[] { 1.0 }, null),
|
||||||
(guid2, new[] { 1.0, 1.0 })
|
(guid2, new[] { 1.0, 1.0 }, null)
|
||||||
);
|
);
|
||||||
_optionsMock.Setup(o => o.Mappings).Returns(mappings);
|
_optionsMock.Setup(o => o.Mappings).Returns(mappings);
|
||||||
|
|
||||||
@@ -178,7 +184,31 @@ public class MappingMatcherTests
|
|||||||
result.Partial.RequestMatchResult.AverageTotalScore.Should().Be(1.0);
|
result.Partial.RequestMatchResult.AverageTotalScore.Should().Be(1.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ConcurrentDictionary<Guid, IMapping> InitMappings(params (Guid guid, double[] scores)[] matches)
|
[Fact]
|
||||||
|
public void MappingMatcher_FindBestMatch_WhenProbabilityFailsFirst_ShouldReturnSecondMatch()
|
||||||
|
{
|
||||||
|
// Assign
|
||||||
|
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 }, 1.0),
|
||||||
|
(guid2, new[] { 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(guid2);
|
||||||
|
result.Match.RequestMatchResult.AverageTotalScore.Should().Be(1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ConcurrentDictionary<Guid, IMapping> InitMappings(params (Guid guid, double[] scores, double? probability)[] matches)
|
||||||
{
|
{
|
||||||
var mappings = new ConcurrentDictionary<Guid, IMapping>();
|
var mappings = new ConcurrentDictionary<Guid, IMapping>();
|
||||||
|
|
||||||
@@ -193,6 +223,8 @@ public class MappingMatcherTests
|
|||||||
requestMatchResult.AddScore(typeof(object), score);
|
requestMatchResult.AddScore(typeof(object), score);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mappingMock.SetupGet(m => m.Probability).Returns(match.probability);
|
||||||
|
|
||||||
mappingMock.Setup(m => m.GetRequestMatchResult(It.IsAny<RequestMessage>(), It.IsAny<string>())).Returns(requestMatchResult);
|
mappingMock.Setup(m => m.GetRequestMatchResult(It.IsAny<RequestMessage>(), It.IsAny<string>())).Returns(requestMatchResult);
|
||||||
|
|
||||||
mappings.TryAdd(match.guid, mappingMock.Object);
|
mappings.TryAdd(match.guid, mappingMock.Object);
|
||||||
|
|||||||
@@ -177,7 +177,7 @@ public class WireMockMiddlewareTests
|
|||||||
_mappingMock.SetupGet(m => m.Provider).Returns(responseBuilder);
|
_mappingMock.SetupGet(m => m.Provider).Returns(responseBuilder);
|
||||||
_mappingMock.SetupGet(m => m.Settings).Returns(settings);
|
_mappingMock.SetupGet(m => m.Settings).Returns(settings);
|
||||||
|
|
||||||
var newMappingFromProxy = new Mapping(Guid.NewGuid(), _updatedAt, string.Empty, string.Empty, null, settings, Request.Create(), Response.Create(), 0, null, null, null, null, null, false, null, null);
|
var newMappingFromProxy = new Mapping(Guid.NewGuid(), _updatedAt, string.Empty, string.Empty, null, settings, Request.Create(), Response.Create(), 0, null, null, null, null, null, false, null, null, null);
|
||||||
_mappingMock.Setup(m => m.ProvideResponseAsync(It.IsAny<RequestMessage>())).ReturnsAsync((new ResponseMessage(), newMappingFromProxy));
|
_mappingMock.Setup(m => m.ProvideResponseAsync(It.IsAny<RequestMessage>())).ReturnsAsync((new ResponseMessage(), newMappingFromProxy));
|
||||||
|
|
||||||
var requestBuilder = Request.Create().UsingAnyMethod();
|
var requestBuilder = Request.Create().UsingAnyMethod();
|
||||||
@@ -231,7 +231,7 @@ public class WireMockMiddlewareTests
|
|||||||
_mappingMock.SetupGet(m => m.Provider).Returns(responseBuilder);
|
_mappingMock.SetupGet(m => m.Provider).Returns(responseBuilder);
|
||||||
_mappingMock.SetupGet(m => m.Settings).Returns(settings);
|
_mappingMock.SetupGet(m => m.Settings).Returns(settings);
|
||||||
|
|
||||||
var newMappingFromProxy = new Mapping(Guid.NewGuid(), _updatedAt, "my-title", "my-description", null, settings, Request.Create(), Response.Create(), 0, null, null, null, null, null, false, null, data: null);
|
var newMappingFromProxy = new Mapping(Guid.NewGuid(), _updatedAt, "my-title", "my-description", null, settings, Request.Create(), Response.Create(), 0, null, null, null, null, null, false, null, data: null, probability: null);
|
||||||
_mappingMock.Setup(m => m.ProvideResponseAsync(It.IsAny<RequestMessage>())).ReturnsAsync((new ResponseMessage(), newMappingFromProxy));
|
_mappingMock.Setup(m => m.ProvideResponseAsync(It.IsAny<RequestMessage>())).ReturnsAsync((new ResponseMessage(), newMappingFromProxy));
|
||||||
|
|
||||||
var requestBuilder = Request.Create().UsingAnyMethod();
|
var requestBuilder = Request.Create().UsingAnyMethod();
|
||||||
|
|||||||
@@ -136,7 +136,8 @@ public partial class MappingConverterTests
|
|||||||
null,
|
null,
|
||||||
false,
|
false,
|
||||||
null,
|
null,
|
||||||
data: null
|
data: null,
|
||||||
|
probability: 0.3
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,8 +9,9 @@
|
|||||||
.WithBody("b")
|
.WithBody("b")
|
||||||
)
|
)
|
||||||
.WithGuid("8e7b9ab7-e18e-4502-8bc9-11e6679811cc")
|
.WithGuid("8e7b9ab7-e18e-4502-8bc9-11e6679811cc")
|
||||||
|
.WithProbability(0.3)
|
||||||
.RespondWith(Response.Create()
|
.RespondWith(Response.Create()
|
||||||
.WithHeader("Keep-Alive)", "test")
|
.WithHeader("Keep-Alive", "test")
|
||||||
.WithBody("bbb")
|
.WithBody("bbb")
|
||||||
.WithDelay(12345)
|
.WithDelay(12345)
|
||||||
.WithTransformer()
|
.WithTransformer()
|
||||||
|
|||||||
@@ -10,8 +10,9 @@ builder
|
|||||||
.WithBody("b")
|
.WithBody("b")
|
||||||
)
|
)
|
||||||
.WithGuid("8e7b9ab7-e18e-4502-8bc9-11e6679811cc")
|
.WithGuid("8e7b9ab7-e18e-4502-8bc9-11e6679811cc")
|
||||||
|
.WithProbability(0.3)
|
||||||
.RespondWith(Response.Create()
|
.RespondWith(Response.Create()
|
||||||
.WithHeader("Keep-Alive)", "test")
|
.WithHeader("Keep-Alive", "test")
|
||||||
.WithBody("bbb")
|
.WithBody("bbb")
|
||||||
.WithDelay(12345)
|
.WithDelay(12345)
|
||||||
.WithTransformer()
|
.WithTransformer()
|
||||||
|
|||||||
@@ -9,8 +9,9 @@
|
|||||||
.WithBody("b")
|
.WithBody("b")
|
||||||
)
|
)
|
||||||
.WithGuid("8e7b9ab7-e18e-4502-8bc9-11e6679811cc")
|
.WithGuid("8e7b9ab7-e18e-4502-8bc9-11e6679811cc")
|
||||||
|
.WithProbability(0.3)
|
||||||
.RespondWith(Response.Create()
|
.RespondWith(Response.Create()
|
||||||
.WithHeader("Keep-Alive)", "test")
|
.WithHeader("Keep-Alive", "test")
|
||||||
.WithBody("bbb")
|
.WithBody("bbb")
|
||||||
.WithDelay(12345)
|
.WithDelay(12345)
|
||||||
.WithTransformer()
|
.WithTransformer()
|
||||||
|
|||||||
@@ -10,8 +10,9 @@ server
|
|||||||
.WithBody("b")
|
.WithBody("b")
|
||||||
)
|
)
|
||||||
.WithGuid("8e7b9ab7-e18e-4502-8bc9-11e6679811cc")
|
.WithGuid("8e7b9ab7-e18e-4502-8bc9-11e6679811cc")
|
||||||
|
.WithProbability(0.3)
|
||||||
.RespondWith(Response.Create()
|
.RespondWith(Response.Create()
|
||||||
.WithHeader("Keep-Alive)", "test")
|
.WithHeader("Keep-Alive", "test")
|
||||||
.WithBody("bbb")
|
.WithBody("bbb")
|
||||||
.WithDelay(12345)
|
.WithDelay(12345)
|
||||||
.WithTransformer()
|
.WithTransformer()
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
Guid: Guid_1,
|
||||||
|
UpdatedAt: DateTime_1,
|
||||||
|
Title: ,
|
||||||
|
Description: ,
|
||||||
|
Priority: 42,
|
||||||
|
Request: {},
|
||||||
|
Response: {},
|
||||||
|
UseWebhooksFireAndForget: false,
|
||||||
|
Probability: 0.4
|
||||||
|
}
|
||||||
@@ -57,7 +57,7 @@ public partial class MappingConverterTests
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
var mapping = new Mapping(_guid, _updatedAt, string.Empty, string.Empty, null, _settings, request, response, 0, null, null, null, null, webhooks, false, null, data: null);
|
var mapping = new Mapping(_guid, _updatedAt, string.Empty, string.Empty, null, _settings, request, response, 0, null, null, null, null, webhooks, false, null, data: null, probability: null);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var model = _sut.ToMappingModel(mapping);
|
var model = _sut.ToMappingModel(mapping);
|
||||||
@@ -130,7 +130,7 @@ public partial class MappingConverterTests
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
var mapping = new Mapping(_guid, _updatedAt, string.Empty, string.Empty, null, _settings, request, response, 0, null, null, null, null, webhooks, true, null, data: null);
|
var mapping = new Mapping(_guid, _updatedAt, string.Empty, string.Empty, null, _settings, request, response, 0, null, null, null, null, webhooks, true, null, data: null, probability: null);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var model = _sut.ToMappingModel(mapping);
|
var model = _sut.ToMappingModel(mapping);
|
||||||
@@ -168,7 +168,7 @@ public partial class MappingConverterTests
|
|||||||
var description = "my-description";
|
var description = "my-description";
|
||||||
var request = Request.Create();
|
var request = Request.Create();
|
||||||
var response = Response.Create();
|
var response = Response.Create();
|
||||||
var mapping = new Mapping(_guid, _updatedAt, title, description, null, _settings, request, response, 0, null, null, null, null, null, false, null, data: null);
|
var mapping = new Mapping(_guid, _updatedAt, title, description, null, _settings, request, response, 0, null, null, null, null, null, false, null, data: null, probability: null);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var model = _sut.ToMappingModel(mapping);
|
var model = _sut.ToMappingModel(mapping);
|
||||||
@@ -188,7 +188,7 @@ public partial class MappingConverterTests
|
|||||||
// Assign
|
// Assign
|
||||||
var request = Request.Create();
|
var request = Request.Create();
|
||||||
var response = Response.Create().WithBodyAsJson(new { x = "x" }).WithTransformer();
|
var response = Response.Create().WithBodyAsJson(new { x = "x" }).WithTransformer();
|
||||||
var mapping = new Mapping(_guid, _updatedAt, string.Empty, string.Empty, null, _settings, request, response, 42, null, null, null, null, null, false, null, data: null);
|
var mapping = new Mapping(_guid, _updatedAt, string.Empty, string.Empty, null, _settings, request, response, 42, null, null, null, null, null, false, null, data: null, probability: null);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var model = _sut.ToMappingModel(mapping);
|
var model = _sut.ToMappingModel(mapping);
|
||||||
@@ -217,7 +217,7 @@ public partial class MappingConverterTests
|
|||||||
End = end,
|
End = end,
|
||||||
TTL = ttl
|
TTL = ttl
|
||||||
};
|
};
|
||||||
var mapping = new Mapping(_guid, _updatedAt, string.Empty, string.Empty, null, _settings, request, response, 42, null, null, null, null, null, false, timeSettings, data: null);
|
var mapping = new Mapping(_guid, _updatedAt, string.Empty, string.Empty, null, _settings, request, response, 42, null, null, null, null, null, false, timeSettings, data: null, probability: null);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var model = _sut.ToMappingModel(mapping);
|
var model = _sut.ToMappingModel(mapping);
|
||||||
@@ -248,7 +248,7 @@ public partial class MappingConverterTests
|
|||||||
{
|
{
|
||||||
var request = Request.Create();
|
var request = Request.Create();
|
||||||
var response = Response.Create().WithDelay(test.Delay);
|
var response = Response.Create().WithDelay(test.Delay);
|
||||||
var mapping = new Mapping(Guid.NewGuid(), _updatedAt, string.Empty, string.Empty, string.Empty, _settings, request, response, 42, null, null, null, null, null, false, null, data: null);
|
var mapping = new Mapping(Guid.NewGuid(), _updatedAt, string.Empty, string.Empty, string.Empty, _settings, request, response, 42, null, null, null, null, null, false, null, data: null, probability: null);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var model = _sut.ToMappingModel(mapping);
|
var model = _sut.ToMappingModel(mapping);
|
||||||
@@ -266,7 +266,7 @@ public partial class MappingConverterTests
|
|||||||
var delay = 1000;
|
var delay = 1000;
|
||||||
var request = Request.Create();
|
var request = Request.Create();
|
||||||
var response = Response.Create().WithDelay(delay);
|
var response = Response.Create().WithDelay(delay);
|
||||||
var mapping = new Mapping(_guid, _updatedAt, string.Empty, string.Empty, null, _settings, request, response, 42, null, null, null, null, null, false, null, data: null);
|
var mapping = new Mapping(_guid, _updatedAt, string.Empty, string.Empty, null, _settings, request, response, 42, null, null, null, null, null, false, null, data: null, probability: null);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var model = _sut.ToMappingModel(mapping);
|
var model = _sut.ToMappingModel(mapping);
|
||||||
@@ -286,7 +286,7 @@ public partial class MappingConverterTests
|
|||||||
int minimumDelay = 1000;
|
int minimumDelay = 1000;
|
||||||
var request = Request.Create();
|
var request = Request.Create();
|
||||||
var response = Response.Create().WithRandomDelay(minimumDelay);
|
var response = Response.Create().WithRandomDelay(minimumDelay);
|
||||||
var mapping = new Mapping(_guid, _updatedAt, string.Empty, string.Empty, null, _settings, request, response, 42, null, null, null, null, null, false, null, data: null);
|
var mapping = new Mapping(_guid, _updatedAt, string.Empty, string.Empty, null, _settings, request, response, 42, null, null, null, null, null, false, null, data: null, probability: null);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var model = _sut.ToMappingModel(mapping);
|
var model = _sut.ToMappingModel(mapping);
|
||||||
@@ -309,7 +309,7 @@ public partial class MappingConverterTests
|
|||||||
int maximumDelay = 2000;
|
int maximumDelay = 2000;
|
||||||
var request = Request.Create();
|
var request = Request.Create();
|
||||||
var response = Response.Create().WithRandomDelay(minimumDelay, maximumDelay);
|
var response = Response.Create().WithRandomDelay(minimumDelay, maximumDelay);
|
||||||
var mapping = new Mapping(_guid, _updatedAt, string.Empty, string.Empty, null, _settings, request, response, 42, null, null, null, null, null, false, null, data: null);
|
var mapping = new Mapping(_guid, _updatedAt, string.Empty, string.Empty, null, _settings, request, response, 42, null, null, null, null, null, false, null, data: null, probability: null);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var model = _sut.ToMappingModel(mapping);
|
var model = _sut.ToMappingModel(mapping);
|
||||||
@@ -323,5 +323,25 @@ public partial class MappingConverterTests
|
|||||||
// Verify
|
// Verify
|
||||||
return Verifier.Verify(model);
|
return Verifier.Verify(model);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public Task ToMappingModel_WithProbability_ReturnsCorrectModel()
|
||||||
|
{
|
||||||
|
// Assign
|
||||||
|
double probability = 0.4;
|
||||||
|
var request = Request.Create();
|
||||||
|
var response = Response.Create();
|
||||||
|
var mapping = new Mapping(_guid, _updatedAt, string.Empty, string.Empty, null, _settings, request, response, 42, null, null, null, null, null, false, null, data: null, probability: probability);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var model = _sut.ToMappingModel(mapping);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
model.Should().NotBeNull();
|
||||||
|
model.Probability.Should().Be(0.4);
|
||||||
|
|
||||||
|
// Verify
|
||||||
|
return Verifier.Verify(model);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -10,17 +10,17 @@ public class BytesEncodingUtilsTests
|
|||||||
[Fact]
|
[Fact]
|
||||||
public void TryGetEncoding_UTF32()
|
public void TryGetEncoding_UTF32()
|
||||||
{
|
{
|
||||||
var result = BytesEncodingUtils.TryGetEncoding(new byte[] { 0xff, 0xfe, 0x00, 0x00 }, out Encoding encoding);
|
var result = BytesEncodingUtils.TryGetEncoding(new byte[] { 0xff, 0xfe, 0x00, 0x00 }, out var encoding);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
result.Should().BeTrue();
|
result.Should().BeTrue();
|
||||||
encoding.CodePage.Should().Be(Encoding.UTF32.CodePage);
|
encoding?.CodePage.Should().Be(Encoding.UTF32.CodePage);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void TryGetEncoding_Invalid()
|
public void TryGetEncoding_Invalid()
|
||||||
{
|
{
|
||||||
var result = BytesEncodingUtils.TryGetEncoding(new byte[] { 0xff }, out Encoding encoding);
|
var result = BytesEncodingUtils.TryGetEncoding(new byte[] { 0xff }, out var encoding);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
result.Should().BeFalse();
|
result.Should().BeFalse();
|
||||||
|
|||||||
105
test/WireMock.Net.Tests/Util/CSharpFormatterTests.cs
Normal file
105
test/WireMock.Net.Tests/Util/CSharpFormatterTests.cs
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
using FluentAssertions;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
using WireMock.Util;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace WireMock.Net.Tests.Util;
|
||||||
|
|
||||||
|
public class CSharpFormatterTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void ConvertToAnonymousObjectDefinition_ShouldReturn_ValidValue_WhenJsonBodyIsValidJsonString()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var jsonBody = new { Key1 = "value1", Key2 = 42, F = 1.2 };
|
||||||
|
var expectedOutput = "new\r\n {\r\n Key1 = \"value1\",\r\n Key2 = 42,\r\n F = 1.2\r\n }";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = CSharpFormatter.ConvertToAnonymousObjectDefinition(jsonBody);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Should().Be(expectedOutput);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ToCSharpStringLiteral_ShouldReturn_ValidValue_WhenStringIsNotNull()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var inputString = "test string";
|
||||||
|
var expectedOutput = "\"test string\"";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = CSharpFormatter.ToCSharpStringLiteral(inputString);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Should().Be(expectedOutput);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ToCSharpStringLiteral_ShouldReturn_ValidValue_WhenStringContainsNewLineCharacters()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var inputString = "line1\nline2\nline3";
|
||||||
|
var expectedOutput = "@\"line1\nline2\nline3\"";
|
||||||
|
|
||||||
|
// Action
|
||||||
|
var result = CSharpFormatter.ToCSharpStringLiteral(inputString);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Should().Be(expectedOutput);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void FormatPropertyName_ShouldReturn_ValidPropertyName_WhenPropertyNameIsNotReserved()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var propertyName = "propertyname";
|
||||||
|
var expectedOutput = "propertyname";
|
||||||
|
|
||||||
|
// Action
|
||||||
|
var result = CSharpFormatter.FormatPropertyName(propertyName);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Should().Be(expectedOutput);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void FormatPropertyName_ShouldReturn_ValidPropertyName_WhenPropertyNameIsReserved()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var propertyName = "class";
|
||||||
|
var expectedOutput = "@class";
|
||||||
|
|
||||||
|
// Action
|
||||||
|
var result = CSharpFormatter.FormatPropertyName(propertyName);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Should().Be(expectedOutput);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ConvertJsonToAnonymousObjectDefinition_ShouldReturn_ValidObject_WhenJsonInputIsValid()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var jObject = new JObject
|
||||||
|
(
|
||||||
|
new JProperty("Name", "John Smith"),
|
||||||
|
new JProperty("Age", 25.1f),
|
||||||
|
new JProperty("Gender", "Male"),
|
||||||
|
new JProperty("address", new JObject
|
||||||
|
(
|
||||||
|
new JProperty("Street", "123 Main St"),
|
||||||
|
new JProperty("City", "Anytown"),
|
||||||
|
new JProperty("State", "CA"),
|
||||||
|
new JProperty("Zip", "90001")
|
||||||
|
)
|
||||||
|
));
|
||||||
|
var expectedOutput = "new\r\n{\r\n Name = \"John Smith\",\r\n Age = 25.1,\r\n Gender = \"Male\",\r\n address = new\r\n {\r\n Street = \"123 Main St\",\r\n City = \"Anytown\",\r\n State = \"CA\",\r\n Zip = \"90001\"\r\n }\r\n}";
|
||||||
|
|
||||||
|
// Action
|
||||||
|
var result = CSharpFormatter.ConvertJsonToAnonymousObjectDefinition(jObject);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Should().Be(expectedOutput);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,5 +7,6 @@ server
|
|||||||
)
|
)
|
||||||
.WithGuid("90356dba-b36c-469a-a17e-669cd84f1f05")
|
.WithGuid("90356dba-b36c-469a-a17e-669cd84f1f05")
|
||||||
.RespondWith(Response.Create()
|
.RespondWith(Response.Create()
|
||||||
|
.WithStatusCode(200)
|
||||||
.WithBody("1")
|
.WithBody("1")
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -7,18 +7,84 @@ server
|
|||||||
)
|
)
|
||||||
.WithGuid("90356dba-b36c-469a-a17e-669cd84f1f05")
|
.WithGuid("90356dba-b36c-469a-a17e-669cd84f1f05")
|
||||||
.RespondWith(Response.Create()
|
.RespondWith(Response.Create()
|
||||||
|
.WithStatusCode(200)
|
||||||
.WithBody("1")
|
.WithBody("1")
|
||||||
);
|
);
|
||||||
|
|
||||||
server
|
server
|
||||||
.Given(Request.Create()
|
.Given(Request.Create()
|
||||||
.UsingMethod("GET")
|
.UsingMethod("POST")
|
||||||
.WithPath("/foo2")
|
.WithPath("/foo2")
|
||||||
.WithParam("p2", "abc")
|
.WithParam("p2", "abc")
|
||||||
|
.WithHeader("h1", "W/\"234f2q3r\"", true)
|
||||||
)
|
)
|
||||||
.WithGuid("1b731398-4a5b-457f-a6e3-d65e541c428f")
|
.WithGuid("1b731398-4a5b-457f-a6e3-d65e541c428f")
|
||||||
.RespondWith(Response.Create()
|
.RespondWith(Response.Create()
|
||||||
.WithHeader("hk)", "hv")
|
.WithStatusCode("201")
|
||||||
|
.WithHeader("hk", "hv")
|
||||||
|
.WithHeader("ETag", "W/\"168d8e\"")
|
||||||
.WithBody("2")
|
.WithBody("2")
|
||||||
);
|
);
|
||||||
|
|
||||||
|
server
|
||||||
|
.Given(Request.Create()
|
||||||
|
.UsingMethod("DELETE")
|
||||||
|
.WithUrl("https://localhost/test")
|
||||||
|
)
|
||||||
|
.WithGuid("f74fd144-df53-404f-8e35-da22a640bd5f")
|
||||||
|
.RespondWith(Response.Create()
|
||||||
|
.WithStatusCode(208)
|
||||||
|
.WithBodyAsJson(new
|
||||||
|
{
|
||||||
|
@as = 1,
|
||||||
|
b = 1.2,
|
||||||
|
d = true,
|
||||||
|
e = false,
|
||||||
|
f = new [] { 1, 2, 3, 4 },
|
||||||
|
g = new
|
||||||
|
{
|
||||||
|
z1 = 1,
|
||||||
|
z2 = 2,
|
||||||
|
z3 = new [] { "a", "b", "c" },
|
||||||
|
z4 = new []
|
||||||
|
{
|
||||||
|
new
|
||||||
|
{
|
||||||
|
a = 1,
|
||||||
|
b = 2
|
||||||
|
},
|
||||||
|
new
|
||||||
|
{
|
||||||
|
a = 2,
|
||||||
|
b = 3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
date_field = "2023-05-08T11:20:19",
|
||||||
|
string_field_with_date = "2021-03-13T21:04:00Z",
|
||||||
|
multiline_text = @"This
|
||||||
|
is
|
||||||
|
multiline
|
||||||
|
text
|
||||||
|
"
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
server
|
||||||
|
.Given(Request.Create()
|
||||||
|
.UsingMethod("POST")
|
||||||
|
.WithPath("/foo3")
|
||||||
|
.WithBody(new JsonPartialMatcher(
|
||||||
|
value: "{ a = 1, b = 2 }",
|
||||||
|
ignoreCase: false,
|
||||||
|
throwException: false,
|
||||||
|
regex: false
|
||||||
|
))
|
||||||
|
)
|
||||||
|
.WithGuid("4126dec8-470b-4eff-93bb-c24f83b8b1fd")
|
||||||
|
.RespondWith(Response.Create()
|
||||||
|
.WithStatusCode(200)
|
||||||
|
.WithBody(@"Line1
|
||||||
|
Some ""value"" in Line2")
|
||||||
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Net;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Net.Http.Headers;
|
using System.Net.Http.Headers;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
@@ -17,6 +18,7 @@ using WireMock.Admin.Settings;
|
|||||||
using WireMock.Client;
|
using WireMock.Client;
|
||||||
using WireMock.Handlers;
|
using WireMock.Handlers;
|
||||||
using WireMock.Logging;
|
using WireMock.Logging;
|
||||||
|
using WireMock.Matchers;
|
||||||
using WireMock.Models;
|
using WireMock.Models;
|
||||||
using WireMock.Net.Tests.VerifyExtensions;
|
using WireMock.Net.Tests.VerifyExtensions;
|
||||||
using WireMock.RequestBuilders;
|
using WireMock.RequestBuilders;
|
||||||
@@ -717,6 +719,8 @@ public class WireMockAdminApiTests
|
|||||||
// Arrange
|
// Arrange
|
||||||
var guid1 = Guid.Parse("90356dba-b36c-469a-a17e-669cd84f1f05");
|
var guid1 = Guid.Parse("90356dba-b36c-469a-a17e-669cd84f1f05");
|
||||||
var guid2 = Guid.Parse("1b731398-4a5b-457f-a6e3-d65e541c428f");
|
var guid2 = Guid.Parse("1b731398-4a5b-457f-a6e3-d65e541c428f");
|
||||||
|
var guid3 = Guid.Parse("f74fd144-df53-404f-8e35-da22a640bd5f");
|
||||||
|
var guid4 = Guid.Parse("4126DEC8-470B-4EFF-93BB-C24F83B8B1FD");
|
||||||
var server = WireMockServer.StartWithAdminInterface();
|
var server = WireMockServer.StartWithAdminInterface();
|
||||||
|
|
||||||
server
|
server
|
||||||
@@ -738,21 +742,54 @@ public class WireMockAdminApiTests
|
|||||||
Request.Create()
|
Request.Create()
|
||||||
.WithPath("/foo2")
|
.WithPath("/foo2")
|
||||||
.WithParam("p2", "abc")
|
.WithParam("p2", "abc")
|
||||||
.UsingGet()
|
.WithHeader("h1", "W/\"234f2q3r\"")
|
||||||
|
.UsingPost()
|
||||||
)
|
)
|
||||||
.WithGuid(guid2)
|
.WithGuid(guid2)
|
||||||
.RespondWith(
|
.RespondWith(
|
||||||
Response.Create()
|
Response.Create()
|
||||||
.WithStatusCode(201)
|
.WithStatusCode("201")
|
||||||
.WithHeader("hk", "hv")
|
.WithHeader("hk", "hv")
|
||||||
|
.WithHeader("ETag", "W/\"168d8e\"")
|
||||||
.WithBody("2")
|
.WithBody("2")
|
||||||
);
|
);
|
||||||
|
|
||||||
|
server
|
||||||
|
.Given(
|
||||||
|
Request.Create()
|
||||||
|
.WithUrl("https://localhost/test")
|
||||||
|
.UsingDelete()
|
||||||
|
)
|
||||||
|
.WithGuid(guid3)
|
||||||
|
.RespondWith(
|
||||||
|
Response.Create()
|
||||||
|
.WithStatusCode(HttpStatusCode.AlreadyReported)
|
||||||
|
.WithBodyAsJson(new { @as = 1, b=1.2, d=true, e=false, f=new[]{1,2,3,4}, g= new{z1=1, z2=2, z3=new []{"a","b","c"}, z4=new[]{new {a=1, b=2},new {a=2, b=3}}}, date_field = new DateTime(2023,05,08,11,20,19), string_field_with_date="2021-03-13T21:04:00Z", multiline_text= @"This
|
||||||
|
is
|
||||||
|
multiline
|
||||||
|
text
|
||||||
|
" })
|
||||||
|
);
|
||||||
|
|
||||||
|
server
|
||||||
|
.Given(
|
||||||
|
Request.Create()
|
||||||
|
.WithPath("/foo3")
|
||||||
|
.WithBody(new JsonPartialMatcher(new { a=1, b=2}))
|
||||||
|
.UsingPost()
|
||||||
|
)
|
||||||
|
.WithGuid(guid4)
|
||||||
|
.RespondWith(
|
||||||
|
Response.Create()
|
||||||
|
.WithStatusCode(200)
|
||||||
|
.WithBody("Line1\r\nSome \"value\" in Line2")
|
||||||
|
);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var api = RestClient.For<IWireMockAdminApi>(server.Url);
|
var api = RestClient.For<IWireMockAdminApi>(server.Url);
|
||||||
|
|
||||||
var mappings = await api.GetMappingsAsync().ConfigureAwait(false);
|
var mappings = await api.GetMappingsAsync().ConfigureAwait(false);
|
||||||
mappings.Should().HaveCount(2);
|
mappings.Should().HaveCount(4);
|
||||||
|
|
||||||
var code = await api.GetMappingsCodeAsync().ConfigureAwait(false);
|
var code = await api.GetMappingsCodeAsync().ConfigureAwait(false);
|
||||||
|
|
||||||
|
|||||||
@@ -570,6 +570,52 @@ public class WireMockServerProxyTests
|
|||||||
Check.That(matchers).Contains("name");
|
Check.That(matchers).Contains("name");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task WireMockServer_Proxy_Should_replace_old_path_value_with_new_path_value_in_replace_settings()
|
||||||
|
{
|
||||||
|
// Assign
|
||||||
|
var replaceSettings = new ProxyUrlReplaceSettings
|
||||||
|
{
|
||||||
|
OldValue = "value-to-replace",
|
||||||
|
NewValue = "new-value"
|
||||||
|
};
|
||||||
|
string path = $"/prx_{Guid.NewGuid()}";
|
||||||
|
var serverForProxyForwarding = WireMockServer.Start();
|
||||||
|
serverForProxyForwarding
|
||||||
|
.Given(Request.Create().WithPath($"/{replaceSettings.NewValue}{path}"))
|
||||||
|
.RespondWith(Response.Create());
|
||||||
|
|
||||||
|
var settings = new WireMockServerSettings
|
||||||
|
{
|
||||||
|
ProxyAndRecordSettings = new ProxyAndRecordSettings
|
||||||
|
{
|
||||||
|
Url = serverForProxyForwarding.Urls[0],
|
||||||
|
SaveMapping = true,
|
||||||
|
SaveMappingToFile = false,
|
||||||
|
ReplaceSettings = replaceSettings
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var server = WireMockServer.Start(settings);
|
||||||
|
var defaultMapping = server.Mappings.First();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var requestMessage = new HttpRequestMessage
|
||||||
|
{
|
||||||
|
Method = HttpMethod.Post,
|
||||||
|
RequestUri = new Uri($"{server.Urls[0]}/{replaceSettings.OldValue}{path}"),
|
||||||
|
Content = new StringContent("stringContent")
|
||||||
|
};
|
||||||
|
|
||||||
|
var handler = new HttpClientHandler();
|
||||||
|
await new HttpClient(handler).SendAsync(requestMessage).ConfigureAwait(false);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var mapping = serverForProxyForwarding.Mappings.FirstOrDefault(m => m.Guid != defaultMapping.Guid);
|
||||||
|
var score = mapping.RequestMatcher.GetMatchingScore(serverForProxyForwarding.LogEntries.First().RequestMessage,
|
||||||
|
new RequestMatchResult());
|
||||||
|
Check.That(score).IsEqualTo(1.0);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task WireMockServer_Proxy_Should_preserve_content_header_in_proxied_request_with_empty_content()
|
public async Task WireMockServer_Proxy_Should_preserve_content_header_in_proxied_request_with_empty_content()
|
||||||
{
|
{
|
||||||
@@ -701,7 +747,7 @@ public class WireMockServerProxyTests
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Send some binary content in a request through the proxy and check that the same content
|
/// Send some binary content in a request through the proxy and check that the same content
|
||||||
/// arrived at the target. As example a JPEG/JIFF header is used, which is not representable
|
/// arrived at the target. As example a JPEG/JIFF header is used, which is not representable
|
||||||
/// in UTF8 and breaks if it is not treated as binary content.
|
/// in UTF8 and breaks if it is not treated as binary content.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task WireMockServer_Proxy_Should_preserve_binary_request_content()
|
public async Task WireMockServer_Proxy_Should_preserve_binary_request_content()
|
||||||
|
|||||||
@@ -4,10 +4,13 @@ using System.Net;
|
|||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using FluentAssertions;
|
using FluentAssertions;
|
||||||
|
using Moq;
|
||||||
|
using WireMock.Logging;
|
||||||
using WireMock.Models;
|
using WireMock.Models;
|
||||||
using WireMock.RequestBuilders;
|
using WireMock.RequestBuilders;
|
||||||
using WireMock.ResponseBuilders;
|
using WireMock.ResponseBuilders;
|
||||||
using WireMock.Server;
|
using WireMock.Server;
|
||||||
|
using WireMock.Settings;
|
||||||
using WireMock.Types;
|
using WireMock.Types;
|
||||||
using WireMock.Util;
|
using WireMock.Util;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
@@ -71,7 +74,7 @@ public class WireMockServerWebhookTests
|
|||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
var response = await new HttpClient().SendAsync(request).ConfigureAwait(false);
|
var response = await new HttpClient().SendAsync(request).ConfigureAwait(false);
|
||||||
string content = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
|
var content = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
|
||||||
|
|
||||||
response.StatusCode.Should().Be(HttpStatusCode.OK);
|
response.StatusCode.Should().Be(HttpStatusCode.OK);
|
||||||
content.Should().Be("a-response");
|
content.Should().Be("a-response");
|
||||||
@@ -120,7 +123,7 @@ public class WireMockServerWebhookTests
|
|||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
var response = await new HttpClient().SendAsync(request).ConfigureAwait(false);
|
var response = await new HttpClient().SendAsync(request).ConfigureAwait(false);
|
||||||
string content = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
|
var content = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
|
||||||
|
|
||||||
response.StatusCode.Should().Be(HttpStatusCode.OK);
|
response.StatusCode.Should().Be(HttpStatusCode.OK);
|
||||||
content.Should().Be("a-response");
|
content.Should().Be("a-response");
|
||||||
@@ -132,6 +135,115 @@ public class WireMockServerWebhookTests
|
|||||||
serverReceivingTheWebhook.Dispose();
|
serverReceivingTheWebhook.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task WireMockServer_WithWebhook_When_WebhookEndPointReturnsError_Should_LogWarning()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var serverReceivingTheWebhook = WireMockServer.Start();
|
||||||
|
serverReceivingTheWebhook.Given(Request.Create().WithPath("/x").UsingPost()).RespondWith(Response.Create().WithBody("!Server Error!").WithStatusCode(500));
|
||||||
|
|
||||||
|
var loggerMock = new Mock<IWireMockLogger>();
|
||||||
|
var settings = new WireMockServerSettings
|
||||||
|
{
|
||||||
|
Logger = loggerMock.Object
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var guid = "942cb963-c9a3-4e9c-8e71-c1b26d2a4a05";
|
||||||
|
var server = WireMockServer.Start(settings);
|
||||||
|
server.Given(Request.Create().UsingPost())
|
||||||
|
.WithWebhook(new Webhook
|
||||||
|
{
|
||||||
|
Request = new WebhookRequest
|
||||||
|
{
|
||||||
|
Url = serverReceivingTheWebhook.Url! + "/{{request.Query.q}}",
|
||||||
|
Method = "post",
|
||||||
|
BodyData = new BodyData
|
||||||
|
{
|
||||||
|
BodyAsString = "abc",
|
||||||
|
DetectedBodyType = BodyType.String,
|
||||||
|
DetectedBodyTypeFromContentType = BodyType.String
|
||||||
|
},
|
||||||
|
UseTransformer = true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.WithGuid(guid)
|
||||||
|
.RespondWith(Response.Create().WithBody("a-response"));
|
||||||
|
|
||||||
|
var request = new HttpRequestMessage
|
||||||
|
{
|
||||||
|
Method = HttpMethod.Post,
|
||||||
|
RequestUri = new Uri($"{server.Urls[0]}/TST?q=x"),
|
||||||
|
Content = new StringContent("test")
|
||||||
|
};
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var response = await new HttpClient().SendAsync(request).ConfigureAwait(false);
|
||||||
|
var content = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
|
||||||
|
|
||||||
|
response.StatusCode.Should().Be(HttpStatusCode.OK);
|
||||||
|
content.Should().Be("a-response");
|
||||||
|
|
||||||
|
loggerMock.Verify(l => l.Warn("Sending message to Webhook [0] from Mapping '942cb963-c9a3-4e9c-8e71-c1b26d2a4a05' failed. HttpStatusCode: InternalServerError Content: !Server Error!"));
|
||||||
|
|
||||||
|
serverReceivingTheWebhook.LogEntries.Should().HaveCount(1);
|
||||||
|
serverReceivingTheWebhook.LogEntries.First().MappingGuid.Should().NotBeNull();
|
||||||
|
|
||||||
|
server.Dispose();
|
||||||
|
serverReceivingTheWebhook.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task WireMockServer_WithWebhook_When_WebhookEndPointDoesNotExists_Should_LogError()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var loggerMock = new Mock<IWireMockLogger>();
|
||||||
|
var settings = new WireMockServerSettings
|
||||||
|
{
|
||||||
|
Logger = loggerMock.Object
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var guid = "942cb963-c9a3-4e9c-8e71-c1b26d2a4a05";
|
||||||
|
var server = WireMockServer.Start(settings);
|
||||||
|
server.Given(Request.Create().UsingPost())
|
||||||
|
.WithWebhook(new Webhook
|
||||||
|
{
|
||||||
|
Request = new WebhookRequest
|
||||||
|
{
|
||||||
|
Url = "http://error-not-does-exist-" + Guid.NewGuid(),
|
||||||
|
Method = "post",
|
||||||
|
BodyData = new BodyData
|
||||||
|
{
|
||||||
|
BodyAsString = "abc",
|
||||||
|
DetectedBodyType = BodyType.String,
|
||||||
|
DetectedBodyTypeFromContentType = BodyType.String
|
||||||
|
},
|
||||||
|
UseTransformer = true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.WithGuid(guid)
|
||||||
|
.RespondWith(Response.Create().WithBody("a-response"));
|
||||||
|
|
||||||
|
var request = new HttpRequestMessage
|
||||||
|
{
|
||||||
|
Method = HttpMethod.Post,
|
||||||
|
RequestUri = new Uri(server.Urls[0]),
|
||||||
|
Content = new StringContent("test")
|
||||||
|
};
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var response = await new HttpClient().SendAsync(request).ConfigureAwait(false);
|
||||||
|
var content = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
|
||||||
|
|
||||||
|
response.StatusCode.Should().Be(HttpStatusCode.OK);
|
||||||
|
content.Should().Be("a-response");
|
||||||
|
|
||||||
|
loggerMock.Verify(l => l.Error(It.Is<string>(formatString => formatString.Contains("Sending message to Webhook [0] from Mapping '942cb963-c9a3-4e9c-8e71-c1b26d2a4a05' failed. Exception"))));
|
||||||
|
|
||||||
|
server.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task WireMockServer_WithWebhookArgs_Should_Send_StringMessage_To_Webhook()
|
public async Task WireMockServer_WithWebhookArgs_Should_Send_StringMessage_To_Webhook()
|
||||||
{
|
{
|
||||||
@@ -142,7 +254,7 @@ public class WireMockServerWebhookTests
|
|||||||
// Act
|
// Act
|
||||||
var server = WireMockServer.Start();
|
var server = WireMockServer.Start();
|
||||||
server.Given(Request.Create().UsingPost())
|
server.Given(Request.Create().UsingPost())
|
||||||
.WithWebhook(serverReceivingTheWebhook.Urls[0], "post", null, "OK !", true, TransformerType.Handlebars)
|
.WithWebhook(serverReceivingTheWebhook.Urls[0], "post", null, "OK !")
|
||||||
.RespondWith(Response.Create().WithBody("a-response"));
|
.RespondWith(Response.Create().WithBody("a-response"));
|
||||||
|
|
||||||
var request = new HttpRequestMessage
|
var request = new HttpRequestMessage
|
||||||
@@ -154,7 +266,7 @@ public class WireMockServerWebhookTests
|
|||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
var response = await new HttpClient().SendAsync(request).ConfigureAwait(false);
|
var response = await new HttpClient().SendAsync(request).ConfigureAwait(false);
|
||||||
string content = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
|
var content = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
|
||||||
|
|
||||||
response.StatusCode.Should().Be(HttpStatusCode.OK);
|
response.StatusCode.Should().Be(HttpStatusCode.OK);
|
||||||
content.Should().Be("a-response");
|
content.Should().Be("a-response");
|
||||||
@@ -176,7 +288,7 @@ public class WireMockServerWebhookTests
|
|||||||
// Act
|
// Act
|
||||||
var server = WireMockServer.Start();
|
var server = WireMockServer.Start();
|
||||||
server.Given(Request.Create().UsingPost())
|
server.Given(Request.Create().UsingPost())
|
||||||
.WithWebhook(serverReceivingTheWebhook.Urls[0], "post", null, new { Status = "OK" }, true, TransformerType.Handlebars)
|
.WithWebhook(serverReceivingTheWebhook.Urls[0], "post", null, new { Status = "OK" })
|
||||||
.RespondWith(Response.Create().WithBody("a-response"));
|
.RespondWith(Response.Create().WithBody("a-response"));
|
||||||
|
|
||||||
var request = new HttpRequestMessage
|
var request = new HttpRequestMessage
|
||||||
@@ -188,7 +300,7 @@ public class WireMockServerWebhookTests
|
|||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
var response = await new HttpClient().SendAsync(request).ConfigureAwait(false);
|
var response = await new HttpClient().SendAsync(request).ConfigureAwait(false);
|
||||||
string content = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
|
var content = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
|
||||||
|
|
||||||
response.StatusCode.Should().Be(HttpStatusCode.OK);
|
response.StatusCode.Should().Be(HttpStatusCode.OK);
|
||||||
content.Should().Be("a-response");
|
content.Should().Be("a-response");
|
||||||
|
|||||||
@@ -0,0 +1,37 @@
|
|||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using WireMock.RequestBuilders;
|
||||||
|
using WireMock.ResponseBuilders;
|
||||||
|
using WireMock.Server;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace WireMock.Net.Tests;
|
||||||
|
|
||||||
|
public partial class WireMockServerTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public async Task WireMockServer_WithProbability()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var server = WireMockServer.Start();
|
||||||
|
server
|
||||||
|
.Given(Request.Create().UsingGet().WithPath("/foo"))
|
||||||
|
.WithProbability(0.5)
|
||||||
|
.RespondWith(Response.Create().WithStatusCode(200));
|
||||||
|
|
||||||
|
server
|
||||||
|
.Given(Request.Create().UsingGet().WithPath("/foo"))
|
||||||
|
.RespondWith(Response.Create().WithStatusCode(500));
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var requestUri = new Uri($"http://localhost:{server.Port}/foo");
|
||||||
|
var response = await server.CreateClient().GetAsync(requestUri).ConfigureAwait(false);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.True(new[] { HttpStatusCode.OK, HttpStatusCode.InternalServerError }.Contains(response.StatusCode));
|
||||||
|
|
||||||
|
server.Stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -34,6 +34,32 @@ public partial class WireMockServerTests
|
|||||||
_testOutputHelper = testOutputHelper;
|
_testOutputHelper = testOutputHelper;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void WireMockServer_Start()
|
||||||
|
{
|
||||||
|
// Act
|
||||||
|
var server = WireMockServer.Start();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
server.IsStarted.Should().BeTrue();
|
||||||
|
server.IsStartedWithAdminInterface.Should().BeFalse();
|
||||||
|
|
||||||
|
server.Stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void WireMockServer_StartWithAdminInterface()
|
||||||
|
{
|
||||||
|
// Act
|
||||||
|
var server = WireMockServer.StartWithAdminInterface();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
server.IsStarted.Should().BeTrue();
|
||||||
|
server.IsStartedWithAdminInterface.Should().BeTrue();
|
||||||
|
|
||||||
|
server.Stop();
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task WireMockServer_Should_Reset_LogEntries()
|
public async Task WireMockServer_Should_Reset_LogEntries()
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user