diff --git a/CHANGELOG.md b/CHANGELOG.md index 13ae35c4..0911f9ae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +# 1.0.4.8 (23 July 2018) + + - [#167](https://github.com/WireMock-Net/WireMock.Net/issues/167) - Feature: Support for JsonPath in the response (with HandleBars) +feature + +Commits: bd5e99b082...4e0b22db08 + + # 1.0.4.7 (19 July 2018) - [#169](https://github.com/WireMock-Net/WireMock.Net/pull/169) - Fix for Restricted Response headers contributed by Stef Heyenrath ([StefH](https://github.com/StefH)) +fix diff --git a/GitReleaseNotes.txt b/GitReleaseNotes.txt index b3eccff4..7a4234e7 100644 --- a/GitReleaseNotes.txt +++ b/GitReleaseNotes.txt @@ -1,5 +1,5 @@ https://github.com/GitTools/GitReleaseNotes -GitReleaseNotes.exe . /OutputFile CHANGELOG.md /Version 1.0.4.7 +GitReleaseNotes.exe . /OutputFile CHANGELOG.md /Version 1.0.4.8 GitReleaseNotes.exe . /OutputFile CHANGELOG.md /allTags diff --git a/README.md b/README.md index 5563ddce..febd5dbb 100644 --- a/README.md +++ b/README.md @@ -4,27 +4,27 @@ A C# .NET version based on [mock4net](https://github.com/alexvictoor/mock4net) w ### Info | | | | --- | --- | -| **Chat** | [![Gitter](https://img.shields.io/gitter/room/wiremock_dotnet/Lobby.svg)](https://gitter.im/wiremock_dotnet/Lobby) | -| **Issues** | [![GitHub issues](https://img.shields.io/github/issues/WireMock-Net/WireMock.Net.svg)](https://github.com/WireMock-Net/WireMock.Net/issues) | -| **Build** | [![Build status](https://ci.appveyor.com/api/projects/status/b3n6q3ygbww4lyls?svg=true)](https://ci.appveyor.com/project/StefH/wiremock-net) | -| | | -| ***Sonar*** |   | -|  **Sonar Quality Gate** | [![Sonar Quality Gate](https://sonarcloud.io/api/project_badges/measure?project=wiremock&metric=alert_status)](https://sonarcloud.io/project/issues?id=wiremock) | -|  **Sonar Bugs** | [![Sonar Bugs](https://sonarcloud.io/api/project_badges/measure?project=wiremock&metric=bugs)](https://sonarcloud.io/project/issues?id=wiremock&resolved=false&types=BUG) | -|  **Sonar Code Smells** | [![Sonar Code Smells](https://sonarcloud.io/api/project_badges/measure?project=wiremock&metric=code_smells)](https://sonarcloud.io/project/issues?id=wiremock&resolved=false&types=CODE_SMELL) | -|  **Sonar Coverage** | [![Sonar Coverage](https://sonarcloud.io/api/project_badges/measure?project=wiremock&metric=coverage)](https://sonarcloud.io/component_measures?id=wiremock&metric=coverage) | -|  **Coveralls.io** | [![Coverage Status](https://coveralls.io/repos/github/WireMock-Net/WireMock.Net/badge.svg?branch=master)](https://coveralls.io/github/WireMock-Net/WireMock.Net?branch=master) | +| ***Project*** |   | +|   **Chat** | [![Gitter](https://img.shields.io/gitter/room/wiremock_dotnet/Lobby.svg)](https://gitter.im/wiremock_dotnet/Lobby) | +|   **Issues** | [![GitHub issues](https://img.shields.io/github/issues/WireMock-Net/WireMock.Net.svg)](https://github.com/WireMock-Net/WireMock.Net/issues) | | | | +| ***Quality*** |   | +|   **Build** | [![Build status](https://ci.appveyor.com/api/projects/status/b3n6q3ygbww4lyls?svg=true)](https://ci.appveyor.com/project/StefH/wiremock-net) | +|   **CodeFactor** | [![CodeFactor](https://www.codefactor.io/repository/github/wiremock-net/wiremock.net/badge)](https://www.codefactor.io/repository/github/wiremock-net/wiremock.net) +|   **Sonar Quality Gate** | [![Sonar Quality Gate](https://sonarcloud.io/api/project_badges/measure?project=wiremock&metric=alert_status)](https://sonarcloud.io/project/issues?id=wiremock) | +|   **Sonar Bugs** | [![Sonar Bugs](https://sonarcloud.io/api/project_badges/measure?project=wiremock&metric=bugs)](https://sonarcloud.io/project/issues?id=wiremock&resolved=false&types=BUG) | +|   **Sonar Code Smells** | [![Sonar Code Smells](https://sonarcloud.io/api/project_badges/measure?project=wiremock&metric=code_smells)](https://sonarcloud.io/project/issues?id=wiremock&resolved=false&types=CODE_SMELL) | +|   **Sonar Coverage** | [![Sonar Coverage](https://sonarcloud.io/api/project_badges/measure?project=wiremock&metric=coverage)](https://sonarcloud.io/component_measures?id=wiremock&metric=coverage) | +|   **Coveralls** | [![Coverage Status](https://coveralls.io/repos/github/WireMock-Net/WireMock.Net/badge.svg?branch=master)](https://coveralls.io/github/WireMock-Net/WireMock.Net?branch=master) | +| | | ***Nuget*** |   | -|  **WireMock.Net** | [![NuGet Badge WireMock.Net](https://buildstats.info/nuget/WireMock.Net)](https://www.nuget.org/packages/WireMock.Net) | -|  **WireMock.Net.StandAlone** | [![NuGet Badge WireMock.Net.StandAlone](https://buildstats.info/nuget/WireMock.Net.StandAlone)](https://www.nuget.org/packages/WireMock.Net.StandAlone) | +|   **WireMock.Net** | [![NuGet Badge WireMock.Net](https://buildstats.info/nuget/WireMock.Net)](https://www.nuget.org/packages/WireMock.Net) | +|   **WireMock.Net.StandAlone** | [![NuGet Badge WireMock.Net.StandAlone](https://buildstats.info/nuget/WireMock.Net.StandAlone)](https://www.nuget.org/packages/WireMock.Net.StandAlone) | ### Frameworks The following frameworks are supported: -- net 4.5.2 and up -- net 4.6 and up -- netstandard 1.3 -- netstandard 2.0 +- net 4.5.2 and up & net 4.6 and up +- netstandard 1.3 & netstandard 2.0 ## Build info To build you need: diff --git a/examples/WireMock.Net.ConsoleApplication/MainApp.cs b/examples/WireMock.Net.ConsoleApplication/MainApp.cs index e3455112..a44bf4aa 100644 --- a/examples/WireMock.Net.ConsoleApplication/MainApp.cs +++ b/examples/WireMock.Net.ConsoleApplication/MainApp.cs @@ -256,6 +256,46 @@ namespace WireMock.Net.ConsoleApplication .WithDelay(TimeSpan.FromMilliseconds(100)) ); + server + .Given(Request.Create().WithPath("/jsonpathtestToken").UsingPost()) + .RespondWith(Response.Create() + .WithHeader("Content-Type", "application/json") + .WithBody("{{JsonPath.SelectToken request.body \"$.Manufacturers[?(@.Name == 'Acme Co')]\"}}") + .WithTransformer() + ); + + server + .Given(Request.Create().WithPath("/zubinix").UsingPost()) + .RespondWith(Response.Create() + .WithHeader("Content-Type", "application/json") + .WithBody("{ \"result\": \"{{JsonPath.SelectToken request.bodyAsJson \"username\"}}\" }") + .WithTransformer() + ); + + server + .Given(Request.Create().WithPath("/zubinix2").UsingPost()) + .RespondWith(Response.Create() + .WithHeader("Content-Type", "application/json") + .WithBodyAsJson(new { path = "{{request.path}}", result = "{{JsonPath.SelectToken request.bodyAsJson \"username\"}}" }) + .WithTransformer() + ); + + server + .Given(Request.Create().WithPath("/jsonpathtestTokenJson").UsingPost()) + .RespondWith(Response.Create() + .WithHeader("Content-Type", "application/json") + .WithBodyAsJson(new { status = "OK", url = "{{request.url}}", transformed = "{{JsonPath.SelectToken request.body \"$.Manufacturers[?(@.Name == 'Acme Co')]\"}}" } ) + .WithTransformer() + ); + + server + .Given(Request.Create().WithPath("/jsonpathtestTokens").UsingPost()) + .RespondWith(Response.Create() + .WithHeader("Content-Type", "application/json") + .WithBody("[{{#JsonPath.SelectTokens request.body \"$..Products[?(@.Price >= 50)].Name\"}} { \"idx\":{{id}}, \"value\":\"{{value}}\" }, {{/JsonPath.SelectTokens}} {} ]") + .WithTransformer() + ); + server .Given(Request.Create() .WithPath("/state1") diff --git a/examples/WireMock.Net.Service/Service-Install.bat b/examples/WireMock.Net.Service/Service-Install.bat index d6064bf1..bf890021 100644 --- a/examples/WireMock.Net.Service/Service-Install.bat +++ b/examples/WireMock.Net.Service/Service-Install.bat @@ -1,5 +1,5 @@ @echo off -call uninstall.bat +call Service-Uninstall.bat SET mypath=%~dp0 SET targetpath=C:\Services\WireMock.Net.Service\ diff --git a/examples/WireMock.Net.WebApplication/WireMock.Net.WebApplication.NETCore2.csproj b/examples/WireMock.Net.WebApplication/WireMock.Net.WebApplication.NETCore2.csproj index 8269e247..c8ca0e7f 100644 --- a/examples/WireMock.Net.WebApplication/WireMock.Net.WebApplication.NETCore2.csproj +++ b/examples/WireMock.Net.WebApplication/WireMock.Net.WebApplication.NETCore2.csproj @@ -1,19 +1,25 @@  - netcoreapp2.0 + netcoreapp2.0;netcoreapp2.1 win10-x64 WireMock.Net.WebApplication.Program WireMock.Net.WebApplication WireMock.Net.WebApplication - + + + + + + + diff --git a/src/WireMock.Net.StandAlone/WireMock.Net.StandAlone.csproj b/src/WireMock.Net.StandAlone/WireMock.Net.StandAlone.csproj index 6da4d57c..f7493b32 100644 --- a/src/WireMock.Net.StandAlone/WireMock.Net.StandAlone.csproj +++ b/src/WireMock.Net.StandAlone/WireMock.Net.StandAlone.csproj @@ -3,7 +3,7 @@ Lightweight StandAlone Http Mocking Server for .Net. WireMock.Net.StandAlone - 1.0.4.7 + 1.0.4.8-preview-01 Stef Heyenrath net452;net46;netstandard1.3;netstandard2.0 true diff --git a/src/WireMock.Net/Matchers/JsonMatcher.cs b/src/WireMock.Net/Matchers/JsonMatcher.cs index 95a88a62..d39477c3 100644 --- a/src/WireMock.Net/Matchers/JsonMatcher.cs +++ b/src/WireMock.Net/Matchers/JsonMatcher.cs @@ -73,10 +73,21 @@ namespace WireMock.Matchers JToken jtokenInput = input is JToken tokenInput ? tokenInput : JObject.FromObject(input); // Check if JToken or string or object - JToken jtokenValue = - Value is JToken tokenValue ? tokenValue : - Value is string stringValue ? JToken.Parse(stringValue) : - JObject.FromObject(input); + JToken jtokenValue; + switch (Value) + { + case JToken tokenValue: + jtokenValue = tokenValue; + break; + + case string stringValue: + jtokenValue = JToken.Parse(stringValue); + break; + + default: + jtokenValue = JObject.FromObject(Value); + break; + } match = JToken.DeepEquals(jtokenValue, jtokenInput); } diff --git a/src/WireMock.Net/Matchers/MatchBehaviourHelper.cs b/src/WireMock.Net/Matchers/MatchBehaviourHelper.cs index a45dafec..9468d1fa 100644 --- a/src/WireMock.Net/Matchers/MatchBehaviourHelper.cs +++ b/src/WireMock.Net/Matchers/MatchBehaviourHelper.cs @@ -1,5 +1,4 @@ - -namespace WireMock.Matchers +namespace WireMock.Matchers { internal static class MatchBehaviourHelper { diff --git a/src/WireMock.Net/Owin/OwinResponseMapper.cs b/src/WireMock.Net/Owin/OwinResponseMapper.cs index f266446d..7cde04f0 100644 --- a/src/WireMock.Net/Owin/OwinResponseMapper.cs +++ b/src/WireMock.Net/Owin/OwinResponseMapper.cs @@ -76,6 +76,11 @@ namespace WireMock.Owin #endif ) { + if (responseMessage == null) + { + return; + } + response.StatusCode = responseMessage.StatusCode; byte[] bytes = null; diff --git a/src/WireMock.Net/Server/FluentMockServer.Admin.cs b/src/WireMock.Net/Server/FluentMockServer.Admin.cs index c32d2991..c98fdd97 100644 --- a/src/WireMock.Net/Server/FluentMockServer.Admin.cs +++ b/src/WireMock.Net/Server/FluentMockServer.Admin.cs @@ -701,7 +701,6 @@ namespace WireMock.Server { responseBuilder = responseBuilder.WithBodyFromBase64(responseModel.BodyFromBase64, ToEncoding(responseModel.BodyEncoding)); } - else if (responseModel.BodyAsFile != null) { responseBuilder = responseBuilder.WithBodyFromFile(responseModel.BodyAsFile); diff --git a/src/WireMock.Net/Server/FluentMockServer.cs b/src/WireMock.Net/Server/FluentMockServer.cs index 6b43b302..04ad7e39 100644 --- a/src/WireMock.Net/Server/FluentMockServer.cs +++ b/src/WireMock.Net/Server/FluentMockServer.cs @@ -14,6 +14,7 @@ using WireMock.Owin; using WireMock.RequestBuilders; using WireMock.ResponseProviders; using WireMock.Settings; +using WireMock.Transformers; using WireMock.Validation; namespace WireMock.Server @@ -387,7 +388,6 @@ namespace WireMock.Server public void SetMaxRequestLogCount([CanBeNull] int? maxRequestLogCount) { _options.MaxRequestLogCount = maxRequestLogCount; - } /// diff --git a/src/WireMock.Net/Transformers/HandleBarsHelpers.cs b/src/WireMock.Net/Transformers/HandleBarsHelpers.cs new file mode 100644 index 00000000..17078d1c --- /dev/null +++ b/src/WireMock.Net/Transformers/HandleBarsHelpers.cs @@ -0,0 +1,89 @@ +using System; +using System.Collections.Generic; +using HandlebarsDotNet; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using WireMock.Validation; + +namespace WireMock.Transformers +{ + internal static class HandlebarsHelpers + { + public static void Register() + { + Handlebars.RegisterHelper("JsonPath.SelectToken", (writer, context, arguments) => + { + (JObject valueToProcess, string jsonpath) = Parse(arguments); + + JToken result = null; + try + { + result = valueToProcess.SelectToken(jsonpath); + } + catch (JsonException) + { + // Ignore JsonException and return + return; + } + + if (result != null) + { + writer.WriteSafeString(result); + } + }); + + Handlebars.RegisterHelper("JsonPath.SelectTokens", (writer, options, context, arguments) => + { + (JObject valueToProcess, string jsonpath) = Parse(arguments); + + IEnumerable values = null; + try + { + values = valueToProcess.SelectTokens(jsonpath); + } + catch (JsonException) + { + // Ignore JsonException and return + return; + } + + if (values == null) + { + return; + } + + int id = 0; + foreach (JToken value in values) + { + options.Template(writer, new { id, value }); + id++; + } + }); + } + + private static (JObject valueToProcess, string jsonpath) Parse(object[] arguments) + { + Check.Condition(arguments, args => args.Length == 2, nameof(arguments)); + Check.NotNull(arguments[0], "arguments[0]"); + Check.NotNullOrEmpty(arguments[1] as string, "arguments[1]"); + + JObject valueToProcess; + + switch (arguments[0]) + { + case string jsonAsString: + valueToProcess = JObject.Parse(jsonAsString); + break; + + case JObject jsonAsJObject: + valueToProcess = jsonAsJObject; + break; + + default: + throw new NotSupportedException($"The value '{arguments[0]}' with type {arguments[0].GetType()} cannot be used in Handlebars JsonPath."); + } + + return (valueToProcess, arguments[1] as string); + } + } +} \ No newline at end of file diff --git a/src/WireMock.Net/Transformers/ResponseMessageTransformer.cs b/src/WireMock.Net/Transformers/ResponseMessageTransformer.cs index 7e22ad35..36de18bf 100644 --- a/src/WireMock.Net/Transformers/ResponseMessageTransformer.cs +++ b/src/WireMock.Net/Transformers/ResponseMessageTransformer.cs @@ -2,12 +2,18 @@ using System.Linq; using HandlebarsDotNet; using Newtonsoft.Json; +using Newtonsoft.Json.Linq; using WireMock.Util; namespace WireMock.Transformers { internal static class ResponseMessageTransformer { + static ResponseMessageTransformer() + { + HandlebarsHelpers.Register(); + } + public static ResponseMessage Transform(RequestMessage requestMessage, ResponseMessage original) { bool bodyIsJson = original.BodyAsJson != null; @@ -20,21 +26,13 @@ namespace WireMock.Transformers var template = new { request = requestMessage }; - // Body - Formatting formatting = original.BodyAsJsonIndented == true ? Formatting.Indented : Formatting.None; - string body = bodyIsJson ? JsonConvert.SerializeObject(original.BodyAsJson, formatting) : original.Body; - if (body != null) + if (!bodyIsJson) { - var templateBody = Handlebars.Compile(body); - - if (!bodyIsJson) - { - responseMessage.Body = templateBody(template); - } - else - { - responseMessage.BodyAsJson = JsonConvert.DeserializeObject(templateBody(template)); - } + TransformBodyAsString(template, original, responseMessage); + } + else + { + TransformBodyAsJson(template, original, responseMessage); } // Headers @@ -54,5 +52,79 @@ namespace WireMock.Transformers return responseMessage; } + + private static void TransformBodyAsJson(object template, ResponseMessage original, ResponseMessage responseMessage) + { + JObject jobject; + switch (original.BodyAsJson) + { + case JObject bodyAsJObject: + jobject = bodyAsJObject; + break; + + default: + jobject = JObject.FromObject(original.BodyAsJson); + break; + } + + WalkNode(jobject, template); + + responseMessage.BodyAsJson = jobject; + } + + private static void WalkNode(JToken node, object template) + { + if (node.Type == JTokenType.Object) + { + // In case of Object, loop all children. + foreach (JProperty child in node.Children()) + { + WalkNode(child.Value, template); + } + } + else if (node.Type == JTokenType.Array) + { + // In case of Array, loop all items. + foreach (JToken child in node.Children()) + { + WalkNode(child, template); + } + } + else if (node.Type == JTokenType.String) + { + // In case of string, try to transform the value. + string stringValue = node.Value(); + if (string.IsNullOrEmpty(stringValue)) + { + return; + } + + var templateForStringValue = Handlebars.Compile(stringValue); + string transformedString = templateForStringValue(template); + if (!string.Equals(stringValue, transformedString)) + { + JToken value; + try + { + // Try to convert this string into a real JsonObject + value = JToken.Parse(transformedString); + } + catch (JsonException) + { + // Ignore JsonException and just convert to JToken + value = transformedString; + } + + node.Replace(value); + } + } + } + + private static void TransformBodyAsString(object template, ResponseMessage original, ResponseMessage responseMessage) + { + var templateBody = Handlebars.Compile(original.Body); + + responseMessage.Body = templateBody(template); + } } } \ No newline at end of file diff --git a/src/WireMock.Net/WireMock.Net.csproj b/src/WireMock.Net/WireMock.Net.csproj index 2dc2e055..9cf8bd90 100644 --- a/src/WireMock.Net/WireMock.Net.csproj +++ b/src/WireMock.Net/WireMock.Net.csproj @@ -3,7 +3,7 @@ Lightweight Http Mocking Server for .Net, inspired by WireMock from the Java landscape. WireMock.Net - 1.0.4.7 + 1.0.4.8-preview-01 Stef Heyenrath net452;net46;netstandard1.3;netstandard2.0 true @@ -41,7 +41,7 @@ All - + @@ -53,6 +53,7 @@ + @@ -62,6 +63,7 @@ + @@ -69,6 +71,7 @@ + diff --git a/test/WireMock.Net.Tests/FluentMockServerTests.cs b/test/WireMock.Net.Tests/FluentMockServerTests.cs index 9b45208d..572483da 100644 --- a/test/WireMock.Net.Tests/FluentMockServerTests.cs +++ b/test/WireMock.Net.Tests/FluentMockServerTests.cs @@ -585,7 +585,6 @@ namespace WireMock.Net.Tests Check.That(response).IsEqualTo("/fooBar"); } - [Fact] public async Task FluentMockServer_Should_exclude_restrictedResponseHeader_for_IOwinResponse() { diff --git a/test/WireMock.Net.Tests/Matchers/JsonMatcherTests.cs b/test/WireMock.Net.Tests/Matchers/JsonMatcherTests.cs index 14336c3f..85bc18d6 100644 --- a/test/WireMock.Net.Tests/Matchers/JsonMatcherTests.cs +++ b/test/WireMock.Net.Tests/Matchers/JsonMatcherTests.cs @@ -65,7 +65,7 @@ namespace WireMock.Net.Tests.Matchers public void JsonMatcher_IsMatch_JObject1() { // Assign - var matcher = new JsonMatcher(new { Id = 1, Name = "test" }); + var matcher = new JsonMatcher(new { Id = 1, Name = "Test" }); // Act var jobject = new JObject @@ -83,7 +83,7 @@ namespace WireMock.Net.Tests.Matchers public void JsonMatcher_IsMatch_JObject2() { // Assign - var matcher = new JsonMatcher(new { Id = 1, Name = "test" }); + var matcher = new JsonMatcher(new { Id = 1, Name = "Test" }); // Act var jobject = JObject.Parse("{ \"Id\" : 1, \"Name\" : \"Test\" }"); diff --git a/test/WireMock.Net.Tests/ResponseBuilderTests/ResponseWithHandlebarsTests.cs b/test/WireMock.Net.Tests/ResponseBuilderTests/ResponseWithHandlebarsTests.cs index 00d8988a..eb0fa8cb 100644 --- a/test/WireMock.Net.Tests/ResponseBuilderTests/ResponseWithHandlebarsTests.cs +++ b/test/WireMock.Net.Tests/ResponseBuilderTests/ResponseWithHandlebarsTests.cs @@ -4,6 +4,7 @@ using System.Text; using System.Threading.Tasks; using Microsoft.Owin; using Newtonsoft.Json; +using Newtonsoft.Json.Linq; using NFluent; using WireMock.Models; using WireMock.ResponseBuilders; @@ -19,7 +20,7 @@ namespace WireMock.Net.Tests.ResponseBuilderTests [Fact] public async Task Response_ProvideResponse_Handlebars_WithBodyAsJson() { - // given + // Assign string jsonString = "{ \"things\": [ { \"name\": \"RequiredThing\" }, { \"name\": \"Wiremock\" } ] }"; var bodyData = new BodyData { @@ -32,17 +33,17 @@ namespace WireMock.Net.Tests.ResponseBuilderTests .WithBodyAsJson(new { x = "test {{request.url}}" }) .WithTransformer(); - // act + // Act var responseMessage = await response.ProvideResponseAsync(request); - // then + // Assert Check.That(JsonConvert.SerializeObject(responseMessage.BodyAsJson)).Equals("{\"x\":\"test http://localhost/foo\"}"); } [Fact] public async Task Response_ProvideResponse_Handlebars_UrlPathVerb() { - // given + // Assign var body = new BodyData { BodyAsString = "whatever" @@ -53,17 +54,17 @@ namespace WireMock.Net.Tests.ResponseBuilderTests .WithBody("test {{request.url}} {{request.path}} {{request.method}}") .WithTransformer(); - // act + // Act var responseMessage = await response.ProvideResponseAsync(request); - // then + // Assert Check.That(responseMessage.Body).Equals("test http://localhost/foo /foo post"); } [Fact] public async Task Response_ProvideResponse_Handlebars_UrlPath() { - // given + // Assign var urlDetails = UrlUtils.Parse(new Uri("http://localhost/wiremock/a/b"), new PathString("/wiremock")); var request = new RequestMessage(urlDetails, "POST", ClientIp); @@ -71,17 +72,17 @@ namespace WireMock.Net.Tests.ResponseBuilderTests .WithBody("{{request.url}} {{request.absoluteurl}} {{request.path}} {{request.absolutepath}}") .WithTransformer(); - // act + // Act var responseMessage = await response.ProvideResponseAsync(request); - // then + // Assert Check.That(responseMessage.Body).Equals("http://localhost/a/b http://localhost/wiremock/a/b /a/b /wiremock/a/b"); } [Fact] public async Task Response_ProvideResponse_Handlebars_PathSegments() { - // given + // Assign var urlDetails = UrlUtils.Parse(new Uri("http://localhost/wiremock/a/b"), new PathString("/wiremock")); var request = new RequestMessage(urlDetails, "POST", ClientIp); @@ -89,17 +90,17 @@ namespace WireMock.Net.Tests.ResponseBuilderTests .WithBody("{{request.pathsegments.[0]}} {{request.absolutepathsegments.[0]}}") .WithTransformer(); - // act + // Act var responseMessage = await response.ProvideResponseAsync(request); - // then + // Assert Check.That(responseMessage.Body).Equals("a wiremock"); } [Fact] public async Task Response_ProvideResponse_Handlebars_Query() { - // given + // Assign var body = new BodyData { BodyAsString = "abc" @@ -110,17 +111,17 @@ namespace WireMock.Net.Tests.ResponseBuilderTests .WithBody("test keya={{request.query.a}} idx={{request.query.a.[0]}} idx={{request.query.a.[1]}} keyb={{request.query.b}}") .WithTransformer(); - // act + // Act var responseMessage = await response.ProvideResponseAsync(request); - // then + // Assert Check.That(responseMessage.Body).Equals("test keya=1 idx=1 idx=2 keyb=5"); } [Fact] public async Task Response_ProvideResponse_Handlebars_Header() { - // given + // Assign var body = new BodyData { BodyAsString = "abc" @@ -129,10 +130,10 @@ namespace WireMock.Net.Tests.ResponseBuilderTests var response = Response.Create().WithHeader("x", "{{request.headers.Content-Type}}").WithBody("test").WithTransformer(); - // act + // Act var responseMessage = await response.ProvideResponseAsync(request); - // then + // Assert Check.That(responseMessage.Body).Equals("test"); Check.That(responseMessage.Headers).ContainsKey("x"); Check.That(responseMessage.Headers["x"]).ContainsExactly("text/plain"); @@ -141,7 +142,7 @@ namespace WireMock.Net.Tests.ResponseBuilderTests [Fact] public async Task Response_ProvideResponse_Handlebars_Headers() { - // given + // Assign var body = new BodyData { BodyAsString = "abc" @@ -150,10 +151,10 @@ namespace WireMock.Net.Tests.ResponseBuilderTests var response = Response.Create().WithHeader("x", "{{request.headers.Content-Type}}", "{{request.url}}").WithBody("test").WithTransformer(); - // act + // Act var responseMessage = await response.ProvideResponseAsync(request); - // then + // Assert Check.That(responseMessage.Body).Equals("test"); Check.That(responseMessage.Headers).ContainsKey("x"); Check.That(responseMessage.Headers["x"]).Contains("text/plain"); @@ -163,7 +164,7 @@ namespace WireMock.Net.Tests.ResponseBuilderTests [Fact] public async Task Response_ProvideResponse_Handlebars_Origin_Port_Protocol_Host() { - // given + // Assign var body = new BodyData { BodyAsString = "abc" @@ -174,11 +175,298 @@ namespace WireMock.Net.Tests.ResponseBuilderTests .WithBody("test {{request.origin}} {{request.port}} {{request.protocol}} {{request.host}}") .WithTransformer(); - // act + // Act var responseMessage = await response.ProvideResponseAsync(request); - // then + // Assert Check.That(responseMessage.Body).Equals("test http://localhost:1234 1234 http localhost"); } + + [Fact] + public async Task Response_ProvideResponse_Handlebars_JsonPath_SelectToken_ResponseBodyAsJson() + { + // Assign + var body = new BodyData + { + BodyAsString = @"{ + ""Stores"": [ + ""Lambton Quay"", + ""Willis Street"" + ], + ""Manufacturers"": [ + { + ""Name"": ""Acme Co"", + ""Products"": [ + { + ""Name"": ""Anvil"", + ""Price"": 50 + } + ] + }, + { + ""Name"": ""Contoso"", + ""Products"": [ + { + ""Name"": ""Elbow Grease"", + ""Price"": 99.95 + }, + { + ""Name"": ""Headlight Fluid"", + ""Price"": 4 + } + ] + } + ] + }" + }; + + var request = new RequestMessage(new UrlDetails("http://localhost:1234"), "POST", ClientIp, body); + + var response = Response.Create() + .WithHeader("Content-Type", "application/json") + .WithBodyAsJson(new { x = "{{JsonPath.SelectToken request.body \"$.Manufacturers[?(@.Name == 'Acme Co')]\"}}" }) + .WithTransformer(); + + // Act + var responseMessage = await response.ProvideResponseAsync(request); + + // Assert + JObject j = JObject.FromObject(responseMessage.BodyAsJson); + Check.That(j["x"]).IsNotNull(); + Check.That(j["x"]["Name"].ToString()).Equals("Acme Co"); + } + + [Fact] + public async Task Response_ProvideResponse_Handlebars_JsonPath_SelectToken_Request_BodyAsString() + { + // Assign + var body = new BodyData + { + BodyAsString = @"{ + ""Stores"": [ + ""Lambton Quay"", + ""Willis Street"" + ], + ""Manufacturers"": [ + { + ""Name"": ""Acme Co"", + ""Products"": [ + { + ""Name"": ""Anvil"", + ""Price"": 50 + } + ] + }, + { + ""Name"": ""Contoso"", + ""Products"": [ + { + ""Name"": ""Elbow Grease"", + ""Price"": 99.95 + }, + { + ""Name"": ""Headlight Fluid"", + ""Price"": 4 + } + ] + } + ] + }" + }; + + var request = new RequestMessage(new UrlDetails("http://localhost:1234"), "POST", ClientIp, body); + + var response = Response.Create() + .WithHeader("Content-Type", "application/json") + .WithBody("{{JsonPath.SelectToken request.body \"$.Manufacturers[?(@.Name == 'Acme Co')]\"}}") + .WithTransformer(); + + // Act + var responseMessage = await response.ProvideResponseAsync(request); + + // Assert + Check.That(responseMessage.Body).Equals("{\r\n \"Name\": \"Acme Co\",\r\n \"Products\": [\r\n {\r\n \"Name\": \"Anvil\",\r\n \"Price\": 50\r\n }\r\n ]\r\n}"); + } + + [Fact] + public async Task Response_ProvideResponse_Handlebars_JsonPath_SelectToken_Request_BodyAsJObject() + { + // Assign + var body = new BodyData + { + BodyAsJson = JObject.Parse(@"{ + 'Stores': [ + 'Lambton Quay', + 'Willis Street' + ], + 'Manufacturers': [ + { + 'Name': 'Acme Co', + 'Products': [ + { + 'Name': 'Anvil', + 'Price': 50 + } + ] + }, + { + 'Name': 'Contoso', + 'Products': [ + { + 'Name': 'Elbow Grease', + 'Price': 99.95 + }, + { + 'Name': 'Headlight Fluid', + 'Price': 4 + } + ] + } + ] + }") + }; + + var request = new RequestMessage(new UrlDetails("http://localhost:1234"), "POST", ClientIp, body); + + var response = Response.Create() + .WithHeader("Content-Type", "application/json") + .WithBody("{{JsonPath.SelectToken request.bodyAsJson \"$.Manufacturers[?(@.Name == 'Acme Co')]\"}}") + .WithTransformer(); + + // Act + var responseMessage = await response.ProvideResponseAsync(request); + + // Assert + Check.That(responseMessage.Body).Equals("{\r\n \"Name\": \"Acme Co\",\r\n \"Products\": [\r\n {\r\n \"Name\": \"Anvil\",\r\n \"Price\": 50\r\n }\r\n ]\r\n}"); + } + + [Fact] + public async Task Response_ProvideResponse_Handlebars_JsonPath_SelectTokens_Request_BodyAsString() + { + // Assign + var body = new BodyData + { + BodyAsString = @"{ + ""Stores"": [ + ""Lambton Quay"", + ""Willis Street"" + ], + ""Manufacturers"": [ + { + ""Name"": ""Acme Co"", + ""Products"": [ + { + ""Name"": ""Anvil"", + ""Price"": 50 + } + ] + }, + { + ""Name"": ""Contoso"", + ""Products"": [ + { + ""Name"": ""Elbow Grease"", + ""Price"": 99.95 + }, + { + ""Name"": ""Headlight Fluid"", + ""Price"": 4 + } + ] + } + ] + }" + }; + + var request = new RequestMessage(new UrlDetails("http://localhost:1234"), "POST", ClientIp, body); + + var response = Response.Create() + .WithHeader("Content-Type", "application/json") + .WithBody("{{#JsonPath.SelectTokens request.body \"$..Products[?(@.Price >= 50)].Name\"}}{{id}} {{value}},{{/JsonPath.SelectTokens}}") + .WithTransformer(); + + // Act + var responseMessage = await response.ProvideResponseAsync(request); + + // Assert + Check.That(responseMessage.Body).Equals("0 Anvil,1 Elbow Grease,"); + } + + [Fact] + public async Task Response_ProvideResponse_Handlebars_JsonPath_SelectTokens_Request_BodyAsJObject() + { + // Assign + var body = new BodyData + { + BodyAsJson = JObject.Parse(@"{ + 'Stores': [ + 'Lambton Quay', + 'Willis Street' + ], + 'Manufacturers': [ + { + 'Name': 'Acme Co', + 'Products': [ + { + 'Name': 'Anvil', + 'Price': 50 + } + ] + }, + { + 'Name': 'Contoso', + 'Products': [ + { + 'Name': 'Elbow Grease', + 'Price': 99.95 + }, + { + 'Name': 'Headlight Fluid', + 'Price': 4 + } + ] + } + ] + }") + }; + + var request = new RequestMessage(new UrlDetails("http://localhost:1234"), "POST", ClientIp, body); + + var response = Response.Create() + .WithHeader("Content-Type", "application/json") + .WithBody("{{#JsonPath.SelectTokens request.bodyAsJson \"$..Products[?(@.Price >= 50)].Name\"}}{{id}} {{value}},{{/JsonPath.SelectTokens}}") + .WithTransformer(); + + // Act + var responseMessage = await response.ProvideResponseAsync(request); + + // Assert + Check.That(responseMessage.Body).Equals("0 Anvil,1 Elbow Grease,"); + } + + [Fact] + public void Response_ProvideResponse_Handlebars_JsonPath_SelectTokens_Throws() + { + // Assign + var body = new BodyData + { + BodyAsJson = JObject.Parse(@"{ + 'Stores': [ + 'Lambton Quay', + 'Willis Street' + ] + }") + }; + + var request = new RequestMessage(new UrlDetails("http://localhost:1234"), "POST", ClientIp, body); + + var response = Response.Create() + .WithHeader("Content-Type", "application/json") + .WithBody("{{#JsonPath.SelectTokens request.body \"$..Products[?(@.Price >= 50)].Name\"}}{{id}} {{value}},{{/JsonPath.SelectTokens}}") + .WithTransformer(); + + // Act + Check.ThatAsyncCode(() => response.ProvideResponseAsync(request)).Throws(); + } } } \ No newline at end of file diff --git a/test/WireMock.Net.Tests/ResponseTests.cs b/test/WireMock.Net.Tests/ResponseTests.cs index 9b8f2872..a25cb819 100644 --- a/test/WireMock.Net.Tests/ResponseTests.cs +++ b/test/WireMock.Net.Tests/ResponseTests.cs @@ -1,5 +1,4 @@ -using System; -using NFluent; +using NFluent; using WireMock.Models; using WireMock.ResponseBuilders; using Xunit;