diff --git a/src/WireMock.Net.Abstractions/Admin/Mappings/FaultModel.cs b/src/WireMock.Net.Abstractions/Admin/Mappings/FaultModel.cs index a55728fe..622aeecc 100644 --- a/src/WireMock.Net.Abstractions/Admin/Mappings/FaultModel.cs +++ b/src/WireMock.Net.Abstractions/Admin/Mappings/FaultModel.cs @@ -1,19 +1,18 @@ -namespace WireMock.Admin.Mappings +namespace WireMock.Admin.Mappings; + +/// +/// Fault Model +/// +[FluentBuilder.AutoGenerateBuilder] +public class FaultModel { /// - /// Fault Model + /// Gets or sets the fault. Can be null, "", NONE, EMPTY_RESPONSE or MALFORMED_RESPONSE_CHUNK. /// - [FluentBuilder.AutoGenerateBuilder] - public class FaultModel - { - /// - /// Gets or sets the fault. Can be null, "", NONE, EMPTY_RESPONSE, MALFORMED_RESPONSE_CHUNK or RANDOM_DATA_THEN_CLOSE. - /// - public string Type { get; set; } + public string? Type { get; set; } - /// - /// Gets or sets the fault percentage. - /// - public double? Percentage { get; set; } - } + /// + /// Gets or sets the fault percentage. + /// + public double? Percentage { get; set; } } \ No newline at end of file diff --git a/src/WireMock.Net.Abstractions/Admin/Mappings/MappingModel.cs b/src/WireMock.Net.Abstractions/Admin/Mappings/MappingModel.cs index d7932f4f..9f6061a2 100644 --- a/src/WireMock.Net.Abstractions/Admin/Mappings/MappingModel.cs +++ b/src/WireMock.Net.Abstractions/Admin/Mappings/MappingModel.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using WireMock.Models; namespace WireMock.Admin.Mappings; @@ -94,4 +93,9 @@ public class MappingModel /// /// public object? Data { get; set; } + + /// + /// The probability when this request should be matched. Value is between 0 and 1. [Optional] + /// + public double? Probability { get; set; } } \ No newline at end of file diff --git a/src/WireMock.Net.Abstractions/ResponseBuilders/FaultType.cs b/src/WireMock.Net.Abstractions/ResponseBuilders/FaultType.cs index 5c922868..d65951a7 100644 --- a/src/WireMock.Net.Abstractions/ResponseBuilders/FaultType.cs +++ b/src/WireMock.Net.Abstractions/ResponseBuilders/FaultType.cs @@ -1,23 +1,23 @@ -namespace WireMock.ResponseBuilders +// ReSharper disable InconsistentNaming +namespace WireMock.ResponseBuilders; + +/// +/// The FaultType enumeration +/// +public enum FaultType { /// - /// The FaultType enumeration + /// No Fault /// - public enum FaultType - { - /// - /// No Fault - /// - NONE, + NONE, - /// - /// Return a completely empty response. - /// - EMPTY_RESPONSE, + /// + /// Return a completely empty response. + /// + EMPTY_RESPONSE, - /// - /// Send a defined status header, then garbage, then close the connection. - /// - MALFORMED_RESPONSE_CHUNK - } + /// + /// Send a defined status header, then garbage, then close the connection. + /// + MALFORMED_RESPONSE_CHUNK } \ No newline at end of file diff --git a/src/WireMock.Net/IMapping.cs b/src/WireMock.Net/IMapping.cs index b53caa0a..20b3e947 100644 --- a/src/WireMock.Net/IMapping.cs +++ b/src/WireMock.Net/IMapping.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.Threading.Tasks; using WireMock.Matchers.Request; using WireMock.Models; @@ -132,6 +131,11 @@ public interface IMapping /// object? Data { get; } + /// + /// The probability when this request should be matched. Value is between 0 and 1. [Optional] + /// + double? Probability { get; } + /// /// ProvideResponseAsync /// diff --git a/src/WireMock.Net/Json/DynamicPropertyWithValue.cs b/src/WireMock.Net/Json/DynamicPropertyWithValue.cs index 55219734..9cf2bd69 100644 --- a/src/WireMock.Net/Json/DynamicPropertyWithValue.cs +++ b/src/WireMock.Net/Json/DynamicPropertyWithValue.cs @@ -4,7 +4,7 @@ using System.Linq.Dynamic.Core; namespace WireMock.Json; -public class DynamicPropertyWithValue : DynamicProperty +internal class DynamicPropertyWithValue : DynamicProperty { public object? Value { get; } diff --git a/src/WireMock.Net/Mapping.cs b/src/WireMock.Net/Mapping.cs index 6323fbe2..b678780a 100644 --- a/src/WireMock.Net/Mapping.cs +++ b/src/WireMock.Net/Mapping.cs @@ -75,6 +75,9 @@ public class Mapping : IMapping /// public object? Data { get; } + /// + public double? Probability { get; } + /// /// Initializes a new instance of the class. /// @@ -95,6 +98,7 @@ public class Mapping : IMapping /// Use Fire and Forget for the defined webhook(s). [Optional] /// The TimeSettings. [Optional] /// The data object. [Optional] + /// Define the probability when this request should be matched. [Optional] public Mapping( Guid guid, DateTime updatedAt, @@ -112,7 +116,8 @@ public class Mapping : IMapping IWebhook[]? webhooks, bool? useWebhooksFireAndForget, ITimeSettings? timeSettings, - object? data) + object? data, + double? probability) { Guid = guid; UpdatedAt = updatedAt; @@ -131,6 +136,7 @@ public class Mapping : IMapping UseWebhooksFireAndForget = useWebhooksFireAndForget; TimeSettings = timeSettings; Data = data; + Probability = probability; } /// diff --git a/src/WireMock.Net/MappingBuilder.cs b/src/WireMock.Net/MappingBuilder.cs index 66ad1d4b..da6f479f 100644 --- a/src/WireMock.Net/MappingBuilder.cs +++ b/src/WireMock.Net/MappingBuilder.cs @@ -69,12 +69,7 @@ public class MappingBuilder : IMappingBuilder /// public MappingModel[] GetMappings() { - return GetMappingsInternal().Select(_mappingConverter.ToMappingModel).ToArray(); - } - - internal IMapping[] GetMappingsInternal() - { - return _options.Mappings.Values.ToArray().Where(m => !m.IsAdminInterface).ToArray(); + return GetNonAdminMappings().Select(_mappingConverter.ToMappingModel).ToArray(); } /// @@ -86,7 +81,7 @@ public class MappingBuilder : IMappingBuilder /// 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) { return null; @@ -101,7 +96,7 @@ public class MappingBuilder : IMappingBuilder { var sb = new StringBuilder(); bool addStart = true; - foreach (var mapping in GetMappingsInternal()) + foreach (var mapping in GetNonAdminMappings()) { sb.AppendLine(_mappingConverter.ToCSharpCode(mapping, new MappingConverterSettings { AddStart = addStart, ConverterType = converterType })); @@ -123,7 +118,7 @@ public class MappingBuilder : IMappingBuilder /// public void SaveMappingsToFolder(string? folder) { - foreach (var mapping in GetNonAdminMappings().Where(m => !m.IsAdminInterface)) + foreach (var mapping in GetNonAdminMappings()) { _mappingToFileSaver.SaveMappingToFile(mapping, folder); } @@ -131,7 +126,7 @@ public class MappingBuilder : IMappingBuilder 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) diff --git a/src/WireMock.Net/Matchers/Request/RequestMessageCompositeMatcher.cs b/src/WireMock.Net/Matchers/Request/RequestMessageCompositeMatcher.cs index 1c8dfb20..0ecd738e 100644 --- a/src/WireMock.Net/Matchers/Request/RequestMessageCompositeMatcher.cs +++ b/src/WireMock.Net/Matchers/Request/RequestMessageCompositeMatcher.cs @@ -1,6 +1,5 @@ using System.Collections.Generic; using System.Linq; -using JetBrains.Annotations; using Stef.Validation; namespace WireMock.Matchers.Request; @@ -25,9 +24,9 @@ public abstract class RequestMessageCompositeMatcher : IRequestMatcher /// /// The request matchers. /// The CompositeMatcherType type (Defaults to 'And') - protected RequestMessageCompositeMatcher([NotNull] IEnumerable requestMatchers, CompositeMatcherType type = CompositeMatcherType.And) + protected RequestMessageCompositeMatcher(IEnumerable requestMatchers, CompositeMatcherType type = CompositeMatcherType.And) { - Guard.NotNull(requestMatchers, nameof(requestMatchers)); + Guard.NotNull(requestMatchers); _type = type; RequestMatchers = requestMatchers; diff --git a/src/WireMock.Net/Owin/AspNetCoreSelfHost.cs b/src/WireMock.Net/Owin/AspNetCoreSelfHost.cs index 151c7d1b..60fefacd 100644 --- a/src/WireMock.Net/Owin/AspNetCoreSelfHost.cs +++ b/src/WireMock.Net/Owin/AspNetCoreSelfHost.cs @@ -11,6 +11,7 @@ using Microsoft.Extensions.DependencyInjection; using Stef.Validation; using WireMock.Logging; using WireMock.Owin.Mappers; +using WireMock.Services; using WireMock.Util; namespace WireMock.Owin @@ -66,6 +67,7 @@ namespace WireMock.Owin { services.AddSingleton(_wireMockMiddlewareOptions); services.AddSingleton(); + services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); diff --git a/src/WireMock.Net/Owin/Mappers/OwinResponseMapper.cs b/src/WireMock.Net/Owin/Mappers/OwinResponseMapper.cs index 3f0e1394..44857333 100644 --- a/src/WireMock.Net/Owin/Mappers/OwinResponseMapper.cs +++ b/src/WireMock.Net/Owin/Mappers/OwinResponseMapper.cs @@ -62,11 +62,11 @@ namespace WireMock.Owin.Mappers switch (responseMessage.FaultType) { case FaultType.EMPTY_RESPONSE: - bytes = IsFault(responseMessage) ? new byte[0] : GetNormalBody(responseMessage); + bytes = IsFault(responseMessage) ? EmptyArray.Value : GetNormalBody(responseMessage); break; case FaultType.MALFORMED_RESPONSE_CHUNK: - bytes = GetNormalBody(responseMessage) ?? new byte[0]; + bytes = GetNormalBody(responseMessage) ?? EmptyArray.Value; if (IsFault(responseMessage)) { bytes = bytes.Take(bytes.Length / 2).Union(_randomizerBytes.Generate()).ToArray(); @@ -87,7 +87,7 @@ namespace WireMock.Owin.Mappers case { } typeAsString when typeAsString == typeof(string): // 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); break; diff --git a/src/WireMock.Net/Owin/MappingMatcher.cs b/src/WireMock.Net/Owin/MappingMatcher.cs index 6360e6a6..d979979c 100644 --- a/src/WireMock.Net/Owin/MappingMatcher.cs +++ b/src/WireMock.Net/Owin/MappingMatcher.cs @@ -1,18 +1,21 @@ using System; using System.Collections.Generic; using System.Linq; -using WireMock.Extensions; using Stef.Validation; +using WireMock.Extensions; +using WireMock.Services; namespace WireMock.Owin; internal class MappingMatcher : IMappingMatcher { private readonly IWireMockMiddlewareOptions _options; + private readonly IRandomizerDoubleBetween0And1 _randomizerDoubleBetween0And1; - public MappingMatcher(IWireMockMiddlewareOptions options) + public MappingMatcher(IWireMockMiddlewareOptions options, IRandomizerDoubleBetween0And1 randomizerDoubleBetween0And1) { _options = Guard.NotNull(options); + _randomizerDoubleBetween0And1 = Guard.NotNull(randomizerDoubleBetween0And1); } public (MappingMatcherResult? Match, MappingMatcherResult? Partial) FindBestMatch(RequestMessage request) @@ -21,7 +24,12 @@ internal class MappingMatcher : IMappingMatcher var possibleMappings = new List(); - 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 { diff --git a/src/WireMock.Net/Owin/OwinSelfHost.cs b/src/WireMock.Net/Owin/OwinSelfHost.cs index 58992ff1..7f4dbd3e 100644 --- a/src/WireMock.Net/Owin/OwinSelfHost.cs +++ b/src/WireMock.Net/Owin/OwinSelfHost.cs @@ -9,6 +9,9 @@ using JetBrains.Annotations; using WireMock.Logging; using WireMock.Owin.Mappers; using Stef.Validation; +using RandomDataGenerator.FieldOptions; +using RandomDataGenerator.Randomizers; +using WireMock.Services; namespace WireMock.Owin; @@ -70,7 +73,7 @@ internal class OwinSelfHost : IOwinSelfHost { var requestMapper = new OwinRequestMapper(); var responseMapper = new OwinResponseMapper(_options); - var matcher = new MappingMatcher(_options); + var matcher = new MappingMatcher(_options, new RandomizerDoubleBetween0And1()); Action startup = app => { diff --git a/src/WireMock.Net/RequestBuilders/IRequestBuilder.cs b/src/WireMock.Net/RequestBuilders/IRequestBuilder.cs index 3662a38a..75303905 100644 --- a/src/WireMock.Net/RequestBuilders/IRequestBuilder.cs +++ b/src/WireMock.Net/RequestBuilders/IRequestBuilder.cs @@ -1,9 +1,8 @@ -namespace WireMock.RequestBuilders +namespace WireMock.RequestBuilders; + +/// +/// IRequestBuilder +/// +public interface IRequestBuilder : IClientIPRequestBuilder { - /// - /// IRequestBuilder - /// - public interface IRequestBuilder : IClientIPRequestBuilder - { - } } \ No newline at end of file diff --git a/src/WireMock.Net/RequestBuilders/Request.WithCookies.cs b/src/WireMock.Net/RequestBuilders/Request.WithCookies.cs index 215b9887..c29b4595 100644 --- a/src/WireMock.Net/RequestBuilders/Request.WithCookies.cs +++ b/src/WireMock.Net/RequestBuilders/Request.WithCookies.cs @@ -1,82 +1,81 @@ -using System; +using System; using System.Collections.Generic; using WireMock.Matchers; using WireMock.Matchers.Request; using Stef.Validation; -namespace WireMock.RequestBuilders +namespace WireMock.RequestBuilders; + +public partial class Request { - public partial class Request + /// + public IRequestBuilder WithCookie(string name, string pattern, MatchBehaviour matchBehaviour) { - /// - public IRequestBuilder WithCookie(string name, string pattern, MatchBehaviour matchBehaviour) - { - return WithCookie(name, pattern, true, matchBehaviour); - } + return WithCookie(name, pattern, true, matchBehaviour); + } - /// - public IRequestBuilder WithCookie(string name, string pattern, bool ignoreCase = true, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch) - { - Guard.NotNull(name, nameof(name)); - Guard.NotNull(pattern, nameof(pattern)); + /// + public IRequestBuilder WithCookie(string name, string pattern, bool ignoreCase = true, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch) + { + Guard.NotNull(name, nameof(name)); + Guard.NotNull(pattern, nameof(pattern)); - _requestMatchers.Add(new RequestMessageCookieMatcher(matchBehaviour, name, pattern, ignoreCase)); - return this; - } + _requestMatchers.Add(new RequestMessageCookieMatcher(matchBehaviour, name, pattern, ignoreCase)); + return this; + } - /// - public IRequestBuilder WithCookie(string name, string[] patterns, MatchBehaviour matchBehaviour) - { - return WithCookie(name, patterns, true, matchBehaviour); - } + /// + public IRequestBuilder WithCookie(string name, string[] patterns, MatchBehaviour matchBehaviour) + { + return WithCookie(name, patterns, true, matchBehaviour); + } - /// - public IRequestBuilder WithCookie(string name, string[] patterns, bool ignoreCase = true, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch) - { - Guard.NotNull(name, nameof(name)); - Guard.NotNull(patterns, nameof(patterns)); + /// + public IRequestBuilder WithCookie(string name, string[] patterns, bool ignoreCase = true, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch) + { + Guard.NotNull(name, nameof(name)); + Guard.NotNull(patterns, nameof(patterns)); - _requestMatchers.Add(new RequestMessageCookieMatcher(matchBehaviour, name, ignoreCase, patterns)); - return this; - } + _requestMatchers.Add(new RequestMessageCookieMatcher(matchBehaviour, name, ignoreCase, patterns)); + return this; + } - /// - public IRequestBuilder WithCookie(string name, params IStringMatcher[] matchers) - { - Guard.NotNull(name, nameof(name)); - Guard.NotNullOrEmpty(matchers, nameof(matchers)); + /// + public IRequestBuilder WithCookie(string name, params IStringMatcher[] matchers) + { + Guard.NotNull(name, nameof(name)); + Guard.NotNullOrEmpty(matchers, nameof(matchers)); - _requestMatchers.Add(new RequestMessageCookieMatcher(MatchBehaviour.AcceptOnMatch, name, false, matchers)); - return this; - } + _requestMatchers.Add(new RequestMessageCookieMatcher(MatchBehaviour.AcceptOnMatch, name, false, matchers)); + return this; + } - /// - public IRequestBuilder WithCookie(string name, bool ignoreCase, params IStringMatcher[] matchers) - { - Guard.NotNull(name, nameof(name)); - Guard.NotNullOrEmpty(matchers, nameof(matchers)); + /// + public IRequestBuilder WithCookie(string name, bool ignoreCase, params IStringMatcher[] matchers) + { + Guard.NotNull(name, nameof(name)); + Guard.NotNullOrEmpty(matchers, nameof(matchers)); - _requestMatchers.Add(new RequestMessageCookieMatcher(MatchBehaviour.AcceptOnMatch, name, ignoreCase, matchers)); - return this; - } + _requestMatchers.Add(new RequestMessageCookieMatcher(MatchBehaviour.AcceptOnMatch, name, ignoreCase, matchers)); + return this; + } - /// - public IRequestBuilder WithCookie(string name, bool ignoreCase, MatchBehaviour matchBehaviour, params IStringMatcher[] matchers) - { - Guard.NotNull(name, nameof(name)); - Guard.NotNullOrEmpty(matchers, nameof(matchers)); + /// + public IRequestBuilder WithCookie(string name, bool ignoreCase, MatchBehaviour matchBehaviour, params IStringMatcher[] matchers) + { + Guard.NotNull(name, nameof(name)); + Guard.NotNullOrEmpty(matchers, nameof(matchers)); - _requestMatchers.Add(new RequestMessageCookieMatcher(matchBehaviour, name, ignoreCase, matchers)); - return this; - } + _requestMatchers.Add(new RequestMessageCookieMatcher(matchBehaviour, name, ignoreCase, matchers)); + return this; + } - /// - public IRequestBuilder WithCookie(params Func, bool>[] funcs) - { - Guard.NotNullOrEmpty(funcs, nameof(funcs)); + /// + public IRequestBuilder WithCookie(params Func, bool>[] funcs) + { + Guard.NotNullOrEmpty(funcs, nameof(funcs)); - _requestMatchers.Add(new RequestMessageCookieMatcher(funcs)); - return this; - } + _requestMatchers.Add(new RequestMessageCookieMatcher(funcs)); + return this; } } \ No newline at end of file diff --git a/src/WireMock.Net/RequestBuilders/Request.WithParam.cs b/src/WireMock.Net/RequestBuilders/Request.WithParam.cs index 06a925e6..5ce3034c 100644 --- a/src/WireMock.Net/RequestBuilders/Request.WithParam.cs +++ b/src/WireMock.Net/RequestBuilders/Request.WithParam.cs @@ -7,86 +7,85 @@ using WireMock.Matchers.Request; using WireMock.Types; using Stef.Validation; -namespace WireMock.RequestBuilders +namespace WireMock.RequestBuilders; + +public partial class Request { - public partial class Request + /// + public IRequestBuilder WithParam(string key, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch) { - /// - public IRequestBuilder WithParam(string key, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch) - { - return WithParam(key, false, matchBehaviour); - } + return WithParam(key, false, matchBehaviour); + } - /// - public IRequestBuilder WithParam(string key, bool ignoreCase, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch) - { - Guard.NotNull(key); + /// + public IRequestBuilder WithParam(string key, bool ignoreCase, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch) + { + Guard.NotNull(key); - _requestMatchers.Add(new RequestMessageParamMatcher(matchBehaviour, key, ignoreCase)); - return this; - } + _requestMatchers.Add(new RequestMessageParamMatcher(matchBehaviour, key, ignoreCase)); + return this; + } - /// - public IRequestBuilder WithParam(string key, params string[] values) - { - return WithParam(key, MatchBehaviour.AcceptOnMatch, false, values); - } + /// + public IRequestBuilder WithParam(string key, params string[] values) + { + return WithParam(key, MatchBehaviour.AcceptOnMatch, false, values); + } - /// - public IRequestBuilder WithParam(string key, bool ignoreCase, params string[] values) - { - return WithParam(key, MatchBehaviour.AcceptOnMatch, ignoreCase, values); - } + /// + public IRequestBuilder WithParam(string key, bool ignoreCase, params string[] values) + { + return WithParam(key, MatchBehaviour.AcceptOnMatch, ignoreCase, values); + } - /// - public IRequestBuilder WithParam(string key, params IStringMatcher[] matchers) - { - return WithParam(key, MatchBehaviour.AcceptOnMatch, false, matchers); - } + /// + public IRequestBuilder WithParam(string key, params IStringMatcher[] matchers) + { + return WithParam(key, MatchBehaviour.AcceptOnMatch, false, matchers); + } - /// - public IRequestBuilder WithParam(string key, bool ignoreCase, params IStringMatcher[] matchers) - { - return WithParam(key, MatchBehaviour.AcceptOnMatch, ignoreCase, matchers); - } + /// + public IRequestBuilder WithParam(string key, bool ignoreCase, params IStringMatcher[] matchers) + { + return WithParam(key, MatchBehaviour.AcceptOnMatch, ignoreCase, matchers); + } - /// - public IRequestBuilder WithParam(string key, MatchBehaviour matchBehaviour, params string[] values) - { - return WithParam(key, matchBehaviour, false, values); - } + /// + public IRequestBuilder WithParam(string key, MatchBehaviour matchBehaviour, params string[] values) + { + return WithParam(key, matchBehaviour, false, values); + } - /// - public IRequestBuilder WithParam(string key, MatchBehaviour matchBehaviour, bool ignoreCase = false, params string[] values) - { - Guard.NotNull(key); + /// + public IRequestBuilder WithParam(string key, MatchBehaviour matchBehaviour, bool ignoreCase = false, params string[] values) + { + Guard.NotNull(key); - _requestMatchers.Add(new RequestMessageParamMatcher(matchBehaviour, key, ignoreCase, values)); - return this; - } + _requestMatchers.Add(new RequestMessageParamMatcher(matchBehaviour, key, ignoreCase, values)); + return this; + } - /// - public IRequestBuilder WithParam(string key, MatchBehaviour matchBehaviour, params IStringMatcher[] matchers) - { - return WithParam(key, matchBehaviour, false, matchers); - } + /// + public IRequestBuilder WithParam(string key, MatchBehaviour matchBehaviour, params IStringMatcher[] matchers) + { + return WithParam(key, matchBehaviour, false, matchers); + } - /// - public IRequestBuilder WithParam(string key, MatchBehaviour matchBehaviour, bool ignoreCase, params IStringMatcher[] matchers) - { - Guard.NotNull(key); + /// + public IRequestBuilder WithParam(string key, MatchBehaviour matchBehaviour, bool ignoreCase, params IStringMatcher[] matchers) + { + Guard.NotNull(key); - _requestMatchers.Add(new RequestMessageParamMatcher(matchBehaviour, key, ignoreCase, matchers)); - return this; - } + _requestMatchers.Add(new RequestMessageParamMatcher(matchBehaviour, key, ignoreCase, matchers)); + return this; + } - /// - public IRequestBuilder WithParam(params Func>, bool>[] funcs) - { - Guard.NotNullOrEmpty(funcs, nameof(funcs)); + /// + public IRequestBuilder WithParam(params Func>, bool>[] funcs) + { + Guard.NotNullOrEmpty(funcs, nameof(funcs)); - _requestMatchers.Add(new RequestMessageParamMatcher(funcs)); - return this; - } + _requestMatchers.Add(new RequestMessageParamMatcher(funcs)); + return this; } } \ No newline at end of file diff --git a/src/WireMock.Net/RequestBuilders/Request.WithUrl.cs b/src/WireMock.Net/RequestBuilders/Request.WithUrl.cs index 6954623d..7f811e93 100644 --- a/src/WireMock.Net/RequestBuilders/Request.WithUrl.cs +++ b/src/WireMock.Net/RequestBuilders/Request.WithUrl.cs @@ -3,47 +3,46 @@ using Stef.Validation; using WireMock.Matchers; using WireMock.Matchers.Request; -namespace WireMock.RequestBuilders +namespace WireMock.RequestBuilders; + +public partial class Request { - public partial class Request + /// + public IRequestBuilder WithUrl(params IStringMatcher[] matchers) { - /// - public IRequestBuilder WithUrl(params IStringMatcher[] matchers) - { - return WithUrl(MatchOperator.Or, matchers); - } - - /// - public IRequestBuilder WithUrl(MatchOperator matchOperator, params IStringMatcher[] matchers) - { - Guard.NotNullOrEmpty(matchers); - - _requestMatchers.Add(new RequestMessageUrlMatcher(MatchBehaviour.AcceptOnMatch, matchOperator, matchers)); - return this; - } - - /// - public IRequestBuilder WithUrl(params string[] urls) - { - return WithUrl(MatchOperator.Or, urls); - } - - /// - public IRequestBuilder WithUrl(MatchOperator matchOperator, params string[] urls) - { - Guard.NotNullOrEmpty(urls); - - _requestMatchers.Add(new RequestMessageUrlMatcher(MatchBehaviour.AcceptOnMatch, matchOperator, urls)); - return this; - } - - /// - public IRequestBuilder WithUrl(params Func[] funcs) - { - Guard.NotNullOrEmpty(funcs); - - _requestMatchers.Add(new RequestMessageUrlMatcher(funcs)); - return this; - } + return WithUrl(MatchOperator.Or, matchers); } -} + + /// + public IRequestBuilder WithUrl(MatchOperator matchOperator, params IStringMatcher[] matchers) + { + Guard.NotNullOrEmpty(matchers); + + _requestMatchers.Add(new RequestMessageUrlMatcher(MatchBehaviour.AcceptOnMatch, matchOperator, matchers)); + return this; + } + + /// + public IRequestBuilder WithUrl(params string[] urls) + { + return WithUrl(MatchOperator.Or, urls); + } + + /// + public IRequestBuilder WithUrl(MatchOperator matchOperator, params string[] urls) + { + Guard.NotNullOrEmpty(urls); + + _requestMatchers.Add(new RequestMessageUrlMatcher(MatchBehaviour.AcceptOnMatch, matchOperator, urls)); + return this; + } + + /// + public IRequestBuilder WithUrl(params Func[] funcs) + { + Guard.NotNullOrEmpty(funcs); + + _requestMatchers.Add(new RequestMessageUrlMatcher(funcs)); + return this; + } +} \ No newline at end of file diff --git a/src/WireMock.Net/RequestBuilders/Request.cs b/src/WireMock.Net/RequestBuilders/Request.cs index 189c4a20..10255d5b 100644 --- a/src/WireMock.Net/RequestBuilders/Request.cs +++ b/src/WireMock.Net/RequestBuilders/Request.cs @@ -63,5 +63,4 @@ public partial class Request : RequestMessageCompositeMatcher, IRequestBuilder { return _requestMatchers.OfType().FirstOrDefault(func); } - } \ No newline at end of file diff --git a/src/WireMock.Net/Serialization/MappingConverter.cs b/src/WireMock.Net/Serialization/MappingConverter.cs index cba301c4..cc510da4 100644 --- a/src/WireMock.Net/Serialization/MappingConverter.cs +++ b/src/WireMock.Net/Serialization/MappingConverter.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.Linq; using System.Text; using System.Threading; @@ -14,6 +15,7 @@ using WireMock.RequestBuilders; using WireMock.ResponseBuilders; using WireMock.Settings; using WireMock.Types; +using WireMock.Util; namespace WireMock.Serialization; @@ -33,7 +35,7 @@ internal class MappingConverter settings ??= new MappingConverterSettings(); var request = (Request)mapping.RequestMatcher; - var response = (Response) mapping.Provider; + var response = (Response)mapping.Provider; var clientIPMatcher = request.GetRequestMessageMatcher(); var pathMatcher = request.GetRequestMessageMatcher(); @@ -115,6 +117,11 @@ internal class MappingConverter // Guid sb.AppendLine($" .WithGuid(\"{mapping.Guid}\")"); + if (mapping.Probability != null) + { + sb.AppendLine($" .WithProbability({mapping.Probability.Value.ToString(CultureInfoUtils.CultureInfoEnUS)})"); + } + // Response sb.AppendLine(" .RespondWith(Response.Create()"); @@ -183,6 +190,7 @@ internal class MappingConverter WhenStateIs = mapping.ExecutionConditionState, SetStateTo = mapping.NextState, Data = mapping.Data, + Probability = mapping.Probability, Request = new RequestModel { Headers = headerMatchers.Any() ? headerMatchers.Select(hm => new HeaderModel diff --git a/src/WireMock.Net/Serialization/ProxyMappingConverter.cs b/src/WireMock.Net/Serialization/ProxyMappingConverter.cs index 483cf294..0c5b2e53 100644 --- a/src/WireMock.Net/Serialization/ProxyMappingConverter.cs +++ b/src/WireMock.Net/Serialization/ProxyMappingConverter.cs @@ -189,7 +189,8 @@ internal class ProxyMappingConverter webhooks: null, useWebhooksFireAndForget: null, timeSettings: null, - data: mapping?.Data + data: mapping?.Data, + probability: null ); } } \ No newline at end of file diff --git a/src/WireMock.Net/Server/IRespondWithAProvider.cs b/src/WireMock.Net/Server/IRespondWithAProvider.cs index 2f81df7e..b62840b5 100644 --- a/src/WireMock.Net/Server/IRespondWithAProvider.cs +++ b/src/WireMock.Net/Server/IRespondWithAProvider.cs @@ -175,5 +175,13 @@ public interface IRespondWithAProvider /// lookup data "1" /// /// + /// The . IRespondWithAProvider WithData(object data); + + /// + /// Define the probability when this request should be matched. Value is between 0 and 1. + /// + /// The probability when this request should be matched. Value is between 0 and 1. + /// The . + IRespondWithAProvider WithProbability(double probability); } \ No newline at end of file diff --git a/src/WireMock.Net/Server/RespondWithAProvider.cs b/src/WireMock.Net/Server/RespondWithAProvider.cs index dc658183..7b7740fa 100644 --- a/src/WireMock.Net/Server/RespondWithAProvider.cs +++ b/src/WireMock.Net/Server/RespondWithAProvider.cs @@ -18,6 +18,12 @@ namespace WireMock.Server; /// 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 string? _title; private string? _description; @@ -26,13 +32,8 @@ internal class RespondWithAProvider : IRespondWithAProvider private string? _nextState; private string? _scenario; 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 double? _probability; public Guid Guid { get; private set; } @@ -92,7 +93,8 @@ internal class RespondWithAProvider : IRespondWithAProvider Webhooks, _useWebhookFireAndForget, TimeSettings, - Data); + Data, + _probability); _registrationCallback(mapping, _saveToFile); } @@ -285,6 +287,13 @@ internal class RespondWithAProvider : IRespondWithAProvider return this; } + public IRespondWithAProvider WithProbability(double probability) + { + _probability = Guard.Condition(probability, p => p is >= 0 and <= 1.0); + + return this; + } + private static IWebhook InitWebhook( string url, string method, diff --git a/src/WireMock.Net/Server/WireMockServer.ConvertMapping.cs b/src/WireMock.Net/Server/WireMockServer.ConvertMapping.cs index 3786d839..8b5e97e9 100644 --- a/src/WireMock.Net/Server/WireMockServer.ConvertMapping.cs +++ b/src/WireMock.Net/Server/WireMockServer.ConvertMapping.cs @@ -113,6 +113,11 @@ public partial class WireMockServer respondProvider.WithWebhookFireAndForget(mappingModel.UseWebhooksFireAndForget.Value); } + if (mappingModel.Probability != null) + { + respondProvider.WithProbability(mappingModel.Probability.Value); + } + var responseBuilder = InitResponseBuilder(mappingModel.Response); respondProvider.RespondWith(responseBuilder); diff --git a/src/WireMock.Net/Services/IRandomizerDoubleBetween0And1.cs b/src/WireMock.Net/Services/IRandomizerDoubleBetween0And1.cs new file mode 100644 index 00000000..9bf0480d --- /dev/null +++ b/src/WireMock.Net/Services/IRandomizerDoubleBetween0And1.cs @@ -0,0 +1,6 @@ +namespace WireMock.Services; + +internal interface IRandomizerDoubleBetween0And1 +{ + double Generate(); +} \ No newline at end of file diff --git a/src/WireMock.Net/Services/RandomizerDoubleBetween0And1.cs b/src/WireMock.Net/Services/RandomizerDoubleBetween0And1.cs new file mode 100644 index 00000000..84e658e4 --- /dev/null +++ b/src/WireMock.Net/Services/RandomizerDoubleBetween0And1.cs @@ -0,0 +1,14 @@ +using RandomDataGenerator.FieldOptions; +using RandomDataGenerator.Randomizers; + +namespace WireMock.Services; + +internal class RandomizerDoubleBetween0And1 : IRandomizerDoubleBetween0And1 +{ + private readonly IRandomizerNumber _randomizerDoubleBetween0And1 = RandomizerFactory.GetRandomizer(new FieldOptionsDouble { Min = 0, Max = 1 }); + + public double Generate() + { + return _randomizerDoubleBetween0And1.Generate() ?? 0; + } +} \ No newline at end of file diff --git a/src/WireMock.Net/Util/CultureInfoExtensions.cs b/src/WireMock.Net/Util/CultureInfoExtensions.cs index 086c153e..2469804e 100644 --- a/src/WireMock.Net/Util/CultureInfoExtensions.cs +++ b/src/WireMock.Net/Util/CultureInfoExtensions.cs @@ -5,6 +5,8 @@ namespace WireMock.Util; internal static class CultureInfoUtils { + public static readonly CultureInfo CultureInfoEnUS = new("en-US"); + public static CultureInfo Parse(string? value) { if (value is null) diff --git a/src/WireMock.Org.Abstractions/MappingsResponseFaultConstants.cs b/src/WireMock.Org.Abstractions/MappingsResponseFaultConstants.cs index 50bc8435..60d75fed 100644 --- a/src/WireMock.Org.Abstractions/MappingsResponseFaultConstants.cs +++ b/src/WireMock.Org.Abstractions/MappingsResponseFaultConstants.cs @@ -1,16 +1,11 @@ -namespace WireMock.Org.Abstractions +namespace WireMock.Org.Abstractions; + +/// +/// The fault to apply (instead of a full, valid response). +/// +public static class MappingsResponseFaultConstants { - /// - /// The fault to apply (instead of a full, valid response). - /// - public static class MappingsResponseFaultConstants - { - public const string CONNECTIONRESETBYPEER = "CONNECTION_RESET_BY_PEER"; + public const string EMPTYRESPONSE = "EMPTY_RESPONSE"; - public const string EMPTYRESPONSE = "EMPTY_RESPONSE"; - - public const string MALFORMEDRESPONSECHUNK = "MALFORMED_RESPONSE_CHUNK"; - - public const string RANDOMDATATHENCLOSE = "RANDOM_DATA_THEN_CLOSE"; - } -} + public const string MALFORMEDRESPONSECHUNK = "MALFORMED_RESPONSE_CHUNK"; +} \ No newline at end of file diff --git a/test/WireMock.Net.Tests/Owin/MappingMatcherTests.cs b/test/WireMock.Net.Tests/Owin/MappingMatcherTests.cs index 3ed1ba9b..b86aced4 100644 --- a/test/WireMock.Net.Tests/Owin/MappingMatcherTests.cs +++ b/test/WireMock.Net.Tests/Owin/MappingMatcherTests.cs @@ -6,6 +6,7 @@ using WireMock.Logging; using WireMock.Matchers.Request; using WireMock.Models; using WireMock.Owin; +using WireMock.Services; using WireMock.Util; using Xunit; @@ -14,6 +15,8 @@ namespace WireMock.Net.Tests.Owin; public class MappingMatcherTests { private readonly Mock _optionsMock; + private readonly Mock _randomizerDoubleBetween0And1Mock; + private readonly MappingMatcher _sut; public MappingMatcherTests() @@ -29,7 +32,10 @@ public class MappingMatcherTests loggerMock.Setup(l => l.Error(It.IsAny())); _optionsMock.Setup(o => o.Logger).Returns(loggerMock.Object); - _sut = new MappingMatcher(_optionsMock.Object); + _randomizerDoubleBetween0And1Mock = new Mock(); + _randomizerDoubleBetween0And1Mock.Setup(r => r.Generate()).Returns(0.0); + + _sut = new MappingMatcher(_optionsMock.Object, _randomizerDoubleBetween0And1Mock.Object); } [Fact] @@ -76,8 +82,8 @@ public class MappingMatcherTests var guid2 = Guid.Parse("00000000-0000-0000-0000-000000000002"); var mappings = InitMappings ( - (guid1, new[] { 0.1 }), - (guid2, new[] { 1.0 }) + (guid1, new[] { 0.1 }, null), + (guid2, new[] { 1.0 }, null) ); _optionsMock.Setup(o => o.Mappings).Returns(mappings); @@ -104,8 +110,8 @@ public class MappingMatcherTests var guid2 = Guid.Parse("00000000-0000-0000-0000-000000000002"); var mappings = InitMappings ( - (guid1, new[] { 0.1 }), - (guid2, new[] { 0.9 }) + (guid1, new[] { 0.1 }, null), + (guid2, new[] { 0.9 }, null) ); _optionsMock.Setup(o => o.Mappings).Returns(mappings); @@ -131,8 +137,8 @@ public class MappingMatcherTests _optionsMock.SetupGet(o => o.AllowPartialMapping).Returns(true); var mappings = InitMappings( - (guid1, new[] { 0.1 }), - (guid2, new[] { 0.9 }) + (guid1, new[] { 0.1 }, null), + (guid2, new[] { 0.9 }, null) ); _optionsMock.Setup(o => o.Mappings).Returns(mappings); @@ -158,8 +164,8 @@ public class MappingMatcherTests 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 }), - (guid2, new[] { 1.0, 1.0 }) + (guid1, new[] { 1.0 }, null), + (guid2, new[] { 1.0, 1.0 }, null) ); _optionsMock.Setup(o => o.Mappings).Returns(mappings); @@ -178,7 +184,31 @@ public class MappingMatcherTests result.Partial.RequestMatchResult.AverageTotalScore.Should().Be(1.0); } - private static ConcurrentDictionary 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 InitMappings(params (Guid guid, double[] scores, double? probability)[] matches) { var mappings = new ConcurrentDictionary(); @@ -193,6 +223,8 @@ public class MappingMatcherTests requestMatchResult.AddScore(typeof(object), score); } + mappingMock.SetupGet(m => m.Probability).Returns(match.probability); + mappingMock.Setup(m => m.GetRequestMatchResult(It.IsAny(), It.IsAny())).Returns(requestMatchResult); mappings.TryAdd(match.guid, mappingMock.Object); diff --git a/test/WireMock.Net.Tests/Owin/WireMockMiddlewareTests.cs b/test/WireMock.Net.Tests/Owin/WireMockMiddlewareTests.cs index 4e253fc8..d42e1eaa 100644 --- a/test/WireMock.Net.Tests/Owin/WireMockMiddlewareTests.cs +++ b/test/WireMock.Net.Tests/Owin/WireMockMiddlewareTests.cs @@ -177,7 +177,7 @@ public class WireMockMiddlewareTests _mappingMock.SetupGet(m => m.Provider).Returns(responseBuilder); _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())).ReturnsAsync((new ResponseMessage(), newMappingFromProxy)); var requestBuilder = Request.Create().UsingAnyMethod(); @@ -231,7 +231,7 @@ public class WireMockMiddlewareTests _mappingMock.SetupGet(m => m.Provider).Returns(responseBuilder); _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())).ReturnsAsync((new ResponseMessage(), newMappingFromProxy)); var requestBuilder = Request.Create().UsingAnyMethod(); diff --git a/test/WireMock.Net.Tests/Serialization/MappingConverterTests.ToCSharpCode.cs b/test/WireMock.Net.Tests/Serialization/MappingConverterTests.ToCSharpCode.cs index 1e629313..c34a32d9 100644 --- a/test/WireMock.Net.Tests/Serialization/MappingConverterTests.ToCSharpCode.cs +++ b/test/WireMock.Net.Tests/Serialization/MappingConverterTests.ToCSharpCode.cs @@ -136,7 +136,8 @@ public partial class MappingConverterTests null, false, null, - data: null + data: null, + probability: 0.3 ); } } diff --git a/test/WireMock.Net.Tests/Serialization/MappingConverterTests.ToCSharpCode_With_Builder_And_AddStartIsFalse.verified.txt b/test/WireMock.Net.Tests/Serialization/MappingConverterTests.ToCSharpCode_With_Builder_And_AddStartIsFalse.verified.txt index 19739c17..bd54bf3d 100644 --- a/test/WireMock.Net.Tests/Serialization/MappingConverterTests.ToCSharpCode_With_Builder_And_AddStartIsFalse.verified.txt +++ b/test/WireMock.Net.Tests/Serialization/MappingConverterTests.ToCSharpCode_With_Builder_And_AddStartIsFalse.verified.txt @@ -9,6 +9,7 @@ .WithBody("b") ) .WithGuid("8e7b9ab7-e18e-4502-8bc9-11e6679811cc") + .WithProbability(0.3) .RespondWith(Response.Create() .WithHeader("Keep-Alive)", "test") .WithBody("bbb") diff --git a/test/WireMock.Net.Tests/Serialization/MappingConverterTests.ToCSharpCode_With_Builder_And_AddStartIsTrue.verified.txt b/test/WireMock.Net.Tests/Serialization/MappingConverterTests.ToCSharpCode_With_Builder_And_AddStartIsTrue.verified.txt index 95b1a6c4..e742782b 100644 --- a/test/WireMock.Net.Tests/Serialization/MappingConverterTests.ToCSharpCode_With_Builder_And_AddStartIsTrue.verified.txt +++ b/test/WireMock.Net.Tests/Serialization/MappingConverterTests.ToCSharpCode_With_Builder_And_AddStartIsTrue.verified.txt @@ -10,6 +10,7 @@ builder .WithBody("b") ) .WithGuid("8e7b9ab7-e18e-4502-8bc9-11e6679811cc") + .WithProbability(0.3) .RespondWith(Response.Create() .WithHeader("Keep-Alive)", "test") .WithBody("bbb") diff --git a/test/WireMock.Net.Tests/Serialization/MappingConverterTests.ToCSharpCode_With_Server_And_AddStartIsFalse.verified.txt b/test/WireMock.Net.Tests/Serialization/MappingConverterTests.ToCSharpCode_With_Server_And_AddStartIsFalse.verified.txt index 779a9172..3d682ac9 100644 --- a/test/WireMock.Net.Tests/Serialization/MappingConverterTests.ToCSharpCode_With_Server_And_AddStartIsFalse.verified.txt +++ b/test/WireMock.Net.Tests/Serialization/MappingConverterTests.ToCSharpCode_With_Server_And_AddStartIsFalse.verified.txt @@ -9,6 +9,7 @@ .WithBody("b") ) .WithGuid("8e7b9ab7-e18e-4502-8bc9-11e6679811cc") + .WithProbability(0.3) .RespondWith(Response.Create() .WithHeader("Keep-Alive)", "test") .WithBody("bbb") diff --git a/test/WireMock.Net.Tests/Serialization/MappingConverterTests.ToCSharpCode_With_Server_And_AddStartIsTrue.verified.txt b/test/WireMock.Net.Tests/Serialization/MappingConverterTests.ToCSharpCode_With_Server_And_AddStartIsTrue.verified.txt index 894a8860..f215220d 100644 --- a/test/WireMock.Net.Tests/Serialization/MappingConverterTests.ToCSharpCode_With_Server_And_AddStartIsTrue.verified.txt +++ b/test/WireMock.Net.Tests/Serialization/MappingConverterTests.ToCSharpCode_With_Server_And_AddStartIsTrue.verified.txt @@ -10,6 +10,7 @@ server .WithBody("b") ) .WithGuid("8e7b9ab7-e18e-4502-8bc9-11e6679811cc") + .WithProbability(0.3) .RespondWith(Response.Create() .WithHeader("Keep-Alive)", "test") .WithBody("bbb") diff --git a/test/WireMock.Net.Tests/Serialization/MappingConverterTests.ToMappingModel_WithProbability_ReturnsCorrectModel.verified.txt b/test/WireMock.Net.Tests/Serialization/MappingConverterTests.ToMappingModel_WithProbability_ReturnsCorrectModel.verified.txt new file mode 100644 index 00000000..9fab2fca --- /dev/null +++ b/test/WireMock.Net.Tests/Serialization/MappingConverterTests.ToMappingModel_WithProbability_ReturnsCorrectModel.verified.txt @@ -0,0 +1,11 @@ +{ + Guid: Guid_1, + UpdatedAt: DateTime_1, + Title: , + Description: , + Priority: 42, + Request: {}, + Response: {}, + UseWebhooksFireAndForget: false, + Probability: 0.4 +} \ No newline at end of file diff --git a/test/WireMock.Net.Tests/Serialization/MappingConverterTests.cs b/test/WireMock.Net.Tests/Serialization/MappingConverterTests.cs index ec968b51..4e0f47a7 100644 --- a/test/WireMock.Net.Tests/Serialization/MappingConverterTests.cs +++ b/test/WireMock.Net.Tests/Serialization/MappingConverterTests.cs @@ -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 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 var model = _sut.ToMappingModel(mapping); @@ -168,7 +168,7 @@ public partial class MappingConverterTests var description = "my-description"; var request = Request.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 var model = _sut.ToMappingModel(mapping); @@ -188,7 +188,7 @@ public partial class MappingConverterTests // Assign var request = Request.Create(); 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 var model = _sut.ToMappingModel(mapping); @@ -217,7 +217,7 @@ public partial class MappingConverterTests End = end, 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 var model = _sut.ToMappingModel(mapping); @@ -248,7 +248,7 @@ public partial class MappingConverterTests { var request = Request.Create(); 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 var model = _sut.ToMappingModel(mapping); @@ -266,7 +266,7 @@ public partial class MappingConverterTests var delay = 1000; var request = Request.Create(); 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 var model = _sut.ToMappingModel(mapping); @@ -286,7 +286,7 @@ public partial class MappingConverterTests int minimumDelay = 1000; var request = Request.Create(); 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 var model = _sut.ToMappingModel(mapping); @@ -309,7 +309,7 @@ public partial class MappingConverterTests int maximumDelay = 2000; var request = Request.Create(); 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 var model = _sut.ToMappingModel(mapping); @@ -323,5 +323,25 @@ public partial class MappingConverterTests // Verify 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 \ No newline at end of file diff --git a/test/WireMock.Net.Tests/WireMockServerTests.WithProbability.cs b/test/WireMock.Net.Tests/WireMockServerTests.WithProbability.cs new file mode 100644 index 00000000..e635a9cc --- /dev/null +++ b/test/WireMock.Net.Tests/WireMockServerTests.WithProbability.cs @@ -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(); + } +} \ No newline at end of file