Update Transformer functionality to return value instead of string (#858)

This commit is contained in:
Stef Heyenrath
2022-12-11 11:07:56 +01:00
committed by GitHub
parent 6b03dfaa8c
commit 9606fee8cb
25 changed files with 639 additions and 506 deletions
+1
View File
@@ -33,6 +33,7 @@
<s:Boolean x:Key="/Default/UserDictionary/Words/=Scriban/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/UserDictionary/Words/=Scriban/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Sigil/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/UserDictionary/Words/=Sigil/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Stef/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/UserDictionary/Words/=Stef/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=templated/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Victoor/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/UserDictionary/Words/=Victoor/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Webhook/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/UserDictionary/Words/=Webhook/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Webhooks/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/UserDictionary/Words/=Webhooks/@EntryIndexedValue">True</s:Boolean>
@@ -1,36 +1,25 @@
using System; namespace WireMock.Types;
namespace WireMock.Types /// <summary>
/// Logic to use when replace a JSON node using the Transformer.
/// </summary>
public enum ReplaceNodeOptions
{ {
/// <summary> /// <summary>
/// Flags to use when replace a JSON node using the Transformer. /// Try to evaluate a templated value.
/// In case this is valid, return the value and if the value can be converted to a primitive type, use that value.
/// </summary> /// </summary>
[Flags] EvaluateAndTryToConvert = 0,
public enum ReplaceNodeOptions
{
/// <summary>
/// Default
/// </summary>
None = 0
///// <summary> /// <summary>
///// Replace boolean string value to a real boolean value. (This is used by default to maintain backward compatibility.) /// Try to evaluate a templated value.
///// </summary> /// In case this is valid, return the value, else fallback to the parse behavior.
//Bool = 0b00000001, /// </summary>
Evaluate = 1,
///// <summary> /// <summary>
///// Replace integer string value to a real integer value. /// Parse templated string to a templated string.
///// </summary> /// (keep a templated string value as string value).
//Integer = 0b00000010, /// </summary>
Parse = 2
///// <summary>
///// Replace long string value to a real long value.
///// </summary>
//Long = 0b00000100,
///// <summary>
///// Replace all string values to a real values.
///// </summary>
//All = Bool | Integer | Long
}
} }
@@ -1,23 +1,22 @@
namespace WireMock.Types namespace WireMock.Types;
/// <summary>
/// The ResponseMessage Transformers
/// </summary>
public enum TransformerType
{ {
/// <summary> /// <summary>
/// The ResponseMessage Transformers /// https://github.com/Handlebars-Net/Handlebars.Net
/// </summary> /// </summary>
public enum TransformerType Handlebars,
{
/// <summary>
/// https://github.com/Handlebars-Net/Handlebars.Net
/// </summary>
Handlebars,
/// <summary> /// <summary>
/// https://github.com/scriban/scriban : default /// https://github.com/scriban/scriban : default
/// </summary> /// </summary>
Scriban, Scriban,
/// <summary> /// <summary>
/// https://github.com/scriban/scriban : DotLiquid /// https://github.com/scriban/scriban : DotLiquid
/// </summary> /// </summary>
ScribanDotLiquid ScribanDotLiquid
}
} }
+3 -3
View File
@@ -51,14 +51,14 @@ internal class WebhookSender
switch (webhookRequest.TransformerType) switch (webhookRequest.TransformerType)
{ {
case TransformerType.Handlebars: case TransformerType.Handlebars:
var factoryHandlebars = new HandlebarsContextFactory(_settings.FileSystemHandler, _settings.HandlebarsRegistrationCallback); var factoryHandlebars = new HandlebarsContextFactory(_settings);
transformer = new Transformer(factoryHandlebars); transformer = new Transformer(_settings, factoryHandlebars);
break; break;
case TransformerType.Scriban: case TransformerType.Scriban:
case TransformerType.ScribanDotLiquid: case TransformerType.ScribanDotLiquid:
var factoryDotLiquid = new ScribanContextFactory(_settings.FileSystemHandler, webhookRequest.TransformerType); var factoryDotLiquid = new ScribanContextFactory(_settings.FileSystemHandler, webhookRequest.TransformerType);
transformer = new Transformer(factoryDotLiquid); transformer = new Transformer(_settings, factoryDotLiquid);
break; break;
default: default:
@@ -29,5 +29,5 @@ public interface ITransformResponseBuilder : IDelayResponseBuilder
/// <returns> /// <returns>
/// The <see cref="IResponseBuilder"/>. /// The <see cref="IResponseBuilder"/>.
/// </returns> /// </returns>
IResponseBuilder WithTransformer(TransformerType transformerType = TransformerType.Handlebars, bool transformContentFromBodyAsFile = false, ReplaceNodeOptions options = ReplaceNodeOptions.None); IResponseBuilder WithTransformer(TransformerType transformerType = TransformerType.Handlebars, bool transformContentFromBodyAsFile = false, ReplaceNodeOptions options = ReplaceNodeOptions.Evaluate);
} }
@@ -207,7 +207,7 @@ public partial class Response : IResponseBuilder
} }
/// <inheritdoc /> /// <inheritdoc />
public IResponseBuilder WithTransformer(TransformerType transformerType, bool transformContentFromBodyAsFile = false, ReplaceNodeOptions options = ReplaceNodeOptions.None) public IResponseBuilder WithTransformer(TransformerType transformerType, bool transformContentFromBodyAsFile = false, ReplaceNodeOptions options = ReplaceNodeOptions.Evaluate)
{ {
UseTransformer = true; UseTransformer = true;
TransformerType = transformerType; TransformerType = transformerType;
@@ -314,14 +314,14 @@ public partial class Response : IResponseBuilder
switch (TransformerType) switch (TransformerType)
{ {
case TransformerType.Handlebars: case TransformerType.Handlebars:
var factoryHandlebars = new HandlebarsContextFactory(settings.FileSystemHandler, settings.HandlebarsRegistrationCallback); var factoryHandlebars = new HandlebarsContextFactory(settings);
responseMessageTransformer = new Transformer(factoryHandlebars); responseMessageTransformer = new Transformer(settings, factoryHandlebars);
break; break;
case TransformerType.Scriban: case TransformerType.Scriban:
case TransformerType.ScribanDotLiquid: case TransformerType.ScribanDotLiquid:
var factoryDotLiquid = new ScribanContextFactory(settings.FileSystemHandler, TransformerType); var factoryDotLiquid = new ScribanContextFactory(settings.FileSystemHandler, TransformerType);
responseMessageTransformer = new Transformer(factoryDotLiquid); responseMessageTransformer = new Transformer(settings, factoryDotLiquid);
break; break;
default: default:
@@ -39,7 +39,7 @@ internal static class WebhookMapper
if (!Enum.TryParse<ReplaceNodeOptions>(model.Request.TransformerReplaceNodeOptions, out var option)) if (!Enum.TryParse<ReplaceNodeOptions>(model.Request.TransformerReplaceNodeOptions, out var option))
{ {
option = ReplaceNodeOptions.None; option = ReplaceNodeOptions.Evaluate;
} }
webhook.Request.TransformerReplaceNodeOptions = option; webhook.Request.TransformerReplaceNodeOptions = option;
} }
@@ -250,7 +250,7 @@ public partial class WireMockServer
if (!Enum.TryParse<ReplaceNodeOptions>(responseModel.TransformerReplaceNodeOptions, out var option)) if (!Enum.TryParse<ReplaceNodeOptions>(responseModel.TransformerReplaceNodeOptions, out var option))
{ {
option = ReplaceNodeOptions.None; option = ReplaceNodeOptions.Evaluate;
} }
responseBuilder = responseBuilder.WithTransformer( responseBuilder = responseBuilder.WithTransformer(
transformerType, transformerType,
@@ -10,272 +10,279 @@ using WireMock.Logging;
using WireMock.Matchers; using WireMock.Matchers;
using WireMock.RegularExpressions; using WireMock.RegularExpressions;
using WireMock.Types; using WireMock.Types;
using System.Globalization;
#if USE_ASPNETCORE #if USE_ASPNETCORE
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
#endif #endif
namespace WireMock.Settings namespace WireMock.Settings;
/// <summary>
/// WireMockServerSettings
/// </summary>
public class WireMockServerSettings
{ {
/// <summary> /// <summary>
/// WireMockServerSettings /// Gets or sets the http port.
/// </summary> /// </summary>
public class WireMockServerSettings [PublicAPI]
{ public int? Port { get; set; }
/// <summary>
/// Gets or sets the http port.
/// </summary>
[PublicAPI]
public int? Port { get; set; }
/// <summary> /// <summary>
/// Gets or sets the use SSL. /// Gets or sets the use SSL.
/// </summary> /// </summary>
// ReSharper disable once InconsistentNaming // ReSharper disable once InconsistentNaming
[PublicAPI] [PublicAPI]
public bool? UseSSL { get; set; } public bool? UseSSL { get; set; }
/// <summary> /// <summary>
/// Defines on which scheme (http/https) to host. (This overrides the <c>UseSSL</c> value). /// Defines on which scheme (http/https) to host. (This overrides the <c>UseSSL</c> value).
/// </summary> /// </summary>
[PublicAPI] [PublicAPI]
public HostingScheme? HostingScheme { get; set; } public HostingScheme? HostingScheme { get; set; }
/// <summary> /// <summary>
/// Gets or sets whether to start admin interface. /// Gets or sets whether to start admin interface.
/// </summary> /// </summary>
[PublicAPI] [PublicAPI]
public bool? StartAdminInterface { get; set; } public bool? StartAdminInterface { get; set; }
/// <summary> /// <summary>
/// Gets or sets if the static mappings should be read at startup. /// Gets or sets if the static mappings should be read at startup.
/// </summary> /// </summary>
[PublicAPI] [PublicAPI]
public bool? ReadStaticMappings { get; set; } public bool? ReadStaticMappings { get; set; }
/// <summary> /// <summary>
/// Watch the static mapping files + folder for changes when running. /// Watch the static mapping files + folder for changes when running.
/// </summary> /// </summary>
[PublicAPI] [PublicAPI]
public bool? WatchStaticMappings { get; set; } public bool? WatchStaticMappings { get; set; }
/// <summary> /// <summary>
/// A value indicating whether subdirectories within the static mappings path should be monitored. /// A value indicating whether subdirectories within the static mappings path should be monitored.
/// </summary> /// </summary>
[PublicAPI] [PublicAPI]
public bool? WatchStaticMappingsInSubdirectories { get; set; } public bool? WatchStaticMappingsInSubdirectories { get; set; }
/// <summary> /// <summary>
/// Gets or sets if the proxy and record settings. /// Gets or sets if the proxy and record settings.
/// </summary> /// </summary>
[PublicAPI] [PublicAPI]
public ProxyAndRecordSettings? ProxyAndRecordSettings { get; set; } public ProxyAndRecordSettings? ProxyAndRecordSettings { get; set; }
/// <summary> /// <summary>
/// Gets or sets the urls. /// Gets or sets the urls.
/// </summary> /// </summary>
[PublicAPI] [PublicAPI]
public string[]? Urls { get; set; } public string[]? Urls { get; set; }
/// <summary> /// <summary>
/// StartTimeout /// StartTimeout
/// </summary> /// </summary>
[PublicAPI] [PublicAPI]
public int StartTimeout { get; set; } = 10000; public int StartTimeout { get; set; } = 10000;
/// <summary> /// <summary>
/// Allow Partial Mapping (default set to false). /// Allow Partial Mapping (default set to false).
/// </summary> /// </summary>
[PublicAPI] [PublicAPI]
public bool? AllowPartialMapping { get; set; } public bool? AllowPartialMapping { get; set; }
/// <summary> /// <summary>
/// The username needed for __admin access. /// The username needed for __admin access.
/// </summary> /// </summary>
[PublicAPI] [PublicAPI]
public string? AdminUsername { get; set; } public string? AdminUsername { get; set; }
/// <summary> /// <summary>
/// The password needed for __admin access. /// The password needed for __admin access.
/// </summary> /// </summary>
[PublicAPI] [PublicAPI]
public string? AdminPassword { get; set; } public string? AdminPassword { get; set; }
/// <summary> /// <summary>
/// The AzureAD Tenant needed for __admin access. /// The AzureAD Tenant needed for __admin access.
/// </summary> /// </summary>
[PublicAPI] [PublicAPI]
public string? AdminAzureADTenant { get; set; } public string? AdminAzureADTenant { get; set; }
/// <summary> /// <summary>
/// The AzureAD Audience / Resource for __admin access. /// The AzureAD Audience / Resource for __admin access.
/// </summary> /// </summary>
[PublicAPI] [PublicAPI]
public string? AdminAzureADAudience { get; set; } public string? AdminAzureADAudience { get; set; }
/// <summary> /// <summary>
/// The RequestLog expiration in hours (optional). /// The RequestLog expiration in hours (optional).
/// </summary> /// </summary>
[PublicAPI] [PublicAPI]
public int? RequestLogExpirationDuration { get; set; } public int? RequestLogExpirationDuration { get; set; }
/// <summary> /// <summary>
/// The MaxRequestLog count (optional). /// The MaxRequestLog count (optional).
/// </summary> /// </summary>
[PublicAPI] [PublicAPI]
public int? MaxRequestLogCount { get; set; } public int? MaxRequestLogCount { get; set; }
/// <summary> /// <summary>
/// Action which is called (with the IAppBuilder or IApplicationBuilder) before the internal WireMockMiddleware is initialized. [Optional] /// Action which is called (with the IAppBuilder or IApplicationBuilder) before the internal WireMockMiddleware is initialized. [Optional]
/// </summary> /// </summary>
[PublicAPI] [PublicAPI]
[JsonIgnore] [JsonIgnore]
public Action<object>? PreWireMockMiddlewareInit { get; set; } public Action<object>? PreWireMockMiddlewareInit { get; set; }
/// <summary> /// <summary>
/// Action which is called (with the IAppBuilder or IApplicationBuilder) after the internal WireMockMiddleware is initialized. [Optional] /// Action which is called (with the IAppBuilder or IApplicationBuilder) after the internal WireMockMiddleware is initialized. [Optional]
/// </summary> /// </summary>
[PublicAPI] [PublicAPI]
[JsonIgnore] [JsonIgnore]
public Action<object>? PostWireMockMiddlewareInit { get; set; } public Action<object>? PostWireMockMiddlewareInit { get; set; }
#if USE_ASPNETCORE #if USE_ASPNETCORE
/// <summary> /// <summary>
/// Action which is called with IServiceCollection when ASP.NET Core DI is being configured. [Optional] /// Action which is called with IServiceCollection when ASP.NET Core DI is being configured. [Optional]
/// </summary> /// </summary>
[PublicAPI] [PublicAPI]
[JsonIgnore] [JsonIgnore]
public Action<IServiceCollection>? AdditionalServiceRegistration { get; set; } public Action<IServiceCollection>? AdditionalServiceRegistration { get; set; }
/// <summary> /// <summary>
/// Policies to use when using CORS. By default CORS is disabled. [Optional] /// Policies to use when using CORS. By default CORS is disabled. [Optional]
/// </summary> /// </summary>
[PublicAPI] [PublicAPI]
public CorsPolicyOptions? CorsPolicyOptions { get; set; } public CorsPolicyOptions? CorsPolicyOptions { get; set; }
#endif #endif
/// <summary> /// <summary>
/// The IWireMockLogger which logs Debug, Info, Warning or Error /// The IWireMockLogger which logs Debug, Info, Warning or Error
/// </summary> /// </summary>
[PublicAPI] [PublicAPI]
[JsonIgnore] [JsonIgnore]
public IWireMockLogger Logger { get; set; } = null!; public IWireMockLogger Logger { get; set; } = null!;
/// <summary> /// <summary>
/// Handler to interact with the file system to read and write static mapping files. /// Handler to interact with the file system to read and write static mapping files.
/// </summary> /// </summary>
[PublicAPI] [PublicAPI]
[JsonIgnore] [JsonIgnore]
public IFileSystemHandler FileSystemHandler { get; set; } = null!; public IFileSystemHandler FileSystemHandler { get; set; } = null!;
/// <summary> /// <summary>
/// Action which can be used to add additional Handlebars registrations. [Optional] /// Action which can be used to add additional Handlebars registrations. [Optional]
/// </summary> /// </summary>
[PublicAPI] [PublicAPI]
[JsonIgnore] [JsonIgnore]
public Action<IHandlebars, IFileSystemHandler>? HandlebarsRegistrationCallback { get; set; } public Action<IHandlebars, IFileSystemHandler>? HandlebarsRegistrationCallback { get; set; }
/// <summary> /// <summary>
/// Allow the usage of CSharpCodeMatcher (default is not allowed). /// Allow the usage of CSharpCodeMatcher (default is not allowed).
/// </summary> /// </summary>
[PublicAPI] [PublicAPI]
public bool? AllowCSharpCodeMatcher { get; set; } public bool? AllowCSharpCodeMatcher { get; set; }
/// <summary> /// <summary>
/// Allow a Body for all HTTP Methods. (default set to false). /// Allow a Body for all HTTP Methods. (default set to false).
/// </summary> /// </summary>
[PublicAPI] [PublicAPI]
public bool? AllowBodyForAllHttpMethods { get; set; } public bool? AllowBodyForAllHttpMethods { get; set; }
/// <summary> /// <summary>
/// Allow only a HttpStatus Code in the response which is defined. (default set to false). /// Allow only a HttpStatus Code in the response which is defined. (default set to false).
/// - false : also null, 0, empty or invalid HttpStatus codes are allowed. /// - false : also null, 0, empty or invalid HttpStatus codes are allowed.
/// - true : only codes defined in <see cref="System.Net.HttpStatusCode"/> are allowed. /// - true : only codes defined in <see cref="System.Net.HttpStatusCode"/> are allowed.
/// </summary> /// </summary>
[PublicAPI] [PublicAPI]
public bool? AllowOnlyDefinedHttpStatusCodeInResponse { get; set; } public bool? AllowOnlyDefinedHttpStatusCodeInResponse { get; set; }
/// <summary> /// <summary>
/// Set to true to disable Json deserialization when processing requests. (default set to false). /// Set to true to disable Json deserialization when processing requests. (default set to false).
/// </summary> /// </summary>
[PublicAPI] [PublicAPI]
public bool? DisableJsonBodyParsing { get; set; } public bool? DisableJsonBodyParsing { get; set; }
/// <summary> /// <summary>
/// Disable support for GZip and Deflate request body decompression. (default set to false). /// Disable support for GZip and Deflate request body decompression. (default set to false).
/// </summary> /// </summary>
[PublicAPI] [PublicAPI]
public bool? DisableRequestBodyDecompressing { get; set; } public bool? DisableRequestBodyDecompressing { get; set; }
/// <summary> /// <summary>
/// Handle all requests synchronously. (default set to false). /// Handle all requests synchronously. (default set to false).
/// </summary> /// </summary>
[PublicAPI] [PublicAPI]
public bool? HandleRequestsSynchronously { get; set; } public bool? HandleRequestsSynchronously { get; set; }
/// <summary> /// <summary>
/// Throw an exception when the <see cref="IMatcher"/> fails because of invalid input. (default set to false). /// Throw an exception when the <see cref="IMatcher"/> fails because of invalid input. (default set to false).
/// </summary> /// </summary>
[PublicAPI] [PublicAPI]
public bool? ThrowExceptionWhenMatcherFails { get; set; } public bool? ThrowExceptionWhenMatcherFails { get; set; }
/// <summary> /// <summary>
/// If https is used, these settings can be used to configure the CertificateSettings in case a custom certificate instead the default .NET certificate should be used. /// If https is used, these settings can be used to configure the CertificateSettings in case a custom certificate instead the default .NET certificate should be used.
/// ///
/// X509StoreName and X509StoreLocation should be defined /// X509StoreName and X509StoreLocation should be defined
/// OR /// OR
/// X509CertificateFilePath and X509CertificatePassword should be defined /// X509CertificateFilePath and X509CertificatePassword should be defined
/// </summary> /// </summary>
[PublicAPI] [PublicAPI]
public WireMockCertificateSettings? CertificateSettings { get; set; } public WireMockCertificateSettings? CertificateSettings { get; set; }
/// <summary> /// <summary>
/// Defines if custom CertificateSettings are defined /// Defines if custom CertificateSettings are defined
/// </summary> /// </summary>
[PublicAPI] [PublicAPI]
public bool CustomCertificateDefined => CertificateSettings?.IsDefined == true; public bool CustomCertificateDefined => CertificateSettings?.IsDefined == true;
/// <summary> /// <summary>
/// Defines the global IWebhookSettings to use. /// Defines the global IWebhookSettings to use.
/// </summary> /// </summary>
[PublicAPI] [PublicAPI]
public WebhookSettings? WebhookSettings { get; set; } public WebhookSettings? WebhookSettings { get; set; }
/// <summary> /// <summary>
/// Use the <see cref="RegexExtended"/> instead of the default <see cref="Regex"/> (default set to true). /// Use the <see cref="RegexExtended"/> instead of the default <see cref="Regex"/> (default set to true).
/// </summary> /// </summary>
[PublicAPI] [PublicAPI]
public bool? UseRegexExtended { get; set; } = true; public bool? UseRegexExtended { get; set; } = true;
/// <summary> /// <summary>
/// Save unmatched requests to a file using the <see cref="IFileSystemHandler"/> (default set to false). /// Save unmatched requests to a file using the <see cref="IFileSystemHandler"/> (default set to false).
/// </summary> /// </summary>
[PublicAPI] [PublicAPI]
public bool? SaveUnmatchedRequests { get; set; } public bool? SaveUnmatchedRequests { get; set; }
/// <summary> /// <summary>
/// Don't save the response-string in the LogEntry when WithBody(Func{IRequestMessage, string}) or WithBody(Func{IRequestMessage, Task{string}}) is used. (default set to false). /// Don't save the response-string in the LogEntry when WithBody(Func{IRequestMessage, string}) or WithBody(Func{IRequestMessage, Task{string}}) is used. (default set to false).
/// </summary> /// </summary>
[PublicAPI] [PublicAPI]
public bool? DoNotSaveDynamicResponseInLogEntry { get; set; } public bool? DoNotSaveDynamicResponseInLogEntry { get; set; }
/// <summary> /// <summary>
/// See <seealso cref="QueryParameterMultipleValueSupport"/>. /// See <seealso cref="QueryParameterMultipleValueSupport"/>.
/// ///
/// Default value = "All". /// Default value = "All".
/// </summary> /// </summary>
[PublicAPI] [PublicAPI]
public QueryParameterMultipleValueSupport? QueryParameterMultipleValueSupport { get; set; } public QueryParameterMultipleValueSupport? QueryParameterMultipleValueSupport { get; set; }
/// <summary> /// <summary>
/// Custom matcher mappings for static mappings /// Custom matcher mappings for static mappings
/// </summary> /// </summary>
[PublicAPI, JsonIgnore] [PublicAPI, JsonIgnore]
public IDictionary<string, Func<MatcherModel, IMatcher>>? CustomMatcherMappings { get; set; } public IDictionary<string, Func<MatcherModel, IMatcher>>? CustomMatcherMappings { get; set; }
/// <summary> /// <summary>
/// The <see cref="JsonSerializerSettings"/> used when the a JSON response is generated. /// The <see cref="JsonSerializerSettings"/> used when the a JSON response is generated.
/// </summary> /// </summary>
[PublicAPI, JsonIgnore] [PublicAPI, JsonIgnore]
public JsonSerializerSettings? JsonSerializerSettings { get; set; } public JsonSerializerSettings? JsonSerializerSettings { get; set; }
}
/// <summary>
/// The Culture to use.
/// Currently used for:
/// - Handlebars Transformer
/// </summary>
public CultureInfo Culture { get; set; } = CultureInfo.CurrentCulture;
} }
@@ -1,8 +1,11 @@
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Linq;
using JetBrains.Annotations; using JetBrains.Annotations;
using Stef.Validation; using Stef.Validation;
using WireMock.Logging; using WireMock.Logging;
using WireMock.Types; using WireMock.Types;
using WireMock.Util;
namespace WireMock.Settings; namespace WireMock.Settings;
@@ -55,7 +58,8 @@ public static class WireMockServerSettingsParser
WatchStaticMappingsInSubdirectories = parser.GetBoolValue("WatchStaticMappingsInSubdirectories"), WatchStaticMappingsInSubdirectories = parser.GetBoolValue("WatchStaticMappingsInSubdirectories"),
HostingScheme = parser.GetEnumValue<HostingScheme>(nameof(WireMockServerSettings.HostingScheme)), HostingScheme = parser.GetEnumValue<HostingScheme>(nameof(WireMockServerSettings.HostingScheme)),
DoNotSaveDynamicResponseInLogEntry = parser.GetBoolValue(nameof(WireMockServerSettings.DoNotSaveDynamicResponseInLogEntry)), DoNotSaveDynamicResponseInLogEntry = parser.GetBoolValue(nameof(WireMockServerSettings.DoNotSaveDynamicResponseInLogEntry)),
QueryParameterMultipleValueSupport = parser.GetEnumValue<QueryParameterMultipleValueSupport>(nameof(WireMockServerSettings.QueryParameterMultipleValueSupport)) QueryParameterMultipleValueSupport = parser.GetEnumValue<QueryParameterMultipleValueSupport>(nameof(WireMockServerSettings.QueryParameterMultipleValueSupport)),
Culture = parser.GetValue(nameof(WireMockServerSettings.Culture), strings => CultureInfoUtils.Parse(strings.FirstOrDefault()), CultureInfo.CurrentCulture)
}; };
#if USE_ASPNETCORE #if USE_ASPNETCORE
@@ -1,27 +1,26 @@
using System;
using HandlebarsDotNet; using HandlebarsDotNet;
using HandlebarsDotNet.Helpers.Attributes; using HandlebarsDotNet.Helpers.Attributes;
using HandlebarsDotNet.Helpers.Enums; using HandlebarsDotNet.Helpers.Enums;
using HandlebarsDotNet.Helpers.Helpers; using HandlebarsDotNet.Helpers.Helpers;
using Stef.Validation;
using WireMock.Handlers; using WireMock.Handlers;
namespace WireMock.Transformers.Handlebars namespace WireMock.Transformers.Handlebars;
internal class FileHelpers : BaseHelpers, IHelpers
{ {
internal class FileHelpers : BaseHelpers, IHelpers private readonly IFileSystemHandler _fileSystemHandler;
public FileHelpers(IHandlebars context, IFileSystemHandler fileSystemHandler) : base(context)
{ {
private readonly IFileSystemHandler _fileSystemHandler; _fileSystemHandler = Guard.NotNull(fileSystemHandler);
}
public FileHelpers(IHandlebars context, IFileSystemHandler fileSystemHandler) : base(context) [HandlebarsWriter(WriterType.String, usage: HelperUsage.Both, passContext: true, name: "File")]
{ public string Read(Context context, string path)
_fileSystemHandler = fileSystemHandler ?? throw new ArgumentNullException(nameof(fileSystemHandler)); {
} var templateFunc = Context.Compile(path);
string transformed = templateFunc(context.Value);
[HandlebarsWriter(WriterType.String, usage: HelperUsage.Both, passContext: true, name: "File")] return _fileSystemHandler.ReadResponseBodyAsString(transformed);
public string Read(Context context, string path)
{
var templateFunc = Context.Compile(path);
string transformed = templateFunc(context.Value);
return _fileSystemHandler.ReadResponseBodyAsString(transformed);
}
} }
} }
@@ -1,18 +1,35 @@
using HandlebarsDotNet; using HandlebarsDotNet;
using HandlebarsDotNet.Helpers.Extensions;
using Stef.Validation;
using WireMock.Handlers; using WireMock.Handlers;
namespace WireMock.Transformers.Handlebars namespace WireMock.Transformers.Handlebars;
internal class HandlebarsContext : IHandlebarsContext
{ {
internal class HandlebarsContext : IHandlebarsContext public IHandlebars Handlebars { get; }
public IFileSystemHandler FileSystemHandler { get; }
public HandlebarsContext(IHandlebars handlebars, IFileSystemHandler fileSystemHandler)
{ {
public IHandlebars Handlebars { get; set; } Handlebars = Guard.NotNull(handlebars);
FileSystemHandler = Guard.NotNull(fileSystemHandler);
}
public IFileSystemHandler FileSystemHandler { get; set; } public string ParseAndRender(string text, object model)
{
var template = Handlebars.Compile(text);
return template(model);
}
public string ParseAndRender(string text, object model) public object? ParseAndEvaluate(string text, object model)
{
if (Handlebars.TryEvaluate(text, model, out var result) && result is not UndefinedBindingResult)
{ {
var template = Handlebars.Compile(text); return result;
return template(model);
} }
return ParseAndRender(text, model);
} }
} }
@@ -1,33 +1,30 @@
using System;
using HandlebarsDotNet; using HandlebarsDotNet;
using Stef.Validation; using Stef.Validation;
using WireMock.Handlers; using WireMock.Settings;
namespace WireMock.Transformers.Handlebars; namespace WireMock.Transformers.Handlebars;
internal class HandlebarsContextFactory : ITransformerContextFactory internal class HandlebarsContextFactory : ITransformerContextFactory
{ {
private readonly IFileSystemHandler _fileSystemHandler; private readonly WireMockServerSettings _settings;
private readonly Action<IHandlebars, IFileSystemHandler>? _action;
public HandlebarsContextFactory(IFileSystemHandler fileSystemHandler, Action<IHandlebars, IFileSystemHandler>? action) public HandlebarsContextFactory(WireMockServerSettings settings)
{ {
_fileSystemHandler = Guard.NotNull(fileSystemHandler); _settings = Guard.NotNull(settings);
_action = action;
} }
public ITransformerContext Create() public ITransformerContext Create()
{ {
var handlebars = HandlebarsDotNet.Handlebars.Create(); var config = new HandlebarsConfiguration
WireMockHandlebarsHelpers.Register(handlebars, _fileSystemHandler);
_action?.Invoke(handlebars, _fileSystemHandler);
return new HandlebarsContext
{ {
Handlebars = handlebars, FormatProvider = _settings.Culture
FileSystemHandler = _fileSystemHandler
}; };
var handlebars = HandlebarsDotNet.Handlebars.Create(config);
WireMockHandlebarsHelpers.Register(handlebars, _settings.FileSystemHandler);
_settings.HandlebarsRegistrationCallback?.Invoke(handlebars, _settings.FileSystemHandler);
return new HandlebarsContext(handlebars, _settings.FileSystemHandler);
} }
} }
@@ -1,9 +1,8 @@
using HandlebarsDotNet; using HandlebarsDotNet;
namespace WireMock.Transformers.Handlebars namespace WireMock.Transformers.Handlebars;
interface IHandlebarsContext : ITransformerContext
{ {
interface IHandlebarsContext : ITransformerContext IHandlebars Handlebars { get; }
{
IHandlebars Handlebars { get; set; }
}
} }
@@ -1,11 +1,12 @@
using WireMock.Handlers; using WireMock.Handlers;
namespace WireMock.Transformers namespace WireMock.Transformers;
internal interface ITransformerContext
{ {
interface ITransformerContext IFileSystemHandler FileSystemHandler { get; }
{
IFileSystemHandler FileSystemHandler { get; set; }
string ParseAndRender(string text, object model); string ParseAndRender(string text, object model);
}
object? ParseAndEvaluate(string text, object model);
} }
@@ -3,25 +3,30 @@ using Stef.Validation;
using WireMock.Handlers; using WireMock.Handlers;
using WireMock.Types; using WireMock.Types;
namespace WireMock.Transformers.Scriban namespace WireMock.Transformers.Scriban;
internal class ScribanContext : ITransformerContext
{ {
internal class ScribanContext : ITransformerContext private readonly TransformerType _transformerType;
public IFileSystemHandler FileSystemHandler { get; }
public ScribanContext(IFileSystemHandler fileSystemHandler, TransformerType transformerType)
{ {
private readonly TransformerType _transformerType; FileSystemHandler = Guard.NotNull(fileSystemHandler);
_transformerType = transformerType;
}
public IFileSystemHandler FileSystemHandler { get; set; } public string ParseAndRender(string text, object model)
{
var template = _transformerType == TransformerType.ScribanDotLiquid ? Template.ParseLiquid(text) : Template.Parse(text);
public ScribanContext(IFileSystemHandler fileSystemHandler, TransformerType transformerType) return template.Render(model, member => member.Name);
{ }
FileSystemHandler = Guard.NotNull(fileSystemHandler);
_transformerType = transformerType;
}
public string ParseAndRender(string text, object model) public object? ParseAndEvaluate(string text, object model)
{ {
var template = _transformerType == TransformerType.ScribanDotLiquid ? Template.ParseLiquid(text) : Template.Parse(text); // In case of Scriban, call ParseAndRender.
return ParseAndRender(text, model);
return template.Render(model, member => member.Name);
}
} }
} }
@@ -2,22 +2,21 @@ using WireMock.Handlers;
using WireMock.Types; using WireMock.Types;
using Stef.Validation; using Stef.Validation;
namespace WireMock.Transformers.Scriban namespace WireMock.Transformers.Scriban;
internal class ScribanContextFactory : ITransformerContextFactory
{ {
internal class ScribanContextFactory : ITransformerContextFactory private readonly IFileSystemHandler _fileSystemHandler;
private readonly TransformerType _transformerType;
public ScribanContextFactory(IFileSystemHandler fileSystemHandler, TransformerType transformerType)
{ {
private readonly IFileSystemHandler _fileSystemHandler; _fileSystemHandler = Guard.NotNull(fileSystemHandler);
private readonly TransformerType _transformerType; _transformerType = Guard.Condition(transformerType, t => t is TransformerType.Scriban or TransformerType.ScribanDotLiquid);
}
public ScribanContextFactory(IFileSystemHandler fileSystemHandler, TransformerType transformerType) public ITransformerContext Create()
{ {
_fileSystemHandler = Guard.NotNull(fileSystemHandler); return new ScribanContext(_fileSystemHandler, _transformerType);
_transformerType = Guard.Condition(transformerType, t => t == TransformerType.Scriban || t == TransformerType.ScribanDotLiquid);
}
public ITransformerContext Create()
{
return new ScribanContext(_fileSystemHandler, _transformerType);
}
} }
} }
+98 -44
View File
@@ -1,9 +1,11 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Newtonsoft.Json; using Newtonsoft.Json;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using Stef.Validation; using Stef.Validation;
using System; using WireMock.Settings;
using System.Collections.Generic;
using System.Linq;
using WireMock.Types; using WireMock.Types;
using WireMock.Util; using WireMock.Util;
@@ -11,11 +13,19 @@ namespace WireMock.Transformers;
internal class Transformer : ITransformer internal class Transformer : ITransformer
{ {
private static readonly Type[] SupportedTypes = { typeof(bool), typeof(long), typeof(int), typeof(double), typeof(Guid), typeof(DateTime), typeof(TimeSpan), typeof(Uri) };
private readonly JsonSerializer _jsonSerializer;
private readonly ITransformerContextFactory _factory; private readonly ITransformerContextFactory _factory;
public Transformer(ITransformerContextFactory factory) public Transformer(WireMockServerSettings settings, ITransformerContextFactory factory)
{ {
_factory = Guard.NotNull(factory); _factory = Guard.NotNull(factory);
_jsonSerializer = new JsonSerializer
{
Culture = Guard.NotNull(settings).Culture
};
} }
public IBodyData? TransformBody( public IBodyData? TransformBody(
@@ -109,7 +119,7 @@ internal class Transformer : ITransformer
}); });
} }
private static IBodyData? TransformBodyData(ITransformerContext transformerContext, ReplaceNodeOptions options, TransformModel model, IBodyData original, bool useTransformerForBodyAsFile) private IBodyData? TransformBodyData(ITransformerContext transformerContext, ReplaceNodeOptions options, TransformModel model, IBodyData original, bool useTransformerForBodyAsFile)
{ {
return original.DetectedBodyType switch return original.DetectedBodyType switch
{ {
@@ -139,33 +149,33 @@ internal class Transformer : ITransformer
return newHeaders; return newHeaders;
} }
private static IBodyData TransformBodyAsJson(ITransformerContext handlebarsContext, ReplaceNodeOptions options, object model, IBodyData original) private IBodyData TransformBodyAsJson(ITransformerContext transformerContext, ReplaceNodeOptions options, object model, IBodyData original)
{ {
JToken? jToken = null; JToken? jToken = null;
switch (original.BodyAsJson) switch (original.BodyAsJson)
{ {
case JObject bodyAsJObject: case JObject bodyAsJObject:
jToken = bodyAsJObject.DeepClone(); jToken = bodyAsJObject.DeepClone();
WalkNode(handlebarsContext, options, jToken, model); WalkNode(transformerContext, options, jToken, model);
break; break;
case JArray bodyAsJArray: case JArray bodyAsJArray:
jToken = bodyAsJArray.DeepClone(); jToken = bodyAsJArray.DeepClone();
WalkNode(handlebarsContext, options, jToken, model); WalkNode(transformerContext, options, jToken, model);
break; break;
case Array bodyAsArray: case Array bodyAsArray:
jToken = JArray.FromObject(bodyAsArray); jToken = JArray.FromObject(bodyAsArray, _jsonSerializer);
WalkNode(handlebarsContext, options, jToken, model); WalkNode(transformerContext, options, jToken, model);
break; break;
case string bodyAsString: case string bodyAsString:
jToken = ReplaceSingleNode(handlebarsContext, options, bodyAsString, model); jToken = ReplaceSingleNode(transformerContext, options, bodyAsString, model);
break; break;
case not null: case not null:
jToken = JObject.FromObject(original.BodyAsJson); jToken = JObject.FromObject(original.BodyAsJson, _jsonSerializer);
WalkNode(handlebarsContext, options, jToken, model); WalkNode(transformerContext, options, jToken, model);
break; break;
} }
@@ -178,9 +188,9 @@ internal class Transformer : ITransformer
}; };
} }
private static JToken ReplaceSingleNode(ITransformerContext handlebarsContext, ReplaceNodeOptions options, string stringValue, object model) private JToken ReplaceSingleNode(ITransformerContext transformerContext, ReplaceNodeOptions options, string stringValue, object model)
{ {
string transformedString = handlebarsContext.ParseAndRender(stringValue, model); string transformedString = transformerContext.ParseAndRender(stringValue, model);
if (!string.Equals(stringValue, transformedString)) if (!string.Equals(stringValue, transformedString))
{ {
@@ -202,7 +212,7 @@ internal class Transformer : ITransformer
return stringValue; return stringValue;
} }
private static void WalkNode(ITransformerContext handlebarsContext, ReplaceNodeOptions options, JToken node, object model) private void WalkNode(ITransformerContext transformerContext, ReplaceNodeOptions options, JToken node, object model)
{ {
switch (node.Type) switch (node.Type)
{ {
@@ -210,7 +220,7 @@ internal class Transformer : ITransformer
// In case of Object, loop all children. Do a ToArray() to avoid `Collection was modified` exceptions. // In case of Object, loop all children. Do a ToArray() to avoid `Collection was modified` exceptions.
foreach (var child in node.Children<JProperty>().ToArray()) foreach (var child in node.Children<JProperty>().ToArray())
{ {
WalkNode(handlebarsContext, options, child.Value, model); WalkNode(transformerContext, options, child.Value, model);
} }
break; break;
@@ -218,7 +228,7 @@ internal class Transformer : ITransformer
// In case of Array, loop all items. Do a ToArray() to avoid `Collection was modified` exceptions. // In case of Array, loop all items. Do a ToArray() to avoid `Collection was modified` exceptions.
foreach (var child in node.Children().ToArray()) foreach (var child in node.Children().ToArray())
{ {
WalkNode(handlebarsContext, options, child, model); WalkNode(transformerContext, options, child, model);
} }
break; break;
@@ -230,8 +240,8 @@ internal class Transformer : ITransformer
return; return;
} }
string transformed = handlebarsContext.ParseAndRender(stringValue!, model); var transformed = transformerContext.ParseAndEvaluate(stringValue, model);
if (!string.Equals(stringValue, transformed)) if (!Equals(stringValue, transformed))
{ {
ReplaceNodeValue(options, node, transformed); ReplaceNodeValue(options, node, transformed);
} }
@@ -240,44 +250,88 @@ internal class Transformer : ITransformer
} }
// ReSharper disable once UnusedParameter.Local // ReSharper disable once UnusedParameter.Local
private static void ReplaceNodeValue(ReplaceNodeOptions options, JToken node, string transformedString) private void ReplaceNodeValue(ReplaceNodeOptions options, JToken node, object? transformedValue)
{ {
StringUtils.TryParseQuotedString(transformedString, out var result, out _); switch (transformedValue)
if (bool.TryParse(result, out var valueAsBoolean) || bool.TryParse(transformedString, out valueAsBoolean))
{ {
node.Replace(valueAsBoolean); case JValue jValue:
return; node.Replace(jValue);
} return;
JToken value; case string transformedString:
try if (TryConvert(transformedString, out var convertedFromStringValue))
{ {
// Try to convert this string into a JsonObject node.Replace(JToken.FromObject(convertedFromStringValue, _jsonSerializer));
value = JToken.Parse(transformedString); }
} else
catch (JsonException) {
{ node.Replace(ParseAsJObject(transformedString));
// Ignore JsonException and just keep string value and convert to JToken }
value = transformedString; break;
}
node.Replace(value); case WireMockList<string> strings:
switch (strings.Count)
{
case 1:
node.Replace(ParseAsJObject(strings[0]));
return;
case > 1:
node.Replace(JToken.FromObject(strings.ToArray(), _jsonSerializer));
return;
}
break;
case { }:
if (TryConvert(transformedValue, out var convertedValue))
{
node.Replace(JToken.FromObject(convertedValue, _jsonSerializer));
}
return;
default: // It's null, skip it. Maybe remove it ?
return;
}
} }
private static IBodyData TransformBodyAsString(ITransformerContext handlebarsContext, object model, IBodyData original) private static JToken ParseAsJObject(string stringValue)
{
return JsonUtils.TryParseAsJObject(stringValue, out var parsedAsjObject) ? parsedAsjObject : stringValue;
}
private static bool TryConvert(object? transformedValue, [NotNullWhen(true)] out object? convertedValue)
{
foreach (var supportedType in SupportedTypes)
{
try
{
convertedValue = Convert.ChangeType(transformedValue, supportedType);
return convertedValue is not null;
}
catch
{
// Ignore
}
}
convertedValue = null;
return false;
}
private static IBodyData TransformBodyAsString(ITransformerContext transformerContext, object model, IBodyData original)
{ {
return new BodyData return new BodyData
{ {
Encoding = original.Encoding, Encoding = original.Encoding,
DetectedBodyType = original.DetectedBodyType, DetectedBodyType = original.DetectedBodyType,
DetectedBodyTypeFromContentType = original.DetectedBodyTypeFromContentType, DetectedBodyTypeFromContentType = original.DetectedBodyTypeFromContentType,
BodyAsString = handlebarsContext.ParseAndRender(original.BodyAsString!, model) BodyAsString = transformerContext.ParseAndRender(original.BodyAsString!, model)
}; };
} }
private static IBodyData TransformBodyAsFile(ITransformerContext handlebarsContext, object model, IBodyData original, bool useTransformerForBodyAsFile) private static IBodyData TransformBodyAsFile(ITransformerContext transformerContext, object model, IBodyData original, bool useTransformerForBodyAsFile)
{ {
string transformedBodyAsFilename = handlebarsContext.ParseAndRender(original.BodyAsFile!, model); string transformedBodyAsFilename = transformerContext.ParseAndRender(original.BodyAsFile!, model);
if (!useTransformerForBodyAsFile) if (!useTransformerForBodyAsFile)
{ {
@@ -289,12 +343,12 @@ internal class Transformer : ITransformer
}; };
} }
string text = handlebarsContext.FileSystemHandler.ReadResponseBodyAsString(transformedBodyAsFilename); string text = transformerContext.FileSystemHandler.ReadResponseBodyAsString(transformedBodyAsFilename);
return new BodyData return new BodyData
{ {
DetectedBodyType = BodyType.String, DetectedBodyType = BodyType.String,
DetectedBodyTypeFromContentType = original.DetectedBodyTypeFromContentType, DetectedBodyTypeFromContentType = original.DetectedBodyTypeFromContentType,
BodyAsString = handlebarsContext.ParseAndRender(text, model), BodyAsString = transformerContext.ParseAndRender(text, model),
BodyAsFile = transformedBodyAsFilename BodyAsFile = transformedBodyAsFilename
}; };
} }
@@ -0,0 +1,40 @@
using System;
using System.Globalization;
namespace WireMock.Util;
internal static class CultureInfoUtils
{
public static CultureInfo Parse(string? value)
{
if (value is null)
{
return CultureInfo.CurrentCulture;
}
try
{
#if !NETSTANDARD1_3
if (int.TryParse(value, out var culture))
{
return new CultureInfo(culture);
}
#endif
if (string.Equals(value, nameof(CultureInfo.CurrentCulture), StringComparison.OrdinalIgnoreCase))
{
return CultureInfo.CurrentCulture;
}
if (string.Equals(value, nameof(CultureInfo.InvariantCulture), StringComparison.OrdinalIgnoreCase))
{
return CultureInfo.InvariantCulture;
}
return new CultureInfo(value);
}
catch
{
return CultureInfo.CurrentCulture;
}
}
}
+9 -9
View File
@@ -87,8 +87,8 @@
<PackageReference Include="System.ValueTuple" Version="4.5.0" /> <PackageReference Include="System.ValueTuple" Version="4.5.0" />
<PackageReference Include="Scriban.Signed" Version="2.1.4" /> <PackageReference Include="Scriban.Signed" Version="2.1.4" />
<PackageReference Include="Nullable" Version="1.3.1"> <PackageReference Include="Nullable" Version="1.3.1">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>
</ItemGroup> </ItemGroup>
@@ -163,13 +163,13 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Handlebars.Net.Helpers" Version="2.3.10" /> <PackageReference Include="Handlebars.Net.Helpers" Version="2.3.11" />
<PackageReference Include="Handlebars.Net.Helpers.DynamicLinq" Version="2.3.10" /> <PackageReference Include="Handlebars.Net.Helpers.DynamicLinq" Version="2.3.11" />
<PackageReference Include="Handlebars.Net.Helpers.Humanizer" Version="2.3.10" /> <PackageReference Include="Handlebars.Net.Helpers.Humanizer" Version="2.3.11" />
<PackageReference Include="Handlebars.Net.Helpers.Json" Version="2.3.10" /> <PackageReference Include="Handlebars.Net.Helpers.Json" Version="2.3.11" />
<PackageReference Include="Handlebars.Net.Helpers.Random" Version="2.3.10" /> <PackageReference Include="Handlebars.Net.Helpers.Random" Version="2.3.11" />
<PackageReference Include="Handlebars.Net.Helpers.XPath" Version="2.3.10" /> <PackageReference Include="Handlebars.Net.Helpers.XPath" Version="2.3.11" />
<PackageReference Include="Handlebars.Net.Helpers.Xeger" Version="2.3.10" /> <PackageReference Include="Handlebars.Net.Helpers.Xeger" Version="2.3.11" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
@@ -108,7 +108,7 @@ public class ResponseWithHandlebarsJsonPathTests
var response = await responseBuilder.ProvideResponseAsync(_mappingMock.Object, request, _settings).ConfigureAwait(false); var response = await responseBuilder.ProvideResponseAsync(_mappingMock.Object, request, _settings).ConfigureAwait(false);
// Assert // Assert
JObject j = JObject.FromObject(response.Message.BodyData.BodyAsJson); JObject j = JObject.FromObject(response.Message.BodyData.BodyAsJson!);
Check.That(j["x"].Value<long>()).Equals(99); Check.That(j["x"].Value<long>()).Equals(99);
} }
@@ -77,7 +77,7 @@ public class ResponseWithHandlebarsRandomTests
} }
[Theory] [Theory]
[InlineData(ReplaceNodeOptions.None, JTokenType.Integer)] [InlineData(ReplaceNodeOptions.Evaluate, JTokenType.Integer)]
//[InlineData(ReplaceNodeOptions.Bool, JTokenType.String)] //[InlineData(ReplaceNodeOptions.Bool, JTokenType.String)]
//[InlineData(ReplaceNodeOptions.Integer, JTokenType.Integer)] //[InlineData(ReplaceNodeOptions.Integer, JTokenType.Integer)]
//[InlineData(ReplaceNodeOptions.Bool | ReplaceNodeOptions.Integer, JTokenType.Integer)] //[InlineData(ReplaceNodeOptions.Bool | ReplaceNodeOptions.Integer, JTokenType.Integer)]
@@ -14,6 +14,8 @@ using WireMock.Settings;
using WireMock.Types; using WireMock.Types;
using WireMock.Util; using WireMock.Util;
using Xunit; using Xunit;
using System.Globalization;
using CultureAwareTesting.xUnit;
#if NET452 #if NET452
using Microsoft.Owin; using Microsoft.Owin;
#else #else
@@ -24,6 +26,7 @@ namespace WireMock.Net.Tests.ResponseBuilders;
public class ResponseWithTransformerTests public class ResponseWithTransformerTests
{ {
private readonly Mock<IFileSystemHandler> _filesystemHandlerMock;
private readonly WireMockServerSettings _settings = new(); private readonly WireMockServerSettings _settings = new();
private const string ClientIp = "::1"; private const string ClientIp = "::1";
@@ -33,10 +36,10 @@ public class ResponseWithTransformerTests
{ {
_mappingMock = new Mock<IMapping>(); _mappingMock = new Mock<IMapping>();
var filesystemHandlerMock = new Mock<IFileSystemHandler>(MockBehavior.Strict); _filesystemHandlerMock = new Mock<IFileSystemHandler>(MockBehavior.Strict);
filesystemHandlerMock.Setup(fs => fs.ReadResponseBodyAsString(It.IsAny<string>())).Returns("abc"); _filesystemHandlerMock.Setup(fs => fs.ReadResponseBodyAsString(It.IsAny<string>())).Returns("abc");
_settings.FileSystemHandler = filesystemHandlerMock.Object; _settings.FileSystemHandler = _filesystemHandlerMock.Object;
} }
[Theory] [Theory]
@@ -333,7 +336,7 @@ public class ResponseWithTransformerTests
// Assert // Assert
Check.That(response.Message.BodyData!.BodyAsString).Equals("test"); Check.That(response.Message.BodyData!.BodyAsString).Equals("test");
Check.That(response.Message.Headers).ContainsKey("x"); Check.That(response.Message.Headers).ContainsKey("x");
Check.That(response.Message.Headers["x"]).Contains("text/plain"); Check.That(response.Message.Headers!["x"]).Contains("text/plain");
Check.That(response.Message.Headers["x"]).Contains("http://localhost/foo"); Check.That(response.Message.Headers["x"]).Contains("http://localhost/foo");
} }
@@ -358,7 +361,7 @@ public class ResponseWithTransformerTests
// Assert // Assert
Check.That(response.Message.BodyData!.BodyAsString).Equals("test"); Check.That(response.Message.BodyData!.BodyAsString).Equals("test");
Check.That(response.Message.Headers).ContainsKey("x"); Check.That(response.Message.Headers).ContainsKey("x");
Check.That(response.Message.Headers["x"]).Contains("text/plain"); Check.That(response.Message.Headers!["x"]).Contains("text/plain");
Check.That(response.Message.Headers["x"]).Contains("http://localhost/foo"); Check.That(response.Message.Headers["x"]).Contains("http://localhost/foo");
} }
@@ -394,7 +397,7 @@ public class ResponseWithTransformerTests
public async Task Response_ProvideResponse_Transformer_WithBodyAsJson_ResultAsObject(TransformerType transformerType) public async Task Response_ProvideResponse_Transformer_WithBodyAsJson_ResultAsObject(TransformerType transformerType)
{ {
// Assign // Assign
string jsonString = "{ \"things\": [ { \"name\": \"RequiredThing\" }, { \"name\": \"WireMock\" } ] }"; string jsonString = "{ \"id\": 42, \"things\": [ { \"name\": \"RequiredThing\" }, { \"name\": \"WireMock\" } ] }";
var bodyData = new BodyData var bodyData = new BodyData
{ {
BodyAsJson = JsonConvert.DeserializeObject(jsonString), BodyAsJson = JsonConvert.DeserializeObject(jsonString),
@@ -411,7 +414,49 @@ public class ResponseWithTransformerTests
var response = await responseBuilder.ProvideResponseAsync(_mappingMock.Object, request, _settings).ConfigureAwait(false); var response = await responseBuilder.ProvideResponseAsync(_mappingMock.Object, request, _settings).ConfigureAwait(false);
// Assert // Assert
Check.That(JsonConvert.SerializeObject(response.Message.BodyData.BodyAsJson)).Equals("{\"x\":\"test /foo_object\"}"); Check.That(JsonConvert.SerializeObject(response.Message.BodyData!.BodyAsJson)).Equals("{\"x\":\"test /foo_object\"}");
}
[CulturedTheory("en-US")]
[InlineData(TransformerType.Handlebars, "{ \"id\": 42 }", "{\"x\":\"test 42\",\"y\":42}")]
[InlineData(TransformerType.Scriban, "{ \"id\": 42 }", "{\"x\":\"test 42\",\"y\":42}")]
[InlineData(TransformerType.ScribanDotLiquid, "{ \"id\": 42 }", "{\"x\":\"test 42\",\"y\":42}")]
[InlineData(TransformerType.Handlebars, "{ \"id\": true }", "{\"x\":\"test True\",\"y\":true}")]
[InlineData(TransformerType.Scriban, "{ \"id\": true }", "{\"x\":\"test True\",\"y\":true}")]
[InlineData(TransformerType.ScribanDotLiquid, "{ \"id\": true }", "{\"x\":\"test True\",\"y\":true}")]
[InlineData(TransformerType.Handlebars, "{ \"id\": 0.005 }", "{\"x\":\"test 0.005\",\"y\":0.005}")]
[InlineData(TransformerType.Scriban, "{ \"id\": 0.005 }", "{\"x\":\"test 0.005\",\"y\":0.005}")]
[InlineData(TransformerType.ScribanDotLiquid, "{ \"id\": 0.005 }", "{\"x\":\"test 0.005\",\"y\":0.005}")]
public async Task Response_ProvideResponse_Transformer_WithBodyAsJson_KeepType(TransformerType transformerType, string jsonString, string expected)
{
// Assign
var culture = CultureInfo.CreateSpecificCulture("en-US");
var settings = new WireMockServerSettings
{
FileSystemHandler = _filesystemHandlerMock.Object,
Culture = culture
};
var jsonSettings = new JsonSerializerSettings
{
Culture = culture
};
var bodyData = new BodyData
{
BodyAsJson = JsonConvert.DeserializeObject(jsonString, jsonSettings),
DetectedBodyType = BodyType.Json,
Encoding = Encoding.UTF8
};
var request = new RequestMessage(new UrlDetails("http://localhost/foo_object"), "POST", ClientIp, bodyData);
var responseBuilder = Response.Create()
.WithBodyAsJson(new { x = "test {{request.BodyAsJson.id}}", y = "{{request.BodyAsJson.id}}" })
.WithTransformer(transformerType);
// Act
var response = await responseBuilder.ProvideResponseAsync(_mappingMock.Object, request, settings).ConfigureAwait(false);
// Assert
JsonConvert.SerializeObject(response.Message.BodyData!.BodyAsJson).Should().Be(expected);
} }
[Theory] [Theory]
@@ -431,7 +476,7 @@ public class ResponseWithTransformerTests
var response = await responseBuilder.ProvideResponseAsync(_mappingMock.Object, request, _settings).ConfigureAwait(false); var response = await responseBuilder.ProvideResponseAsync(_mappingMock.Object, request, _settings).ConfigureAwait(false);
// Assert // Assert
JsonConvert.SerializeObject(response.Message.BodyData.BodyAsJson).Should().Be("[{\"x\":\"test\"}]"); JsonConvert.SerializeObject(response.Message.BodyData!.BodyAsJson).Should().Be("[{\"x\":\"test\"}]");
} }
[Theory] [Theory]
@@ -452,7 +497,7 @@ public class ResponseWithTransformerTests
var response = await responseBuilder.ProvideResponseAsync(_mappingMock.Object, request, _settings).ConfigureAwait(false); var response = await responseBuilder.ProvideResponseAsync(_mappingMock.Object, request, _settings).ConfigureAwait(false);
// Assert // Assert
JsonConvert.SerializeObject(response.Message.BodyData.BodyAsJson).Should().Be("[{\"x\":\"test\"}]"); JsonConvert.SerializeObject(response.Message.BodyData!.BodyAsJson).Should().Be("[{\"x\":\"test\"}]");
} }
//[Theory] //[Theory]
@@ -488,15 +533,15 @@ public class ResponseWithTransformerTests
[InlineData(TransformerType.Handlebars, "\"a\"", "\"a\"")] [InlineData(TransformerType.Handlebars, "\"a\"", "\"a\"")]
[InlineData(TransformerType.Handlebars, "\" \"", "\" \"")] [InlineData(TransformerType.Handlebars, "\" \"", "\" \"")]
[InlineData(TransformerType.Handlebars, "\"'\"", "\"'\"")] [InlineData(TransformerType.Handlebars, "\"'\"", "\"'\"")]
[InlineData(TransformerType.Handlebars, "\"false\"", "false")] // bool is special [InlineData(TransformerType.Handlebars, "\"false\"", "\"false\"")]
[InlineData(TransformerType.Handlebars, "false", "false")] [InlineData(TransformerType.Handlebars, "false", "false")]
[InlineData(TransformerType.Handlebars, "\"true\"", "true")] // bool is special [InlineData(TransformerType.Handlebars, "\"true\"", "\"true\"")]
[InlineData(TransformerType.Handlebars, "true", "true")] [InlineData(TransformerType.Handlebars, "true", "true")]
[InlineData(TransformerType.Handlebars, "\"-42\"", "-42")] // todo [InlineData(TransformerType.Handlebars, "\"-42\"", "\"-42\"")]
[InlineData(TransformerType.Handlebars, "-42", "-42")] [InlineData(TransformerType.Handlebars, "-42", "-42")]
[InlineData(TransformerType.Handlebars, "\"2147483647\"", "2147483647")] // todo [InlineData(TransformerType.Handlebars, "\"2147483647\"", "\"2147483647\"")]
[InlineData(TransformerType.Handlebars, "2147483647", "2147483647")] [InlineData(TransformerType.Handlebars, "2147483647", "2147483647")]
[InlineData(TransformerType.Handlebars, "\"9223372036854775807\"", "9223372036854775807")] // todo [InlineData(TransformerType.Handlebars, "\"9223372036854775807\"", "\"9223372036854775807\"")]
[InlineData(TransformerType.Handlebars, "9223372036854775807", "9223372036854775807")] [InlineData(TransformerType.Handlebars, "9223372036854775807", "9223372036854775807")]
public async Task Response_ProvideResponse_Transformer_WithBodyAsJson_And_ReplaceNodeOptionsKeep(TransformerType transformerType, string value, string expected) public async Task Response_ProvideResponse_Transformer_WithBodyAsJson_And_ReplaceNodeOptionsKeep(TransformerType transformerType, string value, string expected)
{ {
@@ -511,50 +556,13 @@ public class ResponseWithTransformerTests
var responseBuilder = Response.Create() var responseBuilder = Response.Create()
.WithBodyAsJson(new { text = "{{request.bodyAsJson.x}}" }) .WithBodyAsJson(new { text = "{{request.bodyAsJson.x}}" })
.WithTransformer(transformerType, false, ReplaceNodeOptions.None); .WithTransformer(transformerType, false, ReplaceNodeOptions.Evaluate);
// Act // Act
var response = await responseBuilder.ProvideResponseAsync(_mappingMock.Object, request, _settings).ConfigureAwait(false); var response = await responseBuilder.ProvideResponseAsync(_mappingMock.Object, request, _settings).ConfigureAwait(false);
// Assert // Assert
JsonConvert.SerializeObject(response.Message.BodyData.BodyAsJson).Should().Be($"{{\"text\":{expected}}}"); JsonConvert.SerializeObject(response.Message.BodyData!.BodyAsJson).Should().Be($"{{\"text\":{expected}}}");
}
[Theory]
[InlineData(TransformerType.Handlebars, "\"\"", "\"\"")]
[InlineData(TransformerType.Handlebars, "\"a\"", "\"a\"")]
[InlineData(TransformerType.Handlebars, "\" \"", "\" \"")]
[InlineData(TransformerType.Handlebars, "\"'\"", "\"'\"")]
[InlineData(TransformerType.Handlebars, "\"false\"", "false")] // bool is special
[InlineData(TransformerType.Handlebars, "false", "false")]
[InlineData(TransformerType.Handlebars, "\"true\"", "true")] // bool is special
[InlineData(TransformerType.Handlebars, "true", "true")]
[InlineData(TransformerType.Handlebars, "\"-42\"", "\"-42\"")]
[InlineData(TransformerType.Handlebars, "-42", "\"-42\"")]
[InlineData(TransformerType.Handlebars, "\"2147483647\"", "\"2147483647\"")]
[InlineData(TransformerType.Handlebars, "2147483647", "\"2147483647\"")]
[InlineData(TransformerType.Handlebars, "\"9223372036854775807\"", "\"9223372036854775807\"")]
[InlineData(TransformerType.Handlebars, "9223372036854775807", "\"9223372036854775807\"")]
public async Task Response_ProvideResponse_Transformer_WithBodyAsJsonWithExtraQuotes_AlwaysMakesString(TransformerType transformerType, string value, string expected)
{
string jsonString = $"{{ \"x\": {value} }}";
var bodyData = new BodyData
{
BodyAsJson = JsonConvert.DeserializeObject(jsonString),
DetectedBodyType = BodyType.Json,
Encoding = Encoding.UTF8
};
var request = new RequestMessage(new UrlDetails("http://localhost/foo_object"), "POST", ClientIp, bodyData);
var responseBuilder = Response.Create()
.WithBodyAsJson(new { text = "\"{{request.bodyAsJson.x}}\"" })
.WithTransformer(transformerType);
// Act
var response = await responseBuilder.ProvideResponseAsync(_mappingMock.Object, request, _settings).ConfigureAwait(false);
// Assert
JsonConvert.SerializeObject(response.Message.BodyData.BodyAsJson).Should().Be($"{{\"text\":{expected}}}");
} }
[Theory] [Theory]
@@ -581,7 +589,7 @@ public class ResponseWithTransformerTests
var response = await responseBuilder.ProvideResponseAsync(_mappingMock.Object, request, _settings).ConfigureAwait(false); var response = await responseBuilder.ProvideResponseAsync(_mappingMock.Object, request, _settings).ConfigureAwait(false);
// Assert // Assert
Check.That(JsonConvert.SerializeObject(response.Message.BodyData.BodyAsJson)).Equals("[\"first\",\"/foo_array\",\"test 1\",\"test 2\",\"last\"]"); Check.That(JsonConvert.SerializeObject(response.Message.BodyData!.BodyAsJson)).Equals("[\"first\",\"/foo_array\",\"test 1\",\"test 2\",\"last\"]");
} }
[Fact] [Fact]
@@ -598,7 +606,7 @@ public class ResponseWithTransformerTests
var response = await responseBuilder.ProvideResponseAsync(_mappingMock.Object, request, _settings).ConfigureAwait(false); var response = await responseBuilder.ProvideResponseAsync(_mappingMock.Object, request, _settings).ConfigureAwait(false);
// Assert // Assert
Check.That(response.Message.BodyData.BodyAsFile).Equals(@"c:\1\test.xml"); Check.That(response.Message.BodyData!.BodyAsFile).Equals(@"c:\1\test.xml");
} }
[Theory(Skip = @"Does not work in Scriban --> c:\\[""1""]\\test.xml")] [Theory(Skip = @"Does not work in Scriban --> c:\\[""1""]\\test.xml")]
@@ -617,7 +625,7 @@ public class ResponseWithTransformerTests
var response = await responseBuilder.ProvideResponseAsync(_mappingMock.Object, request, _settings).ConfigureAwait(false); var response = await responseBuilder.ProvideResponseAsync(_mappingMock.Object, request, _settings).ConfigureAwait(false);
// Assert // Assert
Check.That(response.Message.BodyData.BodyAsFile).Equals(@"c:\1\test.xml"); Check.That(response.Message.BodyData!.BodyAsFile).Equals(@"c:\1\test.xml");
} }
[Theory] [Theory]
@@ -642,7 +650,7 @@ public class ResponseWithTransformerTests
var response = await responseBuilder.ProvideResponseAsync(_mappingMock.Object, request, _settings).ConfigureAwait(false); var response = await responseBuilder.ProvideResponseAsync(_mappingMock.Object, request, _settings).ConfigureAwait(false);
// Assert // Assert
Check.That(response.Message.BodyData.BodyAsFile).Equals(@"c:\1\test.xml"); Check.That(response.Message.BodyData!.BodyAsFile).Equals(@"c:\1\test.xml");
Check.That(response.Message.BodyData.DetectedBodyType).Equals(BodyType.String); Check.That(response.Message.BodyData.DetectedBodyType).Equals(BodyType.String);
Check.That(response.Message.BodyData!.BodyAsString).Equals("<xml MyUniqueNumber=\"1\"></xml>"); Check.That(response.Message.BodyData!.BodyAsString).Equals("<xml MyUniqueNumber=\"1\"></xml>");
} }
@@ -671,14 +679,15 @@ public class ResponseWithTransformerTests
var response = await responseBuilder.ProvideResponseAsync(_mappingMock.Object, request, _settings).ConfigureAwait(false); var response = await responseBuilder.ProvideResponseAsync(_mappingMock.Object, request, _settings).ConfigureAwait(false);
// Assert // Assert
Check.That(JsonConvert.SerializeObject(response.Message.BodyData.BodyAsJson)).Equals("\"test\""); Check.That(JsonConvert.SerializeObject(response.Message.BodyData!.BodyAsJson)).Equals("\"test\"");
} }
[Fact(Skip = "todo...")] [Fact(Skip = "todo...")]
//[Fact]
public async Task Response_ProvideResponse_Handlebars_WithBodyAsJson_ResultAsTemplatedString() public async Task Response_ProvideResponse_Handlebars_WithBodyAsJson_ResultAsTemplatedString()
{ {
// Assign // Assign
string jsonString = "{ \"name\": \"WireMock\" }"; string jsonString = "{ \"name\": \"WireMock\", \"id\": 12345 }";
var bodyData = new BodyData var bodyData = new BodyData
{ {
BodyAsJson = JsonConvert.DeserializeObject(jsonString), BodyAsJson = JsonConvert.DeserializeObject(jsonString),
@@ -688,14 +697,14 @@ public class ResponseWithTransformerTests
var request = new RequestMessage(new UrlDetails("http://localhost/foo_object"), "POST", ClientIp, bodyData); var request = new RequestMessage(new UrlDetails("http://localhost/foo_object"), "POST", ClientIp, bodyData);
var responseBuilder = Response.Create() var responseBuilder = Response.Create()
.WithBodyAsJson("{{{request.BodyAsJson}}}") .WithBodyAsJson("{{{request.BodyAsJson.name}}}")
.WithTransformer(); .WithTransformer();
// Act // Act
var response = await responseBuilder.ProvideResponseAsync(_mappingMock.Object, request, _settings).ConfigureAwait(false); var response = await responseBuilder.ProvideResponseAsync(_mappingMock.Object, request, _settings).ConfigureAwait(false);
// Assert // Assert
Check.That(JsonConvert.SerializeObject(response.Message.BodyData.BodyAsJson)).Equals("{\"name\":\"WireMock\"}"); Check.That(JsonConvert.SerializeObject(response.Message.BodyData!.BodyAsJson)).Equals("{\"name\":\"WireMock\"}");
} }
[Theory(Skip = "{{{ }}} Does not work in Scriban")] [Theory(Skip = "{{{ }}} Does not work in Scriban")]
@@ -721,7 +730,7 @@ public class ResponseWithTransformerTests
var response = await responseBuilder.ProvideResponseAsync(_mappingMock.Object, request, _settings).ConfigureAwait(false); var response = await responseBuilder.ProvideResponseAsync(_mappingMock.Object, request, _settings).ConfigureAwait(false);
// Assert // Assert
Check.That(JsonConvert.SerializeObject(response.Message.BodyData.BodyAsJson)).Equals("{\"name\":\"WireMock\"}"); Check.That(JsonConvert.SerializeObject(response.Message.BodyData!.BodyAsJson)).Equals("{\"name\":\"WireMock\"}");
} }
[Theory] [Theory]
@@ -749,7 +758,7 @@ public class ResponseWithTransformerTests
var response = await responseBuilder.ProvideResponseAsync(_mappingMock.Object, request, _settings).ConfigureAwait(false); var response = await responseBuilder.ProvideResponseAsync(_mappingMock.Object, request, _settings).ConfigureAwait(false);
// Assert // Assert
response.Message.BodyData.BodyAsString.Should().Be(text); response.Message.BodyData!.BodyAsString.Should().Be(text);
response.Message.BodyData.Encoding.Should().Be(enc); response.Message.BodyData.Encoding.Should().Be(enc);
} }
} }
@@ -1,52 +1,59 @@
using System;
using FluentAssertions; using FluentAssertions;
using HandlebarsDotNet;
using Moq; using Moq;
using WireMock.Handlers; using WireMock.Handlers;
using WireMock.Settings;
using WireMock.Transformers.Handlebars; using WireMock.Transformers.Handlebars;
using Xunit; using Xunit;
namespace WireMock.Net.Tests.Transformers.Handlebars namespace WireMock.Net.Tests.Transformers.Handlebars;
public class HandlebarsContextFactoryTests
{ {
public class HandlebarsContextFactoryTests private readonly Mock<IFileSystemHandler> _fileSystemHandlerMock = new();
[Fact]
public void Create_WithNullAction_DoesNotInvokeAction()
{ {
private readonly Mock<IFileSystemHandler> _fileSystemHandlerMock = new Mock<IFileSystemHandler>(); // Arrange
var settings = new WireMockServerSettings
[Fact]
public void Create_WithNullAction_DoesNotInvokeAction()
{ {
// Arrange FileSystemHandler = _fileSystemHandlerMock.Object
var sut = new HandlebarsContextFactory(_fileSystemHandlerMock.Object, null); };
var sut = new HandlebarsContextFactory(settings);
// Act // Act
var result = sut.Create(); var result = sut.Create();
// Assert // Assert
result.Should().NotBeNull(); result.Should().NotBeNull();
} }
[Fact] [Fact]
public void Create_WithAction_InvokesAction() public void Create_WithAction_InvokesAction()
{
// Arrange
int num = 0;
var settings = new WireMockServerSettings
{ {
// Arrange FileSystemHandler = _fileSystemHandlerMock.Object,
int num = 0; HandlebarsRegistrationCallback = (ctx, fs) =>
Action<IHandlebars, IFileSystemHandler> action = (ctx, fs) =>
{ {
ctx.Should().NotBeNull(); ctx.Should().NotBeNull();
fs.Should().NotBeNull(); fs.Should().NotBeNull();
num++; num++;
}; }
var sut = new HandlebarsContextFactory(_fileSystemHandlerMock.Object, action); };
// Act var sut = new HandlebarsContextFactory(settings);
var result = sut.Create();
// Assert // Act
result.Should().NotBeNull(); var result = sut.Create();
// Verify // Assert
num.Should().Be(1); result.Should().NotBeNull();
}
// Verify
num.Should().Be(1);
} }
} }
@@ -20,6 +20,10 @@
<GenerateBindingRedirectsOutputType>true</GenerateBindingRedirectsOutputType> <GenerateBindingRedirectsOutputType>true</GenerateBindingRedirectsOutputType>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(TargetFramework)' == 'net452' or '$(TargetFramework)' == 'net461'">
<DefineConstants>NETFRAMEWORK</DefineConstants>
</PropertyGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\src\WireMock.Net.Abstractions\WireMock.Net.Abstractions.csproj" /> <ProjectReference Include="..\..\src\WireMock.Net.Abstractions\WireMock.Net.Abstractions.csproj" />
<ProjectReference Include="..\..\src\WireMock.Net.FluentAssertions\WireMock.Net.FluentAssertions.csproj" /> <ProjectReference Include="..\..\src\WireMock.Net.FluentAssertions\WireMock.Net.FluentAssertions.csproj" />
@@ -35,8 +39,10 @@
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>
<PackageReference Include="CultureAwareTesting.xUnit" Version="0.0.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.0" />
<PackageReference Include="xunit" Version="2.4.1" /> <PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.core" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3"> <PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>