diff --git a/src/WireMock.Net.Abstractions/Admin/Settings/SettingsModel.cs b/src/WireMock.Net.Abstractions/Admin/Settings/SettingsModel.cs index e9f4eccd..602d0cb1 100644 --- a/src/WireMock.Net.Abstractions/Admin/Settings/SettingsModel.cs +++ b/src/WireMock.Net.Abstractions/Admin/Settings/SettingsModel.cs @@ -1,82 +1,92 @@ using System.Text.RegularExpressions; using WireMock.Handlers; +using WireMock.Types; -namespace WireMock.Admin.Settings +namespace WireMock.Admin.Settings; + +/// +/// Settings +/// +[FluentBuilder.AutoGenerateBuilder] +public class SettingsModel { /// - /// Settings + /// Gets or sets the global delay in milliseconds. /// - [FluentBuilder.AutoGenerateBuilder] - public class SettingsModel - { - /// - /// Gets or sets the global delay in milliseconds. - /// - public int? GlobalProcessingDelay { get; set; } + public int? GlobalProcessingDelay { get; set; } - /// - /// Gets or sets if partial mapping is allowed. - /// - public bool? AllowPartialMapping { get; set; } + /// + /// Gets or sets if partial mapping is allowed. + /// + public bool? AllowPartialMapping { get; set; } - /// - /// Gets or sets the RequestLog expiration in hours - /// - public int? RequestLogExpirationDuration { get; set; } + /// + /// Gets or sets the RequestLog expiration in hours + /// + public int? RequestLogExpirationDuration { get; set; } - /// - /// Gets or sets the MaxRequestLog count. - /// - public int? MaxRequestLogCount { get; set; } + /// + /// Gets or sets the MaxRequestLog count. + /// + public int? MaxRequestLogCount { get; set; } - /// - /// Allow a Body for all HTTP Methods. (default set to false). - /// - public bool? AllowBodyForAllHttpMethods { get; set; } + /// + /// Allow a Body for all HTTP Methods. (default set to false). + /// + public bool? AllowBodyForAllHttpMethods { get; set; } - /// - /// Handle all requests synchronously. (default set to false). - /// - public bool? HandleRequestsSynchronously { get; set; } + /// + /// Handle all requests synchronously. (default set to false). + /// + public bool? HandleRequestsSynchronously { get; set; } - /// - /// Throw an exception when the Matcher fails because of invalid input. (default set to false). - /// - public bool? ThrowExceptionWhenMatcherFails { get; set; } + /// + /// Throw an exception when the Matcher fails because of invalid input. (default set to false). + /// + public bool? ThrowExceptionWhenMatcherFails { get; set; } - /// - /// Use the RegexExtended instead of the default . (default set to true). - /// - public bool? UseRegexExtended { get; set; } + /// + /// Use the RegexExtended instead of the default . (default set to true). + /// + public bool? UseRegexExtended { get; set; } - /// - /// Save unmatched requests to a file using the . (default set to false). - /// - public bool? SaveUnmatchedRequests { get; set; } + /// + /// Save unmatched requests to a file using the . (default set to false). + /// + public bool? SaveUnmatchedRequests { get; set; } - /// - /// Gets or sets if the static mappings should be read at startup. - /// - public bool? ReadStaticMappings { get; set; } + /// + /// Gets or sets if the static mappings should be read at startup. + /// + public bool? ReadStaticMappings { get; set; } - /// - /// Watch the static mapping files + folder for changes when running. - /// - public bool? WatchStaticMappings { get; set; } + /// + /// Watch the static mapping files + folder for changes when running. + /// + public bool? WatchStaticMappings { get; set; } - /// - /// A value indicating whether subdirectories within the static mappings path should be monitored. - /// - public bool? WatchStaticMappingsInSubdirectories { get; set; } + /// + /// A value indicating whether subdirectories within the static mappings path should be monitored. + /// + public bool? WatchStaticMappingsInSubdirectories { get; set; } - /// - /// Policies to use when using CORS. By default CORS is disabled. [Optional] - /// - public string? CorsPolicyOptions { get; set; } + /// + /// Policies to use when using CORS. By default CORS is disabled. [Optional] + /// + public string? CorsPolicyOptions { get; set; } - /// - /// The proxy and record settings. - /// - public ProxyAndRecordSettingsModel? ProxyAndRecordSettings { get; set; } - } + /// + /// The proxy and record settings. + /// + public ProxyAndRecordSettingsModel? ProxyAndRecordSettings { get; set; } + + /// + /// Defines on which scheme (http/https) to host. (This overrides the UseSSL value). + /// + public HostingScheme? HostingScheme { get; set; } + + /// + /// 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). + /// + public bool? DoNotSaveDynamicResponseInLogEntry { get; set; } } \ No newline at end of file diff --git a/src/WireMock.Net.Abstractions/Models/IBodyData.cs b/src/WireMock.Net.Abstractions/Models/IBodyData.cs index 38912666..faafd2c0 100644 --- a/src/WireMock.Net.Abstractions/Models/IBodyData.cs +++ b/src/WireMock.Net.Abstractions/Models/IBodyData.cs @@ -1,6 +1,4 @@ -using System.Diagnostics.CodeAnalysis; using System.Text; -using JetBrains.Annotations; using WireMock.Types; namespace WireMock.Util; @@ -59,4 +57,9 @@ public interface IBodyData /// The body encoding. /// Encoding? Encoding { get; set; } + + /// + /// Defines if this BodyData is the result of a dynamically created response-string. ( + /// + public string? IsFuncUsed { get; set; } } \ No newline at end of file diff --git a/src/WireMock.Net/Logging/LogEntry.cs b/src/WireMock.Net/Logging/LogEntry.cs index 4be2861c..a5b556d1 100644 --- a/src/WireMock.Net/Logging/LogEntry.cs +++ b/src/WireMock.Net/Logging/LogEntry.cs @@ -12,13 +12,13 @@ public class LogEntry : ILogEntry public Guid Guid { get; set; } /// - public IRequestMessage RequestMessage { get; set; } + public IRequestMessage RequestMessage { get; set; } = null!; /// - public IResponseMessage ResponseMessage { get; set; } + public IResponseMessage ResponseMessage { get; set; } = null!; /// - public IRequestMatchResult RequestMatchResult { get; set; } + public IRequestMatchResult RequestMatchResult { get; set; } = null!; /// public Guid? MappingGuid { get; set; } @@ -33,5 +33,5 @@ public class LogEntry : ILogEntry public string? PartialMappingTitle { get; set; } /// - public IRequestMatchResult PartialMatchResult { get; set; } + public IRequestMatchResult PartialMatchResult { get; set; } = null!; } \ No newline at end of file diff --git a/src/WireMock.Net/Matchers/LinqMatcher.cs b/src/WireMock.Net/Matchers/LinqMatcher.cs index ec41f79c..80167b8c 100644 --- a/src/WireMock.Net/Matchers/LinqMatcher.cs +++ b/src/WireMock.Net/Matchers/LinqMatcher.cs @@ -21,6 +21,7 @@ public class LinqMatcher : IObjectMatcher, IStringMatcher /// public MatchBehaviour MatchBehaviour { get; } + /// public bool ThrowException { get; } diff --git a/src/WireMock.Net/Models/BodyData.cs b/src/WireMock.Net/Models/BodyData.cs index d9640637..7d00e971 100644 --- a/src/WireMock.Net/Models/BodyData.cs +++ b/src/WireMock.Net/Models/BodyData.cs @@ -1,41 +1,43 @@ using System.Text; using WireMock.Types; -namespace WireMock.Util +namespace WireMock.Util; + +/// +/// BodyData +/// +public class BodyData : IBodyData { - /// - /// BodyData - /// - public class BodyData : IBodyData - { - /// - public Encoding? Encoding { get; set; } + /// + public Encoding? Encoding { get; set; } - /// - public string? BodyAsString { get; set; } + /// + public string? BodyAsString { get; set; } - /// - public object? BodyAsJson { get; set; } + /// + public object? BodyAsJson { get; set; } - /// - public byte[]? BodyAsBytes { get; set; } + /// + public byte[]? BodyAsBytes { get; set; } - /// - public bool? BodyAsJsonIndented { get; set; } + /// + public bool? BodyAsJsonIndented { get; set; } - /// - public string? BodyAsFile { get; set; } + /// + public string? BodyAsFile { get; set; } - /// - public bool? BodyAsFileIsCached { get; set; } + /// + public bool? BodyAsFileIsCached { get; set; } - /// - public BodyType? DetectedBodyType { get; set; } + /// + public BodyType? DetectedBodyType { get; set; } - /// - public BodyType? DetectedBodyTypeFromContentType { get; set; } + /// + public BodyType? DetectedBodyTypeFromContentType { get; set; } - /// - public string? DetectedCompression { get; set; } - } + /// + public string? DetectedCompression { get; set; } + + /// + public string? IsFuncUsed { get; set; } } \ No newline at end of file diff --git a/src/WireMock.Net/Owin/IWireMockMiddlewareOptions.cs b/src/WireMock.Net/Owin/IWireMockMiddlewareOptions.cs index 1594bfcd..929e3994 100644 --- a/src/WireMock.Net/Owin/IWireMockMiddlewareOptions.cs +++ b/src/WireMock.Net/Owin/IWireMockMiddlewareOptions.cs @@ -69,4 +69,6 @@ internal interface IWireMockMiddlewareOptions bool CustomCertificateDefined { get; } bool? SaveUnmatchedRequests { get; set; } + + public bool? DoNotSaveDynamicResponseInLogEntry { get; set; } } \ No newline at end of file diff --git a/src/WireMock.Net/Owin/WireMockMiddleware.cs b/src/WireMock.Net/Owin/WireMockMiddleware.cs index 23445942..d78d4a35 100644 --- a/src/WireMock.Net/Owin/WireMockMiddleware.cs +++ b/src/WireMock.Net/Owin/WireMockMiddleware.cs @@ -32,22 +32,25 @@ namespace WireMock.Owin private readonly IOwinRequestMapper _requestMapper; private readonly IOwinResponseMapper _responseMapper; private readonly IMappingMatcher _mappingMatcher; + private readonly LogEntryMapper _logEntryMapper; #if !USE_ASPNETCORE public WireMockMiddleware(Next next, IWireMockMiddlewareOptions options, IOwinRequestMapper requestMapper, IOwinResponseMapper responseMapper, IMappingMatcher mappingMatcher) : base(next) { - _options = Guard.NotNull(options, nameof(options)); - _requestMapper = Guard.NotNull(requestMapper, nameof(requestMapper)); - _responseMapper = Guard.NotNull(responseMapper, nameof(responseMapper)); - _mappingMatcher = Guard.NotNull(mappingMatcher, nameof(mappingMatcher)); + _options = Guard.NotNull(options); + _requestMapper = Guard.NotNull(requestMapper); + _responseMapper = Guard.NotNull(responseMapper); + _mappingMatcher = Guard.NotNull(mappingMatcher); + _logEntryMapper = new LogEntryMapper(options); } #else public WireMockMiddleware(Next next, IWireMockMiddlewareOptions options, IOwinRequestMapper requestMapper, IOwinResponseMapper responseMapper, IMappingMatcher mappingMatcher) { - _options = Guard.NotNull(options, nameof(options)); - _requestMapper = Guard.NotNull(requestMapper, nameof(requestMapper)); - _responseMapper = Guard.NotNull(responseMapper, nameof(responseMapper)); - _mappingMatcher = Guard.NotNull(mappingMatcher, nameof(mappingMatcher)); + _options = Guard.NotNull(options); + _requestMapper = Guard.NotNull(requestMapper); + _responseMapper = Guard.NotNull(responseMapper); + _mappingMatcher = Guard.NotNull(mappingMatcher); + _logEntryMapper = new LogEntryMapper(options); } #endif @@ -264,7 +267,7 @@ namespace WireMock.Owin private void LogRequest(LogEntry entry, bool addRequest) { - _options.Logger.DebugRequestResponse(LogEntryMapper.Map(entry), entry.RequestMessage.Path.StartsWith("/__admin/")); + _options.Logger.DebugRequestResponse(_logEntryMapper.Map(entry), entry.RequestMessage.Path.StartsWith("/__admin/")); if (addRequest) { diff --git a/src/WireMock.Net/Owin/WireMockMiddlewareOptions.cs b/src/WireMock.Net/Owin/WireMockMiddlewareOptions.cs index fffdfde9..568d1bd1 100644 --- a/src/WireMock.Net/Owin/WireMockMiddlewareOptions.cs +++ b/src/WireMock.Net/Owin/WireMockMiddlewareOptions.cs @@ -84,4 +84,7 @@ internal class WireMockMiddlewareOptions : IWireMockMiddlewareOptions /// public bool? SaveUnmatchedRequests { get; set; } + + /// + public bool? DoNotSaveDynamicResponseInLogEntry { get; set; } } \ No newline at end of file diff --git a/src/WireMock.Net/ResponseBuilders/Response.WithBody.cs b/src/WireMock.Net/ResponseBuilders/Response.WithBody.cs index a73f4a8c..852eafa1 100644 --- a/src/WireMock.Net/ResponseBuilders/Response.WithBody.cs +++ b/src/WireMock.Net/ResponseBuilders/Response.WithBody.cs @@ -21,7 +21,8 @@ public partial class Response { DetectedBodyType = BodyType.String, BodyAsString = bodyFactory(req), - Encoding = encoding ?? Encoding.UTF8 + Encoding = encoding ?? Encoding.UTF8, + IsFuncUsed = "Func" } }); } @@ -37,7 +38,8 @@ public partial class Response { DetectedBodyType = BodyType.String, BodyAsString = await bodyFactory(req).ConfigureAwait(false), - Encoding = encoding ?? Encoding.UTF8 + Encoding = encoding ?? Encoding.UTF8, + IsFuncUsed = "Func>" } }); } diff --git a/src/WireMock.Net/ResponseBuilders/Response.cs b/src/WireMock.Net/ResponseBuilders/Response.cs index 536feb87..23221f8a 100644 --- a/src/WireMock.Net/ResponseBuilders/Response.cs +++ b/src/WireMock.Net/ResponseBuilders/Response.cs @@ -325,15 +325,15 @@ public partial class Response : IResponseBuilder break; default: - throw new NotImplementedException($"TransformerType '{TransformerType}' is not supported."); + throw new NotSupportedException($"TransformerType '{TransformerType}' is not supported."); } return (responseMessageTransformer.Transform(mapping, requestMessage, responseMessage, UseTransformerForBodyAsFile, TransformerReplaceNodeOptions), null); } - if (!UseTransformer && ResponseMessage.BodyData?.BodyAsFileIsCached == true) + if (!UseTransformer && ResponseMessage.BodyData?.BodyAsFileIsCached == true && responseMessage.BodyData?.BodyAsFile is not null) { - ResponseMessage.BodyData.BodyAsBytes = settings.FileSystemHandler.ReadResponseBodyAsFile(responseMessage.BodyData!.BodyAsFile); + ResponseMessage.BodyData.BodyAsBytes = settings.FileSystemHandler.ReadResponseBodyAsFile(responseMessage.BodyData.BodyAsFile); } return (responseMessage, null); diff --git a/src/WireMock.Net/Serialization/LogEntryMapper.cs b/src/WireMock.Net/Serialization/LogEntryMapper.cs index 6f9d9c81..d2dfe243 100644 --- a/src/WireMock.Net/Serialization/LogEntryMapper.cs +++ b/src/WireMock.Net/Serialization/LogEntryMapper.cs @@ -1,16 +1,25 @@ using System.Linq; +using Stef.Validation; using WireMock.Admin.Mappings; using WireMock.Admin.Requests; using WireMock.Logging; using WireMock.Matchers.Request; +using WireMock.Owin; using WireMock.ResponseBuilders; using WireMock.Types; namespace WireMock.Serialization; -internal static class LogEntryMapper +internal class LogEntryMapper { - public static LogEntryModel Map(ILogEntry logEntry) + private readonly IWireMockMiddlewareOptions _options; + + public LogEntryMapper(IWireMockMiddlewareOptions options) + { + _options = Guard.NotNull(options); + } + + public LogEntryModel Map(ILogEntry logEntry) { var logRequestModel = new LogRequestModel { @@ -78,25 +87,7 @@ internal static class LogEntryMapper logResponseModel.DetectedBodyType = logEntry.ResponseMessage.BodyData.DetectedBodyType; logResponseModel.DetectedBodyTypeFromContentType = logEntry.ResponseMessage.BodyData.DetectedBodyTypeFromContentType; - switch (logEntry.ResponseMessage.BodyData.DetectedBodyType) - { - case BodyType.String: - logResponseModel.Body = logEntry.ResponseMessage.BodyData.BodyAsString; - break; - - case BodyType.Json: - logResponseModel.BodyAsJson = logEntry.ResponseMessage.BodyData.BodyAsJson; - break; - - case BodyType.Bytes: - logResponseModel.BodyAsBytes = logEntry.ResponseMessage.BodyData.BodyAsBytes; - break; - - case BodyType.File: - logResponseModel.BodyAsFile = logEntry.ResponseMessage.BodyData.BodyAsFile; - logResponseModel.BodyAsFileIsCached = logEntry.ResponseMessage.BodyData.BodyAsFileIsCached; - break; - } + MapBody(logEntry, logResponseModel); logResponseModel.BodyEncoding = logEntry.ResponseMessage.BodyData.Encoding != null ? new EncodingModel @@ -124,6 +115,36 @@ internal static class LogEntryMapper }; } + private void MapBody(ILogEntry logEntry, LogResponseModel logResponseModel) + { + switch (logEntry.ResponseMessage.BodyData!.DetectedBodyType) + { + case BodyType.String: + if (!string.IsNullOrEmpty(logEntry.ResponseMessage.BodyData.IsFuncUsed) && _options.DoNotSaveDynamicResponseInLogEntry == true) + { + logResponseModel.Body = logEntry.ResponseMessage.BodyData.IsFuncUsed; + } + else + { + logResponseModel.Body = logEntry.ResponseMessage.BodyData.BodyAsString; + } + break; + + case BodyType.Json: + logResponseModel.BodyAsJson = logEntry.ResponseMessage.BodyData.BodyAsJson; + break; + + case BodyType.Bytes: + logResponseModel.BodyAsBytes = logEntry.ResponseMessage.BodyData.BodyAsBytes; + break; + + case BodyType.File: + logResponseModel.BodyAsFile = logEntry.ResponseMessage.BodyData.BodyAsFile; + logResponseModel.BodyAsFileIsCached = logEntry.ResponseMessage.BodyData.BodyAsFileIsCached; + break; + } + } + private static LogRequestMatchModel? Map(IRequestMatchResult? matchResult) { if (matchResult == null) diff --git a/src/WireMock.Net/Server/WireMockServer.Admin.cs b/src/WireMock.Net/Server/WireMockServer.Admin.cs index 2609a554..f8399fca 100644 --- a/src/WireMock.Net/Server/WireMockServer.Admin.cs +++ b/src/WireMock.Net/Server/WireMockServer.Admin.cs @@ -277,6 +277,8 @@ public partial class WireMockServer UseRegexExtended = _settings.UseRegexExtended, WatchStaticMappings = _settings.WatchStaticMappings, WatchStaticMappingsInSubdirectories = _settings.WatchStaticMappingsInSubdirectories, + HostingScheme = _settings.HostingScheme, + DoNotSaveDynamicResponseInLogEntry = _settings.DoNotSaveDynamicResponseInLogEntry, #if USE_ASPNETCORE CorsPolicyOptions = _settings.CorsPolicyOptions?.ToString() @@ -305,6 +307,7 @@ public partial class WireMockServer _settings.UseRegexExtended = settings.UseRegexExtended; _settings.WatchStaticMappings = settings.WatchStaticMappings; _settings.WatchStaticMappingsInSubdirectories = settings.WatchStaticMappingsInSubdirectories; + _settings.DoNotSaveDynamicResponseInLogEntry = settings.DoNotSaveDynamicResponseInLogEntry; InitSettings(_settings); @@ -525,7 +528,7 @@ public partial class WireMockServer return ResponseMessageBuilder.Create("Request not found", 404); } - var model = LogEntryMapper.Map(entry); + var model = new LogEntryMapper(_options).Map(entry); return ToJson(model); } @@ -546,9 +549,10 @@ public partial class WireMockServer #region Requests private IResponseMessage RequestsGet(IRequestMessage requestMessage) { + var logEntryMapper = new LogEntryMapper(_options); var result = LogEntries .Where(r => !r.RequestMessage.Path.StartsWith("/__admin/")) - .Select(LogEntryMapper.Map); + .Select(logEntryMapper.Map); return ToJson(result); } @@ -578,7 +582,8 @@ public partial class WireMockServer } } - var result = dict.OrderBy(x => x.Value.AverageTotalScore).Select(x => x.Key).Select(LogEntryMapper.Map); + var logEntryMapper = new LogEntryMapper(_options); + var result = dict.OrderBy(x => x.Value.AverageTotalScore).Select(x => x.Key).Select(logEntryMapper.Map); return ToJson(result); } diff --git a/src/WireMock.Net/Server/WireMockServer.cs b/src/WireMock.Net/Server/WireMockServer.cs index cb6248f9..77efceb3 100644 --- a/src/WireMock.Net/Server/WireMockServer.cs +++ b/src/WireMock.Net/Server/WireMockServer.cs @@ -295,6 +295,7 @@ public partial class WireMockServer : IWireMockServer _options.DisableJsonBodyParsing = _settings.DisableJsonBodyParsing; _options.HandleRequestsSynchronously = settings.HandleRequestsSynchronously; _options.SaveUnmatchedRequests = settings.SaveUnmatchedRequests; + _options.DoNotSaveDynamicResponseInLogEntry = settings.DoNotSaveDynamicResponseInLogEntry; if (settings.CustomCertificateDefined) { diff --git a/src/WireMock.Net/Settings/WireMockServerSettings.cs b/src/WireMock.Net/Settings/WireMockServerSettings.cs index f8b36585..4b525fd2 100644 --- a/src/WireMock.Net/Settings/WireMockServerSettings.cs +++ b/src/WireMock.Net/Settings/WireMockServerSettings.cs @@ -252,6 +252,12 @@ namespace WireMock.Settings [PublicAPI] public bool? SaveUnmatchedRequests { get; set; } + /// + /// 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). + /// + [PublicAPI] + public bool? DoNotSaveDynamicResponseInLogEntry { get; set; } + /// /// Custom matcher mappings for static mappings /// diff --git a/src/WireMock.Net/Settings/WireMockServerSettingsParser.cs b/src/WireMock.Net/Settings/WireMockServerSettingsParser.cs index 32fecedb..3f875998 100644 --- a/src/WireMock.Net/Settings/WireMockServerSettingsParser.cs +++ b/src/WireMock.Net/Settings/WireMockServerSettingsParser.cs @@ -54,7 +54,8 @@ public static class WireMockServerSettingsParser UseRegexExtended = parser.GetBoolValue(nameof(WireMockServerSettings.UseRegexExtended), true), WatchStaticMappings = parser.GetBoolValue("WatchStaticMappings"), WatchStaticMappingsInSubdirectories = parser.GetBoolValue("WatchStaticMappingsInSubdirectories"), - HostingScheme = parser.GetEnumValue(nameof(WireMockServerSettings.HostingScheme)) + HostingScheme = parser.GetEnumValue(nameof(WireMockServerSettings.HostingScheme)), + DoNotSaveDynamicResponseInLogEntry = parser.GetBoolValue(nameof(WireMockServerSettings.DoNotSaveDynamicResponseInLogEntry)) }; #if USE_ASPNETCORE diff --git a/test/WireMock.Net.Tests/ResponseBuilders/ResponseWithBodyTests.cs b/test/WireMock.Net.Tests/ResponseBuilders/ResponseWithBodyTests.cs index 81b083c5..e3702f95 100644 --- a/test/WireMock.Net.Tests/ResponseBuilders/ResponseWithBodyTests.cs +++ b/test/WireMock.Net.Tests/ResponseBuilders/ResponseWithBodyTests.cs @@ -21,12 +21,12 @@ public class ResponseWithBodyTests private readonly Mock _mappingMock; private readonly Mock _filesystemHandlerMock; - private readonly WireMockServerSettings _settings = new (); + private readonly WireMockServerSettings _settings = new(); public ResponseWithBodyTests() { _mappingMock = new Mock(); - + _filesystemHandlerMock = new Mock(MockBehavior.Strict); _filesystemHandlerMock.Setup(fs => fs.ReadResponseBodyAsString(It.IsAny())).Returns("abc"); @@ -197,61 +197,7 @@ public class ResponseWithBodyTests } [Fact] - public async Task Response_ProvideResponse_WithBody_Func() - { - // Assign - var request = new RequestMessage(new UrlDetails("http://localhost/test"), "GET", ClientIp); - - var responseBuilder = Response.Create() - .WithStatusCode(500) - .WithHeader("H1", "X1") - .WithHeader("H2", "X2") - .WithBody(req => $"path: {req.Path}"); - - // Act - var response = await responseBuilder.ProvideResponseAsync(_mappingMock.Object, request, _settings).ConfigureAwait(false); - - // Assert - Check.That(response.Message.BodyData.BodyAsString).IsEqualTo("path: /test"); - Check.That(response.Message.BodyData.BodyAsBytes).IsNull(); - Check.That(response.Message.BodyData.BodyAsJson).IsNull(); - Check.That(response.Message.BodyData.Encoding.CodePage).Equals(Encoding.UTF8.CodePage); - Check.That(response.Message.StatusCode).IsEqualTo(500); - Check.That(response.Message.Headers["H1"].ToString()).IsEqualTo("X1"); - Check.That(response.Message.Headers["H2"].ToString()).IsEqualTo("X2"); - } - - [Fact] - public async Task Response_ProvideResponse_WithBody_FuncAsync() - { - // Assign - var request = new RequestMessage(new UrlDetails("http://localhost/test"), "GET", ClientIp); - - var responseBuilder = Response.Create() - .WithStatusCode(500) - .WithHeader("H1", "X1") - .WithHeader("H2", "X2") - .WithBody(async req => - { - await Task.Delay(1).ConfigureAwait(false); - return $"path: {req.Path}"; - }); - - // Act - var response = await responseBuilder.ProvideResponseAsync(_mappingMock.Object, request, _settings).ConfigureAwait(false); - - // Assert - Check.That(response.Message.BodyData.BodyAsString).IsEqualTo("path: /test"); - Check.That(response.Message.BodyData.BodyAsBytes).IsNull(); - Check.That(response.Message.BodyData.BodyAsJson).IsNull(); - Check.That(response.Message.BodyData.Encoding.CodePage).Equals(Encoding.UTF8.CodePage); - Check.That(response.Message.StatusCode).IsEqualTo(500); - Check.That(response.Message.Headers["H1"].ToString()).IsEqualTo("X1"); - Check.That(response.Message.Headers["H2"].ToString()).IsEqualTo("X2"); - } - - [Fact] - public async Task Response_ProvideResponse_WithJsonBodyAndTransform_Func() + public async Task Response_ProvideResponse_WithJsonBodyAndTransform() { // Assign const int request1Id = 1; diff --git a/test/WireMock.Net.Tests/ResponseBuilders/ResponseWithCallbackTests.cs b/test/WireMock.Net.Tests/ResponseBuilders/ResponseWithCallbackTests.cs index ac1413a8..97806713 100644 --- a/test/WireMock.Net.Tests/ResponseBuilders/ResponseWithCallbackTests.cs +++ b/test/WireMock.Net.Tests/ResponseBuilders/ResponseWithCallbackTests.cs @@ -1,8 +1,10 @@ using System.Collections.Generic; using System.Net; +using System.Text; using System.Threading.Tasks; using FluentAssertions; using Moq; +using NFluent; using WireMock.Handlers; using WireMock.Models; using WireMock.ResponseBuilders; @@ -18,17 +20,124 @@ public class ResponseWithCallbackTests private const string ClientIp = "::1"; private readonly Mock _mappingMock; - private readonly Mock _filesystemHandlerMock; private readonly WireMockServerSettings _settings = new(); public ResponseWithCallbackTests() { _mappingMock = new Mock(); - _filesystemHandlerMock = new Mock(MockBehavior.Strict); - _filesystemHandlerMock.Setup(fs => fs.ReadResponseBodyAsString(It.IsAny())).Returns("abc"); + var filesystemHandlerMock = new Mock(MockBehavior.Strict); + filesystemHandlerMock.Setup(fs => fs.ReadResponseBodyAsString(It.IsAny())).Returns("abc"); - _settings.FileSystemHandler = _filesystemHandlerMock.Object; + _settings.FileSystemHandler = filesystemHandlerMock.Object; + } + + [Fact] + public async Task Response_ProvideResponse_WithBody_Func() + { + // Assign + var request = new RequestMessage(new UrlDetails("http://localhost/test"), "GET", ClientIp); + + var responseBuilder = Response.Create() + .WithStatusCode(500) + .WithHeader("H1", "X1") + .WithHeader("H2", "X2") + .WithBody(req => $"path: {req.Path}"); + + // Act + var response = await responseBuilder.ProvideResponseAsync(_mappingMock.Object, request, _settings).ConfigureAwait(false); + + // Assert + Check.That(response.Message.BodyData.BodyAsString).IsEqualTo("path: /test"); + Check.That(response.Message.BodyData.BodyAsBytes).IsNull(); + Check.That(response.Message.BodyData.BodyAsJson).IsNull(); + Check.That(response.Message.BodyData.Encoding.CodePage).Equals(Encoding.UTF8.CodePage); + Check.That(response.Message.StatusCode).IsEqualTo(500); + Check.That(response.Message.Headers["H1"].ToString()).IsEqualTo("X1"); + Check.That(response.Message.Headers["H2"].ToString()).IsEqualTo("X2"); + } + + [Fact] + public async Task Response_ProvideResponse_WithBody_Func_DynamicCode_Should_IsFuncUsed() + { + // Assign + var request = new RequestMessage(new UrlDetails("http://localhost/test"), "GET", ClientIp); + + var data = new[] { "x", "y" }; + var i = 0; + var responseBuilder = Response.Create() + .WithBody(_ => + { + var value = data[i]; + i++; + return value; + }); + + // Act (2x) + var response1 = await responseBuilder.ProvideResponseAsync(_mappingMock.Object, request, _settings).ConfigureAwait(false); + var response2 = await responseBuilder.ProvideResponseAsync(_mappingMock.Object, request, _settings).ConfigureAwait(false); + + // Assert + response1.Message.BodyData!.BodyAsString.Should().Be("x"); + response1.Message.BodyData!.IsFuncUsed.Should().Be("Func"); + response2.Message.BodyData!.BodyAsString.Should().Be("y"); + response2.Message.BodyData!.IsFuncUsed.Should().Be("Func"); + } + + [Fact] + public async Task Response_ProvideResponse_WithBody_AsyncFunc_DynamicCode_Should_IsFuncUsed() + { + // Assign + var request = new RequestMessage(new UrlDetails("http://localhost/test"), "GET", ClientIp); + + var data = new[] { "x", "y" }; + var i = 0; + var responseBuilder = Response.Create() + .WithBody(_ => + { + var value = data[i]; + i++; + return Task.FromResult(value); + }); + + // Act (2x) + var response1 = await responseBuilder.ProvideResponseAsync(_mappingMock.Object, request, _settings).ConfigureAwait(false); + var response2 = await responseBuilder.ProvideResponseAsync(_mappingMock.Object, request, _settings).ConfigureAwait(false); + + // Assert + response1.Message.BodyData!.BodyAsString.Should().Be("x"); + response1.Message.BodyData!.IsFuncUsed.Should().Be("Func>"); + response2.Message.BodyData!.BodyAsString.Should().Be("y"); + response2.Message.BodyData!.IsFuncUsed.Should().Be("Func>"); + } + + [Fact] + public async Task Response_ProvideResponse_WithBody_FuncAsync() + { + // Assign + var request = new RequestMessage(new UrlDetails("http://localhost/test"), "GET", ClientIp); + + var responseBuilder = Response.Create() + .WithStatusCode(500) + .WithHeader("H1", "X1") + .WithHeader("H2", "X2") + .WithBody(async req => + { + await Task.Delay(1).ConfigureAwait(false); + return $"path: {req.Path}"; + }); + + // Act + var response = await responseBuilder.ProvideResponseAsync(_mappingMock.Object, request, _settings).ConfigureAwait(false); + + // Assert + Check.That(response.Message.BodyData.BodyAsString).IsEqualTo("path: /test"); + Check.That(response.Message.BodyData.BodyAsBytes).IsNull(); + Check.That(response.Message.BodyData.BodyAsJson).IsNull(); + Check.That(response.Message.BodyData.Encoding.CodePage).Equals(Encoding.UTF8.CodePage); + Check.That(response.Message.StatusCode).IsEqualTo(500); + Check.That(response.Message.Headers["H1"].ToString()).IsEqualTo("X1"); + Check.That(response.Message.Headers["H2"].ToString()).IsEqualTo("X2"); } [Fact] diff --git a/test/WireMock.Net.Tests/Serialization/LogEntryMapperTests.cs b/test/WireMock.Net.Tests/Serialization/LogEntryMapperTests.cs index 59bc86fc..76b46e99 100644 --- a/test/WireMock.Net.Tests/Serialization/LogEntryMapperTests.cs +++ b/test/WireMock.Net.Tests/Serialization/LogEntryMapperTests.cs @@ -2,6 +2,7 @@ using FluentAssertions; using NFluent; using WireMock.Logging; using WireMock.Models; +using WireMock.Owin; using WireMock.ResponseBuilders; using WireMock.Serialization; using WireMock.Types; @@ -12,6 +13,15 @@ namespace WireMock.Net.Tests.Serialization; public class LogEntryMapperTests { + private readonly IWireMockMiddlewareOptions _options = new WireMockMiddlewareOptions(); + + private readonly LogEntryMapper _sut; + + public LogEntryMapperTests() + { + _sut = new LogEntryMapper(_options); + } + [Fact] public void LogEntryMapper_Map_LogEntry_Check_BodyTypeBytes() { @@ -39,7 +49,7 @@ public class LogEntryMapperTests }; // Act - var result = LogEntryMapper.Map(logEntry); + var result = _sut.Map(logEntry); // Assert Check.That(result.Request.DetectedBodyType).IsEqualTo("Bytes"); @@ -74,7 +84,7 @@ public class LogEntryMapperTests }; // Act - var result = LogEntryMapper.Map(logEntry); + var result = _sut.Map(logEntry); // Assert Check.That(result.Request.DetectedBodyType).IsNull(); @@ -111,10 +121,49 @@ public class LogEntryMapperTests }; // Act - var result = LogEntryMapper.Map(logEntry); + var result = _sut.Map(logEntry); // Assert result.Response.FaultType.Should().Be("EMPTY_RESPONSE"); result.Response.FaultPercentage.Should().Be(0.5); } + + [Fact] + public void LogEntryMapper_Map_LogEntry_WhenFuncIsUsed_And_DoNotSaveDynamicResponseInLogEntry_Is_True_Should_NotSave_StringResponse() + { + // Assign + var options = new WireMockMiddlewareOptions + { + DoNotSaveDynamicResponseInLogEntry = true + }; + var isFuncUsed = "Func"; + var logEntry = new LogEntry + { + RequestMessage = new RequestMessage( + new UrlDetails("http://localhost"), + "post", + "::1", + new BodyData + { + DetectedBodyType = BodyType.Bytes, + BodyAsBytes = new byte[] { 0 } + } + ), + ResponseMessage = new ResponseMessage + { + BodyData = new BodyData + { + DetectedBodyType = BodyType.String, + BodyAsString = "test", + IsFuncUsed = isFuncUsed + } + } + }; + + // Act + var result = new LogEntryMapper(options).Map(logEntry); + + // Assert + result.Response.Body.Should().Be(isFuncUsed); + } } \ No newline at end of file