Compare commits

..

2 Commits

Author SHA1 Message Date
Stef Heyenrath
04cba25bdf Add extra unit tests 2022-11-17 17:14:33 +01:00
Stef Heyenrath
ef5f988786 Add Settings.QueryParameterMultipleValueSupport (#836)
* QueryParameterMultipleValueSupport

* .

* ,

* ,
2022-11-08 19:27:44 +01:00
35 changed files with 1459 additions and 1277 deletions

View File

@@ -89,4 +89,11 @@ public class SettingsModel
/// Don't save the response-string in the LogEntry when WithBody(Func{IRequestMessage, string}) or WithBody(Func{IRequestMessage, Task{string}}) is used. (default set to false). /// Don't save the response-string in the LogEntry when WithBody(Func{IRequestMessage, string}) or WithBody(Func{IRequestMessage, Task{string}}) is used. (default set to false).
/// </summary> /// </summary>
public bool? DoNotSaveDynamicResponseInLogEntry { get; set; } public bool? DoNotSaveDynamicResponseInLogEntry { get; set; }
/// <summary>
/// See <seealso cref="QueryParameterMultipleValueSupport"/>.
///
/// Default value = "All".
/// </summary>
public QueryParameterMultipleValueSupport? QueryParameterMultipleValueSupport { get; set; }
} }

View File

@@ -28,7 +28,7 @@ public interface IRequestMessage
/// <summary> /// <summary>
/// The ProxyUrl (if a proxy is used). /// The ProxyUrl (if a proxy is used).
/// </summary> /// </summary>
string ProxyUrl { get; set; } string? ProxyUrl { get; set; }
/// <summary> /// <summary>
/// Gets the DateTime. /// Gets the DateTime.

View File

@@ -0,0 +1,28 @@
using System;
namespace WireMock.Types;
[Flags]
public enum QueryParameterMultipleValueSupport
{
// Support none
None = 0x0,
// Support "&" as multi-value-separator --> "key=value&key=anotherValue"
Ampersand = 0x1,
// Support ";" as multi-value-separator --> "key=value;key=anotherValue"
SemiColon = 0x2,
// Support "," as multi-value-separator --> "key=1,2"
Comma = 0x4,
// Support "&" and ";" as multi-value-separator --> "key=value&key=anotherValue" and also "key=value;key=anotherValue"
AmpersandAndSemiColon = Ampersand | SemiColon,
// Support "&" and ";" as multi-value-separator
NoComma = AmpersandAndSemiColon,
// Support all multi-value-separators ("&" and ";" and ",")
All = Ampersand | SemiColon | Comma
}

View File

@@ -1,11 +1,11 @@
#if NET46 || NETSTANDARD2_0 #if NET46 || NETSTANDARD2_0
using System.Collections.Generic; using System.Collections.Generic;
namespace WireMock.Net.OpenApiParser.Extensions namespace WireMock.Net.OpenApiParser.Extensions;
internal static class DictionaryExtensions
{ {
internal static class DictionaryExtensions public static bool TryAdd<TKey, TValue>(this Dictionary<TKey, TValue>? dictionary, TKey key, TValue value)
{
public static bool TryAdd<TKey, TValue>(this Dictionary<TKey, TValue> dictionary, TKey key, TValue value)
{ {
if (dictionary is null || dictionary.ContainsKey(key)) if (dictionary is null || dictionary.ContainsKey(key))
{ {
@@ -16,6 +16,5 @@ namespace WireMock.Net.OpenApiParser.Extensions
return true; return true;
} }
}
} }
#endif #endif

View File

@@ -1,12 +1,12 @@
using Microsoft.OpenApi.Any; using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Interfaces; using Microsoft.OpenApi.Interfaces;
using Microsoft.OpenApi.Models; using Microsoft.OpenApi.Models;
using WireMock.Net.OpenApiParser.Types; using WireMock.Net.OpenApiParser.Types;
namespace WireMock.Net.OpenApiParser.Extensions namespace WireMock.Net.OpenApiParser.Extensions;
internal static class OpenApiSchemaExtensions
{ {
internal static class OpenApiSchemaExtensions
{
/// <summary> /// <summary>
/// https://stackoverflow.com/questions/48111459/how-to-define-a-property-that-can-be-string-or-null-in-openapi-swagger /// https://stackoverflow.com/questions/48111459/how-to-define-a-property-that-can-be-string-or-null-in-openapi-swagger
/// </summary> /// </summary>
@@ -23,7 +23,7 @@ namespace WireMock.Net.OpenApiParser.Extensions
return false; return false;
} }
public static SchemaType GetSchemaType(this OpenApiSchema schema) public static SchemaType GetSchemaType(this OpenApiSchema? schema)
{ {
switch (schema?.Type) switch (schema?.Type)
{ {
@@ -53,7 +53,7 @@ namespace WireMock.Net.OpenApiParser.Extensions
} }
} }
public static SchemaFormat GetSchemaFormat(this OpenApiSchema schema) public static SchemaFormat GetSchemaFormat(this OpenApiSchema? schema)
{ {
switch (schema?.Format) switch (schema?.Format)
{ {
@@ -88,5 +88,4 @@ namespace WireMock.Net.OpenApiParser.Extensions
return SchemaFormat.Undefined; return SchemaFormat.Undefined;
} }
} }
}
} }

View File

