Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 77 additions & 0 deletions ObjectSemantics.NET.Tests/CognitiveMapTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,83 @@ public void Library_Entry_Point_String_Extension_Should_Work(string personName)
Assert.Equal($"I am {personName}!", generatedTemplate);
}

[Fact]
public void Property_Of_Object_Should_Be_Mapped()
{
CustomerPayment customerPayment = new CustomerPayment
{
Amount = 100_000_000,
Customer = new Customer
{
CompanyName = "CRUDSOFT TECHNOLOGIES"
}
};
string generatedTemplate = "Paid Amount: {{ Amount:N2 }} By {{ Customer.CompanyName }}".Map(customerPayment);
Assert.Equal("Paid Amount: 100,000,000.00 By CRUDSOFT TECHNOLOGIES", generatedTemplate);
}

[Fact]
public void Nested_Property_Should_Be_Empty_When_Parent_Object_Is_Null()
{
CustomerPayment customerPayment = new CustomerPayment
{
Amount = 100_000_000,
Customer = null
};

string generatedTemplate = "Paid Amount: {{ Amount:N2 }} By {{ Customer.CompanyName }}".Map(customerPayment);
Assert.Equal("Paid Amount: 100,000,000.00 By ", generatedTemplate);
}

[Fact]
public void Nested_Property_Should_Be_Empty_When_Target_Property_Is_Null()
{
CustomerPayment customerPayment = new CustomerPayment
{
Amount = 100_000_000,
Customer = new Customer
{
CompanyName = null
}
};

string generatedTemplate = "Paid Amount: {{ Amount:N2 }} By {{ Customer.CompanyName }}".Map(customerPayment);
Assert.Equal("Paid Amount: 100,000,000.00 By ", generatedTemplate);
}

[Fact]
public void Deeply_Nested_Property_Should_Be_Mapped()
{
CustomerPayment customerPayment = new CustomerPayment
{
Amount = 100_000_000,
Customer = new Customer
{
BankingDetail = new CustomerBankingDetail
{
BankName = "KCB BANK"
}
}
};

string generatedTemplate = "Paid Amount: {{ Amount:N2 }} Via {{ Customer.BankingDetail.BankName }}".Map(customerPayment);
Assert.Equal("Paid Amount: 100,000,000.00 Via KCB BANK", generatedTemplate);
}

[Fact]
public void Deeply_Nested_Property_Should_Be_Empty_When_Object_Is_Null()
{
CustomerPayment customerPayment = new CustomerPayment
{
Amount = 100_000_000,
Customer = null
};

string generatedTemplate = "Paid Amount: {{ Amount:N2 }} Via {{ Customer.BankingDetail.BankName }}".Map(customerPayment);
Assert.Equal("Paid Amount: 100,000,000.00 Via ", generatedTemplate);
}


