From 17a669cb5389c4f877095e88e6c2c082ac14cc9c Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Tue, 29 Aug 2023 20:58:04 +0200 Subject: [PATCH 1/9] Random.GenerateAsOutputWithType --- .../Handlebars.Net.Helpers.Core.csproj | 1 - .../Utils/SimpleJsonUtils.cs | 16 +++++ .../Helpers/RandomHelpers.cs | 60 +++++++++++++++---- .../Models/OutputWithType.cs | 27 +++++++++ .../Helpers/RandomHelpersTests.cs | 26 +++++++- .../Templates/RandomHelpersTemplateTests.cs | 17 ++++++ 6 files changed, 131 insertions(+), 16 deletions(-) create mode 100644 src/Handlebars.Net.Helpers.Core/Utils/SimpleJsonUtils.cs create mode 100644 src/Handlebars.Net.Helpers.Random/Models/OutputWithType.cs diff --git a/src/Handlebars.Net.Helpers.Core/Handlebars.Net.Helpers.Core.csproj b/src/Handlebars.Net.Helpers.Core/Handlebars.Net.Helpers.Core.csproj index f5e8612..b3c725b 100644 --- a/src/Handlebars.Net.Helpers.Core/Handlebars.Net.Helpers.Core.csproj +++ b/src/Handlebars.Net.Helpers.Core/Handlebars.Net.Helpers.Core.csproj @@ -34,5 +34,4 @@ - \ No newline at end of file diff --git a/src/Handlebars.Net.Helpers.Core/Utils/SimpleJsonUtils.cs b/src/Handlebars.Net.Helpers.Core/Utils/SimpleJsonUtils.cs new file mode 100644 index 0000000..4a001fd --- /dev/null +++ b/src/Handlebars.Net.Helpers.Core/Utils/SimpleJsonUtils.cs @@ -0,0 +1,16 @@ +using HandlebarsDotNet.Helpers.Json; + +namespace HandlebarsDotNet.Helpers.Utils; + +public static class SimpleJsonUtils +{ + public static string? SerializeObject(object? value) + { + return SimpleJson.SerializeObject(value); + } + + public static T? DeserializeObject(string? json) + { + return SimpleJson.DeserializeObject(json); + } +} \ No newline at end of file diff --git a/src/Handlebars.Net.Helpers.Random/Helpers/RandomHelpers.cs b/src/Handlebars.Net.Helpers.Random/Helpers/RandomHelpers.cs index 3aa4030..c51d0c8 100644 --- a/src/Handlebars.Net.Helpers.Random/Helpers/RandomHelpers.cs +++ b/src/Handlebars.Net.Helpers.Random/Helpers/RandomHelpers.cs @@ -4,10 +4,12 @@ using HandlebarsDotNet.Helpers.Attributes; using HandlebarsDotNet.Helpers.Enums; using HandlebarsDotNet.Helpers.Helpers; +using HandlebarsDotNet.Helpers.Models; using HandlebarsDotNet.Helpers.Parsers; using RandomDataGenerator.FieldOptions; using RandomDataGenerator.Randomizers; +// ReSharper disable once CheckNamespace namespace HandlebarsDotNet.Helpers; internal class RandomHelpers : BaseHelpers, IHelpers @@ -25,8 +27,28 @@ public RandomHelpers(IHandlebars context) : base(context) return Generate(hash); } + /// + /// For backwards compatibility with WireMock.Net + /// + [HandlebarsWriter(WriterType.Value, "RandomAsOutputWithType")] + public string? RandomAsOutputWithType(IDictionary hash) + { + return GenerateAsOutputWithType(hash); + } + [HandlebarsWriter(WriterType.Value)] public object? Generate(IDictionary hash) + { + return GenerateInternal(hash, false); + } + + [HandlebarsWriter(WriterType.String)] + public string? GenerateAsOutputWithType(IDictionary hash) + { + return GenerateInternal(hash, true) is not OutputWithType outputWithType ? null : outputWithType.Serialize(); + } + + private object? GenerateInternal(IDictionary hash, bool outputWithType) { var fieldOptions = GetFieldOptionsFromHash(hash); dynamic randomizer = RandomizerFactory.GetRandomizerAsDynamic(fieldOptions); @@ -35,34 +57,46 @@ public RandomHelpers(IHandlebars context) : base(context) if (fieldOptions is IFieldOptionsDateTime) { DateTime? date = randomizer.Generate(); - return date?.ToString("s", Context.Configuration.FormatProvider); + return GetRandomValue(outputWithType, () => date?.ToString("s", Context.Configuration.FormatProvider)); } // If the IFieldOptionsGuid defines Uppercase, use the 'GenerateAsString' method. if (fieldOptions is IFieldOptionsGuid fieldOptionsGuid) { - return fieldOptionsGuid.Uppercase ? randomizer.GenerateAsString() : randomizer.Generate(); + return GetRandomValue(outputWithType, () => fieldOptionsGuid.Uppercase ? randomizer.GenerateAsString() : randomizer.Generate()); } - return randomizer.Generate(); + return GetRandomValue(outputWithType, () => randomizer.Generate()); } private FieldOptionsAbstract GetFieldOptionsFromHash(IDictionary hash) { - if (hash.TryGetValue("Type", out var value) && value is string randomType) + if (!hash.TryGetValue("Type", out var value) || value is not string randomType) { - var newProperties = new Dictionary(); - foreach (var item in hash.Where(p => p.Key != "Type")) - { - bool convertObjectArrayToStringList = randomType == "StringList"; - var parsedArgumentValue = ArgumentsParser.Parse(Context, item.Value, convertObjectArrayToStringList); + throw new HandlebarsException("The Type argument is missing."); + } - newProperties.Add(item.Key, parsedArgumentValue); - } + var newProperties = new Dictionary(); + foreach (var item in hash.Where(p => p.Key != "Type")) + { + var convertObjectArrayToStringList = randomType == "StringList"; + var parsedArgumentValue = ArgumentsParser.Parse(Context, item.Value, convertObjectArrayToStringList); - return FieldOptionsFactory.GetFieldOptions(randomType, newProperties!); + newProperties.Add(item.Key, parsedArgumentValue); } - throw new HandlebarsException("The Type argument is missing."); + return FieldOptionsFactory.GetFieldOptions(randomType, newProperties!); + } + + private static object? GetRandomValue(bool outputWithType, Func func) + { + var value = func(); + + return outputWithType ? new OutputWithType + { + Value = value, + FullType = value?.GetType().FullName, + Type = value?.GetType().Name + } : value; } } \ No newline at end of file diff --git a/src/Handlebars.Net.Helpers.Random/Models/OutputWithType.cs b/src/Handlebars.Net.Helpers.Random/Models/OutputWithType.cs new file mode 100644 index 0000000..0af7667 --- /dev/null +++ b/src/Handlebars.Net.Helpers.Random/Models/OutputWithType.cs @@ -0,0 +1,27 @@ +using HandlebarsDotNet.Helpers.Utils; + +namespace HandlebarsDotNet.Helpers.Models; + +public class OutputWithType +{ + public object? Value { get; set; } + + public string? Type { get; set; } + + public string? FullType { get; set; } + + public override string ToString() + { + return SimpleJsonUtils.SerializeObject(this) ?? string.Empty; + } + + public string? Serialize() + { + return SimpleJsonUtils.SerializeObject(this); + } + + public static OutputWithType? Deserialize(string? json) + { + return SimpleJsonUtils.DeserializeObject(json); + } +} \ No newline at end of file diff --git a/test/Handlebars.Net.Helpers.Tests/Helpers/RandomHelpersTests.cs b/test/Handlebars.Net.Helpers.Tests/Helpers/RandomHelpersTests.cs index 1d9cff5..1b0c375 100644 --- a/test/Handlebars.Net.Helpers.Tests/Helpers/RandomHelpersTests.cs +++ b/test/Handlebars.Net.Helpers.Tests/Helpers/RandomHelpersTests.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using FluentAssertions; +using HandlebarsDotNet.Helpers.Models; using Moq; using Xunit; @@ -18,7 +19,7 @@ public RandomHelpersTests() } [Fact] - public void Random() + public void Random_Integer() { // Arrange var hash = new Dictionary @@ -32,6 +33,27 @@ public void Random() var result = _sut.Random(hash); // Assert - (result as int?).Should().BeInRange(1000, 9999); + result.Should().BeOfType().Which.Should().BeInRange(1000, 9999); + } + + [Fact] + public void RandomAsOutputWithType_Integer() + { + // Arrange + var hash = new Dictionary + { + { "Type", "Integer" }, + { "Min", 1000 }, + { "Max", 9999 } + }; + + // Act + var result = _sut.RandomAsOutputWithType(hash); + + // Assert + var outputWithType = OutputWithType.Deserialize(result)!; + outputWithType.Value.Should().BeOfType().Which.Should().BeInRange(1000, 9999); + outputWithType.Type.Should().Be("Int32"); + outputWithType.FullType.Should().Be("System.Int32"); } } \ No newline at end of file diff --git a/test/Handlebars.Net.Helpers.Tests/Templates/RandomHelpersTemplateTests.cs b/test/Handlebars.Net.Helpers.Tests/Templates/RandomHelpersTemplateTests.cs index 3a1622c..647b219 100644 --- a/test/Handlebars.Net.Helpers.Tests/Templates/RandomHelpersTemplateTests.cs +++ b/test/Handlebars.Net.Helpers.Tests/Templates/RandomHelpersTemplateTests.cs @@ -1,5 +1,6 @@ using FluentAssertions; using HandlebarsDotNet.Helpers.Enums; +using HandlebarsDotNet.Helpers.Models; using Xunit; namespace HandlebarsDotNet.Helpers.Tests.Templates; @@ -28,6 +29,22 @@ public void Random_Integer() int.Parse(result).Should().BeInRange(1000, 9999); } + [Fact] + public void Random_Integer_OutputWithType() + { + // Arrange + var action = _handlebarsContext.Compile("{{Random.GenerateAsOutputWithType Type=\"Integer\" Min=1000 Max=9999}}"); + + // Act + var result = action(""); + + // Assert + var outputWithType = OutputWithType.Deserialize(result)!; + outputWithType.Value.Should().BeOfType().Which.Should().BeInRange(1000, 9999); + outputWithType.Type.Should().Be("Int32"); + outputWithType.FullType.Should().Be("System.Int32"); + } + [Fact] public void Random_StringList() { From 0e849ad44f80975312ea804f326be71ec4d87269 Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Tue, 29 Aug 2023 21:00:21 +0200 Subject: [PATCH 2/9] . --- .../Helpers/RandomHelpersTests.cs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/test/Handlebars.Net.Helpers.Tests/Helpers/RandomHelpersTests.cs b/test/Handlebars.Net.Helpers.Tests/Helpers/RandomHelpersTests.cs index 1b0c375..3f31656 100644 --- a/test/Handlebars.Net.Helpers.Tests/Helpers/RandomHelpersTests.cs +++ b/test/Handlebars.Net.Helpers.Tests/Helpers/RandomHelpersTests.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using FluentAssertions; using HandlebarsDotNet.Helpers.Models; using Moq; @@ -18,6 +19,19 @@ public RandomHelpersTests() _sut = new RandomHelpers(contextMock.Object); } + [Fact] + public void Random_TypeIsMissing_Should_Throw() + { + // Arrange + var hash = new Dictionary(); + + // Act + Action action = () => _sut.Random(hash); + + // Assert + action.Should().Throw().Which.Message.Should().Be("The Type argument is missing."); + } + [Fact] public void Random_Integer() { From 41c5a2875709d2c067f07cb6d37c725901af5836 Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Wed, 1 Nov 2023 22:44:13 +0100 Subject: [PATCH 3/9] ................... --- .../Handlebars.Net.Helpers.Random.csproj | 4 + .../Helpers/RandomHelpers.cs | 37 +++- .../Models/OutputWithType.cs | 90 ++++++++- .../Helpers/RandomHelpersTests.cs | 6 +- .../Templates/RandomHelpersTemplateTests.cs | 177 +++++++++++++++++- 5 files changed, 291 insertions(+), 23 deletions(-) diff --git a/src/Handlebars.Net.Helpers.Random/Handlebars.Net.Helpers.Random.csproj b/src/Handlebars.Net.Helpers.Random/Handlebars.Net.Helpers.Random.csproj index 0dead86..7acb2eb 100644 --- a/src/Handlebars.Net.Helpers.Random/Handlebars.Net.Helpers.Random.csproj +++ b/src/Handlebars.Net.Helpers.Random/Handlebars.Net.Helpers.Random.csproj @@ -12,6 +12,10 @@ + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/src/Handlebars.Net.Helpers.Random/Helpers/RandomHelpers.cs b/src/Handlebars.Net.Helpers.Random/Helpers/RandomHelpers.cs index c51d0c8..563c073 100644 --- a/src/Handlebars.Net.Helpers.Random/Helpers/RandomHelpers.cs +++ b/src/Handlebars.Net.Helpers.Random/Helpers/RandomHelpers.cs @@ -6,6 +6,7 @@ using HandlebarsDotNet.Helpers.Helpers; using HandlebarsDotNet.Helpers.Models; using HandlebarsDotNet.Helpers.Parsers; +using HandlebarsDotNet.Helpers.Utils; using RandomDataGenerator.FieldOptions; using RandomDataGenerator.Randomizers; @@ -42,6 +43,9 @@ public RandomHelpers(IHandlebars context) : base(context) return GenerateInternal(hash, false); } + /// + /// It returns a JSON string. + /// [HandlebarsWriter(WriterType.String)] public string? GenerateAsOutputWithType(IDictionary hash) { @@ -57,16 +61,19 @@ public RandomHelpers(IHandlebars context) : base(context) if (fieldOptions is IFieldOptionsDateTime) { DateTime? date = randomizer.Generate(); - return GetRandomValue(outputWithType, () => date?.ToString("s", Context.Configuration.FormatProvider)); + return GetRandomValue(outputWithType, () => date?.ToString("s", Context.Configuration.FormatProvider), () => date); } // If the IFieldOptionsGuid defines Uppercase, use the 'GenerateAsString' method. if (fieldOptions is IFieldOptionsGuid fieldOptionsGuid) { - return GetRandomValue(outputWithType, () => fieldOptionsGuid.Uppercase ? randomizer.GenerateAsString() : randomizer.Generate()); + return GetRandomValue(outputWithType, + () => fieldOptionsGuid.Uppercase ? randomizer.GenerateAsString() : randomizer.Generate(), + () => randomizer.Generate() + ); } - return GetRandomValue(outputWithType, () => randomizer.Generate()); + return GetRandomValue(outputWithType, () => randomizer.Generate(), null); } private FieldOptionsAbstract GetFieldOptionsFromHash(IDictionary hash) @@ -88,15 +95,31 @@ private FieldOptionsAbstract GetFieldOptionsFromHash(IDictionary func) + private static object? GetRandomValue(bool outputWithType, Func funcNormal, Func? funcWithType) { - var value = func(); + object? value; + if (outputWithType && funcWithType != null) + { + value = funcWithType(); + } + else + { + value = funcNormal(); + } + + var name = value?.GetType().Name; + var fullName = value?.GetType().FullName; + + if (value is IEnumerable array) + { + value = ArrayUtils.ToArray(array); + } return outputWithType ? new OutputWithType { Value = value, - FullType = value?.GetType().FullName, - Type = value?.GetType().Name + FullTypeName = fullName, + TypeName = name } : value; } } \ No newline at end of file diff --git a/src/Handlebars.Net.Helpers.Random/Models/OutputWithType.cs b/src/Handlebars.Net.Helpers.Random/Models/OutputWithType.cs index 0af7667..c3bc507 100644 --- a/src/Handlebars.Net.Helpers.Random/Models/OutputWithType.cs +++ b/src/Handlebars.Net.Helpers.Random/Models/OutputWithType.cs @@ -1,4 +1,7 @@ -using HandlebarsDotNet.Helpers.Utils; +using System; +using System.Diagnostics.CodeAnalysis; +using HandlebarsDotNet.Helpers.Json; +using HandlebarsDotNet.Helpers.Utils; namespace HandlebarsDotNet.Helpers.Models; @@ -6,9 +9,9 @@ public class OutputWithType { public object? Value { get; set; } - public string? Type { get; set; } + public string? TypeName { get; set; } - public string? FullType { get; set; } + public string? FullTypeName { get; set; } public override string ToString() { @@ -20,8 +23,85 @@ public override string ToString() return SimpleJsonUtils.SerializeObject(this); } - public static OutputWithType? Deserialize(string? json) + public static OutputWithType Deserialize(string? json) { - return SimpleJsonUtils.DeserializeObject(json); + var jsonObject = SimpleJsonUtils.DeserializeObject(json); + + if (jsonObject == null) + { + throw new InvalidOperationException(); + } + + if (!jsonObject.TryGetValue(nameof(Value), out var value)) + { + throw new MissingMemberException($"Property '{nameof(Value)}' is not found on type '{nameof(OutputWithType)}'."); + } + + if (!jsonObject.TryGetValue(nameof(TypeName), out var typeName) || typeName is not string typeNameAsString) + { + throw new MissingMemberException($"Property '{nameof(TypeName)}' is not found on type '{nameof(OutputWithType)}'."); + } + + if (!jsonObject.TryGetValue(nameof(FullTypeName), out var fullTypeName) || fullTypeName is not string fullTypeNameAsString) + { + throw new MissingMemberException($"Property '{nameof(FullTypeName)}' is not found on type '{nameof(OutputWithType)}'."); + } + + var fullType = Type.GetType(fullTypeNameAsString); + if (fullType == null) + { + throw new TypeLoadException($"Type '{fullTypeNameAsString}' is not found."); + } + + if (TryConvert(value, fullType, out var convertedValue)) + { + value = convertedValue; + } + + return new OutputWithType + { + Value = value, + TypeName = typeNameAsString, + FullTypeName = fullTypeNameAsString + }; + } + + private static bool TryConvert(object? value, Type fullType, [NotNullWhen(true)] out object? result) + { + try + { + if (fullType == typeof(Guid) && value is string guidAsString) + { + result = new Guid(guidAsString); + return true; + } + + if (fullType.IsArray && value is JsonArray jsonArray) + { + var elementType = fullType.GetElementType()!; + var newArray = Array.CreateInstance(elementType, jsonArray.Count); + for (var i = 0; i < jsonArray.Count; i++) + { + newArray.SetValue(Convert.ChangeType(jsonArray[i], elementType), i); + } + + result = newArray; + } + else if (fullType == typeof(TimeSpan) && value is JsonObject timeSpanAsJsonObject) + { + result = TimeSpan.FromTicks((long)timeSpanAsJsonObject["Ticks"]); + } + else + { + result = Convert.ChangeType(value, fullType); + } + + return true; + } + catch + { + result = default; + return false; + } } } \ No newline at end of file diff --git a/test/Handlebars.Net.Helpers.Tests/Helpers/RandomHelpersTests.cs b/test/Handlebars.Net.Helpers.Tests/Helpers/RandomHelpersTests.cs index 3f31656..512623f 100644 --- a/test/Handlebars.Net.Helpers.Tests/Helpers/RandomHelpersTests.cs +++ b/test/Handlebars.Net.Helpers.Tests/Helpers/RandomHelpersTests.cs @@ -66,8 +66,8 @@ public void RandomAsOutputWithType_Integer() // Assert var outputWithType = OutputWithType.Deserialize(result)!; - outputWithType.Value.Should().BeOfType().Which.Should().BeInRange(1000, 9999); - outputWithType.Type.Should().Be("Int32"); - outputWithType.FullType.Should().Be("System.Int32"); + outputWithType.Value.Should().BeOfType().Which.Should().BeInRange(1000, 9999); + outputWithType.TypeName.Should().Be("Int32"); + outputWithType.FullTypeName.Should().Be("System.Int32"); } } \ No newline at end of file diff --git a/test/Handlebars.Net.Helpers.Tests/Templates/RandomHelpersTemplateTests.cs b/test/Handlebars.Net.Helpers.Tests/Templates/RandomHelpersTemplateTests.cs index 647b219..3880133 100644 --- a/test/Handlebars.Net.Helpers.Tests/Templates/RandomHelpersTemplateTests.cs +++ b/test/Handlebars.Net.Helpers.Tests/Templates/RandomHelpersTemplateTests.cs @@ -1,4 +1,5 @@ -using FluentAssertions; +using System; +using FluentAssertions; using HandlebarsDotNet.Helpers.Enums; using HandlebarsDotNet.Helpers.Models; using Xunit; @@ -29,32 +30,192 @@ public void Random_Integer() int.Parse(result).Should().BeInRange(1000, 9999); } + [Fact] + public void Random_StringList() + { + // Arrange + var action = _handlebarsContext.Compile("{{Random.Generate Type=\"StringList\" Values=[\"a\", \"b\", \"c\"]}}"); + + // Act + var result = action(""); + + // Assert + result.Should().NotBeNullOrEmpty(); + } + + [Fact] + public void Random_StringList_OutputWithType() + { + // Arrange + var action = _handlebarsContext.Compile("{{Random.GenerateAsOutputWithType Type=\"StringList\" Values=[\"a\", \"b\", \"c\"]}}"); + + // Act + var result = action(""); + + // Assert + var outputWithType = OutputWithType.Deserialize(result)!; + outputWithType.Value.Should().BeOfType().Which.Should().NotBeNullOrEmpty(); + outputWithType.TypeName.Should().Be("String"); + outputWithType.FullTypeName.Should().Be("System.String"); + } + [Fact] public void Random_Integer_OutputWithType() { // Arrange - var action = _handlebarsContext.Compile("{{Random.GenerateAsOutputWithType Type=\"Integer\" Min=1000 Max=9999}}"); + var action = _handlebarsContext.Compile("{{Random.GenerateAsOutputWithType Type=\"Integer\" Min=1000 Max=9999 }}"); // Act var result = action(""); // Assert var outputWithType = OutputWithType.Deserialize(result)!; - outputWithType.Value.Should().BeOfType().Which.Should().BeInRange(1000, 9999); - outputWithType.Type.Should().Be("Int32"); - outputWithType.FullType.Should().Be("System.Int32"); + outputWithType.Value.Should().BeOfType().Which.Should().BeInRange(1000, 9999); + outputWithType.TypeName.Should().Be("Int32"); + outputWithType.FullTypeName.Should().Be("System.Int32"); } [Fact] - public void Random_StringList() + public void Random_Guid_OutputWithType() { // Arrange - var action = _handlebarsContext.Compile("{{Random.Generate Type=\"StringList\" Values=[\"a\", \"b\", \"c\"]}}"); + var action = _handlebarsContext.Compile("{{Random.GenerateAsOutputWithType Type=\"Guid\" }}"); + + // Act + var result = action(""); + + // Assert + var outputWithType = OutputWithType.Deserialize(result)!; + outputWithType.Value.Should().BeOfType(); + outputWithType.TypeName.Should().Be("Guid"); + outputWithType.FullTypeName.Should().Be("System.Guid"); + } + + [Fact] + public void Random_City_OutputWithType() + { + // Arrange + var action = _handlebarsContext.Compile("{{Random.GenerateAsOutputWithType Type=\"City\"}}"); + + // Act + var result = action(""); + + // Assert + var outputWithType = OutputWithType.Deserialize(result)!; + outputWithType.Value.Should().BeOfType().Which.Should().NotBeNullOrEmpty(); + outputWithType.TypeName.Should().Be("String"); + outputWithType.FullTypeName.Should().Be("System.String"); + } + + [Fact] + public void Random_Boolean_OutputWithType() + { + // Arrange + var action = _handlebarsContext.Compile("{{Random.GenerateAsOutputWithType Type=\"Boolean\"}}"); + + // Act + var result = action(""); + + // Assert + var outputWithType = OutputWithType.Deserialize(result)!; + outputWithType.Value.Should().BeOfType(); + outputWithType.TypeName.Should().Be("Boolean"); + outputWithType.FullTypeName.Should().Be("System.Boolean"); + } + + [Fact] + public void Random_Byte_OutputWithType() + { + // Arrange + var action = _handlebarsContext.Compile("{{Random.GenerateAsOutputWithType Type=\"Byte\"}}"); // Act var result = action(""); // Assert - result.Should().NotBeEmpty("a"); + var outputWithType = OutputWithType.Deserialize(result)!; + outputWithType.Value.Should().BeOfType().Which.Should().BeInRange(byte.MinValue, byte.MaxValue); + outputWithType.TypeName.Should().Be("Byte"); + outputWithType.FullTypeName.Should().Be("System.Byte"); + } + + [Fact] + public void Random_Bytes_OutputWithType() + { + // Arrange + var action = _handlebarsContext.Compile("{{Random.GenerateAsOutputWithType Type=\"Bytes\" Min=1 Max=9 }}"); + + // Act + var result = action(""); + + // Assert + var outputWithType = OutputWithType.Deserialize(result)!; + outputWithType.Value.Should().BeOfType().Which.Should().NotBeNullOrEmpty(); + outputWithType.TypeName.Should().Be("Byte[]"); + outputWithType.FullTypeName.Should().Be("System.Byte[]"); + } + + [Fact] + public void Random_Double_OutputWithType() + { + // Arrange + var action = _handlebarsContext.Compile("{{Random.GenerateAsOutputWithType Type=\"Double\"}}"); + + // Act + var result = action(""); + + // Assert + var outputWithType = OutputWithType.Deserialize(result)!; + outputWithType.Value.Should().BeOfType(); + outputWithType.TypeName.Should().Be("Double"); + outputWithType.FullTypeName.Should().Be("System.Double"); + } + + [Fact] + public void Random_Float_OutputWithType() + { + // Arrange + var action = _handlebarsContext.Compile("{{Random.GenerateAsOutputWithType Type=\"Float\"}}"); + + // Act + var result = action(""); + + // Assert + var outputWithType = OutputWithType.Deserialize(result)!; + outputWithType.Value.Should().BeOfType(); + outputWithType.TypeName.Should().Be("Single"); + outputWithType.FullTypeName.Should().Be("System.Single"); + } + + [Fact] + public void Random_DateTime_OutputWithType() + { + // Arrange + var action = _handlebarsContext.Compile("{{Random.GenerateAsOutputWithType Type=\"DateTime\"}}"); + + // Act + var result = action(""); + + // Assert + var outputWithType = OutputWithType.Deserialize(result)!; + outputWithType.Value.Should().BeOfType(); + outputWithType.TypeName.Should().Be("DateTime"); + outputWithType.FullTypeName.Should().Be("System.DateTime"); + } + + [Fact] + public void Random_TimeSpan_OutputWithType() + { + // Arrange + var action = _handlebarsContext.Compile("{{Random.GenerateAsOutputWithType Type=\"TimeSpan\"}}"); + + // Act + var result = action(""); + + // Assert + var outputWithType = OutputWithType.Deserialize(result)!; + outputWithType.Value.Should().BeOfType(); + outputWithType.TypeName.Should().Be("TimeSpan"); + outputWithType.FullTypeName.Should().Be("System.TimeSpan"); } } \ No newline at end of file From c5d154d979d03981105227686895778cbdfc8c87 Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Sat, 4 Nov 2023 13:38:51 +0100 Subject: [PATCH 4/9] . --- .../Templates/RandomHelpersTemplateTests.cs | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/test/Handlebars.Net.Helpers.Tests/Templates/RandomHelpersTemplateTests.cs b/test/Handlebars.Net.Helpers.Tests/Templates/RandomHelpersTemplateTests.cs index 3880133..fc66f0b 100644 --- a/test/Handlebars.Net.Helpers.Tests/Templates/RandomHelpersTemplateTests.cs +++ b/test/Handlebars.Net.Helpers.Tests/Templates/RandomHelpersTemplateTests.cs @@ -53,7 +53,7 @@ public void Random_StringList_OutputWithType() var result = action(""); // Assert - var outputWithType = OutputWithType.Deserialize(result)!; + var outputWithType = OutputWithType.Deserialize(result); outputWithType.Value.Should().BeOfType().Which.Should().NotBeNullOrEmpty(); outputWithType.TypeName.Should().Be("String"); outputWithType.FullTypeName.Should().Be("System.String"); @@ -69,7 +69,7 @@ public void Random_Integer_OutputWithType() var result = action(""); // Assert - var outputWithType = OutputWithType.Deserialize(result)!; + var outputWithType = OutputWithType.Deserialize(result); outputWithType.Value.Should().BeOfType().Which.Should().BeInRange(1000, 9999); outputWithType.TypeName.Should().Be("Int32"); outputWithType.FullTypeName.Should().Be("System.Int32"); @@ -85,7 +85,7 @@ public void Random_Guid_OutputWithType() var result = action(""); // Assert - var outputWithType = OutputWithType.Deserialize(result)!; + var outputWithType = OutputWithType.Deserialize(result); outputWithType.Value.Should().BeOfType(); outputWithType.TypeName.Should().Be("Guid"); outputWithType.FullTypeName.Should().Be("System.Guid"); @@ -101,7 +101,7 @@ public void Random_City_OutputWithType() var result = action(""); // Assert - var outputWithType = OutputWithType.Deserialize(result)!; + var outputWithType = OutputWithType.Deserialize(result); outputWithType.Value.Should().BeOfType().Which.Should().NotBeNullOrEmpty(); outputWithType.TypeName.Should().Be("String"); outputWithType.FullTypeName.Should().Be("System.String"); @@ -117,7 +117,7 @@ public void Random_Boolean_OutputWithType() var result = action(""); // Assert - var outputWithType = OutputWithType.Deserialize(result)!; + var outputWithType = OutputWithType.Deserialize(result); outputWithType.Value.Should().BeOfType(); outputWithType.TypeName.Should().Be("Boolean"); outputWithType.FullTypeName.Should().Be("System.Boolean"); @@ -133,7 +133,7 @@ public void Random_Byte_OutputWithType() var result = action(""); // Assert - var outputWithType = OutputWithType.Deserialize(result)!; + var outputWithType = OutputWithType.Deserialize(result); outputWithType.Value.Should().BeOfType().Which.Should().BeInRange(byte.MinValue, byte.MaxValue); outputWithType.TypeName.Should().Be("Byte"); outputWithType.FullTypeName.Should().Be("System.Byte"); @@ -149,7 +149,7 @@ public void Random_Bytes_OutputWithType() var result = action(""); // Assert - var outputWithType = OutputWithType.Deserialize(result)!; + var outputWithType = OutputWithType.Deserialize(result); outputWithType.Value.Should().BeOfType().Which.Should().NotBeNullOrEmpty(); outputWithType.TypeName.Should().Be("Byte[]"); outputWithType.FullTypeName.Should().Be("System.Byte[]"); @@ -165,7 +165,7 @@ public void Random_Double_OutputWithType() var result = action(""); // Assert - var outputWithType = OutputWithType.Deserialize(result)!; + var outputWithType = OutputWithType.Deserialize(result); outputWithType.Value.Should().BeOfType(); outputWithType.TypeName.Should().Be("Double"); outputWithType.FullTypeName.Should().Be("System.Double"); @@ -181,7 +181,7 @@ public void Random_Float_OutputWithType() var result = action(""); // Assert - var outputWithType = OutputWithType.Deserialize(result)!; + var outputWithType = OutputWithType.Deserialize(result); outputWithType.Value.Should().BeOfType(); outputWithType.TypeName.Should().Be("Single"); outputWithType.FullTypeName.Should().Be("System.Single"); @@ -197,7 +197,7 @@ public void Random_DateTime_OutputWithType() var result = action(""); // Assert - var outputWithType = OutputWithType.Deserialize(result)!; + var outputWithType = OutputWithType.Deserialize(result); outputWithType.Value.Should().BeOfType(); outputWithType.TypeName.Should().Be("DateTime"); outputWithType.FullTypeName.Should().Be("System.DateTime"); @@ -213,7 +213,7 @@ public void Random_TimeSpan_OutputWithType() var result = action(""); // Assert - var outputWithType = OutputWithType.Deserialize(result)!; + var outputWithType = OutputWithType.Deserialize(result); outputWithType.Value.Should().BeOfType(); outputWithType.TypeName.Should().Be("TimeSpan"); outputWithType.FullTypeName.Should().Be("System.TimeSpan"); From abef8ce92e6fc300ac7417843b6950a4558602db Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Sat, 4 Nov 2023 14:18:45 +0100 Subject: [PATCH 5/9] more tests --- .../Models/OutputWithType.cs | 85 +++++-- .../Models/OutputWithTypeTests.cs | 231 ++++++++++++++++++ 2 files changed, 302 insertions(+), 14 deletions(-) create mode 100644 test/Handlebars.Net.Helpers.Tests/Models/OutputWithTypeTests.cs diff --git a/src/Handlebars.Net.Helpers.Random/Models/OutputWithType.cs b/src/Handlebars.Net.Helpers.Random/Models/OutputWithType.cs index c3bc507..42418fd 100644 --- a/src/Handlebars.Net.Helpers.Random/Models/OutputWithType.cs +++ b/src/Handlebars.Net.Helpers.Random/Models/OutputWithType.cs @@ -32,42 +32,99 @@ public static OutputWithType Deserialize(string? json) throw new InvalidOperationException(); } - if (!jsonObject.TryGetValue(nameof(Value), out var value)) + if (!TryGetValue(jsonObject, out var value)) { throw new MissingMemberException($"Property '{nameof(Value)}' is not found on type '{nameof(OutputWithType)}'."); } - if (!jsonObject.TryGetValue(nameof(TypeName), out var typeName) || typeName is not string typeNameAsString) + if (!TryGetTypeName(jsonObject, out var typeName)) { throw new MissingMemberException($"Property '{nameof(TypeName)}' is not found on type '{nameof(OutputWithType)}'."); } - if (!jsonObject.TryGetValue(nameof(FullTypeName), out var fullTypeName) || fullTypeName is not string fullTypeNameAsString) + if (!TryGetFullTypeName(jsonObject, out var fullTypeName)) { throw new MissingMemberException($"Property '{nameof(FullTypeName)}' is not found on type '{nameof(OutputWithType)}'."); } - var fullType = Type.GetType(fullTypeNameAsString); - if (fullType == null) + return new OutputWithType + { + Value = TryConvert(value, fullTypeName, out var convertedValue) ? convertedValue : value, + TypeName = typeName, + FullTypeName = fullTypeName + }; + } + + public static bool TryDeserialize(string? json, [NotNullWhen(true)] out OutputWithType? outputWithType) + { + JsonObject? jsonObject; + try { - throw new TypeLoadException($"Type '{fullTypeNameAsString}' is not found."); + jsonObject = SimpleJsonUtils.DeserializeObject(json); + } + catch + { + outputWithType = null; + return false; } - if (TryConvert(value, fullType, out var convertedValue)) + if (jsonObject != null && + TryGetValue(jsonObject, out var value) && + TryGetTypeName(jsonObject, out var typeName) && + TryGetFullTypeName(jsonObject, out var fullTypeName) + ) { - value = convertedValue; + outputWithType = new OutputWithType + { + Value = TryConvert(value, fullTypeName, out var convertedValue) ? convertedValue : value, + TypeName = typeName, + FullTypeName = fullTypeName + }; + return true; } - return new OutputWithType + outputWithType = default; + return false; + } + + private static bool TryGetValue(JsonObject jsonObject, out object value) + { + return jsonObject.TryGetValue(nameof(Value), out value); + } + + private static bool TryGetTypeName(JsonObject jsonObject, [NotNullWhen(true)] out string? value) + { + if (jsonObject.TryGetValue(nameof(TypeName), out var typeName) && typeName is string typeNameAsString) { - Value = value, - TypeName = typeNameAsString, - FullTypeName = fullTypeNameAsString - }; + value = typeNameAsString; + return true; + } + + value = default; + return false; } - private static bool TryConvert(object? value, Type fullType, [NotNullWhen(true)] out object? result) + private static bool TryGetFullTypeName(JsonObject jsonObject, [NotNullWhen(true)] out string? value) { + if (jsonObject.TryGetValue(nameof(FullTypeName), out var fullTypeName) && fullTypeName is string fullTypeNameAsString) + { + value = fullTypeNameAsString; + return true; + } + + value = default; + return false; + } + + private static bool TryConvert(object? value, string fullTypeName, [NotNullWhen(true)] out object? result) + { + var fullType = Type.GetType(fullTypeName); + if (fullType == null) + { + result = default; + return false; + } + try { if (fullType == typeof(Guid) && value is string guidAsString) diff --git a/test/Handlebars.Net.Helpers.Tests/Models/OutputWithTypeTests.cs b/test/Handlebars.Net.Helpers.Tests/Models/OutputWithTypeTests.cs new file mode 100644 index 0000000..86b04a1 --- /dev/null +++ b/test/Handlebars.Net.Helpers.Tests/Models/OutputWithTypeTests.cs @@ -0,0 +1,231 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using FluentAssertions; +using HandlebarsDotNet.Helpers.Models; +using Xunit; + +namespace HandlebarsDotNet.Helpers.Tests.Models; + +[ExcludeFromCodeCoverage] +public class OutputWithTypeTests +{ + [Fact] + public void Deserialize_ForString_ShouldReturnOutputWithType_WhenJsonIsValid() + { + // Arrange + var json = "{\"Value\":\"test\",\"TypeName\":\"String\",\"FullTypeName\":\"System.String\"}"; + + // Act + var result = OutputWithType.Deserialize(json); + + // Assert + result.Should().NotBeNull(); + result.Value.Should().BeOfType().And.Be("test"); + result.TypeName.Should().Be("String"); + result.FullTypeName.Should().Be("System.String"); + } + + [Fact] + public void Deserialize_ForInteger_ShouldReturnOutputWithType_WhenJsonIsValid() + { + // Arrange + var json = "{\"Value\":\"12345\",\"TypeName\":\"Int32\",\"FullTypeName\":\"System.Int32\"}"; + + // Act + var result = OutputWithType.Deserialize(json); + + result.Should().NotBeNull(); + result.Value.Should().BeOfType().And.Be(12345); + result.TypeName.Should().Be("Int32"); + result.FullTypeName.Should().Be("System.Int32"); + } + + [Fact] + public void Deserialize_WhenValueCannotBeConvertedToFullType_ShouldKeepOriginalValue() + { + // Arrange + var json = "{\"Value\":\"test\",\"TypeName\":\"Int32\",\"FullTypeName\":\"System.Int32\"}"; + + // Act + var result = OutputWithType.Deserialize(json); + + // Assert + result.Should().NotBeNull(); + result.Value.Should().BeOfType().And.Be("test"); + result.TypeName.Should().Be("Int32"); + result.FullTypeName.Should().Be("System.Int32"); + } + + [Fact] + public void Deserialize_ShouldThrowMissingMemberException_WhenValueIsMissing() + { + // Arrange + var json = "{\"TypeName\":\"String\",\"FullTypeName\":\"System.String\"}"; + + // Act + Action act = () => OutputWithType.Deserialize(json); + + // Assert + act.Should().Throw() + .WithMessage("*'Value'*"); + } + + [Fact] + public void Deserialize_ShouldThrowMissingMemberException_WhenTypeNameIsMissing() + { + // Arrange + var json = "{\"Value\":\"12345\",\"FullTypeName\":\"System.Int32\"}"; + + // Act + Action act = () => OutputWithType.Deserialize(json); + + // Assert + act.Should().Throw() + .WithMessage("*'TypeName'*"); + } + + [Fact] + public void Deserialize_ShouldThrowMissingMemberException_WhenFullTypeNameIsMissing() + { + // Arrange + var json = "{\"Value\":\"test\",\"TypeName\":\"String\"}"; + + // Act + Action act = () => OutputWithType.Deserialize(json); + + // Assert + act.Should().Throw() + .WithMessage("*'FullTypeName'*"); + } + + [Fact] + public void Deserialize_ShouldThrowInvalidOperationException_WhenJsonIsNull() + { + // Act + Action act = () => OutputWithType.Deserialize(null); + + // Assert + act.Should().Throw(); + } + + [Fact] + public void Deserialize_ShouldThrowJsonException_WhenJsonIsInvalid() + { + // Act + Action act = () => OutputWithType.Deserialize("invalid json"); + + // Assert + act.Should().Throw(); + } + + [Fact] + public void TryDeserialize_ForString_ShouldReturnTrue_WhenJsonIsValid() + { + // Arrange + var json = "{\"Value\":\"test\",\"TypeName\":\"String\",\"FullTypeName\":\"System.String\"}"; + + // Act + var result = OutputWithType.TryDeserialize(json, out var output); + + // Assert + result.Should().BeTrue(); + output!.Value.Should().NotBeNull().And.BeOfType().And.Be("test"); + output.TypeName.Should().Be("String"); + output.FullTypeName.Should().Be("System.String"); + } + + [Fact] + public void TryDeserialize_ForInteger_ShouldReturnTrue() + { + // Arrange + var json = "{\"Value\":\"12345\",\"TypeName\":\"Int32\",\"FullTypeName\":\"System.Int32\"}"; + + // Act + var result = OutputWithType.TryDeserialize(json, out var output); + + result.Should().BeTrue(); + output!.Value.Should().BeOfType().And.Be(12345); + output.TypeName.Should().Be("Int32"); + output.FullTypeName.Should().Be("System.Int32"); + } + + [Fact] + public void TryDeserialize_WhenValueCannotBeConvertedToFullType_ShouldReturnTrue_And_KeepOriginalValue() + { + // Arrange + var json = "{\"Value\":\"test\",\"TypeName\":\"Int32\",\"FullTypeName\":\"System.Int32\"}"; + + // Act + var result = OutputWithType.TryDeserialize(json, out var output); + + // Assert + result.Should().BeTrue(); + output!.Value.Should().BeOfType().And.Be("test"); + output.TypeName.Should().Be("Int32"); + output.FullTypeName.Should().Be("System.Int32"); + } + + [Fact] + public void TryDeserialize_ShouldReturnFalse_WhenValueIsMissing() + { + // Arrange + var json = "{\"TypeName\":\"String\",\"FullTypeName\":\"System.String\"}"; + + // Act + var result = OutputWithType.TryDeserialize(json, out var output); + + // Assert + result.Should().BeFalse(); + output.Should().BeNull(); + } + + [Fact] + public void TryDeserialize_ShouldReturnFalse_WhenTypeNameIsMissing() + { + // Arrange + var json = "{\"Value\":\"test\",\"FullTypeName\":\"System.String\"}"; + + // Act + var result = OutputWithType.TryDeserialize(json, out var output); + + // Assert + result.Should().BeFalse(); + output.Should().BeNull(); + } + + [Fact] + public void TryDeserialize_ShouldReturnFalse_WhenFullTypeNameIsMissing() + { + // Arrange + var json = "{\"Value\":\"test\",\"TypeName\":\"String\"}"; + + // Act + var result = OutputWithType.TryDeserialize(json, out var output); + + // Assert + result.Should().BeFalse(); + output.Should().BeNull(); + } + + [Fact] + public void TryDeserialize_ShouldReturnFalse_WhenJsonIsNull() + { + // Act + var result = OutputWithType.TryDeserialize(null, out var output); + + // Assert + result.Should().BeFalse(); + output.Should().BeNull(); + } + + [Fact] + public void TryDeserialize_ShouldReturnFalse_WhenJsonIsInvalid() + { + // Act + var result = OutputWithType.TryDeserialize("invalid json", out var output); + + // Assert + result.Should().BeFalse(); + output.Should().BeNull(); + } +} \ No newline at end of file From 823cfb4136829ff4e48014b48dde8acf1f061886 Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Sat, 4 Nov 2023 14:35:41 +0100 Subject: [PATCH 6/9] ... --- .../Models/OutputWithType.cs | 31 ++++++++++++++----- .../Models/OutputWithTypeTests.cs | 28 +++++++++++++++++ 2 files changed, 52 insertions(+), 7 deletions(-) diff --git a/src/Handlebars.Net.Helpers.Random/Models/OutputWithType.cs b/src/Handlebars.Net.Helpers.Random/Models/OutputWithType.cs index 42418fd..f4b4416 100644 --- a/src/Handlebars.Net.Helpers.Random/Models/OutputWithType.cs +++ b/src/Handlebars.Net.Helpers.Random/Models/OutputWithType.cs @@ -47,9 +47,14 @@ public static OutputWithType Deserialize(string? json) throw new MissingMemberException($"Property '{nameof(FullTypeName)}' is not found on type '{nameof(OutputWithType)}'."); } + if (!TryGetType(fullTypeName, out var fullType)) + { + throw new TypeLoadException($"Unable to load Type with FullTypeName '{fullTypeName}'."); + } + return new OutputWithType { - Value = TryConvert(value, fullTypeName, out var convertedValue) ? convertedValue : value, + Value = TryConvert(value, fullType, out var convertedValue) ? convertedValue : value, TypeName = typeName, FullTypeName = fullTypeName }; @@ -71,12 +76,13 @@ public static bool TryDeserialize(string? json, [NotNullWhen(true)] out OutputWi if (jsonObject != null && TryGetValue(jsonObject, out var value) && TryGetTypeName(jsonObject, out var typeName) && - TryGetFullTypeName(jsonObject, out var fullTypeName) - ) + TryGetFullTypeName(jsonObject, out var fullTypeName) && + TryGetType(fullTypeName, out var fullType) + ) { outputWithType = new OutputWithType { - Value = TryConvert(value, fullTypeName, out var convertedValue) ? convertedValue : value, + Value = TryConvert(value, fullType, out var convertedValue) ? convertedValue : value, TypeName = typeName, FullTypeName = fullTypeName }; @@ -116,15 +122,26 @@ private static bool TryGetFullTypeName(JsonObject jsonObject, [NotNullWhen(true) return false; } - private static bool TryConvert(object? value, string fullTypeName, [NotNullWhen(true)] out object? result) + private static bool TryGetType(string fullTypeName, [NotNullWhen(true)] out Type? type) + { + type = Type.GetType(fullTypeName); + return type != null; + } + + private static Type GetType(string fullTypeName) { var fullType = Type.GetType(fullTypeName); + if (fullType == null) { - result = default; - return false; + throw new TypeLoadException($"Unable to load Type with FullTypeName '{fullTypeName}'."); } + return fullType; + } + + private static bool TryConvert(object? value, Type fullType, [NotNullWhen(true)] out object? result) + { try { if (fullType == typeof(Guid) && value is string guidAsString) diff --git a/test/Handlebars.Net.Helpers.Tests/Models/OutputWithTypeTests.cs b/test/Handlebars.Net.Helpers.Tests/Models/OutputWithTypeTests.cs index 86b04a1..31ebb18 100644 --- a/test/Handlebars.Net.Helpers.Tests/Models/OutputWithTypeTests.cs +++ b/test/Handlebars.Net.Helpers.Tests/Models/OutputWithTypeTests.cs @@ -98,6 +98,20 @@ public void Deserialize_ShouldThrowMissingMemberException_WhenFullTypeNameIsMiss .WithMessage("*'FullTypeName'*"); } + [Fact] + public void Deserialize_ShouldException_WhenFullTypeNameIsInvalid() + { + // Arrange + var json = "{\"Value\":\"test\",\"TypeName\":\"Int32\",\"FullTypeName\":\"abc\"}"; + + // Act + Action act = () => OutputWithType.Deserialize(json); + + // Assert + act.Should().Throw() + .WithMessage("Unable to load Type with FullTypeName 'abc'."); + } + [Fact] public void Deserialize_ShouldThrowInvalidOperationException_WhenJsonIsNull() { @@ -207,6 +221,20 @@ public void TryDeserialize_ShouldReturnFalse_WhenFullTypeNameIsMissing() output.Should().BeNull(); } + [Fact] + public void TryDeserialize_ShouldReturnFalse_WhenFullTypeNameInvalid() + { + // Arrange + var json = "{\"Value\":\"test\",\"TypeName\":\"Int32\",\"FullTypeName\":\"abc\"}"; + + // Act + var result = OutputWithType.TryDeserialize(json, out var output); + + // Assert + result.Should().BeFalse(); + output.Should().BeNull(); + } + [Fact] public void TryDeserialize_ShouldReturnFalse_WhenJsonIsNull() { From a44b967f7642477f2973dc22a7c49f51082d5bb6 Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Sun, 5 Nov 2023 11:41:39 +0100 Subject: [PATCH 7/9] Uri --- .../Helpers/RandomHelpers.cs | 5 ++++ .../Models/OutputWithType.cs | 30 ++++++++----------- .../Models/OutputWithTypeTests.cs | 15 ++++++++++ .../Templates/RandomHelpersTemplateTests.cs | 13 ++++++++ 4 files changed, 46 insertions(+), 17 deletions(-) diff --git a/src/Handlebars.Net.Helpers.Random/Helpers/RandomHelpers.cs b/src/Handlebars.Net.Helpers.Random/Helpers/RandomHelpers.cs index 563c073..5d617ff 100644 --- a/src/Handlebars.Net.Helpers.Random/Helpers/RandomHelpers.cs +++ b/src/Handlebars.Net.Helpers.Random/Helpers/RandomHelpers.cs @@ -15,6 +15,8 @@ namespace HandlebarsDotNet.Helpers; internal class RandomHelpers : BaseHelpers, IHelpers { + // private const string KeepType = nameof(KeepType); + public RandomHelpers(IHandlebars context) : base(context) { } @@ -25,6 +27,9 @@ public RandomHelpers(IHandlebars context) : base(context) [HandlebarsWriter(WriterType.Value, "Random")] public object? Random(IDictionary hash) { + //var keepType = hash.TryGetValue(KeepType, out var keepTypeValue) && + // (keepTypeValue is true || keepTypeValue is string keepTypeAsString && string.Equals(keepTypeAsString, bool.TrueString, StringComparison.OrdinalIgnoreCase)); + //return keepType ? RandomAsOutputWithType(hash) : Generate(hash); return Generate(hash); } diff --git a/src/Handlebars.Net.Helpers.Random/Models/OutputWithType.cs b/src/Handlebars.Net.Helpers.Random/Models/OutputWithType.cs index f4b4416..9618cd0 100644 --- a/src/Handlebars.Net.Helpers.Random/Models/OutputWithType.cs +++ b/src/Handlebars.Net.Helpers.Random/Models/OutputWithType.cs @@ -124,22 +124,10 @@ private static bool TryGetFullTypeName(JsonObject jsonObject, [NotNullWhen(true) private static bool TryGetType(string fullTypeName, [NotNullWhen(true)] out Type? type) { - type = Type.GetType(fullTypeName); + type = Type.GetType(fullTypeName) ?? Type.GetType($"{fullTypeName}, System"); return type != null; } - private static Type GetType(string fullTypeName) - { - var fullType = Type.GetType(fullTypeName); - - if (fullType == null) - { - throw new TypeLoadException($"Unable to load Type with FullTypeName '{fullTypeName}'."); - } - - return fullType; - } - private static bool TryConvert(object? value, Type fullType, [NotNullWhen(true)] out object? result) { try @@ -150,6 +138,18 @@ private static bool TryConvert(object? value, Type fullType, [NotNullWhen(true)] return true; } + if (fullType == typeof(Uri) && value is string uriAsString) + { + result = new Uri(uriAsString); + return true; + } + + if (fullType == typeof(TimeSpan) && value is JsonObject timeSpanAsJsonObject) + { + result = TimeSpan.FromTicks((long)timeSpanAsJsonObject["Ticks"]); + return true; + } + if (fullType.IsArray && value is JsonArray jsonArray) { var elementType = fullType.GetElementType()!; @@ -161,10 +161,6 @@ private static bool TryConvert(object? value, Type fullType, [NotNullWhen(true)] result = newArray; } - else if (fullType == typeof(TimeSpan) && value is JsonObject timeSpanAsJsonObject) - { - result = TimeSpan.FromTicks((long)timeSpanAsJsonObject["Ticks"]); - } else { result = Convert.ChangeType(value, fullType); diff --git a/test/Handlebars.Net.Helpers.Tests/Models/OutputWithTypeTests.cs b/test/Handlebars.Net.Helpers.Tests/Models/OutputWithTypeTests.cs index 31ebb18..10ba41d 100644 --- a/test/Handlebars.Net.Helpers.Tests/Models/OutputWithTypeTests.cs +++ b/test/Handlebars.Net.Helpers.Tests/Models/OutputWithTypeTests.cs @@ -163,6 +163,21 @@ public void TryDeserialize_ForInteger_ShouldReturnTrue() output.FullTypeName.Should().Be("System.Int32"); } + [Fact] + public void TryDeserialize_ForUri_ShouldReturnTrue() + { + // Arrange + var json = "{\"Value\":\"https://localhost\",\"TypeName\":\"Uri\",\"FullTypeName\":\"System.Uri\"}"; + + // Act + var result = OutputWithType.TryDeserialize(json, out var output); + + result.Should().BeTrue(); + output!.Value.Should().BeOfType().And.Be(new Uri("https://localhost")); + output.TypeName.Should().Be("Uri"); + output.FullTypeName.Should().Be("System.Uri"); + } + [Fact] public void TryDeserialize_WhenValueCannotBeConvertedToFullType_ShouldReturnTrue_And_KeepOriginalValue() { diff --git a/test/Handlebars.Net.Helpers.Tests/Templates/RandomHelpersTemplateTests.cs b/test/Handlebars.Net.Helpers.Tests/Templates/RandomHelpersTemplateTests.cs index fc66f0b..44bd1d0 100644 --- a/test/Handlebars.Net.Helpers.Tests/Templates/RandomHelpersTemplateTests.cs +++ b/test/Handlebars.Net.Helpers.Tests/Templates/RandomHelpersTemplateTests.cs @@ -17,6 +17,19 @@ public RandomHelpersTemplateTests() HandlebarsHelpers.Register(_handlebarsContext, Category.Random); } + [Fact] + public void Random_ForWireMock() + { + // Arrange + var action = _handlebarsContext.Compile("{{Random Type=\"Integer\" Min=1000 Max=9999}}"); + + // Act + var result = action(""); + + // Assert + int.Parse(result).Should().BeInRange(1000, 9999); + } + [Fact] public void Random_Integer() { From fd7e3bd0b5518b66f8b04f0f8b40f7a27bd42692 Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Wed, 6 Dec 2023 19:03:01 +0100 Subject: [PATCH 8/9] ??? --- .../Helpers/RandomHelpers.cs | 28 +++------------- .../HandlebarsHelpers.cs | 7 +++- .../Models/OutputWithType.cs | 21 +++++++++++- .../Helpers/RandomHelpersTests.cs | 2 +- .../Templates/RandomHelpersTemplateTests.cs | 32 +++++++++++++++++++ 5 files changed, 64 insertions(+), 26 deletions(-) rename src/{Handlebars.Net.Helpers.Random => Handlebars.Net.Helpers}/Models/OutputWithType.cs (91%) diff --git a/src/Handlebars.Net.Helpers.Random/Helpers/RandomHelpers.cs b/src/Handlebars.Net.Helpers.Random/Helpers/RandomHelpers.cs index 5d617ff..acb6238 100644 --- a/src/Handlebars.Net.Helpers.Random/Helpers/RandomHelpers.cs +++ b/src/Handlebars.Net.Helpers.Random/Helpers/RandomHelpers.cs @@ -15,8 +15,6 @@ namespace HandlebarsDotNet.Helpers; internal class RandomHelpers : BaseHelpers, IHelpers { - // private const string KeepType = nameof(KeepType); - public RandomHelpers(IHandlebars context) : base(context) { } @@ -24,20 +22,17 @@ public RandomHelpers(IHandlebars context) : base(context) /// /// For backwards compatibility with WireMock.Net /// - [HandlebarsWriter(WriterType.Value, "Random")] - public object? Random(IDictionary hash) + [HandlebarsWriter(WriterType.String, "Random")] + public string? Random(IDictionary hash) { - //var keepType = hash.TryGetValue(KeepType, out var keepTypeValue) && - // (keepTypeValue is true || keepTypeValue is string keepTypeAsString && string.Equals(keepTypeAsString, bool.TrueString, StringComparison.OrdinalIgnoreCase)); - //return keepType ? RandomAsOutputWithType(hash) : Generate(hash); return Generate(hash); } /// /// For backwards compatibility with WireMock.Net /// - [HandlebarsWriter(WriterType.Value, "RandomAsOutputWithType")] - public string? RandomAsOutputWithType(IDictionary hash) + [HandlebarsWriter(WriterType.String, "RandomKeepType")] + public string? RandomKeepType(IDictionary hash) { return GenerateAsOutputWithType(hash); } @@ -112,19 +107,6 @@ private FieldOptionsAbstract GetFieldOptionsFromHash(IDictionary array) - { - value = ArrayUtils.ToArray(array); - } - - return outputWithType ? new OutputWithType - { - Value = value, - FullTypeName = fullName, - TypeName = name - } : value; + return outputWithType ? OutputWithType.Parse(value) : value; } } \ No newline at end of file diff --git a/src/Handlebars.Net.Helpers/HandlebarsHelpers.cs b/src/Handlebars.Net.Helpers/HandlebarsHelpers.cs index bff3f95..c2b3613 100644 --- a/src/Handlebars.Net.Helpers/HandlebarsHelpers.cs +++ b/src/Handlebars.Net.Helpers/HandlebarsHelpers.cs @@ -213,7 +213,12 @@ private static void RegisterValueHelper(IHandlebars handlebarsContext, object in { HandlebarsReturnWithOptionsHelper helper = (in HelperOptions options, in Context context, in Arguments arguments) => { - return InvokeMethod(passContext ? context : null, false, handlebarsContext, helperName, methodInfo, arguments, instance, options); + var value = InvokeMethod(passContext ? context : null, false, handlebarsContext, helperName, methodInfo, arguments, instance, options); + + var keepType = arguments.Hash.TryGetValue("KeepType", out var keepTypeValue) && + (keepTypeValue is true || keepTypeValue is string keepTypeAsString && string.Equals(keepTypeAsString, bool.TrueString, StringComparison.OrdinalIgnoreCase)); + + return keepType ? OutputWithType.Parse(value).Serialize() : value; }; handlebarsContext.RegisterHelper(helperName, helper); diff --git a/src/Handlebars.Net.Helpers.Random/Models/OutputWithType.cs b/src/Handlebars.Net.Helpers/Models/OutputWithType.cs similarity index 91% rename from src/Handlebars.Net.Helpers.Random/Models/OutputWithType.cs rename to src/Handlebars.Net.Helpers/Models/OutputWithType.cs index 9618cd0..f840a8c 100644 --- a/src/Handlebars.Net.Helpers.Random/Models/OutputWithType.cs +++ b/src/Handlebars.Net.Helpers/Models/OutputWithType.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using HandlebarsDotNet.Helpers.Json; using HandlebarsDotNet.Helpers.Utils; @@ -13,9 +14,27 @@ public class OutputWithType public string? FullTypeName { get; set; } + public static OutputWithType Parse(object? value) + { + var typeName = value?.GetType().Name; + var fullTypeName = value?.GetType().FullName; + + if (value is IEnumerable array) + { + value = ArrayUtils.ToArray(array); + } + + return new OutputWithType + { + Value = value, + FullTypeName = fullTypeName, + TypeName = typeName + }; + } + public override string ToString() { - return SimpleJsonUtils.SerializeObject(this) ?? string.Empty; + return Serialize() ?? string.Empty; } public string? Serialize() diff --git a/test/Handlebars.Net.Helpers.Tests/Helpers/RandomHelpersTests.cs b/test/Handlebars.Net.Helpers.Tests/Helpers/RandomHelpersTests.cs index 512623f..88b13f3 100644 --- a/test/Handlebars.Net.Helpers.Tests/Helpers/RandomHelpersTests.cs +++ b/test/Handlebars.Net.Helpers.Tests/Helpers/RandomHelpersTests.cs @@ -62,7 +62,7 @@ public void RandomAsOutputWithType_Integer() }; // Act - var result = _sut.RandomAsOutputWithType(hash); + var result = _sut.RandomKeepType(hash); // Assert var outputWithType = OutputWithType.Deserialize(result)!; diff --git a/test/Handlebars.Net.Helpers.Tests/Templates/RandomHelpersTemplateTests.cs b/test/Handlebars.Net.Helpers.Tests/Templates/RandomHelpersTemplateTests.cs index 44bd1d0..350db68 100644 --- a/test/Handlebars.Net.Helpers.Tests/Templates/RandomHelpersTemplateTests.cs +++ b/test/Handlebars.Net.Helpers.Tests/Templates/RandomHelpersTemplateTests.cs @@ -30,6 +30,38 @@ public void Random_ForWireMock() int.Parse(result).Should().BeInRange(1000, 9999); } + [Fact] + public void Random_ForWireMock_KeepTypeTrue() + { + // Arrange + var action = _handlebarsContext.Compile("{{Random KeepType=true Type=\"Integer\" Min=1000 Max=9999}}"); + + // Act + var result = action(""); + + // Assert + var outputWithType = OutputWithType.Deserialize(result); + outputWithType.Value.Should().BeOfType().Which.Should().BeInRange(1000, 9999); + outputWithType.TypeName.Should().Be("Int32"); + outputWithType.FullTypeName.Should().Be("System.Int32"); + } + + [Fact] + public void RandomKeepType_ForWireMock() + { + // Arrange + var action = _handlebarsContext.Compile("{{RandomKeepType Type=\"Integer\" Min=1000 Max=9999}}"); + + // Act + var result = action(""); + + // Assert + var outputWithType = OutputWithType.Deserialize(result); + outputWithType.Value.Should().BeOfType().Which.Should().BeInRange(1000, 9999); + outputWithType.TypeName.Should().Be("Int32"); + outputWithType.FullTypeName.Should().Be("System.Int32"); + } + [Fact] public void Random_Integer() { From a20eddede664719d50ccc030d0bd66ec8dc2a61b Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Sat, 9 Dec 2023 15:10:43 +0100 Subject: [PATCH 9/9] .. --- .../Helpers/RandomHelpers.cs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/Handlebars.Net.Helpers.Random/Helpers/RandomHelpers.cs b/src/Handlebars.Net.Helpers.Random/Helpers/RandomHelpers.cs index acb6238..3e3d0e3 100644 --- a/src/Handlebars.Net.Helpers.Random/Helpers/RandomHelpers.cs +++ b/src/Handlebars.Net.Helpers.Random/Helpers/RandomHelpers.cs @@ -6,7 +6,6 @@ using HandlebarsDotNet.Helpers.Helpers; using HandlebarsDotNet.Helpers.Models; using HandlebarsDotNet.Helpers.Parsers; -using HandlebarsDotNet.Helpers.Utils; using RandomDataGenerator.FieldOptions; using RandomDataGenerator.Randomizers; @@ -23,7 +22,7 @@ public RandomHelpers(IHandlebars context) : base(context) /// For backwards compatibility with WireMock.Net /// [HandlebarsWriter(WriterType.String, "Random")] - public string? Random(IDictionary hash) + public object? Random(IDictionary hash) { return Generate(hash); } @@ -40,7 +39,9 @@ public RandomHelpers(IHandlebars context) : base(context) [HandlebarsWriter(WriterType.Value)] public object? Generate(IDictionary hash) { - return GenerateInternal(hash, false); + var keepType = hash.TryGetValue("KeepType", out var value) && value is true; + + return GenerateInternal(hash, keepType); } /// @@ -67,7 +68,7 @@ public RandomHelpers(IHandlebars context) : base(context) // If the IFieldOptionsGuid defines Uppercase, use the 'GenerateAsString' method. if (fieldOptions is IFieldOptionsGuid fieldOptionsGuid) { - return GetRandomValue(outputWithType, + return GetRandomValue(outputWithType, () => fieldOptionsGuid.Uppercase ? randomizer.GenerateAsString() : randomizer.Generate(), () => randomizer.Generate() );