mirror of
https://github.com/wiremock/WireMock.Net.git
synced 2026-04-17 22:50:05 +02:00
Swagger support (#749)
* r * fix * sw * x * s * . * . * . * CreateTypeFromJObject * . * . * f * c * . * . * . * . * . * . * ok * , * . * . * . * . * n * pact * fix * schema * null * fluent * r * -p * . * . * refs * .
This commit is contained in:
@@ -5,7 +5,6 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<VersionPrefix>1.4.41</VersionPrefix>
|
<VersionPrefix>1.4.41</VersionPrefix>
|
||||||
<PackageReleaseNotes>See CHANGELOG.md</PackageReleaseNotes>
|
|
||||||
<PackageIcon>WireMock.Net-Logo.png</PackageIcon>
|
<PackageIcon>WireMock.Net-Logo.png</PackageIcon>
|
||||||
<PackageProjectUrl>https://github.com/WireMock-Net/WireMock.Net</PackageProjectUrl>
|
<PackageProjectUrl>https://github.com/WireMock-Net/WireMock.Net</PackageProjectUrl>
|
||||||
<PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
|
<PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
|
||||||
|
|||||||
@@ -24,4 +24,5 @@
|
|||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Victoor/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=Victoor/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Webhook/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=Webhook/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Webhooks/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=Webhooks/@EntryIndexedValue">True</s:Boolean>
|
||||||
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=wiremockserver/@EntryIndexedValue">True</s:Boolean>
|
||||||
</wpf:ResourceDictionary>
|
</wpf:ResourceDictionary>
|
||||||
@@ -1,26 +1,23 @@
|
|||||||
using System;
|
|
||||||
using Microsoft.OpenApi.Models;
|
|
||||||
using RandomDataGenerator.FieldOptions;
|
using RandomDataGenerator.FieldOptions;
|
||||||
using RandomDataGenerator.Randomizers;
|
using RandomDataGenerator.Randomizers;
|
||||||
using WireMock.Net.OpenApiParser.Settings;
|
using WireMock.Net.OpenApiParser.Settings;
|
||||||
|
|
||||||
namespace WireMock.Net.OpenApiParser.ConsoleApp
|
namespace WireMock.Net.OpenApiParser.ConsoleApp;
|
||||||
{
|
|
||||||
public class DynamicDataGeneration : WireMockOpenApiParserDynamicExampleValues
|
|
||||||
{
|
|
||||||
public override string String
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
//Since you have your Schema, you can get if max-lenght is set. You can generate accurate examples with this settings
|
|
||||||
var maxLength = this.Schema.MaxLength ?? 9;
|
|
||||||
|
|
||||||
return RandomizerFactory.GetRandomizer(new FieldOptionsTextRegex
|
public class DynamicDataGeneration : WireMockOpenApiParserDynamicExampleValues
|
||||||
{
|
{
|
||||||
Pattern = $"[0-9A-Z]{{{maxLength}}}"
|
public override string String
|
||||||
}).Generate() ?? "example-string";
|
{
|
||||||
}
|
get
|
||||||
set { }
|
{
|
||||||
|
// Since you have your Schema, you can get if max-length is set. You can generate accurate examples with this settings
|
||||||
|
var maxLength = Schema.MaxLength ?? 9;
|
||||||
|
|
||||||
|
return RandomizerFactory.GetRandomizer(new FieldOptionsTextRegex
|
||||||
|
{
|
||||||
|
Pattern = $"[0-9A-Z]{{{maxLength}}}"
|
||||||
|
}).Generate() ?? "example-string";
|
||||||
}
|
}
|
||||||
|
set { }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,130 @@
|
|||||||
|
openapi: 3.0.1
|
||||||
|
info:
|
||||||
|
title: Basic-String-Test
|
||||||
|
description: Basic string test
|
||||||
|
version: "4.5.2"
|
||||||
|
servers:
|
||||||
|
- url: https://localhost/examples
|
||||||
|
paths:
|
||||||
|
/string/basic:
|
||||||
|
get:
|
||||||
|
tags:
|
||||||
|
- basic-string
|
||||||
|
description: Basic string test
|
||||||
|
operationId: getBasicString1
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: successful operation
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
/string/maxlenght/minlenght:
|
||||||
|
get:
|
||||||
|
tags:
|
||||||
|
- basic-string
|
||||||
|
description: Basic string test with maxlength and minlength properties
|
||||||
|
operationId: getBasicString2
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: successful operation
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
maxLength: 8
|
||||||
|
minLength: 8
|
||||||
|
/string/maxlenght:
|
||||||
|
get:
|
||||||
|
tags:
|
||||||
|
- basic-string
|
||||||
|
description: Basic string test with maxlength property
|
||||||
|
operationId: getBasicString3
|
||||||
|
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: successful operation
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
maxLength: 5
|
||||||
|
/string/minlenght:
|
||||||
|
get:
|
||||||
|
tags:
|
||||||
|
- basic-string
|
||||||
|
description: Basic string test with minlength property
|
||||||
|
operationId: getBasicString
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: successful operation
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
minLength: 10
|
||||||
|
/string/enum:
|
||||||
|
get:
|
||||||
|
tags:
|
||||||
|
- basic-string
|
||||||
|
description: Basic string test with enum property
|
||||||
|
operationId: getBasicString4
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: successful operation
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
enum:
|
||||||
|
- response1
|
||||||
|
- response2
|
||||||
|
/string/pattern/uri:
|
||||||
|
get:
|
||||||
|
tags:
|
||||||
|
- basic-string
|
||||||
|
description: Basic string test with uri pattern property
|
||||||
|
operationId: getBasicString5
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: successful operation
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
pattern: '^(http|https|ftp|sftp)://((([0-9]|([1-9][0-9])|(1[0-9][0-9])|(2[0-4][0-9]))\.){3}([0-9]|([1-9][0-9])|(1[0-9][0-9])|(2[0-4][0-9]))|((www\.|())[a-z0-9]{2,5}\.([a-z]{2,3}((\.[a-z]{2})|()))))(()|(:((102[5-9])|(1[0-9][3-9][0-9])|(1[1-9][0-9]{2})|([2-9][0-9]{3})|([2-5][0-9]{4})|(1[0-9]{4})|(60000))))$'
|
||||||
|
/string/pattern/ipv4:
|
||||||
|
get:
|
||||||
|
tags:
|
||||||
|
- basic-string
|
||||||
|
description: Basic string test with ipv4 pattern property
|
||||||
|
operationId: getBasicString6
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: successful operation
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
pattern: '^(([0-9]|([1-9][0-9])|(1[0-9][0-9])|(2[0-4][0-9]))\.){3}([0-9]|([1-9][0-9])|(1[0-9][0-9])|(2[0-4][0-9]))$'
|
||||||
|
/string/header/ipv4:
|
||||||
|
get:
|
||||||
|
tags:
|
||||||
|
- basic-string
|
||||||
|
description: Basic string test with ipv4 pattern property
|
||||||
|
operationId: getBasicString7
|
||||||
|
parameters:
|
||||||
|
- name: Header-Sample
|
||||||
|
in: header
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
pattern: "ipv4 pattern"
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: successful operation
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
pattern: '^(([0-9]|([1-9][0-9])|(1[0-9][0-9])|(2[0-4][0-9]))\.){3}([0-9]|([1-9][0-9])|(1[0-9][0-9])|(2[0-4][0-9]))$'
|
||||||
@@ -0,0 +1,157 @@
|
|||||||
|
{
|
||||||
|
"openapi": "3.0.2",
|
||||||
|
"info": {
|
||||||
|
"title": "Swagger Petstore - OpenAPI 3.0",
|
||||||
|
"description": "This is a sample Pet Store Server based on the OpenAPI 3.0 specification. You can find out more about\nSwagger at [http://swagger.io](http://swagger.io). In the third iteration of the pet store, we've switched to the design first approach!\nYou can now help us improve the API whether it's by making changes to the definition itself or to the code.\nThat way, with time, we can improve the API in general, and expose some of the new features in OAS3.\n\nSome useful links:\n- [The Pet Store repository](https://github.com/swagger-api/swagger-petstore)\n- [The source API definition for the Pet Store](https://github.com/swagger-api/swagger-petstore/blob/master/src/main/resources/openapi.yaml)",
|
||||||
|
"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"
|
||||||
|
},
|
||||||
|
"version": "1.0.11"
|
||||||
|
},
|
||||||
|
"externalDocs": {
|
||||||
|
"description": "Find out more about Swagger",
|
||||||
|
"url": "http://swagger.io"
|
||||||
|
},
|
||||||
|
"servers": [ { "url": "/api/v3" } ],
|
||||||
|
"tags": [
|
||||||
|
{
|
||||||
|
"name": "pet",
|
||||||
|
"description": "Everything about your Pets",
|
||||||
|
"externalDocs": {
|
||||||
|
"description": "Find out more",
|
||||||
|
"url": "http://swagger.io"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "store",
|
||||||
|
"description": "Access to Petstore orders",
|
||||||
|
"externalDocs": {
|
||||||
|
"description": "Find out more about our store",
|
||||||
|
"url": "http://swagger.io"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "user",
|
||||||
|
"description": "Operations about user"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"paths": {
|
||||||
|
"/pet": {
|
||||||
|
"put": {
|
||||||
|
"tags": [ "pet" ],
|
||||||
|
"summary": "Update an existing pet",
|
||||||
|
"description": "Update an existing pet by Id",
|
||||||
|
"operationId": "updatePet",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"name": "api_key",
|
||||||
|
"in": "header",
|
||||||
|
"description": "",
|
||||||
|
"required": true,
|
||||||
|
"schema": { "type": "string" }
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"requestBody": {
|
||||||
|
"description": "Update an existent pet in the store",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": { "$ref": "#/components/schemas/Pet" }
|
||||||
|
},
|
||||||
|
"application/xml": {
|
||||||
|
"schema": { "$ref": "#/components/schemas/Pet" }
|
||||||
|
},
|
||||||
|
"application/x-www-form-urlencoded": {
|
||||||
|
"schema": { "$ref": "#/components/schemas/Pet" }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "Successful operation",
|
||||||
|
"content": {
|
||||||
|
"application/xml": {
|
||||||
|
"schema": { "$ref": "#/components/schemas/Pet" }
|
||||||
|
},
|
||||||
|
"application/json": {
|
||||||
|
"schema": { "$ref": "#/components/schemas/Pet" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"400": { "description": "Invalid ID supplied" },
|
||||||
|
"404": { "description": "Pet not found" },
|
||||||
|
"405": { "description": "Validation exception" }
|
||||||
|
},
|
||||||
|
"security": [ { "petstore_auth": [ "write:pets", "read:pets" ] } ]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"components": {
|
||||||
|
"schemas": {
|
||||||
|
"Category": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "int64",
|
||||||
|
"example": 1
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"type": "string",
|
||||||
|
"example": "Dogs"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"xml": { "name": "category" }
|
||||||
|
},
|
||||||
|
"Tag": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "int64"
|
||||||
|
},
|
||||||
|
"name": { "type": "string" }
|
||||||
|
},
|
||||||
|
"xml": { "name": "tag" }
|
||||||
|
},
|
||||||
|
"Pet": {
|
||||||
|
"required": [ "name", "photoUrls" ],
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "int64",
|
||||||
|
"example": 10
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"type": "string",
|
||||||
|
"example": "doggie"
|
||||||
|
},
|
||||||
|
"category": { "$ref": "#/components/schemas/Category" },
|
||||||
|
"photoUrls": {
|
||||||
|
"type": "array",
|
||||||
|
"xml": { "wrapped": true },
|
||||||
|
"items": {
|
||||||
|
"type": "string",
|
||||||
|
"xml": { "name": "photoUrl" }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"tags": {
|
||||||
|
"type": "array",
|
||||||
|
"xml": { "wrapped": true },
|
||||||
|
"items": { "$ref": "#/components/schemas/Tag" }
|
||||||
|
},
|
||||||
|
"status": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "pet status in the store",
|
||||||
|
"enum": [ "available", "pending", "sold" ]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"xml": { "name": "pet" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,178 @@
|
|||||||
|
swagger: "2.0"
|
||||||
|
info:
|
||||||
|
description: "This is a sample server Petstore server. You can find out more about Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/). For this sample, you can use the api key `special-key` to test the authorization filters."
|
||||||
|
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:
|
||||||
|
- "https"
|
||||||
|
- "http"
|
||||||
|
paths:
|
||||||
|
/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:
|
||||||
|
"200":
|
||||||
|
description: "successful operation"
|
||||||
|
schema:
|
||||||
|
$ref: "#/definitions/Order"
|
||||||
|
default:
|
||||||
|
description: "successful operation"
|
||||||
|
definitions:
|
||||||
|
Order:
|
||||||
|
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:
|
||||||
|
type: "object"
|
||||||
|
properties:
|
||||||
|
id:
|
||||||
|
type: "integer"
|
||||||
|
format: "int64"
|
||||||
|
name:
|
||||||
|
type: "string"
|
||||||
|
xml:
|
||||||
|
name: "Category"
|
||||||
|
User:
|
||||||
|
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:
|
||||||
|
type: "object"
|
||||||
|
properties:
|
||||||
|
id:
|
||||||
|
type: "integer"
|
||||||
|
format: "int64"
|
||||||
|
name:
|
||||||
|
type: "string"
|
||||||
|
xml:
|
||||||
|
name: "Tag"
|
||||||
|
Pet:
|
||||||
|
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:
|
||||||
|
type: "object"
|
||||||
|
properties:
|
||||||
|
code:
|
||||||
|
type: "integer"
|
||||||
|
format: "int32"
|
||||||
|
type:
|
||||||
|
type: "string"
|
||||||
|
message:
|
||||||
|
type: "string"
|
||||||
|
externalDocs:
|
||||||
|
description: "Find out more about Swagger"
|
||||||
|
url: "http://swagger.io"
|
||||||
@@ -0,0 +1,150 @@
|
|||||||
|
{
|
||||||
|
"openapi": "3.0.2",
|
||||||
|
"info": {
|
||||||
|
"title": "Swagger Petstore - OpenAPI 3.0",
|
||||||
|
"description": "This is a sample Pet Store Server based on the OpenAPI 3.0 specification. You can find out more about\nSwagger at [http://swagger.io](http://swagger.io). In the third iteration of the pet store, we've switched to the design first approach!\nYou can now help us improve the API whether it's by making changes to the definition itself or to the code.\nThat way, with time, we can improve the API in general, and expose some of the new features in OAS3.\n\nSome useful links:\n- [The Pet Store repository](https://github.com/swagger-api/swagger-petstore)\n- [The source API definition for the Pet Store](https://github.com/swagger-api/swagger-petstore/blob/master/src/main/resources/openapi.yaml) ",
|
||||||
|
"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"
|
||||||
|
},
|
||||||
|
"version": "1.0.4"
|
||||||
|
},
|
||||||
|
"externalDocs": {
|
||||||
|
"description": "Find out more about Swagger",
|
||||||
|
"url": "http://swagger.io"
|
||||||
|
},
|
||||||
|
"servers": [ { "url": "/api/v3" } ],
|
||||||
|
"tags": [
|
||||||
|
{
|
||||||
|
"name": "pet",
|
||||||
|
"description": "Everything about your Pets",
|
||||||
|
"externalDocs": {
|
||||||
|
"description": "Find out more",
|
||||||
|
"url": "http://swagger.io"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "store",
|
||||||
|
"description": "Operations about user"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "user",
|
||||||
|
"description": "Access to Petstore orders",
|
||||||
|
"externalDocs": {
|
||||||
|
"description": "Find out more about our store",
|
||||||
|
"url": "http://swagger.io"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"paths": {
|
||||||
|
"/pet": {
|
||||||
|
"put": {
|
||||||
|
"tags": [ "pet" ],
|
||||||
|
"summary": "Update an existing pet",
|
||||||
|
"description": "Update an existing pet by Id",
|
||||||
|
"operationId": "updatePet",
|
||||||
|
"requestBody": {
|
||||||
|
"description": "Update an existent pet in the store",
|
||||||
|
"content": {
|
||||||
|
"application/json": { "schema": { "$ref": "#/components/schemas/Pet" } },
|
||||||
|
"application/xml": { "schema": { "$ref": "#/components/schemas/Pet" } },
|
||||||
|
"application/x-www-form-urlencoded": { "schema": { "$ref": "#/components/schemas/Pet" } }
|
||||||
|
},
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "Successful operation",
|
||||||
|
"content": {
|
||||||
|
"application/xml": { "schema": { "$ref": "#/components/schemas/Pet" } },
|
||||||
|
"application/json": { "schema": { "$ref": "#/components/schemas/Pet" } }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"400": { "description": "Invalid ID supplied" },
|
||||||
|
"404": { "description": "Pet not found" },
|
||||||
|
"405": { "description": "Validation exception" }
|
||||||
|
},
|
||||||
|
"security": [ { "petstore_auth": [ "write:pets", "read:pets" ] } ]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"components": {
|
||||||
|
"schemas": {
|
||||||
|
"Category": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "int64",
|
||||||
|
"example": 1
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"type": "string",
|
||||||
|
"example": "Dogs"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"xml": { "name": "category" }
|
||||||
|
},
|
||||||
|
"Tag": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "int64"
|
||||||
|
},
|
||||||
|
"name": { "type": "string" }
|
||||||
|
},
|
||||||
|
"xml": { "name": "tag" }
|
||||||
|
},
|
||||||
|
"Pet": {
|
||||||
|
"required": [ "name", "photoUrls" ],
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "int64",
|
||||||
|
"example": 10
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"type": "string",
|
||||||
|
"example": "doggie"
|
||||||
|
},
|
||||||
|
"category": { "$ref": "#/components/schemas/Category" },
|
||||||
|
"photoUrls": {
|
||||||
|
"type": "array",
|
||||||
|
"xml": { "wrapped": true },
|
||||||
|
"items": {
|
||||||
|
"type": "string",
|
||||||
|
"xml": { "name": "photoUrl" }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"tags": {
|
||||||
|
"type": "array",
|
||||||
|
"xml": { "wrapped": true },
|
||||||
|
"items": { "$ref": "#/components/schemas/Tag" }
|
||||||
|
},
|
||||||
|
"status": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "pet status in the store",
|
||||||
|
"enum": [ "available", "pending", "sold" ]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"xml": { "name": "pet" }
|
||||||
|
},
|
||||||
|
"ApiResponse": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"code": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "int32"
|
||||||
|
},
|
||||||
|
"type": { "type": "string" },
|
||||||
|
"message": { "type": "string" }
|
||||||
|
},
|
||||||
|
"xml": { "name": "##default" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,59 +1,90 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using Microsoft.OpenApi.Readers;
|
using WireMock.RequestBuilders;
|
||||||
using Newtonsoft.Json;
|
using WireMock.ResponseBuilders;
|
||||||
|
|
||||||
namespace WireMock.Net.OpenApiParser.ConsoleApp
|
namespace WireMock.Net.OpenApiParser.ConsoleApp;
|
||||||
{
|
|
||||||
class Program
|
|
||||||
{
|
|
||||||
private const string Folder = "OpenApiFiles";
|
|
||||||
static void Main(string[] args)
|
|
||||||
{
|
|
||||||
//RunOthersOpenApiParserExample();
|
|
||||||
|
|
||||||
RunMockServerWithDynamicExampleGeneration();
|
class Program
|
||||||
}
|
{
|
||||||
|
private const string Folder = "OpenApiFiles";
|
||||||
private static void RunMockServerWithDynamicExampleGeneration() {
|
static void Main(string[] args)
|
||||||
//Run your mocking framework specifieing youur Example Values generator class.
|
{
|
||||||
var serverCustomer_V2_json = Run.RunServer(Path.Combine(Folder, "Swagger_Customer_V2.0.json"), "http://localhost:8090/", true, new DynamicDataGeneration(), Types.ExampleValueType.Value, Types.ExampleValueType.Value);
|
RunOthersOpenApiParserExample();
|
||||||
Console.WriteLine("Press any key to stop the servers");
|
|
||||||
|
|
||||||
Console.ReadKey();
|
|
||||||
serverCustomer_V2_json.Stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void RunOthersOpenApiParserExample()
|
|
||||||
{
|
|
||||||
var serverOpenAPIExamples = Run.RunServer(Path.Combine(Folder, "openAPIExamples.yaml"), "https://localhost:9091/");
|
|
||||||
var serverPetstore_V2_json = Run.RunServer(Path.Combine(Folder, "Swagger_Petstore_V2.0.json"), "https://localhost:9092/");
|
|
||||||
var serverPetstore_V2_yaml = Run.RunServer(Path.Combine(Folder, "Swagger_Petstore_V2.0.yaml"), "https://localhost:9093/");
|
|
||||||
var serverPetstore_V300_yaml = Run.RunServer(Path.Combine(Folder, "Swagger_Petstore_V3.0.0.yaml"), "https://localhost:9094/");
|
|
||||||
var serverPetstore_V302_json = Run.RunServer(Path.Combine(Folder, "Swagger_Petstore_V3.0.2.json"), "https://localhost:9095/");
|
|
||||||
|
|
||||||
Console.WriteLine("Press any key to stop the servers");
|
//RunMockServerWithDynamicExampleGeneration();
|
||||||
Console.ReadKey();
|
}
|
||||||
|
|
||||||
serverOpenAPIExamples.Stop();
|
private static void RunMockServerWithDynamicExampleGeneration()
|
||||||
serverPetstore_V2_json.Stop();
|
{
|
||||||
serverPetstore_V2_yaml.Stop();
|
//Run your mocking framework specifieing youur Example Values generator class.
|
||||||
serverPetstore_V300_yaml.Stop();
|
var serverCustomer_V2_json = Run.RunServer(Path.Combine(Folder, "Swagger_Customer_V2.0.json"), "http://localhost:8090/", true, new DynamicDataGeneration(), Types.ExampleValueType.Value, Types.ExampleValueType.Value);
|
||||||
serverPetstore_V302_json.Stop();
|
Console.WriteLine("Press any key to stop the servers");
|
||||||
|
|
||||||
//IWireMockOpenApiParser parser = new WireMockOpenApiParser();
|
Console.ReadKey();
|
||||||
|
serverCustomer_V2_json.Stop();
|
||||||
|
}
|
||||||
|
|
||||||
//var petStoreModels = parser.FromStream(File.OpenRead("petstore-openapi3.json"), out OpenApiDiagnostic diagnostic1);
|
private static void RunOthersOpenApiParserExample()
|
||||||
//string petStoreJson = JsonConvert.SerializeObject(petStoreModels, Settings);
|
{
|
||||||
// File.WriteAllText("../../../wiremock-petstore-openapi3.json", petStoreJson);
|
var serverOpenAPIExamples = Run.RunServer(Path.Combine(Folder, "openAPIExamples.yaml"), "https://localhost:9091/");
|
||||||
|
var serverPetstore_V2_json = Run.RunServer(Path.Combine(Folder, "Swagger_Petstore_V2.0.json"), "https://localhost:9092/");
|
||||||
|
var serverPetstore_V2_yaml = Run.RunServer(Path.Combine(Folder, "Swagger_Petstore_V2.0.yaml"), "https://localhost:9093/");
|
||||||
|
var serverPetstore_V300_yaml = Run.RunServer(Path.Combine(Folder, "Swagger_Petstore_V3.0.0.yaml"), "https://localhost:9094/");
|
||||||
|
var serverPetstore_V302_json = Run.RunServer(Path.Combine(Folder, "Swagger_Petstore_V3.0.2.json"), "https://localhost:9095/");
|
||||||
|
var testopenapifile_json = Run.RunServer(Path.Combine(Folder, "testopenapifile.json"), "https://localhost:9096/");
|
||||||
|
var file_errorYaml = Run.RunServer(Path.Combine(Folder, "file_error.yaml"), "https://localhost:9097/");
|
||||||
|
var file_petJson = Run.RunServer(Path.Combine(Folder, "pet.json"), "https://localhost:9098/");
|
||||||
|
var refsYaml = Run.RunServer(Path.Combine(Folder, "refs.yaml"), "https://localhost:9099/");
|
||||||
|
|
||||||
//Run.RunServer(petStoreModels);
|
testopenapifile_json
|
||||||
|
.Given(Request.Create().WithPath("/x").UsingGet())
|
||||||
|
.WithTitle("t")
|
||||||
|
.WithDescription("d")
|
||||||
|
.RespondWith(Response.Create()
|
||||||
|
.WithStatusCode(200)
|
||||||
|
.WithHeader("Content-Type", "application/json")
|
||||||
|
.WithBodyAsJson(new
|
||||||
|
{
|
||||||
|
result = "ok"
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
//var mappingModels2 = parser.FromStream(File.OpenRead("infura.yaml"), out OpenApiDiagnostic diagnostic2);
|
testopenapifile_json
|
||||||
//Console.WriteLine(JsonConvert.SerializeObject(diagnostic2, Settings));
|
.Given(Request.Create().WithPath("/y").UsingGet())
|
||||||
|
.WithTitle("t2")
|
||||||
|
.WithDescription("d2")
|
||||||
|
.RespondWith(Response.Create()
|
||||||
|
.WithStatusCode(200)
|
||||||
|
.WithHeader("Content-Type", "application/json")
|
||||||
|
.WithBodyAsJson(new[] { "string-value"})
|
||||||
|
);
|
||||||
|
|
||||||
//string json2 = JsonConvert.SerializeObject(mappingModels2, Settings);
|
Console.WriteLine("Press any key to stop the servers");
|
||||||
//Console.WriteLine(json2);
|
Console.ReadKey();
|
||||||
}
|
|
||||||
}
|
serverOpenAPIExamples.Stop();
|
||||||
|
serverPetstore_V2_json.Stop();
|
||||||
|
serverPetstore_V2_yaml.Stop();
|
||||||
|
serverPetstore_V300_yaml.Stop();
|
||||||
|
serverPetstore_V302_json.Stop();
|
||||||
|
testopenapifile_json.Stop();
|
||||||
|
file_errorYaml.Stop();
|
||||||
|
file_petJson.Stop();
|
||||||
|
refsYaml.Stop();
|
||||||
|
|
||||||
|
//IWireMockOpenApiParser parser = new WireMockOpenApiParser();
|
||||||
|
|
||||||
|
//var petStoreModels = parser.FromStream(File.OpenRead("petstore-openapi3.json"), out OpenApiDiagnostic diagnostic1);
|
||||||
|
//string petStoreJson = JsonConvert.SerializeObject(petStoreModels, Settings);
|
||||||
|
// File.WriteAllText("../../../wiremock-petstore-openapi3.json", petStoreJson);
|
||||||
|
|
||||||
|
//Run.RunServer(petStoreModels);
|
||||||
|
|
||||||
|
//var mappingModels2 = parser.FromStream(File.OpenRead("infura.yaml"), out OpenApiDiagnostic diagnostic2);
|
||||||
|
//Console.WriteLine(JsonConvert.SerializeObject(diagnostic2, Settings));
|
||||||
|
|
||||||
|
//string json2 = JsonConvert.SerializeObject(mappingModels2, Settings);
|
||||||
|
//Console.WriteLine(json2);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,44 +1,29 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<TargetFramework>net5.0</TargetFramework>
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\..\src\WireMock.Net.Abstractions\WireMock.Net.Abstractions.csproj" />
|
<ProjectReference Include="..\..\src\WireMock.Net.Abstractions\WireMock.Net.Abstractions.csproj" />
|
||||||
<ProjectReference Include="..\..\src\WireMock.Net.OpenApiParser\WireMock.Net.OpenApiParser.csproj" />
|
<ProjectReference Include="..\..\src\WireMock.Net.OpenApiParser\WireMock.Net.OpenApiParser.csproj" />
|
||||||
<ProjectReference Include="..\..\src\WireMock.Net\WireMock.Net.csproj" />
|
<ProjectReference Include="..\..\src\WireMock.Net\WireMock.Net.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Update="infura.yaml">
|
<None Update="*.yaml">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</None>
|
</None>
|
||||||
<None Update="OpenApiFiles\openAPIExamples.yaml">
|
<None Update="*.json">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</None>
|
</None>
|
||||||
<None Update="OpenApiFiles\Swagger_Petstore_V2.0.json">
|
<None Update="OpenApiFiles\*.yaml">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</None>
|
</None>
|
||||||
<None Update="OpenApiFiles\Swagger_Petstore_V2.0.yaml">
|
<None Update="OpenApiFiles\*.json">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</None>
|
</None>
|
||||||
<None Update="OpenApiFiles\Swagger_Petstore_V3.0.0.yaml">
|
</ItemGroup>
|
||||||
<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>
|
|
||||||
<None Update="petstore.yml">
|
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
|
||||||
</None>
|
|
||||||
<None Update="OpenApiFiles\Swagger_Customer_V2.0.json">
|
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
|
||||||
</None>
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace WireMock.Admin.Mappings
|
namespace WireMock.Admin.Mappings
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Body Model
|
/// Body Model
|
||||||
@@ -9,11 +9,11 @@
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the matcher.
|
/// Gets or sets the matcher.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public MatcherModel Matcher { get; set; }
|
public MatcherModel? Matcher { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the matchers.
|
/// Gets or sets the matchers.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public MatcherModel[] Matchers { get; set; }
|
public MatcherModel[]? Matchers { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,30 +1,31 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace WireMock.Admin.Mappings;
|
namespace WireMock.Admin.Mappings
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Cookie Model
|
|
||||||
/// </summary>
|
|
||||||
[FluentBuilder.AutoGenerateBuilder]
|
|
||||||
public class CookieModel
|
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the name.
|
/// Cookie Model
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string Name { get; set; } = null!;
|
[FluentBuilder.AutoGenerateBuilder]
|
||||||
|
public class CookieModel
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the name.
|
||||||
|
/// </summary>
|
||||||
|
public string Name { get; set; } = null!;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the matchers.
|
/// Gets or sets the matchers.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IList<MatcherModel>? Matchers { get; set; }
|
public IList<MatcherModel>? Matchers { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the ignore case.
|
/// Gets or sets the ignore case.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool? IgnoreCase { get; set; }
|
public bool? IgnoreCase { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Reject on match.
|
/// Reject on match.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool? RejectOnMatch { get; set; }
|
public bool? RejectOnMatch { get; set; }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -14,17 +14,17 @@ namespace WireMock.Admin.Mappings
|
|||||||
/// <summary>
|
/// <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>
|
/// </summary>
|
||||||
public object Pattern { get; set; }
|
public object? Pattern { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the patterns. Can be array of strings (default) or an array of objects.
|
/// Gets or sets the patterns. Can be array of strings (default) or an array of objects.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public object[] Patterns { get; set; }
|
public object[]? Patterns { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the pattern as a file.
|
/// Gets or sets the pattern as a file.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string PatternAsFile { get; set; }
|
public string? PatternAsFile { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the ignore case.
|
/// Gets or sets the ignore case.
|
||||||
@@ -36,4 +36,4 @@ namespace WireMock.Admin.Mappings
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public bool? RejectOnMatch { get; set; }
|
public bool? RejectOnMatch { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace WireMock.Admin.Mappings
|
namespace WireMock.Admin.Mappings
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// PathModel
|
/// PathModel
|
||||||
@@ -9,6 +9,6 @@
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the matchers.
|
/// Gets or sets the matchers.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public MatcherModel[] Matchers { get; set; }
|
public MatcherModel[]? Matchers { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
using System.Linq;
|
||||||
|
using WireMock.Admin.Mappings;
|
||||||
|
|
||||||
|
namespace WireMock.Extensions;
|
||||||
|
|
||||||
|
public static class RequestModelExtensions
|
||||||
|
{
|
||||||
|
public static string? GetPathAsString(this RequestModel request)
|
||||||
|
{
|
||||||
|
var path = request.Path switch
|
||||||
|
{
|
||||||
|
string pathAsString => pathAsString,
|
||||||
|
PathModel pathModel => pathModel.Matchers?.FirstOrDefault()?.Pattern as string,
|
||||||
|
_ => null
|
||||||
|
};
|
||||||
|
|
||||||
|
return FixPath(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string? FixPath(string? path)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(path))
|
||||||
|
{
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
return path!.StartsWith("/") ? path : $"/{path}";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
using WireMock.Admin.Mappings;
|
||||||
|
|
||||||
|
namespace WireMock.Extensions;
|
||||||
|
|
||||||
|
public static class ResponseModelExtensions
|
||||||
|
{
|
||||||
|
private const string DefaultStatusCode = "200";
|
||||||
|
|
||||||
|
public static string GetStatusCodeAsString(this ResponseModel response)
|
||||||
|
{
|
||||||
|
return response.StatusCode switch
|
||||||
|
{
|
||||||
|
string statusCodeAsString => statusCodeAsString,
|
||||||
|
|
||||||
|
int statusCodeAsInt => statusCodeAsInt.ToString(),
|
||||||
|
|
||||||
|
_ => response.StatusCode?.ToString() ?? DefaultStatusCode
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using WireMock.ResponseBuilders;
|
using WireMock.ResponseBuilders;
|
||||||
using WireMock.Types;
|
using WireMock.Types;
|
||||||
using WireMock.Util;
|
using WireMock.Util;
|
||||||
@@ -13,7 +13,7 @@ namespace WireMock
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The Body.
|
/// The Body.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
IBodyData BodyData { get; }
|
IBodyData? BodyData { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the body destination (SameAsSource, String or Bytes).
|
/// Gets the body destination (SameAsSource, String or Bytes).
|
||||||
|
|||||||
@@ -1,130 +1,132 @@
|
|||||||
|
#pragma warning disable CS1591
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using FluentAssertions;
|
using FluentAssertions;
|
||||||
using FluentAssertions.Execution;
|
using FluentAssertions.Execution;
|
||||||
using WireMock.Server;
|
using WireMock.Server;
|
||||||
|
|
||||||
// ReSharper disable once CheckNamespace
|
// ReSharper disable once CheckNamespace
|
||||||
namespace WireMock.FluentAssertions
|
namespace WireMock.FluentAssertions;
|
||||||
|
|
||||||
|
public class WireMockAssertions
|
||||||
{
|
{
|
||||||
public class WireMockAssertions
|
private readonly IWireMockServer _subject;
|
||||||
|
private readonly int? _callsCount;
|
||||||
|
|
||||||
|
public WireMockAssertions(IWireMockServer subject, int? callsCount)
|
||||||
{
|
{
|
||||||
private readonly IWireMockServer _subject;
|
_subject = subject;
|
||||||
|
_callsCount = callsCount;
|
||||||
|
}
|
||||||
|
|
||||||
public WireMockAssertions(IWireMockServer subject, int? callsCount)
|
[CustomAssertion]
|
||||||
|
public AndConstraint<WireMockAssertions> AtAbsoluteUrl(string absoluteUrl, string because = "", params object[] becauseArgs)
|
||||||
|
{
|
||||||
|
Execute.Assertion
|
||||||
|
.BecauseOf(because, becauseArgs)
|
||||||
|
.Given(() => _subject.LogEntries.Select(x => x.RequestMessage).ToList())
|
||||||
|
.ForCondition(requests => requests.Any())
|
||||||
|
.FailWith(
|
||||||
|
"Expected {context:wiremockserver} to have been called at address matching the absolute url {0}{reason}, but no calls were made.",
|
||||||
|
absoluteUrl)
|
||||||
|
.Then
|
||||||
|
.ForCondition(x => _callsCount == null && x.Any(y => y.AbsoluteUrl == absoluteUrl) || _callsCount == x.Count(y => y.AbsoluteUrl == absoluteUrl))
|
||||||
|
.FailWith(
|
||||||
|
"Expected {context:wiremockserver} to have been called at address matching the absolute url {0}{reason}, but didn't find it among the calls to {1}.",
|
||||||
|
_ => absoluteUrl, requests => requests.Select(request => request.AbsoluteUrl));
|
||||||
|
|
||||||
|
return new AndConstraint<WireMockAssertions>(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
[CustomAssertion]
|
||||||
|
public AndConstraint<WireMockAssertions> AtUrl(string url, string because = "", params object[] becauseArgs)
|
||||||
|
{
|
||||||
|
Execute.Assertion
|
||||||
|
.BecauseOf(because, becauseArgs)
|
||||||
|
.Given(() => _subject.LogEntries.Select(x => x.RequestMessage).ToList())
|
||||||
|
.ForCondition(requests => requests.Any())
|
||||||
|
.FailWith(
|
||||||
|
"Expected {context:wiremockserver} to have been called at address matching the url {0}{reason}, but no calls were made.",
|
||||||
|
url)
|
||||||
|
.Then
|
||||||
|
.ForCondition(x => _callsCount == null && x.Any(y => y.Url == url) || _callsCount == x.Count(y => y.Url == url))
|
||||||
|
.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)
|
||||||
|
{
|
||||||
|
Execute.Assertion
|
||||||
|
.BecauseOf(because, becauseArgs)
|
||||||
|
.Given(() => _subject.LogEntries.Select(x => x.RequestMessage).ToList())
|
||||||
|
.ForCondition(requests => requests.Any())
|
||||||
|
.FailWith(
|
||||||
|
"Expected {context:wiremockserver} to have been called with proxy url {0}{reason}, but no calls were made.",
|
||||||
|
proxyUrl)
|
||||||
|
.Then
|
||||||
|
.ForCondition(x => _callsCount == null && x.Any(y => y.ProxyUrl == proxyUrl) || _callsCount == x.Count(y => y.ProxyUrl == proxyUrl))
|
||||||
|
.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)
|
||||||
|
{
|
||||||
|
Execute.Assertion
|
||||||
|
.BecauseOf(because, becauseArgs)
|
||||||
|
.Given(() => _subject.LogEntries.Select(x => x.RequestMessage).ToList())
|
||||||
|
.ForCondition(requests => requests.Any())
|
||||||
|
.FailWith(
|
||||||
|
"Expected {context:wiremockserver} to have been called from client IP {0}{reason}, but no calls were made.",
|
||||||
|
clientIP)
|
||||||
|
.Then
|
||||||
|
.ForCondition(x => _callsCount == null && x.Any(y => y.ClientIP == clientIP) || _callsCount == x.Count(y => y.ClientIP == clientIP))
|
||||||
|
.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);
|
||||||
|
}
|
||||||
|
|
||||||
|
[CustomAssertion]
|
||||||
|
public AndConstraint<WireMockAssertions> WithHeader(string expectedKey, string value, string because = "", params object[] becauseArgs)
|
||||||
|
=> WithHeader(expectedKey, new[] { value }, because, becauseArgs);
|
||||||
|
|
||||||
|
[CustomAssertion]
|
||||||
|
public AndConstraint<WireMockAssertions> WithHeader(string expectedKey, string[] expectedValues, string because = "", params object[] becauseArgs)
|
||||||
|
{
|
||||||
|
var headersDictionary = _subject.LogEntries.SelectMany(x => x.RequestMessage.Headers)
|
||||||
|
.ToDictionary(x => x.Key, x => x.Value);
|
||||||
|
|
||||||
|
using (new AssertionScope("headers from requests sent"))
|
||||||
{
|
{
|
||||||
_subject = subject;
|
headersDictionary.Should().ContainKey(expectedKey, because, becauseArgs);
|
||||||
}
|
}
|
||||||
|
|
||||||
[CustomAssertion]
|
using (new AssertionScope($"header \"{expectedKey}\" from requests sent with value(s)"))
|
||||||
public AndConstraint<WireMockAssertions> AtAbsoluteUrl(string absoluteUrl, string because = "", params object[] becauseArgs)
|
|
||||||
{
|
{
|
||||||
Execute.Assertion
|
if (expectedValues.Length == 1)
|
||||||
.BecauseOf(because, becauseArgs)
|
|
||||||
.Given(() => _subject.LogEntries.Select(x => x.RequestMessage).ToList())
|
|
||||||
.ForCondition(requests => requests.Any())
|
|
||||||
.FailWith(
|
|
||||||
"Expected {context:wiremockserver} to have been called at address matching the absolute url {0}{reason}, but no calls were made.",
|
|
||||||
absoluteUrl)
|
|
||||||
.Then
|
|
||||||
.ForCondition(x => x.Any(y => y.AbsoluteUrl == absoluteUrl))
|
|
||||||
.FailWith(
|
|
||||||
"Expected {context:wiremockserver} to have been called at address matching the absolute url {0}{reason}, but didn't find it among the calls to {1}.",
|
|
||||||
_ => absoluteUrl, requests => requests.Select(request => request.AbsoluteUrl));
|
|
||||||
|
|
||||||
return new AndConstraint<WireMockAssertions>(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
[CustomAssertion]
|
|
||||||
public AndConstraint<WireMockAssertions> WithHeader(string expectedKey, string value, string because = "", params object[] becauseArgs)
|
|
||||||
=> WithHeader(expectedKey, new[] { value }, because, becauseArgs);
|
|
||||||
|
|
||||||
[CustomAssertion]
|
|
||||||
public AndConstraint<WireMockAssertions> WithHeader(string expectedKey, string[] expectedValues, string because = "", params object[] becauseArgs)
|
|
||||||
{
|
|
||||||
var headersDictionary = _subject.LogEntries.SelectMany(x => x.RequestMessage.Headers)
|
|
||||||
.ToDictionary(x => x.Key, x => x.Value);
|
|
||||||
|
|
||||||
using (new AssertionScope("headers from requests sent"))
|
|
||||||
{
|
{
|
||||||
headersDictionary.Should().ContainKey(expectedKey, because, becauseArgs);
|
headersDictionary[expectedKey].Should().Contain(expectedValues.First());
|
||||||
}
|
}
|
||||||
|
else
|
||||||
using (new AssertionScope($"header \"{expectedKey}\" from requests sent with value(s)"))
|
|
||||||
{
|
{
|
||||||
if (expectedValues.Length == 1)
|
var trimmedHeaderValues = string.Join(",", headersDictionary[expectedKey].Select(x => x)).Split(',')
|
||||||
|
.Select(x => x.Trim())
|
||||||
|
.ToList();
|
||||||
|
foreach (var expectedValue in expectedValues)
|
||||||
{
|
{
|
||||||
headersDictionary[expectedKey].Should().Contain(expectedValues.First());
|
trimmedHeaderValues.Should().Contain(expectedValue);
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var trimmedHeaderValues = string.Join(",", headersDictionary[expectedKey].Select(x => x)).Split(',')
|
|
||||||
.Select(x => x.Trim())
|
|
||||||
.ToList();
|
|
||||||
foreach (var expectedValue in expectedValues)
|
|
||||||
{
|
|
||||||
trimmedHeaderValues.Should().Contain(expectedValue);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return new AndConstraint<WireMockAssertions>(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[CustomAssertion]
|
return new AndConstraint<WireMockAssertions>(this);
|
||||||
public AndConstraint<WireMockAssertions> AtUrl(string url, string because = "", params object[] becauseArgs)
|
|
||||||
{
|
|
||||||
Execute.Assertion
|
|
||||||
.BecauseOf(because, becauseArgs)
|
|
||||||
.Given(() => _subject.LogEntries.Select(x => x.RequestMessage).ToList())
|
|
||||||
.ForCondition(requests => requests.Any())
|
|
||||||
.FailWith(
|
|
||||||
"Expected {context:wiremockserver} to have been called at address matching the url {0}{reason}, but no calls were made.",
|
|
||||||
url)
|
|
||||||
.Then
|
|
||||||
.ForCondition(x => x.Any(y => y.Url == url))
|
|
||||||
.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)
|
|
||||||
{
|
|
||||||
Execute.Assertion
|
|
||||||
.BecauseOf(because, becauseArgs)
|
|
||||||
.Given(() => _subject.LogEntries.Select(x => x.RequestMessage).ToList())
|
|
||||||
.ForCondition(requests => requests.Any())
|
|
||||||
.FailWith(
|
|
||||||
"Expected {context:wiremockserver} to have been called with proxy url {0}{reason}, but no calls were made.",
|
|
||||||
proxyUrl)
|
|
||||||
.Then
|
|
||||||
.ForCondition(x => x.Any(y => y.ProxyUrl == proxyUrl))
|
|
||||||
.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)
|
|
||||||
{
|
|
||||||
Execute.Assertion
|
|
||||||
.BecauseOf(because, becauseArgs)
|
|
||||||
.Given(() => _subject.LogEntries.Select(x => x.RequestMessage).ToList())
|
|
||||||
.ForCondition(requests => requests.Any())
|
|
||||||
.FailWith(
|
|
||||||
"Expected {context:wiremockserver} to have been called from client IP {0}{reason}, but no calls were made.",
|
|
||||||
clientIP)
|
|
||||||
.Then
|
|
||||||
.ForCondition(x => x.Any(y => y.ClientIP == clientIP))
|
|
||||||
.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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -17,6 +17,15 @@ namespace WireMock.FluentAssertions
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Asserts if <see cref="IWireMockServer"/> has received no calls.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns><see cref="WireMockAssertions"/></returns>
|
||||||
|
public WireMockAssertions HaveReceivedNoCalls()
|
||||||
|
{
|
||||||
|
return new WireMockAssertions(Subject, 0);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Asserts if <see cref="IWireMockServer"/> has received a call.
|
/// Asserts if <see cref="IWireMockServer"/> has received a call.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
namespace WireMock.Constants
|
namespace WireMock.Constants;
|
||||||
|
|
||||||
|
internal static class WireMockConstants
|
||||||
{
|
{
|
||||||
internal static class WireMockConstants
|
public const int AdminPriority = int.MinValue;
|
||||||
{
|
public const int MinPriority = -1_000_000;
|
||||||
public const int AdminPriority = int.MinValue;
|
public const int ProxyPriority = -2_000_000;
|
||||||
public const int MinPriority = -1_000_000;
|
|
||||||
public const int ProxyPriority = -2_000_000;
|
public const string ContentTypeJson = "application/json";
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,11 +1,10 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using JetBrains.Annotations;
|
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
using WireMock.Util;
|
|
||||||
using Stef.Validation;
|
using Stef.Validation;
|
||||||
|
using WireMock.Util;
|
||||||
|
|
||||||
namespace WireMock.Matchers
|
namespace WireMock.Matchers
|
||||||
{
|
{
|
||||||
@@ -38,7 +37,7 @@ namespace WireMock.Matchers
|
|||||||
/// <param name="value">The string value to check for equality.</param>
|
/// <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="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>
|
/// <param name="throwException">Throw an exception when the internal matching fails because of invalid input.</param>
|
||||||
public JsonMatcher([NotNull] string value, bool ignoreCase = false, bool throwException = false) : this(MatchBehaviour.AcceptOnMatch, value, ignoreCase, throwException)
|
public JsonMatcher(string value, bool ignoreCase = false, bool throwException = false) : this(MatchBehaviour.AcceptOnMatch, value, ignoreCase, throwException)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -48,7 +47,7 @@ namespace WireMock.Matchers
|
|||||||
/// <param name="value">The object value to check for equality.</param>
|
/// <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="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>
|
/// <param name="throwException">Throw an exception when the internal matching fails because of invalid input.</param>
|
||||||
public JsonMatcher([NotNull] object value, bool ignoreCase = false, bool throwException = false) : this(MatchBehaviour.AcceptOnMatch, value, ignoreCase, throwException)
|
public JsonMatcher(object value, bool ignoreCase = false, bool throwException = false) : this(MatchBehaviour.AcceptOnMatch, value, ignoreCase, throwException)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -59,7 +58,7 @@ namespace WireMock.Matchers
|
|||||||
/// <param name="value">The value to check for equality.</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="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>
|
/// <param name="throwException">Throw an exception when the internal matching fails because of invalid input.</param>
|
||||||
public JsonMatcher(MatchBehaviour matchBehaviour, [NotNull] object value, bool ignoreCase = false, bool throwException = false)
|
public JsonMatcher(MatchBehaviour matchBehaviour, object value, bool ignoreCase = false, bool throwException = false)
|
||||||
{
|
{
|
||||||
Guard.NotNull(value, nameof(value));
|
Guard.NotNull(value, nameof(value));
|
||||||
|
|
||||||
@@ -75,12 +74,12 @@ namespace WireMock.Matchers
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc cref="IObjectMatcher.IsMatch"/>
|
/// <inheritdoc cref="IObjectMatcher.IsMatch"/>
|
||||||
public double IsMatch(object input)
|
public double IsMatch(object? input)
|
||||||
{
|
{
|
||||||
bool match = false;
|
bool match = false;
|
||||||
|
|
||||||
// When input is null or byte[], return Mismatch.
|
// When input is null or byte[], return Mismatch.
|
||||||
if (input != null && !(input is byte[]))
|
if (input != null && input is not byte[])
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -132,7 +131,7 @@ namespace WireMock.Matchers
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string ToUpper(string input)
|
private static string? ToUpper(string? input)
|
||||||
{
|
{
|
||||||
return input?.ToUpperInvariant();
|
return input?.ToUpperInvariant();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,108 +8,107 @@ using WireMock.Models;
|
|||||||
using WireMock.RegularExpressions;
|
using WireMock.RegularExpressions;
|
||||||
using Stef.Validation;
|
using Stef.Validation;
|
||||||
|
|
||||||
namespace WireMock.Matchers
|
namespace WireMock.Matchers;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Regular Expression Matcher
|
||||||
|
/// </summary>
|
||||||
|
/// <inheritdoc cref="IStringMatcher"/>
|
||||||
|
/// <inheritdoc cref="IIgnoreCaseMatcher"/>
|
||||||
|
public class RegexMatcher : IStringMatcher, IIgnoreCaseMatcher
|
||||||
{
|
{
|
||||||
|
private readonly AnyOf<string, StringPattern>[] _patterns;
|
||||||
|
private readonly Regex[] _expressions;
|
||||||
|
|
||||||
|
/// <inheritdoc cref="IMatcher.MatchBehaviour"/>
|
||||||
|
public MatchBehaviour MatchBehaviour { get; }
|
||||||
|
|
||||||
|
/// <inheritdoc cref="IMatcher.ThrowException"/>
|
||||||
|
public bool ThrowException { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Regular Expression Matcher
|
/// Initializes a new instance of the <see cref="RegexMatcher"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <inheritdoc cref="IStringMatcher"/>
|
/// <param name="pattern">The pattern.</param>
|
||||||
/// <inheritdoc cref="IIgnoreCaseMatcher"/>
|
/// <param name="ignoreCase">Ignore the case from the pattern.</param>
|
||||||
public class RegexMatcher : IStringMatcher, IIgnoreCaseMatcher
|
/// <param name="throwException">Throw an exception when the internal matching fails because of invalid input.</param>
|
||||||
|
/// <param name="useRegexExtended">Use RegexExtended (default = true).</param>
|
||||||
|
public RegexMatcher([NotNull, RegexPattern] AnyOf<string, StringPattern> pattern, bool ignoreCase = false, bool throwException = false, bool useRegexExtended = true) :
|
||||||
|
this(MatchBehaviour.AcceptOnMatch, new[] { pattern }, ignoreCase, throwException, useRegexExtended)
|
||||||
{
|
{
|
||||||
private readonly AnyOf<string, StringPattern>[] _patterns;
|
|
||||||
private readonly Regex[] _expressions;
|
|
||||||
|
|
||||||
/// <inheritdoc cref="IMatcher.MatchBehaviour"/>
|
|
||||||
public MatchBehaviour MatchBehaviour { get; }
|
|
||||||
|
|
||||||
/// <inheritdoc cref="IMatcher.ThrowException"/>
|
|
||||||
public bool ThrowException { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the <see cref="RegexMatcher"/> class.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="pattern">The pattern.</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>
|
|
||||||
/// <param name="useRegexExtended">Use RegexExtended (default = true).</param>
|
|
||||||
public RegexMatcher([NotNull, RegexPattern] AnyOf<string, StringPattern> pattern, bool ignoreCase = false, bool throwException = false, bool useRegexExtended = true) :
|
|
||||||
this(MatchBehaviour.AcceptOnMatch, new[] { pattern }, ignoreCase, throwException, useRegexExtended)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the <see cref="RegexMatcher"/> class.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="matchBehaviour">The match behaviour.</param>
|
|
||||||
/// <param name="pattern">The pattern.</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>
|
|
||||||
/// <param name="useRegexExtended">Use RegexExtended (default = true).</param>
|
|
||||||
public RegexMatcher(MatchBehaviour matchBehaviour, [NotNull, RegexPattern] AnyOf<string, StringPattern> pattern, bool ignoreCase = false, bool throwException = false, bool useRegexExtended = true) :
|
|
||||||
this(matchBehaviour, new[] { pattern }, ignoreCase, throwException, useRegexExtended)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the <see cref="RegexMatcher"/> class.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="matchBehaviour">The match behaviour.</param>
|
|
||||||
/// <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>
|
|
||||||
/// <param name="useRegexExtended">Use RegexExtended (default = true).</param>
|
|
||||||
public RegexMatcher(MatchBehaviour matchBehaviour, [NotNull, RegexPattern] AnyOf<string, StringPattern>[] patterns, bool ignoreCase = false, bool throwException = false, bool useRegexExtended = true)
|
|
||||||
{
|
|
||||||
Guard.NotNull(patterns, nameof(patterns));
|
|
||||||
|
|
||||||
_patterns = patterns;
|
|
||||||
IgnoreCase = ignoreCase;
|
|
||||||
MatchBehaviour = matchBehaviour;
|
|
||||||
ThrowException = throwException;
|
|
||||||
|
|
||||||
RegexOptions options = RegexOptions.Compiled | RegexOptions.Multiline;
|
|
||||||
|
|
||||||
if (ignoreCase)
|
|
||||||
{
|
|
||||||
options |= RegexOptions.IgnoreCase;
|
|
||||||
}
|
|
||||||
|
|
||||||
_expressions = patterns.Select(p => useRegexExtended ? new RegexExtended(p.GetPattern(), options) : new Regex(p.GetPattern(), options)).ToArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc cref="IStringMatcher.IsMatch"/>
|
|
||||||
public virtual double IsMatch(string input)
|
|
||||||
{
|
|
||||||
double match = MatchScores.Mismatch;
|
|
||||||
if (input != null)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
match = MatchScores.ToScore(_expressions.Select(e => e.IsMatch(input)));
|
|
||||||
}
|
|
||||||
catch (Exception)
|
|
||||||
{
|
|
||||||
if (ThrowException)
|
|
||||||
{
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return MatchBehaviourHelper.Convert(MatchBehaviour, match);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc cref="IStringMatcher.GetPatterns"/>
|
|
||||||
public virtual AnyOf<string, StringPattern>[] GetPatterns()
|
|
||||||
{
|
|
||||||
return _patterns;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc cref="IMatcher.Name"/>
|
|
||||||
public virtual string Name => "RegexMatcher";
|
|
||||||
|
|
||||||
/// <inheritdoc cref="IIgnoreCaseMatcher.IgnoreCase"/>
|
|
||||||
public bool IgnoreCase { get; }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="RegexMatcher"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="matchBehaviour">The match behaviour.</param>
|
||||||
|
/// <param name="pattern">The pattern.</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>
|
||||||
|
/// <param name="useRegexExtended">Use RegexExtended (default = true).</param>
|
||||||
|
public RegexMatcher(MatchBehaviour matchBehaviour, [NotNull, RegexPattern] AnyOf<string, StringPattern> pattern, bool ignoreCase = false, bool throwException = false, bool useRegexExtended = true) :
|
||||||
|
this(matchBehaviour, new[] { pattern }, ignoreCase, throwException, useRegexExtended)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="RegexMatcher"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="matchBehaviour">The match behaviour.</param>
|
||||||
|
/// <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>
|
||||||
|
/// <param name="useRegexExtended">Use RegexExtended (default = true).</param>
|
||||||
|
public RegexMatcher(MatchBehaviour matchBehaviour, [NotNull, RegexPattern] AnyOf<string, StringPattern>[] patterns, bool ignoreCase = false, bool throwException = false, bool useRegexExtended = true)
|
||||||
|
{
|
||||||
|
Guard.NotNull(patterns, nameof(patterns));
|
||||||
|
|
||||||
|
_patterns = patterns;
|
||||||
|
IgnoreCase = ignoreCase;
|
||||||
|
MatchBehaviour = matchBehaviour;
|
||||||
|
ThrowException = throwException;
|
||||||
|
|
||||||
|
RegexOptions options = RegexOptions.Compiled | RegexOptions.Multiline;
|
||||||
|
|
||||||
|
if (ignoreCase)
|
||||||
|
{
|
||||||
|
options |= RegexOptions.IgnoreCase;
|
||||||
|
}
|
||||||
|
|
||||||
|
_expressions = patterns.Select(p => useRegexExtended ? new RegexExtended(p.GetPattern(), options) : new Regex(p.GetPattern(), options)).ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc cref="IStringMatcher.IsMatch"/>
|
||||||
|
public virtual double IsMatch(string input)
|
||||||
|
{
|
||||||
|
double match = MatchScores.Mismatch;
|
||||||
|
if (input != null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
match = MatchScores.ToScore(_expressions.Select(e => e.IsMatch(input)));
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
if (ThrowException)
|
||||||
|
{
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return MatchBehaviourHelper.Convert(MatchBehaviour, match);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public virtual AnyOf<string, StringPattern>[] GetPatterns()
|
||||||
|
{
|
||||||
|
return _patterns;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public virtual string Name => nameof(RegexMatcher);
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public bool IgnoreCase { get; }
|
||||||
}
|
}
|
||||||
@@ -5,75 +5,74 @@ using JetBrains.Annotations;
|
|||||||
using WireMock.Extensions;
|
using WireMock.Extensions;
|
||||||
using WireMock.Models;
|
using WireMock.Models;
|
||||||
|
|
||||||
namespace WireMock.Matchers
|
namespace WireMock.Matchers;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// WildcardMatcher
|
||||||
|
/// </summary>
|
||||||
|
/// <seealso cref="RegexMatcher" />
|
||||||
|
public class WildcardMatcher : RegexMatcher
|
||||||
{
|
{
|
||||||
|
private readonly AnyOf<string, StringPattern>[] _patterns;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// WildcardMatcher
|
/// Initializes a new instance of the <see cref="WildcardMatcher"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <seealso cref="RegexMatcher" />
|
/// <param name="pattern">The pattern.</param>
|
||||||
public class WildcardMatcher : RegexMatcher
|
/// <param name="ignoreCase">IgnoreCase</param>
|
||||||
|
public WildcardMatcher([NotNull] AnyOf<string, StringPattern> pattern, bool ignoreCase = false) : this(new[] { pattern }, ignoreCase)
|
||||||
{
|
{
|
||||||
private readonly AnyOf<string, StringPattern>[] _patterns;
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="WildcardMatcher"/> class.
|
/// Initializes a new instance of the <see cref="WildcardMatcher"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="pattern">The pattern.</param>
|
/// <param name="matchBehaviour">The match behaviour.</param>
|
||||||
/// <param name="ignoreCase">IgnoreCase</param>
|
/// <param name="pattern">The pattern.</param>
|
||||||
public WildcardMatcher([NotNull] AnyOf<string, StringPattern> pattern, bool ignoreCase = false) : this(new[] { pattern }, ignoreCase)
|
/// <param name="ignoreCase">IgnoreCase</param>
|
||||||
{
|
public WildcardMatcher(MatchBehaviour matchBehaviour, [NotNull] AnyOf<string, StringPattern> pattern, bool ignoreCase = false) : this(matchBehaviour, new[] { pattern }, ignoreCase)
|
||||||
}
|
{
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="WildcardMatcher"/> class.
|
/// Initializes a new instance of the <see cref="WildcardMatcher"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="matchBehaviour">The match behaviour.</param>
|
/// <param name="patterns">The patterns.</param>
|
||||||
/// <param name="pattern">The pattern.</param>
|
/// <param name="ignoreCase">IgnoreCase</param>
|
||||||
/// <param name="ignoreCase">IgnoreCase</param>
|
public WildcardMatcher([NotNull] AnyOf<string, StringPattern>[] patterns, bool ignoreCase = false) : this(MatchBehaviour.AcceptOnMatch, patterns, ignoreCase)
|
||||||
public WildcardMatcher(MatchBehaviour matchBehaviour, [NotNull] AnyOf<string, StringPattern> pattern, bool ignoreCase = false) : this(matchBehaviour, new[] { pattern }, ignoreCase)
|
{
|
||||||
{
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="WildcardMatcher"/> class.
|
/// Initializes a new instance of the <see cref="WildcardMatcher"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="patterns">The patterns.</param>
|
/// <param name="matchBehaviour">The match behaviour.</param>
|
||||||
/// <param name="ignoreCase">IgnoreCase</param>
|
/// <param name="patterns">The patterns.</param>
|
||||||
public WildcardMatcher([NotNull] AnyOf<string, StringPattern>[] patterns, bool ignoreCase = false) : this(MatchBehaviour.AcceptOnMatch, patterns, ignoreCase)
|
/// <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] AnyOf<string, StringPattern>[] patterns, bool ignoreCase = false, bool throwException = false) :
|
||||||
|
base(matchBehaviour, CreateArray(patterns), ignoreCase, throwException)
|
||||||
|
{
|
||||||
|
_patterns = patterns;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <inheritdoc />
|
||||||
/// Initializes a new instance of the <see cref="WildcardMatcher"/> class.
|
public override AnyOf<string, StringPattern>[] GetPatterns()
|
||||||
/// </summary>
|
{
|
||||||
/// <param name="matchBehaviour">The match behaviour.</param>
|
return _patterns;
|
||||||
/// <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] AnyOf<string, StringPattern>[] patterns, bool ignoreCase = false, bool throwException = false) :
|
|
||||||
base(matchBehaviour, CreateArray(patterns), ignoreCase, throwException)
|
|
||||||
{
|
|
||||||
_patterns = patterns;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc cref="IStringMatcher.GetPatterns"/>
|
/// <inheritdoc />
|
||||||
public override AnyOf<string, StringPattern>[] GetPatterns()
|
public override string Name => nameof(WildcardMatcher);
|
||||||
{
|
|
||||||
return _patterns;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc cref="IMatcher.Name"/>
|
private static AnyOf<string, StringPattern>[] CreateArray(AnyOf<string, StringPattern>[] patterns)
|
||||||
public override string Name => "WildcardMatcher";
|
{
|
||||||
|
return patterns.Select(pattern => new AnyOf<string, StringPattern>(
|
||||||
private static AnyOf<string, StringPattern>[] CreateArray(AnyOf<string, StringPattern>[] patterns)
|
|
||||||
{
|
|
||||||
return patterns.Select(pattern => new AnyOf<string, StringPattern>(
|
|
||||||
new StringPattern
|
new StringPattern
|
||||||
{
|
{
|
||||||
Pattern = "^" + Regex.Escape(pattern.GetPattern()).Replace(@"\*", ".*").Replace(@"\?", ".") + "$",
|
Pattern = "^" + Regex.Escape(pattern.GetPattern()).Replace(@"\*", ".*").Replace(@"\?", ".") + "$",
|
||||||
PatternAsFile = pattern.IsSecond ? pattern.Second.PatternAsFile : null
|
PatternAsFile = pattern.IsSecond ? pattern.Second.PatternAsFile : null
|
||||||
}))
|
}))
|
||||||
.ToArray();
|
.ToArray();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -17,6 +17,6 @@ namespace WireMock.Owin.Mappers
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="responseMessage">The ResponseMessage</param>
|
/// <param name="responseMessage">The ResponseMessage</param>
|
||||||
/// <param name="response">The OwinResponse/HttpResponse</param>
|
/// <param name="response">The OwinResponse/HttpResponse</param>
|
||||||
Task MapAsync(IResponseMessage responseMessage, IResponse response);
|
Task MapAsync(IResponseMessage? responseMessage, IResponse response);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -47,20 +47,18 @@ namespace WireMock.Owin.Mappers
|
|||||||
/// <param name="options">The IWireMockMiddlewareOptions.</param>
|
/// <param name="options">The IWireMockMiddlewareOptions.</param>
|
||||||
public OwinResponseMapper(IWireMockMiddlewareOptions options)
|
public OwinResponseMapper(IWireMockMiddlewareOptions options)
|
||||||
{
|
{
|
||||||
Guard.NotNull(options, nameof(options));
|
_options = Guard.NotNull(options);
|
||||||
|
|
||||||
_options = options;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc cref="IOwinResponseMapper.MapAsync"/>
|
/// <inheritdoc cref="IOwinResponseMapper.MapAsync"/>
|
||||||
public async Task MapAsync(IResponseMessage responseMessage, IResponse response)
|
public async Task MapAsync(IResponseMessage? responseMessage, IResponse response)
|
||||||
{
|
{
|
||||||
if (responseMessage == null)
|
if (responseMessage == null)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
byte[] bytes;
|
byte[]? bytes;
|
||||||
switch (responseMessage.FaultType)
|
switch (responseMessage.FaultType)
|
||||||
{
|
{
|
||||||
case FaultType.EMPTY_RESPONSE:
|
case FaultType.EMPTY_RESPONSE:
|
||||||
@@ -122,33 +120,28 @@ namespace WireMock.Owin.Mappers
|
|||||||
return responseMessage.FaultPercentage == null || _randomizerDouble.Generate() <= responseMessage.FaultPercentage;
|
return responseMessage.FaultPercentage == null || _randomizerDouble.Generate() <= responseMessage.FaultPercentage;
|
||||||
}
|
}
|
||||||
|
|
||||||
private byte[] GetNormalBody(IResponseMessage responseMessage)
|
private byte[]? GetNormalBody(IResponseMessage responseMessage)
|
||||||
{
|
{
|
||||||
byte[] bytes = null;
|
|
||||||
switch (responseMessage.BodyData?.DetectedBodyType)
|
switch (responseMessage.BodyData?.DetectedBodyType)
|
||||||
{
|
{
|
||||||
case BodyType.String:
|
case BodyType.String:
|
||||||
bytes = (responseMessage.BodyData.Encoding ?? _utf8NoBom).GetBytes(responseMessage.BodyData.BodyAsString);
|
return (responseMessage.BodyData.Encoding ?? _utf8NoBom).GetBytes(responseMessage.BodyData.BodyAsString);
|
||||||
break;
|
|
||||||
|
|
||||||
case BodyType.Json:
|
case BodyType.Json:
|
||||||
Formatting formatting = responseMessage.BodyData.BodyAsJsonIndented == true
|
var formatting = responseMessage.BodyData.BodyAsJsonIndented == true
|
||||||
? Formatting.Indented
|
? Formatting.Indented
|
||||||
: Formatting.None;
|
: Formatting.None;
|
||||||
string jsonBody = JsonConvert.SerializeObject(responseMessage.BodyData.BodyAsJson, new JsonSerializerSettings { Formatting = formatting, NullValueHandling = NullValueHandling.Ignore });
|
string jsonBody = JsonConvert.SerializeObject(responseMessage.BodyData.BodyAsJson, new JsonSerializerSettings { Formatting = formatting, NullValueHandling = NullValueHandling.Ignore });
|
||||||
bytes = (responseMessage.BodyData.Encoding ?? _utf8NoBom).GetBytes(jsonBody);
|
return (responseMessage.BodyData.Encoding ?? _utf8NoBom).GetBytes(jsonBody);
|
||||||
break;
|
|
||||||
|
|
||||||
case BodyType.Bytes:
|
case BodyType.Bytes:
|
||||||
bytes = responseMessage.BodyData.BodyAsBytes;
|
return responseMessage.BodyData.BodyAsBytes;
|
||||||
break;
|
|
||||||
|
|
||||||
case BodyType.File:
|
case BodyType.File:
|
||||||
bytes = _options.FileSystemHandler.ReadResponseBodyAsFile(responseMessage.BodyData.BodyAsFile);
|
return _options.FileSystemHandler.ReadResponseBodyAsFile(responseMessage.BodyData.BodyAsFile);
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return bytes;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void SetResponseHeaders(IResponseMessage responseMessage, IResponse response)
|
private static void SetResponseHeaders(IResponseMessage responseMessage, IResponse response)
|
||||||
|
|||||||
@@ -72,8 +72,8 @@ namespace WireMock.Owin
|
|||||||
var request = await _requestMapper.MapAsync(ctx.Request, _options).ConfigureAwait(false);
|
var request = await _requestMapper.MapAsync(ctx.Request, _options).ConfigureAwait(false);
|
||||||
|
|
||||||
var logRequest = false;
|
var logRequest = false;
|
||||||
IResponseMessage response = null;
|
IResponseMessage? response = null;
|
||||||
(MappingMatcherResult Match, MappingMatcherResult Partial) result = (null, null);
|
(MappingMatcherResult? Match, MappingMatcherResult? Partial) result = (null, null);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
foreach (var mapping in _options.Mappings.Values.Where(m => m?.Scenario != null))
|
foreach (var mapping in _options.Mappings.Values.Where(m => m?.Scenario != null))
|
||||||
|
|||||||
@@ -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.
|
// For more details see 'mock4net/LICENSE.txt' and 'mock4net/readme.md' in this project root.
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
@@ -27,7 +27,7 @@ namespace WireMock
|
|||||||
public string BodyDestination { get; set; }
|
public string BodyDestination { get; set; }
|
||||||
|
|
||||||
/// <inheritdoc cref="IResponseMessage.BodyData" />
|
/// <inheritdoc cref="IResponseMessage.BodyData" />
|
||||||
public IBodyData BodyData { get; set; }
|
public IBodyData? BodyData { get; set; }
|
||||||
|
|
||||||
/// <inheritdoc cref="IResponseMessage.FaultType" />
|
/// <inheritdoc cref="IResponseMessage.FaultType" />
|
||||||
public FaultType FaultType { get; set; }
|
public FaultType FaultType { get; set; }
|
||||||
|
|||||||
@@ -1,50 +1,49 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using WireMock.Admin.Mappings;
|
using WireMock.Admin.Mappings;
|
||||||
|
using WireMock.Constants;
|
||||||
using WireMock.Http;
|
using WireMock.Http;
|
||||||
using WireMock.Types;
|
using WireMock.Types;
|
||||||
using WireMock.Util;
|
using WireMock.Util;
|
||||||
|
|
||||||
namespace WireMock
|
namespace WireMock;
|
||||||
|
|
||||||
|
internal static class ResponseMessageBuilder
|
||||||
{
|
{
|
||||||
internal static class ResponseMessageBuilder
|
private static readonly IDictionary<string, WireMockList<string>> ContentTypeJsonHeaders = new Dictionary<string, WireMockList<string>>
|
||||||
{
|
{
|
||||||
private static string ContentTypeJson = "application/json";
|
{ HttpKnownHeaderNames.ContentType, new WireMockList<string> { WireMockConstants.ContentTypeJson } }
|
||||||
private static readonly IDictionary<string, WireMockList<string>> ContentTypeJsonHeaders = new Dictionary<string, WireMockList<string>>
|
};
|
||||||
|
|
||||||
|
internal static ResponseMessage Create(string? message, int statusCode = 200, Guid? guid = null)
|
||||||
|
{
|
||||||
|
var response = new ResponseMessage
|
||||||
{
|
{
|
||||||
{ HttpKnownHeaderNames.ContentType, new WireMockList<string> { ContentTypeJson } }
|
StatusCode = statusCode,
|
||||||
|
Headers = ContentTypeJsonHeaders
|
||||||
};
|
};
|
||||||
|
|
||||||
internal static ResponseMessage Create(string message, int statusCode = 200, Guid? guid = null)
|
if (message != null)
|
||||||
{
|
{
|
||||||
var response = new ResponseMessage
|
response.BodyData = new BodyData
|
||||||
{
|
{
|
||||||
StatusCode = statusCode,
|
DetectedBodyType = BodyType.Json,
|
||||||
Headers = ContentTypeJsonHeaders
|
BodyAsJson = new StatusModel
|
||||||
};
|
|
||||||
|
|
||||||
if (message != null)
|
|
||||||
{
|
|
||||||
response.BodyData = new BodyData
|
|
||||||
{
|
{
|
||||||
DetectedBodyType = BodyType.Json,
|
Guid = guid,
|
||||||
BodyAsJson = new StatusModel
|
Status = message
|
||||||
{
|
}
|
||||||
Guid = guid,
|
|
||||||
Status = message
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static ResponseMessage Create(int statusCode)
|
|
||||||
{
|
|
||||||
return new ResponseMessage
|
|
||||||
{
|
|
||||||
StatusCode = statusCode
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static ResponseMessage Create(int statusCode)
|
||||||
|
{
|
||||||
|
return new ResponseMessage
|
||||||
|
{
|
||||||
|
StatusCode = statusCode
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,30 +1,34 @@
|
|||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
using Newtonsoft.Json.Serialization;
|
||||||
|
|
||||||
namespace WireMock.Serialization
|
namespace WireMock.Serialization;
|
||||||
|
|
||||||
|
internal static class JsonSerializationConstants
|
||||||
{
|
{
|
||||||
internal static class JsonSerializationConstants
|
public static readonly JsonSerializerSettings JsonSerializerSettingsDefault = new()
|
||||||
{
|
{
|
||||||
public static readonly JsonSerializerSettings JsonSerializerSettingsDefault = new JsonSerializerSettings
|
Formatting = Formatting.Indented,
|
||||||
{
|
NullValueHandling = NullValueHandling.Ignore
|
||||||
Formatting = Formatting.Indented,
|
};
|
||||||
NullValueHandling = NullValueHandling.Ignore
|
|
||||||
};
|
|
||||||
|
|
||||||
public static readonly JsonSerializerSettings JsonSerializerSettingsIncludeNullValues = new JsonSerializerSettings
|
public static readonly JsonSerializerSettings JsonSerializerSettingsIncludeNullValues = new()
|
||||||
{
|
{
|
||||||
Formatting = Formatting.Indented,
|
Formatting = Formatting.Indented,
|
||||||
NullValueHandling = NullValueHandling.Include
|
NullValueHandling = NullValueHandling.Include
|
||||||
};
|
};
|
||||||
|
|
||||||
public static readonly JsonSerializerSettings JsonDeserializerSettingsWithDateParsingNone = new JsonSerializerSettings
|
public static readonly JsonSerializerSettings JsonDeserializerSettingsWithDateParsingNone = new()
|
||||||
{
|
{
|
||||||
DateParseHandling = DateParseHandling.None
|
DateParseHandling = DateParseHandling.None
|
||||||
};
|
};
|
||||||
|
|
||||||
public static readonly JsonSerializerSettings JsonSerializerSettingsPact = new JsonSerializerSettings
|
public static readonly JsonSerializerSettings JsonSerializerSettingsPact = new()
|
||||||
|
{
|
||||||
|
Formatting = Formatting.Indented,
|
||||||
|
NullValueHandling = NullValueHandling.Ignore,
|
||||||
|
ContractResolver = new DefaultContractResolver
|
||||||
{
|
{
|
||||||
Formatting = Formatting.Indented,
|
NamingStrategy = new CamelCaseNamingStrategy()
|
||||||
NullValueHandling = NullValueHandling.Ignore
|
}
|
||||||
};
|
};
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -2,7 +2,6 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using AnyOfTypes;
|
using AnyOfTypes;
|
||||||
using JetBrains.Annotations;
|
|
||||||
using SimMetrics.Net;
|
using SimMetrics.Net;
|
||||||
using WireMock.Admin.Mappings;
|
using WireMock.Admin.Mappings;
|
||||||
using WireMock.Extensions;
|
using WireMock.Extensions;
|
||||||
@@ -22,12 +21,12 @@ namespace WireMock.Serialization
|
|||||||
_settings = settings ?? throw new ArgumentNullException(nameof(settings));
|
_settings = settings ?? throw new ArgumentNullException(nameof(settings));
|
||||||
}
|
}
|
||||||
|
|
||||||
public IMatcher[] Map([CanBeNull] IEnumerable<MatcherModel> matchers)
|
public IMatcher[] Map(IEnumerable<MatcherModel>? matchers)
|
||||||
{
|
{
|
||||||
return matchers?.Select(Map).Where(m => m != null).ToArray();
|
return matchers?.Select(Map).Where(m => m != null).ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
public IMatcher Map([CanBeNull] MatcherModel matcher)
|
public IMatcher? Map(MatcherModel? matcher)
|
||||||
{
|
{
|
||||||
if (matcher == null)
|
if (matcher == null)
|
||||||
{
|
{
|
||||||
@@ -36,7 +35,7 @@ namespace WireMock.Serialization
|
|||||||
|
|
||||||
string[] parts = matcher.Name.Split('.');
|
string[] parts = matcher.Name.Split('.');
|
||||||
string matcherName = parts[0];
|
string matcherName = parts[0];
|
||||||
string matcherType = parts.Length > 1 ? parts[1] : null;
|
string? matcherType = parts.Length > 1 ? parts[1] : null;
|
||||||
var stringPatterns = ParseStringPatterns(matcher);
|
var stringPatterns = ParseStringPatterns(matcher);
|
||||||
var matchBehaviour = matcher.RejectOnMatch == true ? MatchBehaviour.RejectOnMatch : MatchBehaviour.AcceptOnMatch;
|
var matchBehaviour = matcher.RejectOnMatch == true ? MatchBehaviour.RejectOnMatch : MatchBehaviour.AcceptOnMatch;
|
||||||
bool ignoreCase = matcher.IgnoreCase == true;
|
bool ignoreCase = matcher.IgnoreCase == true;
|
||||||
@@ -69,16 +68,16 @@ namespace WireMock.Serialization
|
|||||||
return new RegexMatcher(matchBehaviour, stringPatterns, ignoreCase, throwExceptionWhenMatcherFails, useRegexExtended);
|
return new RegexMatcher(matchBehaviour, stringPatterns, ignoreCase, throwExceptionWhenMatcherFails, useRegexExtended);
|
||||||
|
|
||||||
case nameof(JsonMatcher):
|
case nameof(JsonMatcher):
|
||||||
object valueForJsonMatcher = matcher.Pattern ?? matcher.Patterns;
|
var valueForJsonMatcher = matcher.Pattern ?? matcher.Patterns;
|
||||||
return new JsonMatcher(matchBehaviour, valueForJsonMatcher, ignoreCase, throwExceptionWhenMatcherFails);
|
return new JsonMatcher(matchBehaviour, valueForJsonMatcher!, ignoreCase, throwExceptionWhenMatcherFails);
|
||||||
|
|
||||||
case nameof(JsonPartialMatcher):
|
case nameof(JsonPartialMatcher):
|
||||||
object valueForJsonPartialMatcher = matcher.Pattern ?? matcher.Patterns;
|
var valueForJsonPartialMatcher = matcher.Pattern ?? matcher.Patterns;
|
||||||
return new JsonPartialMatcher(matchBehaviour, valueForJsonPartialMatcher, ignoreCase, throwExceptionWhenMatcherFails);
|
return new JsonPartialMatcher(matchBehaviour, valueForJsonPartialMatcher!, ignoreCase, throwExceptionWhenMatcherFails);
|
||||||
|
|
||||||
case nameof(JsonPartialWildcardMatcher):
|
case nameof(JsonPartialWildcardMatcher):
|
||||||
object valueForJsonPartialWildcardMatcher = matcher.Pattern ?? matcher.Patterns;
|
var valueForJsonPartialWildcardMatcher = matcher.Pattern ?? matcher.Patterns;
|
||||||
return new JsonPartialWildcardMatcher(matchBehaviour, valueForJsonPartialWildcardMatcher, ignoreCase, throwExceptionWhenMatcherFails);
|
return new JsonPartialWildcardMatcher(matchBehaviour, valueForJsonPartialWildcardMatcher!, ignoreCase, throwExceptionWhenMatcherFails);
|
||||||
|
|
||||||
case nameof(JsonPathMatcher):
|
case nameof(JsonPathMatcher):
|
||||||
return new JsonPathMatcher(matchBehaviour, throwExceptionWhenMatcherFails, stringPatterns);
|
return new JsonPathMatcher(matchBehaviour, throwExceptionWhenMatcherFails, stringPatterns);
|
||||||
@@ -114,12 +113,12 @@ namespace WireMock.Serialization
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public MatcherModel[] Map([CanBeNull] IEnumerable<IMatcher> matchers)
|
public MatcherModel[] Map(IEnumerable<IMatcher>? matchers)
|
||||||
{
|
{
|
||||||
return matchers?.Select(Map).Where(m => m != null).ToArray();
|
return matchers?.Select(Map).Where(m => m != null).ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
public MatcherModel Map([CanBeNull] IMatcher matcher)
|
public MatcherModel? Map(IMatcher? matcher)
|
||||||
{
|
{
|
||||||
if (matcher == null)
|
if (matcher == null)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -2,28 +2,25 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using WireMock.Admin.Mappings;
|
using WireMock.Admin.Mappings;
|
||||||
|
using WireMock.Extensions;
|
||||||
|
using WireMock.Matchers;
|
||||||
using WireMock.Pact.Models.V2;
|
using WireMock.Pact.Models.V2;
|
||||||
|
using WireMock.Server;
|
||||||
using WireMock.Util;
|
using WireMock.Util;
|
||||||
|
|
||||||
namespace WireMock.Server;
|
namespace WireMock.Serialization;
|
||||||
|
|
||||||
public partial class WireMockServer
|
internal static class PactMapper
|
||||||
{
|
{
|
||||||
private const string DefaultPath = "/";
|
|
||||||
private const string DefaultMethod = "GET";
|
private const string DefaultMethod = "GET";
|
||||||
private const int DefaultStatus = 200;
|
private const int DefaultStatusCode = 200;
|
||||||
private const string DefaultConsumer = "Default Consumer";
|
private const string DefaultConsumer = "Default Consumer";
|
||||||
private const string DefaultProvider = "Default Provider";
|
private const string DefaultProvider = "Default Provider";
|
||||||
|
|
||||||
/// <summary>
|
public static (string FileName, byte[] Bytes) ToPact(WireMockServer server, string? filename = null)
|
||||||
/// Save the mappings as a Pact Json file V2.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="folder">The folder to save the pact file.</param>
|
|
||||||
/// <param name="filename">The filename for the .json file [optional].</param>
|
|
||||||
public void SavePact(string folder, string? filename = null)
|
|
||||||
{
|
{
|
||||||
var consumer = Consumer ?? DefaultConsumer;
|
var consumer = server.Consumer ?? DefaultConsumer;
|
||||||
var provider = Provider ?? DefaultProvider;
|
var provider = server.Provider ?? DefaultProvider;
|
||||||
|
|
||||||
filename ??= $"{consumer} - {provider}.json";
|
filename ??= $"{consumer} - {provider}.json";
|
||||||
|
|
||||||
@@ -33,41 +30,31 @@ public partial class WireMockServer
|
|||||||
Provider = new Pacticipant { Name = provider }
|
Provider = new Pacticipant { Name = provider }
|
||||||
};
|
};
|
||||||
|
|
||||||
foreach (var mapping in MappingModels)
|
foreach (var mapping in server.MappingModels.OrderBy(m => m.Guid))
|
||||||
{
|
{
|
||||||
|
var path = mapping.Request.GetPathAsString();
|
||||||
|
if (path == null)
|
||||||
|
{
|
||||||
|
// Path is null (probably a Func<>), skip this.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
var interaction = new Interaction
|
var interaction = new Interaction
|
||||||
{
|
{
|
||||||
Description = mapping.Description,
|
Description = mapping.Description,
|
||||||
ProviderState = mapping.Title,
|
ProviderState = mapping.Title,
|
||||||
Request = MapRequest(mapping.Request),
|
Request = MapRequest(mapping.Request, path),
|
||||||
Response = MapResponse(mapping.Response)
|
Response = MapResponse(mapping.Response)
|
||||||
};
|
};
|
||||||
|
|
||||||
pact.Interactions.Add(interaction);
|
pact.Interactions.Add(interaction);
|
||||||
}
|
}
|
||||||
|
|
||||||
var bytes = JsonUtils.SerializeAsPactFile(pact);
|
return (filename, JsonUtils.SerializeAsPactFile(pact));
|
||||||
_settings.FileSystemHandler.WriteFile(folder, filename, bytes);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Request MapRequest(RequestModel request)
|
private static Request MapRequest(RequestModel request, string path)
|
||||||
{
|
{
|
||||||
string path;
|
|
||||||
switch (request.Path)
|
|
||||||
{
|
|
||||||
case string pathAsString:
|
|
||||||
path = pathAsString;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case PathModel pathModel:
|
|
||||||
path = GetPatternAsStringFromMatchers(pathModel.Matchers, DefaultPath);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
path = DefaultPath;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Request
|
return new Request
|
||||||
{
|
{
|
||||||
Method = request.Methods?.FirstOrDefault() ?? DefaultMethod,
|
Method = request.Methods?.FirstOrDefault() ?? DefaultMethod,
|
||||||
@@ -97,7 +84,7 @@ public partial class WireMockServer
|
|||||||
{
|
{
|
||||||
if (statusCode is string statusCodeAsString)
|
if (statusCode is string statusCodeAsString)
|
||||||
{
|
{
|
||||||
return int.TryParse(statusCodeAsString, out var statusCodeAsInt) ? statusCodeAsInt : DefaultStatus;
|
return int.TryParse(statusCodeAsString, out var statusCodeAsInt) ? statusCodeAsInt : DefaultStatusCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (statusCode != null)
|
if (statusCode != null)
|
||||||
@@ -106,7 +93,7 @@ public partial class WireMockServer
|
|||||||
return Convert.ToInt32(statusCode);
|
return Convert.ToInt32(statusCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
return DefaultStatus;
|
return DefaultStatusCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string? MapQueryParameters(IList<ParamModel>? queryParameters)
|
private static string? MapQueryParameters(IList<ParamModel>? queryParameters)
|
||||||
@@ -118,41 +105,37 @@ public partial class WireMockServer
|
|||||||
|
|
||||||
var values = queryParameters
|
var values = queryParameters
|
||||||
.Where(qp => qp.Matchers != null && qp.Matchers.Any() && qp.Matchers[0].Pattern is string)
|
.Where(qp => qp.Matchers != null && qp.Matchers.Any() && qp.Matchers[0].Pattern is string)
|
||||||
.Select(param => $"{Uri.EscapeDataString(param.Name)}={Uri.EscapeDataString((string)param.Matchers![0].Pattern)}");
|
.Select(param => $"{Uri.EscapeDataString(param.Name)}={Uri.EscapeDataString((string)param.Matchers![0].Pattern!)}");
|
||||||
|
|
||||||
return string.Join("&", values);
|
return string.Join("&", values);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static IDictionary<string, string>? MapRequestHeaders(IList<HeaderModel>? headers)
|
private static IDictionary<string, string>? MapRequestHeaders(IList<HeaderModel>? headers)
|
||||||
{
|
{
|
||||||
if (headers == null)
|
var validHeaders = headers?.Where(h => h.Matchers != null && h.Matchers.Any() && h.Matchers[0].Pattern is string);
|
||||||
{
|
return validHeaders?.ToDictionary(x => x.Name, y => (string)y.Matchers![0].Pattern!);
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
var validHeaders = headers.Where(h => h.Matchers != null && h.Matchers.Any() && h.Matchers[0].Pattern is string);
|
|
||||||
return validHeaders.ToDictionary(x => x.Name, y => (string)y.Matchers![0].Pattern);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static IDictionary<string, string>? MapResponseHeaders(IDictionary<string, object>? headers)
|
private static IDictionary<string, string>? MapResponseHeaders(IDictionary<string, object>? headers)
|
||||||
{
|
{
|
||||||
if (headers == null)
|
var validHeaders = headers?.Where(h => h.Value is string);
|
||||||
{
|
return validHeaders?.ToDictionary(x => x.Key, y => (string)y.Value);
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
var validHeaders = headers.Where(h => h.Value is string);
|
|
||||||
return validHeaders.ToDictionary(x => x.Key, y => (string)y.Value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static object? MapBody(BodyModel? body)
|
private static object? MapBody(BodyModel? body)
|
||||||
{
|
{
|
||||||
if (body == null || body.Matcher.Name != "JsonMatcher")
|
if (body?.Matcher == null || body.Matchers == null)
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return body.Matcher.Pattern;
|
if (body.Matcher is { Name: nameof(JsonMatcher) })
|
||||||
|
{
|
||||||
|
return body.Matcher.Pattern;
|
||||||
|
}
|
||||||
|
|
||||||
|
var jsonMatcher = body.Matchers.FirstOrDefault(m => m.Name == nameof(JsonMatcher));
|
||||||
|
return jsonMatcher?.Pattern;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string GetPatternAsStringFromMatchers(MatcherModel[]? matchers, string defaultValue)
|
private static string GetPatternAsStringFromMatchers(MatcherModel[]? matchers, string defaultValue)
|
||||||
325
src/WireMock.Net/Serialization/SwaggerMapper.cs
Normal file
325
src/WireMock.Net/Serialization/SwaggerMapper.cs
Normal file
@@ -0,0 +1,325 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using NJsonSchema;
|
||||||
|
using NJsonSchema.Extensions;
|
||||||
|
using NSwag;
|
||||||
|
using WireMock.Admin.Mappings;
|
||||||
|
using WireMock.Constants;
|
||||||
|
using WireMock.Extensions;
|
||||||
|
using WireMock.Matchers;
|
||||||
|
using WireMock.Server;
|
||||||
|
using WireMock.Util;
|
||||||
|
|
||||||
|
namespace WireMock.Serialization;
|
||||||
|
|
||||||
|
internal static class SwaggerMapper
|
||||||
|
{
|
||||||
|
private const string DefaultMethod = "GET";
|
||||||
|
private const string Generator = "WireMock.Net";
|
||||||
|
|
||||||
|
private static readonly JsonSchema JsonSchemaString = new() { Type = JsonObjectType.String };
|
||||||
|
|
||||||
|
public static string ToSwagger(WireMockServer server)
|
||||||
|
{
|
||||||
|
var openApiDocument = new OpenApiDocument
|
||||||
|
{
|
||||||
|
Generator = Generator,
|
||||||
|
Info = new OpenApiInfo
|
||||||
|
{
|
||||||
|
Title = $"{Generator} Mappings Swagger specification",
|
||||||
|
Version = SystemUtils.Version
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
foreach (var url in server.Urls)
|
||||||
|
{
|
||||||
|
openApiDocument.Servers.Add(new OpenApiServer
|
||||||
|
{
|
||||||
|
Url = url
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var mapping in server.MappingModels)
|
||||||
|
{
|
||||||
|
var path = mapping.Request.GetPathAsString();
|
||||||
|
if (path == null)
|
||||||
|
{
|
||||||
|
// Path is null (probably a Func<>), skip this.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var operation = new OpenApiOperation();
|
||||||
|
foreach (var openApiParameter in MapRequestQueryParameters(mapping.Request.Params))
|
||||||
|
{
|
||||||
|
operation.Parameters.Add(openApiParameter);
|
||||||
|
}
|
||||||
|
foreach (var openApiParameter in MapRequestHeaders(mapping.Request.Headers))
|
||||||
|
{
|
||||||
|
operation.Parameters.Add(openApiParameter);
|
||||||
|
}
|
||||||
|
foreach (var openApiParameter in MapRequestCookies(mapping.Request.Cookies))
|
||||||
|
{
|
||||||
|
operation.Parameters.Add(openApiParameter);
|
||||||
|
}
|
||||||
|
|
||||||
|
operation.RequestBody = MapRequestBody(mapping.Request);
|
||||||
|
|
||||||
|
var response = MapResponse(mapping.Response);
|
||||||
|
if (response != null)
|
||||||
|
{
|
||||||
|
operation.Responses.Add(mapping.Response.GetStatusCodeAsString(), response);
|
||||||
|
}
|
||||||
|
|
||||||
|
var method = mapping.Request.Methods?.FirstOrDefault() ?? DefaultMethod;
|
||||||
|
if (!openApiDocument.Paths.ContainsKey(path))
|
||||||
|
{
|
||||||
|
var openApiPathItem = new OpenApiPathItem
|
||||||
|
{
|
||||||
|
{ method, operation }
|
||||||
|
};
|
||||||
|
|
||||||
|
openApiDocument.Paths.Add(path, openApiPathItem);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// The combination of path+method uniquely identify an operation. Duplicates are not allowed.
|
||||||
|
if (!openApiDocument.Paths[path].ContainsKey(method))
|
||||||
|
{
|
||||||
|
openApiDocument.Paths[path].Add(method, operation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return openApiDocument.ToJson(SchemaType.OpenApi3, Formatting.Indented);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IEnumerable<OpenApiParameter> MapRequestQueryParameters(IList<ParamModel>? queryParameters)
|
||||||
|
{
|
||||||
|
if (queryParameters == null)
|
||||||
|
{
|
||||||
|
return new List<OpenApiParameter>();
|
||||||
|
}
|
||||||
|
|
||||||
|
return queryParameters
|
||||||
|
.Where(x => x.Matchers != null && x.Matchers.Any())
|
||||||
|
.Select(x => new
|
||||||
|
{
|
||||||
|
x.Name,
|
||||||
|
Details = GetDetailsFromMatcher(x.Matchers![0])
|
||||||
|
})
|
||||||
|
.Select(x => new OpenApiParameter
|
||||||
|
{
|
||||||
|
Name = x.Name,
|
||||||
|
Example = x.Details.Example,
|
||||||
|
Description = x.Details.Description,
|
||||||
|
Kind = OpenApiParameterKind.Query,
|
||||||
|
Schema = x.Details.JsonSchemaRegex,
|
||||||
|
IsRequired = !x.Details.Reject
|
||||||
|
})
|
||||||
|
.ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IEnumerable<OpenApiParameter> MapRequestHeaders(IList<HeaderModel>? headers)
|
||||||
|
{
|
||||||
|
if (headers == null)
|
||||||
|
{
|
||||||
|
return new List<OpenApiHeader>();
|
||||||
|
}
|
||||||
|
|
||||||
|
return headers
|
||||||
|
.Where(x => x.Matchers != null && x.Matchers.Any())
|
||||||
|
.Select(x => new
|
||||||
|
{
|
||||||
|
x.Name,
|
||||||
|
Details = GetDetailsFromMatcher(x.Matchers![0])
|
||||||
|
})
|
||||||
|
.Select(x => new OpenApiHeader
|
||||||
|
{
|
||||||
|
Name = x.Name,
|
||||||
|
Example = x.Details.Example,
|
||||||
|
Description = x.Details.Description,
|
||||||
|
Kind = OpenApiParameterKind.Header,
|
||||||
|
Schema = x.Details.JsonSchemaRegex,
|
||||||
|
IsRequired = !x.Details.Reject
|
||||||
|
})
|
||||||
|
.ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IEnumerable<OpenApiParameter> MapRequestCookies(IList<CookieModel>? cookies)
|
||||||
|
{
|
||||||
|
if (cookies == null)
|
||||||
|
{
|
||||||
|
return new List<OpenApiParameter>();
|
||||||
|
}
|
||||||
|
|
||||||
|
return cookies
|
||||||
|
.Where(x => x.Matchers != null && x.Matchers.Any())
|
||||||
|
.Select(x => new
|
||||||
|
{
|
||||||
|
x.Name,
|
||||||
|
Details = GetDetailsFromMatcher(x.Matchers![0])
|
||||||
|
})
|
||||||
|
.Select(x => new OpenApiParameter
|
||||||
|
{
|
||||||
|
Name = x.Name,
|
||||||
|
Example = x.Details.Example,
|
||||||
|
Description = x.Details.Description,
|
||||||
|
Kind = OpenApiParameterKind.Cookie,
|
||||||
|
Schema = x.Details.JsonSchemaRegex,
|
||||||
|
IsRequired = !x.Details.Reject
|
||||||
|
})
|
||||||
|
.ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static (JsonSchema JsonSchemaRegex, string? Example, string? Description, bool Reject) GetDetailsFromMatcher(MatcherModel matcher)
|
||||||
|
{
|
||||||
|
var pattern = GetPatternAsStringFromMatcher(matcher);
|
||||||
|
var reject = matcher.RejectOnMatch == true;
|
||||||
|
var description = $"{matcher.Name} with RejectOnMatch = '{reject}' and Pattern = '{pattern}'";
|
||||||
|
|
||||||
|
return matcher.Name is nameof(RegexMatcher) ?
|
||||||
|
(new JsonSchema { Type = JsonObjectType.String, Format = "regex", Pattern = pattern }, pattern, description, reject) :
|
||||||
|
(JsonSchemaString, pattern, description, reject);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static OpenApiRequestBody? MapRequestBody(RequestModel request)
|
||||||
|
{
|
||||||
|
var body = MapRequestBody(request.Body);
|
||||||
|
if (body == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var openApiMediaType = new OpenApiMediaType
|
||||||
|
{
|
||||||
|
Schema = GetJsonSchema(body)
|
||||||
|
};
|
||||||
|
|
||||||
|
var requestBodyPost = new OpenApiRequestBody();
|
||||||
|
requestBodyPost.Content.Add(GetContentType(request), openApiMediaType);
|
||||||
|
|
||||||
|
return requestBodyPost;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static OpenApiResponse? MapResponse(ResponseModel response)
|
||||||
|
{
|
||||||
|
if (response.Body != null)
|
||||||
|
{
|
||||||
|
return new OpenApiResponse
|
||||||
|
{
|
||||||
|
Schema = new JsonSchemaProperty
|
||||||
|
{
|
||||||
|
Type = JsonObjectType.String,
|
||||||
|
Example = response.Body
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (response.BodyAsBytes != null)
|
||||||
|
{
|
||||||
|
// https://stackoverflow.com/questions/62794949/how-to-define-byte-array-in-openapi-3-0
|
||||||
|
return new OpenApiResponse
|
||||||
|
{
|
||||||
|
Schema = new JsonSchemaProperty
|
||||||
|
{
|
||||||
|
Type = JsonObjectType.Array,
|
||||||
|
Items =
|
||||||
|
{
|
||||||
|
new JsonSchema
|
||||||
|
{
|
||||||
|
Type = JsonObjectType.String,
|
||||||
|
Format = JsonFormatStrings.Byte
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (response.BodyAsJson == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new OpenApiResponse
|
||||||
|
{
|
||||||
|
Schema = GetJsonSchema(response.BodyAsJson)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static JsonSchema GetJsonSchema(object instance)
|
||||||
|
{
|
||||||
|
switch (instance)
|
||||||
|
{
|
||||||
|
case string instanceAsString:
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var value = JsonConvert.DeserializeObject(instanceAsString);
|
||||||
|
return GetJsonSchema(value!);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return JsonSchemaString;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
return instance.ToJsonSchema();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static object? MapRequestBody(BodyModel? body)
|
||||||
|
{
|
||||||
|
if (body == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var matcher = GetMatcher(body.Matcher, body.Matchers);
|
||||||
|
if (matcher is { Name: nameof(JsonMatcher) })
|
||||||
|
{
|
||||||
|
var pattern = GetPatternAsStringFromMatcher(matcher);
|
||||||
|
if (JsonUtils.TryParseAsJObject(pattern, out var jObject))
|
||||||
|
{
|
||||||
|
return jObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
return pattern;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetContentType(RequestModel request)
|
||||||
|
{
|
||||||
|
var contentType = request.Headers?.FirstOrDefault(h => h.Name == "Content-Type");
|
||||||
|
|
||||||
|
return contentType == null ?
|
||||||
|
WireMockConstants.ContentTypeJson :
|
||||||
|
GetPatternAsStringFromMatchers(contentType.Matchers, WireMockConstants.ContentTypeJson);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetPatternAsStringFromMatchers(IList<MatcherModel>? matchers, string defaultValue)
|
||||||
|
{
|
||||||
|
if (matchers == null || !matchers.Any())
|
||||||
|
{
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return GetPatternAsStringFromMatcher(matchers.First()) ?? defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string? GetPatternAsStringFromMatcher(MatcherModel matcher)
|
||||||
|
{
|
||||||
|
if (matcher.Pattern is string patternAsString)
|
||||||
|
{
|
||||||
|
return patternAsString;
|
||||||
|
}
|
||||||
|
|
||||||
|
return matcher.Patterns?.FirstOrDefault() as string;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static MatcherModel? GetMatcher(MatcherModel? matcher, MatcherModel[]? matchers)
|
||||||
|
{
|
||||||
|
return matcher ?? matchers?.FirstOrDefault();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -35,7 +35,6 @@ namespace WireMock.Server;
|
|||||||
public partial class WireMockServer
|
public partial class WireMockServer
|
||||||
{
|
{
|
||||||
private const int EnhancedFileSystemWatcherTimeoutMs = 1000;
|
private const int EnhancedFileSystemWatcherTimeoutMs = 1000;
|
||||||
private const string ContentTypeJson = "application/json";
|
|
||||||
private const string AdminFiles = "/__admin/files";
|
private const string AdminFiles = "/__admin/files";
|
||||||
private const string AdminMappings = "/__admin/mappings";
|
private const string AdminMappings = "/__admin/mappings";
|
||||||
private const string AdminMappingsWireMockOrg = "/__admin/mappings/wiremock.org";
|
private const string AdminMappingsWireMockOrg = "/__admin/mappings/wiremock.org";
|
||||||
@@ -45,7 +44,7 @@ public partial class WireMockServer
|
|||||||
private const string QueryParamReloadStaticMappings = "reloadStaticMappings";
|
private const string QueryParamReloadStaticMappings = "reloadStaticMappings";
|
||||||
|
|
||||||
private readonly Guid _proxyMappingGuid = new("e59914fd-782e-428e-91c1-4810ffb86567");
|
private readonly Guid _proxyMappingGuid = new("e59914fd-782e-428e-91c1-4810ffb86567");
|
||||||
private readonly RegexMatcher _adminRequestContentTypeJson = new ContentTypeMatcher(ContentTypeJson, true);
|
private readonly RegexMatcher _adminRequestContentTypeJson = new ContentTypeMatcher(WireMockConstants.ContentTypeJson, true);
|
||||||
private readonly RegexMatcher _adminMappingsGuidPathMatcher = new(@"^\/__admin\/mappings\/([0-9A-Fa-f]{8}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{12})$");
|
private readonly RegexMatcher _adminMappingsGuidPathMatcher = new(@"^\/__admin\/mappings\/([0-9A-Fa-f]{8}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{12})$");
|
||||||
private readonly RegexMatcher _adminRequestsGuidPathMatcher = new(@"^\/__admin\/requests\/([0-9A-Fa-f]{8}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{12})$");
|
private readonly RegexMatcher _adminRequestsGuidPathMatcher = new(@"^\/__admin\/requests\/([0-9A-Fa-f]{8}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{12})$");
|
||||||
|
|
||||||
@@ -73,7 +72,10 @@ public partial class WireMockServer
|
|||||||
Given(Request.Create().WithPath(_adminMappingsGuidPathMatcher).UsingDelete()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(MappingDelete));
|
Given(Request.Create().WithPath(_adminMappingsGuidPathMatcher).UsingDelete()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(MappingDelete));
|
||||||
|
|
||||||
// __admin/mappings/save
|
// __admin/mappings/save
|
||||||
Given(Request.Create().WithPath(AdminMappings + "/save").UsingPost()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(MappingsSave));
|
Given(Request.Create().WithPath($"{AdminMappings}/save").UsingPost()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(MappingsSave));
|
||||||
|
|
||||||
|
// __admin/mappings/swagger
|
||||||
|
Given(Request.Create().WithPath($"{AdminMappings}/swagger").UsingGet()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(SwaggerGet));
|
||||||
|
|
||||||
// __admin/requests
|
// __admin/requests
|
||||||
Given(Request.Create().WithPath(AdminRequests).UsingGet()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(RequestsGet));
|
Given(Request.Create().WithPath(AdminRequests).UsingGet()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(RequestsGet));
|
||||||
@@ -148,7 +150,7 @@ public partial class WireMockServer
|
|||||||
|
|
||||||
/// <inheritdoc cref="IWireMockServer.WatchStaticMappings" />
|
/// <inheritdoc cref="IWireMockServer.WatchStaticMappings" />
|
||||||
[PublicAPI]
|
[PublicAPI]
|
||||||
public void WatchStaticMappings([CanBeNull] string folder = null)
|
public void WatchStaticMappings(string? folder = null)
|
||||||
{
|
{
|
||||||
if (folder == null)
|
if (folder == null)
|
||||||
{
|
{
|
||||||
@@ -379,6 +381,20 @@ public partial class WireMockServer
|
|||||||
#endregion Mapping/{guid}
|
#endregion Mapping/{guid}
|
||||||
|
|
||||||
#region Mappings
|
#region Mappings
|
||||||
|
private IResponseMessage SwaggerGet(IRequestMessage requestMessage)
|
||||||
|
{
|
||||||
|
return new ResponseMessage
|
||||||
|
{
|
||||||
|
BodyData = new BodyData
|
||||||
|
{
|
||||||
|
DetectedBodyType = BodyType.String,
|
||||||
|
BodyAsString = SwaggerMapper.ToSwagger(this)
|
||||||
|
},
|
||||||
|
StatusCode = (int)HttpStatusCode.OK,
|
||||||
|
Headers = new Dictionary<string, WireMockList<string>> { { HttpKnownHeaderNames.ContentType, new WireMockList<string>(WireMockConstants.ContentTypeJson) } }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
private IResponseMessage MappingsSave(IRequestMessage requestMessage)
|
private IResponseMessage MappingsSave(IRequestMessage requestMessage)
|
||||||
{
|
{
|
||||||
SaveStaticMappings();
|
SaveStaticMappings();
|
||||||
@@ -667,6 +683,19 @@ public partial class WireMockServer
|
|||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
#region Pact
|
||||||
|
/// <summary>
|
||||||
|
/// Save the mappings as a Pact Json file V2.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="folder">The folder to save the pact file.</param>
|
||||||
|
/// <param name="filename">The filename for the .json file [optional].</param>
|
||||||
|
[PublicAPI]
|
||||||
|
public void SavePact(string folder, string? filename = null)
|
||||||
|
{
|
||||||
|
var (filenameUpdated, bytes) = PactMapper.ToPact(this, filename);
|
||||||
|
_settings.FileSystemHandler.WriteFile(folder, filenameUpdated, bytes);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// This stores details about the consumer of the interaction.
|
/// This stores details about the consumer of the interaction.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -688,7 +717,7 @@ public partial class WireMockServer
|
|||||||
Provider = provider;
|
Provider = provider;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
#endregion
|
||||||
private IRequestBuilder? InitRequestBuilder(RequestModel requestModel, bool pathOrUrlRequired)
|
private IRequestBuilder? InitRequestBuilder(RequestModel requestModel, bool pathOrUrlRequired)
|
||||||
{
|
{
|
||||||
IRequestBuilder requestBuilder = Request.Create();
|
IRequestBuilder requestBuilder = Request.Create();
|
||||||
@@ -904,68 +933,6 @@ public partial class WireMockServer
|
|||||||
return responseBuilder;
|
return responseBuilder;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ResponseMessage ToJson<T>(T result, bool keepNullValues = false)
|
|
||||||
{
|
|
||||||
return new ResponseMessage
|
|
||||||
{
|
|
||||||
BodyData = new BodyData
|
|
||||||
{
|
|
||||||
DetectedBodyType = BodyType.String,
|
|
||||||
BodyAsString = JsonConvert.SerializeObject(result, keepNullValues ? JsonSerializationConstants.JsonSerializerSettingsIncludeNullValues : JsonSerializationConstants.JsonSerializerSettingsDefault)
|
|
||||||
},
|
|
||||||
StatusCode = (int)HttpStatusCode.OK,
|
|
||||||
Headers = new Dictionary<string, WireMockList<string>> { { HttpKnownHeaderNames.ContentType, new WireMockList<string>(ContentTypeJson) } }
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private Encoding? ToEncoding(EncodingModel? encodingModel)
|
|
||||||
{
|
|
||||||
return encodingModel != null ? Encoding.GetEncoding(encodingModel.CodePage) : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private T? DeserializeObject<T>(IRequestMessage requestMessage)
|
|
||||||
{
|
|
||||||
if (requestMessage?.BodyData?.DetectedBodyType == BodyType.String)
|
|
||||||
{
|
|
||||||
return JsonUtils.DeserializeObject<T>(requestMessage.BodyData.BodyAsString);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (requestMessage?.BodyData?.DetectedBodyType == BodyType.Json)
|
|
||||||
{
|
|
||||||
return ((JObject)requestMessage.BodyData.BodyAsJson).ToObject<T>();
|
|
||||||
}
|
|
||||||
|
|
||||||
return default(T);
|
|
||||||
}
|
|
||||||
|
|
||||||
private T[] DeserializeRequestMessageToArray<T>(IRequestMessage requestMessage)
|
|
||||||
{
|
|
||||||
if (requestMessage.BodyData?.DetectedBodyType == BodyType.Json)
|
|
||||||
{
|
|
||||||
var bodyAsJson = requestMessage.BodyData.BodyAsJson;
|
|
||||||
|
|
||||||
return DeserializeObjectToArray<T>(bodyAsJson);
|
|
||||||
}
|
|
||||||
|
|
||||||
return default(T[]);
|
|
||||||
}
|
|
||||||
|
|
||||||
private T[] DeserializeObjectToArray<T>(object value)
|
|
||||||
{
|
|
||||||
if (value is JArray jArray)
|
|
||||||
{
|
|
||||||
return jArray.ToObject<T[]>();
|
|
||||||
}
|
|
||||||
|
|
||||||
var singleResult = ((JObject)value).ToObject<T>();
|
|
||||||
return new[] { singleResult };
|
|
||||||
}
|
|
||||||
|
|
||||||
private T[] DeserializeJsonToArray<T>(string value)
|
|
||||||
{
|
|
||||||
return DeserializeObjectToArray<T>(JsonUtils.DeserializeObject(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DisposeEnhancedFileSystemWatcher()
|
private void DisposeEnhancedFileSystemWatcher()
|
||||||
{
|
{
|
||||||
if (_enhancedFileSystemWatcher != null)
|
if (_enhancedFileSystemWatcher != null)
|
||||||
@@ -1012,4 +979,66 @@ public partial class WireMockServer
|
|||||||
DeleteMapping(args.FullPath);
|
DeleteMapping(args.FullPath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static Encoding? ToEncoding(EncodingModel? encodingModel)
|
||||||
|
{
|
||||||
|
return encodingModel != null ? Encoding.GetEncoding(encodingModel.CodePage) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ResponseMessage ToJson<T>(T result, bool keepNullValues = false)
|
||||||
|
{
|
||||||
|
return new ResponseMessage
|
||||||
|
{
|
||||||
|
BodyData = new BodyData
|
||||||
|
{
|
||||||
|
DetectedBodyType = BodyType.String,
|
||||||
|
BodyAsString = JsonConvert.SerializeObject(result, keepNullValues ? JsonSerializationConstants.JsonSerializerSettingsIncludeNullValues : JsonSerializationConstants.JsonSerializerSettingsDefault)
|
||||||
|
},
|
||||||
|
StatusCode = (int)HttpStatusCode.OK,
|
||||||
|
Headers = new Dictionary<string, WireMockList<string>> { { HttpKnownHeaderNames.ContentType, new WireMockList<string>(WireMockConstants.ContentTypeJson) } }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static T? DeserializeObject<T>(IRequestMessage requestMessage)
|
||||||
|
{
|
||||||
|
if (requestMessage?.BodyData?.DetectedBodyType == BodyType.String)
|
||||||
|
{
|
||||||
|
return JsonUtils.DeserializeObject<T>(requestMessage.BodyData.BodyAsString);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (requestMessage?.BodyData?.DetectedBodyType == BodyType.Json)
|
||||||
|
{
|
||||||
|
return ((JObject)requestMessage.BodyData.BodyAsJson).ToObject<T>();
|
||||||
|
}
|
||||||
|
|
||||||
|
return default(T);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static T[] DeserializeRequestMessageToArray<T>(IRequestMessage requestMessage)
|
||||||
|
{
|
||||||
|
if (requestMessage.BodyData?.DetectedBodyType == BodyType.Json)
|
||||||
|
{
|
||||||
|
var bodyAsJson = requestMessage.BodyData.BodyAsJson;
|
||||||
|
|
||||||
|
return DeserializeObjectToArray<T>(bodyAsJson);
|
||||||
|
}
|
||||||
|
|
||||||
|
return default(T[]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static T[] DeserializeJsonToArray<T>(string value)
|
||||||
|
{
|
||||||
|
return DeserializeObjectToArray<T>(JsonUtils.DeserializeObject(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static T[] DeserializeObjectToArray<T>(object value)
|
||||||
|
{
|
||||||
|
if (value is JArray jArray)
|
||||||
|
{
|
||||||
|
return jArray.ToObject<T[]>();
|
||||||
|
}
|
||||||
|
|
||||||
|
var singleResult = ((JObject)value).ToObject<T>();
|
||||||
|
return new[] { singleResult };
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -5,87 +5,89 @@ using System.Collections.ObjectModel;
|
|||||||
using System.Collections.Specialized;
|
using System.Collections.Specialized;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
|
using Stef.Validation;
|
||||||
using WireMock.Logging;
|
using WireMock.Logging;
|
||||||
using WireMock.Matchers;
|
using WireMock.Matchers;
|
||||||
using WireMock.Matchers.Request;
|
using WireMock.Matchers.Request;
|
||||||
|
|
||||||
namespace WireMock.Server
|
namespace WireMock.Server;
|
||||||
|
|
||||||
|
public partial class WireMockServer
|
||||||
{
|
{
|
||||||
public partial class WireMockServer
|
/// <inheritdoc cref="IWireMockServer.LogEntriesChanged" />
|
||||||
|
[PublicAPI]
|
||||||
|
public event NotifyCollectionChangedEventHandler LogEntriesChanged
|
||||||
{
|
{
|
||||||
/// <inheritdoc cref="IWireMockServer.LogEntriesChanged" />
|
add
|
||||||
[PublicAPI]
|
|
||||||
public event NotifyCollectionChangedEventHandler LogEntriesChanged
|
|
||||||
{
|
{
|
||||||
add
|
_options.LogEntries.CollectionChanged += (sender, eventRecordArgs) =>
|
||||||
{
|
{
|
||||||
_options.LogEntries.CollectionChanged += (sender, eventRecordArgs) =>
|
try
|
||||||
{
|
{
|
||||||
try
|
value(sender, eventRecordArgs);
|
||||||
{
|
|
||||||
value(sender, eventRecordArgs);
|
|
||||||
}
|
|
||||||
catch (Exception exception)
|
|
||||||
{
|
|
||||||
_options.Logger.Error("Error calling the LogEntriesChanged event handler: {0}", exception.Message);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
remove => _options.LogEntries.CollectionChanged -= value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc cref="IWireMockServer.LogEntries" />
|
|
||||||
[PublicAPI]
|
|
||||||
public IEnumerable<ILogEntry> LogEntries => new ReadOnlyCollection<LogEntry>(_options.LogEntries.ToList());
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The search log-entries based on matchers.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="matchers">The matchers.</param>
|
|
||||||
/// <returns>The <see cref="IEnumerable"/>.</returns>
|
|
||||||
[PublicAPI]
|
|
||||||
public IEnumerable<LogEntry> FindLogEntries([NotNull] params IRequestMatcher[] matchers)
|
|
||||||
{
|
|
||||||
var results = new Dictionary<LogEntry, RequestMatchResult>();
|
|
||||||
|
|
||||||
foreach (var log in _options.LogEntries.ToList())
|
|
||||||
{
|
|
||||||
var requestMatchResult = new RequestMatchResult();
|
|
||||||
foreach (var matcher in matchers)
|
|
||||||
{
|
|
||||||
matcher.GetMatchingScore(log.RequestMessage, requestMatchResult);
|
|
||||||
}
|
}
|
||||||
|
catch (Exception exception)
|
||||||
if (requestMatchResult.AverageTotalScore > MatchScores.AlmostPerfect)
|
|
||||||
{
|
{
|
||||||
results.Add(log, requestMatchResult);
|
_options.Logger.Error("Error calling the LogEntriesChanged event handler: {0}", exception.Message);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
return new ReadOnlyCollection<LogEntry>(results.OrderBy(x => x.Value).Select(x => x.Key).ToList());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc cref="IWireMockServer.ResetLogEntries" />
|
remove => _options.LogEntries.CollectionChanged -= value;
|
||||||
[PublicAPI]
|
}
|
||||||
public void ResetLogEntries()
|
|
||||||
{
|
|
||||||
_options.LogEntries.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc cref="IWireMockServer.DeleteLogEntry" />
|
/// <inheritdoc cref="IWireMockServer.LogEntries" />
|
||||||
[PublicAPI]
|
[PublicAPI]
|
||||||
public bool DeleteLogEntry(Guid guid)
|
public IEnumerable<ILogEntry> LogEntries => new ReadOnlyCollection<LogEntry>(_options.LogEntries.ToList());
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The search log-entries based on matchers.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="matchers">The matchers.</param>
|
||||||
|
/// <returns>The <see cref="IEnumerable"/>.</returns>
|
||||||
|
[PublicAPI]
|
||||||
|
public IEnumerable<LogEntry> FindLogEntries(params IRequestMatcher[] matchers)
|
||||||
|
{
|
||||||
|
Guard.NotNull(matchers);
|
||||||
|
|
||||||
|
var results = new Dictionary<LogEntry, RequestMatchResult>();
|
||||||
|
|
||||||
|
foreach (var log in _options.LogEntries.ToList())
|
||||||
{
|
{
|
||||||
// Check a LogEntry exists with the same GUID, if so, remove it.
|
var requestMatchResult = new RequestMatchResult();
|
||||||
var existing = _options.LogEntries.ToList().FirstOrDefault(m => m.Guid == guid);
|
foreach (var matcher in matchers)
|
||||||
if (existing != null)
|
|
||||||
{
|
{
|
||||||
_options.LogEntries.Remove(existing);
|
matcher.GetMatchingScore(log.RequestMessage, requestMatchResult);
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
if (requestMatchResult.AverageTotalScore > MatchScores.AlmostPerfect)
|
||||||
|
{
|
||||||
|
results.Add(log, requestMatchResult);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return new ReadOnlyCollection<LogEntry>(results.OrderBy(x => x.Value).Select(x => x.Key).ToList());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc cref="IWireMockServer.ResetLogEntries" />
|
||||||
|
[PublicAPI]
|
||||||
|
public void ResetLogEntries()
|
||||||
|
{
|
||||||
|
_options.LogEntries.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc cref="IWireMockServer.DeleteLogEntry" />
|
||||||
|
[PublicAPI]
|
||||||
|
public bool DeleteLogEntry(Guid guid)
|
||||||
|
{
|
||||||
|
// Check a LogEntry exists with the same GUID, if so, remove it.
|
||||||
|
var existing = _options.LogEntries.ToList().FirstOrDefault(m => m.Guid == guid);
|
||||||
|
if (existing != null)
|
||||||
|
{
|
||||||
|
_options.LogEntries.Remove(existing);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -5,18 +5,49 @@ using System.Linq;
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
using WireMock.Pact.Models.V2;
|
|
||||||
using WireMock.Serialization;
|
using WireMock.Serialization;
|
||||||
|
|
||||||
namespace WireMock.Util;
|
namespace WireMock.Util;
|
||||||
|
|
||||||
internal static class JsonUtils
|
internal static class JsonUtils
|
||||||
{
|
{
|
||||||
public static bool TryParseAsComplexObject(string strInput, [NotNullWhen(true)] out JToken? token)
|
public static Type CreateTypeFromJObject(JObject instance, string? fullName = null)
|
||||||
{
|
{
|
||||||
token = null;
|
static Type ConvertType(JToken value, string? propertyName = null)
|
||||||
|
{
|
||||||
|
var type = value.Type;
|
||||||
|
return type switch
|
||||||
|
{
|
||||||
|
JTokenType.Array => value.HasValues ? ConvertType(value.First!, propertyName).MakeArrayType() : typeof(object).MakeArrayType(),
|
||||||
|
JTokenType.Boolean => typeof(bool),
|
||||||
|
JTokenType.Bytes => typeof(byte[]),
|
||||||
|
JTokenType.Date => typeof(DateTime),
|
||||||
|
JTokenType.Guid => typeof(Guid),
|
||||||
|
JTokenType.Float => typeof(float),
|
||||||
|
JTokenType.Integer => typeof(long),
|
||||||
|
JTokenType.Null => typeof(object),
|
||||||
|
JTokenType.Object => CreateTypeFromJObject((JObject)value, propertyName),
|
||||||
|
JTokenType.String => typeof(string),
|
||||||
|
JTokenType.TimeSpan => typeof(TimeSpan),
|
||||||
|
JTokenType.Uri => typeof(string),
|
||||||
|
_ => typeof(object)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(strInput))
|
var properties = new Dictionary<string, Type>();
|
||||||
|
foreach (var item in instance.Properties())
|
||||||
|
{
|
||||||
|
properties.Add(item.Name, ConvertType(item.Value, item.Name));
|
||||||
|
}
|
||||||
|
|
||||||
|
return TypeBuilderUtils.BuildType(properties, fullName) ?? throw new InvalidOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool TryParseAsJObject(string? strInput, [NotNullWhen(true)] out JObject? value)
|
||||||
|
{
|
||||||
|
value = null;
|
||||||
|
|
||||||
|
if (strInput == null || string.IsNullOrWhiteSpace(strInput))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -30,7 +61,7 @@ internal static class JsonUtils
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Try to convert this string into a JToken
|
// Try to convert this string into a JToken
|
||||||
token = JToken.Parse(strInput);
|
value = JObject.Parse(strInput);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
@@ -105,17 +136,19 @@ internal static class JsonUtils
|
|||||||
|
|
||||||
private static void WalkNode(JToken node, string? path, string? propertyName, List<string> lines)
|
private static void WalkNode(JToken node, string? path, string? propertyName, List<string> lines)
|
||||||
{
|
{
|
||||||
if (node.Type == JTokenType.Object)
|
switch (node.Type)
|
||||||
{
|
{
|
||||||
ProcessObject(node, propertyName, lines);
|
case JTokenType.Object:
|
||||||
}
|
ProcessObject(node, propertyName, lines);
|
||||||
else if (node.Type == JTokenType.Array)
|
break;
|
||||||
{
|
|
||||||
ProcessArray(node, propertyName, lines);
|
case JTokenType.Array:
|
||||||
}
|
ProcessArray(node, propertyName, lines);
|
||||||
else
|
break;
|
||||||
{
|
|
||||||
ProcessItem(node, path ?? "it", propertyName, lines);
|
default:
|
||||||
|
ProcessItem(node, path ?? "it", propertyName, lines);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -125,7 +158,7 @@ internal static class JsonUtils
|
|||||||
var text = new StringBuilder("new (");
|
var text = new StringBuilder("new (");
|
||||||
|
|
||||||
// In case of Object, loop all children. Do a ToArray() to avoid `Collection was modified` exceptions.
|
// In case of Object, loop all children. Do a ToArray() to avoid `Collection was modified` exceptions.
|
||||||
foreach (JProperty child in node.Children<JProperty>().ToArray())
|
foreach (var child in node.Children<JProperty>().ToArray())
|
||||||
{
|
{
|
||||||
WalkNode(child.Value, child.Path, child.Name, items);
|
WalkNode(child.Value, child.Path, child.Name, items);
|
||||||
}
|
}
|
||||||
@@ -147,8 +180,8 @@ internal static class JsonUtils
|
|||||||
var text = new StringBuilder("(new [] { ");
|
var text = new StringBuilder("(new [] { ");
|
||||||
|
|
||||||
// In case of Array, loop all items. Do a ToArray() to avoid `Collection was modified` exceptions.
|
// In case of Array, loop all items. Do a ToArray() to avoid `Collection was modified` exceptions.
|
||||||
int idx = 0;
|
var idx = 0;
|
||||||
foreach (JToken child in node.Children().ToArray())
|
foreach (var child in node.Children().ToArray())
|
||||||
{
|
{
|
||||||
WalkNode(child, $"{node.Path}[{idx}]", null, items);
|
WalkNode(child, $"{node.Path}[{idx}]", null, items);
|
||||||
idx++;
|
idx++;
|
||||||
@@ -165,50 +198,21 @@ internal static class JsonUtils
|
|||||||
lines.Add(text.ToString());
|
lines.Add(text.ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void ProcessItem(JToken node, string path, string propertyName, List<string> lines)
|
private static void ProcessItem(JToken node, string path, string? propertyName, List<string> lines)
|
||||||
{
|
{
|
||||||
string castText;
|
var castText = node.Type switch
|
||||||
switch (node.Type)
|
|
||||||
{
|
{
|
||||||
case JTokenType.Boolean:
|
JTokenType.Boolean => $"bool({path})",
|
||||||
castText = $"bool({path})";
|
JTokenType.Date => $"DateTime({path})",
|
||||||
break;
|
JTokenType.Float => $"double({path})",
|
||||||
|
JTokenType.Guid => $"Guid({path})",
|
||||||
case JTokenType.Date:
|
JTokenType.Integer => $"long({path})",
|
||||||
castText = $"DateTime({path})";
|
JTokenType.Null => "null",
|
||||||
break;
|
JTokenType.String => $"string({path})",
|
||||||
|
JTokenType.TimeSpan => $"TimeSpan({path})",
|
||||||
case JTokenType.Float:
|
JTokenType.Uri => $"Uri({path})",
|
||||||
castText = $"double({path})";
|
_ => throw new NotSupportedException($"JTokenType '{node.Type}' cannot be converted to a Dynamic Linq cast operator.")
|
||||||
break;
|
};
|
||||||
|
|
||||||
case JTokenType.Guid:
|
|
||||||
castText = $"Guid({path})";
|
|
||||||
break;
|
|
||||||
|
|
||||||
case JTokenType.Integer:
|
|
||||||
castText = $"long({path})";
|
|
||||||
break;
|
|
||||||
|
|
||||||
case JTokenType.Null:
|
|
||||||
castText = "null";
|
|
||||||
break;
|
|
||||||
|
|
||||||
case JTokenType.String:
|
|
||||||
castText = $"string({path})";
|
|
||||||
break;
|
|
||||||
|
|
||||||
case JTokenType.TimeSpan:
|
|
||||||
castText = $"TimeSpan({path})";
|
|
||||||
break;
|
|
||||||
|
|
||||||
case JTokenType.Uri:
|
|
||||||
castText = $"Uri({path})";
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
throw new NotSupportedException($"JTokenType '{node.Type}' cannot be converted to a Dynamic Linq cast operator.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(propertyName))
|
if (!string.IsNullOrEmpty(propertyName))
|
||||||
{
|
{
|
||||||
|
|||||||
8
src/WireMock.Net/Util/SystemUtils.cs
Normal file
8
src/WireMock.Net/Util/SystemUtils.cs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
namespace WireMock.Util;
|
||||||
|
|
||||||
|
internal static class SystemUtils
|
||||||
|
{
|
||||||
|
public static readonly string Version = typeof(SystemUtils).GetTypeInfo().Assembly.GetName().Version.ToString();
|
||||||
|
}
|
||||||
118
src/WireMock.Net/Util/TypeBuilderUtils.cs
Normal file
118
src/WireMock.Net/Util/TypeBuilderUtils.cs
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Reflection.Emit;
|
||||||
|
|
||||||
|
namespace WireMock.Util;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Code based on https://stackoverflow.com/questions/40507909/convert-jobject-to-anonymous-object
|
||||||
|
/// </summary>
|
||||||
|
internal static class TypeBuilderUtils
|
||||||
|
{
|
||||||
|
private static readonly ConcurrentDictionary<IDictionary<string, Type>, Type> Types = new();
|
||||||
|
|
||||||
|
private static readonly ModuleBuilder ModuleBuilder =
|
||||||
|
AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("WireMock.Net.Reflection"), AssemblyBuilderAccess.Run)
|
||||||
|
.DefineDynamicModule("WireMock.Net.Reflection.Module");
|
||||||
|
|
||||||
|
public static Type BuildType(IDictionary<string, Type> properties, string? name = null)
|
||||||
|
{
|
||||||
|
var keyExists = Types.Keys.FirstOrDefault(k => Compare(k, properties));
|
||||||
|
if (keyExists != null)
|
||||||
|
{
|
||||||
|
return Types[keyExists];
|
||||||
|
}
|
||||||
|
|
||||||
|
var typeBuilder = GetTypeBuilder(name ?? Guid.NewGuid().ToString());
|
||||||
|
foreach (var property in properties)
|
||||||
|
{
|
||||||
|
CreateGetSetMethods(typeBuilder, property.Key, property.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
var type = typeBuilder.CreateTypeInfo().AsType();
|
||||||
|
|
||||||
|
Types.TryAdd(properties, type);
|
||||||
|
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// https://stackoverflow.com/questions/3804367/testing-for-equality-between-dictionaries-in-c-sharp
|
||||||
|
/// </summary>
|
||||||
|
private static bool Compare<TKey, TValue>(IDictionary<TKey, TValue> dict1, IDictionary<TKey, TValue> dict2)
|
||||||
|
{
|
||||||
|
if (dict1 == dict2)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dict1.Count != dict2.Count)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var valueComparer = EqualityComparer<TValue>.Default;
|
||||||
|
|
||||||
|
foreach (var kvp in dict1)
|
||||||
|
{
|
||||||
|
if (!dict2.TryGetValue(kvp.Key, out var value2))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!valueComparer.Equals(kvp.Value, value2))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static TypeBuilder GetTypeBuilder(string name)
|
||||||
|
{
|
||||||
|
return ModuleBuilder.DefineType(name,
|
||||||
|
TypeAttributes.Public |
|
||||||
|
TypeAttributes.Class |
|
||||||
|
TypeAttributes.AutoClass |
|
||||||
|
TypeAttributes.AnsiClass |
|
||||||
|
TypeAttributes.BeforeFieldInit |
|
||||||
|
TypeAttributes.AutoLayout,
|
||||||
|
null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void CreateGetSetMethods(TypeBuilder typeBuilder, string propertyName, Type propertyType)
|
||||||
|
{
|
||||||
|
var fieldBuilder = typeBuilder.DefineField("_" + propertyName, propertyType, FieldAttributes.Private);
|
||||||
|
|
||||||
|
var propertyBuilder = typeBuilder.DefineProperty(propertyName, PropertyAttributes.HasDefault, propertyType, null);
|
||||||
|
|
||||||
|
var getPropertyMethodBuilder = typeBuilder.DefineMethod("get_" + propertyName, MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, propertyType, Type.EmptyTypes);
|
||||||
|
var getIl = getPropertyMethodBuilder.GetILGenerator();
|
||||||
|
|
||||||
|
getIl.Emit(OpCodes.Ldarg_0);
|
||||||
|
getIl.Emit(OpCodes.Ldfld, fieldBuilder);
|
||||||
|
getIl.Emit(OpCodes.Ret);
|
||||||
|
|
||||||
|
var setPropertyMethodBuilder = typeBuilder.DefineMethod("set_" + propertyName, MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, null, new[] { propertyType });
|
||||||
|
var setIl = setPropertyMethodBuilder.GetILGenerator();
|
||||||
|
var modifyProperty = setIl.DefineLabel();
|
||||||
|
|
||||||
|
var exitSet = setIl.DefineLabel();
|
||||||
|
|
||||||
|
setIl.MarkLabel(modifyProperty);
|
||||||
|
setIl.Emit(OpCodes.Ldarg_0);
|
||||||
|
setIl.Emit(OpCodes.Ldarg_1);
|
||||||
|
setIl.Emit(OpCodes.Stfld, fieldBuilder);
|
||||||
|
|
||||||
|
setIl.Emit(OpCodes.Nop);
|
||||||
|
setIl.MarkLabel(exitSet);
|
||||||
|
setIl.Emit(OpCodes.Ret);
|
||||||
|
|
||||||
|
propertyBuilder.SetGetMethod(getPropertyMethodBuilder);
|
||||||
|
propertyBuilder.SetSetMethod(setPropertyMethodBuilder);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -58,11 +58,12 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="JetBrains.Annotations" Version="2022.1.0" PrivateAssets="All" />
|
<PackageReference Include="JetBrains.Annotations" Version="2022.1.0" PrivateAssets="All" />
|
||||||
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1" PrivateAssets="All" />
|
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1" PrivateAssets="All" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||||
|
<PackageReference Include="NJsonSchema.Extensions" Version="0.1.0" />
|
||||||
|
<PackageReference Include="NSwag.Core" Version="13.15.10" />
|
||||||
<PackageReference Include="SimMetrics.Net" Version="1.0.5" />
|
<PackageReference Include="SimMetrics.Net" Version="1.0.5" />
|
||||||
<PackageReference Include="Stef.Validation" Version="0.1.0" />
|
<PackageReference Include="Stef.Validation" Version="0.1.0" />
|
||||||
<PackageReference Include="System.Linq.Dynamic.Core" Version="1.2.18" />
|
<PackageReference Include="System.Linq.Dynamic.Core" Version="1.2.18" />
|
||||||
<!--<PackageReference Include="RandomDataGenerator.Net" Version="1.0.15" />-->
|
|
||||||
<PackageReference Include="JmesPath.Net" Version="1.0.125" />
|
<PackageReference Include="JmesPath.Net" Version="1.0.125" />
|
||||||
<PackageReference Include="AnyOf" Version="0.3.0" />
|
<PackageReference Include="AnyOf" Version="0.3.0" />
|
||||||
<PackageReference Include="TinyMapper" Version="3.0.3" />
|
<PackageReference Include="TinyMapper" Version="3.0.3" />
|
||||||
|
|||||||
@@ -12,277 +12,318 @@ using WireMock.Settings;
|
|||||||
using Xunit;
|
using Xunit;
|
||||||
using static System.Environment;
|
using static System.Environment;
|
||||||
|
|
||||||
namespace WireMock.Net.Tests.FluentAssertions
|
namespace WireMock.Net.Tests.FluentAssertions;
|
||||||
|
|
||||||
|
public class WireMockAssertionsTests : IDisposable
|
||||||
{
|
{
|
||||||
public class WireMockAssertionsTests : IDisposable
|
private readonly WireMockServer _server;
|
||||||
|
private readonly HttpClient _httpClient;
|
||||||
|
private readonly int _portUsed;
|
||||||
|
|
||||||
|
public WireMockAssertionsTests()
|
||||||
{
|
{
|
||||||
private readonly WireMockServer _server;
|
_server = WireMockServer.Start();
|
||||||
private readonly HttpClient _httpClient;
|
_server.Given(Request.Create().UsingAnyMethod())
|
||||||
private readonly int _portUsed;
|
.RespondWith(Response.Create().WithSuccess());
|
||||||
|
_portUsed = _server.Ports.First();
|
||||||
|
|
||||||
public WireMockAssertionsTests()
|
_httpClient = new HttpClient { BaseAddress = new Uri(_server.Urls[0]) };
|
||||||
{
|
}
|
||||||
_server = WireMockServer.Start();
|
|
||||||
_server.Given(Request.Create().UsingAnyMethod())
|
|
||||||
.RespondWith(Response.Create().WithSuccess());
|
|
||||||
_portUsed = _server.Ports.First();
|
|
||||||
|
|
||||||
_httpClient = new HttpClient { BaseAddress = new Uri(_server.Urls[0]) };
|
[Fact]
|
||||||
}
|
public async Task HaveReceivedNoCalls_AtAbsoluteUrl_WhenACallWasNotMadeToAbsoluteUrl_Should_BeOK()
|
||||||
|
{
|
||||||
|
await _httpClient.GetAsync("xxx").ConfigureAwait(false);
|
||||||
|
|
||||||
[Fact]
|
_server.Should()
|
||||||
public async Task AtAbsoluteUrl_WhenACallWasMadeToAbsoluteUrl_Should_BeOK()
|
.HaveReceivedNoCalls()
|
||||||
{
|
.AtAbsoluteUrl($"http://localhost:{_portUsed}/anyurl");
|
||||||
await _httpClient.GetAsync("anyurl").ConfigureAwait(false);
|
}
|
||||||
|
|
||||||
_server.Should()
|
[Fact]
|
||||||
.HaveReceivedACall()
|
public async Task HaveReceived0Calls_AtAbsoluteUrl_WhenACallWasNotMadeToAbsoluteUrl_Should_BeOK()
|
||||||
.AtAbsoluteUrl($"http://localhost:{_portUsed}/anyurl");
|
{
|
||||||
}
|
await _httpClient.GetAsync("xxx").ConfigureAwait(false);
|
||||||
|
|
||||||
[Fact]
|
_server.Should()
|
||||||
public void AtAbsoluteUrl_Should_ThrowWhenNoCallsWereMade()
|
.HaveReceived(0).Calls()
|
||||||
{
|
.AtAbsoluteUrl($"http://localhost:{_portUsed}/anyurl");
|
||||||
Action act = () => _server.Should()
|
}
|
||||||
.HaveReceivedACall()
|
|
||||||
.AtAbsoluteUrl("anyurl");
|
|
||||||
|
|
||||||
act.Should().Throw<Exception>()
|
[Fact]
|
||||||
.And.Message.Should()
|
public async Task HaveReceived1Calls_AtAbsoluteUrl_WhenACallWasMadeToAbsoluteUrl_Should_BeOK()
|
||||||
.Be(
|
{
|
||||||
"Expected _server to have been called at address matching the absolute url \"anyurl\", but no calls were made.");
|
await _httpClient.GetAsync("anyurl").ConfigureAwait(false);
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
_server.Should()
|
||||||
public async Task AtAbsoluteUrl_Should_ThrowWhenNoCallsMatchingTheAbsoluteUrlWereMade()
|
.HaveReceived(1).Calls()
|
||||||
{
|
.AtAbsoluteUrl($"http://localhost:{_portUsed}/anyurl");
|
||||||
await _httpClient.GetAsync("").ConfigureAwait(false);
|
}
|
||||||
|
|
||||||
Action act = () => _server.Should()
|
[Fact]
|
||||||
.HaveReceivedACall()
|
public async Task HaveReceived2Calls_AtAbsoluteUrl_WhenACallWasMadeToAbsoluteUrl_Should_BeOK()
|
||||||
.AtAbsoluteUrl("anyurl");
|
{
|
||||||
|
await _httpClient.GetAsync("anyurl").ConfigureAwait(false);
|
||||||
|
|
||||||
act.Should().Throw<Exception>()
|
await _httpClient.GetAsync("anyurl").ConfigureAwait(false);
|
||||||
.And.Message.Should()
|
|
||||||
.Be(
|
|
||||||
$"Expected _server to have been called at address matching the absolute url \"anyurl\", but didn't find it among the calls to {{\"http://localhost:{_portUsed}/\"}}.");
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
_server.Should()
|
||||||
public async Task WithHeader_WhenACallWasMadeWithExpectedHeader_Should_BeOK()
|
.HaveReceived(2).Calls()
|
||||||
{
|
.AtAbsoluteUrl($"http://localhost:{_portUsed}/anyurl");
|
||||||
_httpClient.DefaultRequestHeaders.Add("Authorization", "Bearer a");
|
}
|
||||||
await _httpClient.GetAsync("").ConfigureAwait(false);
|
|
||||||
|
|
||||||
_server.Should()
|
[Fact]
|
||||||
.HaveReceivedACall()
|
public async Task HaveReceivedACall_AtAbsoluteUrl_WhenACallWasMadeToAbsoluteUrl_Should_BeOK()
|
||||||
.WithHeader("Authorization", "Bearer a");
|
{
|
||||||
}
|
await _httpClient.GetAsync("anyurl").ConfigureAwait(false);
|
||||||
|
|
||||||
[Fact]
|
_server.Should()
|
||||||
public async Task WithHeader_WhenACallWasMadeWithExpectedHeaderAmongMultipleHeaderValues_Should_BeOK()
|
.HaveReceivedACall()
|
||||||
{
|
.AtAbsoluteUrl($"http://localhost:{_portUsed}/anyurl");
|
||||||
_httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/xml"));
|
}
|
||||||
_httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
|
|
||||||
await _httpClient.GetAsync("").ConfigureAwait(false);
|
|
||||||
|
|
||||||
_server.Should()
|
[Fact]
|
||||||
.HaveReceivedACall()
|
public void HaveReceivedACall_AtAbsoluteUrl_Should_ThrowWhenNoCallsWereMade()
|
||||||
.WithHeader("Accept", new[] { "application/xml", "application/json" });
|
{
|
||||||
}
|
Action act = () => _server.Should()
|
||||||
|
.HaveReceivedACall()
|
||||||
|
.AtAbsoluteUrl("anyurl");
|
||||||
|
|
||||||
[Fact]
|
act.Should().Throw<Exception>()
|
||||||
public async Task WithHeader_Should_ThrowWhenNoCallsMatchingTheHeaderNameWereMade()
|
.And.Message.Should()
|
||||||
{
|
.Be(
|
||||||
await _httpClient.GetAsync("").ConfigureAwait(false);
|
"Expected _server to have been called at address matching the absolute url \"anyurl\", but no calls were made.");
|
||||||
|
}
|
||||||
|
|
||||||
Action act = () => _server.Should()
|
[Fact]
|
||||||
.HaveReceivedACall()
|
public async Task HaveReceivedACall_AtAbsoluteUrl_Should_ThrowWhenNoCallsMatchingTheAbsoluteUrlWereMade()
|
||||||
.WithHeader("Authorization", "value");
|
{
|
||||||
|
await _httpClient.GetAsync("").ConfigureAwait(false);
|
||||||
|
|
||||||
act.Should().Throw<Exception>()
|
Action act = () => _server.Should()
|
||||||
.And.Message.Should()
|
.HaveReceivedACall()
|
||||||
.Contain("to contain key \"Authorization\".");
|
.AtAbsoluteUrl("anyurl");
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
act.Should().Throw<Exception>()
|
||||||
public async Task WithHeader_Should_ThrowWhenNoCallsMatchingTheHeaderValuesWereMade()
|
.And.Message.Should()
|
||||||
{
|
.Be(
|
||||||
_httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/xml"));
|
$"Expected _server to have been called at address matching the absolute url \"anyurl\", but didn't find it among the calls to {{\"http://localhost:{_portUsed}/\"}}.");
|
||||||
_httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
|
}
|
||||||
await _httpClient.GetAsync("").ConfigureAwait(false);
|
|
||||||
|
|
||||||
Action act = () => _server.Should()
|
[Fact]
|
||||||
.HaveReceivedACall()
|
public async Task HaveReceivedACall_WithHeader_WhenACallWasMadeWithExpectedHeader_Should_BeOK()
|
||||||
.WithHeader("Accept", "missing-value");
|
{
|
||||||
|
_httpClient.DefaultRequestHeaders.Add("Authorization", "Bearer a");
|
||||||
|
await _httpClient.GetAsync("").ConfigureAwait(false);
|
||||||
|
|
||||||
var sentHeaders = _server.LogEntries.SelectMany(x => x.RequestMessage.Headers)
|
_server.Should()
|
||||||
.ToDictionary(x => x.Key, x => x.Value)["Accept"]
|
.HaveReceivedACall()
|
||||||
.Select(x => $"\"{x}\"")
|
.WithHeader("Authorization", "Bearer a");
|
||||||
.ToList();
|
}
|
||||||
|
|
||||||
var sentHeaderString = "{" + string.Join(", ", sentHeaders) + "}";
|
[Fact]
|
||||||
|
public async Task HaveReceivedACall_WithHeader_WhenACallWasMadeWithExpectedHeaderAmongMultipleHeaderValues_Should_BeOK()
|
||||||
|
{
|
||||||
|
_httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/xml"));
|
||||||
|
_httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
|
||||||
|
await _httpClient.GetAsync("").ConfigureAwait(false);
|
||||||
|
|
||||||
act.Should().Throw<Exception>()
|
_server.Should()
|
||||||
.And.Message.Should()
|
.HaveReceivedACall()
|
||||||
.Be(
|
.WithHeader("Accept", new[] { "application/xml", "application/json" });
|
||||||
$"Expected header \"Accept\" from requests sent with value(s) {sentHeaderString} to contain \"missing-value\".{NewLine}");
|
}
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task WithHeader_Should_ThrowWhenNoCallsMatchingTheHeaderWithMultipleValuesWereMade()
|
public async Task HaveReceivedACall_WithHeader_Should_ThrowWhenNoCallsMatchingTheHeaderNameWereMade()
|
||||||
{
|
{
|
||||||
_httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/xml"));
|
await _httpClient.GetAsync("").ConfigureAwait(false);
|
||||||
_httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
|
|
||||||
await _httpClient.GetAsync("").ConfigureAwait(false);
|
|
||||||
|
|
||||||
Action act = () => _server.Should()
|
Action act = () => _server.Should()
|
||||||
.HaveReceivedACall()
|
.HaveReceivedACall()
|
||||||
.WithHeader("Accept", new[] { "missing-value1", "missing-value2" });
|
.WithHeader("Authorization", "value");
|
||||||
|
|
||||||
const string missingValue1Message =
|
act.Should().Throw<Exception>()
|
||||||
"Expected header \"Accept\" from requests sent with value(s) {\"application/xml\", \"application/json\"} to contain \"missing-value1\".";
|
.And.Message.Should()
|
||||||
const string missingValue2Message =
|
.Contain("to contain key \"Authorization\".");
|
||||||
"Expected header \"Accept\" from requests sent with value(s) {\"application/xml\", \"application/json\"} to contain \"missing-value2\".";
|
}
|
||||||
|
|
||||||
act.Should().Throw<Exception>()
|
[Fact]
|
||||||
.And.Message.Should()
|
public async Task HaveReceivedACall_WithHeader_Should_ThrowWhenNoCallsMatchingTheHeaderValuesWereMade()
|
||||||
.Be($"{string.Join(NewLine, missingValue1Message, missingValue2Message)}{NewLine}");
|
{
|
||||||
}
|
_httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/xml"));
|
||||||
|
_httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
|
||||||
|
await _httpClient.GetAsync("").ConfigureAwait(false);
|
||||||
|
|
||||||
[Fact]
|
Action act = () => _server.Should()
|
||||||
public async Task AtUrl_WhenACallWasMadeToUrl_Should_BeOK()
|
.HaveReceivedACall()
|
||||||
{
|
.WithHeader("Accept", "missing-value");
|
||||||
await _httpClient.GetAsync("anyurl").ConfigureAwait(false);
|
|
||||||
|
|
||||||
_server.Should()
|
var sentHeaders = _server.LogEntries.SelectMany(x => x.RequestMessage.Headers)
|
||||||
.HaveReceivedACall()
|
.ToDictionary(x => x.Key, x => x.Value)["Accept"]
|
||||||
.AtUrl($"http://localhost:{_portUsed}/anyurl");
|
.Select(x => $"\"{x}\"")
|
||||||
}
|
.ToList();
|
||||||
|
|
||||||
[Fact]
|
var sentHeaderString = "{" + string.Join(", ", sentHeaders) + "}";
|
||||||
public void AtUrl_Should_ThrowWhenNoCallsWereMade()
|
|
||||||
{
|
|
||||||
Action act = () => _server.Should()
|
|
||||||
.HaveReceivedACall()
|
|
||||||
.AtUrl("anyurl");
|
|
||||||
|
|
||||||
act.Should().Throw<Exception>()
|
act.Should().Throw<Exception>()
|
||||||
.And.Message.Should()
|
.And.Message.Should()
|
||||||
.Be(
|
.Be(
|
||||||
"Expected _server to have been called at address matching the url \"anyurl\", but no calls were made.");
|
$"Expected header \"Accept\" from requests sent with value(s) {sentHeaderString} to contain \"missing-value\".{NewLine}");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task AtUrl_Should_ThrowWhenNoCallsMatchingTheUrlWereMade()
|
public async Task HaveReceivedACall_WithHeader_Should_ThrowWhenNoCallsMatchingTheHeaderWithMultipleValuesWereMade()
|
||||||
{
|
{
|
||||||
await _httpClient.GetAsync("").ConfigureAwait(false);
|
_httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/xml"));
|
||||||
|
_httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
|
||||||
|
await _httpClient.GetAsync("").ConfigureAwait(false);
|
||||||
|
|
||||||
Action act = () => _server.Should()
|
Action act = () => _server.Should()
|
||||||
.HaveReceivedACall()
|
.HaveReceivedACall()
|
||||||
.AtUrl("anyurl");
|
.WithHeader("Accept", new[] { "missing-value1", "missing-value2" });
|
||||||
|
|
||||||
act.Should().Throw<Exception>()
|
const string missingValue1Message =
|
||||||
.And.Message.Should()
|
"Expected header \"Accept\" from requests sent with value(s) {\"application/xml\", \"application/json\"} to contain \"missing-value1\".";
|
||||||
.Be(
|
const string missingValue2Message =
|
||||||
$"Expected _server to have been called at address matching the url \"anyurl\", but didn't find it among the calls to {{\"http://localhost:{_portUsed}/\"}}.");
|
"Expected header \"Accept\" from requests sent with value(s) {\"application/xml\", \"application/json\"} to contain \"missing-value2\".";
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
act.Should().Throw<Exception>()
|
||||||
public async Task WithProxyUrl_WhenACallWasMadeWithProxyUrl_Should_BeOK()
|
.And.Message.Should()
|
||||||
{
|
.Be($"{string.Join(NewLine, missingValue1Message, missingValue2Message)}{NewLine}");
|
||||||
_server.ResetMappings();
|
}
|
||||||
_server.Given(Request.Create().UsingAnyMethod())
|
|
||||||
.RespondWith(Response.Create().WithProxy(new ProxyAndRecordSettings { Url = "http://localhost:9999" }));
|
|
||||||
|
|
||||||
await _httpClient.GetAsync("").ConfigureAwait(false);
|
[Fact]
|
||||||
|
public async Task HaveReceivedACall_AtUrl_WhenACallWasMadeToUrl_Should_BeOK()
|
||||||
|
{
|
||||||
|
await _httpClient.GetAsync("anyurl").ConfigureAwait(false);
|
||||||
|
|
||||||
_server.Should()
|
_server.Should()
|
||||||
.HaveReceivedACall()
|
.HaveReceivedACall()
|
||||||
.WithProxyUrl($"http://localhost:9999");
|
.AtUrl($"http://localhost:{_portUsed}/anyurl");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void WithProxyUrl_Should_ThrowWhenNoCallsWereMade()
|
public void HaveReceivedACall_AtUrl_Should_ThrowWhenNoCallsWereMade()
|
||||||
{
|
{
|
||||||
_server.ResetMappings();
|
Action act = () => _server.Should()
|
||||||
_server.Given(Request.Create().UsingAnyMethod())
|
.HaveReceivedACall()
|
||||||
.RespondWith(Response.Create().WithProxy(new ProxyAndRecordSettings { Url = "http://localhost:9999" }));
|
.AtUrl("anyurl");
|
||||||
|
|
||||||
Action act = () => _server.Should()
|
act.Should().Throw<Exception>()
|
||||||
.HaveReceivedACall()
|
.And.Message.Should()
|
||||||
.WithProxyUrl("anyurl");
|
.Be(
|
||||||
|
"Expected _server to have been called at address matching the url \"anyurl\", but no calls were made.");
|
||||||
|
}
|
||||||
|
|
||||||
act.Should().Throw<Exception>()
|
[Fact]
|
||||||
.And.Message.Should()
|
public async Task HaveReceivedACall_AtUrl_Should_ThrowWhenNoCallsMatchingTheUrlWereMade()
|
||||||
.Be(
|
{
|
||||||
"Expected _server to have been called with proxy url \"anyurl\", but no calls were made.");
|
await _httpClient.GetAsync("").ConfigureAwait(false);
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
Action act = () => _server.Should()
|
||||||
public async Task WithProxyUrl_Should_ThrowWhenNoCallsWithTheProxyUrlWereMade()
|
.HaveReceivedACall()
|
||||||
{
|
.AtUrl("anyurl");
|
||||||
_server.ResetMappings();
|
|
||||||
_server.Given(Request.Create().UsingAnyMethod())
|
|
||||||
.RespondWith(Response.Create().WithProxy(new ProxyAndRecordSettings { Url = "http://localhost:9999" }));
|
|
||||||
|
|
||||||
await _httpClient.GetAsync("").ConfigureAwait(false);
|
act.Should().Throw<Exception>()
|
||||||
|
.And.Message.Should()
|
||||||
|
.Be(
|
||||||
|
$"Expected _server to have been called at address matching the url \"anyurl\", but didn't find it among the calls to {{\"http://localhost:{_portUsed}/\"}}.");
|
||||||
|
}
|
||||||
|
|
||||||
Action act = () => _server.Should()
|
[Fact]
|
||||||
.HaveReceivedACall()
|
public async Task HaveReceivedACall_WithProxyUrl_WhenACallWasMadeWithProxyUrl_Should_BeOK()
|
||||||
.WithProxyUrl("anyurl");
|
{
|
||||||
|
_server.ResetMappings();
|
||||||
|
_server.Given(Request.Create().UsingAnyMethod())
|
||||||
|
.RespondWith(Response.Create().WithProxy(new ProxyAndRecordSettings { Url = "http://localhost:9999" }));
|
||||||
|
|
||||||
act.Should().Throw<Exception>()
|
await _httpClient.GetAsync("").ConfigureAwait(false);
|
||||||
.And.Message.Should()
|
|
||||||
.Be(
|
|
||||||
$"Expected _server to have been called with proxy url \"anyurl\", but didn't find it among the calls with {{\"http://localhost:9999\"}}.");
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
_server.Should()
|
||||||
public async Task FromClientIP_whenACallWasMadeFromClientIP_Should_BeOK()
|
.HaveReceivedACall()
|
||||||
{
|
.WithProxyUrl($"http://localhost:9999");
|
||||||
await _httpClient.GetAsync("").ConfigureAwait(false);
|
}
|
||||||
var clientIP = _server.LogEntries.Last().RequestMessage.ClientIP;
|
|
||||||
|
|
||||||
_server.Should()
|
[Fact]
|
||||||
.HaveReceivedACall()
|
public void HaveReceivedACall_WithProxyUrl_Should_ThrowWhenNoCallsWereMade()
|
||||||
.FromClientIP(clientIP);
|
{
|
||||||
}
|
_server.ResetMappings();
|
||||||
|
_server.Given(Request.Create().UsingAnyMethod())
|
||||||
|
.RespondWith(Response.Create().WithProxy(new ProxyAndRecordSettings { Url = "http://localhost:9999" }));
|
||||||
|
|
||||||
[Fact]
|
Action act = () => _server.Should()
|
||||||
public void FromClientIP_Should_ThrowWhenNoCallsWereMade()
|
.HaveReceivedACall()
|
||||||
{
|
.WithProxyUrl("anyurl");
|
||||||
Action act = () => _server.Should()
|
|
||||||
.HaveReceivedACall()
|
|
||||||
.FromClientIP("different-ip");
|
|
||||||
|
|
||||||
act.Should().Throw<Exception>()
|
act.Should().Throw<Exception>()
|
||||||
.And.Message.Should()
|
.And.Message.Should()
|
||||||
.Be(
|
.Be(
|
||||||
"Expected _server to have been called from client IP \"different-ip\", but no calls were made.");
|
"Expected _server to have been called with proxy url \"anyurl\", but no calls were made.");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task FromClientIP_Should_ThrowWhenNoCallsFromClientIPWereMade()
|
public async Task HaveReceivedACall_WithProxyUrl_Should_ThrowWhenNoCallsWithTheProxyUrlWereMade()
|
||||||
{
|
{
|
||||||
await _httpClient.GetAsync("").ConfigureAwait(false);
|
_server.ResetMappings();
|
||||||
var clientIP = _server.LogEntries.Last().RequestMessage.ClientIP;
|
_server.Given(Request.Create().UsingAnyMethod())
|
||||||
|
.RespondWith(Response.Create().WithProxy(new ProxyAndRecordSettings { Url = "http://localhost:9999" }));
|
||||||
|
|
||||||
Action act = () => _server.Should()
|
await _httpClient.GetAsync("").ConfigureAwait(false);
|
||||||
.HaveReceivedACall()
|
|
||||||
.FromClientIP("different-ip");
|
|
||||||
|
|
||||||
act.Should().Throw<Exception>()
|
Action act = () => _server.Should()
|
||||||
.And.Message.Should()
|
.HaveReceivedACall()
|
||||||
.Be(
|
.WithProxyUrl("anyurl");
|
||||||
$"Expected _server to have been called from client IP \"different-ip\", but didn't find it among the calls from IP(s) {{\"{clientIP}\"}}.");
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
act.Should().Throw<Exception>()
|
||||||
{
|
.And.Message.Should()
|
||||||
_server?.Stop();
|
.Be(
|
||||||
_server?.Dispose();
|
$"Expected _server to have been called with proxy url \"anyurl\", but didn't find it among the calls with {{\"http://localhost:9999\"}}.");
|
||||||
_httpClient?.Dispose();
|
}
|
||||||
}
|
|
||||||
|
[Fact]
|
||||||
|
public async Task HaveReceivedACall_FromClientIP_whenACallWasMadeFromClientIP_Should_BeOK()
|
||||||
|
{
|
||||||
|
await _httpClient.GetAsync("").ConfigureAwait(false);
|
||||||
|
var clientIP = _server.LogEntries.Last().RequestMessage.ClientIP;
|
||||||
|
|
||||||
|
_server.Should()
|
||||||
|
.HaveReceivedACall()
|
||||||
|
.FromClientIP(clientIP);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void HaveReceivedACall_FromClientIP_Should_ThrowWhenNoCallsWereMade()
|
||||||
|
{
|
||||||
|
Action act = () => _server.Should()
|
||||||
|
.HaveReceivedACall()
|
||||||
|
.FromClientIP("different-ip");
|
||||||
|
|
||||||
|
act.Should().Throw<Exception>()
|
||||||
|
.And.Message.Should()
|
||||||
|
.Be(
|
||||||
|
"Expected _server to have been called from client IP \"different-ip\", but no calls were made.");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task HaveReceivedACall_FromClientIP_Should_ThrowWhenNoCallsFromClientIPWereMade()
|
||||||
|
{
|
||||||
|
await _httpClient.GetAsync("").ConfigureAwait(false);
|
||||||
|
var clientIP = _server.LogEntries.Last().RequestMessage.ClientIP;
|
||||||
|
|
||||||
|
Action act = () => _server.Should()
|
||||||
|
.HaveReceivedACall()
|
||||||
|
.FromClientIP("different-ip");
|
||||||
|
|
||||||
|
act.Should().Throw<Exception>()
|
||||||
|
.And.Message.Should()
|
||||||
|
.Be(
|
||||||
|
$"Expected _server to have been called from client IP \"different-ip\", but didn't find it among the calls from IP(s) {{\"{clientIP}\"}}.");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_server?.Stop();
|
||||||
|
_server?.Dispose();
|
||||||
|
_httpClient?.Dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -57,6 +57,7 @@ public class PactTests
|
|||||||
.WithHeader("Accept", "application/json")
|
.WithHeader("Accept", "application/json")
|
||||||
)
|
)
|
||||||
.WithTitle("A GET request to retrieve the something")
|
.WithTitle("A GET request to retrieve the something")
|
||||||
|
.WithGuid("23e2aedb-166c-467b-b9f6-9b0817cb1636")
|
||||||
.RespondWith(
|
.RespondWith(
|
||||||
Response.Create()
|
Response.Create()
|
||||||
.WithStatusCode(HttpStatusCode.OK)
|
.WithStatusCode(HttpStatusCode.OK)
|
||||||
@@ -77,6 +78,7 @@ public class PactTests
|
|||||||
.WithBody(new JsonMatcher("{ \"Id\" : \"1\", \"FirstName\" : \"Totally\" }"))
|
.WithBody(new JsonMatcher("{ \"Id\" : \"1\", \"FirstName\" : \"Totally\" }"))
|
||||||
)
|
)
|
||||||
.WithTitle("A Post request to add the something")
|
.WithTitle("A Post request to add the something")
|
||||||
|
.WithGuid("f3f8abe7-7d1e-4518-afa1-d295ce7dadfd")
|
||||||
.RespondWith(
|
.RespondWith(
|
||||||
Response.Create()
|
Response.Create()
|
||||||
.WithStatusCode(HttpStatusCode.RedirectMethod)
|
.WithStatusCode(HttpStatusCode.RedirectMethod)
|
||||||
|
|||||||
@@ -1,32 +1,32 @@
|
|||||||
{
|
{
|
||||||
"Consumer": {
|
"consumer": {
|
||||||
"Name": "Something API Consumer Get"
|
"name": "Something API Consumer Get"
|
||||||
},
|
},
|
||||||
"Interactions": [
|
"interactions": [
|
||||||
{
|
{
|
||||||
"ProviderState": "A GET request to retrieve the something",
|
"providerState": "A GET request to retrieve the something",
|
||||||
"Request": {
|
"request": {
|
||||||
"Headers": {
|
"headers": {
|
||||||
"Accept": "application/json"
|
"Accept": "application/json"
|
||||||
},
|
},
|
||||||
"Method": "GET",
|
"method": "GET",
|
||||||
"Path": "/tester",
|
"path": "/tester",
|
||||||
"Query": "q1=test&q2=ok"
|
"query": "q1=test&q2=ok"
|
||||||
},
|
},
|
||||||
"Response": {
|
"response": {
|
||||||
"Body": {
|
"body": {
|
||||||
"Id": "tester",
|
"id": "tester",
|
||||||
"FirstName": "Totally",
|
"firstName": "Totally",
|
||||||
"LastName": "Awesome"
|
"lastName": "Awesome"
|
||||||
},
|
},
|
||||||
"Headers": {
|
"headers": {
|
||||||
"Content-Type": "application/json; charset=utf-8"
|
"Content-Type": "application/json; charset=utf-8"
|
||||||
},
|
},
|
||||||
"Status": 200
|
"status": 200
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"Provider": {
|
"provider": {
|
||||||
"Name": "Something API"
|
"name": "Something API"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,50 +1,49 @@
|
|||||||
{
|
{
|
||||||
"Consumer": {
|
"consumer": {
|
||||||
"Name": "Something API Consumer Multiple"
|
"name": "Something API Consumer Multiple"
|
||||||
},
|
},
|
||||||
"Interactions": [
|
"interactions": [
|
||||||
{
|
{
|
||||||
"ProviderState": "A Post request to add the something",
|
"providerState": "A GET request to retrieve the something",
|
||||||
"Request": {
|
"request": {
|
||||||
"Headers": {
|
"headers": {
|
||||||
"Accept": "application/json"
|
"Accept": "application/json"
|
||||||
},
|
},
|
||||||
"Method": "POST",
|
"method": "POST",
|
||||||
"Path": "/add",
|
"path": "/tester",
|
||||||
"Body": "{ \"Id\" : \"1\", \"FirstName\" : \"Totally\" }"
|
"query": "q1=test&q2=ok"
|
||||||
},
|
},
|
||||||
"Response": {
|
"response": {
|
||||||
"Body": {
|
"body": {
|
||||||
"Id": "1",
|
"id": "tester",
|
||||||
"FirstName": "Totally"
|
"firstName": "Totally",
|
||||||
|
"lastName": "Awesome"
|
||||||
},
|
},
|
||||||
"Status": 303
|
"headers": {
|
||||||
|
"Content-Type": "application/json; charset=utf-8"
|
||||||
|
},
|
||||||
|
"status": 200
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ProviderState": "A GET request to retrieve the something",
|
"providerState": "A Post request to add the something",
|
||||||
"Request": {
|
"request": {
|
||||||
"Headers": {
|
"headers": {
|
||||||
"Accept": "application/json"
|
"Accept": "application/json"
|
||||||
},
|
},
|
||||||
"Method": "POST",
|
"method": "POST",
|
||||||
"Path": "/tester",
|
"path": "/add"
|
||||||
"Query": "q1=test&q2=ok"
|
|
||||||
},
|
},
|
||||||
"Response": {
|
"response": {
|
||||||
"Body": {
|
"body": {
|
||||||
"Id": "tester",
|
"id": "1",
|
||||||
"FirstName": "Totally",
|
"firstName": "Totally"
|
||||||
"LastName": "Awesome"
|
|
||||||
},
|
},
|
||||||
"Headers": {
|
"status": 303
|
||||||
"Content-Type": "application/json; charset=utf-8"
|
|
||||||
},
|
|
||||||
"Status": 200
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"Provider": {
|
"provider": {
|
||||||
"Name": "Something API"
|
"name": "Something API"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,115 +1,167 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Linq.Dynamic.Core;
|
using System.Linq.Dynamic.Core;
|
||||||
|
using System.Reflection;
|
||||||
using FluentAssertions;
|
using FluentAssertions;
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
using NFluent;
|
using NFluent;
|
||||||
using WireMock.Util;
|
using WireMock.Util;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace WireMock.Net.Tests.Util
|
namespace WireMock.Net.Tests.Util;
|
||||||
|
|
||||||
|
public class JsonUtilsTests
|
||||||
{
|
{
|
||||||
public class JsonUtilsTests
|
[Fact]
|
||||||
|
public void JsonUtils_ParseJTokenToObject()
|
||||||
{
|
{
|
||||||
[Fact]
|
// Assign
|
||||||
public void JsonUtils_ParseJTokenToObject()
|
object value = "test";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
string result = JsonUtils.ParseJTokenToObject<string>(value);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Check.That(result).IsEqualTo(default(string));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void JsonUtils_GenerateDynamicLinqStatement_JToken()
|
||||||
|
{
|
||||||
|
// Assign
|
||||||
|
JToken instance = "Test";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
string line = JsonUtils.GenerateDynamicLinqStatement(instance);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var queryable = new[] { instance }.AsQueryable().Select(line);
|
||||||
|
bool result = queryable.Any("it == \"Test\"");
|
||||||
|
Check.That(result).IsTrue();
|
||||||
|
|
||||||
|
Check.That(line).IsEqualTo("string(it)");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void JsonUtils_GenerateDynamicLinqStatement_JArray_Indexer()
|
||||||
|
{
|
||||||
|
// Assign
|
||||||
|
var instance = new JObject
|
||||||
{
|
{
|
||||||
// Assign
|
{ "Items", new JArray(new JValue(4), new JValue(8)) }
|
||||||
object value = "test";
|
};
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
string result = JsonUtils.ParseJTokenToObject<string>(value);
|
string line = JsonUtils.GenerateDynamicLinqStatement(instance);
|
||||||
|
|
||||||
// Assert
|
// Assert 1
|
||||||
Check.That(result).IsEqualTo(default(string));
|
line.Should().Be("new ((new [] { long(Items[0]), long(Items[1])}) as Items)");
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
// Assert 2
|
||||||
public void JsonUtils_GenerateDynamicLinqStatement_JToken()
|
var queryable = new[] { instance }.AsQueryable().Select(line);
|
||||||
|
bool result = queryable.Any("Items != null");
|
||||||
|
result.Should().BeTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void JsonUtils_GenerateDynamicLinqStatement_JObject2()
|
||||||
|
{
|
||||||
|
// Assign
|
||||||
|
var instance = new JObject
|
||||||
{
|
{
|
||||||
// Assign
|
{"U", new JValue(new Uri("http://localhost:80/abc?a=5"))},
|
||||||
JToken j = "Test";
|
{"N", new JValue((object?) null)},
|
||||||
|
{"G", new JValue(Guid.NewGuid())},
|
||||||
// Act
|
{"Flt", new JValue(10.0f)},
|
||||||
string line = JsonUtils.GenerateDynamicLinqStatement(j);
|
{"Dbl", new JValue(Math.PI)},
|
||||||
|
{"Check", new JValue(true)},
|
||||||
// Assert
|
|
||||||
var queryable = new[] { j }.AsQueryable().Select(line);
|
|
||||||
bool result = queryable.Any("it == \"Test\"");
|
|
||||||
Check.That(result).IsTrue();
|
|
||||||
|
|
||||||
Check.That(line).IsEqualTo("string(it)");
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void JsonUtils_GenerateDynamicLinqStatement_JArray_Indexer()
|
|
||||||
{
|
|
||||||
// Assign
|
|
||||||
var j = new JObject
|
|
||||||
{
|
{
|
||||||
{ "Items", new JArray(new[] { new JValue(4), new JValue(8) }) }
|
"Child", new JObject
|
||||||
};
|
|
||||||
|
|
||||||
// Act
|
|
||||||
string line = JsonUtils.GenerateDynamicLinqStatement(j);
|
|
||||||
|
|
||||||
// Assert 1
|
|
||||||
line.Should().Be("new ((new [] { long(Items[0]), long(Items[1])}) as Items)");
|
|
||||||
|
|
||||||
// Assert 2
|
|
||||||
var queryable = new[] { j }.AsQueryable().Select(line);
|
|
||||||
bool result = queryable.Any("Items != null");
|
|
||||||
result.Should().BeTrue();
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void JsonUtils_GenerateDynamicLinqStatement_JObject2()
|
|
||||||
{
|
|
||||||
// Assign
|
|
||||||
var j = new JObject
|
|
||||||
{
|
|
||||||
{"U", new JValue(new Uri("http://localhost:80/abc?a=5"))},
|
|
||||||
{"N", new JValue((object) null)},
|
|
||||||
{"G", new JValue(Guid.NewGuid())},
|
|
||||||
{"Flt", new JValue(10.0f)},
|
|
||||||
{"Dbl", new JValue(Math.PI)},
|
|
||||||
{"Check", new JValue(true)},
|
|
||||||
{
|
{
|
||||||
"Child", new JObject
|
{"ChildId", new JValue(4)},
|
||||||
{
|
{"ChildDateTime", new JValue(new DateTime(2018, 2, 17))},
|
||||||
{"ChildId", new JValue(4)},
|
{"TS", new JValue(TimeSpan.FromMilliseconds(999))}
|
||||||
{"ChildDateTime", new JValue(new DateTime(2018, 2, 17))},
|
}
|
||||||
{"TS", new JValue(TimeSpan.FromMilliseconds(999))}
|
},
|
||||||
}
|
{"I", new JValue(9)},
|
||||||
},
|
{"L", new JValue(long.MaxValue)},
|
||||||
{"I", new JValue(9)},
|
{"Name", new JValue("Test")}
|
||||||
{"L", new JValue(long.MaxValue)},
|
};
|
||||||
{"Name", new JValue("Test")}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
string line = JsonUtils.GenerateDynamicLinqStatement(j);
|
string line = JsonUtils.GenerateDynamicLinqStatement(instance);
|
||||||
|
|
||||||
// Assert 1
|
// Assert 1
|
||||||
line.Should().Be("new (Uri(U) as U, null as N, Guid(G) as G, double(Flt) as Flt, double(Dbl) as Dbl, bool(Check) as Check, new (long(Child.ChildId) as ChildId, DateTime(Child.ChildDateTime) as ChildDateTime, TimeSpan(Child.TS) as TS) as Child, long(I) as I, long(L) as L, string(Name) as Name)");
|
line.Should().Be("new (Uri(U) as U, null as N, Guid(G) as G, double(Flt) as Flt, double(Dbl) as Dbl, bool(Check) as Check, new (long(Child.ChildId) as ChildId, DateTime(Child.ChildDateTime) as ChildDateTime, TimeSpan(Child.TS) as TS) as Child, long(I) as I, long(L) as L, string(Name) as Name)");
|
||||||
|
|
||||||
// Assert 2
|
// Assert 2
|
||||||
var queryable = new[] { j }.AsQueryable().Select(line);
|
var queryable = new[] { instance }.AsQueryable().Select(line);
|
||||||
bool result = queryable.Any("I > 1 && L > 1");
|
bool result = queryable.Any("I > 1 && L > 1");
|
||||||
result.Should().BeTrue();
|
result.Should().BeTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void JsonUtils_GenerateDynamicLinqStatement_Throws()
|
public void JsonUtils_GenerateDynamicLinqStatement_Throws()
|
||||||
|
{
|
||||||
|
// Assign
|
||||||
|
var instance = new JObject
|
||||||
{
|
{
|
||||||
// Assign
|
{ "B", new JValue(new byte[] {48, 49}) }
|
||||||
var j = new JObject
|
};
|
||||||
{
|
|
||||||
{ "B", new JValue(new byte[] {48, 49}) }
|
|
||||||
};
|
|
||||||
|
|
||||||
// Act and Assert
|
// Act and Assert
|
||||||
Check.ThatCode(() => JsonUtils.GenerateDynamicLinqStatement(j)).Throws<NotSupportedException>();
|
Check.ThatCode(() => JsonUtils.GenerateDynamicLinqStatement(instance)).Throws<NotSupportedException>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void JsonUtils_CreateTypeFromJObject()
|
||||||
|
{
|
||||||
|
// Assign
|
||||||
|
var instance = new JObject
|
||||||
|
{
|
||||||
|
{"U", new JValue(new Uri("http://localhost:80/abc?a=5"))},
|
||||||
|
{"N", new JValue((object?) null)},
|
||||||
|
{"G", new JValue(Guid.NewGuid())},
|
||||||
|
{"Flt", new JValue(10.0f)},
|
||||||
|
{"Dbl", new JValue(Math.PI)},
|
||||||
|
{"Check", new JValue(true)},
|
||||||
|
{
|
||||||
|
"Child", new JObject
|
||||||
|
{
|
||||||
|
{"ChildId", new JValue(4)},
|
||||||
|
{"ChildDateTime", new JValue(new DateTime(2018, 2, 17))},
|
||||||
|
{"ChildTimeSpan", new JValue(TimeSpan.FromMilliseconds(999))}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{"I", new JValue(9)},
|
||||||
|
{"L", new JValue(long.MaxValue)},
|
||||||
|
{"S", new JValue("Test")},
|
||||||
|
{"C", new JValue('c')}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var type = JsonUtils.CreateTypeFromJObject(instance);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var setProperties = type
|
||||||
|
.GetProperties(BindingFlags.Public | BindingFlags.Instance)
|
||||||
|
.Where(pi => pi.GetMethod != null).Select(pi => $"{pi.GetMethod}")
|
||||||
|
.ToArray();
|
||||||
|
|
||||||
|
setProperties.Should().HaveCount(11);
|
||||||
|
setProperties.Should().BeEquivalentTo(new[]
|
||||||
|
{
|
||||||
|
"System.String get_U()",
|
||||||
|
"System.Object get_N()",
|
||||||
|
"System.Guid get_G()",
|
||||||
|
"Single get_Flt()",
|
||||||
|
"Single get_Dbl()",
|
||||||
|
"Boolean get_Check()",
|
||||||
|
"Child get_Child()",
|
||||||
|
"Int64 get_I()",
|
||||||
|
"Int64 get_L()",
|
||||||
|
"System.String get_S()",
|
||||||
|
"System.String get_C()"
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
14
test/WireMock.Net.Tests/Util/SystemUtilsTests.cs
Normal file
14
test/WireMock.Net.Tests/Util/SystemUtilsTests.cs
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
using FluentAssertions;
|
||||||
|
using WireMock.Util;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace WireMock.Net.Tests.Util;
|
||||||
|
|
||||||
|
public class SystemUtilsTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void Version()
|
||||||
|
{
|
||||||
|
SystemUtils.Version.Should().NotBeEmpty();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -83,28 +83,16 @@
|
|||||||
<None Update="responsebody.json">
|
<None Update="responsebody.json">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</None>
|
</None>
|
||||||
<None Update="__admin\mappings.org\mapping1.json">
|
<None Update="__admin\mappings.org\*.json">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</None>
|
</None>
|
||||||
<None Update="__admin\mappings\00000002-ee28-4f29-ae63-1ac9b0802d86.json">
|
<None Update="__admin\mappings\*.json">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</None>
|
</None>
|
||||||
<None Update="__admin\mappings\00000002-ee28-4f29-ae63-1ac9b0802d87.json">
|
<None Update="__admin\mappings\*.xml">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</None>
|
</None>
|
||||||
<None Update="__admin\mappings\351f0240-bba0-4bcb-93c6-1feba0fe8799.json">
|
<None Update="__admin\mappings\subdirectory\*.xml">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
|
||||||
</None>
|
|
||||||
<None Update="__admin\mappings\array.json">
|
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
|
||||||
</None>
|
|
||||||
<None Update="__admin\mappings\documentdb_root.json">
|
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
|
||||||
</None>
|
|
||||||
<None Update="__admin\mappings\MyXmlResponse.xml">
|
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
|
||||||
</None>
|
|
||||||
<None Update="__admin\mappings\subdirectory\MyXmlResponse.xml">
|
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</None>
|
</None>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ namespace WireMock.Net.Tests
|
|||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
server.Mappings.Should().NotBeNull();
|
server.Mappings.Should().NotBeNull();
|
||||||
server.Mappings.Should().HaveCount(25);
|
server.Mappings.Should().HaveCount(26);
|
||||||
server.Mappings.All(m => m.Priority == WireMockConstants.AdminPriority).Should().BeTrue();
|
server.Mappings.All(m => m.Priority == WireMockConstants.AdminPriority).Should().BeTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,9 +100,9 @@ namespace WireMock.Net.Tests
|
|||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
server.Mappings.Should().NotBeNull();
|
server.Mappings.Should().NotBeNull();
|
||||||
server.Mappings.Should().HaveCount(26);
|
server.Mappings.Should().HaveCount(27);
|
||||||
|
|
||||||
server.Mappings.Count(m => m.Priority == WireMockConstants.AdminPriority).Should().Be(25);
|
server.Mappings.Count(m => m.Priority == WireMockConstants.AdminPriority).Should().Be(26);
|
||||||
server.Mappings.Count(m => m.Priority == WireMockConstants.ProxyPriority).Should().Be(1);
|
server.Mappings.Count(m => m.Priority == WireMockConstants.ProxyPriority).Should().Be(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user