Compare commits

..

12 Commits

Author SHA1 Message Date
Stef Heyenrath
1e44f52ad6 1.5.10 2022-11-06 13:29:12 +01:00
Stef Heyenrath
7fd1d30d0e Add WireMockNullLogger as valid commandline logger option (#845)
* Add WireMockNullLogger as valid commandline logger option

* .
2022-11-06 13:25:26 +01:00
Gerhard Gradnig
49b29d74dc Webhook: Use the transformed URL to create the HttpRequestMessage (#843)
Co-authored-by: Gerhard Gradnig <gerhard.gradnig@admiral.at>
2022-11-05 10:54:39 +01:00
Stef Heyenrath
80931e9fb5 Update Azure.Storage NuGets 2022-10-29 14:06:43 +02:00
Stef Heyenrath
cce344ff83 1.5.9 2022-10-29 14:02:30 +02:00
Stef Heyenrath
0972d2cb8f Add option to ProxySettings to append guid to mapping file (#838)
* Add option to ProxySettings to append guid to mapping file

* .

* .

* .
2022-10-29 13:58:29 +02:00
dependabot[bot]
a39b7fc633 Bump Microsoft.AspNetCore.Server.Kestrel.Core (#837)
Bumps [Microsoft.AspNetCore.Server.Kestrel.Core](https://github.com/aspnet/AspNetCore) from 2.1.7 to 2.1.25.
- [Release notes](https://github.com/aspnet/AspNetCore/releases)
- [Changelog](https://github.com/dotnet/aspnetcore/blob/main/docs/ReleasePlanning.md)
- [Commits](https://github.com/aspnet/AspNetCore/compare/v2.1.7...v2.1.25)

---
updated-dependencies:
- dependency-name: Microsoft.AspNetCore.Server.Kestrel.Core
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-10-27 07:46:01 +02:00
Stef Heyenrath
31298d281d Support deleting / resetting a single scenario (#834)
* Support deleting / resetting a single scenario

* move files
2022-10-26 08:43:51 +02:00
Stef Heyenrath
b4c32dd66b WireMock.Net.WebApplication.NET6 2022-10-25 17:19:13 +02:00
Stef Heyenrath
57115f1a3d Add setting to skip saving the string-response in the logging when using WithBody(Func...) (#828)
* Add extra unit-test for Response WithBody (dynamic code)

* DoNotSaveDynamicResponseInLogEntry

* update SettingsModel

* fix

* .
2022-10-21 14:47:26 +02:00
Stef Heyenrath
306c69f478 Fixes for WireMock.Net.FluentAssertions (callcount behaviour) (#832)
* UsingAnyMethod

* fix

* .
2022-10-17 21:50:24 +02:00
Stef Heyenrath
fb8fec0376 Update funding.yml 2022-10-16 09:17:41 +02:00
81 changed files with 1844 additions and 774 deletions

4
.github/FUNDING.yml vendored
View File

@@ -1,6 +1,6 @@
# These are supported funding model platforms
github: # [StefH]
github: [StefH]
patreon: # Replace with a single Patreon username
open_collective: # wiremocknet
ko_fi: # Replace with a single Ko-fi username
@@ -9,4 +9,4 @@ community_bridge: # Replace with a single Community Bridge project-name e.g., cl
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
custom: https://www.paypal.me/stefheyenrath
custom: https://www.paypal.me/stefheyenrath

View File

@@ -1,3 +1,15 @@
# 1.5.10 (06 November 2022)
- [#843](https://github.com/WireMock-Net/WireMock.Net/pull/843) - Webhook Templating: Use the transformed URL to create the HttpRequestMessage contributed by [ggradnig](https://github.com/ggradnig)
- [#845](https://github.com/WireMock-Net/WireMock.Net/pull/845) - Add WireMockNullLogger as valid commandline logger option [feature] contributed by [StefH](https://github.com/StefH)
# 1.5.9 (29 October 2022)
- [#828](https://github.com/WireMock-Net/WireMock.Net/pull/828) - Add setting to skip saving the string-response in the logging when using WithBody(Func...) [feature] contributed by [StefH](https://github.com/StefH)
- [#832](https://github.com/WireMock-Net/WireMock.Net/pull/832) - Fixes for WireMock.Net.FluentAssertions (callcount behaviour) [feature] contributed by [StefH](https://github.com/StefH)
- [#834](https://github.com/WireMock-Net/WireMock.Net/pull/834) - Support deleting / resetting a single scenario [feature] contributed by [StefH](https://github.com/StefH)
- [#837](https://github.com/WireMock-Net/WireMock.Net/pull/837) - Bump Microsoft.AspNetCore.Server.Kestrel.Core from 2.1.7 to 2.1.25 in /examples/WireMock.Net.StandAlone.Net461 [dependencies] contributed by [dependabot[bot]](https://github.com/apps/dependabot)
- [#838](https://github.com/WireMock-Net/WireMock.Net/pull/838) - Add option to ProxySettings to append guid to mapping file contributed by [StefH](https://github.com/StefH)
- [#826](https://github.com/WireMock-Net/WireMock.Net/issues/826) - Dynamic Body not to be cached when a Func is used to created the body [feature]
# 1.5.8 (16 October 2022)
- [#816](https://github.com/WireMock-Net/WireMock.Net/pull/816) - Some fixes to WireMock.Net.Assertions [feature] contributed by [StefH](https://github.com/StefH)
- [#817](https://github.com/WireMock-Net/WireMock.Net/pull/817) - ExactMatcher : IgnoreCase [feature] contributed by [StefH](https://github.com/StefH)

View File

@@ -4,7 +4,7 @@
</PropertyGroup>
<PropertyGroup>
<VersionPrefix>1.5.8</VersionPrefix>
<VersionPrefix>1.5.10</VersionPrefix>
<PackageIcon>WireMock.Net-Logo.png</PackageIcon>
<PackageProjectUrl>https://github.com/WireMock-Net/WireMock.Net</PackageProjectUrl>
<PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>

View File

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

View File

@@ -1,9 +1,5 @@
# 1.5.8 (16 October 2022)
- #816 Some fixes to WireMock.Net.Assertions [feature]
- #817 ExactMatcher : IgnoreCase [feature]
- #824 WebHook - Transform Url [feature]
- #814 WithHeader cannot handle multiple requests with the same header key values [bug]
- #815 Why does UsingMethod check _callscount? [bug]
- #822 Webhook with generic url, body and custom header values [feature]
# 1.5.10 (06 November 2022)
- #843 Webhook Templating: Use the transformed URL to create the HttpRequestMessage
- #845 Add WireMockNullLogger as valid commandline logger option [feature]
The full release notes can be found here: https://github.com/WireMock-Net/WireMock.Net/blob/master/CHANGELOG.md

View File

@@ -25,6 +25,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
.gitignore = .gitignore
CHANGELOG.md = CHANGELOG.md
Directory.Build.props = Directory.Build.props
.github\FUNDING.yml = .github\FUNDING.yml
Generate-ReleaseNotes.cmd = Generate-ReleaseNotes.cmd
nuget.config = nuget.config
PackageReleaseNotes.template = PackageReleaseNotes.template
@@ -105,6 +106,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WireMock.Net.xUnit", "src\W
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WireMock.Net.Console.NET6.WithCertificate", "examples\WireMock.Net.Console.NET6.WithCertificate\WireMock.Net.Console.NET6.WithCertificate.csproj", "{7C2A9DE8-C89F-4841-9058-6B9BF81E5E34}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WireMockAzureQueueExample", "examples\WireMockAzureQueueExample\WireMockAzureQueueExample.csproj", "{BAA9EC2A-874B-45CE-8E51-A73622DC7F3D}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WireMockAzureQueueProxy", "examples\WireMockAzureQueueProxy\WireMockAzureQueueProxy.csproj", "{ADB557D8-D66B-4387-912B-3F73E290B478}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -251,6 +256,14 @@ Global
{7C2A9DE8-C89F-4841-9058-6B9BF81E5E34}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7C2A9DE8-C89F-4841-9058-6B9BF81E5E34}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7C2A9DE8-C89F-4841-9058-6B9BF81E5E34}.Release|Any CPU.Build.0 = Release|Any CPU
{BAA9EC2A-874B-45CE-8E51-A73622DC7F3D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{BAA9EC2A-874B-45CE-8E51-A73622DC7F3D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BAA9EC2A-874B-45CE-8E51-A73622DC7F3D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BAA9EC2A-874B-45CE-8E51-A73622DC7F3D}.Release|Any CPU.Build.0 = Release|Any CPU
{ADB557D8-D66B-4387-912B-3F73E290B478}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{ADB557D8-D66B-4387-912B-3F73E290B478}.Debug|Any CPU.Build.0 = Debug|Any CPU
{ADB557D8-D66B-4387-912B-3F73E290B478}.Release|Any CPU.ActiveCfg = Release|Any CPU
{ADB557D8-D66B-4387-912B-3F73E290B478}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -293,6 +306,8 @@ Global
{670C7562-C154-442E-A249-7D26849BCD13} = {985E0ADB-D4B4-473A-AA40-567E279B7946}
{0DE0954F-8C00-4E8D-B94A-4361FC1CBE44} = {8F890C6F-9ACC-438D-928A-AD61CDA862F2}
{7C2A9DE8-C89F-4841-9058-6B9BF81E5E34} = {985E0ADB-D4B4-473A-AA40-567E279B7946}
{BAA9EC2A-874B-45CE-8E51-A73622DC7F3D} = {985E0ADB-D4B4-473A-AA40-567E279B7946}
{ADB557D8-D66B-4387-912B-3F73E290B478} = {985E0ADB-D4B4-473A-AA40-567E279B7946}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {DC539027-9852-430C-B19F-FD035D018458}

View File

@@ -1,10 +1,18 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=AD/@EntryIndexedValue">AD</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=CONNECT/@EntryIndexedValue">CONNECT</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=CS/@EntryIndexedValue">CS</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=DELETE/@EntryIndexedValue">DELETE</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=EC/@EntryIndexedValue">EC</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=GET/@EntryIndexedValue">GET</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=HEAD/@EntryIndexedValue">HEAD</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=ID/@EntryIndexedValue">ID</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=IP/@EntryIndexedValue">IP</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=MD/@EntryIndexedValue">MD5</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=OPTIONS/@EntryIndexedValue">OPTIONS</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=PATCH/@EntryIndexedValue">PATCH</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=POST/@EntryIndexedValue">POST</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=PUT/@EntryIndexedValue">PUT</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=RSA/@EntryIndexedValue">RSA</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=SSL/@EntryIndexedValue">SSL</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=TE/@EntryIndexedValue">TE</s:String>

View File

@@ -24,7 +24,7 @@
<package id="Microsoft.AspNetCore.Routing.Abstractions" version="2.1.1" targetFramework="net461" />
<package id="Microsoft.AspNetCore.Server.IISIntegration" version="2.1.2" targetFramework="net461" />
<package id="Microsoft.AspNetCore.Server.Kestrel" version="2.1.3" targetFramework="net461" />
<package id="Microsoft.AspNetCore.Server.Kestrel.Core" version="2.1.7" targetFramework="net461" />
<package id="Microsoft.AspNetCore.Server.Kestrel.Core" version="2.1.25" targetFramework="net461" />
<package id="Microsoft.AspNetCore.Server.Kestrel.Https" version="2.1.3" targetFramework="net461" />
<package id="Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions" version="2.1.3" targetFramework="net461" />
<package id="Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets" version="2.1.3" targetFramework="net461" />

View File

@@ -0,0 +1,173 @@
{
"$schema": "https://schema.management.azure.com/schemas/2018-05-01/subscriptionDeploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"metadata": {
"_dependencyType": "compute.function.linux.appService"
},
"parameters": {
"resourceGroupName": {
"type": "string",
"defaultValue": "linux-app-service",
"metadata": {
"description": "Name of the resource group for the resource. It is recommended to put resources under same resource group for better tracking."
}
},
"resourceGroupLocation": {
"type": "string",
"defaultValue": "westeurope",
"metadata": {
"description": "Location of the resource group. Resource groups could have different location than resources, however by default we use API versions from latest hybrid profile which support all locations for resource types we support."
}
},
"resourceName": {
"type": "string",
"defaultValue": "WireMockNetWebApplicationNET6Linux",
"metadata": {
"description": "Name of the main resource to be created by this template."
}
},
"resourceLocation": {
"type": "string",
"defaultValue": "[parameters('resourceGroupLocation')]",
"metadata": {
"description": "Location of the resource. By default use resource group's location, unless the resource provider is not supported there."
}
}
},
"resources": [
{
"type": "Microsoft.Resources/resourceGroups",
"name": "[parameters('resourceGroupName')]",
"location": "[parameters('resourceGroupLocation')]",
"apiVersion": "2019-10-01"
},
{
"type": "Microsoft.Resources/deployments",
"name": "[concat(parameters('resourceGroupName'), 'Deployment', uniqueString(concat(parameters('resourceName'), subscription().subscriptionId)))]",
"resourceGroup": "[parameters('resourceGroupName')]",
"apiVersion": "2019-10-01",
"dependsOn": [
"[parameters('resourceGroupName')]"
],
"properties": {
"mode": "Incremental",
"expressionEvaluationOptions": {
"scope": "inner"
},
"parameters": {
"resourceGroupName": {
"value": "[parameters('resourceGroupName')]"
},
"resourceGroupLocation": {
"value": "[parameters('resourceGroupLocation')]"
},
"resourceName": {
"value": "[parameters('resourceName')]"
},
"resourceLocation": {
"value": "[parameters('resourceLocation')]"
}
},
"template": {
"$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"resourceGroupName": {
"type": "string"
},
"resourceGroupLocation": {
"type": "string"
},
"resourceName": {
"type": "string"
},
"resourceLocation": {
"type": "string"
}
},
"variables": {
"storage_name": "[toLower(concat('storage', uniqueString(concat(parameters('resourceName'), subscription().subscriptionId))))]",
"appServicePlan_name": "[concat('Plan', uniqueString(concat(parameters('resourceName'), subscription().subscriptionId)))]",
"storage_ResourceId": "[concat('/subscriptions/', subscription().subscriptionId, '/resourceGroups/', parameters('resourceGroupName'), '/providers/Microsoft.Storage/storageAccounts/', variables('storage_name'))]",
"appServicePlan_ResourceId": "[concat('/subscriptions/', subscription().subscriptionId, '/resourceGroups/', parameters('resourceGroupName'), '/providers/Microsoft.Web/serverFarms/', variables('appServicePlan_name'))]",
"function_ResourceId": "[concat('/subscriptions/', subscription().subscriptionId, '/resourceGroups/', parameters('resourceGroupName'), '/providers/Microsoft.Web/sites/', parameters('resourceName'))]"
},
"resources": [
{
"location": "[parameters('resourceLocation')]",
"name": "[parameters('resourceName')]",
"type": "Microsoft.Web/sites",
"apiVersion": "2015-08-01",
"tags": {
"[concat('hidden-related:', variables('appServicePlan_ResourceId'))]": "empty"
},
"dependsOn": [
"[variables('appServicePlan_ResourceId')]",
"[variables('storage_ResourceId')]"
],
"kind": "functionapp",
"properties": {
"name": "[parameters('resourceName')]",
"kind": "functionapp",
"httpsOnly": true,
"reserved": false,
"serverFarmId": "[variables('appServicePlan_ResourceId')]",
"siteConfig": {
"alwaysOn": true,
"linuxFxVersion": "dotnet|3.1"
}
},
"identity": {
"type": "SystemAssigned"
},
"resources": [
{
"name": "appsettings",
"type": "config",
"apiVersion": "2015-08-01",
"dependsOn": [
"[variables('function_ResourceId')]"
],
"properties": {
"AzureWebJobsStorage": "[concat('DefaultEndpointsProtocol=https;AccountName=', variables('storage_name'), ';AccountKey=', listKeys(variables('storage_ResourceId'), '2017-10-01').keys[0].value, ';EndpointSuffix=', 'core.windows.net')]",
"FUNCTIONS_EXTENSION_VERSION": "~3",
"FUNCTIONS_WORKER_RUNTIME": "dotnet"
}
}
]
},
{
"location": "[parameters('resourceGroupLocation')]",
"name": "[variables('storage_name')]",
"type": "Microsoft.Storage/storageAccounts",
"apiVersion": "2017-10-01",
"tags": {
"[concat('hidden-related:', concat('/providers/Microsoft.Web/sites/', parameters('resourceName')))]": "empty"
},
"properties": {
"supportsHttpsTrafficOnly": true
},
"sku": {
"name": "Standard_LRS"
},
"kind": "Storage"
},
{
"location": "[parameters('resourceGroupLocation')]",
"name": "[variables('appServicePlan_name')]",
"type": "Microsoft.Web/serverFarms",
"apiVersion": "2015-02-01",
"kind": "linux",
"properties": {
"name": "[variables('appServicePlan_name')]",
"sku": "Standard",
"workerSizeId": "0",
"reserved": true
}
}
]
}
}
}
]
}

View File

@@ -1,33 +1,29 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net6</TargetFramework>
<RuntimeIdentifiers>win10-x64</RuntimeIdentifiers>
<StartupObject>WireMock.Net.WebApplication.Program</StartupObject>
<AssemblyName>WireMock.Net.WebApplication</AssemblyName>
<RootNamespace>WireMock.Net.WebApplication</RootNamespace>
<UserSecretsId>efcf4a18-fd7c-4622-1111-336d65290599</UserSecretsId>
<AspNetCoreHostingModel>OutOfProcess</AspNetCoreHostingModel>
</PropertyGroup>
<PropertyGroup>
<TargetFramework>net6</TargetFramework>
<!--<RuntimeIdentifiers>win10-x64</RuntimeIdentifiers>-->
<StartupObject>WireMock.Net.WebApplication.Program</StartupObject>
<AssemblyName>WireMock.Net.WebApplication</AssemblyName>
<RootNamespace>WireMock.Net.WebApplication</RootNamespace>
<UserSecretsId>efcf4a18-fd7c-4622-1111-336d65290599</UserSecretsId>
<AspNetCoreHostingModel>OutOfProcess</AspNetCoreHostingModel>
</PropertyGroup>
<ItemGroup>
<Content Remove="Properties\1.launchSettings.json" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="6.0.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="6.0.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\WireMock.Net.Abstractions\WireMock.Net.Abstractions.csproj" />
<ProjectReference Include="..\..\src\WireMock.Net\WireMock.Net.csproj" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\WireMock.Net.Abstractions\WireMock.Net.Abstractions.csproj" />
<ProjectReference Include="..\..\src\WireMock.Net\WireMock.Net.csproj" />
</ItemGroup>
<ItemGroup>
<Content Update="appsettings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<Content Update="appsettings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
</Project>

View File

@@ -15,15 +15,7 @@
"WireMockServerSettings": {
"StartAdminInterface": true,
"Urls": [
"https://localhost:8081/"
],
"AllowPartialMapping": false,
"HandleRequestsSynchronously": true,
"ThrowExceptionWhenMatcherFails": true,
"ProxyAndRecordSettings": {
"Url": "http://postman-echo.com/post",
"SaveMapping": true,
"SaveMappingToFile": true
}
"http://localhost"
]
}
}

View File

@@ -0,0 +1,264 @@
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
# Azure Functions localsettings file
local.settings.json
# User-specific files
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
# Visual Studio 2015 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUNIT
*.VisualState.xml
TestResult.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# DNX
project.lock.json
project.fragment.lock.json
artifacts/
*_i.c
*_p.c
*_i.h
*.ilk
*.meta
*.obj
*.pch
*.pdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# JustCode is a .NET coding add-in
.JustCode
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# TODO: Comment the next line if you want to checkin your web deploy settings
# but database connection strings (with potential passwords) will be unencrypted
#*.pubxml
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# The packages folder can be ignored because of Package Restore
**/packages/*
# except build/, which is used as an MSBuild target.
!**/packages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/packages/repositories.config
# NuGet v3's project.json files produces more ignoreable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
node_modules/
orleans.codegen.cs
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
# SQL Server files
*.mdf
*.ldf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
paket-files/
# FAKE - F# Make
.fake/
# JetBrains Rider
.idea/
*.sln.iml
# CodeRush
.cr/
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc

View File

@@ -0,0 +1,13 @@
using Microsoft.Azure.WebJobs;
using Microsoft.Extensions.Logging;
namespace WireMockAzureQueueExample;
public class Function1
{
[FunctionName("Function1")]
public void Run([QueueTrigger("myqueue-items", Connection = "ConnectionStringToWireMock")]string myQueueItem, ILogger log)
{
log.LogWarning($"C# Queue trigger function processed: {myQueueItem}");
}
}

View File

@@ -0,0 +1,9 @@
{
"profiles": {
"WireMockAzureQueueExample": {
"commandName": "Project",
"commandLineArgs": "--port 7290",
"launchBrowser": false
}
}
}

View File

@@ -0,0 +1,14 @@
{
"dependencies": {
"appInsights1": {
"type": "appInsights"
},
"secrets1": {
"type": "secrets"
},
"storage1": {
"type": "storage",
"connectionId": "AzureWebJobsStorage"
}
}
}

View File

@@ -0,0 +1,16 @@
{
"dependencies": {
"appInsights1": {
"type": "appInsights.sdk"
},
"secrets1": {
"type": "secrets.user"
},
"storage1": {
"secretStore": null,
"resourceId": null,
"type": "storage.emulator",
"connectionId": "AzureWebJobsStorage"
}
}
}

View File

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

View File

@@ -0,0 +1,11 @@
{
"version": "2.0",
"logging": {
"applicationInsights": {
"samplingSettings": {
"isEnabled": true,
"excludedTypes": "Request"
}
}
}
}

View File

@@ -0,0 +1,7 @@
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
"FUNCTIONS_WORKER_RUNTIME": "dotnet"
}
}

View File

@@ -0,0 +1,40 @@
using Newtonsoft.Json;
using WireMock.Logging;
using WireMock.Server;
using WireMock.Settings;
namespace WireMockAzureQueueProxy;
static class Program
{
static void Main(params string[] args)
{
var server = WireMockServer.Start(new WireMockServerSettings
{
Logger = new WireMockConsoleLogger(),
Urls = new[] { "http://localhost:20001/" },
StartAdminInterface = false,
ReadStaticMappings = true,
WatchStaticMappings = true,
WatchStaticMappingsInSubdirectories = true,
//ProxyAndRecordSettings = new ProxyAndRecordSettings
//{
// Url = "http://127.0.0.1:10001",
// SaveMapping = true,
// SaveMappingToFile = true,
// AppendGuidToSavedMappingFile = true
//}
});
System.Console.WriteLine("Press any key to stop the server");
System.Console.ReadKey();
server.Stop();
System.Console.WriteLine("Displaying all requests");
var allRequests = server.LogEntries;
System.Console.WriteLine(JsonConvert.SerializeObject(allRequests, Formatting.Indented));
System.Console.WriteLine("Press any key to quit");
System.Console.ReadKey();
}
}

View File

@@ -0,0 +1,23 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\WireMock.Net.Abstractions\WireMock.Net.Abstractions.csproj" />
<ProjectReference Include="..\..\src\WireMock.Net\WireMock.Net.csproj" />
</ItemGroup>
<ItemGroup>
<None Update="__admin\mappings\*.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,44 @@
{
"Guid": "b4a2ff02-fb7f-4496-8c04-9aafc4f5f8f7",
"Title": "Proxy Mapping for DELETE /devstoreaccount1/myqueue-items/messages/41b2aadc-d6ea-4c3c-ae20-2ae72eb08d88",
"Description": "Proxy Mapping for DELETE /devstoreaccount1/myqueue-items/messages/41b2aadc-d6ea-4c3c-ae20-2ae72eb08d88",
"Request": {
"Path": {
"Matchers": [
{
"Name": "WildcardMatcher",
"Pattern": "/devstoreaccount1/myqueue-items/messages/41b2aadc-d6ea-4c3c-ae20-2ae72eb08d88",
"IgnoreCase": false
}
]
},
"Methods": [
"DELETE"
],
"Params": [
{
"Name": "popreceipt",
"Matchers": [
{
"Name": "WildcardMatcher",
"Pattern": "*",
"IgnoreCase": false
}
]
}
]
},
"Response": {
"StatusCode": 204,
"Headers": {
"Server": "Azurite-Queue/3.19.0",
"x-ms-client-request-id": "{{request.headers.x-ms-client-request-id}}",
"x-ms-request-id": "{{Random Type=\"Guid\"}}",
"x-ms-version": "2021-10-04",
"Date": "{{DateTime.Now \"ddd, dd MMM yyy HH:mm:ss GMT\"}}",
"Connection": "keep-alive",
"Keep-Alive": "timeout=5"
},
"UseTransformer": true
}
}

View File

@@ -0,0 +1,60 @@
{
"Scenario": "AzureQueue Get Messages",
"WhenStateIs": "No more messages",
"SetStateTo": "No more messages",
"Guid": "4c871968-29ee-472b-a548-170444d4cc3e",
"Title": "Proxy Mapping for GET NO MSG /devstoreaccount1/myqueue-items/messages",
"Description": "Proxy Mapping for GET NO MSG /devstoreaccount1/myqueue-items/messages",
"Request": {
"Path": {
"Matchers": [
{
"Name": "WildcardMatcher",
"Pattern": "/devstoreaccount1/myqueue-items/messages",
"IgnoreCase": false
}
]
},
"Methods": [
"GET"
],
"Params": [
{
"Name": "numofmessages",
"Matchers": [
{
"Name": "ExactMatcher",
"Pattern": "16",
"IgnoreCase": false
}
]
},
{
"Name": "visibilitytimeout",
"Matchers": [
{
"Name": "ExactMatcher",
"Pattern": "600",
"IgnoreCase": false
}
]
}
]
},
"Response": {
"StatusCode": 200,
"Body": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?><QueueMessagesList/>",
"Headers": {
"Content-Type": "application/xml",
"Server": "Azurite-Queue/3.19.0",
"x-ms-client-request-id": "{{request.headers.x-ms-client-request-id}}",
"x-ms-request-id": "{{Random Type=\"Guid\"}}",
"x-ms-version": "2021-10-04",
"Date": "{{DateTime.Now \"ddd, dd MMM yyy HH:mm:ss GMT\"}}",
"Connection": "keep-alive",
"Keep-Alive": "timeout=5",
"Transfer-Encoding": "chunked"
},
"UseTransformer": true
}
}

View File

@@ -0,0 +1,59 @@
{
"Scenario": "AzureQueue Get Messages",
"SetStateTo": "No more messages",
"Guid": "da9c6799-fbf8-41b6-8933-0df50f821ebb",
"Title": "Proxy Mapping for GET /devstoreaccount1/myqueue-items/messages",
"Description": "Proxy Mapping for GET /devstoreaccount1/myqueue-items/messages",
"Request": {
"Path": {
"Matchers": [
{
"Name": "WildcardMatcher",
"Pattern": "/devstoreaccount1/myqueue-items/messages",
"IgnoreCase": false
}
]
},
"Methods": [
"GET"
],
"Params": [
{
"Name": "numofmessages",
"Matchers": [
{
"Name": "WildcardMatcher",
"Pattern": "*",
"IgnoreCase": true
}
]
},
{
"Name": "visibilitytimeout",
"Matchers": [
{
"Name": "WildcardMatcher",
"Pattern": "*",
"IgnoreCase": false
}
]
}
]
},
"Response": {
"StatusCode": 200,
"Body": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?><QueueMessagesList><QueueMessage><MessageId>41b2aadc-d6ea-4c3c-ae20-2ae72eb08d88</MessageId><InsertionTime>Sat, 29 Oct 2022 07:11:40 GMT</InsertionTime><ExpirationTime>Sat, 31 Dec 2022 07:11:40 GMT</ExpirationTime><PopReceipt>MjlPY3QyMDIyMDc6MTE6NDAyMjU2</PopReceipt><TimeNextVisible>Sat, 29 Oct 2022 07:21:40 GMT</TimeNextVisible><DequeueCount>1</DequeueCount><MessageText>c3RlZg==</MessageText></QueueMessage></QueueMessagesList>",
"Headers": {
"Content-Type": "application/xml",
"Server": "Azurite-Queue/3.19.0",
"x-ms-client-request-id": "{{request.headers.x-ms-client-request-id}}",
"x-ms-request-id": "{{Random Type=\"Guid\"}}",
"x-ms-version": "2021-10-04",
"Date": "{{DateTime.Now \"ddd, dd MMM yyy HH:mm:ss GMT\"}}",
"Connection": "keep-alive",
"Keep-Alive": "timeout=5",
"Transfer-Encoding": "chunked"
},
"UseTransformer": true
}
}

View File

@@ -0,0 +1,46 @@
{
"Guid": "17c7a389-98e1-4383-975d-54c82d1e3860",
"Title": "Proxy Mapping for HEAD /devstoreaccount1/myqueue-items",
"Description": "Proxy Mapping for HEAD /devstoreaccount1/myqueue-items",
"Request": {
"Path": {
"Matchers": [
{
"Name": "WildcardMatcher",
"Pattern": "/devstoreaccount1/myqueue-items",
"IgnoreCase": false
}
]
},
"Methods": [
"HEAD"
],
"Params": [
{
"Name": "comp",
"Matchers": [
{
"Name": "ExactMatcher",
"Pattern": "metadata",
"IgnoreCase": false
}
]
}
]
},
"Response": {
"StatusCode": 200,
"Body": "",
"Headers": {
"Server": "Azurite-Queue/3.19.0",
"x-ms-client-request-id": "{{request.headers.x-ms-client-request-id}}",
"x-ms-approximate-messages-count": "0",
"x-ms-request-id": "{{Random Type=\"Guid\"}}",
"x-ms-version": "2021-10-04",
"Date": "{{DateTime.Now \"ddd, dd MMM yyy HH:mm:ss GMT\"}}",
"Connection": "keep-alive",
"Keep-Alive": "timeout=5"
},
"UseTransformer": true
}
}

View File

@@ -1,67 +1,71 @@
namespace WireMock.Admin.Settings
namespace WireMock.Admin.Settings;
[FluentBuilder.AutoGenerateBuilder]
public class ProxyAndRecordSettingsModel
{
[FluentBuilder.AutoGenerateBuilder]
public class ProxyAndRecordSettingsModel
{
/// <summary>
/// The clientCertificate thumbprint or subject name fragment to use.
/// Example thumbprint : "D2DBF135A8D06ACCD0E1FAD9BFB28678DF7A9818". Example subject name: "www.google.com""
/// </summary>
public string ClientX509Certificate2ThumbprintOrSubjectName { get; set; }
/// <summary>
/// The clientCertificate thumbprint or subject name fragment to use.
/// Example thumbprint : "D2DBF135A8D06ACCD0E1FAD9BFB28678DF7A9818". Example subject name: "www.google.com""
/// </summary>
public string ClientX509Certificate2ThumbprintOrSubjectName { get; set; }
/// <summary>
/// Defines the WebProxySettings.
/// </summary>
public WebProxySettingsModel WebProxySettings { get; set; }
/// <summary>
/// Defines the WebProxySettings.
/// </summary>
public WebProxySettingsModel WebProxySettings { get; set; }
/// <summary>
/// Proxy requests should follow redirection (30x).
/// </summary>
public bool? AllowAutoRedirect { get; set; }
/// <summary>
/// Proxy requests should follow redirection (30x).
/// </summary>
public bool? AllowAutoRedirect { get; set; }
/// <summary>
/// The URL to proxy.
/// </summary>
public string Url { get; set; }
/// <summary>
/// The URL to proxy.
/// </summary>
public string Url { get; set; }
/// <summary>
/// Save the mapping for each request/response to the internal Mappings.
/// </summary>
public bool SaveMapping { get; set; }
/// <summary>
/// Save the mapping for each request/response to the internal Mappings.
/// </summary>
public bool SaveMapping { get; set; }
/// <summary>
/// Save the mapping for each request/response also to a file. (Note that SaveMapping must also be set to true.)
/// </summary>
public bool SaveMappingToFile { get; set; }
/// <summary>
/// Save the mapping for each request/response also to a file. (Note that SaveMapping must also be set to true.)
/// </summary>
public bool SaveMappingToFile { get; set; }
/// <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.)
/// The pattern can contain a single value like "200", but also ranges like "2xx", "100,300,600" or "100-299,6xx" are supported.
/// </summary>
public string SaveMappingForStatusCodePattern { get; set; } = "*";
/// <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.)
/// The pattern can contain a single value like "200", but also ranges like "2xx", "100,300,600" or "100-299,6xx" are supported.
/// </summary>
public string SaveMappingForStatusCodePattern { get; set; } = "*";
/// <summary>
/// Defines a list from headers which will be excluded from the saved mappings.
/// </summary>
public string[] ExcludedHeaders { get; set; }
/// <summary>
/// Defines a list from headers which will be excluded from the saved mappings.
/// </summary>
public string[] ExcludedHeaders { get; set; }
/// <summary>
/// Defines a list of cookies which will be excluded from the saved mappings.
/// </summary>
public string[] ExcludedCookies { get; set; }
/// <summary>
/// Defines a list of cookies which will be excluded from the saved mappings.
/// </summary>
public string[] ExcludedCookies { get; set; }
/// <summary>
/// Prefer the Proxy Mapping over the saved Mapping (in case SaveMapping is set to <c>true</c>).
/// </summary>
// public bool PreferProxyMapping { get; set; }
/// <summary>
/// Prefer the Proxy Mapping over the saved Mapping (in case SaveMapping is set to <c>true</c>).
/// </summary>
// public bool PreferProxyMapping { get; set; }
/// <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.
/// - <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.
///
/// Default value is false.
/// </summary>
public bool UseDefinedRequestMatchers { get; set; }
}
/// <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.
/// - <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.
///
/// Default value is false.
/// </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; }
}

View File

@@ -1,82 +1,92 @@
using System.Text.RegularExpressions;
using WireMock.Handlers;
using WireMock.Types;
namespace WireMock.Admin.Settings
namespace WireMock.Admin.Settings;
/// <summary>
/// Settings
/// </summary>
[FluentBuilder.AutoGenerateBuilder]
public class SettingsModel
{
/// <summary>
/// Settings
/// Gets or sets the global delay in milliseconds.
/// </summary>
[FluentBuilder.AutoGenerateBuilder]
public class SettingsModel
{
/// <summary>
/// Gets or sets the global delay in milliseconds.
/// </summary>
public int? GlobalProcessingDelay { get; set; }
public int? GlobalProcessingDelay { get; set; }
/// <summary>
/// Gets or sets if partial mapping is allowed.
/// </summary>
public bool? AllowPartialMapping { get; set; }
/// <summary>
/// Gets or sets if partial mapping is allowed.
/// </summary>
public bool? AllowPartialMapping { get; set; }
/// <summary>
/// Gets or sets the RequestLog expiration in hours
/// </summary>
public int? RequestLogExpirationDuration { get; set; }
/// <summary>
/// Gets or sets the RequestLog expiration in hours
/// </summary>
public int? RequestLogExpirationDuration { get; set; }
/// <summary>
/// Gets or sets the MaxRequestLog count.
/// </summary>
public int? MaxRequestLogCount { get; set; }
/// <summary>
/// Gets or sets the MaxRequestLog count.
/// </summary>
public int? MaxRequestLogCount { get; set; }
/// <summary>
/// Allow a Body for all HTTP Methods. (default set to false).
/// </summary>
public bool? AllowBodyForAllHttpMethods { get; set; }
/// <summary>
/// Allow a Body for all HTTP Methods. (default set to false).
/// </summary>
public bool? AllowBodyForAllHttpMethods { get; set; }
/// <summary>
/// Handle all requests synchronously. (default set to false).
/// </summary>
public bool? HandleRequestsSynchronously { get; set; }
/// <summary>
/// Handle all requests synchronously. (default set to false).
/// </summary>
public bool? HandleRequestsSynchronously { get; set; }
/// <summary>
/// Throw an exception when the Matcher fails because of invalid input. (default set to false).
/// </summary>
public bool? ThrowExceptionWhenMatcherFails { get; set; }
/// <summary>
/// Throw an exception when the Matcher fails because of invalid input. (default set to false).
/// </summary>
public bool? ThrowExceptionWhenMatcherFails { get; set; }
/// <summary>
/// Use the RegexExtended instead of the default <see cref="Regex"/>. (default set to true).
/// </summary>
public bool? UseRegexExtended { get; set; }
/// <summary>
/// Use the RegexExtended instead of the default <see cref="Regex"/>. (default set to true).
/// </summary>
public bool? UseRegexExtended { get; set; }
/// <summary>
/// Save unmatched requests to a file using the <see cref="IFileSystemHandler"/>. (default set to false).
/// </summary>
public bool? SaveUnmatchedRequests { get; set; }
/// <summary>
/// Save unmatched requests to a file using the <see cref="IFileSystemHandler"/>. (default set to false).
/// </summary>
public bool? SaveUnmatchedRequests { get; set; }
/// <summary>
/// Gets or sets if the static mappings should be read at startup.
/// </summary>
public bool? ReadStaticMappings { get; set; }
/// <summary>
/// Gets or sets if the static mappings should be read at startup.
/// </summary>
public bool? ReadStaticMappings { get; set; }
/// <summary>
/// Watch the static mapping files + folder for changes when running.
/// </summary>
public bool? WatchStaticMappings { get; set; }
/// <summary>
/// Watch the static mapping files + folder for changes when running.
/// </summary>
public bool? WatchStaticMappings { get; set; }
/// <summary>
/// A value indicating whether subdirectories within the static mappings path should be monitored.
/// </summary>
public bool? WatchStaticMappingsInSubdirectories { get; set; }
/// <summary>
/// A value indicating whether subdirectories within the static mappings path should be monitored.
/// </summary>
public bool? WatchStaticMappingsInSubdirectories { get; set; }
/// <summary>
/// Policies to use when using CORS. By default CORS is disabled. [Optional]
/// </summary>
public string? CorsPolicyOptions { get; set; }
/// <summary>
/// Policies to use when using CORS. By default CORS is disabled. [Optional]
/// </summary>
public string? CorsPolicyOptions { get; set; }
/// <summary>
/// The proxy and record settings.
/// </summary>
public ProxyAndRecordSettingsModel? ProxyAndRecordSettings { get; set; }
}
/// <summary>
/// The proxy and record settings.
/// </summary>
public ProxyAndRecordSettingsModel? ProxyAndRecordSettings { get; set; }
/// <summary>
/// Defines on which scheme (http/https) to host. (This overrides the <c>UseSSL</c> value).
/// </summary>
public HostingScheme? HostingScheme { get; set; }
/// <summary>
/// Don't save the response-string in the LogEntry when WithBody(Func{IRequestMessage, string}) or WithBody(Func{IRequestMessage, Task{string}}) is used. (default set to false).
/// </summary>
public bool? DoNotSaveDynamicResponseInLogEntry { get; set; }
}

View File

@@ -1,9 +1,9 @@
namespace WireMock.Http;
namespace WireMock.Constants;
/// <summary>
/// https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods
/// </summary>
internal static class HttpRequestMethods
public static class HttpRequestMethod
{
public const string CONNECT = "CONNECT";
public const string DELETE = "DELETE";

View File

@@ -1,6 +1,4 @@
using System.Diagnostics.CodeAnalysis;
using System.Text;
using JetBrains.Annotations;
using WireMock.Types;
namespace WireMock.Util;
@@ -59,4 +57,9 @@ public interface IBodyData
/// The body encoding.
/// </summary>
Encoding? Encoding { get; set; }
/// <summary>
/// Defines if this BodyData is the result of a dynamically created response-string. (
/// </summary>
public string? IsFuncUsed { get; set; }
}

View File

@@ -1,10 +1,8 @@
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using JetBrains.Annotations;
using WireMock.Admin.Mappings;
using WireMock.Logging;
using WireMock.Matchers.Request;
namespace WireMock.Server
{
@@ -144,6 +142,11 @@ namespace WireMock.Server
/// </summary>
void ResetScenarios();
/// <summary>
/// Resets a specific Scenario by the name.
/// </summary>
bool ResetScenario(string name);
/// <summary>
/// Resets the LogEntries.
/// </summary>

View File

@@ -4,6 +4,7 @@ using System.Collections.Generic;
using System.Linq;
using FluentAssertions;
using FluentAssertions.Execution;
using WireMock.Constants;
using WireMock.Server;
using WireMock.Types;
@@ -12,9 +13,10 @@ namespace WireMock.FluentAssertions;
public class WireMockAssertions
{
private const string Any = "*";
private readonly int? _callsCount;
private IReadOnlyList<IRequestMessage> _requestMessages;
private IReadOnlyList<KeyValuePair<string, WireMockList<string>>> _headers;
private readonly IReadOnlyList<KeyValuePair<string, WireMockList<string>>> _headers;
public WireMockAssertions(IWireMockServer subject, int? callsCount)
{
@@ -27,12 +29,13 @@ public class WireMockAssertions
public AndWhichConstraint<WireMockAssertions, string> AtAbsoluteUrl(string absoluteUrl, string because = "", params object[] becauseArgs)
{
Func<IRequestMessage, bool> predicate = request => string.Equals(request.AbsoluteUrl, absoluteUrl, StringComparison.OrdinalIgnoreCase);
var (filter, condition) = BuildFilterAndCondition(predicate);
Execute.Assertion
.BecauseOf(because, becauseArgs)
.Given(() => _requestMessages)
.ForCondition(requests => requests.Any())
.ForCondition(requests => _callsCount == 0 || requests.Any())
.FailWith(
"Expected {context:wiremockserver} to have been called at address matching the absolute url {0}{reason}, but no calls were made.",
absoluteUrl
@@ -53,15 +56,17 @@ public class WireMockAssertions
public AndWhichConstraint<WireMockAssertions, string> AtUrl(string url, string because = "", params object[] becauseArgs)
{
Func<IRequestMessage, bool> predicate = request => string.Equals(request.Url, url, StringComparison.OrdinalIgnoreCase);
var (filter, condition) = BuildFilterAndCondition(predicate);
Execute.Assertion
.BecauseOf(because, becauseArgs)
.Given(() => _requestMessages)
.ForCondition(requests => requests.Any())
.ForCondition(requests => _callsCount == 0 || requests.Any())
.FailWith(
"Expected {context:wiremockserver} to have been called at address matching the url {0}{reason}, but no calls were made.",
url)
url
)
.Then
.ForCondition(condition)
.FailWith(
@@ -79,12 +84,13 @@ public class WireMockAssertions
public AndWhichConstraint<WireMockAssertions, string> WithProxyUrl(string proxyUrl, string because = "", params object[] becauseArgs)
{
Func<IRequestMessage, bool> predicate = request => string.Equals(request.ProxyUrl, proxyUrl, StringComparison.OrdinalIgnoreCase);
var (filter, condition) = BuildFilterAndCondition(predicate);
Execute.Assertion
.BecauseOf(because, becauseArgs)
.Given(() => _requestMessages)
.ForCondition(requests => requests.Any())
.ForCondition(requests => _callsCount == 0 || requests.Any())
.FailWith(
"Expected {context:wiremockserver} to have been called with proxy url {0}{reason}, but no calls were made.",
proxyUrl
@@ -94,8 +100,7 @@ public class WireMockAssertions
.FailWith(
"Expected {context:wiremockserver} to have been called with proxy url {0}{reason}, but didn't find it among the calls with {1}.",
_ => proxyUrl,
requests => requests
.Select(request => request.ProxyUrl)
requests => requests.Select(request => request.ProxyUrl)
);
_requestMessages = filter(_requestMessages).ToList();
@@ -107,23 +112,23 @@ public class WireMockAssertions
public AndWhichConstraint<WireMockAssertions, string> FromClientIP(string clientIP, string because = "", params object[] becauseArgs)
{
Func<IRequestMessage, bool> predicate = request => string.Equals(request.ClientIP, clientIP, StringComparison.OrdinalIgnoreCase);
var (filter, condition) = BuildFilterAndCondition(predicate);
Execute.Assertion
.BecauseOf(because, becauseArgs)
.Given(() => _requestMessages)
.ForCondition(requests => requests.Any())
.ForCondition(requests => _callsCount == 0 || requests.Any())
.FailWith(
"Expected {context:wiremockserver} to have been called from client IP {0}{reason}, but no calls were made.",
clientIP)
.Then
.ForCondition(requests =>
(_callsCount == null && requests.Any(req => req.ClientIP == clientIP)) ||
(_callsCount == requests.Count(req => req.ClientIP == clientIP))
clientIP
)
.Then
.ForCondition(condition)
.FailWith(
"Expected {context:wiremockserver} to have been called from client IP {0}{reason}, but didn't find it among the calls from IP(s) {1}.",
_ => clientIP, requests => requests.Select(request => request.ClientIP));
_ => clientIP, requests => requests.Select(request => request.ClientIP)
);
_requestMessages = filter(_requestMessages).ToList();
@@ -165,50 +170,57 @@ public class WireMockAssertions
[CustomAssertion]
public AndConstraint<WireMockAssertions> UsingConnect(string because = "", params object[] becauseArgs)
=> UsingMethod("CONNECT", because, becauseArgs);
=> UsingMethod(HttpRequestMethod.CONNECT, because, becauseArgs);
[CustomAssertion]
public AndConstraint<WireMockAssertions> UsingDelete(string because = "", params object[] becauseArgs)
=> UsingMethod("DELETE", because, becauseArgs);
=> UsingMethod(HttpRequestMethod.DELETE, because, becauseArgs);
[CustomAssertion]
public AndConstraint<WireMockAssertions> UsingGet(string because = "", params object[] becauseArgs)
=> UsingMethod("GET", because, becauseArgs);
=> UsingMethod(HttpRequestMethod.GET, because, becauseArgs);
[CustomAssertion]
public AndConstraint<WireMockAssertions> UsingHead(string because = "", params object[] becauseArgs)
=> UsingMethod("HEAD", because, becauseArgs);
=> UsingMethod(HttpRequestMethod.HEAD, because, becauseArgs);
[CustomAssertion]
public AndConstraint<WireMockAssertions> UsingOptions(string because = "", params object[] becauseArgs)
=> UsingMethod("OPTIONS", because, becauseArgs);
=> UsingMethod(HttpRequestMethod.OPTIONS, because, becauseArgs);
[CustomAssertion]
public AndConstraint<WireMockAssertions> UsingPost(string because = "", params object[] becauseArgs)
=> UsingMethod("POST", because, becauseArgs);
=> UsingMethod(HttpRequestMethod.POST, because, becauseArgs);
[CustomAssertion]
public AndConstraint<WireMockAssertions> UsingPatch(string because = "", params object[] becauseArgs)
=> UsingMethod("PATCH", because, becauseArgs);
=> UsingMethod(HttpRequestMethod.PATCH, because, becauseArgs);
[CustomAssertion]
public AndConstraint<WireMockAssertions> UsingPut(string because = "", params object[] becauseArgs)
=> UsingMethod("PUT", because, becauseArgs);
=> UsingMethod(HttpRequestMethod.PUT, because, becauseArgs);
[CustomAssertion]
public AndConstraint<WireMockAssertions> UsingTrace(string because = "", params object[] becauseArgs)
=> UsingMethod("TRACE", because, becauseArgs);
=> UsingMethod(HttpRequestMethod.TRACE, because, becauseArgs);
[CustomAssertion]
public AndConstraint<WireMockAssertions> UsingAnyMethod(string because = "", params object[] becauseArgs)
=> UsingMethod(Any, because, becauseArgs);
[CustomAssertion]
public AndConstraint<WireMockAssertions> UsingMethod(string method, string because = "", params object[] becauseArgs)
{
Func<IRequestMessage, bool> predicate = request => string.Equals(request.Method, method, StringComparison.OrdinalIgnoreCase);
var any = method == Any;
Func<IRequestMessage, bool> predicate = request => (any && !string.IsNullOrEmpty(request.Method)) ||
string.Equals(request.Method, method, StringComparison.OrdinalIgnoreCase);
var (filter, condition) = BuildFilterAndCondition(predicate);
Execute.Assertion
.BecauseOf(because, becauseArgs)
.Given(() => _requestMessages)
.ForCondition(requests => requests.Any())
.ForCondition(requests => _callsCount == 0 || requests.Any())
.FailWith(
"Expected {context:wiremockserver} to have been called using method {0}{reason}, but no calls were made.",
method
@@ -230,12 +242,6 @@ public class WireMockAssertions
{
Func<IReadOnlyList<IRequestMessage>, IReadOnlyList<IRequestMessage>> filter = requests => requests.Where(predicate).ToList();
return
(
filter,
requests =>
(_callsCount == null && filter(_requestMessages).Any()) ||
(_callsCount == filter(_requestMessages).Count())
);
return (filter, requests => (_callsCount is null && filter(requests).Any()) || _callsCount == filter(requests).Count);
}
}

View File

@@ -1,4 +1,4 @@
using RestEase;
using RestEase;
using System;
using System.Collections.Generic;
using System.Net.Http.Headers;
@@ -179,6 +179,20 @@ namespace WireMock.Client
[Post("scenarios")]
Task<StatusModel> ResetScenariosAsync();
/// <summary>
/// Delete (reset) a specific scenario
/// </summary>
[Delete("scenarios/{name}")]
[AllowAnyStatusCode]
Task<StatusModel> DeleteScenarioAsync([Path] string name);
/// <summary>
/// Delete (reset) all scenarios
/// </summary>
[Post("scenarios/{name}/reset")]
[AllowAnyStatusCode]
Task<StatusModel> ResetScenarioAsync([Path] string name);
/// <summary>
/// Create a new File
/// </summary>

View File

@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using WireMock.Util;
@@ -35,15 +36,18 @@ internal static class HttpResponseMessageHelper
contentEncodingHeader = headers.First(header => string.Equals(header.Key, HttpKnownHeaderNames.ContentEncoding, StringComparison.OrdinalIgnoreCase)).Value;
}
var bodyParserSettings = new BodyParserSettings
if (httpResponseMessage.StatusCode != HttpStatusCode.NoContent) // A body is not allowed for 204.
{
Stream = stream,
ContentType = contentTypeHeader?.FirstOrDefault(),
DeserializeJson = deserializeJson,
ContentEncoding = contentEncodingHeader?.FirstOrDefault(),
DecompressGZipAndDeflate = decompressGzipAndDeflate
};
responseMessage.BodyData = await BodyParser.ParseAsync(bodyParserSettings).ConfigureAwait(false);
var bodyParserSettings = new BodyParserSettings
{
Stream = stream,
ContentType = contentTypeHeader?.FirstOrDefault(),
DeserializeJson = deserializeJson,
ContentEncoding = contentEncodingHeader?.FirstOrDefault(),
DecompressGZipAndDeflate = decompressGzipAndDeflate
};
responseMessage.BodyData = await BodyParser.ParseAsync(bodyParserSettings).ConfigureAwait(false);
}
}
foreach (var header in headers)

View File

@@ -89,7 +89,7 @@ internal class WebhookSender
};
// Create HttpRequestMessage
var httpRequestMessage = HttpRequestMessageHelper.Create(requestMessage, webhookRequest.Url);
var httpRequestMessage = HttpRequestMessageHelper.Create(requestMessage, webhookRequestUrl);
// Delay (if required)
if (TryGetDelay(webhookRequest, out var delay))

View File

@@ -12,13 +12,13 @@ public class LogEntry : ILogEntry
public Guid Guid { get; set; }
/// <inheritdoc cref="ILogEntry.RequestMessage" />
public IRequestMessage RequestMessage { get; set; }
public IRequestMessage RequestMessage { get; set; } = null!;
/// <inheritdoc cref="ILogEntry.ResponseMessage" />
public IResponseMessage ResponseMessage { get; set; }
public IResponseMessage ResponseMessage { get; set; } = null!;
/// <inheritdoc cref="ILogEntry.RequestMatchResult" />
public IRequestMatchResult RequestMatchResult { get; set; }
public IRequestMatchResult RequestMatchResult { get; set; } = null!;
/// <inheritdoc cref="ILogEntry.MappingGuid" />
public Guid? MappingGuid { get; set; }
@@ -33,5 +33,5 @@ public class LogEntry : ILogEntry
public string? PartialMappingTitle { get; set; }
/// <inheritdoc cref="ILogEntry.PartialMatchResult" />
public IRequestMatchResult PartialMatchResult { get; set; }
public IRequestMatchResult PartialMatchResult { get; set; } = null!;
}

View File

@@ -2,73 +2,72 @@ using Newtonsoft.Json;
using System;
using WireMock.Admin.Requests;
namespace WireMock.Logging
namespace WireMock.Logging;
/// <summary>
/// WireMockConsoleLogger which logs to Console
/// </summary>
/// <seealso cref="IWireMockLogger" />
public class WireMockConsoleLogger : IWireMockLogger
{
/// <summary>
/// WireMockConsoleLogger which logs to Console
/// Initializes a new instance of the <see cref="WireMockConsoleLogger"/> class.
/// </summary>
/// <seealso cref="IWireMockLogger" />
public class WireMockConsoleLogger : IWireMockLogger
public WireMockConsoleLogger()
{
/// <summary>
/// Initializes a new instance of the <see cref="WireMockConsoleLogger"/> class.
/// </summary>
public WireMockConsoleLogger()
{
Console.OutputEncoding = System.Text.Encoding.UTF8;
}
Console.OutputEncoding = System.Text.Encoding.UTF8;
}
/// <see cref="IWireMockLogger.Debug"/>
public void Debug(string formatString, params object[] args)
{
Console.WriteLine(Format("Debug", formatString, args));
}
/// <see cref="IWireMockLogger.Debug"/>
public void Debug(string formatString, params object[] args)
{
Console.WriteLine(Format("Debug", formatString, args));
}
/// <see cref="IWireMockLogger.Info"/>
public void Info(string formatString, params object[] args)
{
Console.WriteLine(Format("Info", formatString, args));
}
/// <see cref="IWireMockLogger.Info"/>
public void Info(string formatString, params object[] args)
{
Console.WriteLine(Format("Info", formatString, args));
}
/// <see cref="IWireMockLogger.Warn"/>
public void Warn(string formatString, params object[] args)
{
Console.WriteLine(Format("Warn", formatString, args));
}
/// <see cref="IWireMockLogger.Warn"/>
public void Warn(string formatString, params object[] args)
{
Console.WriteLine(Format("Warn", formatString, args));
}
/// <see cref="IWireMockLogger.Error(string, object[])"/>
public void Error(string formatString, params object[] args)
{
Console.WriteLine(Format("Error", formatString, args));
}
/// <see cref="IWireMockLogger.Error(string, object[])"/>
public void Error(string formatString, params object[] args)
{
Console.WriteLine(Format("Error", formatString, args));
}
/// <see cref="IWireMockLogger.Error(string, Exception)"/>
public void Error(string formatString, Exception exception)
{
Console.WriteLine(Format("Error", formatString, exception.Message));
/// <see cref="IWireMockLogger.Error(string, Exception)"/>
public void Error(string formatString, Exception exception)
{
Console.WriteLine(Format("Error", formatString, exception.Message));
if (exception is AggregateException ae)
if (exception is AggregateException ae)
{
ae.Handle(ex =>
{
ae.Handle(ex =>
{
Console.WriteLine(Format("Error", "Exception {0}", ex.Message));
return true;
});
}
}
/// <see cref="IWireMockLogger.DebugRequestResponse"/>
public void DebugRequestResponse(LogEntryModel logEntryModel, bool isAdminRequest)
{
string message = JsonConvert.SerializeObject(logEntryModel, Formatting.Indented);
Console.WriteLine(Format("DebugRequestResponse", "Admin[{0}] {1}", isAdminRequest, message));
}
private static string Format(string level, string formatString, params object[] args)
{
var message = args.Length > 0 ? string.Format(formatString, args) : formatString;
return $"{DateTime.UtcNow} [{level}] : {message}";
Console.WriteLine(Format("Error", "Exception {0}", ex.Message));
return true;
});
}
}
/// <see cref="IWireMockLogger.DebugRequestResponse"/>
public void DebugRequestResponse(LogEntryModel logEntryModel, bool isAdminRequest)
{
string message = JsonConvert.SerializeObject(logEntryModel, Formatting.Indented);
Console.WriteLine(Format("DebugRequestResponse", "Admin[{0}] {1}", isAdminRequest, message));
}
private static string Format(string level, string formatString, params object[] args)
{
var message = args.Length > 0 ? string.Format(formatString, args) : formatString;
return $"{DateTime.UtcNow} [{level}] : {message}";
}
}

View File

@@ -1,48 +1,47 @@
using System;
using System;
using WireMock.Admin.Requests;
namespace WireMock.Logging
namespace WireMock.Logging;
/// <summary>
/// WireMockNullLogger which does not log.
/// </summary>
/// <seealso cref="IWireMockLogger" />
public class WireMockNullLogger : IWireMockLogger
{
/// <summary>
/// WireMockNullLogger which does not log.
/// </summary>
/// <seealso cref="IWireMockLogger" />
public class WireMockNullLogger : IWireMockLogger
/// <see cref="IWireMockLogger.Debug"/>
public void Debug(string formatString, params object[] args)
{
/// <see cref="IWireMockLogger.Debug"/>
public void Debug(string formatString, params object[] args)
{
// Log nothing
}
// Log nothing
}
/// <see cref="IWireMockLogger.Info"/>
public void Info(string formatString, params object[] args)
{
// Log nothing
}
/// <see cref="IWireMockLogger.Info"/>
public void Info(string formatString, params object[] args)
{
// Log nothing
}
/// <see cref="IWireMockLogger.Warn"/>
public void Warn(string formatString, params object[] args)
{
// Log nothing
}
/// <see cref="IWireMockLogger.Warn"/>
public void Warn(string formatString, params object[] args)
{
// Log nothing
}
/// <see cref="IWireMockLogger.Error(string, object[])"/>
public void Error(string formatString, params object[] args)
{
// Log nothing
}
/// <see cref="IWireMockLogger.Error(string, object[])"/>
public void Error(string formatString, params object[] args)
{
// Log nothing
}
/// <see cref="IWireMockLogger.Error(string, Exception)"/>
public void Error(string formatString, Exception exception)
{
// Log nothing
}
/// <see cref="IWireMockLogger.Error(string, Exception)"/>
public void Error(string formatString, Exception exception)
{
// Log nothing
}
/// <see cref="IWireMockLogger.DebugRequestResponse"/>
public void DebugRequestResponse(LogEntryModel logEntryModel, bool isAdminRequest)
{
// Log nothing
}
/// <see cref="IWireMockLogger.DebugRequestResponse"/>
public void DebugRequestResponse(LogEntryModel logEntryModel, bool isAdminRequest)
{
// Log nothing
}
}

View File

@@ -21,6 +21,7 @@ public class LinqMatcher : IObjectMatcher, IStringMatcher
/// <inheritdoc cref="IMatcher.MatchBehaviour"/>
public MatchBehaviour MatchBehaviour { get; }
/// <inheritdoc cref="IMatcher.ThrowException"/>
public bool ThrowException { get; }

View File

@@ -1,41 +1,43 @@
using System.Text;
using WireMock.Types;
namespace WireMock.Util
namespace WireMock.Util;
/// <summary>
/// BodyData
/// </summary>
public class BodyData : IBodyData
{
/// <summary>
/// BodyData
/// </summary>
public class BodyData : IBodyData
{
/// <inheritdoc cref="IBodyData.Encoding" />
public Encoding? Encoding { get; set; }
/// <inheritdoc cref="IBodyData.Encoding" />
public Encoding? Encoding { get; set; }
/// <inheritdoc />
public string? BodyAsString { get; set; }
/// <inheritdoc />
public string? BodyAsString { get; set; }
/// <inheritdoc cref="IBodyData.BodyAsJson" />
public object? BodyAsJson { get; set; }
/// <inheritdoc cref="IBodyData.BodyAsJson" />
public object? BodyAsJson { get; set; }
/// <inheritdoc cref="IBodyData.BodyAsBytes" />
public byte[]? BodyAsBytes { get; set; }
/// <inheritdoc cref="IBodyData.BodyAsBytes" />
public byte[]? BodyAsBytes { get; set; }
/// <inheritdoc cref="IBodyData.BodyAsJsonIndented" />
public bool? BodyAsJsonIndented { get; set; }
/// <inheritdoc cref="IBodyData.BodyAsJsonIndented" />
public bool? BodyAsJsonIndented { get; set; }
/// <inheritdoc cref="IBodyData.BodyAsFile" />
public string? BodyAsFile { get; set; }
/// <inheritdoc cref="IBodyData.BodyAsFile" />
public string? BodyAsFile { get; set; }
/// <inheritdoc cref="IBodyData.BodyAsFileIsCached" />
public bool? BodyAsFileIsCached { get; set; }
/// <inheritdoc cref="IBodyData.BodyAsFileIsCached" />
public bool? BodyAsFileIsCached { get; set; }
/// <inheritdoc cref="IBodyData.DetectedBodyType" />
public BodyType? DetectedBodyType { get; set; }
/// <inheritdoc cref="IBodyData.DetectedBodyType" />
public BodyType? DetectedBodyType { get; set; }
/// <inheritdoc cref="IBodyData.DetectedBodyTypeFromContentType" />
public BodyType? DetectedBodyTypeFromContentType { get; set; }
/// <inheritdoc cref="IBodyData.DetectedBodyTypeFromContentType" />
public BodyType? DetectedBodyTypeFromContentType { get; set; }
/// <inheritdoc cref="IRequestMessage.DetectedCompression" />
public string? DetectedCompression { get; set; }
}
/// <inheritdoc cref="IRequestMessage.DetectedCompression" />
public string? DetectedCompression { get; set; }
/// <inheritdoc />
public string? IsFuncUsed { get; set; }
}

View File

@@ -69,4 +69,6 @@ internal interface IWireMockMiddlewareOptions
bool CustomCertificateDefined { get; }
bool? SaveUnmatchedRequests { get; set; }
public bool? DoNotSaveDynamicResponseInLogEntry { get; set; }
}

View File

@@ -99,7 +99,14 @@ namespace WireMock.Owin.Mappers
if (bytes != null)
{
await response.Body.WriteAsync(bytes, 0, bytes.Length).ConfigureAwait(false);
try
{
await response.Body.WriteAsync(bytes, 0, bytes.Length).ConfigureAwait(false);
}
catch (Exception ex)
{
_options.Logger.Warn("Error writing response body. Exception : {0}", ex);
}
}
}

View File

@@ -32,22 +32,25 @@ namespace WireMock.Owin
private readonly IOwinRequestMapper _requestMapper;
private readonly IOwinResponseMapper _responseMapper;
private readonly IMappingMatcher _mappingMatcher;
private readonly LogEntryMapper _logEntryMapper;
#if !USE_ASPNETCORE
public WireMockMiddleware(Next next, IWireMockMiddlewareOptions options, IOwinRequestMapper requestMapper, IOwinResponseMapper responseMapper, IMappingMatcher mappingMatcher) : base(next)
{
_options = Guard.NotNull(options, nameof(options));
_requestMapper = Guard.NotNull(requestMapper, nameof(requestMapper));
_responseMapper = Guard.NotNull(responseMapper, nameof(responseMapper));
_mappingMatcher = Guard.NotNull(mappingMatcher, nameof(mappingMatcher));
_options = Guard.NotNull(options);
_requestMapper = Guard.NotNull(requestMapper);
_responseMapper = Guard.NotNull(responseMapper);
_mappingMatcher = Guard.NotNull(mappingMatcher);
_logEntryMapper = new LogEntryMapper(options);
}
#else
public WireMockMiddleware(Next next, IWireMockMiddlewareOptions options, IOwinRequestMapper requestMapper, IOwinResponseMapper responseMapper, IMappingMatcher mappingMatcher)
{
_options = Guard.NotNull(options, nameof(options));
_requestMapper = Guard.NotNull(requestMapper, nameof(requestMapper));
_responseMapper = Guard.NotNull(responseMapper, nameof(responseMapper));
_mappingMatcher = Guard.NotNull(mappingMatcher, nameof(mappingMatcher));
_options = Guard.NotNull(options);
_requestMapper = Guard.NotNull(requestMapper);
_responseMapper = Guard.NotNull(responseMapper);
_mappingMatcher = Guard.NotNull(mappingMatcher);
_logEntryMapper = new LogEntryMapper(options);
}
#endif
@@ -264,7 +267,7 @@ namespace WireMock.Owin
private void LogRequest(LogEntry entry, bool addRequest)
{
_options.Logger.DebugRequestResponse(LogEntryMapper.Map(entry), entry.RequestMessage.Path.StartsWith("/__admin/"));
_options.Logger.DebugRequestResponse(_logEntryMapper.Map(entry), entry.RequestMessage.Path.StartsWith("/__admin/"));
if (addRequest)
{

View File

@@ -26,7 +26,7 @@ internal class WireMockMiddlewareOptions : IWireMockMiddlewareOptions
public ConcurrentDictionary<Guid, IMapping> Mappings { get; } = new ConcurrentDictionary<Guid, IMapping>();
public ConcurrentDictionary<string, ScenarioState> Scenarios { get; } = new();
public ConcurrentDictionary<string, ScenarioState> Scenarios { get; } = new(StringComparer.OrdinalIgnoreCase);
public ConcurrentObservableCollection<LogEntry> LogEntries { get; } = new();
@@ -84,4 +84,7 @@ internal class WireMockMiddlewareOptions : IWireMockMiddlewareOptions
/// <inheritdoc cref="IWireMockMiddlewareOptions.SaveUnmatchedRequests"/>
public bool? SaveUnmatchedRequests { get; set; }
/// <inheritdoc />
public bool? DoNotSaveDynamicResponseInLogEntry { get; set; }
}

View File

@@ -1,3 +1,4 @@
#pragma warning disable CS1591
namespace WireMock.Pact.Models.V2;
public class Interaction

View File

@@ -1,25 +1,25 @@
namespace WireMock.Pact.Models.V2
#pragma warning disable CS1591
namespace WireMock.Pact.Models.V2;
public class MatchingRule
{
public class MatchingRule
{
/// <summary>
/// type or regex
/// </summary>
public string Match { get; set; } = "type";
/// <summary>
/// type or regex
/// </summary>
public string Match { get; set; } = "type";
/// <summary>
/// Used for Match = "type"
/// </summary>
public string Min { get; set; }
/// <summary>
/// Used for Match = "type"
/// </summary>
public string Min { get; set; }
/// <summary>
/// Used for Match = "type"
/// </summary>
public string Max { get; set; }
/// <summary>
/// Used for Match = "type"
/// </summary>
public string Max { get; set; }
/// <summary>
/// Used for Match = "regex"
/// </summary>
public string Regex { get; set; }
}
/// <summary>
/// Used for Match = "regex"
/// </summary>
public string Regex { get; set; }
}

View File

@@ -1,9 +1,9 @@
namespace WireMock.Pact.Models.V2
{
public class Metadata
{
public string PactSpecificationVersion { get; set; }
#pragma warning disable CS1591
namespace WireMock.Pact.Models.V2;
public PactSpecification PactSpecification { get; set; } = new PactSpecification();
}
public class Metadata
{
public string PactSpecificationVersion { get; set; }
public PactSpecification PactSpecification { get; set; } = new PactSpecification();
}

View File

@@ -1,15 +1,15 @@
#pragma warning disable CS1591
using System.Collections.Generic;
namespace WireMock.Pact.Models.V2
namespace WireMock.Pact.Models.V2;
public class Pact
{
public class Pact
{
public Pacticipant Consumer { get; set; }
public Pacticipant Consumer { get; set; }
public List<Interaction> Interactions { get; set; } = new List<Interaction>();
public List<Interaction> Interactions { get; set; } = new List<Interaction>();
public Metadata Metadata { get; set; }
public Metadata Metadata { get; set; }
public Pacticipant Provider { get; set; }
}
public Pacticipant Provider { get; set; }
}

View File

@@ -1,4 +1,6 @@
#pragma warning disable CS1591
using System.Collections.Generic;
using WireMock.Constants;
namespace WireMock.Pact.Models.V2;
@@ -6,7 +8,7 @@ public class PactRequest
{
public IDictionary<string, string>? Headers { get; set; }
public string Method { get; set; } = "GET";
public string Method { get; set; } = HttpRequestMethod.GET;
public string? Path { get; set; } = "/";

View File

@@ -1,3 +1,4 @@
#pragma warning disable CS1591
using System.Collections.Generic;
namespace WireMock.Pact.Models.V2;

View File

@@ -1,11 +1,11 @@
namespace WireMock.Pact.Models.V2
#pragma warning disable CS1591
namespace WireMock.Pact.Models.V2;
public class PactRust
{
public class PactRust
{
public string Ffi { get; set; }
public string Ffi { get; set; }
public string Mockserver { get; set; }
public string Mockserver { get; set; }
public string Models { get; set; }
}
public string Models { get; set; }
}

View File

@@ -1,7 +1,7 @@
namespace WireMock.Pact.Models.V2
#pragma warning disable CS1591
namespace WireMock.Pact.Models.V2;
public class PactSpecification
{
public class PactSpecification
{
public string Version { get; set; } = "2.0";
}
public string Version { get; set; } = "2.0";
}

View File

@@ -1,7 +1,7 @@
namespace WireMock.Pact.Models.V2
#pragma warning disable CS1591
namespace WireMock.Pact.Models.V2;
public class Pacticipant
{
public class Pacticipant
{
public string Name { get; set; }
}
public string Name { get; set; }
}

View File

@@ -1,11 +1,11 @@
#pragma warning disable CS1591
using System.Collections.Generic;
namespace WireMock.Pact.Models.V2
{
public class ProviderState
{
public string Name { get; set; }
namespace WireMock.Pact.Models.V2;
public IDictionary<string, string> Params { get; set; }
}
public class ProviderState
{
public string Name { get; set; }
public IDictionary<string, string> Params { get; set; }
}

View File

@@ -1,17 +1,10 @@
using Stef.Validation;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using WireMock.Constants;
using Stef.Validation;
using WireMock.Http;
using WireMock.Matchers;
using WireMock.RequestBuilders;
using WireMock.ResponseBuilders;
using WireMock.Serialization;
using WireMock.Settings;
using WireMock.Types;
using WireMock.Util;
namespace WireMock.Proxy;
@@ -62,69 +55,4 @@ internal class ProxyHelper
return (responseMessage, newMapping);
}
private IMapping ToMapping(ProxyAndRecordSettings proxyAndRecordSettings, IRequestMessage requestMessage, ResponseMessage responseMessage)
{
var excludedHeaders = proxyAndRecordSettings.ExcludedHeaders ?? new string[] { };
var excludedCookies = proxyAndRecordSettings.ExcludedCookies ?? new string[] { };
var request = Request.Create();
request.WithPath(requestMessage.Path);
request.UsingMethod(requestMessage.Method);
requestMessage.Query?.Loop((key, value) => request.WithParam(key, false, value.ToArray()));
requestMessage.Cookies?.Loop((key, value) =>
{
if (!excludedCookies.Contains(key, StringComparer.OrdinalIgnoreCase))
{
request.WithCookie(key, value);
}
});
var allExcludedHeaders = new List<string>(excludedHeaders) { "Cookie" };
requestMessage.Headers?.Loop((key, value) =>
{
if (!allExcludedHeaders.Contains(key, StringComparer.OrdinalIgnoreCase))
{
request.WithHeader(key, value.ToArray());
}
});
bool throwExceptionWhenMatcherFails = _settings.ThrowExceptionWhenMatcherFails == true;
switch (requestMessage.BodyData?.DetectedBodyType)
{
case BodyType.Json:
request.WithBody(new JsonMatcher(MatchBehaviour.AcceptOnMatch, requestMessage.BodyData.BodyAsJson!, true, throwExceptionWhenMatcherFails));
break;
case BodyType.String:
request.WithBody(new ExactMatcher(MatchBehaviour.AcceptOnMatch, false, throwExceptionWhenMatcherFails, MatchOperator.Or, requestMessage.BodyData.BodyAsString));
break;
case BodyType.Bytes:
request.WithBody(new ExactObjectMatcher(MatchBehaviour.AcceptOnMatch, requestMessage.BodyData.BodyAsBytes, throwExceptionWhenMatcherFails));
break;
}
var response = Response.Create(responseMessage);
return new Mapping
(
guid: Guid.NewGuid(),
title: $"Proxy Mapping for {requestMessage.Method} {requestMessage.Path}",
description: string.Empty,
path: null,
settings: _settings,
request,
response,
priority: WireMockConstants.ProxyPriority, // This was 0
scenario: null,
executionConditionState: null,
nextState: null,
stateTimes: null,
webhooks: null,
useWebhooksFireAndForget: null,
timeSettings: null
);
}
}

View File

@@ -1,10 +1,10 @@
// This source file is based on mock4net by Alexandre Victoor which is licensed under the Apache 2.0 License.
// For more details see 'mock4net/LICENSE.txt' and 'mock4net/readme.md' in this project root.
using System.Linq;
using WireMock.Http;
using Stef.Validation;
using WireMock.Constants;
using WireMock.Matchers;
using WireMock.Matchers.Request;
using Stef.Validation;
namespace WireMock.RequestBuilders;
@@ -13,63 +13,63 @@ public partial class Request
/// <inheritdoc />
public IRequestBuilder UsingConnect(MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch)
{
_requestMatchers.Add(new RequestMessageMethodMatcher(matchBehaviour, MatchOperator.Or, HttpRequestMethods.CONNECT));
_requestMatchers.Add(new RequestMessageMethodMatcher(matchBehaviour, MatchOperator.Or, HttpRequestMethod.CONNECT));
return this;
}
/// <inheritdoc />
public IRequestBuilder UsingDelete(MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch)
{
_requestMatchers.Add(new RequestMessageMethodMatcher(matchBehaviour, MatchOperator.Or, HttpRequestMethods.DELETE));
_requestMatchers.Add(new RequestMessageMethodMatcher(matchBehaviour, MatchOperator.Or, HttpRequestMethod.DELETE));
return this;
}
/// <inheritdoc />
public IRequestBuilder UsingGet(MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch)
{
_requestMatchers.Add(new RequestMessageMethodMatcher(matchBehaviour, MatchOperator.Or, HttpRequestMethods.GET));
_requestMatchers.Add(new RequestMessageMethodMatcher(matchBehaviour, MatchOperator.Or, HttpRequestMethod.GET));
return this;
}
/// <inheritdoc />
public IRequestBuilder UsingHead(MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch)
{
_requestMatchers.Add(new RequestMessageMethodMatcher(matchBehaviour, MatchOperator.Or, HttpRequestMethods.HEAD));
_requestMatchers.Add(new RequestMessageMethodMatcher(matchBehaviour, MatchOperator.Or, HttpRequestMethod.HEAD));
return this;
}
/// <inheritdoc />
public IRequestBuilder UsingOptions(MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch)
{
_requestMatchers.Add(new RequestMessageMethodMatcher(matchBehaviour, MatchOperator.Or, HttpRequestMethods.OPTIONS));
_requestMatchers.Add(new RequestMessageMethodMatcher(matchBehaviour, MatchOperator.Or, HttpRequestMethod.OPTIONS));
return this;
}
/// <inheritdoc />
public IRequestBuilder UsingPost(MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch)
{
_requestMatchers.Add(new RequestMessageMethodMatcher(matchBehaviour, MatchOperator.Or, HttpRequestMethods.POST));
_requestMatchers.Add(new RequestMessageMethodMatcher(matchBehaviour, MatchOperator.Or, HttpRequestMethod.POST));
return this;
}
/// <inheritdoc />
public IRequestBuilder UsingPatch(MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch)
{
_requestMatchers.Add(new RequestMessageMethodMatcher(matchBehaviour, MatchOperator.Or, HttpRequestMethods.PATCH));
_requestMatchers.Add(new RequestMessageMethodMatcher(matchBehaviour, MatchOperator.Or, HttpRequestMethod.PATCH));
return this;
}
/// <inheritdoc />
public IRequestBuilder UsingPut(MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch)
{
_requestMatchers.Add(new RequestMessageMethodMatcher(matchBehaviour, MatchOperator.Or, HttpRequestMethods.PUT));
_requestMatchers.Add(new RequestMessageMethodMatcher(matchBehaviour, MatchOperator.Or, HttpRequestMethod.PUT));
return this;
}
/// <inheritdoc />
public IRequestBuilder UsingTrace(MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch)
{
_requestMatchers.Add(new RequestMessageMethodMatcher(matchBehaviour, MatchOperator.Or, HttpRequestMethods.TRACE));
_requestMatchers.Add(new RequestMessageMethodMatcher(matchBehaviour, MatchOperator.Or, HttpRequestMethod.TRACE));
return this;
}

View File

@@ -21,7 +21,8 @@ public partial class Response
{
DetectedBodyType = BodyType.String,
BodyAsString = bodyFactory(req),
Encoding = encoding ?? Encoding.UTF8
Encoding = encoding ?? Encoding.UTF8,
IsFuncUsed = "Func<IRequestMessage, string>"
}
});
}
@@ -37,7 +38,8 @@ public partial class Response
{
DetectedBodyType = BodyType.String,
BodyAsString = await bodyFactory(req).ConfigureAwait(false),
Encoding = encoding ?? Encoding.UTF8
Encoding = encoding ?? Encoding.UTF8,
IsFuncUsed = "Func<IRequestMessage, Task<string>>"
}
});
}

View File

@@ -325,15 +325,15 @@ public partial class Response : IResponseBuilder
break;
default:
throw new NotImplementedException($"TransformerType '{TransformerType}' is not supported.");
throw new NotSupportedException($"TransformerType '{TransformerType}' is not supported.");
}
return (responseMessageTransformer.Transform(mapping, requestMessage, responseMessage, UseTransformerForBodyAsFile, TransformerReplaceNodeOptions), null);
}
if (!UseTransformer && ResponseMessage.BodyData?.BodyAsFileIsCached == true)
if (!UseTransformer && ResponseMessage.BodyData?.BodyAsFileIsCached == true && responseMessage.BodyData?.BodyAsFile is not null)
{
ResponseMessage.BodyData.BodyAsBytes = settings.FileSystemHandler.ReadResponseBodyAsFile(responseMessage.BodyData!.BodyAsFile);
ResponseMessage.BodyData.BodyAsBytes = settings.FileSystemHandler.ReadResponseBodyAsFile(responseMessage.BodyData.BodyAsFile);
}
return (responseMessage, null);

View File

@@ -8,7 +8,7 @@ public class ScenarioState
/// <summary>
/// Gets or sets the Name (from the Scenario).
/// </summary>
public string? Name { get; set; }
public string Name { get; set; } = null!;
/// <summary>
/// Gets or sets the NextState.

View File

@@ -1,16 +1,25 @@
using System.Linq;
using Stef.Validation;
using WireMock.Admin.Mappings;
using WireMock.Admin.Requests;
using WireMock.Logging;
using WireMock.Matchers.Request;
using WireMock.Owin;
using WireMock.ResponseBuilders;
using WireMock.Types;
namespace WireMock.Serialization;
internal static class LogEntryMapper
internal class LogEntryMapper
{
public static LogEntryModel Map(ILogEntry logEntry)
private readonly IWireMockMiddlewareOptions _options;
public LogEntryMapper(IWireMockMiddlewareOptions options)
{
_options = Guard.NotNull(options);
}
public LogEntryModel Map(ILogEntry logEntry)
{
var logRequestModel = new LogRequestModel
{
@@ -78,25 +87,7 @@ internal static class LogEntryMapper
logResponseModel.DetectedBodyType = logEntry.ResponseMessage.BodyData.DetectedBodyType;
logResponseModel.DetectedBodyTypeFromContentType = logEntry.ResponseMessage.BodyData.DetectedBodyTypeFromContentType;
switch (logEntry.ResponseMessage.BodyData.DetectedBodyType)
{
case BodyType.String:
logResponseModel.Body = logEntry.ResponseMessage.BodyData.BodyAsString;
break;
case BodyType.Json:
logResponseModel.BodyAsJson = logEntry.ResponseMessage.BodyData.BodyAsJson;
break;
case BodyType.Bytes:
logResponseModel.BodyAsBytes = logEntry.ResponseMessage.BodyData.BodyAsBytes;
break;
case BodyType.File:
logResponseModel.BodyAsFile = logEntry.ResponseMessage.BodyData.BodyAsFile;
logResponseModel.BodyAsFileIsCached = logEntry.ResponseMessage.BodyData.BodyAsFileIsCached;
break;
}
MapBody(logEntry, logResponseModel);
logResponseModel.BodyEncoding = logEntry.ResponseMessage.BodyData.Encoding != null
? new EncodingModel
@@ -124,6 +115,36 @@ internal static class LogEntryMapper
};
}
private void MapBody(ILogEntry logEntry, LogResponseModel logResponseModel)
{
switch (logEntry.ResponseMessage.BodyData!.DetectedBodyType)
{
case BodyType.String:
if (!string.IsNullOrEmpty(logEntry.ResponseMessage.BodyData.IsFuncUsed) && _options.DoNotSaveDynamicResponseInLogEntry == true)
{
logResponseModel.Body = logEntry.ResponseMessage.BodyData.IsFuncUsed;
}
else
{
logResponseModel.Body = logEntry.ResponseMessage.BodyData.BodyAsString;
}
break;
case BodyType.Json:
logResponseModel.BodyAsJson = logEntry.ResponseMessage.BodyData.BodyAsJson;
break;
case BodyType.Bytes:
logResponseModel.BodyAsBytes = logEntry.ResponseMessage.BodyData.BodyAsBytes;
break;
case BodyType.File:
logResponseModel.BodyAsFile = logEntry.ResponseMessage.BodyData.BodyAsFile;
logResponseModel.BodyAsFileIsCached = logEntry.ResponseMessage.BodyData.BodyAsFileIsCached;
break;
}
}
private static LogRequestMatchModel? Map(IRequestMatchResult? matchResult)
{
if (matchResult == null)

View File

@@ -13,11 +13,8 @@ internal class MappingToFileSaver
public MappingToFileSaver(WireMockServerSettings settings, MappingConverter mappingConverter)
{
Guard.NotNull(settings);
Guard.NotNull(mappingConverter);
_settings = settings;
_mappingConverter = mappingConverter;
_settings = Guard.NotNull(settings);
_mappingConverter = Guard.NotNull(mappingConverter);
}
public void SaveMappingToFile(IMapping mapping, string? folder = null)
@@ -30,17 +27,31 @@ internal class MappingToFileSaver
}
var model = _mappingConverter.ToMappingModel(mapping);
string filename = (!string.IsNullOrEmpty(mapping.Title) ? SanitizeFileName(mapping.Title) : mapping.Guid.ToString()) + ".json";
string path = Path.Combine(folder, filename);
var filename = BuildSanitizedFileName(mapping);
var path = Path.Combine(folder, filename);
_settings.Logger.Info("Saving Mapping file {0}", filename);
_settings.Logger.Info("Saving Mapping file {0}", path);
_settings.FileSystemHandler.WriteMappingFile(path, JsonConvert.SerializeObject(model, JsonSerializationConstants.JsonSerializerSettingsDefault));
}
private static string SanitizeFileName(string name, char replaceChar = '_')
private string BuildSanitizedFileName(IMapping mapping, char replaceChar = '_')
{
return Path.GetInvalidFileNameChars().Aggregate(name, (current, c) => current.Replace(c, replaceChar));
string name;
if (!string.IsNullOrEmpty(mapping.Title))
{
name = mapping.Title!;
if (_settings.ProxyAndRecordSettings?.AppendGuidToSavedMappingFile == true)
{
name += $"{replaceChar}{mapping.Guid}";
}
}
else
{
name = mapping.Guid.ToString();
}
return $"{Path.GetInvalidFileNameChars().Aggregate(name, (current, c) => current.Replace(c, replaceChar))}.json";
}
}

View File

@@ -3,9 +3,7 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
@@ -18,11 +16,9 @@ using WireMock.Http;
using WireMock.Logging;
using WireMock.Matchers;
using WireMock.Matchers.Request;
using WireMock.Proxy;
using WireMock.RequestBuilders;
using WireMock.ResponseProviders;
using WireMock.Serialization;
using WireMock.Settings;
using WireMock.Types;
using WireMock.Util;
@@ -46,6 +42,8 @@ public partial class WireMockServer
private static readonly RegexMatcher AdminRequestContentTypeJson = new ContentTypeMatcher(WireMockConstants.ContentTypeJson, true);
private static readonly RegexMatcher AdminMappingsGuidPathMatcher = new(@"^\/__admin\/mappings\/([0-9A-Fa-f]{8}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{12})$");
private static readonly RegexMatcher AdminRequestsGuidPathMatcher = new(@"^\/__admin\/requests\/([0-9A-Fa-f]{8}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{12})$");
private static readonly RegexMatcher AdminScenariosNameMatcher = new(@"^\/__admin\/scenarios\/.+$");
private static readonly RegexMatcher AdminScenariosNameWithResetMatcher = new(@"^\/__admin\/scenarios\/.+\/reset$");
private EnhancedFileSystemWatcher? _enhancedFileSystemWatcher;
@@ -93,9 +91,11 @@ public partial class WireMockServer
// __admin/scenarios
Given(Request.Create().WithPath(AdminScenarios).UsingGet()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(ScenariosGet));
Given(Request.Create().WithPath(AdminScenarios).UsingDelete()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(ScenariosReset));
Given(Request.Create().WithPath(AdminScenariosNameMatcher).UsingDelete()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(ScenarioReset));
// __admin/scenarios/reset
Given(Request.Create().WithPath(AdminScenarios + "/reset").UsingPost()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(ScenariosReset));
Given(Request.Create().WithPath(AdminScenariosNameWithResetMatcher).UsingPost()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(ScenarioReset));
// __admin/files/{filename}
Given(Request.Create().WithPath(_adminFilesFilenamePathMatcher).UsingPost()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(FilePost));
@@ -204,62 +204,6 @@ public partial class WireMockServer
}
#endregion
#region Proxy and Record
private HttpClient? _httpClientForProxy;
private void InitProxyAndRecord(WireMockServerSettings settings)
{
if (settings.ProxyAndRecordSettings == null)
{
_httpClientForProxy = null;
DeleteMapping(ProxyMappingGuid);
return;
}
_httpClientForProxy = HttpClientBuilder.Build(settings.ProxyAndRecordSettings);
var proxyRespondProvider = Given(Request.Create().WithPath("/*").UsingAnyMethod()).WithGuid(ProxyMappingGuid).WithTitle("Default Proxy Mapping on /*");
if (settings.StartAdminInterface == true)
{
proxyRespondProvider.AtPriority(WireMockConstants.ProxyPriority);
}
proxyRespondProvider.RespondWith(new ProxyAsyncResponseProvider(ProxyAndRecordAsync, settings));
}
private async Task<IResponseMessage> ProxyAndRecordAsync(IRequestMessage requestMessage, WireMockServerSettings settings)
{
var requestUri = new Uri(requestMessage.Url);
var proxyUri = new Uri(settings.ProxyAndRecordSettings!.Url);
var proxyUriWithRequestPathAndQuery = new Uri(proxyUri, requestUri.PathAndQuery);
var proxyHelper = new ProxyHelper(settings);
var (responseMessage, mapping) = await proxyHelper.SendAsync(
null,
_settings.ProxyAndRecordSettings!,
_httpClientForProxy!,
requestMessage,
proxyUriWithRequestPathAndQuery.AbsoluteUri
).ConfigureAwait(false);
if (mapping != null)
{
if (settings.ProxyAndRecordSettings.SaveMapping)
{
_options.Mappings.TryAdd(mapping.Guid, mapping);
}
if (settings.ProxyAndRecordSettings.SaveMappingToFile)
{
_mappingToFileSaver.SaveMappingToFile(mapping);
}
}
return responseMessage;
}
#endregion
#region Settings
private IResponseMessage SettingsGet(IRequestMessage requestMessage)
{
@@ -277,6 +221,8 @@ public partial class WireMockServer
UseRegexExtended = _settings.UseRegexExtended,
WatchStaticMappings = _settings.WatchStaticMappings,
WatchStaticMappingsInSubdirectories = _settings.WatchStaticMappingsInSubdirectories,
HostingScheme = _settings.HostingScheme,
DoNotSaveDynamicResponseInLogEntry = _settings.DoNotSaveDynamicResponseInLogEntry,
#if USE_ASPNETCORE
CorsPolicyOptions = _settings.CorsPolicyOptions?.ToString()
@@ -305,6 +251,7 @@ public partial class WireMockServer
_settings.UseRegexExtended = settings.UseRegexExtended;
_settings.WatchStaticMappings = settings.WatchStaticMappings;
_settings.WatchStaticMappingsInSubdirectories = settings.WatchStaticMappingsInSubdirectories;
_settings.DoNotSaveDynamicResponseInLogEntry = settings.DoNotSaveDynamicResponseInLogEntry;
InitSettings(_settings);
@@ -525,7 +472,7 @@ public partial class WireMockServer
return ResponseMessageBuilder.Create("Request not found", 404);
}
var model = LogEntryMapper.Map(entry);
var model = new LogEntryMapper(_options).Map(entry);
return ToJson(model);
}
@@ -546,9 +493,10 @@ public partial class WireMockServer
#region Requests
private IResponseMessage RequestsGet(IRequestMessage requestMessage)
{
var logEntryMapper = new LogEntryMapper(_options);
var result = LogEntries
.Where(r => !r.RequestMessage.Path.StartsWith("/__admin/"))
.Select(LogEntryMapper.Map);
.Select(logEntryMapper.Map);
return ToJson(result);
}
@@ -578,7 +526,8 @@ public partial class WireMockServer
}
}
var result = dict.OrderBy(x => x.Value.AverageTotalScore).Select(x => x.Key).Select(LogEntryMapper.Map);
var logEntryMapper = new LogEntryMapper(_options);
var result = dict.OrderBy(x => x.Value.AverageTotalScore).Select(x => x.Key).Select(logEntryMapper.Map);
return ToJson(result);
}
@@ -605,6 +554,17 @@ public partial class WireMockServer
return ResponseMessageBuilder.Create("Scenarios reset");
}
private IResponseMessage ScenarioReset(IRequestMessage requestMessage)
{
var name = string.Equals(HttpRequestMethod.DELETE, requestMessage.Method, StringComparison.OrdinalIgnoreCase) ?
requestMessage.Path.Substring(AdminScenarios.Length + 1) :
requestMessage.Path.Split('/').Reverse().Skip(1).First();
return ResetScenario(name) ?
ResponseMessageBuilder.Create("Scenario reset") :
ResponseMessageBuilder.Create($"No scenario found by name '{name}'.", 404);
}
#endregion
#region Pact

View File

@@ -0,0 +1,68 @@
using System;
using System.Net.Http;
using System.Threading.Tasks;
using WireMock.Constants;
using WireMock.Http;
using WireMock.Proxy;
using WireMock.RequestBuilders;
using WireMock.ResponseProviders;
using WireMock.Settings;
namespace WireMock.Server;
public partial class WireMockServer
{
private HttpClient? _httpClientForProxy;
private void InitProxyAndRecord(WireMockServerSettings settings)
{
if (settings.ProxyAndRecordSettings == null)
{
_httpClientForProxy = null;
DeleteMapping(ProxyMappingGuid);
return;
}
_httpClientForProxy = HttpClientBuilder.Build(settings.ProxyAndRecordSettings);
var proxyRespondProvider = Given(Request.Create().WithPath("/*").UsingAnyMethod()).WithGuid(ProxyMappingGuid).WithTitle("Default Proxy Mapping on /*");
if (settings.StartAdminInterface == true)
{
proxyRespondProvider.AtPriority(WireMockConstants.ProxyPriority);
}
proxyRespondProvider.RespondWith(new ProxyAsyncResponseProvider(ProxyAndRecordAsync, settings));
}
private async Task<IResponseMessage> ProxyAndRecordAsync(IRequestMessage requestMessage, WireMockServerSettings settings)
{
var requestUri = new Uri(requestMessage.Url);
var proxyUri = new Uri(settings.ProxyAndRecordSettings!.Url);
var proxyUriWithRequestPathAndQuery = new Uri(proxyUri, requestUri.PathAndQuery);
var proxyHelper = new ProxyHelper(settings);
var (responseMessage, mapping) = await proxyHelper.SendAsync(
null,
_settings.ProxyAndRecordSettings!,
_httpClientForProxy!,
requestMessage,
proxyUriWithRequestPathAndQuery.AbsoluteUri
).ConfigureAwait(false);
if (mapping != null)
{
if (settings.ProxyAndRecordSettings.SaveMapping)
{
_options.Mappings.TryAdd(mapping.Guid, mapping);
}
if (settings.ProxyAndRecordSettings.SaveMappingToFile)
{
_mappingToFileSaver.SaveMappingToFile(mapping);
}
}
return responseMessage;
}
}

View File

@@ -295,6 +295,7 @@ public partial class WireMockServer : IWireMockServer
_options.DisableJsonBodyParsing = _settings.DisableJsonBodyParsing;
_options.HandleRequestsSynchronously = settings.HandleRequestsSynchronously;
_options.SaveUnmatchedRequests = settings.SaveUnmatchedRequests;
_options.DoNotSaveDynamicResponseInLogEntry = settings.DoNotSaveDynamicResponseInLogEntry;
if (settings.CustomCertificateDefined)
{
@@ -476,6 +477,13 @@ public partial class WireMockServer : IWireMockServer
_options.Scenarios.Clear();
}
/// <inheritdoc />
[PublicAPI]
public bool ResetScenario(string name)
{
return _options.Scenarios.ContainsKey(name) && _options.Scenarios.TryRemove(name, out _);
}
/// <inheritdoc cref="IWireMockServer.WithMapping(MappingModel[])" />
[PublicAPI]
public IWireMockServer WithMapping(params MappingModel[] mappings)

View File

@@ -58,4 +58,9 @@ public class ProxyAndRecordSettings : HttpClientSettings
/// Default value is false.
/// </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; }
}

View File

@@ -12,7 +12,6 @@ using WireMock.RegularExpressions;
using WireMock.Types;
#if USE_ASPNETCORE
using Microsoft.Extensions.DependencyInjection;
using WireMock.Types;
#endif
namespace WireMock.Settings
@@ -253,6 +252,12 @@ namespace WireMock.Settings
[PublicAPI]
public bool? SaveUnmatchedRequests { get; set; }
/// <summary>
/// Don't save the response-string in the LogEntry when WithBody(Func{IRequestMessage, string}) or WithBody(Func{IRequestMessage, Task{string}}) is used. (default set to false).
/// </summary>
[PublicAPI]
public bool? DoNotSaveDynamicResponseInLogEntry { get; set; }
/// <summary>
/// Custom matcher mappings for static mappings
/// </summary>

View File

@@ -54,20 +54,31 @@ public static class WireMockServerSettingsParser
UseRegexExtended = parser.GetBoolValue(nameof(WireMockServerSettings.UseRegexExtended), true),
WatchStaticMappings = parser.GetBoolValue("WatchStaticMappings"),
WatchStaticMappingsInSubdirectories = parser.GetBoolValue("WatchStaticMappingsInSubdirectories"),
HostingScheme = parser.GetEnumValue<HostingScheme>(nameof(WireMockServerSettings.HostingScheme))
HostingScheme = parser.GetEnumValue<HostingScheme>(nameof(WireMockServerSettings.HostingScheme)),
DoNotSaveDynamicResponseInLogEntry = parser.GetBoolValue(nameof(WireMockServerSettings.DoNotSaveDynamicResponseInLogEntry))
};
#if USE_ASPNETCORE
settings.CorsPolicyOptions = parser.GetEnumValue(nameof(WireMockServerSettings.CorsPolicyOptions), CorsPolicyOptions.None);
#endif
if (logger != null)
var loggerType = parser.GetStringValue("WireMockLogger");
switch (loggerType)
{
settings.Logger = logger;
}
else if (parser.GetStringValue("WireMockLogger") == "WireMockConsoleLogger")
{
settings.Logger = new WireMockConsoleLogger();
case nameof(WireMockConsoleLogger):
settings.Logger = new WireMockConsoleLogger();
break;
case nameof(WireMockNullLogger):
settings.Logger = new WireMockNullLogger();
break;
default:
if (logger != null)
{
settings.Logger = logger;
}
break;
}
if (parser.Contains(nameof(WireMockServerSettings.Port)))
@@ -93,6 +104,7 @@ public static class WireMockServerSettingsParser
SaveMappingForStatusCodePattern = parser.GetStringValue("SaveMappingForStatusCodePattern", "*"),
SaveMappingToFile = parser.GetBoolValue("SaveMappingToFile"),
UseDefinedRequestMatchers = parser.GetBoolValue(nameof(ProxyAndRecordSettings.UseDefinedRequestMatchers)),
AppendGuidToSavedMappingFile = parser.GetBoolValue(nameof(ProxyAndRecordSettings.AppendGuidToSavedMappingFile)),
Url = proxyUrl!
};

