Add WithProbability (#922)

* WithProbability

* fix

* x

* ,

* .

* .

* .
This commit is contained in:
Stef Heyenrath
2023-04-12 20:48:53 +02:00
committed by GitHub
parent f3d52adbb2
commit 090e0eb437
36 changed files with 442 additions and 274 deletions

View File

@@ -1,19 +1,18 @@
namespace WireMock.Admin.Mappings namespace WireMock.Admin.Mappings;
/// <summary>
/// Fault Model
/// </summary>
[FluentBuilder.AutoGenerateBuilder]
public class FaultModel
{ {
/// <summary> /// <summary>
/// Fault Model /// Gets or sets the fault. Can be null, "", NONE, EMPTY_RESPONSE or MALFORMED_RESPONSE_CHUNK.
/// </summary> /// </summary>
[FluentBuilder.AutoGenerateBuilder] public string? Type { get; set; }
public class FaultModel
{
/// <summary>
/// Gets or sets the fault. Can be null, "", NONE, EMPTY_RESPONSE, MALFORMED_RESPONSE_CHUNK or RANDOM_DATA_THEN_CLOSE.
/// </summary>
public string Type { get; set; }
/// <summary> /// <summary>
/// Gets or sets the fault percentage. /// Gets or sets the fault percentage.
/// </summary> /// </summary>
public double? Percentage { get; set; } public double? Percentage { get; set; }
}
} }

View File

@@ -1,5 +1,4 @@
using System; using System;
using System.Collections.Generic;
using WireMock.Models; using WireMock.Models;
namespace WireMock.Admin.Mappings; namespace WireMock.Admin.Mappings;
@@ -94,4 +93,9 @@ public class MappingModel
/// </example> /// </example>
/// </summary> /// </summary>
public object? Data { get; set; } public object? Data { get; set; }
/// <summary>
/// The probability when this request should be matched. Value is between 0 and 1. [Optional]
/// </summary>
public double? Probability { get; set; }
} }

View File

@@ -1,23 +1,23 @@
namespace WireMock.ResponseBuilders // ReSharper disable InconsistentNaming
namespace WireMock.ResponseBuilders;
/// <summary>
/// The FaultType enumeration
/// </summary>
public enum FaultType
{ {
/// <summary> /// <summary>
/// The FaultType enumeration /// No Fault
/// </summary> /// </summary>
public enum FaultType NONE,
{
/// <summary>
/// No Fault
/// </summary>
NONE,
/// <summary> /// <summary>
/// Return a completely empty response. /// Return a completely empty response.
/// </summary> /// </summary>
EMPTY_RESPONSE, EMPTY_RESPONSE,
/// <summary> /// <summary>
/// Send a defined status header, then garbage, then close the connection. /// Send a defined status header, then garbage, then close the connection.
/// </summary> /// </summary>
MALFORMED_RESPONSE_CHUNK MALFORMED_RESPONSE_CHUNK
}
} }

View File

@@ -1,5 +1,4 @@
using System; using System;
using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
using WireMock.Matchers.Request; using WireMock.Matchers.Request;
using WireMock.Models; using WireMock.Models;
@@ -132,6 +131,11 @@ public interface IMapping
/// </summary> /// </summary>
object? Data { get; } object? Data { get; }
/// <summary>
/// The probability when this request should be matched. Value is between 0 and 1. [Optional]
/// </summary>
double? Probability { get; }
/// <summary> /// <summary>
/// ProvideResponseAsync /// ProvideResponseAsync
/// </summary> /// </summary>

View File

