From 250254b213708a679179a4780ec9f7e2e0cc1e2f Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Mon, 18 Jan 2016 13:36:40 +0200 Subject: [PATCH 1/4] return root object instead of storing it --- utils/cond_loader.go | 13 ++++++------- utils/cond_loader_test.go | 6 +++--- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/utils/cond_loader.go b/utils/cond_loader.go index e9b375ee5..a7bcd617d 100644 --- a/utils/cond_loader.go +++ b/utils/cond_loader.go @@ -57,9 +57,7 @@ func isOperator(s string) bool { return strings.HasPrefix(s, "*") } -type CondLoader struct { - RootElement CondElement -} +type CondLoader struct{} func (cp *CondLoader) Load(a map[string]interface{}, parentElement CondElement) (CondElement, error) { for key, value := range a { @@ -93,9 +91,10 @@ func (cp *CondLoader) Load(a map[string]interface{}, parentElement CondElement) return nil, nil } -func (cp *CondLoader) Parse(s string) (err error) { +func (cp *CondLoader) Parse(s string) (root CondElement, err error) { a := make(map[string]interface{}) - json.Unmarshal([]byte([]byte(s)), &a) - cp.RootElement, err = cp.Load(a, nil) - return + if err := json.Unmarshal([]byte([]byte(s)), &a); err != nil { + return nil, err + } + return cp.Load(a, nil) } diff --git a/utils/cond_loader_test.go b/utils/cond_loader_test.go index 5d4a251ed..70b0382f8 100644 --- a/utils/cond_loader_test.go +++ b/utils/cond_loader_test.go @@ -4,8 +4,8 @@ import "testing" func TestCondLoader(t *testing.T) { cl := &CondLoader{} - err := cl.Parse(`{"*or":[{"test":1},{"field":{"*gt":1}},{"best":"coco"}]}`) - if err != nil || cl.RootElement == nil { - t.Errorf("Error loading structure: %+v (%v)", ToIJSON(cl.RootElement), err) + root, err := cl.Parse(`{"*or":[{"test":1},{"field":{"*gt":1}},{"best":"coco"}]}`) + if err != nil || root == nil { + t.Errorf("Error loading structure: %+v (%v)", ToIJSON(root), err) } } From f097ac86469dee2f2593f426cb9c688480029b2a Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Mon, 18 Jan 2016 13:40:56 +0200 Subject: [PATCH 2/4] no account protection (*transfer_monetary_default) --- engine/action.go | 3 +++ utils/consts.go | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/engine/action.go b/engine/action.go index cfce5fe18..79e0df5df 100644 --- a/engine/action.go +++ b/engine/action.go @@ -604,6 +604,9 @@ func removeBalanceAction(ub *Account, sq *StatsQueueTriggered, a *Action, acs Ac } func transferMonetaryDefault(acc *Account, sq *StatsQueueTriggered, a *Action, acs Actions) error { + if acc == nil { + return utils.NewErrAccountNotFound("", "*transfer_monetary_default") + } if _, exists := acc.BalanceMap[utils.MONETARY]; !exists { return utils.ErrNotFound } diff --git a/utils/consts.go b/utils/consts.go index 75530494f..5eddb8d00 100644 --- a/utils/consts.go +++ b/utils/consts.go @@ -13,6 +13,10 @@ func NewErrServerError(err error) error { return fmt.Errorf("SERVER_ERROR: %s", err) } +func NewErrAccountNotFound(accID, context string) error { + return fmt.Errorf("ACCOUNT_NOT_FOUND: %s in %s", accID, context) +} + var ( ErrNotImplemented = errors.New("NOT_IMPLEMENTED") ErrNotFound = errors.New("NOT_FOUND") From e0c8ea02c5225b32c974fee3f80126084e6ae4fa Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Mon, 18 Jan 2016 17:22:18 +0200 Subject: [PATCH 3/4] refined error messages --- console/scheduler_queue.go | 2 +- engine/action.go | 3 ++- engine/calldesc.go | 6 +++++- utils/consts.go | 7 ++----- 4 files changed, 10 insertions(+), 8 deletions(-) diff --git a/console/scheduler_queue.go b/console/scheduler_queue.go index 079575373..c91eb92a7 100644 --- a/console/scheduler_queue.go +++ b/console/scheduler_queue.go @@ -22,7 +22,7 @@ import "github.com/cgrates/cgrates/apier/v1" func init() { c := &CmdGetScheduledActions{ - name: "scheduler_queue", + name: "2", rpcMethod: "ApierV1.GetScheduledActions", rpcParams: &v1.AttrsGetScheduledActions{}, } diff --git a/engine/action.go b/engine/action.go index 79e0df5df..fdf0eaff1 100644 --- a/engine/action.go +++ b/engine/action.go @@ -605,7 +605,8 @@ func removeBalanceAction(ub *Account, sq *StatsQueueTriggered, a *Action, acs Ac func transferMonetaryDefault(acc *Account, sq *StatsQueueTriggered, a *Action, acs Actions) error { if acc == nil { - return utils.NewErrAccountNotFound("", "*transfer_monetary_default") + utils.Logger.Err("*transfer_monetary_default called without account") + return utils.ErrAccountNotFound } if _, exists := acc.BalanceMap[utils.MONETARY]; !exists { return utils.ErrNotFound diff --git a/engine/calldesc.go b/engine/calldesc.go index 7ceb823f2..46771baa4 100644 --- a/engine/calldesc.go +++ b/engine/calldesc.go @@ -195,7 +195,11 @@ func (cd *CallDescriptor) LoadRatingPlans() (err error) { err, _ = cd.getRatingPlansForPrefix(cd.GetKey(FALLBACK_SUBJECT), 1) } //load the rating plans - if err != nil || !cd.continousRatingInfos() { + if err != nil { + utils.Logger.Err(fmt.Sprintf("Rating plan not found for destination %s and account: %s, subject: %s", cd.Destination, cd.GetAccountKey(), cd.GetKey(cd.Subject))) + err = utils.ErrRatingPlanNotFound + } + if !cd.continousRatingInfos() { utils.Logger.Err(fmt.Sprintf("Destination %s not authorized for account: %s, subject: %s", cd.Destination, cd.GetAccountKey(), cd.GetKey(cd.Subject))) err = utils.ErrUnauthorizedDestination } diff --git a/utils/consts.go b/utils/consts.go index 5eddb8d00..345408b29 100644 --- a/utils/consts.go +++ b/utils/consts.go @@ -13,10 +13,6 @@ func NewErrServerError(err error) error { return fmt.Errorf("SERVER_ERROR: %s", err) } -func NewErrAccountNotFound(accID, context string) error { - return fmt.Errorf("ACCOUNT_NOT_FOUND: %s in %s", accID, context) -} - var ( ErrNotImplemented = errors.New("NOT_IMPLEMENTED") ErrNotFound = errors.New("NOT_FOUND") @@ -30,7 +26,8 @@ var ( ErrInvalidPath = errors.New("INVALID_PATH") ErrInvalidKey = errors.New("INVALID_KEY") ErrUnauthorizedDestination = errors.New("UNAUTHORIZED_DESTINATION") - ErrAccountNotFound = errors.New("AccountNotFound") + ErrRatingPlanNotFound = errors.New("RATING_PLAN_NOT_FOUND") + ErrAccountNotFound = errors.New("ACCOUNT_NOT_FOUND") ) const ( From 5e7bdbcd974d6836eda0eacb448f12798dd06c00 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Mon, 18 Jan 2016 22:03:11 +0200 Subject: [PATCH 4/4] condition language parser and tests --- engine/account.go | 23 ++-- engine/action.go | 8 +- utils/cond_loader.go | 173 ++++++++++++++++++------- utils/cond_loader_test.go | 258 +++++++++++++++++++++++++++++++++++++- 4 files changed, 399 insertions(+), 63 deletions(-) diff --git a/engine/account.go b/engine/account.go index ec486c756..e539721f0 100644 --- a/engine/account.go +++ b/engine/account.go @@ -697,17 +697,20 @@ func (acc *Account) DebitConnectionFee(cc *CallCost, usefulMoneyBalances Balance } } -func (acc *Account) matchConditions(condition string) (bool, error) { - condMap := make(map[string]interface{}) - if err := json.Unmarshal([]byte(condition), condMap); err != nil { - return false, err +func (acc *Account) matchConditions(condition, balanceType string) (bool, error) { + + cl := &utils.CondLoader{} + cl.Parse(condition) + for _, b := range acc.BalanceMap[balanceType] { + check, err := cl.Check(b) + if err != nil { + return false, err + } + if check { + return true, nil + } } - operator, found := condMap["Operator"] - if !found { - operator = utils.COND_EQ - } - _ = operator - return true, nil + return false, nil } // used in some api for transition diff --git a/engine/action.go b/engine/action.go index fdf0eaff1..97a1b807b 100644 --- a/engine/action.go +++ b/engine/action.go @@ -628,7 +628,7 @@ func transferMonetaryDefault(acc *Account, sq *StatsQueueTriggered, a *Action, a } func conditionalDebitAction(acc *Account, sq *StatsQueueTriggered, a *Action, acs Actions) error { - if matched, err := acc.matchConditions(a.ExtraParameters); matched { + if matched, err := acc.matchConditions(a.ExtraParameters, a.BalanceType); matched { return debitAction(acc, sq, a, acs) } else { return err @@ -636,7 +636,7 @@ func conditionalDebitAction(acc *Account, sq *StatsQueueTriggered, a *Action, ac } func conditionalDebitResetAction(acc *Account, sq *StatsQueueTriggered, a *Action, acs Actions) error { - if matched, err := acc.matchConditions(a.ExtraParameters); matched { + if matched, err := acc.matchConditions(a.ExtraParameters, a.BalanceType); matched { return debitResetAction(acc, sq, a, acs) } else { return err @@ -644,7 +644,7 @@ func conditionalDebitResetAction(acc *Account, sq *StatsQueueTriggered, a *Actio } func conditionalTopupAction(acc *Account, sq *StatsQueueTriggered, a *Action, acs Actions) error { - if matched, err := acc.matchConditions(a.ExtraParameters); matched { + if matched, err := acc.matchConditions(a.ExtraParameters, a.BalanceType); matched { return topupAction(acc, sq, a, acs) } else { return err @@ -652,7 +652,7 @@ func conditionalTopupAction(acc *Account, sq *StatsQueueTriggered, a *Action, ac } func conditionalTopupResetAction(acc *Account, sq *StatsQueueTriggered, a *Action, acs Actions) error { - if matched, err := acc.matchConditions(a.ExtraParameters); matched { + if matched, err := acc.matchConditions(a.ExtraParameters, a.BalanceType); matched { return topupResetAction(acc, sq, a, acs) } else { return err diff --git a/utils/cond_loader.go b/utils/cond_loader.go index a7bcd617d..08695f8f1 100644 --- a/utils/cond_loader.go +++ b/utils/cond_loader.go @@ -2,88 +2,164 @@ package utils import ( "encoding/json" + "errors" + "reflect" "strings" ) const ( - COND_EQ = "*eq" - COND_GT = "*gt" - COND_LT = "*lt" - COND_EXP = "*exp" + CondEQ = "*eq" + CondGT = "*gt" + CondGTE = "*gte" + CondLT = "*lt" + CondLTE = "*lte" + CondEXP = "*exp" + CondOR = "*or" + CondAND = "*and" ) -type CondElement interface { - AddChild(CondElement) - CheckStruct(interface{}) (bool, error) +var ( + ErrNotNumerical = errors.New("NOT_NUMERICAL") +) + +type condElement interface { + addChild(condElement) error + checkStruct(interface{}) (bool, error) } -type OperatorSlice struct { - Operator string - Slice []CondElement +type operatorSlice struct { + operator string + slice []condElement } -func (os *OperatorSlice) AddChild(ce CondElement) { - os.Slice = append(os.Slice, ce) +func (os *operatorSlice) addChild(ce condElement) error { + os.slice = append(os.slice, ce) + return nil } -func (os *OperatorSlice) CheckStruct(o interface{}) (bool, error) { return true, nil } - -type KeyStruct struct { - Key string - Struct CondElement +func (os *operatorSlice) checkStruct(o interface{}) (bool, error) { + switch os.operator { + case CondOR: + for _, cond := range os.slice { + check, err := cond.checkStruct(o) + if err != nil { + return false, err + } + if check { + return true, nil + } + } + case CondAND: + accumulator := true + for _, cond := range os.slice { + check, err := cond.checkStruct(o) + if err != nil { + return false, err + } + accumulator = accumulator && check + } + return accumulator, nil + } + return false, nil } -func (ks *KeyStruct) AddChild(ce CondElement) { - ks.Struct = ce -} -func (ks *KeyStruct) CheckStruct(o interface{}) (bool, error) { return true, nil } - -type OperatorValue struct { - Operator string - Value interface{} +type keyStruct struct { + key string + elem condElement } -func (ov *OperatorValue) AddChild(CondElement) {} -func (ov *OperatorValue) CheckStruct(o interface{}) (bool, error) { return true, nil } - -type KeyValue struct { - Key string - Value interface{} +func (ks *keyStruct) addChild(ce condElement) error { + ks.elem = ce + return nil +} +func (ks *keyStruct) checkStruct(o interface{}) (bool, error) { + obj := reflect.ValueOf(o) + if obj.Kind() == reflect.Ptr { + obj = obj.Elem() + } + value := obj.FieldByName(ks.key) + return ks.elem.checkStruct(value.Interface()) } -func (os *KeyValue) AddChild(CondElement) {} -func (os *KeyValue) CheckStruct(o interface{}) (bool, error) { return true, nil } +type operatorValue struct { + operator string + value interface{} +} + +func (ov *operatorValue) addChild(condElement) error { return ErrNotImplemented } +func (ov *operatorValue) checkStruct(o interface{}) (bool, error) { + if ov.operator == CondEQ { + return ov.value == o, nil + } + var of, vf float64 + var ok bool + if of, ok = o.(float64); !ok { + return false, ErrNotNumerical + } + if vf, ok = ov.value.(float64); !ok { + return false, ErrNotNumerical + } + switch ov.operator { + case CondGT: + return of > vf, nil + case CondGTE: + return of >= vf, nil + case CondLT: + return of < vf, nil + case CondLTE: + return of <= vf, nil + + } + return true, nil +} + +type keyValue struct { + key string + value interface{} +} + +func (kv *keyValue) addChild(condElement) error { return ErrNotImplemented } +func (kv *keyValue) checkStruct(o interface{}) (bool, error) { + obj := reflect.ValueOf(o) + if obj.Kind() == reflect.Ptr { + obj = obj.Elem() + } + value := obj.FieldByName(kv.key) + return value.Interface() == kv.value, nil +} func isOperator(s string) bool { return strings.HasPrefix(s, "*") } -type CondLoader struct{} +type CondLoader struct { + rootElement condElement +} -func (cp *CondLoader) Load(a map[string]interface{}, parentElement CondElement) (CondElement, error) { +func (cp *CondLoader) load(a map[string]interface{}, parentElement condElement) (condElement, error) { for key, value := range a { - var currentElement CondElement + var currentElement condElement switch t := value.(type) { case []interface{}: - currentElement = &OperatorSlice{Operator: key} + currentElement = &operatorSlice{operator: key} for _, e := range t { - cp.Load(e.(map[string]interface{}), currentElement) + cp.load(e.(map[string]interface{}), currentElement) } case map[string]interface{}: - currentElement = &KeyStruct{Key: key} + currentElement = &keyStruct{key: key} //log.Print("map: ", t) - cp.Load(t, currentElement) + cp.load(t, currentElement) case interface{}: if isOperator(key) { - currentElement = &OperatorValue{Operator: key, Value: t} + currentElement = &operatorValue{operator: key, value: t} } else { - currentElement = &KeyValue{Key: key, Value: t} + currentElement = &keyValue{key: key, value: t} } //log.Print("generic interface: ", t) default: return nil, ErrParserError } if parentElement != nil { - parentElement.AddChild(currentElement) + parentElement.addChild(currentElement) } else { return currentElement, nil } @@ -91,10 +167,15 @@ func (cp *CondLoader) Load(a map[string]interface{}, parentElement CondElement) return nil, nil } -func (cp *CondLoader) Parse(s string) (root CondElement, err error) { +func (cp *CondLoader) Parse(s string) (err error) { a := make(map[string]interface{}) if err := json.Unmarshal([]byte([]byte(s)), &a); err != nil { - return nil, err + return err } - return cp.Load(a, nil) + cp.rootElement, err = cp.load(a, nil) + return +} + +func (cp *CondLoader) Check(o interface{}) (bool, error) { + return cp.rootElement.checkStruct(o) } diff --git a/utils/cond_loader_test.go b/utils/cond_loader_test.go index 70b0382f8..e5ef1c42f 100644 --- a/utils/cond_loader_test.go +++ b/utils/cond_loader_test.go @@ -4,8 +4,260 @@ import "testing" func TestCondLoader(t *testing.T) { cl := &CondLoader{} - root, err := cl.Parse(`{"*or":[{"test":1},{"field":{"*gt":1}},{"best":"coco"}]}`) - if err != nil || root == nil { - t.Errorf("Error loading structure: %+v (%v)", ToIJSON(root), err) + err := cl.Parse(`{"*or":[{"test":1},{"field":{"*gt":1}},{"best":"coco"}]}`) + if err != nil { + t.Errorf("Error loading structure: %+v (%v)", ToIJSON(cl.rootElement), err) + } +} + +func TestCondKeyValue(t *testing.T) { + o := struct { + Test string + Field float64 + Other bool + }{ + Test: "test", + Field: 6.0, + Other: true, + } + cl := &CondLoader{} + err := cl.Parse(`{"Test":"test"}`) + if err != nil { + t.Errorf("Error loading structure: %+v (%v)", ToIJSON(cl.rootElement), err) + } + if check, err := cl.Check(o); !check || err != nil { + t.Errorf("Error checking struct: %v %v (%v)", check, err, ToIJSON(cl.rootElement)) + } + err = cl.Parse(`{"Field":6}`) + if err != nil { + t.Errorf("Error loading structure: %+v (%v)", ToIJSON(cl.rootElement), err) + } + if check, err := cl.Check(o); !check || err != nil { + t.Errorf("Error checking struct: %v %v (%v)", check, err, ToIJSON(cl.rootElement)) + } + err = cl.Parse(`{"Other":true}`) + if err != nil { + t.Errorf("Error loading structure: %+v (%v)", ToIJSON(cl.rootElement), err) + } + if check, err := cl.Check(o); !check || err != nil { + t.Errorf("Error checking struct: %v %v (%v)", check, err, ToIJSON(cl.rootElement)) + } +} + +func TestCondKeyValuePointer(t *testing.T) { + o := &struct { + Test string + Field float64 + Other bool + }{ + Test: "test", + Field: 6.0, + Other: true, + } + cl := &CondLoader{} + err := cl.Parse(`{"Test":"test"}`) + if err != nil { + t.Errorf("Error loading structure: %+v (%v)", ToIJSON(cl.rootElement), err) + } + if check, err := cl.Check(o); !check || err != nil { + t.Errorf("Error checking struct: %v %v (%v)", check, err, ToIJSON(cl.rootElement)) + } + err = cl.Parse(`{"Field":6}`) + if err != nil { + t.Errorf("Error loading structure: %+v (%v)", ToIJSON(cl.rootElement), err) + } + if check, err := cl.Check(o); !check || err != nil { + t.Errorf("Error checking struct: %v %v (%v)", check, err, ToIJSON(cl.rootElement)) + } + err = cl.Parse(`{"Other":true}`) + if err != nil { + t.Errorf("Error loading structure: %+v (%v)", ToIJSON(cl.rootElement), err) + } + if check, err := cl.Check(o); !check || err != nil { + t.Errorf("Error checking struct: %v %v (%v)", check, err, ToIJSON(cl.rootElement)) + } +} + +func TestCondOperatorValue(t *testing.T) { + root := &operatorValue{operator: "*gt", value: 3.4} + if check, err := root.checkStruct(3.5); !check || err != nil { + t.Errorf("Error checking struct: %v %v (%v)", check, err, ToIJSON(root)) + } + root = &operatorValue{operator: "*eq", value: 3.4} + if check, err := root.checkStruct(3.5); check || err != nil { + t.Errorf("Error checking struct: %v %v (%v)", check, err, ToIJSON(root)) + } + root = &operatorValue{operator: "*eq", value: 3.4} + if check, err := root.checkStruct(3.4); !check || err != nil { + t.Errorf("Error checking struct: %v %v (%v)", check, err, ToIJSON(root)) + } + root = &operatorValue{operator: "*eq", value: "zinc"} + if check, err := root.checkStruct("zinc"); !check || err != nil { + t.Errorf("Error checking struct: %v %v (%v)", check, err, ToIJSON(root)) + } +} + +func TestCondKeyStruct(t *testing.T) { + o := struct { + Test string + Field float64 + Other bool + }{ + Test: "test", + Field: 6.0, + Other: true, + } + cl := &CondLoader{} + err := cl.Parse(`{"Field":{"*gt": 5}}`) + if err != nil { + t.Errorf("Error loading structure: %+v (%v)", ToIJSON(cl.rootElement), err) + } + if check, err := cl.Check(o); !check || err != nil { + t.Errorf("Error checking struct: %v %v (%v)", check, err, ToIJSON(cl.rootElement)) + } + err = cl.Parse(`{"Test":{"*gt": 5}}`) + if err != nil { + t.Errorf("Error loading structure: %+v (%v)", ToIJSON(cl.rootElement), err) + } + if check, err := cl.Check(o); check || err != ErrNotNumerical { + t.Errorf("Error checking struct: %v %v (%v)", check, err, ToIJSON(cl.rootElement)) + } + err = cl.Parse(`{"Field":{"*gte": 6}}`) + if err != nil { + t.Errorf("Error loading structure: %+v (%v)", ToIJSON(cl.rootElement), err) + } + if check, err := cl.Check(o); !check || err != nil { + t.Errorf("Error checking struct: %v %v (%v)", check, err, ToIJSON(cl.rootElement)) + } + err = cl.Parse(`{"Field":{"*lt": 7}}`) + if err != nil { + t.Errorf("Error loading structure: %+v (%v)", ToIJSON(cl.rootElement), err) + } + if check, err := cl.Check(o); !check || err != nil { + t.Errorf("Error checking struct: %v %v (%v)", check, err, ToIJSON(cl.rootElement)) + } + err = cl.Parse(`{"Field":{"*lte": 6}}`) + if err != nil { + t.Errorf("Error loading structure: %+v (%v)", ToIJSON(cl.rootElement), err) + } + if check, err := cl.Check(o); !check || err != nil { + t.Errorf("Error checking struct: %v %v (%v)", check, err, ToIJSON(cl.rootElement)) + } + err = cl.Parse(`{"Field":{"*eq": 6}}`) + if err != nil { + t.Errorf("Error loading structure: %+v (%v)", ToIJSON(cl.rootElement), err) + } + if check, err := cl.Check(o); !check || err != nil { + t.Errorf("Error checking struct: %v %v (%v)", check, err, ToIJSON(cl.rootElement)) + } + err = cl.Parse(`{"Test":{"*eq": "test"}}`) + if err != nil { + t.Errorf("Error loading structure: %+v (%v)", ToIJSON(cl.rootElement), err) + } + if check, err := cl.Check(o); !check || err != nil { + t.Errorf("Error checking struct: %v %v (%v)", check, err, ToIJSON(cl.rootElement)) + } +} + +func TestCondKeyStructPointer(t *testing.T) { + o := &struct { + Test string + Field float64 + Other bool + }{ + Test: "test", + Field: 6.0, + Other: true, + } + cl := &CondLoader{} + err := cl.Parse(`{"Field":{"*gt": 5}}`) + if err != nil { + t.Errorf("Error loading structure: %+v (%v)", ToIJSON(cl.rootElement), err) + } + if check, err := cl.Check(o); !check || err != nil { + t.Errorf("Error checking struct: %v %v (%v)", check, err, ToIJSON(cl.rootElement)) + } + err = cl.Parse(`{"Test":{"*gt": 5}}`) + if err != nil { + t.Errorf("Error loading structure: %+v (%v)", ToIJSON(cl.rootElement), err) + } + if check, err := cl.Check(o); check || err != ErrNotNumerical { + t.Errorf("Error checking struct: %v %v (%v)", check, err, ToIJSON(cl.rootElement)) + } + err = cl.Parse(`{"Field":{"*gte": 6}}`) + if err != nil { + t.Errorf("Error loading structure: %+v (%v)", ToIJSON(cl.rootElement), err) + } + if check, err := cl.Check(o); !check || err != nil { + t.Errorf("Error checking struct: %v %v (%v)", check, err, ToIJSON(cl.rootElement)) + } + err = cl.Parse(`{"Field":{"*lt": 7}}`) + if err != nil { + t.Errorf("Error loading structure: %+v (%v)", ToIJSON(cl.rootElement), err) + } + if check, err := cl.Check(o); !check || err != nil { + t.Errorf("Error checking struct: %v %v (%v)", check, err, ToIJSON(cl.rootElement)) + } + err = cl.Parse(`{"Field":{"*lte": 6}}`) + if err != nil { + t.Errorf("Error loading structure: %+v (%v)", ToIJSON(cl.rootElement), err) + } + if check, err := cl.Check(o); !check || err != nil { + t.Errorf("Error checking struct: %v %v (%v)", check, err, ToIJSON(cl.rootElement)) + } + err = cl.Parse(`{"Field":{"*eq": 6}}`) + if err != nil { + t.Errorf("Error loading structure: %+v (%v)", ToIJSON(cl.rootElement), err) + } + if check, err := cl.Check(o); !check || err != nil { + t.Errorf("Error checking struct: %v %v (%v)", check, err, ToIJSON(cl.rootElement)) + } + err = cl.Parse(`{"Test":{"*eq": "test"}}`) + if err != nil { + t.Errorf("Error loading structure: %+v (%v)", ToIJSON(cl.rootElement), err) + } + if check, err := cl.Check(o); !check || err != nil { + t.Errorf("Error checking struct: %v %v (%v)", check, err, ToIJSON(cl.rootElement)) + } +} + +func TestCondOperatorSlice(t *testing.T) { + o := &struct { + Test string + Field float64 + Other bool + }{ + Test: "test", + Field: 6.0, + Other: true, + } + cl := &CondLoader{} + err := cl.Parse(`{"*or":[{"Test":"test"},{"Field":{"*gt":5}},{"Other":true}]}`) + if err != nil { + t.Errorf("Error loading structure: %+v (%v)", ToIJSON(cl.rootElement), err) + } + if check, err := cl.Check(o); !check || err != nil { + t.Errorf("Error checking struct: %v %v (%v)", check, err, ToIJSON(cl.rootElement)) + } + err = cl.Parse(`{"*or":[{"Test":"test"},{"Field":{"*gt":7}},{"Other":false}]}`) + if err != nil { + t.Errorf("Error loading structure: %+v (%v)", ToIJSON(cl.rootElement), err) + } + if check, err := cl.Check(o); !check || err != nil { + t.Errorf("Error checking struct: %v %v (%v)", check, err, ToIJSON(cl.rootElement)) + } + err = cl.Parse(`{"*and":[{"Test":"test"},{"Field":{"*gt":5}},{"Other":true}]}`) + if err != nil { + t.Errorf("Error loading structure: %+v (%v)", ToIJSON(cl.rootElement), err) + } + if check, err := cl.Check(o); !check || err != nil { + t.Errorf("Error checking struct: %v %v (%v)", check, err, ToIJSON(cl.rootElement)) + } + err = cl.Parse(`{"*and":[{"Test":"test"},{"Field":{"*gt":7}},{"Other":false}]}`) + if err != nil { + t.Errorf("Error loading structure: %+v (%v)", ToIJSON(cl.rootElement), err) + } + if check, err := cl.Check(o); check || err != nil { + t.Errorf("Error checking struct: %v %v (%v)", check, err, ToIJSON(cl.rootElement)) } }