View File

@@ -10,7 +10,7 @@ interface ITransformer
IBodyData? TransformBody(IMapping mapping, IRequestMessage originalRequestMessage, IResponseMessage originalResponseMessage, IBodyData? bodyData, ReplaceNodeOptions options);
IDictionary<string, WireMockList<string>>? TransformHeaders(IMapping mapping, IRequestMessage originalRequestMessage, IResponseMessage originalResponseMessage, IDictionary<string, WireMockList<string>>? headers);
IDictionary<string, WireMockList<string>> TransformHeaders(IMapping mapping, IRequestMessage originalRequestMessage, IResponseMessage originalResponseMessage, IDictionary<string, WireMockList<string>>? headers);
string TransformString(IMapping mapping, IRequestMessage originalRequestMessage, IResponseMessage originalResponseMessage, string? value);
}

View File

@@ -36,7 +36,7 @@ internal class Transformer : ITransformer
return newBodyData;
}
public IDictionary<string, WireMockList<string>>? TransformHeaders(
public IDictionary<string, WireMockList<string>> TransformHeaders(
IMapping mapping,
IRequestMessage originalRequestMessage,
IResponseMessage originalResponseMessage,
@@ -239,6 +239,7 @@ internal class Transformer : ITransformer
}
}
// ReSharper disable once UnusedParameter.Local
private static void ReplaceNodeValue(ReplaceNodeOptions options, JToken node, string transformedString)
{
StringUtils.TryParseQuotedString(transformedString, out var result, out _);
@@ -270,13 +271,13 @@ internal class Transformer : ITransformer
Encoding = original.Encoding,
DetectedBodyType = original.DetectedBodyType,
DetectedBodyTypeFromContentType = original.DetectedBodyTypeFromContentType,
BodyAsString = handlebarsContext.ParseAndRender(original.BodyAsString, model)
BodyAsString = handlebarsContext.ParseAndRender(original.BodyAsString!, model)
};
}
private static IBodyData TransformBodyAsFile(ITransformerContext handlebarsContext, object model, IBodyData original, bool useTransformerForBodyAsFile)
{
string transformedBodyAsFilename = handlebarsContext.ParseAndRender(original.BodyAsFile, model);
string transformedBodyAsFilename = handlebarsContext.ParseAndRender(original.BodyAsFile!, model);
if (!useTransformerForBodyAsFile)
{

View File

@@ -5,7 +5,7 @@ using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
using Stef.Validation;
using WireMock.Http;
using WireMock.Constants;
using WireMock.Matchers;
using WireMock.Types;
@@ -29,15 +29,15 @@ internal static class BodyParser
*/
private static readonly IDictionary<string, bool> BodyAllowedForMethods = new Dictionary<string, bool>
{
{ HttpRequestMethods.HEAD, false },
{ HttpRequestMethods.GET, false },
{ HttpRequestMethods.PUT, true },
{ HttpRequestMethods.POST, true },
{ HttpRequestMethods.DELETE, true },
{ HttpRequestMethods.TRACE, false },
{ HttpRequestMethods.OPTIONS, true },
{ HttpRequestMethods.CONNECT, false },
{ HttpRequestMethods.PATCH, true }
{ HttpRequestMethod.HEAD, false },
{ HttpRequestMethod.GET, false },
{ HttpRequestMethod.PUT, true },
{ HttpRequestMethod.POST, true },
{ HttpRequestMethod.DELETE, true },
{ HttpRequestMethod.TRACE, false },
{ HttpRequestMethod.OPTIONS, true },
{ HttpRequestMethod.CONNECT, false },
{ HttpRequestMethod.PATCH, true }
};
private static readonly IStringMatcher[] MultipartContentTypesMatchers = {
@@ -122,7 +122,7 @@ internal static class BodyParser
// In case of MultiPart: check if the BodyAsBytes is a valid UTF8 or ASCII string, in that case read as String else keep as-is
if (data.DetectedBodyTypeFromContentType == BodyType.MultiPart)
{
if (BytesEncodingUtils.TryGetEncoding(data.BodyAsBytes, out Encoding encoding) &&
if (BytesEncodingUtils.TryGetEncoding(data.BodyAsBytes, out var encoding) &&
SupportedBodyAsStringEncodingForMultipart.Select(x => x.Equals(encoding)).Any())
{
data.BodyAsString = encoding.GetString(data.BodyAsBytes);

View File

@@ -21,10 +21,6 @@
<PublicSign Condition=" '$(OS)' != 'Windows_NT' ">true</PublicSign>
</PropertyGroup>
<!--<ItemGroup>
<None Include="WireMock.Net-Logo.png" Pack="true" PackagePath="../../"/>
</ItemGroup>-->
<!-- https://github.com/aspnet/RoslynCodeDomProvider/issues/51 -->
<!-- This is needed else we cannot build net452 in Azure DevOps Pipeline -->
<!--<Target Name="CheckIfShouldKillVBCSCompiler" />-->
@@ -145,7 +141,6 @@
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'netcoreapp3.1' ">
<!--<PackageReference Include="System.Diagnostics.CodeAnalysis" Version="4.3.0" />-->
<PackageReference Include="Nullable" Version="1.3.0" />
</ItemGroup>

View File

@@ -5,56 +5,55 @@ using WireMock.Logging;
using WireMock.Net.StandAlone;
using WireMock.Server;
namespace WireMock.Net
namespace WireMock.Net;
public class Program
{
public class Program
private static readonly int SleepTime = 30000;
private static readonly ILogger XLogger = LoggerFactory.Create(o =>
{
private static readonly int SleepTime = 30000;
private static readonly ILogger xLogger = LoggerFactory.Create(o =>
o.SetMinimumLevel(LogLevel.Debug);
o.AddSimpleConsole(options =>
{
o.SetMinimumLevel(LogLevel.Debug);
o.AddSimpleConsole(options =>
{
options.IncludeScopes = true;
options.SingleLine = false;
options.TimestampFormat = "yyyy-MM-ddTHH:mm:ss ";
});
}).CreateLogger("WireMock.Net");
private static readonly IWireMockLogger Logger = new WireMockLogger(xLogger);
options.IncludeScopes = true;
options.SingleLine = false;
options.TimestampFormat = "yyyy-MM-ddTHH:mm:ss ";
});
}).CreateLogger("WireMock.Net");
private static readonly IWireMockLogger Logger = new WireMockLogger(XLogger);
private static WireMockServer Server;
private static WireMockServer _server = null!;
static async Task Main(string[] args)
static async Task Main(string[] args)
{
if (!StandAloneApp.TryStart(args, out _server!, Logger))
{
if (!StandAloneApp.TryStart(args, out Server, Logger))
{
return;
}
Logger.Info("Press Ctrl+C to shut down");
Console.CancelKeyPress += (s, e) =>
{
Stop("CancelKeyPress");
};
System.Runtime.Loader.AssemblyLoadContext.Default.Unloading += ctx =>
{
Stop("AssemblyLoadContext.Default.Unloading");
};
while (true)
{
Logger.Info("Server running : {IsStarted}", Server.IsStarted);
await Task.Delay(SleepTime).ConfigureAwait(false);
}
return;
}
private static void Stop(string why)
Logger.Info("Press Ctrl+C to shut down");
Console.CancelKeyPress += (s, e) =>
{
Logger.Info("Server stopping because '{why}'", why);
Server.Stop();
Logger.Info("Server stopped");
Stop("CancelKeyPress");
};
System.Runtime.Loader.AssemblyLoadContext.Default.Unloading += ctx =>
{
Stop("AssemblyLoadContext.Default.Unloading");
};
while (true)
{
Logger.Info("Server running : {0}", _server.IsStarted);
await Task.Delay(SleepTime).ConfigureAwait(false);
}
}
private static void Stop(string why)
{
Logger.Info("Server stopping because '{0}'", why);
_server.Stop();
Logger.Info("Server stopped");
}
}

View File

@@ -1,8 +1,8 @@
{
"profiles": {
"dotnet-WireMock.Net": {
"commandName": "Project",
"commandLineArgs": ""
"commandName": "Project",
"commandLineArgs": "--WireMockLogger s"
}
}
}

View File

@@ -4,58 +4,57 @@ using Microsoft.Extensions.Logging;
using WireMock.Admin.Requests;
using WireMock.Logging;
namespace WireMock.Net
namespace WireMock.Net;
public class WireMockLogger : IWireMockLogger
{
public class WireMockLogger : IWireMockLogger
private readonly JsonSerializerOptions _options = new()
{
private readonly JsonSerializerOptions options = new JsonSerializerOptions
{
WriteIndented = true
};
WriteIndented = true
};
private readonly ILogger _logger;
private readonly ILogger _logger;
public WireMockLogger(ILogger logger)
{
_logger = logger;
}
public WireMockLogger(ILogger logger)
{
_logger = logger;
}
/// <see cref="IWireMockLogger.Debug"/>
public void Debug(string formatString, params object[] args)
{
_logger.LogDebug(formatString, args);
}
/// <see cref="IWireMockLogger.Debug"/>
public void Debug(string formatString, params object[] args)
{
_logger.LogDebug(formatString, args);
}
/// <see cref="IWireMockLogger.Info"/>
public void Info(string formatString, params object[] args)
{
_logger.LogInformation(formatString, args);
}
/// <see cref="IWireMockLogger.Info"/>
public void Info(string formatString, params object[] args)
{
_logger.LogInformation(formatString, args);
}
/// <see cref="IWireMockLogger.Warn"/>
public void Warn(string formatString, params object[] args)
{
_logger.LogWarning(formatString, args);
}
/// <see cref="IWireMockLogger.Warn"/>
public void Warn(string formatString, params object[] args)
{
_logger.LogWarning(formatString, args);
}
/// <see cref="IWireMockLogger.Error(string, object[])"/>
public void Error(string formatString, params object[] args)
{
_logger.LogError(formatString, args);
}
/// <see cref="IWireMockLogger.Error(string, object[])"/>
public void Error(string formatString, params object[] args)
{
_logger.LogError(formatString, args);
}
/// <see cref="IWireMockLogger.Error(string, Exception)"/>
public void Error(string formatString, Exception exception)
{
_logger.LogError(formatString, exception);
}
/// <see cref="IWireMockLogger.Error(string, Exception)"/>
public void Error(string formatString, Exception exception)
{
_logger.LogError(formatString, exception);
}
/// <see cref="IWireMockLogger.DebugRequestResponse"/>
public void DebugRequestResponse(LogEntryModel logEntryModel, bool isAdminRequest)
{
string message = JsonSerializer.Serialize(logEntryModel, options);
/// <see cref="IWireMockLogger.DebugRequestResponse"/>
public void DebugRequestResponse(LogEntryModel logEntryModel, bool isAdminRequest)
{
string message = JsonSerializer.Serialize(logEntryModel, _options);
_logger.LogDebug("Admin[{IsAdmin}] {Message}", isAdminRequest, message);
}
_logger.LogDebug("Admin[{IsAdmin}] {Message}", isAdminRequest, message);
}
}

View File

@@ -562,6 +562,44 @@ public class WireMockAssertionsTests : IDisposable
.UsingTrace();
}
[Fact]
public async Task HaveReceivedACall_UsingAnyMethod_WhenACallWasMadeUsingGet_Should_BeOK()
{
await _httpClient.SendAsync(new HttpRequestMessage(new HttpMethod("GET"), "anyurl")).ConfigureAwait(false);
_server.Should()
.HaveReceivedACall()
.UsingAnyMethod();
}
[Fact]
public void HaveReceivedNoCalls_UsingAnyMethod_WhenNoCallsWereMade_Should_BeOK()
{
_server
.Should()
.HaveReceived(0)
.Calls()
.UsingAnyMethod();
_server
.Should()
.HaveReceivedNoCalls()
.UsingAnyMethod();
}
[Fact]
public void HaveReceivedNoCalls_AtUrl_WhenNoCallsWereMade_Should_BeOK()
{
_server.Should()
.HaveReceived(0)
.Calls()
.AtUrl(_server.Url ?? string.Empty);
_server.Should()
.HaveReceivedNoCalls()
.AtUrl(_server.Url ?? string.Empty);
}
public void Dispose()
{
_server?.Stop();

View File

@@ -21,12 +21,12 @@ public class ResponseWithBodyTests
private readonly Mock<IMapping> _mappingMock;
private readonly Mock<IFileSystemHandler> _filesystemHandlerMock;
private readonly WireMockServerSettings _settings = new ();
private readonly WireMockServerSettings _settings = new();
public ResponseWithBodyTests()
{
_mappingMock = new Mock<IMapping>();
_filesystemHandlerMock = new Mock<IFileSystemHandler>(MockBehavior.Strict);
_filesystemHandlerMock.Setup(fs => fs.ReadResponseBodyAsString(It.IsAny<string>())).Returns("abc");
@@ -197,61 +197,7 @@ public class ResponseWithBodyTests
}
[Fact]
public async Task Response_ProvideResponse_WithBody_Func()
{
// Assign
var request = new RequestMessage(new UrlDetails("http://localhost/test"), "GET", ClientIp);
var responseBuilder = Response.Create()
.WithStatusCode(500)
.WithHeader("H1", "X1")
.WithHeader("H2", "X2")
.WithBody(req => $"path: {req.Path}");
// Act
var response = await responseBuilder.ProvideResponseAsync(_mappingMock.Object, request, _settings).ConfigureAwait(false);
// Assert
Check.That(response.Message.BodyData.BodyAsString).IsEqualTo("path: /test");
Check.That(response.Message.BodyData.BodyAsBytes).IsNull();
Check.That(response.Message.BodyData.BodyAsJson).IsNull();
Check.That(response.Message.BodyData.Encoding.CodePage).Equals(Encoding.UTF8.CodePage);
Check.That(response.Message.StatusCode).IsEqualTo(500);
Check.That(response.Message.Headers["H1"].ToString()).IsEqualTo("X1");
Check.That(response.Message.Headers["H2"].ToString()).IsEqualTo("X2");
}
[Fact]
public async Task Response_ProvideResponse_WithBody_FuncAsync()
{
// Assign
var request = new RequestMessage(new UrlDetails("http://localhost/test"), "GET", ClientIp);
var responseBuilder = Response.Create()
.WithStatusCode(500)
.WithHeader("H1", "X1")
.WithHeader("H2", "X2")
.WithBody(async req =>
{
await Task.Delay(1).ConfigureAwait(false);
return $"path: {req.Path}";
});
// Act
var response = await responseBuilder.ProvideResponseAsync(_mappingMock.Object, request, _settings).ConfigureAwait(false);
// Assert
Check.That(response.Message.BodyData.BodyAsString).IsEqualTo("path: /test");
Check.That(response.Message.BodyData.BodyAsBytes).IsNull();
Check.That(response.Message.BodyData.BodyAsJson).IsNull();
Check.That(response.Message.BodyData.Encoding.CodePage).Equals(Encoding.UTF8.CodePage);
Check.That(response.Message.StatusCode).IsEqualTo(500);
Check.That(response.Message.Headers["H1"].ToString()).IsEqualTo("X1");
Check.That(response.Message.Headers["H2"].ToString()).IsEqualTo("X2");
}
[Fact]
public async Task Response_ProvideResponse_WithJsonBodyAndTransform_Func()
public async Task Response_ProvideResponse_WithJsonBodyAndTransform()
{
// Assign
const int request1Id = 1;

View File

@@ -1,8 +1,10 @@
using System.Collections.Generic;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using FluentAssertions;
using Moq;
using NFluent;
using WireMock.Handlers;
using WireMock.Models;
using WireMock.ResponseBuilders;
@@ -18,17 +20,124 @@ public class ResponseWithCallbackTests
private const string ClientIp = "::1";
private readonly Mock<IMapping> _mappingMock;
private readonly Mock<IFileSystemHandler> _filesystemHandlerMock;
private readonly WireMockServerSettings _settings = new();
public ResponseWithCallbackTests()
{
_mappingMock = new Mock<IMapping>();
_filesystemHandlerMock = new Mock<IFileSystemHandler>(MockBehavior.Strict);
_filesystemHandlerMock.Setup(fs => fs.ReadResponseBodyAsString(It.IsAny<string>())).Returns("abc");
var filesystemHandlerMock = new Mock<IFileSystemHandler>(MockBehavior.Strict);
filesystemHandlerMock.Setup(fs => fs.ReadResponseBodyAsString(It.IsAny<string>())).Returns("abc");
_settings.FileSystemHandler = _filesystemHandlerMock.Object;
_settings.FileSystemHandler = filesystemHandlerMock.Object;
}
[Fact]
public async Task Response_ProvideResponse_WithBody_Func()
{
// Assign
var request = new RequestMessage(new UrlDetails("http://localhost/test"), "GET", ClientIp);
var responseBuilder = Response.Create()
.WithStatusCode(500)
.WithHeader("H1", "X1")
.WithHeader("H2", "X2")
.WithBody(req => $"path: {req.Path}");
// Act
var response = await responseBuilder.ProvideResponseAsync(_mappingMock.Object, request, _settings).ConfigureAwait(false);
// Assert
Check.That(response.Message.BodyData.BodyAsString).IsEqualTo("path: /test");
Check.That(response.Message.BodyData.BodyAsBytes).IsNull();
Check.That(response.Message.BodyData.BodyAsJson).IsNull();
Check.That(response.Message.BodyData.Encoding.CodePage).Equals(Encoding.UTF8.CodePage);
Check.That(response.Message.StatusCode).IsEqualTo(500);
Check.That(response.Message.Headers["H1"].ToString()).IsEqualTo("X1");
Check.That(response.Message.Headers["H2"].ToString()).IsEqualTo("X2");
}
[Fact]
public async Task Response_ProvideResponse_WithBody_Func_DynamicCode_Should_IsFuncUsed()
{
// Assign
var request = new RequestMessage(new UrlDetails("http://localhost/test"), "GET", ClientIp);
var data = new[] { "x", "y" };
var i = 0;
var responseBuilder = Response.Create()
.WithBody(_ =>
{
var value = data[i];
i++;
return value;
});
// Act (2x)
var response1 = await responseBuilder.ProvideResponseAsync(_mappingMock.Object, request, _settings).ConfigureAwait(false);
var response2 = await responseBuilder.ProvideResponseAsync(_mappingMock.Object, request, _settings).ConfigureAwait(false);
// Assert
response1.Message.BodyData!.BodyAsString.Should().Be("x");
response1.Message.BodyData!.IsFuncUsed.Should().Be("Func<IRequestMessage, string>");
response2.Message.BodyData!.BodyAsString.Should().Be("y");
response2.Message.BodyData!.IsFuncUsed.Should().Be("Func<IRequestMessage, string>");
}
[Fact]
public async Task Response_ProvideResponse_WithBody_AsyncFunc_DynamicCode_Should_IsFuncUsed()
{
// Assign
var request = new RequestMessage(new UrlDetails("http://localhost/test"), "GET", ClientIp);
var data = new[] { "x", "y" };
var i = 0;
var responseBuilder = Response.Create()
.WithBody(_ =>
{
var value = data[i];
i++;
return Task.FromResult(value);
});
// Act (2x)
var response1 = await responseBuilder.ProvideResponseAsync(_mappingMock.Object, request, _settings).ConfigureAwait(false);
var response2 = await responseBuilder.ProvideResponseAsync(_mappingMock.Object, request, _settings).ConfigureAwait(false);
// Assert
response1.Message.BodyData!.BodyAsString.Should().Be("x");
response1.Message.BodyData!.IsFuncUsed.Should().Be("Func<IRequestMessage, Task<string>>");
response2.Message.BodyData!.BodyAsString.Should().Be("y");
response2.Message.BodyData!.IsFuncUsed.Should().Be("Func<IRequestMessage, Task<string>>");
}
[Fact]
public async Task Response_ProvideResponse_WithBody_FuncAsync()
{
// Assign
var request = new RequestMessage(new UrlDetails("http://localhost/test"), "GET", ClientIp);
var responseBuilder = Response.Create()
.WithStatusCode(500)
.WithHeader("H1", "X1")
.WithHeader("H2", "X2")
.WithBody(async req =>
{
await Task.Delay(1).ConfigureAwait(false);
return $"path: {req.Path}";
});
// Act
var response = await responseBuilder.ProvideResponseAsync(_mappingMock.Object, request, _settings).ConfigureAwait(false);
// Assert
Check.That(response.Message.BodyData.BodyAsString).IsEqualTo("path: /test");
Check.That(response.Message.BodyData.BodyAsBytes).IsNull();
Check.That(response.Message.BodyData.BodyAsJson).IsNull();
Check.That(response.Message.BodyData.Encoding.CodePage).Equals(Encoding.UTF8.CodePage);
Check.That(response.Message.StatusCode).IsEqualTo(500);
Check.That(response.Message.Headers["H1"].ToString()).IsEqualTo("X1");
Check.That(response.Message.Headers["H2"].ToString()).IsEqualTo("X2");
}
[Fact]

View File

@@ -2,6 +2,7 @@ using FluentAssertions;
using NFluent;
using WireMock.Logging;
using WireMock.Models;
using WireMock.Owin;
using WireMock.ResponseBuilders;
using WireMock.Serialization;
using WireMock.Types;
@@ -12,6 +13,15 @@ namespace WireMock.Net.Tests.Serialization;
public class LogEntryMapperTests
{
private readonly IWireMockMiddlewareOptions _options = new WireMockMiddlewareOptions();
private readonly LogEntryMapper _sut;
public LogEntryMapperTests()
{
_sut = new LogEntryMapper(_options);
}
[Fact]
public void LogEntryMapper_Map_LogEntry_Check_BodyTypeBytes()
{
@@ -39,7 +49,7 @@ public class LogEntryMapperTests
};
// Act
var result = LogEntryMapper.Map(logEntry);
var result = _sut.Map(logEntry);
// Assert
Check.That(result.Request.DetectedBodyType).IsEqualTo("Bytes");
@@ -74,7 +84,7 @@ public class LogEntryMapperTests
};
// Act
var result = LogEntryMapper.Map(logEntry);
var result = _sut.Map(logEntry);
// Assert
Check.That(result.Request.DetectedBodyType).IsNull();
@@ -111,10 +121,49 @@ public class LogEntryMapperTests
};
// Act
var result = LogEntryMapper.Map(logEntry);
var result = _sut.Map(logEntry);
// Assert
result.Response.FaultType.Should().Be("EMPTY_RESPONSE");
result.Response.FaultPercentage.Should().Be(0.5);
}
[Fact]
public void LogEntryMapper_Map_LogEntry_WhenFuncIsUsed_And_DoNotSaveDynamicResponseInLogEntry_Is_True_Should_NotSave_StringResponse()
{
// Assign
var options = new WireMockMiddlewareOptions
{
DoNotSaveDynamicResponseInLogEntry = true
};
var isFuncUsed = "Func<IRequestMessage, string>";
var logEntry = new LogEntry
{
RequestMessage = new RequestMessage(
new UrlDetails("http://localhost"),
"post",
"::1",
new BodyData
{
DetectedBodyType = BodyType.Bytes,
BodyAsBytes = new byte[] { 0 }
}
),
ResponseMessage = new ResponseMessage
{
BodyData = new BodyData
{
DetectedBodyType = BodyType.String,
BodyAsString = "test",
IsFuncUsed = isFuncUsed
}
}
};
// Act
var result = new LogEntryMapper(options).Map(logEntry);
// Assert
result.Response.Body.Should().Be(isFuncUsed);
}
}

View File

@@ -603,5 +603,31 @@ public class WireMockAdminApiTests
server.Stop();
}
[Fact]
public async Task IWireMockAdminApi_DeleteScenarioUsingDeleteAsync()
{
// Arrange
var name = "x";
var server = WireMockServer.StartWithAdminInterface();
var api = RestClient.For<IWireMockAdminApi>(server.Urls[0]);
// Act
var status = await api.DeleteScenarioAsync(name).ConfigureAwait(false);
status.Status.Should().Be("No scenario found by name 'x'.");
}
[Fact]
public async Task IWireMockAdminApi_DeleteScenarioUsingPostAsync()
{
// Arrange
var name = "x";
var server = WireMockServer.StartWithAdminInterface();
var api = RestClient.For<IWireMockAdminApi>(server.Urls[0]);
// Act
var status = await api.ResetScenarioAsync(name).ConfigureAwait(false);
status.Status.Should().Be("No scenario found by name 'x'.");
}
}
#endif

View File

@@ -117,7 +117,7 @@ public class WireMockServerProxyTests
}
// Assert
server.Mappings.Should().HaveCount(28);
server.Mappings.Should().HaveCount(30);
}
[Fact]

