From 0aa5586e558cfb8307fe9a3ed218e09b43504df8 Mon Sep 17 00:00:00 2001 From: "George Njeri (Swagfin)" Date: Wed, 23 Jul 2025 20:30:31 +0300 Subject: [PATCH 1/2] chore: support for bool condition inside if block Added If Condition tests --- ObjectSemantics.NET.Tests/IfConditionTests.cs | 117 ++++++++++++++++++ .../MoqModels/Student.cs | 2 + .../StringFormattingTests.cs | 49 ++++++++ .../Algorithim/GavinsAlgorithim.cs | 2 - .../ExtractedObjPropertyExtensions.cs | 16 +++ .../ObjectSemantics.NET.csproj | 6 +- 6 files changed, 187 insertions(+), 5 deletions(-) create mode 100644 ObjectSemantics.NET.Tests/IfConditionTests.cs diff --git a/ObjectSemantics.NET.Tests/IfConditionTests.cs b/ObjectSemantics.NET.Tests/IfConditionTests.cs new file mode 100644 index 0000000..dc997a9 --- /dev/null +++ b/ObjectSemantics.NET.Tests/IfConditionTests.cs @@ -0,0 +1,117 @@ +using ObjectSemantics.NET.Tests.MoqModels; +using System.Collections.Generic; +using Xunit; + +namespace ObjectSemantics.NET.Tests +{ + public class IfConditionTests + { + [Theory] + [InlineData(1, "Valid")] + [InlineData(0, "Invalid")] + public void Should_Render_If_Block_When_Condition_Is_True(int id, string expected) + { + var model = new Invoice { Id = id }; + + var template = new ObjectSemanticsTemplate + { + FileContents = @"{{ #if(Id == 1) }}Valid{{ #else }}Invalid{{ #endif }}" + }; + + string result = template.Map(model); + Assert.Equal(expected, result); + } + + + [Theory] + [InlineData(18, "Minor")] + [InlineData(21, "Adult")] + [InlineData(5, "Minor")] + public void Should_Handle_LessThan_Or_Equal(int age, string expected) + { + var model = new Student { Age = age }; + + var template = new ObjectSemanticsTemplate + { + FileContents = @"{{ #if(Age <= 18) }}Minor{{ #else }}Adult{{ #endif }}" + }; + + var result = template.Map(model); + Assert.Equal(expected, result); + } + + + [Theory] + [InlineData(1, "1")] + [InlineData(0, "Error")] + [InlineData(-1, "Error")] + [InlineData(5, "5")] + [InlineData(+2, "2")] + public void Should_Handle_Whitespace_And_Case_Insensitive_Condition(int id, string expected) + { + var model = new Invoice { Id = id }; + + var template = new ObjectSemanticsTemplate + { + FileContents = @"{{ #if( id > 0 ) }}{{id}}{{ #else }}Error{{ #endif }}" + }; + + var result = template.Map(model); + Assert.Equal(expected, result); + } + + + [Fact] + public void Should_Render_If_Block_Without_Else_When_True() + { + var model = new Student { IsActive = true }; + + var template = new ObjectSemanticsTemplate + { + FileContents = @"Student: John {{ #if(IsActive == true) }}[Is Active]{{ #endif }}" + }; + + var result = template.Map(model); + Assert.Equal("Student: John [Is Active]", result); + } + + [Fact] + public void Should_Evaluate_If_Enumerable_Count() + { + var model = new Student + { + Invoices = new List + { + new Invoice{ Id = 2, RefNo = "INV_002" }, + new Invoice{ Id = 1, RefNo = "INV_001" } + } + }; + + var template = new ObjectSemanticsTemplate + { + FileContents = @"{{ #if(Invoices == 2) }}Matched{{ #else }}Not Matched{{ #endif }}" + }; + + var result = template.Map(model); + Assert.Equal("Matched", result); + } + + [Fact] + public void Should_Evaluate_Empty_Enumerable_As_Zero() + { + var model = new Student + { + Invoices = new List() + }; + + var template = new ObjectSemanticsTemplate + { + FileContents = @"{{ #if(Invoices == 0) }}No invoices available{{ #else }}Invoices Found{{ #endif }}" + }; + + var result = template.Map(model); + Assert.Equal("No invoices available", result); + } + + } +} diff --git a/ObjectSemantics.NET.Tests/MoqModels/Student.cs b/ObjectSemantics.NET.Tests/MoqModels/Student.cs index 5351d52..929e3de 100644 --- a/ObjectSemantics.NET.Tests/MoqModels/Student.cs +++ b/ObjectSemantics.NET.Tests/MoqModels/Student.cs @@ -8,10 +8,12 @@ internal class Student public Guid Id { get; set; } = Guid.NewGuid(); public string StudentName { get; set; } public double Balance { get; set; } + public int Age { get; set; } public DateTime RegDate { get; set; } = DateTime.Now; public List Invoices { get; set; } = new List(); public string[] ArrayOfString { get; set; } = new string[] { }; public double[] ArrayOfDouble { get; set; } = new double[] { }; + public bool IsActive { get; set; } public List StudentClockInDetails { get; set; } = new List(); } class StudentClockInDetail diff --git a/ObjectSemantics.NET.Tests/StringFormattingTests.cs b/ObjectSemantics.NET.Tests/StringFormattingTests.cs index 068e3dc..07354f4 100644 --- a/ObjectSemantics.NET.Tests/StringFormattingTests.cs +++ b/ObjectSemantics.NET.Tests/StringFormattingTests.cs @@ -24,6 +24,31 @@ public void Should_Accept_String_To_UpperCase_or_LowerCase_Formatting() Assert.Equal(expectedString, generatedTemplate, false, true, true); } + [Theory] + [InlineData("john doe", "John Doe")] + [InlineData("JANE DOE", "Jane Doe")] + [InlineData("aLiCe joHNsOn", "Alice Johnson")] + [InlineData("", "")] + [InlineData(null, "")] + public void Should_Convert_StudentName_To_TitleCase(string studentName, string expectedTitleCase) + { + // Create Model + Student student = new Student + { + StudentName = studentName + }; + + // Template + var template = new ObjectSemanticsTemplate + { + FileContents = @"{{ StudentName:titlecase }}" + }; + + string generatedTemplate = template.Map(student); + + Assert.Equal(expectedTitleCase, generatedTemplate, ignoreCase: false, ignoreLineEndingDifferences: true, ignoreWhiteSpaceDifferences: true); + } + [Fact] public void Should_Accept_Number_To_String_Formatting() @@ -116,5 +141,29 @@ public void Should_Accept_String_From_BASE64_Formatting() string expectedString = "Original String: Sm9obiBET0U= | From BASE64 String: John DOE"; Assert.Equal(expectedString, generatedTemplate, false, true, true); } + + [Theory] + [InlineData("Alice Johnson", "13")] + [InlineData("Bob", "3")] + [InlineData("", "0")] + [InlineData(null, "")] + public void Should_Return_Correct_Length_Of_StudentName(string studentName, string expectedLength) + { + // Create Model + Student student = new Student + { + StudentName = studentName + }; + + // Template + var template = new ObjectSemanticsTemplate + { + FileContents = @"{{ StudentName:length }}" + }; + + string generatedTemplate = template.Map(student); + + Assert.Equal(expectedLength, generatedTemplate, ignoreCase: false, ignoreLineEndingDifferences: true, ignoreWhiteSpaceDifferences: true); + } } } diff --git a/ObjectSemantics.NET/Algorithim/GavinsAlgorithim.cs b/ObjectSemantics.NET/Algorithim/GavinsAlgorithim.cs index 8903615..e985311 100644 --- a/ObjectSemantics.NET/Algorithim/GavinsAlgorithim.cs +++ b/ObjectSemantics.NET/Algorithim/GavinsAlgorithim.cs @@ -238,6 +238,4 @@ private static List GetObjPropertiesFromUnknown(object val } return list; } - - } \ No newline at end of file diff --git a/ObjectSemantics.NET/Extensions/ExtractedObjPropertyExtensions.cs b/ObjectSemantics.NET/Extensions/ExtractedObjPropertyExtensions.cs index 1f62ffe..4ffab68 100644 --- a/ObjectSemantics.NET/Extensions/ExtractedObjPropertyExtensions.cs +++ b/ObjectSemantics.NET/Extensions/ExtractedObjPropertyExtensions.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.Linq; using System.Security; @@ -43,6 +44,10 @@ private static string GetAppliedPropertyFormatting(this ExtractedObjProperty p, return p.StringFormatted.ToBase64String(); else if (customFormattingValue.ToLower().Equals("frombase64")) return p.StringFormatted.FromBase64String(); + else if (customFormattingValue.ToLower().Equals("length")) + return p.StringFormatted?.Length.ToString(); + else if (customFormattingValue.ToLower().Equals("titlecase")) + return CultureInfo.CurrentCulture.TextInfo.ToTitleCase(p.StringFormatted?.ToLower() ?? string.Empty); else return p.StringFormatted; } @@ -98,6 +103,17 @@ public static bool IsPropertyValueConditionPassed(this ExtractedObjProperty prop default: return false; } } + else if (property.Type == typeof(bool)) + { + bool v1 = Convert.ToBoolean(property.OriginalValue); + bool v2 = Convert.ToBoolean(GetConvertibleValue(valueComparer)); + switch (criteria) + { + case "==": return v1 == v2; + case "!=": return v1 != v2; + default: return false; + } + } else if (property.IsEnumerableObject) { int v1 = (property.OriginalValue == null) ? 0 : ((IEnumerable)property.OriginalValue).Count(); diff --git a/ObjectSemantics.NET/ObjectSemantics.NET.csproj b/ObjectSemantics.NET/ObjectSemantics.NET.csproj index 9d12ae2..89c6572 100644 --- a/ObjectSemantics.NET/ObjectSemantics.NET.csproj +++ b/ObjectSemantics.NET/ObjectSemantics.NET.csproj @@ -15,9 +15,9 @@ ToBase64 FromBase64 . Added template extension method to allow mapping directly from Template - 6.0.4 - 6.0.4 - 6.0.4 + 6.0.5 + 6.0.5 + 6.0.5 false README.md From cc8680d5f3a2aeb2a5e3c90d03e5767fc5092315 Mon Sep 17 00:00:00 2001 From: "George Njeri (Swagfin)" Date: Wed, 23 Jul 2025 20:40:02 +0300 Subject: [PATCH 2/2] nuget yml file update --- .github/workflows/release-nuget-pkg.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/release-nuget-pkg.yml b/.github/workflows/release-nuget-pkg.yml index 1c984f1..a1a587d 100644 --- a/.github/workflows/release-nuget-pkg.yml +++ b/.github/workflows/release-nuget-pkg.yml @@ -13,6 +13,8 @@ jobs: uses: actions/checkout@v4 - name: Setup .NET SDK uses: actions/setup-dotnet@v1 + with: + dotnet-version: '8.0.x' - name: Build run: dotnet build ObjectSemantics.NET/ObjectSemantics.NET.csproj -c Release - name: Test