Compare commits

...

34 Commits

Author SHA1 Message Date
Stef Heyenrath
c333b8b263 1.4.27 2021-11-17 07:45:08 +00:00
Daniel L. Romero
32ddd48321 Support RequestBody (#678)
* Support RequestBody

* SerializeObject in the request matcher and apply JsonMatcher

* Refactor names

* Call to method TryGetContent

* Applied comments

* Changes applied!

* Comments applied V2!
2021-11-17 08:16:31 +01:00
Daniel L. Romero
260abf5275 Support enums in properties (#681)
* Support enums when a example is generated, priority is the enum

* Add null validation to Enum

* Refactor MapSchemaEnum

* Redactor merhod name

* Resolve merge conflict

* Check schema null

* Refactor GetRandom method
2021-11-16 20:38:34 +01:00
Daniel L. Romero
d3b2422ec1 Support examples in properties (#680)
* When the schema properti has an example then it uses the example

* Comments applied
2021-11-08 08:45:01 +01:00
Stef Heyenrath
823917a4ab Simplify test 'Response_ProvideResponseAsync_Handlebars_Humanizer' 2021-11-05 08:03:57 +00:00
Stef Heyenrath
1e968106d1 1.4.26 2021-11-04 07:10:56 +00:00
Daniel L. Romero
49e10ed20f Support basepath from servers (#675)
* Support basepath from servers

* Refactor BasePath

* Comments applied
2021-11-02 15:28:02 +01:00
Daniel L. Romero
c3ec3a66b3 Fix random generate data in url no spaces (#676)
* Fix random generate data in url no spaces

* Format class
2021-11-01 18:53:44 +01:00
Stef Heyenrath
5644491942 1.4.26 2021-10-30 09:26:33 +02:00
Stef Heyenrath
6e5e629525 "RandomDataGenerator.Net" Version="1.0.13" 2021-10-30 09:15:43 +02:00
Daniel L. Romero
77ee123340 Support examples random data generation (#673)
* Add Support Random Generation in Examples, this commit add the bool property DynamicExamples.

* Comments applied

* Remove  '#pragma warning disable 1591'
2021-10-30 09:13:24 +02:00
Daniel L. Romero
ae2a05e86b Improve method MapSchemaToObject to support array and object (#670) 2021-10-28 08:21:30 +02:00
Stef Heyenrath
0e06cf6346 1.4.25 2021-10-27 19:22:46 +02:00
Stef Heyenrath
e9db520cc3 Add TimeSettings (Start, End and TTL) (#661)
* time

* .

* UT

* using JetBrains.Annotations;
2021-10-27 18:57:13 +02:00
Stef Heyenrath
a0e661fae9 Support Schema Example and Support AllOf in definitions (#669)
* Support an Example within a Schema

* Support AllOf in definitions

* Refactor MapSchemaAllOfToObject method and add test files

* Include schema examples

* Refactor duplicate code, create method MapPropertyAsJObject

* Remove commented code

* Merge from master + update WireMock.Net.OpenApiParser.ConsoleApp

Co-authored-by: Daniel <raiga1234@gmail.com>
2021-10-27 09:40:12 +02:00
Stef Heyenrath
25666152bb Add JsonPartialWildcardMatcher (#667)
* JsonPartialWildcardMatcher

* .

* more tests
2021-10-27 08:16:18 +02:00
Stef Heyenrath
6f5eeb5359 Use IWireMockLogger in example 2021-10-26 18:40:34 +02:00
Stef Heyenrath
7ca2095576 Update logging (add version) 2021-10-24 10:44:39 +02:00
Daniel L. Romero
74edad517b Support Array in OpenApi Examples (#664) 2021-10-21 22:12:59 +02:00
Stef Heyenrath
6c65dfcff6 1.4.24 2021-10-20 20:40:02 +02:00
Stef Heyenrath
affe388e30 Add support for AzureAD authentication for REST admin interface (#637) 2021-10-20 16:39:51 +02:00
Stef Heyenrath
48b3e7a305 Add more tests for WithBody (Json and String) and WildcardMatcher 2021-10-19 09:28:52 +00:00
Stef Heyenrath
6194f4e460 Update NotNullOrEmptyMatcher to also implement IStringMatcher (#654)
* Update NotNullOrEmptyMatcher to also implement IStringMatcher

* NotNullOrEmptyMatcher_GetPatterns_Should_Return_EmptyArray

* 24
2021-10-15 12:59:03 +02:00
Stef Heyenrath
57cc616aa3 Implement PatternAsFile for StringMatcher (#651) 2021-10-15 08:54:12 +02:00
Daniel L. Romero
a2a581c84b Refactor method name MapHeaders and httpStatusCode (#649)
* Support edge case: first object, next an array.

* Add mapping to header parameters.

* Refactor the method MapHeadersParameters to MapHeaders

* Refactor method name 'cause is duplicate, only pass httpStatusCode when fail
2021-10-10 19:22:28 +02:00
Stef Heyenrath
e64ed45fcf Fix build from .NET452 and .NET461 example projects 2021-10-09 18:25:18 +02:00
Daniel L. Romero
267f0b7b6d Mapping headers in OpenAPI (#644)
* Support edge case: first object, next an array.

* Add mapping to header parameters.

* Refactor the method MapHeadersParameters to MapHeaders
2021-10-07 19:15:26 +02:00
Daniel L. Romero
34083d826e Support edge case: first object, next an array. (#643) 2021-10-06 17:18:46 +02:00
Stef Heyenrath
72eec7140b WireMock.FluentAssertions 2021-09-29 15:33:06 +00:00
Stef Heyenrath
6fa0f893e7 1.4.23 2021-09-27 21:06:05 +02:00
Stef Heyenrath
2ab075ee09 WireMock.Net.FluentAssertions : upgrade to latest FluentAssertions (#635)
* .

* #634

* fix UT
2021-09-27 21:03:54 +02:00
Stef Heyenrath
772398bea1 1.4.22 2021-09-22 11:39:36 +00:00
Stef Heyenrath
ba0b9d9fd8 Implement Random Delay (#633)
* implement random delays

* fixing CodeFactor issue

* fix code comments

* ...

* UT

Co-authored-by: Michael Yarichuk <michael.yarichuk@gmail.com>
2021-09-22 13:37:41 +02:00
Stef Heyenrath
c67bf75a4b Update FUNDING.yml 2021-09-16 22:25:23 +02:00
120 changed files with 5225 additions and 919 deletions

2
.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

View File

@@ -1,3 +1,35 @@
# 1.4.27 (17 November 2021)
- [#678](https://github.com/WireMock-Net/WireMock.Net/pull/678) - Support RequestBody [feature] contributed by [leolplex](https://github.com/leolplex)
- [#680](https://github.com/WireMock-Net/WireMock.Net/pull/680) - Support examples in properties [feature] contributed by [leolplex](https://github.com/leolplex)
- [#681](https://github.com/WireMock-Net/WireMock.Net/pull/681) - Support enums in properties [feature] contributed by [leolplex](https://github.com/leolplex)
# 1.4.26 (04 November 2021)
- [#670](https://github.com/WireMock-Net/WireMock.Net/pull/670) - Improve method MapSchemaToObject to support array and object [feature] contributed by [leolplex](https://github.com/leolplex)
- [#673](https://github.com/WireMock-Net/WireMock.Net/pull/673) - Support examples random data generation contributed by [leolplex](https://github.com/leolplex)
- [#675](https://github.com/WireMock-Net/WireMock.Net/pull/675) - Support basepath from servers contributed by [leolplex](https://github.com/leolplex)
- [#676](https://github.com/WireMock-Net/WireMock.Net/pull/676) - Fix random generate data in url no spaces [feature] contributed by [leolplex](https://github.com/leolplex)
# 1.4.25 (27 October 2021)
- [#661](https://github.com/WireMock-Net/WireMock.Net/pull/661) - Add TimeSettings (Start, End and TTL) [feature] contributed by [StefH](https://github.com/StefH)
- [#664](https://github.com/WireMock-Net/WireMock.Net/pull/664) - Support Array in OpenApiParser [feature] contributed by [leolplex](https://github.com/leolplex)
- [#667](https://github.com/WireMock-Net/WireMock.Net/pull/667) - Add JsonPartialWildcardMatcher [feature] contributed by [StefH](https://github.com/StefH)
- [#669](https://github.com/WireMock-Net/WireMock.Net/pull/669) - Support Schema Example and Support AllOf in definitions [feature] contributed by [StefH](https://github.com/StefH)
# 1.4.24 (20 October 2021)
- [#637](https://github.com/WireMock-Net/WireMock.Net/pull/637) - Add support for AzureAD authentication for REST admin interface [feature] contributed by [StefH](https://github.com/StefH)
- [#643](https://github.com/WireMock-Net/WireMock.Net/pull/643) - Support edge case: first object, next an array. [feature] contributed by [leolplex](https://github.com/leolplex)
- [#644](https://github.com/WireMock-Net/WireMock.Net/pull/644) - Mapping headers in OpenAPI [feature] contributed by [leolplex](https://github.com/leolplex)
- [#649](https://github.com/WireMock-Net/WireMock.Net/pull/649) - Refactor method name MapHeaders and httpStatusCode contributed by [leolplex](https://github.com/leolplex)
- [#651](https://github.com/WireMock-Net/WireMock.Net/pull/651) - Implement PatternAsFile for StringMatcher [feature] contributed by [StefH](https://github.com/StefH)
- [#654](https://github.com/WireMock-Net/WireMock.Net/pull/654) - Update NotNullOrEmptyMatcher to also implement IStringMatcher [feature] contributed by [StefH](https://github.com/StefH)
# 1.4.23 (27 September 2021)
- [#635](https://github.com/WireMock-Net/WireMock.Net/pull/635) - WireMock.Net.FluentAssertions : upgrade to latest FluentAssertions [feature] contributed by [StefH](https://github.com/StefH)
- [#634](https://github.com/WireMock-Net/WireMock.Net/issues/634) - Upgrade to latest FluentAssertions [bug]
# 1.4.22 (22 September 2021)
- [#633](https://github.com/WireMock-Net/WireMock.Net/pull/633) - Implement Random Delay [feature] contributed by [StefH](https://github.com/StefH)
# 1.4.21 (16 September 2021)
- [#631](https://github.com/WireMock-Net/WireMock.Net/pull/631) - Add WireMock.org RestClient [feature] contributed by [StefH](https://github.com/StefH)

View File

@@ -4,7 +4,7 @@
</PropertyGroup>
<PropertyGroup>
<VersionPrefix>1.4.21</VersionPrefix>
<VersionPrefix>1.4.27</VersionPrefix>
<PackageReleaseNotes>See CHANGELOG.md</PackageReleaseNotes>
<PackageIcon>WireMock.Net-Logo.png</PackageIcon>
<PackageProjectUrl>https://github.com/WireMock-Net/WireMock.Net</PackageProjectUrl>

View File

@@ -1,7 +1,7 @@
rem https://github.com/StefH/GitHubReleaseNotes
SET version=1.4.21
SET version=1.4.27
GitHubReleaseNotes --output CHANGELOG.md --skip-empty-releases --exclude-labels question invalid doc duplicate --version %version%
GitHubReleaseNotes --output CHANGELOG.md --skip-empty-releases --exclude-labels question invalid doc duplicate --version %version% --token %GH_TOKEN%
GitHubReleaseNotes --output PackageReleaseNotes.txt --skip-empty-releases --exclude-labels question invalid doc duplicate --template PackageReleaseNotes.template --version %version%
GitHubReleaseNotes --output PackageReleaseNotes.txt --skip-empty-releases --exclude-labels question invalid doc duplicate --template PackageReleaseNotes.template --version %version% --token %GH_TOKEN%

View File

@@ -1,4 +1,6 @@
# 1.4.21 (16 September 2021)
- #631 Add WireMock.org RestClient [feature]
# 1.4.27 (17 November 2021)
- #678 Support RequestBody [feature]
- #680 Support examples in properties [feature]
- #681 Support enums in properties [feature]
The full release notes can be found here: https://github.com/WireMock-Net/WireMock.Net/blob/master/CHANGELOG.md

View File

@@ -1,4 +1,4 @@
using System.Collections.Generic;
using System.Collections.Generic;
using System.IO;
using WireMock.Handlers;
@@ -80,6 +80,12 @@ namespace WireMock.Net.ConsoleApplication
return File.ReadAllBytes(AdjustPath(path));
}
/// <inheritdoc cref="IFileSystemHandler.ReadFileAsString"/>
public string ReadFileAsString(string path)
{
return File.ReadAllText(path);
}
/// <summary>
/// Adjusts the path to the MappingFolder.
/// </summary>

View File

@@ -1,4 +1,4 @@
using Newtonsoft.Json;
using Newtonsoft.Json;
using HandlebarsDotNet;
using System;
using System.Collections.Generic;
@@ -59,7 +59,10 @@ namespace WireMock.Net.ConsoleApplication
//},
PreWireMockMiddlewareInit = app => { System.Console.WriteLine($"PreWireMockMiddlewareInit : {app.GetType()}"); },
PostWireMockMiddlewareInit = app => { System.Console.WriteLine($"PostWireMockMiddlewareInit : {app.GetType()}"); },
#if USE_ASPNETCORE
AdditionalServiceRegistration = services => { System.Console.WriteLine($"AdditionalServiceRegistration : {services.GetType()}"); },
#endif
Logger = new WireMockConsoleLogger(),
HandlebarsRegistrationCallback = (handlebarsContext, fileSystemHandler) =>
@@ -74,9 +77,17 @@ namespace WireMock.Net.ConsoleApplication
System.Console.WriteLine("WireMockServer listening at {0}", string.Join(",", server.Urls));
server.SetBasicAuthentication("a", "b");
//server.SetAzureADAuthentication("6c2a4722-f3b9-4970-b8fc-fac41e29stef", "8587fde1-7824-42c7-8592-faf92b04stef");
// server.AllowPartialMapping();
server.Given(Request.Create().WithPath("/mypath").UsingPost())
.RespondWith(Response.Create()
.WithHeader("Content-Type", "application/json")
.WithBodyAsJson("{{JsonPath.SelectToken request.body \"..name\"}}")
.WithTransformer()
);
server
.Given(Request.Create().WithPath(p => p.Contains("x")).UsingGet())
.AtPriority(4)

View File

@@ -36,6 +36,9 @@
<ApplicationIcon>..\..\resources\WireMock.Net-Logo.ico</ApplicationIcon>
</PropertyGroup>
<ItemGroup>
<Reference Include="AnyOf, Version=0.2.0.0, Culture=neutral, PublicKeyToken=b35e6abbb527c6b1, processorArchitecture=MSIL">
<HintPath>..\..\packages\AnyOf.0.2.0\lib\net45\AnyOf.dll</HintPath>
</Reference>
<Reference Include="Handlebars, Version=2.0.4.0, Culture=neutral, PublicKeyToken=22225d0bf33cd661, processorArchitecture=MSIL">
<HintPath>..\..\packages\Handlebars.Net.2.0.4\lib\net452\Handlebars.dll</HintPath>
</Reference>

View File

@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="AnyOf" version="0.2.0" targetFramework="net452" />
<package id="Handlebars.Net" version="2.0.4" targetFramework="net452" />
<package id="Handlebars.Net.Helpers" version="2.1.1-preview-01" targetFramework="net452" />
<package id="Handlebars.Net.Helpers.Core" version="2.1.1-preview-01" targetFramework="net452" />

View File

@@ -35,6 +35,9 @@
<StartupObject>WireMock.Net.ConsoleApplication.Program</StartupObject>
</PropertyGroup>
<ItemGroup>
<Reference Include="AnyOf, Version=0.2.0.0, Culture=neutral, PublicKeyToken=b35e6abbb527c6b1, processorArchitecture=MSIL">
<HintPath>..\..\packages\AnyOf.0.2.0\lib\net45\AnyOf.dll</HintPath>
</Reference>
<Reference Include="Handlebars, Version=2.0.4.0, Culture=neutral, PublicKeyToken=22225d0bf33cd661, processorArchitecture=MSIL">
<HintPath>..\..\packages\Handlebars.Net.2.0.4\lib\net46\Handlebars.dll</HintPath>
</Reference>
@@ -47,6 +50,9 @@
<Reference Include="log4net, Version=2.0.12.0, Culture=neutral, PublicKeyToken=669e0ddf0bb1aa2a, processorArchitecture=MSIL">
<HintPath>..\..\packages\log4net.2.0.12\lib\net45\log4net.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Extensions.DependencyInjection.Abstractions, Version=2.2.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
<HintPath>..\..\packages\Microsoft.Extensions.DependencyInjection.Abstractions.2.2.0\lib\netstandard2.0\Microsoft.Extensions.DependencyInjection.Abstractions.dll</HintPath>
</Reference>
<Reference Include="Newtonsoft.Json, Version=11.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\..\packages\Newtonsoft.Json.11.0.2\lib\net45\Newtonsoft.Json.dll</HintPath>
</Reference>

View File

@@ -1,9 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="AnyOf" version="0.2.0" targetFramework="net461" />
<package id="Handlebars.Net" version="2.0.4" targetFramework="net461" />
<package id="Handlebars.Net.Helpers" version="2.1.1-preview-01" targetFramework="net461" />
<package id="Handlebars.Net.Helpers.Core" version="2.1.1-preview-01" targetFramework="net461" />
<package id="log4net" version="2.0.12" targetFramework="net461" />
<package id="Microsoft.Extensions.DependencyInjection.Abstractions" version="2.2.0" targetFramework="net461" />
<package id="Newtonsoft.Json" version="11.0.2" targetFramework="net461" />
<package id="SimMetrics.Net" version="1.0.5" targetFramework="net461" />
<package id="System.ValueTuple" version="4.3.0" targetFramework="net461" />

View File

@@ -39,6 +39,9 @@
<StartupObject>WireMock.Net.ConsoleApplication.Program</StartupObject>
</PropertyGroup>
<ItemGroup>
<Reference Include="AnyOf, Version=0.2.0.0, Culture=neutral, PublicKeyToken=b35e6abbb527c6b1, processorArchitecture=MSIL">
<HintPath>..\..\packages\AnyOf.0.2.0\lib\net45\AnyOf.dll</HintPath>
</Reference>
<Reference Include="Fare, Version=2.1.0.0, Culture=neutral, PublicKeyToken=ea68d375bf33a7c8, processorArchitecture=MSIL">
<HintPath>..\..\packages\Fare.2.1.1\lib\net35\Fare.dll</HintPath>
</Reference>

View File

@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="AnyOf" version="0.2.0" targetFramework="net472" />
<package id="Fare" version="2.1.1" targetFramework="net472" />
<package id="Handlebars.Net" version="2.0.4" targetFramework="net472" />
<package id="Handlebars.Net.Helpers" version="2.1.1-preview-01" targetFramework="net472" />

View File

@@ -0,0 +1,269 @@
{
"swagger": "2.0",
"info": {
"version": "1.0.0",
"title": "Swagger Petstore",
"description": "A sample API that uses a petstore as an example to demonstrate features in the swagger-2.0 specification",
"termsOfService": "http://swagger.io/terms/",
"contact": {
"name": "Swagger API Team"
},
"license": {
"name": "MIT"
}
},
"host": "petstore.swagger.io",
"basePath": "/api",
"schemes": [
"http"
],
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"paths": {
"/pets": {
"get": {
"description": "Returns all pets from the system that the user has access to",
"operationId": "findPets",
"produces": [
"application/json",
"application/xml",
"text/xml",
"text/html"
],
"parameters": [{
"name": "tags",
"in": "query",
"description": "tags to filter by",
"required": false,
"type": "array",
"items": {
"type": "string"
},
"collectionFormat": "csv"
},
{
"name": "limit",
"in": "query",
"description": "maximum number of results to return",
"required": false,
"type": "integer",
"format": "int32"
}
],
"responses": {
"200": {
"description": "pet response",
"schema": {
"example": [{
"name": "MEXAMPLE",
"tag": "MEXAMPLE",
"id": 9988
}, {
"name": "OtherExample",
"tag": "OtherExample",
"id": 8877
}],
"type": "array",
"items": {
"$ref": "#/definitions/Pet"
}
}
},
"default": {
"description": "unexpected error",
"schema": {
"$ref": "#/definitions/ErrorModel"
}
}
}
},
"post": {
"description": "Creates a new pet in the store. Duplicates are allowed",
"operationId": "addPet",
"produces": [
"application/json"
],
"parameters": [{
"name": "pet",
"in": "body",
"description": "Pet to add to the store",
"required": true,
"schema": {
"$ref": "#/definitions/NewPet"
}
}],
"responses": {
"200": {
"description": "pet response",
"schema": {
"$ref": "#/definitions/Pet"
}
},
"default": {
"description": "unexpected error",
"schema": {
"$ref": "#/definitions/ErrorModel"
}
}
}
}
},
"/pet": {
"get": {
"description": "Returns a pet from the system that the user has access to",
"operationId": "findPet",
"produces": [
"application/json",
"application/xml",
"text/xml",
"text/html"
],
"parameters": [{
"name": "tags",
"in": "query",
"description": "tags to filter by",
"required": false,
"type": "array",
"items": {
"type": "string"
},
"collectionFormat": "csv"
},
{
"name": "limit",
"in": "query",
"description": "maximum number of results to return",
"required": false,
"type": "integer",
"format": "int32"
}
],
"responses": {
"200": {
"description": "pet response",
"schema": {
"$ref": "#/definitions/Pet"
}
},
"default": {
"description": "unexpected error",
"schema": {
"$ref": "#/definitions/ErrorModel"
}
}
}
}
},
"/pets/{id}": {
"get": {
"description": "Returns a user based on a single ID, if the user does not have access to the pet",
"operationId": "findPetById",
"produces": [
"application/json",
"application/xml",
"text/xml",
"text/html"
],
"parameters": [{
"name": "id",
"in": "path",
"description": "ID of pet to fetch",
"required": true,
"type": "integer",
"format": "int64"
}],
"responses": {
"200": {
"description": "pet response",
"schema": {
"$ref": "#/definitions/Pet"
}
},
"default": {
"description": "unexpected error",
"schema": {
"$ref": "#/definitions/ErrorModel"
}
}
}
},
"delete": {
"description": "deletes a single pet based on the ID supplied",
"operationId": "deletePet",
"parameters": [{
"name": "id",
"in": "path",
"description": "ID of pet to delete",
"required": true,
"type": "integer",
"format": "int64"
}],
"responses": {
"204": {
"description": "pet deleted"
},
"default": {
"description": "unexpected error",
"schema": {
"$ref": "#/definitions/ErrorModel"
}
}
}
}
}
},
"definitions": {
"Pet": {
"type": "object",
"allOf": [{
"$ref": "#/definitions/NewPet"
},
{
"required": [
"id"
],
"properties": {
"id": {
"type": "integer",
"format": "int64"
}
}
}
]
},
"NewPet": {
"type": "object",
"required": [
"name"
],
"properties": {
"name": {
"type": "string"
},
"tag": {
"type": "string"
}
}
},
"ErrorModel": {
"type": "object",
"required": [
"code",
"message"
],
"properties": {
"code": {
"type": "integer",
"format": "int32"
},
"message": {
"type": "string"
}
}
}
}
}

View File

@@ -0,0 +1,732 @@
swagger: '2.0'
info:
description: 'This is a sample server Petstore server. Copied from https://github.com/swagger-api/swagger-codegen/blob/master/modules/swagger-codegen/src/test/resources/2_0/petstore.yaml.'
version: 1.0.0
title: Swagger Petstore
termsOfService: 'http://swagger.io/terms/'
contact:
email: apiteam@swagger.io
license:
name: Apache-2.0
url: 'http://www.apache.org/licenses/LICENSE-2.0.html'
host: petstore.swagger.io
basePath: /v2
tags:
- name: pet
description: Everything about your Pets
externalDocs:
description: Find out more
url: 'http://swagger.io'
- name: store
description: Access to Petstore orders
- name: user
description: Operations about user
externalDocs:
description: Find out more about our store
url: 'http://swagger.io'
schemes:
- http
paths:
/pet:
post:
tags:
- pet
summary: Add a new pet to the store
description: ''
operationId: addPet
consumes:
- application/json
- application/xml
produces:
- application/xml
- application/json
parameters:
- in: body
name: body
description: Pet object that needs to be added to the store
required: true
schema:
$ref: '#/definitions/Pet'
responses:
'405':
description: Invalid input
security:
- petstore_auth:
- 'write:pets'
- 'read:pets'
put:
tags:
- pet
summary: Update an existing pet
description: ''
operationId: updatePet
consumes:
- application/json
- application/xml
produces:
- application/xml
- application/json
parameters:
- in: body
name: body
description: Pet object that needs to be added to the store
required: true
schema:
$ref: '#/definitions/Pet'
responses:
'400':
description: Invalid ID supplied
'404':
description: Pet not found
'405':
description: Validation exception
security:
- petstore_auth:
- 'write:pets'
- 'read:pets'
/pet/findByStatus:
get:
tags:
- pet
summary: Finds Pets by status
description: Multiple status values can be provided with comma separated strings
operationId: findPetsByStatus
produces:
- application/xml
- application/json
parameters:
- name: status
in: query
description: Status values that need to be considered for filter
required: true
type: array
items:
type: string
enum:
- available
- pending
- sold
default: available
collectionFormat: csv
responses:
'200':
description: successful operation
schema:
type: array
items:
$ref: '#/definitions/Pet'
'400':
description: Invalid status value
security:
- petstore_auth:
- 'write:pets'
- 'read:pets'
/pet/findByTags:
get:
tags:
- pet
summary: Finds Pets by tags
description: 'Multiple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.'
operationId: findPetsByTags
produces:
- application/xml
- application/json
parameters:
- name: tags
in: query
description: Tags to filter by
required: true
type: array
items:
type: string
collectionFormat: csv
responses:
'200':
description: successful operation
schema:
type: array
items:
$ref: '#/definitions/Pet'
'400':
description: Invalid tag value
security:
- petstore_auth:
- 'write:pets'
- 'read:pets'
deprecated: true
'/pet/{petId}':
get:
tags:
- pet
summary: Find pet by ID
description: Returns a single pet
operationId: getPetById
produces:
- application/xml
- application/json
parameters:
- name: petId
in: path
description: ID of pet to return
required: true
type: integer
format: int64
responses:
'200':
description: successful operation
schema:
$ref: '#/definitions/Pet'
'400':
description: Invalid ID supplied
'404':
description: Pet not found
security:
- api_key: []
post:
tags:
- pet
summary: Updates a pet in the store with form data
description: ''
operationId: updatePetWithForm
consumes:
- application/x-www-form-urlencoded
produces:
- application/xml
- application/json
parameters:
- name: petId
in: path
description: ID of pet that needs to be updated
required: true
type: integer
format: int64
- name: name
in: formData
description: Updated name of the pet
required: false
type: string
- name: status
in: formData
description: Updated status of the pet
required: false
type: string
responses:
'405':
description: Invalid input
security:
- petstore_auth:
- 'write:pets'
- 'read:pets'
delete:
tags:
- pet
summary: Deletes a pet
description: ''
operationId: deletePet
produces:
- application/xml
- application/json
parameters:
- name: api_key
in: header
required: false
type: string
- name: petId
in: path
description: Pet id to delete
required: true
type: integer
format: int64
responses:
'200':
description: successful operation
'400':
description: Invalid pet value
security:
- petstore_auth:
- 'write:pets'
- 'read:pets'
'/pet/{petId}/uploadImage':
post:
tags:
- pet
summary: uploads an image
description: ''
operationId: uploadFile
consumes:
- multipart/form-data
produces:
- application/json
parameters:
- name: petId
in: path
description: ID of pet to update
required: true
type: integer
format: int64
- name: additionalMetadata
in: formData
description: Additional data to pass to server
required: false
type: string
- name: file
in: formData
description: file to upload
required: false
type: file
responses:
'200':
description: successful operation
schema:
$ref: '#/definitions/ApiResponse'
security:
- petstore_auth:
- 'write:pets'
- 'read:pets'
/store/inventory:
get:
tags:
- store
summary: Returns pet inventories by status
description: Returns a map of status codes to quantities
operationId: getInventory
produces:
- application/json
parameters: []
responses:
'200':
description: successful operation
schema:
type: object
additionalProperties:
type: integer
format: int32
security:
- api_key: []
/store/order:
post:
tags:
- store
summary: Place an order for a pet
description: ''
operationId: placeOrder
produces:
- application/xml
- application/json
parameters:
- in: body
name: body
description: order placed for purchasing the pet
required: true
schema:
$ref: '#/definitions/Order'
responses:
'200':
description: successful operation
schema:
$ref: '#/definitions/Order'
'400':
description: Invalid Order
'/store/order/{orderId}':
get:
tags:
- store
summary: Find purchase order by ID
description: 'For valid response try integer IDs with value <= 5 or > 10. Other values will generated exceptions'
operationId: getOrderById
produces:
- application/xml
- application/json
parameters:
- name: orderId
in: path
description: ID of pet that needs to be fetched
required: true
type: integer
maximum: 5
minimum: 1
format: int64
responses:
'200':
description: successful operation
schema:
$ref: '#/definitions/Order'
'400':
description: Invalid ID supplied
'404':
description: Order not found
delete:
tags:
- store
summary: Delete purchase order by ID
description: For valid response try integer IDs with value < 1000. Anything above 1000 or nonintegers will generate API errors
operationId: deleteOrder
produces:
- application/xml
- application/json
parameters:
- name: orderId
in: path
description: ID of the order that needs to be deleted
required: true
type: string
responses:
'400':
description: Invalid ID supplied
'404':
description: Order not found
/user:
post:
tags:
- user
summary: Create user
description: This can only be done by the logged in user.
operationId: createUser
produces:
- application/xml
- application/json
parameters:
- in: body
name: body
description: Created user object
required: true
schema:
$ref: '#/definitions/User'
responses:
default:
description: successful operation
/user/createWithArray:
post:
tags:
- user
summary: Creates list of users with given input array
description: ''
operationId: createUsersWithArrayInput
produces:
- application/xml
- application/json
parameters:
- in: body
name: body
description: List of user object
required: true
schema:
type: array
items:
$ref: '#/definitions/User'
responses:
default:
description: successful operation
/user/createWithList:
post:
tags:
- user
summary: Creates list of users with given input array
description: ''
operationId: createUsersWithListInput
produces:
- application/xml
- application/json
parameters:
- in: body
name: body
description: List of user object
required: true
schema:
type: array
items:
$ref: '#/definitions/User'
responses:
default:
description: successful operation
/user/login:
get:
tags:
- user
summary: Logs user into the system
description: ''
operationId: loginUser
produces:
- application/xml
- application/json
parameters:
- name: username
in: query
description: The user name for login
required: true
type: string
- name: password
in: query
description: The password for login in clear text
required: true
type: string
responses:
'200':
description: successful operation
schema:
type: string
headers:
X-Rate-Limit:
type: integer
format: int32
description: calls per hour allowed by the user
X-Expires-After:
type: string
format: date-time
description: date in UTC when toekn expires
'400':
description: Invalid username/password supplied
/user/logout:
get:
tags:
- user
summary: Logs out current logged in user session
description: ''
operationId: logoutUser
produces:
- application/xml
- application/json
parameters: []
responses:
default:
description: successful operation
'/user/{username}':
get:
tags:
- user
summary: Get user by user name
description: ''
operationId: getUserByName
produces:
- application/xml
- application/json
parameters:
- name: username
in: path
description: 'The name that needs to be fetched. Use user1 for testing.'
required: true
type: string
responses:
'200':
description: successful operation
schema:
$ref: '#/definitions/User'
'400':
description: Invalid username supplied
'404':
description: User not found
put:
tags:
- user
summary: Updated user
description: This can only be done by the logged in user.
operationId: updateUser
produces:
- application/xml
- application/json
parameters:
- name: username
in: path
description: name that need to be deleted
required: true
type: string
- in: body
name: body
description: Updated user object
required: true
schema:
$ref: '#/definitions/User'
responses:
'400':
description: Invalid user supplied
'404':
description: User not found
delete:
tags:
- user
summary: Delete user
description: This can only be done by the logged in user.
operationId: deleteUser
produces:
- application/xml
- application/json
parameters:
- name: username
in: path
description: The name that needs to be deleted
required: true
type: string
responses:
'400':
description: Invalid username supplied
'404':
description: User not found
securityDefinitions:
petstore_auth:
type: oauth2
authorizationUrl: 'http://petstore.swagger.io/api/oauth/dialog'
flow: implicit
scopes:
'write:pets': modify pets in your account
'read:pets': read your pets
api_key:
type: apiKey
name: api_key
in: header
definitions:
Order:
title: Pet Order
description: An order for a pets from the pet store
type: object
properties:
id:
type: integer
format: int64
petId:
type: integer
format: int64
quantity:
type: integer
format: int32
shipDate:
type: string
format: date-time
status:
type: string
description: Order Status
enum:
- placed
- approved
- delivered
complete:
type: boolean
default: false
xml:
name: Order
Category:
title: Pet category
description: A category for a pet
type: object
properties:
id:
type: integer
format: int64
name:
type: string
xml:
name: Category
User:
title: a User
description: A User who is purchasing from the pet store
type: object
properties:
id:
type: integer
format: int64
username:
type: string
firstName:
type: string
lastName:
type: string
email:
type: string
password:
type: string
phone:
type: string
userStatus:
type: integer
format: int32
description: User Status
xml:
name: User
Tag:
title: Pet Tag
description: A tag for a pet
type: object
properties:
id:
type: integer
format: int64
name:
type: string
xml:
name: Tag
Pet:
title: a Pet
description: A pet for sale in the pet store
type: object
required:
- name
- photoUrls
properties:
id:
type: integer
format: int64
category:
$ref: '#/definitions/Category'
name:
type: string
example: doggie
photoUrls:
type: array
xml:
name: photoUrl
wrapped: true
items:
type: string
tags:
type: array
xml:
name: tag
wrapped: true
items:
$ref: '#/definitions/Tag'
status:
type: string
description: pet status in the store
enum:
- available
- pending
- sold
xml:
name: Pet
ApiResponse:
title: An uploaded response
description: Describes the result of uploading an image resource
type: object
properties:
code:
type: integer
format: int32
type:
type: string
message:
type: string
#issue: https://github.com/swagger-api/swagger-codegen/issues/7980
Amount:
type: object
description: >
some description
properties:
value:
format: double
type: number
minimum: 0.01
maximum: 1000000000000000
description: >
some description
currency:
$ref: '#/definitions/Currency'
required:
- value
- currency
Currency:
type: string
pattern: '^[A-Z]{3,3}$'
description: >
some description
externalDocs:
description: Find out more about Swagger
url: 'http://swagger.io'

View File

@@ -0,0 +1,109 @@
openapi: "3.0.0"
info:
version: 1.0.0
title: Swagger Petstore
license:
name: MIT
servers:
- url: http://petstore.swagger.io/v1
paths:
/pets:
get:
summary: List all pets
operationId: listPets
tags:
- pets
parameters:
- name: limit
in: query
description: How many items to return at one time (max 100)
required: false
schema:
type: integer
format: int32
responses:
200:
description: An paged array of pets
headers:
x-next:
description: A link to the next page of responses
schema:
type: string
content:
application/json:
schema:
$ref: "#/components/schemas/Pets"
default:
description: unexpected error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
post:
summary: Create a pet
operationId: createPets
tags:
- pets
responses:
201:
description: Null response
default:
description: unexpected error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
/pets/{petId}:
get:
summary: Info for a specific pet
operationId: showPetById
tags:
- pets
parameters:
- name: petId
in: path
required: true
description: The id of the pet to retrieve
schema:
type: string
responses:
200:
description: Expected response to a valid request
content:
application/json:
schema:
$ref: "#/components/schemas/Pets"
default:
description: unexpected error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
components:
schemas:
Pet:
required:
- id
- name
properties:
id:
type: integer
format: int64
name:
type: string
tag:
type: string
Pets:
type: array
items:
$ref: "#/components/schemas/Pet"
Error:
required:
- code
- message
properties:
code:
type: integer
format: int32
message:
type: string

View File

@@ -0,0 +1,96 @@
openapi: 3.0.1
info:
title: API_Test
version: v1
paths:
/WeatherForecast:
get:
tags:
- WeatherForecast
parameters:
- in: "header"
name: X-Correlation-ID
type: "string"
required: true
responses:
'200':
description: Success
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/WeatherForecast'
/leolplex:
get:
tags:
- WeatherForecast
parameters:
- in: "header"
name: X-Correlation-ID
type: "string"
required: true
responses:
'200':
description: Success
content:
application/json:
example:
- date: 2021-10-21T09:13:00.552+00:00
temperatureC: 111
temperatureF: 111
summary: Just-summary
- date: 2021-10-21T09:13:00.000+00:00
temperatureC: 222
temperatureF: 222
summary: Just-summary2
schema:
type: array
items:
$ref: '#/components/schemas/WeatherForecast'
/exampleop:
get:
responses:
"200":
description: OK
content:
application/json:
example:
id: 1
name: get food
completed: false
schema:
properties:
id:
type: integer
name:
type: string
completed:
type: boolean
completed_at:
type: string
format: date-time
nullable: true
required:
- id
- name
- completed
components:
schemas:
WeatherForecast:
type: object
properties:
date:
type: string
format: date-time
temperatureC:
type: integer
format: int32
temperatureF:
type: integer
format: int32
readOnly: true
summary:
type: string
nullable: true
additionalProperties: false

View File

@@ -1,5 +1,3 @@
using Microsoft.OpenApi.Readers;
using Newtonsoft.Json;
using System;
using System.IO;
@@ -7,15 +5,23 @@ namespace WireMock.Net.OpenApiParser.ConsoleApp
{
class Program
{
private static readonly JsonSerializerSettings Settings = new JsonSerializerSettings
{
NullValueHandling = NullValueHandling.Ignore,
Formatting = Formatting.Indented
};
private const string Folder = "OpenApiFiles";
static void Main(string[] args)
{
Run.RunServer("petstore-openapi3.json");
var serverOpenAPIExamples = Run.RunServer(Path.Combine(Folder, "openAPIExamples.yaml"), "http://localhost:9091/");
var serverPetstore_V2_json = Run.RunServer(Path.Combine(Folder, "Swagger_Petstore_V2.0.json"), "http://localhost:9092/");
var serverPetstore_V2_yaml = Run.RunServer(Path.Combine(Folder, "Swagger_Petstore_V2.0.yaml"), "http://localhost:9093/");
var serverPetstore_V300_yaml = Run.RunServer(Path.Combine(Folder, "Swagger_Petstore_V3.0.0.yaml"), "http://localhost:9094/");
var serverPetstore_V302_json = Run.RunServer(Path.Combine(Folder, "Swagger_Petstore_V3.0.2.json"), "http://localhost:9095/");
Console.WriteLine("Press any key to stop the servers");
Console.ReadKey();
serverOpenAPIExamples.Stop();
serverPetstore_V2_json.Stop();
serverPetstore_V2_yaml.Stop();
serverPetstore_V300_yaml.Stop();
serverPetstore_V302_json.Stop();
//IWireMockOpenApiParser parser = new WireMockOpenApiParser();

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using WireMock.Admin.Mappings;
@@ -13,14 +13,12 @@ namespace WireMock.Net.OpenApiParser.ConsoleApp
{
public static class Run
{
public static void RunServer(string path)
public static WireMockServer RunServer(string path, string url)
{
string url1 = "http://localhost:9091/";
var server = WireMockServer.Start(new WireMockServerSettings
{
AllowCSharpCodeMatcher = true,
Urls = new[] { url1 },
Urls = new[] { url },
StartAdminInterface = true,
ReadStaticMappings = false,
WatchStaticMappings = false,
@@ -38,9 +36,7 @@ namespace WireMock.Net.OpenApiParser.ConsoleApp
server.WithMappingFromOpenApiFile(path, settings, out var diag);
Console.WriteLine("Press any key to stop the server");
System.Console.ReadKey();
server.Stop();
return server;
}
public static void RunServer(IEnumerable<MappingModel> mappings)

View File

@@ -15,6 +15,21 @@
<None Update="infura.yaml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="OpenApiFiles\openAPIExamples.yaml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="OpenApiFiles\Swagger_Petstore_V2.0.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="OpenApiFiles\Swagger_Petstore_V2.0.yaml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="OpenApiFiles\Swagger_Petstore_V3.0.0.yaml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="OpenApiFiles\Swagger_Petstore_V3.0.2.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="petstore-openapi3.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>

View File

@@ -1,4 +1,5 @@
using System;
using System;
using WireMock.Models;
namespace WireMock.Admin.Mappings
{
@@ -13,6 +14,11 @@ namespace WireMock.Admin.Mappings
/// </summary>
public Guid? Guid { get; set; }
/// <summary>
/// Gets or sets the TimeSettings when which this mapping should be used.
/// </summary>
public TimeSettingsModel TimeSettings { get; set; }
/// <summary>
/// The unique title.
/// </summary>

View File

@@ -1,4 +1,4 @@
namespace WireMock.Admin.Mappings
namespace WireMock.Admin.Mappings
{
/// <summary>
/// MatcherModel
@@ -12,15 +12,20 @@
public string Name { get; set; }
/// <summary>
/// Gets or sets the pattern. Can be a string (default) or an object;
/// Gets or sets the pattern. Can be a string (default) or an object.
/// </summary>
public object Pattern { get; set; }
/// <summary>
/// Gets or sets the patterns. Can be array of strings (default) or an array of objects;
/// Gets or sets the patterns. Can be array of strings (default) or an array of objects.
/// </summary>
public object[] Patterns { get; set; }
/// <summary>
/// Gets or sets the pattern as a file.
/// </summary>
public string PatternAsFile { get; set; }
/// <summary>
/// Gets or sets the ignore case.
/// </summary>

View File

@@ -83,6 +83,16 @@ namespace WireMock.Admin.Mappings
/// </summary>
public int? Delay { get; set; }
/// <summary>
/// Gets or sets the minimum random delay in milliseconds.
/// </summary>
public int? MinimumRandomDelay { get; set; }
/// <summary>
/// Gets or sets the maximum random delay in milliseconds.
/// </summary>
public int? MaximumRandomDelay { get; set; }
/// <summary>
/// Gets or sets the Proxy URL.
/// </summary>

View File

@@ -0,0 +1,26 @@
using System;
namespace WireMock.Models
{
/// <summary>
/// TimeSettingsModel: Start, End and TTL
/// </summary>
[FluentBuilder.AutoGenerateBuilder]
public class TimeSettingsModel
{
/// <summary>
/// Gets or sets the DateTime from which this mapping should be used. In case this is not defined, it's used (default behavior).
/// </summary>
public DateTime? Start { get; set; }
/// <summary>
/// Gets or sets the DateTime from until this mapping should be used. In case this is not defined, it's used forever (default behavior).
/// </summary>
public DateTime? End { get; set; }
/// <summary>
/// Gets or sets the TTL (Time To Live) in seconds for this mapping. In case this is not defined, it's used (default behavior).
/// </summary>
public int? TTL { get; set; }
}
}

View File

@@ -1,4 +1,4 @@
using JetBrains.Annotations;
using JetBrains.Annotations;
using System.Collections.Generic;
namespace WireMock.Handlers
@@ -89,5 +89,12 @@ namespace WireMock.Handlers
/// <param name="filename">The filename.</param>
/// <returns>The file content as bytes.</returns>
byte[] ReadFile([NotNull] string filename);
/// <summary>
/// Read a file as string.
/// </summary>
/// <param name="filename">The filename.</param>
/// <returns>The file content as a string.</returns>
string ReadFileAsString([NotNull] string filename);
}
}

View File

@@ -0,0 +1,25 @@
using System;
namespace WireMock.Models
{
/// <summary>
/// TimeSettings: Start, End and TTL
/// </summary>
public interface ITimeSettings
{
/// <summary>
/// Gets or sets the DateTime from which this mapping should be used. In case this is not defined, it's used (default behavior).
/// </summary>
DateTime? Start { get; set; }
/// <summary>
/// Gets or sets the DateTime from until this mapping should be used. In case this is not defined, it's used forever (default behavior).
/// </summary>
DateTime? End { get; set; }
/// <summary>
/// Gets or sets the TTL (Time To Live) in seconds for this mapping. In case this is not defined, it's used (default behavior).
/// </summary>
int? TTL { get; set; }
}
}

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using JetBrains.Annotations;
@@ -104,9 +104,9 @@ namespace WireMock.Server
void ReadStaticMappings([CanBeNull] string folder = null);
/// <summary>
/// Removes the basic authentication.
/// Removes the authentication.
/// </summary>
void RemoveBasicAuthentication();
void RemoveAuthentication();
/// <summary>
/// Resets LogEntries and Mappings.
@@ -134,6 +134,13 @@ namespace WireMock.Server
/// <param name="folder">The optional folder. If not defined, use {CurrentFolder}/__admin/mappings</param>
void SaveStaticMappings([CanBeNull] string folder = null);
/// <summary>
/// Sets the basic authentication.
/// </summary>
/// <param name="tenant">The Tenant.</param>
/// <param name="audience">The Audience or Resource.</param>
void SetAzureADAuthentication([NotNull] string tenant, [NotNull] string audience);
/// <summary>
/// Sets the basic authentication.
/// </summary>

View File

@@ -36,7 +36,7 @@ namespace WireMock.FluentAssertions
[CustomAssertion]
public AndConstraint<WireMockAssertions> WithHeader(string expectedKey, string value, string because = "", params object[] becauseArgs)
=> WithHeader(expectedKey, new[] {value}, because, becauseArgs);
=> WithHeader(expectedKey, new[] { value }, because, becauseArgs);
[CustomAssertion]
public AndConstraint<WireMockAssertions> WithHeader(string expectedKey, string[] expectedValues, string because = "", params object[] becauseArgs)
@@ -69,7 +69,7 @@ namespace WireMock.FluentAssertions
return new AndConstraint<WireMockAssertions>(this);
}
[CustomAssertion]
public AndConstraint<WireMockAssertions> AtUrl(string url, string because = "", params object[] becauseArgs)
{
@@ -85,10 +85,10 @@ namespace WireMock.FluentAssertions
.FailWith(
"Expected {context:wiremockserver} to have been called at address matching the url {0}{reason}, but didn't find it among the calls to {1}.",
_ => url, requests => requests.Select(request => request.Url));
return new AndConstraint<WireMockAssertions>(this);
}
[CustomAssertion]
public AndConstraint<WireMockAssertions> WithProxyUrl(string proxyUrl, string because = "", params object[] becauseArgs)
{
@@ -104,10 +104,10 @@ namespace WireMock.FluentAssertions
.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));
return new AndConstraint<WireMockAssertions>(this);
}
[CustomAssertion]
public AndConstraint<WireMockAssertions> FromClientIP(string clientIP, string because = "", params object[] becauseArgs)
{
@@ -123,7 +123,7 @@ namespace WireMock.FluentAssertions
.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));
return new AndConstraint<WireMockAssertions>(this);
}
}

View File

@@ -4,23 +4,39 @@ using WireMock.Server;
// ReSharper disable once CheckNamespace
namespace WireMock.FluentAssertions
{
/// <summary>
/// Contains a number of methods to assert that the <see cref="IWireMockServer"/> is in the expected state.
/// </summary>
public class WireMockReceivedAssertions : ReferenceTypeAssertions<IWireMockServer, WireMockReceivedAssertions>
{
public WireMockReceivedAssertions(IWireMockServer server)
/// <summary>
/// Create a WireMockReceivedAssertions.
/// </summary>
/// <param name="server">The <see cref="IWireMockServer"/>.</param>
public WireMockReceivedAssertions(IWireMockServer server) : base(server)
{
Subject = server;
}
/// <summary>
/// Asserts if <see cref="IWireMockServer"/> has received a call.
/// </summary>
/// <returns><see cref="WireMockAssertions"/></returns>
public WireMockAssertions HaveReceivedACall()
{
return new WireMockAssertions(Subject, null);
}
/// <summary>
/// Asserts if <see cref="IWireMockServer"/> has received n-calls.
/// </summary>
/// <param name="callsCount"></param>
/// <returns><see cref="WireMockANumberOfCallsAssertions"/></returns>
public WireMockANumberOfCallsAssertions HaveReceived(int callsCount)
{
return new WireMockANumberOfCallsAssertions(Subject, callsCount);
}
/// <inheritdoc cref="ReferenceTypeAssertions{IWireMockServer, WireMockReceivedAssertions}.Identifier"/>
protected override string Identifier => "wiremockserver";
}
}

View File

@@ -3,8 +3,16 @@ using WireMock.Server;
// ReSharper disable once CheckNamespace
namespace WireMock.FluentAssertions
{
/// <summary>
/// Contains extension methods for custom assertions in unit tests.
/// </summary>
public static class WireMockExtensions
{
/// <summary>
/// Returns a <see cref="WireMockReceivedAssertions"/> object that can be used to assert the current <see cref="IWireMockServer"/>.
/// </summary>
/// <param name="instance">The WireMockServer</param>
/// <returns><see cref="WireMockReceivedAssertions"/></returns>
public static WireMockReceivedAssertions Should(this IWireMockServer instance)
{
return new WireMockReceivedAssertions(instance);

View File

@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Description>FluentAssertions extensions for WireMock.Net</Description>
@@ -29,13 +29,20 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="FluentAssertions" Version="5.10.3" />
<PackageReference Include="JetBrains.Annotations" Version="2020.1.0" PrivateAssets="All" />
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net451' or '$(TargetFramework)' == 'netstandard1.3'">
<PackageReference Include="FluentAssertions" Version="5.10.3" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' != 'net451' and '$(TargetFramework)' != 'netstandard1.3'">
<PackageReference Include="FluentAssertions" Version="6.1.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\WireMock.Net.Abstractions\WireMock.Net.Abstractions.csproj" />
</ItemGroup>
</Project>
</Project>

View File

@@ -1,225 +1,228 @@
using System;
using System.Linq;
using System;
using System.Linq;
using System.Reflection;
using System.Text;
using JetBrains.Annotations;
using Newtonsoft.Json.Linq;
using WireMock.Exceptions;
using WireMock.Validation;
namespace WireMock.Matchers
{
/// <summary>
/// CSharpCode / CS-Script Matcher
/// </summary>
/// <inheritdoc cref="ICSharpCodeMatcher"/>
internal class CSharpCodeMatcher : ICSharpCodeMatcher
{
private const string TemplateForIsMatchWithString = "public class CodeHelper {{ public bool IsMatch(string it) {{ {0} }} }}";
private const string TemplateForIsMatchWithDynamic = "public class CodeHelper {{ public bool IsMatch(dynamic it) {{ {0} }} }}";
private readonly string[] _usings =
{
"System",
"System.Linq",
"System.Collections.Generic",
"Microsoft.CSharp",
"Newtonsoft.Json.Linq"
};
public MatchBehaviour MatchBehaviour { get; }
/// <inheritdoc cref="IMatcher.ThrowException"/>
public bool ThrowException { get; }
private readonly string[] _patterns;
/// <summary>
/// Initializes a new instance of the <see cref="CSharpCodeMatcher"/> class.
/// </summary>
/// <param name="patterns">The patterns.</param>
public CSharpCodeMatcher([NotNull] params string[] patterns) : this(MatchBehaviour.AcceptOnMatch, patterns)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="CSharpCodeMatcher"/> class.
/// </summary>
/// <param name="matchBehaviour">The match behaviour.</param>
/// <param name="patterns">The patterns.</param>
public CSharpCodeMatcher(MatchBehaviour matchBehaviour, [NotNull] params string[] patterns)
{
Check.NotNull(patterns, nameof(patterns));
MatchBehaviour = matchBehaviour;
ThrowException = false;
_patterns = patterns;
}
public double IsMatch(string input)
{
return IsMatchInternal(input);
}
public double IsMatch(object input)
{
return IsMatchInternal(input);
}
public double IsMatchInternal(object input)
{
double match = MatchScores.Mismatch;
if (input != null)
{
match = MatchScores.ToScore(_patterns.Select(pattern => IsMatch(input, pattern)));
}
return MatchBehaviourHelper.Convert(MatchBehaviour, match);
}
private bool IsMatch(dynamic input, string pattern)
{
bool isMatchWithString = input is string;
var inputValue = isMatchWithString ? input : JObject.FromObject(input);
string source = GetSourceForIsMatchWithString(pattern, isMatchWithString);
object result = null;
#if (NET451 || NET452)
var compilerParams = new System.CodeDom.Compiler.CompilerParameters
{
GenerateInMemory = true,
GenerateExecutable = false,
ReferencedAssemblies =
{
"System.dll",
"System.Core.dll",
"Microsoft.CSharp.dll",
"Newtonsoft.Json.dll"
}
};
using (var codeProvider = new Microsoft.CSharp.CSharpCodeProvider())
{
var compilerResults = codeProvider.CompileAssemblyFromSource(compilerParams, source);
if (compilerResults.Errors.Count != 0)
{
var errors = from System.CodeDom.Compiler.CompilerError er in compilerResults.Errors select er.ToString();
throw new WireMockException(string.Join(", ", errors));
}
object helper = compilerResults.CompiledAssembly.CreateInstance("CodeHelper");
if (helper == null)
{
throw new WireMockException("CSharpCodeMatcher: Unable to create instance from WireMock.CodeHelper");
}
var methodInfo = helper.GetType().GetMethod("IsMatch");
if (methodInfo == null)
{
throw new WireMockException("CSharpCodeMatcher: Unable to find method 'IsMatch' in WireMock.CodeHelper");
}
try
{
result = methodInfo.Invoke(helper, new[] { inputValue });
}
catch (Exception ex)
{
throw new WireMockException("CSharpCodeMatcher: Unable to call method 'IsMatch' in WireMock.CodeHelper", ex);
}
}
#elif (NET46 || NET461)
dynamic script;
try
{
script = CSScriptLibrary.CSScript.Evaluator.CompileCode(source).CreateObject("*");
}
catch (Exception ex)
{
throw new WireMockException("CSharpCodeMatcher: Unable to create compiler for WireMock.CodeHelper", ex);
}
try
{
result = script.IsMatch(inputValue);
}
catch (Exception ex)
{
throw new WireMockException("CSharpCodeMatcher: Problem calling method 'IsMatch' in WireMock.CodeHelper", ex);
}
#elif (NETSTANDARD2_0 || NETSTANDARD2_1 || NETCOREAPP3_1 || NET5_0)
Assembly assembly;
try
using AnyOfTypes;
using JetBrains.Annotations;
using Newtonsoft.Json.Linq;
using WireMock.Exceptions;
using WireMock.Extensions;
using WireMock.Models;
using WireMock.Validation;
namespace WireMock.Matchers
{
/// <summary>
/// CSharpCode / CS-Script Matcher
/// </summary>
/// <inheritdoc cref="ICSharpCodeMatcher"/>
internal class CSharpCodeMatcher : ICSharpCodeMatcher
{
private const string TemplateForIsMatchWithString = "public class CodeHelper {{ public bool IsMatch(string it) {{ {0} }} }}";
private const string TemplateForIsMatchWithDynamic = "public class CodeHelper {{ public bool IsMatch(dynamic it) {{ {0} }} }}";
private readonly string[] _usings =
{
"System",
"System.Linq",
"System.Collections.Generic",
"Microsoft.CSharp",
"Newtonsoft.Json.Linq"
};
public MatchBehaviour MatchBehaviour { get; }
/// <inheritdoc cref="IMatcher.ThrowException"/>
public bool ThrowException { get; }
private readonly AnyOf<string, StringPattern>[] _patterns;
/// <summary>
/// Initializes a new instance of the <see cref="CSharpCodeMatcher"/> class.
/// </summary>
/// <param name="patterns">The patterns.</param>
public CSharpCodeMatcher([NotNull] params AnyOf<string, StringPattern>[] patterns) : this(MatchBehaviour.AcceptOnMatch, patterns)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="CSharpCodeMatcher"/> class.
/// </summary>
/// <param name="matchBehaviour">The match behaviour.</param>
/// <param name="patterns">The patterns.</param>
public CSharpCodeMatcher(MatchBehaviour matchBehaviour, [NotNull] params AnyOf<string, StringPattern>[] patterns)
{
Check.NotNull(patterns, nameof(patterns));
MatchBehaviour = matchBehaviour;
ThrowException = false;
_patterns = patterns;
}
public double IsMatch(string input)
{
return IsMatchInternal(input);
}
public double IsMatch(object input)
{
return IsMatchInternal(input);
}
public double IsMatchInternal(object input)
{
double match = MatchScores.Mismatch;
if (input != null)
{
assembly = CSScriptLib.CSScript.Evaluator.CompileCode(source);
}
catch (Exception ex)
{
throw new WireMockException($"CSharpCodeMatcher: Unable to compile code `{source}` for WireMock.CodeHelper", ex);
}
dynamic script;
match = MatchScores.ToScore(_patterns.Select(pattern => IsMatch(input, pattern.GetPattern())));
}
return MatchBehaviourHelper.Convert(MatchBehaviour, match);
}
private bool IsMatch(dynamic input, string pattern)
{
bool isMatchWithString = input is string;
var inputValue = isMatchWithString ? input : JObject.FromObject(input);
string source = GetSourceForIsMatchWithString(pattern, isMatchWithString);
object result = null;
#if (NET451 || NET452)
var compilerParams = new System.CodeDom.Compiler.CompilerParameters
{
GenerateInMemory = true,
GenerateExecutable = false,
ReferencedAssemblies =
{
"System.dll",
"System.Core.dll",
"Microsoft.CSharp.dll",
"Newtonsoft.Json.dll"
}
};
using (var codeProvider = new Microsoft.CSharp.CSharpCodeProvider())
{
var compilerResults = codeProvider.CompileAssemblyFromSource(compilerParams, source);
if (compilerResults.Errors.Count != 0)
{
var errors = from System.CodeDom.Compiler.CompilerError er in compilerResults.Errors select er.ToString();
throw new WireMockException(string.Join(", ", errors));
}
object helper = compilerResults.CompiledAssembly.CreateInstance("CodeHelper");
if (helper == null)
{
throw new WireMockException("CSharpCodeMatcher: Unable to create instance from WireMock.CodeHelper");
}
var methodInfo = helper.GetType().GetMethod("IsMatch");
if (methodInfo == null)
{
throw new WireMockException("CSharpCodeMatcher: Unable to find method 'IsMatch' in WireMock.CodeHelper");
}
try
{
result = methodInfo.Invoke(helper, new[] { inputValue });
}
catch (Exception ex)
{
throw new WireMockException("CSharpCodeMatcher: Unable to call method 'IsMatch' in WireMock.CodeHelper", ex);
}
}
#elif (NET46 || NET461)
dynamic script;
try
{
#if NETSTANDARD2_0
script = csscript.GenericExtensions.CreateObject(assembly, "*");
#else
script = CSScriptLib.ReflectionExtensions.CreateObject(assembly, "*");
script = CSScriptLibrary.CSScript.Evaluator.CompileCode(source).CreateObject("*");
}
catch (Exception ex)
{
throw new WireMockException("CSharpCodeMatcher: Unable to create compiler for WireMock.CodeHelper", ex);
}
try
{
result = script.IsMatch(inputValue);
}
catch (Exception ex)
{
throw new WireMockException("CSharpCodeMatcher: Problem calling method 'IsMatch' in WireMock.CodeHelper", ex);
}
#elif (NETSTANDARD2_0 || NETSTANDARD2_1 || NETCOREAPP3_1 || NET5_0)
Assembly assembly;
try
{
assembly = CSScriptLib.CSScript.Evaluator.CompileCode(source);
}
catch (Exception ex)
{
throw new WireMockException($"CSharpCodeMatcher: Unable to compile code `{source}` for WireMock.CodeHelper", ex);
}
dynamic script;
try
{
#if NETSTANDARD2_0
script = csscript.GenericExtensions.CreateObject(assembly, "*");
#else
script = CSScriptLib.ReflectionExtensions.CreateObject(assembly, "*");
#endif
}
catch (Exception ex)
}
catch (Exception ex)
{
throw new WireMockException("CSharpCodeMatcher: Unable to create object from assembly", ex);
}
try
{
result = script.IsMatch(inputValue);
}
catch (Exception ex)
{
throw new WireMockException("CSharpCodeMatcher: Problem calling method 'IsMatch' in WireMock.CodeHelper", ex);
}
#else
throw new NotSupportedException("The 'CSharpCodeMatcher' cannot be used in netstandard 1.3");
#endif
try
{
return (bool)result;
}
catch
{
throw new WireMockException($"Unable to cast result '{result}' to bool");
}
}
private string GetSourceForIsMatchWithString(string pattern, bool isMatchWithString)
{
string template = isMatchWithString ? TemplateForIsMatchWithString : TemplateForIsMatchWithDynamic;
var stringBuilder = new StringBuilder();
try
{
result = script.IsMatch(inputValue);
}
catch (Exception ex)
{
throw new WireMockException("CSharpCodeMatcher: Problem calling method 'IsMatch' in WireMock.CodeHelper", ex);
}
#else
throw new NotSupportedException("The 'CSharpCodeMatcher' cannot be used in netstandard 1.3");
#endif
try
{
return (bool)result;
}
catch
{
throw new WireMockException($"Unable to cast result '{result}' to bool");
}
}
private string GetSourceForIsMatchWithString(string pattern, bool isMatchWithString)
{
string template = isMatchWithString ? TemplateForIsMatchWithString : TemplateForIsMatchWithDynamic;
var stringBuilder = new StringBuilder();
foreach (string @using in _usings)
{
stringBuilder.AppendLine($"using {@using};");
}
stringBuilder.AppendLine();
stringBuilder.AppendFormat(template, pattern);
return stringBuilder.ToString();
}
/// <inheritdoc cref="IStringMatcher.GetPatterns"/>
public string[] GetPatterns()
{
return _patterns;
}
/// <inheritdoc cref="IMatcher.Name"/>
public string Name => "CSharpCodeMatcher";
}
}
stringBuilder.AppendLine();
stringBuilder.AppendFormat(template, pattern);
return stringBuilder.ToString();
}
/// <inheritdoc cref="IStringMatcher.GetPatterns"/>
public AnyOf<string, StringPattern>[] GetPatterns()
{
return _patterns;
}
/// <inheritdoc cref="IMatcher.Name"/>
public string Name => "CSharpCodeMatcher";
}
}

View File

@@ -6,6 +6,7 @@ using Microsoft.OpenApi;
using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Models;
using Microsoft.OpenApi.Writers;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using WireMock.Admin.Mappings;
using WireMock.Net.OpenApiParser.Extensions;
@@ -26,34 +27,56 @@ namespace WireMock.Net.OpenApiParser.Mappers
_exampleValueGenerator = new ExampleValueGenerator(settings);
}
public IEnumerable<MappingModel> ToMappingModels(OpenApiPaths paths)
public IEnumerable<MappingModel> ToMappingModels(OpenApiPaths paths, IList<OpenApiServer> servers)
{
return paths.Select(p => MapPath(p.Key, p.Value)).SelectMany(x => x);
return paths.Select(p => MapPath(p.Key, p.Value, servers)).SelectMany(x => x);
}
private IEnumerable<MappingModel> MapPaths(OpenApiPaths paths)
private IEnumerable<MappingModel> MapPaths(OpenApiPaths paths, IList<OpenApiServer> servers)
{
return paths.Select(p => MapPath(p.Key, p.Value)).SelectMany(x => x);
return paths.Select(p => MapPath(p.Key, p.Value, servers)).SelectMany(x => x);
}
private IEnumerable<MappingModel> MapPath(string path, OpenApiPathItem pathItem)
private IEnumerable<MappingModel> MapPath(string path, OpenApiPathItem pathItem, IList<OpenApiServer> servers)
{
return pathItem.Operations.Select(o => MapOperationToMappingModel(path, o.Key.ToString().ToUpperInvariant(), o.Value));
return pathItem.Operations.Select(o => MapOperationToMappingModel(path, o.Key.ToString().ToUpperInvariant(), o.Value, servers));
}
private MappingModel MapOperationToMappingModel(string path, string httpMethod, OpenApiOperation operation)
private MappingModel MapOperationToMappingModel(string path, string httpMethod, OpenApiOperation operation, IList<OpenApiServer> servers)
{
var queryParameters = operation.Parameters.Where(p => p.In == ParameterLocation.Query);
var pathParameters = operation.Parameters.Where(p => p.In == ParameterLocation.Path);
var headers = operation.Parameters.Where(p => p.In == ParameterLocation.Header);
var response = operation.Responses.FirstOrDefault();
TryGetContent(response.Value?.Content, out OpenApiMediaType responseContent, out string responseContentType);
var responseSchema = response.Value?.Content?.FirstOrDefault().Value?.Schema;
var responseExample = responseContent?.Example;
var responseSchemaExample = responseContent?.Schema?.Example;
var body = responseExample != null ? MapOpenApiAnyToJToken(responseExample) : MapSchemaToObject(responseSchema);
var body = responseExample != null ? MapOpenApiAnyToJToken(responseExample) :
responseSchemaExample != null ? MapOpenApiAnyToJToken(responseSchemaExample) :
MapSchemaToObject(responseSchema);
if (int.TryParse(response.Key, out var httpStatusCode))
var requestBodyModel = new BodyModel();
if (operation.RequestBody != null && operation.RequestBody.Content != null)
{
var request = operation.RequestBody.Content;
TryGetContent(request, out OpenApiMediaType requestContent, out string requestContentType);
var requestBodySchema = operation.RequestBody.Content.First().Value?.Schema;
var requestBodyExample = requestContent.Example;
var requestBodySchemaExample = requestContent.Schema?.Example;
var requestBodyMapped = requestBodyExample != null ? MapOpenApiAnyToJToken(requestBodyExample) :
requestBodySchemaExample != null ? MapOpenApiAnyToJToken(requestBodySchemaExample) :
MapSchemaToObject(requestBodySchema);
requestBodyModel = MapRequestBody(requestBodyMapped);
}
if (!int.TryParse(response.Key, out var httpStatusCode))
{
httpStatusCode = 200;
}
@@ -64,8 +87,10 @@ namespace WireMock.Net.OpenApiParser.Mappers
Request = new RequestModel
{
Methods = new[] { httpMethod },
Path = MapPathWithParameters(path, pathParameters),
Params = MapQueryParameters(queryParameters)
Path = MapBasePath(servers) + MapPathWithParameters(path, pathParameters),
Params = MapQueryParameters(queryParameters),
Headers = MapRequestHeaders(headers),
Body = requestBodyModel
},
Response = new ResponseModel
{
@@ -76,6 +101,20 @@ namespace WireMock.Net.OpenApiParser.Mappers
};
}
private BodyModel MapRequestBody(object requestBody)
{
if (requestBody == null)
{
return null;
}
var requestBodyModel = new BodyModel();
requestBodyModel.Matcher = new MatcherModel();
requestBodyModel.Matcher.Name = "JsonMatcher";
requestBodyModel.Matcher.Pattern = JsonConvert.SerializeObject(requestBody, Formatting.Indented);
return requestBodyModel;
}
private bool TryGetContent(IDictionary<string, OpenApiMediaType> contents, out OpenApiMediaType openApiMediaType, out string contentType)
{
openApiMediaType = null;
@@ -138,6 +177,11 @@ namespace WireMock.Net.OpenApiParser.Mappers
}
}
if (schema.AllOf.Count > 0)
{
jArray.Add(MapSchemaAllOfToObject(schema));
}
return jArray;
case SchemaType.Boolean:
@@ -150,22 +194,17 @@ namespace WireMock.Net.OpenApiParser.Mappers
var propertyAsJObject = new JObject();
foreach (var schemaProperty in schema.Properties)
{
string propertyName = schemaProperty.Key;
var openApiSchema = schemaProperty.Value;
if (openApiSchema.GetSchemaType() == SchemaType.Object)
propertyAsJObject.Add(MapPropertyAsJObject(schemaProperty.Value, schemaProperty.Key));
}
if (schema.AllOf.Count > 0)
{
foreach (var property in schema.AllOf)
{
var mapped = MapSchemaToObject(schemaProperty.Value, schemaProperty.Key);
if (mapped is JProperty jp)
foreach (var item in property.Properties)
{
propertyAsJObject.Add(jp);
propertyAsJObject.Add(MapPropertyAsJObject(item.Value, item.Key));
}
}
else
{
bool propertyIsNullable = openApiSchema.Nullable || (openApiSchema.TryGetXNullable(out bool x) && x);
propertyAsJObject.Add(new JProperty(propertyName, _exampleValueGenerator.GetExampleValue(openApiSchema)));
}
}
return name != null ? new JProperty(name, propertyAsJObject) : (JToken)propertyAsJObject;
@@ -175,6 +214,39 @@ namespace WireMock.Net.OpenApiParser.Mappers
}
}
private JObject MapSchemaAllOfToObject(OpenApiSchema schema)
{
var arrayItem = new JObject();
foreach (var property in schema.AllOf)
{
foreach (var item in property.Properties)
{
arrayItem.Add(MapPropertyAsJObject(item.Value, item.Key));
}
}
return arrayItem;
}
private object MapPropertyAsJObject(OpenApiSchema openApiSchema, string key)
{
if (openApiSchema.GetSchemaType() == SchemaType.Object || openApiSchema.GetSchemaType() == SchemaType.Array)
{
var mapped = MapSchemaToObject(openApiSchema, key);
if (mapped is JProperty jp)
{
return jp;
}
else
{
return new JProperty(key, mapped);
}
}
else
{
bool propertyIsNullable = openApiSchema.Nullable || (openApiSchema.TryGetXNullable(out bool x) && x);
return new JProperty(key, _exampleValueGenerator.GetExampleValue(openApiSchema));
}
}
private string MapPathWithParameters(string path, IEnumerable<OpenApiParameter> parameters)
{
if (parameters == null)
@@ -191,6 +263,22 @@ namespace WireMock.Net.OpenApiParser.Mappers
return newPath;
}
private string MapBasePath(IList<OpenApiServer> servers)
{
if (servers == null || servers.Count == 0)
{
return string.Empty;
}
OpenApiServer server = servers.First();
Uri uriResult;
if (Uri.TryCreate(server.Url, UriKind.RelativeOrAbsolute, out uriResult))
{
return uriResult.IsAbsoluteUri ? uriResult.AbsolutePath : uriResult.ToString();
}
return string.Empty;
}
private JToken MapOpenApiAnyToJToken(IOpenApiAny any)
{
if (any == null)
@@ -202,7 +290,14 @@ namespace WireMock.Net.OpenApiParser.Mappers
var writer = new OpenApiJsonWriter(outputString);
any.Write(writer, OpenApiSpecVersion.OpenApi3_0);
return JObject.Parse(outputString.ToString());
if (any.AnyType == AnyType.Array)
{
return JArray.Parse(outputString.ToString());
}
else
{
return JObject.Parse(outputString.ToString());
}
}
private IDictionary<string, object> MapHeaders(string responseContentType, IDictionary<string, OpenApiHeader> headers)
@@ -247,6 +342,26 @@ namespace WireMock.Net.OpenApiParser.Mappers
return list.Any() ? list : null;
}
private IList<HeaderModel> MapRequestHeaders(IEnumerable<OpenApiParameter> headers)
{
var list = headers
.Select(qp => new HeaderModel
{
Name = qp.Name,
Matchers = new[]
{
new MatcherModel
{
Name = "ExactMatcher",
Pattern = GetDefaultValueAsStringForSchemaType(qp.Schema)
}
}
})
.ToList();
return list.Any() ? list : null;
}
private string GetDefaultValueAsStringForSchemaType(OpenApiSchema schema)
{
var value = _exampleValueGenerator.GetExampleValue(schema);

View File

@@ -0,0 +1,52 @@
using System;
namespace WireMock.Net.OpenApiParser.Settings
{
/// <summary>
/// A interface defining the example values to use for the different types.
/// </summary>
public interface IWireMockOpenApiParserExampleValues
{
/// <summary>
/// An example value for a Boolean.
/// </summary>
bool Boolean { get; set; }
/// <summary>
/// An example value for an Integer.
/// </summary>
int Integer { get; set; }
/// <summary>
/// An example value for a Float.
/// </summary>
float Float { get; set; }
/// <summary>
/// An example value for a Double.
/// </summary>
double Double { get; set; }
/// <summary>
/// An example value for a Date.
/// </summary>
Func<DateTime> Date { get; set; }
/// <summary>
/// An example value for a DateTime.
/// </summary>
Func<DateTime> DateTime { get; set; }
/// <summary>
/// An example value for Bytes.
/// </summary>
byte[] Bytes { get; set; }
/// <summary>
/// An example value for a Object.
/// </summary>
object Object { get; set; }
/// <summary>
/// An example value for a String.
/// </summary>
string String { get; set; }
}
}

View File

@@ -0,0 +1,31 @@
using System;
using RandomDataGenerator.FieldOptions;
using RandomDataGenerator.Randomizers;
namespace WireMock.Net.OpenApiParser.Settings
{
/// <summary>
/// A class defining the random example values to use for the different types.
/// </summary>
public class WireMockOpenApiParserDynamicExampleValues : IWireMockOpenApiParserExampleValues
{
/// <inheritdoc />
public bool Boolean { get { return RandomizerFactory.GetRandomizer(new FieldOptionsBoolean()).Generate() ?? true; } set { } }
/// <inheritdoc />
public int Integer { get { return RandomizerFactory.GetRandomizer(new FieldOptionsInteger()).Generate() ?? 42; } set { } }
/// <inheritdoc />
public float Float { get { return RandomizerFactory.GetRandomizer(new FieldOptionsFloat()).Generate() ?? 4.2f; } set { } }
/// <inheritdoc />
public double Double { get { return RandomizerFactory.GetRandomizer(new FieldOptionsDouble()).Generate() ?? 4.2d; } set { } }
/// <inheritdoc />
public Func<DateTime> Date { get { return () => RandomizerFactory.GetRandomizer(new FieldOptionsDateTime()).Generate() ?? System.DateTime.UtcNow.Date; } set { } }
/// <inheritdoc />
public Func<DateTime> DateTime { get { return () => RandomizerFactory.GetRandomizer(new FieldOptionsDateTime()).Generate() ?? System.DateTime.UtcNow; } set { } }
/// <inheritdoc />
public byte[] Bytes { get { return RandomizerFactory.GetRandomizer(new FieldOptionsBytes()).Generate(); } set { } }
/// <inheritdoc />
public object Object { get; set; } = "example-object";
/// <inheritdoc />
public string String { get { return RandomizerFactory.GetRandomizer(new FieldOptionsTextRegex { Pattern = @"^[0-9]{2}[A-Z]{5}[0-9]{2}" }).Generate() ?? "example-string"; } set { } }
}
}

View File

@@ -5,26 +5,25 @@ namespace WireMock.Net.OpenApiParser.Settings
/// <summary>
/// A class defining the example values to use for the different types.
/// </summary>
public class WireMockOpenApiParserExampleValues
public class WireMockOpenApiParserExampleValues : IWireMockOpenApiParserExampleValues
{
#pragma warning disable 1591
/// <inheritdoc />
public bool Boolean { get; set; } = true;
/// <inheritdoc />
public int Integer { get; set; } = 42;
/// <inheritdoc />
public float Float { get; set; } = 4.2f;
/// <inheritdoc />
public double Double { get; set; } = 4.2d;
/// <inheritdoc />
public Func<DateTime> Date { get; set; } = () => System.DateTime.UtcNow.Date;
/// <inheritdoc />
public Func<DateTime> DateTime { get; set; } = () => System.DateTime.UtcNow;
/// <inheritdoc />
public byte[] Bytes { get; set; } = { 48, 49, 50 };
/// <inheritdoc />
public object Object { get; set; } = "example-object";
/// <inheritdoc />
public string String { get; set; } = "example-string";
#pragma warning restore 1591
}
}

View File

@@ -25,6 +25,11 @@ namespace WireMock.Net.OpenApiParser.Settings
/// <summary>
/// The example values to use
/// </summary>
public WireMockOpenApiParserExampleValues ExampleValues { get; } = new WireMockOpenApiParserExampleValues();
public IWireMockOpenApiParserExampleValues ExampleValues { get; set; } = new WireMockOpenApiParserExampleValues();
/// <summary>
/// Are examples generated dynamically?
/// </summary>
public bool DynamicExamples { get; set; } = false;
}
}

View File

@@ -1,5 +1,8 @@
using System;
using System;
using System.Collections.Generic;
using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Models;
using Stef.Validation;
using WireMock.Net.OpenApiParser.Extensions;
using WireMock.Net.OpenApiParser.Settings;
using WireMock.Net.OpenApiParser.Types;
@@ -12,48 +15,105 @@ namespace WireMock.Net.OpenApiParser.Utils
public ExampleValueGenerator(WireMockOpenApiParserSettings settings)
{
_settings = settings ?? throw new ArgumentNullException(nameof(settings));
_settings = Guard.NotNull(settings, nameof(settings));
if (_settings.DynamicExamples)
{
_settings.ExampleValues = new WireMockOpenApiParserDynamicExampleValues();
}
else
{
_settings.ExampleValues = new WireMockOpenApiParserExampleValues();
}
}
public object GetExampleValue(OpenApiSchema schema)
{
var schemaExample = schema?.Example;
var schemaEnum = GetRandomEnumValue(schema?.Enum);
switch (schema?.GetSchemaType())
{
case SchemaType.Boolean:
return _settings.ExampleValues.Boolean;
var exampleBoolean = (OpenApiBoolean)schemaExample;
return exampleBoolean is null ? _settings.ExampleValues.Boolean : exampleBoolean.Value;
case SchemaType.Integer:
return _settings.ExampleValues.Integer;
switch (schema?.GetSchemaFormat())
{
case SchemaFormat.Int64:
var exampleLong = (OpenApiLong)schemaExample;
var enumLong = (OpenApiLong)schemaEnum;
var valueLongEnumOrExample = enumLong is null ? exampleLong?.Value : enumLong?.Value;
return valueLongEnumOrExample ?? _settings.ExampleValues.Integer;
default:
var exampleInteger = (OpenApiInteger)schemaExample;
var enumInteger = (OpenApiInteger)schemaEnum;
var valueIntegerEnumOrExample = enumInteger is null ? exampleInteger?.Value : enumInteger?.Value;
return valueIntegerEnumOrExample ?? _settings.ExampleValues.Integer;
}
case SchemaType.Number:
switch (schema?.GetSchemaFormat())
{
case SchemaFormat.Float:
return _settings.ExampleValues.Float;
var exampleFloat = (OpenApiFloat)schemaExample;
var enumFloat = (OpenApiFloat)schemaEnum;
var valueFloatEnumOrExample = enumFloat is null ? exampleFloat?.Value : enumFloat?.Value;
return valueFloatEnumOrExample ?? _settings.ExampleValues.Float;
default:
return _settings.ExampleValues.Double;
var exampleDouble = (OpenApiDouble)schemaExample;
var enumDouble = (OpenApiDouble)schemaEnum;
var valueDoubleEnumOrExample = enumDouble is null ? exampleDouble?.Value : enumDouble?.Value;
return valueDoubleEnumOrExample ?? _settings.ExampleValues.Double;
}
default:
switch (schema?.GetSchemaFormat())
{
case SchemaFormat.Date:
return DateTimeUtils.ToRfc3339Date(_settings.ExampleValues.Date());
var exampleDate = (OpenApiDate)schemaExample;
var enumDate = (OpenApiDate)schemaEnum;
var valueDateEnumOrExample = enumDate is null ? exampleDate?.Value : enumDate?.Value;
return DateTimeUtils.ToRfc3339Date(valueDateEnumOrExample ?? _settings.ExampleValues.Date());
case SchemaFormat.DateTime:
return DateTimeUtils.ToRfc3339DateTime(_settings.ExampleValues.DateTime());
var exampleDateTime = (OpenApiDateTime)schemaExample;
var enumDateTime = (OpenApiDateTime)schemaEnum;
var valueDateTimeEnumOrExample = enumDateTime is null ? exampleDateTime?.Value : enumDateTime?.Value;
return DateTimeUtils.ToRfc3339DateTime(valueDateTimeEnumOrExample?.DateTime ?? _settings.ExampleValues.DateTime());
case SchemaFormat.Byte:
return _settings.ExampleValues.Bytes;
var exampleByte = (OpenApiByte)schemaExample;
var enumByte = (OpenApiByte)schemaEnum;
var valueByteEnumOrExample = enumByte is null ? exampleByte?.Value : enumByte?.Value;
return valueByteEnumOrExample ?? _settings.ExampleValues.Bytes;
case SchemaFormat.Binary:
return _settings.ExampleValues.Object;
var exampleBinary = (OpenApiBinary)schemaExample;
var enumBinary = (OpenApiBinary)schemaEnum;
var valueBinaryEnumOrExample = enumBinary is null ? exampleBinary?.Value : enumBinary?.Value;
return valueBinaryEnumOrExample ?? _settings.ExampleValues.Object;
default:
return _settings.ExampleValues.String;
var exampleString = (OpenApiString)schemaExample;
var enumString = (OpenApiString)schemaEnum;
var valueStringEnumOrExample = enumString is null ? exampleString?.Value : enumString?.Value;
return valueStringEnumOrExample ?? _settings.ExampleValues.String;
}
}
}
private static IOpenApiAny GetRandomEnumValue(IList<IOpenApiAny> schemaEnum)
{
if (schemaEnum?.Count > 0)
{
int maxValue = schemaEnum.Count - 1;
int randomEnum = new Random().Next(0, maxValue);
return schemaEnum[randomEnum];
}
return null;
}
}
}
}

View File

@@ -22,10 +22,11 @@
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
<PackageReference Include="Microsoft.OpenApi.Readers" Version="1.2.3" />
<PackageReference Include="RamlToOpenApiConverter" Version="0.1.1" />
<PackageReference Include="RamlToOpenApiConverter" Version="0.4.3" />
<PackageReference Include="JetBrains.Annotations" Version="2020.1.0" PrivateAssets="All" />
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" />
<PackageReference Include="Stef.Validation" Version="0.0.3" />
<PackageReference Include="RandomDataGenerator.Net" Version="1.0.13" />
<PackageReference Include="Stef.Validation" Version="0.0.4" />
</ItemGroup>
<ItemGroup>

View File

@@ -62,7 +62,7 @@ namespace WireMock.Net.OpenApiParser
[PublicAPI]
public IEnumerable<MappingModel> FromDocument(OpenApiDocument openApiDocument, WireMockOpenApiParserSettings settings = null)
{
return new OpenApiPathsMapper(settings).ToMappingModels(openApiDocument.Paths);
return new OpenApiPathsMapper(settings).ToMappingModels(openApiDocument.Paths, openApiDocument.Servers);
}
}
}

View File

@@ -1,4 +1,5 @@
using System.Linq;
using System.Linq;
using System.Reflection;
using JetBrains.Annotations;
using WireMock.Logging;
using WireMock.Server;
@@ -12,6 +13,8 @@ namespace WireMock.Net.StandAlone
/// </summary>
public static class StandAloneApp
{
private static readonly string Version = typeof(StandAloneApp).GetTypeInfo().Assembly.GetName().Version.ToString();
/// <summary>
/// Start WireMock.Net standalone Server based on the IWireMockServerSettings.
/// </summary>
@@ -23,7 +26,8 @@ namespace WireMock.Net.StandAlone
var server = WireMockServer.Start(settings);
settings.Logger?.Info("WireMock.Net server listening at {0}", string.Join(",", server.Urls));
settings.Logger?.Info("Version [{0}]", Version);
settings.Logger?.Info("Server listening at {0}", string.Join(",", server.Urls));
return server;
}
@@ -40,7 +44,8 @@ namespace WireMock.Net.StandAlone
if (WireMockServerSettingsParser.TryParseArguments(args, out var settings, logger))
{
settings.Logger?.Debug("WireMock.Net server arguments [{0}]", string.Join(", ", args.Select(a => $"'{a}'")));
settings.Logger?.Info("Version [{0}]", Version);
settings.Logger?.Debug("Server arguments [{0}]", string.Join(", ", args.Select(a => $"'{a}'")));
return Start(settings);
}
@@ -61,7 +66,8 @@ namespace WireMock.Net.StandAlone
if (WireMockServerSettingsParser.TryParseArguments(args, out var settings, logger))
{
settings.Logger?.Debug("WireMock.Net server arguments [{0}]", string.Join(", ", args.Select(a => $"'{a}'")));
settings.Logger?.Info("Version [{0}]", Version);
settings.Logger?.Debug("Server arguments [{0}]", string.Join(", ", args.Select(a => $"'{a}'")));
server = Start(settings);
return true;

View File

@@ -0,0 +1,71 @@
#if !NETSTANDARD1_3
using System.Globalization;
using System.IdentityModel.Tokens.Jwt;
using System.Text.RegularExpressions;
using AnyOfTypes;
using Microsoft.IdentityModel.Protocols;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
using Microsoft.IdentityModel.Tokens;
using WireMock.Matchers;
using WireMock.Models;
namespace WireMock.Authentication
{
/// <summary>
/// https://www.c-sharpcorner.com/article/how-to-validate-azure-ad-token-using-console-application/
/// https://stackoverflow.com/questions/38684865/validation-of-an-azure-ad-bearer-token-in-a-console-application
/// </summary>
internal class AzureADAuthenticationMatcher : IStringMatcher
{
private const string BearerPrefix = "Bearer ";
private readonly string _audience;
private readonly string _stsDiscoveryEndpoint;
public AzureADAuthenticationMatcher(string tenant, string audience)
{
_audience = audience;
_stsDiscoveryEndpoint = string.Format(CultureInfo.InvariantCulture, "https://login.microsoftonline.com/{0}/.well-known/openid-configuration", tenant);
}
public string Name => nameof(AzureADAuthenticationMatcher);
public MatchBehaviour MatchBehaviour => MatchBehaviour.AcceptOnMatch;
public bool ThrowException => false;
public AnyOf<string, StringPattern>[] GetPatterns()
{
return new AnyOf<string, StringPattern>[0];
}
public double IsMatch(string input)
{
var token = Regex.Replace(input, BearerPrefix, string.Empty, RegexOptions.IgnoreCase);
try
{
var configManager = new ConfigurationManager<OpenIdConnectConfiguration>(_stsDiscoveryEndpoint, new OpenIdConnectConfigurationRetriever());
var config = configManager.GetConfigurationAsync().GetAwaiter().GetResult();
var validationParameters = new TokenValidationParameters
{
ValidAudience = _audience,
ValidIssuer = config.Issuer,
IssuerSigningKeys = config.SigningKeys,
ValidateLifetime = true
};
// Throws an Exception as the token is invalid (expired, invalid-formatted, etc.)
new JwtSecurityTokenHandler().ValidateToken(token, validationParameters, out var _);
return MatchScores.Perfect;
}
catch
{
return MatchScores.Mismatch;
}
}
}
}
#endif

View File

@@ -0,0 +1,20 @@
using System;
using System.Text;
using WireMock.Matchers;
namespace WireMock.Authentication
{
internal class BasicAuthenticationMatcher : RegexMatcher
{
public BasicAuthenticationMatcher(string username, string password) : base(BuildPattern(username, password))
{
}
public override string Name => nameof(BasicAuthenticationMatcher);
private static string BuildPattern(string username, string password)
{
return "^(?i)BASIC " + Convert.ToBase64String(Encoding.GetEncoding("ISO-8859-1").GetBytes(username + ":" + password)) + "$";
}
}
}

View File

@@ -0,0 +1,26 @@
using System.Collections.Generic;
using System.Linq;
using AnyOfTypes;
using JetBrains.Annotations;
using WireMock.Models;
namespace WireMock.Extensions
{
internal static class AnyOfExtensions
{
public static string GetPattern([NotNull] this AnyOf<string, StringPattern> value)
{
return value.IsFirst ? value.First : value.Second.Pattern;
}
public static AnyOf<string, StringPattern>[] ToAnyOfPatterns([NotNull] this IEnumerable<string> patterns)
{
return patterns.Select(p => p.ToAnyOfPattern()).ToArray();
}
public static AnyOf<string, StringPattern> ToAnyOfPattern([CanBeNull] this string pattern)
{
return new AnyOf<string, StringPattern>(pattern);
}
}
}

View File

@@ -0,0 +1,35 @@
using System;
using JetBrains.Annotations;
using WireMock.Models;
namespace WireMock.Extensions
{
internal static class TimeSettingsExtensions
{
public static bool IsValid([CanBeNull] this ITimeSettings settings)
{
if (settings == null)
{
return true;
}
var now = DateTime.Now;
var start = settings.Start != null ? settings.Start.Value : now;
DateTime end;
if (settings.End != null)
{
end = settings.End.Value;
}
else if (settings.TTL != null)
{
end = start.AddSeconds(settings.TTL.Value);
}
else
{
end = DateTime.MaxValue;
}
return now >= start && now <= end;
}
}
}

View File

@@ -1,4 +1,4 @@
using System.Collections.Generic;
using System.Collections.Generic;
using System.IO;
using WireMock.Util;
using WireMock.Validation;
@@ -130,6 +130,12 @@ namespace WireMock.Handlers
return File.ReadAllBytes(AdjustPath(filename));
}
/// <inheritdoc cref="IFileSystemHandler.ReadFileAsString"/>
public string ReadFileAsString(string filename)
{
return File.ReadAllText(AdjustPath(Check.NotNullOrEmpty(filename, nameof(filename))));
}
/// <summary>
/// Adjusts the path to the MappingFolder.
/// </summary>
@@ -140,4 +146,4 @@ namespace WireMock.Handlers
return Path.Combine(GetMappingFolder(), filename);
}
}
}
}

View File

@@ -1,4 +1,4 @@
using JetBrains.Annotations;
using JetBrains.Annotations;
using System;
using System.Threading.Tasks;
using WireMock.Matchers.Request;
@@ -18,6 +18,11 @@ namespace WireMock
/// </summary>
Guid Guid { get; }
/// <summary>
/// Gets the TimeSettings (Start, End and TTL).
/// </summary>
ITimeSettings TimeSettings { get; }
/// <summary>
/// Gets the unique title.
/// </summary>

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Threading.Tasks;
using JetBrains.Annotations;
using WireMock.Matchers.Request;
@@ -13,51 +13,54 @@ namespace WireMock
/// </summary>
public class Mapping : IMapping
{
/// <inheritdoc cref="IMapping.Guid" />
/// <inheritdoc />
public Guid Guid { get; }
/// <inheritdoc cref="IMapping.Title" />
/// <inheritdoc />
public string Title { get; }
/// <inheritdoc cref="IMapping.Path" />
/// <inheritdoc />
public string Path { get; set; }
/// <inheritdoc cref="IMapping.Priority" />
/// <inheritdoc />
public int Priority { get; }
/// <inheritdoc cref="IMapping.Scenario" />
/// <inheritdoc />
public string Scenario { get; }
/// <inheritdoc cref="IMapping.ExecutionConditionState" />
/// <inheritdoc />
public string ExecutionConditionState { get; }
/// <inheritdoc cref="IMapping.NextState" />
/// <inheritdoc />
public string NextState { get; }
/// <inheritdoc cref="IMapping.StateTimes" />
/// <inheritdoc />
public int? StateTimes { get; }
/// <inheritdoc cref="IMapping.RequestMatcher" />
/// <inheritdoc />
public IRequestMatcher RequestMatcher { get; }
/// <inheritdoc cref="IMapping.Provider" />
/// <inheritdoc />
public IResponseProvider Provider { get; }
/// <inheritdoc cref="IMapping.Settings" />
/// <inheritdoc />
public IWireMockServerSettings Settings { get; }
/// <inheritdoc cref="IMapping.IsStartState" />
/// <inheritdoc />
public bool IsStartState => Scenario == null || Scenario != null && NextState != null && ExecutionConditionState == null;
/// <inheritdoc cref="IMapping.IsAdminInterface" />
/// <inheritdoc />
public bool IsAdminInterface => Provider is DynamicResponseProvider || Provider is DynamicAsyncResponseProvider || Provider is ProxyAsyncResponseProvider;
/// <inheritdoc cref="IMapping.LogMapping" />
/// <inheritdoc />
public bool LogMapping => !(Provider is DynamicResponseProvider || Provider is DynamicAsyncResponseProvider);
/// <inheritdoc cref="IMapping.Webhooks" />
/// <inheritdoc />
public IWebhook[] Webhooks { get; }
/// <inheritdoc />
public ITimeSettings TimeSettings { get; }
/// <summary>
/// Initializes a new instance of the <see cref="Mapping"/> class.
/// </summary>
@@ -73,6 +76,7 @@ namespace WireMock
/// <param name="nextState">The next state which will occur after the current mapping execution. [Optional]</param>
/// <param name="stateTimes">Only when the current state is executed this number, the next state which will occur. [Optional]</param>
/// <param name="webhooks">The Webhooks. [Optional]</param>
/// <param name="timeSettings">The TimeSettings. [Optional]</param>
public Mapping(
Guid guid,
[CanBeNull] string title,
@@ -85,7 +89,8 @@ namespace WireMock
[CanBeNull] string executionConditionState,
[CanBeNull] string nextState,
[CanBeNull] int? stateTimes,
[CanBeNull] IWebhook[] webhooks)
[CanBeNull] IWebhook[] webhooks,
[CanBeNull] ITimeSettings timeSettings)
{
Guid = guid;
Title = title;
@@ -99,6 +104,7 @@ namespace WireMock
NextState = nextState;
StateTimes = stateTimes;
Webhooks = webhooks;
TimeSettings = timeSettings;
}
/// <inheritdoc cref="IMapping.ProvideResponseAsync" />

View File

@@ -0,0 +1,89 @@
using System.Collections.Generic;
using System.Linq;
using JetBrains.Annotations;
using Newtonsoft.Json.Linq;
namespace WireMock.Matchers
{
/// <summary>
/// Generic AbstractJsonPartialMatcher
/// </summary>
public abstract class AbstractJsonPartialMatcher : JsonMatcher
{
/// <summary>
/// Initializes a new instance of the <see cref="AbstractJsonPartialMatcher"/> class.
/// </summary>
/// <param name="value">The string value to check for equality.</param>
/// <param name="ignoreCase">Ignore the case from the PropertyName and PropertyValue (string only).</param>
/// <param name="throwException">Throw an exception when the internal matching fails because of invalid input.</param>
protected AbstractJsonPartialMatcher([NotNull] string value, bool ignoreCase = false, bool throwException = false)
: base(value, ignoreCase, throwException)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="AbstractJsonPartialMatcher"/> class.
/// </summary>
/// <param name="value">The object value to check for equality.</param>
/// <param name="ignoreCase">Ignore the case from the PropertyName and PropertyValue (string only).</param>
/// <param name="throwException">Throw an exception when the internal matching fails because of invalid input.</param>
protected AbstractJsonPartialMatcher([NotNull] object value, bool ignoreCase = false, bool throwException = false)
: base(value, ignoreCase, throwException)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="AbstractJsonPartialMatcher"/> class.
/// </summary>
/// <param name="matchBehaviour">The match behaviour.</param>
/// <param name="value">The value to check for equality.</param>
/// <param name="ignoreCase">Ignore the case from the PropertyName and PropertyValue (string only).</param>
/// <param name="throwException">Throw an exception when the internal matching fails because of invalid input.</param>
protected AbstractJsonPartialMatcher(MatchBehaviour matchBehaviour, [NotNull] object value, bool ignoreCase = false, bool throwException = false)
: base(matchBehaviour, value, ignoreCase, throwException)
{
}
/// <inheritdoc />
protected override bool IsMatch(JToken value, JToken input)
{
if (value == null || value == input)
{
return true;
}
if (input == null || value.Type != input.Type)
{
return false;
}
switch (value.Type)
{
case JTokenType.Object:
var nestedValues = value.ToObject<Dictionary<string, JToken>>();
return nestedValues?.Any() != true ||
nestedValues.All(pair => IsMatch(pair.Value, input.SelectToken(pair.Key)));
case JTokenType.Array:
var valuesArray = value.ToObject<JToken[]>();
var tokenArray = input.ToObject<JToken[]>();
if (valuesArray?.Any() != true)
{
return true;
}
return tokenArray?.Any() == true &&
valuesArray.All(subFilter => tokenArray.Any(subToken => IsMatch(subFilter, subToken)));
default:
return IsMatch(value.ToString(), input.ToString());
}
}
/// <summary>
/// Check if two strings are a match (matching can be done exact or wildcard)
/// </summary>
protected abstract bool IsMatch(string value, string input);
}
}

View File

@@ -1,5 +1,7 @@
using System.Net.Http.Headers;
using System.Net.Http.Headers;
using AnyOfTypes;
using JetBrains.Annotations;
using WireMock.Models;
namespace WireMock.Matchers
{
@@ -9,14 +11,14 @@ namespace WireMock.Matchers
/// <seealso cref="RegexMatcher" />
public class ContentTypeMatcher : WildcardMatcher
{
private readonly string[] _patterns;
private readonly AnyOf<string, StringPattern>[] _patterns;
/// <summary>
/// Initializes a new instance of the <see cref="ContentTypeMatcher"/> class.
/// </summary>
/// <param name="pattern">The pattern.</param>
/// <param name="ignoreCase">IgnoreCase (default false)</param>
public ContentTypeMatcher([NotNull] string pattern, bool ignoreCase = false) : this(new[] { pattern }, ignoreCase)
public ContentTypeMatcher([NotNull] AnyOf<string, StringPattern> pattern, bool ignoreCase = false) : this(new[] { pattern }, ignoreCase)
{
}
@@ -26,7 +28,7 @@ namespace WireMock.Matchers
/// <param name="matchBehaviour">The match behaviour.</param>
/// <param name="pattern">The pattern.</param>
/// <param name="ignoreCase">IgnoreCase (default false)</param>
public ContentTypeMatcher(MatchBehaviour matchBehaviour, [NotNull] string pattern, bool ignoreCase = false) : this(matchBehaviour, new[] { pattern }, ignoreCase)
public ContentTypeMatcher(MatchBehaviour matchBehaviour, [NotNull] AnyOf<string, StringPattern> pattern, bool ignoreCase = false) : this(matchBehaviour, new[] { pattern }, ignoreCase)
{
}
@@ -35,7 +37,7 @@ namespace WireMock.Matchers
/// </summary>
/// <param name="patterns">The patterns.</param>
/// <param name="ignoreCase">IgnoreCase (default false)</param>
public ContentTypeMatcher([NotNull] string[] patterns, bool ignoreCase = false) : this(MatchBehaviour.AcceptOnMatch, patterns, ignoreCase)
public ContentTypeMatcher([NotNull] AnyOf<string, StringPattern>[] patterns, bool ignoreCase = false) : this(MatchBehaviour.AcceptOnMatch, patterns, ignoreCase)
{
}
@@ -46,7 +48,7 @@ namespace WireMock.Matchers
/// <param name="patterns">The patterns.</param>
/// <param name="ignoreCase">IgnoreCase (default false)</param>
/// <param name="throwException">Throw an exception when the internal matching fails because of invalid input.</param>
public ContentTypeMatcher(MatchBehaviour matchBehaviour, [NotNull] string[] patterns, bool ignoreCase = false, bool throwException = false) :
public ContentTypeMatcher(MatchBehaviour matchBehaviour, [NotNull] AnyOf<string, StringPattern>[] patterns, bool ignoreCase = false, bool throwException = false) :
base(matchBehaviour, patterns, ignoreCase, throwException)
{
_patterns = patterns;
@@ -64,7 +66,7 @@ namespace WireMock.Matchers
}
/// <inheritdoc cref="IStringMatcher.GetPatterns"/>
public override string[] GetPatterns()
public override AnyOf<string, StringPattern>[] GetPatterns()
{
return _patterns;
}

View File

@@ -1,5 +1,8 @@
using System.Linq;
using System.Linq;
using AnyOfTypes;
using JetBrains.Annotations;
using WireMock.Extensions;
using WireMock.Models;
using WireMock.Validation;
namespace WireMock.Matchers
@@ -10,7 +13,7 @@ namespace WireMock.Matchers
/// <seealso cref="IStringMatcher" />
public class ExactMatcher : IStringMatcher
{
private readonly string[] _values;
private readonly AnyOf<string, StringPattern>[] _values;
/// <inheritdoc cref="IMatcher.MatchBehaviour"/>
public MatchBehaviour MatchBehaviour { get; }
@@ -22,7 +25,7 @@ namespace WireMock.Matchers
/// Initializes a new instance of the <see cref="ExactMatcher"/> class.
/// </summary>
/// <param name="values">The values.</param>
public ExactMatcher([NotNull] params string[] values) : this(MatchBehaviour.AcceptOnMatch, false, values)
public ExactMatcher([NotNull] params AnyOf<string, StringPattern>[] values) : this(MatchBehaviour.AcceptOnMatch, false, values)
{
}
@@ -32,9 +35,9 @@ namespace WireMock.Matchers
/// <param name="matchBehaviour">The match behaviour.</param>
/// <param name="throwException">Throw an exception when the internal matching fails because of invalid input.</param>
/// <param name="values">The values.</param>
public ExactMatcher(MatchBehaviour matchBehaviour, bool throwException = false, [NotNull] params string[] values)
public ExactMatcher(MatchBehaviour matchBehaviour, bool throwException = false, [NotNull] params AnyOf<string, StringPattern>[] values)
{
Check.HasNoNulls(values, nameof(values));
Check.NotNull(values, nameof(values));
MatchBehaviour = matchBehaviour;
ThrowException = throwException;
@@ -46,14 +49,14 @@ namespace WireMock.Matchers
{
if (_values.Length == 1)
{
return MatchBehaviourHelper.Convert(MatchBehaviour, MatchScores.ToScore(_values[0] == input));
return MatchBehaviourHelper.Convert(MatchBehaviour, MatchScores.ToScore(_values[0].GetPattern() == input));
}
return MatchBehaviourHelper.Convert(MatchBehaviour, MatchScores.ToScore(_values.Contains(input)));
return MatchBehaviourHelper.Convert(MatchBehaviour, MatchScores.ToScore(_values.Select(v => v.GetPattern()).Contains(input)));
}
/// <inheritdoc cref="IStringMatcher.GetPatterns"/>
public string[] GetPatterns()
public AnyOf<string, StringPattern>[] GetPatterns()
{
return _values;
}

View File

@@ -1,4 +1,7 @@
namespace WireMock.Matchers
using AnyOfTypes;
using WireMock.Models;
namespace WireMock.Matchers
{
/// <summary>
/// IStringMatcher
@@ -17,6 +20,6 @@
/// Gets the patterns.
/// </summary>
/// <returns>Patterns</returns>
string[] GetPatterns();
AnyOf<string, StringPattern>[] GetPatterns();
}
}

View File

@@ -1,7 +1,10 @@
using System.Linq;
using System.Linq;
using AnyOfTypes;
using JetBrains.Annotations;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using WireMock.Extensions;
using WireMock.Models;
using WireMock.Validation;
namespace WireMock.Matchers
@@ -13,7 +16,7 @@ namespace WireMock.Matchers
/// <seealso cref="IObjectMatcher" />
public class JsonPathMatcher : IStringMatcher, IObjectMatcher
{
private readonly string[] _patterns;
private readonly AnyOf<string, StringPattern>[] _patterns;
/// <inheritdoc cref="IMatcher.MatchBehaviour"/>
public MatchBehaviour MatchBehaviour { get; }
@@ -25,7 +28,15 @@ namespace WireMock.Matchers
/// Initializes a new instance of the <see cref="JsonPathMatcher"/> class.
/// </summary>
/// <param name="patterns">The patterns.</param>
public JsonPathMatcher([NotNull] params string[] patterns) : this(MatchBehaviour.AcceptOnMatch, false, patterns)
public JsonPathMatcher([NotNull] params string[] patterns) : this(MatchBehaviour.AcceptOnMatch, false, patterns.ToAnyOfPatterns())
{
}
/// <summary>
/// Initializes a new instance of the <see cref="JsonPathMatcher"/> class.
/// </summary>
/// <param name="patterns">The patterns.</param>
public JsonPathMatcher([NotNull] params AnyOf<string, StringPattern>[] patterns) : this(MatchBehaviour.AcceptOnMatch, false, patterns)
{
}
@@ -35,7 +46,7 @@ namespace WireMock.Matchers
/// <param name="matchBehaviour">The match behaviour.</param>
/// <param name="throwException">Throw an exception when the internal matching fails because of invalid input.</param>
/// <param name="patterns">The patterns.</param>
public JsonPathMatcher(MatchBehaviour matchBehaviour, bool throwException = false, [NotNull] params string[] patterns)
public JsonPathMatcher(MatchBehaviour matchBehaviour, bool throwException = false, [NotNull] params AnyOf<string, StringPattern>[] patterns)
{
Check.NotNull(patterns, nameof(patterns));
@@ -94,7 +105,7 @@ namespace WireMock.Matchers
}
/// <inheritdoc cref="IStringMatcher.GetPatterns"/>
public string[] GetPatterns()
public AnyOf<string, StringPattern>[] GetPatterns()
{
return _patterns;
}
@@ -104,7 +115,7 @@ namespace WireMock.Matchers
private double IsMatch(JToken jtoken)
{
return MatchScores.ToScore(_patterns.Select(pattern => jtoken.SelectToken(pattern) != null));
return MatchScores.ToScore(_patterns.Select(pattern => jtoken.SelectToken(pattern.GetPattern()) != null));
}
}
}

View File

@@ -1,7 +1,10 @@
using DevLab.JmesPath;
using AnyOfTypes;
using DevLab.JmesPath;
using JetBrains.Annotations;
using Newtonsoft.Json;
using System.Linq;
using WireMock.Extensions;
using WireMock.Models;
using WireMock.Validation;
namespace WireMock.Matchers
@@ -11,7 +14,7 @@ namespace WireMock.Matchers
/// </summary>
public class JmesPathMatcher : IStringMatcher, IObjectMatcher
{
private readonly string[] _patterns;
private readonly AnyOf<string, StringPattern>[] _patterns;
/// <inheritdoc cref="IMatcher.MatchBehaviour"/>
public MatchBehaviour MatchBehaviour { get; }
@@ -23,7 +26,15 @@ namespace WireMock.Matchers
/// Initializes a new instance of the <see cref="JmesPathMatcher"/> class.
/// </summary>
/// <param name="patterns">The patterns.</param>
public JmesPathMatcher([NotNull] params string[] patterns) : this(MatchBehaviour.AcceptOnMatch, false, patterns)
public JmesPathMatcher([NotNull] params string[] patterns) : this(MatchBehaviour.AcceptOnMatch, false, patterns.ToAnyOfPatterns())
{
}
/// <summary>
/// Initializes a new instance of the <see cref="JmesPathMatcher"/> class.
/// </summary>
/// <param name="patterns">The patterns.</param>
public JmesPathMatcher([NotNull] params AnyOf<string, StringPattern>[] patterns) : this(MatchBehaviour.AcceptOnMatch, false, patterns)
{
}
@@ -32,7 +43,7 @@ namespace WireMock.Matchers
/// </summary>
/// <param name="throwException">Throw an exception when the internal matching fails because of invalid input.</param>
/// <param name="patterns">The patterns.</param>
public JmesPathMatcher(bool throwException = false, [NotNull] params string[] patterns) : this(MatchBehaviour.AcceptOnMatch, throwException, patterns)
public JmesPathMatcher(bool throwException = false, [NotNull] params AnyOf<string, StringPattern>[] patterns) : this(MatchBehaviour.AcceptOnMatch, throwException, patterns)
{
}
@@ -42,7 +53,7 @@ namespace WireMock.Matchers
/// <param name="matchBehaviour">The match behaviour.</param>
/// <param name="throwException">Throw an exception when the internal matching fails because of invalid input.</param>
/// <param name="patterns">The patterns.</param>
public JmesPathMatcher(MatchBehaviour matchBehaviour, bool throwException = false, [NotNull] params string[] patterns)
public JmesPathMatcher(MatchBehaviour matchBehaviour, bool throwException = false, [NotNull] params AnyOf<string, StringPattern>[] patterns)
{
Check.NotNull(patterns, nameof(patterns));
@@ -59,7 +70,7 @@ namespace WireMock.Matchers
{
try
{
match = MatchScores.ToScore(_patterns.Select(pattern => bool.Parse(new JmesPath().Transform(input, pattern))));
match = MatchScores.ToScore(_patterns.Select(pattern => bool.Parse(new JmesPath().Transform(input, pattern.GetPattern()))));
}
catch (JsonException)
{
@@ -89,7 +100,7 @@ namespace WireMock.Matchers
}
/// <inheritdoc cref="IStringMatcher.GetPatterns"/>
public string[] GetPatterns()
public AnyOf<string, StringPattern>[] GetPatterns()
{
return _patterns;
}

View File

@@ -1,87 +1,38 @@
using System.Collections.Generic;
using System.Linq;
using JetBrains.Annotations;
using Newtonsoft.Json.Linq;
namespace WireMock.Matchers
{
/// <summary>
/// JsonPartialMatcher
/// </summary>
public class JsonPartialMatcher : JsonMatcher
public class JsonPartialMatcher : AbstractJsonPartialMatcher
{
/// <inheritdoc cref="IMatcher.Name"/>
public override string Name => "JsonPartialMatcher";
/// <inheritdoc />
public override string Name => nameof(JsonPartialMatcher);
/// <summary>
/// Initializes a new instance of the <see cref="JsonPartialMatcher"/> class.
/// </summary>
/// <param name="value">The string value to check for equality.</param>
/// <param name="ignoreCase">Ignore the case from the PropertyName and PropertyValue (string only).</param>
/// <param name="throwException">Throw an exception when the internal matching fails because of invalid input.</param>
public JsonPartialMatcher([NotNull] string value, bool ignoreCase = false, bool throwException = false)
/// <inheritdoc />
public JsonPartialMatcher([NotNull] string value, bool ignoreCase = false, bool throwException = false)
: base(value, ignoreCase, throwException)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="JsonPartialMatcher"/> class.
/// </summary>
/// <param name="value">The object value to check for equality.</param>
/// <param name="ignoreCase">Ignore the case from the PropertyName and PropertyValue (string only).</param>
/// <param name="throwException">Throw an exception when the internal matching fails because of invalid input.</param>
public JsonPartialMatcher([NotNull] object value, bool ignoreCase = false, bool throwException = false)
/// <inheritdoc />
public JsonPartialMatcher([NotNull] object value, bool ignoreCase = false, bool throwException = false)
: base(value, ignoreCase, throwException)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="JsonPartialMatcher"/> class.
/// </summary>
/// <param name="matchBehaviour">The match behaviour.</param>
/// <param name="value">The value to check for equality.</param>
/// <param name="ignoreCase">Ignore the case from the PropertyName and PropertyValue (string only).</param>
/// <param name="throwException">Throw an exception when the internal matching fails because of invalid input.</param>
public JsonPartialMatcher(MatchBehaviour matchBehaviour, [NotNull] object value, bool ignoreCase = false, bool throwException = false)
/// <inheritdoc />
public JsonPartialMatcher(MatchBehaviour matchBehaviour, [NotNull] object value, bool ignoreCase = false, bool throwException = false)
: base(matchBehaviour, value, ignoreCase, throwException)
{
}
/// <inheritdoc />
protected override bool IsMatch(JToken value, JToken input)
protected override bool IsMatch(string value, string input)
{
if (value == null || value == input)
{
return true;
}
if (input == null || value.Type != input.Type)
{
return false;
}
switch (value.Type)
{
case JTokenType.Object:
var nestedValues = value.ToObject<Dictionary<string, JToken>>();
return nestedValues?.Any() != true ||
nestedValues.All(pair => IsMatch(pair.Value, input.SelectToken(pair.Key)));
case JTokenType.Array:
var valuesArray = value.ToObject<JToken[]>();
var tokenArray = input.ToObject<JToken[]>();
if (valuesArray?.Any() != true)
{
return true;
}
return tokenArray?.Any() == true &&
valuesArray.All(subFilter => tokenArray.Any(subToken => IsMatch(subFilter, subToken)));
default:
return value.ToString() == input.ToString();
}
var exactStringMatcher = new ExactMatcher(MatchBehaviour.AcceptOnMatch, ThrowException, value);
return MatchScores.IsPerfect(exactStringMatcher.IsMatch(input));
}
}
}

View File

@@ -0,0 +1,38 @@
using JetBrains.Annotations;
namespace WireMock.Matchers
{
/// <summary>
/// JsonPartialWildCardMatcher
/// </summary>
public class JsonPartialWildcardMatcher : AbstractJsonPartialMatcher
{
/// <inheritdoc />
public override string Name => nameof(JsonPartialWildcardMatcher);
/// <inheritdoc />
public JsonPartialWildcardMatcher([NotNull] string value, bool ignoreCase = false, bool throwException = false)
: base(value, ignoreCase, throwException)
{
}
/// <inheritdoc />
public JsonPartialWildcardMatcher([NotNull] object value, bool ignoreCase = false, bool throwException = false)
: base(value, ignoreCase, throwException)
{
}
/// <inheritdoc />
public JsonPartialWildcardMatcher(MatchBehaviour matchBehaviour, [NotNull] object value, bool ignoreCase = false, bool throwException = false)
: base(matchBehaviour, value, ignoreCase, throwException)
{
}
/// <inheritdoc />
protected override bool IsMatch(string value, string input)
{
var wildcardStringMatcher = new WildcardMatcher(MatchBehaviour.AcceptOnMatch, value, IgnoreCase);
return MatchScores.IsPerfect(wildcardStringMatcher.IsMatch(input));
}
}
}

View File

@@ -1,8 +1,12 @@
using System.Linq;
using System.Linq;
using System.Linq.Dynamic.Core;
using AnyOfTypes;
using JetBrains.Annotations;
using Newtonsoft.Json.Linq;
using WireMock.Extensions;
using WireMock.Models;
using WireMock.Util;
using WireMock.Validation;
namespace WireMock.Matchers
{
@@ -13,7 +17,7 @@ namespace WireMock.Matchers
/// <inheritdoc cref="IStringMatcher"/>
public class LinqMatcher : IObjectMatcher, IStringMatcher
{
private readonly string[] _patterns;
private readonly AnyOf<string, StringPattern>[] _patterns;
/// <inheritdoc cref="IMatcher.MatchBehaviour"/>
public MatchBehaviour MatchBehaviour { get; }
@@ -24,7 +28,7 @@ namespace WireMock.Matchers
/// Initializes a new instance of the <see cref="LinqMatcher"/> class.
/// </summary>
/// <param name="pattern">The pattern.</param>
public LinqMatcher([NotNull] string pattern) : this(new[] { pattern })
public LinqMatcher([NotNull] AnyOf<string, StringPattern> pattern) : this(new[] { pattern })
{
}
@@ -32,7 +36,7 @@ namespace WireMock.Matchers
/// Initializes a new instance of the <see cref="LinqMatcher"/> class.
/// </summary>
/// <param name="patterns">The patterns.</param>
public LinqMatcher([NotNull] params string[] patterns) : this(MatchBehaviour.AcceptOnMatch, false, patterns)
public LinqMatcher([NotNull] params AnyOf<string, StringPattern>[] patterns) : this(MatchBehaviour.AcceptOnMatch, false, patterns)
{
}
@@ -41,7 +45,7 @@ namespace WireMock.Matchers
/// </summary>
/// <param name="matchBehaviour">The match behaviour.</param>
/// <param name="pattern">The pattern.</param>
public LinqMatcher(MatchBehaviour matchBehaviour, [NotNull] string pattern) : this(matchBehaviour, false, pattern)
public LinqMatcher(MatchBehaviour matchBehaviour, [NotNull] AnyOf<string, StringPattern> pattern) : this(matchBehaviour, false, pattern)
{
}
@@ -51,8 +55,10 @@ namespace WireMock.Matchers
/// <param name="matchBehaviour">The match behaviour.</param>
/// <param name="patterns">The patterns.</param>
/// <param name="throwException">Throw an exception when the internal matching fails because of invalid input.</param>
public LinqMatcher(MatchBehaviour matchBehaviour, bool throwException = false, [NotNull] params string[] patterns)
public LinqMatcher(MatchBehaviour matchBehaviour, bool throwException = false, [NotNull] params AnyOf<string, StringPattern>[] patterns)
{
Check.NotNull(patterns, nameof(patterns));
MatchBehaviour = matchBehaviour;
ThrowException = throwException;
_patterns = patterns;
@@ -69,7 +75,7 @@ namespace WireMock.Matchers
try
{
// Use the Any(...) method to check if the result matches
match = MatchScores.ToScore(_patterns.Select(pattern => queryable.Any(pattern)));
match = MatchScores.ToScore(_patterns.Select(pattern => queryable.Any(pattern.GetPattern())));
return MatchBehaviourHelper.Convert(MatchBehaviour, match);
}
@@ -129,7 +135,7 @@ namespace WireMock.Matchers
}
/// <inheritdoc cref="IStringMatcher.GetPatterns"/>
public string[] GetPatterns()
public AnyOf<string, StringPattern>[] GetPatterns()
{
return _patterns;
}

View File

@@ -1,4 +1,6 @@
using System.Linq;
using AnyOfTypes;
using WireMock.Models;
namespace WireMock.Matchers
{
@@ -6,7 +8,7 @@ namespace WireMock.Matchers
/// NotNullOrEmptyMatcher
/// </summary>
/// <seealso cref="IObjectMatcher" />
public class NotNullOrEmptyMatcher : IObjectMatcher
public class NotNullOrEmptyMatcher : IObjectMatcher, IStringMatcher
{
/// <inheritdoc cref="IMatcher.Name"/>
public string Name => "NotNullOrEmptyMatcher";
@@ -48,5 +50,19 @@ namespace WireMock.Matchers
return MatchBehaviourHelper.Convert(MatchBehaviour, MatchScores.ToScore(match));
}
/// <inheritdoc cref="IStringMatcher.IsMatch"/>
public double IsMatch(string input)
{
var match = !string.IsNullOrEmpty(input);
return MatchBehaviourHelper.Convert(MatchBehaviour, MatchScores.ToScore(match));
}
/// <inheritdoc cref="IStringMatcher.GetPatterns"/>
public AnyOf<string, StringPattern>[] GetPatterns()
{
return new AnyOf<string, StringPattern>[0];
}
}
}

View File

@@ -1,7 +1,10 @@
using System;
using System;
using System.Linq;
using System.Text.RegularExpressions;
using AnyOfTypes;
using JetBrains.Annotations;
using WireMock.Extensions;
using WireMock.Models;
using WireMock.Validation;
namespace WireMock.Matchers
@@ -13,7 +16,7 @@ namespace WireMock.Matchers
/// <inheritdoc cref="IIgnoreCaseMatcher"/>
public class RegexMatcher : IStringMatcher, IIgnoreCaseMatcher
{
private readonly string[] _patterns;
private readonly AnyOf<string, StringPattern>[] _patterns;
private readonly Regex[] _expressions;
/// <inheritdoc cref="IMatcher.MatchBehaviour"/>
@@ -27,7 +30,7 @@ namespace WireMock.Matchers
/// </summary>
/// <param name="pattern">The pattern.</param>
/// <param name="ignoreCase">Ignore the case from the pattern.</param>
public RegexMatcher([NotNull, RegexPattern] string pattern, bool ignoreCase = false) : this(new[] { pattern }, ignoreCase)
public RegexMatcher([NotNull, RegexPattern] AnyOf<string, StringPattern> pattern, bool ignoreCase = false) : this(new[] { pattern }, ignoreCase)
{
}
@@ -37,7 +40,7 @@ namespace WireMock.Matchers
/// <param name="matchBehaviour">The match behaviour.</param>
/// <param name="pattern">The pattern.</param>
/// <param name="ignoreCase">Ignore the case from the pattern.</param>
public RegexMatcher(MatchBehaviour matchBehaviour, [NotNull, RegexPattern] string pattern, bool ignoreCase = false) : this(matchBehaviour, new[] { pattern }, ignoreCase)
public RegexMatcher(MatchBehaviour matchBehaviour, [NotNull, RegexPattern] AnyOf<string, StringPattern> pattern, bool ignoreCase = false) : this(matchBehaviour, new[] { pattern }, ignoreCase)
{
}
@@ -46,7 +49,7 @@ namespace WireMock.Matchers
/// </summary>
/// <param name="patterns">The patterns.</param>
/// <param name="ignoreCase">Ignore the case from the pattern.</param>
public RegexMatcher([NotNull, RegexPattern] string[] patterns, bool ignoreCase = false) : this(MatchBehaviour.AcceptOnMatch, patterns, ignoreCase)
public RegexMatcher([NotNull, RegexPattern] AnyOf<string, StringPattern>[] patterns, bool ignoreCase = false) : this(MatchBehaviour.AcceptOnMatch, patterns, ignoreCase)
{
}
@@ -57,7 +60,7 @@ namespace WireMock.Matchers
/// <param name="patterns">The patterns.</param>
/// <param name="ignoreCase">Ignore the case from the pattern.</param>
/// <param name="throwException">Throw an exception when the internal matching fails because of invalid input.</param>
public RegexMatcher(MatchBehaviour matchBehaviour, [NotNull, RegexPattern] string[] patterns, bool ignoreCase = false, bool throwException = false)
public RegexMatcher(MatchBehaviour matchBehaviour, [NotNull, RegexPattern] AnyOf<string, StringPattern>[] patterns, bool ignoreCase = false, bool throwException = false)
{
Check.NotNull(patterns, nameof(patterns));
@@ -73,7 +76,7 @@ namespace WireMock.Matchers
options |= RegexOptions.IgnoreCase;
}
_expressions = patterns.Select(p => new Regex(p, options)).ToArray();
_expressions = patterns.Select(p => new Regex(p.GetPattern(), options)).ToArray();
}
/// <inheritdoc cref="IStringMatcher.IsMatch"/>
@@ -99,7 +102,7 @@ namespace WireMock.Matchers
}
/// <inheritdoc cref="IStringMatcher.GetPatterns"/>
public virtual string[] GetPatterns()
public virtual AnyOf<string, StringPattern>[] GetPatterns()
{
return _patterns;
}

View File

@@ -1,8 +1,11 @@
using System.Linq;
using System.Linq;
using AnyOfTypes;
using JetBrains.Annotations;
using SimMetrics.Net;
using SimMetrics.Net.API;
using SimMetrics.Net.Metric;
using WireMock.Extensions;
using WireMock.Models;
using WireMock.Validation;
namespace WireMock.Matchers
@@ -13,7 +16,7 @@ namespace WireMock.Matchers
/// <seealso cref="IStringMatcher" />
public class SimMetricsMatcher : IStringMatcher
{
private readonly string[] _patterns;
private readonly AnyOf<string, StringPattern>[] _patterns;
private readonly SimMetricType _simMetricType;
/// <inheritdoc cref="IMatcher.MatchBehaviour"/>
@@ -27,7 +30,7 @@ namespace WireMock.Matchers
/// </summary>
/// <param name="pattern">The pattern.</param>
/// <param name="simMetricType">The SimMetric Type</param>
public SimMetricsMatcher([NotNull] string pattern, SimMetricType simMetricType = SimMetricType.Levenstein) : this(new[] { pattern }, simMetricType)
public SimMetricsMatcher([NotNull] AnyOf<string, StringPattern> pattern, SimMetricType simMetricType = SimMetricType.Levenstein) : this(new[] { pattern }, simMetricType)
{
}
@@ -37,7 +40,7 @@ namespace WireMock.Matchers
/// <param name="matchBehaviour">The match behaviour.</param>
/// <param name="pattern">The pattern.</param>
/// <param name="simMetricType">The SimMetric Type</param>
public SimMetricsMatcher(MatchBehaviour matchBehaviour, [NotNull] string pattern, SimMetricType simMetricType = SimMetricType.Levenstein) : this(matchBehaviour, new[] { pattern }, simMetricType)
public SimMetricsMatcher(MatchBehaviour matchBehaviour, [NotNull] AnyOf<string, StringPattern> pattern, SimMetricType simMetricType = SimMetricType.Levenstein) : this(matchBehaviour, new[] { pattern }, simMetricType)
{
}
@@ -46,7 +49,16 @@ namespace WireMock.Matchers
/// </summary>
/// <param name="patterns">The patterns.</param>
/// <param name="simMetricType">The SimMetric Type</param>
public SimMetricsMatcher([NotNull] string[] patterns, SimMetricType simMetricType = SimMetricType.Levenstein) : this(MatchBehaviour.AcceptOnMatch, patterns, simMetricType)
public SimMetricsMatcher([NotNull] string[] patterns, SimMetricType simMetricType = SimMetricType.Levenstein) : this(MatchBehaviour.AcceptOnMatch, patterns.ToAnyOfPatterns(), simMetricType)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="SimMetricsMatcher"/> class.
/// </summary>
/// <param name="patterns">The patterns.</param>
/// <param name="simMetricType">The SimMetric Type</param>
public SimMetricsMatcher([NotNull] AnyOf<string, StringPattern>[] patterns, SimMetricType simMetricType = SimMetricType.Levenstein) : this(MatchBehaviour.AcceptOnMatch, patterns, simMetricType)
{
}
@@ -57,9 +69,9 @@ namespace WireMock.Matchers
/// <param name="patterns">The patterns.</param>
/// <param name="simMetricType">The SimMetric Type</param>
/// <param name="throwException">Throw an exception when the internal matching fails because of invalid input.</param>
public SimMetricsMatcher(MatchBehaviour matchBehaviour, [NotNull] string[] patterns, SimMetricType simMetricType = SimMetricType.Levenstein, bool throwException = false)
public SimMetricsMatcher(MatchBehaviour matchBehaviour, [NotNull] AnyOf<string, StringPattern>[] patterns, SimMetricType simMetricType = SimMetricType.Levenstein, bool throwException = false)
{
Check.NotNullOrEmpty(patterns, nameof(patterns));
Check.NotNull(patterns, nameof(patterns));
MatchBehaviour = matchBehaviour;
ThrowException = throwException;
@@ -71,9 +83,9 @@ namespace WireMock.Matchers
/// <inheritdoc cref="IStringMatcher.IsMatch"/>
public double IsMatch(string input)
{
IStringMetric m = GetStringMetricType();
IStringMetric stringmetricType = GetStringMetricType();
return MatchBehaviourHelper.Convert(MatchBehaviour, MatchScores.ToScore(_patterns.Select(p => m.GetSimilarity(p, input))));
return MatchBehaviourHelper.Convert(MatchBehaviour, MatchScores.ToScore(_patterns.Select(p => stringmetricType.GetSimilarity(p.GetPattern(), input))));
}
private IStringMetric GetStringMetricType()
@@ -120,7 +132,7 @@ namespace WireMock.Matchers
}
/// <inheritdoc cref="IStringMatcher.GetPatterns"/>
public string[] GetPatterns()
public AnyOf<string, StringPattern>[] GetPatterns()
{
return _patterns;
}

View File

@@ -1,6 +1,9 @@
using System.Linq;
using System.Linq;
using System.Text.RegularExpressions;
using AnyOfTypes;
using JetBrains.Annotations;
using WireMock.Extensions;
using WireMock.Models;
namespace WireMock.Matchers
{
@@ -10,14 +13,14 @@ namespace WireMock.Matchers
/// <seealso cref="RegexMatcher" />
public class WildcardMatcher : RegexMatcher
{
private readonly string[] _patterns;
private readonly AnyOf<string, StringPattern>[] _patterns;
/// <summary>
/// Initializes a new instance of the <see cref="WildcardMatcher"/> class.
/// </summary>
/// <param name="pattern">The pattern.</param>
/// <param name="ignoreCase">IgnoreCase</param>
public WildcardMatcher([NotNull] string pattern, bool ignoreCase = false) : this(new[] { pattern }, ignoreCase)
public WildcardMatcher([NotNull] AnyOf<string, StringPattern> pattern, bool ignoreCase = false) : this(new[] { pattern }, ignoreCase)
{
}
@@ -27,7 +30,7 @@ namespace WireMock.Matchers
/// <param name="matchBehaviour">The match behaviour.</param>
/// <param name="pattern">The pattern.</param>
/// <param name="ignoreCase">IgnoreCase</param>
public WildcardMatcher(MatchBehaviour matchBehaviour, [NotNull] string pattern, bool ignoreCase = false) : this(matchBehaviour, new[] { pattern }, ignoreCase)
public WildcardMatcher(MatchBehaviour matchBehaviour, [NotNull] AnyOf<string, StringPattern> pattern, bool ignoreCase = false) : this(matchBehaviour, new[] { pattern }, ignoreCase)
{
}
@@ -36,7 +39,7 @@ namespace WireMock.Matchers
/// </summary>
/// <param name="patterns">The patterns.</param>
/// <param name="ignoreCase">IgnoreCase</param>
public WildcardMatcher([NotNull] string[] patterns, bool ignoreCase = false) : this(MatchBehaviour.AcceptOnMatch, patterns, ignoreCase)
public WildcardMatcher([NotNull] AnyOf<string, StringPattern>[] patterns, bool ignoreCase = false) : this(MatchBehaviour.AcceptOnMatch, patterns, ignoreCase)
{
}
@@ -47,19 +50,30 @@ namespace WireMock.Matchers
/// <param name="patterns">The patterns.</param>
/// <param name="ignoreCase">IgnoreCase</param>
/// <param name="throwException">Throw an exception when the internal matching fails because of invalid input.</param>
public WildcardMatcher(MatchBehaviour matchBehaviour, [NotNull] string[] patterns, bool ignoreCase = false, bool throwException = false) :
base(matchBehaviour, patterns.Select(pattern => "^" + Regex.Escape(pattern).Replace(@"\*", ".*").Replace(@"\?", ".") + "$").ToArray(), ignoreCase, throwException)
public WildcardMatcher(MatchBehaviour matchBehaviour, [NotNull] AnyOf<string, StringPattern>[] patterns, bool ignoreCase = false, bool throwException = false) :
base(matchBehaviour, CreateArray(patterns), ignoreCase, throwException)
{
_patterns = patterns;
}
/// <inheritdoc cref="IStringMatcher.GetPatterns"/>
public override string[] GetPatterns()
public override AnyOf<string, StringPattern>[] GetPatterns()
{
return _patterns;
}
/// <inheritdoc cref="IMatcher.Name"/>
public override string Name => "WildcardMatcher";
private static AnyOf<string, StringPattern>[] CreateArray(AnyOf<string, StringPattern>[] patterns)
{
return patterns.Select(pattern => new AnyOf<string, StringPattern>(
new StringPattern
{
Pattern = "^" + Regex.Escape(pattern.GetPattern()).Replace(@"\*", ".*").Replace(@"\?", ".") + "$",
PatternAsFile = pattern.IsSecond ? pattern.Second.PatternAsFile : null
}))
.ToArray();
}
}
}

View File

@@ -1,7 +1,10 @@
using System;
using System;
using System.Linq;
using System.Xml;
using AnyOfTypes;
using JetBrains.Annotations;
using WireMock.Extensions;
using WireMock.Models;
using WireMock.Validation;
#if !NETSTANDARD1_3
using Wmhelp.XPath2;
@@ -15,7 +18,7 @@ namespace WireMock.Matchers
/// <seealso cref="IStringMatcher" />
public class XPathMatcher : IStringMatcher
{
private readonly string[] _patterns;
private readonly AnyOf<string, StringPattern>[] _patterns;
/// <inheritdoc cref="IMatcher.MatchBehaviour"/>
public MatchBehaviour MatchBehaviour { get; }
@@ -27,7 +30,7 @@ namespace WireMock.Matchers
/// Initializes a new instance of the <see cref="XPathMatcher"/> class.
/// </summary>
/// <param name="patterns">The patterns.</param>
public XPathMatcher([NotNull] params string[] patterns) : this(MatchBehaviour.AcceptOnMatch, false, patterns)
public XPathMatcher([NotNull] params AnyOf<string, StringPattern>[] patterns) : this(MatchBehaviour.AcceptOnMatch, false, patterns)
{
}
@@ -37,7 +40,7 @@ namespace WireMock.Matchers
/// <param name="matchBehaviour">The match behaviour.</param>
/// <param name="throwException">Throw an exception when the internal matching fails because of invalid input.</param>
/// <param name="patterns">The patterns.</param>
public XPathMatcher(MatchBehaviour matchBehaviour, bool throwException = false, [NotNull] params string[] patterns)
public XPathMatcher(MatchBehaviour matchBehaviour, bool throwException = false, [NotNull] params AnyOf<string, StringPattern>[] patterns)
{
Check.NotNull(patterns, nameof(patterns));
@@ -56,9 +59,9 @@ namespace WireMock.Matchers
{
var nav = new XmlDocument { InnerXml = input }.CreateNavigator();
#if NETSTANDARD1_3
match = MatchScores.ToScore(_patterns.Select(p => true.Equals(nav.Evaluate($"boolean({p})"))));
match = MatchScores.ToScore(_patterns.Select(p => true.Equals(nav.Evaluate($"boolean({p.GetPattern()})"))));
#else
match = MatchScores.ToScore(_patterns.Select(p => true.Equals(nav.XPath2Evaluate($"boolean({p})"))));
match = MatchScores.ToScore(_patterns.Select(p => true.Equals(nav.XPath2Evaluate($"boolean({p.GetPattern()})"))));
#endif
}
catch (Exception)
@@ -74,7 +77,7 @@ namespace WireMock.Matchers
}
/// <inheritdoc cref="IStringMatcher.GetPatterns"/>
public string[] GetPatterns()
public AnyOf<string, StringPattern>[] GetPatterns()
{
return _patterns;
}

View File

@@ -0,0 +1,18 @@
namespace WireMock.Models
{
/// <summary>
/// StringPattern which defines the Pattern as a string, and optionally the filepath pattern file.
/// </summary>
public struct StringPattern
{
/// <summary>
/// The pattern as string.
/// </summary>
public string Pattern { get; set; }
/// <summary>
/// The filepath (optionally)
/// </summary>
public string PatternAsFile { get; set; }
}
}

View File

@@ -0,0 +1,19 @@
using System;
namespace WireMock.Models
{
/// <summary>
/// TimeSettingsModel: Start, End and TTL
/// </summary>
public class TimeSettings : ITimeSettings
{
/// <inheritdoc />
public DateTime? Start { get; set; }
/// <inheritdoc />
public DateTime? End { get; set; }
/// <inheritdoc />
public int? TTL { get; set; }
}
}

View File

@@ -1,4 +1,4 @@
#if USE_ASPNETCORE
#if USE_ASPNETCORE
using System;
using System.Collections.Generic;
using System.Linq;
@@ -118,17 +118,17 @@ namespace WireMock.Owin
});
#if NETSTANDARD1_3
_logger.Info("WireMock.Net server using netstandard1.3");
_logger.Info("Server using netstandard1.3");
#elif NETSTANDARD2_0
_logger.Info("WireMock.Net server using netstandard2.0");
_logger.Info("Server using netstandard2.0");
#elif NETSTANDARD2_1
_logger.Info("WireMock.Net server using netstandard2.1");
_logger.Info("Server using netstandard2.1");
#elif NETCOREAPP3_1
_logger.Info("WireMock.Net server using .NET Core 3.1");
_logger.Info("Server using .NET Core 3.1");
#elif NET5_0
_logger.Info("WireMock.Net server using .NET 5.0");
_logger.Info("Server using .NET 5.0");
#elif NET46
_logger.Info("WireMock.Net server using .NET Framework 4.6.1 or higher");
_logger.Info("Server using .NET Framework 4.6.1 or higher");
#endif
#if NETSTANDARD1_3

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Concurrent;
using WireMock.Handlers;
using WireMock.Logging;
@@ -19,7 +19,7 @@ namespace WireMock.Owin
TimeSpan? RequestProcessingDelay { get; set; }
IStringMatcher AuthorizationMatcher { get; set; }
IStringMatcher AuthenticationMatcher { get; set; }
bool? AllowPartialMapping { get; set; }

View File

@@ -1,6 +1,7 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using WireMock.Extensions;
using WireMock.Validation;
namespace WireMock.Owin
@@ -19,7 +20,8 @@ namespace WireMock.Owin
public (MappingMatcherResult Match, MappingMatcherResult Partial) FindBestMatch(RequestMessage request)
{
var mappings = new List<MappingMatcherResult>();
foreach (var mapping in _options.Mappings.Values)
foreach (var mapping in _options.Mappings.Values.Where(m => m.TimeSettings.IsValid()))
{
try
{

View File

@@ -1,4 +1,4 @@
#if !USE_ASPNETCORE
#if !USE_ASPNETCORE
using JetBrains.Annotations;
using Microsoft.Owin.Hosting;
using Owin;
@@ -63,9 +63,9 @@ namespace WireMock.Owin
private void StartServers()
{
#if NET46
_logger.Info("WireMock.Net server using .net 4.6.1 or higher");
_logger.Info("Server using .net 4.6.1 or higher");
#else
_logger.Info("WireMock.Net server using .net 4.5.x");
_logger.Info("Server using .net 4.5.x");
#endif
var servers = new List<IDisposable>();

View File

@@ -113,10 +113,10 @@ namespace WireMock.Owin
logRequest = targetMapping.LogMapping;
if (targetMapping.IsAdminInterface && _options.AuthorizationMatcher != null)
if (targetMapping.IsAdminInterface && _options.AuthenticationMatcher != null)
{
bool present = request.Headers.TryGetValue(HttpKnownHeaderNames.Authorization, out WireMockList<string> authorization);
if (!present || _options.AuthorizationMatcher.IsMatch(authorization.ToString()) < MatchScores.Perfect)
if (!present || _options.AuthenticationMatcher.IsMatch(authorization.ToString()) < MatchScores.Perfect)
{
_options.Logger.Error("HttpStatusCode set to 401");
response = ResponseMessageBuilder.Create(null, 401);

View File

@@ -19,7 +19,7 @@ namespace WireMock.Owin
public TimeSpan? RequestProcessingDelay { get; set; }
public IStringMatcher AuthorizationMatcher { get; set; }
public IStringMatcher AuthenticationMatcher { get; set; }
public bool? AllowPartialMapping { get; set; }

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
@@ -21,8 +21,7 @@ namespace WireMock.Proxy
public ProxyHelper([NotNull] IWireMockServerSettings settings)
{
Check.NotNull(settings, nameof(settings));
_settings = settings;
_settings = Check.NotNull(settings, nameof(settings));
}
public async Task<(ResponseMessage Message, IMapping Mapping)> SendAsync(
@@ -105,7 +104,7 @@ namespace WireMock.Proxy
var response = Response.Create(responseMessage);
return new Mapping(Guid.NewGuid(), string.Empty, null, _settings, request, response, 0, null, null, null, null, null);
return new Mapping(Guid.NewGuid(), string.Empty, null, _settings, request, response, 0, null, null, null, null, null, null);
}
}
}

View File

@@ -20,5 +20,13 @@ namespace WireMock.ResponseBuilders
/// <param name="milliseconds">The milliseconds to delay.</param>
/// <returns>The <see cref="IResponseBuilder"/>.</returns>
IResponseBuilder WithDelay(int milliseconds);
/// <summary>
/// Introduce random delay
/// </summary>
/// <param name="minimumMilliseconds">Minimum milliseconds to delay</param>
/// <param name="maximumMilliseconds">Maximum milliseconds to delay</param>
/// <returns>The <see cref="IResponseBuilder"/>.</returns>
IResponseBuilder WithRandomDelay(int minimumMilliseconds = 0, int maximumMilliseconds = 60_000);
}
}

View File

@@ -5,6 +5,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using JetBrains.Annotations;
using WireMock.Proxy;
@@ -24,10 +25,40 @@ namespace WireMock.ResponseBuilders
/// </summary>
public partial class Response : IResponseBuilder
{
private static readonly ThreadLocal<Random> Random = new ThreadLocal<Random>(() => new Random(DateTime.UtcNow.Millisecond));
private TimeSpan? _delay;
/// <summary>
/// The minimum random delay in milliseconds.
/// </summary>
public int? MinimumDelayMilliseconds { get; private set; }
/// <summary>
/// The maximum random delay in milliseconds.
/// </summary>
public int? MaximumDelayMilliseconds { get; private set; }
/// <summary>
/// The delay
/// </summary>
public TimeSpan? Delay { get; private set; }
public TimeSpan? Delay
{
get
{
if (MinimumDelayMilliseconds != null && MaximumDelayMilliseconds != null)
{
return TimeSpan.FromMilliseconds(Random.Value.Next(MinimumDelayMilliseconds.Value, MaximumDelayMilliseconds.Value));
}
return _delay;
}
private set
{
_delay = value;
}
}
/// <summary>
/// Gets a value indicating whether [use transformer].
@@ -334,6 +365,18 @@ namespace WireMock.ResponseBuilders
return WithDelay(TimeSpan.FromMilliseconds(milliseconds));
}
/// <inheritdoc cref="IDelayResponseBuilder.WithRandomDelay(int, int)"/>
public IResponseBuilder WithRandomDelay(int minimumMilliseconds = 0, int maximumMilliseconds = 60_000)
{
Check.Condition(minimumMilliseconds, min => min >= 0, nameof(minimumMilliseconds));
Check.Condition(maximumMilliseconds, max => max > minimumMilliseconds, nameof(maximumMilliseconds));
MinimumDelayMilliseconds = minimumMilliseconds;
MaximumDelayMilliseconds = maximumMilliseconds;
return this;
}
/// <inheritdoc cref="IResponseProvider.ProvideResponseAsync(RequestMessage, IWireMockServerSettings)"/>
public async Task<(ResponseMessage Message, IMapping Mapping)> ProvideResponseAsync(RequestMessage requestMessage, IWireMockServerSettings settings)
{

View File

@@ -1,12 +1,13 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Linq;
using WireMock.Admin.Mappings;
using WireMock.Matchers.Request;
using WireMock.Models;
using WireMock.RequestBuilders;
using WireMock.ResponseBuilders;
using WireMock.Settings;
using WireMock.Types;
using WireMock.Validation;
namespace WireMock.Serialization
{
@@ -16,9 +17,7 @@ namespace WireMock.Serialization
public MappingConverter(MatcherMapper mapper)
{
Check.NotNull(mapper, nameof(mapper));
_mapper = mapper;
_mapper = mapper ?? throw new ArgumentNullException(nameof(mapper));
}
public MappingModel ToMappingModel(IMapping mapping)
@@ -38,6 +37,7 @@ namespace WireMock.Serialization
var mappingModel = new MappingModel
{
Guid = mapping.Guid,
TimeSettings = TimeSettingsMapper.Map(mapping.TimeSettings),
Title = mapping.Title,
Priority = mapping.Priority != 0 ? mapping.Priority : (int?)null,
Scenario = mapping.Scenario,
@@ -81,12 +81,19 @@ namespace WireMock.Serialization
Matchers = _mapper.Map(pm.Matchers)
}).ToList() : null
},
Response = new ResponseModel
{
Delay = (int?)response.Delay?.TotalMilliseconds
}
Response = new ResponseModel()
};
if (response.MinimumDelayMilliseconds >= 0 || response.MaximumDelayMilliseconds > 0)
{
mappingModel.Response.MinimumRandomDelay = response.MinimumDelayMilliseconds;
mappingModel.Response.MaximumRandomDelay = response.MaximumDelayMilliseconds;
}
else
{
mappingModel.Response.Delay = (int?)response.Delay?.TotalMilliseconds;
}
if (mapping.Webhooks?.Length == 1)
{
mappingModel.Webhook = WebhookMapper.Map(mapping.Webhooks[0]);

View File

@@ -1,13 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using AnyOfTypes;
using JetBrains.Annotations;
using SimMetrics.Net;
using WireMock.Admin.Mappings;
using WireMock.Extensions;
using WireMock.Matchers;
using WireMock.Models;
using WireMock.Plugin;
using WireMock.Settings;
using WireMock.Validation;
namespace WireMock.Serialization
{
@@ -17,8 +19,7 @@ namespace WireMock.Serialization
public MatcherMapper(IWireMockServerSettings settings)
{
Check.NotNull(settings, nameof(settings));
_settings = settings;
_settings = settings ?? throw new ArgumentNullException(nameof(settings));
}
public IMatcher[] Map([CanBeNull] IEnumerable<MatcherModel> matchers)
@@ -36,15 +37,14 @@ namespace WireMock.Serialization
string[] parts = matcher.Name.Split('.');
string matcherName = parts[0];
string matcherType = parts.Length > 1 ? parts[1] : null;
string[] stringPatterns = (matcher.Patterns != null ? matcher.Patterns : new[] { matcher.Pattern }).OfType<string>().ToArray();
MatchBehaviour matchBehaviour = matcher.RejectOnMatch == true ? MatchBehaviour.RejectOnMatch : MatchBehaviour.AcceptOnMatch;
var stringPatterns = ParseStringPatterns(matcher);
var matchBehaviour = matcher.RejectOnMatch == true ? MatchBehaviour.RejectOnMatch : MatchBehaviour.AcceptOnMatch;
bool ignoreCase = matcher.IgnoreCase == true;
bool throwExceptionWhenMatcherFails = _settings.ThrowExceptionWhenMatcherFails == true;
switch (matcherName)
{
case "NotNullOrEmptyMatcher":
case nameof(NotNullOrEmptyMatcher):
return new NotNullOrEmptyMatcher(matchBehaviour);
case "CSharpCodeMatcher":
@@ -55,42 +55,46 @@ namespace WireMock.Serialization
throw new NotSupportedException("It's not allowed to use the 'CSharpCodeMatcher' because IWireMockServerSettings.AllowCSharpCodeMatcher is not set to 'true'.");
case "LinqMatcher":
case nameof(LinqMatcher):
return new LinqMatcher(matchBehaviour, throwExceptionWhenMatcherFails, stringPatterns);
case "ExactMatcher":
case nameof(ExactMatcher):
return new ExactMatcher(matchBehaviour, throwExceptionWhenMatcherFails, stringPatterns);
case "ExactObjectMatcher":
case nameof(ExactObjectMatcher):
return CreateExactObjectMatcher(matchBehaviour, stringPatterns[0], throwExceptionWhenMatcherFails);
case "RegexMatcher":
case nameof(RegexMatcher):
return new RegexMatcher(matchBehaviour, stringPatterns, ignoreCase, throwExceptionWhenMatcherFails);
case "JsonMatcher":
object value = matcher.Pattern ?? matcher.Patterns;
return new JsonMatcher(matchBehaviour, value, ignoreCase, throwExceptionWhenMatcherFails);
case nameof(JsonMatcher):
object valueForJsonMatcher = matcher.Pattern ?? matcher.Patterns;
return new JsonMatcher(matchBehaviour, valueForJsonMatcher, ignoreCase, throwExceptionWhenMatcherFails);
case "JsonPartialMatcher":
object matcherValue = matcher.Pattern ?? matcher.Patterns;
return new JsonPartialMatcher(matchBehaviour, matcherValue, ignoreCase, throwExceptionWhenMatcherFails);
case nameof(JsonPartialMatcher):
object valueForJsonPartialMatcher = matcher.Pattern ?? matcher.Patterns;
return new JsonPartialMatcher(matchBehaviour, valueForJsonPartialMatcher, ignoreCase, throwExceptionWhenMatcherFails);
case "JsonPathMatcher":
case nameof(JsonPartialWildcardMatcher):
object valueForJsonPartialWilcardMatcher = matcher.Pattern ?? matcher.Patterns;
return new JsonPartialWildcardMatcher(matchBehaviour, valueForJsonPartialWilcardMatcher, ignoreCase, throwExceptionWhenMatcherFails);
case nameof(JsonPathMatcher):
return new JsonPathMatcher(matchBehaviour, throwExceptionWhenMatcherFails, stringPatterns);
case "JmesPathMatcher":
case nameof(JmesPathMatcher):
return new JmesPathMatcher(matchBehaviour, throwExceptionWhenMatcherFails, stringPatterns);
case "XPathMatcher":
case nameof(XPathMatcher):
return new XPathMatcher(matchBehaviour, throwExceptionWhenMatcherFails, stringPatterns);
case "WildcardMatcher":
case nameof(WildcardMatcher):
return new WildcardMatcher(matchBehaviour, stringPatterns, ignoreCase, throwExceptionWhenMatcherFails);
case "ContentTypeMatcher":
case nameof(ContentTypeMatcher):
return new ContentTypeMatcher(matchBehaviour, stringPatterns, ignoreCase, throwExceptionWhenMatcherFails);
case "SimMetricsMatcher":
case nameof(SimMetricsMatcher):
SimMetricType type = SimMetricType.Levenstein;
if (!string.IsNullOrEmpty(matcherType) && !Enum.TryParse(matcherType, out type))
{
@@ -116,45 +120,85 @@ namespace WireMock.Serialization
return null;
}
object[] patterns = new object[0]; // Default empty array
bool? ignoreCase = matcher is IIgnoreCaseMatcher ignoreCaseMatcher ? ignoreCaseMatcher.IgnoreCase : (bool?)null;
bool? rejectOnMatch = matcher.MatchBehaviour == MatchBehaviour.RejectOnMatch ? true : (bool?)null;
var model = new MatcherModel
{
RejectOnMatch = rejectOnMatch,
IgnoreCase = ignoreCase,
Name = matcher.Name
};
switch (matcher)
{
// If the matcher is a IStringMatcher, get the patterns.
case IStringMatcher stringMatcher:
patterns = stringMatcher.GetPatterns().Cast<object>().ToArray();
var stringPatterns = stringMatcher.GetPatterns();
if (stringPatterns.Length == 1)
{
if (stringPatterns[0].IsFirst)
{
model.Pattern = stringPatterns[0].First;
}
else
{
model.Pattern = stringPatterns[0].Second.Pattern;
model.PatternAsFile = stringPatterns[0].Second.PatternAsFile;
}
}
else
{
model.Patterns = stringPatterns.Select(p => p.GetPattern()).Cast<object>().ToArray();
}
break;
// If the matcher is a IValueMatcher, get the value (can be string or object).
case IValueMatcher valueMatcher:
patterns = new[] { valueMatcher.Value };
model.Patterns = new[] { valueMatcher.Value };
break;
// If the matcher is a ExactObjectMatcher, get the ValueAsObject or ValueAsBytes.
case ExactObjectMatcher exactObjectMatcher:
patterns = new[] { exactObjectMatcher.ValueAsObject ?? exactObjectMatcher.ValueAsBytes };
model.Patterns = new[] { exactObjectMatcher.ValueAsObject ?? exactObjectMatcher.ValueAsBytes };
break;
}
bool? ignoreCase = matcher is IIgnoreCaseMatcher ignoreCaseMatcher ? ignoreCaseMatcher.IgnoreCase : (bool?)null;
bool? rejectOnMatch = matcher.MatchBehaviour == MatchBehaviour.RejectOnMatch ? true : (bool?)null;
return new MatcherModel
{
RejectOnMatch = rejectOnMatch,
IgnoreCase = ignoreCase,
Name = matcher.Name,
Pattern = patterns.Length == 1 ? patterns.First() : null,
Patterns = patterns.Length > 1 ? patterns : null
};
return model;
}
private ExactObjectMatcher CreateExactObjectMatcher(MatchBehaviour matchBehaviour, string stringPattern, bool throwException)
private AnyOf<string, StringPattern>[] ParseStringPatterns(MatcherModel matcher)
{
if (matcher.Pattern is string patternAsString)
{
return new[] { new AnyOf<string, StringPattern>(patternAsString) };
}
if (matcher.Pattern is IEnumerable<string> patternAsStringArray)
{
return patternAsStringArray.ToAnyOfPatterns();
}
if (matcher.Patterns?.OfType<string>() is IEnumerable<string> patternsAsStringArray)
{
return patternsAsStringArray.ToAnyOfPatterns();
}
if (!string.IsNullOrEmpty(matcher.PatternAsFile))
{
var pattern = _settings.FileSystemHandler.ReadFileAsString(matcher.PatternAsFile);
return new[] { new AnyOf<string, StringPattern>(new StringPattern { Pattern = pattern, PatternAsFile = matcher.PatternAsFile }) };
}
return new AnyOf<string, StringPattern>[0];
}
private ExactObjectMatcher CreateExactObjectMatcher(MatchBehaviour matchBehaviour, AnyOf<string, StringPattern> stringPattern, bool throwException)
{
byte[] bytePattern;
try
{
bytePattern = Convert.FromBase64String(stringPattern);
bytePattern = Convert.FromBase64String(stringPattern.GetPattern());
}
catch
{

View File

@@ -0,0 +1,27 @@
using WireMock.Models;
namespace WireMock.Serialization
{
internal static class TimeSettingsMapper
{
public static TimeSettingsModel Map(ITimeSettings settings)
{
return settings != null ? new TimeSettingsModel
{
Start = settings.Start,
End = settings.End,
TTL = settings.TTL
} : null;
}
public static ITimeSettings Map(TimeSettingsModel settings)
{
return settings != null ? new TimeSettings
{
Start = settings.Start,
End = settings.End,
TTL = settings.TTL
} : null;
}
}
}

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using JetBrains.Annotations;
using WireMock.Models;
@@ -24,6 +24,13 @@ namespace WireMock.Server
/// <returns>The <see cref="IRespondWithAProvider"/>.</returns>
IRespondWithAProvider WithGuid(Guid guid);
/// <summary>
/// Define the TimeSettings for this mapping.
/// </summary>
/// <param name="timeSettings">The TimeSettings.</param>
/// <returns>The <see cref="IRespondWithAProvider"/>.</returns>
IRespondWithAProvider WithTimeSettings(ITimeSettings timeSettings);
/// <summary>
/// Define a unique title for this mapping.
/// </summary>

View File

@@ -1,4 +1,4 @@
// This source file is based on mock4net by Alexandre Victoor which is licensed under the Apache 2.0 License.
// 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;
using System.Collections.Generic;
@@ -34,6 +34,8 @@ namespace WireMock.Server
public IWebhook[] Webhooks { get; private set; }
public ITimeSettings TimeSettings { get; private set; }
/// <summary>
/// Initializes a new instance of the <see cref="RespondWithAProvider"/> class.
/// </summary>
@@ -55,7 +57,7 @@ namespace WireMock.Server
/// <param name="provider">The provider.</param>
public void RespondWith(IResponseProvider provider)
{
_registrationCallback(new Mapping(Guid, _title, _path, _settings, _requestMatcher, provider, _priority, _scenario, _executionConditionState, _nextState, _timesInSameState, Webhooks), _saveToFile);
_registrationCallback(new Mapping(Guid, _title, _path, _settings, _requestMatcher, provider, _priority, _scenario, _executionConditionState, _nextState, _timesInSameState, Webhooks, TimeSettings), _saveToFile);
}
/// <see cref="IRespondWithAProvider.WithGuid(string)"/>
@@ -149,6 +151,16 @@ namespace WireMock.Server
return WillSetStateTo(state.ToString(), times);
}
/// <inheritdoc />
public IRespondWithAProvider WithTimeSettings(ITimeSettings timeSettings)
{
Check.NotNull(timeSettings, nameof(timeSettings));
TimeSettings = timeSettings;
return this;
}
/// <see cref="IRespondWithAProvider.WithWebhook(IWebhook[])"/>
public IRespondWithAProvider WithWebhook(params IWebhook[] webhooks)
{
@@ -207,7 +219,7 @@ namespace WireMock.Server
return this;
}
private IWebhook InitWebhook(
private static IWebhook InitWebhook(
string url,
string method,
IDictionary<string, WireMockList<string>> headers,

View File

@@ -450,6 +450,11 @@ namespace WireMock.Server
respondProvider = respondProvider.WithGuid(mappingModel.Guid.Value);
}
if (mappingModel.TimeSettings != null)
{
respondProvider = respondProvider.WithTimeSettings(TimeSettingsMapper.Map(mappingModel.TimeSettings));
}
if (path != null)
{
respondProvider = respondProvider.WithPath(path);
@@ -782,6 +787,10 @@ namespace WireMock.Server
{
responseBuilder = responseBuilder.WithDelay(responseModel.Delay.Value);
}
else if (responseModel.MinimumRandomDelay >= 0 || responseModel.MaximumRandomDelay > 0)
{
responseBuilder = responseBuilder.WithRandomDelay(responseModel.MinimumRandomDelay ?? 0, responseModel.MaximumRandomDelay ?? 60_000);
}
if (responseModel.UseTransformer == true)
{

View File

@@ -260,11 +260,13 @@ namespace WireMock.Server
private void ProcessWireMockOrgJObjectAndUseIMatcher(JObject items, Action<IMatcher> action)
{
IMatcher matcher = null;
if (!(items.First is JProperty firstItem))
{
return;
}
var firstItem = items.First as JProperty;
if (firstItem?.Name == "equalToJson")
IMatcher matcher;
if (firstItem.Name == "equalToJson")
{
matcher = new JsonMatcher(firstItem.Value);
}

View File

@@ -4,15 +4,14 @@ using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using JetBrains.Annotations;
using Newtonsoft.Json;
using WireMock.Admin.Mappings;
using WireMock.Authentication;
using WireMock.Exceptions;
using WireMock.Handlers;
using WireMock.Logging;
using WireMock.Matchers;
using WireMock.Matchers.Request;
using WireMock.Owin;
using WireMock.RequestBuilders;
@@ -201,8 +200,8 @@ namespace WireMock.Server
_settings.Logger = settings.Logger ?? new WireMockNullLogger();
_settings.FileSystemHandler = settings.FileSystemHandler ?? new LocalFileSystemHandler();
_settings.Logger.Info("WireMock.Net by Stef Heyenrath (https://github.com/WireMock-Net/WireMock.Net)");
_settings.Logger.Debug("WireMock.Net server settings {0}", JsonConvert.SerializeObject(settings, Formatting.Indented));
_settings.Logger.Info("By Stef Heyenrath (https://github.com/WireMock-Net/WireMock.Net)");
_settings.Logger.Debug("Server settings {0}", JsonConvert.SerializeObject(settings, Formatting.Indented));
HostUrlOptions urlOptions;
if (settings.Urls != null)
@@ -302,6 +301,11 @@ namespace WireMock.Server
SetBasicAuthentication(settings.AdminUsername, settings.AdminPassword);
}
if (!string.IsNullOrEmpty(settings.AdminAzureADTenant) && !string.IsNullOrEmpty(settings.AdminAzureADAudience))
{
SetAzureADAuthentication(settings.AdminAzureADTenant, settings.AdminAzureADAudience);
}
InitAdmin();
}
@@ -404,22 +408,35 @@ namespace WireMock.Server
_options.AllowPartialMapping = allow;
}
/// <inheritdoc cref="IWireMockServer.SetBasicAuthentication" />
/// <inheritdoc cref="IWireMockServer.SetAzureADAuthentication(string, string)" />
[PublicAPI]
public void SetAzureADAuthentication([NotNull] string tenant, [NotNull] string audience)
{
Check.NotNull(tenant, nameof(tenant));
Check.NotNull(audience, nameof(audience));
#if NETSTANDARD1_3
throw new NotSupportedException("AzureADAuthentication is not supported for NETStandard 1.3");
#else
_options.AuthenticationMatcher = new AzureADAuthenticationMatcher(tenant, audience);
#endif
}
/// <inheritdoc cref="IWireMockServer.SetBasicAuthentication(string, string)" />
[PublicAPI]
public void SetBasicAuthentication([NotNull] string username, [NotNull] string password)
{
Check.NotNull(username, nameof(username));
Check.NotNull(password, nameof(password));
string authorization = Convert.ToBase64String(Encoding.GetEncoding("ISO-8859-1").GetBytes(username + ":" + password));
_options.AuthorizationMatcher = new RegexMatcher(MatchBehaviour.AcceptOnMatch, "^(?i)BASIC " + authorization + "$");
_options.AuthenticationMatcher = new BasicAuthenticationMatcher(username, password);
}
/// <inheritdoc cref="IWireMockServer.RemoveBasicAuthentication" />
/// <inheritdoc cref="IWireMockServer.RemoveAuthentication" />
[PublicAPI]
public void RemoveBasicAuthentication()
public void RemoveAuthentication()
{
_options.AuthorizationMatcher = null;
_options.AuthenticationMatcher = null;
}
/// <inheritdoc cref="IWireMockServer.SetMaxRequestLogCount" />

View File

@@ -1,4 +1,4 @@
using System;
using System;
using HandlebarsDotNet;
using JetBrains.Annotations;
using WireMock.Handlers;
@@ -88,6 +88,18 @@ namespace WireMock.Settings
[PublicAPI]
string AdminPassword { get; set; }
/// <summary>
/// The AzureAD Tenant needed for __admin access.
/// </summary>
[PublicAPI]
string AdminAzureADTenant { get; set; }
/// <summary>
/// The AzureAD Audience / Resource for __admin access.
/// </summary>
[PublicAPI]
string AdminAzureADAudience { get; set; }
/// <summary>
/// The RequestLog expiration in hours (optional).
/// </summary>

View File

@@ -1,4 +1,4 @@
using System;
using System;
using HandlebarsDotNet;
using JetBrains.Annotations;
using Newtonsoft.Json;
@@ -64,6 +64,14 @@ namespace WireMock.Settings
[PublicAPI]
public string AdminPassword { get; set; }
/// <inheritdoc cref="IWireMockServerSettings.AdminAzureADTenant"/>
[PublicAPI]
public string AdminAzureADTenant { get; set; }
/// <inheritdoc cref="IWireMockServerSettings.AdminAzureADAudience"/>
[PublicAPI]
public string AdminAzureADAudience { get; set; }
/// <inheritdoc cref="IWireMockServerSettings.RequestLogExpirationDuration"/>
[PublicAPI]
public int? RequestLogExpirationDuration { get; set; }

View File

@@ -1,4 +1,4 @@
using JetBrains.Annotations;
using JetBrains.Annotations;
using WireMock.Logging;
using WireMock.Validation;
@@ -39,6 +39,8 @@ namespace WireMock.Settings
WatchStaticMappingsInSubdirectories = parser.GetBoolValue("WatchStaticMappingsInSubdirectories"),
AdminUsername = parser.GetStringValue("AdminUsername"),
AdminPassword = parser.GetStringValue("AdminPassword"),
AdminAzureADTenant = parser.GetStringValue(nameof(IWireMockServerSettings.AdminAzureADTenant)),
AdminAzureADAudience = parser.GetStringValue(nameof(IWireMockServerSettings.AdminAzureADAudience)),
MaxRequestLogCount = parser.GetIntValue("MaxRequestLogCount"),
RequestLogExpirationDuration = parser.GetIntValue("RequestLogExpirationDuration"),
AllowCSharpCodeMatcher = parser.GetBoolValue("AllowCSharpCodeMatcher"),

View File

@@ -1,137 +1,143 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Description>Lightweight Http Mocking Server for .Net, inspired by WireMock from the Java landscape.</Description>
<AssemblyTitle>WireMock.Net</AssemblyTitle>
<Authors>Stef Heyenrath</Authors>
<TargetFrameworks>net451;net452;net46;net461;netstandard1.3;netstandard2.0;netstandard2.1;netcoreapp3.1;net5.0</TargetFrameworks>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<AssemblyName>WireMock.Net</AssemblyName>
<PackageId>WireMock.Net</PackageId>
<PackageTags>tdd;mock;http;wiremock;test;server;unittest</PackageTags>
<RootNamespace>WireMock</RootNamespace>
<ProjectGuid>{D3804228-91F4-4502-9595-39584E5A01AD}</ProjectGuid>
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<AllowedOutputExtensionsInPackageBuildOutputFolder>$(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb</AllowedOutputExtensionsInPackageBuildOutputFolder>
<EmbedUntrackedSources>true</EmbedUntrackedSources>
<!--<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Description>Lightweight Http Mocking Server for .Net, inspired by WireMock from the Java landscape.</Description>
<AssemblyTitle>WireMock.Net</AssemblyTitle>
<Authors>Stef Heyenrath</Authors>
<TargetFrameworks>net451;net452;net46;net461;netstandard1.3;netstandard2.0;netstandard2.1;netcoreapp3.1;net5.0</TargetFrameworks>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<AssemblyName>WireMock.Net</AssemblyName>
<PackageId>WireMock.Net</PackageId>
<PackageTags>tdd;mock;http;wiremock;test;server;unittest</PackageTags>
<RootNamespace>WireMock</RootNamespace>
<ProjectGuid>{D3804228-91F4-4502-9595-39584E5A01AD}</ProjectGuid>
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<AllowedOutputExtensionsInPackageBuildOutputFolder>$(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb</AllowedOutputExtensionsInPackageBuildOutputFolder>
<EmbedUntrackedSources>true</EmbedUntrackedSources>
<!--<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<GenerateBindingRedirectsOutputType>true</GenerateBindingRedirectsOutputType>-->
<SignAssembly>true</SignAssembly>
<AssemblyOriginatorKeyFile>WireMock.Net.snk</AssemblyOriginatorKeyFile>
<!--<DelaySign>true</DelaySign>-->
<PublicSign Condition=" '$(OS)' != 'Windows_NT' ">true</PublicSign>
</PropertyGroup>
<SignAssembly>true</SignAssembly>
<AssemblyOriginatorKeyFile>WireMock.Net.snk</AssemblyOriginatorKeyFile>
<!--<DelaySign>true</DelaySign>-->
<PublicSign Condition=" '$(OS)' != 'Windows_NT' ">true</PublicSign>
</PropertyGroup>
<!--<ItemGroup>
<!--<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" />-->
<!-- https://github.com/aspnet/RoslynCodeDomProvider/issues/51 -->
<!-- This is needed else we cannot build net452 in Azure DevOps Pipeline -->
<!--<Target Name="CheckIfShouldKillVBCSCompiler" />-->
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
<!--<PathMap>$(MSBuildProjectDirectory)=/</PathMap>-->
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
<!--<PathMap>$(MSBuildProjectDirectory)=/</PathMap>-->
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Debug - Sonar'">
<CodeAnalysisRuleSet>WireMock.Net.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Debug - Sonar'">
<CodeAnalysisRuleSet>WireMock.Net.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(TargetFramework)' == 'netstandard1.3' or '$(TargetFramework)' == 'netstandard2.0' or '$(TargetFramework)' == 'netstandard2.1'">
<DefineConstants>NETSTANDARD;USE_ASPNETCORE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(TargetFramework)' == 'netstandard1.3' or '$(TargetFramework)' == 'netstandard2.0' or '$(TargetFramework)' == 'netstandard2.1'">
<DefineConstants>NETSTANDARD;USE_ASPNETCORE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(TargetFramework)' == 'netcoreapp2.1' or '$(TargetFramework)' == 'netcoreapp2.2' or '$(TargetFramework)' == 'netcoreapp3.1' or '$(TargetFramework)' == 'net5.0'">
<DefineConstants>USE_ASPNETCORE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(TargetFramework)' == 'netcoreapp2.1' or '$(TargetFramework)' == 'netcoreapp2.2' or '$(TargetFramework)' == 'netcoreapp3.1' or '$(TargetFramework)' == 'net5.0'">
<DefineConstants>USE_ASPNETCORE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(TargetFramework)' == 'net461'">
<DefineConstants>USE_ASPNETCORE;NET46</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(TargetFramework)' == 'net461'">
<DefineConstants>USE_ASPNETCORE;NET46</DefineConstants>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="JetBrains.Annotations" Version="2020.1.0" PrivateAssets="All" />
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" />
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
<PackageReference Include="SimMetrics.Net" Version="1.0.5" />
<PackageReference Include="System.Linq.Dynamic.Core" Version="1.0.12" />
<PackageReference Include="RandomDataGenerator.Net" Version="1.0.12" />
<PackageReference Include="JmesPath.Net" Version="1.0.125" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="JetBrains.Annotations" Version="2020.1.0" PrivateAssets="All" />
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" />
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
<PackageReference Include="SimMetrics.Net" Version="1.0.5" />
<!--<PackageReference Include="Stef.Validation" Version="0.0.3" />-->
<PackageReference Include="System.Linq.Dynamic.Core" Version="1.0.12" />
<PackageReference Include="RandomDataGenerator.Net" Version="1.0.13" />
<PackageReference Include="JmesPath.Net" Version="1.0.125" />
<PackageReference Include="AnyOf" Version="0.2.0" />
<!--<PackageReference Include="TinyMapper" Version="3.0.3" />-->
<!--<PackageReference Include="Mapster" Version="7.2.0" />-->
</ItemGroup>
<ItemGroup Condition="'$(Configuration)' == 'Debug - Sonar'">
<PackageReference Include="SonarAnalyzer.CSharp" Version="7.8.0.7320">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
</ItemGroup>
<ItemGroup Condition="'$(Configuration)' == 'Debug - Sonar'">
<PackageReference Include="SonarAnalyzer.CSharp" Version="7.8.0.7320">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' != 'netstandard1.3' ">
<PackageReference Include="XPath2.Extensions" Version="1.1.0" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' != 'netstandard1.3' ">
<PackageReference Include="XPath2.Extensions" Version="1.1.0" />
<PackageReference Include="Microsoft.IdentityModel.Protocols.OpenIdConnect" Version="6.12.2" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net451' or '$(TargetFramework)' == 'net452' ">
<!-- Required for WebRequestHandler -->
<Reference Include="System.Net.Http.WebRequest" />
<ItemGroup Condition=" '$(TargetFramework)' == 'net451' or '$(TargetFramework)' == 'net452' ">
<!-- Required for WebRequestHandler -->
<Reference Include="System.Net.Http.WebRequest" />
<PackageReference Include="Microsoft.AspNet.WebApi.OwinSelfHost" Version="5.2.6" />
<PackageReference Include="System.ValueTuple" Version="4.5.0" />
<PackageReference Include="Scriban.Signed" Version="2.1.4" />
</ItemGroup>
<PackageReference Include="Microsoft.AspNet.WebApi.OwinSelfHost" Version="5.2.6" />
<PackageReference Include="System.ValueTuple" Version="4.5.0" />
<PackageReference Include="Scriban.Signed" Version="2.1.4" />
<PackageReference Include="Mapster" Version="7.2.0" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net46' ">
<PackageReference Include="Microsoft.AspNet.WebApi.OwinSelfHost" Version="5.2.6" />
<PackageReference Include="Microsoft.Owin" Version="4.0.0" />
<PackageReference Include="Microsoft.Owin.Host.HttpListener" Version="4.0.0" />
<PackageReference Include="Microsoft.Owin.Hosting" Version="4.0.0" />
<PackageReference Include="System.Net.Http" Version="4.3.4" />
<PackageReference Include="System.ValueTuple" Version="4.5.0" />
<PackageReference Include="Scriban.Signed" Version="2.1.4" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net46' ">
<PackageReference Include="Microsoft.AspNet.WebApi.OwinSelfHost" Version="5.2.6" />
<PackageReference Include="Microsoft.Owin" Version="4.0.0" />
<PackageReference Include="Microsoft.Owin.Host.HttpListener" Version="4.0.0" />
<PackageReference Include="Microsoft.Owin.Hosting" Version="4.0.0" />
<PackageReference Include="System.Net.Http" Version="4.3.4" />
<PackageReference Include="System.ValueTuple" Version="4.5.0" />
<PackageReference Include="Scriban.Signed" Version="2.1.4" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net461' ">
<PackageReference Include="Scriban.Signed" Version="2.1.4" />
<PackageReference Include="Microsoft.AspNetCore" Version="2.2.0" />
<ItemGroup Condition=" '$(TargetFramework)' == 'net461' ">
<PackageReference Include="Scriban.Signed" Version="2.1.4" />
<PackageReference Include="Microsoft.AspNetCore" Version="2.2.0" />
<!-- https://github.com/WireMock-Net/WireMock.Net/issues/507 -->
<PackageReference Include="Microsoft.AspNetCore.Server.IIS" Version="2.2.6" />
</ItemGroup>
<!-- https://github.com/WireMock-Net/WireMock.Net/issues/507 -->
<PackageReference Include="Microsoft.AspNetCore.Server.IIS" Version="2.2.6" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard1.3' ">
<PackageReference Include="Microsoft.AspNetCore" Version="1.1.7" />
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel.Https" Version="1.1.3" />
<PackageReference Include="System.Xml.XmlDocument" Version="4.3.0" />
<PackageReference Include="System.Xml.XPath.XmlDocument" Version="4.3.0" />
<PackageReference Include="System.ValueTuple" Version="4.5.0" />
<PackageReference Include="Scriban.Signed" Version="2.1.4" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard1.3' ">
<PackageReference Include="Microsoft.AspNetCore" Version="1.1.7" />
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel.Https" Version="1.1.3" />
<PackageReference Include="System.Xml.XmlDocument" Version="4.3.0" />
<PackageReference Include="System.Xml.XPath.XmlDocument" Version="4.3.0" />
<PackageReference Include="System.ValueTuple" Version="4.5.0" />
<PackageReference Include="Scriban.Signed" Version="2.1.4" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard2.0' or '$(TargetFramework)' == 'netstandard2.1' ">
<PackageReference Include="Scriban.Signed" Version="3.3.2" />
<PackageReference Include="Microsoft.AspNetCore" Version="2.2.0" />
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard2.0' or '$(TargetFramework)' == 'netstandard2.1' ">
<PackageReference Include="Scriban.Signed" Version="3.3.2" />
<PackageReference Include="Microsoft.AspNetCore" Version="2.2.0" />
<!-- https://github.com/WireMock-Net/WireMock.Net/issues/507 -->
<PackageReference Include="Microsoft.AspNetCore.Server.IIS" Version="2.2.6" />
</ItemGroup>
<!-- https://github.com/WireMock-Net/WireMock.Net/issues/507 -->
<PackageReference Include="Microsoft.AspNetCore.Server.IIS" Version="2.2.6" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'netcoreapp3.1' or '$(TargetFramework)' == 'net5.0'">
<FrameworkReference Include="Microsoft.AspNetCore.App" />
<PackageReference Include="Scriban.Signed" Version="3.3.2" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'netcoreapp3.1' or '$(TargetFramework)' == 'net5.0'">
<FrameworkReference Include="Microsoft.AspNetCore.App" />
<PackageReference Include="Scriban.Signed" Version="3.3.2" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Handlebars.Net.Helpers" Version="2.2.1" />
<PackageReference Include="Handlebars.Net.Helpers.DynamicLinq" Version="2.2.1" />
<PackageReference Include="Handlebars.Net.Helpers.Humanizer" Version="2.2.1" />
<PackageReference Include="Handlebars.Net.Helpers.Json" Version="2.2.1" />
<PackageReference Include="Handlebars.Net.Helpers.XPath" Version="2.2.1" />
<PackageReference Include="Handlebars.Net.Helpers.Xeger" Version="2.2.1" />
<PackageReference Include="Handlebars.Net.Helpers.Random" Version="2.2.1" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Handlebars.Net.Helpers" Version="2.2.1" />
<PackageReference Include="Handlebars.Net.Helpers.DynamicLinq" Version="2.2.1" />
<PackageReference Include="Handlebars.Net.Helpers.Humanizer" Version="2.2.1" />
<PackageReference Include="Handlebars.Net.Helpers.Json" Version="2.2.1" />
<PackageReference Include="Handlebars.Net.Helpers.XPath" Version="2.2.1" />
<PackageReference Include="Handlebars.Net.Helpers.Xeger" Version="2.2.1" />
<PackageReference Include="Handlebars.Net.Helpers.Random" Version="2.2.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\WireMock.Net.Abstractions\WireMock.Net.Abstractions.csproj" />
<ProjectReference Include="..\WireMock.Org.Abstractions\WireMock.Org.Abstractions.csproj" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\WireMock.Net.Abstractions\WireMock.Net.Abstractions.csproj" />
<ProjectReference Include="..\WireMock.Org.Abstractions\WireMock.Org.Abstractions.csproj" />
</ItemGroup>
</Project>

View File

@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Description>Commonly used interfaces, models, enumerations and types.</Description>
@@ -33,7 +33,7 @@
</PropertyGroup>
<ItemGroup>
<!--<PackageReference Include="AnyOf" Version="0.0.23" />-->
<!--<PackageReference Include="AnyOf" Version="0.1.0" />-->
<PackageReference Include="JetBrains.Annotations" Version="2021.2.0" PrivateAssets="All" />
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" />
</ItemGroup>

View File

@@ -1,15 +1,16 @@
using System;
using System;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using WireMock.Logging;
using WireMock.Net.StandAlone;
using WireMock.Server;
namespace WireMock
namespace WireMock.Net
{
public class Program
{
private static int SleepTime = 30000;
private static readonly ILogger Logger = LoggerFactory.Create(o =>
private static readonly int SleepTime = 30000;
private static readonly ILogger xLogger = LoggerFactory.Create(o =>
{
o.SetMinimumLevel(LogLevel.Debug);
o.AddSimpleConsole(options =>
@@ -18,18 +19,19 @@ namespace WireMock
options.SingleLine = false;
options.TimestampFormat = "yyyy-MM-ddTHH:mm:ss ";
});
}).CreateLogger(string.Empty);
}).CreateLogger("WireMock.Net");
private static readonly IWireMockLogger Logger = new WireMockLogger(xLogger);
private static WireMockServer Server;
static async Task Main(string[] args)
{
if (!StandAloneApp.TryStart(args, out Server, new WireMockLogger(Logger)))
if (!StandAloneApp.TryStart(args, out Server, Logger))
{
return;
}
Logger.LogInformation("Press Ctrl+C to shut down");
Logger.Info("Press Ctrl+C to shut down");
Console.CancelKeyPress += (s, e) =>
{
@@ -43,16 +45,16 @@ namespace WireMock
while (true)
{
Logger.LogInformation("WireMock.Net server running : {IsStarted}", Server.IsStarted);
Logger.Info("Server running : {IsStarted}", Server.IsStarted);
await Task.Delay(SleepTime);
}
}
private static void Stop(string why)
{
Logger.LogInformation("WireMock.Net server stopping because '{why}'", why);
Logger.Info("Server stopping because '{why}'", why);
Server.Stop();
Logger.LogInformation("WireMock.Net server stopped");
Logger.Info("Server stopped");
}
}
}

View File

@@ -1,10 +1,10 @@
using System;
using System;
using System.Text.Json;
using Microsoft.Extensions.Logging;
using WireMock.Admin.Requests;
using WireMock.Logging;
namespace WireMock
namespace WireMock.Net
{
public class WireMockLogger : IWireMockLogger
{

View File

@@ -1,15 +1,15 @@
using FluentAssertions;
using System;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using FluentAssertions;
using WireMock.FluentAssertions;
using WireMock.RequestBuilders;
using WireMock.ResponseBuilders;
using WireMock.Server;
using Xunit;
using WireMock.FluentAssertions;
using System.Threading.Tasks;
using WireMock.Settings;
using Xunit;
using static System.Environment;
namespace WireMock.Net.Tests.FluentAssertions
@@ -100,16 +100,9 @@ namespace WireMock.Net.Tests.FluentAssertions
.HaveReceivedACall()
.WithHeader("Authorization", "value");
var sentHeaders = _server.LogEntries.SelectMany(x => x.RequestMessage.Headers)
.ToDictionary(x => x.Key, x => x.Value)
.ToList();
var sentHeaderString = "{" + string.Join(", ", sentHeaders) + "}";
act.Should().Throw<Exception>()
.And.Message.Should()
.Be(
$"Expected headers from requests sent {sentHeaderString} to contain key \"Authorization\".{NewLine}");
.Contain("to contain key \"Authorization\".");
}
[Fact]
@@ -156,7 +149,7 @@ namespace WireMock.Net.Tests.FluentAssertions
.And.Message.Should()
.Be($"{string.Join(NewLine, missingValue1Message, missingValue2Message)}{NewLine}");
}
[Fact]
public async Task AtUrl_WhenACallWasMadeToUrl_Should_BeOK()
{
@@ -200,7 +193,7 @@ namespace WireMock.Net.Tests.FluentAssertions
{
_server.ResetMappings();
_server.Given(Request.Create().UsingAnyMethod())
.RespondWith(Response.Create().WithProxy(new ProxyAndRecordSettings {Url = "http://localhost:9999"}));
.RespondWith(Response.Create().WithProxy(new ProxyAndRecordSettings { Url = "http://localhost:9999" }));
await _httpClient.GetAsync("");
@@ -214,8 +207,8 @@ namespace WireMock.Net.Tests.FluentAssertions
{
_server.ResetMappings();
_server.Given(Request.Create().UsingAnyMethod())
.RespondWith(Response.Create().WithProxy(new ProxyAndRecordSettings {Url = "http://localhost:9999"}));
.RespondWith(Response.Create().WithProxy(new ProxyAndRecordSettings { Url = "http://localhost:9999" }));
Action act = () => _server.Should()
.HaveReceivedACall()
.WithProxyUrl("anyurl");
@@ -231,8 +224,8 @@ namespace WireMock.Net.Tests.FluentAssertions
{
_server.ResetMappings();
_server.Given(Request.Create().UsingAnyMethod())
.RespondWith(Response.Create().WithProxy(new ProxyAndRecordSettings {Url = "http://localhost:9999"}));
.RespondWith(Response.Create().WithProxy(new ProxyAndRecordSettings { Url = "http://localhost:9999" }));
await _httpClient.GetAsync("");
Action act = () => _server.Should()
@@ -244,7 +237,7 @@ namespace WireMock.Net.Tests.FluentAssertions
.Be(
$"Expected _server to have been called with proxy url \"anyurl\", but didn't find it among the calls with {{\"http://localhost:9999\"}}.");
}
[Fact]
public async Task FromClientIP_whenACallWasMadeFromClientIP_Should_BeOK()
{

View File

@@ -1,6 +1,6 @@
using NFluent;
using System;
using System.IO;
using NFluent;
using WireMock.Handlers;
using Xunit;
@@ -65,6 +65,13 @@ namespace WireMock.Net.Tests.Handlers
Check.ThatCode(() => _sut.ReadFile(null)).Throws<ArgumentNullException>();
}
[Fact]
public void LocalFileSystemHandler_ReadFileAsString_ThrowsArgumentNullException()
{
// Act
Check.ThatCode(() => _sut.ReadFileAsString(null)).Throws<ArgumentNullException>();
}
[Fact]
public void LocalFileSystemHandler_WriteFile_ThrowsArgumentNullException()
{

View File

@@ -1,4 +1,4 @@
using NFluent;
using NFluent;
using WireMock.Matchers;
using Xunit;
@@ -83,7 +83,7 @@ namespace WireMock.Net.Tests.Matchers
var matcher = new CSharpCodeMatcher("x");
// Act
string[] patterns = matcher.GetPatterns();
var patterns = matcher.GetPatterns();
// Assert
Check.That(patterns).ContainsExactly("x");

View File

@@ -1,4 +1,5 @@
using NFluent;
using AnyOfTypes;
using NFluent;
using WireMock.Matchers;
using Xunit;
@@ -49,7 +50,7 @@ namespace WireMock.Net.Tests.Matchers
var matcher = new ContentTypeMatcher("x");
// Act
string[] patterns = matcher.GetPatterns();
var patterns = matcher.GetPatterns();
// Assert
Check.That(patterns).ContainsExactly("x");

View File

@@ -1,4 +1,4 @@
using NFluent;
using NFluent;
using WireMock.Matchers;
using Xunit;
@@ -26,7 +26,7 @@ namespace WireMock.Net.Tests.Matchers
var matcher = new ExactMatcher("X");
// Act
string[] patterns = matcher.GetPatterns();
var patterns = matcher.GetPatterns();
// Assert
Check.That(patterns).ContainsExactly("X");

View File

@@ -1,4 +1,4 @@
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Linq;
using NFluent;
using WireMock.Matchers;
using Xunit;
@@ -27,7 +27,7 @@ namespace WireMock.Net.Tests.Matchers
var matcher = new JmesPathMatcher("X");
// Act
string[] patterns = matcher.GetPatterns();
var patterns = matcher.GetPatterns();
// Assert
Check.That(patterns).ContainsExactly("X");

View File

@@ -0,0 +1,400 @@
using System;
using System.Collections.Generic;
using System.IO;
using FluentAssertions;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using NFluent;
using WireMock.Matchers;
using Xunit;
namespace WireMock.Net.Tests.Matchers
{
public class JsonPartialWildcardMatcherTests
{
[Fact]
public void JsonPartialWildcardMatcher_GetName()
{
// Assign
var matcher = new JsonPartialWildcardMatcher("{}");
// Act
string name = matcher.Name;
// Assert
Check.That(name).Equals("JsonPartialWildcardMatcher");
}
[Fact]
public void JsonPartialWildcardMatcher_GetValue()
{
// Assign
var matcher = new JsonPartialWildcardMatcher("{}");
// Act
object value = matcher.Value;
// Assert
Check.That(value).Equals("{}");
}
[Fact]
public void JsonPartialWildcardMatcher_WithInvalidStringValue_Should_ThrowException()
{
// Act
Action action = () => new JsonPartialWildcardMatcher(MatchBehaviour.AcceptOnMatch, "{ \"Id\"");
// Assert
action.Should().Throw<JsonException>();
}
[Fact]
public void JsonPartialWildcardMatcher_WithInvalidObjectValue_Should_ThrowException()
{
// Act
Action action = () => new JsonPartialWildcardMatcher(MatchBehaviour.AcceptOnMatch, new MemoryStream());
// Assert
action.Should().Throw<JsonException>();
}
[Fact]
public void JsonPartialWildcardMatcher_IsMatch_WithInvalidValue_And_ThrowExceptionIsFalse_Should_ReturnMismatch()
{
// Assign
var matcher = new JsonPartialWildcardMatcher("");
// Act
double match = matcher.IsMatch(new MemoryStream());
// Assert
Check.That(match).IsEqualTo(0);
}
[Fact]
public void JsonPartialWildcardMatcher_IsMatch_WithInvalidValue_And_ThrowExceptionIsTrue_Should_ReturnMismatch()
{
// Assign
var matcher = new JsonPartialWildcardMatcher("", false, true);
// Act
Action action = () => matcher.IsMatch(new MemoryStream());
// Assert
action.Should().Throw<JsonException>();
}
[Fact]
public void JsonPartialWildcardMatcher_IsMatch_ByteArray()
{
// Assign
var bytes = new byte[0];
var matcher = new JsonPartialWildcardMatcher("");
// Act
double match = matcher.IsMatch(bytes);
// Assert
Check.That(match).IsEqualTo(0);
}
[Fact]
public void JsonPartialWildcardMatcher_IsMatch_NullString()
{
// Assign
string s = null;
var matcher = new JsonPartialWildcardMatcher("");
// Act
double match = matcher.IsMatch(s);
// Assert
Check.That(match).IsEqualTo(0);
}
[Fact]
public void JsonPartialWildcardMatcher_IsMatch_NullObject()
{
// Assign
object o = null;
var matcher = new JsonPartialWildcardMatcher("");
// Act
double match = matcher.IsMatch(o);
// Assert
Check.That(match).IsEqualTo(0);
}
[Fact]
public void JsonPartialWildcardMatcher_IsMatch_JArray()
{
// Assign
var matcher = new JsonPartialWildcardMatcher(new[] { "x", "y" });
// Act
var jArray = new JArray
{
"x",
"y"
};
double match = matcher.IsMatch(jArray);
// Assert
Assert.Equal(1.0, match);
}
[Fact]
public void JsonPartialWildcardMatcher_IsMatch_JObject()
{
// Assign
var matcher = new JsonPartialWildcardMatcher(new { Id = 1, Name = "Test" });
// Act
var jobject = new JObject
{
{ "Id", new JValue(1) },
{ "Name", new JValue("Test") }
};
double match = matcher.IsMatch(jobject);
// Assert
Assert.Equal(1.0, match);
}
[Fact]
public void JsonPartialWildcardMatcher_IsMatch_WithIgnoreCaseTrue_JObject()
{
// Assign
var matcher = new JsonPartialWildcardMatcher(new { id = 1, Name = "test" }, true);
// Act
var jobject = new JObject
{
{ "Id", new JValue(1) },
{ "NaMe", new JValue("Test") }
};
double match = matcher.IsMatch(jobject);
// Assert
Assert.Equal(1.0, match);
}
[Fact]
public void JsonPartialWildcardMatcher_IsMatch_JObjectParsed()
{
// Assign
var matcher = new JsonPartialWildcardMatcher(new { Id = 1, Name = "Test" });
// Act
var jobject = JObject.Parse("{ \"Id\" : 1, \"Name\" : \"Test\" }");
double match = matcher.IsMatch(jobject);
// Assert
Assert.Equal(1.0, match);
}
[Fact]
public void JsonPartialWildcardMatcher_IsMatch_WithIgnoreCaseTrue_JObjectParsed()
{
// Assign
var matcher = new JsonPartialWildcardMatcher(new { Id = 1, Name = "TESt" }, true);
// Act
var jobject = JObject.Parse("{ \"Id\" : 1, \"Name\" : \"Test\" }");
double match = matcher.IsMatch(jobject);
// Assert
Assert.Equal(1.0, match);
}
[Fact]
public void JsonPartialWildcardMatcher_IsMatch_JArrayAsString()
{
// Assign
var matcher = new JsonPartialWildcardMatcher("[ \"x\", \"y\" ]");
// Act
var jArray = new JArray
{
"x",
"y"
};
double match = matcher.IsMatch(jArray);
// Assert
Assert.Equal(1.0, match);
}
[Fact]
public void JsonPartialWildcardMatcher_IsMatch_JObjectAsString()
{
// Assign
var matcher = new JsonPartialWildcardMatcher("{ \"Id\" : 1, \"Name\" : \"Test\" }");
// Act
var jobject = new JObject
{
{ "Id", new JValue(1) },
{ "Name", new JValue("Test") }
};
double match = matcher.IsMatch(jobject);
// Assert
Assert.Equal(1.0, match);
}
[Fact]
public void JsonPartialWildcardMatcher_IsMatch_WithIgnoreCaseTrue_JObjectAsString()
{
// Assign
var matcher = new JsonPartialWildcardMatcher("{ \"Id\" : 1, \"Name\" : \"test\" }", true);
// Act
var jobject = new JObject
{
{ "Id", new JValue(1) },
{ "Name", new JValue("Test") }
};
double match = matcher.IsMatch(jobject);
// Assert
Assert.Equal(1.0, match);
}
[Fact]
public void JsonPartialWildcardMatcher_IsMatch_JObjectAsString_RejectOnMatch()
{
// Assign
var matcher = new JsonPartialWildcardMatcher(MatchBehaviour.RejectOnMatch, "{ \"Id\" : 1, \"Name\" : \"Test\" }");
// Act
var jobject = new JObject
{
{ "Id", new JValue(1) },
{ "Name", new JValue("Test") }
};
double match = matcher.IsMatch(jobject);
// Assert
Assert.Equal(0.0, match);
}
[Fact]
public void JsonPartialWildcardMatcher_IsMatch_JObjectWithDateTimeOffsetAsString()
{
// Assign
var matcher = new JsonPartialWildcardMatcher("{ \"preferredAt\" : \"2019-11-21T10:32:53.2210009+00:00\" }");
// Act
var jobject = new JObject
{
{ "preferredAt", new JValue("2019-11-21T10:32:53.2210009+00:00") }
};
double match = matcher.IsMatch(jobject);
// Assert
Assert.Equal(1.0, match);
}
[Theory]
[InlineData("{\"test\":\"abc\"}", "{\"test\":\"abc\",\"other\":\"xyz\"}")]
[InlineData("\"test\"", "\"test\"")]
[InlineData("123", "123")]
[InlineData("[\"test\"]", "[\"test\"]")]
[InlineData("[\"test\"]", "[\"test\", \"other\"]")]
[InlineData("[123]", "[123]")]
[InlineData("[123]", "[123, 456]")]
[InlineData("{ \"test\":\"value\" }", "{\"test\":\"value\",\"other\":123}")]
[InlineData("{ \"test\":\"value\" }", "{\"test\":\"value\"}")]
[InlineData("{\"test\":{\"nested\":\"value\"}}", "{\"test\":{\"nested\":\"value\"}}")]
public void JsonPartialWildcardMatcher_IsMatch_StringInput_IsValidMatch(string value, string input)
{
// Assign
var matcher = new JsonPartialWildcardMatcher(value);
// Act
double match = matcher.IsMatch(input);
// Assert
Assert.Equal(1.0, match);
}
[Theory]
[InlineData("{\"test\":\"*\"}", "{\"test\":\"xxx\",\"other\":\"xyz\"}")]
[InlineData("\"t*t\"", "\"test\"")]
public void JsonPartialWildcardMatcher_IsMatch_StringInputWithWildcard_IsValidMatch(string value, string input)
{
// Assign
var matcher = new JsonPartialWildcardMatcher(value);
// Act
double match = matcher.IsMatch(input);
// Assert
match.Should().Be(1.0);
}
[Theory]
[InlineData("\"test\"", null)]
[InlineData("\"test1\"", "\"test2\"")]
[InlineData("123", "1234")]
[InlineData("[\"test\"]", "[\"test1\"]")]
[InlineData("[\"test\"]", "[\"test1\", \"test2\"]")]
[InlineData("[123]", "[1234]")]
[InlineData("{}", "\"test\"")]
[InlineData("{ \"test\":\"value\" }", "{\"test\":\"value2\"}")]
[InlineData("{ \"test.nested\":\"value\" }", "{\"test\":{\"nested\":\"value1\"}}")]
[InlineData("{\"test\":{\"test1\":\"value\"}}", "{\"test\":{\"test1\":\"value1\"}}")]
[InlineData("[{ \"test.nested\":\"value\" }]", "[{\"test\":{\"nested\":\"value1\"}}]")]
public void JsonPartialWildcardMatcher_IsMatch_StringInputWithInvalidMatch(string value, string input)
{
// Assign
var matcher = new JsonPartialWildcardMatcher(value);
// Act
double match = matcher.IsMatch(input);
// Assert
Assert.Equal(0.0, match);
}
[Theory]
[InlineData("{ \"test.nested\":123 }", "{\"test\":{\"nested\":123}}")]
[InlineData("{ \"test.nested\":[123, 456] }", "{\"test\":{\"nested\":[123, 456]}}")]
[InlineData("{ \"test.nested\":\"value\" }", "{\"test\":{\"nested\":\"value\"}}")]
[InlineData("{ \"['name.with.dot']\":\"value\" }", "{\"name.with.dot\":\"value\"}")]
[InlineData("[{ \"test.nested\":\"value\" }]", "[{\"test\":{\"nested\":\"value\"}}]")]
[InlineData("[{ \"['name.with.dot']\":\"value\" }]", "[{\"name.with.dot\":\"value\"}]")]
public void JsonPartialWildcardMatcher_IsMatch_ValueAsJPathValidMatch(string value, string input)
{
// Assign
var matcher = new JsonPartialWildcardMatcher(value);
// Act
double match = matcher.IsMatch(input);
// Assert
Assert.Equal(1.0, match);
}
[Theory]
[InlineData("{ \"test.nested\":123 }", "{\"test\":{\"nested\":456}}")]
[InlineData("{ \"test.nested\":[123, 456] }", "{\"test\":{\"nested\":[1, 2]}}")]
[InlineData("{ \"test.nested\":\"value\" }", "{\"test\":{\"nested\":\"value1\"}}")]
[InlineData("{ \"['name.with.dot']\":\"value\" }", "{\"name.with.dot\":\"value1\"}")]
[InlineData("[{ \"test.nested\":\"value\" }]", "[{\"test\":{\"nested\":\"value1\"}}]")]
[InlineData("[{ \"['name.with.dot']\":\"value\" }]", "[{\"name.with.dot\":\"value1\"}]")]
public void JsonPartialWildcardMatcher_IsMatch_ValueAsJPathInvalidMatch(string value, string input)
{
// Assign
var matcher = new JsonPartialWildcardMatcher(value);
// Act
double match = matcher.IsMatch(input);
// Assert
Assert.Equal(0.0, match);
}
}
}

View File

@@ -1,4 +1,4 @@
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Linq;
using NFluent;
using WireMock.Matchers;
using Xunit;
@@ -27,7 +27,7 @@ namespace WireMock.Net.Tests.Matchers
var matcher = new JsonPathMatcher("X");
// Act
string[] patterns = matcher.GetPatterns();
var patterns = matcher.GetPatterns();
// Assert
Check.That(patterns).ContainsExactly("X");

View File

@@ -1,4 +1,4 @@
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Linq;
using NFluent;
using WireMock.Matchers;
using Xunit;
@@ -103,7 +103,7 @@ namespace WireMock.Net.Tests.Matchers
var matcher = new LinqMatcher("x");
// Act
string[] patterns = matcher.GetPatterns();
var patterns = matcher.GetPatterns();
// Assert
Check.That(patterns).ContainsExactly("x");

View File

@@ -36,11 +36,25 @@ namespace WireMock.Net.Tests.Matchers
[InlineData(null, 0.0)]
[InlineData("", 0.0)]
[InlineData("x", 1.0)]
public void NotNullOrEmptyMatcher_IsMatch_String(string data, double expected)
public void NotNullOrEmptyMatcher_IsMatch_String(string @string, double expected)
{
// Act
var matcher = new NotNullOrEmptyMatcher();
double result = matcher.IsMatch(data);
double result = matcher.IsMatch(@string);
// Assert
result.Should().Be(expected);
}
[Theory]
[InlineData(null, 0.0)]
[InlineData("", 0.0)]
[InlineData("x", 1.0)]
public void NotNullOrEmptyMatcher_IsMatch_StringAsObject(string @string, double expected)
{
// Act
var matcher = new NotNullOrEmptyMatcher();
double result = matcher.IsMatch((object)@string);
// Assert
result.Should().Be(expected);
@@ -56,5 +70,15 @@ namespace WireMock.Net.Tests.Matchers
// Assert
result.Should().Be(1.0);
}
[Fact]
public void NotNullOrEmptyMatcher_GetPatterns_Should_Return_EmptyArray()
{
// Act
var patterns = new NotNullOrEmptyMatcher().GetPatterns();
// Assert
patterns.Should().BeEmpty();
}
}
}

View File

@@ -1,4 +1,4 @@
using NFluent;
using NFluent;
using WireMock.Matchers;
using Xunit;
@@ -26,7 +26,7 @@ namespace WireMock.Net.Tests.Matchers
var matcher = new RegexMatcher("X");
// Act
string[] patterns = matcher.GetPatterns();
var patterns = matcher.GetPatterns();
// Assert
Check.That(patterns).ContainsExactly("X");

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