@@ -1,21 +1,19 @@
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Runtime.InteropServices.ComTypes;
using JetBrains.Annotations; using JetBrains.Annotations;
using Microsoft.OpenApi.Models; using Microsoft.OpenApi.Models;
using Microsoft.OpenApi.Readers; using Microsoft.OpenApi.Readers;
using SharpYaml.Model;
using Stef.Validation; using Stef.Validation;
using WireMock.Net.OpenApiParser.Settings; using WireMock.Net.OpenApiParser.Settings;
using WireMock.Server; using WireMock.Server;
namespace WireMock.Net.OpenApiParser.Extensions namespace WireMock.Net.OpenApiParser.Extensions;
/// <summary>
/// Some extension methods for <see cref="IWireMockServer"/>.
/// </summary>
public static class WireMockServerExtensions
{ {
/// <summary>
/// Some extension methods for <see cref="IWireMockServer"/>.
/// </summary>
public static class WireMockServerExtensions
{
/// <summary> /// <summary>
/// Register the mappings via an OpenAPI (swagger) V2 or V3 file. /// Register the mappings via an OpenAPI (swagger) V2 or V3 file.
/// </summary> /// </summary>
@@ -25,7 +23,7 @@ namespace WireMock.Net.OpenApiParser.Extensions
[PublicAPI] [PublicAPI]
public static IWireMockServer WithMappingFromOpenApiFile(this IWireMockServer server, string path, out OpenApiDiagnostic diagnostic) public static IWireMockServer WithMappingFromOpenApiFile(this IWireMockServer server, string path, out OpenApiDiagnostic diagnostic)
{ {
return WithMappingFromOpenApiFile(server, path, null, out diagnostic); return WithMappingFromOpenApiFile(server, path, new WireMockOpenApiParserSettings(), out diagnostic);
} }
/// <summary> /// <summary>
@@ -55,7 +53,7 @@ namespace WireMock.Net.OpenApiParser.Extensions
[PublicAPI] [PublicAPI]
public static IWireMockServer WithMappingFromOpenApiStream(this IWireMockServer server, Stream stream, out OpenApiDiagnostic diagnostic) public static IWireMockServer WithMappingFromOpenApiStream(this IWireMockServer server, Stream stream, out OpenApiDiagnostic diagnostic)
{ {
return WithMappingFromOpenApiStream(server, stream, null, out diagnostic); return WithMappingFromOpenApiStream(server, stream, new WireMockOpenApiParserSettings(), out diagnostic);
} }
/// <summary> /// <summary>
@@ -68,9 +66,9 @@ namespace WireMock.Net.OpenApiParser.Extensions
[PublicAPI] [PublicAPI]
public static IWireMockServer WithMappingFromOpenApiStream(this IWireMockServer server, Stream stream, WireMockOpenApiParserSettings settings, out OpenApiDiagnostic diagnostic) public static IWireMockServer WithMappingFromOpenApiStream(this IWireMockServer server, Stream stream, WireMockOpenApiParserSettings settings, out OpenApiDiagnostic diagnostic)
{ {
Guard.NotNull(server, nameof(server)); Guard.NotNull(server);
Guard.NotNull(stream, nameof(stream)); Guard.NotNull(stream);
Guard.NotNull(settings, nameof(settings)); Guard.NotNull(settings);
var mappings = new WireMockOpenApiParser().FromStream(stream, settings, out diagnostic); var mappings = new WireMockOpenApiParser().FromStream(stream, settings, out diagnostic);
@@ -84,14 +82,13 @@ namespace WireMock.Net.OpenApiParser.Extensions
/// <param name="document">The OpenAPI document to use as mappings.</param> /// <param name="document">The OpenAPI document to use as mappings.</param>
/// <param name="settings">Additional settings [optional]</param> /// <param name="settings">Additional settings [optional]</param>
[PublicAPI] [PublicAPI]
public static IWireMockServer WithMappingFromOpenApiDocument(this IWireMockServer server, OpenApiDocument document, WireMockOpenApiParserSettings settings = null) public static IWireMockServer WithMappingFromOpenApiDocument(this IWireMockServer server, OpenApiDocument document, WireMockOpenApiParserSettings settings)
{ {
Guard.NotNull(server, nameof(server)); Guard.NotNull(server);
Guard.NotNull(document, nameof(document)); Guard.NotNull(document);
var mappings = new WireMockOpenApiParser().FromDocument(document, settings); var mappings = new WireMockOpenApiParser().FromDocument(document, settings);
return server.WithMapping(mappings.ToArray()); return server.WithMapping(mappings.ToArray());
} }
}
} }

View File

@@ -1,4 +1,4 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using Microsoft.OpenApi.Models; using Microsoft.OpenApi.Models;
using Microsoft.OpenApi.Readers; using Microsoft.OpenApi.Readers;
@@ -35,7 +35,7 @@ namespace WireMock.Net.OpenApiParser
/// <param name="document">The source OpenApiDocument</param> /// <param name="document">The source OpenApiDocument</param>
/// <param name="settings">Additional settings [optional]</param> /// <param name="settings">Additional settings [optional]</param>
/// <returns>MappingModel</returns> /// <returns>MappingModel</returns>
IEnumerable<MappingModel> FromDocument(OpenApiDocument document, WireMockOpenApiParserSettings settings = null); IEnumerable<MappingModel> FromDocument(OpenApiDocument document, WireMockOpenApiParserSettings? settings = null);
/// <summary> /// <summary>
/// Generate <see cref="IEnumerable{MappingModel}"/> from a <seealso cref="Stream"/>. /// Generate <see cref="IEnumerable{MappingModel}"/> from a <seealso cref="Stream"/>.

View File

@@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using Microsoft.OpenApi; using Microsoft.OpenApi;
@@ -15,10 +16,10 @@ using WireMock.Net.OpenApiParser.Settings;
using WireMock.Net.OpenApiParser.Types; using WireMock.Net.OpenApiParser.Types;
using WireMock.Net.OpenApiParser.Utils; using WireMock.Net.OpenApiParser.Utils;
namespace WireMock.Net.OpenApiParser.Mappers namespace WireMock.Net.OpenApiParser.Mappers;
internal class OpenApiPathsMapper
{ {
internal class OpenApiPathsMapper
{
private const string HeaderContentType = "Content-Type"; private const string HeaderContentType = "Content-Type";
private readonly WireMockOpenApiParserSettings _settings; private readonly WireMockOpenApiParserSettings _settings;
@@ -26,7 +27,7 @@ namespace WireMock.Net.OpenApiParser.Mappers
public OpenApiPathsMapper(WireMockOpenApiParserSettings settings) public OpenApiPathsMapper(WireMockOpenApiParserSettings settings)
{ {
_settings = Guard.NotNull(settings, nameof(settings)); _settings = Guard.NotNull(settings);
_exampleValueGenerator = new ExampleValueGenerator(settings); _exampleValueGenerator = new ExampleValueGenerator(settings);
} }
@@ -53,7 +54,7 @@ namespace WireMock.Net.OpenApiParser.Mappers
var response = operation.Responses.FirstOrDefault(); var response = operation.Responses.FirstOrDefault();
TryGetContent(response.Value?.Content, out OpenApiMediaType responseContent, out string responseContentType); TryGetContent(response.Value?.Content, out OpenApiMediaType? responseContent, out string? responseContentType);
var responseSchema = response.Value?.Content?.FirstOrDefault().Value?.Schema; var responseSchema = response.Value?.Content?.FirstOrDefault().Value?.Schema;
var responseExample = responseContent?.Example; var responseExample = responseContent?.Example;
var responseSchemaExample = responseContent?.Schema?.Example; var responseSchemaExample = responseContent?.Schema?.Example;
@@ -66,10 +67,10 @@ namespace WireMock.Net.OpenApiParser.Mappers
if (operation.RequestBody != null && operation.RequestBody.Content != null && operation.RequestBody.Required) if (operation.RequestBody != null && operation.RequestBody.Content != null && operation.RequestBody.Required)
{ {
var request = operation.RequestBody.Content; var request = operation.RequestBody.Content;
TryGetContent(request, out OpenApiMediaType requestContent, out string requestContentType); TryGetContent(request, out OpenApiMediaType? requestContent, out _);
var requestBodySchema = operation.RequestBody.Content.First().Value?.Schema; var requestBodySchema = operation.RequestBody.Content.First().Value?.Schema;
var requestBodyExample = requestContent.Example; var requestBodyExample = requestContent!.Example;
var requestBodySchemaExample = requestContent.Schema?.Example; var requestBodySchemaExample = requestContent.Schema?.Example;
var requestBodyMapped = requestBodyExample != null ? MapOpenApiAnyToJToken(requestBodyExample) : var requestBodyMapped = requestBodyExample != null ? MapOpenApiAnyToJToken(requestBodyExample) :
@@ -104,7 +105,7 @@ namespace WireMock.Net.OpenApiParser.Mappers
}; };
} }
private BodyModel MapRequestBody(object requestBody) private BodyModel? MapRequestBody(object? requestBody)
{ {
if (requestBody == null) if (requestBody == null)
{ {
@@ -122,7 +123,7 @@ namespace WireMock.Net.OpenApiParser.Mappers
}; };
} }
private bool TryGetContent(IDictionary<string, OpenApiMediaType> contents, out OpenApiMediaType openApiMediaType, out string contentType) private bool TryGetContent(IDictionary<string, OpenApiMediaType>? contents, [NotNullWhen(true)] out OpenApiMediaType? openApiMediaType, [NotNullWhen(true)] out string? contentType)
{ {
openApiMediaType = null; openApiMediaType = null;
contentType = null; contentType = null;
@@ -147,7 +148,7 @@ namespace WireMock.Net.OpenApiParser.Mappers
return true; return true;
} }
private object MapSchemaToObject(OpenApiSchema schema, string name = null) private object? MapSchemaToObject(OpenApiSchema? schema, string? name = null)
{ {
if (schema == null) if (schema == null)
{ {
@@ -203,6 +204,7 @@ namespace WireMock.Net.OpenApiParser.Mappers
{ {
propertyAsJObject.Add(MapPropertyAsJObject(schemaProperty.Value, schemaProperty.Key)); propertyAsJObject.Add(MapPropertyAsJObject(schemaProperty.Value, schemaProperty.Key));
} }
if (schema.AllOf.Count > 0) if (schema.AllOf.Count > 0)
{ {
foreach (var property in schema.AllOf) foreach (var property in schema.AllOf)
@@ -214,7 +216,7 @@ namespace WireMock.Net.OpenApiParser.Mappers
} }
} }
return name != null ? new JProperty(name, propertyAsJObject) : (JToken)propertyAsJObject; return name != null ? new JProperty(name, propertyAsJObject) : propertyAsJObject;
default: default:
return null; return null;
@@ -243,19 +245,15 @@ namespace WireMock.Net.OpenApiParser.Mappers
{ {
return jp; return jp;
} }
else
{
return new JProperty(key, mapped); return new JProperty(key, mapped);
} }
}
else
{
// bool propertyIsNullable = openApiSchema.Nullable || (openApiSchema.TryGetXNullable(out bool x) && x); // bool propertyIsNullable = openApiSchema.Nullable || (openApiSchema.TryGetXNullable(out bool x) && x);
return new JProperty(key, _exampleValueGenerator.GetExampleValue(openApiSchema)); return new JProperty(key, _exampleValueGenerator.GetExampleValue(openApiSchema));
} }
}
private string MapPathWithParameters(string path, IEnumerable<OpenApiParameter> parameters) private string MapPathWithParameters(string path, IEnumerable<OpenApiParameter>? parameters)
{ {
if (parameters == null) if (parameters == null)
{ {
@@ -272,7 +270,7 @@ namespace WireMock.Net.OpenApiParser.Mappers
return newPath; return newPath;
} }
private string MapBasePath(IList<OpenApiServer> servers) private string MapBasePath(IList<OpenApiServer>? servers)
{ {
if (servers == null || servers.Count == 0) if (servers == null || servers.Count == 0)
{ {
@@ -288,7 +286,7 @@ namespace WireMock.Net.OpenApiParser.Mappers
return string.Empty; return string.Empty;
} }
private JToken MapOpenApiAnyToJToken(IOpenApiAny any) private JToken? MapOpenApiAnyToJToken(IOpenApiAny? any)
{ {
if (any == null) if (any == null)
{ {
@@ -303,17 +301,15 @@ namespace WireMock.Net.OpenApiParser.Mappers
{ {
return JArray.Parse(outputString.ToString()); return JArray.Parse(outputString.ToString());
} }
else
{
return JObject.Parse(outputString.ToString()); return JObject.Parse(outputString.ToString());
} }
}
private IDictionary<string, object> MapHeaders(string responseContentType, IDictionary<string, OpenApiHeader> headers) private IDictionary<string, object?>? MapHeaders(string responseContentType, IDictionary<string, OpenApiHeader> headers)
{ {
var mappedHeaders = headers.ToDictionary( var mappedHeaders = headers.ToDictionary(
item => item.Key, item => item.Key,
item => GetExampleMatcherModel(null, _settings.HeaderPatternToUse).Pattern _ => GetExampleMatcherModel(null, _settings.HeaderPatternToUse).Pattern
); );
if (!string.IsNullOrEmpty(responseContentType)) if (!string.IsNullOrEmpty(responseContentType))
@@ -324,7 +320,7 @@ namespace WireMock.Net.OpenApiParser.Mappers
return mappedHeaders.Keys.Any() ? mappedHeaders : null; return mappedHeaders.Keys.Any() ? mappedHeaders : null;
} }
private IList<ParamModel> MapQueryParameters(IEnumerable<OpenApiParameter> queryParameters) private IList<ParamModel>? MapQueryParameters(IEnumerable<OpenApiParameter> queryParameters)
{ {
var list = queryParameters var list = queryParameters
.Where(req => req.Required) .Where(req => req.Required)
@@ -342,7 +338,7 @@ namespace WireMock.Net.OpenApiParser.Mappers
return list.Any() ? list : null; return list.Any() ? list : null;
} }
private IList<HeaderModel> MapRequestHeaders(IEnumerable<OpenApiParameter> headers) private IList<HeaderModel>? MapRequestHeaders(IEnumerable<OpenApiParameter> headers)
{ {
var list = headers var list = headers
.Where(req => req.Required) .Where(req => req.Required)
@@ -360,7 +356,7 @@ namespace WireMock.Net.OpenApiParser.Mappers
return list.Any() ? list : null; return list.Any() ? list : null;
} }
private MatcherModel GetExampleMatcherModel(OpenApiSchema schema, ExampleValueType type) private MatcherModel GetExampleMatcherModel(OpenApiSchema? schema, ExampleValueType type)
{ {
return type switch return type switch
{ {
@@ -370,7 +366,7 @@ namespace WireMock.Net.OpenApiParser.Mappers
}; };
} }
private string GetExampleValueAsStringForSchemaType(OpenApiSchema schema) private string GetExampleValueAsStringForSchemaType(OpenApiSchema? schema)
{ {
var value = _exampleValueGenerator.GetExampleValue(schema); var value = _exampleValueGenerator.GetExampleValue(schema);
@@ -381,5 +377,4 @@ namespace WireMock.Net.OpenApiParser.Mappers
_ => value.ToString(), _ => value.ToString(),
}; };
} }
}
} }

View File

@@ -1,25 +1,28 @@
using System; using System;
using Microsoft.OpenApi.Models; using Microsoft.OpenApi.Models;
namespace WireMock.Net.OpenApiParser.Settings namespace WireMock.Net.OpenApiParser.Settings;
/// <summary>
/// A interface defining the example values to use for the different types.
/// </summary>
public interface IWireMockOpenApiParserExampleValues
{ {
/// <summary>
/// A interface defining the example values to use for the different types.
/// </summary>
public interface IWireMockOpenApiParserExampleValues
{
/// <summary> /// <summary>
/// An example value for a Boolean. /// An example value for a Boolean.
/// </summary> /// </summary>
bool Boolean { get; set; } bool Boolean { get; set; }
/// <summary> /// <summary>
/// An example value for an Integer. /// An example value for an Integer.
/// </summary> /// </summary>
int Integer { get; set; } int Integer { get; set; }
/// <summary> /// <summary>
/// An example value for a Float. /// An example value for a Float.
/// </summary> /// </summary>
float Float { get; set; } float Float { get; set; }
/// <summary> /// <summary>
/// An example value for a Double. /// An example value for a Double.
/// </summary> /// </summary>
@@ -53,6 +56,5 @@ namespace WireMock.Net.OpenApiParser.Settings
/// <summary> /// <summary>
/// OpenApi Schema to generate dynamic examples more accurate /// OpenApi Schema to generate dynamic examples more accurate
/// </summary> /// </summary>
OpenApiSchema Schema { get; set; } OpenApiSchema? Schema { get; set; }
}
} }

View File

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

View File

@@ -1,32 +1,40 @@
using System; using System;
using Microsoft.OpenApi.Models; using Microsoft.OpenApi.Models;
namespace WireMock.Net.OpenApiParser.Settings namespace WireMock.Net.OpenApiParser.Settings;
/// <summary>
/// A class defining the example values to use for the different types.
/// </summary>
public class WireMockOpenApiParserExampleValues : IWireMockOpenApiParserExampleValues
{ {
/// <summary>
/// A class defining the example values to use for the different types.
/// </summary>
public class WireMockOpenApiParserExampleValues : IWireMockOpenApiParserExampleValues
{
/// <inheritdoc /> /// <inheritdoc />
public virtual bool Boolean { get; set; } = true; public virtual bool Boolean { get; set; } = true;
/// <inheritdoc /> /// <inheritdoc />
public virtual int Integer { get; set; } = 42; public virtual int Integer { get; set; } = 42;
/// <inheritdoc /> /// <inheritdoc />
public virtual float Float { get; set; } = 4.2f; public virtual float Float { get; set; } = 4.2f;
/// <inheritdoc /> /// <inheritdoc />
public virtual double Double { get; set; } = 4.2d; public virtual double Double { get; set; } = 4.2d;
/// <inheritdoc /> /// <inheritdoc />
public virtual Func<DateTime> Date { get; set; } = () => System.DateTime.UtcNow.Date; public virtual Func<DateTime> Date { get; set; } = () => System.DateTime.UtcNow.Date;
/// <inheritdoc /> /// <inheritdoc />
public virtual Func<DateTime> DateTime { get; set; } = () => System.DateTime.UtcNow; public virtual Func<DateTime> DateTime { get; set; } = () => System.DateTime.UtcNow;
/// <inheritdoc /> /// <inheritdoc />
public virtual byte[] Bytes { get; set; } = { 48, 49, 50 }; public virtual byte[] Bytes { get; set; } = { 48, 49, 50 };
/// <inheritdoc /> /// <inheritdoc />
public virtual object Object { get; set; } = "example-object"; public virtual object Object { get; set; } = "example-object";
/// <inheritdoc /> /// <inheritdoc />
public virtual string String { get; set; } = "example-string"; public virtual string String { get; set; } = "example-string";
/// <inheritdoc /> /// <inheritdoc />
public virtual OpenApiSchema Schema { get; set; } = new OpenApiSchema(); public virtual OpenApiSchema? Schema { get; set; } = new OpenApiSchema();
}
} }

View File

@@ -1,12 +1,12 @@
using WireMock.Net.OpenApiParser.Types; using WireMock.Net.OpenApiParser.Types;
namespace WireMock.Net.OpenApiParser.Settings namespace WireMock.Net.OpenApiParser.Settings;
/// <summary>
/// The WireMockOpenApiParser Settings
/// </summary>
public class WireMockOpenApiParserSettings
{ {
/// <summary>
/// The WireMockOpenApiParser Settings
/// </summary>
public class WireMockOpenApiParserSettings
{
/// <summary> /// <summary>
/// The number of array items to generate (default is 3). /// The number of array items to generate (default is 3).
/// </summary> /// </summary>
@@ -34,7 +34,7 @@ namespace WireMock.Net.OpenApiParser.Settings
/// - <see cref="WireMockOpenApiParserExampleValues"/> /// - <see cref="WireMockOpenApiParserExampleValues"/>
/// - <see cref="WireMockOpenApiParserDynamicExampleValues"/> /// - <see cref="WireMockOpenApiParserDynamicExampleValues"/>
/// </summary> /// </summary>
public IWireMockOpenApiParserExampleValues ExampleValues { get; set; } public IWireMockOpenApiParserExampleValues? ExampleValues { get; set; }
/// <summary> /// <summary>
/// Is a Header match case insensitive? (default is true). /// Is a Header match case insensitive? (default is true).
@@ -60,5 +60,4 @@ namespace WireMock.Net.OpenApiParser.Settings
/// Are examples generated dynamically? (default is false). /// Are examples generated dynamically? (default is false).
/// </summary> /// </summary>
public bool DynamicExamples { get; set; } = false; public bool DynamicExamples { get; set; } = false;
}
} }

View File

@@ -1,10 +1,10 @@
namespace WireMock.Net.OpenApiParser.Types namespace WireMock.Net.OpenApiParser.Types;
/// <summary>
/// The example value to use
/// </summary>
public enum ExampleValueType
{ {
/// <summary>
/// The example value to use
/// </summary>
public enum ExampleValueType
{
/// <summary> /// <summary>
/// 1. Use a generated example value based on the SchemaType (default). /// 1. Use a generated example value based on the SchemaType (default).
/// 2. If there is no example value defined in the schema, /// 2. If there is no example value defined in the schema,
@@ -16,5 +16,4 @@ namespace WireMock.Net.OpenApiParser.Types
/// Just use a Wildcard (*) character. /// Just use a Wildcard (*) character.
/// </summary> /// </summary>
Wildcard Wildcard
}
} }

View File

@@ -1,7 +1,7 @@
namespace WireMock.Net.OpenApiParser.Types namespace WireMock.Net.OpenApiParser.Types;
internal enum SchemaFormat
{ {
internal enum SchemaFormat
{
Float, Float,
Double, Double,
@@ -21,5 +21,4 @@
Binary, Binary,
Undefined Undefined
}
} }

View File

@@ -1,7 +1,7 @@
namespace WireMock.Net.OpenApiParser.Types namespace WireMock.Net.OpenApiParser.Types;
internal enum SchemaType
{ {
internal enum SchemaType
{
Object, Object,
Array, Array,
@@ -17,5 +17,4 @@
File, File,
Unknown Unknown
}
} }

View File

@@ -1,10 +1,10 @@
using System; using System;
using System.Globalization; using System.Globalization;
namespace WireMock.Net.OpenApiParser.Utils namespace WireMock.Net.OpenApiParser.Utils;
internal static class DateTimeUtils
{ {
internal static class DateTimeUtils
{
public static string ToRfc3339DateTime(DateTime dateTime) public static string ToRfc3339DateTime(DateTime dateTime)
{ {
return dateTime.ToString("yyyy-MM-dd'T'HH:mm:ss.fffzzz", DateTimeFormatInfo.InvariantInfo); return dateTime.ToString("yyyy-MM-dd'T'HH:mm:ss.fffzzz", DateTimeFormatInfo.InvariantInfo);
@@ -14,5 +14,4 @@ namespace WireMock.Net.OpenApiParser.Utils
{ {
return dateTime.ToString("yyyy-MM-dd", DateTimeFormatInfo.InvariantInfo); return dateTime.ToString("yyyy-MM-dd", DateTimeFormatInfo.InvariantInfo);
} }
}
} }

View File

@@ -7,15 +7,15 @@ using WireMock.Net.OpenApiParser.Extensions;
using WireMock.Net.OpenApiParser.Settings; using WireMock.Net.OpenApiParser.Settings;
using WireMock.Net.OpenApiParser.Types; using WireMock.Net.OpenApiParser.Types;
namespace WireMock.Net.OpenApiParser.Utils namespace WireMock.Net.OpenApiParser.Utils;
internal class ExampleValueGenerator
{ {
internal class ExampleValueGenerator
{
private readonly WireMockOpenApiParserSettings _settings; private readonly WireMockOpenApiParserSettings _settings;
public ExampleValueGenerator(WireMockOpenApiParserSettings settings) public ExampleValueGenerator(WireMockOpenApiParserSettings settings)
{ {
_settings = Guard.NotNull(settings, nameof(settings)); _settings = Guard.NotNull(settings);
// Check if user provided an own implementation // Check if user provided an own implementation
if (settings.ExampleValues is null) if (settings.ExampleValues is null)
@@ -31,7 +31,7 @@ namespace WireMock.Net.OpenApiParser.Utils
} }
} }
public object GetExampleValue(OpenApiSchema schema) public object GetExampleValue(OpenApiSchema? schema)
{ {
var schemaExample = schema?.Example; var schemaExample = schema?.Example;
var schemaEnum = GetRandomEnumValue(schema?.Enum); var schemaEnum = GetRandomEnumValue(schema?.Enum);
@@ -41,7 +41,7 @@ namespace WireMock.Net.OpenApiParser.Utils
switch (schema?.GetSchemaType()) switch (schema?.GetSchemaType())
{ {
case SchemaType.Boolean: case SchemaType.Boolean:
var exampleBoolean = (OpenApiBoolean)schemaExample; var exampleBoolean = schemaExample as OpenApiBoolean;
return exampleBoolean is null ? _settings.ExampleValues.Boolean : exampleBoolean.Value; return exampleBoolean is null ? _settings.ExampleValues.Boolean : exampleBoolean.Value;
case SchemaType.Integer: case SchemaType.Integer:
@@ -112,7 +112,7 @@ namespace WireMock.Net.OpenApiParser.Utils
} }
} }
private static IOpenApiAny GetRandomEnumValue(IList<IOpenApiAny> schemaEnum) private static IOpenApiAny? GetRandomEnumValue(IList<IOpenApiAny>? schemaEnum)
{ {
if (schemaEnum?.Count > 0) if (schemaEnum?.Count > 0)
{ {
@@ -123,5 +123,4 @@ namespace WireMock.Net.OpenApiParser.Utils
return null; return null;
} }
}
} }

View File

@@ -12,7 +12,7 @@
<AssemblyOriginatorKeyFile>../WireMock.Net/WireMock.Net.snk</AssemblyOriginatorKeyFile> <AssemblyOriginatorKeyFile>../WireMock.Net/WireMock.Net.snk</AssemblyOriginatorKeyFile>
<PublicSign Condition=" '$(OS)' != 'Windows_NT' ">true</PublicSign> <PublicSign Condition=" '$(OS)' != 'Windows_NT' ">true</PublicSign>
<PackageLicenseExpression>MIT</PackageLicenseExpression> <PackageLicenseExpression>MIT</PackageLicenseExpression>
<LangVersion>8.0</LangVersion> <LangVersion>10</LangVersion>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Release'"> <PropertyGroup Condition="'$(Configuration)' == 'Release'">
@@ -22,6 +22,10 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="Microsoft.OpenApi.Readers" Version="1.2.3" /> <PackageReference Include="Microsoft.OpenApi.Readers" Version="1.2.3" />
<PackageReference Include="Nullable" Version="1.3.1">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="RamlToOpenApiConverter" Version="0.6.1" /> <PackageReference Include="RamlToOpenApiConverter" Version="0.6.1" />
<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" />

View File

@@ -1,4 +1,4 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using JetBrains.Annotations; using JetBrains.Annotations;
@@ -9,13 +9,13 @@ using WireMock.Admin.Mappings;
using WireMock.Net.OpenApiParser.Mappers; using WireMock.Net.OpenApiParser.Mappers;
using WireMock.Net.OpenApiParser.Settings; using WireMock.Net.OpenApiParser.Settings;
namespace WireMock.Net.OpenApiParser namespace WireMock.Net.OpenApiParser;
/// <summary>
/// Parse a OpenApi/Swagger/V2/V3 or Raml to WireMock.Net MappingModels.
/// </summary>
public class WireMockOpenApiParser : IWireMockOpenApiParser
{ {
/// <summary>
/// Parse a OpenApi/Swagger/V2/V3 or Raml to WireMock.Net MappingModels.
/// </summary>
public class WireMockOpenApiParser : IWireMockOpenApiParser
{
private readonly OpenApiStreamReader _reader = new OpenApiStreamReader(); private readonly OpenApiStreamReader _reader = new OpenApiStreamReader();
/// <inheritdoc cref="IWireMockOpenApiParser.FromFile(string, out OpenApiDiagnostic)" /> /// <inheritdoc cref="IWireMockOpenApiParser.FromFile(string, out OpenApiDiagnostic)" />
@@ -60,9 +60,8 @@ namespace WireMock.Net.OpenApiParser
/// <inheritdoc cref="IWireMockOpenApiParser.FromDocument(OpenApiDocument, WireMockOpenApiParserSettings)" /> /// <inheritdoc cref="IWireMockOpenApiParser.FromDocument(OpenApiDocument, WireMockOpenApiParserSettings)" />
[PublicAPI] [PublicAPI]
public IEnumerable<MappingModel> FromDocument(OpenApiDocument openApiDocument, WireMockOpenApiParserSettings settings = null) public IEnumerable<MappingModel> FromDocument(OpenApiDocument openApiDocument, WireMockOpenApiParserSettings? settings = null)
{ {
return new OpenApiPathsMapper(settings).ToMappingModels(openApiDocument.Paths, openApiDocument.Servers); return new OpenApiPathsMapper(settings).ToMappingModels(openApiDocument.Paths, openApiDocument.Servers);
} }
}
} }

View File

@@ -15,7 +15,7 @@ namespace WireMock.Net.StandAlone;
/// </summary> /// </summary>
public static class StandAloneApp public static class StandAloneApp
{ {
private static readonly string Version = typeof(StandAloneApp).GetTypeInfo().Assembly.GetName().Version.ToString(); private static readonly string Version = typeof(StandAloneApp).GetTypeInfo().Assembly.GetName().Version!.ToString();
/// <summary> /// <summary>
/// Start WireMock.Net standalone Server based on the WireMockServerSettings. /// Start WireMock.Net standalone Server based on the WireMockServerSettings.

View File

@@ -3,13 +3,13 @@ using System.Collections.Concurrent;
using WireMock.Handlers; using WireMock.Handlers;
using WireMock.Logging; using WireMock.Logging;
using WireMock.Matchers; using WireMock.Matchers;
using WireMock.Types;
using WireMock.Util; using WireMock.Util;
#if !USE_ASPNETCORE #if !USE_ASPNETCORE
using Owin; using Owin;
#else #else
using IAppBuilder = Microsoft.AspNetCore.Builder.IApplicationBuilder; using IAppBuilder = Microsoft.AspNetCore.Builder.IApplicationBuilder;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using WireMock.Types;
#endif #endif
namespace WireMock.Owin; namespace WireMock.Owin;
@@ -70,5 +70,7 @@ internal interface IWireMockMiddlewareOptions
bool? SaveUnmatchedRequests { get; set; } bool? SaveUnmatchedRequests { get; set; }
public bool? DoNotSaveDynamicResponseInLogEntry { get; set; } bool? DoNotSaveDynamicResponseInLogEntry { get; set; }
QueryParameterMultipleValueSupport? QueryParameterMultipleValueSupport { get; set; }
} }

View File

@@ -81,11 +81,11 @@ namespace WireMock.Owin.Mappers
var statusCodeType = responseMessage.StatusCode?.GetType(); var statusCodeType = responseMessage.StatusCode?.GetType();
switch (statusCodeType) switch (statusCodeType)
{ {
case Type typeAsIntOrEnum when typeAsIntOrEnum == typeof(int) || typeAsIntOrEnum == typeof(int?) || typeAsIntOrEnum.GetTypeInfo().IsEnum: case { } typeAsIntOrEnum when typeAsIntOrEnum == typeof(int) || typeAsIntOrEnum == typeof(int?) || typeAsIntOrEnum.GetTypeInfo().IsEnum:
response.StatusCode = MapStatusCode((int)responseMessage.StatusCode!); response.StatusCode = MapStatusCode((int)responseMessage.StatusCode!);
break; break;
case Type typeAsString when typeAsString == typeof(string): case { } typeAsString when typeAsString == typeof(string):
// Note: this case will also match on null // Note: this case will also match on null
int.TryParse(responseMessage.StatusCode as string, out int result); int.TryParse(responseMessage.StatusCode as string, out int result);
response.StatusCode = MapStatusCode(result); response.StatusCode = MapStatusCode(result);
@@ -130,7 +130,7 @@ namespace WireMock.Owin.Mappers
switch (responseMessage.BodyData?.DetectedBodyType) switch (responseMessage.BodyData?.DetectedBodyType)
{ {
case BodyType.String: case BodyType.String:
return (responseMessage.BodyData.Encoding ?? _utf8NoBom).GetBytes(responseMessage.BodyData.BodyAsString); return (responseMessage.BodyData.Encoding ?? _utf8NoBom).GetBytes(responseMessage.BodyData.BodyAsString!);
case BodyType.Json: case BodyType.Json:
var formatting = responseMessage.BodyData.BodyAsJsonIndented == true var formatting = responseMessage.BodyData.BodyAsJsonIndented == true
@@ -143,7 +143,7 @@ namespace WireMock.Owin.Mappers
return responseMessage.BodyData.BodyAsBytes; return responseMessage.BodyData.BodyAsBytes;
case BodyType.File: case BodyType.File:
return _options.FileSystemHandler?.ReadResponseBodyAsFile(responseMessage.BodyData.BodyAsFile); return _options.FileSystemHandler?.ReadResponseBodyAsFile(responseMessage.BodyData.BodyAsFile!);
} }
return null; return null;
@@ -161,7 +161,7 @@ namespace WireMock.Owin.Mappers
}); });
// Set other headers // Set other headers
foreach (var item in responseMessage.Headers) foreach (var item in responseMessage.Headers!)
{ {
var headerName = item.Key; var headerName = item.Key;
var value = item.Value; var value = item.Value;

View File

@@ -87,4 +87,7 @@ internal class WireMockMiddlewareOptions : IWireMockMiddlewareOptions
/// <inheritdoc /> /// <inheritdoc />
public bool? DoNotSaveDynamicResponseInLogEntry { get; set; } public bool? DoNotSaveDynamicResponseInLogEntry { get; set; }
/// <inheritdoc />
public QueryParameterMultipleValueSupport? QueryParameterMultipleValueSupport { get; set; }
} }

View File

@@ -6,6 +6,7 @@ using System.Linq;
using System.Net; using System.Net;
using Stef.Validation; using Stef.Validation;
using WireMock.Models; using WireMock.Models;
using WireMock.Owin;
using WireMock.Types; using WireMock.Types;
using WireMock.Util; using WireMock.Util;
@@ -26,7 +27,7 @@ public class RequestMessage : IRequestMessage
public string AbsoluteUrl { get; } public string AbsoluteUrl { get; }
/// <inheritdoc cref="IRequestMessage.ProxyUrl" /> /// <inheritdoc cref="IRequestMessage.ProxyUrl" />
public string ProxyUrl { get; set; } public string? ProxyUrl { get; set; }
/// <inheritdoc cref="IRequestMessage.DateTime" /> /// <inheritdoc cref="IRequestMessage.DateTime" />
public DateTime DateTime { get; set; } public DateTime DateTime { get; set; }
@@ -91,16 +92,36 @@ public class RequestMessage : IRequestMessage
/// <inheritdoc cref="IRequestMessage.Origin" /> /// <inheritdoc cref="IRequestMessage.Origin" />
public string Origin { get; } public string Origin { get; }
/// <summary>
/// Used for Unit Testing
/// </summary>
public RequestMessage(
UrlDetails urlDetails,
string method,
string clientIP,
IBodyData? bodyData = null,
IDictionary<string, string[]>? headers = null,
IDictionary<string, string>? cookies = null) : this(null, urlDetails, method, clientIP, bodyData, headers, cookies)
{
}
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="RequestMessage"/> class. /// Initializes a new instance of the <see cref="RequestMessage"/> class.
/// </summary> /// </summary>
/// <param name="options">The<seealso cref="IWireMockMiddlewareOptions"/>.</param>
/// <param name="urlDetails">The original url details.</param> /// <param name="urlDetails">The original url details.</param>
/// <param name="method">The HTTP method.</param> /// <param name="method">The HTTP method.</param>
/// <param name="clientIP">The client IP Address.</param> /// <param name="clientIP">The client IP Address.</param>
/// <param name="bodyData">The BodyData.</param> /// <param name="bodyData">The BodyData.</param>
/// <param name="headers">The headers.</param> /// <param name="headers">The headers.</param>
/// <param name="cookies">The cookies.</param> /// <param name="cookies">The cookies.</param>
public RequestMessage(UrlDetails urlDetails, string method, string clientIP, IBodyData? bodyData = null, IDictionary<string, string[]>? headers = null, IDictionary<string, string>? cookies = null) internal RequestMessage(
IWireMockMiddlewareOptions? options,
UrlDetails urlDetails, string method,
string clientIP,
IBodyData? bodyData = null,
IDictionary<string, string[]>? headers = null,
IDictionary<string, string>? cookies = null)
{ {
Guard.NotNull(urlDetails, nameof(urlDetails)); Guard.NotNull(urlDetails, nameof(urlDetails));
Guard.NotNull(method, nameof(method)); Guard.NotNull(method, nameof(method));
@@ -134,7 +155,7 @@ public class RequestMessage : IRequestMessage
Headers = headers?.ToDictionary(header => header.Key, header => new WireMockList<string>(header.Value)); Headers = headers?.ToDictionary(header => header.Key, header => new WireMockList<string>(header.Value));
Cookies = cookies; Cookies = cookies;
RawQuery = urlDetails.Url.Query; RawQuery = urlDetails.Url.Query;
Query = QueryStringParser.Parse(RawQuery); Query = QueryStringParser.Parse(RawQuery, options?.QueryParameterMultipleValueSupport);
} }
/// <summary> /// <summary>

View File

@@ -125,9 +125,9 @@ public interface IRespondWithAProvider
/// <summary> /// <summary>
/// Support FireAndForget for any configured Webhooks /// Support FireAndForget for any configured Webhooks
/// </summary> /// </summary>
/// <param name="UseWebhooksFireAndForget"></param> /// <param name="useWebhooksFireAndForget"></param>
/// <returns></returns> /// <returns></returns>
IRespondWithAProvider WithWebhookFireAndForget(bool UseWebhooksFireAndForget); IRespondWithAProvider WithWebhookFireAndForget(bool useWebhooksFireAndForget);
/// <summary> /// <summary>
/// Add a Webhook to call after the response has been generated. /// Add a Webhook to call after the response has been generated.
@@ -141,7 +141,7 @@ public interface IRespondWithAProvider
/// <returns>The <see cref="IRespondWithAProvider"/>.</returns> /// <returns>The <see cref="IRespondWithAProvider"/>.</returns>
IRespondWithAProvider WithWebhook( IRespondWithAProvider WithWebhook(
string url, string url,
string? method = "post", string method = "post",
IDictionary<string, WireMockList<string>>? headers = null, IDictionary<string, WireMockList<string>>? headers = null,
string? body = null, string? body = null,
bool useTransformer = true, bool useTransformer = true,
@@ -160,7 +160,7 @@ public interface IRespondWithAProvider
/// <returns>The <see cref="IRespondWithAProvider"/>.</returns> /// <returns>The <see cref="IRespondWithAProvider"/>.</returns>
IRespondWithAProvider WithWebhook( IRespondWithAProvider WithWebhook(
string url, string url,
string? method = "post", string method = "post",
IDictionary<string, WireMockList<string>>? headers = null, IDictionary<string, WireMockList<string>>? headers = null,
object? body = null, object? body = null,
bool useTransformer = true, bool useTransformer = true,

View File

@@ -30,7 +30,7 @@ internal class RespondWithAProvider : IRespondWithAProvider
private readonly WireMockServerSettings _settings; private readonly WireMockServerSettings _settings;
private readonly bool _saveToFile; private readonly bool _saveToFile;
private bool _useWebhookFireAndForget = false; private bool _useWebhookFireAndForget;
public Guid Guid { get; private set; } = Guid.NewGuid(); public Guid Guid { get; private set; } = Guid.NewGuid();

View File

@@ -223,6 +223,7 @@ public partial class WireMockServer
WatchStaticMappingsInSubdirectories = _settings.WatchStaticMappingsInSubdirectories, WatchStaticMappingsInSubdirectories = _settings.WatchStaticMappingsInSubdirectories,
HostingScheme = _settings.HostingScheme, HostingScheme = _settings.HostingScheme,
DoNotSaveDynamicResponseInLogEntry = _settings.DoNotSaveDynamicResponseInLogEntry, DoNotSaveDynamicResponseInLogEntry = _settings.DoNotSaveDynamicResponseInLogEntry,
QueryParameterMultipleValueSupport = _settings.QueryParameterMultipleValueSupport,
#if USE_ASPNETCORE #if USE_ASPNETCORE
CorsPolicyOptions = _settings.CorsPolicyOptions?.ToString() CorsPolicyOptions = _settings.CorsPolicyOptions?.ToString()
@@ -252,6 +253,7 @@ public partial class WireMockServer
_settings.WatchStaticMappings = settings.WatchStaticMappings; _settings.WatchStaticMappings = settings.WatchStaticMappings;
_settings.WatchStaticMappingsInSubdirectories = settings.WatchStaticMappingsInSubdirectories; _settings.WatchStaticMappingsInSubdirectories = settings.WatchStaticMappingsInSubdirectories;
_settings.DoNotSaveDynamicResponseInLogEntry = settings.DoNotSaveDynamicResponseInLogEntry; _settings.DoNotSaveDynamicResponseInLogEntry = settings.DoNotSaveDynamicResponseInLogEntry;
_settings.QueryParameterMultipleValueSupport = settings.QueryParameterMultipleValueSupport;
InitSettings(_settings); InitSettings(_settings);

View File

@@ -296,6 +296,7 @@ public partial class WireMockServer : IWireMockServer
_options.HandleRequestsSynchronously = settings.HandleRequestsSynchronously; _options.HandleRequestsSynchronously = settings.HandleRequestsSynchronously;
_options.SaveUnmatchedRequests = settings.SaveUnmatchedRequests; _options.SaveUnmatchedRequests = settings.SaveUnmatchedRequests;
_options.DoNotSaveDynamicResponseInLogEntry = settings.DoNotSaveDynamicResponseInLogEntry; _options.DoNotSaveDynamicResponseInLogEntry = settings.DoNotSaveDynamicResponseInLogEntry;
_options.QueryParameterMultipleValueSupport = settings.QueryParameterMultipleValueSupport;
if (settings.CustomCertificateDefined) if (settings.CustomCertificateDefined)
{ {

View File

@@ -258,6 +258,14 @@ namespace WireMock.Settings
[PublicAPI] [PublicAPI]
public bool? DoNotSaveDynamicResponseInLogEntry { get; set; } public bool? DoNotSaveDynamicResponseInLogEntry { get; set; }
/// <summary>
/// See <seealso cref="QueryParameterMultipleValueSupport"/>.
///
/// Default value = "All".
/// </summary>
[PublicAPI]
public QueryParameterMultipleValueSupport? QueryParameterMultipleValueSupport { get; set; }
/// <summary> /// <summary>
/// Custom matcher mappings for static mappings /// Custom matcher mappings for static mappings
/// </summary> /// </summary>

View File

@@ -1,4 +1,3 @@
using System;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using JetBrains.Annotations; using JetBrains.Annotations;
using Stef.Validation; using Stef.Validation;
@@ -55,7 +54,8 @@ public static class WireMockServerSettingsParser
WatchStaticMappings = parser.GetBoolValue("WatchStaticMappings"), WatchStaticMappings = parser.GetBoolValue("WatchStaticMappings"),
WatchStaticMappingsInSubdirectories = parser.GetBoolValue("WatchStaticMappingsInSubdirectories"), WatchStaticMappingsInSubdirectories = parser.GetBoolValue("WatchStaticMappingsInSubdirectories"),
HostingScheme = parser.GetEnumValue<HostingScheme>(nameof(WireMockServerSettings.HostingScheme)), HostingScheme = parser.GetEnumValue<HostingScheme>(nameof(WireMockServerSettings.HostingScheme)),
DoNotSaveDynamicResponseInLogEntry = parser.GetBoolValue(nameof(WireMockServerSettings.DoNotSaveDynamicResponseInLogEntry)) DoNotSaveDynamicResponseInLogEntry = parser.GetBoolValue(nameof(WireMockServerSettings.DoNotSaveDynamicResponseInLogEntry)),
QueryParameterMultipleValueSupport = parser.GetEnumValue<QueryParameterMultipleValueSupport>(nameof(WireMockServerSettings.QueryParameterMultipleValueSupport))
}; };
#if USE_ASPNETCORE #if USE_ASPNETCORE

View File

@@ -11,25 +11,41 @@ namespace WireMock.Util;
/// </summary> /// </summary>
internal static class QueryStringParser internal static class QueryStringParser
{ {
public static IDictionary<string, WireMockList<string>> Parse(string queryString) private static readonly Dictionary<string, WireMockList<string>> Empty = new();
public static IDictionary<string, WireMockList<string>> Parse(string? queryString, QueryParameterMultipleValueSupport? support = null)
{ {
if (string.IsNullOrEmpty(queryString)) if (string.IsNullOrEmpty(queryString))
{ {
return new Dictionary<string, WireMockList<string>>(); return Empty;
} }
var queryParameterMultipleValueSupport = support ?? QueryParameterMultipleValueSupport.All;
string[] JoinParts(string[] parts) string[] JoinParts(string[] parts)
{ {
if (parts.Length > 1) if (parts.Length > 1)
{ {
return parts[1].Split(new[] { "," }, StringSplitOptions.RemoveEmptyEntries); // support "?key=1,2" return queryParameterMultipleValueSupport.HasFlag(QueryParameterMultipleValueSupport.Comma) ?
parts[1].Split(new[] { "," }, StringSplitOptions.RemoveEmptyEntries) : // Support "?key=1,2"
new[] { parts[1] };
} }
return new string[0]; return new string[0];
} }
return queryString.TrimStart('?') var splitOn = new List<string>();
.Split(new[] { '&', ';' }, StringSplitOptions.RemoveEmptyEntries) // Support "?key=value;key=anotherValue" and "?key=value&key=anotherValue" if (queryParameterMultipleValueSupport.HasFlag(QueryParameterMultipleValueSupport.Ampersand))
{
splitOn.Add("&"); // Support "?key=value&key=anotherValue"
}
if (queryParameterMultipleValueSupport.HasFlag(QueryParameterMultipleValueSupport.SemiColon))
{
splitOn.Add(";"); // Support "?key=value;key=anotherValue"
}
return queryString!.TrimStart('?')
.Split(splitOn.ToArray(), StringSplitOptions.RemoveEmptyEntries)
.Select(parameter => parameter.Split(new[] { '=' }, 2, StringSplitOptions.RemoveEmptyEntries)) .Select(parameter => parameter.Split(new[] { '=' }, 2, StringSplitOptions.RemoveEmptyEntries))
.GroupBy(parts => parts[0], JoinParts) .GroupBy(parts => parts[0], JoinParts)
.ToDictionary(grouping => grouping.Key, grouping => new WireMockList<string>(grouping.SelectMany(x => x).Select(WebUtility.UrlDecode))); .ToDictionary(grouping => grouping.Key, grouping => new WireMockList<string>(grouping.SelectMany(x => x).Select(WebUtility.UrlDecode)));

View File

@@ -1,16 +1,17 @@
using NFluent; using NFluent;
using System.Collections.Generic; using System.Collections.Generic;
using WireMock.Matchers.Request; using WireMock.Matchers.Request;
using WireMock.Models; using WireMock.Models;
using WireMock.Owin;
using WireMock.RequestBuilders; using WireMock.RequestBuilders;
using WireMock.Types; using WireMock.Types;
using WireMock.Util; using WireMock.Util;
using Xunit; using Xunit;
namespace WireMock.Net.Tests namespace WireMock.Net.Tests;
public class RequestTests
{ {
public class RequestTests
{
private const string ClientIp = "::1"; private const string ClientIp = "::1";
[Fact] [Fact]
@@ -212,6 +213,24 @@ namespace WireMock.Net.Tests
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0); Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0);
} }
[Fact]
public void Should_specify_requests_matching_given_param_WithComma()
{
// given
var options = new WireMockMiddlewareOptions
{
QueryParameterMultipleValueSupport = QueryParameterMultipleValueSupport.NoComma
};
var spec = Request.Create().WithParam("$filter", "startswith(name,'testName')");
// when
var request = new RequestMessage(options, new UrlDetails("http://localhost/?$filter=startswith(name,'testName')"), "PUT", ClientIp);
// then
var requestMatchResult = new RequestMatchResult();
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0);
}
[Fact] [Fact]
public void Should_specify_requests_matching_given_param_func() public void Should_specify_requests_matching_given_param_func()
{ {
@@ -239,5 +258,4 @@ namespace WireMock.Net.Tests
var requestMatchResult = new RequestMatchResult(); var requestMatchResult = new RequestMatchResult();
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsNotEqualTo(1.0); Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsNotEqualTo(1.0);
} }
}
} }

View File

@@ -1,11 +1,11 @@
using System.Collections.Generic; using System.Collections.Generic;
using NFluent; using NFluent;
using WireMock.Matchers; using WireMock.Matchers;
using Xunit;
using WireMock.RequestBuilders;
using WireMock.Matchers.Request; using WireMock.Matchers.Request;
using WireMock.Models; using WireMock.Models;
using WireMock.RequestBuilders;
using WireMock.Util; using WireMock.Util;
using Xunit;
namespace WireMock.Net.Tests namespace WireMock.Net.Tests
{ {
@@ -19,11 +19,11 @@ namespace WireMock.Net.Tests
// Assign // Assign
var spec = Request.Create().WithPath("/path/a b").UsingAnyMethod(); var spec = Request.Create().WithPath("/path/a b").UsingAnyMethod();
// when // Act
var body = new BodyData(); var body = new BodyData();
var request = new RequestMessage(new UrlDetails("http://localhost/path/a b"), "GET", ClientIp, body); var request = new RequestMessage(new UrlDetails("http://localhost/path/a b"), "GET", ClientIp, body);
// then // Assert
var requestMatchResult = new RequestMatchResult(); var requestMatchResult = new RequestMatchResult();
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0); Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0);
} }
@@ -31,17 +31,17 @@ namespace WireMock.Net.Tests
[Fact] [Fact]
public void Request_WithPath_WithHeader_Match() public void Request_WithPath_WithHeader_Match()
{ {
// given // Arrange
var spec = Request.Create().WithPath("/foo").UsingAnyMethod().WithHeader("X-toto", "tata"); var spec = Request.Create().WithPath("/foo").UsingAnyMethod().WithHeader("X-toto", "tata");
// when // Act
var body = new BodyData var body = new BodyData
{ {
BodyAsString = "abc" BodyAsString = "abc"
}; };
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "PUT", ClientIp, body, new Dictionary<string, string[]> { { "X-toto", new[] { "tata" } } }); var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "PUT", ClientIp, body, new Dictionary<string, string[]> { { "X-toto", new[] { "tata" } } });
// then // Assert
var requestMatchResult = new RequestMatchResult(); var requestMatchResult = new RequestMatchResult();
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0); Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0);
} }
@@ -49,13 +49,13 @@ namespace WireMock.Net.Tests
[Fact] [Fact]
public void Request_WithPath() public void Request_WithPath()
{ {
// given // Arrange
var spec = Request.Create().WithPath("/foo"); var spec = Request.Create().WithPath("/foo");
// when // Act
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "blabla", ClientIp); var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "blabla", ClientIp);
// then // Assert
var requestMatchResult = new RequestMatchResult(); var requestMatchResult = new RequestMatchResult();
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0); Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0);
} }
@@ -76,13 +76,13 @@ namespace WireMock.Net.Tests
[Fact] [Fact]
public void Request_WithPathFunc() public void Request_WithPathFunc()
{ {
// given // Arrange
var spec = Request.Create().WithPath(url => url.EndsWith("/foo")); var spec = Request.Create().WithPath(url => url.EndsWith("/foo"));
// when // Act
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "blabla", ClientIp); var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "blabla", ClientIp);
// then // Assert
var requestMatchResult = new RequestMatchResult(); var requestMatchResult = new RequestMatchResult();
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0); Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0);
} }
@@ -90,13 +90,13 @@ namespace WireMock.Net.Tests
[Fact] [Fact]
public void Request_WithPathRegexMatcher_HasMatch() public void Request_WithPathRegexMatcher_HasMatch()
{ {
// given // Arrange
var spec = Request.Create().WithPath(new RegexMatcher("^/foo")); var spec = Request.Create().WithPath(new RegexMatcher("^/foo"));
// when // Act
var request = new RequestMessage(new UrlDetails("http://localhost/foo/bar"), "blabla", ClientIp); var request = new RequestMessage(new UrlDetails("http://localhost/foo/bar"), "blabla", ClientIp);
// then // Assert
var requestMatchResult = new RequestMatchResult(); var requestMatchResult = new RequestMatchResult();
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0); Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0);
} }
@@ -104,13 +104,13 @@ namespace WireMock.Net.Tests
[Fact] [Fact]
public void Request_WithPathRegexMatcher_HasNoMatch() public void Request_WithPathRegexMatcher_HasNoMatch()
{ {
// given // Arrange
var spec = Request.Create().WithPath("/foo"); var spec = Request.Create().WithPath("/foo");
// when // Act
var request = new RequestMessage(new UrlDetails("http://localhost/bar"), "blabla", ClientIp); var request = new RequestMessage(new UrlDetails("http://localhost/bar"), "blabla", ClientIp);
// then // Assert
var requestMatchResult = new RequestMatchResult(); var requestMatchResult = new RequestMatchResult();
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsNotEqualTo(1.0); Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsNotEqualTo(1.0);
} }
@@ -126,10 +126,10 @@ namespace WireMock.Net.Tests
}; };
var spec = Request.Create().WithPath(new RegexMatcher(pattern)); var spec = Request.Create().WithPath(new RegexMatcher(pattern));
// when // Act
var request = new RequestMessage(new UrlDetails("http://localhost/foo/bar"), "blabla", ClientIp); var request = new RequestMessage(new UrlDetails("http://localhost/foo/bar"), "blabla", ClientIp);
// then // Assert
var requestMatchResult = new RequestMatchResult(); var requestMatchResult = new RequestMatchResult();
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0); Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0);
} }
@@ -137,17 +137,17 @@ namespace WireMock.Net.Tests
[Fact] [Fact]
public void Should_specify_requests_matching_given_path_and_method_delete() public void Should_specify_requests_matching_given_path_and_method_delete()
{ {
// given // Arrange
var spec = Request.Create().WithPath("/foo").UsingDelete(); var spec = Request.Create().WithPath("/foo").UsingDelete();
// when // Act
var body = new BodyData var body = new BodyData
{ {
BodyAsString = "whatever" BodyAsString = "whatever"
}; };
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "Delete", ClientIp, body); var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "Delete", ClientIp, body);
// then // Assert
var requestMatchResult = new RequestMatchResult(); var requestMatchResult = new RequestMatchResult();
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0); Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0);
} }
@@ -155,13 +155,13 @@ namespace WireMock.Net.Tests
[Fact] [Fact]
public void Should_specify_requests_matching_given_path_and_method_get() public void Should_specify_requests_matching_given_path_and_method_get()
{ {
// given // Arrange
var spec = Request.Create().WithPath("/foo").UsingGet(); var spec = Request.Create().WithPath("/foo").UsingGet();
// when // Act
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "GET", ClientIp); var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "GET", ClientIp);
// then // Assert
var requestMatchResult = new RequestMatchResult(); var requestMatchResult = new RequestMatchResult();
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0); Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0);
} }
@@ -169,13 +169,13 @@ namespace WireMock.Net.Tests
[Fact] [Fact]
public void Should_specify_requests_matching_given_path_and_method_head() public void Should_specify_requests_matching_given_path_and_method_head()
{ {
// given // Arrange
var spec = Request.Create().WithPath("/foo").UsingHead(); var spec = Request.Create().WithPath("/foo").UsingHead();
// when // Act
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "HEAD", ClientIp); var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "HEAD", ClientIp);
// then // Assert
var requestMatchResult = new RequestMatchResult(); var requestMatchResult = new RequestMatchResult();
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0); Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0);
} }
@@ -183,13 +183,13 @@ namespace WireMock.Net.Tests
[Fact] [Fact]
public void Should_specify_requests_matching_given_path_and_method_post() public void Should_specify_requests_matching_given_path_and_method_post()
{ {
// given // Arrange
var spec = Request.Create().WithPath("/foo").UsingPost(); var spec = Request.Create().WithPath("/foo").UsingPost();
// when // Act
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "POST", ClientIp); var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "POST", ClientIp);
// then // Assert
var requestMatchResult = new RequestMatchResult(); var requestMatchResult = new RequestMatchResult();
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0); Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0);
} }
@@ -197,13 +197,13 @@ namespace WireMock.Net.Tests
[Fact] [Fact]
public void Should_specify_requests_matching_given_path_and_method_put() public void Should_specify_requests_matching_given_path_and_method_put()
{ {
// given // Arrange
var spec = Request.Create().WithPath("/foo").UsingPut(); var spec = Request.Create().WithPath("/foo").UsingPut();
// when // Act
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "PUT", ClientIp); var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "PUT", ClientIp);
// then // Assert
var requestMatchResult = new RequestMatchResult(); var requestMatchResult = new RequestMatchResult();
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0); Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0);
} }
@@ -211,13 +211,13 @@ namespace WireMock.Net.Tests
[Fact] [Fact]
public void Should_specify_requests_matching_given_path_and_method_patch() public void Should_specify_requests_matching_given_path_and_method_patch()
{ {
// given // Arrange
var spec = Request.Create().WithPath("/foo").UsingPatch(); var spec = Request.Create().WithPath("/foo").UsingPatch();
// when // Act
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "PATCH", ClientIp); var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "PATCH", ClientIp);
// then // Assert
var requestMatchResult = new RequestMatchResult(); var requestMatchResult = new RequestMatchResult();
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0); Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0);
} }
@@ -225,13 +225,13 @@ namespace WireMock.Net.Tests
[Fact] [Fact]
public void Should_exclude_requests_matching_given_path_but_not_http_method() public void Should_exclude_requests_matching_given_path_but_not_http_method()
{ {
// given // Arrange
var spec = Request.Create().WithPath("/foo").UsingPut(); var spec = Request.Create().WithPath("/foo").UsingPut();
// when // Act
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "HEAD", ClientIp); var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "HEAD", ClientIp);
// then // Assert
var requestMatchResult = new RequestMatchResult(); var requestMatchResult = new RequestMatchResult();
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsNotEqualTo(1.0); Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsNotEqualTo(1.0);
} }

