diff --git a/Directory.Build.props b/Directory.Build.props
index 0b119cb3..25a3eb8b 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -4,7 +4,7 @@
- 1.0.4.21
+ 1.0.5
diff --git a/GitHubReleaseNotes.txt b/GitHubReleaseNotes.txt
index e9d9bb2f..1f936d0a 100644
--- a/GitHubReleaseNotes.txt
+++ b/GitHubReleaseNotes.txt
@@ -1,3 +1,3 @@
https://github.com/StefH/GitHubReleaseNotes
-GitHubReleaseNotes.exe --output CHANGELOG.md --skip-empty-releases --version 1.0.4.21
\ No newline at end of file
+GitHubReleaseNotes.exe --output CHANGELOG.md --skip-empty-releases --version 1.0.5
\ No newline at end of file
diff --git a/README.md b/README.md
index 80748659..82988722 100644
--- a/README.md
+++ b/README.md
@@ -5,10 +5,10 @@ A C# .NET version based on [mock4net](https://github.com/alexvictoor/mock4net) w
* HTTP response stubbing, matchable on URL/Path, headers, cookies and body content patterns
* Runs in unit tests, as a standalone process, as windows service, as Azure or IIS or as docker
* Configurable via a fluent DotNet API, JSON files and JSON over HTTP
-* Record/playback of stubs
+* Record/playback of stubs (proxying)
* Per-request conditional proxying
* Stateful behaviour simulation
-* Configurable response delays
+* Response transformation
## Info
| | |
diff --git a/examples/WireMock.Net.Console.Net452.Classic/MainApp.cs b/examples/WireMock.Net.Console.Net452.Classic/MainApp.cs
index 4ec24693..49d30dc5 100644
--- a/examples/WireMock.Net.Console.Net452.Classic/MainApp.cs
+++ b/examples/WireMock.Net.Console.Net452.Classic/MainApp.cs
@@ -388,11 +388,10 @@ namespace WireMock.Net.ConsoleApplication
.WithStatusCode(500)
.WithBody(requestMessage =>
{
- string returnStr = JsonConvert.SerializeObject(new
+ return JsonConvert.SerializeObject(new
{
Message = "Test error"
});
- return returnStr;
})
);
@@ -402,22 +401,28 @@ namespace WireMock.Net.ConsoleApplication
.WithHeader("Content-Type", "application/json")
.WithBodyAsJson(new
{
+ Xeger1 = "{{Xeger \"\\w{4}\\d{5}\"}}",
+ Xeger2 = "{{Xeger \"\\d{5}\"}}",
+ TextRegexPostcode = "{{Random Type=\"TextRegex\" Pattern=\"[1-9][0-9]{3}[A-Z]{2}\"}}",
Text = "{{Random Type=\"Text\" Min=8 Max=20}}",
TextLipsum = "{{Random Type=\"TextLipsum\"}}",
+ IBAN = "{{Random Type=\"IBAN\" CountryCode=\"NL\"}}",
TimeSpan1 = "{{Random Type=\"TimeSpan\" Format=\"c\" IncludeMilliseconds=false}}",
TimeSpan2 = "{{Random Type=\"TimeSpan\"}}",
DateTime1 = "{{Random Type=\"DateTime\"}}",
DateTimeNow = DateTime.Now,
- DateTimeToString = DateTime.Now.ToString("s", CultureInfo.InvariantCulture),
+ DateTimeNowToString = DateTime.Now.ToString("s", CultureInfo.InvariantCulture),
Guid1 = "{{Random Type=\"Guid\" Uppercase=false}}",
Guid2 = "{{Random Type=\"Guid\"}}",
- Integer1 = "{{Random Type=\"Integer\" Min=1000 Max=9999}}",
- Integer2 = "{{#Random Type=\"Integer\" Min=10000000 Max=99999999}}{{this}}{{/Random}}",
- Double1 = "{{Random Type=\"Double\" Min=10 Max=99}}",
- Double2 = "{{Random Type=\"Double\" Min=100 Max=999}}",
+ Boolean = "{{Random Type=\"Boolean\"}}",
+ Integer = "{{Random Type=\"Integer\" Min=1000 Max=9999}}",
+ Long = "{{#Random Type=\"Long\" Min=10000000 Max=99999999}}{{this}}{{/Random}}",
+ Double = "{{Random Type=\"Double\" Min=10 Max=99}}",
+ Float = "{{Random Type=\"Float\" Min=100 Max=999}}",
IP4Address = "{{Random Type=\"IPv4Address\" Min=\"10.2.3.4\"}}",
IP6Address = "{{Random Type=\"IPv6Address\"}}",
- MACAddress = "{{Random Type=\"MACAddress\" Separator=\"-\"}}"
+ MACAddress = "{{Random Type=\"MACAddress\" Separator=\"-\"}}",
+ StringListValue = "{{Random Type=\"StringList\" Values=[\"a\", \"b\", \"c\"]}}"
})
.WithTransformer()
);
diff --git a/src/WireMock.Net/Transformers/HandleBarsHelpers.cs b/src/WireMock.Net/Transformers/HandleBarsHelpers.cs
index 3726d5f9..b40c5bae 100644
--- a/src/WireMock.Net/Transformers/HandleBarsHelpers.cs
+++ b/src/WireMock.Net/Transformers/HandleBarsHelpers.cs
@@ -13,6 +13,8 @@ namespace WireMock.Transformers
HandleBarsLinq.Register(handlebarsContext);
HandleBarsRandom.Register(handlebarsContext);
+
+ HandleBarsXeger.Register(handlebarsContext);
}
}
}
\ No newline at end of file
diff --git a/src/WireMock.Net/Transformers/HandleBarsXeger.cs b/src/WireMock.Net/Transformers/HandleBarsXeger.cs
new file mode 100644
index 00000000..1928c6ca
--- /dev/null
+++ b/src/WireMock.Net/Transformers/HandleBarsXeger.cs
@@ -0,0 +1,39 @@
+using System;
+using Fare;
+using HandlebarsDotNet;
+using WireMock.Validation;
+
+namespace WireMock.Transformers
+{
+ internal static class HandleBarsXeger
+ {
+ public static void Register(IHandlebars handlebarsContext)
+ {
+ handlebarsContext.RegisterHelper("Xeger", (writer, context, arguments) =>
+ {
+ string value = ParseArgumentAndGenerate(arguments);
+ writer.Write(value);
+ });
+
+ handlebarsContext.RegisterHelper("Xeger", (writer, options, context, arguments) =>
+ {
+ string value = ParseArgumentAndGenerate(arguments);
+ options.Template(writer, value);
+ });
+ }
+
+ private static string ParseArgumentAndGenerate(object[] arguments)
+ {
+ Check.Condition(arguments, args => args.Length == 1, nameof(arguments));
+ Check.NotNull(arguments[0], "arguments[0]");
+
+ switch (arguments[0])
+ {
+ case string pattern:
+ return new Xeger(pattern).Generate();
+ }
+
+ throw new NotSupportedException($"The value '{arguments[0]}' with type '{arguments[0]?.GetType()}' cannot be used in Handlebars Xeger.");
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/WireMock.Net/Transformers/ResponseMessageTransformer.cs b/src/WireMock.Net/Transformers/ResponseMessageTransformer.cs
index 00cd809d..62d012d7 100644
--- a/src/WireMock.Net/Transformers/ResponseMessageTransformer.cs
+++ b/src/WireMock.Net/Transformers/ResponseMessageTransformer.cs
@@ -120,23 +120,34 @@ namespace WireMock.Transformers
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);
+ ReplaceNodeValue(node, transformedString);
}
}
}
+ private static void ReplaceNodeValue(JToken node, string stringValue)
+ {
+ if (bool.TryParse(stringValue, out bool valueAsBoolean))
+ {
+ node.Replace(valueAsBoolean);
+ return;
+ }
+
+ JToken value;
+ try
+ {
+ // Try to convert this string into a JsonObject
+ value = JToken.Parse(stringValue);
+ }
+ catch (JsonException)
+ {
+ // Ignore JsonException and just keep string value and convert to JToken
+ value = stringValue;
+ }
+
+ node.Replace(value);
+ }
+
private static void TransformBodyAsString(object template, ResponseMessage original, ResponseMessage responseMessage)
{
var templateBody = HandlebarsContext.Compile(original.BodyData.BodyAsString);
diff --git a/src/WireMock.Net/WireMock.Net.csproj b/src/WireMock.Net/WireMock.Net.csproj
index c8b1cd72..e5751469 100644
--- a/src/WireMock.Net/WireMock.Net.csproj
+++ b/src/WireMock.Net/WireMock.Net.csproj
@@ -58,7 +58,7 @@
runtime; build; native; contentfiles; analyzers
-
+
diff --git a/test/WireMock.Net.Tests/ResponseBuilders/ResponseWithHandlebarsRandomTests.cs b/test/WireMock.Net.Tests/ResponseBuilders/ResponseWithHandlebarsRandomTests.cs
index 3aca51fc..4d119153 100644
--- a/test/WireMock.Net.Tests/ResponseBuilders/ResponseWithHandlebarsRandomTests.cs
+++ b/test/WireMock.Net.Tests/ResponseBuilders/ResponseWithHandlebarsRandomTests.cs
@@ -34,9 +34,30 @@ namespace WireMock.Net.Tests.ResponseBuilders
// Assert
JObject j = JObject.FromObject(responseMessage.BodyData.BodyAsJson);
- Check.That(j["Text"]).IsNotNull();
+ Check.That(j["Text"].Value()).IsNotEmpty();
Check.That(j["Integer"].Value()).IsEqualTo(1000);
- Check.That(j["Long"].Value()).IsStrictlyGreaterThan(77777777).And.IsStrictlyLessThan(99999999);
+ Check.That(j["Long"].Value()).IsStrictlyGreaterThan(77777777).And.IsStrictlyLessThan(99999999);
+ }
+
+ [Fact]
+ public async Task Response_ProvideResponseAsync_Handlebars_Random1_Boolean()
+ {
+ // Assign
+ var request = new RequestMessage(new UrlDetails("http://localhost:1234"), "GET", ClientIp);
+
+ var response = Response.Create()
+ .WithBodyAsJson(new
+ {
+ Value = "{{Random Type=\"Boolean\"}}"
+ })
+ .WithTransformer();
+
+ // Act
+ var responseMessage = await response.ProvideResponseAsync(request);
+
+ // Assert
+ JObject j = JObject.FromObject(responseMessage.BodyData.BodyAsJson);
+ Check.That(j["Value"].Type).IsEqualTo(JTokenType.Boolean);
}
[Fact]
diff --git a/test/WireMock.Net.Tests/ResponseBuilders/ResponseWithHandlebarsXegerTests.cs b/test/WireMock.Net.Tests/ResponseBuilders/ResponseWithHandlebarsXegerTests.cs
new file mode 100644
index 00000000..abca4648
--- /dev/null
+++ b/test/WireMock.Net.Tests/ResponseBuilders/ResponseWithHandlebarsXegerTests.cs
@@ -0,0 +1,60 @@
+using System.Threading.Tasks;
+using Newtonsoft.Json.Linq;
+using NFluent;
+using WireMock.Models;
+using WireMock.ResponseBuilders;
+using Xunit;
+
+namespace WireMock.Net.Tests.ResponseBuilders
+{
+ public class ResponseWithHandlebarsXegerTests
+ {
+ private const string ClientIp = "::1";
+
+ [Fact]
+ public async Task Response_ProvideResponseAsync_Handlebars_Xeger1()
+ {
+ // Assign
+ var request = new RequestMessage(new UrlDetails("http://localhost:1234"), "GET", ClientIp);
+
+ var response = Response.Create()
+ .WithBodyAsJson(new
+ {
+ Number = "{{Xeger \"[1-9]{1}\\d{3}\"}}",
+ Postcode = "{{Xeger \"[1-9][0-9]{3}[A-Z]{2}\"}}"
+ })
+ .WithTransformer();
+
+ // Act
+ var responseMessage = await response.ProvideResponseAsync(request);
+
+ // Assert
+ JObject j = JObject.FromObject(responseMessage.BodyData.BodyAsJson);
+ Check.That(j["Number"].Value()).IsStrictlyGreaterThan(1000).And.IsStrictlyLessThan(9999);
+ Check.That(j["Postcode"].Value()).IsNotEmpty();
+ }
+
+ [Fact]
+ public async Task Response_ProvideResponseAsync_Handlebars_Xeger2()
+ {
+ // Assign
+ var request = new RequestMessage(new UrlDetails("http://localhost:1234"), "GET", ClientIp);
+
+ var response = Response.Create()
+ .WithBodyAsJson(new
+ {
+ Number = "{{#Xeger \"[1-9]{1}\\d{3}\"}}{{this}}{{/Xeger}}",
+ Postcode = "{{#Xeger \"[1-9][0-9]{3}[A-Z]{2}\"}}{{this}}{{/Xeger}}"
+ })
+ .WithTransformer();
+
+ // Act
+ var responseMessage = await response.ProvideResponseAsync(request);
+
+ // Assert
+ JObject j = JObject.FromObject(responseMessage.BodyData.BodyAsJson);
+ Check.That(j["Number"].Value()).IsStrictlyGreaterThan(1000).And.IsStrictlyLessThan(9999);
+ Check.That(j["Postcode"].Value()).IsNotEmpty();
+ }
+ }
+}
\ No newline at end of file