Skip to content

Commit

Permalink
Add nil check for knowledge base inside execute and fetch matching rule
Browse files Browse the repository at this point in the history
passing a nil knowledgebase to exceute or fetch matching rules method
was resulting in a panic which took some time to find, adding a nil
check for both of these methods to throw proper error in this case

added unit tests for the same
  • Loading branch information
Pandey-SaurabhP committed Aug 19, 2023
1 parent 332a254 commit 3ae9c8f
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 3 deletions.
19 changes: 16 additions & 3 deletions engine/GruleEngine.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,12 @@ package engine
import (
"context"
"fmt"
"github.com/sirupsen/logrus"
"go.uber.org/zap"
"sort"
"time"

"github.com/sirupsen/logrus"
"go.uber.org/zap"

"github.com/hyperjumptech/grule-rule-engine/ast"
"github.com/hyperjumptech/grule-rule-engine/logger"
)
Expand Down Expand Up @@ -85,7 +86,6 @@ type GruleEngine struct {

// Execute function is the same as ExecuteWithContext(context.Background())
func (g *GruleEngine) Execute(dataCtx ast.IDataContext, knowledge *ast.KnowledgeBase) error {

return g.ExecuteWithContext(context.Background(), dataCtx, knowledge)
}

Expand Down Expand Up @@ -120,6 +120,12 @@ func (g *GruleEngine) notifyBeginCycle(cycle uint64) {
// The engine will evaluate context cancelation status in each cycle.
// The engine also do conflict resolution of which rule to execute.
func (g *GruleEngine) ExecuteWithContext(ctx context.Context, dataCtx ast.IDataContext, knowledge *ast.KnowledgeBase) error {
// check for nil values of knowledgebase before trying to execute rule
if knowledge == nil {
log.Warn("cannot execute rules from a nil knowledgebase")
return fmt.Errorf("cannot execute rules from a nil knowledgebase")
}

log.Debugf("Starting rule execution using knowledge '%s' version %s. Contains %d rule entries", knowledge.Name, knowledge.Version, len(knowledge.RuleEntries))

// Prepare the timer, we need to measure the processing time in debug mode.
Expand Down Expand Up @@ -241,7 +247,14 @@ func (g *GruleEngine) ExecuteWithContext(ctx context.Context, dataCtx ast.IDataC
// FetchMatchingRules function is responsible to fetch all the rules that matches to a fact against all rule entries
// Returns []*ast.RuleEntry order by salience
func (g *GruleEngine) FetchMatchingRules(dataCtx ast.IDataContext, knowledge *ast.KnowledgeBase) ([]*ast.RuleEntry, error) {
// check for nil values of knowledgebase before trying to access its members
if knowledge == nil {
log.Warn("cannot fetch rules from a nil knowledgebase")
return nil, fmt.Errorf("cannot fetch rules from a nil knowledgebase")
}

log.Debugf("Starting rule matching using knowledge '%s' version %s. Contains %d rule entries", knowledge.Name, knowledge.Version, len(knowledge.RuleEntries))

// Prepare the build-in function and add to datacontext.
defunc := &ast.BuiltInFunctions{
Knowledge: knowledge,
Expand Down
54 changes: 54 additions & 0 deletions examples/NilKnowledgeBasePanic_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// Copyright hyperjumptech/grule-rule-engine Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package examples

import (
"testing"

"grule-rule-engine/ast"
"grule-rule-engine/engine"

"github.com/stretchr/testify/assert"
)

func Test_NoPanicOnEmptyKnowledgeBase(t *testing.T) {
// create a new fact for user
user := &User{
Name: "Calo",
Age: 0,
Male: true,
}
// create an empty data context
dataContext := ast.NewDataContext()
// add the fact struct to the data context
err := dataContext.Add("User", user)
if err != nil {
t.Fatal(err)
}

t.Run("with nil knowledge base in execute", func(t *testing.T) {
eng := &engine.GruleEngine{MaxCycle: 10}
err = eng.Execute(dataContext, nil)

assert.ErrorContains(t, err, "cannot execute rules from a nil knowledgebase")
})

t.Run("with nil knowledge base in FetchMatchingRules", func(t *testing.T) {
eng := &engine.GruleEngine{MaxCycle: 10}
_, err = eng.FetchMatchingRules(dataContext, nil)

assert.ErrorContains(t, err, "cannot fetch rules from a nil knowledgebase")
})
}

0 comments on commit 3ae9c8f

Please sign in to comment.