diff --git a/examples/WireMock.Net.ConsoleApplication/MainApp.cs b/examples/WireMock.Net.ConsoleApplication/MainApp.cs index 6e889a6a..5583162c 100644 --- a/examples/WireMock.Net.ConsoleApplication/MainApp.cs +++ b/examples/WireMock.Net.ConsoleApplication/MainApp.cs @@ -1,5 +1,4 @@ using System; -using System.Linq; using Newtonsoft.Json; using WireMock.Matchers; using WireMock.RequestBuilders; @@ -21,7 +20,7 @@ namespace WireMock.Net.ConsoleApplication { Urls = new[] { url1, url2, url3 }, StartAdminInterface = true, - ReadStaticMappings = true + ReadStaticMappings = false }); System.Console.WriteLine("FluentMockServer listening at {0}", string.Join(" and ", server.Urls)); @@ -29,6 +28,14 @@ namespace WireMock.Net.ConsoleApplication // server.AllowPartialMapping(); + server + .Given(Request.Create().WithPath("/bodyasbytes.png") + .UsingGet()) + .RespondWith(Response.Create() + .WithHeader("Content-Type", "image/png") + .WithBody(Convert.FromBase64String("iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAIAAAACUFjqAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAZdEVYdFNvZnR3YXJlAHBhaW50Lm5ldCA0LjAuMTczbp9jAAAAJ0lEQVQoU2NgUPuPD6Hz0RCEAtJoiAxpCCBXGgmRIo0TofORkdp/AMiMdRVnV6O0AAAAAElFTkSuQmCC")) + ); + server .Given(Request.Create().WithPath("/oauth2/access").UsingPost().WithBody("grant_type=password;username=u;password=p")) .RespondWith(Response.Create() diff --git a/src/WireMock.Net/Admin/Mappings/ResponseModel.cs b/src/WireMock.Net/Admin/Mappings/ResponseModel.cs index 21b95542..cd12d37a 100644 --- a/src/WireMock.Net/Admin/Mappings/ResponseModel.cs +++ b/src/WireMock.Net/Admin/Mappings/ResponseModel.cs @@ -15,6 +15,11 @@ namespace WireMock.Admin.Mappings /// public int? StatusCode { get; set; } + /// + /// Gets or sets the body destination (SameAsSource, String or Bytes). + /// + public string BodyDestination { get; set; } + /// /// Gets or sets the body. /// @@ -29,7 +34,7 @@ namespace WireMock.Admin.Mappings /// /// The body. /// - public string BodyAsBase64 { get; set; } + public string BodyFromBase64 { get; set; } /// /// Gets or sets the body (as JSON object). @@ -39,6 +44,14 @@ namespace WireMock.Admin.Mappings /// public object BodyAsJson { get; set; } + /// + /// Gets or sets the body (as bytearray). + /// + /// + /// The body. + /// + public byte[] BodyAsBytes { get; set; } + /// /// Gets or sets the body encoding. /// diff --git a/src/WireMock.Net/Admin/Requests/LogResponseModel.cs b/src/WireMock.Net/Admin/Requests/LogResponseModel.cs index 5ce9a8c4..4d39fc1d 100644 --- a/src/WireMock.Net/Admin/Requests/LogResponseModel.cs +++ b/src/WireMock.Net/Admin/Requests/LogResponseModel.cs @@ -18,11 +18,21 @@ namespace WireMock.Admin.Requests /// public IDictionary Headers { get; set; } + /// + /// Gets or sets the body destination (SameAsSource, String or Bytes). + /// + public string BodyDestination { get; set; } + /// /// Gets or sets the body. /// public string Body { get; set; } + /// + /// Gets or sets the body. + /// + public byte[] BodyAsBytes { get; set; } + /// /// Gets or sets the original body. /// diff --git a/src/WireMock.Net/Http/HttpClientHelper.cs b/src/WireMock.Net/Http/HttpClientHelper.cs index 2f4eac1a..71ac8447 100644 --- a/src/WireMock.Net/Http/HttpClientHelper.cs +++ b/src/WireMock.Net/Http/HttpClientHelper.cs @@ -71,11 +71,12 @@ namespace WireMock.Http // Call the URL var httpResponseMessage = await client.SendAsync(httpRequestMessage, HttpCompletionOption.ResponseContentRead); - // Transform response var responseMessage = new ResponseMessage { StatusCode = (int)httpResponseMessage.StatusCode, + + // TODO : what about BodyAsBytes ??? Body = await httpResponseMessage.Content.ReadAsStringAsync() }; diff --git a/src/WireMock.Net/Http/HttpKnownHeaderNames.cs b/src/WireMock.Net/Http/HttpKnownHeaderNames.cs new file mode 100644 index 00000000..c17bc0ab --- /dev/null +++ b/src/WireMock.Net/Http/HttpKnownHeaderNames.cs @@ -0,0 +1,96 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace WireMock.Http +{ + /// + /// Copied from https://raw.githubusercontent.com/dotnet/corefx/master/src/Common/src/System/Net/HttpKnownHeaderNames.cs + /// + internal static class HttpKnownHeaderNames + { + public const string Accept = "Accept"; + public const string AcceptCharset = "Accept-Charset"; + public const string AcceptEncoding = "Accept-Encoding"; + public const string AcceptLanguage = "Accept-Language"; + public const string AcceptPatch = "Accept-Patch"; + public const string AcceptRanges = "Accept-Ranges"; + public const string AccessControlAllowCredentials = "Access-Control-Allow-Credentials"; + public const string AccessControlAllowHeaders = "Access-Control-Allow-Headers"; + public const string AccessControlAllowMethods = "Access-Control-Allow-Methods"; + public const string AccessControlAllowOrigin = "Access-Control-Allow-Origin"; + public const string AccessControlExposeHeaders = "Access-Control-Expose-Headers"; + public const string AccessControlMaxAge = "Access-Control-Max-Age"; + public const string Age = "Age"; + public const string Allow = "Allow"; + public const string AltSvc = "Alt-Svc"; + public const string Authorization = "Authorization"; + public const string CacheControl = "Cache-Control"; + public const string Connection = "Connection"; + public const string ContentDisposition = "Content-Disposition"; + public const string ContentEncoding = "Content-Encoding"; + public const string ContentLanguage = "Content-Language"; + public const string ContentLength = "Content-Length"; + public const string ContentLocation = "Content-Location"; + public const string ContentMD5 = "Content-MD5"; + public const string ContentRange = "Content-Range"; + public const string ContentSecurityPolicy = "Content-Security-Policy"; + public const string ContentType = "Content-Type"; + public const string Cookie = "Cookie"; + public const string Cookie2 = "Cookie2"; + public const string Date = "Date"; + public const string ETag = "ETag"; + public const string Expect = "Expect"; + public const string Expires = "Expires"; + public const string From = "From"; + public const string Host = "Host"; + public const string IfMatch = "If-Match"; + public const string IfModifiedSince = "If-Modified-Since"; + public const string IfNoneMatch = "If-None-Match"; + public const string IfRange = "If-Range"; + public const string IfUnmodifiedSince = "If-Unmodified-Since"; + public const string KeepAlive = "Keep-Alive"; + public const string LastModified = "Last-Modified"; + public const string Link = "Link"; + public const string Location = "Location"; + public const string MaxForwards = "Max-Forwards"; + public const string Origin = "Origin"; + public const string P3P = "P3P"; + public const string Pragma = "Pragma"; + public const string ProxyAuthenticate = "Proxy-Authenticate"; + public const string ProxyAuthorization = "Proxy-Authorization"; + public const string ProxyConnection = "Proxy-Connection"; + public const string PublicKeyPins = "Public-Key-Pins"; + public const string Range = "Range"; + public const string Referer = "Referer"; // NB: The spelling-mistake "Referer" for "Referrer" must be matched. + public const string RetryAfter = "Retry-After"; + public const string SecWebSocketAccept = "Sec-WebSocket-Accept"; + public const string SecWebSocketExtensions = "Sec-WebSocket-Extensions"; + public const string SecWebSocketKey = "Sec-WebSocket-Key"; + public const string SecWebSocketProtocol = "Sec-WebSocket-Protocol"; + public const string SecWebSocketVersion = "Sec-WebSocket-Version"; + public const string Server = "Server"; + public const string SetCookie = "Set-Cookie"; + public const string SetCookie2 = "Set-Cookie2"; + public const string StrictTransportSecurity = "Strict-Transport-Security"; + public const string TE = "TE"; + public const string TSV = "TSV"; + public const string Trailer = "Trailer"; + public const string TransferEncoding = "Transfer-Encoding"; + public const string Upgrade = "Upgrade"; + public const string UpgradeInsecureRequests = "Upgrade-Insecure-Requests"; + public const string UserAgent = "User-Agent"; + public const string Vary = "Vary"; + public const string Via = "Via"; + public const string WWWAuthenticate = "WWW-Authenticate"; + public const string Warning = "Warning"; + public const string XAspNetVersion = "X-AspNet-Version"; + public const string XContentDuration = "X-Content-Duration"; + public const string XContentTypeOptions = "X-Content-Type-Options"; + public const string XFrameOptions = "X-Frame-Options"; + public const string XMSEdgeRef = "X-MSEdge-Ref"; + public const string XPoweredBy = "X-Powered-By"; + public const string XRequestID = "X-Request-ID"; + public const string XUACompatible = "X-UA-Compatible"; + } +} \ No newline at end of file diff --git a/src/WireMock.Net/Owin/OwinRequestMapper.cs b/src/WireMock.Net/Owin/OwinRequestMapper.cs index 4ccc3b9a..51c4d891 100644 --- a/src/WireMock.Net/Owin/OwinRequestMapper.cs +++ b/src/WireMock.Net/Owin/OwinRequestMapper.cs @@ -46,7 +46,7 @@ namespace WireMock.Owin string bodyAsString = null; byte[] body = null; Encoding bodyEncoding = null; - if (request.Body != null) + if (ParseBody(method) && request.Body != null) { using (var streamReader = new StreamReader(request.Body)) { @@ -57,18 +57,42 @@ namespace WireMock.Owin body = bodyEncoding.GetBytes(bodyAsString); } - var listenerHeaders = request.Headers; + Dictionary headers = null; + if (request.Headers.Any()) + { + headers = new Dictionary(); + foreach (var header in request.Headers) + { + headers.Add(header.Key, header.Value.FirstOrDefault()); + } + } - var headers = new Dictionary(); - foreach (var header in listenerHeaders) - headers.Add(header.Key, header.Value.FirstOrDefault()); - - var cookies = new Dictionary(); - - foreach (var cookie in request.Cookies) - cookies.Add(cookie.Key, cookie.Value); + IDictionary cookies = null; + if (request.Cookies.Any()) + { + cookies = new Dictionary(); + foreach (var cookie in request.Cookies) + { + cookies.Add(cookie.Key, cookie.Value); + } + } return new RequestMessage(url, method, clientIP, body, bodyAsString, bodyEncoding, headers, cookies) { DateTime = DateTime.Now }; } + + private bool ParseBody(string method) + { + /* + HEAD - No defined body semantics. + GET - No defined body semantics. + PUT - Body supported. + POST - Body supported. + DELETE - No defined body semantics. + TRACE - Body not supported. + OPTIONS - Body supported but no semantics on usage (maybe in the future). + CONNECT - No defined body semantics + */ + return new[] { "PUT", "POST", "OPTIONS" }.Contains(method.ToUpper()); + } } } \ No newline at end of file diff --git a/src/WireMock.Net/Owin/OwinResponseMapper.cs b/src/WireMock.Net/Owin/OwinResponseMapper.cs index d66c9fa4..9abbfaed 100644 --- a/src/WireMock.Net/Owin/OwinResponseMapper.cs +++ b/src/WireMock.Net/Owin/OwinResponseMapper.cs @@ -2,6 +2,7 @@ using System.Linq; using System.Text; using System.Threading.Tasks; +using WireMock.Http; #if !NETSTANDARD using Microsoft.Owin; #else @@ -32,15 +33,28 @@ namespace WireMock.Owin { response.StatusCode = responseMessage.StatusCode; - responseMessage.Headers.ToList().ForEach(pair => response.Headers.Append(pair.Key, pair.Value)); + if (responseMessage.Headers.ContainsKey(HttpKnownHeaderNames.ContentType)) + { + response.ContentType = responseMessage.Headers[HttpKnownHeaderNames.ContentType]; + } + responseMessage.Headers.Where(h => h.Key != HttpKnownHeaderNames.ContentType).ToList().ForEach(pair => response.Headers.Append(pair.Key, pair.Value)); - if (responseMessage.Body == null) + if (responseMessage.Body == null && responseMessage.BodyAsBytes == null) + { return; + } + + if (responseMessage.BodyAsBytes != null) + { + await response.Body.WriteAsync(responseMessage.BodyAsBytes, 0, responseMessage.BodyAsBytes.Length); + return; + } Encoding encoding = responseMessage.BodyEncoding ?? _utf8NoBom; using (var writer = new StreamWriter(response.Body, encoding)) { await writer.WriteAsync(responseMessage.Body); + // TODO : response.ContentLength = responseMessage.Body.Length; } } } diff --git a/src/WireMock.Net/RequestMessage.cs b/src/WireMock.Net/RequestMessage.cs index 7ac655d4..8424ce54 100644 --- a/src/WireMock.Net/RequestMessage.cs +++ b/src/WireMock.Net/RequestMessage.cs @@ -51,7 +51,7 @@ namespace WireMock /// /// Gets the query. /// - public IDictionary> Query { get; } = new Dictionary>(); + public IDictionary> Query { get; } /// /// Gets the bodyAsBytes. @@ -94,34 +94,38 @@ namespace WireMock BodyEncoding = bodyEncoding; Headers = headers; Cookies = cookies; + Query = ParseQuery(url.Query); + } - string query = url.Query; - if (!string.IsNullOrEmpty(query)) + private IDictionary> ParseQuery(string queryString) + { + if (string.IsNullOrEmpty(queryString)) { - if (query.StartsWith("?")) - { - query = query.Substring(1); - } - - Query = query.Split('&').Aggregate( - new Dictionary>(), - (dict, term) => - { - var parts = term.Split('='); - string key = parts[0]; - if (!dict.ContainsKey(key)) - { - dict.Add(key, new WireMockList()); - } - - if (parts.Length == 2) - { - dict[key].Add(parts[1]); - } - - return dict; - }); + return null; } + + if (queryString.StartsWith("?")) + { + queryString = queryString.Substring(1); + } + + return queryString.Split('&').Aggregate(new Dictionary>(), + (dict, term) => + { + var parts = term.Split('='); + string key = parts[0]; + if (!dict.ContainsKey(key)) + { + dict.Add(key, new WireMockList()); + } + + if (parts.Length == 2) + { + dict[key].Add(parts[1]); + } + + return dict; + }); } /// @@ -131,6 +135,11 @@ namespace WireMock /// The query parameter. public List GetParameter(string key) { + if (Query == null) + { + return null; + } + return Query.ContainsKey(key) ? Query[key] : null; } } diff --git a/src/WireMock.Net/ResponseBuilders/BodyDestinationFormat.cs b/src/WireMock.Net/ResponseBuilders/BodyDestinationFormat.cs new file mode 100644 index 00000000..303f3bc1 --- /dev/null +++ b/src/WireMock.Net/ResponseBuilders/BodyDestinationFormat.cs @@ -0,0 +1,20 @@ +namespace WireMock.ResponseBuilders +{ + public static class BodyDestinationFormat + { + /// + /// Same as source (no conversion) + /// + public const string SameAsSource = "SameAsSource"; + + /// + /// Convert to string + /// + public const string String = "String"; + + /// + /// Convert to bytes + /// + public const string Bytes = "Bytes"; + } +} \ No newline at end of file diff --git a/src/WireMock.Net/ResponseBuilders/IBodyResponseBuilder.cs b/src/WireMock.Net/ResponseBuilders/IBodyResponseBuilder.cs index f8f8d3a0..edd84b86 100644 --- a/src/WireMock.Net/ResponseBuilders/IBodyResponseBuilder.cs +++ b/src/WireMock.Net/ResponseBuilders/IBodyResponseBuilder.cs @@ -1,4 +1,5 @@ -using System.Text; +using System; +using System.Text; using JetBrains.Annotations; namespace WireMock.ResponseBuilders @@ -8,16 +9,34 @@ namespace WireMock.ResponseBuilders /// public interface IBodyResponseBuilder : ITransformResponseBuilder { - /// - /// The with body. - /// - /// The body. - /// The body encoding. - /// A . - IResponseBuilder WithBody([NotNull] string body, [CanBeNull] Encoding encoding = null); + ///// + ///// WithBody : Create a string response based on a string. + ///// + ///// The body. + ///// The body encoding. + ///// A . + //// IResponseBuilder WithBody([NotNull] string body, [CanBeNull] Encoding encoding = null); /// - /// The with body as Json. + /// WithBody : Create a ... response based on a string. + /// + /// The body. + /// The Body Destination format (SameAsSource, String or Bytes). + /// The body encoding. + /// A . + IResponseBuilder WithBody([NotNull] string body, [CanBeNull] string destination = BodyDestinationFormat.SameAsSource, [CanBeNull] Encoding encoding = null); + + /// + /// WithBody : Create a ... response based on a bytearray. + /// + /// The body. + /// The Body Destination format (SameAsSource, String or Bytes). + /// The body encoding. + /// A . + IResponseBuilder WithBody([NotNull] byte[] body, [CanBeNull] string destination = BodyDestinationFormat.SameAsSource, [CanBeNull] Encoding encoding = null); + + /// + /// WithBody : Create a string response based on a object (which will be converted to a JSON string). /// /// The body. /// The body encoding. @@ -25,11 +44,12 @@ namespace WireMock.ResponseBuilders IResponseBuilder WithBodyAsJson([NotNull] object body, [CanBeNull] Encoding encoding = null); /// - /// The with body as base64. + /// WithBody : Create a string response based on a Base64 string (which will be decoded to a normal string). /// - /// The body asbase64. + /// The body. /// The Encoding. /// A . - IResponseBuilder WithBodyAsBase64([NotNull] string bodyAsbase64, [CanBeNull] Encoding encoding = null); + [Obsolete] + IResponseBuilder WithBodyFromBase64([NotNull] string bodyAsbase64, [CanBeNull] Encoding encoding = null); } } \ No newline at end of file diff --git a/src/WireMock.Net/ResponseBuilders/Response.cs b/src/WireMock.Net/ResponseBuilders/Response.cs index eab40e34..41604c78 100644 --- a/src/WireMock.Net/ResponseBuilders/Response.cs +++ b/src/WireMock.Net/ResponseBuilders/Response.cs @@ -150,28 +150,59 @@ namespace WireMock.ResponseBuilders return this; } - /// - /// The with body. - /// - /// The body. - /// The body encoding. - /// A . - public IResponseBuilder WithBody(string body, Encoding encoding = null) + /// + public IResponseBuilder WithBody(byte[] body, string destination, Encoding encoding = null) { Check.NotNull(body, nameof(body)); - ResponseMessage.Body = body; - ResponseMessage.BodyEncoding = encoding ?? Encoding.UTF8; + ResponseMessage.BodyDestination = destination; + + switch (destination) + { + case BodyDestinationFormat.String: + var enc = encoding ?? Encoding.UTF8; + ResponseMessage.BodyAsBytes = null; + ResponseMessage.Body = enc.GetString(body); + ResponseMessage.BodyEncoding = enc; + break; + + default: + ResponseMessage.BodyAsBytes = body; + ResponseMessage.BodyEncoding = null; + break; + } return this; } - /// - /// The with body (AsJson object). - /// - /// The body. - /// The body encoding. - /// A . + /// + public IResponseBuilder WithBody(string body, string destination = BodyDestinationFormat.SameAsSource, Encoding encoding = null) + { + Check.NotNull(body, nameof(body)); + + encoding = encoding ?? Encoding.UTF8; + + ResponseMessage.BodyDestination = destination; + + switch (destination) + { + case BodyDestinationFormat.Bytes: + ResponseMessage.Body = null; + ResponseMessage.BodyAsBytes = encoding.GetBytes(body); + ResponseMessage.BodyEncoding = encoding; + break; + + default: + ResponseMessage.Body = body; + ResponseMessage.BodyAsBytes = null; + ResponseMessage.BodyEncoding = encoding; + break; + } + + return this; + } + + /// public IResponseBuilder WithBodyAsJson(object body, Encoding encoding = null) { Check.NotNull(body, nameof(body)); @@ -184,23 +215,20 @@ namespace WireMock.ResponseBuilders ResponseMessage.BodyEncoding = encoding; } + ResponseMessage.BodyDestination = null; ResponseMessage.Body = jsonBody; return this; } - /// - /// The with body as base64. - /// - /// The body asbase64. - /// The Encoding. - /// A . - public IResponseBuilder WithBodyAsBase64(string bodyAsbase64, Encoding encoding = null) + /// + public IResponseBuilder WithBodyFromBase64(string bodyAsbase64, Encoding encoding = null) { Check.NotNull(bodyAsbase64, nameof(bodyAsbase64)); encoding = encoding ?? Encoding.UTF8; + ResponseMessage.BodyDestination = null; ResponseMessage.Body = encoding.GetString(Convert.FromBase64String(bodyAsbase64)); ResponseMessage.BodyEncoding = encoding; diff --git a/src/WireMock.Net/ResponseMessage.cs b/src/WireMock.Net/ResponseMessage.cs index b60ed2ce..9c2bdc15 100644 --- a/src/WireMock.Net/ResponseMessage.cs +++ b/src/WireMock.Net/ResponseMessage.cs @@ -24,11 +24,21 @@ namespace WireMock /// public string BodyOriginal { get; set; } + /// + /// Gets or sets the body destination (SameAsSource, String or Bytes). + /// + public string BodyDestination { get; set; } + /// /// Gets or sets the body. /// public string Body { get; set; } + /// + /// Gets or sets the body. + /// + public byte[] BodyAsBytes { get; set; } + /// /// Gets or sets the body encoding. /// diff --git a/src/WireMock.Net/Serialization/MappingConverter.cs b/src/WireMock.Net/Serialization/MappingConverter.cs index 48c3e567..47661662 100644 --- a/src/WireMock.Net/Serialization/MappingConverter.cs +++ b/src/WireMock.Net/Serialization/MappingConverter.cs @@ -95,16 +95,20 @@ namespace WireMock.Serialization { mappingModel.Response.StatusCode = null; mappingModel.Response.Headers = null; + mappingModel.Response.BodyDestination = null; mappingModel.Response.Body = null; + mappingModel.Response.BodyAsBytes = null; mappingModel.Response.UseTransformer = false; mappingModel.Response.BodyEncoding = null; mappingModel.Response.ProxyUrl = response.ProxyUrl; } else { + mappingModel.Response.BodyDestination = response.ResponseMessage.BodyDestination; mappingModel.Response.StatusCode = response.ResponseMessage.StatusCode; mappingModel.Response.Headers = response.ResponseMessage.Headers; mappingModel.Response.Body = response.ResponseMessage.Body; + mappingModel.Response.BodyAsBytes = response.ResponseMessage.BodyAsBytes; mappingModel.Response.UseTransformer = response.UseTransformer; mappingModel.Response.BodyEncoding = response.ResponseMessage.BodyEncoding != null ? new EncodingModel diff --git a/src/WireMock.Net/Server/FluentMockServer.Admin.cs b/src/WireMock.Net/Server/FluentMockServer.Admin.cs index e311d12a..482b16e3 100644 --- a/src/WireMock.Net/Server/FluentMockServer.Admin.cs +++ b/src/WireMock.Net/Server/FluentMockServer.Admin.cs @@ -412,7 +412,9 @@ namespace WireMock.Server Response = new LogResponseModel { StatusCode = logEntry.ResponseMessage.StatusCode, + BodyDestination = logEntry.ResponseMessage.BodyDestination, Body = logEntry.ResponseMessage.Body, + BodyAsBytes = logEntry.ResponseMessage.BodyAsBytes, BodyOriginal = logEntry.ResponseMessage.BodyOriginal, Headers = logEntry.ResponseMessage.Headers, BodyEncoding = logEntry.ResponseMessage.BodyEncoding != null ? new EncodingModel @@ -612,17 +614,21 @@ namespace WireMock.Server } } - if (responseModel.Body != null) + if (responseModel.BodyAsBytes != null) { - responseBuilder = responseBuilder.WithBody(responseModel.Body, ToEncoding(responseModel.BodyEncoding)); + responseBuilder = responseBuilder.WithBody(responseModel.BodyAsBytes, responseModel.BodyDestination, ToEncoding(responseModel.BodyEncoding)); + } + else if (responseModel.Body != null) + { + responseBuilder = responseBuilder.WithBody(responseModel.Body, responseModel.BodyDestination, ToEncoding(responseModel.BodyEncoding)); } else if (responseModel.BodyAsJson != null) { responseBuilder = responseBuilder.WithBodyAsJson(responseModel.BodyAsJson, ToEncoding(responseModel.BodyEncoding)); } - else if (responseModel.BodyAsBase64 != null) + else if (responseModel.BodyFromBase64 != null) { - responseBuilder = responseBuilder.WithBodyAsBase64(responseModel.BodyAsBase64, ToEncoding(responseModel.BodyEncoding)); + responseBuilder = responseBuilder.WithBodyFromBase64(responseModel.BodyFromBase64, ToEncoding(responseModel.BodyEncoding)); } if (responseModel.UseTransformer) diff --git a/test/WireMock.Net.Tests/FluentMockServerTests.cs b/test/WireMock.Net.Tests/FluentMockServerTests.cs index a220f8ab..cb8b31ae 100644 --- a/test/WireMock.Net.Tests/FluentMockServerTests.cs +++ b/test/WireMock.Net.Tests/FluentMockServerTests.cs @@ -1,417 +1,434 @@ -using System; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Net; -using System.Net.Http; -using System.Threading.Tasks; -using NFluent; -using Xunit; -using WireMock.Matchers; -using WireMock.RequestBuilders; -using WireMock.ResponseBuilders; -using WireMock.Server; - -namespace WireMock.Net.Tests -{ - //[TestFixture] - //[Timeout(5000)] - public class FluentMockServerTests : IDisposable - { - private FluentMockServer _server; - - // For for AppVeyor + OpenCover - private string GetCurrentFolder() - { - string current = Directory.GetCurrentDirectory(); - //if (!current.EndsWith("WireMock.Net.Tests")) - // return Path.Combine(current, "test", "WireMock.Net.Tests"); - - return current; - } - - [Fact] - public void FluentMockServer_StartStop() - { - var server1 = FluentMockServer.Start("http://localhost:9091/"); - server1.Stop(); - - var server2 = FluentMockServer.Start("http://localhost:9091/"); - server2.Stop(); - } - - [Fact] - public void FluentMockServer_ReadStaticMapping_WithNonGuidFilename() - { - var guid = Guid.Parse("04ee4872-9efd-4770-90d3-88d445265d0d"); - string title = "documentdb_root_title"; - - _server = FluentMockServer.Start(); - - string folder = Path.Combine(GetCurrentFolder(), "__admin", "mappings", "documentdb_root.json"); - _server.ReadStaticMapping(folder); - - var mappings = _server.Mappings.ToArray(); - Check.That(mappings).HasSize(1); - - Check.That(mappings.First().RequestMatcher).IsNotNull(); - Check.That(mappings.First().Provider).IsNotNull(); - Check.That(mappings.First().Guid).Equals(guid); - Check.That(mappings.First().Title).Equals(title); - } - - [Fact] - public void FluentMockServer_ReadStaticMapping_WithGuidFilename() - { - string guid = "00000002-ee28-4f29-ae63-1ac9b0802d86"; - - _server = FluentMockServer.Start(); - string folder = Path.Combine(GetCurrentFolder(), "__admin", "mappings", guid + ".json"); - _server.ReadStaticMapping(folder); - - var mappings = _server.Mappings.ToArray(); - Check.That(mappings).HasSize(1); - - Check.That(mappings.First().RequestMatcher).IsNotNull(); - Check.That(mappings.First().Provider).IsNotNull(); - Check.That(mappings.First().Guid).Equals(Guid.Parse(guid)); - Check.That(mappings.First().Title).IsNullOrEmpty(); - } - - [Fact] - public void FluentMockServer_ReadStaticMappings() - { - _server = FluentMockServer.Start(); - - string folder = Path.Combine(GetCurrentFolder(), "__admin", "mappings"); - _server.ReadStaticMappings(folder); - - var mappings = _server.Mappings.ToArray(); - Check.That(mappings).HasSize(2); - } - - [Fact] - public void FluentMockServer_Admin_Mappings_Get() - { - var guid = Guid.Parse("90356dba-b36c-469a-a17e-669cd84f1f05"); - _server = FluentMockServer.Start(); - - _server.Given(Request.Create().WithPath("/foo1").UsingGet()) - .WithGuid(guid) - .RespondWith(Response.Create().WithStatusCode(201).WithBody("1")); - - _server.Given(Request.Create().WithPath("/foo2").UsingGet()) - .RespondWith(Response.Create().WithStatusCode(202).WithBody("2")); - - var mappings = _server.Mappings.ToArray(); - Check.That(mappings).HasSize(2); - - Check.That(mappings.First().RequestMatcher).IsNotNull(); - Check.That(mappings.First().Provider).IsNotNull(); - Check.That(mappings.First().Guid).Equals(guid); - - Check.That(mappings[1].Guid).Not.Equals(guid); - } - - [Fact] - public void FluentMockServer_Admin_Mappings_Add_SameGuid() - { - var guid = Guid.Parse("90356dba-b36c-469a-a17e-669cd84f1f05"); - _server = FluentMockServer.Start(); - - _server.Given(Request.Create().WithPath("/1").UsingGet()) - .WithGuid(guid) - .RespondWith(Response.Create().WithStatusCode(500)); - - var mappings = _server.Mappings.ToArray(); - Check.That(mappings).HasSize(1); - Check.That(mappings.First().Guid).Equals(guid); - - _server.Given(Request.Create().WithPath("/2").UsingGet()) - .WithGuid(guid) - .RespondWith(Response.Create().WithStatusCode(500)); - - Check.That(mappings).HasSize(1); - Check.That(mappings.First().Guid).Equals(guid); - } - - [Fact] - public async Task FluentMockServer_Admin_Mappings_AtPriority() - { - _server = FluentMockServer.Start(); - - // given - _server.Given(Request.Create().WithPath("/1").UsingGet()) - .AtPriority(2) - .RespondWith(Response.Create().WithStatusCode(200)); - - _server.Given(Request.Create().WithPath("/1").UsingGet()) - .AtPriority(1) - .RespondWith(Response.Create().WithStatusCode(400)); - - var mappings = _server.Mappings.ToArray(); - Check.That(mappings).HasSize(2); - Check.That(mappings[0].Priority).Equals(2); - Check.That(mappings[1].Priority).Equals(1); - - // when - var response = await new HttpClient().GetAsync("http://localhost:" + _server.Ports[0] + "/1"); - - // then - Check.That((int)response.StatusCode).IsEqualTo(400); - } - - [Fact] - public async Task FluentMockServer_Admin_Requests_Get() - { - // given - _server = FluentMockServer.Start(); - - // when - await new HttpClient().GetAsync("http://localhost:" + _server.Ports[0] + "/foo"); - - // then - Check.That(_server.LogEntries).HasSize(1); - var requestLogged = _server.LogEntries.First(); - Check.That(requestLogged.RequestMessage.Method).IsEqualTo("get"); - Check.That(requestLogged.RequestMessage.BodyAsBytes).IsEmpty(); - } - - [Fact] - public async Task Should_respond_to_request() - { - // given - _server = FluentMockServer.Start(); - - _server - .Given(Request.Create() - .WithPath("/foo") - .UsingGet()) - .RespondWith(Response.Create() - .WithStatusCode(200) - .WithBody(@"{ msg: ""Hello world!""}")); - - // when - var response = await new HttpClient().GetStringAsync("http://localhost:" + _server.Ports[0] + "/foo"); - - // then - Check.That(response).IsEqualTo(@"{ msg: ""Hello world!""}"); - } - - [Fact] - public async Task Should_respond_to_request_bodyAsBase64() - { - // given - _server = FluentMockServer.Start(); - - _server.Given(Request.Create().WithPath("/foo").UsingGet()).RespondWith(Response.Create().WithBodyAsBase64("SGVsbG8gV29ybGQ/")); - - // when - var response = await new HttpClient().GetStringAsync("http://localhost:" + _server.Ports[0] + "/foo"); - - // then - Check.That(response).IsEqualTo("Hello World?"); - } - - [Fact] - public async Task Should_respond_404_for_unexpected_request() - { - // given - _server = FluentMockServer.Start(); - - // when - var response = await new HttpClient().GetAsync("http://localhost:" + _server.Ports[0] + "/foo"); - - // then - Check.That(response.StatusCode).IsEqualTo(HttpStatusCode.NotFound); - Check.That((int)response.StatusCode).IsEqualTo(404); - } - - [Fact] - public async Task Should_find_a_request_satisfying_a_request_spec() - { - // given - _server = FluentMockServer.Start(); - - // when - await new HttpClient().GetAsync("http://localhost:" + _server.Ports[0] + "/foo"); - await new HttpClient().GetAsync("http://localhost:" + _server.Ports[0] + "/bar"); - - // then - var result = _server.FindLogEntries(Request.Create().WithPath(new RegexMatcher("^/b.*"))).ToList(); - Check.That(result).HasSize(1); - - var requestLogged = result.First(); - Check.That(requestLogged.RequestMessage.Path).IsEqualTo("/bar"); - Check.That(requestLogged.RequestMessage.Url).IsEqualTo("http://localhost:" + _server.Ports[0] + "/bar"); - } - - [Fact] - public async Task Should_reset_requestlogs() - { - // given - _server = FluentMockServer.Start(); - - // when - await new HttpClient().GetAsync("http://localhost:" + _server.Ports[0] + "/foo"); - _server.ResetLogEntries(); - - // then - Check.That(_server.LogEntries).IsEmpty(); - } - - [Fact] - public void Should_reset_mappings() - { - // given - _server = FluentMockServer.Start(); - - _server - .Given(Request.Create() - .WithPath("/foo") - .UsingGet()) - .RespondWith(Response.Create() - .WithBody(@"{ msg: ""Hello world!""}")); - - // when - _server.ResetMappings(); - - // then - Check.That(_server.Mappings).IsEmpty(); - Check.ThatAsyncCode(() => new HttpClient().GetStringAsync("http://localhost:" + _server.Ports[0] + "/foo")) - .ThrowsAny(); - } - - [Fact] - public async Task Should_respond_a_redirect_without_body() - { - // given - _server = FluentMockServer.Start(); - - _server - .Given(Request.Create() - .WithPath("/foo") - .UsingGet()) - .RespondWith(Response.Create() - .WithStatusCode(307) - .WithHeader("Location", "/bar")); - _server - .Given(Request.Create() - .WithPath("/bar") - .UsingGet()) - .RespondWith(Response.Create() - .WithStatusCode(200) - .WithBody("REDIRECT SUCCESSFUL")); - - // when - var response = await new HttpClient().GetStringAsync("http://localhost:" + _server.Ports[0] + "/foo"); - - // then - Check.That(response).IsEqualTo("REDIRECT SUCCESSFUL"); - } - - [Fact] - public async Task Should_delay_responses_for_a_given_route() - { - // given - _server = FluentMockServer.Start(); - - _server - .Given(Request.Create() - .WithPath("/*")) - .RespondWith(Response.Create() - .WithBody(@"{ msg: ""Hello world!""}") - .WithDelay(TimeSpan.FromMilliseconds(200))); - - // when - var watch = new Stopwatch(); - watch.Start(); - await new HttpClient().GetStringAsync("http://localhost:" + _server.Ports[0] + "/foo"); - watch.Stop(); - - // then - Check.That(watch.ElapsedMilliseconds).IsStrictlyGreaterThan(200); - } - - [Fact] - public async Task Should_delay_responses() - { - // given - _server = FluentMockServer.Start(); - _server.AddGlobalProcessingDelay(TimeSpan.FromMilliseconds(200)); - _server - .Given(Request.Create().WithPath("/*")) - .RespondWith(Response.Create().WithBody(@"{ msg: ""Hello world!""}")); - - // when - var watch = new Stopwatch(); - watch.Start(); - await new HttpClient().GetStringAsync("http://localhost:" + _server.Ports[0] + "/foo"); - watch.Stop(); - - // then - Check.That(watch.ElapsedMilliseconds).IsStrictlyGreaterThan(200); - } - - [Fact] - public async Task Should_proxy_responses() - { - // given - _server = FluentMockServer.Start(); - _server - .Given(Request.Create().WithPath("/*")) - .RespondWith(Response.Create().WithProxy("http://www.google.com")); - - // when - var result = await new HttpClient().GetStringAsync("http://localhost:" + _server.Ports[0] + "/search?q=test"); - - // then - Check.That(result).Contains("google"); - } - - //Leaving commented as this requires an actual certificate with password, along with a service that expects a client certificate - //[Fact] - //public async Task Should_proxy_responses_with_client_certificate() - //{ - // // given - // _server = FluentMockServer.Start(); - // _server - // .Given(Request.Create().WithPath("/*")) - // .RespondWith(Response.Create().WithProxy("https://server-that-expects-a-client-certificate", @"\\yourclientcertificatecontainingprivatekey.pfx", "yourclientcertificatepassword")); - - // // when - // var result = await new HttpClient().GetStringAsync("http://localhost:" + _server.Ports[0] + "/someurl?someQuery=someValue"); - - // // then - // Check.That(result).Contains("google"); - //} - - [Fact] - public async Task FluentMockServer_Logging_SetMaxRequestLogCount() - { - // Assign - var client = new HttpClient(); - // Act - _server = FluentMockServer.Start(); - _server.SetMaxRequestLogCount(2); - - await client.GetAsync("http://localhost:" + _server.Ports[0] + "/foo1"); - await client.GetAsync("http://localhost:" + _server.Ports[0] + "/foo2"); - await client.GetAsync("http://localhost:" + _server.Ports[0] + "/foo3"); - - // Assert - Check.That(_server.LogEntries).HasSize(2); - - var requestLoggedA = _server.LogEntries.First(); - Check.That(requestLoggedA.RequestMessage.Path).EndsWith("/foo2"); - - var requestLoggedB = _server.LogEntries.Last(); - Check.That(requestLoggedB.RequestMessage.Path).EndsWith("/foo3"); +using System; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Threading.Tasks; +using NFluent; +using Xunit; +using WireMock.Matchers; +using WireMock.RequestBuilders; +using WireMock.ResponseBuilders; +using WireMock.Server; + +namespace WireMock.Net.Tests +{ + //[TestFixture] + //[Timeout(5000)] + public class FluentMockServerTests : IDisposable + { + private FluentMockServer _server; + + // For for AppVeyor + OpenCover + private string GetCurrentFolder() + { + string current = Directory.GetCurrentDirectory(); + //if (!current.EndsWith("WireMock.Net.Tests")) + // return Path.Combine(current, "test", "WireMock.Net.Tests"); + + return current; } - - public void Dispose() - { - _server?.Stop(); - } - } + + [Fact] + public void FluentMockServer_StartStop() + { + var server1 = FluentMockServer.Start("http://localhost:9091/"); + server1.Stop(); + + var server2 = FluentMockServer.Start("http://localhost:9091/"); + server2.Stop(); + } + + [Fact] + public void FluentMockServer_ReadStaticMapping_WithNonGuidFilename() + { + var guid = Guid.Parse("04ee4872-9efd-4770-90d3-88d445265d0d"); + string title = "documentdb_root_title"; + + _server = FluentMockServer.Start(); + + string folder = Path.Combine(GetCurrentFolder(), "__admin", "mappings", "documentdb_root.json"); + _server.ReadStaticMapping(folder); + + var mappings = _server.Mappings.ToArray(); + Check.That(mappings).HasSize(1); + + Check.That(mappings.First().RequestMatcher).IsNotNull(); + Check.That(mappings.First().Provider).IsNotNull(); + Check.That(mappings.First().Guid).Equals(guid); + Check.That(mappings.First().Title).Equals(title); + } + + [Fact] + public void FluentMockServer_ReadStaticMapping_WithGuidFilename() + { + string guid = "00000002-ee28-4f29-ae63-1ac9b0802d86"; + + _server = FluentMockServer.Start(); + string folder = Path.Combine(GetCurrentFolder(), "__admin", "mappings", guid + ".json"); + _server.ReadStaticMapping(folder); + + var mappings = _server.Mappings.ToArray(); + Check.That(mappings).HasSize(1); + + Check.That(mappings.First().RequestMatcher).IsNotNull(); + Check.That(mappings.First().Provider).IsNotNull(); + Check.That(mappings.First().Guid).Equals(Guid.Parse(guid)); + Check.That(mappings.First().Title).IsNullOrEmpty(); + } + + [Fact] + public void FluentMockServer_ReadStaticMappings() + { + _server = FluentMockServer.Start(); + + string folder = Path.Combine(GetCurrentFolder(), "__admin", "mappings"); + _server.ReadStaticMappings(folder); + + var mappings = _server.Mappings.ToArray(); + Check.That(mappings).HasSize(2); + } + + [Fact] + public void FluentMockServer_Admin_Mappings_Get() + { + var guid = Guid.Parse("90356dba-b36c-469a-a17e-669cd84f1f05"); + _server = FluentMockServer.Start(); + + _server.Given(Request.Create().WithPath("/foo1").UsingGet()) + .WithGuid(guid) + .RespondWith(Response.Create().WithStatusCode(201).WithBody("1")); + + _server.Given(Request.Create().WithPath("/foo2").UsingGet()) + .RespondWith(Response.Create().WithStatusCode(202).WithBody("2")); + + var mappings = _server.Mappings.ToArray(); + Check.That(mappings).HasSize(2); + + Check.That(mappings.First().RequestMatcher).IsNotNull(); + Check.That(mappings.First().Provider).IsNotNull(); + Check.That(mappings.First().Guid).Equals(guid); + + Check.That(mappings[1].Guid).Not.Equals(guid); + } + + [Fact] + public void FluentMockServer_Admin_Mappings_Add_SameGuid() + { + var guid = Guid.Parse("90356dba-b36c-469a-a17e-669cd84f1f05"); + _server = FluentMockServer.Start(); + + _server.Given(Request.Create().WithPath("/1").UsingGet()) + .WithGuid(guid) + .RespondWith(Response.Create().WithStatusCode(500)); + + var mappings = _server.Mappings.ToArray(); + Check.That(mappings).HasSize(1); + Check.That(mappings.First().Guid).Equals(guid); + + _server.Given(Request.Create().WithPath("/2").UsingGet()) + .WithGuid(guid) + .RespondWith(Response.Create().WithStatusCode(500)); + + Check.That(mappings).HasSize(1); + Check.That(mappings.First().Guid).Equals(guid); + } + + [Fact] + public async Task FluentMockServer_Admin_Mappings_AtPriority() + { + _server = FluentMockServer.Start(); + + // given + _server.Given(Request.Create().WithPath("/1").UsingGet()) + .AtPriority(2) + .RespondWith(Response.Create().WithStatusCode(200)); + + _server.Given(Request.Create().WithPath("/1").UsingGet()) + .AtPriority(1) + .RespondWith(Response.Create().WithStatusCode(400)); + + var mappings = _server.Mappings.ToArray(); + Check.That(mappings).HasSize(2); + Check.That(mappings[0].Priority).Equals(2); + Check.That(mappings[1].Priority).Equals(1); + + // when + var response = await new HttpClient().GetAsync("http://localhost:" + _server.Ports[0] + "/1"); + + // then + Check.That((int)response.StatusCode).IsEqualTo(400); + } + + [Fact] + public async Task FluentMockServer_Admin_Requests_Get() + { + // given + _server = FluentMockServer.Start(); + + // when + await new HttpClient().GetAsync("http://localhost:" + _server.Ports[0] + "/foo"); + + // then + Check.That(_server.LogEntries).HasSize(1); + var requestLogged = _server.LogEntries.First(); + Check.That(requestLogged.RequestMessage.Method).IsEqualTo("get"); + Check.That(requestLogged.RequestMessage.BodyAsBytes).IsNull(); + } + + [Fact] + public async Task Should_respond_to_request_bodyAsString() + { + // given + _server = FluentMockServer.Start(); + + _server + .Given(Request.Create() + .WithPath("/foo") + .UsingGet()) + .RespondWith(Response.Create() + .WithStatusCode(200) + .WithBody("Hello world!")); + + // when + var response = await new HttpClient().GetStringAsync("http://localhost:" + _server.Ports[0] + "/foo"); + + // then + Check.That(response).IsEqualTo("Hello world!"); + } + + [Fact] + public async Task Should_respond_to_request_bodyAsBase64() + { + // given + _server = FluentMockServer.Start(); + + _server.Given(Request.Create().WithPath("/foo").UsingGet()).RespondWith(Response.Create().WithBodyFromBase64("SGVsbG8gV29ybGQ/")); + + // when + var response = await new HttpClient().GetStringAsync("http://localhost:" + _server.Ports[0] + "/foo"); + + // then + Check.That(response).IsEqualTo("Hello World?"); + } + + [Fact] + public async Task Should_respond_to_request_bodyAsBytes() + { + // given + _server = FluentMockServer.Start(); + + _server.Given(Request.Create().WithPath("/foo").UsingGet()).RespondWith(Response.Create().WithBody(new byte[] { 48, 49 })); + + // when + var responseAsString = await new HttpClient().GetStringAsync("http://localhost:" + _server.Ports[0] + "/foo"); + var responseAsBytes = await new HttpClient().GetByteArrayAsync("http://localhost:" + _server.Ports[0] + "/foo"); + + // then + Check.That(responseAsString).IsEqualTo("01"); + Check.That(responseAsBytes).ContainsExactly(new byte[] { 48, 49 }); + } + + [Fact] + public async Task Should_respond_404_for_unexpected_request() + { + // given + _server = FluentMockServer.Start(); + + // when + var response = await new HttpClient().GetAsync("http://localhost:" + _server.Ports[0] + "/foo"); + + // then + Check.That(response.StatusCode).IsEqualTo(HttpStatusCode.NotFound); + Check.That((int)response.StatusCode).IsEqualTo(404); + } + + [Fact] + public async Task Should_find_a_request_satisfying_a_request_spec() + { + // given + _server = FluentMockServer.Start(); + + // when + await new HttpClient().GetAsync("http://localhost:" + _server.Ports[0] + "/foo"); + await new HttpClient().GetAsync("http://localhost:" + _server.Ports[0] + "/bar"); + + // then + var result = _server.FindLogEntries(Request.Create().WithPath(new RegexMatcher("^/b.*"))).ToList(); + Check.That(result).HasSize(1); + + var requestLogged = result.First(); + Check.That(requestLogged.RequestMessage.Path).IsEqualTo("/bar"); + Check.That(requestLogged.RequestMessage.Url).IsEqualTo("http://localhost:" + _server.Ports[0] + "/bar"); + } + + [Fact] + public async Task Should_reset_requestlogs() + { + // given + _server = FluentMockServer.Start(); + + // when + await new HttpClient().GetAsync("http://localhost:" + _server.Ports[0] + "/foo"); + _server.ResetLogEntries(); + + // then + Check.That(_server.LogEntries).IsEmpty(); + } + + [Fact] + public void Should_reset_mappings() + { + // given + _server = FluentMockServer.Start(); + + _server + .Given(Request.Create() + .WithPath("/foo") + .UsingGet()) + .RespondWith(Response.Create() + .WithBody(@"{ msg: ""Hello world!""}")); + + // when + _server.ResetMappings(); + + // then + Check.That(_server.Mappings).IsEmpty(); + Check.ThatAsyncCode(() => new HttpClient().GetStringAsync("http://localhost:" + _server.Ports[0] + "/foo")) + .ThrowsAny(); + } + + [Fact] + public async Task Should_respond_a_redirect_without_body() + { + // given + _server = FluentMockServer.Start(); + + _server + .Given(Request.Create() + .WithPath("/foo") + .UsingGet()) + .RespondWith(Response.Create() + .WithStatusCode(307) + .WithHeader("Location", "/bar")); + _server + .Given(Request.Create() + .WithPath("/bar") + .UsingGet()) + .RespondWith(Response.Create() + .WithStatusCode(200) + .WithBody("REDIRECT SUCCESSFUL")); + + // when + var response = await new HttpClient().GetStringAsync("http://localhost:" + _server.Ports[0] + "/foo"); + + // then + Check.That(response).IsEqualTo("REDIRECT SUCCESSFUL"); + } + + [Fact] + public async Task Should_delay_responses_for_a_given_route() + { + // given + _server = FluentMockServer.Start(); + + _server + .Given(Request.Create() + .WithPath("/*")) + .RespondWith(Response.Create() + .WithBody(@"{ msg: ""Hello world!""}") + .WithDelay(TimeSpan.FromMilliseconds(200))); + + // when + var watch = new Stopwatch(); + watch.Start(); + await new HttpClient().GetStringAsync("http://localhost:" + _server.Ports[0] + "/foo"); + watch.Stop(); + + // then + Check.That(watch.ElapsedMilliseconds).IsStrictlyGreaterThan(200); + } + + [Fact] + public async Task Should_delay_responses() + { + // given + _server = FluentMockServer.Start(); + _server.AddGlobalProcessingDelay(TimeSpan.FromMilliseconds(200)); + _server + .Given(Request.Create().WithPath("/*")) + .RespondWith(Response.Create().WithBody(@"{ msg: ""Hello world!""}")); + + // when + var watch = new Stopwatch(); + watch.Start(); + await new HttpClient().GetStringAsync("http://localhost:" + _server.Ports[0] + "/foo"); + watch.Stop(); + + // then + Check.That(watch.ElapsedMilliseconds).IsStrictlyGreaterThan(200); + } + + [Fact] + public async Task Should_proxy_responses() + { + // given + _server = FluentMockServer.Start(); + _server + .Given(Request.Create().WithPath("/*")) + .RespondWith(Response.Create().WithProxy("http://www.google.com")); + + // when + var result = await new HttpClient().GetStringAsync("http://localhost:" + _server.Ports[0] + "/search?q=test"); + + // then + Check.That(result).Contains("google"); + } + + //Leaving commented as this requires an actual certificate with password, along with a service that expects a client certificate + //[Fact] + //public async Task Should_proxy_responses_with_client_certificate() + //{ + // // given + // _server = FluentMockServer.Start(); + // _server + // .Given(Request.Create().WithPath("/*")) + // .RespondWith(Response.Create().WithProxy("https://server-that-expects-a-client-certificate", @"\\yourclientcertificatecontainingprivatekey.pfx", "yourclientcertificatepassword")); + + // // when + // var result = await new HttpClient().GetStringAsync("http://localhost:" + _server.Ports[0] + "/someurl?someQuery=someValue"); + + // // then + // Check.That(result).Contains("google"); + //} + + [Fact] + public async Task FluentMockServer_Logging_SetMaxRequestLogCount() + { + // Assign + var client = new HttpClient(); + // Act + _server = FluentMockServer.Start(); + _server.SetMaxRequestLogCount(2); + + await client.GetAsync("http://localhost:" + _server.Ports[0] + "/foo1"); + await client.GetAsync("http://localhost:" + _server.Ports[0] + "/foo2"); + await client.GetAsync("http://localhost:" + _server.Ports[0] + "/foo3"); + + // Assert + Check.That(_server.LogEntries).HasSize(2); + + var requestLoggedA = _server.LogEntries.First(); + Check.That(requestLoggedA.RequestMessage.Path).EndsWith("/foo2"); + + var requestLoggedB = _server.LogEntries.Last(); + Check.That(requestLoggedB.RequestMessage.Path).EndsWith("/foo3"); + } + + public void Dispose() + { + _server?.Stop(); + } + } } \ No newline at end of file diff --git a/test/WireMock.Net.Tests/ResponseTests.cs b/test/WireMock.Net.Tests/ResponseTests.cs index 18635575..6a74dd7e 100644 --- a/test/WireMock.Net.Tests/ResponseTests.cs +++ b/test/WireMock.Net.Tests/ResponseTests.cs @@ -70,14 +70,52 @@ namespace WireMock.Net.Tests } [Fact] - public async Task Response_ProvideResponse_Encoding_Body() + public async Task Response_ProvideResponse_WithBody_Bytes_Encoding_Destination_String() { // given string bodyAsString = "abc"; byte[] body = Encoding.UTF8.GetBytes(bodyAsString); var request = new RequestMessage(new Uri("http://localhost/foo"), "POST", clientIP, body, bodyAsString, Encoding.UTF8); - var response = Response.Create().WithBody("test", Encoding.ASCII); + var response = Response.Create().WithBody(new byte[] { 48, 49 }, BodyDestinationFormat.String, Encoding.ASCII); + + // act + var responseMessage = await response.ProvideResponseAsync(request); + + // then + Check.That(responseMessage.Body).Equals("01"); + Check.That(responseMessage.BodyAsBytes).IsNull(); + Check.That(responseMessage.BodyEncoding).Equals(Encoding.ASCII); + } + + [Fact] + public async Task Response_ProvideResponse_WithBody_Bytes_Encoding_Destination_Bytes() + { + // given + string bodyAsString = "abc"; + byte[] body = Encoding.UTF8.GetBytes(bodyAsString); + var request = new RequestMessage(new Uri("http://localhost/foo"), "POST", clientIP, body, bodyAsString, Encoding.UTF8); + + var response = Response.Create().WithBody(new byte[] { 48, 49 }, BodyDestinationFormat.SameAsSource, Encoding.ASCII); + + // act + var responseMessage = await response.ProvideResponseAsync(request); + + // then + Check.That(responseMessage.BodyAsBytes).ContainsExactly(new byte[] { 48, 49 }); + Check.That(responseMessage.Body).IsNull(); + Check.That(responseMessage.BodyEncoding).IsNull(); + } + + [Fact] + public async Task Response_ProvideResponse_WithBody_String_Encoding() + { + // given + string bodyAsString = "abc"; + byte[] body = Encoding.UTF8.GetBytes(bodyAsString); + var request = new RequestMessage(new Uri("http://localhost/foo"), "POST", clientIP, body, bodyAsString, Encoding.UTF8); + + var response = Response.Create().WithBody("test", null, Encoding.ASCII); // act var responseMessage = await response.ProvideResponseAsync(request); @@ -88,7 +126,7 @@ namespace WireMock.Net.Tests } [Fact] - public async Task Response_ProvideResponse_Encoding_JsonBody() + public async Task Response_ProvideResponse_WithBody_Object_Encoding() { // given string bodyAsString = "abc";