@@ -4,7 +4,7 @@ using System.Linq.Dynamic.Core;
namespace WireMock.Json; namespace WireMock.Json;
public class DynamicPropertyWithValue : DynamicProperty internal class DynamicPropertyWithValue : DynamicProperty
{ {
public object? Value { get; } public object? Value { get; }

View File

@@ -75,6 +75,9 @@ public class Mapping : IMapping
/// <inheritdoc /> /// <inheritdoc />
public object? Data { get; } public object? Data { get; }
/// <inheritdoc />
public double? Probability { get; }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="Mapping"/> class. /// Initializes a new instance of the <see cref="Mapping"/> class.
/// </summary> /// </summary>
@@ -95,6 +98,7 @@ public class Mapping : IMapping
/// <param name="useWebhooksFireAndForget">Use Fire and Forget for the defined webhook(s). [Optional]</param> /// <param name="useWebhooksFireAndForget">Use Fire and Forget for the defined webhook(s). [Optional]</param>
/// <param name="timeSettings">The TimeSettings. [Optional]</param> /// <param name="timeSettings">The TimeSettings. [Optional]</param>
/// <param name="data">The data object. [Optional]</param> /// <param name="data">The data object. [Optional]</param>
/// <param name="probability">Define the probability when this request should be matched. [Optional]</param>
public Mapping( public Mapping(
Guid guid, Guid guid,
DateTime updatedAt, DateTime updatedAt,
@@ -112,7 +116,8 @@ public class Mapping : IMapping
IWebhook[]? webhooks, IWebhook[]? webhooks,
bool? useWebhooksFireAndForget, bool? useWebhooksFireAndForget,
ITimeSettings? timeSettings, ITimeSettings? timeSettings,
object? data) object? data,
double? probability)
{ {
Guid = guid; Guid = guid;
UpdatedAt = updatedAt; UpdatedAt = updatedAt;
@@ -131,6 +136,7 @@ public class Mapping : IMapping
UseWebhooksFireAndForget = useWebhooksFireAndForget; UseWebhooksFireAndForget = useWebhooksFireAndForget;
TimeSettings = timeSettings; TimeSettings = timeSettings;
Data = data; Data = data;
Probability = probability;
} }
/// <inheritdoc cref="IMapping.ProvideResponseAsync" /> /// <inheritdoc cref="IMapping.ProvideResponseAsync" />

View File

@@ -69,12 +69,7 @@ public class MappingBuilder : IMappingBuilder
/// <inheritdoc /> /// <inheritdoc />
public MappingModel[] GetMappings() public MappingModel[] GetMappings()
{ {
return GetMappingsInternal().Select(_mappingConverter.ToMappingModel).ToArray(); return GetNonAdminMappings().Select(_mappingConverter.ToMappingModel).ToArray();
}
internal IMapping[] GetMappingsInternal()
{
return _options.Mappings.Values.ToArray().Where(m => !m.IsAdminInterface).ToArray();
} }
/// <inheritdoc /> /// <inheritdoc />
@@ -86,7 +81,7 @@ public class MappingBuilder : IMappingBuilder
/// <inheritdoc /> /// <inheritdoc />
public string? ToCSharpCode(Guid guid, MappingConverterType converterType) public string? ToCSharpCode(Guid guid, MappingConverterType converterType)
{ {
var mapping = GetMappingsInternal().FirstOrDefault(m => m.Guid == guid); var mapping = GetNonAdminMappings().FirstOrDefault(m => m.Guid == guid);
if (mapping is null) if (mapping is null)
{ {
return null; return null;
@@ -101,7 +96,7 @@ public class MappingBuilder : IMappingBuilder
{ {
var sb = new StringBuilder(); var sb = new StringBuilder();
bool addStart = true; bool addStart = true;
foreach (var mapping in GetMappingsInternal()) foreach (var mapping in GetNonAdminMappings())
{ {
sb.AppendLine(_mappingConverter.ToCSharpCode(mapping, new MappingConverterSettings { AddStart = addStart, ConverterType = converterType })); sb.AppendLine(_mappingConverter.ToCSharpCode(mapping, new MappingConverterSettings { AddStart = addStart, ConverterType = converterType }));
@@ -123,7 +118,7 @@ public class MappingBuilder : IMappingBuilder
/// <inheritdoc /> /// <inheritdoc />
public void SaveMappingsToFolder(string? folder) public void SaveMappingsToFolder(string? folder)
{ {
foreach (var mapping in GetNonAdminMappings().Where(m => !m.IsAdminInterface)) foreach (var mapping in GetNonAdminMappings())
{ {
_mappingToFileSaver.SaveMappingToFile(mapping, folder); _mappingToFileSaver.SaveMappingToFile(mapping, folder);
} }
@@ -131,7 +126,7 @@ public class MappingBuilder : IMappingBuilder
private IMapping[] GetNonAdminMappings() private IMapping[] GetNonAdminMappings()
{ {
return _options.Mappings.Values.ToArray(); return _options.Mappings.Values.Where(m => !m.IsAdminInterface).OrderBy(m => m.UpdatedAt).ToArray();
} }
private void RegisterMapping(IMapping mapping, bool saveToFile) private void RegisterMapping(IMapping mapping, bool saveToFile)

View File

@@ -1,6 +1,5 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using JetBrains.Annotations;
using Stef.Validation; using Stef.Validation;
namespace WireMock.Matchers.Request; namespace WireMock.Matchers.Request;
@@ -25,9 +24,9 @@ public abstract class RequestMessageCompositeMatcher : IRequestMatcher
/// </summary> /// </summary>
/// <param name="requestMatchers">The request matchers.</param> /// <param name="requestMatchers">The request matchers.</param>
/// <param name="type">The CompositeMatcherType type (Defaults to 'And')</param> /// <param name="type">The CompositeMatcherType type (Defaults to 'And')</param>
protected RequestMessageCompositeMatcher([NotNull] IEnumerable<IRequestMatcher> requestMatchers, CompositeMatcherType type = CompositeMatcherType.And) protected RequestMessageCompositeMatcher(IEnumerable<IRequestMatcher> requestMatchers, CompositeMatcherType type = CompositeMatcherType.And)
{ {
Guard.NotNull(requestMatchers, nameof(requestMatchers)); Guard.NotNull(requestMatchers);
_type = type; _type = type;
RequestMatchers = requestMatchers; RequestMatchers = requestMatchers;

View File

@@ -11,6 +11,7 @@ using Microsoft.Extensions.DependencyInjection;
using Stef.Validation; using Stef.Validation;
using WireMock.Logging; using WireMock.Logging;
using WireMock.Owin.Mappers; using WireMock.Owin.Mappers;
using WireMock.Services;
using WireMock.Util; using WireMock.Util;
namespace WireMock.Owin namespace WireMock.Owin
@@ -66,6 +67,7 @@ namespace WireMock.Owin
{ {
services.AddSingleton(_wireMockMiddlewareOptions); services.AddSingleton(_wireMockMiddlewareOptions);
services.AddSingleton<IMappingMatcher, MappingMatcher>(); services.AddSingleton<IMappingMatcher, MappingMatcher>();
services.AddSingleton<IRandomizerDoubleBetween0And1, RandomizerDoubleBetween0And1>();
services.AddSingleton<IOwinRequestMapper, OwinRequestMapper>(); services.AddSingleton<IOwinRequestMapper, OwinRequestMapper>();
services.AddSingleton<IOwinResponseMapper, OwinResponseMapper>(); services.AddSingleton<IOwinResponseMapper, OwinResponseMapper>();

View File

@@ -62,11 +62,11 @@ namespace WireMock.Owin.Mappers
switch (responseMessage.FaultType) switch (responseMessage.FaultType)
{ {
case FaultType.EMPTY_RESPONSE: case FaultType.EMPTY_RESPONSE:
bytes = IsFault(responseMessage) ? new byte[0] : GetNormalBody(responseMessage); bytes = IsFault(responseMessage) ? EmptyArray<byte>.Value : GetNormalBody(responseMessage);
break; break;
case FaultType.MALFORMED_RESPONSE_CHUNK: case FaultType.MALFORMED_RESPONSE_CHUNK:
bytes = GetNormalBody(responseMessage) ?? new byte[0]; bytes = GetNormalBody(responseMessage) ?? EmptyArray<byte>.Value;
if (IsFault(responseMessage)) if (IsFault(responseMessage))
{ {
bytes = bytes.Take(bytes.Length / 2).Union(_randomizerBytes.Generate()).ToArray(); bytes = bytes.Take(bytes.Length / 2).Union(_randomizerBytes.Generate()).ToArray();
@@ -87,7 +87,7 @@ namespace WireMock.Owin.Mappers
case { } typeAsString when typeAsString == typeof(string): case { } typeAsString when typeAsString == typeof(string):
// Note: this case will also match on null // Note: this case will also match on null
int.TryParse(responseMessage.StatusCode as string, out int result); int.TryParse(responseMessage.StatusCode as string, out var result);
response.StatusCode = MapStatusCode(result); response.StatusCode = MapStatusCode(result);
break; break;

View File

@@ -1,18 +1,21 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using WireMock.Extensions;
using Stef.Validation; using Stef.Validation;
using WireMock.Extensions;
using WireMock.Services;
namespace WireMock.Owin; namespace WireMock.Owin;
internal class MappingMatcher : IMappingMatcher internal class MappingMatcher : IMappingMatcher
{ {
private readonly IWireMockMiddlewareOptions _options; private readonly IWireMockMiddlewareOptions _options;
private readonly IRandomizerDoubleBetween0And1 _randomizerDoubleBetween0And1;
public MappingMatcher(IWireMockMiddlewareOptions options) public MappingMatcher(IWireMockMiddlewareOptions options, IRandomizerDoubleBetween0And1 randomizerDoubleBetween0And1)
{ {
_options = Guard.NotNull(options); _options = Guard.NotNull(options);
_randomizerDoubleBetween0And1 = Guard.NotNull(randomizerDoubleBetween0And1);
} }
public (MappingMatcherResult? Match, MappingMatcherResult? Partial) FindBestMatch(RequestMessage request) public (MappingMatcherResult? Match, MappingMatcherResult? Partial) FindBestMatch(RequestMessage request)
@@ -21,7 +24,12 @@ internal class MappingMatcher : IMappingMatcher
var possibleMappings = new List<MappingMatcherResult>(); var possibleMappings = new List<MappingMatcherResult>();
foreach (var mapping in _options.Mappings.Values.Where(m => m.TimeSettings.IsValid())) var mappings = _options.Mappings.Values
.Where(m => m.TimeSettings.IsValid())
.Where(m => m.Probability is null || m.Probability <= _randomizerDoubleBetween0And1.Generate())
.ToArray();
foreach (var mapping in mappings)
{ {
try try
{ {

View File

@@ -9,6 +9,9 @@ using JetBrains.Annotations;
using WireMock.Logging; using WireMock.Logging;
using WireMock.Owin.Mappers; using WireMock.Owin.Mappers;
using Stef.Validation; using Stef.Validation;
using RandomDataGenerator.FieldOptions;
using RandomDataGenerator.Randomizers;
using WireMock.Services;
namespace WireMock.Owin; namespace WireMock.Owin;
@@ -70,7 +73,7 @@ internal class OwinSelfHost : IOwinSelfHost
{ {
var requestMapper = new OwinRequestMapper(); var requestMapper = new OwinRequestMapper();
var responseMapper = new OwinResponseMapper(_options); var responseMapper = new OwinResponseMapper(_options);
var matcher = new MappingMatcher(_options); var matcher = new MappingMatcher(_options, new RandomizerDoubleBetween0And1());
Action<IAppBuilder> startup = app => Action<IAppBuilder> startup = app =>
{ {

View File

@@ -1,9 +1,8 @@
namespace WireMock.RequestBuilders namespace WireMock.RequestBuilders;
/// <summary>
/// IRequestBuilder
/// </summary>
public interface IRequestBuilder : IClientIPRequestBuilder
{ {
/// <summary>
/// IRequestBuilder
/// </summary>
public interface IRequestBuilder : IClientIPRequestBuilder
{
}
} }

View File

@@ -1,82 +1,81 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using WireMock.Matchers; using WireMock.Matchers;
using WireMock.Matchers.Request; using WireMock.Matchers.Request;
using Stef.Validation; using Stef.Validation;
namespace WireMock.RequestBuilders namespace WireMock.RequestBuilders;
public partial class Request
{ {
public partial class Request /// <inheritdoc cref="ICookiesRequestBuilder.WithCookie(string, string, MatchBehaviour)"/>
public IRequestBuilder WithCookie(string name, string pattern, MatchBehaviour matchBehaviour)
{ {
/// <inheritdoc cref="ICookiesRequestBuilder.WithCookie(string, string, MatchBehaviour)"/> return WithCookie(name, pattern, true, matchBehaviour);
public IRequestBuilder WithCookie(string name, string pattern, MatchBehaviour matchBehaviour) }
{
return WithCookie(name, pattern, true, matchBehaviour);
}
/// <inheritdoc cref="ICookiesRequestBuilder.WithCookie(string, string, bool, MatchBehaviour)"/> /// <inheritdoc cref="ICookiesRequestBuilder.WithCookie(string, string, bool, MatchBehaviour)"/>
public IRequestBuilder WithCookie(string name, string pattern, bool ignoreCase = true, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch) public IRequestBuilder WithCookie(string name, string pattern, bool ignoreCase = true, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch)
{ {
Guard.NotNull(name, nameof(name)); Guard.NotNull(name, nameof(name));
Guard.NotNull(pattern, nameof(pattern)); Guard.NotNull(pattern, nameof(pattern));
_requestMatchers.Add(new RequestMessageCookieMatcher(matchBehaviour, name, pattern, ignoreCase)); _requestMatchers.Add(new RequestMessageCookieMatcher(matchBehaviour, name, pattern, ignoreCase));
return this; return this;
} }
/// <inheritdoc cref="ICookiesRequestBuilder.WithCookie(string, string[], MatchBehaviour)"/> /// <inheritdoc cref="ICookiesRequestBuilder.WithCookie(string, string[], MatchBehaviour)"/>
public IRequestBuilder WithCookie(string name, string[] patterns, MatchBehaviour matchBehaviour) public IRequestBuilder WithCookie(string name, string[] patterns, MatchBehaviour matchBehaviour)
{ {
return WithCookie(name, patterns, true, matchBehaviour); return WithCookie(name, patterns, true, matchBehaviour);
} }
/// <inheritdoc cref="ICookiesRequestBuilder.WithCookie(string, string[], bool, MatchBehaviour)"/> /// <inheritdoc cref="ICookiesRequestBuilder.WithCookie(string, string[], bool, MatchBehaviour)"/>
public IRequestBuilder WithCookie(string name, string[] patterns, bool ignoreCase = true, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch) public IRequestBuilder WithCookie(string name, string[] patterns, bool ignoreCase = true, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch)
{ {
Guard.NotNull(name, nameof(name)); Guard.NotNull(name, nameof(name));
Guard.NotNull(patterns, nameof(patterns)); Guard.NotNull(patterns, nameof(patterns));
_requestMatchers.Add(new RequestMessageCookieMatcher(matchBehaviour, name, ignoreCase, patterns)); _requestMatchers.Add(new RequestMessageCookieMatcher(matchBehaviour, name, ignoreCase, patterns));
return this; return this;
} }
/// <inheritdoc cref="ICookiesRequestBuilder.WithCookie(string, IStringMatcher[])"/> /// <inheritdoc cref="ICookiesRequestBuilder.WithCookie(string, IStringMatcher[])"/>
public IRequestBuilder WithCookie(string name, params IStringMatcher[] matchers) public IRequestBuilder WithCookie(string name, params IStringMatcher[] matchers)
{ {
Guard.NotNull(name, nameof(name)); Guard.NotNull(name, nameof(name));
Guard.NotNullOrEmpty(matchers, nameof(matchers)); Guard.NotNullOrEmpty(matchers, nameof(matchers));
_requestMatchers.Add(new RequestMessageCookieMatcher(MatchBehaviour.AcceptOnMatch, name, false, matchers)); _requestMatchers.Add(new RequestMessageCookieMatcher(MatchBehaviour.AcceptOnMatch, name, false, matchers));
return this; return this;
} }
/// <inheritdoc cref="ICookiesRequestBuilder.WithCookie(string, bool, IStringMatcher[])"/> /// <inheritdoc cref="ICookiesRequestBuilder.WithCookie(string, bool, IStringMatcher[])"/>
public IRequestBuilder WithCookie(string name, bool ignoreCase, params IStringMatcher[] matchers) public IRequestBuilder WithCookie(string name, bool ignoreCase, params IStringMatcher[] matchers)
{ {
Guard.NotNull(name, nameof(name)); Guard.NotNull(name, nameof(name));
Guard.NotNullOrEmpty(matchers, nameof(matchers)); Guard.NotNullOrEmpty(matchers, nameof(matchers));
_requestMatchers.Add(new RequestMessageCookieMatcher(MatchBehaviour.AcceptOnMatch, name, ignoreCase, matchers)); _requestMatchers.Add(new RequestMessageCookieMatcher(MatchBehaviour.AcceptOnMatch, name, ignoreCase, matchers));
return this; return this;
} }
/// <inheritdoc cref="ICookiesRequestBuilder.WithCookie(string, IStringMatcher[])"/> /// <inheritdoc cref="ICookiesRequestBuilder.WithCookie(string, IStringMatcher[])"/>
public IRequestBuilder WithCookie(string name, bool ignoreCase, MatchBehaviour matchBehaviour, params IStringMatcher[] matchers) public IRequestBuilder WithCookie(string name, bool ignoreCase, MatchBehaviour matchBehaviour, params IStringMatcher[] matchers)
{ {
Guard.NotNull(name, nameof(name)); Guard.NotNull(name, nameof(name));
Guard.NotNullOrEmpty(matchers, nameof(matchers)); Guard.NotNullOrEmpty(matchers, nameof(matchers));
_requestMatchers.Add(new RequestMessageCookieMatcher(matchBehaviour, name, ignoreCase, matchers)); _requestMatchers.Add(new RequestMessageCookieMatcher(matchBehaviour, name, ignoreCase, matchers));
return this; return this;
} }
/// <inheritdoc cref="ICookiesRequestBuilder.WithCookie(Func{IDictionary{string, string}, bool}[])"/> /// <inheritdoc cref="ICookiesRequestBuilder.WithCookie(Func{IDictionary{string, string}, bool}[])"/>
public IRequestBuilder WithCookie(params Func<IDictionary<string, string>, bool>[] funcs) public IRequestBuilder WithCookie(params Func<IDictionary<string, string>, bool>[] funcs)
{ {
Guard.NotNullOrEmpty(funcs, nameof(funcs)); Guard.NotNullOrEmpty(funcs, nameof(funcs));
_requestMatchers.Add(new RequestMessageCookieMatcher(funcs)); _requestMatchers.Add(new RequestMessageCookieMatcher(funcs));
return this; return this;
}
} }
} }

View File

@@ -7,86 +7,85 @@ using WireMock.Matchers.Request;
using WireMock.Types; using WireMock.Types;
using Stef.Validation; using Stef.Validation;
namespace WireMock.RequestBuilders namespace WireMock.RequestBuilders;
public partial class Request
{ {
public partial class Request /// <inheritdoc cref="IParamsRequestBuilder.WithParam(string, MatchBehaviour)"/>
public IRequestBuilder WithParam(string key, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch)
{ {
/// <inheritdoc cref="IParamsRequestBuilder.WithParam(string, MatchBehaviour)"/> return WithParam(key, false, matchBehaviour);
public IRequestBuilder WithParam(string key, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch) }
{
return WithParam(key, false, matchBehaviour);
}
/// <inheritdoc cref="IParamsRequestBuilder.WithParam(string, bool, MatchBehaviour)"/> /// <inheritdoc cref="IParamsRequestBuilder.WithParam(string, bool, MatchBehaviour)"/>
public IRequestBuilder WithParam(string key, bool ignoreCase, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch) public IRequestBuilder WithParam(string key, bool ignoreCase, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch)
{ {
Guard.NotNull(key); Guard.NotNull(key);
_requestMatchers.Add(new RequestMessageParamMatcher(matchBehaviour, key, ignoreCase)); _requestMatchers.Add(new RequestMessageParamMatcher(matchBehaviour, key, ignoreCase));
return this; return this;
} }
/// <inheritdoc cref="IParamsRequestBuilder.WithParam(string, string[])"/> /// <inheritdoc cref="IParamsRequestBuilder.WithParam(string, string[])"/>
public IRequestBuilder WithParam(string key, params string[] values) public IRequestBuilder WithParam(string key, params string[] values)
{ {
return WithParam(key, MatchBehaviour.AcceptOnMatch, false, values); return WithParam(key, MatchBehaviour.AcceptOnMatch, false, values);
} }
/// <inheritdoc cref="IParamsRequestBuilder.WithParam(string, bool, string[])"/> /// <inheritdoc cref="IParamsRequestBuilder.WithParam(string, bool, string[])"/>
public IRequestBuilder WithParam(string key, bool ignoreCase, params string[] values) public IRequestBuilder WithParam(string key, bool ignoreCase, params string[] values)
{ {
return WithParam(key, MatchBehaviour.AcceptOnMatch, ignoreCase, values); return WithParam(key, MatchBehaviour.AcceptOnMatch, ignoreCase, values);
} }
/// <inheritdoc cref="IParamsRequestBuilder.WithParam(string, IStringMatcher[])"/> /// <inheritdoc cref="IParamsRequestBuilder.WithParam(string, IStringMatcher[])"/>
public IRequestBuilder WithParam(string key, params IStringMatcher[] matchers) public IRequestBuilder WithParam(string key, params IStringMatcher[] matchers)
{ {
return WithParam(key, MatchBehaviour.AcceptOnMatch, false, matchers); return WithParam(key, MatchBehaviour.AcceptOnMatch, false, matchers);
} }
/// <inheritdoc cref="IParamsRequestBuilder.WithParam(string, bool, IStringMatcher[])"/> /// <inheritdoc cref="IParamsRequestBuilder.WithParam(string, bool, IStringMatcher[])"/>
public IRequestBuilder WithParam(string key, bool ignoreCase, params IStringMatcher[] matchers) public IRequestBuilder WithParam(string key, bool ignoreCase, params IStringMatcher[] matchers)
{ {
return WithParam(key, MatchBehaviour.AcceptOnMatch, ignoreCase, matchers); return WithParam(key, MatchBehaviour.AcceptOnMatch, ignoreCase, matchers);
} }
/// <inheritdoc cref="IParamsRequestBuilder.WithParam(string, MatchBehaviour, string[])"/> /// <inheritdoc cref="IParamsRequestBuilder.WithParam(string, MatchBehaviour, string[])"/>
public IRequestBuilder WithParam(string key, MatchBehaviour matchBehaviour, params string[] values) public IRequestBuilder WithParam(string key, MatchBehaviour matchBehaviour, params string[] values)
{ {
return WithParam(key, matchBehaviour, false, values); return WithParam(key, matchBehaviour, false, values);
} }
/// <inheritdoc cref="IParamsRequestBuilder.WithParam(string, MatchBehaviour, bool, string[])"/> /// <inheritdoc cref="IParamsRequestBuilder.WithParam(string, MatchBehaviour, bool, string[])"/>
public IRequestBuilder WithParam(string key, MatchBehaviour matchBehaviour, bool ignoreCase = false, params string[] values) public IRequestBuilder WithParam(string key, MatchBehaviour matchBehaviour, bool ignoreCase = false, params string[] values)
{ {
Guard.NotNull(key); Guard.NotNull(key);
_requestMatchers.Add(new RequestMessageParamMatcher(matchBehaviour, key, ignoreCase, values)); _requestMatchers.Add(new RequestMessageParamMatcher(matchBehaviour, key, ignoreCase, values));
return this; return this;
} }
/// <inheritdoc cref="IParamsRequestBuilder.WithParam(string, MatchBehaviour, IStringMatcher[])"/> /// <inheritdoc cref="IParamsRequestBuilder.WithParam(string, MatchBehaviour, IStringMatcher[])"/>
public IRequestBuilder WithParam(string key, MatchBehaviour matchBehaviour, params IStringMatcher[] matchers) public IRequestBuilder WithParam(string key, MatchBehaviour matchBehaviour, params IStringMatcher[] matchers)
{ {
return WithParam(key, matchBehaviour, false, matchers); return WithParam(key, matchBehaviour, false, matchers);
} }
/// <inheritdoc cref="IParamsRequestBuilder.WithParam(string, MatchBehaviour, bool, IStringMatcher[])"/> /// <inheritdoc cref="IParamsRequestBuilder.WithParam(string, MatchBehaviour, bool, IStringMatcher[])"/>
public IRequestBuilder WithParam(string key, MatchBehaviour matchBehaviour, bool ignoreCase, params IStringMatcher[] matchers) public IRequestBuilder WithParam(string key, MatchBehaviour matchBehaviour, bool ignoreCase, params IStringMatcher[] matchers)
{ {
Guard.NotNull(key); Guard.NotNull(key);
_requestMatchers.Add(new RequestMessageParamMatcher(matchBehaviour, key, ignoreCase, matchers)); _requestMatchers.Add(new RequestMessageParamMatcher(matchBehaviour, key, ignoreCase, matchers));
return this; return this;
} }
/// <inheritdoc cref="IParamsRequestBuilder.WithParam(Func{IDictionary{string, WireMockList{string}}, bool}[])"/> /// <inheritdoc cref="IParamsRequestBuilder.WithParam(Func{IDictionary{string, WireMockList{string}}, bool}[])"/>
public IRequestBuilder WithParam(params Func<IDictionary<string, WireMockList<string>>, bool>[] funcs) public IRequestBuilder WithParam(params Func<IDictionary<string, WireMockList<string>>, bool>[] funcs)
{ {
Guard.NotNullOrEmpty(funcs, nameof(funcs)); Guard.NotNullOrEmpty(funcs, nameof(funcs));
_requestMatchers.Add(new RequestMessageParamMatcher(funcs)); _requestMatchers.Add(new RequestMessageParamMatcher(funcs));
return this; return this;
}
} }
} }

View File

@@ -3,47 +3,46 @@ using Stef.Validation;
using WireMock.Matchers; using WireMock.Matchers;
using WireMock.Matchers.Request; using WireMock.Matchers.Request;
namespace WireMock.RequestBuilders namespace WireMock.RequestBuilders;
public partial class Request
{ {
public partial class Request /// <inheritdoc />
public IRequestBuilder WithUrl(params IStringMatcher[] matchers)
{ {
/// <inheritdoc /> return WithUrl(MatchOperator.Or, matchers);
public IRequestBuilder WithUrl(params IStringMatcher[] matchers)
{
return WithUrl(MatchOperator.Or, matchers);
}
/// <inheritdoc />
public IRequestBuilder WithUrl(MatchOperator matchOperator, params IStringMatcher[] matchers)
{
Guard.NotNullOrEmpty(matchers);
_requestMatchers.Add(new RequestMessageUrlMatcher(MatchBehaviour.AcceptOnMatch, matchOperator, matchers));
return this;
}
/// <inheritdoc />
public IRequestBuilder WithUrl(params string[] urls)
{
return WithUrl(MatchOperator.Or, urls);
}
/// <inheritdoc />
public IRequestBuilder WithUrl(MatchOperator matchOperator, params string[] urls)
{
Guard.NotNullOrEmpty(urls);
_requestMatchers.Add(new RequestMessageUrlMatcher(MatchBehaviour.AcceptOnMatch, matchOperator, urls));
return this;
}
/// <inheritdoc cref="IUrlAndPathRequestBuilder.WithUrl(Func{string, bool}[])"/>
public IRequestBuilder WithUrl(params Func<string, bool>[] funcs)
{
Guard.NotNullOrEmpty(funcs);
_requestMatchers.Add(new RequestMessageUrlMatcher(funcs));
return this;
}
} }
}
/// <inheritdoc />
public IRequestBuilder WithUrl(MatchOperator matchOperator, params IStringMatcher[] matchers)
{
Guard.NotNullOrEmpty(matchers);
_requestMatchers.Add(new RequestMessageUrlMatcher(MatchBehaviour.AcceptOnMatch, matchOperator, matchers));
return this;
}
/// <inheritdoc />
public IRequestBuilder WithUrl(params string[] urls)
{
return WithUrl(MatchOperator.Or, urls);
}
/// <inheritdoc />
public IRequestBuilder WithUrl(MatchOperator matchOperator, params string[] urls)
{
Guard.NotNullOrEmpty(urls);
_requestMatchers.Add(new RequestMessageUrlMatcher(MatchBehaviour.AcceptOnMatch, matchOperator, urls));
return this;
}
/// <inheritdoc cref="IUrlAndPathRequestBuilder.WithUrl(Func{string, bool}[])"/>
public IRequestBuilder WithUrl(params Func<string, bool>[] funcs)
{
Guard.NotNullOrEmpty(funcs);
_requestMatchers.Add(new RequestMessageUrlMatcher(funcs));
return this;
}
}

View File

@@ -63,5 +63,4 @@ public partial class Request : RequestMessageCompositeMatcher, IRequestBuilder
{ {
return _requestMatchers.OfType<T>().FirstOrDefault(func); return _requestMatchers.OfType<T>().FirstOrDefault(func);
} }
} }

View File

@@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
@@ -14,6 +15,7 @@ using WireMock.RequestBuilders;
using WireMock.ResponseBuilders; using WireMock.ResponseBuilders;
using WireMock.Settings; using WireMock.Settings;
using WireMock.Types; using WireMock.Types;
using WireMock.Util;
namespace WireMock.Serialization; namespace WireMock.Serialization;
@@ -33,7 +35,7 @@ internal class MappingConverter
settings ??= new MappingConverterSettings(); settings ??= new MappingConverterSettings();
var request = (Request)mapping.RequestMatcher; var request = (Request)mapping.RequestMatcher;
var response = (Response) mapping.Provider; var response = (Response)mapping.Provider;
var clientIPMatcher = request.GetRequestMessageMatcher<RequestMessageClientIPMatcher>(); var clientIPMatcher = request.GetRequestMessageMatcher<RequestMessageClientIPMatcher>();
var pathMatcher = request.GetRequestMessageMatcher<RequestMessagePathMatcher>(); var pathMatcher = request.GetRequestMessageMatcher<RequestMessagePathMatcher>();
@@ -115,6 +117,11 @@ internal class MappingConverter
// Guid // Guid
sb.AppendLine($" .WithGuid(\"{mapping.Guid}\")"); sb.AppendLine($" .WithGuid(\"{mapping.Guid}\")");
if (mapping.Probability != null)
{
sb.AppendLine($" .WithProbability({mapping.Probability.Value.ToString(CultureInfoUtils.CultureInfoEnUS)})");
}
// Response // Response
sb.AppendLine(" .RespondWith(Response.Create()"); sb.AppendLine(" .RespondWith(Response.Create()");
@@ -183,6 +190,7 @@ internal class MappingConverter
WhenStateIs = mapping.ExecutionConditionState, WhenStateIs = mapping.ExecutionConditionState,
SetStateTo = mapping.NextState, SetStateTo = mapping.NextState,
Data = mapping.Data, Data = mapping.Data,
Probability = mapping.Probability,
Request = new RequestModel Request = new RequestModel
{ {
Headers = headerMatchers.Any() ? headerMatchers.Select(hm => new HeaderModel Headers = headerMatchers.Any() ? headerMatchers.Select(hm => new HeaderModel

View File

@@ -189,7 +189,8 @@ internal class ProxyMappingConverter
webhooks: null, webhooks: null,
useWebhooksFireAndForget: null, useWebhooksFireAndForget: null,
timeSettings: null, timeSettings: null,
data: mapping?.Data data: mapping?.Data,
probability: null
); );
} }
} }

View File

@@ -175,5 +175,13 @@ public interface IRespondWithAProvider
/// lookup data "1" /// lookup data "1"
/// </example> /// </example>
/// </summary> /// </summary>
/// <returns>The <see cref="IRespondWithAProvider"/>.</returns>
IRespondWithAProvider WithData(object data); IRespondWithAProvider WithData(object data);
/// <summary>
/// Define the probability when this request should be matched. Value is between 0 and 1.
/// </summary>
/// <param name="probability">The probability when this request should be matched. Value is between 0 and 1.</param>
/// <returns>The <see cref="IRespondWithAProvider"/>.</returns>
IRespondWithAProvider WithProbability(double probability);
} }

View File

@@ -18,6 +18,12 @@ namespace WireMock.Server;
/// </summary> /// </summary>
internal class RespondWithAProvider : IRespondWithAProvider internal class RespondWithAProvider : IRespondWithAProvider
{ {
private readonly RegistrationCallback _registrationCallback;
private readonly IRequestMatcher _requestMatcher;
private readonly WireMockServerSettings _settings;
private readonly IDateTimeUtils _dateTimeUtils;
private readonly bool _saveToFile;
private int _priority; private int _priority;
private string? _title; private string? _title;
private string? _description; private string? _description;
@@ -26,13 +32,8 @@ internal class RespondWithAProvider : IRespondWithAProvider
private string? _nextState; private string? _nextState;
private string? _scenario; private string? _scenario;
private int _timesInSameState = 1; private int _timesInSameState = 1;
private readonly RegistrationCallback _registrationCallback;
private readonly IRequestMatcher _requestMatcher;
private readonly WireMockServerSettings _settings;
private readonly IDateTimeUtils _dateTimeUtils;
private readonly bool _saveToFile;
private bool? _useWebhookFireAndForget; private bool? _useWebhookFireAndForget;
private double? _probability;
public Guid Guid { get; private set; } public Guid Guid { get; private set; }
@@ -92,7 +93,8 @@ internal class RespondWithAProvider : IRespondWithAProvider
Webhooks, Webhooks,
_useWebhookFireAndForget, _useWebhookFireAndForget,
TimeSettings, TimeSettings,
Data); Data,
_probability);
_registrationCallback(mapping, _saveToFile); _registrationCallback(mapping, _saveToFile);
} }
@@ -285,6 +287,13 @@ internal class RespondWithAProvider : IRespondWithAProvider
return this; return this;
} }
public IRespondWithAProvider WithProbability(double probability)
{
_probability = Guard.Condition(probability, p => p is >= 0 and <= 1.0);
return this;
}
private static IWebhook InitWebhook( private static IWebhook InitWebhook(
string url, string url,
string method, string method,

View File

@@ -113,6 +113,11 @@ public partial class WireMockServer
respondProvider.WithWebhookFireAndForget(mappingModel.UseWebhooksFireAndForget.Value); respondProvider.WithWebhookFireAndForget(mappingModel.UseWebhooksFireAndForget.Value);
} }
if (mappingModel.Probability != null)
{
respondProvider.WithProbability(mappingModel.Probability.Value);
}
var responseBuilder = InitResponseBuilder(mappingModel.Response); var responseBuilder = InitResponseBuilder(mappingModel.Response);
respondProvider.RespondWith(responseBuilder); respondProvider.RespondWith(responseBuilder);

View File

@@ -0,0 +1,6 @@
namespace WireMock.Services;
internal interface IRandomizerDoubleBetween0And1
{
double Generate();
}

View File

@@ -0,0 +1,14 @@
using RandomDataGenerator.FieldOptions;
using RandomDataGenerator.Randomizers;
namespace WireMock.Services;
internal class RandomizerDoubleBetween0And1 : IRandomizerDoubleBetween0And1
{
private readonly IRandomizerNumber<double> _randomizerDoubleBetween0And1 = RandomizerFactory.GetRandomizer(new FieldOptionsDouble { Min = 0, Max = 1 });
public double Generate()
{
return _randomizerDoubleBetween0And1.Generate() ?? 0;
}
}

View File

@@ -5,6 +5,8 @@ namespace WireMock.Util;
internal static class CultureInfoUtils internal static class CultureInfoUtils
{ {
public static readonly CultureInfo CultureInfoEnUS = new("en-US");
public static CultureInfo Parse(string? value) public static CultureInfo Parse(string? value)
{ {
if (value is null) if (value is null)

View File

@@ -1,16 +1,11 @@
namespace WireMock.Org.Abstractions namespace WireMock.Org.Abstractions;
/// <summary>
/// The fault to apply (instead of a full, valid response).
/// </summary>
public static class MappingsResponseFaultConstants
{ {
/// <summary> public const string EMPTYRESPONSE = "EMPTY_RESPONSE";
/// The fault to apply (instead of a full, valid response).
/// </summary>
public static class MappingsResponseFaultConstants
{
public const string CONNECTIONRESETBYPEER = "CONNECTION_RESET_BY_PEER";
public const string EMPTYRESPONSE = "EMPTY_RESPONSE"; public const string MALFORMEDRESPONSECHUNK = "MALFORMED_RESPONSE_CHUNK";
}
public const string MALFORMEDRESPONSECHUNK = "MALFORMED_RESPONSE_CHUNK";
public const string RANDOMDATATHENCLOSE = "RANDOM_DATA_THEN_CLOSE";
}
}

View File

@@ -6,6 +6,7 @@ using WireMock.Logging;
using WireMock.Matchers.Request; using WireMock.Matchers.Request;
using WireMock.Models; using WireMock.Models;
using WireMock.Owin; using WireMock.Owin;
using WireMock.Services;
using WireMock.Util; using WireMock.Util;
using Xunit; using Xunit;
@@ -14,6 +15,8 @@ namespace WireMock.Net.Tests.Owin;
public class MappingMatcherTests public class MappingMatcherTests
{ {
private readonly Mock<IWireMockMiddlewareOptions> _optionsMock; private readonly Mock<IWireMockMiddlewareOptions> _optionsMock;
private readonly Mock<IRandomizerDoubleBetween0And1> _randomizerDoubleBetween0And1Mock;
private readonly MappingMatcher _sut; private readonly MappingMatcher _sut;
public MappingMatcherTests() public MappingMatcherTests()
@@ -29,7 +32,10 @@ public class MappingMatcherTests
loggerMock.Setup(l => l.Error(It.IsAny<string>())); loggerMock.Setup(l => l.Error(It.IsAny<string>()));
_optionsMock.Setup(o => o.Logger).Returns(loggerMock.Object); _optionsMock.Setup(o => o.Logger).Returns(loggerMock.Object);
_sut = new MappingMatcher(_optionsMock.Object); _randomizerDoubleBetween0And1Mock = new Mock<IRandomizerDoubleBetween0And1>();
_randomizerDoubleBetween0And1Mock.Setup(r => r.Generate()).Returns(0.0);
_sut = new MappingMatcher(_optionsMock.Object, _randomizerDoubleBetween0And1Mock.Object);
} }
[Fact] [Fact]
@@ -76,8 +82,8 @@ public class MappingMatcherTests
var guid2 = Guid.Parse("00000000-0000-0000-0000-000000000002"); var guid2 = Guid.Parse("00000000-0000-0000-0000-000000000002");
var mappings = InitMappings var mappings = InitMappings
( (
(guid1, new[] { 0.1 }), (guid1, new[] { 0.1 }, null),
(guid2, new[] { 1.0 }) (guid2, new[] { 1.0 }, null)
); );
_optionsMock.Setup(o => o.Mappings).Returns(mappings); _optionsMock.Setup(o => o.Mappings).Returns(mappings);
@@ -104,8 +110,8 @@ public class MappingMatcherTests
var guid2 = Guid.Parse("00000000-0000-0000-0000-000000000002"); var guid2 = Guid.Parse("00000000-0000-0000-0000-000000000002");
var mappings = InitMappings var mappings = InitMappings
( (
(guid1, new[] { 0.1 }), (guid1, new[] { 0.1 }, null),
(guid2, new[] { 0.9 }) (guid2, new[] { 0.9 }, null)
); );
_optionsMock.Setup(o => o.Mappings).Returns(mappings); _optionsMock.Setup(o => o.Mappings).Returns(mappings);
@@ -131,8 +137,8 @@ public class MappingMatcherTests
_optionsMock.SetupGet(o => o.AllowPartialMapping).Returns(true); _optionsMock.SetupGet(o => o.AllowPartialMapping).Returns(true);
var mappings = InitMappings( var mappings = InitMappings(
(guid1, new[] { 0.1 }), (guid1, new[] { 0.1 }, null),
(guid2, new[] { 0.9 }) (guid2, new[] { 0.9 }, null)
); );
_optionsMock.Setup(o => o.Mappings).Returns(mappings); _optionsMock.Setup(o => o.Mappings).Returns(mappings);
@@ -158,8 +164,8 @@ public class MappingMatcherTests
var guid1 = Guid.Parse("00000000-0000-0000-0000-000000000001"); var guid1 = Guid.Parse("00000000-0000-0000-0000-000000000001");
var guid2 = Guid.Parse("00000000-0000-0000-0000-000000000002"); var guid2 = Guid.Parse("00000000-0000-0000-0000-000000000002");
var mappings = InitMappings( var mappings = InitMappings(
(guid1, new[] { 1.0 }), (guid1, new[] { 1.0 }, null),
(guid2, new[] { 1.0, 1.0 }) (guid2, new[] { 1.0, 1.0 }, null)
); );
_optionsMock.Setup(o => o.Mappings).Returns(mappings); _optionsMock.Setup(o => o.Mappings).Returns(mappings);
@@ -178,7 +184,31 @@ public class MappingMatcherTests
result.Partial.RequestMatchResult.AverageTotalScore.Should().Be(1.0); result.Partial.RequestMatchResult.AverageTotalScore.Should().Be(1.0);
} }
private static ConcurrentDictionary<Guid, IMapping> InitMappings(params (Guid guid, double[] scores)[] matches) [Fact]
public void MappingMatcher_FindBestMatch_WhenProbabilityFailsFirst_ShouldReturnSecondMatch()
{
// Assign
var guid1 = Guid.Parse("00000000-0000-0000-0000-000000000001");
var guid2 = Guid.Parse("00000000-0000-0000-0000-000000000002");
var mappings = InitMappings
(
(guid1, new[] { 1.0 }, 1.0),
(guid2, new[] { 1.0 }, null)
);
_optionsMock.Setup(o => o.Mappings).Returns(mappings);
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "GET", "::1");
// Act
var result = _sut.FindBestMatch(request);
// Assert
result.Match.Should().NotBeNull();
result.Match!.Mapping.Guid.Should().Be(guid2);
result.Match.RequestMatchResult.AverageTotalScore.Should().Be(1.0);
}
private static ConcurrentDictionary<Guid, IMapping> InitMappings(params (Guid guid, double[] scores, double? probability)[] matches)
{ {
var mappings = new ConcurrentDictionary<Guid, IMapping>(); var mappings = new ConcurrentDictionary<Guid, IMapping>();
@@ -193,6 +223,8 @@ public class MappingMatcherTests
requestMatchResult.AddScore(typeof(object), score); requestMatchResult.AddScore(typeof(object), score);
} }
mappingMock.SetupGet(m => m.Probability).Returns(match.probability);
mappingMock.Setup(m => m.GetRequestMatchResult(It.IsAny<RequestMessage>(), It.IsAny<string>())).Returns(requestMatchResult); mappingMock.Setup(m => m.GetRequestMatchResult(It.IsAny<RequestMessage>(), It.IsAny<string>())).Returns(requestMatchResult);
mappings.TryAdd(match.guid, mappingMock.Object); mappings.TryAdd(match.guid, mappingMock.Object);

View File

@@ -177,7 +177,7 @@ public class WireMockMiddlewareTests
_mappingMock.SetupGet(m => m.Provider).Returns(responseBuilder); _mappingMock.SetupGet(m => m.Provider).Returns(responseBuilder);
_mappingMock.SetupGet(m => m.Settings).Returns(settings); _mappingMock.SetupGet(m => m.Settings).Returns(settings);
var newMappingFromProxy = new Mapping(Guid.NewGuid(), _updatedAt, string.Empty, string.Empty, null, settings, Request.Create(), Response.Create(), 0, null, null, null, null, null, false, null, null); var newMappingFromProxy = new Mapping(Guid.NewGuid(), _updatedAt, string.Empty, string.Empty, null, settings, Request.Create(), Response.Create(), 0, null, null, null, null, null, false, null, null, null);
_mappingMock.Setup(m => m.ProvideResponseAsync(It.IsAny<RequestMessage>())).ReturnsAsync((new ResponseMessage(), newMappingFromProxy)); _mappingMock.Setup(m => m.ProvideResponseAsync(It.IsAny<RequestMessage>())).ReturnsAsync((new ResponseMessage(), newMappingFromProxy));
var requestBuilder = Request.Create().UsingAnyMethod(); var requestBuilder = Request.Create().UsingAnyMethod();
@@ -231,7 +231,7 @@ public class WireMockMiddlewareTests
_mappingMock.SetupGet(m => m.Provider).Returns(responseBuilder); _mappingMock.SetupGet(m => m.Provider).Returns(responseBuilder);
_mappingMock.SetupGet(m => m.Settings).Returns(settings); _mappingMock.SetupGet(m => m.Settings).Returns(settings);
var newMappingFromProxy = new Mapping(Guid.NewGuid(), _updatedAt, "my-title", "my-description", null, settings, Request.Create(), Response.Create(), 0, null, null, null, null, null, false, null, data: null); var newMappingFromProxy = new Mapping(Guid.NewGuid(), _updatedAt, "my-title", "my-description", null, settings, Request.Create(), Response.Create(), 0, null, null, null, null, null, false, null, data: null, probability: null);
_mappingMock.Setup(m => m.ProvideResponseAsync(It.IsAny<RequestMessage>())).ReturnsAsync((new ResponseMessage(), newMappingFromProxy)); _mappingMock.Setup(m => m.ProvideResponseAsync(It.IsAny<RequestMessage>())).ReturnsAsync((new ResponseMessage(), newMappingFromProxy));
var requestBuilder = Request.Create().UsingAnyMethod(); var requestBuilder = Request.Create().UsingAnyMethod();

View File

@@ -136,7 +136,8 @@ public partial class MappingConverterTests
null, null,
false, false,
null, null,
data: null data: null,
probability: 0.3
); );
} }
} }

View File

@@ -9,6 +9,7 @@
.WithBody("b") .WithBody("b")
) )
.WithGuid("8e7b9ab7-e18e-4502-8bc9-11e6679811cc") .WithGuid("8e7b9ab7-e18e-4502-8bc9-11e6679811cc")
.WithProbability(0.3)
.RespondWith(Response.Create() .RespondWith(Response.Create()
.WithHeader("Keep-Alive)", "test") .WithHeader("Keep-Alive)", "test")
.WithBody("bbb") .WithBody("bbb")

View File

@@ -10,6 +10,7 @@ builder
.WithBody("b") .WithBody("b")
) )
.WithGuid("8e7b9ab7-e18e-4502-8bc9-11e6679811cc") .WithGuid("8e7b9ab7-e18e-4502-8bc9-11e6679811cc")
.WithProbability(0.3)
.RespondWith(Response.Create() .RespondWith(Response.Create()
.WithHeader("Keep-Alive)", "test") .WithHeader("Keep-Alive)", "test")
.WithBody("bbb") .WithBody("bbb")

View File

@@ -9,6 +9,7 @@
.WithBody("b") .WithBody("b")
) )
.WithGuid("8e7b9ab7-e18e-4502-8bc9-11e6679811cc") .WithGuid("8e7b9ab7-e18e-4502-8bc9-11e6679811cc")
.WithProbability(0.3)
.RespondWith(Response.Create() .RespondWith(Response.Create()
.WithHeader("Keep-Alive)", "test") .WithHeader("Keep-Alive)", "test")
.WithBody("bbb") .WithBody("bbb")

View File

@@ -10,6 +10,7 @@ server
.WithBody("b") .WithBody("b")
) )
.WithGuid("8e7b9ab7-e18e-4502-8bc9-11e6679811cc") .WithGuid("8e7b9ab7-e18e-4502-8bc9-11e6679811cc")
.WithProbability(0.3)
.RespondWith(Response.Create() .RespondWith(Response.Create()
.WithHeader("Keep-Alive)", "test") .WithHeader("Keep-Alive)", "test")
.WithBody("bbb") .WithBody("bbb")

View File

@@ -0,0 +1,11 @@
{
Guid: Guid_1,
UpdatedAt: DateTime_1,
Title: ,
Description: ,
Priority: 42,
Request: {},
Response: {},
UseWebhooksFireAndForget: false,
Probability: 0.4
}

View File

@@ -57,7 +57,7 @@ public partial class MappingConverterTests
} }
} }
}; };
var mapping = new Mapping(_guid, _updatedAt, string.Empty, string.Empty, null, _settings, request, response, 0, null, null, null, null, webhooks, false, null, data: null); var mapping = new Mapping(_guid, _updatedAt, string.Empty, string.Empty, null, _settings, request, response, 0, null, null, null, null, webhooks, false, null, data: null, probability: null);
// Act // Act
var model = _sut.ToMappingModel(mapping); var model = _sut.ToMappingModel(mapping);
@@ -130,7 +130,7 @@ public partial class MappingConverterTests
} }
} }
}; };
var mapping = new Mapping(_guid, _updatedAt, string.Empty, string.Empty, null, _settings, request, response, 0, null, null, null, null, webhooks, true, null, data: null); var mapping = new Mapping(_guid, _updatedAt, string.Empty, string.Empty, null, _settings, request, response, 0, null, null, null, null, webhooks, true, null, data: null, probability: null);
// Act // Act
var model = _sut.ToMappingModel(mapping); var model = _sut.ToMappingModel(mapping);
@@ -168,7 +168,7 @@ public partial class MappingConverterTests
var description = "my-description"; var description = "my-description";
var request = Request.Create(); var request = Request.Create();
var response = Response.Create(); var response = Response.Create();
var mapping = new Mapping(_guid, _updatedAt, title, description, null, _settings, request, response, 0, null, null, null, null, null, false, null, data: null); var mapping = new Mapping(_guid, _updatedAt, title, description, null, _settings, request, response, 0, null, null, null, null, null, false, null, data: null, probability: null);
// Act // Act
var model = _sut.ToMappingModel(mapping); var model = _sut.ToMappingModel(mapping);
@@ -188,7 +188,7 @@ public partial class MappingConverterTests
// Assign // Assign
var request = Request.Create(); var request = Request.Create();
var response = Response.Create().WithBodyAsJson(new { x = "x" }).WithTransformer(); var response = Response.Create().WithBodyAsJson(new { x = "x" }).WithTransformer();
var mapping = new Mapping(_guid, _updatedAt, string.Empty, string.Empty, null, _settings, request, response, 42, null, null, null, null, null, false, null, data: null); var mapping = new Mapping(_guid, _updatedAt, string.Empty, string.Empty, null, _settings, request, response, 42, null, null, null, null, null, false, null, data: null, probability: null);
// Act // Act
var model = _sut.ToMappingModel(mapping); var model = _sut.ToMappingModel(mapping);
@@ -217,7 +217,7 @@ public partial class MappingConverterTests
End = end, End = end,
TTL = ttl TTL = ttl
}; };
var mapping = new Mapping(_guid, _updatedAt, string.Empty, string.Empty, null, _settings, request, response, 42, null, null, null, null, null, false, timeSettings, data: null); var mapping = new Mapping(_guid, _updatedAt, string.Empty, string.Empty, null, _settings, request, response, 42, null, null, null, null, null, false, timeSettings, data: null, probability: null);
// Act // Act
var model = _sut.ToMappingModel(mapping); var model = _sut.ToMappingModel(mapping);
@@ -248,7 +248,7 @@ public partial class MappingConverterTests
{ {
var request = Request.Create(); var request = Request.Create();
var response = Response.Create().WithDelay(test.Delay); var response = Response.Create().WithDelay(test.Delay);
var mapping = new Mapping(Guid.NewGuid(), _updatedAt, string.Empty, string.Empty, string.Empty, _settings, request, response, 42, null, null, null, null, null, false, null, data: null); var mapping = new Mapping(Guid.NewGuid(), _updatedAt, string.Empty, string.Empty, string.Empty, _settings, request, response, 42, null, null, null, null, null, false, null, data: null, probability: null);
// Act // Act
var model = _sut.ToMappingModel(mapping); var model = _sut.ToMappingModel(mapping);
@@ -266,7 +266,7 @@ public partial class MappingConverterTests
var delay = 1000; var delay = 1000;
var request = Request.Create(); var request = Request.Create();
var response = Response.Create().WithDelay(delay); var response = Response.Create().WithDelay(delay);
var mapping = new Mapping(_guid, _updatedAt, string.Empty, string.Empty, null, _settings, request, response, 42, null, null, null, null, null, false, null, data: null); var mapping = new Mapping(_guid, _updatedAt, string.Empty, string.Empty, null, _settings, request, response, 42, null, null, null, null, null, false, null, data: null, probability: null);
// Act // Act
var model = _sut.ToMappingModel(mapping); var model = _sut.ToMappingModel(mapping);
@@ -286,7 +286,7 @@ public partial class MappingConverterTests
int minimumDelay = 1000; int minimumDelay = 1000;
var request = Request.Create(); var request = Request.Create();
var response = Response.Create().WithRandomDelay(minimumDelay); var response = Response.Create().WithRandomDelay(minimumDelay);
var mapping = new Mapping(_guid, _updatedAt, string.Empty, string.Empty, null, _settings, request, response, 42, null, null, null, null, null, false, null, data: null); var mapping = new Mapping(_guid, _updatedAt, string.Empty, string.Empty, null, _settings, request, response, 42, null, null, null, null, null, false, null, data: null, probability: null);
// Act // Act
var model = _sut.ToMappingModel(mapping); var model = _sut.ToMappingModel(mapping);
@@ -309,7 +309,7 @@ public partial class MappingConverterTests
int maximumDelay = 2000; int maximumDelay = 2000;
var request = Request.Create(); var request = Request.Create();
var response = Response.Create().WithRandomDelay(minimumDelay, maximumDelay); var response = Response.Create().WithRandomDelay(minimumDelay, maximumDelay);
var mapping = new Mapping(_guid, _updatedAt, string.Empty, string.Empty, null, _settings, request, response, 42, null, null, null, null, null, false, null, data: null); var mapping = new Mapping(_guid, _updatedAt, string.Empty, string.Empty, null, _settings, request, response, 42, null, null, null, null, null, false, null, data: null, probability: null);
// Act // Act
var model = _sut.ToMappingModel(mapping); var model = _sut.ToMappingModel(mapping);
@@ -323,5 +323,25 @@ public partial class MappingConverterTests
// Verify // Verify
return Verifier.Verify(model); return Verifier.Verify(model);
} }
[Fact]
public Task ToMappingModel_WithProbability_ReturnsCorrectModel()
{
// Assign
double probability = 0.4;
var request = Request.Create();
var response = Response.Create();
var mapping = new Mapping(_guid, _updatedAt, string.Empty, string.Empty, null, _settings, request, response, 42, null, null, null, null, null, false, null, data: null, probability: probability);
// Act
var model = _sut.ToMappingModel(mapping);
// Assert
model.Should().NotBeNull();
model.Probability.Should().Be(0.4);
// Verify
return Verifier.Verify(model);
}
} }
#endif #endif

View File

@@ -0,0 +1,37 @@
using System;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using WireMock.RequestBuilders;
using WireMock.ResponseBuilders;
using WireMock.Server;
using Xunit;
namespace WireMock.Net.Tests;
public partial class WireMockServerTests
{
[Fact]
public async Task WireMockServer_WithProbability()
{
// Arrange
var server = WireMockServer.Start();
server
.Given(Request.Create().UsingGet().WithPath("/foo"))
.WithProbability(0.5)
.RespondWith(Response.Create().WithStatusCode(200));
server
.Given(Request.Create().UsingGet().WithPath("/foo"))
.RespondWith(Response.Create().WithStatusCode(500));
// Act
var requestUri = new Uri($"http://localhost:{server.Port}/foo");
var response = await server.CreateClient().GetAsync(requestUri).ConfigureAwait(false);
// Assert
Assert.True(new[] { HttpStatusCode.OK, HttpStatusCode.InternalServerError }.Contains(response.StatusCode));
server.Stop();
}
}