forked from microsoft/RulesEngine
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathIssue513Test.cs
More file actions
98 lines (90 loc) · 3.56 KB
/
Issue513Test.cs
File metadata and controls
98 lines (90 loc) · 3.56 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using RulesEngine.Models;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Threading.Tasks;
using Xunit;
namespace RulesEngine.UnitTest
{
[ExcludeFromCodeCoverage]
public class Issue513Support
{
public class Record
{
public int Id { get; set; }
public string Tag { get; set; }
}
}
[ExcludeFromCodeCoverage]
public class Issue513Test
{
// Documents the recommended pattern for "OR-semantics across many rules":
// wrap them under a single parent rule with Operator="OrElse" and set
// NestedRuleExecutionMode.Performance so the engine short-circuits on the
// first true child. The reporter's 13-rules × 600-records scenario fits
// this shape directly.
[Fact]
public async Task NestedOr_WithPerformanceMode_ShortCircuitsOnFirstTrue()
{
var workflow = new Workflow
{
WorkflowName = "wf",
Rules = new[]
{
new Rule
{
RuleName = "AnyOfThese",
Operator = "OrElse",
Rules = Enumerable.Range(0, 13)
.Select(i => new Rule
{
RuleName = $"R{i}",
Expression = $"input1.Tag == \"t{i}\""
})
.ToList()
}
}
};
var engine = new RulesEngine(
new[] { workflow },
new ReSettings { NestedRuleExecutionMode = NestedRuleExecutionMode.Performance });
// Matches the first child rule (Tag == "t0").
var firstMatch = new Issue513Support.Record { Tag = "t0" };
var firstResults = await engine.ExecuteAllRulesAsync("wf", firstMatch);
Assert.Single(firstResults);
Assert.True(firstResults[0].IsSuccess);
// Only the first child evaluated → child results count is 1, not 13.
Assert.Single(firstResults[0].ChildResults);
// Matches no rule.
var noMatch = new Issue513Support.Record { Tag = "none" };
var noResults = await engine.ExecuteAllRulesAsync("wf", noMatch);
Assert.False(noResults[0].IsSuccess);
// All 13 children evaluated because none matched.
Assert.Equal(13, noResults[0].ChildResults.Count());
}
// Compared to top-level rules, where every rule runs every time and you get a
// result tree per rule. This is the "no-short-circuit" baseline.
[Fact]
public async Task TopLevelRules_NoShortCircuit_AlwaysEvaluatesAll()
{
var workflow = new Workflow
{
WorkflowName = "wf",
Rules = Enumerable.Range(0, 13)
.Select(i => new Rule
{
RuleName = $"R{i}",
Expression = $"input1.Tag == \"t{i}\""
})
.ToArray()
};
var engine = new RulesEngine(new[] { workflow });
var match = new Issue513Support.Record { Tag = "t0" };
var results = await engine.ExecuteAllRulesAsync("wf", match);
Assert.Equal(13, results.Count);
Assert.Equal(1, results.Count(r => r.IsSuccess));
}
}
}