Temporary variables with the same name across rules may override each other's values.
Describe the bug
Temporary variables with the same name across rules may override each other's values.
To Reproduce
I wrote two tests to reproduce this issue, the code below is the first test, and it runs as expected.
func TestGruleSameNameVariableOverrideIssue1(t *testing.T) {
rules := `
rule UpdateFirstName "Update first name" {
when
Fact.IsAdult == true
then
Fact.FirstName = "John";
Retract("UpdateFirstName");
}
rule UpdateLastName "Update last name" {
when
Fact.IsAdult == true
then
Fact.LastName = "Doe";
Retract("UpdateLastName");
}
`
knowledgeLibrary := ast.NewKnowledgeLibrary()
ruleBuilder := builder.NewRuleBuilder(knowledgeLibrary)
err := ruleBuilder.BuildRuleFromResource("TestRules", "0.0.1", pkg.NewBytesResource([]byte(rules)))
assert.NoError(t, err)
knowledgeBase, err := knowledgeLibrary.NewKnowledgeBaseInstance("TestRules", "0.0.1")
assert.NoError(t, err)
dataContext := ast.NewDataContext()
fact := &testFact{
IsAdult: true,
}
err = dataContext.Add("Fact", fact)
assert.NoError(t, err)
eng := engine.NewGruleEngine()
err = eng.Execute(dataContext, knowledgeBase)
assert.NoError(t, err)
assert.Equal(t, "John", fact.FirstName)
assert.Equal(t, "Doe", fact.LastName)
}
After that, I wrote another test. As you can see, the only difference is that I use a temporary variable to store a value before assigning it to a field of the fact.
However, it appears that this temporary variable is shared across rules—if a value is assigned in ruleA, it can still be accessed in ruleB.
I'm not sure whether this is a bug, as I couldn't find any relevant explanation or example in the official documentation. But the behavior does seem unexpected.
func TestGruleSameNameVariableOverrideIssue2(t *testing.T) {
rules := `
rule UpdateFirstName "Update first name" {
when
Fact.IsAdult == true
then
tmp = "John";
Fact.FirstName = tmp;
Retract("UpdateFirstName");
}
rule UpdateLastName "Update last name" {
when
Fact.IsAdult == true
then
tmp = "Doe";
Fact.LastName = tmp;
Retract("UpdateLastName");
}
`
// ...
}
Output:
&{true John John} or &{true Doe Doe}
If I rename the variables to tmp1 and tmp2, everything works as expected again. It seems that Grule does support temporary variables within rules, but the behavior is somewhat unreliable.