View File

@@ -81,7 +81,7 @@ namespace WireMock.Net.Tests
// Assert
server.Mappings.Should().NotBeNull();
server.Mappings.Should().HaveCount(26);
server.Mappings.Should().HaveCount(28);
server.Mappings.All(m => m.Priority == WireMockConstants.AdminPriority).Should().BeTrue();
}
@@ -100,9 +100,9 @@ namespace WireMock.Net.Tests
// Assert
server.Mappings.Should().NotBeNull();
server.Mappings.Should().HaveCount(27);
server.Mappings.Should().HaveCount(29);
server.Mappings.Count(m => m.Priority == WireMockConstants.AdminPriority).Should().Be(26);
server.Mappings.Count(m => m.Priority == WireMockConstants.AdminPriority).Should().Be(28);
server.Mappings.Count(m => m.Priority == WireMockConstants.ProxyPriority).Should().Be(1);
}

View File

@@ -89,7 +89,7 @@ public class WireMockServerWebhookTests
{
// Assign
var serverReceivingTheWebhook = WireMockServer.Start();
serverReceivingTheWebhook.Given(Request.Create().WithPath("x").UsingPost()).RespondWith(Response.Create().WithStatusCode(200));
serverReceivingTheWebhook.Given(Request.Create().WithPath("/x").UsingPost()).RespondWith(Response.Create().WithStatusCode(200));
// Act
var server = WireMockServer.Start();
@@ -126,6 +126,7 @@ public class WireMockServerWebhookTests
content.Should().Be("a-response");
serverReceivingTheWebhook.LogEntries.Should().HaveCount(1);
serverReceivingTheWebhook.LogEntries.First().MappingGuid.Should().NotBeNull();
server.Dispose();
serverReceivingTheWebhook.Dispose();