View File

@@ -0,0 +1,30 @@
using FluentAssertions;
using WireMock.Matchers;
using Xunit;
using WireMock.RequestBuilders;
using WireMock.Matchers.Request;
using WireMock.Models;
using WireMock.Util;
namespace WireMock.Net.Tests
{
public class RequestWithUrlTests
{
private const string ClientIp = "::1";
[Fact]
public void Request_WithUrl_Regex()
{
// Assign
var spec = Request.Create().WithUrl(new RegexMatcher("(some\\/service\\/v1\\/name)([?]{1})(param.source=SYSTEM){1}([&]{1})(param.id=123457890){1}$")).UsingAnyMethod();
// Act
var body = new BodyData();
var request = new RequestMessage(new UrlDetails("https://localhost/some/service/v1/name?param.source=SYSTEM&param.id=123457890"), "POST", ClientIp, body);
// Assert
var requestMatchResult = new RequestMatchResult();
spec.GetMatchingScore(request, requestMatchResult).Should().Be(1.0);
}
}
}

View File

@@ -1,18 +1,18 @@
using FluentAssertions; using FluentAssertions;
using System.Collections.Generic; using System.Collections.Generic;
using WireMock.Types; using WireMock.Types;
using WireMock.Util; using WireMock.Util;
using Xunit; using Xunit;
namespace WireMock.Net.Tests.Util namespace WireMock.Net.Tests.Util;
public class QueryStringParserTests
{ {
public class QueryStringParserTests
{
[Fact] [Fact]
public void Parse_WithNullString() public void Parse_WithNullString()
{ {
// Assign // Assign
string query = null; string? query = null;
// Act // Act
var result = QueryStringParser.Parse(query); var result = QueryStringParser.Parse(query);
@@ -172,11 +172,39 @@ namespace WireMock.Net.Tests.Util
// Assert // Assert
result.Count.Should().Be(1); result.Count.Should().Be(1);
result["key"].Should().Equal(new WireMockList<string>(new[] { "value", "anotherValue" })); result["key"].Should().Equal(new WireMockList<string>("value", "anotherValue"));
} }
[Fact] [Fact]
public void Parse_With1ParamContainingComma() public void Parse_WithMultipleParamWithSameKeySeparatedByAmp()
{
// Assign
string query = "?key=1&key=2";
// Act
var result = QueryStringParser.Parse(query);
// Assert
result.Count.Should().Be(1);
result["key"].Should().Equal(new WireMockList<string>("1", "2"));
}
[Fact]
public void Parse_With1ParamContainingComma_When_SupportMultiValueUsingComma_Is_True()
{
// Assign
string query = "?key=1,2,3";
// Act
var result = QueryStringParser.Parse(query);
// Assert
result.Count.Should().Be(1);
result["key"].Should().Equal(new WireMockList<string>("1", "2", "3"));
}
[Fact]
public void Parse_With1ParamContainingCommaAndAmpCombined_When_SupportMultiValueUsingComma_Is_Comma()
{ {
// Assign // Assign
string query = "?key=1,2&key=3"; string query = "?key=1,2&key=3";
@@ -186,7 +214,21 @@ namespace WireMock.Net.Tests.Util
// Assert // Assert
result.Count.Should().Be(1); result.Count.Should().Be(1);
result["key"].Should().Equal(new WireMockList<string>(new[] { "1", "2", "3" })); result["key"].Should().Equal(new WireMockList<string>("1", "2", "3"));
}
[Fact]
public void Parse_With1ParamContainingComma_SupportMultiValueUsingComma_Is_AmpersandAndSemiColon()
{
// Assign
string query = "?$filter=startswith(name,'testName')";
// Act
var result = QueryStringParser.Parse(query, QueryParameterMultipleValueSupport.AmpersandAndSemiColon);
// Assert
result.Count.Should().Be(1);
result["$filter"].Should().Equal(new WireMockList<string>("startswith(name,'testName')"));
} }
[Fact] [Fact]
@@ -200,7 +242,7 @@ namespace WireMock.Net.Tests.Util
// Assert // Assert
result.Count.Should().Be(1); result.Count.Should().Be(1);
result["winkel"].Should().Equal(new WireMockList<string>(new[] { "C&A" })); result["winkel"].Should().Equal(new WireMockList<string>("C&A"));
} }
[Fact] [Fact]
@@ -214,7 +256,7 @@ namespace WireMock.Net.Tests.Util
// Assert // Assert
result.Count.Should().Be(1); result.Count.Should().Be(1);
result["Transaction"].Should().Equal(new WireMockList<string>(new[] { "(123)" })); result["Transaction"].Should().Equal(new WireMockList<string>("(123)"));
} }
[Fact] [Fact]
@@ -228,7 +270,7 @@ namespace WireMock.Net.Tests.Util
// Assert // Assert
result.Count.Should().Be(1); result.Count.Should().Be(1);
result["key"].Should().Equal(new WireMockList<string>(new[] { "value", "anotherValue" })); result["key"].Should().Equal(new WireMockList<string>("value", "anotherValue"));
} }
[Fact] [Fact]
@@ -263,5 +305,4 @@ namespace WireMock.Net.Tests.Util
result["startIndex"].Should().Equal(new WireMockList<string>()); result["startIndex"].Should().Equal(new WireMockList<string>());
result["startPage"].Should().Equal(new WireMockList<string>("1\"")); result["startPage"].Should().Equal(new WireMockList<string>("1\""));
} }
}
} }