[Fact]
public void Additional_Headers_Should_Also_Be_Mapped()
{
Expand Down
226 changes: 226 additions & 0 deletions ObjectSemantics.NET.Tests/ExpressionFunctionTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,226 @@
using ObjectSemantics.NET.Tests.MoqModels;
using System.Collections.Generic;
using Xunit;

namespace ObjectSemantics.NET.Tests
{
public class ExpressionFunctionTests
{
[Fact]
public void Should_Sum_Collection_Path()
{
OrderEmailModel model = new OrderEmailModel
{
Customer = new OrderCustomer
{
Payments = new List<OrderPayment>
{
new OrderPayment { Amount = 1000 },
new OrderPayment { Amount = 2000.75m },
new OrderPayment { Amount = 999.25m }
}
}
};

string result = model.Map("{{ __sum(Customer.Payments.Amount):N2 }}");
Assert.Equal("4,000.00", result);
}

[Fact]
public void Should_Average_Collection_Path_With_Single_Underscore()
{
OrderEmailModel model = new OrderEmailModel
{
Customer = new OrderCustomer
{
Payments = new List<OrderPayment>
{
new OrderPayment { PaidAmount = 300 },
new OrderPayment { PaidAmount = 900 },
new OrderPayment { PaidAmount = 1200 }
}
}
};

string result = model.Map("{{ _avg(Customer.Payments.PaidAmount):N2 }}");
Assert.Equal("800.00", result);
}

[Fact]
public void Should_Calculate_Property_Expression()
{
OrderEmailModel model = new OrderEmailModel
{
PaidAmount = 10000,
Customer = new OrderCustomer
{
CreditLimit = 4500
}
};

string result = model.Map("{{ __calc(PaidAmount - Customer.CreditLimit):N2 }}");
Assert.Equal("5,500.00", result);
}

[Fact]
public void Should_Calculate_Expression_Including_Parentheses_And_Decimals()
{
OrderEmailModel model = new OrderEmailModel
{
Subtotal = 1000,
Tax = 160
};

string result = model.Map("{{ __calc((Subtotal + Tax) * 0.5):N2 }}");
Assert.Equal("580.00", result);
}

[Fact]
public void Should_Calculate_Count_Min_And_Max_Aggregates()
{
OrderEmailModel model = new OrderEmailModel
{
Customer = new OrderCustomer
{
Payments = new List<OrderPayment>
{
new OrderPayment { Amount = 700 },
new OrderPayment { Amount = 200 },
new OrderPayment { Amount = 1100 }
}
}
};

string template = "{{ __count(Customer.Payments.Amount) }}|{{ __min(Customer.Payments.Amount):N2 }}|{{ __max(Customer.Payments.Amount):N2 }}";
string result = model.Map(template);
Assert.Equal("3|200.00|1,100.00", result);
}

[Fact]
public void Should_Calculate_Per_Row_Expression_Inside_Loop()
{
OrderEmailModel model = new OrderEmailModel
{
Items = new List<OrderLineItem>
{
new OrderLineItem { Quantity = 2, UnitPrice = 350 },
new OrderLineItem { Quantity = 3, UnitPrice = 100.5m }
}
};

string template = "{{ #foreach(Items) }}[{{ __calc(Quantity * UnitPrice):N2 }}]{{ #endforeach }}";
string result = model.Map(template);

Assert.Equal("[700.00][301.50]", result);
}

[Fact]
public void Should_Render_Empty_When_Calc_Expression_Is_Invalid()
{
OrderEmailModel model = new OrderEmailModel
{
PaidAmount = 5000,
Customer = new OrderCustomer
{
CreditLimit = 1000
}
};

string result = model.Map("A{{ __calc(PaidAmount - UnknownValue):N2 }}B");
Assert.Equal("AB", result);
}

[Fact]
public void Should_Render_Empty_For_Unknown_Aggregate_Path()
{
OrderEmailModel model = new OrderEmailModel
{
Customer = new OrderCustomer
{
Payments = new List<OrderPayment>
{
new OrderPayment { Amount = 100 }
}
}
};

string result = model.Map("{{ __sum(Customer.UnknownPayments.Amount) }}|{{ __avg(Customer.UnknownPayments.Amount) }}|{{ __count(Customer.UnknownPayments.Amount) }}|{{ __min(Customer.UnknownPayments.Amount) }}|{{ __max(Customer.UnknownPayments.Amount) }}");
Assert.Equal("||||", result);
}

[Fact]
public void Should_Return_Zero_For_Aggregates_When_Source_Is_Null()
{
OrderEmailModel model = new OrderEmailModel
{
Customer = null
};

string result = model.Map("{{ __sum(Customer.Payments.Amount) }}|{{ __avg(Customer.Payments.Amount) }}|{{ __count(Customer.Payments.Amount) }}|{{ __min(Customer.Payments.Amount) }}|{{ __max(Customer.Payments.Amount) }}");
Assert.Equal("0|0|0|0|0", result);
}

[Fact]
public void Should_Return_Zero_When_Calc_Uses_Null_Operand_Path()
{
OrderEmailModel model = new OrderEmailModel
{
PaidAmount = 5000,
Customer = null
};

string result = model.Map("A{{ __calc(PaidAmount - Customer.CreditLimit) }}B");
Assert.Equal("A0B", result);
}

[Fact]
public void Should_Support_Expressions_Without_Number_Format()
{
OrderEmailModel model = new OrderEmailModel
{
PaidAmount = 10000,
Customer = new OrderCustomer
{
CreditLimit = 4500,
Payments = new List<OrderPayment>
{
new OrderPayment { Amount = 1000 },
new OrderPayment { Amount = 2000 },
new OrderPayment { Amount = 1500 }
}
}
};

string result = model.Map("{{ __sum(Customer.Payments.Amount) }}|{{ __calc(PaidAmount - Customer.CreditLimit) }}");
Assert.Equal("4500|5500", result);
}

[Fact]
public void Should_Render_Empty_When_Sum_Path_Is_String_Based()
{
OrderEmailModel model = new OrderEmailModel
{
Items = new List<OrderLineItem>
{
new OrderLineItem { Name = "Keyboard" },
new OrderLineItem { Name = "Mouse" }
}
};

string result = model.Map("X{{ __sum(Items.Name) }}Y");
Assert.Equal("XY", result);
}

[Fact]
public void Should_Render_Empty_When_Sum_Path_Is_Date_Based()
{
OrderEmailModel model = new OrderEmailModel
{
OrderDate = new System.DateTime(2026, 03, 07)
};

string result = model.Map("X{{ __sum(OrderDate) }}Y");
Assert.Equal("XY", result);
}
}
}
7 changes: 7 additions & 0 deletions ObjectSemantics.NET.Tests/MoqModels/CustomerPayment.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,12 @@ public class Customer
public string FirstName { get; set; }
public string LastName { get; set; }
public string CompanyName { get; set; }
public CustomerBankingDetail BankingDetail { get; set; }
}

public class CustomerBankingDetail
{
public string BankName { get; set; }
public string AccountNumber { get; set; }
}
}
48 changes: 48 additions & 0 deletions ObjectSemantics.NET.Tests/MoqModels/OrderEmailModel.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
using System;
using System.Collections.Generic;

namespace ObjectSemantics.NET.Tests.MoqModels
{
public class OrderEmailModel
{
public string OrderNo { get; set; }
public DateTime OrderDate { get; set; }
public decimal Subtotal { get; set; }
public decimal Tax { get; set; }
public decimal Total { get; set; }
public decimal PaidAmount { get; set; }
public bool IsPaid { get; set; }
public OrderCustomer Customer { get; set; }
public List<OrderLineItem> Items { get; set; } = new List<OrderLineItem>();
}

public class OrderCustomer
{
public string FullName { get; set; }
public string Email { get; set; }
public decimal CreditLimit { get; set; }
public List<OrderPayment> Payments { get; set; } = new List<OrderPayment>();
public OrderAddress BillingAddress { get; set; }
}

public class OrderAddress
{
public string Line1 { get; set; }
public string City { get; set; }
public string Country { get; set; }
}

public class OrderLineItem
{
public string Name { get; set; }
public int Quantity { get; set; }
public decimal UnitPrice { get; set; }
public decimal LineTotal { get; set; }
}

public class OrderPayment
{
public decimal Amount { get; set; }
public decimal PaidAmount { get; set; }
}
}
Loading