using System.Collections.Generic; using FluentAssertions; using Workflow.Domain.Expressions; using Xunit; namespace Workflow.Tests.Condition; public class ConditionEvaluatorTests { private readonly ConditionEvaluator _sut = new(); #region Simple Comparisons [Fact] public void Evaluate_Equals_ReturnsTrue_WhenValueMatches() { // Arrange const string condition = """{ "field": "status", "op": "==", "value": "approved" }"""; var variables = new Dictionary { ["status"] = "approved" }; // Act var result = _sut.Evaluate(condition, variables); // Assert result.Should().BeTrue(); } [Fact] public void Evaluate_Equals_ReturnsFalse_WhenValueDiffers() { // Arrange const string condition = """{ "field": "status", "op": "==", "value": "approved" }"""; var variables = new Dictionary { ["status"] = "rejected" }; // Act var result = _sut.Evaluate(condition, variables); // Assert result.Should().BeFalse(); } [Fact] public void Evaluate_NotEquals_ReturnsTrue_WhenValueDiffers() { // Arrange const string condition = """{ "field": "status", "op": "!=", "value": "approved" }"""; var variables = new Dictionary { ["status"] = "rejected" }; // Act var result = _sut.Evaluate(condition, variables); // Assert result.Should().BeTrue(); } [Fact] public void Evaluate_GreaterThan_ReturnsTrue_WhenValueIsGreater() { // Arrange const string condition = """{ "field": "amount", "op": ">", "value": 5000 }"""; var variables = new Dictionary { ["amount"] = 7500 }; // Act var result = _sut.Evaluate(condition, variables); // Assert result.Should().BeTrue(); } [Fact] public void Evaluate_GreaterThan_ReturnsFalse_WhenValueIsLessOrEqual() { // Arrange const string condition = """{ "field": "amount", "op": ">", "value": 5000 }"""; var variables = new Dictionary { ["amount"] = 5000 }; // Act var result = _sut.Evaluate(condition, variables); // Assert result.Should().BeFalse(); } [Fact] public void Evaluate_LessThan_ReturnsTrue_WhenValueIsLess() { // Arrange const string condition = """{ "field": "days", "op": "<", "value": 5 }"""; var variables = new Dictionary { ["days"] = 3 }; // Act var result = _sut.Evaluate(condition, variables); // Assert result.Should().BeTrue(); } [Fact] public void Evaluate_GreaterThanOrEqual_ReturnsTrue_WhenValueIsEqual() { // Arrange const string condition = """{ "field": "amount", "op": ">=", "value": 5000 }"""; var variables = new Dictionary { ["amount"] = 5000 }; // Act var result = _sut.Evaluate(condition, variables); // Assert result.Should().BeTrue(); } [Fact] public void Evaluate_LessThanOrEqual_ReturnsTrue_WhenValueIsEqual() { // Arrange const string condition = """{ "field": "days", "op": "<=", "value": 3 }"""; var variables = new Dictionary { ["days"] = 3 }; // Act var result = _sut.Evaluate(condition, variables); // Assert result.Should().BeTrue(); } [Fact] public void Evaluate_Contains_ReturnsTrue_WhenFieldContainsValue() { // Arrange const string condition = """{ "field": "description", "op": "contains", "value": "urgent" }"""; var variables = new Dictionary { ["description"] = "This is an urgent request" }; // Act var result = _sut.Evaluate(condition, variables); // Assert result.Should().BeTrue(); } [Fact] public void Evaluate_Contains_ReturnsFalse_WhenFieldDoesNotContainValue() { // Arrange const string condition = """{ "field": "description", "op": "contains", "value": "urgent" }"""; var variables = new Dictionary { ["description"] = "This is a routine request" }; // Act var result = _sut.Evaluate(condition, variables); // Assert result.Should().BeFalse(); } [Fact] public void Evaluate_In_ReturnsTrue_WhenValueInArray() { // Arrange const string condition = """{ "field": "status", "op": "in", "value": ["pending", "review", "approved"] }"""; var variables = new Dictionary { ["status"] = "review" }; // Act var result = _sut.Evaluate(condition, variables); // Assert result.Should().BeTrue(); } [Fact] public void Evaluate_In_ReturnsFalse_WhenValueNotInArray() { // Arrange const string condition = """{ "field": "status", "op": "in", "value": ["pending", "review", "approved"] }"""; var variables = new Dictionary { ["status"] = "rejected" }; // Act var result = _sut.Evaluate(condition, variables); // Assert result.Should().BeFalse(); } #endregion #region Combined Conditions [Fact] public void Evaluate_And_ReturnsTrue_WhenAllConditionsTrue() { // Arrange const string condition = """ { "and": [ { "field": "days", "op": "<=", "value": 3 }, { "field": "type", "op": "==", "value": "annual" } ]} """; var variables = new Dictionary { ["days"] = 2, ["type"] = "annual" }; // Act var result = _sut.Evaluate(condition, variables); // Assert result.Should().BeTrue(); } [Fact] public void Evaluate_And_ReturnsFalse_WhenAnyConditionFalse() { // Arrange const string condition = """ { "and": [ { "field": "days", "op": "<=", "value": 3 }, { "field": "type", "op": "==", "value": "annual" } ]} """; var variables = new Dictionary { ["days"] = 5, ["type"] = "annual" }; // Act var result = _sut.Evaluate(condition, variables); // Assert result.Should().BeFalse(); } [Fact] public void Evaluate_Or_ReturnsTrue_WhenAnyConditionTrue() { // Arrange const string condition = """ { "or": [ { "field": "amount", "op": ">", "value": 10000 }, { "field": "level", "op": "==", "value": "executive" } ]} """; var variables = new Dictionary { ["amount"] = 5000, ["level"] = "executive" }; // Act var result = _sut.Evaluate(condition, variables); // Assert result.Should().BeTrue(); } [Fact] public void Evaluate_Or_ReturnsFalse_WhenAllConditionsFalse() { // Arrange const string condition = """ { "or": [ { "field": "amount", "op": ">", "value": 10000 }, { "field": "level", "op": "==", "value": "executive" } ]} """; var variables = new Dictionary { ["amount"] = 5000, ["level"] = "manager" }; // Act var result = _sut.Evaluate(condition, variables); // Assert result.Should().BeFalse(); } #endregion #region Built-in Variables [Fact] public void Evaluate_LastResult_ReturnsApprovedResult() { // Arrange const string condition = """{ "field": "$lastResult", "op": "==", "value": "approved" }"""; var variables = new Dictionary { ["$lastResult"] = "approved" }; // Act var result = _sut.Evaluate(condition, variables); // Assert result.Should().BeTrue(); } [Fact] public void Evaluate_LastResult_WithRejectedResult() { // Arrange const string condition = """{ "field": "$lastResult", "op": "==", "value": "approved" }"""; var variables = new Dictionary { ["$lastResult"] = "rejected" }; // Act var result = _sut.Evaluate(condition, variables); // Assert result.Should().BeFalse(); } #endregion #region Nested Conditions [Fact] public void Evaluate_NestedAndOr_CombinesCorrectly() { // Arrange // (type == "sick" OR type == "personal") AND (days <= 1) const string condition = """ { "and": [ { "or": [ { "field": "type", "op": "==", "value": "sick" }, { "field": "type", "op": "==", "value": "personal" } ]}, { "field": "days", "op": "<=", "value": 1 } ]} """; var variables = new Dictionary { ["type"] = "sick", ["days"] = 1 }; // Act var result = _sut.Evaluate(condition, variables); // Assert result.Should().BeTrue(); } #endregion #region Edge Cases [Fact] public void Evaluate_MissingField_ReturnsFalse() { // Arrange const string condition = """{ "field": "nonexistent", "op": "==", "value": "something" }"""; var variables = new Dictionary { ["other"] = "value" }; // Act var result = _sut.Evaluate(condition, variables); // Assert result.Should().BeFalse(); } [Fact] public void Evaluate_NullVariable_ReturnsFalse() { // Arrange const string condition = """{ "field": "status", "op": "==", "value": "approved" }"""; var variables = new Dictionary { ["status"] = null! }; // Act var result = _sut.Evaluate(condition, variables); // Assert result.Should().BeFalse(); } [Fact] public void Evaluate_EmptyCondition_ReturnsTrue() { // Arrange const string condition = ""; var variables = new Dictionary(); // Act var result = _sut.Evaluate(condition, variables); // Assert result.Should().BeTrue(); } [Fact] public void Evaluate_TypeMismatch_ReturnsFalse() { // Arrange const string condition = """{ "field": "amount", "op": ">", "value": 5000 }"""; var variables = new Dictionary { ["amount"] = "not-a-number" }; // Act var result = _sut.Evaluate(condition, variables); // Assert result.Should().BeFalse(); } #endregion #region Numeric Comparisons [Fact] public void Evaluate_NumericGreaterThan_WithStringVariables_ParsesCorrectly() { // Arrange const string condition = """{ "field": "total", "op": ">", "value": 100 }"""; var variables = new Dictionary { ["total"] = "250" }; // Act var result = _sut.Evaluate(condition, variables); // Assert result.Should().BeTrue(); } [Fact] public void Evaluate_NumericLessThan_WithDecimalValues() { // Arrange const string condition = """{ "field": "price", "op": "<", "value": 99.99 }"""; var variables = new Dictionary { ["price"] = 49.50 }; // Act var result = _sut.Evaluate(condition, variables); // Assert result.Should().BeTrue(); } #endregion }